Skip to content

Commit

Permalink
feat: load referenced config from package.json (#14044)
Browse files Browse the repository at this point in the history
  • Loading branch information
rafaelrabelos committed Nov 30, 2023
1 parent 01a923b commit 20e66df
Show file tree
Hide file tree
Showing 4 changed files with 114 additions and 9 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
- `[jest-circus, jest-cli, jest-config]` Add `waitNextEventLoopTurnForUnhandledRejectionEvents` flag to minimise performance impact of correct detection of unhandled promise rejections introduced in [#14315](https://github.com/jestjs/jest/pull/14315) ([#14681](https://github.com/jestjs/jest/pull/14681))
- `[jest-config]` [**BREAKING**] Add `mts` and `cts` to default `moduleFileExtensions` config ([#14369](https://github.com/facebook/jest/pull/14369))
- `[jest-config]` [**BREAKING**] Update `testMatch` and `testRegex` default option for supporting `mjs`, `cjs`, `mts`, and `cts` ([#14584](https://github.com/jestjs/jest/pull/14584))
- `[jest-config]` Loads config file from provided path in `package.json` ([#14044](https://github.com/facebook/jest/pull/14044))
- `[@jest/core]` [**BREAKING**] Group together open handles with the same stack trace ([#13417](https://github.com/jestjs/jest/pull/13417), & [#14543](https://github.com/jestjs/jest/pull/14543))
- `[@jest/core]` Add `perfStats` to surface test setup overhead ([#14622](https://github.com/jestjs/jest/pull/14622))
- `[@jest/core]` [**BREAKING**] Changed `--filter` to accept an object with shape `{ filtered: Array<string> }` to match [documentation](https://jestjs.io/docs/cli#--filterfile) ([#13319](https://github.com/jestjs/jest/pull/13319))
Expand Down
9 changes: 9 additions & 0 deletions docs/Configuration.md
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,15 @@ Alternatively Jest's configuration can be defined through the `"jest"` key in th
}
```

Also Jest's configuration json file can be referenced through the `"jest"` key in the `package.json` of your project:

```json title="package.json"
{
"name": "my-project",
"jest": "./path/to/config.json"
}
```

## Options

:::info
Expand Down
66 changes: 66 additions & 0 deletions packages/jest-config/src/__tests__/resolveConfigPath.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,72 @@ describe.each(JEST_CONFIG_EXT_ORDER.slice(0))(
);
}).toThrow(NO_ROOT_DIR_ERROR_PATTERN);
});

test('file path from "jest" key', () => {
const anyFileName = `anyJestConfigfile${extension}`;
const relativePackageJsonPath = 'a/b/c/package.json';
const relativeAnyFilePath = `a/b/c/conf/${anyFileName}`;
const absolutePackageJsonPath = path.resolve(
DIR,
relativePackageJsonPath,
);
const absoluteAnyFilePath = path.resolve(DIR, relativeAnyFilePath);

writeFiles(DIR, {
'a/b/c/package.json': `{ "jest": "conf/${anyFileName}" }`,
});
writeFiles(DIR, {[relativeAnyFilePath]: ''});

const result = resolveConfigPath(
path.dirname(absolutePackageJsonPath),
DIR,
);

expect(result).toBe(absoluteAnyFilePath);
});

test('not a file path from "jest" key', () => {
const anyFileName = `anyJestConfigfile${extension}`;
const relativePackageJsonPath = 'a/b/c/package.json';
const relativeAnyFilePath = `a/b/c/conf/${anyFileName}`;
const absolutePackageJsonPath = path.resolve(
DIR,
relativePackageJsonPath,
);

writeFiles(DIR, {
'a/b/c/package.json': '{ "jest": {"verbose": true} }',
});
writeFiles(DIR, {[relativeAnyFilePath]: ''});

const result = resolveConfigPath(
path.dirname(absolutePackageJsonPath),
DIR,
);

expect(result).toBe(absolutePackageJsonPath);
});

test('not a valid file when "jest" key is a path', () => {
const anyFileName = `anyJestConfigfile${extension}`;
const relativePackageJsonPath = 'a/b/c/package.json';
const relativeAnyFilePath = `a/b/c/conf/${anyFileName}`;
const absolutePackageJsonPath = path.resolve(
DIR,
relativePackageJsonPath,
);

writeFiles(DIR, {
'a/b/c/package.json': '{ "jest": "conf/nonExistentConfigfile.json" }',
});
writeFiles(DIR, {[relativeAnyFilePath]: ''});

expect(() =>
resolveConfigPath(path.dirname(absolutePackageJsonPath), DIR),
).toThrow(
/Jest expects the string configuration to point to a file, but .* not\./,
);
});
},
);

Expand Down
47 changes: 38 additions & 9 deletions packages/jest-config/src/resolveConfigPath.ts
Original file line number Diff line number Diff line change
Expand Up @@ -75,8 +75,33 @@ const resolveConfigPathByTraversing = (
).filter(isFile);

const packageJson = findPackageJson(pathToResolve);
if (packageJson && hasPackageJsonJestKey(packageJson)) {
configFiles.push(packageJson);

if (packageJson) {
const jestKey = getPackageJsonJestKey(packageJson);

if (jestKey) {
if (typeof jestKey === 'string') {
const absolutePath = path.isAbsolute(jestKey)
? jestKey
: path.resolve(pathToResolve, jestKey);

if (!isFile(absolutePath)) {
throw new ValidationError(
`${BULLET}Validation Error`,
` Configuration in ${chalk.bold(packageJson)} is not valid. ` +
`Jest expects the string configuration to point to a file, but ${absolutePath} is not. ` +
`Please check your Jest configuration in ${chalk.bold(
packageJson,
)}.`,
DOCUMENTATION_NOTE,
);
}

configFiles.push(absolutePath);
} else {
configFiles.push(packageJson);
}
}
}

if (!skipMultipleConfigError && configFiles.length > 1) {
Expand Down Expand Up @@ -111,14 +136,18 @@ const findPackageJson = (pathToResolve: string) => {
return undefined;
};

const hasPackageJsonJestKey = (packagePath: string) => {
const content = fs.readFileSync(packagePath, 'utf8');
const getPackageJsonJestKey = (
packagePath: string,
): Record<string, unknown> | string | undefined => {
try {
return 'jest' in JSON.parse(content);
} catch {
// If package is not a valid JSON
return false;
}
const content = fs.readFileSync(packagePath, 'utf8');
const parsedContent = JSON.parse(content);

if ('jest' in parsedContent) {
return parsedContent.jest;
}
} catch {}
return undefined;
};

const makeResolutionErrorMessage = (initialPath: string, cwd: string) =>
Expand Down

0 comments on commit 20e66df

Please sign in to comment.