UNPKG

@monorepo-utils/workspaces-to-typescript-project-references

Version:
293 lines (286 loc) 13.5 kB
"use strict"; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.toRootProjectReferences = exports.toProjectReferences = exports.DEFAULT_TSCONFIGPATH = void 0; const fs_1 = __importDefault(require("fs")); const upath_1 = __importDefault(require("upath")); const comment_json_1 = __importDefault(require("comment-json")); const workspaces_1 = require("./manager/workspaces"); const assert_1 = __importDefault(require("assert")); exports.DEFAULT_TSCONFIGPATH = "tsconfig.json"; const sortReferences = (references) => { return references.slice().sort((refA, refB) => (refA.path > refB.path ? 1 : -1)); }; const getReferenceInPackage = (tsconfigFilePath) => { try { const dirPath = upath_1.default.dirname(tsconfigFilePath); const fileName = upath_1.default.basename(tsconfigFilePath); const files = fs_1.default.readdirSync(dirPath); const tsconfigFilePaths = files .filter(file => file !== fileName && file.startsWith('tsconfig.') && file.endsWith('.json')) .map(file => ({ path: `./${file}` })); return tsconfigFilePaths; } catch (err) { return []; } }; // when tsconfig.json → ../path/to/dir // when tsconfig.x.json → ../path/to/dir/tsconfig.x.json const getReferencePathToTsConfig = (options) => { var _a; const tsConfigPath = (_a = options.tsConfigPath) !== null && _a !== void 0 ? _a : exports.DEFAULT_TSCONFIGPATH; const tsConfigDirName = upath_1.default.dirname(tsConfigPath); const pathComponents = [options.packageLocation, tsConfigDirName]; // If the file name is not tsconfig.json (the default), // then append it to the generated path const tsConfigFileName = upath_1.default.basename(tsConfigPath); if (tsConfigFileName !== exports.DEFAULT_TSCONFIGPATH) { pathComponents.push(tsConfigFileName); } return upath_1.default.join(...pathComponents); }; const validateDuplicatedDependencies = (packageReferences) => { const packageNameSet = new Set(); const errors = []; packageReferences.forEach((ref) => { if (packageNameSet.has(ref.name)) { errors.push(new Error(`This package is deduplicated in dependencies and devDependencies: ${ref.name} Please remove duplicate dependencies from dependencies or devDependencies.`)); return; } packageNameSet.add(ref.name); }); return { ok: errors.length === 0, errors }; }; const toProjectReferences = (options) => { const plugins = Array.isArray(options.plugins) && options.plugins.length > 0 ? options.plugins : [workspaces_1.plugin]; const pluginImplementations = plugins.map((plugin) => plugin(options)); // use first plugin const supportPlugin = pluginImplementations.find((plugin) => { return plugin.supports(); }); if (!supportPlugin) { throw new Error("Not found supported plugin"); } const relativeName = (pkgPath) => { return upath_1.default.relative(options.rootDir, pkgPath); }; const allPackages = supportPlugin.getAllPackages(); const errors = []; const canNotRecoverErrors = []; allPackages.forEach((packageInfo) => { var _a, _b, _c; const tsconfigFilePath = (_b = (_a = options.tsConfigPathFinder) === null || _a === void 0 ? void 0 : _a.call(options, packageInfo.location)) !== null && _b !== void 0 ? _b : upath_1.default.join(packageInfo.location, exports.DEFAULT_TSCONFIGPATH); if (!fs_1.default.existsSync(tsconfigFilePath)) { // Skip has not tsconfig.json return; } const tsconfigJSON = comment_json_1.default.parse(fs_1.default.readFileSync(tsconfigFilePath, "utf-8")); const references = supportPlugin.getDependencies(packageInfo.packageJSON); const resolvedReferences = references .map((reference) => { const absolutePathOrNull = supportPlugin.resolve(reference); if (!absolutePathOrNull) { return; } if (!upath_1.default.isAbsolute(absolutePathOrNull)) { throw new Error(`Plugin#resolve should return absolute path: ${absolutePathOrNull}, plugin: ${supportPlugin}`); } return { name: reference.name, version: reference.version, absolutePath: absolutePathOrNull }; }) .filter((r) => Boolean(r)); const referenceValidateResult = validateDuplicatedDependencies(resolvedReferences); // if found an error, do early return: https://github.com/azu/monorepo-utils/issues/56 if (!referenceValidateResult.ok) { canNotRecoverErrors.push(...referenceValidateResult.errors); return; } const newProjectReferences = resolvedReferences .map((reference) => { var _a; const absolutePath = reference.absolutePath; // Should ignore no-ts package // https://github.com/azu/monorepo-utils/issues/53 const referencePackageTsConfigFilePath = upath_1.default.resolve(absolutePath, (_a = options.tsConfigPath) !== null && _a !== void 0 ? _a : exports.DEFAULT_TSCONFIGPATH); if (!fs_1.default.existsSync(referencePackageTsConfigFilePath)) { return; } // { path } value const referenceTsConfigPath = getReferencePathToTsConfig({ packageLocation: absolutePath, tsConfigPath: options.tsConfigPath }); if (packageInfo.location === referenceTsConfigPath) { const selfName = relativeName(packageInfo.location); errors.push(new Error(`[${selfName}] Self dependencies is something wrong: ${selfName} refer to ${relativeName(referenceTsConfigPath)}`)); } const basePackageLocation = upath_1.default.dirname(upath_1.default.resolve(packageInfo.location, tsconfigFilePath)); return { path: upath_1.default.relative(basePackageLocation, referenceTsConfigPath) }; }) .filter((r) => Boolean(r)); if (options.includesLocal) { const referenceInLocal = getReferenceInPackage(tsconfigFilePath); newProjectReferences.push(...referenceInLocal); } const currentProjectReferences = (_c = tsconfigJSON["references"]) !== null && _c !== void 0 ? _c : []; if (options.checkOnly) { // check try { // create pure object for compare const cleanCurrentProjectReferences = JSON.parse(JSON.stringify(comment_json_1.default.parse(comment_json_1.default.stringify(currentProjectReferences), undefined, true))); // TODO: move sorting to updating logic when next major release // https://github.com/azu/monorepo-utils/issues/44 assert_1.default.deepStrictEqual(sortReferences(cleanCurrentProjectReferences), sortReferences(newProjectReferences)); } catch (error) { const selfName = relativeName(packageInfo.location); errors.push(new Error(`[${selfName}] ${error.message}`)); } return; } else { // update if (errors.length === 0) { if (currentProjectReferences.length === 0 && newProjectReferences.length === 0) { // As to not add `references: []` needlessly return; } tsconfigJSON["references"] = newProjectReferences; const oldContents = comment_json_1.default.stringify(comment_json_1.default.parse(fs_1.default.readFileSync(tsconfigFilePath, { encoding: "utf-8" })), null, 2); const newContents = comment_json_1.default.stringify(tsconfigJSON, null, 2); if (newContents !== oldContents) { fs_1.default.writeFileSync(tsconfigFilePath, `${newContents}\n`, "utf-8"); } } } }); if (canNotRecoverErrors.length > 0) { return { ok: false, aggregateError: { message: `workspaces-to-typescript-project-references found ${canNotRecoverErrors.length} errors. - ${canNotRecoverErrors.map((error) => error.message.split("\n")[0]).join("\n- ")} Please resolve these error before updates tsconfig.json `, errors: canNotRecoverErrors } }; } if (errors.length > 0) { return { ok: false, aggregateError: { message: `workspaces-to-typescript-project-references found ${errors.length} errors. Please update your tsconfig.json via following command. $ workspaces-to-typescript-project-references `, errors } }; } return { ok: true }; }; exports.toProjectReferences = toProjectReferences; const toRootProjectReferences = (options) => { var _a, _b, _c, _d; const plugins = Array.isArray(options.plugins) && options.plugins.length > 0 ? options.plugins : [workspaces_1.plugin]; const pluginImplementations = plugins.map((plugin) => plugin(options)); // use first plugin const supportPlugin = pluginImplementations.find((plugin) => { return plugin.supports(); }); if (!supportPlugin) { throw new Error("Not found supported plugin"); } const allPackages = supportPlugin.getAllPackages(); const errors = []; const rootTsconfigFilePath = (_b = (_a = options.tsConfigPathFinder) === null || _a === void 0 ? void 0 : _a.call(options, options.rootDir)) !== null && _b !== void 0 ? _b : upath_1.default.join(options.rootDir, exports.DEFAULT_TSCONFIGPATH); if (!fs_1.default.existsSync(rootTsconfigFilePath)) { const tsConfigPath = (_c = options.tsConfigPath) !== null && _c !== void 0 ? _c : exports.DEFAULT_TSCONFIGPATH; return { ok: false, aggregateError: { message: `Not found ${tsConfigPath} in ${rootTsconfigFilePath}`, errors } }; } const tsconfigJSON = comment_json_1.default.parse(fs_1.default.readFileSync(rootTsconfigFilePath, "utf-8")); const projectReferences = allPackages .filter((pkg) => { var _a, _b; const tsconfigFilePath = (_b = (_a = options.tsConfigPathFinder) === null || _a === void 0 ? void 0 : _a.call(options, pkg.location)) !== null && _b !== void 0 ? _b : upath_1.default.join(pkg.location, exports.DEFAULT_TSCONFIGPATH); // Skip if the package has not tsconfig.json return fs_1.default.existsSync(tsconfigFilePath); }) .map((pkg) => { var _a; const tsConfigPath = (_a = options.tsConfigPath) !== null && _a !== void 0 ? _a : exports.DEFAULT_TSCONFIGPATH; const pathComponents = [upath_1.default.relative(options.rootDir, pkg.location)]; // If the file name is not tsconfig.json (the default), // then append it to the generated path const tsConfigFileName = upath_1.default.basename(tsConfigPath); if (tsConfigFileName !== exports.DEFAULT_TSCONFIGPATH) { pathComponents.push(tsConfigFileName); } return { path: upath_1.default.join(...pathComponents) }; }); if (options.includesLocal) { const referenceInLocal = getReferenceInPackage(rootTsconfigFilePath); projectReferences.push(...referenceInLocal); } if (options.checkOnly) { // check try { const currentProjectReferences = (_d = tsconfigJSON["references"]) !== null && _d !== void 0 ? _d : []; const cleanCurrentProjectReferences = JSON.parse(JSON.stringify(comment_json_1.default.parse(comment_json_1.default.stringify(currentProjectReferences), undefined, true))); // TODO: move sorting to updating logic when next major release // https://github.com/azu/monorepo-utils/issues/44 assert_1.default.deepStrictEqual(sortReferences(cleanCurrentProjectReferences), sortReferences(projectReferences)); } catch (error) { errors.push(new Error(`[root] ${error.message}`)); } } else { const updatedTsConfigJSON = Object.assign(Object.assign({}, tsconfigJSON), { references: projectReferences }); const oldContents = comment_json_1.default.stringify(projectReferences, null, 2); const newContents = comment_json_1.default.stringify(updatedTsConfigJSON, null, 2); if (newContents !== oldContents) { fs_1.default.writeFileSync(rootTsconfigFilePath, `${newContents}\n`, "utf-8"); } } if (errors.length > 0) { return { ok: false, aggregateError: { message: `workspaces-to-typescript-project-references found ${errors.length} errors in root tsconfig. Please update your <root>/tsconfig.json via following command. $ workspaces-to-typescript-project-references --includesRoot `, errors } }; } return { ok: true }; }; exports.toRootProjectReferences = toRootProjectReferences; //# sourceMappingURL=index.js.map