UNPKG

prepack

Version:

Execute a JS bundle, serialize global state and side effects to a snapshot that can be quickly restored.

408 lines (377 loc) 15.8 kB
"use strict"; var _errors = require("./errors.js"); var _options = require("./options.js"); require("./serializer/types.js"); var _prepackNode = require("./prepack-node.js"); var _fs = require("fs"); var _fs2 = _interopRequireDefault(_fs); var _v = require("v8"); var _v2 = _interopRequireDefault(_v); var _package = require("../package.json"); var _invariant = require("./invariant"); var _invariant2 = _interopRequireDefault(_invariant); var _nodeZip = require("node-zip"); var _nodeZip2 = _interopRequireDefault(_nodeZip); var _path = require("path"); var _path2 = _interopRequireDefault(_path); function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } // Prepack helper /** * 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. */ /* eslint-disable no-shadow */ function run(Object, Array, console, JSON, process, prepackStdin, prepackFileSync, FatalError, CompatibilityValues, fs) { let HELP_STR = ` input The name of the file to run Prepack over (for web please provide the single js bundle file) --out The name of the output file --compatibility The target environment for Prepack [${CompatibilityValues.map(v => `"${v}"`).join(", ")}] --mathRandomSeed If you want Prepack to evaluate Math.random() calls, please provide a seed. --srcmapIn The input sourcemap filename. If present, Prepack will output a sourcemap that maps from the original file (pre-input sourcemap) to Prepack's output --srcmapOut The output sourcemap filename. --maxStackDepth Specify the maximum call stack depth. --timeout The amount of time in seconds until Prepack should time out. --abstractEffectsInAdditionalFunctions Experimental flag to allow abstract effectful function calls. --lazyObjectsRuntime Enable lazy objects feature and specify the JS runtime that support this feature. --debugNames Changes the output of Prepack so that for named functions and variables that get emitted into Prepack's output, the original name is appended as a suffix to Prepack's generated identifier. --speculate Enable speculative initialization of modules (for the module system Prepack has builtin knowledge about). Prepack will try to execute all factory functions it is able to. --trace Traces the order of module initialization. --serialize Serializes the partially evaluated global environment as a program that recreates it. (default = true) --check [start[, count]] Check residual functions for diagnostic messages. Do not serialize or produce residual code. --residual Produces the residual program that results after constant folding. --profile Enables console logging of profile information of different phases of prepack. --statsFile The name of the output file where statistics will be written to. --heapGraphFilePath The name of the output file where heap graph will be written to. --inlineExpressions When generating code, tells prepack to avoid naming expressions when they are only used once, and instead inline them where they are used. --simpleClosures When generating code, tells prepack to not defer initializing closures --omitInvariants When generating code, tells prepack to omit writing invariants. (Invariants generated by default.) --emitConcreteModel Synthesize concrete model values for abstract models(defined by __assumeDataProperty). --version Output the version number. --repro Create a zip file with all information needed to reproduce a Prepack run" `; let args = Array.from(process.argv); args.splice(0, 2); let inputFilenames = []; let outputFilename; let check; let compatibility; let mathRandomSeed; let inputSourceMap; let outputSourceMap; let statsFileName; let maxStackDepth; let timeout; let debugIdentifiers; let lazyObjectsRuntime; let heapGraphFilePath; let debugInFilePath; let debugOutFilePath; let reactOutput = "create-element"; let reproFilePath; let flags = { initializeMoreModules: false, trace: false, debugNames: false, omitInvariants: false, emitConcreteModel: false, inlineExpressions: false, simpleClosures: false, abstractEffectsInAdditionalFunctions: false, logStatistics: false, logModules: false, delayInitializations: false, delayUnsupportedRequires: false, accelerateUnsupportedRequires: true, internalDebug: false, debugScopes: false, serialize: false, residual: false, profile: false, reactEnabled: false }; let reproArguments = []; let reproFileNames = []; let inputFile = fileName => { reproFileNames.push(fileName); return _path2.default.basename(fileName); }; while (args.length) { let arg = args.shift(); if (!arg.startsWith("--")) { inputFilenames.push(arg); reproArguments.push(inputFile(arg)); } else { arg = arg.slice(2); switch (arg) { case "out": arg = args.shift(); outputFilename = arg; // skip for repro purposes break; case "compatibility": arg = args.shift(); if (!CompatibilityValues.includes(arg)) { console.error(`Unsupported compatibility: ${arg}`); process.exit(1); } compatibility = arg; reproArguments.push("--compatibility", compatibility); break; case "mathRandomSeed": mathRandomSeed = args.shift(); reproArguments.push("--mathRandomSeed", mathRandomSeed); break; case "srcmapIn": inputSourceMap = args.shift(); reproArguments.push("--srcmapIn", inputFile(inputSourceMap)); break; case "srcmapOut": outputSourceMap = args.shift(); // skip for repro purposes break; case "statsFile": statsFileName = args.shift(); // skip for repro purposes break; case "maxStackDepth": let value = args.shift(); if (isNaN(value)) { console.error("Stack depth value must be a number"); process.exit(1); } maxStackDepth = parseInt(value, 10); reproArguments.push("--maxStackDepth", maxStackDepth.toString()); break; case "timeout": let seconds = args.shift(); if (isNaN(seconds)) { console.error("Timeout must be a number"); process.exit(1); } timeout = parseInt(seconds, 10) * 1000; reproArguments.push("--timeout", timeout.toString()); break; case "debugIdentifiers": let debugIdentifiersString = args.shift(); debugIdentifiers = debugIdentifiersString.split(","); reproArguments.push("--debugIdentifiers", debugIdentifiersString); break; case "check": let range = args.shift(); if (range.startsWith("--")) { args.unshift(range); range = "0"; } let pair = range.split(","); if (pair.length === 1) pair.push(Number.MAX_SAFE_INTEGER); let start = +pair[0]; if (start < 0 || !Number.isInteger(start)) { console.error("check start offset must be a number"); process.exit(1); } let count = +pair[1]; if (count < 0 || !Number.isInteger(count)) { console.error("check count must be a number"); process.exit(1); } check = [start, count]; reproArguments.push("--check", range); break; case "debugInFilePath": debugInFilePath = args.shift(); // skip for repro purposes break; case "debugOutFilePath": debugOutFilePath = args.shift(); // skip for repro purposes break; case "lazyObjectsRuntime": lazyObjectsRuntime = args.shift(); reproArguments.push("--lazyObjectsRuntime", lazyObjectsRuntime); break; case "heapGraphFilePath": heapGraphFilePath = args.shift(); // skip for repro purposes break; case "reactOutput": arg = args.shift(); if (!_options.ReactOutputValues.includes(arg)) { console.error(`Unsupported reactOutput: ${arg}`); process.exit(1); } reactOutput = arg; reproArguments.push("--reactOutput", reactOutput); break; case "repro": reproFilePath = args.shift(); // skip for repro purposes break; case "help": const options = ["-- | input.js", "--out output.js", "--compatibility jsc", "--mathRandomSeed seedvalue", "--srcmapIn inputMap", "--srcmapOut outputMap", "--maxStackDepth depthValue", "--timeout seconds", "--debugIdentifiers id1,id2,...", "--check [start[, number]]", "--lazyObjectsRuntime lazyObjectsRuntimeName", "--heapGraphFilePath heapGraphFilePath", "--reactOutput " + _options.ReactOutputValues.join(" | "), "--repro reprofile.zip"]; for (let flag of Object.keys(flags)) options.push(`--${flag}`); console.log("Usage: prepack.js " + options.map(option => `[ ${option} ]`).join(" ") + "\n" + HELP_STR); return; case "version": console.log(_package.version); return; default: if (arg in flags) { flags[arg] = true; reproArguments.push("--" + arg); } else { console.error(`Unknown option: ${arg}`); process.exit(1); } } } } if (reproFilePath !== undefined) { const zip = (0, _nodeZip2.default)(); for (let fileName of reproFileNames) { let content = fs.readFileSync(fileName, "utf8"); zip.file(_path2.default.basename(fileName), content); } zip.file("repro.sh", `#!/bin/bash if [ -z "$PREPACK" ]; then echo "Set environment variable PREPACK to bin/prepack.js in your Prepack directory." else node "$PREPACK" ${reproArguments.map(a => `"${a}"`).join(" ")} fi `); const data = zip.generate({ base64: false, compression: "DEFLATE" }); fs.writeFileSync(reproFilePath, data, "binary"); } if (!flags.serialize && !flags.residual) flags.serialize = true; if (check) { flags.serialize = false; flags.residual = false; } let resolvedOptions = Object.assign({}, { compatibility, mathRandomSeed, inputSourceMapFilename: inputSourceMap, errorHandler, sourceMaps: !!outputSourceMap, maxStackDepth, timeout, debugIdentifiers, check, lazyObjectsRuntime, debugInFilePath, debugOutFilePath, reactOutput }, flags); if (heapGraphFilePath) resolvedOptions.heapGraphFormat = "DotLanguage"; if (lazyObjectsRuntime && (resolvedOptions.delayInitializations || resolvedOptions.inlineExpressions)) { console.error("lazy objects feature is incompatible with delayInitializations and inlineExpressions options"); process.exit(1); } let errors = new Map(); let errorList = []; function errorHandler(diagnostic) { if (diagnostic.location) errors.set(diagnostic.location, diagnostic);else errorList.push(diagnostic); return "Recover"; } function printDiagnostics() { let foundFatal = false; if (errors.size > 0 || errorList.length > 0) { console.error("Errors found while prepacking"); let printError = (error, locString = "At an unknown location") => { foundFatal = foundFatal || error.severity === "FatalError"; console.error(`${locString} ${error.severity} ${error.errorCode}: ${error.message}` + ` (https://github.com/facebook/prepack/wiki/${error.errorCode})`); let callStack = error.callStack; if (callStack !== undefined) { let eolPos = callStack.indexOf("\n"); if (eolPos > 0) console.error(callStack.substring(eolPos + 1)); } }; for (let [loc, error] of errors) { let sourceMessage = ""; switch (loc.source) { case null: case "": sourceMessage = "In an unknown source file"; break; case "no-filename-specified": sourceMessage = "In stdin"; break; default: (0, _invariant2.default)(loc !== null && loc.source !== null); sourceMessage = `In input file ${loc.source}`; break; } let locString = `${sourceMessage}(${loc.start.line}:${loc.start.column + 1})`; printError(error, locString); } for (let error of errorList) printError(error); } return foundFatal; } try { if (inputFilenames.length === 0) { prepackStdin(resolvedOptions, processSerializedCode, printDiagnostics); return; } let serialized = prepackFileSync(inputFilenames, resolvedOptions); printDiagnostics(); if (resolvedOptions.serialize && serialized) processSerializedCode(serialized); } catch (err) { printDiagnostics(); //FatalErrors must have generated at least one CompilerDiagnostic. if (err instanceof FatalError) { (0, _invariant2.default)(errors.size > 0 || errorList.length > 0, "FatalError must generate at least one CompilerDiagnostic"); } else { // if it is not a FatalError, it means prepack failed, and we should display the Prepack stack trace. console.error(err.stack); process.exit(1); } } function processSerializedCode(serialized) { if (serialized.code === "") { console.error("Prepack returned empty code."); return; } if (outputFilename) { console.log(`Prepacked source code written to ${outputFilename}.`); fs.writeFileSync(outputFilename, serialized.code); } else { console.log(serialized.code); } if (statsFileName) { if (serialized.statistics === undefined || serialized.timingStats === undefined) { return; } let stats = { SerializerStatistics: serialized.statistics, TimingStatistics: serialized.timingStats, MemoryStatistics: _v2.default.getHeapStatistics() }; fs.writeFileSync(statsFileName, JSON.stringify(stats)); } if (outputSourceMap) { fs.writeFileSync(outputSourceMap, serialized.map ? JSON.stringify(serialized.map) : ""); } if (heapGraphFilePath) { (0, _invariant2.default)(serialized.heapGraph); fs.writeFileSync(heapGraphFilePath, serialized.heapGraph); } } return true; } if (typeof __residual === "function") { // If we're running inside of Prepack. This is the residual function we'll // want to leave untouched in the final program. __residual("boolean", run, Object, Array, console, JSON, process, _prepackNode.prepackStdin, _prepackNode.prepackFileSync, _errors.FatalError, _options.CompatibilityValues, _fs2.default); } else { run(Object, Array, console, JSON, process, _prepackNode.prepackStdin, _prepackNode.prepackFileSync, _errors.FatalError, _options.CompatibilityValues, _fs2.default); } //# sourceMappingURL=prepack-cli.js.map