I'm trying to use Parcel 2 to develop libraries so that:
node or in the browser, andIn theory, this should be as easy as:
main target with esmodule and setting "type": "module" in package.json,types target, andbrowser target that includes deps and puts exports on globalThis.However, even after spending hours trying to get a combination of builds that can approximate this (see this template) I still run into lots of bugs and compatibility issues. Many of these are because Parcel is trying to do something smart instead of straightforward.
I'd like to know:
I love Parcel because the development experience (especially compile times) is really great and "zero-config" used to work really well for me. I'd really like to figure out how to keep using it.
Issues I currently haven't resolved include:
comlinktypes build seems result in dangling references in the generated d.ts files right now.parcelRequire or require in browser builds, preventing code from running in the first place.runtime-regenerator/runtime sometimes, but I can't predict when. (Shouldn't the build be standalone?)______ is not a constructor for classes, which I can sometimes work around using a browserslist field in package.json but not always.<), appearing to recompile but using an old cached version of a file that I just changed, or just plainly failing to pick up on file changes.Some things I've figured out:
browser field of package.json in a dependency for all browser builds, so I can't name my "browser build" browser. (I've taken to calling it browser-global).This should do what you want
{
"type": "module",
"main": "dist/index.js",
"browser": "dist/browser.js",
"types": "dist/index.d.ts",
"targets": {
"main": {
"outputFormat": "esmodule"
},
"browser": {
"includeNodeModules": true
}
}
}
(type: ”module" is rather new and not picked up by Parcel automatically)
puts exports on globalThis.
That is not implemented (yet).
a browser target that includes deps and puts exports on globalThis.
The problem I see here is that many bundlers prefer the browser entry to main, so your setup would not work well when bundled.
This should do what you want
Thanks! I believe this still has a significant amount of issues for me. I'll try a branch of a project and identify exactly which issues remain.
I've tried taking a project and reducing the targets.
Build output for https://github.com/cubing/timer-db/commit/a6061a8d9aa61a43fad1b6a11e5adc58600f6bb2
✨ Built in 4.73s
dist/index.d.ts 1.61 KB 324ms
dist/browser-global.js 218.06 KB 365ms
dist/crypto.1a367996.js 795.25 KB 365ms
dist/index.js 11.71 KB 365ms
dist/crypto.1ca165c3.js 122 B 365ms
✨ Built in 3.16s
dist/browser-global.js 218.08 KB 392ms
dist/crypto.1a367996.js 795.25 KB 391ms
This still has the following issues:
types target is still broken. [1]npx parcel build tries to build all targets using the same source file (including the browser-global build that currently needs a separate source file). [2]require(".") in the node REPL. [3]"./dist/index.js" from a module file in node without an error about an expected CommonJS format in one of the library's imports. [4]require that is meant to be invoked conditionally (when running in node) that ideally I shouldn't have to.tsconfig.json and .babelrc to work around other issues that happen out of the box.[1] The generated dist/index.d.ts file references non-existent files:
import { Attempt, StoredAttempt, EventName } from "./data/Attempt";
import { StoredSessionMetadata, SessionMetadata } from "./data/SessionMetadata";
import { Storage } from "./storage/storage";
/******** SessionUUID ********/
type SessionUUID = string;
//... (expected export code)
export type { Attempt, StoredAttempt } from "./data/Attempt";
Each of those imported files don't exist in the build folder.
[2] I can try to fix this by replacing:
npx parcel build src/index.ts &&
npx parcel build --target browser-global src/targets/browser-global.ts
with:
npx parcel build src/index.ts --target main &&
npx parcel build src/index.ts --target types &&
npx parcel build --target browser-global src/targets/browser-global.ts
That's already three invocations of Parcel. However, it doesn't work(!). The first two invocations actually both behave like npx parcel build src/index.ts.
Build output for https://github.com/cubing/timer-db/commit/71d8a0e6657d9a34046bcd4b5cec4c8651c6faf2
✨ Built in 1.28s
dist/index.d.ts 1.61 KB 327ms
dist/browser-global.js 218.06 KB 371ms
dist/crypto.1a367996.js 795.25 KB 371ms
dist/index.js 10.53 KB 372ms
dist/crypto.f937c408.js 120 B 372ms
dist/index.esm.js 11.71 KB 372ms
dist/crypto.1ca165c3.js 122 B 372ms
✨ Built in 1.56s
dist/index.d.ts 1.61 KB 414ms
dist/browser-global.js 218.06 KB 464ms
dist/crypto.1a367996.js 795.25 KB 464ms
dist/index.js 10.53 KB 463ms
dist/crypto.f937c408.js 120 B 463ms
dist/index.esm.js 11.71 KB 463ms
dist/crypto.1ca165c3.js 122 B 463ms
✨ Built in 1.34s
dist/browser-global.js 218.08 KB 389ms
dist/crypto.1a367996.js 795.25 KB 388ms
I can fix this by using a separate entry file for the types target:
npx parcel build --target main src/index.ts &&
npx parcel build --target types src/targets/types.ts &&
npx parcel build --target browser-global src/targets/browser-global.ts
Built output for https://github.com/cubing/timer-db/commit/630f34b717849cef517764409781bca28429a714
✨ Built in 2.97s
dist/index.js 11.71 KB 357ms
dist/crypto.1ca165c3.js 122 B 357ms
✨ Built in 3.22s
dist/index.d.ts 1.61 KB 14ms
✨ Built in 3.23s
dist/browser-global.js 218.08 KB 442ms
dist/crypto.1a367996.js 795.25 KB 441ms
[3] This is arguably not Parcel's fault. I can fix this by using four targets:
npx parcel build --target main src/index.ts &&
npx parcel build --target module src/targets/module.ts &&
npx parcel build --target types src/targets/types.ts &&
npx parcel build --target browser-global src/targets/browser-global.ts
Build output for https://github.com/cubing/timer-db/commit/1c02556436d08b18932ed80650fcf8822c7cbd50
✨ Built in 3.35s
dist/index.js 10.53 KB 369ms
dist/crypto.f937c408.js 120 B 368ms
✨ Built in 2.86s
dist/index.esm.js 11.71 KB 363ms
dist/crypto.1ca165c3.js 122 B 363ms
✨ Built in 902ms
dist/index.d.ts 1.61 KB 6ms
✨ Built in 3.37s
dist/browser-global.js 218.08 KB 479ms
dist/crypto.1a367996.js 795.25 KB 478ms
[4] e.g. using node node-test.js in the commit in question. I still don't know how to resolve this.
At this point:
types output still doesn't work.If I can fix the types issue, this isn't totally unreasonable. But this feels like a lot of workarounds to use a "zero-config" tool for a fairly straightforward project, and I'm worried this isn't the end of my issues. 😔
By the way, I also think that it's perfectly reasonable to say "Parcel 2 isn't meant to be used to maintain libraries in TypeScript" and e.g. say I should be using Rollup. I have a lot of qualms with Rollup and vastly prefer Parcel 2 (Parcel 1 was already excellent for building web apps!), but I can understand if Parcel 2 has different goals. I'm trying to get an understanding of the situation by filing this issue.
I'd also love to contribute fixes/improvements to Parcel 2, but at the moment I need to focus on the actual libraries I'm writing.
Parcel 1 wasn't meant to build libraries. But Parcel 2 is
Using npx parcel build tries to build all targets using the same source file (including the browser-global build that currently needs a separate source file)
Having entries assigned to specific targets isn't possible at the moment.
Using a single entry for main/module/targets should work, otherwise it's a bug.
For browser-global, you need to use a separate entry until we add back the --global option from Parcel 1.
There is a section about this in the docs, someone just needs to fill it with content. :slightly_smiling_face:
Anyone have any advice on this?
Most helpful comment
Anyone have any advice on this?