Skip to content

Instantly share code, notes, and snippets.

@mattpocock
Created June 10, 2024 19:51
Show Gist options
  • Save mattpocock/5f98fddad53f16447a03ff765deb1bd3 to your computer and use it in GitHub Desktop.
Save mattpocock/5f98fddad53f16447a03ff765deb1bd3 to your computer and use it in GitHub Desktop.

Imagine a package.json that looks like this:

{
  "name": "my-package",
  "type": "module",
  "exports": {
    ".": {
      "source": "./src/index.ts",
      "import": "./dist/index.js",
      "require": "./dist/index.cjs"
    }
  }
}

We're using exports to define a single entrypoint, my-package. my-package can be accessed in one of three ways:

  • Via a ESM import -> ./dist/index.js
  • Via require -> ./dist/index.cjs
  • Some other MYSTERIOUS WAY -> ./src/index.ts

In this way, you can see that we're letting CJS users and ESM users access built versions of the package. But what the hell is that source thing?

Well, for those who want to, you can directly access the source file that created both the cjs and esm file. This is via a custom condition.

Custom Conditions

You can use custom conditions in exports in package.json. You can call them anything you like. By convention, we're calling this one source - and it points to the source file.

This custom condition won't be used by the app/package consuming my-package unless we opt in. We do that in the tsconfig.json:

{
  "compilerOptions": {
    "customConditions": ["source"]
  }
}

And, perhaps, in our vite.config.ts:

export default {
  resolve: {
    conditions: ['source']
  }
}

Now, both the types and the Vite app will target the source, not the transpiled output. This is useful because any changes in my-package will automatically propagate through any apps.

But, it's also opt-in - so you can publish this package as-is without any issues.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment