Debugger sourcemaps (#2154)

Summary:
Release Notes:
- Debugger recognizes sourcemaps and opens the correct file/line when debugging in Nuclide.
    - Explanation of how sourcemap translation works in [this Quip](https://fb.quip.com/0uQCAtkuPAHF). This is probably useful to read before attempting to read the changes to `Debugger.js` and `PathNormalizer.js`.
- Removed unnecessary abstractions in `IsStatement` and `DebuggerOptions`.
- Refactored multi-file support to conform to Nuclide initialization protocol
- Refactored `DebuggerConfigArguments` so as to pass sourceFiles without increase Flow cycle length.

Example of FatalError on FB4A: https://www.dropbox.com/s/ig8vrlpcazbygo3/Prepack%20Fatal%20Diagnostic%20on%20FB4A.mov?dl=0

NOTE: Apologies for the large number of commits -- I incorrectly pulled in some from master when rebasing
Closes https://github.com/facebook/prepack/pull/2154

Differential Revision: D8707082

Pulled By: caiismyname

fbshipit-source-id: 812610c0345536a1ad9b480449c16f34018040c3
This commit is contained in:
David Cai 2018-06-29 20:10:31 -07:00 committed by Facebook Github Bot
parent 3240eae91b
commit e79def7cd2
17 changed files with 300 additions and 116 deletions

View File

@ -20,22 +20,24 @@ import * as partialEvaluators from "./partial-evaluators/index.js";
import { Environment } from "./singletons.js";
import { ObjectValue } from "./values/index.js";
import { DebugServer } from "./debugger/server/Debugger.js";
import type { DebugChannel } from "./debugger/server/channel/DebugChannel.js";
import simplifyAndRefineAbstractValue from "./utils/simplifier.js";
import invariant from "./invariant.js";
import type { DebuggerConfigArguments } from "./types";
export default function(
opts: RealmOptions = {},
debugChannel: void | DebugChannel = undefined,
debuggerConfigArgs: void | DebuggerConfigArguments,
statistics: void | RealmStatistics = undefined
): Realm {
initializeSingletons();
let r = new Realm(opts, statistics || new RealmStatistics());
// Presence of debugChannel indicates we wish to use debugger.
if (debugChannel) {
invariant(debugChannel.debuggerIsAttached(), "Debugger intends to be used but is not attached.");
invariant(opts.debuggerConfigArgs !== undefined, "Debugger intends to be used but does not have config arguments.");
r.debuggerInstance = new DebugServer(debugChannel, r, opts.debuggerConfigArgs);
if (debuggerConfigArgs) {
let debugChannel = debuggerConfigArgs.debugChannel;
if (debugChannel) {
invariant(debugChannel.debuggerIsAttached(), "Debugger intends to be used but is not attached.");
r.debuggerInstance = new DebugServer(debugChannel, r, debuggerConfigArgs);
}
}
let i = r.intrinsics;

View File

@ -114,9 +114,10 @@ class PrepackDebugSession extends DebugSession {
let adapterChannel = new AdapterChannel(inFilePath, outFilePath);
this._adapterChannel = adapterChannel;
this._registerMessageCallbacks();
let separatedSourceFiles = args.sourceFile.trim().split(/\s+/g); // Split on all whitespace
let launchArgs: PrepackLaunchArguments = {
kind: "launch",
sourceFiles: args.sourceFiles,
sourceFiles: separatedSourceFiles,
prepackRuntime: args.prepackRuntime,
prepackArguments: args.prepackArguments,
debugInFilePath: inFilePath,

View File

@ -99,7 +99,10 @@ export class AdapterChannel {
if (args.prepackRuntime.length > 0) {
// user specified a Prepack path
runtime = "node";
prepackCommand = [args.prepackRuntime].concat(prepackCommand);
// Increase node's memory allowance so Prepack can handle large inputs
prepackCommand = ["--max_old_space_size=8192", "--stack_size=10000"]
.concat([args.prepackRuntime])
.concat(prepackCommand);
}
this._prepackProcess = child_process.spawn(runtime, prepackCommand);

View File

@ -10,7 +10,7 @@
/* @flow strict */
// More error types will be added as needed
export type DebuggerErrorType = "Invalid command" | "Invalid response" | "Startup Error";
export type DebuggerErrorType = "Invalid command" | "Invalid response" | "Startup Error" | "Invalid input";
export class DebuggerError extends Error {
constructor(errorType: DebuggerErrorType, message: string) {

View File

@ -10,7 +10,6 @@
/* @flow strict */
import * as DebugProtocol from "vscode-debugprotocol";
import type { Severity } from "../../errors.js";
export type DebuggerRequest = {
id: number,
@ -40,10 +39,6 @@ export type PrepackLaunchArguments = {
exitCallback: () => void,
};
export type DebuggerConfigArguments = {
diagnosticSeverity?: Severity,
};
export type Breakpoint = {
filePath: string,
line: number,
@ -167,7 +162,7 @@ export type EvaluateResult = {
export type LaunchRequestArguments = {
...DebugProtocol.LaunchRequestArguments,
noDebug?: boolean,
sourceFiles: Array<string>,
sourceFile: string,
prepackRuntime: string,
prepackArguments: Array<string>,
};

View File

@ -157,7 +157,7 @@ export class UISession {
_processInitializeResponse(response: DebugProtocol.InitializeResponse) {
let launchArgs: LaunchRequestArguments = {
prepackRuntime: this._prepackRuntime,
sourceFiles: this._sourceFiles,
sourceFile: this._sourceFiles.join(" "),
prepackArguments: this._prepackArguments,
};
this._sendLaunchRequest(launchArgs);

View File

@ -13,7 +13,6 @@ import { PerFileBreakpointMap } from "./PerFileBreakpointMap.js";
import { Breakpoint } from "./Breakpoint.js";
import type { Breakpoint as BreakpointType } from "./../common/types.js";
import { BabelNode } from "babel-types";
import { IsStatement } from "./../../methods/is.js";
// Storing BreakpointStores for all source files
export class BreakpointManager {
@ -23,7 +22,6 @@ export class BreakpointManager {
_breakpointMaps: Map<string, PerFileBreakpointMap>;
getStoppableBreakpoint(ast: BabelNode): void | Breakpoint {
if (!IsStatement(ast)) return;
if (ast.loc && ast.loc.source) {
let location = ast.loc;
let filePath = location.source;

View File

@ -25,7 +25,6 @@ import type {
VariablesArguments,
EvaluateArguments,
SourceData,
DebuggerConfigArguments,
} from "./../common/types.js";
import type { Realm } from "./../../realm.js";
import { ExecutionContext } from "./../../realm.js";
@ -42,6 +41,8 @@ import {
} from "./../../environment.js";
import { CompilerDiagnostic } from "../../errors.js";
import type { Severity } from "../../errors.js";
import { SourceMapManager } from "./SourceMapManager.js";
import type { DebuggerConfigArguments } from "../../types";
export class DebugServer {
constructor(channel: DebugChannel, realm: Realm, configArgs: DebuggerConfigArguments) {
@ -52,6 +53,7 @@ export class DebugServer {
this._stepManager = new SteppingManager(this._realm, /* default discard old steppers */ false);
this._stopEventManager = new StopEventManager();
this._diagnosticSeverity = configArgs.diagnosticSeverity || "FatalError";
this._sourceMapManager = new SourceMapManager(configArgs.buckRoot, configArgs.sourcemaps);
this.waitForRun(undefined);
}
// the collection of breakpoints
@ -65,6 +67,7 @@ export class DebugServer {
_lastExecuted: SourceData;
// Severity at which debugger will break when CompilerDiagnostics are generated. Default is Fatal.
_diagnosticSeverity: Severity;
_sourceMapManager: SourceMapManager;
/* Block until adapter says to run
/* ast: the current ast node we are stopped on
@ -87,10 +90,11 @@ export class DebugServer {
if (breakpoint) stoppables.push(breakpoint);
let reason = this._stopEventManager.getDebuggeeStopReason(ast, stoppables);
if (reason) {
invariant(ast.loc && ast.loc.source);
this._channel.sendStoppedResponse(reason, ast.loc.source, ast.loc.start.line, ast.loc.start.column);
invariant(ast.loc && ast.loc !== null);
this.waitForRun(ast.loc);
let location = ast.loc;
invariant(location && location.source);
let absolutePath = this._sourceMapManager.relativeToAbsolute(location.source);
this._channel.sendStoppedResponse(reason, absolutePath, location.start.line, location.start.column);
this.waitForRun(location);
}
}
}
@ -101,6 +105,13 @@ export class DebugServer {
let requestID = request.id;
let command = request.command;
let args = request.arguments;
// Convert incoming location sources to relative paths in order to match internal representation of filenames.
if (args.kind === "breakpoint") {
for (let bp of args.breakpoints) {
bp.filePath = this._sourceMapManager.absoluteToRelative(bp.filePath);
}
}
switch (command) {
case DebugMessage.BREAKPOINT_ADD_COMMAND:
invariant(args.kind === "breakpoint");
@ -182,7 +193,7 @@ export class DebugServer {
let frameInfo: Stackframe = {
id: this._realm.contextStack.length - 1 - i,
functionName: functionName,
fileName: fileName,
fileName: this._sourceMapManager.relativeToAbsolute(fileName), // Outward facing paths must be absolute.
line: line,
column: column,
};
@ -278,12 +289,16 @@ export class DebugServer {
let line = ast.loc.start.line;
let column = ast.loc.start.column;
let stackSize = this._realm.contextStack.length;
// check if the current location is same as the last one
// Check if the current location is same as the last one.
// Does not check columns since column debugging is not supported.
// Column support is unnecessary because these nodes will have been sourcemap-translated.
// Ignoring columns prevents:
// - Lines with multiple AST nodes from triggering the same breakpoint more than once.
// - Step-out from completing in the same line that it was set in.
if (
this._lastExecuted &&
filePath === this._lastExecuted.filePath &&
line === this._lastExecuted.line &&
column === this._lastExecuted.column &&
stackSize === this._lastExecuted.stackSize
) {
return false;
@ -299,18 +314,18 @@ export class DebugServer {
return false;
}
/*
Displays PP error message, then waits for user to run the program to
continue (similar to a breakpoint).
*/
// Displays Prepack error message, then waits for user to run the program to continue (similar to a breakpoint).
handlePrepackError(diagnostic: CompilerDiagnostic) {
invariant(diagnostic.location && diagnostic.location.source);
// The following constructs the message and stop-instruction that is sent to the UI to actually stop the execution.
let location = diagnostic.location;
let absoluteSource = "";
if (location.source !== null) absoluteSource = this._sourceMapManager.relativeToAbsolute(location.source);
let message = `${diagnostic.severity} ${diagnostic.errorCode}: ${diagnostic.message}`;
console.log(message);
this._channel.sendStoppedResponse(
"Diagnostic",
location.source || "",
absoluteSource,
location.start.line,
location.start.column,
message

View File

@ -0,0 +1,227 @@
/**
* Copyright (c) 2017-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*/
/* @flow strict-local */
import type { SourceFile } from "./../../types.js";
import { DebuggerError } from "./../common/DebuggerError.js";
import invariant from "../common/invariant.js";
/**
* Sourcemap paths can come in one of two formats:
* - Relative: The paths include `../` and can be followed from the sourcemap's location
* to arrive at the original source's location. In this format, path conversion
* requires two different prefixes (MapDifference and CommonPrefix) that must be
* discovered from the input paths.
* - Common Directory: The paths take the format of an absolute path (`/foo/bar`) and
* assume there is a common prefix to the path that, when added, will make the path an
* valid absolute path. This prefix is passed in as the `buckRoot` argument.
* Example:
* In a directory structure with /A/B/map.js and /A/C/original.js,
* the sourcemaps would have the following path structures:
* - Relative: ../C/original.js, with `CP` = /A and 'MD' = ../
* - Common Directory: /C/original.js, with `buckRoot` = /A
*/
export class SourceMapManager {
constructor(buckRoot?: string, sourceMaps?: Array<SourceFile>) {
// Use presence of buck root argument to indicate which path format sourcemap prefixes take on.
if (buckRoot !== undefined) {
if (sourceMaps === undefined) {
throw new DebuggerError(
"Invalid input",
"Can't provide a sourcemap directory root without having sourcemaps present"
);
}
this._buckRoot = buckRoot;
if (this._buckRoot[this._buckRoot.length - 1] === "/") {
// Remove trailing slash to prepare for prepending to internal paths.
this._buckRoot = this._buckRoot.slice(0, -1);
}
} else {
// If sourcemaps don't exist, set prefixes to undefined and break.
if (sourceMaps) {
for (let map of sourceMaps) {
if (map.sourceMapContents === undefined || map.sourceMapContents === "") {
this._sourcemapCommonPrefix = undefined;
this._sourcemapMapDifference = undefined;
return;
}
}
} else {
this._sourcemapCommonPrefix = undefined;
this._sourcemapMapDifference = undefined;
return;
}
// Extract common prefix and map difference
let originalSourcePaths = [];
let mapPaths = [];
for (let map of sourceMaps) {
invariant(map.sourceMapContents); // Checked above.
let parsed = JSON.parse(map.sourceMapContents);
// Two formats for sourcemaps exist.
if ("sections" in parsed) {
for (let section of parsed.sections) {
for (let source of section.map.sources) {
originalSourcePaths.push(this._getAbsoluteSourcePath(map.filePath, source));
}
}
} else {
for (let source of parsed.sources) {
originalSourcePaths.push(this._getAbsoluteSourcePath(map.filePath, source));
}
}
mapPaths.push(this._stripEmptyStringBookends(map.filePath.split("/")));
}
let originalSourceCommonPrefix = this._findCommonPrefix(originalSourcePaths);
let originalSourceCPElements = this._stripEmptyStringBookends(originalSourceCommonPrefix.split("/"));
let mapCommonPrefix = this._findCommonPrefix(mapPaths);
let mapCPElements = this._stripEmptyStringBookends(mapCommonPrefix.split("/"));
this._sourcemapCommonPrefix = this._findCommonPrefix([originalSourceCPElements, mapCPElements]);
this._sourcemapMapDifference = this._findMapDifference(this._sourcemapCommonPrefix, mapCommonPrefix);
}
}
// Prefixes used to translate between relative paths stored in AST nodes and absolute paths given to IDE.
_sourcemapCommonPrefix: void | string; // For paths relative to map location. (Used in Babel format)
_sourcemapMapDifference: void | string; // For paths relative to map location. (Used in Babel format)
_buckRoot: void | string; // For paths relative to directory root. (Used in Buck format)
/**
* Assumes that input file and sourcemap are in the same directory.
* Assumes pathToInput is an absolute path and pathToSource is relative.
* Uses pathToSource to find absolute path of original source file.
*/
_getAbsoluteSourcePath(pathToInput: string, pathToSource: string): Array<string> {
// pathToInput is an absolute path to the file being prepacked.
let fullPath = this._stripEmptyStringBookends(pathToInput.split("/"));
// Remove last entry because it is the filename, while we want the parent directory of the input file.
fullPath.pop();
// Traverse the path to the source file.
let steps = pathToSource.split("/");
for (let step of steps) {
switch (step) {
case ".":
break;
case "..":
fullPath.pop();
break;
default:
fullPath.push(step);
break;
}
}
return fullPath;
}
/**
* Finds the longest possible prefix common to all input paths.
* Input paths must be absolute.
* Input is nested array because each path must be separated into elements.
*/
_findCommonPrefix(paths: Array<Array<string>>): string {
// Find the point at which the paths diverge.
let divergenceIndex = 0;
let allPathsMatch = true;
let maxDivergenceIndex = Math.max(...paths.map(path => path.length));
while (allPathsMatch && divergenceIndex < maxDivergenceIndex) {
let entry = paths[0][divergenceIndex]; // Arbitrary choice of 0th path, since we're checking if all entires match.
for (let path of paths) {
if (path[divergenceIndex] !== entry) {
allPathsMatch = false;
break;
}
}
if (allPathsMatch) divergenceIndex += 1;
}
// Edge case: if there's only one path, it will match itself, including the filename at the end.
// For 2+ paths, even if they all share a prefix, the filenames will not match, so this is not needed.
if (paths.length === 1) divergenceIndex -= 1;
// Concatenate prefix into string that's bookended by slashes for use as an absolute path prefix.
return `/${paths[0].slice(0, divergenceIndex).join("/")}/`;
}
/**
* Finds the path that must be followed to arrive at the directory of the
* common prefix from the sourcemap.
*/
_findMapDifference(commonPrefix: string, mapPrefix: string): string {
// Find difference in path between the map's location and the common prefix.
let mapPrefixUniqueElements = this._stripEmptyStringBookends(mapPrefix.replace(commonPrefix, "").split("/"));
let mapDifference = "";
for (let i = 0; i < mapPrefixUniqueElements.length; i++) {
mapDifference = mapDifference.concat("../");
}
return mapDifference;
}
/**
* Takes in ["", "foo", "bar", ""] and returns ["foo", "bar"]
*/
_stripEmptyStringBookends(path: Array<string>): Array<string> {
if (path[0] === "") path.shift();
if (path[path.length - 1] === "") path.pop();
return path;
}
/**
* Used by DebugAdapter to convert relative paths (used internally in debugging/Prepack engine)
* into absolute paths (used by debugging UI/IDE).
*/
relativeToAbsolute(path: string): string {
let absolute;
if (this._buckRoot !== undefined) {
let dirRoot = this._buckRoot;
if (
// If the "relative" path is actually absolute, then don't prepend anything.
this._stripEmptyStringBookends(path.split("/"))[0] ===
this._stripEmptyStringBookends(this._buckRoot.split("/"))[0]
) {
absolute = path;
} else {
let separator = path[0] === "/" ? "" : "/";
absolute = dirRoot + separator + path;
}
} else {
if (this._sourcemapCommonPrefix !== undefined && this._sourcemapMapDifference !== undefined) {
absolute = path.replace(this._sourcemapMapDifference, "");
invariant(this._sourcemapCommonPrefix !== undefined);
absolute = this._sourcemapCommonPrefix + absolute;
} else {
absolute = path;
}
}
return absolute;
}
/**
* Used by DebugAdapter to convert absolute paths (used by debugging UI/IDE)
* into relative paths (used internally in debugging/Prepack engine).
*/
absoluteToRelative(path: string): string {
let relative;
if (this._buckRoot !== undefined) {
relative = path.replace(this._buckRoot, "");
} else {
if (this._sourcemapCommonPrefix !== undefined && this._sourcemapMapDifference !== undefined) {
relative = path.replace(this._sourcemapCommonPrefix, "");
invariant(this._sourcemapMapDifference !== undefined);
relative = this._sourcemapMapDifference + relative;
} else {
relative = path;
}
}
return relative;
}
}

View File

@ -9,7 +9,6 @@
/* @flow strict-local */
import type { SourceData } from "./../common/types.js";
import { IsStatement } from "./../../methods/is.js";
import { BabelNode } from "babel-types";
import invariant from "./../common/invariant.js";
@ -32,8 +31,6 @@ export class Stepper {
// The same node in two different excutions contexts (e.g. recursive call)
// will not be detected. Check the stackSize (via realm) in those cases.
isAstLocationChanged(ast: BabelNode) {
// we should only step to statements
if (!IsStatement(ast)) return false;
let loc = ast.loc;
if (!loc) return false;
let filePath = loc.source;

View File

@ -31,7 +31,6 @@ import { Value } from "../values/index.js";
import invariant from "../invariant.js";
import { HasName, HasCompatibleType } from "./has.js";
import type { BabelNodeExpression, BabelNodeCallExpression, BabelNodeLVal, BabelNodeClassMethod } from "babel-types";
import { BabelNode } from "babel-types";
// ECMA262 22.1.3.1.1
export function IsConcatSpreadable(realm: Realm, _O: Value): boolean {
@ -358,46 +357,3 @@ export function IsStatic(classElement: BabelNodeClassMethod): boolean {
// $FlowFixMe need to backport static property to BabelNodeClassMethod
return classElement.static;
}
export function IsStatement(node: BabelNode): boolean {
switch (node.type) {
case "BlockStatement":
case "BreakStatement":
case "ContinueStatement":
case "DebuggerStatement":
case "DoWhileStatement":
case "EmptyStatement":
case "ExpressionStatement":
case "ForInStatement":
case "ForStatement":
case "FunctionDeclaration":
case "IfStatement":
case "LabeledStatement":
case "ReturnStatement":
case "SwitchStatement":
case "ThrowStatement":
case "TryStatement":
case "VariableDeclaration":
case "WhileStatement":
case "WithStatement":
case "ClassDeclaration":
case "ExportAllDeclaration":
case "ExportDefaultDeclaration":
case "ExportNamedDeclaration":
case "ForOfStatement":
case "ImportDeclaration":
case "DeclareClass":
case "DeclareFunction":
case "DeclareInterface":
case "DeclareModule":
case "DeclareModuleExports":
case "DeclareTypeAlias":
case "DeclareVariable":
case "InterfaceDeclaration":
case "TypeAlias":
case "ForAwaitStatement":
return true;
default:
return false;
}
}

View File

@ -7,10 +7,9 @@
* of patent rights can be found in the PATENTS file in the same directory.
*/
/* @flow strict */
/* @flow strict-local */
import type { ErrorHandler } from "./errors.js";
import type { DebuggerConfigArguments } from "./debugger/common/types";
export type Compatibility =
| "browser"
@ -75,7 +74,6 @@ export type RealmOptions = {
reactOptimizeNestedFunctions?: boolean,
stripFlow?: boolean,
abstractValueImpliesMax?: number,
debuggerConfigArgs?: DebuggerConfigArguments,
};
export type SerializerOptions = {
@ -99,9 +97,4 @@ export type PartialEvaluatorOptions = {
sourceMaps?: boolean,
};
export type DebuggerOptions = {
inFilePath: string,
outFilePath: string,
};
export const defaultOptions = {};

View File

@ -30,7 +30,7 @@ import invariant from "./invariant";
import zipFactory from "node-zip";
import path from "path";
import JSONTokenizer from "./utils/JSONTokenizer.js";
import type { DebuggerConfigArguments } from "./debugger/common/types";
import type { DebuggerConfigArguments } from "./types";
// Prepack helper
declare var __residual: any;
@ -80,6 +80,7 @@ function run(
--repro Create a zip file with all information needed to reproduce a Prepack run"
--cpuprofile Create a CPU profile file for the run that can be loaded into the Chrome JavaScript CPU Profile viewer",
--debugDiagnosticSeverity FatalError | RecoverableError | Warning | Information (default = FatalError). Diagnostic level at which debugger will stop
--debugBuckRoot Root directory that buck assumes when creating sourcemap paths.
`;
let args = Array.from(process.argv);
args.splice(0, 2);
@ -274,6 +275,9 @@ function run(
);
debuggerConfigArgs.diagnosticSeverity = arg;
break;
case "debugBuckRoot":
debuggerConfigArgs.buckRoot = args.shift();
break;
case "help":
const options = [
"-- | input.js",

View File

@ -15,7 +15,6 @@
import { defaultOptions } from "./options";
import { FatalError } from "./errors.js";
import { type PrepackOptions } from "./prepack-options";
import { getDebuggerOptions } from "./prepack-options";
import { prepackNodeCLI, prepackNodeCLISync } from "./prepack-node-environment.js";
import { prepackSources } from "./prepack-standalone.js";
import { type SourceMap } from "./types.js";
@ -142,11 +141,14 @@ export function prepackFileSync(filenames: Array<string>, options: PrepackOption
}
return { filePath: filename, fileContents: code, sourceMapContents: sourceMap };
});
let debugChannel;
// The existence of debug[In/Out]FilePath represents the desire to use the debugger.
if (options.debugInFilePath !== undefined && options.debugOutFilePath !== undefined) {
let debugOptions = getDebuggerOptions(options);
let ioWrapper = new FileIOWrapper(false, debugOptions.inFilePath, debugOptions.outFilePath);
debugChannel = new DebugChannel(ioWrapper);
if (options.debuggerConfigArgs === undefined) options.debuggerConfigArgs = {};
let ioWrapper = new FileIOWrapper(false, options.debugInFilePath, options.debugOutFilePath);
options.debuggerConfigArgs.debugChannel = new DebugChannel(ioWrapper);
options.debuggerConfigArgs.sourcemaps = sourceFiles;
}
return prepackSources(sourceFiles, options, debugChannel, createStatistics(options));
return prepackSources(sourceFiles, options, options.debuggerConfigArgs, createStatistics(options));
}

View File

@ -10,17 +10,9 @@
/* @flow strict-local */
import type { ErrorHandler } from "./errors.js";
import type {
SerializerOptions,
RealmOptions,
Compatibility,
DebuggerOptions,
ReactOutputTypes,
InvariantModeTypes,
} from "./options";
import type { SerializerOptions, RealmOptions, Compatibility, ReactOutputTypes, InvariantModeTypes } from "./options";
import { Realm } from "./realm.js";
import invariant from "./invariant.js";
import type { DebuggerConfigArguments } from "./debugger/common/types";
import type { DebuggerConfigArguments } from "./types";
export type PrepackOptions = {|
additionalGlobals?: Realm => void,
@ -156,13 +148,3 @@ export function getSerializerOptions({
}
return result;
}
export function getDebuggerOptions({ debugInFilePath, debugOutFilePath }: PrepackOptions): DebuggerOptions {
invariant(debugInFilePath !== undefined, "Debugger invoked without input file path");
invariant(debugOutFilePath !== undefined, "Debugger invoked without output file path");
let result: DebuggerOptions = {
inFilePath: debugInFilePath,
outFilePath: debugOutFilePath,
};
return result;
}

View File

@ -23,7 +23,6 @@ import type { PrepackOptions } from "./prepack-options";
import { defaultOptions } from "./options";
import invariant from "./invariant.js";
import { version } from "../package.json";
import type { DebugChannel } from "./debugger/server/channel/DebugChannel.js";
import { type SerializedResult } from "./serializer/types.js";
import { SerializerStatistics } from "./serializer/statistics.js";
import { ResidualHeapVisitor } from "./serializer/ResidualHeapVisitor.js";
@ -31,16 +30,17 @@ import { Modules } from "./utils/modules.js";
import { Logger } from "./utils/logger.js";
import { Generator } from "./utils/generator.js";
import { AbstractObjectValue, AbstractValue, ObjectValue } from "./values/index.js";
import type { DebuggerConfigArguments } from "./types";
export function prepackSources(
sources: Array<SourceFile>,
options: PrepackOptions = defaultOptions,
debugChannel: DebugChannel | void = undefined,
debuggerConfigArgs: void | DebuggerConfigArguments,
statistics: SerializerStatistics | void = undefined
): SerializedResult {
let realmOptions = getRealmOptions(options);
realmOptions.errorHandler = options.errorHandler;
let realm = construct_realm(realmOptions, debugChannel, statistics || new SerializerStatistics());
let realm = construct_realm(realmOptions, debuggerConfigArgs, statistics || new SerializerStatistics());
initializeGlobals(realm);
if (typeof options.additionalGlobals === "function") {
options.additionalGlobals(realm);
@ -82,6 +82,7 @@ export function prepackSources(
sourceMapContents: serialized.map && JSON.stringify(serialized.map),
},
];
let debugChannel = debuggerConfigArgs ? debuggerConfigArgs.debugChannel : undefined;
realm = construct_realm(realmOptions, debugChannel);
initializeGlobals(realm);
if (typeof options.additionalGlobals === "function") {

View File

@ -52,6 +52,7 @@ import type {
import type { Bindings, Effects, EvaluationResult, PropertyBindings, CreatedObjects, Realm } from "./realm.js";
import { CompilerDiagnostic } from "./errors.js";
import type { Severity } from "./errors.js";
import type { DebugChannel } from "./debugger/server/channel/DebugChannel.js";
export const ElementSize = {
Float32: 4,
@ -1053,3 +1054,10 @@ export type UtilsType = {|
getTypeFromName: string => void | typeof Value,
describeValue: Value => string,
|};
export type DebuggerConfigArguments = {
diagnosticSeverity?: Severity,
sourcemaps?: Array<SourceFile>,
buckRoot?: string,
debugChannel?: DebugChannel,
};