@nx/jest
Version:
87 lines (86 loc) • 3.51 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.default = default_1;
const devkit_1 = require("@nx/devkit");
const internal_1 = require("@nx/js/internal");
const jsonc_parser_1 = require("jsonc-parser");
const config_file_1 = require("../../utils/config/config-file");
const FORMATTING_OPTIONS = {
formattingOptions: { keepLines: true, insertSpaces: true, tabSize: 2 },
};
// A jest config mentioning any of these uses swc/babel/angular (or is the root
// aggregator), not ts-jest, so skip it.
const NON_TS_JEST_MARKERS = [
'@swc/jest',
'babel-jest',
'jest-preset-angular',
'getJestProjectsAsync',
];
/**
* NXC-4591: ts-jest 29.2+ uses `moduleResolution: node10` on the CommonJS path
* for TypeScript < 6, which ignores package `exports` and breaks exports-only
* workspace libs (TS2307). `isolatedModules: true` makes ts-jest transpile per
* file, skipping that resolution. TS >= 6 is unaffected.
*/
async function default_1(tree) {
// TS >= 6 resolves `exports` under bundler + commonjs; not affected.
if ((0, internal_1.isTypescriptVersionAtLeast)(tree, '6.0.0')) {
return;
}
// Path-based workspaces resolve via tsconfig `paths`, not `exports`; skip them.
if (!(0, internal_1.isUsingTsSolutionSetup)(tree)) {
return;
}
// Fresh ts-solution workspaces already enable this in the base config.
if (rootEnablesIsolatedModules(tree)) {
return;
}
let count = 0;
for (const [, project] of (0, devkit_1.getProjects)(tree)) {
const specPath = getTsJestSpecTsconfig(tree, project.root);
if (specPath && setIsolatedModules(tree, specPath)) {
count++;
}
}
if (count > 0) {
devkit_1.logger.info(`NXC-4591: set "isolatedModules": true in ${count} tsconfig.spec.json file(s) so ts-jest resolves exports-only workspace libraries on TypeScript < 6.`);
}
await (0, devkit_1.formatFiles)(tree);
}
function rootEnablesIsolatedModules(tree) {
return (readCompilerOption(tree, 'tsconfig.base.json', 'isolatedModules') === true);
}
// The project's tsconfig.spec.json if it runs ts-jest, else null.
function getTsJestSpecTsconfig(tree, root) {
const jestConfig = (0, config_file_1.findJestConfig)(tree, root);
if (!jestConfig) {
return null;
}
const source = tree.read(jestConfig, 'utf-8') ?? '';
if (NON_TS_JEST_MARKERS.some((marker) => source.includes(marker))) {
return null;
}
const specPath = (0, devkit_1.joinPathFragments)(root, 'tsconfig.spec.json');
return tree.exists(specPath) ? specPath : null;
}
function setIsolatedModules(tree, tsconfigPath) {
if (readCompilerOption(tree, tsconfigPath, 'isolatedModules') === true) {
return false;
}
const contents = tree.read(tsconfigPath, 'utf-8');
if (!contents) {
return false;
}
const edits = (0, jsonc_parser_1.modify)(contents, ['compilerOptions', 'isolatedModules'], true, FORMATTING_OPTIONS);
tree.write(tsconfigPath, (0, jsonc_parser_1.applyEdits)(contents, edits));
return true;
}
function readCompilerOption(tree, tsconfigPath, option) {
const contents = tree.read(tsconfigPath, 'utf-8');
if (!contents) {
return undefined;
}
const root = (0, jsonc_parser_1.parseTree)(contents);
const node = root && (0, jsonc_parser_1.findNodeAtLocation)(root, ['compilerOptions', option]);
return node ? (0, jsonc_parser_1.getNodeValue)(node) : undefined;
}