GithubHelp home page GithubHelp logo

frehner / modern-guide-to-packaging-js-library Goto Github PK

View Code? Open in Web Editor NEW
1.4K 1.4K 37.0 58 KB

A guide to help ensure your JavaScript library is the most compatible, fast, and efficient library you can make.

License: GNU General Public License v3.0

javascript javascript-library npm

modern-guide-to-packaging-js-library's Introduction

Hello!

I'm Anthony Frehner, a frontend web developer and architect. I like to contribute to open source when I have the time! I'm currently working on Hydrogen and maintaining the Modern Guide to Packaging Your JavaScript Library 🙂

I've recently been providing feedback on a WICG proposal for which I also spent some time making a polyfill. I am a core-team member of the single-spa open source library, I occasionally give conference talks - here's my talk at React Rally 2019 - and write technical articles, and I proposed a new CSS unit called vhc which eventually turned into the dvh, lvh, and svh (and equivalent *vw) CSS units, which was very exciting for me personally!

modern-guide-to-packaging-js-library's People

Contributors

angrychocobo avatar ashutoshbw avatar frehner avatar konnorrogers avatar senorbeast avatar symind avatar zamiell avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

modern-guide-to-packaging-js-library's Issues

Typical webpack skips node_modules

Is esNext for libraries a good idea? I distinctly remember as a developer I do not actively run babel through node_modules and when I do, it is only for a few libraries not supporting old browsers

`exports` advice is potentially wrong for types

Hello, and thanks for the excellent guide.

When setting up my library, I followed this guide and copy-pasted the exports example provided here:
https://github.com/frehner/modern-guide-to-packaging-js-library?tab=readme-ov-file#define-your-exports

However, after I published my library and then used publint, it flagged my package.json file for this:
https://publint.dev/rules#export_types_invalid_format

Thus, I think that the posted example is wrong, and it should be updated to satisfy this lint rule.

Warn about the dangers of Peer Dependencies

You should also add the framework to your library's `package.json`'s [peer dependencies](#set-your-peerdependencies) to indicate to developers that you rely on that framework.

I found there were a lot of libraries that had peer dependency pinned to React 17, but the React 18 upgrade was still perfectly compatible. It caused all kinds of unnecessary warnings when attempting to install the library with React 18.

It's nuanced and might be different for every library, but a better approach might be to have the peer dependency have a minimum supported version, but support all versions after that. If a library's breaking changes will affect your library, you can pin the peer dependency to the last working version of the dependency, and then when you release an upgrade to your library that is compatible, you can remove the pin and possibly bump the minimum supported version.

From the Node.js blog:

One piece of advice: peer dependency requirements, unlike those for regular dependencies, should be lenient. You should not lock your peer dependencies down to specific patch versions. It would be really annoying if one Chai plugin peer-depended on Chai 1.4.1, while another depended on Chai 1.5.0, simply because the authors were lazy and didn't spend the time figuring out the actual minimum version of Chai they are compatible with.
The best way to determine what your peer dependency requirements should be is to actually follow semver. Assume that only changes in the host package's major version will break your plugin. Thus, if you've worked with every 1.x version of the host package, use "~1.0" or "1.x" to express this. If you depend on features introduced in 1.5.2, use ">= 1.5.2 < 2".

Link to bundler documentation

Feedback:

I do think that linking to webpack/rollup/vite/nextjs/other documentation would be helpful. Like an entry page for 'how to configure a bundler' or maybe other blog posts.

My current thinking is that if this becomes enough of a demand, then we can create additional docs for each bundler. But that's not yet a task I want to take on.

Updating docs on `type="commonjs"`

As per this new update to NodeJS:

The new flag --experimental-detect-module can be used to automatically run ES modules when their syntax can be detected. For “ambiguous” files, which are .js or extensionless files with no package.json with a type field, Node.js will parse the file to detect ES module syntax; if found, it will run the file as an ES module, otherwise it will run the file as a CommonJS module. The same applies to string input via --eval or STDIN.

We hope to make detection enabled by default in a future version of Node.js. Detection increases startup time, so we encourage everyone — especially package authors — to add a type field to package.json, even for the default "type": "commonjs". The presence of a type field, or explicit extensions such as .mjs or .cjs, will opt out of detection.

https://nodejs.org/en/blog/release/v21.1.0#automatically-detect-and-run-esm-syntax

We should add a link to that document and explain that having type="commonjs" will ensure faster parsing time when using the auto-module-detection flag.

CSS- one bundle for all styles or distributed among modules?

Discussed in #11

Originally posted by meniYud September 1, 2022
What do you do with your stylesheets? When you bundle your library code (and keep file structure), Do you create one big 'bundle.css' (the 'simpler' way in most bundlers) ? Or is it better to keep each css next to its consumer js file? (i.e. If the end user consumes only 'button' from my library, it makes no scence for them to include my entire bundle.css ...)

Version this guide?

I've been wondering if I should version this guide, so that people can be notified through GitHub when there is an update.

Thinking about it, it's not too often that you start a new library (for the most part), so I doubt people would want the notifications. They'll probably just come to this when starting a library, and then call it good after it's ready.

I'll leave this issue here though incase there is interest in this feature. 🤷

Using umd in the browser field breaks webpack 4

I have my package.json set up as advised:

{
  "main": "dist/cjs/index.js",
  "module": "dist/esm/index.js",
  "browser": "dist/umd/index.min.js",
  "types": "dist/types/index.d.ts"
}

However, Webpack 4 has the highest priority for the browser entry. This causes the build to import the umd and throws a "Critical Dependency error." Could be wrong, but I think you would want Webpack to use commonjs (or even esm) for imports. Changing the browser field to the cjs file fixes it.

Add section to explain importance and order of package.json

Many libraries that supports many bundlers and type of apps like @mui. Not use the exports field.

They use a package.json inside of they components to specify, where is the module, the main, the types, etc. (module resolution)

It would be excellent if you can add a section for this, or I can make contribution with more info about that.

PD: Thanks for sharing this guide it's excellent.

Feedback

Some feedback from my initial read-through:

Output to esm, cjs, and umd formats

It might be helpful to use an example when explaining that cjs is sometimes worth catering to separately from umd. I'd suggest using the example of conditional require() based on process.env.NODE_ENV, a common technique within the React ecosystem (because React itself uses this technique). These don't generally produce conditional dependencies when bundled from umd (conditions get ignored and dependencies included unconditionally).

Don't minify

I'm not quite sure I agree with this section as-written. Application bundlers like Webpack do a significantly poorer job of minifying library code, and can't be configured to minify a given library in the way that would be most optimal. As an example, constant inlining is extremely valuable for performance and size when compiling (minifying) a library, but constant inlining within dependencies is only currently possible in Rollup with specific configuration.

Perhaps it would be worth breaking apart the minification advice. It's generally the case that application bundling+minifying setups do a reasonably effective job of minifying whitespace and local names, so libraries can skip those two forms of minification and produce a package containing relatively readable code. However, application bundling+minifying setups are generally much less effective at optimizing across module boundaries, and cannot make assumptions about code structure that are required to minify optimally.

I've generally tried to explain this as: if you're concerned about size and perf, you can compile your code before publishing using a tool like Terser, but configure it to perform optimizations rather than whitespace and name compression.

As an illustrative example: none of the current commonly-used minifiers will compress property names in the code from an npm module. Compressing/inlining/dissolving properties of internal objects within a library is a hugely valuable optimization for both size and performance. Packages that publish code without running optimizations like this are leaving these benefits on the floor.

Target modern browsers

It might be worth including a quick explanation of legacy browser support fallback in the TS example:

As one example, if you're transpiling from TypeScript, you could create two versions of your package's code:

  1. an esm version with modern JavaScript generated by setting "target" in your tsconfig.json to ESNext
  2. a umd version with more broadly-compatible JavaScript generated by setting "target" in your tsconfig.json to ES5

Most users will get the modern code, but those using older bundler configurations or loading the code using a <script> tag will get the version with additional transpilation for older browser support.

Define your exports

  • nit: "export maps" was used super early-on, but quickly replaced with "package exports" - I think much of the documentation now refers to them as such.
  • "script" isn't used by anything, as far as I am aware of. I'm not sure why the Webpack docs mention it, webpack doesn't seem to ever use the property.
  • "module" - it might be worth mentioning that this field's purpose is an optimization for systems that transparently handle CommonJS require() of an ES Module, so that require('x') and import 'x' bundle/execute the same ES Module instead of resulting in two copies.
  • "default" - there is generally no reason to specify both "require" and "default", since there are no module systems that resolve Package Exports but lack support for both CommonJS and ESM.
  • It might be worth using more cautious language in the bit about "development" + "production". Because these aren't supported by default in Rollup or Node, relying on them for nontrivial differentiation between dev/prod bundles is uncommon and a bit of a support/onboarding burden.

Set the default module type for your JS files

It's a tiny detail, but I believe the existence of a package.json within a subdirectory currently will not override the default .js file extension association if that file is reached by resolving from the "exports" field of a package.json in a parent directory. I might be wrong on this though!

Set the browser field

Not sure if it's worth mentioning, but unpkg.com currently ignores the "browser" field. You can set an "unpkg" field to control which path unpkg will serve when a package is requested directly (unpkg.com/foo). A lot of folks include both browser and unpkg, both pointing to a UMD bundle.

Looking for some help

Hey!

I'm just reading this article, and got a little confused by the part in brackets of this sentence:
Instead, we want to focus on things that apply to every library and bundler (or lack of bundler).

So, does the "lack of bundler" means this kind of scenario, that we want to package a library, but using no bundler?

Thanks!

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.