UNPKG

@nx/jest

Version:

The Nx Plugin for Jest contains executors and generators allowing your workspace to use the powerful Jest testing capabilities.

122 lines (121 loc) 5 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.default = rewriteJestProjectGenerator; exports.rewriteJestProjectGeneratorUsage = rewriteJestProjectGeneratorUsage; const devkit_1 = require("@nx/devkit"); const TS_EXTENSIONS = ['.ts', '.tsx', '.cts', '.mts']; const PACKAGE = '@nx/jest'; const OLD_NAME = 'jestProjectGenerator'; const NEW_NAME = 'configurationGenerator'; let ts; async function rewriteJestProjectGenerator(tree) { let touchedCount = 0; (0, devkit_1.visitNotIgnoredFiles)(tree, '.', (filePath) => { if (!TS_EXTENSIONS.some((ext) => filePath.endsWith(ext))) { return; } const original = tree.read(filePath, 'utf-8'); if (!original || !original.includes(OLD_NAME) || !original.includes(PACKAGE)) { return; } const updated = rewriteJestProjectGeneratorUsage(original); if (updated !== original) { tree.write(filePath, updated); touchedCount += 1; } }); if (touchedCount > 0) { devkit_1.logger.info(`Replaced the deprecated \`${OLD_NAME}\` with \`${NEW_NAME}\` in ${touchedCount} file(s).`); } await (0, devkit_1.formatFiles)(tree); } function rewriteJestProjectGeneratorUsage(source) { ts ??= (0, devkit_1.ensurePackage)('typescript', '*'); const sourceFile = ts.createSourceFile('tmp.ts', source, ts.ScriptTarget.Latest, /* setParentNodes */ true, ts.ScriptKind.TSX); // `jestProjectGenerator as alias` — only the imported name changes. let aliasedSpecifier; // `jestProjectGenerator` — the local binding is the old name itself. let plainSpecifier; // Whether `configurationGenerator` is already a binding in the file, which // means a plain rename would produce a duplicate import specifier. let newNameAlreadyBound = false; for (const stmt of sourceFile.statements) { if (!ts.isImportDeclaration(stmt)) continue; if (!ts.isStringLiteral(stmt.moduleSpecifier) || stmt.moduleSpecifier.text !== PACKAGE) { continue; } const named = stmt.importClause?.namedBindings; if (!named || !ts.isNamedImports(named)) continue; for (const spec of named.elements) { const importedName = (spec.propertyName ?? spec.name).text; if (importedName === OLD_NAME) { if (spec.propertyName) { aliasedSpecifier = spec; } else { plainSpecifier = spec; } } if (importedName === NEW_NAME || spec.name.text === NEW_NAME) { newNameAlreadyBound = true; } } } if (!aliasedSpecifier && !plainSpecifier) { return source; } const changes = []; // `jestProjectGenerator as alias` -> `configurationGenerator as alias`. // Call sites use `alias`, so only the imported name is rewritten. if (aliasedSpecifier?.propertyName) { replaceNode(sourceFile, aliasedSpecifier.propertyName, NEW_NAME, changes); } if (plainSpecifier) { if (newNameAlreadyBound) { // A plain rename would collide with the existing `configurationGenerator` // binding, so alias the import and leave the call sites untouched. replaceNode(sourceFile, plainSpecifier.name, `${NEW_NAME} as ${OLD_NAME}`, changes); } else { // Rename the import binding and every bare-identifier usage in the file. for (const id of collectBareIdentifiers(sourceFile)) { replaceNode(sourceFile, id, NEW_NAME, changes); } } } return changes.length > 0 ? (0, devkit_1.applyChangesToString)(source, changes) : source; } /** * Collect every `jestProjectGenerator` identifier that is a standalone * reference — the import binding and its call sites — while skipping member * accesses like `someObject.jestProjectGenerator`, which are unrelated. */ function collectBareIdentifiers(sourceFile) { const found = []; const visit = (node) => { if (ts.isIdentifier(node) && node.text === OLD_NAME) { const parent = node.parent; const isMemberName = parent && ts.isPropertyAccessExpression(parent) && parent.name === node; const isQualifiedName = parent && ts.isQualifiedName(parent) && parent.right === node; if (!isMemberName && !isQualifiedName) { found.push(node); } } ts.forEachChild(node, visit); }; visit(sourceFile); return found; } function replaceNode(sourceFile, node, text, changes) { const start = node.getStart(sourceFile); const end = node.getEnd(); changes.push({ type: devkit_1.ChangeType.Delete, start, length: end - start }, { type: devkit_1.ChangeType.Insert, index: start, text }); }