Skip to content

refactor(jest-snapshot): refactor Prettier related part #15546

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

Draft
wants to merge 2 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
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
4 changes: 2 additions & 2 deletions packages/jest-snapshot/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -37,9 +37,9 @@
"jest-matcher-utils": "workspace:*",
"jest-message-util": "workspace:*",
"jest-util": "workspace:*",
"make-synchronized": "^0.4.2",
"pretty-format": "workspace:*",
"semver": "^7.5.3",
"synckit": "^0.9.0"
"semver": "^7.5.3"
},
"devDependencies": {
"@babel/preset-flow": "^7.7.2",
Expand Down
110 changes: 12 additions & 98 deletions packages/jest-snapshot/src/InlineSnapshots.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,54 +5,38 @@
* LICENSE file in the root directory of this source tree.
*/

import * as path from 'path';
import {types} from 'util';
import * as fs from 'graceful-fs';
import type {
CustomParser as PrettierCustomParser,
BuiltInParserName as PrettierParserName,
} from 'prettier-v2';
import semver = require('semver');
import {createSyncFn} from 'synckit';
import {runPrettier, runPrettier2} from './prettier';
import type {InlineSnapshot} from './types';
import {
groupSnapshotsByFile,
processInlineSnapshotsWithBabel,
processPrettierAst,
} from './utils';
import {groupSnapshotsByFile, processInlineSnapshotsWithBabel} from './utils';

type Prettier = typeof import('prettier-v2');
type WorkerFn = (
prettierPath: string,
filepath: string,
sourceFileWithSnapshots: string,
snapshotMatcherNames: Array<string>,
) => string;
type Prettier = typeof runPrettier;
type Prettier2 = typeof import('prettier-v2');

const cachedPrettier = new Map<string, Prettier | WorkerFn>();
const cachedPrettier = new Map<string, Prettier | Prettier2>();

export function saveInlineSnapshots(
snapshots: Array<InlineSnapshot>,
rootDir: string,
prettierPath: string | null,
): void {
let prettier: Prettier | undefined = prettierPath
? (cachedPrettier.get(`module|${prettierPath}`) as Prettier)
let prettier: Prettier2 | undefined = prettierPath
? (cachedPrettier.get(`module|${prettierPath}`) as Prettier2)
: undefined;
let workerFn: WorkerFn | undefined = prettierPath
? (cachedPrettier.get(`worker|${prettierPath}`) as WorkerFn)
let workerFn: Prettier | undefined = prettierPath
? (cachedPrettier.get(`worker|${prettierPath}`) as Prettier)
: undefined;
if (prettierPath && !prettier) {
try {
prettier =
// @ts-expect-error requireOutside
requireOutside(prettierPath) as Prettier;
requireOutside(prettierPath) as Prettier2;
cachedPrettier.set(`module|${prettierPath}`, prettier);

if (semver.gte(prettier.version, '3.0.0')) {
workerFn = createSyncFn(
require.resolve(/*webpackIgnore: true*/ './worker'),
) as WorkerFn;
workerFn = runPrettier;

Check warning on line 39 in packages/jest-snapshot/src/InlineSnapshots.ts

View check run for this annotation

Codecov / codecov/patch

packages/jest-snapshot/src/InlineSnapshots.ts#L39

Added line #L39 was not covered by tests
cachedPrettier.set(`worker|${prettierPath}`, workerFn);
}
} catch (error) {
Expand Down Expand Up @@ -86,7 +70,7 @@
snapshotMatcherNames,
);
} else if (prettier && semver.gte(prettier.version, '1.5.0')) {
newSourceFile = runPrettier(
newSourceFile = runPrettier2(
prettier,
sourceFilePath,
sourceFileWithSnapshots,
Expand All @@ -99,73 +83,3 @@
}
}
}

const runPrettier = (
prettier: Prettier,
sourceFilePath: string,
sourceFileWithSnapshots: string,
snapshotMatcherNames: Array<string>,
) => {
// Resolve project configuration.
// For older versions of Prettier, do not load configuration.
const config = prettier.resolveConfig
? prettier.resolveConfig.sync(sourceFilePath, {editorconfig: true})
: null;

// Prioritize parser found in the project config.
// If not found detect the parser for the test file.
// For older versions of Prettier, fallback to a simple parser detection.
// @ts-expect-error - `inferredParser` is `string`
const inferredParser: PrettierParserName | null | undefined =
(typeof config?.parser === 'string' && config.parser) ||
(prettier.getFileInfo
? prettier.getFileInfo.sync(sourceFilePath).inferredParser
: simpleDetectParser(sourceFilePath));

if (!inferredParser) {
throw new Error(
`Could not infer Prettier parser for file ${sourceFilePath}`,
);
}

// Snapshots have now been inserted. Run prettier to make sure that the code is
// formatted, except snapshot indentation. Snapshots cannot be formatted until
// after the initial format because we don't know where the call expression
// will be placed (specifically its indentation), so we have to do two
// prettier.format calls back-to-back.
return prettier.format(
prettier.format(sourceFileWithSnapshots, {
...config,
filepath: sourceFilePath,
}),
{
...config,
filepath: sourceFilePath,
parser: createFormattingParser(snapshotMatcherNames, inferredParser),
},
);
};

// This parser formats snapshots to the correct indentation.
const createFormattingParser =
(
snapshotMatcherNames: Array<string>,
inferredParser: PrettierParserName,
): PrettierCustomParser =>
(text, parsers, options) => {
// Workaround for https://github.com/prettier/prettier/issues/3150
options.parser = inferredParser;

const ast = parsers[inferredParser](text, options);
processPrettierAst(ast, options, snapshotMatcherNames);

return ast;
};

const simpleDetectParser = (filePath: string): PrettierParserName => {
const extname = path.extname(filePath);
if (/\.tsx?$/.test(extname)) {
return 'typescript';
}
return 'babel';
};
145 changes: 145 additions & 0 deletions packages/jest-snapshot/src/prettier.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,145 @@
/**
* 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.
*/

import * as path from 'path';
import {makeSynchronizedFunction} from 'make-synchronized';
import type {
CustomParser as PrettierCustomParser,
BuiltInParserName as PrettierParserName,
} from 'prettier-v2';
import {processPrettierAst} from './utils';

const simpleDetectParser = (filePath: string): PrettierParserName => {
const extname = path.extname(filePath);

Check warning on line 17 in packages/jest-snapshot/src/prettier.ts

View check run for this annotation

Codecov / codecov/patch

packages/jest-snapshot/src/prettier.ts#L17

Added line #L17 was not covered by tests
if (/\.tsx?$/.test(extname)) {
return 'typescript';

Check warning on line 19 in packages/jest-snapshot/src/prettier.ts

View check run for this annotation

Codecov / codecov/patch

packages/jest-snapshot/src/prettier.ts#L19

Added line #L19 was not covered by tests
}
return 'babel';

Check warning on line 21 in packages/jest-snapshot/src/prettier.ts

View check run for this annotation

Codecov / codecov/patch

packages/jest-snapshot/src/prettier.ts#L21

Added line #L21 was not covered by tests
};

// This parser formats snapshots to the correct indentation.
const createFormattingParser =
(
snapshotMatcherNames: Array<string>,
inferredParser: PrettierParserName,
): PrettierCustomParser =>
(text, parsers, options) => {
// Workaround for https://github.com/prettier/prettier/issues/3150
options.parser = inferredParser;

const ast = parsers[inferredParser](text, options);
processPrettierAst(ast, options, snapshotMatcherNames);

return ast;
};

async function getInferredParser(filepath: string) {
const fileInfo = await prettier.getFileInfo(filepath);
return fileInfo.inferredParser;

Check warning on line 42 in packages/jest-snapshot/src/prettier.ts

View check run for this annotation

Codecov / codecov/patch

packages/jest-snapshot/src/prettier.ts#L40-L42

Added lines #L40 - L42 were not covered by tests
}

let prettier: typeof import('prettier');
export const runPrettier = makeSynchronizedFunction(
__filename,
async function runPrettierAsync(
prettierPath: string,
filepath: string,
sourceFileWithSnapshots: string,
snapshotMatcherNames: Array<string>,
): Promise<string> {

Check warning on line 53 in packages/jest-snapshot/src/prettier.ts

View check run for this annotation

Codecov / codecov/patch

packages/jest-snapshot/src/prettier.ts#L53

Added line #L53 was not covered by tests
// @ts-expect-error requireOutside
prettier ??= requireOutside(/*webpackIgnore: true*/ prettierPath);

Check warning on line 55 in packages/jest-snapshot/src/prettier.ts

View check run for this annotation

Codecov / codecov/patch

packages/jest-snapshot/src/prettier.ts#L55

Added line #L55 was not covered by tests

const config = await prettier.resolveConfig(filepath, {

Check warning on line 57 in packages/jest-snapshot/src/prettier.ts

View check run for this annotation

Codecov / codecov/patch

packages/jest-snapshot/src/prettier.ts#L57

Added line #L57 was not covered by tests
editorconfig: true,
});

const inferredParser: string | null =
typeof config?.parser === 'string'
? config.parser
: await getInferredParser(filepath);

Check warning on line 64 in packages/jest-snapshot/src/prettier.ts

View check run for this annotation

Codecov / codecov/patch

packages/jest-snapshot/src/prettier.ts#L63-L64

Added lines #L63 - L64 were not covered by tests

if (!inferredParser) {
throw new Error(`Could not infer Prettier parser for file ${filepath}`);

Check warning on line 67 in packages/jest-snapshot/src/prettier.ts

View check run for this annotation

Codecov / codecov/patch

packages/jest-snapshot/src/prettier.ts#L67

Added line #L67 was not covered by tests
}

sourceFileWithSnapshots = await prettier.format(sourceFileWithSnapshots, {

Check warning on line 70 in packages/jest-snapshot/src/prettier.ts

View check run for this annotation

Codecov / codecov/patch

packages/jest-snapshot/src/prettier.ts#L70

Added line #L70 was not covered by tests
...config,
filepath,
parser: inferredParser,
});

// @ts-expect-error private API
const {ast} = await prettier.__debug.parse(sourceFileWithSnapshots, {

Check warning on line 77 in packages/jest-snapshot/src/prettier.ts

View check run for this annotation

Codecov / codecov/patch

packages/jest-snapshot/src/prettier.ts#L77

Added line #L77 was not covered by tests
...config,
filepath,
originalText: sourceFileWithSnapshots,
parser: inferredParser,
});
processPrettierAst(ast, config, snapshotMatcherNames, true);

Check warning on line 83 in packages/jest-snapshot/src/prettier.ts

View check run for this annotation

Codecov / codecov/patch

packages/jest-snapshot/src/prettier.ts#L83

Added line #L83 was not covered by tests
// Snapshots have now been inserted. Run prettier to make sure that the code is
// formatted, except snapshot indentation. Snapshots cannot be formatted until
// after the initial format because we don't know where the call expression
// will be placed (specifically its indentation), so we have to do two
// prettier.format calls back-to-back.
// @ts-expect-error private API
const formatAST = await prettier.__debug.formatAST(ast, {

Check warning on line 90 in packages/jest-snapshot/src/prettier.ts

View check run for this annotation

Codecov / codecov/patch

packages/jest-snapshot/src/prettier.ts#L90

Added line #L90 was not covered by tests
...config,
filepath,
originalText: sourceFileWithSnapshots,
parser: inferredParser,
});
return formatAST.formatted;

Check warning on line 96 in packages/jest-snapshot/src/prettier.ts

View check run for this annotation

Codecov / codecov/patch

packages/jest-snapshot/src/prettier.ts#L96

Added line #L96 was not covered by tests
},
'runPrettier',
);

export function runPrettier2(
prettier: typeof import('prettier-v2'),
sourceFilePath: string,
sourceFileWithSnapshots: string,
snapshotMatcherNames: Array<string>,
): string {
// Resolve project configuration.
// For older versions of Prettier, do not load configuration.
const config = prettier.resolveConfig
? prettier.resolveConfig.sync(sourceFilePath, {editorconfig: true})
: null;

Check warning on line 111 in packages/jest-snapshot/src/prettier.ts

View check run for this annotation

Codecov / codecov/patch

packages/jest-snapshot/src/prettier.ts#L111

Added line #L111 was not covered by tests

// Prioritize parser found in the project config.
// If not found detect the parser for the test file.
// For older versions of Prettier, fallback to a simple parser detection.
// @ts-expect-error - `inferredParser` is `string`
const inferredParser: PrettierParserName | null | undefined =
(typeof config?.parser === 'string' && config.parser) ||
(prettier.getFileInfo
? prettier.getFileInfo.sync(sourceFilePath).inferredParser
: simpleDetectParser(sourceFilePath));

Check warning on line 121 in packages/jest-snapshot/src/prettier.ts

View check run for this annotation

Codecov / codecov/patch

packages/jest-snapshot/src/prettier.ts#L121

Added line #L121 was not covered by tests

if (!inferredParser) {
throw new Error(

Check warning on line 124 in packages/jest-snapshot/src/prettier.ts

View check run for this annotation

Codecov / codecov/patch

packages/jest-snapshot/src/prettier.ts#L124

Added line #L124 was not covered by tests
`Could not infer Prettier parser for file ${sourceFilePath}`,
);
}

// Snapshots have now been inserted. Run prettier to make sure that the code is
// formatted, except snapshot indentation. Snapshots cannot be formatted until
// after the initial format because we don't know where the call expression
// will be placed (specifically its indentation), so we have to do two
// prettier.format calls back-to-back.
return prettier.format(
prettier.format(sourceFileWithSnapshots, {
...config,
filepath: sourceFilePath,
}),
{
...config,
filepath: sourceFilePath,
parser: createFormattingParser(snapshotMatcherNames, inferredParser),
},
);
}
70 changes: 0 additions & 70 deletions packages/jest-snapshot/src/worker.ts

This file was deleted.

7 changes: 6 additions & 1 deletion scripts/buildUtils.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -231,7 +231,12 @@ export function createBuildConfigs() {
: pkg.name === 'jest-repl'
? {repl: path.resolve(packageDir, './src/cli/repl.ts')}
: pkg.name === 'jest-snapshot'
? {worker: path.resolve(packageDir, './src/worker.ts')}
? {
prettier: path.resolve(
packageDir,
'./src/prettier.ts',
),
}
: {};

const extraEntryPoints =
Expand Down
Loading
Loading