diff --git a/packages/@clc/dependencies/package.json b/packages/@clc/dependencies/package.json index b6f4eda0..5d800a99 100644 --- a/packages/@clc/dependencies/package.json +++ b/packages/@clc/dependencies/package.json @@ -21,7 +21,7 @@ "default": "./dist/esm/index.mjs" }, "require": { - "types": "./dist/cjs-types/index.d.ts", + "types": "./dist/cjs-types/index.d.cts", "default": "./dist/cjs/index.cjs" } }, diff --git a/packages/@clc/nx/executors/package-json/executor.mjs b/packages/@clc/nx/executors/package-json/executor.mjs index 015f8e9b..90dcb5c3 100644 --- a/packages/@clc/nx/executors/package-json/executor.mjs +++ b/packages/@clc/nx/executors/package-json/executor.mjs @@ -82,10 +82,9 @@ async function config(pkg, mjs, mts, type, context) { default: mjs ? './src/index.mjs' : './dist/esm/index.mjs', }, require: { - types: - mjs || mts - ? './dist/cjs-types/index.d.ts' - : './dist/types/index.d.ts', + // A `.d.ts` under `"type": "module"` is read as ESM, so the CommonJS + // condition needs the `.d.cts` that `scripts/dts-to-dcts.mjs` emits. + types: './dist/cjs-types/index.d.cts', default: './dist/cjs/index.cjs', }, /* eslint-enable sort-keys */ diff --git a/packages/@clc/nx/src/create-nodes.mjs b/packages/@clc/nx/src/create-nodes.mjs index 90e90028..31699fd0 100644 --- a/packages/@clc/nx/src/create-nodes.mjs +++ b/packages/@clc/nx/src/create-nodes.mjs @@ -182,16 +182,13 @@ export const createNodes = [ '{workspaceRoot}/tsconfig.base.json', '{workspaceRoot}/tsconfig.references.json', '{workspaceRoot}/tsconfig.json', + '{workspaceRoot}/scripts/dts-to-dcts.mjs', '{projectRoot}/tsconfig.json', '{projectRoot}/package.json', - '{projectRoot}/src/**/*.[jt]s?(x)', + '{projectRoot}/src/**/*.?(m|c)[jt]s?(x)', ], options: { - command: mjs - ? `tsc --project {projectRoot}/tsconfig.json && scripts/dmts-to-dts {projectRoot}` - : mts - ? `tsc --project {projectRoot}/tsconfig.json && scripts/dmts-to-dts {projectRoot}` - : `tsc --project {projectRoot}/tsconfig.json`, + command: `tsc --project {projectRoot}/tsconfig.json && node scripts/dts-to-dcts.mjs {projectRoot}`, }, outputs: [ '{projectRoot}/dist/.tsconfig.tsbuildinfo', diff --git a/packages/@clc/test-helpers/package.json b/packages/@clc/test-helpers/package.json index 9872a4eb..33c1e891 100644 --- a/packages/@clc/test-helpers/package.json +++ b/packages/@clc/test-helpers/package.json @@ -18,7 +18,7 @@ "default": "./dist/esm/index.mjs" }, "require": { - "types": "./dist/cjs-types/index.d.ts", + "types": "./dist/cjs-types/index.d.cts", "default": "./dist/cjs/index.cjs" } }, diff --git a/packages/@code-like-a-carpenter/assert/package.json b/packages/@code-like-a-carpenter/assert/package.json index d54c6e2f..144d953c 100644 --- a/packages/@code-like-a-carpenter/assert/package.json +++ b/packages/@code-like-a-carpenter/assert/package.json @@ -13,7 +13,7 @@ "default": "./src/index.mjs" }, "require": { - "types": "./dist/cjs-types/index.d.ts", + "types": "./dist/cjs-types/index.d.cts", "default": "./dist/cjs/index.cjs" } }, diff --git a/packages/@code-like-a-carpenter/aws-env-loader/package.json b/packages/@code-like-a-carpenter/aws-env-loader/package.json index 4b035dc6..a9a670c6 100644 --- a/packages/@code-like-a-carpenter/aws-env-loader/package.json +++ b/packages/@code-like-a-carpenter/aws-env-loader/package.json @@ -8,7 +8,7 @@ "default": "./dist/esm/index.mjs" }, "require": { - "types": "./dist/types/index.d.ts", + "types": "./dist/cjs-types/index.d.cts", "default": "./dist/cjs/index.cjs" } }, diff --git a/packages/@code-like-a-carpenter/cli-core/package.json b/packages/@code-like-a-carpenter/cli-core/package.json index cf8d51b3..b294a4d5 100644 --- a/packages/@code-like-a-carpenter/cli-core/package.json +++ b/packages/@code-like-a-carpenter/cli-core/package.json @@ -18,7 +18,7 @@ "default": "./src/index.mjs" }, "require": { - "types": "./dist/cjs-types/index.d.ts", + "types": "./dist/cjs-types/index.d.cts", "default": "./dist/cjs/index.cjs" } }, diff --git a/packages/@code-like-a-carpenter/cli-plugin-example/package.json b/packages/@code-like-a-carpenter/cli-plugin-example/package.json index 7bc3fc7a..f7a4e90a 100644 --- a/packages/@code-like-a-carpenter/cli-plugin-example/package.json +++ b/packages/@code-like-a-carpenter/cli-plugin-example/package.json @@ -17,7 +17,7 @@ "default": "./dist/esm/index.mjs" }, "require": { - "types": "./dist/types/index.d.ts", + "types": "./dist/cjs-types/index.d.cts", "default": "./dist/cjs/index.cjs" } }, diff --git a/packages/@code-like-a-carpenter/cli/package.json b/packages/@code-like-a-carpenter/cli/package.json index fb4ee133..a8843dac 100644 --- a/packages/@code-like-a-carpenter/cli/package.json +++ b/packages/@code-like-a-carpenter/cli/package.json @@ -42,7 +42,7 @@ "default": "./dist/esm/index.mjs" }, "require": { - "types": "./dist/types/index.d.ts", + "types": "./dist/cjs-types/index.d.cts", "default": "./dist/cjs/index.cjs" } }, diff --git a/packages/@code-like-a-carpenter/contract-tests/package.json b/packages/@code-like-a-carpenter/contract-tests/package.json index b75d142e..ed0c15ec 100644 --- a/packages/@code-like-a-carpenter/contract-tests/package.json +++ b/packages/@code-like-a-carpenter/contract-tests/package.json @@ -19,7 +19,7 @@ "default": "./dist/esm/index.mjs" }, "require": { - "types": "./dist/types/index.d.ts", + "types": "./dist/cjs-types/index.d.cts", "default": "./dist/cjs/index.cjs" } }, diff --git a/packages/@code-like-a-carpenter/dotenv/package.json b/packages/@code-like-a-carpenter/dotenv/package.json index f82ea354..f9e810aa 100644 --- a/packages/@code-like-a-carpenter/dotenv/package.json +++ b/packages/@code-like-a-carpenter/dotenv/package.json @@ -13,7 +13,7 @@ "default": "./dist/esm/index.mjs" }, "require": { - "types": "./dist/types/index.d.ts", + "types": "./dist/cjs-types/index.d.cts", "default": "./dist/cjs/index.cjs" } }, diff --git a/packages/@code-like-a-carpenter/env/package.json b/packages/@code-like-a-carpenter/env/package.json index 6418b7ae..d17c80e8 100644 --- a/packages/@code-like-a-carpenter/env/package.json +++ b/packages/@code-like-a-carpenter/env/package.json @@ -8,7 +8,7 @@ "default": "./src/index.mjs" }, "require": { - "types": "./dist/cjs-types/index.d.ts", + "types": "./dist/cjs-types/index.d.cts", "default": "./dist/cjs/index.cjs" } }, diff --git a/packages/@code-like-a-carpenter/errors/package.json b/packages/@code-like-a-carpenter/errors/package.json index 685bd290..7fc0c418 100644 --- a/packages/@code-like-a-carpenter/errors/package.json +++ b/packages/@code-like-a-carpenter/errors/package.json @@ -13,7 +13,7 @@ "default": "./dist/esm/index.mjs" }, "require": { - "types": "./dist/types/index.d.ts", + "types": "./dist/cjs-types/index.d.cts", "default": "./dist/cjs/index.cjs" } }, diff --git a/packages/@code-like-a-carpenter/exception/package.json b/packages/@code-like-a-carpenter/exception/package.json index 2cff852c..c3bdcdc4 100644 --- a/packages/@code-like-a-carpenter/exception/package.json +++ b/packages/@code-like-a-carpenter/exception/package.json @@ -8,7 +8,7 @@ "default": "./dist/esm/index.mjs" }, "require": { - "types": "./dist/types/index.d.ts", + "types": "./dist/cjs-types/index.d.cts", "default": "./dist/cjs/index.cjs" } }, diff --git a/packages/@code-like-a-carpenter/foundation-intermediate-representation/package.json b/packages/@code-like-a-carpenter/foundation-intermediate-representation/package.json index 73d7c334..1714a5b5 100644 --- a/packages/@code-like-a-carpenter/foundation-intermediate-representation/package.json +++ b/packages/@code-like-a-carpenter/foundation-intermediate-representation/package.json @@ -13,7 +13,7 @@ "default": "./dist/esm/index.mjs" }, "require": { - "types": "./dist/types/index.d.ts", + "types": "./dist/cjs-types/index.d.cts", "default": "./dist/cjs/index.cjs" } }, diff --git a/packages/@code-like-a-carpenter/foundation-parser/package.json b/packages/@code-like-a-carpenter/foundation-parser/package.json index 6b22c2d6..6b52c3c2 100644 --- a/packages/@code-like-a-carpenter/foundation-parser/package.json +++ b/packages/@code-like-a-carpenter/foundation-parser/package.json @@ -13,7 +13,7 @@ "default": "./dist/esm/index.mjs" }, "require": { - "types": "./dist/types/index.d.ts", + "types": "./dist/cjs-types/index.d.cts", "default": "./dist/cjs/index.cjs" } }, diff --git a/packages/@code-like-a-carpenter/foundation-plugin-cloudformation/package.json b/packages/@code-like-a-carpenter/foundation-plugin-cloudformation/package.json index 7e7ace0d..d1c71af1 100644 --- a/packages/@code-like-a-carpenter/foundation-plugin-cloudformation/package.json +++ b/packages/@code-like-a-carpenter/foundation-plugin-cloudformation/package.json @@ -13,7 +13,7 @@ "default": "./dist/esm/index.mjs" }, "require": { - "types": "./dist/types/index.d.ts", + "types": "./dist/cjs-types/index.d.cts", "default": "./dist/cjs/index.cjs" } }, diff --git a/packages/@code-like-a-carpenter/foundation-plugin-typescript/package.json b/packages/@code-like-a-carpenter/foundation-plugin-typescript/package.json index 305d3022..92b59a92 100644 --- a/packages/@code-like-a-carpenter/foundation-plugin-typescript/package.json +++ b/packages/@code-like-a-carpenter/foundation-plugin-typescript/package.json @@ -13,7 +13,7 @@ "default": "./dist/esm/index.mjs" }, "require": { - "types": "./dist/types/index.d.ts", + "types": "./dist/cjs-types/index.d.cts", "default": "./dist/cjs/index.cjs" } }, diff --git a/packages/@code-like-a-carpenter/foundation-runtime/package.json b/packages/@code-like-a-carpenter/foundation-runtime/package.json index c06f75da..81e34c64 100644 --- a/packages/@code-like-a-carpenter/foundation-runtime/package.json +++ b/packages/@code-like-a-carpenter/foundation-runtime/package.json @@ -13,7 +13,7 @@ "default": "./dist/esm/index.mjs" }, "require": { - "types": "./dist/types/index.d.ts", + "types": "./dist/cjs-types/index.d.cts", "default": "./dist/cjs/index.cjs" } }, diff --git a/packages/@code-like-a-carpenter/foundation-transform-environment/package.json b/packages/@code-like-a-carpenter/foundation-transform-environment/package.json index d31fb2cc..44e51687 100644 --- a/packages/@code-like-a-carpenter/foundation-transform-environment/package.json +++ b/packages/@code-like-a-carpenter/foundation-transform-environment/package.json @@ -18,7 +18,7 @@ "default": "./dist/esm/index.mjs" }, "require": { - "types": "./dist/types/index.d.ts", + "types": "./dist/cjs-types/index.d.cts", "default": "./dist/cjs/index.cjs" } }, diff --git a/packages/@code-like-a-carpenter/graphql-codegen-helpers/package.json b/packages/@code-like-a-carpenter/graphql-codegen-helpers/package.json index 996535ab..a10651d9 100644 --- a/packages/@code-like-a-carpenter/graphql-codegen-helpers/package.json +++ b/packages/@code-like-a-carpenter/graphql-codegen-helpers/package.json @@ -17,7 +17,7 @@ "default": "./dist/esm/index.mjs" }, "require": { - "types": "./dist/types/index.d.ts", + "types": "./dist/cjs-types/index.d.cts", "default": "./dist/cjs/index.cjs" } }, diff --git a/packages/@code-like-a-carpenter/interact/package.json b/packages/@code-like-a-carpenter/interact/package.json index a49eda02..c51fb51b 100644 --- a/packages/@code-like-a-carpenter/interact/package.json +++ b/packages/@code-like-a-carpenter/interact/package.json @@ -13,7 +13,7 @@ "default": "./dist/esm/index.mjs" }, "require": { - "types": "./dist/types/index.d.ts", + "types": "./dist/cjs-types/index.d.cts", "default": "./dist/cjs/index.cjs" } }, diff --git a/packages/@code-like-a-carpenter/lambda-handlers/package.json b/packages/@code-like-a-carpenter/lambda-handlers/package.json index 0fd21ca5..559a37b4 100644 --- a/packages/@code-like-a-carpenter/lambda-handlers/package.json +++ b/packages/@code-like-a-carpenter/lambda-handlers/package.json @@ -23,7 +23,7 @@ "default": "./dist/esm/index.mjs" }, "require": { - "types": "./dist/types/index.d.ts", + "types": "./dist/cjs-types/index.d.cts", "default": "./dist/cjs/index.cjs" } }, diff --git a/packages/@code-like-a-carpenter/logger/package.json b/packages/@code-like-a-carpenter/logger/package.json index c27cf07a..bb7b4f84 100644 --- a/packages/@code-like-a-carpenter/logger/package.json +++ b/packages/@code-like-a-carpenter/logger/package.json @@ -13,7 +13,7 @@ "default": "./dist/esm/index.mjs" }, "require": { - "types": "./dist/types/index.d.ts", + "types": "./dist/cjs-types/index.d.cts", "default": "./dist/cjs/index.cjs" } }, diff --git a/packages/@code-like-a-carpenter/parallel/package.json b/packages/@code-like-a-carpenter/parallel/package.json index f485a185..b56534cb 100644 --- a/packages/@code-like-a-carpenter/parallel/package.json +++ b/packages/@code-like-a-carpenter/parallel/package.json @@ -13,7 +13,7 @@ "default": "./dist/esm/index.mjs" }, "require": { - "types": "./dist/types/index.d.ts", + "types": "./dist/cjs-types/index.d.cts", "default": "./dist/cjs/index.cjs" } }, diff --git a/packages/@code-like-a-carpenter/sentry/package.json b/packages/@code-like-a-carpenter/sentry/package.json index 6ffea8ab..ef216672 100644 --- a/packages/@code-like-a-carpenter/sentry/package.json +++ b/packages/@code-like-a-carpenter/sentry/package.json @@ -13,7 +13,7 @@ "default": "./dist/esm/index.mjs" }, "require": { - "types": "./dist/types/index.d.ts", + "types": "./dist/cjs-types/index.d.cts", "default": "./dist/cjs/index.cjs" } }, diff --git a/packages/@code-like-a-carpenter/telemetry/package.json b/packages/@code-like-a-carpenter/telemetry/package.json index d27baa68..0a31371a 100644 --- a/packages/@code-like-a-carpenter/telemetry/package.json +++ b/packages/@code-like-a-carpenter/telemetry/package.json @@ -13,7 +13,7 @@ "default": "./dist/esm/index.mjs" }, "require": { - "types": "./dist/types/index.d.ts", + "types": "./dist/cjs-types/index.d.cts", "default": "./dist/cjs/index.cjs" } }, diff --git a/packages/@code-like-a-carpenter/tool-deps/package.json b/packages/@code-like-a-carpenter/tool-deps/package.json index dca6f612..2978e8c1 100644 --- a/packages/@code-like-a-carpenter/tool-deps/package.json +++ b/packages/@code-like-a-carpenter/tool-deps/package.json @@ -21,7 +21,7 @@ "default": "./src/index.mjs" }, "require": { - "types": "./dist/cjs-types/index.d.ts", + "types": "./dist/cjs-types/index.d.cts", "default": "./dist/cjs/index.cjs" } }, diff --git a/packages/@code-like-a-carpenter/tool-foundation/package.json b/packages/@code-like-a-carpenter/tool-foundation/package.json index 799b4ec6..74db66be 100644 --- a/packages/@code-like-a-carpenter/tool-foundation/package.json +++ b/packages/@code-like-a-carpenter/tool-foundation/package.json @@ -14,7 +14,7 @@ "default": "./dist/esm/index.mjs" }, "require": { - "types": "./dist/types/index.d.ts", + "types": "./dist/cjs-types/index.d.cts", "default": "./dist/cjs/index.cjs" } }, diff --git a/packages/@code-like-a-carpenter/tool-inliner/package.json b/packages/@code-like-a-carpenter/tool-inliner/package.json index 4b6c1884..fb1465b9 100644 --- a/packages/@code-like-a-carpenter/tool-inliner/package.json +++ b/packages/@code-like-a-carpenter/tool-inliner/package.json @@ -13,7 +13,7 @@ "default": "./src/index.mjs" }, "require": { - "types": "./dist/cjs-types/index.d.ts", + "types": "./dist/cjs-types/index.d.cts", "default": "./dist/cjs/index.cjs" } }, diff --git a/packages/@code-like-a-carpenter/tool-json-schema/package.json b/packages/@code-like-a-carpenter/tool-json-schema/package.json index 74afad13..f95febd0 100644 --- a/packages/@code-like-a-carpenter/tool-json-schema/package.json +++ b/packages/@code-like-a-carpenter/tool-json-schema/package.json @@ -13,7 +13,7 @@ "default": "./src/index.mjs" }, "require": { - "types": "./dist/cjs-types/index.d.ts", + "types": "./dist/cjs-types/index.d.cts", "default": "./dist/cjs/index.cjs" } }, diff --git a/packages/@code-like-a-carpenter/tool-stack/package.json b/packages/@code-like-a-carpenter/tool-stack/package.json index f41737f9..2a7a7817 100644 --- a/packages/@code-like-a-carpenter/tool-stack/package.json +++ b/packages/@code-like-a-carpenter/tool-stack/package.json @@ -13,7 +13,7 @@ "default": "./src/index.mjs" }, "require": { - "types": "./dist/cjs-types/index.d.ts", + "types": "./dist/cjs-types/index.d.cts", "default": "./dist/cjs/index.cjs" } }, diff --git a/packages/@code-like-a-carpenter/tool-tool/package.json b/packages/@code-like-a-carpenter/tool-tool/package.json index 0822f0a7..1f52be44 100644 --- a/packages/@code-like-a-carpenter/tool-tool/package.json +++ b/packages/@code-like-a-carpenter/tool-tool/package.json @@ -25,7 +25,7 @@ "default": "./src/index.mjs" }, "require": { - "types": "./dist/cjs-types/index.d.ts", + "types": "./dist/cjs-types/index.d.cts", "default": "./dist/cjs/index.cjs" } }, diff --git a/packages/@code-like-a-carpenter/tooling-common/package.json b/packages/@code-like-a-carpenter/tooling-common/package.json index eda60293..61187908 100644 --- a/packages/@code-like-a-carpenter/tooling-common/package.json +++ b/packages/@code-like-a-carpenter/tooling-common/package.json @@ -13,7 +13,7 @@ "default": "./src/index.mjs" }, "require": { - "types": "./dist/cjs-types/index.d.ts", + "types": "./dist/cjs-types/index.d.cts", "default": "./dist/cjs/index.cjs" } }, diff --git a/packages/@code-like-a-carpenter/wait-for/package.json b/packages/@code-like-a-carpenter/wait-for/package.json index 089fbac9..f8d24fe8 100644 --- a/packages/@code-like-a-carpenter/wait-for/package.json +++ b/packages/@code-like-a-carpenter/wait-for/package.json @@ -13,7 +13,7 @@ "default": "./dist/esm/index.mjs" }, "require": { - "types": "./dist/types/index.d.ts", + "types": "./dist/cjs-types/index.d.cts", "default": "./dist/cjs/index.cjs" } }, diff --git a/packages/@code-like-a-carpenter/workbench-config/package.json b/packages/@code-like-a-carpenter/workbench-config/package.json index 74af2488..51f88905 100644 --- a/packages/@code-like-a-carpenter/workbench-config/package.json +++ b/packages/@code-like-a-carpenter/workbench-config/package.json @@ -13,7 +13,7 @@ "default": "./src/index.mjs" }, "require": { - "types": "./dist/cjs-types/index.d.ts", + "types": "./dist/cjs-types/index.d.cts", "default": "./dist/cjs/index.cjs" } }, diff --git a/scripts/dmts-to-dts b/scripts/dmts-to-dts deleted file mode 100755 index f1984026..00000000 --- a/scripts/dmts-to-dts +++ /dev/null @@ -1,12 +0,0 @@ -#!/usr/bin/env bash - -set -euo pipefail - -PROJECT_ROOT=$1 -EXTENSION=${2:-} - -for f in $(find $PROJECT_ROOT/dist/types -type f); do - outfile=$(echo $f | sed -e 's#/types/#/cjs-types/#' | sed -e 's#.d.mts#.d.ts#') - mkdir -p "$(dirname "$outfile")" - sed -e "s#.mjs#${EXTENSION}#g" "$f" | sed -e "s#.mts#${EXTENSION}#g" > "$outfile" -done diff --git a/scripts/dts-to-dcts.mjs b/scripts/dts-to-dcts.mjs new file mode 100755 index 00000000..f039c6e8 --- /dev/null +++ b/scripts/dts-to-dcts.mjs @@ -0,0 +1,258 @@ +#!/usr/bin/env node + +/** + * Produces `dist/cjs-types` from `dist/types`. + * + * Every package is `"type": "module"`, so a `.d.ts` under it is read as ESM no + * matter what `exports` says. The CommonJS half of `exports` therefore needs + * declarations whose extension forces CJS: `.d.cts`, whose relative specifiers + * resolve to sibling `.d.cts` files (a `.cjs` specifier resolves to `.d.cts` + * under node16). + */ + +import {cp, mkdir, readdir, readFile, rm, writeFile} from 'node:fs/promises'; +import path from 'node:path'; +import process from 'node:process'; + +const SPECIFIER_EXTENSIONS = [ + '.mts', + '.mjs', + '.cts', + '.cjs', + '.tsx', + '.jsx', + '.ts', + '.js', +]; + +const DECLARATION_SUFFIXES = [ + '.d.mts.map', + '.d.cts.map', + '.d.ts.map', + '.d.mts', + '.d.cts', + '.d.ts', +]; + +/** + * Extensions TypeScript resolves without help from this script: JSON keeps + * resolving as JSON, and the file itself is copied through unchanged. + */ +const PASSTHROUGH_EXTENSIONS = ['.json']; + +/** + * Treats every quoted string starting with `./` or `../` as a module + * specifier, which covers `from`, bare `import`, `import()` types, `require()`, + * and `declare module` alike. Any other quoted relative path gets the same + * treatment — a string literal type, a template literal type, or a doc comment + * — and an extensionless one throws. No declaration in this repo has one, and + * the alternative is parsing TypeScript here. + */ +const RELATIVE_SPECIFIER = /(['"])(\.\.?\/[^'"\n]*)\1/g; + +/** + * Reference paths need their own pass because, unlike a module specifier, one + * may name a sibling without a leading `./`. + */ +const REFERENCE_PATH = /( name.endsWith(suffix)); +} + +/** + * A specifier that already names a declaration file points at the sibling + * `.d.cts` rather than at a `.cjs` implementation. + * + * An extensionless or directory specifier has no rewrite that works: node16 + * CommonJS resolution finds neither `./x.d.cts` through `./x` nor + * `./dir/index.d.cts` through `./dir`, so the declaration would resolve to + * nothing. Fail rather than emit output that type-checks as an unresolved + * module. + * + * @param {string} specifier + * @param {string} source the declaration being converted, for the error + * @returns {string} + */ +function toCjsSpecifier(specifier, source) { + if (declarationSuffix(specifier)) { + return toCjsFilename(specifier); + } + for (const extension of SPECIFIER_EXTENSIONS) { + if (specifier.endsWith(extension)) { + return `${specifier.slice(0, -extension.length)}.cjs`; + } + } + if ( + PASSTHROUGH_EXTENSIONS.some((extension) => specifier.endsWith(extension)) + ) { + return specifier; + } + throw new Error( + `${source}: relative specifier "${specifier}" has no extension to rewrite; ` + + `CommonJS declarations cannot resolve it. Give the import an explicit extension.` + ); +} + +/** + * @param {string} filename + * @returns {string} + */ +function toCjsFilename(filename) { + const suffix = declarationSuffix(filename); + if (suffix) { + const replacement = suffix.endsWith('.map') ? '.d.cts.map' : '.d.cts'; + return `${filename.slice(0, -suffix.length)}${replacement}`; + } + return filename; +} + +/** + * @param {string} filename + * @returns {boolean} + */ +function isDeclaration(filename) { + return ( + filename.endsWith('.d.ts') || + filename.endsWith('.d.mts') || + filename.endsWith('.d.cts') + ); +} + +/** + * @param {string} filename + * @returns {boolean} + */ +function isDeclarationMap(filename) { + return ( + filename.endsWith('.d.ts.map') || + filename.endsWith('.d.mts.map') || + filename.endsWith('.d.cts.map') + ); +} + +/** + * @param {string} contents + * @param {string} source the declaration being converted, for error messages + * @returns {string} + */ +function convertDeclaration(contents, source) { + return contents + .replace( + REFERENCE_PATH, + (_match, prefix, _quote, target, suffix) => + `${prefix}${toCjsFilename(target)}${suffix}` + ) + .replace( + RELATIVE_SPECIFIER, + (_match, quote, specifier) => + `${quote}${toCjsSpecifier(specifier, source)}${quote}` + ) + .replace( + SOURCE_MAPPING_URL, + (_match, prefix, url) => `${prefix}${toCjsFilename(url)}` + ); +} + +/** + * `sources` stay correct because `dist/cjs-types` sits at the same depth as + * `dist/types`; only the name of the declaration the map describes changes. + * + * `mappings` are left alone. Rewriting `.ts` to `.cjs` lengthens a specifier by + * one character, so columns after it on that line are off by one; that is + * cheaper to live with than re-emitting the map. + * + * @param {string} contents + * @returns {string} + */ +function convertDeclarationMap(contents) { + const map = JSON.parse(contents); + if (typeof map.file === 'string') { + map.file = toCjsFilename(map.file); + } + return `${JSON.stringify(map)}\n`; +} + +/** + * @param {string} dir + * @returns {Promise} paths relative to `dir` + */ +async function walk(dir) { + const entries = await readdir(dir, {recursive: true, withFileTypes: true}); + return entries + .filter((entry) => entry.isFile()) + .map((entry) => + path.relative(dir, path.join(entry.parentPath, entry.name)) + ); +} + +/** + * @param {string} projectRoot + */ +async function main(projectRoot) { + const typesDir = path.join(projectRoot, 'dist', 'types'); + const cjsTypesDir = path.join(projectRoot, 'dist', 'cjs-types'); + + const relativePaths = await walk(typesDir); + if (relativePaths.length === 0) { + throw new Error( + `${typesDir} is empty; every package's require condition points at dist/cjs-types/index.d.cts` + ); + } + + // Anything left in `dist/cjs-types` from a previous run would otherwise + // survive a rename or deletion in `dist/types`. + await rm(cjsTypesDir, {force: true, recursive: true}); + + /** @type {Map} */ + const claimed = new Map(); + + for (const relativePath of relativePaths) { + const from = path.join(typesDir, relativePath); + const to = path.join( + cjsTypesDir, + path.join( + path.dirname(relativePath), + toCjsFilename(path.basename(relativePath)) + ) + ); + + const previous = claimed.get(to); + if (previous) { + throw new Error( + `${previous} and ${relativePath} both convert to ${path.relative(projectRoot, to)}` + ); + } + claimed.set(to, relativePath); + + await mkdir(path.dirname(to), {recursive: true}); + + const filename = path.basename(relativePath); + if (isDeclaration(filename)) { + await writeFile( + to, + convertDeclaration(await readFile(from, 'utf8'), relativePath) + ); + } else if (isDeclarationMap(filename)) { + await writeFile(to, convertDeclarationMap(await readFile(from, 'utf8'))); + } else { + await cp(from, to); + } + } +} + +const [projectRoot] = process.argv.slice(2); +if (!projectRoot) { + process.stderr.write( + `usage: ${path.basename(process.argv[1])} \n` + ); + process.exit(1); +} + +await main(projectRoot);