I'm looking for feedback on a pattern I coded while tinkering around.
Context
I'm creating a package for my company, which going to be released publicly. The DX (Developer Experience) is the most important thing for us, and for such I'm picking the latest trends in dev: Typescript, esm, etc... I want to propose multiple modules and make the import easy to use, similar to (for example) nextjs's next/Router.
[EDIT] I found about this thread and cross-referenced for exposure.
First steps
I started my first attempt with a simple single package, tsconfig and src/, tests/ and dist/ folder. Nothing incredibly exotic there, but if you've been doing that, you know that publishing this will result in your import paths to contain dist later on.
You can fix this by using package.json's main field, but that'd do only for the top-level module; you could use exports, but it doesn't work well with Typescript. The easiest fix is to cp package.json dist/ && npm publish dist/, and here you go, problem solved.
Then I thought that to test this, I'd be npm link my dist/ folder, virtually creating "a package in package" with few problems:
- the
cp package.json dist/part would grow to have*.md, and who knows what else/how it'd grow (and I'd like not to have build script files) - I'd still have to build a test app to test my library on the DX level, and where to host it?
So I thought: why not elevate it all to be a monorepo?
Turning point
The tree structure would move from:
myLib/
package.json → build + test + publish script
tsconfig.json
jest.config.json
.npmignore → managing what to distribute
...lots of config files
src/
dist/
tests/
to:
myLib/
package.json → workspaces
src-pkg/
package.json → build script
tsconfig.json → localized conf for build
dist-pkg/
package.json → publish script
test-pkg/
package.json → test script
jest.config.json → localized conf for test
To pass the built files from src-pkg to dist-pkg, i only need to point the folder in tsc (tsc --project . --outDir ../dist-pkg)
It's even possible to restore the same flow as in a single package by orchestrating workspaces command in npm scripts on the top level, such as:
{
"workspaces": ["src-pkg", "test-pkg", "dist-pkg"],
"scripts": {
"build": "yarn workspace src-pkg run build",
"test": "yarn workspace test-pkg run test",
"publish": "yarn workspace dist-pkg run publish"
}
}
Stepping back
The perks of this structure is mostly in the test-pkg. It can easily test src-pkg or dist-pkg at will, since both of the structures are meant to be flat and similar. so that, the workflow can be tested before the build, or after.
Also (and this is my main area of interest), by being outside of the src-pkg the test-pkg has the PoV of a consumer of my library (Typescript context and all) - especially if using the dist-pkg as dependency - and so I can work efficiently on the developer experience exactly as if I was my future customers.
There are small other things, notably the stability of expansion by using a monorepo, or also that I can provide a separated "public friendly" readme in the dist-pkg while having more internals/private details in the src-pkg manifests.
The drawbacks are notably the management of semver (src-pkg and dist-pkg being separated) and also that it's a structure I've never seen before... and it feels a bit like a misuse of the monorepo structure.
So, I've built it and it works... but even if it's possible to build doesn't mean it should be.
People building libraries and SDKs, what do y'all think?
Thanks for your feedback.