From 6403a6c575fd974d796a9148323b8d080a720ba2 Mon Sep 17 00:00:00 2001 From: Emily Xiong Date: Fri, 27 Sep 2024 13:03:10 -0400 Subject: [PATCH] chore(testing): add deprecated comment for getJestProjects --- packages/jest/migrations.json | 6 ++ ...Projects-with-getJestProjectsAsync.spec.ts | 65 +++++++++++++ ...tJestProjects-with-getJestProjectsAsync.ts | 94 +++++++++++++++++++ .../src/utils/config/get-jest-projects.ts | 2 + packages/nx/src/project-graph/file-utils.ts | 4 +- 5 files changed, 169 insertions(+), 2 deletions(-) create mode 100644 packages/jest/src/migrations/update-20-0-0/replace-getJestProjects-with-getJestProjectsAsync.spec.ts create mode 100644 packages/jest/src/migrations/update-20-0-0/replace-getJestProjects-with-getJestProjectsAsync.ts diff --git a/packages/jest/migrations.json b/packages/jest/migrations.json index 12bddd7d972d7..8fb3f04fb9af8 100644 --- a/packages/jest/migrations.json +++ b/packages/jest/migrations.json @@ -16,6 +16,12 @@ "version": "17.1.0-beta.2", "description": "Move jest executor options to nx.json targetDefaults", "implementation": "./src/migrations/update-17-1-0/move-options-to-target-defaults" + }, + "replace-getJestProjects-with-getJestProjectsAsync": { + "cli": "nx", + "version": "20.0.0-beta.5", + "description": "replace getJestProjects with getJestProjectsAsync", + "implementation": "./src/migrations/update-20-0-0/replace-getJestProjects-with-getJestProjectsAsync" } }, "packageJsonUpdates": { diff --git a/packages/jest/src/migrations/update-20-0-0/replace-getJestProjects-with-getJestProjectsAsync.spec.ts b/packages/jest/src/migrations/update-20-0-0/replace-getJestProjects-with-getJestProjectsAsync.spec.ts new file mode 100644 index 0000000000000..e654118a65884 --- /dev/null +++ b/packages/jest/src/migrations/update-20-0-0/replace-getJestProjects-with-getJestProjectsAsync.spec.ts @@ -0,0 +1,65 @@ +import { Tree } from '@nx/devkit'; +import { createTree } from '@nx/devkit/testing'; +import update from './replace-getJestProjects-with-getJestProjectsAsync'; + +describe('replace-getJestProjects-with-getJestProjectsAsync', () => { + let tree: Tree; + + beforeEach(() => { + tree = createTree(); + }); + + it('should replace getJestProjects with getJestProjectsAsync', async () => { + tree.write( + 'jest.config.ts', + ` + const { getJestProjects } = require('@nx/jest'); + + module.exports = { + projects: getJestProjects(), + }; + ` + ); + await update(tree); + const updatedJestConfig = tree.read('jest.config.ts')?.toString(); + expect(updatedJestConfig).toMatchInlineSnapshot(` + " + const { getJestProjectsAsync } = require('@nx/jest'); + + export default async () => ({ + projects: await getJestProjectsAsync(), + }); + " + `); + }); + + it('should replace getJestProjects with getJestProjectsAsync with additonal properties', async () => { + tree.write( + 'jest.config.ts', + ` + const { getJestProjects } = require('@nx/jest'); + + module.exports = { + projects: getJestProjects(), + filename: __filename, + env: process.env, + dirname: __dirname + }; + ` + ); + await update(tree); + const updatedJestConfig = tree.read('jest.config.ts')?.toString(); + expect(updatedJestConfig).toMatchInlineSnapshot(` + " + const { getJestProjectsAsync } = require('@nx/jest'); + + export default async () => ({ + projects: await getJestProjectsAsync(), + filename: __filename, + env: process.env, + dirname: __dirname + }); + " + `); + }); +}); diff --git a/packages/jest/src/migrations/update-20-0-0/replace-getJestProjects-with-getJestProjectsAsync.ts b/packages/jest/src/migrations/update-20-0-0/replace-getJestProjects-with-getJestProjectsAsync.ts new file mode 100644 index 0000000000000..ae005222f1cdc --- /dev/null +++ b/packages/jest/src/migrations/update-20-0-0/replace-getJestProjects-with-getJestProjectsAsync.ts @@ -0,0 +1,94 @@ +// go through the jest.config files +// see if it imports from @nx/jest and if it uses getJestProjects +// replace getJestProjects with getJestProjectsAsync + +import { globAsync, Tree } from '@nx/devkit'; +import { ensureTypescript } from '@nx/js/src/utils/typescript/ensure-typescript'; +import { BinaryExpression, ExpressionStatement } from 'typescript'; + +let tsModule: typeof import('typescript'); + +export default async function update(tree: Tree) { + if (!tsModule) { + tsModule = ensureTypescript(); + } + + const jestConfigPaths = await globAsync(tree, [ + '**/jest.config.{cjs,mjs,js,cts,mts,ts}', + ]); + jestConfigPaths.forEach((jestConfigPath) => { + const oldContent = tree.read(jestConfigPath).toString(); + if (oldContent?.includes('projects: getJestProjects()')) { + let sourceFile = tsModule.createSourceFile( + jestConfigPath, + oldContent, + tsModule.ScriptTarget.Latest, + true + ); + + // find the import statement for @nx/jest + const importStatement = sourceFile.statements.find( + (statement) => + tsModule.isVariableStatement(statement) && + statement.declarationList.declarations.some( + (declaration) => + tsModule.isCallExpression(declaration.initializer) && + tsModule.isIdentifier(declaration.initializer.expression) && + declaration.initializer.expression.escapedText === 'require' && + tsModule.isStringLiteral(declaration.initializer.arguments[0]) && + declaration.initializer.arguments[0].text === '@nx/jest' + ) + ); + if (importStatement) { + // find export statement with `projects: getJestProjects()` + let exportStatement = sourceFile.statements.find( + (statement) => + tsModule.isExpressionStatement(statement) && + tsModule.isBinaryExpression(statement.expression) && + tsModule.isPropertyAccessExpression(statement.expression.left) && + tsModule.isObjectLiteralExpression(statement.expression.right) && + statement.expression.operatorToken.kind === + tsModule.SyntaxKind.EqualsToken && + tsModule.isIdentifier(statement.expression.left.expression) && + statement.expression.left.expression.escapedText === 'module' && + tsModule.isIdentifier(statement.expression.left.name) && + statement.expression.left.name.escapedText === 'exports' && + statement.expression.right.properties.some( + (property) => + tsModule.isPropertyAssignment(property) && + tsModule.isIdentifier(property.name) && + property.name.escapedText === 'projects' && + tsModule.isCallExpression(property.initializer) && + tsModule.isIdentifier(property.initializer.expression) && + property.initializer.expression.escapedText === + 'getJestProjects' + ) + ) as ExpressionStatement; + + if (exportStatement) { + // replace getJestProjects with getJestProjectsAsync in export statement + const rightExpression = ( + exportStatement.expression as BinaryExpression + ).right.getText(); + const newExpression = rightExpression.replace( + 'getJestProjects()', + 'await getJestProjectsAsync()' + ); + const newStatement = `export default async () => (${newExpression});`; + let newContent = oldContent.replace( + exportStatement.getText(), + newStatement + ); + + // replace getJestProjects with getJestProjectsAsync in import statement + newContent = newContent.replace( + 'getJestProjects', + 'getJestProjectsAsync' + ); + + tree.write(jestConfigPath, newContent); + } + } + } + }); +} diff --git a/packages/jest/src/utils/config/get-jest-projects.ts b/packages/jest/src/utils/config/get-jest-projects.ts index f06422b493716..d9c68aa0e8072 100644 --- a/packages/jest/src/utils/config/get-jest-projects.ts +++ b/packages/jest/src/utils/config/get-jest-projects.ts @@ -12,6 +12,8 @@ function getJestConfigProjectPath(projectJestConfigPath: string): string { } /** + * TODO(v21): Remove this function + * @deprecated To get projects use {@link getJestProjectsAsync} instead. This will be removed in v21. * Get a list of paths to all the jest config files * using the Nx Jest executor. * diff --git a/packages/nx/src/project-graph/file-utils.ts b/packages/nx/src/project-graph/file-utils.ts index 6f609ae6f22e7..bc2ba06557505 100644 --- a/packages/nx/src/project-graph/file-utils.ts +++ b/packages/nx/src/project-graph/file-utils.ts @@ -135,8 +135,8 @@ function defaultReadFileAtRevision( } /** - * TODO(v20): Remove this function - * @deprecated To get projects use {@link retrieveProjectConfigurations} instead. This will be removed in v20. + * TODO(v21): Remove this function + * @deprecated To get projects use {@link retrieveProjectConfigurations} instead. This will be removed in v21. */ export function readWorkspaceConfig(opts: { format: 'angularCli' | 'nx';