@expo/cli
Version:
326 lines (325 loc) • 14.5 kB
JavaScript
/**
* Copyright © 2022 650 Industries.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/ "use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
function _export(target, all) {
for(var name in all)Object.defineProperty(target, name, {
enumerable: true,
get: all[name]
});
}
_export(exports, {
getDefaultMetroResolver: function() {
return getDefaultMetroResolver;
},
withMetroErrorReportingResolver: function() {
return withMetroErrorReportingResolver;
},
withMetroMutatedResolverContext: function() {
return withMetroMutatedResolverContext;
},
withMetroResolvers: function() {
return withMetroResolvers;
}
});
function _chalk() {
const data = /*#__PURE__*/ _interop_require_default(require("chalk"));
_chalk = function() {
return data;
};
return data;
}
function _metroresolver() {
const data = /*#__PURE__*/ _interop_require_wildcard(require("metro-resolver"));
_metroresolver = function() {
return data;
};
return data;
}
function _path() {
const data = /*#__PURE__*/ _interop_require_default(require("path"));
_path = function() {
return data;
};
return data;
}
const _metroErrors = require("./metroErrors");
const _env = require("../../../utils/env");
function _interop_require_default(obj) {
return obj && obj.__esModule ? obj : {
default: obj
};
}
function _getRequireWildcardCache(nodeInterop) {
if (typeof WeakMap !== "function") return null;
var cacheBabelInterop = new WeakMap();
var cacheNodeInterop = new WeakMap();
return (_getRequireWildcardCache = function(nodeInterop) {
return nodeInterop ? cacheNodeInterop : cacheBabelInterop;
})(nodeInterop);
}
function _interop_require_wildcard(obj, nodeInterop) {
if (!nodeInterop && obj && obj.__esModule) {
return obj;
}
if (obj === null || typeof obj !== "object" && typeof obj !== "function") {
return {
default: obj
};
}
var cache = _getRequireWildcardCache(nodeInterop);
if (cache && cache.has(obj)) {
return cache.get(obj);
}
var newObj = {
__proto__: null
};
var hasPropertyDescriptor = Object.defineProperty && Object.getOwnPropertyDescriptor;
for(var key in obj){
if (key !== "default" && Object.prototype.hasOwnProperty.call(obj, key)) {
var desc = hasPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : null;
if (desc && (desc.get || desc.set)) {
Object.defineProperty(newObj, key, desc);
} else {
newObj[key] = obj[key];
}
}
}
newObj.default = obj;
if (cache) {
cache.set(obj, newObj);
}
return newObj;
}
const debug = require('debug')('expo:metro:withMetroResolvers');
function getDefaultMetroResolver(projectRoot) {
return (context, moduleName, platform)=>{
return _metroresolver().resolve(context, moduleName, platform);
};
}
function optionsKeyForContext(context) {
const canonicalize = require('metro-core/src/canonicalize');
// Compound key for the resolver cache
return JSON.stringify(context.customResolverOptions ?? {}, canonicalize) ?? '';
}
function withMetroResolvers(config, resolvers) {
var _config_resolver, _config_resolver1;
debug(`Appending ${resolvers.length} custom resolvers to Metro config. (has custom resolver: ${!!((_config_resolver = config.resolver) == null ? void 0 : _config_resolver.resolveRequest)})`);
// const hasUserDefinedResolver = !!config.resolver?.resolveRequest;
// const defaultResolveRequest = getDefaultMetroResolver(projectRoot);
const originalResolveRequest = (_config_resolver1 = config.resolver) == null ? void 0 : _config_resolver1.resolveRequest;
return {
...config,
resolver: {
...config.resolver,
resolveRequest (context, moduleName, platform) {
const upstreamResolveRequest = context.resolveRequest;
const universalContext = {
...context,
resolveRequest (ctx, moduleName, platform) {
for (const resolver of resolvers){
try {
const res = resolver(ctx, moduleName, platform);
if (res) {
return res;
}
} catch (error) {
var _ctx_customResolverOptions;
// If the error is directly related to a resolver not being able to resolve a module, then
// we can ignore the error and try the next resolver. Otherwise, we should throw the error.
const isResolutionError = (0, _metroErrors.isFailedToResolveNameError)(error) || (0, _metroErrors.isFailedToResolvePathError)(error);
if (!isResolutionError) {
throw error;
}
debug(`Custom resolver (${resolver.name || '<anonymous>'}) threw: ${error.constructor.name}. (module: ${moduleName}, platform: ${platform}, env: ${(_ctx_customResolverOptions = ctx.customResolverOptions) == null ? void 0 : _ctx_customResolverOptions.environment}, origin: ${ctx.originModulePath})`);
}
}
// If we haven't returned by now, use the original resolver or upstream resolver.
return upstreamResolveRequest(ctx, moduleName, platform);
}
};
// If the user defined a resolver, run it first and depend on the documented
// chaining logic: https://facebook.github.io/metro/docs/resolution/#resolution-algorithm
//
// config.resolver.resolveRequest = (context, moduleName, platform) => {
//
// // Do work...
//
// return context.resolveRequest(context, moduleName, platform);
// };
const firstResolver = originalResolveRequest ?? universalContext.resolveRequest;
return firstResolver(universalContext, moduleName, platform);
}
}
};
}
function withMetroMutatedResolverContext(config, getContext) {
var _config_resolver;
const defaultResolveRequest = getDefaultMetroResolver(config.projectRoot);
const originalResolveRequest = (_config_resolver = config.resolver) == null ? void 0 : _config_resolver.resolveRequest;
return {
...config,
resolver: {
...config.resolver,
resolveRequest (context, moduleName, platform) {
const universalContext = getContext(context, moduleName, platform);
const firstResolver = originalResolveRequest ?? universalContext.resolveRequest ?? defaultResolveRequest;
return firstResolver(universalContext, moduleName, platform);
}
}
};
}
function withMetroErrorReportingResolver(config) {
var _config_resolver;
if (!_env.env.EXPO_METRO_UNSTABLE_ERRORS) {
return config;
}
const originalResolveRequest = (_config_resolver = config.resolver) == null ? void 0 : _config_resolver.resolveRequest;
function mutateResolutionError(error, context, moduleName, platform) {
var _config_server;
const inputPlatform = platform ?? 'null';
const mapByOrigin = depGraph.get(optionsKeyForContext(context));
const mapByPlatform = mapByOrigin == null ? void 0 : mapByOrigin.get(inputPlatform);
if (!mapByPlatform) {
return error;
}
// collect all references inversely using some expensive lookup
const getReferences = (origin)=>{
const inverseOrigin = [];
if (!mapByPlatform) {
return inverseOrigin;
}
for (const [originKey, mapByTarget] of mapByPlatform){
// search comparing origin to path
const found = [
...mapByTarget.values()
].find((resolution)=>resolution.path === origin);
if (found) {
inverseOrigin.push({
origin,
previous: originKey,
request: found.request
});
}
}
return inverseOrigin;
};
const pad = (num)=>{
return new Array(num).fill(' ').join('');
};
const root = ((_config_server = config.server) == null ? void 0 : _config_server.unstable_serverRoot) ?? config.projectRoot;
const recurseBackWithLimit = (req, limit, count = 0)=>{
const results = {
origin: req.origin,
request: req.request,
previous: []
};
if (count >= limit) {
return results;
}
const inverse = getReferences(req.origin);
for (const match of inverse){
// Use more qualified name if possible
// results.origin = match.origin;
// Found entry point
if (req.origin === match.previous) {
continue;
}
results.previous.push(recurseBackWithLimit({
origin: match.previous,
request: match.request
}, limit, count + 1));
}
return results;
};
const inverseTree = recurseBackWithLimit({
origin: context.originModulePath,
request: moduleName
}, // TODO: Do we need to expose this?
35);
if (inverseTree.previous.length > 0) {
debug('Found inverse graph:', JSON.stringify(inverseTree, null, 2));
let extraMessage = _chalk().default.bold('Import stack:');
const printRecursive = (tree, depth = 0)=>{
let filename = _path().default.relative(root, tree.origin);
if (filename.match(/\?ctx=[\w\d]+$/)) {
filename = filename.replace(/\?ctx=[\w\d]+$/, _chalk().default.dim(' (require.context)'));
} else {
let formattedRequest = _chalk().default.green(`"${tree.request}"`);
if (// If bundling for web and the import is pulling internals from outside of react-native
// then mark it as an invalid import.
inputPlatform === 'web' && !/^(node_modules\/)?react-native\//.test(filename) && tree.request.match(/^react-native\/.*/)) {
formattedRequest = formattedRequest + (0, _chalk().default)`\n {yellow Importing react-native internals is not supported on web.}`;
}
filename = filename + (0, _chalk().default)`\n{gray |} {cyan import} ${formattedRequest}\n`;
}
let line = '\n' + pad(depth) + _chalk().default.gray(' ') + filename;
if (filename.match(/node_modules/)) {
line = _chalk().default.gray(// Bold the node module name
line.replace(/node_modules\/([^/]+)/, (_match, p1)=>{
return 'node_modules/' + _chalk().default.bold(p1);
}));
}
extraMessage += line;
for (const child of tree.previous){
printRecursive(child, // Only add depth if there are multiple children
tree.previous.length > 1 ? depth + 1 : depth);
}
};
printRecursive(inverseTree);
debug('inverse graph message:', extraMessage);
// @ts-expect-error
error._expoImportStack = extraMessage;
} else {
debug('Found no inverse tree for:', context.originModulePath);
}
return error;
}
const depGraph = new Map();
return {
...config,
resolver: {
...config.resolver,
resolveRequest (context, moduleName, platform) {
const storeResult = (res)=>{
const inputPlatform = platform ?? 'null';
const key = optionsKeyForContext(context);
if (!depGraph.has(key)) depGraph.set(key, new Map());
const mapByTarget = depGraph.get(key);
if (!mapByTarget.has(inputPlatform)) mapByTarget.set(inputPlatform, new Map());
const mapByPlatform = mapByTarget.get(inputPlatform);
if (!mapByPlatform.has(context.originModulePath)) mapByPlatform.set(context.originModulePath, new Set());
const setForModule = mapByPlatform.get(context.originModulePath);
const qualifiedModuleName = (res == null ? void 0 : res.type) === 'sourceFile' ? res.filePath : moduleName;
setForModule.add({
path: qualifiedModuleName,
request: moduleName
});
};
// If the user defined a resolver, run it first and depend on the documented
// chaining logic: https://facebook.github.io/metro/docs/resolution/#resolution-algorithm
//
// config.resolver.resolveRequest = (context, moduleName, platform) => {
//
// // Do work...
//
// return context.resolveRequest(context, moduleName, platform);
// };
try {
const firstResolver = originalResolveRequest ?? context.resolveRequest;
const res = firstResolver(context, moduleName, platform);
storeResult(res);
return res;
} catch (error) {
throw mutateResolutionError(error, context, moduleName, platform);
}
}
}
};
}
//# sourceMappingURL=withMetroResolvers.js.map