diff --git a/package.json b/package.json
index 056c6e9807..551af7b7f9 100644
--- a/package.json
+++ b/package.json
@@ -35,7 +35,7 @@
"dependencies": {
"jest-environment-jsdom": "27.0.0-next.1",
"pretty-format": "27.0.0-next.1",
- "ts-jest": "^27.0.0-next.5"
+ "ts-jest": "^27.0.0-next.6"
},
"peerDependencies": {
"@angular-devkit/build-angular": ">=0.901.12",
diff --git a/src/__tests__/__mocks__/foo.component.ts b/src/__tests__/__mocks__/foo.component.ts
deleted file mode 100644
index 4290a3cdf1..0000000000
--- a/src/__tests__/__mocks__/foo.component.ts
+++ /dev/null
@@ -1,9 +0,0 @@
-import { Component } from '@angular/core';
-
-@Component({
- selector: 'foo-root',
- template: `
Hello World !!
`,
-})
-export class FooComponent {
- title: number = 'test-app-v10';
-}
diff --git a/src/__tests__/__mocks__/foo.service.ts b/src/__tests__/__mocks__/foo.service.ts
deleted file mode 100644
index bed2626d6f..0000000000
--- a/src/__tests__/__mocks__/foo.service.ts
+++ /dev/null
@@ -1,9 +0,0 @@
-import { Injectable } from '@angular/core';
-
-export class ClassInject {}
-
-@Injectable()
-export class MyService {
- // eslint-disable-next-line
- constructor(_v: ClassInject) {}
-}
diff --git a/src/__tests__/__mocks__/foo.spec.ts b/src/__tests__/__mocks__/foo.spec.ts
deleted file mode 100644
index 29818aa937..0000000000
--- a/src/__tests__/__mocks__/foo.spec.ts
+++ /dev/null
@@ -1,7 +0,0 @@
-import { jest } from '@jest/globals';
-
-import { getFoo } from './foo';
-
-jest.mock('./foo');
-
-console.log(getFoo());
diff --git a/src/__tests__/__mocks__/foo.ts b/src/__tests__/__mocks__/foo.ts
deleted file mode 100644
index d39beb2f5c..0000000000
--- a/src/__tests__/__mocks__/foo.ts
+++ /dev/null
@@ -1,3 +0,0 @@
-export function getFoo(): string {
- return 'foo';
-}
diff --git a/src/__tests__/__mocks__/forward-ref.ts b/src/__tests__/__mocks__/forward-ref.ts
deleted file mode 100644
index 0d8817c254..0000000000
--- a/src/__tests__/__mocks__/forward-ref.ts
+++ /dev/null
@@ -1,14 +0,0 @@
-import { forwardRef, Inject } from '@angular/core';
-
-export class Door {
- lock: Lock;
-
- // Door attempts to inject Lock, despite it not being defined yet.
- // forwardRef makes this possible.
- constructor(@Inject(forwardRef(() => Lock)) lock: Lock) {
- this.lock = lock;
- }
-}
-
-// Only at this point Lock is defined.
-export class Lock {}
diff --git a/src/__tests__/__snapshots__/downlevel-ctor.spec.ts.snap b/src/__tests__/__snapshots__/downlevel-ctor.spec.ts.snap
deleted file mode 100644
index fd11978f99..0000000000
--- a/src/__tests__/__snapshots__/downlevel-ctor.spec.ts.snap
+++ /dev/null
@@ -1,47 +0,0 @@
-// Jest Snapshot v1, https://goo.gl/fbAQLP
-
-exports[`Downlevel ctor transformer should use downlevel ctor transformer from Angular for isolatedModules false/true 1`] = `
-"\\"use strict\\";
-Object.defineProperty(exports, \\"__esModule\\", { value: true });
-exports.Lock = exports.Door = void 0;
-const core_1 = require(\\"@angular/core\\");
-class Door {
- // Door attempts to inject Lock, despite it not being defined yet.
- // forwardRef makes this possible.
- constructor(lock) {
- this.lock = lock;
- }
-}
-exports.Door = Door;
-Door.ctorParameters = () => [
- { type: Lock, decorators: [{ type: core_1.Inject, args: [core_1.forwardRef(() => Lock),] }] }
-];
-// Only at this point Lock is defined.
-class Lock {
-}
-exports.Lock = Lock;
-//# "
-`;
-
-exports[`Downlevel ctor transformer should use downlevel ctor transformer from Angular for isolatedModules false/true 2`] = `
-"\\"use strict\\";
-Object.defineProperty(exports, \\"__esModule\\", { value: true });
-exports.Lock = exports.Door = void 0;
-const core_1 = require(\\"@angular/core\\");
-class Door {
- // Door attempts to inject Lock, despite it not being defined yet.
- // forwardRef makes this possible.
- constructor(lock) {
- this.lock = lock;
- }
-}
-exports.Door = Door;
-Door.ctorParameters = () => [
- { type: Lock, decorators: [{ type: core_1.Inject, args: [core_1.forwardRef(() => Lock),] }] }
-];
-// Only at this point Lock is defined.
-class Lock {
-}
-exports.Lock = Lock;
-//# "
-`;
diff --git a/src/__tests__/__snapshots__/hoisting.spec.ts.snap b/src/__tests__/__snapshots__/hoisting.spec.ts.snap
deleted file mode 100644
index 7adcdee381..0000000000
--- a/src/__tests__/__snapshots__/hoisting.spec.ts.snap
+++ /dev/null
@@ -1,21 +0,0 @@
-// Jest Snapshot v1, https://goo.gl/fbAQLP
-
-exports[`Hoisting should hoist correctly with isolatedModules false 1`] = `
-"\\"use strict\\";
-Object.defineProperty(exports, \\"__esModule\\", { value: true });
-const globals_1 = require(\\"@jest/globals\\");
-globals_1.jest.mock('./foo');
-const foo_1 = require(\\"./foo\\");
-console.log(foo_1.getFoo());
-//# "
-`;
-
-exports[`Hoisting should hoist correctly with isolatedModules true 1`] = `
-"\\"use strict\\";
-Object.defineProperty(exports, \\"__esModule\\", { value: true });
-const globals_1 = require(\\"@jest/globals\\");
-globals_1.jest.mock('./foo');
-const foo_1 = require(\\"./foo\\");
-console.log(foo_1.getFoo());
-//# "
-`;
diff --git a/src/__tests__/__snapshots__/ng-jest-compiler.spec.ts.snap b/src/__tests__/__snapshots__/ng-jest-compiler.spec.ts.snap
index 146be42f6f..39bccf0224 100644
--- a/src/__tests__/__snapshots__/ng-jest-compiler.spec.ts.snap
+++ b/src/__tests__/__snapshots__/ng-jest-compiler.spec.ts.snap
@@ -1,69 +1,41 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
-exports[`NgJestCompiler with isolatedModule false should compile codes with useESM true 1`] = `
-"import { __decorate } from \\"tslib\\";
-import __NG_CLI_RESOURCE__0 from \\"./app.component.html\\";
-import { Component } from '@angular/core';
-let AppComponent = class AppComponent {
- constructor() {
- this.title = 'test-app-v10';
- }
-};
-AppComponent = __decorate([
- Component({
- selector: 'app-root',
- template: __NG_CLI_RESOURCE__0,
- styles: []
- })
-], AppComponent);
-export { AppComponent };
-//# "
+exports[`NgJestCompiler with isolatedModules false should transform codes with useESM false using hoisting, replace resources and downlevel ctor transformers 1`] = `
+Object {
+ "before": Array [
+ [Function],
+ [Function],
+ [Function],
+ ],
+}
`;
-exports[`NgJestCompiler with isolatedModule false should compile new code when file changes: from hasErrorFileContent 1`] = `
-"\\"use strict\\";
-Object.defineProperty(exports, \\"__esModule\\", { value: true });
-exports.AppComponent = void 0;
-const tslib_1 = require(\\"tslib\\");
-const core_1 = require(\\"@angular/core\\");
-let = class {
- constructor() {
- this. = ;
- }
-};
- = tslib_1.__decorate([
- core_1.Component({
- selector: 'app-root',
- template: require(\\"./app.component.html\\"),
- styles: []
- })
-], );
-exports.AppComponent = ;
-//# "
+exports[`NgJestCompiler with isolatedModules false should transform codes with useESM true using hoisting, replace resources and downlevel ctor transformers 1`] = `
+Object {
+ "before": Array [
+ [Function],
+ [Function],
+ [Function],
+ ],
+}
`;
-exports[`NgJestCompiler with isolatedModule false should compile new code when file changes: from noErrorFileContent 1`] = `
-"\\"use strict\\";
-Object.defineProperty(exports, \\"__esModule\\", { value: true });
-exports.AppComponent = void 0;
-const tslib_1 = require(\\"tslib\\");
-const core_1 = require(\\"@angular/core\\");
-let AppComponent = class AppComponent {
- constructor() {
- this.title = 'test-app-v10';
- }
-};
-AppComponent = tslib_1.__decorate([
- core_1.Component({
- selector: 'app-root',
- template: require(\\"./app.component.html\\"),
- styles: []
- })
-], AppComponent);
-exports.AppComponent = AppComponent;
-//# "
+exports[`NgJestCompiler with isolatedModules true should transform codes with useESM false using hoisting, replace resources and downlevel ctor transformers 1`] = `
+Object {
+ "before": Array [
+ [Function],
+ [Function],
+ [Function],
+ ],
+}
`;
-exports[`NgJestCompiler with isolatedModule false should throw diagnostics error for new file which is: known by Program 1`] = `"src/__tests__/__mocks__/foo.component.ts(8,3): error TS2322: Type 'string' is not assignable to type 'number'."`;
-
-exports[`NgJestCompiler with isolatedModule false should throw diagnostics error for new file which is: not known by Program 1`] = `"src/__tests__/__mocks__/foo.component.ts(8,3): error TS2322: Type 'string' is not assignable to type 'number'."`;
+exports[`NgJestCompiler with isolatedModules true should transform codes with useESM true using hoisting, replace resources and downlevel ctor transformers 1`] = `
+Object {
+ "before": Array [
+ [Function],
+ [Function],
+ [Function],
+ ],
+}
+`;
diff --git a/src/__tests__/__snapshots__/replace-resources.spec.ts.snap b/src/__tests__/__snapshots__/replace-resources.spec.ts.snap
deleted file mode 100644
index 853eaf6e61..0000000000
--- a/src/__tests__/__snapshots__/replace-resources.spec.ts.snap
+++ /dev/null
@@ -1,85 +0,0 @@
-// Jest Snapshot v1, https://goo.gl/fbAQLP
-
-exports[`Replace resources transformer with isolatedModules false should use replaceResources transformer from @angular/compiler-cli with useESM false 1`] = `
-"\\"use strict\\";
-Object.defineProperty(exports, \\"__esModule\\", { value: true });
-exports.AppComponent = void 0;
-const tslib_1 = require(\\"tslib\\");
-const core_1 = require(\\"@angular/core\\");
-let AppComponent = class AppComponent {
- constructor() {
- this.title = 'test-app-v10';
- }
-};
-AppComponent = tslib_1.__decorate([
- core_1.Component({
- selector: 'app-root',
- template: require(\\"./app.component.html\\"),
- styles: []
- })
-], AppComponent);
-exports.AppComponent = AppComponent;
-//# "
-`;
-
-exports[`Replace resources transformer with isolatedModules false should use replaceResources transformer from @angular/compiler-cli with useESM true 1`] = `
-"import { __decorate } from \\"tslib\\";
-import __NG_CLI_RESOURCE__0 from \\"./app.component.html\\";
-import { Component } from '@angular/core';
-let AppComponent = class AppComponent {
- constructor() {
- this.title = 'test-app-v10';
- }
-};
-AppComponent = __decorate([
- Component({
- selector: 'app-root',
- template: __NG_CLI_RESOURCE__0,
- styles: []
- })
-], AppComponent);
-export { AppComponent };
-//# "
-`;
-
-exports[`Replace resources transformer with isolatedModules true should use replaceResources transformer from @angular/compiler-cli with useESM false 1`] = `
-"\\"use strict\\";
-Object.defineProperty(exports, \\"__esModule\\", { value: true });
-exports.AppComponent = void 0;
-const tslib_1 = require(\\"tslib\\");
-const core_1 = require(\\"@angular/core\\");
-let AppComponent = class AppComponent {
- constructor() {
- this.title = 'test-app-v10';
- }
-};
-AppComponent = tslib_1.__decorate([
- core_1.Component({
- selector: 'app-root',
- template: require(\\"./app.component.html\\"),
- styles: []
- })
-], AppComponent);
-exports.AppComponent = AppComponent;
-//# "
-`;
-
-exports[`Replace resources transformer with isolatedModules true should use replaceResources transformer from @angular/compiler-cli with useESM true 1`] = `
-"import { __decorate } from \\"tslib\\";
-import __NG_CLI_RESOURCE__0 from \\"./app.component.html\\";
-import { Component } from '@angular/core';
-let AppComponent = class AppComponent {
- constructor() {
- this.title = 'test-app-v10';
- }
-};
-AppComponent = __decorate([
- Component({
- selector: 'app-root',
- template: __NG_CLI_RESOURCE__0,
- styles: []
- })
-], AppComponent);
-export { AppComponent };
-//# "
-`;
diff --git a/src/__tests__/downlevel-ctor.spec.ts b/src/__tests__/downlevel-ctor.spec.ts
deleted file mode 100644
index 83774f77a0..0000000000
--- a/src/__tests__/downlevel-ctor.spec.ts
+++ /dev/null
@@ -1,38 +0,0 @@
-import { readFileSync } from 'fs';
-import { join } from 'path';
-
-import { SOURCE_MAPPING_PREFIX } from 'ts-jest/dist/compiler/compiler-utils';
-
-import { NgJestCompiler } from '../compiler/ng-jest-compiler';
-import { NgJestConfig } from '../config/ng-jest-config';
-
-import { jestCfgStub } from './__helpers__/test-constants';
-import { mockFolder } from './__helpers__/test-helpers';
-
-describe('Downlevel ctor transformer', () => {
- const fileName = join(mockFolder, 'forward-ref.ts');
- const fileContent = readFileSync(fileName, 'utf-8');
-
- test.each([true, false])(
- 'should use downlevel ctor transformer from Angular for isolatedModules false/true',
- (isolatedModules) => {
- const ngJestConfig = new NgJestConfig({
- ...jestCfgStub,
- globals: {
- // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
- 'ts-jest': {
- ...jestCfgStub.globals['ts-jest'],
- isolatedModules,
- },
- },
- });
- const compiler = new NgJestCompiler(ngJestConfig, new Map());
-
- // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
- const emittedResult = compiler.getCompiledOutput(fileName, fileContent, false)!;
-
- // Source map is different based on file location which can fail on CI, so we only compare snapshot for js
- expect(emittedResult.substring(0, emittedResult.indexOf(SOURCE_MAPPING_PREFIX))).toMatchSnapshot();
- },
- );
-});
diff --git a/src/__tests__/hoisting.spec.ts b/src/__tests__/hoisting.spec.ts
deleted file mode 100644
index 53034432b0..0000000000
--- a/src/__tests__/hoisting.spec.ts
+++ /dev/null
@@ -1,33 +0,0 @@
-import { readFileSync } from 'fs';
-import { join } from 'path';
-
-import { SOURCE_MAPPING_PREFIX } from 'ts-jest/dist/compiler/compiler-utils';
-
-import { NgJestCompiler } from '../compiler/ng-jest-compiler';
-import { NgJestConfig } from '../config/ng-jest-config';
-
-import { jestCfgStub } from './__helpers__/test-constants';
-import { mockFolder } from './__helpers__/test-helpers';
-
-describe('Hoisting', () => {
- // Verify if we use `ts-jest` hoisting transformer
- test.each([true, false])('should hoist correctly with isolatedModules %p', (isolatedModules) => {
- const ngJestConfig = new NgJestConfig({
- ...jestCfgStub,
- globals: {
- 'ts-jest': {
- ...jestCfgStub.globals['ts-jest'],
- isolatedModules,
- },
- },
- });
- const fileName = join(mockFolder, 'foo.spec.ts');
- const compiler = new NgJestCompiler(ngJestConfig, new Map());
-
- // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
- const emittedResult = compiler.getCompiledOutput(fileName, readFileSync(fileName, 'utf-8'), false)!;
-
- // Source map is different based on file location which can fail on CI, so we only compare snapshot for js
- expect(emittedResult.substring(0, emittedResult.indexOf(SOURCE_MAPPING_PREFIX))).toMatchSnapshot();
- });
-});
diff --git a/src/__tests__/index.spec.ts b/src/__tests__/index.spec.ts
index 3ba5c7e42c..3c87eb5f67 100644
--- a/src/__tests__/index.spec.ts
+++ b/src/__tests__/index.spec.ts
@@ -1,137 +1,12 @@
-import { jest } from '@jest/globals';
-import { TsJestTransformer } from 'ts-jest/dist/ts-jest-transformer';
-
-import { NgJestCompiler } from '../compiler/ng-jest-compiler';
+import * as ngJest from '..';
import { NgJestTransformer } from '../ng-jest-transformer';
-describe('NgJestTransformer', () => {
- describe('_configsFor', () => {
- test(
- 'should return the same config set for same values with different jest config objects' +
- ' but their serialized versions are the same',
- () => {
- const obj1 = {
- config: { cwd: process.cwd(), extensionsToTreatAsEsm: [], globals: {}, testMatch: [], testRegex: [] },
- cacheFS: new Map(),
- };
- const obj2 = { ...obj1, config: { ...obj1.config, globals: {} } };
- // @ts-expect-error testing purpose
- const cs1 = new NgJestTransformer()._configsFor(obj1);
- // @ts-expect-error testing purpose
- const cs2 = new NgJestTransformer()._configsFor(obj2);
-
- expect(cs2).toBe(cs1);
- },
- );
-
- test('should return the same config set for same values with jest config objects', () => {
- const obj1 = {
- config: { cwd: process.cwd(), extensionsToTreatAsEsm: [], globals: {}, testMatch: [], testRegex: [] },
- cacheFS: new Map(),
- };
- const obj2 = { ...obj1 };
- // @ts-expect-error testing purpose
- const cs1 = new NgJestTransformer()._configsFor(obj1);
- // @ts-expect-error testing purpose
- const cs2 = new NgJestTransformer()._configsFor(obj2);
-
- expect(cs2).toBe(cs1);
- });
- });
-
- describe('getCacheKey', () => {
- test('should call getCacheKey method from parent class TsJestTransformer', () => {
- TsJestTransformer.prototype.getCacheKey = jest.fn();
- const input = {
- fileContent: 'export default "foo"',
- fileName: 'foo.ts',
- jestConfigStr: '{"foo": "bar"}',
- // eslint-disable-next-line
- options: { config: { foo: 'bar' } as any, instrument: false, rootDir: '/foo' },
- };
- const tr = new NgJestTransformer();
- // @ts-expect-error testing purpose
- tr.getCacheKey(input.fileContent, input.fileName, input.jestConfigStr, input.options);
-
- // eslint-disable-next-line @typescript-eslint/unbound-method
- expect(TsJestTransformer.prototype.getCacheKey).toHaveBeenCalledWith(
- input.fileContent,
- input.fileName,
- input.jestConfigStr,
- input.options,
- );
- });
- });
-
- describe('process', () => {
- const baseJestCfg = {
- cwd: './',
- testMatch: ['**/__tests__/**/*.[jt]s?(x)', '**/?(*.)+(spec|test).[jt]s?(x)'],
- testRegex: ['(/__tests__/.*|(\\\\.|/)(test|spec))\\\\.[jt]sx?$'],
- extensionsToTreatAsEsm: [],
- };
-
- beforeEach(() => {
- jest.spyOn(NgJestCompiler.prototype, 'getCompiledOutput').mockReturnValueOnce('');
- });
-
- afterEach(() => {
- jest.restoreAllMocks();
- });
-
- test.each(['foo.ts', 'foo.js'])('should compile ts or js with allowJs by NgJestCompiler', (fileName) => {
- const jestCfg = {
- ...baseJestCfg,
- globals: { 'ts-jest': { tsconfig: { allowJs: true } } },
- };
- const input = {
- fileContent: 'const foo = 1',
- // eslint-disable-next-line
- options: { config: { ...jestCfg } as any, instrument: false, rootDir: '/foo', cacheFS: new Map(), },
- };
- const ngJestTransformer = new NgJestTransformer();
-
- // @ts-expect-error testing purpose
- ngJestTransformer.process(input.fileContent, fileName, input.options);
-
- // eslint-disable-next-line @typescript-eslint/unbound-method
- expect(NgJestCompiler.prototype.getCompiledOutput).toHaveBeenCalledWith(fileName, input.fileContent, undefined);
- });
-
- test.each([
- {
- fileName: 'foo.html',
- fileContent: 'Hello world
',
- },
- {
- fileName: 'foo.js',
- fileContent: 'const foo = 1',
- },
- {
- fileName: 'foo.d.ts',
- fileContent: 'type foo = number',
- },
- ])('should compile other files with ts-jest', ({ fileName, fileContent }) => {
- const jestCfg = {
- ...baseJestCfg,
- globals: {
- 'ts-jest': {
- tsconfig: { allowJs: false },
- stringifyContentPathRegex: '\\.html$',
- },
- },
- };
- const input = {
- // eslint-disable-next-line
- options: { config: { ...jestCfg } as any, instrument: false, rootDir: '/foo', cacheFS: new Map(), },
- };
- const ngJestTransformer = new NgJestTransformer();
-
- // @ts-expect-error testing purpose
- ngJestTransformer.process(fileContent, fileName, input.options);
-
- // eslint-disable-next-line @typescript-eslint/unbound-method
- expect(NgJestCompiler.prototype.getCompiledOutput).not.toHaveBeenCalled();
- });
+describe('createTransformer', () => {
+ it('should create different instances', () => {
+ const tr1 = ngJest.createTransformer();
+ const tr2 = ngJest.createTransformer();
+ expect(tr1).toBeInstanceOf(NgJestTransformer);
+ expect(tr2).toBeInstanceOf(NgJestTransformer);
+ expect(tr1).not.toBe(tr2);
});
});
diff --git a/src/__tests__/ng-jest-compiler.spec.ts b/src/__tests__/ng-jest-compiler.spec.ts
index 764076ac59..e952f4fbfa 100644
--- a/src/__tests__/ng-jest-compiler.spec.ts
+++ b/src/__tests__/ng-jest-compiler.spec.ts
@@ -2,8 +2,6 @@ import { readFileSync } from 'fs';
import { join } from 'path';
import { jest } from '@jest/globals';
-import { SOURCE_MAPPING_PREFIX } from 'ts-jest/dist/compiler/compiler-utils';
-import ts from 'typescript';
import { NgJestCompiler } from '../compiler/ng-jest-compiler';
import { NgJestConfig } from '../config/ng-jest-config';
@@ -11,8 +9,11 @@ import { NgJestConfig } from '../config/ng-jest-config';
import { jestCfgStub } from './__helpers__/test-constants';
import { mockFolder } from './__helpers__/test-helpers';
+const fileName = join(mockFolder, 'app.component.ts');
+const fileContent = readFileSync(fileName, 'utf-8');
+
describe('NgJestCompiler', () => {
- describe('with isolatedModules true', () => {
+ describe.each([true, false])('with isolatedModules %p', (isolatedModules) => {
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
const baseJestCfg = {
...jestCfgStub,
@@ -20,183 +21,36 @@ describe('NgJestCompiler', () => {
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
'ts-jest': {
...jestCfgStub.globals['ts-jest'],
- isolatedModules: true,
+ isolatedModules,
+ tsconfig: {
+ esModuleInterop: false,
+ allowSyntheticDefaultImports: false,
+ },
},
},
};
- test.each([true, false])('should call transpileModule with useESM %p', (useESM) => {
- const ngJestConfig = new NgJestConfig({
- ...baseJestCfg,
- globals: {
- // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
- 'ts-jest': {
- ...baseJestCfg.globals['ts-jest'],
- useESM,
- tsconfig: {
- esModuleInterop: false,
- allowSyntheticDefaultImports: false,
+ test.each([true, false])(
+ 'should transform codes with useESM %p using hoisting, replace resources and downlevel ctor transformers',
+ (useESM) => {
+ const ngJestConfig = new NgJestConfig({
+ ...baseJestCfg,
+ globals: {
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
+ 'ts-jest': {
+ ...baseJestCfg.globals['ts-jest'],
+ useESM,
},
},
- },
- });
- const fileName = join(mockFolder, 'foo.service.ts');
- const compiler = new NgJestCompiler(ngJestConfig, new Map());
- // @ts-expect-error testing purpose
- compiler._transpileModule = jest.fn().mockReturnValueOnce({
- outputText: 'var foo = 1',
- diagnostics: [],
- sourceMapText: '{}',
- });
-
- // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
- compiler.getCompiledOutput(fileName, readFileSync(fileName, 'utf-8'), useESM)!;
-
- // @ts-expect-error testing purpose
- expect(compiler._transpileModule).toHaveBeenCalled();
- // @ts-expect-error testing purpose
- const { module, esModuleInterop, allowSyntheticDefaultImports } = compiler._transpileModule.mock.calls[0][1]
- .compilerOptions as ts.CompilerOptions;
- if (useESM) {
- expect(module).not.toEqual(ts.ModuleKind.CommonJS);
- // verify if allowSyntheticDefaultImports is hardcoded to true
- expect(allowSyntheticDefaultImports).toEqual(true);
- // verify if esModuleInterop is hardcoded to true
- expect(esModuleInterop).toEqual(true);
- } else {
- expect(module).toEqual(ts.ModuleKind.CommonJS);
- }
- // @ts-expect-error _initialCompilerOptions is a private property
- expect(compiler._initialCompilerOptions.esModuleInterop).not.toEqual(true);
- // @ts-expect-error _initialCompilerOptions is a private property
- expect(compiler._initialCompilerOptions.allowSyntheticDefaultImports).not.toEqual(true);
- });
- });
-
- describe('with isolatedModule false', () => {
- const sourceMapStub =
- '{"version":3,"file":"app.component.js","sourceRoot":"","sources":["app.component.ts"],"names":[],"mappings":";;;;AAAA,wCAA0C;IAO7B,YAAY,SAAZ,YAAY;;QACvB,UAAK,GAAG,cAAc,CAAC;IACzB,CAAC;CAAA,CAAA;AAFY,YAAY;IALxB,gBAAS,CAAC;QACT,QAAQ,EAAE,UAAU;QACpB,WAAW,EAAE,sBAAsB;QACnC,SAAS,EAAE,CAAC,sBAAsB,CAAC;KACpC,CAAC;GACW,YAAY,CAExB;AAFY,oCAAY","sourcesContent":["import { Component } from \'@angular/core\';\\n\\n@Component({\\n selector: \'app-root\',\\n templateUrl: \'./app.component.html\',\\n styleUrls: [\'./app.component.scss\'],\\n})\\nexport class AppComponent {\\n title = \'test-app-v10\';\\n}\\n"]}';
- const compiledOutputStub =
- '"use strict";\n' +
- 'Object.defineProperty(exports, "__esModule", { value: true });\n' +
- 'exports.AppComponent = void 0;\n' +
- 'const tslib_1 = require("tslib");\n' +
- 'const core_1 = require("@angular/core");\n' +
- 'let AppComponent = class AppComponent {\n' +
- ' constructor() {\n' +
- " this.title = 'test-app-v10';\n" +
- ' }\n' +
- '};\n' +
- 'AppComponent = tslib_1.__decorate([\n' +
- ' core_1.Component({\n' +
- " selector: 'app-root',\n" +
- " templateUrl: './app.component.html',\n" +
- " styleUrls: ['./app.component.scss'],\n" +
- ' })\n' +
- '], AppComponent);\n' +
- 'exports.AppComponent = AppComponent;\n' +
- '//# sourceMappingURL=app.component.js.map\n';
-
- const noErrorFileName = join(mockFolder, 'app.component.ts');
- const noErrorFileContent = readFileSync(noErrorFileName, 'utf-8');
- const hasErrorFileName = join(mockFolder, 'foo.component.ts');
- const hasErrorFileContent = readFileSync(hasErrorFileName, 'utf-8');
-
- test.each([noErrorFileName, undefined])(
- 'should return compiled result for new file which is not known or known by Program',
- (fileName) => {
- const ngJestConfig = new NgJestConfig(jestCfgStub);
- ngJestConfig.parsedTsConfig = {
- ...ngJestConfig.parsedTsConfig,
- rootNames: fileName ? [fileName] : [],
- };
+ });
const compiler = new NgJestCompiler(ngJestConfig, new Map());
- // @ts-expect-error bypass type checking to access private property
- compiler._tsHost.getEmittedResult = jest.fn().mockReturnValueOnce([compiledOutputStub, sourceMapStub]);
+ // @ts-expect-error testing purpose
+ const makeTransformersSpy = jest.spyOn(compiler, '_makeTransformers');
- // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
- compiler.getCompiledOutput(noErrorFileName, noErrorFileContent, false)!;
+ compiler.getCompiledOutput(fileContent, fileName, true);
- // @ts-expect-error bypass type checking to access private property
- // eslint-disable-next-line @typescript-eslint/unbound-method
- expect(compiler._tsHost.getEmittedResult).toHaveBeenCalled();
+ expect(makeTransformersSpy.mock.results[0].value).toMatchSnapshot();
},
);
-
- test('should compile new code when file changes', () => {
- const ngJestConfig = new NgJestConfig(jestCfgStub);
- const compiler = new NgJestCompiler(ngJestConfig, new Map());
-
- // Compile the same file with 2 versions of content: noErrorFileContent and hasErrorFileContent
- const fileName = noErrorFileName;
- const emittedResult1 = compiler.getCompiledOutput(fileName, noErrorFileContent, true);
- expect(emittedResult1.substring(0, emittedResult1.indexOf(SOURCE_MAPPING_PREFIX))).toMatchSnapshot(
- 'from noErrorFileContent',
- );
- const emittedResult2 = compiler.getCompiledOutput(fileName, hasErrorFileContent, true);
- expect(emittedResult2.substring(0, emittedResult2.indexOf(SOURCE_MAPPING_PREFIX))).toMatchSnapshot(
- 'from hasErrorFileContent',
- );
-
- expect(emittedResult1).not.toEqual(emittedResult2);
- });
-
- test('should compile codes with useESM true', () => {
- const ngJestConfig = new NgJestConfig({
- ...jestCfgStub,
- globals: {
- // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
- 'ts-jest': {
- ...jestCfgStub.globals['ts-jest'],
- useESM: true,
- tsconfig: {
- esModuleInterop: false,
- allowSyntheticDefaultImports: false,
- },
- },
- },
- });
- const compiler = new NgJestCompiler(ngJestConfig, new Map());
-
- // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
- const emittedResult = compiler.getCompiledOutput(noErrorFileName, noErrorFileContent, true)!;
-
- // Source map is different based on file location which can fail on CI, so we only compare snapshot for js
- expect(emittedResult.substring(0, emittedResult.indexOf(SOURCE_MAPPING_PREFIX))).toMatchSnapshot();
- // @ts-expect-error _compilerOptions is a private property
- expect(compiler._compilerOptions.esModuleInterop).toEqual(true);
- // @ts-expect-error _compilerOptions is a private property
- expect(compiler._compilerOptions.allowSyntheticDefaultImports).toEqual(true);
- // @ts-expect-error _initialCompilerOptions is a private property
- expect(compiler._initialCompilerOptions.esModuleInterop).not.toEqual(true);
- // @ts-expect-error _initialCompilerOptions is a private property
- expect(compiler._initialCompilerOptions.allowSyntheticDefaultImports).not.toEqual(true);
- });
-
- test.each([hasErrorFileName, undefined])('should throw diagnostics error for new file which is', (fileName) => {
- const ngJestConfig = new NgJestConfig(jestCfgStub);
- ngJestConfig.parsedTsConfig = {
- ...ngJestConfig.parsedTsConfig,
- rootNames: fileName ? [fileName] : [],
- };
- const compiler = new NgJestCompiler(ngJestConfig, new Map());
-
- expect(() =>
- compiler.getCompiledOutput(hasErrorFileName, hasErrorFileContent, false),
- ).toThrowErrorMatchingSnapshot(fileName ? 'known by Program' : 'not known by Program');
- });
-
- test('should not throw diagnostics error when shouldReportDiagnostics return false', () => {
- const ngJestConfig = new NgJestConfig(jestCfgStub);
- ngJestConfig.parsedTsConfig = {
- ...ngJestConfig.parsedTsConfig,
- rootNames: [hasErrorFileName],
- };
- // @ts-expect-error testing purpose
- ngJestConfig.shouldReportDiagnostics = jest.fn().mockReturnValueOnce(false);
- const compiler = new NgJestCompiler(ngJestConfig, new Map());
-
- expect(() => compiler.getCompiledOutput(hasErrorFileName, hasErrorFileContent, false)).not.toThrowError();
- });
});
});
diff --git a/src/__tests__/ng-jest-transformer.spec.ts b/src/__tests__/ng-jest-transformer.spec.ts
new file mode 100644
index 0000000000..7c64720bde
--- /dev/null
+++ b/src/__tests__/ng-jest-transformer.spec.ts
@@ -0,0 +1,86 @@
+import { jest } from '@jest/globals';
+import { TsJestTransformer } from 'ts-jest/dist/ts-jest-transformer';
+
+import { NgJestTransformer } from '../ng-jest-transformer';
+
+describe('NgJestTransformer', () => {
+ describe('_configsFor', () => {
+ test(
+ 'should return the same config set for same values with different jest config objects' +
+ ' but their serialized versions are the same',
+ () => {
+ const obj1 = {
+ config: { cwd: process.cwd(), extensionsToTreatAsEsm: [], globals: {}, testMatch: [], testRegex: [] },
+ cacheFS: new Map(),
+ };
+ const obj2 = { ...obj1, config: { ...obj1.config, globals: {} } };
+ // @ts-expect-error testing purpose
+ const cs1 = new NgJestTransformer()._configsFor(obj1);
+ // @ts-expect-error testing purpose
+ const cs2 = new NgJestTransformer()._configsFor(obj2);
+
+ expect(cs2).toBe(cs1);
+ },
+ );
+
+ test('should return the same config set for same values with jest config objects', () => {
+ const obj1 = {
+ config: { cwd: process.cwd(), extensionsToTreatAsEsm: [], globals: {}, testMatch: [], testRegex: [] },
+ cacheFS: new Map(),
+ };
+ const obj2 = { ...obj1 };
+ // @ts-expect-error testing purpose
+ const cs1 = new NgJestTransformer()._configsFor(obj1);
+ // @ts-expect-error testing purpose
+ const cs2 = new NgJestTransformer()._configsFor(obj2);
+
+ expect(cs2).toBe(cs1);
+ });
+ });
+
+ describe('getCacheKey', () => {
+ test('should call getCacheKey method from parent class TsJestTransformer', () => {
+ TsJestTransformer.prototype.getCacheKey = jest.fn();
+ const input = {
+ fileContent: 'export default "foo"',
+ fileName: 'foo.ts',
+ jestConfigStr: '{"foo": "bar"}',
+ // eslint-disable-next-line
+ options: { config: { foo: 'bar' } as any, instrument: false, rootDir: '/foo' },
+ };
+ const tr = new NgJestTransformer();
+ // @ts-expect-error testing purpose
+ tr.getCacheKey(input.fileContent, input.fileName, input.jestConfigStr, input.options);
+
+ // eslint-disable-next-line @typescript-eslint/unbound-method
+ expect(TsJestTransformer.prototype.getCacheKey).toHaveBeenCalledWith(
+ input.fileContent,
+ input.fileName,
+ input.jestConfigStr,
+ input.options,
+ );
+ });
+ });
+
+ describe('process', () => {
+ test('should call getCacheKey method from parent class TsJestTransformer', () => {
+ TsJestTransformer.prototype.process = jest.fn();
+ const input = {
+ fileContent: 'export default "foo"',
+ fileName: 'foo.ts',
+ // eslint-disable-next-line
+ options: { config: { foo: 'bar' } as any, instrument: false, rootDir: '/foo' },
+ };
+ const tr = new NgJestTransformer();
+ // @ts-expect-error testing purpose
+ tr.process(input.fileContent, input.fileName, input.options);
+
+ // eslint-disable-next-line @typescript-eslint/unbound-method
+ expect(TsJestTransformer.prototype.process).toHaveBeenCalledWith(
+ input.fileContent,
+ input.fileName,
+ input.options,
+ );
+ });
+ });
+});
diff --git a/src/__tests__/replace-resources.spec.ts b/src/__tests__/replace-resources.spec.ts
deleted file mode 100644
index 1a67ad8612..0000000000
--- a/src/__tests__/replace-resources.spec.ts
+++ /dev/null
@@ -1,40 +0,0 @@
-import { readFileSync } from 'fs';
-import { join } from 'path';
-
-import { SOURCE_MAPPING_PREFIX } from 'ts-jest/dist/compiler/compiler-utils';
-
-import { NgJestCompiler } from '../compiler/ng-jest-compiler';
-import { NgJestConfig } from '../config/ng-jest-config';
-
-import { jestCfgStub } from './__helpers__/test-constants';
-import { mockFolder } from './__helpers__/test-helpers';
-
-describe('Replace resources transformer', () => {
- const fileName = join(mockFolder, 'app.component.ts');
- const fileContent = readFileSync(fileName, 'utf-8');
-
- describe.each([true, false])('with isolatedModules %p', (isolatedModules) => {
- test.each([true, false])(
- 'should use replaceResources transformer from @angular/compiler-cli with useESM %p',
- (useESM) => {
- const ngJestConfig = new NgJestConfig({
- ...jestCfgStub,
- globals: {
- 'ts-jest': {
- ...jestCfgStub.globals['ts-jest'],
- isolatedModules,
- useESM,
- },
- },
- });
- const compiler = new NgJestCompiler(ngJestConfig, new Map());
-
- // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
- const emittedResult = compiler.getCompiledOutput(fileName, fileContent, useESM)!;
-
- // Source map is different based on file location which can fail on CI, so we only compare snapshot for js
- expect(emittedResult.substring(0, emittedResult.indexOf(SOURCE_MAPPING_PREFIX))).toMatchSnapshot();
- },
- );
- });
-});
diff --git a/src/compiler/ng-jest-compiler.ts b/src/compiler/ng-jest-compiler.ts
index b284f310d4..f25618d67b 100644
--- a/src/compiler/ng-jest-compiler.ts
+++ b/src/compiler/ng-jest-compiler.ts
@@ -1,196 +1,32 @@
-import { CompilerHost, CompilerOptions } from '@angular/compiler-cli';
-import { createCompilerHost } from '@angular/compiler-cli/src/transformers/compiler_host';
-import type { Logger } from 'bs-logger';
-import { updateOutput } from 'ts-jest/dist/compiler/compiler-utils';
-import type {
- CompilerInstance,
- TTypeScript,
- ResolvedModulesMap,
- TsJestAstTransformer,
- TsCompilerInstance,
-} from 'ts-jest/dist/types';
+import { TsCompiler } from 'ts-jest/dist/compiler/ts-compiler';
+import type { TsJestAstTransformer, TTypeScript } from 'ts-jest/dist/types';
import type * as ts from 'typescript';
import type { NgJestConfig } from '../config/ng-jest-config';
-import { factory as constructorDownlevelCtor } from '../transformers/downlevel-ctor';
-import { factory as replaceResources } from '../transformers/replace-resources';
+import { constructorDownlevelCtor } from '../transformers/downlevel-ctor';
+import { replaceResources } from '../transformers/replace-resources';
-import { NgJestCompilerHost } from './compiler-host';
-
-interface PatchedTranspileOptions {
- fileName: string;
- compilerOptions: ts.CompilerOptions;
- moduleName?: string;
- renamedDependencies?: ts.MapLike;
-}
-
-export class NgJestCompiler implements CompilerInstance {
- private _compilerOptions!: CompilerOptions;
- private _program: ts.Program | undefined;
- private _compilerHost: CompilerHost | undefined;
- private _tsHost: NgJestCompilerHost | undefined;
- private _rootNames: string[] = [];
- private readonly _initialCompilerOptions: CompilerOptions;
- private readonly _logger: Logger;
- private readonly _ts: TTypeScript;
+export class NgJestCompiler extends TsCompiler {
+ protected readonly _ts: TTypeScript;
constructor(readonly ngJestConfig: NgJestConfig, readonly jestCacheFS: Map) {
- this._logger = this.ngJestConfig.logger;
+ super(ngJestConfig, jestCacheFS);
this._ts = this.ngJestConfig.compilerModule;
- this._initialCompilerOptions = { ...this.ngJestConfig.parsedTsConfig.options };
- this._setupOptions(this.ngJestConfig);
this._logger.debug('created NgJestCompiler');
}
- getResolvedModulesMap(fileContent: string, fileName: string): ResolvedModulesMap {
- this._tsHost?.updateMemoryHost(fileName, fileContent);
-
- // eslint-disable-next-line
- return (this._program?.getSourceFile(fileName) as any)?.resolvedModules;
- }
-
- getCompiledOutput(fileName: string, fileContent: string, supportsStaticESM: boolean): string {
- const customTransformers = this.ngJestConfig.resolvedTransformers;
- let moduleKind = this._initialCompilerOptions.module;
- let esModuleInterop = this._initialCompilerOptions.esModuleInterop;
- let allowSyntheticDefaultImports = this._initialCompilerOptions.allowSyntheticDefaultImports;
- if (supportsStaticESM && this.ngJestConfig.useESM) {
- moduleKind =
- !moduleKind ||
- (moduleKind &&
- ![this._ts.ModuleKind.ES2015, this._ts.ModuleKind.ES2020, this._ts.ModuleKind.ESNext].includes(moduleKind))
- ? this._ts.ModuleKind.ESNext
- : moduleKind;
- // Make sure `esModuleInterop` and `allowSyntheticDefaultImports` true to support import CJS into ESM
- esModuleInterop = true;
- allowSyntheticDefaultImports = true;
- } else {
- moduleKind = this._ts.ModuleKind.CommonJS;
- }
- this._compilerOptions = {
- ...this._compilerOptions,
- allowSyntheticDefaultImports,
- esModuleInterop,
- module: moduleKind,
- };
-
- if (this._program) {
- // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
- this._tsHost!.updateMemoryHost(fileName, fileContent);
-
- let sourceFile: ts.SourceFile | undefined;
- if (!this._rootNames.includes(fileName)) {
- this._logger.debug({ fileName }, 'getCompiledOutput: adding file to rootNames and updating Program');
- this._rootNames = [...this._rootNames, fileName];
- this._createOrUpdateProgram();
- sourceFile = this._program.getSourceFile(fileName);
- } else {
- sourceFile = this._program.getSourceFile(fileName);
- if (sourceFile) {
- const replaceSpan: ts.TextSpan = { start: 0, length: sourceFile.text.length };
- sourceFile.update(fileContent, { span: replaceSpan, newLength: fileContent.length });
- }
- }
-
- this._logger.debug({ fileName }, 'getCompiledOutput: compiling using Program');
-
- const emitResult = this._program.emit(
- sourceFile,
- undefined,
- undefined,
- undefined,
- this.makeTransformers(customTransformers, this._program),
- );
-
- // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
- const compiledOutput: [string, string] = this._tsHost!.getEmittedResult();
- const allDiagnostics: ts.Diagnostic[] = [];
- if (this.ngJestConfig.shouldReportDiagnostics(fileName)) {
- this._logger.debug({ fileName }, 'getCompiledOutput: getting diagnostics');
-
- allDiagnostics.push(
- ...emitResult.diagnostics,
- ...this._program.getSyntacticDiagnostics(sourceFile),
- ...this._program.getSemanticDiagnostics(sourceFile),
- );
- }
- if (!allDiagnostics.length) {
- this._logger.debug({ fileName }, 'getCompiledOutput: update compiled output including inline source map');
-
- return updateOutput(compiledOutput[0], fileName, compiledOutput[1]);
- } else {
- this.ngJestConfig.raiseDiagnostics(allDiagnostics, fileName);
-
- return '';
- }
- } else {
- this._logger.debug({ fileName }, 'getCompiledOutput: compiling as isolated module');
-
- const result: ts.TranspileOutput = this._transpileModule(
- fileContent,
- {
- fileName,
- compilerOptions: this._compilerOptions,
- },
- customTransformers,
- );
- if (result.diagnostics && this.ngJestConfig.shouldReportDiagnostics(fileName)) {
- this.ngJestConfig.raiseDiagnostics(result.diagnostics, fileName, this._logger);
- }
-
- // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
- return updateOutput(result.outputText, fileName, result.sourceMapText!);
- }
- }
-
- private _setupOptions({ parsedTsConfig }: NgJestConfig): void {
- this._logger.debug({ parsedTsConfig }, '_setupOptions: initializing compiler config');
-
- this._compilerOptions = { ...this._initialCompilerOptions };
- this._rootNames = parsedTsConfig.rootNames.filter((rootName) => !this.ngJestConfig.isTestFile(rootName));
- if (this._compilerOptions.strictMetadataEmit) {
- this._logger.warn(
- `Using Angular compiler option 'strictMetadataEmit' for applications might cause undefined behavior.`,
- );
- }
- if (!this.ngJestConfig.isolatedModules) {
- this._logger.debug(
- { compilerOptions: this._compilerOptions },
- '_setupOptions: creating Compiler Host using @angular/compiler-cli createCompilerHost',
- );
-
- this._tsHost = new NgJestCompilerHost(this._logger, this.ngJestConfig, this.jestCacheFS);
- this._compilerHost = createCompilerHost({
- options: this._compilerOptions,
- tsHost: this._tsHost,
- });
- this._createOrUpdateProgram();
- }
- }
-
- private _createOrUpdateProgram(): void {
- const oldTsProgram = this._program;
-
- this._logger.debug(`_createOrUpdateProgram: ${oldTsProgram ? 'update' : 'create'} TypeScript Program`);
-
- this._program = this._ts.createProgram(this._rootNames, this._compilerOptions, this._compilerHost, oldTsProgram);
- }
-
/**
* Copy from https://github.com/microsoft/TypeScript/blob/master/src/services/transpile.ts
* This is required because the exposed function `transpileModule` from TypeScript doesn't allow to access `Program`
* and we need `Program` to be able to use Angular `replace-resources` transformer.
*/
- private _transpileModule(
- fileContent: string,
- transpileOptions: PatchedTranspileOptions,
- customTransformers: TsJestAstTransformer,
- ): ts.TranspileOutput {
+ protected _transpileOutput(fileContent: string, fileName: string): ts.TranspileOutput {
const diagnostics: ts.Diagnostic[] = [];
- const options: ts.CompilerOptions = transpileOptions.compilerOptions
+ const compilerOptions = { ...this._compilerOptions };
+ const options: ts.CompilerOptions = compilerOptions
? // @ts-expect-error internal TypeScript API
- this._ts.fixupCompilerOptions(transpileOptions.compilerOptions, diagnostics)
+ this._ts.fixupCompilerOptions(compilerOptions, diagnostics)
: {};
// mix in default options
@@ -217,19 +53,9 @@ export class NgJestCompiler implements CompilerInstance {
options.allowNonTsExtensions = true;
// if jsx is specified then treat file as .tsx
- const inputFileName =
- transpileOptions.fileName ||
- (transpileOptions.compilerOptions && transpileOptions.compilerOptions.jsx ? 'module.tsx' : 'module.ts');
+ const inputFileName = fileName || (compilerOptions && compilerOptions.jsx ? 'module.tsx' : 'module.ts');
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
const sourceFile = this._ts.createSourceFile(inputFileName, fileContent, options.target!); // TODO: GH#18217
- if (transpileOptions.moduleName) {
- sourceFile.moduleName = transpileOptions.moduleName;
- }
-
- if (transpileOptions.renamedDependencies) {
- // @ts-expect-error internal TypeScript API
- sourceFile.renamedDependencies = new Map(getEntries(transpileOptions.renamedDependencies));
- }
// @ts-expect-error internal TypeScript API
const newLine = this._ts.getNewLineCharacter(options);
@@ -265,20 +91,20 @@ export class NgJestCompiler implements CompilerInstance {
getDirectories: () => [],
};
- const program = this._ts.createProgram([inputFileName], options, compilerHost);
+ this.program = this._ts.createProgram([inputFileName], options, compilerHost);
if (this.ngJestConfig.shouldReportDiagnostics(inputFileName)) {
// @ts-expect-error internal TypeScript API
- this._ts.addRange(/*to*/ diagnostics, /*from*/ program.getSyntacticDiagnostics(sourceFile));
+ this._ts.addRange(/*to*/ diagnostics, /*from*/ this.program.getSyntacticDiagnostics(sourceFile));
// @ts-expect-error internal TypeScript API
- this._ts.addRange(/*to*/ diagnostics, /*from*/ program.getOptionsDiagnostics());
+ this._ts.addRange(/*to*/ diagnostics, /*from*/ this.program.getOptionsDiagnostics());
}
// Emit
- program.emit(
+ this.program.emit(
/*targetSourceFile*/ undefined,
/*writeFile*/ undefined,
/*cancellationToken*/ undefined,
/*emitOnlyDtsFiles*/ undefined,
- this.makeTransformers(customTransformers, program),
+ this._makeTransformers(this.ngJestConfig.resolvedTransformers),
);
// @ts-expect-error internal TypeScript API
@@ -287,21 +113,17 @@ export class NgJestCompiler implements CompilerInstance {
return { outputText, diagnostics, sourceMapText };
}
- private makeTransformers(customTransformers: TsJestAstTransformer, program: ts.Program): ts.CustomTransformers {
+ protected _makeTransformers(customTransformers: TsJestAstTransformer): ts.CustomTransformers {
return {
+ ...super._makeTransformers(customTransformers).after,
+ ...super._makeTransformers(customTransformers).afterDeclarations,
before: [
...customTransformers.before.map((beforeTransformer) =>
- beforeTransformer.factory({ configSet: this.ngJestConfig, program }, beforeTransformer.options),
+ beforeTransformer.factory(this, beforeTransformer.options),
),
- replaceResources({ program } as TsCompilerInstance),
- constructorDownlevelCtor({ program } as TsCompilerInstance),
+ replaceResources(this),
+ constructorDownlevelCtor(this),
] as Array | ts.CustomTransformerFactory>,
- after: customTransformers.after.map((afterTransformer) =>
- afterTransformer.factory({ configSet: this.ngJestConfig, program }, afterTransformer.options),
- ) as Array | ts.CustomTransformerFactory>,
- afterDeclarations: customTransformers.afterDeclarations.map((afterDeclarations) =>
- afterDeclarations.factory({ configSet: this.ngJestConfig, program }, afterDeclarations.options),
- ) as Array>,
};
}
}
diff --git a/src/config/ng-jest-config.ts b/src/config/ng-jest-config.ts
index 3aa774fbd7..4324423c6f 100644
--- a/src/config/ng-jest-config.ts
+++ b/src/config/ng-jest-config.ts
@@ -2,14 +2,9 @@ import { NodeJSFileSystem, setFileSystem } from '@angular/compiler-cli/src/ngtsc
import { formatDiagnostics, readConfiguration, ParsedConfiguration } from '@angular/compiler-cli/src/perform_compile';
import { ConfigSet } from 'ts-jest/dist/config/config-set';
import type { ProjectConfigTsJest } from 'ts-jest/dist/types';
-import type { CompilerOptions } from 'typescript';
+import type { CompilerOptions, ParsedCommandLine } from 'typescript';
export class NgJestConfig extends ConfigSet {
- /**
- * Override `ts-jest` property
- */
- parsedTsConfig!: ParsedConfiguration;
-
constructor(readonly jestCfg: ProjectConfigTsJest) {
super(jestCfg);
}
@@ -17,7 +12,7 @@ export class NgJestConfig extends ConfigSet {
/**
* Override `ts-jest` behavior because we use `readConfiguration` which will read and resolve tsconfig.
*/
- protected _resolveTsConfig(compilerOptions?: CompilerOptions, resolvedConfigFile?: string): ParsedConfiguration {
+ protected _resolveTsConfig(compilerOptions?: CompilerOptions, resolvedConfigFile?: string): ParsedCommandLine {
/**
* To be able to use `readConfiguration` function from `@angular/cli`, we need to setup file system. For `CommonJS`,
* it is not a problem to import directly the function from `@angular/cli`. However, with `ESM`, we will get an
@@ -56,6 +51,7 @@ export class NgJestConfig extends ConfigSet {
return {
...result,
+ fileNames: result.rootNames,
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
options: {
...result.options,
diff --git a/src/ng-jest-transformer.ts b/src/ng-jest-transformer.ts
index 66f01e98d3..31659c3913 100644
--- a/src/ng-jest-transformer.ts
+++ b/src/ng-jest-transformer.ts
@@ -1,6 +1,3 @@
-import type { TransformedSource } from '@jest/transform';
-import type { Config } from '@jest/types';
-import { DECLARATION_TYPE_EXT, JS_JSX_REGEX } from 'ts-jest/dist/constants';
import { TsJestTransformer } from 'ts-jest/dist/ts-jest-transformer';
import type { TransformOptionsTsJest, ProjectConfigTsJest } from 'ts-jest/dist/types';
import { stringify } from 'ts-jest/dist/utils/json';
@@ -23,23 +20,6 @@ export class NgJestTransformer extends TsJestTransformer {
private static readonly _cachedConfigSets: CachedConfigSet[] = [];
protected _compiler!: NgJestCompiler;
- process(
- fileContent: string,
- filePath: Config.Path,
- transformOptions: TransformOptionsTsJest,
- ): TransformedSource | string {
- const isDefinitionFile = filePath.endsWith(DECLARATION_TYPE_EXT);
- // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
- const isJsFile = JS_JSX_REGEX.test(filePath);
- const ngJestCfg = this._configsFor(transformOptions);
- // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
- const shouldStringifyContent = ngJestCfg.shouldStringifyContent(filePath);
-
- return shouldStringifyContent || isDefinitionFile || (!ngJestCfg.parsedTsConfig.options.allowJs && isJsFile)
- ? super.process(fileContent, filePath, transformOptions)
- : this._compiler.getCompiledOutput(filePath, fileContent, transformOptions.supportsStaticESM);
- }
-
/**
* Override `ts-jest` method to load our `NgJestConfig` class
*/
diff --git a/src/transformers/downlevel-ctor.ts b/src/transformers/downlevel-ctor.ts
index e52c32f39f..6672d4b9a0 100644
--- a/src/transformers/downlevel-ctor.ts
+++ b/src/transformers/downlevel-ctor.ts
@@ -9,11 +9,33 @@
* This will make a bit more effort in maintaining but it will be easier to just copy to work with ESM
*/
import { Decorator, ReflectionHost, TypeScriptReflectionHost } from '@angular/compiler-cli/src/ngtsc/reflection';
-import { TsCompilerInstance } from 'ts-jest/dist/types';
+import type { TsCompilerInstance } from 'ts-jest/dist/types';
import ts from 'typescript';
import { isAliasImportDeclaration, loadIsReferencedAliasDeclarationPatch } from './patch-alias-reference-resolution';
+/**
+ * Transform for downleveling Angular decorators and Angular-decorated class constructor
+ * parameters for dependency injection. This transform can be used by the CLI for JIT-mode
+ * compilation where constructor parameters and associated Angular decorators should be
+ * downleveled so that apps are not exposed to the ES2015 temporal dead zone limitation
+ * in TypeScript. See https://github.com/angular/angular-cli/pull/14473 for more details.
+ */
+export function constructorDownlevelCtor({ program }: TsCompilerInstance): ts.TransformerFactory {
+ // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
+ const typeChecker = program!.getTypeChecker();
+ const reflectionHost = new TypeScriptReflectionHost(typeChecker);
+
+ return getDownlevelDecoratorsTransform(
+ typeChecker,
+ reflectionHost,
+ [],
+ /* isCore */ false,
+ /* enableClosureCompiler */ false,
+ /* skipClassDecorators */ true,
+ );
+}
+
/**
* Whether a given decorator should be treated as an Angular decorator.
* Either it's used in @angular/core, or it's imported from there.
@@ -729,30 +751,3 @@ function getDownlevelDecoratorsTransform(
};
};
}
-
-// this is a unique identifier for your transformer
-export const name = 'downlevel-ctor';
-// increment this each time you change the behavior of your transformer
-export const version = 1;
-
-/**
- * Transform for downleveling Angular decorators and Angular-decorated class constructor
- * parameters for dependency injection. This transform can be used by the CLI for JIT-mode
- * compilation where constructor parameters and associated Angular decorators should be
- * downleveled so that apps are not exposed to the ES2015 temporal dead zone limitation
- * in TypeScript. See https://github.com/angular/angular-cli/pull/14473 for more details.
- */
-export function factory({ program }: TsCompilerInstance): ts.TransformerFactory {
- // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
- const typeChecker = program!.getTypeChecker();
- const reflectionHost = new TypeScriptReflectionHost(typeChecker);
-
- return getDownlevelDecoratorsTransform(
- typeChecker,
- reflectionHost,
- [],
- /* isCore */ false,
- /* enableClosureCompiler */ false,
- /* skipClassDecorators */ true,
- );
-}
diff --git a/src/transformers/replace-resources.ts b/src/transformers/replace-resources.ts
index 4430681a23..5eb5dbfebe 100644
--- a/src/transformers/replace-resources.ts
+++ b/src/transformers/replace-resources.ts
@@ -5,16 +5,11 @@
* Use of this source code is governed by an MIT-style license that can be
* found in the LICENSE file at https://angular.io/license
*/
-import { TsCompilerInstance } from 'ts-jest/dist/types';
+import type { TsCompilerInstance } from 'ts-jest/dist/types';
import ts from 'typescript';
import { STYLES, STYLE_URLS, TEMPLATE_URL, TEMPLATE, REQUIRE, COMPONENT } from '../constants';
-// this is a unique identifier for your transformer
-export const name = 'replace-resources';
-// increment this each time you change the behavior of your transformer
-export const version = 1;
-
const shouldTransform = (fileName: string) => !fileName.endsWith('.ngfactory.ts') && !fileName.endsWith('.ngstyle.ts');
/**
* Source https://github.com/angular/angular-cli/blob/master/packages/ngtools/webpack/src/transformers/replace_resources.ts
@@ -49,7 +44,7 @@ const shouldTransform = (fileName: string) => !fileName.endsWith('.ngfactory.ts'
* styles: [],
* })
*/
-export function factory({ program }: TsCompilerInstance): ts.TransformerFactory {
+export function replaceResources({ program }: TsCompilerInstance): ts.TransformerFactory {
return (context: ts.TransformationContext) => {
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
const typeChecker = program!.getTypeChecker();
diff --git a/yarn.lock b/yarn.lock
index 7bdf8a2bb9..066c173205 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -10737,10 +10737,10 @@ trim-off-newlines@^1.0.0:
resolved "https://registry.yarnpkg.com/trim-off-newlines/-/trim-off-newlines-1.0.1.tgz#9f9ba9d9efa8764c387698bcbfeb2c848f11adb3"
integrity sha1-n5up2e+odkw4dpi8v+sshI8RrbM=
-ts-jest@^27.0.0-next.5:
- version "27.0.0-next.5"
- resolved "https://registry.yarnpkg.com/ts-jest/-/ts-jest-27.0.0-next.5.tgz#5be205a300ab6266359f786ca067d1ec07a9a01e"
- integrity sha512-NTix8kPJCPnOlIGpKmaKAh+W11OP4la5I2LdUuAfoPHn4ik8JK08mVLU+ATe9RN5xvKUgkAYYO5HwbtNpuESLA==
+ts-jest@^27.0.0-next.6:
+ version "27.0.0-next.6"
+ resolved "https://registry.yarnpkg.com/ts-jest/-/ts-jest-27.0.0-next.6.tgz#59c9392eff845e4ff90360938aa4d2b3a354a654"
+ integrity sha512-GCxbKghc6qlUnOLUMNan2inOcn+kHmVpHxWgAtsDhhxVNYmsOEK/lzK4XU6C2FDCThCKhBpZUT3sjIWNAvq3mQ==
dependencies:
"@types/jest" "26.x"
bs-logger "0.x"