Files
Fluxup_PAP/node_modules/babel-preset-expo/build/detect-dynamic-exports.js
2026-03-10 16:18:05 +00:00

88 lines
4.5 KiB
JavaScript

"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.detectDynamicExports = detectDynamicExports;
const debug = require('debug')('expo:babel:exports');
// A babel pass to detect the usage of `module.exports` or `exports` in a module for use in
// export all expansion passes during tree shaking.
function detectDynamicExports(api) {
const { types: t } = api;
return {
name: 'expo-detect-dynamic-exports',
pre(file) {
assertExpoMetadata(file.metadata);
file.metadata.hasCjsExports = false;
},
visitor: {
// Any usage of `module.exports` or `exports` will mark the module as non-static.
// module.exports.a = 1;
// exports.a = 1;
CallExpression(path, state) {
assertExpoMetadata(state.file.metadata);
if (state.file.metadata.hasCjsExports)
return;
const callee = path.node.callee;
if (
// Object.assign(...)
t.isMemberExpression(callee) &&
t.isIdentifier(callee.object, { name: 'Object' }) &&
t.isIdentifier(callee.property, { name: 'assign' }) &&
// Allow `Object.assign(module.exports)` since it does nothing. Must have a second argument.
path.node.arguments.length > 1) {
const isModuleExports = t.isMemberExpression(path.node.arguments[0]) &&
t.isIdentifier(path.node.arguments[0].object, { name: 'module' }) &&
// Second argument is `exports` or 'exports'
// .exports
(t.isIdentifier(path.node.arguments[0].property, { name: 'exports' }) ||
// ['exports']
(t.isStringLiteral(path.node.arguments[0].property) &&
path.node.arguments[0].property.value === 'exports'));
// NOTE: Cannot match `['exp' + 'orts']`. We'd need to run after minification to match that confidently.
// Check for Object.assign(module.exports, ...), Object.assign(exports, ...)
if (
// module.exports
isModuleExports ||
// exports
t.isIdentifier(path.node.arguments[0], { name: 'exports' })) {
debug('Found Object.assign to module.exports or exports at ' + path.node.loc?.start.line);
state.file.metadata.hasCjsExports = true;
}
}
},
AssignmentExpression(path, state) {
assertExpoMetadata(state.file.metadata);
if (state.file.metadata.hasCjsExports)
return;
const left = path.node.left;
// Detect module.exports.foo = ... or exports.foo = ...
if ((t.isMemberExpression(left) &&
((t.isIdentifier(left.object, { name: 'module' }) &&
(t.isIdentifier(left.property, { name: 'exports' }) ||
(t.isStringLiteral(left.property) && left.property.value === 'exports'))) ||
t.isIdentifier(left.object, { name: 'exports' }))) ||
('object' in left &&
t.isMemberExpression(left.object) &&
t.isIdentifier(left.object.object, { name: 'module' }) &&
(t.isIdentifier(left.object.property, { name: 'exports' }) ||
(t.isStringLiteral(left.object.property) &&
left.object.property.value === 'exports')))) {
debug('Found assignment to module.exports or exports at ' + path.node.loc?.start.line);
state.file.metadata.hasCjsExports = true;
}
else if (t.isIdentifier(left, { name: 'exports' }) &&
path.scope.hasGlobal('exports') &&
// Ensure left is not defined in any scope
!path.scope.hasBinding('exports')) {
debug('Found assignment to exports at ' + path.node.loc?.start.line);
state.file.metadata.hasCjsExports = true;
}
},
},
};
}
function assertExpoMetadata(metadata) {
if (metadata && typeof metadata === 'object') {
return;
}
throw new Error('Expected Babel state.file.metadata to be an object');
}