ESM Support
Closed this issue · 10 comments
I've been trying to add ESM support to Comunica, one of the issues that I am hitting is that this types package does not support ESM.
I've created a minimal reproduction of the issue in https://github.com/jeswr/rdfjs-esm-playground; and the build error can be seen in the CI (https://github.com/jeswr/rdfjs-esm-playground/actions/runs/9968505181/job/27543901149).
Any advice on how to dual package a types-only package like this would be greatly appreciated.
this types package does not support ESM.
You sure? The types are used a million of ESM codebases :)
Overall, the use od ESNext
for TS module
is not recommended. I'd use NodeNext
instead, as suggested by the build error
I'd use NodeNext instead
In Comunica, I am trying to set up dual packaging of CJS and ESM. It appears that when NodeNext
is set, it is required that { "type": "module" }
be set in the package.json in order for ESM to be emitted rather than CJS. Since I am dual packaging I do not want to set { "type": "module" }
(instead I just want to set the "main", "module" and "export" fields, similarly to https://github.com/RubenVerborgh/AsyncIterator/); hence why I am setting "module": "ESNext"
in the tsconfig.json for the module build.
Do you have experience getting such builds working for dual packaging directly using tsc
- ideally a pointer to a repo using @rdfjs/types
that successfully does dual packaging. Otherwise I will need @rdfjs/types
to build with ESNext
, or, I will need to resort to using bundlers. I've already tried using rollup, however, the time it takes to build for a monorepo such as Comunica is > 10min.
cc @rubensworks
Do you have experience getting such builds working for dual packaging directly using tsc - ideally a pointer to a repo using
@rdfjs/types
that successfully does dual packaging
I'm sure none of this is an issue in the types. Those are types only, so it does not matter what is your target because they are not a runtime thing. For CJS you would set moduleResolution=Node10
and module=CommonJS
.
In the past I built dual packages where I compiled ESM using tsc
and CJS with babel
because the former could not change the target extension. I don't think I have any dual package left though.
So yea, are you sure you want a dual package in the first place? Looking around, many libraries are now ESM-only, finally :)
So yea, are you sure you want a dual package in the first place? Looking around, many libraries are now ESM-only, finally :)
I'll direct that question towards @rubensworks :)
For reference, I replicated my tsc/babel setup in your playground: jeswr/rdfjs-esm-playground#1
Have you tried putting @rdfjs/types
in devDependencies
?
I suspect your problem would go away if it was there.
I know there is a tendency to put types in dependencies but in my mind, types, just as typescript, are things we rely on at build time, not execution time, so it seems wrong to put them in dependencies. TypeScript projects that rely on your libraries will still be able to see clearly in declaration files where the interfaces come from and can install those as devDependencies in turn.
so it seems wrong to put them in dependencies
I don't think it should matter what kind of dependency you use. Also, there is good reason to actually use regular dependencies if your code uses the RDF/JS types anywhere in the exported modules. Doing otherwise sometimes causes build errors unless types are installed explicit, which I find violates the principle of least surprise.
@jeswr have you had a chance to look at my link above?
Yes - got it working; thankyou!
Working on the Comunica ESM migration in comunica/comunica#1379
I know there is a tendency to put types in dependencies but in my mind, types, just as typescript, are things we rely on at build time, not execution time, so it seems wrong to put them in dependencies.
The rule of thumb I go with is:
- If you are not building a package, or your package does not re-export the types, then put it as a
devDependency
- If you do re-export the types from you package it needs to be a
dependency
so that consumers of your library can use the typescript interfaces you expose without installing dependencies they don't directly depend on