diff --git a/.gitignore b/.gitignore index a8181d048f..312e410896 100644 --- a/.gitignore +++ b/.gitignore @@ -7,6 +7,7 @@ node_modules e2e/**/.yarn/* examples/**/.yarn/* website/.yarn/* +tmp .idx .vscode !.yarn/patches diff --git a/src/__snapshots__/ng-jest-transformer.spec.ts.snap b/src/__snapshots__/ng-jest-transformer.spec.ts.snap index 169942df83..47279ade65 100644 --- a/src/__snapshots__/ng-jest-transformer.spec.ts.snap +++ b/src/__snapshots__/ng-jest-transformer.spec.ts.snap @@ -4,7 +4,7 @@ exports[`NgJestTransformer should use esbuild to process mjs or \`node_modules\` [ " const pi = parseFloat(3.124); - + export { pi }; ", { @@ -23,7 +23,7 @@ exports[`NgJestTransformer should use esbuild to process mjs or \`node_modules\` [ " const pi = parseFloat(3.124); - + export { pi }; ", { @@ -42,7 +42,7 @@ exports[`NgJestTransformer should use esbuild to process mjs or \`node_modules\` [ " const pi = parseFloat(3.124); - + export { pi }; ", { @@ -61,7 +61,7 @@ exports[`NgJestTransformer should use esbuild to process mjs or \`node_modules\` [ " const pi = parseFloat(3.124); - + export { pi }; ", { @@ -80,7 +80,7 @@ exports[`NgJestTransformer should use esbuild to process mjs or \`node_modules\` [ " const pi = parseFloat(3.124); - + export { pi }; ", { @@ -99,7 +99,7 @@ exports[`NgJestTransformer should use esbuild to process mjs or \`node_modules\` [ " const pi = parseFloat(3.124); - + export { pi }; ", { @@ -118,7 +118,7 @@ exports[`NgJestTransformer should use esbuild to process mjs or \`node_modules\` [ " const pi = parseFloat(3.124); - + export { pi }; ", { @@ -137,7 +137,7 @@ exports[`NgJestTransformer should use esbuild to process mjs or \`node_modules\` [ " const pi = parseFloat(3.124); - + export { pi }; ", { @@ -156,7 +156,7 @@ exports[`NgJestTransformer should use esbuild to process mjs or \`node_modules\` [ " const pi = parseFloat(3.124); - + export { pi }; ", { @@ -175,7 +175,7 @@ exports[`NgJestTransformer should use esbuild to process mjs or \`node_modules\` [ " const pi = parseFloat(3.124); - + export { pi }; ", { @@ -194,7 +194,7 @@ exports[`NgJestTransformer should use esbuild to process mjs or \`node_modules\` [ " const pi = parseFloat(3.124); - + export { pi }; ", { @@ -213,7 +213,7 @@ exports[`NgJestTransformer should use esbuild to process mjs or \`node_modules\` [ " const pi = parseFloat(3.124); - + export { pi }; ", { diff --git a/src/ng-jest-transformer.spec.ts b/src/ng-jest-transformer.spec.ts index 752c02fbb4..48477bfc41 100644 --- a/src/ng-jest-transformer.spec.ts +++ b/src/ng-jest-transformer.spec.ts @@ -1,6 +1,7 @@ import { transformSync } from 'esbuild'; import { TsJestTransformer } from 'ts-jest'; +import packageJson from '../package.json'; import { NgJestCompiler } from './compiler/ng-jest-compiler'; import { NgJestConfig } from './config/ng-jest-config'; import { NgJestTransformer } from './ng-jest-transformer'; @@ -49,7 +50,7 @@ describe('NgJestTransformer', () => { tr.process( ` const pi = parseFloat(3.124); - + export { pi }; `, 'foo.js', @@ -73,7 +74,7 @@ describe('NgJestTransformer', () => { tr.process( ` const pi = parseFloat(3.124); - + export { pi }; `, 'node_modules/tslib.es6.js', @@ -125,7 +126,7 @@ describe('NgJestTransformer', () => { tr.process( ` const pi = parseFloat(3.124); - + export { pi }; `, 'foo.mjs', @@ -134,7 +135,7 @@ describe('NgJestTransformer', () => { tr.process( ` const pi = parseFloat(3.124); - + export { pi }; `, 'node_modules/foo.js', @@ -184,7 +185,7 @@ describe('NgJestTransformer', () => { tr.process( ` const pi = parseFloat(3.124); - + export { pi }; `, 'foo.mjs', @@ -193,7 +194,7 @@ describe('NgJestTransformer', () => { tr.process( ` const pi = parseFloat(3.124); - + export { pi }; `, 'node_modules/foo.js', @@ -205,4 +206,25 @@ describe('NgJestTransformer', () => { mockedTransformSync.mockClear(); }); + + it('should include version from package.json', async () => { + const transformCfg = { + config: { + cwd: process.cwd(), + extensionsToTreatAsEsm: [], + testMatch: [], + testRegex: [], + }, + } as any; // eslint-disable-line @typescript-eslint/no-explicit-any + const tr = new NgJestTransformer({ + isolatedModules: true, + }); + + packageJson.version = '1.0.0'; + const cacheKey1 = tr.getCacheKey('export const foo = 1', 'file1.ts', transformCfg); + packageJson.version = '2.0.0'; + const cacheKey2 = tr.getCacheKey('export const foo = 1', 'file1.ts', transformCfg); + + expect(cacheKey1).not.toBe(cacheKey2); + }); }); diff --git a/src/ng-jest-transformer.ts b/src/ng-jest-transformer.ts index 687172c431..ed3720631c 100644 --- a/src/ng-jest-transformer.ts +++ b/src/ng-jest-transformer.ts @@ -1,3 +1,5 @@ +import { createHash } from 'node:crypto'; + import type { TransformedSource } from '@jest/transform'; import { LogContexts, LogLevels, type Logger, createLogger } from 'bs-logger'; import { transformSync } from 'esbuild'; @@ -6,6 +8,41 @@ import { type TsJestTransformerOptions, ConfigSet, TsJestTransformer, type TsJes import { NgJestCompiler } from './compiler/ng-jest-compiler'; import { NgJestConfig } from './config/ng-jest-config'; +// stores hashes made out of only one argument being a string +const cache: Record = {}; + +type DataItem = string | Buffer; + +const sha1 = (...data: DataItem[]): string => { + const canCache = data.length === 1 && typeof data[0] === 'string'; + // caching + let cacheKey!: string; + if (canCache) { + cacheKey = data[0] as string; + if (cacheKey in cache) { + return cache[cacheKey]; + } + } + + // we use SHA1 because it's the fastest provided by node + // and we are not concerned about security here + const hash = createHash('sha1'); + data.forEach((item) => { + if (typeof item === 'string') { + hash.update(item, 'utf8'); + } else { + hash.update(item); + } + }); + const res = hash.digest('hex').toString(); + + if (canCache) { + cache[cacheKey] = res; + } + + return res; +}; + export class NgJestTransformer extends TsJestTransformer { readonly #ngJestLogger: Logger; @@ -15,8 +52,7 @@ export class NgJestTransformer extends TsJestTransformer { context: { [LogContexts.package]: 'jest-preset-angular', [LogContexts.logLevel]: LogLevels.trace, - // eslint-disable-next-line @typescript-eslint/no-require-imports - version: require('../package.json').version, + version: this.version, }, targets: process.env.NG_JEST_LOG ?? undefined, }); @@ -30,6 +66,11 @@ export class NgJestTransformer extends TsJestTransformer { this._compiler = new NgJestCompiler(configSet, cacheFS); } + private get version(): string { + // eslint-disable-next-line @typescript-eslint/no-require-imports + return require('../package.json').version; + } + process(fileContent: string, filePath: string, transformOptions: TsJestTransformOptions): TransformedSource { // @ts-expect-error we are accessing the private cache to avoid creating new objects all the time const configSet = super._configsFor(transformOptions); @@ -55,4 +96,8 @@ export class NgJestTransformer extends TsJestTransformer { return super.process(fileContent, filePath, transformOptions); } } + + getCacheKey(fileContent: string, filePath: string, transformOptions: TsJestTransformOptions): string { + return sha1(super.getCacheKey(fileContent, filePath, transformOptions), this.version); + } } diff --git a/tsconfig.json b/tsconfig.json index 4593769bf6..7e0cc02bd0 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -11,6 +11,7 @@ "noImplicitReturns": true, "skipLibCheck": true, "esModuleInterop": true, + "resolveJsonModule": true, "moduleResolution": "Node", "target": "ES2015", "module": "CommonJS",