mirror of
https://github.com/facebook/react.git
synced 2026-02-21 19:31:52 +00:00
[compiler][snap] Fixes to relative path resolution; compile subcommand (#35688)
More snap improvements for use with agents: * `yarn snap compile [--debug] <path>` for compiling any file, optionally with debug logs * `yarn snap minimize <path>` now accepts path as a positional param for consistency w 'compile' command * Both compile/minimize commands properly handle paths relative to the compiler/ directory. When using `yarn snap` the current working directory is compiler/packages/snap, but you're generally running it from the compiler directory so this matches expectations of callers better.
This commit is contained in:
@@ -294,6 +294,15 @@ yarn snap -p <fixture-name>
|
||||
# Run with debug output (shows all passes)
|
||||
yarn snap -p <fixture-name> -d
|
||||
|
||||
# Compile any file (not just fixtures) and see output
|
||||
yarn snap compile <path>
|
||||
|
||||
# Compile any file with debug output (alternative to yarn snap -d -p when you don't have a fixture)
|
||||
yarn snap compile --debug <path>
|
||||
|
||||
# Minimize a failing test case to its minimal reproduction
|
||||
yarn snap minimize <path>
|
||||
|
||||
# Update expected outputs
|
||||
yarn snap -u
|
||||
```
|
||||
|
||||
@@ -7,19 +7,21 @@
|
||||
|
||||
import path from 'path';
|
||||
|
||||
export const PROJECT_ROOT = path.join(process.cwd(), '..', '..');
|
||||
|
||||
// We assume this is run from `babel-plugin-react-compiler`
|
||||
export const PROJECT_ROOT = path.normalize(
|
||||
path.join(process.cwd(), '..', 'babel-plugin-react-compiler'),
|
||||
export const BABEL_PLUGIN_ROOT = path.normalize(
|
||||
path.join(PROJECT_ROOT, 'packages', 'babel-plugin-react-compiler'),
|
||||
);
|
||||
|
||||
export const PROJECT_SRC = path.normalize(
|
||||
path.join(PROJECT_ROOT, 'dist', 'index.js'),
|
||||
export const BABEL_PLUGIN_SRC = path.normalize(
|
||||
path.join(BABEL_PLUGIN_ROOT, 'dist', 'index.js'),
|
||||
);
|
||||
export const PRINT_HIR_IMPORT = 'printFunctionWithOutlined';
|
||||
export const PRINT_REACTIVE_IR_IMPORT = 'printReactiveFunction';
|
||||
export const PARSE_CONFIG_PRAGMA_IMPORT = 'parseConfigPragmaForTests';
|
||||
export const FIXTURES_PATH = path.join(
|
||||
PROJECT_ROOT,
|
||||
BABEL_PLUGIN_ROOT,
|
||||
'src',
|
||||
'__tests__',
|
||||
'fixtures',
|
||||
|
||||
@@ -12,7 +12,7 @@ import traverse from '@babel/traverse';
|
||||
import * as t from '@babel/types';
|
||||
import type {parseConfigPragmaForTests as ParseConfigPragma} from 'babel-plugin-react-compiler/src/Utils/TestUtils';
|
||||
import {parseInput} from './compiler.js';
|
||||
import {PARSE_CONFIG_PRAGMA_IMPORT, PROJECT_SRC} from './constants.js';
|
||||
import {PARSE_CONFIG_PRAGMA_IMPORT, BABEL_PLUGIN_SRC} from './constants.js';
|
||||
|
||||
type CompileSuccess = {kind: 'success'};
|
||||
type CompileParseError = {kind: 'parse_error'; message: string};
|
||||
@@ -1919,7 +1919,7 @@ export function minimize(
|
||||
sourceType: 'module' | 'script',
|
||||
): MinimizeResult {
|
||||
// Load the compiler plugin
|
||||
const importedCompilerPlugin = require(PROJECT_SRC) as Record<
|
||||
const importedCompilerPlugin = require(BABEL_PLUGIN_SRC) as Record<
|
||||
string,
|
||||
unknown
|
||||
>;
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
import watcher from '@parcel/watcher';
|
||||
import path from 'path';
|
||||
import ts from 'typescript';
|
||||
import {FIXTURES_PATH, PROJECT_ROOT} from './constants';
|
||||
import {FIXTURES_PATH, BABEL_PLUGIN_ROOT} from './constants';
|
||||
import {TestFilter, getFixtures} from './fixture-utils';
|
||||
import {execSync} from 'child_process';
|
||||
|
||||
@@ -17,7 +17,7 @@ export function watchSrc(
|
||||
onComplete: (isSuccess: boolean) => void,
|
||||
): ts.WatchOfConfigFile<ts.SemanticDiagnosticsBuilderProgram> {
|
||||
const configPath = ts.findConfigFile(
|
||||
/*searchPath*/ PROJECT_ROOT,
|
||||
/*searchPath*/ BABEL_PLUGIN_ROOT,
|
||||
ts.sys.fileExists,
|
||||
'tsconfig.json',
|
||||
);
|
||||
@@ -166,7 +166,7 @@ function subscribeTsc(
|
||||
let isCompilerBuildValid = false;
|
||||
if (isTypecheckSuccess) {
|
||||
try {
|
||||
execSync('yarn build', {cwd: PROJECT_ROOT});
|
||||
execSync('yarn build', {cwd: BABEL_PLUGIN_ROOT});
|
||||
console.log('Built compiler successfully with tsup');
|
||||
isCompilerBuildValid = true;
|
||||
} catch (e) {
|
||||
|
||||
@@ -5,7 +5,6 @@
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
import {codeFrameColumns} from '@babel/code-frame';
|
||||
import type {PluginObj} from '@babel/core';
|
||||
import type {parseConfigPragmaForTests as ParseConfigPragma} from 'babel-plugin-react-compiler/src/Utils/TestUtils';
|
||||
import type {printFunctionWithOutlined as PrintFunctionWithOutlined} from 'babel-plugin-react-compiler/src/HIR/PrintHIR';
|
||||
@@ -15,7 +14,7 @@ import {
|
||||
PARSE_CONFIG_PRAGMA_IMPORT,
|
||||
PRINT_HIR_IMPORT,
|
||||
PRINT_REACTIVE_IR_IMPORT,
|
||||
PROJECT_SRC,
|
||||
BABEL_PLUGIN_SRC,
|
||||
} from './constants';
|
||||
import {TestFixture, getBasename, isExpectError} from './fixture-utils';
|
||||
import {TestResult, writeOutputToString} from './reporter';
|
||||
@@ -65,7 +64,7 @@ async function compile(
|
||||
let compileResult: TransformResult | null = null;
|
||||
let error: string | null = null;
|
||||
try {
|
||||
const importedCompilerPlugin = require(PROJECT_SRC) as Record<
|
||||
const importedCompilerPlugin = require(BABEL_PLUGIN_SRC) as Record<
|
||||
string,
|
||||
unknown
|
||||
>;
|
||||
|
||||
@@ -12,7 +12,7 @@ import * as readline from 'readline';
|
||||
import ts from 'typescript';
|
||||
import yargs from 'yargs';
|
||||
import {hideBin} from 'yargs/helpers';
|
||||
import {PROJECT_ROOT} from './constants';
|
||||
import {BABEL_PLUGIN_ROOT, PROJECT_ROOT} from './constants';
|
||||
import {TestFilter, getFixtures} from './fixture-utils';
|
||||
import {TestResult, TestResults, report, update} from './reporter';
|
||||
import {
|
||||
@@ -26,7 +26,14 @@ import {execSync} from 'child_process';
|
||||
import fs from 'fs';
|
||||
import path from 'path';
|
||||
import {minimize} from './minimize';
|
||||
import {parseLanguage, parseSourceType} from './compiler';
|
||||
import {parseInput, parseLanguage, parseSourceType} from './compiler';
|
||||
import {
|
||||
PARSE_CONFIG_PRAGMA_IMPORT,
|
||||
PRINT_HIR_IMPORT,
|
||||
PRINT_REACTIVE_IR_IMPORT,
|
||||
BABEL_PLUGIN_SRC,
|
||||
} from './constants';
|
||||
import chalk from 'chalk';
|
||||
|
||||
const WORKER_PATH = require.resolve('./runner-worker.js');
|
||||
const NUM_WORKERS = cpus().length - 1;
|
||||
@@ -48,6 +55,11 @@ type MinimizeOptions = {
|
||||
update: boolean;
|
||||
};
|
||||
|
||||
type CompileOptions = {
|
||||
path: string;
|
||||
debug: boolean;
|
||||
};
|
||||
|
||||
async function runTestCommand(opts: TestOptions): Promise<void> {
|
||||
const worker: Worker & typeof runnerWorker = new Worker(WORKER_PATH, {
|
||||
enableWorkerThreads: opts.workerThreads,
|
||||
@@ -106,7 +118,7 @@ async function runTestCommand(opts: TestOptions): Promise<void> {
|
||||
);
|
||||
} else {
|
||||
try {
|
||||
execSync('yarn build', {cwd: PROJECT_ROOT});
|
||||
execSync('yarn build', {cwd: BABEL_PLUGIN_ROOT});
|
||||
console.log('Built compiler successfully with tsup');
|
||||
|
||||
// Determine which filter to use
|
||||
@@ -147,7 +159,7 @@ async function runMinimizeCommand(opts: MinimizeOptions): Promise<void> {
|
||||
// Resolve the input path
|
||||
const inputPath = path.isAbsolute(opts.path)
|
||||
? opts.path
|
||||
: path.resolve(process.cwd(), opts.path);
|
||||
: path.resolve(PROJECT_ROOT, opts.path);
|
||||
|
||||
// Check if file exists
|
||||
if (!fs.existsSync(inputPath)) {
|
||||
@@ -196,6 +208,128 @@ async function runMinimizeCommand(opts: MinimizeOptions): Promise<void> {
|
||||
}
|
||||
}
|
||||
|
||||
async function runCompileCommand(opts: CompileOptions): Promise<void> {
|
||||
// Resolve the input path
|
||||
const inputPath = path.isAbsolute(opts.path)
|
||||
? opts.path
|
||||
: path.resolve(PROJECT_ROOT, opts.path);
|
||||
|
||||
// Check if file exists
|
||||
if (!fs.existsSync(inputPath)) {
|
||||
console.error(`Error: File not found: ${inputPath}`);
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
// Read the input file
|
||||
const input = fs.readFileSync(inputPath, 'utf-8');
|
||||
const filename = path.basename(inputPath);
|
||||
const firstLine = input.substring(0, input.indexOf('\n'));
|
||||
const language = parseLanguage(firstLine);
|
||||
const sourceType = parseSourceType(firstLine);
|
||||
|
||||
// Import the compiler
|
||||
const importedCompilerPlugin = require(BABEL_PLUGIN_SRC) as Record<
|
||||
string,
|
||||
any
|
||||
>;
|
||||
const BabelPluginReactCompiler = importedCompilerPlugin['default'];
|
||||
const parseConfigPragmaForTests =
|
||||
importedCompilerPlugin[PARSE_CONFIG_PRAGMA_IMPORT];
|
||||
const printFunctionWithOutlined = importedCompilerPlugin[PRINT_HIR_IMPORT];
|
||||
const printReactiveFunctionWithOutlined =
|
||||
importedCompilerPlugin[PRINT_REACTIVE_IR_IMPORT];
|
||||
const EffectEnum = importedCompilerPlugin['Effect'];
|
||||
const ValueKindEnum = importedCompilerPlugin['ValueKind'];
|
||||
const ValueReasonEnum = importedCompilerPlugin['ValueReason'];
|
||||
|
||||
// Setup debug logger
|
||||
let lastLogged: string | null = null;
|
||||
const debugIRLogger = opts.debug
|
||||
? (value: any) => {
|
||||
let printed: string;
|
||||
switch (value.kind) {
|
||||
case 'hir':
|
||||
printed = printFunctionWithOutlined(value.value);
|
||||
break;
|
||||
case 'reactive':
|
||||
printed = printReactiveFunctionWithOutlined(value.value);
|
||||
break;
|
||||
case 'debug':
|
||||
printed = value.value;
|
||||
break;
|
||||
case 'ast':
|
||||
printed = '(ast)';
|
||||
break;
|
||||
default:
|
||||
printed = String(value);
|
||||
}
|
||||
|
||||
if (printed !== lastLogged) {
|
||||
lastLogged = printed;
|
||||
console.log(`${chalk.green(value.name)}:\n${printed}\n`);
|
||||
} else {
|
||||
console.log(`${chalk.blue(value.name)}: (no change)\n`);
|
||||
}
|
||||
}
|
||||
: () => {};
|
||||
|
||||
// Parse the input
|
||||
let ast;
|
||||
try {
|
||||
ast = parseInput(input, filename, language, sourceType);
|
||||
} catch (e: any) {
|
||||
console.error(`Parse error: ${e.message}`);
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
// Build plugin options
|
||||
const config = parseConfigPragmaForTests(firstLine, {compilationMode: 'all'});
|
||||
const options = {
|
||||
...config,
|
||||
environment: {
|
||||
...config.environment,
|
||||
},
|
||||
logger: {
|
||||
logEvent: () => {},
|
||||
debugLogIRs: debugIRLogger,
|
||||
},
|
||||
enableReanimatedCheck: false,
|
||||
};
|
||||
|
||||
// Compile
|
||||
const {transformFromAstSync} = require('@babel/core');
|
||||
try {
|
||||
const result = transformFromAstSync(ast, input, {
|
||||
filename: '/' + filename,
|
||||
highlightCode: false,
|
||||
retainLines: true,
|
||||
compact: true,
|
||||
plugins: [[BabelPluginReactCompiler, options]],
|
||||
sourceType: 'module',
|
||||
ast: false,
|
||||
cloneInputAst: true,
|
||||
configFile: false,
|
||||
babelrc: false,
|
||||
});
|
||||
|
||||
if (result?.code != null) {
|
||||
// Format the output
|
||||
const prettier = require('prettier');
|
||||
const formatted = await prettier.format(result.code, {
|
||||
semi: true,
|
||||
parser: language === 'typescript' ? 'babel-ts' : 'flow',
|
||||
});
|
||||
console.log(formatted);
|
||||
} else {
|
||||
console.error('Error: No code emitted from compiler');
|
||||
process.exit(1);
|
||||
}
|
||||
} catch (e: any) {
|
||||
console.error(e.message);
|
||||
process.exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
yargs(hideBin(process.argv))
|
||||
.command(
|
||||
['test', '$0'],
|
||||
@@ -245,14 +379,15 @@ yargs(hideBin(process.argv))
|
||||
},
|
||||
)
|
||||
.command(
|
||||
'minimize',
|
||||
'minimize <path>',
|
||||
'Minimize a test case to reproduce a compiler error',
|
||||
yargs => {
|
||||
return yargs
|
||||
.string('path')
|
||||
.alias('p', 'path')
|
||||
.describe('path', 'Path to the file to minimize')
|
||||
.demandOption('path')
|
||||
.positional('path', {
|
||||
describe: 'Path to the file to minimize',
|
||||
type: 'string',
|
||||
demandOption: true,
|
||||
})
|
||||
.boolean('update')
|
||||
.alias('u', 'update')
|
||||
.describe(
|
||||
@@ -265,6 +400,25 @@ yargs(hideBin(process.argv))
|
||||
await runMinimizeCommand(argv as unknown as MinimizeOptions);
|
||||
},
|
||||
)
|
||||
.command(
|
||||
'compile <path>',
|
||||
'Compile a file with the React Compiler',
|
||||
yargs => {
|
||||
return yargs
|
||||
.positional('path', {
|
||||
describe: 'Path to the file to compile',
|
||||
type: 'string',
|
||||
demandOption: true,
|
||||
})
|
||||
.boolean('debug')
|
||||
.alias('d', 'debug')
|
||||
.describe('debug', 'Enable debug logging to print HIR for each pass')
|
||||
.default('debug', false);
|
||||
},
|
||||
async argv => {
|
||||
await runCompileCommand(argv as unknown as CompileOptions);
|
||||
},
|
||||
)
|
||||
.help('help')
|
||||
.strict()
|
||||
.demandCommand()
|
||||
|
||||
Reference in New Issue
Block a user