From f5668698f8fab78b3008d936aa5001f134f530e2 Mon Sep 17 00:00:00 2001 From: Ahn <27772165+ahnpnl@users.noreply.github.com> Date: Tue, 26 Apr 2022 00:40:58 +0200 Subject: [PATCH] feat: remove `path-mapping` AST transformer (#3455) BREAKING CHANGE `path-mapping` AST transformer is no longer shipped in `ts-jest` v28. Please use an alternative one like https://github.com/LeDDGroup/typescript-transform-paths instead. --- .../__snapshots__/path-mapping.spec.ts.snap | 65 ------ src/transformers/path-mapping.spec.ts | 132 ------------ src/transformers/path-mapping.ts | 190 ------------------ .../options/astTransformers.md | 47 ----- 4 files changed, 434 deletions(-) delete mode 100644 src/transformers/__snapshots__/path-mapping.spec.ts.snap delete mode 100644 src/transformers/path-mapping.spec.ts delete mode 100644 src/transformers/path-mapping.ts diff --git a/src/transformers/__snapshots__/path-mapping.spec.ts.snap b/src/transformers/__snapshots__/path-mapping.spec.ts.snap deleted file mode 100644 index a2ff3ec250..0000000000 --- a/src/transformers/__snapshots__/path-mapping.spec.ts.snap +++ /dev/null @@ -1,65 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`path-mapping should replace alias path with relative path which is resolved from paths tsconfig with custom extensions 1`] = ` -"import styles from \\"../../../utils/json.css\\"; -console.log(styles); -" -`; - -exports[`path-mapping should replace alias path with relative path which is resolved from paths tsconfig with custom extensions 2`] = ` -"import jsonData from \\"../../../utils/json.json\\"; -console.log(jsonData); -" -`; - -exports[`path-mapping should replace alias path with relative path which is resolved from paths tsconfig with custom extensions 3`] = ` -"import vueComponent from \\"../../../utils/json.vue\\"; -console.log(vueComponent); -" -`; - -exports[`path-mapping should replace alias path with relative path which is resolved from paths tsconfig with js/ts extension 1`] = ` -"\\"use strict\\"; -Object.defineProperty(exports, \\"__esModule\\", { value: true }); -exports.jsonUtils = void 0; -var json_1 = require(\\"./src/utils/json.ts\\"); -var json_2 = require(\\"./src/utils/json.ts\\"); -var json = require(\\"./src/utils/json.ts\\"); -exports.jsonUtils = require(\\"./src/utils/json.ts\\"); -var stringify = require(\\"./src/utils/json.ts\\").stringify; -var foo = require(\\"./src/utils/json.ts\\"); -Promise.resolve().then(function () { return require(\\"./src/utils/json.ts\\"); }).then(function (module) { - module.parse('{foo:1}'); -}); -var json_3 = require(\\"./src/utils/json.ts\\"); -stringify({ foo: 1 }); -(0, json_1.parse)('{foo:1}'); -console.log(json); -console.log(foo); -var bar = { foo: 2 }; -json_2.default.foo(1); -" -`; - -exports[`path-mapping should replace alias path with relative path which is resolved from paths tsconfig with js/ts extension 2`] = ` -"\\"use strict\\"; -Object.defineProperty(exports, \\"__esModule\\", { value: true }); -exports.jsonUtils = void 0; -var json_1 = require(\\"./src/utils/json.ts\\"); -var json_2 = require(\\"./src/utils/json.ts\\"); -var json = require(\\"./src/utils/json.ts\\"); -exports.jsonUtils = require(\\"./src/utils/json.ts\\"); -var stringify = require(\\"./src/utils/json.ts\\").stringify; -var foo = require(\\"./src/utils/json.ts\\"); -Promise.resolve().then(function () { return require(\\"./src/utils/json.ts\\"); }).then(function (module) { - module.parse('{foo:1}'); -}); -var json_3 = require(\\"./src/utils/json.ts\\"); -stringify({ foo: 1 }); -(0, json_1.parse)('{foo:1}'); -console.log(json); -console.log(foo); -var bar = { foo: 2 }; -json_2.default.foo(1); -" -`; diff --git a/src/transformers/path-mapping.spec.ts b/src/transformers/path-mapping.spec.ts deleted file mode 100644 index e4a4104bb6..0000000000 --- a/src/transformers/path-mapping.spec.ts +++ /dev/null @@ -1,132 +0,0 @@ -import path from 'path' - -import ts from 'typescript' - -import { createConfigSet, makeCompiler } from '../__helpers__/fakers' -import { TsCompiler } from '../legacy/compiler' -import { normalizeSlashes } from '../utils/normalize-slashes' - -import { factory as pathMapping, name, version } from './path-mapping' - -const TS_JS_CODE_WITH_PATH_ALIAS = ` - import { parse } from '@utils/json' - import hoo from '@utils/json' - import * as json from '@utils/json' - export * as jsonUtils from '@utils/json' - const { stringify } = require('@utils/json') - import foo = require('@utils/json') - import('@utils/json').then(module => { - module.parse('{foo:1}') - }) - import type { Foo} from '@utils/json' - stringify({ foo: 1 }) - parse('{foo:1}') - console.log(json) - console.log(foo) - const bar: Foo = { foo: 2 } - hoo.foo(1) -` - -const printer = ts.createPrinter() - -describe('path-mapping', () => { - test('should have correct transformer name and version', () => { - expect(name).toBe('path-mapping') - expect(version).toBe(2) - }) - - test.each([ - { - baseUrl: '.', - paths: { - '@utils/*': ['src/utils/*'], - }, - }, - { - rootDirs: ['./', 'foo'], - baseUrl: '.', - paths: { - '@utils/*': ['src/utils/*'], - }, - }, - ])( - 'should replace alias path with relative path which is resolved from paths tsconfig with js/ts extension', - (tsconfig) => { - const configSet = createConfigSet({ - tsJestConfig: { - tsconfig, - }, - }) - const createFactory = () => pathMapping(new TsCompiler(configSet, new Map())) - const transpile = (source: string) => ts.transpileModule(source, { transformers: { before: [createFactory()] } }) - jest.spyOn(ts, 'resolveModuleName').mockReturnValue({ - resolvedModule: { - resolvedFileName: require.resolve('../utils/json'), - extension: 'ts', - } as any, // eslint-disable-line @typescript-eslint/no-explicit-any - }) - const out = transpile(TS_JS_CODE_WITH_PATH_ALIAS) - - expect(normalizeSlashes(out.outputText).replace(/\/\//g, '/')).toMatchSnapshot() - - jest.resetAllMocks() - }, - ) - - test.each([ - { - code: ` - import styles from '@utils/app.css' - - console.log(styles) - `, - extension: 'css', - }, - { - code: ` - import jsonData from '@utils/data.json' - - console.log(jsonData) - `, - extension: 'json', - }, - { - code: ` - import vueComponent from '@utils/component.vue' - - console.log(vueComponent) - `, - extension: 'vue', - }, - ])( - 'should replace alias path with relative path which is resolved from paths tsconfig with custom extensions', - ({ code, extension }) => { - const resolvedFileNameStub = path.join('..', `utils/json.${extension}`) - jest.spyOn(ts, 'resolveModuleName').mockReturnValue({ - resolvedModule: { - resolvedFileName: resolvedFileNameStub, - extension, - } as any, // eslint-disable-line @typescript-eslint/no-explicit-any - }) - const sourceFile = ts.createSourceFile(__filename, code, ts.ScriptTarget.ES2015) - const result = ts.transform(sourceFile, [ - pathMapping( - makeCompiler({ - tsJestConfig: { - tsconfig: { - baseUrl: '.', - paths: { - '@utils/*': ['src/utils/*'], - }, - }, - }, - }), - ), - ]) - - const transformedSourceFile = result.transformed[0] - - expect(printer.printFile(transformedSourceFile).replace(/\\\\/g, '/')).toMatchSnapshot() - }, - ) -}) diff --git a/src/transformers/path-mapping.ts b/src/transformers/path-mapping.ts deleted file mode 100644 index db0fe91351..0000000000 --- a/src/transformers/path-mapping.ts +++ /dev/null @@ -1,190 +0,0 @@ -/** - * This transformer is heavily inspired from: - * - https://github.com/LeDDGroup/typescript-transform-paths - * - https://github.com/dropbox/ts-transform-import-path-rewrite - * Thank you: @longlho, @LeddGroup for all the great works - */ -import { basename, dirname, isAbsolute, join, normalize, relative } from 'path' - -import { LogContexts, LogLevels } from 'bs-logger' -import type * as _ts from 'typescript' - -import type { TsCompilerInstance } from '../types' - -/** - * Remember to increase the version whenever transformer's content is changed. This is to inform Jest to not reuse - * the previous cache which contains old transformer's content - */ -export const version = 2 -// Used for constructing cache key -export const name = 'path-mapping' - -const isBaseDir = (base: string, dir: string) => !relative(base, dir)?.startsWith('.') - -/** - * The factory of import path alias transformer factory. - */ -export function factory({ - configSet, -}: TsCompilerInstance): (ctx: _ts.TransformationContext) => _ts.Transformer<_ts.SourceFile> { - const logger = configSet.logger.child({ namespace: name }) - logger.warn( - 'path-mapping AST transformer is deprecated and will be removed in `ts-jest` v28. Please use an alternative one, like https://github.com/LeDDGroup/typescript-transform-paths instead', - ) - const ts = configSet.compilerModule - const tsFactory = ts.factory ? ts.factory : ts - const compilerOptions = configSet.parsedTsConfig.options - const rootDirs = compilerOptions.rootDirs?.filter(isAbsolute) - - const isDynamicImport = (node: _ts.Node): node is _ts.CallExpression => - ts.isCallExpression(node) && node.expression.kind === ts.SyntaxKind.ImportKeyword - const isRequire = (node: _ts.Node): node is _ts.CallExpression => - ts.isCallExpression(node) && - ts.isIdentifier(node.expression) && - node.expression.text === 'require' && - ts.isStringLiteral(node.arguments[0]) && - node.arguments.length === 1 - - const createVisitor = (ctx: _ts.TransformationContext, sf: _ts.SourceFile) => { - const { fileName } = sf - const fileDir = normalize(dirname(fileName)) - const rewritePath = (importPath: string): string => { - let p = importPath - const { resolvedModule } = ts.resolveModuleName(importPath, fileName, compilerOptions, ts.sys) - if (resolvedModule) { - const { resolvedFileName } = resolvedModule - let filePath = fileDir - let modulePath = dirname(resolvedFileName) - /* Handle rootDirs mapping */ - if (rootDirs) { - let fileRootDir = '' - let moduleRootDir = '' - for (const rootDir of rootDirs) { - if (isBaseDir(rootDir, resolvedFileName) && rootDir.length > moduleRootDir.length) moduleRootDir = rootDir - if (isBaseDir(rootDir, fileName) && rootDir.length > fileRootDir.length) fileRootDir = rootDir - } - /* Remove base dirs to make relative to root */ - if (fileRootDir && moduleRootDir) { - filePath = relative(fileRootDir, filePath) - modulePath = relative(moduleRootDir, modulePath) - } - } - p = normalize(join(relative(filePath, modulePath), basename(resolvedFileName))) - p = p.startsWith('.') ? p : `./${p}` - } - - return p - } - const visitor: _ts.Visitor = (node) => { - let rewrittenPath: string | undefined - const newNode = ts.getMutableClone(node) - /** - * e.g. - * - import('@utils/json') - * - const { stringify } = require('@utils/json') - */ - if (isDynamicImport(node) || isRequire(node)) { - rewrittenPath = rewritePath((node.arguments[0] as _ts.StringLiteral).text) - const argumentArrays = tsFactory.createNodeArray([tsFactory.createStringLiteral(rewrittenPath)]) - - return ts.factory - ? ts.factory.updateCallExpression(node, node.expression, node.typeArguments, argumentArrays) - : ts.updateCall(node, node.expression, node.typeArguments, argumentArrays) - } - // legacy import, e.g. import foo = require('@utils/json') - if (ts.isExternalModuleReference(node) && ts.isStringLiteral(node.expression)) { - rewrittenPath = rewritePath(node.expression.text) - - return tsFactory.updateExternalModuleReference( - newNode as _ts.ExternalModuleReference, - tsFactory.createStringLiteral(rewrittenPath), - ) - } - /** - * e.g. - * - import { parse } from '@utils/json' - * - import * as json from '@utils/json' - */ - if (ts.isImportDeclaration(node) && ts.isStringLiteral(node.moduleSpecifier)) { - rewrittenPath = rewritePath(node.moduleSpecifier.text) - - return +ts.versionMajorMinor >= 4.5 - ? tsFactory.updateImportDeclaration( - node, - node.decorators, - node.modifiers, - node.importClause, - tsFactory.createStringLiteral(rewrittenPath), - node.assertClause, - ) - : // @ts-expect-error ts < 4.5 doesn't have last argument - tsFactory.updateImportDeclaration( - node, - node.decorators, - node.modifiers, - node.importClause, - tsFactory.createStringLiteral(rewrittenPath), - ) - } - // e.g. export * as jsonUtils from '@utils/json' - if (ts.isExportDeclaration(node) && node.moduleSpecifier && ts.isStringLiteral(node.moduleSpecifier)) { - rewrittenPath = rewritePath(node.moduleSpecifier.text) - const stringLiteralNode = tsFactory.createStringLiteral(rewrittenPath) - if (ts.factory) { - return +ts.versionMajorMinor >= 4.5 - ? ts.factory.updateExportDeclaration( - node, - node.decorators, - node.modifiers, - node.isTypeOnly, - node.exportClause, - stringLiteralNode, - node.assertClause, - ) - : // @ts-expect-error ts < 4.5 doesn't have last argument - ts.factory.updateExportDeclaration( - node, - node.decorators, - node.modifiers, - node.isTypeOnly, - node.exportClause, - stringLiteralNode, - ) - } else { - return ts.updateExportDeclaration( - node, - node.decorators, - node.modifiers, - node.exportClause, - stringLiteralNode, - node.isTypeOnly, - ) - } - } - // 3.8 import type, e.g. import type { Foo } from '@utils/json' - if ( - ts.isImportTypeNode(node) && - ts.isLiteralTypeNode(node.argument) && - ts.isStringLiteral(node.argument.literal) - ) { - // `.text` instead of `getText` bc this node doesn't map to sf (it's generated d.ts) - rewrittenPath = rewritePath(node.argument.literal.text) - const importArguments = tsFactory.createLiteralTypeNode(tsFactory.createStringLiteral(rewrittenPath)) - - return tsFactory.updateImportTypeNode(node, importArguments, node.qualifier, node.typeArguments, node.isTypeOf) - } - - return ts.visitEachChild(node, visitor, ctx) - } - - return visitor - } - - // returns the transformer factory - return (ctx: _ts.TransformationContext): _ts.Transformer<_ts.SourceFile> => - logger.wrap( - { [LogContexts.logLevel]: LogLevels.debug, call: null }, - 'visitSourceFileNode(): path mapping', - (sf: _ts.SourceFile) => ts.visitNode(sf, createVisitor(ctx, sf)), - ) -} diff --git a/website/docs/getting-started/options/astTransformers.md b/website/docs/getting-started/options/astTransformers.md index 152a3fd6e5..c3cd3bf08d 100644 --- a/website/docs/getting-started/options/astTransformers.md +++ b/website/docs/getting-started/options/astTransformers.md @@ -88,53 +88,6 @@ module.exports = { } ``` -### Public transformers - -`ts-jest` is able to expose transformers for public usage to provide the possibility to opt-in/out for users. Currently -the exposed transformers are: - -- `path-mapping` convert alias import/export to relative import/export path base on `paths` in `tsconfig`. - This transformer works similar to `moduleNameMapper` in `jest.config.js`. When using this transformer, one might not need - `moduleNameMapper` anymore. - -:::warning - -`path-mapping` AST transformer is now deprecated. Please should use an alternative one like https://github.com/LeDDGroup/typescript-transform-paths instead. - -::: - -#### Example of opt-in transformers - -```js -// jest.config.js -module.exports = { - // [...] - globals: { - 'ts-jest': { - astTransformers: { - before: ['ts-jest/dist/transformers/path-mapping'], - }, - }, - }, -} -``` - -```json -// OR package.json -{ - // [...] - "jest": { - "globals": { - "ts-jest": { - "astTransformers": { - "before": ["ts-jest/dist/transformers/path-mapping"] - } - } - } - } -} -``` - ### Writing custom TypeScript AST transformers To write a custom TypeScript AST transformers, one can take a look at [the one](https://github.com/kulshekhar/ts-jest/tree/main/src/transformers) that `ts-jest` is using.