/**
* 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;