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(create-jest): Add npm init / yarn create initialiser #14453

Merged
merged 28 commits into from
Sep 7, 2023
Merged
Changes from 25 commits
Commits
Show all changes
28 commits
Select commit Hold shift + click to select a range
30e957f
feat(create-jest): Add `npm init` / `yarn create` initialiser
dj-stormtrooper Aug 27, 2023
5b580b1
fix: Updated `yarn.lock`
dj-stormtrooper Aug 27, 2023
e804edb
feat: Add typescript API
dj-stormtrooper Aug 27, 2023
546db25
fix: Prettier error
dj-stormtrooper Aug 27, 2023
e503b28
Merge branch 'main' of github.com:dj-stormtrooper/jest into feat/crea…
dj-stormtrooper Aug 30, 2023
0ffab46
feat: Move config initialisation logic to `create-jest` package
dj-stormtrooper Aug 30, 2023
df988bf
fix: Eslint errors
dj-stormtrooper Aug 30, 2023
92db787
feat: Add `rootDir` to `create-jest` CLI API
dj-stormtrooper Aug 30, 2023
66384ac
fix: Typesafe values operators for `create-jest` package
dj-stormtrooper Aug 30, 2023
e8d2043
fix: Extra diff
dj-stormtrooper Aug 30, 2023
d160788
fix: Remove redundant file
dj-stormtrooper Aug 30, 2023
c9b4ecb
Update packages/create-jest/src/runCreate.ts
dj-stormtrooper Aug 31, 2023
b126c72
fix: Shortcut for nullable values check
dj-stormtrooper Aug 31, 2023
7b366be
chore: Add `create-jest` to typecheck tests configuration
dj-stormtrooper Aug 31, 2023
0e9aed8
fix: Remove unnecessary type declaration
dj-stormtrooper Aug 31, 2023
48b3d1d
fix: Remove unnecessary `local` check
dj-stormtrooper Aug 31, 2023
bbae051
fix: Remove `bin` from exports
dj-stormtrooper Aug 31, 2023
0b4ea0f
fix: Remove extra CLI interface
dj-stormtrooper Aug 31, 2023
8fe5b8d
fix: Restore `bin` export
dj-stormtrooper Aug 31, 2023
810fec8
Merge branch 'feat/create-jest-package' of github.com:dj-stormtrooper…
dj-stormtrooper Aug 31, 2023
513ad24
fix: New package name in error message
dj-stormtrooper Sep 2, 2023
dedea5f
docs: Add `create-jest` usage
dj-stormtrooper Sep 2, 2023
8ae6c55
fix: More generic error message for malformed json in `create-jest`
dj-stormtrooper Sep 2, 2023
8ca2c6a
Merge branch 'main' into feat/create-jest-package
dj-stormtrooper Sep 7, 2023
77adb36
docs: Add `create-jest` to the changelog
dj-stormtrooper Sep 7, 2023
0dcf317
Update packages/create-jest/src/generateConfigFile.ts
SimenB Sep 7, 2023
cc0e430
purdy
SimenB Sep 7, 2023
9e3ddfa
strict
SimenB Sep 7, 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
2 changes: 1 addition & 1 deletion .eslintrc.cjs
Original file line number Diff line number Diff line change
@@ -357,7 +357,7 @@ module.exports = {
files: [
'scripts/*',
'packages/*/__benchmarks__/test.js',
'packages/jest-cli/src/init/index.ts',
'packages/create-jest/src/runCreate.ts',
'packages/jest-repl/src/cli/runtime-cli.ts',
],
rules: {
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -2,6 +2,8 @@

### Features

- `[create-jest]` Add `npm init` / `yarn create` initialiser for Jest projects ([#14465](https://github.com/jestjs/jest/pull/14453))

### Fixes

- `[jest-snapshot]` Allow for strings as well as template literals in inline snapshots ([#14465](https://github.com/jestjs/jest/pull/14465))
@@ -13,6 +15,8 @@

### Chore & Maintenance

- `[jest-cli]` Move internal config initialisation logic to the `create-jest` package ([#14465](https://github.com/jestjs/jest/pull/14453))

## 29.6.4

### Fixes
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -131,7 +131,7 @@ If you'd like to learn more about running `jest` through the command line, take
Based on your project, Jest will ask you a few questions and will create a basic configuration file with a short description for each option:

```bash
jest --init
yarn create jest
```

### Using Babel
4 changes: 2 additions & 2 deletions docs/GettingStarted.md
Original file line number Diff line number Diff line change
@@ -67,8 +67,8 @@ If you'd like to learn more about running `jest` through the command line, take

Based on your project, Jest will ask you a few questions and will create a basic configuration file with a short description for each option:

```bash
jest --init
```bash npm2yarn
npm init jest@latest
```

### Using Babel
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -111,7 +111,7 @@
"test": "yarn lint && yarn jest",
"typecheck": "yarn typecheck:examples && yarn typecheck:tests",
"typecheck:examples": "tsc -p examples/angular --noEmit && tsc -p examples/expect-extend --noEmit && tsc -p examples/typescript --noEmit",
"typecheck:tests": "tsc -b packages/{babel-jest,babel-plugin-jest-hoist,diff-sequences,expect,expect-utils,jest-circus,jest-cli,jest-config,jest-console,jest-snapshot,jest-util,jest-validate,jest-watcher,jest-worker,pretty-format}/**/__tests__",
"typecheck:tests": "tsc -b packages/{babel-jest,babel-plugin-jest-hoist,create-jest,diff-sequences,expect,expect-utils,jest-circus,jest-cli,jest-config,jest-console,jest-snapshot,jest-util,jest-validate,jest-watcher,jest-worker,pretty-format}/**/__tests__",
"verify-old-ts": "node ./scripts/verifyOldTs.mjs",
"verify-pnp": "node ./scripts/verifyPnP.mjs",
"watch": "yarn build:js && node ./scripts/watch.mjs",
8 changes: 8 additions & 0 deletions packages/create-jest/.npmignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
**/__mocks__/**
**/__tests__/**
__typetests__
src
tsconfig.json
tsconfig.tsbuildinfo
api-extractor.json
.eslintcache
11 changes: 11 additions & 0 deletions packages/create-jest/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
# create-jest

> Getting started with Jest with a single command

```bash
npm init jest@latest
# Or for Yarn
yarn create jest
# Or for pnpm
pnpm create jest
```
8 changes: 8 additions & 0 deletions packages/create-jest/bin/create-jest.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
#!/usr/bin/env node
/**
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
require('..').runCLI();
42 changes: 42 additions & 0 deletions packages/create-jest/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
{
"name": "create-jest",
"description": "Create a new Jest project",
"version": "29.6.4",
"repository": {
"type": "git",
"url": "https://github.com/jestjs/jest.git",
"directory": "packages/create-jest"
},
"license": "MIT",
"bin": "./bin/create-jest.js",
"main": "./build/index.js",
"types": "./build/index.d.ts",
"exports": {
".": {
"types": "./build/index.d.ts",
"default": "./build/index.js"
},
"./package.json": "./package.json",
"./bin/create-jest": "./bin/create-jest.js"
SimenB marked this conversation as resolved.
Show resolved Hide resolved
},
"dependencies": {
"@jest/types": "workspace:^",
"chalk": "^4.0.0",
"exit": "^0.1.2",
"graceful-fs": "^4.2.9",
"jest-config": "workspace:^",
"jest-util": "workspace:^",
"prompts": "^2.0.1"
},
"engines": {
"node": "^14.15.0 || ^16.10.0 || >=18.0.0"
},
"publishConfig": {
"access": "public"
},
"devDependencies": {
"@types/exit": "^0.1.30",
"@types/graceful-fs": "^4.1.3",
"@types/prompts": "^2.0.1"
}
}
Original file line number Diff line number Diff line change
@@ -10,7 +10,7 @@ import * as path from 'path';
import {writeFileSync} from 'graceful-fs';
import * as prompts from 'prompts';
import {constants} from 'jest-config';
import init from '../';
import {runCreate} from '../runCreate';

const {JEST_CONFIG_EXT_ORDER} = constants;

@@ -44,7 +44,7 @@ describe('init', () => {
it('should return the default configuration (an empty config)', async () => {
jest.mocked(prompts).mockResolvedValueOnce({});

await init(resolveFromFixture('only-package-json'));
await runCreate(resolveFromFixture('only-package-json'));

const writtenJestConfigFilename =
jest.mocked(writeFileSync).mock.calls[0][0];
@@ -71,7 +71,7 @@ describe('init', () => {
it('should generate empty config with mjs extension', async () => {
jest.mocked(prompts).mockResolvedValueOnce({});

await init(resolveFromFixture('type-module'));
await runCreate(resolveFromFixture('type-module'));

const writtenJestConfigFilename =
jest.mocked(writeFileSync).mock.calls[0][0];
@@ -93,7 +93,7 @@ describe('init', () => {
it('should create configuration for {clearMocks: true}', async () => {
jest.mocked(prompts).mockResolvedValueOnce({clearMocks: true});

await init(resolveFromFixture('only-package-json'));
await runCreate(resolveFromFixture('only-package-json'));

const writtenJestConfig = jest.mocked(writeFileSync).mock.calls[0][1];
const evaluatedConfig = eval(writtenJestConfig as string) as Record<
@@ -107,7 +107,7 @@ describe('init', () => {
it('should create configuration for {coverage: true}', async () => {
jest.mocked(prompts).mockResolvedValueOnce({coverage: true});

await init(resolveFromFixture('only-package-json'));
await runCreate(resolveFromFixture('only-package-json'));

const writtenJestConfig = jest.mocked(writeFileSync).mock.calls[0][1];
const evaluatedConfig = eval(writtenJestConfig as string) as Record<
@@ -124,7 +124,7 @@ describe('init', () => {
it('should create configuration for {coverageProvider: "babel"}', async () => {
jest.mocked(prompts).mockResolvedValueOnce({coverageProvider: 'babel'});

await init(resolveFromFixture('only-package-json'));
await runCreate(resolveFromFixture('only-package-json'));

const writtenJestConfig = jest.mocked(writeFileSync).mock.calls[0][1];
const evaluatedConfig = eval(writtenJestConfig as string) as Record<
@@ -138,7 +138,7 @@ describe('init', () => {
it('should create configuration for {coverageProvider: "v8"}', async () => {
jest.mocked(prompts).mockResolvedValueOnce({coverageProvider: 'v8'});

await init(resolveFromFixture('only-package-json'));
await runCreate(resolveFromFixture('only-package-json'));

const writtenJestConfig = jest.mocked(writeFileSync).mock.calls[0][1];
const evaluatedConfig = eval(writtenJestConfig as string) as Record<
@@ -152,7 +152,7 @@ describe('init', () => {
it('should create configuration for {environment: "jsdom"}', async () => {
jest.mocked(prompts).mockResolvedValueOnce({environment: 'jsdom'});

await init(resolveFromFixture('only-package-json'));
await runCreate(resolveFromFixture('only-package-json'));

const writtenJestConfig = jest.mocked(writeFileSync).mock.calls[0][1];
const evaluatedConfig = eval(writtenJestConfig as string) as Record<
@@ -165,7 +165,7 @@ describe('init', () => {
it('should create configuration for {environment: "node"}', async () => {
jest.mocked(prompts).mockResolvedValueOnce({environment: 'node'});

await init(resolveFromFixture('only-package-json'));
await runCreate(resolveFromFixture('only-package-json'));

const writtenJestConfig = jest.mocked(writeFileSync).mock.calls[0][1];
const evaluatedConfig = eval(writtenJestConfig as string) as Record<
@@ -178,7 +178,7 @@ describe('init', () => {
it('should create package.json with configured test command when {scripts: true}', async () => {
jest.mocked(prompts).mockResolvedValueOnce({scripts: true});

await init(resolveFromFixture('only-package-json'));
await runCreate(resolveFromFixture('only-package-json'));

const writtenPackageJson = jest.mocked(writeFileSync).mock.calls[0][1];
const parsedPackageJson = JSON.parse(writtenPackageJson as string) as {
@@ -196,7 +196,7 @@ describe('init', () => {
expect.assertions(1);

try {
await init(resolveFromFixture('no-package-json'));
await runCreate(resolveFromFixture('no-package-json'));
} catch (error) {
expect((error as Error).message).toMatch(
'Could not find a "package.json" file in',
@@ -215,7 +215,9 @@ describe('init', () => {
.mockResolvedValueOnce({continue: true})
.mockResolvedValueOnce({});

await init(resolveFromFixture(`has-jest-config-file-${extension}`));
await runCreate(
resolveFromFixture(`has-jest-config-file-${extension}`),
);

expect(jest.mocked(prompts).mock.calls[0][0]).toMatchSnapshot();

@@ -230,7 +232,9 @@ describe('init', () => {
it('user answered with "No"', async () => {
jest.mocked(prompts).mockResolvedValueOnce({continue: false});

await init(resolveFromFixture(`has-jest-config-file-${extension}`));
await runCreate(
resolveFromFixture(`has-jest-config-file-${extension}`),
);
// return after first prompt
expect(prompts).toHaveBeenCalledTimes(1);
});
@@ -243,7 +247,7 @@ describe('init', () => {
it('user answered with "Yes"', async () => {
jest.mocked(prompts).mockResolvedValueOnce({useTypescript: true});

await init(resolveFromFixture('test-generated-jest-config-ts'));
await runCreate(resolveFromFixture('test-generated-jest-config-ts'));

expect(jest.mocked(prompts).mock.calls[0][0]).toMatchSnapshot();

@@ -264,7 +268,7 @@ describe('init', () => {
it('user answered with "No"', async () => {
jest.mocked(prompts).mockResolvedValueOnce({useTypescript: false});

await init(resolveFromFixture('test-generated-jest-config-ts'));
await runCreate(resolveFromFixture('test-generated-jest-config-ts'));

const jestConfigFileName = jest.mocked(writeFileSync).mock.calls[0][0];

@@ -282,7 +286,7 @@ describe('init', () => {
.mockResolvedValueOnce({continue: true})
.mockResolvedValueOnce({});

await init(resolveFromFixture('has-jest-config-in-package-json'));
await runCreate(resolveFromFixture('has-jest-config-in-package-json'));

expect(jest.mocked(prompts).mock.calls[0][0]).toMatchSnapshot();

@@ -296,7 +300,7 @@ describe('init', () => {
it('should not ask "test script question"', async () => {
jest.mocked(prompts).mockResolvedValueOnce({});

await init(resolveFromFixture('test-script-configured'));
await runCreate(resolveFromFixture('test-script-configured'));

const questions = jest.mocked(prompts).mock.calls[0][0] as Array<
prompts.PromptObject<string>
5 changes: 5 additions & 0 deletions packages/create-jest/src/__tests__/tsconfig.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
{
"extends": "../../../../tsconfig.test.json",
"include": ["./**/*"],
"references": [{"path": "../../"}]
}
Original file line number Diff line number Diff line change
@@ -16,10 +16,7 @@ export class NotFoundPackageJsonError extends Error {

export class MalformedPackageJsonError extends Error {
constructor(packageJsonPath: string) {
super(
`There is malformed json in ${packageJsonPath}\n` +
'Fix it, and then run "jest --init"',
);
super(`There is malformed json in ${packageJsonPath}`);
this.name = '';
// eslint-disable-next-line @typescript-eslint/no-empty-function
Error.captureStackTrace(this, () => {});
Original file line number Diff line number Diff line change
@@ -7,13 +7,14 @@

import type {Config} from '@jest/types';
import {defaults, descriptions} from 'jest-config';
import type {PromptsResults} from './types';

const stringifyOption = (
option: keyof Config.InitialOptions,
map: Partial<Config.InitialOptions>,
linePrefix = '',
): string => {
const optionDescription = ` // ${descriptions[option]}`;
const optionDescription = ` // ${descriptions[option] ?? ''}`;
SimenB marked this conversation as resolved.
Show resolved Hide resolved
const stringifiedObject = `${option}: ${JSON.stringify(
map[option],
null,
@@ -27,7 +28,7 @@ const stringifyOption = (
};

const generateConfigFile = (
results: Record<string, unknown>,
results: PromptsResults,
generateEsm = false,
): string => {
const {useTypescript, coverage, coverageProvider, clearMocks, environment} =
8 changes: 8 additions & 0 deletions packages/create-jest/src/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
/**
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/

export {runCreate, runCLI} from './runCreate';
File renamed without changes.
Loading