diff --git a/docs/user/config/astTransformers.md b/docs/user/config/astTransformers.md
index a2c58eadd2..a077787920 100644
--- a/docs/user/config/astTransformers.md
+++ b/docs/user/config/astTransformers.md
@@ -7,13 +7,15 @@ TypeScript AST transformers and provide them to `ts-jest` to include into compil
The option is `astTransformers` and it allows ones to specify which 3 types of TypeScript AST transformers to use with `ts-jest`:
-- `before` means your transformers get run before TS ones, which means your transformers will get raw TS syntax
-instead of transpiled syntax (e.g `import` instead of `require` or `define` ).
+- `before` means your transformers get run before TS ones, which means your transformers will get raw TS syntax
+ instead of transpiled syntax (e.g `import` instead of `require` or `define` ).
- `after` means your transformers get run after TS ones, which gets transpiled syntax.
- `afterDeclarations` means your transformers get run during `d.ts` generation phase, allowing you to transform output type declarations.
### Examples
+#### Basic Transformers
+
```js
@@ -25,9 +27,52 @@ module.exports = {
astTransformers: {
before: ['my-custom-transformer'],
},
+ },
+ },
+}
+```
+
+
+
+```js
+// OR package.json
+{
+ // [...]
+ "jest": {
+ "globals": {
+ "ts-jest": {
+ astTransformers: {
+ "before": ["my-custom-transformer"]
+ }
+ }
}
}
-};
+}
+```
+
+
+
+#### Configuring transformers with options
+
+
+
+```js
+// jest.config.js
+module.exports = {
+ // [...]
+ globals: {
+ 'ts-jest': {
+ astTransformers: {
+ before: [
+ {
+ path: 'my-custom-transformer-that-needs-extra-opts',
+ options: {}, // extra options to pass to transformers here
+ },
+ ],
+ },
+ },
+ },
+}
```
@@ -40,7 +85,10 @@ module.exports = {
"globals": {
"ts-jest": {
astTransformers: {
- "before": ["my-custom-transformer"]
+ "before": [{
+ path: 'my-custom-transformer-that-needs-extra-opts',
+ options: {} // extra options to pass to transformers here
+ }]
}
}
}
@@ -55,9 +103,9 @@ module.exports = {
`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.
+- `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.
#### Example of opt-in transformers
@@ -72,9 +120,9 @@ module.exports = {
astTransformers: {
before: ['ts-jest/dist/transformers/path-mapping'],
},
- }
- }
-};
+ },
+ },
+}
```
@@ -97,7 +145,6 @@ module.exports = {
-
### 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/master/src/transformers) that `ts-jest` is using.
diff --git a/e2e/__cases__/ast-transformers/with-extra-options/foo.js b/e2e/__cases__/ast-transformers/with-extra-options/foo.js
new file mode 100644
index 0000000000..2fae9f1050
--- /dev/null
+++ b/e2e/__cases__/ast-transformers/with-extra-options/foo.js
@@ -0,0 +1,18 @@
+const { LogContexts, LogLevels } = require('bs-logger')
+
+function factory(cs, extraOpts = Object.create(null)) {
+ const logger = cs.logger.child({ namespace: 'dummy-transformer' })
+ const ts = cs.compilerModule
+ logger.debug('Dummy transformer with extra options', JSON.stringify(extraOpts))
+
+ function createVisitor(_ctx, _sf) {
+ return (node) => node
+ }
+
+ return (ctx) =>
+ logger.wrap({ [LogContexts.logLevel]: LogLevels.debug, call: null }, 'visitSourceFileNode(): dummy', (sf) =>
+ ts.visitNode(sf, createVisitor(ctx, sf))
+ )
+}
+
+exports.factory = factory
diff --git a/e2e/__cases__/ast-transformers/with-extra-options/with-extra-options.spec.ts b/e2e/__cases__/ast-transformers/with-extra-options/with-extra-options.spec.ts
new file mode 100644
index 0000000000..11634b90f3
--- /dev/null
+++ b/e2e/__cases__/ast-transformers/with-extra-options/with-extra-options.spec.ts
@@ -0,0 +1,5 @@
+const a = 1;
+
+it('should pass', () => {
+ expect(a).toEqual(1);
+})
diff --git a/e2e/__tests__/__snapshots__/ast-transformers.test.ts.snap b/e2e/__tests__/__snapshots__/ast-transformers.test.ts.snap
new file mode 100644
index 0000000000..0ed37052b1
--- /dev/null
+++ b/e2e/__tests__/__snapshots__/ast-transformers.test.ts.snap
@@ -0,0 +1,19 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`AST transformers with extra options should pass using template "default" 1`] = `
+Array [
+ "[level:20] Dummy transformer with extra options {\\"foo\\":\\"bar\\"}",
+]
+`;
+
+exports[`AST transformers with extra options should pass using template "with-babel-7" 1`] = `
+Array [
+ "[level:20] Dummy transformer with extra options {\\"foo\\":\\"bar\\"}",
+]
+`;
+
+exports[`AST transformers with extra options should pass using template "with-babel-7-string-config" 1`] = `
+Array [
+ "[level:20] Dummy transformer with extra options {\\"foo\\":\\"bar\\"}",
+]
+`;
diff --git a/e2e/__tests__/ast-transformers.test.ts b/e2e/__tests__/ast-transformers.test.ts
new file mode 100644
index 0000000000..a744e45cd9
--- /dev/null
+++ b/e2e/__tests__/ast-transformers.test.ts
@@ -0,0 +1,38 @@
+import { configureTestCase } from '../__helpers__/test-case'
+import { allValidPackageSets } from '../__helpers__/templates'
+import { existsSync } from "fs"
+import { LogContexts, LogLevels } from 'bs-logger'
+
+describe('AST transformers', () => {
+ describe('with extra options', () => {
+ const testCase = configureTestCase('ast-transformers/with-extra-options', {
+ env: { TS_JEST_LOG: 'ts-jest.log' },
+ tsJestConfig: {
+ astTransformers: {
+ before: [{
+ path: require.resolve('../__cases__/ast-transformers/with-extra-options/foo'),
+ options: {
+ foo: 'bar',
+ },
+ }],
+ },
+ },
+ })
+
+ testCase.runWithTemplates(allValidPackageSets, 0, (runTest, { testLabel }) => {
+ it(testLabel, () => {
+ const result = runTest()
+ expect(result.status).toBe(0)
+ expect(existsSync(result.logFilePath)).toBe(true)
+ const filteredEntries = result.logFileEntries
+ // keep only debug and above
+ .filter(m => (m.context[LogContexts.logLevel] || 0) >= LogLevels.debug)
+ // simplify entries
+ // eslint-disable-next-line @typescript-eslint/restrict-template-expressions
+ .map(e => result.normalize(`[level:${e.context[LogContexts.logLevel]}] ${e.message}`))
+ .filter(logging => logging.includes('Dummy transformer with extra options'))
+ expect(filteredEntries).toMatchSnapshot()
+ })
+ })
+ })
+})
diff --git a/src/config/__snapshots__/config-set.spec.ts.snap b/src/config/__snapshots__/config-set.spec.ts.snap
index e310da8a30..fb0447b0d5 100644
--- a/src/config/__snapshots__/config-set.spec.ts.snap
+++ b/src/config/__snapshots__/config-set.spec.ts.snap
@@ -355,3 +355,14 @@ exports[`tsJest transformers should display deprecation warning message when con
"[level:40] The configuration for astTransformers as string[] is deprecated and will be removed in ts-jest 27. Please define your custom AST transformers in a form of an object. More information you can check online documentation https://kulshekhar.github.io/ts-jest/user/config/astTransformers
"
`;
+
+exports[`tsJest transformers should support transformers with options 1`] = `
+Array [
+ Object {
+ "options": Object {
+ "foo": 1,
+ },
+ "path": Any,
+ },
+]
+`;
diff --git a/src/config/config-set.spec.ts b/src/config/config-set.spec.ts
index 10a29f8678..ee8451687e 100644
--- a/src/config/config-set.spec.ts
+++ b/src/config/config-set.spec.ts
@@ -161,6 +161,37 @@ describe('tsJest', () => {
expect(logger.target.lines[1]).toMatchSnapshot()
})
+ it('should support transformers with options', () => {
+ const cs = createConfigSet({
+ jestConfig: {
+ rootDir: 'src',
+ cwd: 'src',
+ globals: {
+ 'ts-jest': {
+ astTransformers: {
+ before: [
+ {
+ path: 'dummy-transformer',
+ options: {
+ foo: 1,
+ },
+ },
+ ],
+ },
+ },
+ },
+ } as any,
+ logger,
+ resolve: null,
+ })
+
+ expect(cs.tsJest.transformers.before).toMatchSnapshot([
+ {
+ path: expect.any(String),
+ },
+ ])
+ })
+
it.each([
{},
{
diff --git a/src/config/config-set.ts b/src/config/config-set.ts
index 2d941370aa..f284749656 100644
--- a/src/config/config-set.ts
+++ b/src/config/config-set.ts
@@ -56,10 +56,15 @@ import { TSError } from '../utils/ts-error'
const logger = rootLogger.child({ namespace: 'config' })
+interface AstTransformerObj> {
+ transformModule: AstTransformerDesc
+ options?: T
+}
+
interface AstTransformer {
- before: AstTransformerDesc[]
- after?: AstTransformerDesc[]
- afterDeclarations?: AstTransformerDesc[]
+ before: AstTransformerObj[]
+ after?: AstTransformerObj[]
+ afterDeclarations?: AstTransformerObj[]
}
/**
* @internal
@@ -184,29 +189,52 @@ export class ConfigSet {
this.logger.warn(Deprecations.AstTransformerArrayConfig)
transformers = {
- before: astTransformers.map((transformerPath) => this.resolvePath(transformerPath, { nodeResolve: true })),
+ before: astTransformers.map((transformerPath) => ({
+ path: this.resolvePath(transformerPath, { nodeResolve: true }),
+ })),
}
} else {
if (astTransformers.before) {
transformers = {
- before: astTransformers.before.map((transformerPath: string) =>
- this.resolvePath(transformerPath, { nodeResolve: true }),
+ before: astTransformers.before.map((transformer) =>
+ typeof transformer === 'string'
+ ? {
+ path: this.resolvePath(transformer, { nodeResolve: true }),
+ }
+ : {
+ ...transformer,
+ path: this.resolvePath(transformer.path, { nodeResolve: true }),
+ },
),
}
}
if (astTransformers.after) {
transformers = {
...transformers,
- after: astTransformers.after.map((transformerPath: string) =>
- this.resolvePath(transformerPath, { nodeResolve: true }),
+ after: astTransformers.after.map((transformer) =>
+ typeof transformer === 'string'
+ ? {
+ path: this.resolvePath(transformer, { nodeResolve: true }),
+ }
+ : {
+ ...transformer,
+ path: this.resolvePath(transformer.path, { nodeResolve: true }),
+ },
),
}
}
if (astTransformers.afterDeclarations) {
transformers = {
...transformers,
- afterDeclarations: astTransformers.afterDeclarations.map((transformerPath: string) =>
- this.resolvePath(transformerPath, { nodeResolve: true }),
+ afterDeclarations: astTransformers.afterDeclarations.map((transformer) =>
+ typeof transformer === 'string'
+ ? {
+ path: this.resolvePath(transformer, { nodeResolve: true }),
+ }
+ : {
+ ...transformer,
+ path: this.resolvePath(transformer.path, { nodeResolve: true }),
+ },
),
}
}
@@ -402,28 +430,57 @@ export class ConfigSet {
@Memoize()
private get astTransformers(): AstTransformer {
let astTransformers: AstTransformer = {
- before: [...internalAstTransformers],
+ before: [
+ ...internalAstTransformers.map((transformer) => ({
+ transformModule: transformer,
+ })),
+ ],
}
const { transformers } = this.tsJest
if (transformers.before) {
astTransformers = {
before: [
...astTransformers.before,
- ...transformers.before.map((transformerFilePath: string) => require(transformerFilePath)),
+ ...transformers.before.map((transformer) =>
+ typeof transformer === 'string'
+ ? {
+ transformModule: require(transformer),
+ }
+ : {
+ transformModule: require(transformer.path),
+ options: transformer.options,
+ },
+ ),
],
}
}
if (transformers.after) {
astTransformers = {
...astTransformers,
- after: transformers.after.map((transformerFilePath: string) => require(transformerFilePath)),
+ after: transformers.after.map((transformer) =>
+ typeof transformer === 'string'
+ ? {
+ transformModule: require(transformer),
+ }
+ : {
+ transformModule: require(transformer.path),
+ options: transformer.options,
+ },
+ ),
}
}
if (transformers.afterDeclarations) {
astTransformers = {
...astTransformers,
- afterDeclarations: transformers.afterDeclarations.map((transformerFilePath: string) =>
- require(transformerFilePath),
+ afterDeclarations: transformers.afterDeclarations.map((transformer) =>
+ typeof transformer === 'string'
+ ? {
+ transformModule: require(transformer),
+ }
+ : {
+ transformModule: require(transformer.path),
+ options: transformer.options,
+ },
),
}
}
@@ -437,20 +494,24 @@ export class ConfigSet {
@Memoize()
get tsCustomTransformers(): CustomTransformers {
let customTransformers: CustomTransformers = {
- before: this.astTransformers.before.map((t) => t.factory(this)) as TransformerFactory[],
+ before: this.astTransformers.before.map((t) => t.transformModule.factory(this, t.options)) as TransformerFactory<
+ SourceFile
+ >[],
}
if (this.astTransformers.after) {
customTransformers = {
...customTransformers,
- after: this.astTransformers.after.map((t) => t.factory(this)) as TransformerFactory[],
+ after: this.astTransformers.after.map((t) => t.transformModule.factory(this, t.options)) as TransformerFactory<
+ SourceFile
+ >[],
}
}
if (this.astTransformers.afterDeclarations) {
customTransformers = {
...customTransformers,
- afterDeclarations: this.astTransformers.afterDeclarations.map((t) => t.factory(this)) as TransformerFactory<
- Bundle | SourceFile
- >[],
+ afterDeclarations: this.astTransformers.afterDeclarations.map((t) =>
+ t.transformModule.factory(this, t.options),
+ ) as TransformerFactory[],
}
}
@@ -660,7 +721,7 @@ export class ConfigSet {
digest: this.tsJestDigest,
transformers: Object.values(this.astTransformers)
.reduce((acc, val) => acc.concat(val), [])
- .map((t: AstTransformerDesc) => `${t.name}@${t.version}`),
+ .map(({ transformModule }: AstTransformerObj) => `${transformModule.name}@${transformModule.version}`),
jest,
tsJest: this.tsJest,
babel: this.babel,
diff --git a/src/types.ts b/src/types.ts
index ef17b21cc6..882d3980f4 100644
--- a/src/types.ts
+++ b/src/types.ts
@@ -24,10 +24,15 @@ export type BabelJestTransformer = {
*/
export type BabelConfig = _babel.TransformOptions
+export interface AstTransformer> {
+ path: string
+ options?: T
+}
+
export interface ConfigCustomTransformer {
- before?: string[]
- after?: string[]
- afterDeclarations?: string[]
+ before?: (string | AstTransformer)[]
+ after?: (string | AstTransformer)[]
+ afterDeclarations?: (string | AstTransformer)[]
}
export interface TsJestGlobalOptions {
@@ -229,8 +234,11 @@ export interface CompilerInstance {
/**
* @internal
*/
-export interface AstTransformerDesc {
+export interface AstTransformerDesc> {
name: string
version: number
- factory(cs: ConfigSet): _ts.TransformerFactory<_ts.SourceFile> | _ts.TransformerFactory<_ts.Bundle | _ts.SourceFile>
+ factory(
+ cs: ConfigSet,
+ opts?: T,
+ ): _ts.TransformerFactory<_ts.SourceFile> | _ts.TransformerFactory<_ts.Bundle | _ts.SourceFile>
}