Giter Club home page Giter Club logo

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

The Modern Guide to Packaging your JavaScript library

English | 简体中文

Context about this guide

This guide is written to provide at-a-glance suggestions that most libraries should follow, while also giving additional information if you want to understand why a suggestion is made or if you feel like you need to deviate from these suggestions. This guide is meant for libraries and not for applications (apps).

To emphasize, this is a list of suggestions and is not meant to be definitive list that all libraries must follow, or to provide flame bait for libraries that do not follow these suggestions. Each library is unique and there are probably good reasons a library may chose to not implement any given suggestion.

Finally, this guide is not meant to be specific to any particular bundler - there already exist many guides on how to set configs in specific bundlers. Instead, we want to focus on things that apply to every library and bundler (or lack of bundler).


Tooling

Use tools to validate important settings
  • publint.dev validates important settings with your package.json and suggests improvements if it finds them
  • arethetypeswrong validates that your TypeScript types are output and configured correctly

Output to esm, cjs, and umd formats

Supporting the whole ecosystem

esm is short for "EcmaScript module."

cjs is short for "CommonJS module."

umd is short for "Universal Module Definition," and can be run by a raw <script> tag, or in CommonJS module loaders, or by AMD module loaders.

Without getting into the flame wars that generally happen around esm and cjs formats, esm is considered "the future" but cjs still has a strong hold on the community and ecosystem. esm is easier for bundlers to correctly tree shake, so it is especially important for libraries to have this format. It's also possible that some day in the future your library only needs to output to esm.

You may have noticed that umd is already compatible with CommonJS module loaders - so why would you want to have both cjs and umd output? One reason is that CommonJS files generally perform better when conditionally depended on compared to umd files; for example:

if (process.env.NODE_ENV === "production") {
  module.exports = require("my-lib.production.js");
} else {
  module.exports = require("my-lib.development.js");
}

The above example, when used with CommonJS modules, will only end up with either the production or development bundle. However, with a UMD module, it may be the case that a developer would end up with both bundles. Refer to this discussion for more information.

Finally, if your library is stateful, be aware that this does open the possibility of your library running into the dual package hazard, which can occur in situations where a developer ends up with both a cjs and esm version of your library in their application. The "dual package hazard" article linked above describes some ways to mitigate this issue, and the module condition in package.json#exports can also help prevent this from happening.

Output to multiple files

Better tree shaking by maintaining the file structure

If you use a bundler or transpilier in your library, it can be configured to output files in the same way that they were authored. This makes it easier to mark specific files as having side effects, which helps the developer's bundler with tree shaking. Refer to this article for more details.

An exception is if you are making a bundle meant to be used directly in the browser without any bundler (commonly, these are umd bundles but could also be modern esm bundles as well). In this case, it is better to have the browser request a single large file than need to request multiple smaller ones. Additionally, you should minify the bundle and create sourcemaps for it.

To Minify or Not to Minify

Determine your preferred level of minification

There are certain levels of minification you can apply to your library, and depending on how aggressive you want to be will determine how small your code will be once it's finally through a developer's bundler.

For example, most bundlers are already configured to remove whitespace and other easy optimizations, even from an NPM module (in this case, your library). According to Terser - a popular JavaScript mangler/compressor - that type of compression can reduce your bundle's final size by up to 95%. In some cases, you may be happy with those savings with no effort on your part.

However, there are additional savings that can occur if you were to run a minifier on your library before publishing, but doing so requires deeply understanding the settings and side-effects of your minifer. These type of compressions are generally not run by minifiers on NPM modules, and therefore you will miss out on those savings unless you do it yourself. Refer to this issue for additional information.

Finally, if you are creating a bundle intended to be used directly in the browser without a bundler (commonly, these are umd bundles but could also be modern esm bundles as well), you should always minify your bundle, create sourcemaps for it, and output to a single file.

Create sourcemaps

When using a bundler or transpiler, generate sourcemaps

Any sort of transformation of your source code to a bundle will produce errors that point at the wrong location in your code. Help your future self out and create sourcemaps, even if your transformations are small.

Create TypeScript types

Types improve the developer experience

As the number of developers using TypeScript continues to grow, having types built-in to your library will help improve the developer experience (DX). Additionally, devs who are not using TypeScript also get a better DX when they use an editor that understands types (such as VSCode, which uses the types to power its Intellisense feature).

However, creating types does NOT mean you must author your library in TypeScript.

One option is to continue using JavaScript in your source code and then also supplement it with JSDoc comments. You would then configure TypeScript to only emit the declaration files from your JavaScript source code.

Another option is to write the TypeScript type files directly, in an index.d.ts file.

Once you have the types file, make sure you set your package.json#exports and package.json#types fields.

Externalize frameworks

Don't include a copy of React, Vue, etc. in your bundle

When building a library that relies on a framework (such as React, Vue, etc.) or is a plugin for another library, you'll want to add that framework to your bundler's "externals" (or equivalent) configuration. This will make it so that your library will reference the framework but will not include it in the bundle. This will prevent bugs and also reduce the size of your library's package.

You should also add that framework to your library's package.json's peer dependencies, which will help developers discover that you rely on that framework.

Target modern browsers

Use modern features and let devs support older browsers if needed

This article on web.dev makes a great case for your library to target modern features, and offers guidelines on how to:

  • Enable developers to support older browsers when using your library
  • Output multiple bundles that support various levels of browser support

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"="esnext" in your tsconfig.json
  2. A umd version with more broadly-compatible JavaScript generated by setting "target"="es5" in your tsconfig.json

With these settings, 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.

Transpile if necessary

Turn TypeScript or JSX into function calls If your library's source code in in a format that requires transpilation, such as TypeScript, React or Vue components, etc., then your output should be transpiled. For example:
  • Your TypeScript library should output JavaScript bundles
  • Your React components like <Example /> should output bundles that use jsx() or createElement() instead of JSX syntax.

When transpiling this way, make sure you create sourcemaps as well.

Keep a changelog

Track updates and changes

It doesn't matter whether it's through automatic tooling or through manual process, as long as developers have a way to see what has changed and how it affects them. Ideally, every change to your library's version has a corresponding update in your changelog.

Split out your CSS files

Enable devs to only include the CSS they need

If you are creating a CSS library (like Bootstrap, Tailwind, etc.), it may be easier to provide a single CSS bundle that includes all the functionality that your library provides. However, even in that situation, your CSS bundle may end up becoming large enough that it affects the performance of the devs' sites. To help prevent that, libraries generally provide methods of generating a CSS bundle that only includes the necessary CSS for what the developer is using (for example, see how Bootstrap and Tailwind do it).

If CSS is only a part of what your library exposes (for example, a component library that has default styles), then it is ideal if you separate out your CSS into individual bundles per component that are imported when the corresponding component is used. One example of this is react-component.

package.json settings

There are a lot of important settings and fields to talk about in package.json; I will highlight the most important ones here, but be aware that there are additional fields that you can set as well.

Set the name field

Give a name to your library

The name field will determine the name of your package on npm, and therefore the name that developers will use to install your library.

Note that there are restrictions on what you can name your library, and additionally you can add a "scope" if your library is part of an organization. Refer to the name docs on npm for more details.

The name and the version fields combine to create a unique identifier for each iteration of your library.

Set the version field

Publish updates to your library by changing the version

As noted in the name section, the name and the version combine to create a unique identifier for your library on npm. When you make updates to the code in your library, you can then update the version field and publish to allow developers to get that new code.

Libraries are encouraged to use a versioning strategy called semver, but note that some libraries choose to calver or their own unique versioning strategy. Whichever strategy you choose to use, you should document it so that developers understand how your library's versioning works.

You should also keep track of your changes in a changelog.

Define your exports

exports define the public API for your library

The exports field on package.json - sometimes called "package exports" - is an incredibly useful addition, though it does add some complexity. The two most important things that it does is:

  1. Defines what can and cannot be imported from your library, and what the name of it is. If it's not listed in exports, then developers cannot import/require it. In other words, it acts like a public API for users of your library and helps define what is public and what is internal.
  2. Allows you to change which file is imported based on conditions (that you can define), such as "Was the file imported or required? Does the developer want a development or production version of my library?" etc.

There are some good docs from the NodeJS team and the Webpack team on the possibilities here. I'll provide one example that covers the most common use-cases:

{
  "exports": {
    ".": {
      "module": "./dist/index.mjs",
      "import": {
        "types": "./dist/index.d.mts",
        "default": "./dist/index.mjs"
      },
      "require": {
        "types": "./dist/index.d.cts",
        "default": "./dist/index.cjs"
      },
      "default": "./dist/index.mjs"
    },
    "./package.json": "./package.json"
  },
  "types": "./dist/index.d.ts"
}

Let us dive into the meaning of these fields and why I chose this specific shape:

  • "." indicates the default entry for your package
  • The resolution happens from top to bottom and stops as soon as a matching field is found; the order of entries is very important
  • The module field is an "unofficial" field that is supported by bundlers like Webpack and Rollup. It should come before import and require, and point to an esm-only bundle -- which can be the same as your original esm bundle if it's purely esm. As noted in the formats section, it is meant to help bundlers only include one copy of your library, no matter if it was imported or requireed. For a deeper dive and the reasoning behind this decision, you can read more here, here, and here.
  • The import field is for when someone imports your library.
  • The require field is for when someone requires your library.
  • The default field is used as a fallback for if none of the conditions match. While it may not be used at the moment, it's good to have it for "unknown future situations".

If a bundler or environment understands the exports field, then the package.json's top-level main, types, module, and browser fields are ignored, as exports supersedes those fields. However, it's still important to set those fields, for tools or runtimes that do not yet understand the exports field.

If you have a "development" and a "production" bundle (for example, you have warnings in the development bundle that don't exist in the production bundle), then you can also set them in the exports field with "development" and "production". Note that some bundlers like webpack and vite will recognize these conditions automatically; however, while Rollup can be configured to recognize them, that is something that you would have to instruct developers to do in their own bundler config.

(Note that while the "types" field is covered in a different section, it is included in the above snippet for people who are copy-pasting the example. Even though we have two separate "types" fields inside of the "export" field, the "types" field is also required at the root of the object for full backwards compatibility, as tools like arethetypeswrong will fail your package otherwise.)

List the files to be published

files defines which files are included in your NPM package

The files field indicates to the npm CLI which files and folders to include when you package your library to be put on NPM's package registry.

For example, if you transform your code from TypeScript into JavaScript, you probably don't want to include the TypeScript source code in your NPM package. (Instead, you should include sourcemaps)

Files can take an array of strings (and those strings can include glob-like syntax if needed), so generally it will look like:

{
  "files": ["dist"]
}

Be aware that the files array doesn't accept a relative specifier; writing "files": ["./dist"] will not work as expected.

One great way to verify you have set the files field up correctly is by running npm publish --dry-run, which will list off the files that will be included based on this setting.

Set the default module type for your JS files

type dictates which module system your .js files use

Runtimes and bundlers need a way to determine what type of module system your .js files are using - ESM or CommonJS. Because CommonJS came first, that is the what bundlers will assume by default, but you can control it by adding "type" your package.json.

Your options are either "type":"module" or "type":"commonjs", and though you can leave it blank (to default to CommonJS) it's highly recommended that you set it to one or the other to explicity declare which one you're using.

Note that you can have a mix of module types in the project, through a couple of tricks:

  • .mjs files will always be ESM modules, even if your package.json has "type": "commonjs" (or nothing for type)
  • .cjs files will always be CommonJS modules, even if your package.json has "type": "module"
  • You can add additional package.json files that are nested inside of folders; runtimes and bundlers look for the nearest package.json and will traverse the folder path upwards until they find it. This means you could have two different folders, both using .js files, but each with their own package.json set to a different type to get both a CommonJS- and ESM-based folder.

Refer to the excellent NodeJS documentation here and here for more information.

List which modules have sideEffects

Setting the sideEffects field enables tree shaking

Much a like creating a pure function can bring benefits, creating a "pure module" enables certain benefits as well; bundlers can do a much better job of tree shaking your library.

The way to communicate to bundlers which of your modules are "pure" or not is by setting the sideEffects field in package.json - without this field, bundlers have to assume that all of your modules are impure.

sideEffects can either be set to false to indicate that none of your modules have side effects, or an array of strings to list which files have side effects. For example:

{
  // all modules are "pure"
  "sideEffects": false
}

or

{
  // all modules are "pure" except "module.js"
  "sideEffects": ["module.js"]
}

So, what make a module "impure?" Some examples are modifying a global variable, sending an API request, or importing CSS, without the developer doing anything to invoke that action. For example:

// a module with side effects

export const myVar = "hello";

window.example = "testing";

By importing myVar, your module sets window.example automatically! For example:

import { myVar } from "library";

console.log(window.example);
// logs "testing"

In some cases, like polyfills, that behavior is intentional. However, if we wanted to make this module "pure", we could move the assignment to window.example into a function. For example:

// a "pure module"

export const myVar = "hello";

export function setExample() {
  window.example = "testing";
}

This is now a "pure module." Also note the difference in how things look on the developer's side of things:

import { myVar, setExample } from "library";

console.log(window.example);
// logs "undefined"

setExample();

console.log(window.example);
// logs "testing"

Refer to this article for more details.

Set the main field

main defines the CommonJS entry

main is a fallback for bundlers or runtimes that don't yet understand package.json#exports; if a bundler/environment does understand package exports, then main is not used.

main should point to a CommonJS-compatible bundle; it should probably match the same file as your package export's require field.

Set the module field

module defines the ESM entry

module is a fallback for bundlers or runtimes that don't yet understand package.json#exports; if a bundler/environment does understand package exports, then module is not used.

module should point to a ESM-compatible bundle; it should probably match the same file as your package export's module and/or import field.

Set additional fields for CDNs

Support CDNs like unpkg and jsdelivr

To enable your library to "work by default" on CDNs like unpkg and jsdelivr, you can set their specific fields to point to your umd bundle. For example:

{
  "unpkg": "./dist/index.umd.js",
  "jsdelivr": "./dist/index.umd.js"
}

Consider setting the browser field

browser points to a bundle that works in the browser

browser is a fallback for bundlers or runtimes that don't yet understand package.json#exports; if a bundler/environment does understand package exports, then browser is not used.

browser should point to an esm bundle that works in the browser. However, you'll only need to set this field if you are creating different bundles for browsers and servers (and/or other non-browser environments). If you're not creating multiple bundles for multiple environments, or if your bundles are "pure JavaScript" / "universal" and can be run in any JavaScript environment, then you don't need to to set the browser field.

If you do need to set this field, here's an excellent guide on the different ways you can configure it.

Note that the browser field shouldn't point to a umd bundle, as that would make it so that your library isn't tree shaked by bundlers (like Webpack) that prioritize this field over the others such as module and main.

Set the types field

types defines the TypeScript types

types is a fallback for bundlers or runtimes that don't yet understand package.json#exports; if a bundler/environment does understand package exports, then types is not used.

types should point to your TypeScript entry file, such as index.d.ts; it should probably match the same file as your package export's types field.

List your peerDependencies

If you rely on another framework or library, set it as a peer dependency

You should externalize any frameworks you rely on. However, in doing so, your library will only work if the developer installs the framework you need on their own. One way to help them know that they need to install the framework is by setting peerDependencies - for example, if you were building a React library, it would potentially look like this:

{
  "peerDependencies": {
    "react": "^18.2.0",
    "react-dom": "^18.2.0"
  }
}

Refer to this article for more details.

You should also document your reliance on these dependencies; for example, npm v3-v6 does not install peer dependencies, while npm v7+ will automatically install peer dependencies.

State which license your library falls under

Protect yourself and other contributors

An open source license protects contributors and users. Businesses and savvy developers won’t touch a project without this protection.

That quote comes from Choose a License, which is also a great resource for deciding which license is right for your project.

Once you have decided on a license, the npm Docs for the license describe the format that the license field takes. An example:

{
  "license": "MIT"
}

Additionally, you can create a LICENSE.txt file in the root of your project and copy the license text there.


Special Thanks

A big thank you to the people who took the time out of their busy schedules to review and suggest improvements to the first draft of this document (ordered by last name):

  • Joel Denning @joeldenning
  • Fran Dios @frandiox
  • Kent C. Dodds @kentcdodds
  • Carlos Filoteo @filoxo
  • Jason Miller @developit
  • Konnor Rogers @paramagicdev
  • Matt Seccafien @cartogram
  • Nate Silva @natessilva
  • Cong-Cong Pan @SyMind

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  avatar

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

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.

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

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.

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.

`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.

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!

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.

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. 🤷

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".

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 ...)

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.