Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(config): specify package.json location (#823) #1013

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
2 changes: 2 additions & 0 deletions docs/user/config/index.md
Expand Up @@ -193,6 +193,7 @@ All options have default values which should fit most of the projects. Click on
| [**`diagnostics`**][diagnostics] | [Diagnostics related configuration.][diagnostics] | `boolean`\|`object` | `true` |
| [**`babelConfig`**][babelConfig] | [Babel(Jest) related configuration.][babelConfig] | `boolean`\|`object` | _disabled_ |
| [**`stringifyContentPathRegex`**][stringifyContentPathRegex] | [Files which will become modules returning self content.][stringifyContentPathRegex] | `string`\|`RegExp` | _disabled_ |
| [**`packageJson`**][packageJson] | [Package metadata.][packageJson] | `string`\|`object`\|`boolean` | _auto_ |

### Upgrading

Expand Down Expand Up @@ -222,3 +223,4 @@ npx ts-jest config:migrate package.json
[diagnostics]: diagnostics
[babelConfig]: babelConfig
[stringifyContentPathRegex]: stringifyContentPathRegex
[packageJson]: packageJson
69 changes: 69 additions & 0 deletions docs/user/config/packageJson.md
@@ -0,0 +1,69 @@
---
title: packageJson Config option
---

The `packageJson` option specifies the `package.json` file to use. An inline object may also be specified instead of a file path.

By default, the `package.json` file at the root of the project will be used. If it cannot be found, an empty project definition will be used instead.

### Examples

#### Path to a `packageJson` file

The path should be relative to the current working directory where you start Jest from. You can also use `<rootDir>` in the path to start from the project root dir.

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

```js
// jest.config.js
module.exports = {
// [...]
globals: {
'ts-jest': {
packageJson: 'package.json'
}
}
};
```

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

```js
// OR from a non-trivial path
// jest.config.js
module.exports = {
// [...]
globals: {
'ts-jest': {
packageJson: '<rootDir>/../../shared/package.json'
}
}
};
```

</div></div>

#### Inline package metadata

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

```js
// jest.config.js
module.exports = {
// [...]
globals: {
'ts-jest': {
packageJson: {
"name": "my-project",
"version": "1.0.0",
"dependencies": {
// [...]
}
}
}
}
};
```

</div></div>

1 change: 1 addition & 0 deletions src/__helpers__/fakers.ts
Expand Up @@ -60,6 +60,7 @@ export function tsJestConfig(options?: Partial<TsJestConfig>): TsJestConfig {
transformers: [],
babelConfig: undefined,
tsConfig: undefined,
packageJson: undefined,
stringifyContentPathRegex: undefined,
diagnostics: { ignoreCodes: [], pretty: false, throws: true },
...options,
Expand Down
4 changes: 4 additions & 0 deletions src/config/__snapshots__/config-set.spec.ts.snap
Expand Up @@ -32,6 +32,10 @@ Object {
"throws": true,
},
"isolatedModules": false,
"packageJson": Object {
"kind": "file",
"value": undefined,
},
"stringifyContentPathRegex": undefined,
"transformers": Array [],
"tsConfig": Object {
Expand Down
34 changes: 33 additions & 1 deletion src/config/config-set.spec.ts
Expand Up @@ -103,6 +103,34 @@ describe('tsJest', () => {
})
}) // tsconfig

describe('packageJson', () => {
it('should be correct for default value', () => {
const EXPECTED = {
kind: 'file',
value: undefined,
}
expect(get().packageJson).toEqual(EXPECTED)
})

it('should be correct for given file', () => {
const FILE = 'bar/tsconfig.foo.json'
const EXPECTED = {
kind: 'file',
value: defaultResolve(FILE),
}
expect(get({ packageJson: FILE }).packageJson).toEqual(EXPECTED)
})

it('should be correct for inline config', () => {
const CONFIG = { foo: 'bar' }
const EXPECTED = {
kind: 'inline',
value: CONFIG,
}
expect(get({ packageJson: CONFIG as any }).packageJson).toEqual(EXPECTED)
})
}) // packageJson

describe('babelConfig', () => {
it('should be correct for default value', () => {
expect(get().babelConfig).toBeUndefined()
Expand Down Expand Up @@ -532,7 +560,7 @@ describe('cacheKey', () => {
cs.jsonValue.value = val
// digest is mocked in src/__mocks__/index.ts
expect(cs.cacheKey).toMatchInlineSnapshot(
'"{\\"digest\\":\\"a0d51ca854194df8191d0e65c0ca4730f510f332\\",\\"jest\\":{\\"__backported\\":true,\\"globals\\":{}},\\"projectDepVersions\\":{\\"dev\\":\\"1.2.5\\",\\"opt\\":\\"1.2.3\\",\\"peer\\":\\"1.2.4\\",\\"std\\":\\"1.2.6\\"},\\"transformers\\":[\\"hoisting-jest-mock@1\\"],\\"tsJest\\":{\\"compiler\\":\\"typescript\\",\\"diagnostics\\":{\\"ignoreCodes\\":[6059,18002,18003],\\"pretty\\":true,\\"throws\\":true},\\"isolatedModules\\":false,\\"transformers\\":[]},\\"tsconfig\\":{\\"declaration\\":false,\\"inlineSourceMap\\":false,\\"inlineSources\\":true,\\"module\\":1,\\"noEmit\\":false,\\"outDir\\":\\"$$ts-jest$$\\",\\"removeComments\\":false,\\"sourceMap\\":true,\\"target\\":1}}"',
'"{\\"digest\\":\\"a0d51ca854194df8191d0e65c0ca4730f510f332\\",\\"jest\\":{\\"__backported\\":true,\\"globals\\":{}},\\"projectDepVersions\\":{\\"dev\\":\\"1.2.5\\",\\"opt\\":\\"1.2.3\\",\\"peer\\":\\"1.2.4\\",\\"std\\":\\"1.2.6\\"},\\"transformers\\":[\\"hoisting-jest-mock@1\\"],\\"tsJest\\":{\\"compiler\\":\\"typescript\\",\\"diagnostics\\":{\\"ignoreCodes\\":[6059,18002,18003],\\"pretty\\":true,\\"throws\\":true},\\"isolatedModules\\":false,\\"packageJson\\":{\\"kind\\":\\"file\\"},\\"transformers\\":[]},\\"tsconfig\\":{\\"declaration\\":false,\\"inlineSourceMap\\":false,\\"inlineSources\\":true,\\"module\\":1,\\"noEmit\\":false,\\"outDir\\":\\"$$ts-jest$$\\",\\"removeComments\\":false,\\"sourceMap\\":true,\\"target\\":1}}"',
)
})
}) // cacheKey
Expand Down Expand Up @@ -579,6 +607,10 @@ Object {
"throws": true,
},
"isolatedModules": false,
"packageJson": Object {
"kind": "file",
"value": undefined,
},
"stringifyContentPathRegex": undefined,
"transformers": Array [],
"tsConfig": undefined,
Expand Down
52 changes: 44 additions & 8 deletions src/config/config-set.ts
Expand Up @@ -114,18 +114,38 @@ const toDiagnosticCodeList = (items: any, into: number[] = []): number[] => {
export class ConfigSet {
@Memoize()
get projectPackageJson(): Record<string, any> {
const {
tsJest: { packageJson },
} = this

if (packageJson && packageJson.kind === 'inline') {
return packageJson.value
}

if (packageJson && packageJson.kind === 'file' && packageJson.value) {
const path = this.resolvePath(packageJson.value)
if (existsSync(path)) {
return require(path)
}
this.logger.warn(Errors.UnableToFindProjectRoot)
return {}
}

const tsJestRoot = resolve(__dirname, '..', '..')
let pkgPath = resolve(tsJestRoot, '..', '..', 'package.json')
let exists = existsSync(pkgPath)
if (!exists) {
if (realpathSync(this.rootDir) === realpathSync(tsJestRoot)) {
pkgPath = resolve(tsJestRoot, 'package.json')
exists = true
} else {
this.logger.warn(Errors.UnableToFindProjectRoot)
if (existsSync(pkgPath)) {
return require(pkgPath)
}

if (realpathSync(this.rootDir) === realpathSync(tsJestRoot)) {
pkgPath = resolve(tsJestRoot, 'package.json')
if (existsSync(pkgPath)) {
return require(pkgPath)
}
}
return exists ? require(pkgPath) : {}

this.logger.warn(Errors.UnableToFindProjectRoot)
return {}
}

@Memoize()
Expand Down Expand Up @@ -183,6 +203,21 @@ export class ConfigSet {
}
}

// packageJson
const { packageJson: packageJsonOpt } = options
let packageJson: TsJestConfig['packageJson']
if (typeof packageJsonOpt === 'string' || packageJsonOpt == null || packageJsonOpt === true) {
packageJson = {
kind: 'file',
value: typeof packageJsonOpt === 'string' ? this.resolvePath(packageJsonOpt) : undefined,
}
} else if (typeof packageJsonOpt === 'object') {
packageJson = {
kind: 'inline',
value: packageJsonOpt,
}
}

// transformers
const transformers = (options.astTransformers || []).map(mod => this.resolvePath(mod, { nodeResolve: true }))

Expand Down Expand Up @@ -234,6 +269,7 @@ export class ConfigSet {
// parsed options
const res: TsJestConfig = {
tsConfig,
packageJson,
babelConfig,
diagnostics,
isolatedModules: !!options.isolatedModules,
Expand Down
18 changes: 18 additions & 0 deletions src/types.ts
Expand Up @@ -21,6 +21,14 @@ export interface TsJestGlobalOptions {
*/
tsConfig?: boolean | string | CompilerOptions

/**
* packageJson. It can be:
* - `true` (or `undefined`, it's the default): use default package.json file
* - `path/to/package.json`: path to a specific package.json file (<rootDir> can be used)
* - `{...}`: contents of a package.json
*/
packageJson?: boolean | string | object

/**
* Whether to compile files as isolated modules (disables some features and type-checking, default to `false`):
*/
Expand Down Expand Up @@ -93,10 +101,20 @@ interface TsJestConfig$babelConfig$inline {
value: BabelConfig
}
type TsJestConfig$babelConfig = TsJestConfig$babelConfig$file | TsJestConfig$babelConfig$inline | undefined
interface TsJestConfig$packageJson$file {
kind: 'file'
value: string | undefined
}
interface TsJestConfig$packageJson$inline {
kind: 'inline'
value: any
}
type TsJestConfig$packageJson = TsJestConfig$packageJson$file | TsJestConfig$packageJson$inline | undefined
type TsJestConfig$stringifyContentPathRegex = string | undefined

export interface TsJestConfig {
tsConfig: TsJestConfig$tsConfig
packageJson: TsJestConfig$packageJson
isolatedModules: boolean
compiler: string
diagnostics: TsJestConfig$diagnostics
Expand Down