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/load referenced config from packagejson #14044

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
31 commits
Select commit Hold shift + click to select a range
3210659
Loads .json config file when jest key has a path
rafaelrabelos Apr 1, 2023
398dc06
Adds feature explaining section for docs
rafaelrabelos Apr 1, 2023
d0fd07d
Adds changelog info
rafaelrabelos Apr 1, 2023
2225077
Update changelog with PR info
rafaelrabelos Apr 1, 2023
0948287
Update CHANGELOG.md
SimenB Apr 5, 2023
b6a58ff
Merge branch 'main' into feat/load_referenced_config_from_packagejson
SimenB Apr 5, 2023
c539a99
fix non callable expressions
rafaelrabelos Apr 5, 2023
0a21122
Fixed formatting issues in Configuration.md
rafaelrabelos Apr 5, 2023
111ce11
Refactor to optimize readFileAsync calls
rafaelrabelos Apr 6, 2023
a06ed19
Merge branch 'main' into feat/load_referenced_config_from_packagejson
SimenB Apr 19, 2023
6fa9dde
Merge branch 'main' into feat/load_referenced_config_from_packagejson
rafaelrabelos Aug 9, 2023
26f5427
Lint fixing for resolveConfigPath
rafaelrabelos Aug 9, 2023
b3379dc
Fixes code style issues from prettier
rafaelrabelos Aug 9, 2023
dd4238b
Avoid multiple configuration files
rafaelrabelos Aug 11, 2023
517e302
Remove unecessary try-catch
rafaelrabelos Aug 11, 2023
8f326a2
Refactor for "jest" key catch on parse errors
rafaelrabelos Aug 11, 2023
e50fda7
adds test case for load config file from jest key
rafaelrabelos Aug 11, 2023
1f3b8f0
Adds fs dependency
rafaelrabelos Aug 11, 2023
94e23f2
Test and fix file resolution
rafaelrabelos Aug 12, 2023
8fcfa68
Improves doc example
rafaelrabelos Aug 12, 2023
d174794
Update docs/Configuration.md
rafaelrabelos Aug 13, 2023
a551494
Merge branch 'main' into feat/load_referenced_config_from_packagejson
SimenB Aug 13, 2023
fce78ca
Remove AAA comments from tests
rafaelrabelos Aug 16, 2023
6149986
make resolveJestKey types well defined
rafaelrabelos Aug 16, 2023
ddbb1c8
Merge branch 'feat/load_referenced_config_from_packagejson' of https:…
rafaelrabelos Aug 16, 2023
af8716b
Aditional typing refactor
rafaelrabelos Aug 16, 2023
78c0ba5
Test case for when jest key is not a valid file
rafaelrabelos Aug 16, 2023
b552e19
Optmize Parse calls
rafaelrabelos Aug 17, 2023
ab6d71f
Merge branch 'main' into feat/load_referenced_config_from_packagejson
SimenB Nov 27, 2023
00167af
do not support directory
SimenB Nov 27, 2023
886420c
bring test back, and simplify
SimenB Nov 27, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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