Skip to content

Commit

Permalink
feat(transformers): add path-mapping custom AST transformer (#1927)
Browse files Browse the repository at this point in the history
  • Loading branch information
ahnpnl committed Sep 4, 2020
1 parent cd60dc7 commit 3325186
Show file tree
Hide file tree
Showing 33 changed files with 4,602 additions and 29 deletions.
2 changes: 2 additions & 0 deletions .eslintrc.js
Expand Up @@ -41,6 +41,8 @@ module.exports = {
'getter-return': 'warn',
'guard-for-in': 'error',
'id-match': 'error',
'jest/valid-title': 'off',
'jest/no-conditional-expect': 'off',
'jsdoc/check-alignment': 'error',
'jsdoc/check-indentation': 'error',
'jsdoc/newline-after-description': 'warn',
Expand Down
48 changes: 48 additions & 0 deletions docs/user/config/astTransformers.md
Expand Up @@ -50,6 +50,54 @@ module.exports = {

</div></div>

### 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.

#### Example of opt-in transformers

<div class="row"><div class="col-md-6" markdown="block">

```js
// jest.config.js
module.exports = {
// [...]
globals: {
'ts-jest': {
astTransformers: {
before: ['ts-jest/dist/transformers/path-mapping'],
},
}
}
};
```

</div><div class="col-md-6" markdown="block">

```js
// OR package.json
{
// [...]
"jest": {
"globals": {
"ts-jest": {
astTransformers: {
"before": ["ts-jest/dist/transformers/path-mapping"]
}
}
}
}
}
```

</div></div>


### 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.
7 changes: 7 additions & 0 deletions e2e/__cases__/path-mapping/direct-import.spec.ts
@@ -0,0 +1,7 @@
import { getWelcomeMessage } from '@share/foo'

test('should return welcome message', () => {
const userName = 'github-user'

expect(getWelcomeMessage(userName)).toEqual(`yolo ${userName}`)
})
6 changes: 6 additions & 0 deletions e2e/__cases__/path-mapping/dynamic-import.spec.ts
@@ -0,0 +1,6 @@
test('should return welcome message', async () => {
const userName = 'github-user'
const foo = await import('@share/foo')

expect(foo.getWelcomeMessage(userName)).toEqual(`yolo ${userName}`)
})
7 changes: 7 additions & 0 deletions e2e/__cases__/path-mapping/export.spec.ts
@@ -0,0 +1,7 @@
import { getWelcomeMessage } from '@share/export'

test('should return welcome message', async () => {
const userName = ''

expect(getWelcomeMessage(userName)).toEqual(`yolo ${userName}`)
})
7 changes: 7 additions & 0 deletions e2e/__cases__/path-mapping/import-default.spec.ts
@@ -0,0 +1,7 @@
import foo from '@share/foo'

test('should return welcome message', () => {
const userName = 'github-user'

expect(foo(userName)).toBe(`yolo ${userName}`)
})
7 changes: 7 additions & 0 deletions e2e/__cases__/path-mapping/import-legacy.spec.ts
@@ -0,0 +1,7 @@
import foo = require('@share/foo')

test('should return welcome message', () => {
const userName = 'github-user'

expect(foo.getWelcomeMessage(userName)).toEqual(`yolo ${userName}`)
})
7 changes: 7 additions & 0 deletions e2e/__cases__/path-mapping/import-require.spec.ts
@@ -0,0 +1,7 @@
const foo = require('@share/foo')

test('should return welcome message', () => {
const userName = 'github-user'

expect(foo.getWelcomeMessage(userName)).toEqual(`yolo ${userName}`)
})
7 changes: 7 additions & 0 deletions e2e/__cases__/path-mapping/import-star.spec.ts
@@ -0,0 +1,7 @@
import * as foo from '@share/foo'

test('should return welcome message', () => {
const userName = 'github-user'

expect(foo.getWelcomeMessage(userName)).toEqual(`yolo ${userName}`)
})
9 changes: 9 additions & 0 deletions e2e/__cases__/path-mapping/import-type.spec.ts
@@ -0,0 +1,9 @@
import type { Foo } from '@share/foo'

test('should work', () => {
const a: Foo = {
bar: 1,
}

expect(a).toBeTruthy()
})
1 change: 1 addition & 0 deletions e2e/__cases__/path-mapping/share/export.ts
@@ -0,0 +1 @@
export { getWelcomeMessage } from '@share/foo'
13 changes: 13 additions & 0 deletions e2e/__cases__/path-mapping/share/foo.ts
@@ -0,0 +1,13 @@
export function getWelcomeMessage(username: string): string {
return `yolo ${username}`;
}

function getMessage(username: string) {
return getWelcomeMessage(username)
}

export interface Foo {
bar: number
}

export default getMessage
12 changes: 12 additions & 0 deletions e2e/__cases__/path-mapping/tsconfig.json
@@ -0,0 +1,12 @@
{
"compilerOptions": {
"target": "es5",
"baseUrl": ".",
"paths": {
"@share/*": ["share/*"]
}
},
"exclude": [
"node_modules"
]
}
2 changes: 1 addition & 1 deletion e2e/__external-repos__/custom-typings/jest.config.js
@@ -1,4 +1,4 @@
module.exports = {
preset: 'ts-jest',
testEnvironment: 'node',
testEnvironment: 'node'
}
2 changes: 2 additions & 0 deletions e2e/__external-repos__/path-mapping/.gitignore
@@ -0,0 +1,2 @@
.idea
node_modules
9 changes: 9 additions & 0 deletions e2e/__external-repos__/path-mapping/README.md
@@ -0,0 +1,9 @@
# A project to demonstrate how to setup `ts-jest` only to work together with `jest`

## Installation
Run `yarn` to install dependencies

## Overview about configuration
The project contains:
- A `jest.config.js` which contains config for `ts-jest`.
- A `tsconfig.json` which contains config for `typescript`.
23 changes: 23 additions & 0 deletions e2e/__external-repos__/path-mapping/foo.spec.ts
@@ -0,0 +1,23 @@
import { isStoreOwner } from './foo';
import { getWelcomeMessage } from '@share/get-welcome-message';
import type { Foo } from '@share/typings'

describe('Test optional chaining', () => {
test(`should work`, () => {
expect(isStoreOwner({
isStoreOwner: false,
})).toEqual(false)
})

test(`test export *`, () => {
expect(getWelcomeMessage('foo')).toEqual('yolo foo')
})

test(`test import type`, () => {
const foo: Foo = {
bar: 1,
}

expect(foo).toBeTruthy()
})
});
5 changes: 5 additions & 0 deletions e2e/__external-repos__/path-mapping/foo.ts
@@ -0,0 +1,5 @@
interface User {
isStoreOwner: boolean
}

export const isStoreOwner = (user: User) => user?.isStoreOwner;
16 changes: 16 additions & 0 deletions e2e/__external-repos__/path-mapping/jest.config.js
@@ -0,0 +1,16 @@
// For a detailed explanation regarding each configuration property, visit:
// https://jestjs.io/docs/en/configuration.html
/** @typedef {import('ts-jest')} */
/** @type {import('@jest/types').Config.InitialOptions} */
module.exports = {
preset: 'ts-jest',
globals: {
'ts-jest': {
astTransformers: {
before: [
'ts-jest/dist/transformers/path-mapping'
]
}
}
}
};
14 changes: 14 additions & 0 deletions e2e/__external-repos__/path-mapping/package.json
@@ -0,0 +1,14 @@
{
"name": "ts-jest-example",
"version": "1.0.0",
"license": "MIT",
"devDependencies": {
"@types/jest": "26.x",
"jest": "26.x",
"ts-jest": "26.x",
"typescript": "~4.0.2"
},
"scripts": {
"test": "jest"
}
}
@@ -0,0 +1,21 @@
import { getWelcomeMessage } from './get-welcome-message';

describe(`getWelcomeMessage()`, (): void => {
let username: string;

describe(`when the given username is a simple string`, (): void => {
beforeEach(
(): void => {
username = `C0ZEN`;
}
);

it(`should return a message for this username`, (): void => {
expect.assertions(1);

const result = getWelcomeMessage(username);

expect(result).toStrictEqual(`yolo C0ZEN`);
});
});
});
@@ -0,0 +1,3 @@
export function getWelcomeMessage(username: string): string {
return `yolo ${username}`;
}
3 changes: 3 additions & 0 deletions e2e/__external-repos__/path-mapping/src/share/typings.ts
@@ -0,0 +1,3 @@
export interface Foo {
bar: number
}
16 changes: 16 additions & 0 deletions e2e/__external-repos__/path-mapping/tsconfig.json
@@ -0,0 +1,16 @@
{
"compilerOptions": {
"target": "es5",
"module": "commonjs",
"baseUrl": ".",
"declaration": true,
"strict": true,
"forceConsistentCasingInFileNames": true,
"paths": {
"@share/*": ["src/share/*"]
}
},
"exclude": [
"node_modules"
]
}

0 comments on commit 3325186

Please sign in to comment.