package-json-exports
TypeScript icon, indicating that this package has built-in type declarations

0.0.2 • Public • Published

package-json-exports

A file based package.json exports generator depending on fast-glob.

Installation

pnpm i -D package-json-exports

Concepts

As a library maintainer, maintaining exports in the package.json file can be challenging when there are many files in the package, especially if we choose to provide individual files for certain reasons, instead of bundling them into a single file. Keeping the exports up-to-date manually when the files change is error-prone and time-consuming. This library solves this issue by generating exports automatically based on the files in the package. It can be integrated into the build process and used with rules to generate exports with ease.

Usage

import { generateExports } from "package-json-exports";

const packageJson = JSON.parse(await fs.readFile("package.json", "utf-8"));
packageJson.main = "./index.js";
packageJson.module = "./esm/index.js";
packageJson.types = "./index.d.ts";
packageJson.exports = await generateExports(options);

await fs.writeFile("package.json", JSON.stringify(packageJson, null, 2));

Recipes

The exports in this library's package.json follows DAISHI KATO's How Jotai Specifies Package Entry Points, and here is the generating script: scripts/generatePackageJson.ts.

Options

rules

  • Type: Rule[]

The rules to generate the exports.

Each rule is applied in order, and later rules override previous ones.

Each rule only executes fast-glob once. To improve performance, try to use fewer rules.

rules.pattern

  • Type: string

The matching pattern for fast-glob.

rules.exports

  • Type: (matchedFilePath: string) => condition[]

You can return an array of conditions. A condition is an array of the property path from exports. For example, if you want to generate:

{
  "exports": {
    "foo": {
      "bar": "./src/index.js"
    }
  }
}

the condition should be ["foo", "bar"].

You can return several conditions:

{
  exports: (matchedFilePath) => {
    return [
      ["foo", "bar"],
      ["foo", "baz"],
    ];
  },
}

The result will be:

{
  "exports": {
    "foo": {
      "bar": "./src/index.js",
      "baz": "./src/index.js"
    }
  }
}

You can also return [] to ignore the file.

Notice that the matchedFilePath is always starts with ./.

rules.fastGlobOptions

  • Type: FastGlob.Options

This option is passed to fast-glob when matching the files. It's useful when you want to ignore some files.

defaultConditionKey

  • Type: string
  • Default: undefined

The default condition key to use when conditions conflict.

For example if there are files:

src/index.js

And the rules are:

[
  {
    pattern: "**/*.js",
    exports: (matchedFilePath) => {
      return [[generateExports.pathCondition(matchedFilePath), "require"]];
    },
  },
  {
    pattern: "**/*",
    exports: (matchedFilePath) => {
      return [[generateExports.pathCondition(matchedFilePath)]];
    },
  },
];

The result will be:

{
  "exports": {
    "./src/index.js": {
      "require": "./src/index.js",
      "default": "./src/index.js"
    }
  }
}

Without setting defaultConditionKey, it throws an error when conditions conflict.

fastGlobOptions

  • Type: FastGlob.Options

You can set the cwd and ignore options here. dot option is set to true by default. You can override it by setting it to false.

simplify

  • Type: boolean
  • Default: false

Simplify the exports. It will replace the object with only one property with the value of the property recursively.

For example, if the exports was:

{
  "exports": {
    "./src/index.js": {
      "browser": {
        "import": "./src/index.js"
      }
    },
    "./src/foo.js": {
      "require": "./src/foo.js",
      "default": "./src/foo.js"
    }
  }
}

It will be simplified to:

{
  "exports": {
    "./src/index.js": "./src/index.js",
    "./src/foo.js": {
      "require": "./src/foo.js",
      "default": "./src/foo.js"
    }
  }
}

If the exports was:

{
  "exports": {
    "node": {
      "production": {
        "import": "./dist/index.js"
      }
    }
  }
}

It will be simplified to:

{
  "exports": "./dist/index.js"
}

Utilities

generateExports.normalizePath

  • Type: (path: string) => string

This function can be used to normalize the path with leading ./.

For example:

generateExports.pathCondition("foo"); // ➡️ "./foo"
generateExports.pathCondition("./foo"); // ➡️ "./foo"
generateExports.pathCondition("./foo.js"); // ➡️ "./foo.js"

generateExports.removeExtension

  • Type: (path: string, extname?: string) => string

This function can be used to remove the extension name.

For example:

generateExports.pathCondition("foo"); // ➡️ "foo"
generateExports.pathCondition("./foo"); // ➡️ "./foo"
generateExports.pathCondition("./foo.js"); // ➡️ "./foo"
generateExports.pathCondition("./foo/bar.js"); // ➡️ "./foo/bar"

generateExports.pathCondition

  • Type: (path: string, extname?: string) => string

pathCondition is a combination of normalizePath and removeExtension. This function can be used to generate either the path condition. It will remove the extension name and add ./ if the path doesn't start with ./.

For example:

generateExports.pathCondition("foo"); // ➡️ "./foo"
generateExports.pathCondition("./foo"); // ➡️ "./foo"
generateExports.pathCondition("./foo.js"); // ➡️ "./foo"
generateExports.pathCondition("./foo.js", ".js"); // ➡️ "./foo"
generateExports.pathCondition("./foo.d.ts"); // ➡️ "./foo.d"
generateExports.pathCondition("./foo.d.ts", ".d.ts"); // ➡️ "./foo"

To Learn More

License

ViPro ©️ MIT

Package Sidebar

Install

npm i package-json-exports

Weekly Downloads

1

Version

0.0.2

License

MIT

Unpacked Size

41.3 kB

Total Files

38

Last publish

Collaborators

  • vdustr