mirror of
https://github.com/facebook/react.git
synced 2025-11-01 09:12:30 +00:00
[snap] Refactor fixture utils to separate package
--- Move shared functions to `fixture-test-utils` package.
This commit is contained in:
@@ -0,0 +1,25 @@
|
||||
{
|
||||
"name": "fixture-test-utils",
|
||||
"version": "1.0.0",
|
||||
"main": "dist/index.js",
|
||||
"license": "MIT",
|
||||
"scripts": {
|
||||
"build": "rimraf dist && tsc",
|
||||
"test": "echo 'no tests'",
|
||||
"prettier": "prettier --write src"
|
||||
},
|
||||
"dependencies": {
|
||||
"@parcel/watcher": "^2.1.0",
|
||||
"chalk": "4",
|
||||
"readline": "^1.3.0",
|
||||
"typescript": "^5.1.0",
|
||||
"yargs": "^17.7.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/node": "^18.7.18",
|
||||
"@typescript-eslint/eslint-plugin": "^5.51.0",
|
||||
"@typescript-eslint/parser": "^5.51.0",
|
||||
"prettier": "2.8.8",
|
||||
"rimraf": "^3.0.2"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,122 @@
|
||||
import type { runReactForgetBabelPlugin as RunReactForgetBabelPlugin } from "babel-plugin-react-forget/src/Babel/RunReactForgetBabelPlugin";
|
||||
import type { Effect, ValueKind } from "babel-plugin-react-forget/src/HIR";
|
||||
|
||||
const FlowPragmas = [/\/\/\s@flow$/gm, /\*\s@flow$/gm];
|
||||
|
||||
export function parseLanguage(source: string): "flow" | "typescript" {
|
||||
let useFlow = false;
|
||||
for (const flowPragma of FlowPragmas) {
|
||||
useFlow ||= !!source.match(flowPragma);
|
||||
}
|
||||
return useFlow ? "flow" : "typescript";
|
||||
}
|
||||
|
||||
export function transformFixtureInput(
|
||||
input: string,
|
||||
basename: string,
|
||||
pluginFn: typeof RunReactForgetBabelPlugin,
|
||||
includeAst: boolean = false
|
||||
) {
|
||||
// Extract the first line to quickly check for custom test directives
|
||||
const firstLine = input.substring(0, input.indexOf("\n"));
|
||||
|
||||
let language = parseLanguage(firstLine);
|
||||
let enableOnlyOnUseForgetDirective = false;
|
||||
let gating = null;
|
||||
let instrumentForget = null;
|
||||
let panicOnBailout = true;
|
||||
let memoizeJsxElements = true;
|
||||
let enableAssumeHooksFollowRulesOfReact = false;
|
||||
let enableTreatHooksAsFunctions = true;
|
||||
let disableAllMemoization = false;
|
||||
let validateRefAccessDuringRender = true;
|
||||
let validateNoSetStateInRender = true;
|
||||
let enableEmitFreeze = null;
|
||||
let enableOptimizeFunctionExpressions = true;
|
||||
let enableOnlyOnReactScript = false;
|
||||
|
||||
if (firstLine.indexOf("@forgetDirective") !== -1) {
|
||||
enableOnlyOnUseForgetDirective = true;
|
||||
}
|
||||
if (firstLine.includes("@gating")) {
|
||||
gating = {
|
||||
source: "ReactForgetFeatureFlag",
|
||||
importSpecifierName: "isForgetEnabled_Fixtures",
|
||||
};
|
||||
}
|
||||
if (firstLine.includes("@instrumentForget")) {
|
||||
instrumentForget = {
|
||||
source: "react-forget-runtime",
|
||||
importSpecifierName: "useRenderCounter",
|
||||
};
|
||||
}
|
||||
if (firstLine.includes("@panicOnBailout false")) {
|
||||
panicOnBailout = false;
|
||||
}
|
||||
if (firstLine.includes("@memoizeJsxElements false")) {
|
||||
memoizeJsxElements = false;
|
||||
}
|
||||
if (firstLine.includes("@enableAssumeHooksFollowRulesOfReact true")) {
|
||||
enableAssumeHooksFollowRulesOfReact = true;
|
||||
}
|
||||
if (firstLine.includes("@enableTreatHooksAsFunctions false")) {
|
||||
enableTreatHooksAsFunctions = false;
|
||||
}
|
||||
if (firstLine.includes("@disableAllMemoization true")) {
|
||||
disableAllMemoization = true;
|
||||
}
|
||||
if (firstLine.includes("@validateRefAccessDuringRender false")) {
|
||||
validateRefAccessDuringRender = false;
|
||||
}
|
||||
if (firstLine.includes("@validateNoSetStateInRender false")) {
|
||||
validateNoSetStateInRender = false;
|
||||
}
|
||||
if (firstLine.includes("@enableOptimizeFunctionExpressions false")) {
|
||||
enableOptimizeFunctionExpressions = false;
|
||||
}
|
||||
if (firstLine.includes("@enableEmitFreeze")) {
|
||||
enableEmitFreeze = {
|
||||
source: "react-forget-runtime",
|
||||
importSpecifierName: "makeReadOnly",
|
||||
};
|
||||
}
|
||||
if (firstLine.indexOf("@reactScriptDirective") !== -1) {
|
||||
enableOnlyOnReactScript = true;
|
||||
language = "flow";
|
||||
}
|
||||
|
||||
return pluginFn(input, basename, language, {
|
||||
environment: {
|
||||
customHooks: new Map([
|
||||
[
|
||||
"useFreeze",
|
||||
{
|
||||
valueKind: "frozen" as ValueKind,
|
||||
effectKind: "freeze" as Effect,
|
||||
},
|
||||
],
|
||||
]),
|
||||
enableAssumeHooksFollowRulesOfReact,
|
||||
enableFunctionCallSignatureOptimizations: true,
|
||||
disableAllMemoization,
|
||||
enableTreatHooksAsFunctions,
|
||||
inlineUseMemo: true,
|
||||
memoizeJsxElements,
|
||||
validateHooksUsage: true,
|
||||
validateRefAccessDuringRender,
|
||||
validateFrozenLambdas: true,
|
||||
validateNoSetStateInRender,
|
||||
enableEmitFreeze,
|
||||
enableOptimizeFunctionExpressions,
|
||||
assertValidMutableRanges: true,
|
||||
},
|
||||
enableOnlyOnUseForgetDirective,
|
||||
enableOnlyOnReactScript,
|
||||
logger: null,
|
||||
gating,
|
||||
instrumentForget,
|
||||
panicOnBailout,
|
||||
isDev: true,
|
||||
noEmit: false,
|
||||
});
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
import path from "path";
|
||||
|
||||
// We assume this is run from `babel-plugin-react-forget`
|
||||
export const COMPILER_PATH = path.join(
|
||||
process.cwd(),
|
||||
"dist",
|
||||
"Babel",
|
||||
"RunReactForgetBabelPlugin.js"
|
||||
);
|
||||
export const LOGGER_PATH = path.join(
|
||||
process.cwd(),
|
||||
"dist",
|
||||
"Utils",
|
||||
"logger.js"
|
||||
);
|
||||
export const FIXTURES_PATH = path.join(
|
||||
process.cwd(),
|
||||
"src",
|
||||
"__tests__",
|
||||
"fixtures",
|
||||
"compiler"
|
||||
);
|
||||
export const FILTER_FILENAME = "testfilter.txt";
|
||||
export const FILTER_PATH = path.join(process.cwd(), FILTER_FILENAME);
|
||||
@@ -0,0 +1,161 @@
|
||||
import fs from "fs/promises";
|
||||
import glob from "glob";
|
||||
import invariant from "invariant";
|
||||
import path from "path";
|
||||
import { FILTER_PATH, FIXTURES_PATH } from "./constants";
|
||||
|
||||
export type TestFilter =
|
||||
| {
|
||||
kind: "only";
|
||||
paths: Array<string>;
|
||||
}
|
||||
| {
|
||||
kind: "skip";
|
||||
paths: Array<string>;
|
||||
};
|
||||
|
||||
async function exists(file: string): Promise<boolean> {
|
||||
try {
|
||||
await fs.access(file);
|
||||
return true;
|
||||
} catch {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
export async function readTestFilter(): Promise<TestFilter | null> {
|
||||
if (!(await exists(FILTER_PATH))) {
|
||||
throw new Error(`testfilter file not found at ${FILTER_PATH}`);
|
||||
}
|
||||
|
||||
const input = await fs.readFile(FILTER_PATH, "utf8");
|
||||
const lines = input.trim().split("\n");
|
||||
if (lines.length < 2) {
|
||||
console.warn("Misformed filter file. Expected at least two lines.");
|
||||
return null;
|
||||
}
|
||||
|
||||
let filter: "only" | "skip" | null = null;
|
||||
if (lines[0]!.indexOf("@only") !== -1) {
|
||||
filter = "only";
|
||||
}
|
||||
if (lines[0]!.indexOf("@skip") !== -1) {
|
||||
filter = "skip";
|
||||
}
|
||||
if (filter === null) {
|
||||
console.warn(
|
||||
"Misformed filter file. Expected first line to contain @only or @skip"
|
||||
);
|
||||
return null;
|
||||
}
|
||||
lines.shift();
|
||||
return {
|
||||
kind: filter,
|
||||
paths: lines,
|
||||
};
|
||||
}
|
||||
|
||||
export type TestFixture = {
|
||||
basename: string;
|
||||
inputPath: string;
|
||||
inputExists: boolean;
|
||||
outputPath: string;
|
||||
outputExists: boolean;
|
||||
};
|
||||
|
||||
export function getFixtures(
|
||||
filter: TestFilter | null
|
||||
): Map<string, TestFixture> {
|
||||
// search for fixtures within nested directories
|
||||
const files = glob.sync(`**/*.{js,md}`, {
|
||||
cwd: FIXTURES_PATH,
|
||||
});
|
||||
const fixtures: Map<string, TestFixture> = new Map();
|
||||
|
||||
for (const filePath of files) {
|
||||
const basename = path.basename(
|
||||
path.basename(filePath, ".js"),
|
||||
".expect.md"
|
||||
);
|
||||
// "partial" paths do not include suffixes
|
||||
const partialRelativePath = path.join(path.dirname(filePath), basename);
|
||||
// Replicate jest test behavior
|
||||
if (basename.startsWith("todo.")) {
|
||||
continue;
|
||||
}
|
||||
if (filter) {
|
||||
if (
|
||||
filter.kind === "only" &&
|
||||
filter.paths.indexOf(partialRelativePath) === -1
|
||||
) {
|
||||
continue;
|
||||
} else if (
|
||||
filter.kind === "skip" &&
|
||||
filter.paths.indexOf(partialRelativePath) !== -1
|
||||
) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
let fixtureInfo = fixtures.get(partialRelativePath);
|
||||
if (fixtureInfo === undefined) {
|
||||
const partialAbsolutePath = path.join(FIXTURES_PATH, partialRelativePath);
|
||||
fixtureInfo = {
|
||||
basename,
|
||||
inputPath: `${partialAbsolutePath}.js`,
|
||||
inputExists: false,
|
||||
outputPath: `${partialAbsolutePath}.expect.md`,
|
||||
outputExists: false,
|
||||
};
|
||||
fixtures.set(partialRelativePath, fixtureInfo);
|
||||
}
|
||||
|
||||
if (filePath.endsWith(".js")) {
|
||||
fixtureInfo.inputExists = true;
|
||||
} else {
|
||||
fixtureInfo.outputExists = true;
|
||||
}
|
||||
}
|
||||
|
||||
return fixtures;
|
||||
}
|
||||
|
||||
function wrapWithTripleBackticks(s: string, ext: string | null = null): string {
|
||||
return `\`\`\`${ext ?? ""}
|
||||
${s}
|
||||
\`\`\``;
|
||||
}
|
||||
|
||||
export function writeOutputToString(
|
||||
input: string,
|
||||
output: string | null,
|
||||
error: Error | null
|
||||
) {
|
||||
// leading newline intentional
|
||||
let result = `
|
||||
## Input
|
||||
|
||||
${wrapWithTripleBackticks(input, "javascript")}
|
||||
`; // trailing newline + space internional
|
||||
|
||||
if (output != null) {
|
||||
result += `
|
||||
## Code
|
||||
|
||||
${output == null ? "[ none ]" : wrapWithTripleBackticks(output, "javascript")}
|
||||
`;
|
||||
} else {
|
||||
result += "\n";
|
||||
}
|
||||
|
||||
if (error != null) {
|
||||
const errorMessage = error.message.replace(/^\/.*?:\s/, "");
|
||||
|
||||
result += `
|
||||
## Error
|
||||
|
||||
${wrapWithTripleBackticks(errorMessage)}
|
||||
\n`;
|
||||
}
|
||||
return result + ` `;
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
export * from "./constants";
|
||||
export * from "./fixture-utils";
|
||||
export * from "./compiler-utils";
|
||||
@@ -0,0 +1,20 @@
|
||||
{
|
||||
"extends": "@tsconfig/node16-strictest/tsconfig.json",
|
||||
"compilerOptions": {
|
||||
"declaration": true,
|
||||
"outDir": "dist",
|
||||
"jsx": "react-jsxdev",
|
||||
|
||||
// weaken strictness from preset
|
||||
"importsNotUsedAsValues": "remove",
|
||||
"noUncheckedIndexedAccess": false,
|
||||
"noUnusedParameters": false,
|
||||
"useUnknownInCatchVariables": false,
|
||||
"target": "ES2015",
|
||||
// ideally turn off only during dev, or on a per-file basis
|
||||
"noUnusedLocals": false,
|
||||
"sourceMap": true
|
||||
},
|
||||
"exclude": ["node_modules"],
|
||||
"include": ["src/**/*.ts"]
|
||||
}
|
||||
@@ -9,7 +9,7 @@
|
||||
"src"
|
||||
],
|
||||
"scripts": {
|
||||
"build": "rimraf dist && tsc",
|
||||
"build": "yarn workspace fixture-test-utils run build && rimraf dist && tsc",
|
||||
"test": "echo 'no tests'",
|
||||
"lint": "yarn eslint src"
|
||||
},
|
||||
@@ -22,7 +22,8 @@
|
||||
"chalk": "4",
|
||||
"readline": "^1.3.0",
|
||||
"typescript": "^5.1.0",
|
||||
"yargs": "^17.7.1"
|
||||
"yargs": "^17.7.1",
|
||||
"fixture-test-utils": "*"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/node": "^18.7.18",
|
||||
|
||||
@@ -6,9 +6,8 @@
|
||||
*/
|
||||
|
||||
import type { runReactForgetBabelPlugin as RunReactForgetBabelPlugin } from 'babel-plugin-react-forget/src/Babel/RunReactForgetBabelPlugin';
|
||||
import type { Effect, ValueKind } from 'babel-plugin-react-forget/src/HIR';
|
||||
import { TestFixture, transformFixtureInput, writeOutputToString } from 'fixture-test-utils';
|
||||
import fs from "fs/promises";
|
||||
import { exists } from "./utils";
|
||||
|
||||
const originalConsoleError = console.error;
|
||||
|
||||
@@ -33,12 +32,6 @@ export type TestResult = {
|
||||
unexpectedError: string | null;
|
||||
};
|
||||
|
||||
export type TestFixture = {
|
||||
basename: string;
|
||||
inputPath: string;
|
||||
outputPath: string;
|
||||
};
|
||||
|
||||
export async function compile(
|
||||
compilerPath: string,
|
||||
loggerPath: string,
|
||||
@@ -54,11 +47,11 @@ export async function compile(
|
||||
clearRequireCache();
|
||||
}
|
||||
version = compilerVersion;
|
||||
const { inputPath, outputPath, basename } = fixture;
|
||||
const input = (await exists(inputPath))
|
||||
const { inputPath, inputExists, outputPath, outputExists, basename } = fixture;
|
||||
const input = inputExists
|
||||
? await fs.readFile(inputPath, "utf8")
|
||||
: null;
|
||||
const expected = (await exists(outputPath))
|
||||
const expected = outputExists
|
||||
? await fs.readFile(outputPath, "utf8")
|
||||
: null;
|
||||
|
||||
@@ -86,108 +79,7 @@ export async function compile(
|
||||
// since console log order is non-deterministic
|
||||
const shouldLogPragma = input.split("\n")[0].includes("@debug");
|
||||
toggleLogging(isOnlyFixture && shouldLogPragma);
|
||||
|
||||
// Extract the first line to quickly check for custom test directives
|
||||
const firstLine = input.substring(0, input.indexOf("\n"));
|
||||
let language = parseLanguage(firstLine);
|
||||
|
||||
let enableOnlyOnUseForgetDirective = false;
|
||||
let gating = null;
|
||||
let instrumentForget = null;
|
||||
let panicOnBailout = true;
|
||||
let memoizeJsxElements = true;
|
||||
let enableAssumeHooksFollowRulesOfReact = false;
|
||||
let enableTreatHooksAsFunctions = true;
|
||||
let disableAllMemoization = false;
|
||||
let validateRefAccessDuringRender = true;
|
||||
let validateNoSetStateInRender = true;
|
||||
let enableEmitFreeze = null;
|
||||
let enableOptimizeFunctionExpressions = true;
|
||||
let enableOnlyOnReactScript = false;
|
||||
if (firstLine.indexOf("@forgetDirective") !== -1) {
|
||||
enableOnlyOnUseForgetDirective = true;
|
||||
}
|
||||
if (firstLine.includes("@gating")) {
|
||||
gating = {
|
||||
source: "ReactForgetFeatureFlag",
|
||||
importSpecifierName: "isForgetEnabled_Fixtures",
|
||||
};
|
||||
}
|
||||
if (firstLine.includes("@instrumentForget")) {
|
||||
instrumentForget = {
|
||||
source: "react-forget-runtime",
|
||||
importSpecifierName: "useRenderCounter",
|
||||
};
|
||||
}
|
||||
if (firstLine.includes("@panicOnBailout false")) {
|
||||
panicOnBailout = false;
|
||||
}
|
||||
if (firstLine.includes("@memoizeJsxElements false")) {
|
||||
memoizeJsxElements = false;
|
||||
}
|
||||
if (firstLine.includes("@enableAssumeHooksFollowRulesOfReact true")) {
|
||||
enableAssumeHooksFollowRulesOfReact = true;
|
||||
}
|
||||
if (firstLine.includes("@enableTreatHooksAsFunctions false")) {
|
||||
enableTreatHooksAsFunctions = false;
|
||||
}
|
||||
if (firstLine.includes("@disableAllMemoization true")) {
|
||||
disableAllMemoization = true;
|
||||
}
|
||||
if (firstLine.includes("@validateRefAccessDuringRender false")) {
|
||||
validateRefAccessDuringRender = false;
|
||||
}
|
||||
if (firstLine.includes("@validateNoSetStateInRender false")) {
|
||||
validateNoSetStateInRender = false;
|
||||
}
|
||||
if (firstLine.includes("@enableOptimizeFunctionExpressions false")) {
|
||||
enableOptimizeFunctionExpressions = false;
|
||||
}
|
||||
if (firstLine.includes("@enableEmitFreeze")) {
|
||||
enableEmitFreeze = {
|
||||
source: "react-forget-runtime",
|
||||
importSpecifierName: "makeReadOnly",
|
||||
};
|
||||
}
|
||||
if (firstLine.indexOf("@reactScriptDirective") !== -1) {
|
||||
enableOnlyOnReactScript = true;
|
||||
language = "flow";
|
||||
}
|
||||
|
||||
code = runReactForgetBabelPlugin(input, basename, language, {
|
||||
environment: {
|
||||
customHooks: new Map([
|
||||
[
|
||||
"useFreeze",
|
||||
{
|
||||
valueKind: "frozen" as ValueKind,
|
||||
effectKind: "freeze" as Effect,
|
||||
},
|
||||
],
|
||||
]),
|
||||
enableAssumeHooksFollowRulesOfReact,
|
||||
enableFunctionCallSignatureOptimizations: true,
|
||||
disableAllMemoization,
|
||||
enableTreatHooksAsFunctions,
|
||||
inlineUseMemo: true,
|
||||
memoizeJsxElements,
|
||||
validateHooksUsage: true,
|
||||
validateRefAccessDuringRender,
|
||||
validateFrozenLambdas: true,
|
||||
validateNoSetStateInRender,
|
||||
enableEmitFreeze,
|
||||
enableOptimizeFunctionExpressions,
|
||||
assertValidMutableRanges: true,
|
||||
},
|
||||
enableOnlyOnUseForgetDirective,
|
||||
enableOnlyOnReactScript,
|
||||
logger: null,
|
||||
gating,
|
||||
instrumentForget,
|
||||
panicOnBailout,
|
||||
isDev: true,
|
||||
noEmit: false,
|
||||
}).code;
|
||||
code = transformFixtureInput(input, basename, runReactForgetBabelPlugin).code;
|
||||
} catch (e) {
|
||||
error = e;
|
||||
}
|
||||
@@ -202,92 +94,28 @@ export async function compile(
|
||||
}
|
||||
}
|
||||
|
||||
let output: string;
|
||||
const output = writeOutputToString(input, code, error);
|
||||
const expectError = basename.startsWith("error.");
|
||||
let unexpectedError: string | null = null;
|
||||
if (expectError) {
|
||||
if (error === null) {
|
||||
return {
|
||||
inputPath,
|
||||
outputPath,
|
||||
actual: code,
|
||||
expected,
|
||||
unexpectedError: `Expected an error to be thrown for fixture: '${basename}', remove the 'error.' prefix if an error is not expected.`,
|
||||
};
|
||||
} else if (code != null) {
|
||||
output = `${formatOutput(code)}\n${formatErrorOutput(error)}`;
|
||||
} else {
|
||||
output = formatErrorOutput(error);
|
||||
unexpectedError = `Expected an error to be thrown for fixture: '${basename}', remove the 'error.' prefix if an error is not expected.`;
|
||||
}
|
||||
} else {
|
||||
if (error !== null) {
|
||||
return {
|
||||
inputPath,
|
||||
outputPath,
|
||||
actual: code,
|
||||
expected,
|
||||
unexpectedError: `Expected fixture '${basename}' to succeed but it failed with error:\n\n${error.message}`,
|
||||
};
|
||||
unexpectedError = `Expected fixture '${basename}' to succeed but it failed with error:\n\n${error.message}`;
|
||||
} else if (code == null || code.length === 0) {
|
||||
unexpectedError = `Expected output for fixture '${basename}'.`;
|
||||
}
|
||||
if (code == null || code.length === 0) {
|
||||
return {
|
||||
inputPath,
|
||||
outputPath,
|
||||
actual: code,
|
||||
expected,
|
||||
unexpectedError: `Expected output for fixture '${basename}'.`,
|
||||
};
|
||||
}
|
||||
output = formatOutput(code);
|
||||
}
|
||||
|
||||
// leading newline intentional
|
||||
const actual = `
|
||||
## Input
|
||||
|
||||
${wrapWithTripleBackticks(input, "javascript")}
|
||||
|
||||
${output}
|
||||
`; // trailing newline + space internional
|
||||
|
||||
console.error = originalConsoleError;
|
||||
|
||||
return {
|
||||
inputPath,
|
||||
outputPath,
|
||||
actual,
|
||||
actual: output,
|
||||
expected,
|
||||
unexpectedError: null,
|
||||
unexpectedError,
|
||||
};
|
||||
}
|
||||
|
||||
function formatErrorOutput(error: Error): string {
|
||||
error.message = error.message.replace(/^\/.*?:\s/, "");
|
||||
return `
|
||||
## Error
|
||||
|
||||
${wrapWithTripleBackticks(error.message)}
|
||||
`;
|
||||
}
|
||||
|
||||
function formatOutput(code: string): string {
|
||||
return `
|
||||
## Code
|
||||
|
||||
${wrapWithTripleBackticks(code, "javascript")}
|
||||
`.trim();
|
||||
}
|
||||
|
||||
function wrapWithTripleBackticks(s: string, ext: string | null = null): string {
|
||||
return `\`\`\`${ext ?? ""}
|
||||
${s}
|
||||
\`\`\``;
|
||||
}
|
||||
|
||||
const FlowPragmas = [/\/\/\s@flow$/gm, /\*\s@flow$/gm];
|
||||
function parseLanguage(source: string): "flow" | "typescript" {
|
||||
let useFlow = false;
|
||||
for (const flowPragma of FlowPragmas) {
|
||||
useFlow ||= !!source.match(flowPragma);
|
||||
}
|
||||
return useFlow ? "flow" : "typescript";
|
||||
}
|
||||
|
||||
@@ -17,7 +17,7 @@ if (process.stdin.isTTY) {
|
||||
process.stdin.setRawMode(true);
|
||||
}
|
||||
|
||||
process.stdin.on("keypress", function (chunk, key) {
|
||||
process.stdin.on("keypress", function (_, key) {
|
||||
if (key && key.name === "c" && key.ctrl) {
|
||||
// handle sigint
|
||||
if (childProc) {
|
||||
|
||||
@@ -7,8 +7,8 @@
|
||||
|
||||
import watcher from "@parcel/watcher";
|
||||
import chalk from "chalk";
|
||||
import { COMPILER_PATH, LOGGER_PATH, FIXTURES_PATH, FILTER_FILENAME, FILTER_PATH, readTestFilter, TestFilter } from 'fixture-test-utils';
|
||||
import fs from "fs/promises";
|
||||
import glob from "glob";
|
||||
import invariant from "invariant";
|
||||
import { diff } from "jest-diff";
|
||||
import { Worker } from "jest-worker";
|
||||
@@ -19,26 +19,10 @@ import ts from "typescript";
|
||||
import yargs from "yargs";
|
||||
import { hideBin } from "yargs/helpers";
|
||||
import { TestResult } from "./compiler-worker";
|
||||
import * as compiler from "./compiler-worker.js";
|
||||
import { exists } from "./utils";
|
||||
import * as compiler from "./compiler-worker";
|
||||
import {getFixtures} from 'fixture-test-utils';
|
||||
|
||||
const WORKER_PATH = require.resolve("./compiler-worker.js");
|
||||
const COMPILER_PATH = path.join(
|
||||
process.cwd(),
|
||||
"dist",
|
||||
"Babel",
|
||||
"RunReactForgetBabelPlugin.js"
|
||||
);
|
||||
const LOGGER_PATH = path.join(process.cwd(), "dist", "Utils", "logger.js");
|
||||
const FIXTURES_PATH = path.join(
|
||||
process.cwd(),
|
||||
"src",
|
||||
"__tests__",
|
||||
"fixtures",
|
||||
"compiler"
|
||||
);
|
||||
const FILTER_FILENAME = "testfilter.txt";
|
||||
const FILTER_PATH = path.join(process.cwd(), FILTER_FILENAME);
|
||||
|
||||
readline.emitKeypressEvents(process.stdin);
|
||||
|
||||
@@ -109,49 +93,6 @@ function clearConsole() {
|
||||
console.log("\u001Bc");
|
||||
}
|
||||
|
||||
function getFixtures(
|
||||
filter: TestFilter | null
|
||||
): Map<string, compiler.TestFixture> {
|
||||
// search for fixtures within nested directories
|
||||
const files = glob.sync(`**/*.{js,md}`, {
|
||||
cwd: FIXTURES_PATH,
|
||||
});
|
||||
const fixtures = new Map();
|
||||
|
||||
for (const filePath of files) {
|
||||
const basename = path.basename(
|
||||
path.basename(filePath, ".js"),
|
||||
".expect.md"
|
||||
);
|
||||
// "partial" paths do not include suffixes
|
||||
const partialRelativePath = path.join(path.dirname(filePath), basename);
|
||||
const partialAbsolutePath = path.join(FIXTURES_PATH, partialRelativePath);
|
||||
// Replicate jest test behavior
|
||||
if (basename.startsWith("todo.")) {
|
||||
continue;
|
||||
}
|
||||
if (filter) {
|
||||
if (
|
||||
filter.kind === "only" &&
|
||||
filter.paths.indexOf(partialRelativePath) === -1
|
||||
) {
|
||||
continue;
|
||||
} else if (
|
||||
filter.kind === "skip" &&
|
||||
filter.paths.indexOf(partialRelativePath) !== -1
|
||||
) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
fixtures.set(partialRelativePath, {
|
||||
basename,
|
||||
inputPath: `${partialAbsolutePath}.js`,
|
||||
outputPath: `${partialAbsolutePath}.expect.md`,
|
||||
});
|
||||
}
|
||||
// console.log("fixtures!", JSON.stringify(Array.from(fixtures.keys())));
|
||||
return fixtures;
|
||||
}
|
||||
/**
|
||||
* Do a test run and return the test results
|
||||
*/
|
||||
@@ -395,48 +336,6 @@ enum Mode {
|
||||
Update = "Update",
|
||||
}
|
||||
|
||||
type TestFilter =
|
||||
| {
|
||||
kind: "only";
|
||||
paths: Array<string>;
|
||||
}
|
||||
| {
|
||||
kind: "skip";
|
||||
paths: Array<string>;
|
||||
};
|
||||
|
||||
async function readTestFilter(): Promise<TestFilter | null> {
|
||||
if (!(await exists(FILTER_PATH))) {
|
||||
throw new Error(`testfilter file not found at ${FILTER_PATH}`);
|
||||
}
|
||||
|
||||
const input = await fs.readFile(FILTER_PATH, "utf8");
|
||||
const lines = input.trim().split("\n");
|
||||
if (lines.length < 2) {
|
||||
console.warn("Misformed filter file. Expected at least two lines.");
|
||||
return null;
|
||||
}
|
||||
|
||||
let filter: "only" | "skip" | null = null;
|
||||
if (lines[0]!.indexOf("@only") !== -1) {
|
||||
filter = "only";
|
||||
}
|
||||
if (lines[0]!.indexOf("@skip") !== -1) {
|
||||
filter = "skip";
|
||||
}
|
||||
if (filter === null) {
|
||||
console.warn(
|
||||
"Misformed filter file. Expected first line to contain @only or @skip"
|
||||
);
|
||||
return null;
|
||||
}
|
||||
lines.shift();
|
||||
return {
|
||||
kind: filter,
|
||||
paths: lines,
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Runs the compiler in watch or single-execution mode
|
||||
*/
|
||||
|
||||
@@ -1,17 +0,0 @@
|
||||
/**
|
||||
* 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 fs from "fs/promises";
|
||||
|
||||
export async function exists(file: string): Promise<boolean> {
|
||||
try {
|
||||
await fs.access(file);
|
||||
return true;
|
||||
} catch {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user