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
JavaScript
;
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