/** * Copyright (c) Facebook, Inc. and its affiliates. * * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * * * @format */ "use strict"; function _objectWithoutProperties(source, excluded) { if (source == null) return {}; var target = _objectWithoutPropertiesLoose(source, excluded); var key, i; if (Object.getOwnPropertySymbols) { var sourceSymbolKeys = Object.getOwnPropertySymbols(source); for (i = 0; i < sourceSymbolKeys.length; i++) { key = sourceSymbolKeys[i]; if (excluded.indexOf(key) >= 0) continue; if (!Object.prototype.propertyIsEnumerable.call(source, key)) continue; target[key] = source[key]; } } return target; } function _objectWithoutPropertiesLoose(source, excluded) { if (source == null) return {}; var target = {}; var sourceKeys = Object.keys(source); var key, i; for (i = 0; i < sourceKeys.length; i++) { key = sourceKeys[i]; if (excluded.indexOf(key) >= 0) continue; target[key] = source[key]; } return target; } function _objectSpread(target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i] != null ? arguments[i] : {}; var ownKeys = Object.keys(source); if (typeof Object.getOwnPropertySymbols === "function") { ownKeys = ownKeys.concat( Object.getOwnPropertySymbols(source).filter(function(sym) { return Object.getOwnPropertyDescriptor(source, sym).enumerable; }) ); } ownKeys.forEach(function(key) { _defineProperty(target, key, source[key]); }); } return target; } function _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; } function asyncGeneratorStep(gen, resolve, reject, _next, _throw, key, arg) { try { var info = gen[key](arg); var value = info.value; } catch (error) { reject(error); return; } if (info.done) { resolve(value); } else { Promise.resolve(value).then(_next, _throw); } } function _asyncToGenerator(fn) { return function() { var self = this, args = arguments; return new Promise(function(resolve, reject) { var gen = fn.apply(self, args); function _next(value) { asyncGeneratorStep(gen, resolve, reject, _next, _throw, "next", value); } function _throw(err) { asyncGeneratorStep(gen, resolve, reject, _next, _throw, "throw", err); } _next(undefined); }); }; } const JsFileWrapping = require("../ModuleGraph/worker/JsFileWrapping"); const assetTransformer = require("../assetTransformer"); const babylon = require("@babel/parser"); const collectDependencies = require("../ModuleGraph/worker/collectDependencies"); const constantFoldingPlugin = require("./worker/constant-folding-plugin"); const generateImportNames = require("../ModuleGraph/worker/generateImportNames"); const generate = require("@babel/generator").default; const getKeyFromFiles = require("../lib/getKeyFromFiles"); const getMinifier = require("../lib/getMinifier"); const importExportPlugin = require("./worker/import-export-plugin"); const inlinePlugin = require("./worker/inline-plugin"); const inlineRequiresPlugin = require("babel-preset-fbjs/plugins/inline-requires"); const normalizePseudoglobals = require("./worker/normalizePseudoglobals"); const _require = require("@babel/core"), transformFromAstSync = _require.transformFromAstSync; const _require2 = require("metro-cache"), stableHash = _require2.stableHash; const types = require("@babel/types"); const _require3 = require("metro-source-map"), fromRawMappings = _require3.fromRawMappings, toBabelSegments = _require3.toBabelSegments, toSegmentTuple = _require3.toSegmentTuple; function getDynamicDepsBehavior(inPackages, filename) { switch (inPackages) { case "reject": return "reject"; case "throwAtRuntime": const isPackage = /(?:^|[/\\])node_modules[/\\]/.test(filename); return isPackage ? inPackages : "reject"; default: inPackages; throw new Error( `invalid value for dynamic deps behavior: \`${inPackages}\`` ); } } class JsTransformer { constructor(projectRoot, config) { this._projectRoot = projectRoot; this._config = config; } transform(filename, data, options) { var _this = this; return _asyncToGenerator(function*() { const sourceCode = data.toString("utf8"); let type = "js/module"; if (options.type === "asset") { type = "js/module/asset"; } if (options.type === "script") { type = "js/script"; } if (filename.endsWith(".json")) { let code = JsFileWrapping.wrapJson(sourceCode); let map = []; if (options.minify) { var _ref = yield _this._minifyCode(filename, code, sourceCode, map); map = _ref.map; code = _ref.code; } return { dependencies: [], output: [ { data: { code, map }, type } ] }; } // $FlowFixMe TODO t26372934 Plugin system const transformer = require(_this._config.babelTransformerPath); const transformerArgs = { filename, options: _objectSpread({}, options, { enableBabelRCLookup: _this._config.enableBabelRCLookup, enableBabelRuntime: _this._config.enableBabelRuntime, // Inline requires are now performed at a secondary step. We cannot // unfortunately remove it from the internal transformer, since this one // is used by other tooling, and this would affect it. inlineRequires: false, projectRoot: _this._projectRoot, publicPath: _this._config.publicPath }), plugins: [], src: sourceCode }; const transformResult = type === "js/module/asset" ? yield assetTransformer.transform( transformerArgs, _this._config.assetRegistryPath, _this._config.assetPlugins ) : yield transformer.transform(transformerArgs); // Transformers can ouptut null ASTs (if they ignore the file). In that case // we need to parse the module source code to get their AST. let ast = transformResult.ast || babylon.parse(sourceCode, { sourceType: "unambiguous" }); const _generateImportNames = generateImportNames(ast), importDefault = _generateImportNames.importDefault, importAll = _generateImportNames.importAll; // Add "use strict" if the file was parsed as a module, and the directive did // not exist yet. const directives = ast.program.directives; if ( ast.program.sourceType === "module" && directives.findIndex(d => d.value.value === "use strict") === -1 ) { directives.push(types.directive(types.directiveLiteral("use strict"))); } // Perform the import-export transform (in case it's still needed), then // fold requires and perform constant folding (if in dev). const plugins = []; const opts = _objectSpread({}, options, { inlineableCalls: [importDefault, importAll], importDefault, importAll }); if (options.experimentalImportSupport) { plugins.push([importExportPlugin, opts]); } if (options.inlineRequires) { plugins.push([inlineRequiresPlugin, opts]); } if (!options.dev) { plugins.push([constantFoldingPlugin, opts]); plugins.push([inlinePlugin, opts]); } var _transformFromAstSync = transformFromAstSync(ast, "", { ast: true, babelrc: false, code: false, configFile: false, comments: false, compact: false, filename, plugins, sourceMaps: false }); ast = _transformFromAstSync.ast; let dependencyMapName = ""; let dependencies; let wrappedAst; // If the module to transform is a script (meaning that is not part of the // dependency graph and it code will just be prepended to the bundle modules), // we need to wrap it differently than a commonJS module (also, scripts do // not have dependencies). if (type === "js/script") { dependencies = []; wrappedAst = JsFileWrapping.wrapPolyfill(ast); } else { try { const opts = { asyncRequireModulePath: _this._config.asyncRequireModulePath, dynamicRequires: getDynamicDepsBehavior( _this._config.dynamicDepsInPackages, filename ), inlineableCalls: [importDefault, importAll], keepRequireNames: options.dev }; var _collectDependencies = collectDependencies(ast, opts); dependencies = _collectDependencies.dependencies; dependencyMapName = _collectDependencies.dependencyMapName; } catch (error) { if (error instanceof collectDependencies.InvalidRequireCallError) { throw new InvalidRequireCallError(error, filename); } throw error; } var _JsFileWrapping$wrapM = JsFileWrapping.wrapModule( ast, importDefault, importAll, dependencyMapName ); wrappedAst = _JsFileWrapping$wrapM.ast; } const reserved = options.minify && data.length <= _this._config.optimizationSizeLimit ? normalizePseudoglobals(wrappedAst) : []; const result = generate( wrappedAst, { comments: false, compact: false, filename, retainLines: false, sourceFileName: filename, sourceMaps: true }, sourceCode ); let map = result.rawMappings ? result.rawMappings.map(toSegmentTuple) : []; let code = result.code; if (options.minify) { var _ref2 = yield _this._minifyCode( filename, result.code, sourceCode, map, reserved ); map = _ref2.map; code = _ref2.code; } return { dependencies, output: [ { data: { code, map }, type } ] }; })(); } _minifyCode(filename, code, source, map) { var _this2 = this; let reserved = arguments.length > 4 && arguments[4] !== undefined ? arguments[4] : []; return _asyncToGenerator(function*() { const sourceMap = fromRawMappings([ { code, source, map, path: filename } ]).toMap(undefined, {}); const minify = getMinifier(_this2._config.minifierPath); try { const minified = minify({ code, map: sourceMap, filename, reserved, config: _this2._config.minifierConfig }); return { code: minified.code, map: minified.map ? toBabelSegments(minified.map).map(toSegmentTuple) : [] }; } catch (error) { if (error.constructor.name === "JS_Parse_Error") { throw new Error( `${error.message} in file ${filename} at ${error.line}:${error.col}` ); } throw error; } })(); } getCacheKey() { const _this$_config = this._config, babelTransformerPath = _this$_config.babelTransformerPath, minifierPath = _this$_config.minifierPath, config = _objectWithoutProperties(_this$_config, [ "babelTransformerPath", "minifierPath" ]); const filesKey = getKeyFromFiles([ require.resolve(babelTransformerPath), require.resolve(minifierPath), require.resolve("../ModuleGraph/worker/JsFileWrapping"), require.resolve("../assetTransformer"), require.resolve("../ModuleGraph/worker/collectDependencies"), require.resolve("./worker/constant-folding-plugin"), require.resolve("../lib/getMinifier"), require.resolve("./worker/inline-plugin"), require.resolve("./worker/import-export-plugin"), require.resolve("./worker/normalizePseudoglobals"), require.resolve("../ModuleGraph/worker/optimizeDependencies"), require.resolve("../ModuleGraph/worker/generateImportNames") ]); const babelTransformer = require(babelTransformerPath); const babelTransformerKey = babelTransformer.getCacheKey ? babelTransformer.getCacheKey() : ""; return [ filesKey, stableHash(config).toString("hex"), babelTransformerKey ].join("$"); } } class InvalidRequireCallError extends Error { constructor(innerError, filename) { super(`${filename}:${innerError.message}`); this.innerError = innerError; this.filename = filename; } } module.exports = JsTransformer;