UNPKG

tsconfig-to-dual-package

Version:

A simple tool that add package.json({"type":"commonjs"/"module"}) to TypeScript outDir for dual package.

162 lines (161 loc) 7.29 kB
"use strict"; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.tsconfigToDualPackages = exports.createModuleTypePackage = exports.findTsConfig = exports.findNearestPackageJson = void 0; const promises_1 = __importDefault(require("node:fs/promises")); const node_path_1 = __importDefault(require("node:path")); const resolve_tsconfig_1 = require("resolve-tsconfig"); const typescript_1 = __importDefault(require("typescript")); const formatDiagnostics = (diagnostics) => { return diagnostics .map((diagnostic) => { if (diagnostic.file) { const { line, character } = diagnostic.file.getLineAndCharacterOfPosition(diagnostic.start); const message = typescript_1.default.flattenDiagnosticMessageText(diagnostic.messageText, "\n"); return `${diagnostic.file.fileName} (${line + 1},${character + 1}): ${message}`; } else { return typescript_1.default.flattenDiagnosticMessageText(diagnostic.messageText, "\n"); } }) .join("\n"); }; const findNearestPackageJson = async ({ cwd }) => { // cwd is root, throw and error if (cwd === node_path_1.default.dirname(cwd)) { throw new Error("Failed to find package.json"); } const packageJsonFilePath = node_path_1.default.join(cwd, "package.json"); const exists = await promises_1.default.stat(packageJsonFilePath).catch(() => false); if (!exists) { return (0, exports.findNearestPackageJson)({ cwd: node_path_1.default.dirname(cwd) }); } return JSON.parse(await promises_1.default.readFile(packageJsonFilePath, "utf-8")); }; exports.findNearestPackageJson = findNearestPackageJson; const findTsConfig = async ({ targetTsConfigFilePaths, cwd }) => { if (targetTsConfigFilePaths && targetTsConfigFilePaths.length > 0) { return targetTsConfigFilePaths.map((value) => { return node_path_1.default.resolve(cwd, value); }); } const dirents = await promises_1.default.readdir(cwd, { withFileTypes: true }); // ok: tsconfig.json // ok: tsconfig.cjs.json // ok: tsconfig-cjs.json // ng: cjs-tsconfig.json // ng: tsconfig.json.cjs return dirents .filter((dirent) => { // remove non-tsconfig.json return node_path_1.default.extname(dirent.name) === ".json" && dirent.name.startsWith("tsconfig"); }) .map((dirent) => { return node_path_1.default.join(cwd, dirent.name); }); }; exports.findTsConfig = findTsConfig; const createModuleTypePackage = async ({ cwd, type, debug }) => { // if the field has relative path, it should not be included in generated package.json // because it is different relative path from the package.json // https://docs.npmjs.com/cli/v9/configuring-npm/package-json // https://nodejs.org/api/packages.html#community-conditions-definitions const OMIT_FIELDS = ["main", "module", "browser", "types", "exports"]; try { const basePkg = JSON.parse(await promises_1.default.readFile(node_path_1.default.resolve(cwd, "package.json"), "utf-8")); const filteredPkg = Object.fromEntries(Object.entries(basePkg).filter(([key]) => !OMIT_FIELDS.includes(key))); return { ...filteredPkg, type }; } catch (e) { if (debug) { console.error("Failed to load package.json", { cwd, type, error: e }); } throw new Error(`Failed to read package.json in ${cwd}`, { cause: e }); } }; exports.createModuleTypePackage = createModuleTypePackage; const getModuleType = ({ packageType, moduleKind }) => { // TODO: get more better way if (moduleKind >= typescript_1.default.ModuleKind.ES2015 && moduleKind <= typescript_1.default.ModuleKind.ESNext) { return "module"; } else if (moduleKind >= typescript_1.default.ModuleKind.Node16 && moduleKind <= typescript_1.default.ModuleKind.NodeNext) { // use package.json's type // if type is not set, use commonjs // > The emitted JavaScript uses either CommonJS or ES2020 output depending on the file extension and the value of the type setting in the nearest package.json. Module resolution also works differently. // https://www.typescriptlang.org/tsconfig#node16nodenext-nightly-builds // https://www.typescriptlang.org/docs/handbook/esm-node.html return packageType !== null && packageType !== void 0 ? packageType : "commonjs"; } else if (moduleKind === typescript_1.default.ModuleKind.CommonJS) { return "commonjs"; } throw new Error("Non-support module kind: " + moduleKind); }; const tsconfigToDualPackages = async ({ targetTsConfigFilePaths, cwd, debug }) => { const debugWithDefault = debug !== null && debug !== void 0 ? debug : false; const packageJson = await (0, exports.findNearestPackageJson)({ cwd }); // search tsconfig*.json const tsconfigFilePaths = await (0, exports.findTsConfig)({ targetTsConfigFilePaths, cwd }); // load tsconfig.json const tsconfigs = await Promise.all(tsconfigFilePaths.map(async (tsconfigFilePath) => { return { filePath: tsconfigFilePath, tsconfig: await (0, resolve_tsconfig_1.resolveTsConfig)({ filePath: tsconfigFilePath }) }; })); // create package.json for dual package const dualPackages = await Promise.all(tsconfigs.map(async ({ filePath, tsconfig }) => { if (tsconfig.diagnostics || !tsconfig.config) { const error = new Error(`Failed to load tsconfig: ${filePath} ${formatDiagnostics(tsconfig.diagnostics)} `); if (debugWithDefault) { console.error({ error, filePath, tsconfig }); } throw error; } if (!tsconfig.config.options.outDir) { throw new Error(`Failed to find "outDir" option in tsconfig.json`); } if (!tsconfig.config.options.module) { throw new Error(`Failed to find "module" option in tsconfig.json`); } return { outDir: tsconfig.config.options.outDir, pkg: await (0, exports.createModuleTypePackage)({ cwd, type: getModuleType({ packageType: packageJson.type, moduleKind: tsconfig.config.options.module }), debug: debugWithDefault }) }; })); // write to <outDir>/package.json await Promise.all(dualPackages.map(async (dualPackage) => { const { outDir, pkg } = await dualPackage; await promises_1.default.mkdir(node_path_1.default.resolve(cwd, outDir), { recursive: true }); await promises_1.default.writeFile(node_path_1.default.resolve(cwd, outDir, "package.json"), JSON.stringify(pkg, null, 2), "utf-8"); })); }; exports.tsconfigToDualPackages = tsconfigToDualPackages; //# sourceMappingURL=tsconfig-to-dual-package.js.map