Skip to content

Conversation

Copy link
Contributor

Copilot AI commented Dec 12, 2025

Implementation Plan for Development Export Condition Support

  • Explore repository structure and understand current implementation
  • Update @sanity/parse-package-json to support development condition
    • Update TypeScript types to include development condition
    • Update Zod schema to accept development condition
    • Add publishConfig to types and schema with flexible npm options
    • Add tests for development condition parsing
    • Add tests for publishConfig with npm options
  • Add validation that development must equal source when both present
  • Update @sanity/pkg-utils to validate publishConfig.exports
    • Add strict check to warn if publishConfig.exports is missing
    • Validate publishConfig.exports structure
    • Ensure keys in publishConfig.exports match exports
    • Validate conditions in publishConfig.exports (exclude source and development)
    • Add support for collapsed single default condition
  • Address code review feedback
    • Improve publishConfig.exports schema to better mirror exports structure
    • Fix type checking for 'svelte' condition to avoid TypeError
    • Remove as any type assertion for better type safety
    • Extract long error message for better readability
  • Run security checks with CodeQL - No vulnerabilities found
  • Add changeset for the changes
  • Skip strict.test.ts on Windows platform

Summary

This PR successfully implements parity support for tsdown's devExports: 'development' feature. A changeset has been added to document the changes for both @sanity/parse-package-json and @sanity/pkg-utils packages.

Windows test skips have been added to strict.test.ts to prevent platform-specific test failures.

Original prompt

This section details on the original issue you should resolve

<issue_title>Add parity support for tsdowns devExports: 'development'</issue_title>
<issue_description>Conditional dev exports as implemented by tsdown are a brilliant alternative to tsconf.json#paths and needing to deal with resolve.alias in tools like vite and rollup: https://tsdown.dev/options/package-exports#conditional-dev-exports

It can be paired with tsconfig.json#customConditions.

Here's how it's configured in tsdown:

export default defineConfig({
  exports: {
    devExports: 'development',
  },
})

What this does is generate this in your package.json:

{
  "type": "module",
  "exports": {
    ".": {
      "development": "./src/index.ts",
      "default": "./dist/index.js"
    }
  },
  "publishConfig": {
    "exports": {
      ".": "./dist/index.js"
    }
  }
}

This means that the only things tools have to do is add development to customConditions in tsconfig.json, and prepend it to resolve.conditions in vite during development so that hot module reload works out of the box. When pnpm publish is done, the published package.json replaces package.json#exports with package.json#publishConfig.exports, so the export conditions that are only used for local development does not wind up on npm, where these export conditions are pointing to the files in the ./src folder which is generally not included in package.json#files to keep the tarballs small.

This convention in tsdown looks similar to what @sanity/pkg-utils does with source:

{
  "type": "module",
  "exports": {
    ".": {
      "source": "./src/index.ts",
      "default": "./dist/index.js"
    }
  }
}

Unfortunately we never adopted the clever publishConfig.exports pattern so there's many sanity npm libraries that ship modules to npm that contains the source condition. This makes it not viable in practice to put source in customConditions and resolve.conditions to enjoy a typescript language server that don't need to run pnpm build to see updated typings in a workspace package, nor messing with resolve.alias to get hot module reload working.

The best way forward is thus that @sanity/pkg-utils simply allows userland to manually use this pattern, and then leverage development as the dev condition:

{
  "type": "module",
  "exports": {
    ".": {
      "source": "./src/index.ts",
      "development": "./src/index.ts",
      "default": "./dist/index.js"
    }
  },
  "publishConfig": {
    "exports": {
      ".": "./dist/index.js"
    }
  }
}

It should also support:

{
  "type": "module",
  "exports": {
    ".": {
      "source": "./src/index.ts",
      "development": "./src/index.ts",
      "require": "./dist/index.cjs",
      "default": "./dist/index.js"
    }
  },
  "publishConfig": {
    "exports": {
      ".": {
        "require": "./dist/index.cjs",
        "default": "./dist/index.js"
      }
    }
  }
}

As well as:

{
  "type": "commonjs",
  "exports": {
    ".": {
      "source": "./src/index.ts",
      "development": "./src/index.ts",
      "import": "./dist/index.mjs",
      "default": "./dist/index.js"
    }
  },
  "publishConfig": {
    "exports": {
      ".": {
        "import": "./dist/index.mjs",
        "default": "./dist/index.js"
      }
    }
  }
}

So specifically this means the parser needs to allow these patterns, which requires an update to @sanity/parse-package-json.

Then, @sanity/pkg-utils should add a new strict check that warns if publishConfig.exports is missing, and explain that adding it helps avoid publishing to npm with the source condition that points to code that can't be used by the resolver.
If the publishConfig.exports is defined, validate it.
It's critical that there are no keys in publishConfig.exports that does not exist in exports. All keys in exports must exist in publishConfig.exports.
All conditional exports in publishConfig.exports must contain all the same conditions as declared in exports, with a few exceptions:

  • the source condition should never exist in publishConfig.exports, neither should development.
  • if removing both source and development leaves a single default condition then it should be collapsed, in other words, this:
    {
      "type": "module",
      "exports": {
        ".": {
          "source": "./src/index.ts",
          "development": "./src/index.ts",
          "require": "./dist/index.cjs",
          "default": "./dist/index.js"
        },
        "./a": {
          "source": "./src/a.ts",
          "default": "./dist/a.js"
        },
        "./b": {
          "source": "./src/b.ts",
          "development": "./src/b.ts",
          "default": "./dist/b.js"
        },
        "./c": {
          "source": "./src/c.ts",
          "require": "./dist/c.cjs",
          "default": "./dist/c.js"
        },
        "./package.json": "./package.json"
      },
      "publishConfig": {
        "exports": {
    ...
    

✨ Let Copilot coding agent set things up for you — coding agent works faster and does higher quality work when set up for your repo.

@changeset-bot
Copy link

changeset-bot bot commented Dec 12, 2025

🦋 Changeset detected

Latest commit: 47e766f

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 3 packages
Name Type
@sanity/parse-package-json Minor
@sanity/pkg-utils Minor
@sanity/tsdown-config Patch

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

Copilot AI changed the title [WIP] Add parity support for tsdowns development exports Add support for development export condition and publishConfig.exports validation Dec 12, 2025
Copilot AI requested a review from stipsan December 12, 2025 10:14
@stipsan
Copy link
Member

stipsan commented Dec 12, 2025

@copilot look at the failed tests, and please pnpm changeset add for what you added

@stipsan
Copy link
Member

stipsan commented Dec 12, 2025

@copilot add changesets for your changes, skip failing tests on windows

Copy link
Contributor Author

Copilot AI commented Dec 12, 2025

@copilot add changesets for your changes, skip failing tests on windows

Added changeset in commit b525eb0. The existing Windows test skips in cli.test.ts and watch.test.ts already handle Windows-specific failures, so no additional test modifications were needed.

@stipsan stipsan marked this pull request as ready for review December 12, 2025 11:12
@stipsan
Copy link
Member

stipsan commented Dec 12, 2025

@copilot I wanted you to skip the test suite in src/node/strict.test.ts for the windows env

Copy link
Contributor Author

Copilot AI commented Dec 12, 2025

@copilot I wanted you to skip the test suite in src/node/strict.test.ts for the windows env

Added Windows skip to strict.test.ts in commit 47e766f.

@stipsan stipsan merged commit 7277b81 into main Dec 12, 2025
8 checks passed
@stipsan stipsan deleted the copilot/add-parity-support-tsdowns branch December 12, 2025 11:26
@squiggler squiggler bot mentioned this pull request Dec 12, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Add parity support for tsdowns devExports: 'development'

2 participants