dotalias icon indicating copy to clipboard operation
dotalias copied to clipboard

Programmatic Interface

Open moltar opened this issue 3 years ago • 13 comments

Would be great to have a programmatic interface to integrate into other projects.

I'd like to use it with projen.

moltar avatar Feb 05 '22 15:02 moltar

Hey, @moltar. That's an interesting idea. Do you have some imaginary API in mind that we could expose to help you implement this?

Internally, this library just maps a single declaration file to multiple sources (jest, webpack, etc.). All mapping functions can be described as:

function map(config: AliasConfig): any

But as I understand, you want to use some sort of API so that dotailas would know about your custom mapping and then expose you the result?

I've looked at projen, and it looks like higher-level tooling. You should be able to use dotalias with any frameworks/project generators without issues, as dotalias exposes you configs to insert into the individual tools right away.

kettanaito avatar Feb 05 '22 16:02 kettanaito

Do you have some imaginary API in mind that we could expose to help you implement this?

The original API is almost fine, I think.

The only issue is that it will throw if it can't find the source paths.

https://github.com/open-draft/dotalias/blob/45d6f438cf908d8ab058e1b2fcc969f16db93061/src/index.ts#L10-L12

So, maybe alias can be a fn that accepts paths, or defaults to discovering them.


alias({ foo: '../foo' }).jest
alias({ foo: '../foo' }).WebpackPlugin
// ...

I've looked at projen, and it looks like higher-level tooling. You should be able to use dotalias with any frameworks/project generators without issues, as dotalias exposes you configs to insert into the individual tools right away.

Right, almost there.

The only thing is that projen does not commit the config files to the filesystem, until all configs are synth'ed in memory.

We can access the config in memory (tsconfig object) during synth, and I could pass it to alias. But currently alias assumes that it will read the files from the filesystem.

I am proposing that alias should be able to accept existing path config as a parameter without reading anything from disk.

moltar avatar Feb 07 '22 05:02 moltar

Thanks for the examples! It's much more clear now.

I think we can create a new config API which would act as you've described:

  1. Accept an explicit configuration of paths;
  2. Produce the alias-compatible API, exposing the generated configurations for Jest, webpack, etc.
import { config } from 'dotalias'

const alias = config({
  server: '../packages/server/'
})

alias.jest
alias.WebpackPlugin

It's important we keep the actual usage identical to the one you get with using alias that reads the paths from the configuration files in the file system.

One question I have is how should the config behave if there is a .alias configuration file in your project? Should it merge two configs, giving the one you pass to config() the priority? Should it replace the config?

kettanaito avatar Feb 08 '22 12:02 kettanaito

IMO in programmatic use case everything should be explicit without any side effects, e.g. no reading from disk.

moltar avatar Feb 08 '22 13:02 moltar

Yeap, in favor with you on that 👍

Nice, I think we have the API worked out. Would you be interested in opening a pull request with the draft implementation? I could use your help with this.

kettanaito avatar Feb 08 '22 13:02 kettanaito

Certainly can try.

One thing I am not sure of, is how to handle this part:

https://github.com/open-draft/dotalias/blob/45d6f438cf908d8ab058e1b2fcc969f16db93061/src/index.ts#L10-L12

Since this is in the global scope, this will always throw in the programmatic use case, and it's kind of impossible to hide it now, since index exports alias that is already resolved.

moltar avatar Feb 09 '22 03:02 moltar

We can start by moving what's currently in index.ts to alias.ts. The index module will then point to both alias and new configure:

src/
  index.ts
  alias.ts
  configure.ts

This won't solve the global-scope configuration check just yet. We have two options:

Make alias a function, so that reading configuration happens when it's invoked.

Pros:

  • Encapsulated configuration reading/handling and related errors;

Cons:

  • A breaking change;
  • Loses a singleton nature; configured will be read everytime you call alias().

Use a Proxy around the alias object.

This only retain the current API but still requires some sort of memorization of the configuration so we don't read it multiple times.

Expose alias as an explicit import.

As in:

import { alias } from 'dotalias/alias'

This can also be dotalias/runtime or similar.

This way we scope the config reading to that explicit import, instead of exporting alias from the root-level index.ts.

Actually, I think this may be a good solution for config:

import { config } from 'dotalias/config'

config({ ... })

I'd recommend this option. The config module can be built separately and required to be imported directly, circumventing all the logic from alias.

What do you think?

kettanaito avatar Feb 09 '22 12:02 kettanaito

Sure, I was considering all these options too, but wasn't sure if breaking change was an option.

Not a huge fan of:

import { config } from 'dotalias/config'

But I think that might be the best option in this case.

Similar patterns exist already, e.g. see:dotenv/config.

moltar avatar Feb 10 '22 03:02 moltar

Another thought...

Might be beneficial to expose programmatic API for each converter independently.

The downside is to that approach is that it exposes (right now) internal API, and then makes it harder to move away from it later.

But the benefit is that if you only need one converter, then you don't need to call all other converters to get the full alias object back.

Thoughts?

moltar avatar Feb 10 '22 03:02 moltar

So basically make toJest and other converters public?

kettanaito avatar Feb 10 '22 11:02 kettanaito

Yeah.

Because, for example, for our use case, we have no use for Webpack, so it's just extra CPU cycles to create an instance.

moltar avatar Feb 11 '22 12:02 moltar

Then I suppose it's a replacement for config(), is that correct?

I can see both APIs being useful but I'd start with the one that immediately covers your use case.

I'm all hands for the following thing:

import { toRollup, toJest } from 'alias/config'

const config = { foo: '../bar/foo' }

const aliasOptions = toRollup(config)
const jestMapperOptions = toJest(config)

Would that be the functionality you're looking for?

By the way, I don't think there's particular trouble exporting the internal converters. I'd pretty much like to do so if it helps people achieve their goals. Converters are not complex and implement the same call signature.

kettanaito avatar Feb 11 '22 14:02 kettanaito

import { toRollup, toJest } from 'alias/config'

Yup, that's what we are looking for!

moltar avatar Feb 14 '22 12:02 moltar