render ( es, legacy ) { const magicString = this.magicString.clone(); for ( const node of this.ast.body ) { node.render( magicString, es ); } if ( this.namespace().needsNamespaceBlock ) { magicString.append( '\n\n' + this.namespace().renderBlock( es, legacy, '\t' ) ); // TODO use correct indentation } return magicString.trim(); }
export default function commonjs ( options = {} ) { const extensions = options.extensions || ['.js']; const filter = createFilter( options.include, options.exclude ); const ignoreGlobal = options.ignoreGlobal; const firstpass = ignoreGlobal ? firstpassNoGlobal : firstpassGlobal; let bundleUsesGlobal = false; let bundleRequiresWrappers = false; const sourceMap = options.sourceMap !== false; let customNamedExports = {}; if ( options.namedExports ) { Object.keys( options.namedExports ).forEach( id => { let resolvedId; try { resolvedId = nodeResolveSync( id, { basedir: process.cwd() }); } catch ( err ) { resolvedId = resolve( id ); } customNamedExports[ resolvedId ] = options.namedExports[ id ]; }); } return { resolveId ( importee, importer ) { if ( importee[0] !== '.' || !importer) return; // not our problem const resolved = resolve( dirname( importer ), importee ); const candidates = getCandidates( resolved, extensions ); for ( let i = 0; i < candidates.length; i += 1 ) { try { const stats = statSync( candidates[i] ); if ( stats.isFile() ) return candidates[i]; } catch ( err ) { /* noop */ } } }, transform ( code, id ) { if ( !filter( id ) ) return null; if ( extensions.indexOf( extname( id ) ) === -1 ) return null; if ( !firstpass.test( code ) ) return null; let ast; try { ast = acorn.parse( code, { ecmaVersion: 6, sourceType: 'module' }); } catch ( err ) { err.message += ` in ${id}`; throw err; } const magicString = new MagicString( code ); let required = {}; let uid = 0; let scope = attachScopes( ast, 'scope' ); let uses = { module: false, exports: false, global: false }; let namedExports = {}; if ( customNamedExports[ id ] ) { customNamedExports[ id ].forEach( name => namedExports[ name ] = true ); } let scopeDepth = 0; walk( ast, { enter ( node, parent ) { if ( node.scope ) scope = node.scope; if ( /^Function/.test( node.type ) ) scopeDepth += 1; if ( sourceMap ) { magicString.addSourcemapLocation( node.start ); magicString.addSourcemapLocation( node.end ); } // Is this an assignment to exports or module.exports? if ( node.type === 'AssignmentExpression' ) { if ( node.left.type !== 'MemberExpression' ) return; const flattened = flatten( node.left ); if ( !flattened ) return; if ( scope.contains( flattened.name ) ) return; const match = exportsPattern.exec( flattened.keypath ); if ( !match || flattened.keypath === 'exports' ) return; if ( flattened.keypath === 'module.exports' && node.right.type === 'ObjectExpression' ) { return node.right.properties.forEach( prop => { if ( prop.computed || prop.key.type !== 'Identifier' ) return; const name = prop.key.name; if ( name === makeLegalIdentifier( name ) ) namedExports[ name ] = true; }); } if ( match[1] ) namedExports[ match[1] ] = true; return; } if ( node.type === 'Identifier' ) { if ( ( node.name in uses && !uses[ node.name ] ) && isReference( node, parent ) && !scope.contains( node.name ) ) uses[ node.name ] = true; return; } if ( node.type === 'ThisExpression' && scopeDepth === 0 && !ignoreGlobal ) { uses.global = true; magicString.overwrite( node.start, node.end, `__commonjs_global`, true ); return; } if ( node.type !== 'CallExpression' ) return; if ( node.callee.name !== 'require' || scope.contains( 'require' ) ) return; if ( node.arguments.length !== 1 || node.arguments[0].type !== 'Literal' ) return; // TODO handle these weird cases? const source = node.arguments[0].value; let existing = required[ source ]; let name; if ( !existing ) { name = `require$$${uid++}`; required[ source ] = { source, name, importsDefault: false }; } else { name = required[ source ].name; } if ( parent.type !== 'ExpressionStatement' ) { required[ source ].importsDefault = true; magicString.overwrite( node.start, node.end, name ); } else { // is a bare import, e.g. `require('foo');` magicString.remove( parent.start, parent.end ); } }, leave ( node ) { if ( node.scope ) scope = scope.parent; if ( /^Function/.test( node.type ) ) scopeDepth -= 1; } }); const sources = Object.keys( required ); if ( !sources.length && !uses.module && !uses.exports && !uses.global ) { if ( Object.keys( namedExports ).length ) { throw new Error( `Custom named exports were specified for ${id} but it does not appear to be a CommonJS module` ); } return null; // not a CommonJS module } bundleRequiresWrappers = true; const name = getName( id ); const importBlock = sources.length ? sources.map( source => { const { name, importsDefault } = required[ source ]; return `import ${importsDefault ? `${name} from ` : ``}'${source}';`; }).join( '\n' ) : ''; const args = `module${uses.exports || uses.global ? ', exports' : ''}${uses.global ? ', global' : ''}`; const intro = `\n\nvar ${name} = __commonjs(function (${args}) {\n`; let outro = `\n});\n\nexport default (${name} && typeof ${name} === 'object' && 'default' in ${name} ? ${name}['default'] : ${name});\n`; outro += Object.keys( namedExports ) .filter( key => !blacklistedExports[ key ] ) .map( x => `export var ${x} = ${name}.${x};` ) .join( '\n' ); magicString.trim() .prepend( importBlock + intro ) .trim() .append( outro ); code = magicString.toString(); const map = sourceMap ? magicString.generateMap() : null; if ( uses.global ) bundleUsesGlobal = true; return { code, map }; }, intro () { var intros = []; if ( bundleUsesGlobal ) { intros.push( `var __commonjs_global = typeof window !== 'undefined' ? window : typeof global !== 'undefined' ? global : typeof self !== 'undefined' ? self : {}` ); } if ( bundleRequiresWrappers ) { intros.push( `function __commonjs(fn, module) { return module = { exports: {} }, fn(module, module.exports${bundleUsesGlobal ? ', __commonjs_global' : ''}), module.exports; }\n` ); } return intros.join( '\n' ); } }; }
function transformCommonjs ( code, id, isEntry, ignoreGlobal, ignoreRequire, customNamedExports, sourceMap, allowDynamicRequire ) { var firstpass = ignoreGlobal ? firstpassNoGlobal : firstpassGlobal; if ( !firstpass.test( code ) ) { return null; } var ast = tryParse( code, id ); // if there are top-level import/export declarations, this is ES not CommonJS for ( var i = 0, list = ast.body; i < list.length; i += 1 ) { var node = list[i]; if ( importExportDeclaration.test( node.type ) ) { return null; } } var magicString = new MagicString( code ); var required = {}; // Because objects have no guaranteed ordering, yet we need it, // we need to keep track of the order in a array var sources = []; var uid = 0; var scope = attachScopes( ast, 'scope' ); var uses = { module: false, exports: false, global: false, require: false }; var lexicalDepth = 0; var programDepth = 0; var globals = new Set(); var HELPERS_NAME = deconflict( scope, globals, 'commonjsHelpers' ); // TODO technically wrong since globals isn't populated yet, but ¯\_(ツ)_/¯ var namedExports = {}; // TODO handle transpiled modules var shouldWrap = /__esModule/.test( code ); function isRequireStatement ( node ) { if ( !node ) { return; } if ( node.type !== 'CallExpression' ) { return; } if ( node.callee.name !== 'require' || scope.contains( 'require' ) ) { return; } if ( node.arguments.length !== 1 || node.arguments[0].type !== 'Literal' ) { return; } // TODO handle these weird cases? if ( ignoreRequire( node.arguments[0].value ) ) { return; } return true; } function getRequired ( node, name ) { var source = node.arguments[0].value; var existing = required[ source ]; if ( existing === undefined ) { sources.push( source ); if ( !name ) { name = "require$$" + (uid++); } required[ source ] = { source: source, name: name, importsDefault: false }; } return required[ source ]; } // do a first pass, see which names are assigned to. This is necessary to prevent // illegally replacing `var foo = require('foo')` with `import foo from 'foo'`, // where `foo` is later reassigned. (This happens in the wild. CommonJS, sigh) var assignedTo = new Set(); walk( ast, { enter: function enter ( node ) { if ( node.type !== 'AssignmentExpression' ) { return; } if ( node.left.type === 'MemberExpression' ) { return; } extractNames( node.left ).forEach( function (name) { assignedTo.add( name ); }); } }); walk( ast, { enter: function enter ( node, parent ) { if ( sourceMap ) { magicString.addSourcemapLocation( node.start ); magicString.addSourcemapLocation( node.end ); } // skip dead branches if ( parent && ( parent.type === 'IfStatement' || parent.type === 'ConditionalExpression' ) ) { if ( node === parent.consequent && isFalsy( parent.test ) ) { return this.skip(); } if ( node === parent.alternate && isTruthy( parent.test ) ) { return this.skip(); } } if ( node._skip ) { return this.skip(); } programDepth += 1; if ( node.scope ) { scope = node.scope; } if ( /^Function/.test( node.type ) ) { lexicalDepth += 1; } // rewrite `this` as `commonjsHelpers.commonjsGlobal` if ( node.type === 'ThisExpression' && lexicalDepth === 0 ) { uses.global = true; if ( !ignoreGlobal ) { magicString.overwrite( node.start, node.end, (HELPERS_NAME + ".commonjsGlobal"), true ); } return; } // rewrite `typeof module`, `typeof module.exports` and `typeof exports` (https://github.com/rollup/rollup-plugin-commonjs/issues/151) if ( node.type === 'UnaryExpression' && node.operator === 'typeof' ) { var flattened = flatten( node.argument ); if ( !flattened ) { return; } if ( scope.contains( flattened.name ) ) { return; } if ( flattened.keypath === 'module.exports' || flattened.keypath === 'module' || flattened.keypath === 'exports' ) { magicString.overwrite( node.start, node.end, "'object'", false ); } } // rewrite `require` (if not already handled) `global` and `define`, and handle free references to // `module` and `exports` as these mean we need to wrap the module in commonjsHelpers.createCommonjsModule if ( node.type === 'Identifier' ) { if ( isReference( node, parent ) && !scope.contains( node.name ) ) { if ( node.name in uses ) { if ( node.name === 'require' ) { if ( allowDynamicRequire ) { return; } magicString.overwrite( node.start, node.end, (HELPERS_NAME + ".commonjsRequire"), true ); } uses[ node.name ] = true; if ( node.name === 'global' && !ignoreGlobal ) { magicString.overwrite( node.start, node.end, (HELPERS_NAME + ".commonjsGlobal"), true ); } // if module or exports are used outside the context of an assignment // expression, we need to wrap the module if ( node.name === 'module' || node.name === 'exports' ) { shouldWrap = true; } } if ( node.name === 'define' ) { magicString.overwrite( node.start, node.end, 'undefined', true ); } globals.add( node.name ); } return; } // Is this an assignment to exports or module.exports? if ( node.type === 'AssignmentExpression' ) { if ( node.left.type !== 'MemberExpression' ) { return; } var flattened$1 = flatten( node.left ); if ( !flattened$1 ) { return; } if ( scope.contains( flattened$1.name ) ) { return; } var match = exportsPattern.exec( flattened$1.keypath ); if ( !match || flattened$1.keypath === 'exports' ) { return; } uses[ flattened$1.name ] = true; // we're dealing with `module.exports = ...` or `[module.]exports.foo = ...` – // if this isn't top-level, we'll need to wrap the module if ( programDepth > 3 ) { shouldWrap = true; } node.left._skip = true; if ( flattened$1.keypath === 'module.exports' && node.right.type === 'ObjectExpression' ) { return node.right.properties.forEach( function (prop) { if ( prop.computed || prop.key.type !== 'Identifier' ) { return; } var name = prop.key.name; if ( name === makeLegalIdentifier( name ) ) { namedExports[ name ] = true; } }); } if ( match[1] ) { namedExports[ match[1] ] = true; } return; } // if this is `var x = require('x')`, we can do `import x from 'x'` if ( node.type === 'VariableDeclarator' && node.id.type === 'Identifier' && isRequireStatement( node.init ) ) { // for now, only do this for top-level requires. maybe fix this in future if ( scope.parent ) { return; } // edge case — CJS allows you to assign to imports. ES doesn't if ( assignedTo.has( node.id.name ) ) { return; } var r$1 = getRequired( node.init, node.id.name ); r$1.importsDefault = true; if ( r$1.name === node.id.name ) { node._shouldRemove = true; } } if ( !isRequireStatement( node ) ) { return; } var r = getRequired( node ); if ( parent.type === 'ExpressionStatement' ) { // is a bare import, e.g. `require('foo');` magicString.remove( parent.start, parent.end ); } else { r.importsDefault = true; magicString.overwrite( node.start, node.end, r.name ); } node.callee._skip = true; }, leave: function leave ( node ) { programDepth -= 1; if ( node.scope ) { scope = scope.parent; } if ( /^Function/.test( node.type ) ) { lexicalDepth -= 1; } if ( node.type === 'VariableDeclaration' ) { var keepDeclaration = false; var c = node.declarations[0].start; for ( var i = 0; i < node.declarations.length; i += 1 ) { var declarator = node.declarations[i]; if ( declarator._shouldRemove ) { magicString.remove( c, declarator.end ); } else { if ( !keepDeclaration ) { magicString.remove( c, declarator.start ); keepDeclaration = true; } c = declarator.end; } } if ( !keepDeclaration ) { magicString.remove( node.start, node.end ); } } } }); if ( !sources.length && !uses.module && !uses.exports && !uses.require && ( ignoreGlobal || !uses.global ) ) { if ( Object.keys( namedExports ).length ) { throw new Error( ("Custom named exports were specified for " + id + " but it does not appear to be a CommonJS module") ); } return null; // not a CommonJS module } var includeHelpers = shouldWrap || uses.global || uses.require; var importBlock = ( includeHelpers ? [ ("import * as " + HELPERS_NAME + " from '" + HELPERS_ID + "';") ] : [] ).concat( sources.map( function (source) { // import the actual module before the proxy, so that we know // what kind of proxy to build return ("import '" + source + "';"); }), sources.map( function (source) { var ref = required[ source ]; var name = ref.name; var importsDefault = ref.importsDefault; return ("import " + (importsDefault ? (name + " from ") : "") + "'" + PREFIX + source + "';"); }) ).join( '\n' ) + '\n\n'; var namedExportDeclarations = []; var wrapperStart = ''; var wrapperEnd = ''; var moduleName = deconflict( scope, globals, getName( id ) ); if ( !isEntry ) { var exportModuleExports = { str: ("export { " + moduleName + " as __moduleExports };"), name: '__moduleExports' }; namedExportDeclarations.push( exportModuleExports ); } var name = getName( id ); function addExport ( x ) { var deconflicted = deconflict( scope, globals, name ); var declaration = deconflicted === name ? ("export var " + x + " = " + moduleName + "." + x + ";") : ("var " + deconflicted + " = " + moduleName + "." + x + ";\nexport { " + deconflicted + " as " + x + " };"); namedExportDeclarations.push({ str: declaration, name: x }); } if ( customNamedExports ) { customNamedExports.forEach( addExport ); } var defaultExportPropertyAssignments = []; var hasDefaultExport = false; if ( shouldWrap ) { var args = "module" + (uses.exports ? ', exports' : ''); wrapperStart = "var " + moduleName + " = " + HELPERS_NAME + ".createCommonjsModule(function (" + args + ") {\n"; wrapperEnd = "\n});"; Object.keys( namedExports ) .filter( function (key) { return !blacklist[ key ]; } ) .forEach( addExport ); } else { var names = []; ast.body.forEach( function (node) { if ( node.type === 'ExpressionStatement' && node.expression.type === 'AssignmentExpression' ) { var left = node.expression.left; var flattened = flatten( left ); if ( !flattened ) { return; } var match = exportsPattern.exec( flattened.keypath ); if ( !match ) { return; } if ( flattened.keypath === 'module.exports' ) { hasDefaultExport = true; magicString.overwrite( left.start, left.end, ("var " + moduleName) ); } else { var name = match[1]; var deconflicted = deconflict( scope, globals, name ); names.push({ name: name, deconflicted: deconflicted }); magicString.overwrite( node.start, left.end, ("var " + deconflicted) ); var declaration = name === deconflicted ? ("export { " + name + " };") : ("export { " + deconflicted + " as " + name + " };"); namedExportDeclarations.push({ str: declaration, name: name }); defaultExportPropertyAssignments.push( (moduleName + "." + name + " = " + deconflicted + ";") ); } } }); if ( !hasDefaultExport ) { wrapperEnd = "\n\nvar " + moduleName + " = {\n" + (names.map( function (ref) { var name = ref.name; var deconflicted = ref.deconflicted; return ("\t" + name + ": " + deconflicted); } ).join( ',\n' )) + "\n};"; } } var defaultExport = /__esModule/.test( code ) ? ("export default " + HELPERS_NAME + ".unwrapExports(" + moduleName + ");") : ("export default " + moduleName + ";"); var named = namedExportDeclarations .filter( function (x) { return x.name !== 'default' || !hasDefaultExport; } ) .map( function (x) { return x.str; } ); var exportBlock = '\n\n' + [ defaultExport ] .concat( named ) .concat( hasDefaultExport ? defaultExportPropertyAssignments : [] ) .join( '\n' ); magicString.trim() .prepend( importBlock + wrapperStart ) .trim() .append( wrapperEnd + exportBlock ); code = magicString.toString(); var map = sourceMap ? magicString.generateMap() : null; return { code: code, map: map }; }
render ( es ) { let magicString = this.magicString.clone(); this.statements.forEach( statement => { if ( !statement.isIncluded ) { magicString.remove( statement.start, statement.next ); return; } statement.stringLiteralRanges.forEach( range => magicString.indentExclusionRanges.push( range ) ); // skip `export { foo, bar, baz }` if ( statement.node.type === 'ExportNamedDeclaration' ) { if ( statement.node.isSynthetic ) return; // skip `export { foo, bar, baz }` if ( statement.node.declaration === null ) { magicString.remove( statement.start, statement.next ); return; } } // split up/remove var declarations as necessary if ( statement.node.type === 'VariableDeclaration' ) { const declarator = statement.node.declarations[0]; if ( declarator.id.type === 'Identifier' ) { const declaration = this.declarations[ declarator.id.name ]; if ( declaration.exportName && declaration.isReassigned ) { // `var foo = ...` becomes `exports.foo = ...` magicString.remove( statement.start, declarator.init ? declarator.start : statement.next ); if ( !declarator.init ) return; } } else { // we handle destructuring differently, because whereas we can rewrite // `var foo = ...` as `exports.foo = ...`, in a case like `var { a, b } = c()` // where `a` or `b` is exported and reassigned, we have to append // `exports.a = a;` and `exports.b = b` instead extractNames( declarator.id ).forEach( name => { const declaration = this.declarations[ name ]; if ( declaration.exportName && declaration.isReassigned ) { magicString.insertLeft( statement.end, `;\nexports.${name} = ${declaration.render( es )}` ); } }); } if ( statement.node.isSynthetic ) { // insert `var/let/const` if necessary magicString.insertRight( statement.start, `${statement.node.kind} ` ); magicString.insertLeft( statement.end, ';' ); magicString.overwrite( statement.end, statement.next, '\n' ); // TODO account for trailing newlines } } let toDeshadow = blank(); statement.references.forEach( reference => { const { start, end } = reference; if ( reference.isUndefined ) { magicString.overwrite( start, end, 'undefined', true ); } const declaration = reference.declaration; if ( declaration ) { const name = declaration.render( es ); // the second part of this check is necessary because of // namespace optimisation – name of `foo.bar` could be `bar` if ( reference.name === name && name.length === end - start ) return; reference.rewritten = true; // prevent local variables from shadowing renamed references const identifier = name.match( /[^\.]+/ )[0]; if ( reference.scope.contains( identifier ) ) { toDeshadow[ identifier ] = `${identifier}$$`; // TODO more robust mechanism } if ( reference.isShorthandProperty ) { magicString.insertLeft( end, `: ${name}` ); } else { magicString.overwrite( start, end, name, true ); } } }); if ( keys( toDeshadow ).length ) { statement.references.forEach( reference => { if ( !reference.rewritten && reference.name in toDeshadow ) { const replacement = toDeshadow[ reference.name ]; magicString.overwrite( reference.start, reference.end, reference.isShorthandProperty ? `${reference.name}: ${replacement}` : replacement, true ); } }); } // modify exports as necessary if ( statement.isExportDeclaration ) { // remove `export` from `export var foo = 42` // TODO: can we do something simpler here? // we just want to remove `export`, right? if ( statement.node.type === 'ExportNamedDeclaration' && statement.node.declaration.type === 'VariableDeclaration' ) { const name = extractNames( statement.node.declaration.declarations[ 0 ].id )[ 0 ]; const declaration = this.declarations[ name ]; // TODO is this even possible? if ( !declaration ) throw new Error( `Missing declaration for ${name}!` ); let end; if ( es ) { end = statement.node.declaration.start; } else { if ( declaration.exportName && declaration.isReassigned ) { const declarator = statement.node.declaration.declarations[0]; end = declarator.init ? declarator.start : statement.next; } else { end = statement.node.declaration.start; } } magicString.remove( statement.node.start, end ); } else if ( statement.node.type === 'ExportAllDeclaration' ) { // TODO: remove once `export * from 'external'` is supported. magicString.remove( statement.start, statement.next ); } // remove `export` from `export class Foo {...}` or `export default Foo` // TODO default exports need different treatment else if ( statement.node.declaration.id ) { magicString.remove( statement.node.start, statement.node.declaration.start ); } else if ( statement.node.type === 'ExportDefaultDeclaration' ) { const defaultDeclaration = this.declarations.default; // prevent `var foo = foo` if ( defaultDeclaration.original && !defaultDeclaration.original.isReassigned ) { magicString.remove( statement.start, statement.next ); return; } const defaultName = defaultDeclaration.render(); // prevent `var undefined = sideEffectyDefault(foo)` if ( !defaultDeclaration.exportName && !defaultDeclaration.isUsed ) { magicString.remove( statement.start, statement.node.declaration.start ); return; } // anonymous functions should be converted into declarations if ( statement.node.declaration.type === 'FunctionExpression' ) { magicString.overwrite( statement.node.start, statement.node.declaration.start + 8, `function ${defaultName}` ); } else { magicString.overwrite( statement.node.start, statement.node.declaration.start, `${this.bundle.varOrConst} ${defaultName} = ` ); } } else { throw new Error( 'Unhandled export' ); } } }); // add namespace block if necessary const namespace = this.declarations['*']; if ( namespace && namespace.needsNamespaceBlock ) { magicString.append( '\n\n' + namespace.renderBlock( magicString.getIndentString() ) ); } return magicString.trim(); }
transform: function transform(code, id) { if (!filter(id)) return null; if (extensions.indexOf(path.extname(id)) === -1) return null; if (!firstpass.test(code)) return null; var ast = void 0; try { ast = acorn.parse(code, { plugins: { es7: true }, ecmaVersion: 6, sourceType: 'module' }); } catch (err) { err.message += ' in ' + id; throw err; } var magicString = new MagicString(code); var required = {}; var uid = 0; var scope = rollupPluginutils.attachScopes(ast, 'scope'); var uses = { module: false, exports: false, global: false, es6Export: false }; if (ignoreGlobal) { delete uses.global; } var usesRequire = false; var namedExports = {}; if (customNamedExports[id]) { customNamedExports[id].forEach(function(name) { return namedExports[name] = true; }); } var scopeDepth = 0; estreeWalker.walk(ast, { enter: function enter(node, parent) { if (node.type == "ExportAllDeclaration") uses.es6Export = true; if (node.type == "ExportNamedDeclaration") uses.es6Export = true; if (node.type == "ExportDefaultDeclaration") uses.es6Export = true; if (node.scope) scope = node.scope; if (/^Function/.test(node.type)) scopeDepth += 1; if (sourceMap) { magicString.addSourcemapLocation(node.start); magicString.addSourcemapLocation(node.end); } // Is this an assignment to exports or module.exports? if (node.type === 'AssignmentExpression') { if (node.left.type !== 'MemberExpression') return; var flattened = flatten(node.left); if (!flattened) return; if (scope.contains(flattened.name)) return; var match = exportsPattern.exec(flattened.keypath); if (!match || flattened.keypath === 'exports') return; if (flattened.keypath === 'module.exports' && node.right.type === 'ObjectExpression') { return node.right.properties.forEach(function(prop) { if (prop.computed || prop.key.type !== 'Identifier') return; var name = prop.key.name; if (name === rollupPluginutils.makeLegalIdentifier(name)) namedExports[name] = true; }); } if (match[1]) namedExports[match[1]] = true; return; } if (node.type === 'Identifier') { if (node.name in uses && !uses[node.name] && isReference(node, parent) && !scope.contains(node.name)) { if (parent && (parent.operator === 'typeof' || parent.type === 'ConditionalExpression')) { return; } else { if (node.name === 'global') { magicString.overwrite(node.start, node.end, '__commonjs_global', true); } uses[node.name] = true; } } return; } if (node.type === 'ThisExpression' && scopeDepth === 0 && !ignoreGlobal) { uses.global = true; magicString.overwrite(node.start, node.end, '__commonjs_global', true); return; } if (node.type !== 'CallExpression') return; if (node.callee.name !== 'require' || scope.contains('require')) return; if (node.arguments.length !== 1 || node.arguments[0].type !== 'Literal') return; // TODO handle these weird cases? usesRequire = true; var source = node.arguments[0].value; var existing = required[source]; var name = void 0; if (!existing) { name = 'require$$' + uid++; required[source] = { source: source, name: name, importsDefault: false }; } else { name = required[source].name; } if (parent.type !== 'ExpressionStatement') { required[source].importsDefault = true; magicString.overwrite(node.start, node.end, name); } else { // is a bare import, e.g. `require('foo');` magicString.remove(parent.start, parent.end); } }, leave: function leave(node) { if (node.scope) scope = scope.parent; if (/^Function/.test(node.type)) scopeDepth -= 1; } }); var sources = Object.keys(required); if (options.ignoreGlobal) { uses.global = false; } if (!sources.length && !uses.module && !uses.exports && !uses.global && !usesRequire) { if (Object.keys(namedExports).length) { throw new Error('Custom named exports were specified for ' + id + ' but it does not appear to be a CommonJS module'); } return null; // not a CommonJS module } var importBlock = sources.length ? sources.map(function(source) { var _required$source = required[source]; var name = _required$source.name; var importsDefault = _required$source.importsDefault; return 'import ' + (importsDefault ? name + ' from ' : '') + '\'' + source + '\';'; }).join('\n') : ''; if (!uses.es6Export) { var intro = '\n\nvar module = {exports: {}};\n\nvar exports = module.exports;\n\n'; var outro = '\n\nexport default __get_exports(module);\n'; outro += Object.keys(namedExports).filter(function(key) { return !blacklistedExports[key]; }).map(function(x) { return 'export var ' + x + ' = module.exports.' + x + ';'; }).join('\n'); magicString.trim().prepend(importBlock + intro).trim().append(outro); } else { magicString.trim().prepend(importBlock).trim(); // console.log(magicString.slice(parent.start, parent.end)); //console.log(magicString.toString()); } code = magicString.toString(); var map = sourceMap ? magicString.generateMap() : null; if (uses.global) bundleUsesGlobal = true; return { code: code, map: map }; },
export function transformCommonjs( parse, code, id, isEntry, ignoreGlobal, ignoreRequire, customNamedExports, sourceMap, allowDynamicRequire, astCache ) { const ast = astCache || tryParse(parse, code, id); const magicString = new MagicString(code); const required = {}; // Because objects have no guaranteed ordering, yet we need it, // we need to keep track of the order in a array const sources = []; let uid = 0; let scope = attachScopes(ast, 'scope'); const uses = { module: false, exports: false, global: false, require: false }; let lexicalDepth = 0; let programDepth = 0; const globals = new Set(); const HELPERS_NAME = deconflict(scope, globals, 'commonjsHelpers'); // TODO technically wrong since globals isn't populated yet, but ¯\_(ツ)_/¯ const namedExports = {}; // TODO handle transpiled modules let shouldWrap = /__esModule/.test(code); function isRequireStatement(node) { if (!node) return; if (node.type !== 'CallExpression') return; if (node.callee.name !== 'require' || scope.contains('require')) return; if (node.arguments.length === 0) return; // Weird case of require() without arguments return true; } function hasDynamicArguments(node) { return ( node.arguments.length > 1 || (node.arguments[0].type !== 'Literal' && (node.arguments[0].type !== 'TemplateLiteral' || node.arguments[0].expressions.length > 0)) ); } function isStaticRequireStatement(node) { if (!isRequireStatement(node)) return; if (hasDynamicArguments(node)) return; if (ignoreRequire(node.arguments[0].value)) return; return true; } function getRequireStringArg(node) { return node.arguments[0].type === 'Literal' ? node.arguments[0].value : node.arguments[0].quasis[0].value.cooked; } function getRequired(node, name) { const sourceId = getRequireStringArg(node); const existing = required[sourceId]; if (existing === undefined) { if (!name) { do name = `require$$${uid++}`; while (scope.contains(name)); } sources.push(sourceId); required[sourceId] = { source: sourceId, name, importsDefault: false }; } return required[sourceId]; } // do a first pass, see which names are assigned to. This is necessary to prevent // illegally replacing `var foo = require('foo')` with `import foo from 'foo'`, // where `foo` is later reassigned. (This happens in the wild. CommonJS, sigh) const assignedTo = new Set(); walk(ast, { enter(node) { if (node.type !== 'AssignmentExpression') return; if (node.left.type === 'MemberExpression') return; extractNames(node.left).forEach(name => { assignedTo.add(name); }); } }); walk(ast, { enter(node, parent) { if (sourceMap) { magicString.addSourcemapLocation(node.start); magicString.addSourcemapLocation(node.end); } // skip dead branches if (parent && (parent.type === 'IfStatement' || parent.type === 'ConditionalExpression')) { if (node === parent.consequent && isFalsy(parent.test)) return this.skip(); if (node === parent.alternate && isTruthy(parent.test)) return this.skip(); } if (node._skip) return this.skip(); programDepth += 1; if (node.scope) scope = node.scope; if (functionType.test(node.type)) lexicalDepth += 1; // if toplevel return, we need to wrap it if (node.type === 'ReturnStatement' && lexicalDepth === 0) { shouldWrap = true; } // rewrite `this` as `commonjsHelpers.commonjsGlobal` if (node.type === 'ThisExpression' && lexicalDepth === 0) { uses.global = true; if (!ignoreGlobal) magicString.overwrite(node.start, node.end, `${HELPERS_NAME}.commonjsGlobal`, { storeName: true }); return; } // rewrite `typeof module`, `typeof module.exports` and `typeof exports` (https://github.com/rollup/rollup-plugin-commonjs/issues/151) if (node.type === 'UnaryExpression' && node.operator === 'typeof') { const flattened = flatten(node.argument); if (!flattened) return; if (scope.contains(flattened.name)) return; if ( flattened.keypath === 'module.exports' || flattened.keypath === 'module' || flattened.keypath === 'exports' ) { magicString.overwrite(node.start, node.end, `'object'`, { storeName: false }); } } // rewrite `require` (if not already handled) `global` and `define`, and handle free references to // `module` and `exports` as these mean we need to wrap the module in commonjsHelpers.createCommonjsModule if (node.type === 'Identifier') { if (isReference(node, parent) && !scope.contains(node.name)) { if (node.name in uses) { if (node.name === 'require') { if (allowDynamicRequire) return; magicString.overwrite(node.start, node.end, `${HELPERS_NAME}.commonjsRequire`, { storeName: true }); } uses[node.name] = true; if (node.name === 'global' && !ignoreGlobal) { magicString.overwrite(node.start, node.end, `${HELPERS_NAME}.commonjsGlobal`, { storeName: true }); } // if module or exports are used outside the context of an assignment // expression, we need to wrap the module if (node.name === 'module' || node.name === 'exports') { shouldWrap = true; } } if (node.name === 'define') { magicString.overwrite(node.start, node.end, 'undefined', { storeName: true }); } globals.add(node.name); } return; } // Is this an assignment to exports or module.exports? if (node.type === 'AssignmentExpression') { if (node.left.type !== 'MemberExpression') return; const flattened = flatten(node.left); if (!flattened) return; if (scope.contains(flattened.name)) return; const match = exportsPattern.exec(flattened.keypath); if (!match || flattened.keypath === 'exports') return; uses[flattened.name] = true; // we're dealing with `module.exports = ...` or `[module.]exports.foo = ...` – // if this isn't top-level, we'll need to wrap the module if (programDepth > 3) shouldWrap = true; node.left._skip = true; if (flattened.keypath === 'module.exports' && node.right.type === 'ObjectExpression') { return node.right.properties.forEach(prop => { if (prop.computed || prop.key.type !== 'Identifier') return; const name = prop.key.name; if (name === makeLegalIdentifier(name)) namedExports[name] = true; }); } if (match[1]) namedExports[match[1]] = true; return; } // if this is `var x = require('x')`, we can do `import x from 'x'` if ( node.type === 'VariableDeclarator' && node.id.type === 'Identifier' && isStaticRequireStatement(node.init) ) { // for now, only do this for top-level requires. maybe fix this in future if (scope.parent) return; // edge case — CJS allows you to assign to imports. ES doesn't if (assignedTo.has(node.id.name)) return; const required = getRequired(node.init, node.id.name); required.importsDefault = true; if (required.name === node.id.name) { node._shouldRemove = true; } } if (!isStaticRequireStatement(node)) return; const required = getRequired(node); if (parent.type === 'ExpressionStatement') { // is a bare import, e.g. `require('foo');` magicString.remove(parent.start, parent.end); } else { required.importsDefault = true; magicString.overwrite(node.start, node.end, required.name); } node.callee._skip = true; }, leave(node) { programDepth -= 1; if (node.scope) scope = scope.parent; if (functionType.test(node.type)) lexicalDepth -= 1; if (node.type === 'VariableDeclaration') { let keepDeclaration = false; let c = node.declarations[0].start; for (let i = 0; i < node.declarations.length; i += 1) { const declarator = node.declarations[i]; if (declarator._shouldRemove) { magicString.remove(c, declarator.end); } else { if (!keepDeclaration) { magicString.remove(c, declarator.start); keepDeclaration = true; } c = declarator.end; } } if (!keepDeclaration) { magicString.remove(node.start, node.end); } } } }); if ( !sources.length && !uses.module && !uses.exports && !uses.require && (ignoreGlobal || !uses.global) ) { if (Object.keys(namedExports).length) { throw new Error( `Custom named exports were specified for ${id} but it does not appear to be a CommonJS module` ); } return null; // not a CommonJS module } const includeHelpers = shouldWrap || uses.global || uses.require; const importBlock = (includeHelpers ? [`import * as ${HELPERS_NAME} from '${HELPERS_ID}';`] : []) .concat( sources.map(source => { // import the actual module before the proxy, so that we know // what kind of proxy to build return `import '${source}';`; }), sources.map(source => { const { name, importsDefault } = required[source]; return `import ${importsDefault ? `${name} from ` : ``}'${PROXY_PREFIX}${source}';`; }) ) .join('\n') + '\n\n'; const namedExportDeclarations = []; let wrapperStart = ''; let wrapperEnd = ''; const moduleName = deconflict(scope, globals, getName(id)); if (!isEntry) { const exportModuleExports = { str: `export { ${moduleName} as __moduleExports };`, name: '__moduleExports' }; namedExportDeclarations.push(exportModuleExports); } const name = getName(id); function addExport(x) { const deconflicted = deconflict(scope, globals, name); const declaration = deconflicted === name ? `export var ${x} = ${moduleName}.${x};` : `var ${deconflicted} = ${moduleName}.${x};\nexport { ${deconflicted} as ${x} };`; namedExportDeclarations.push({ str: declaration, name: x }); } if (customNamedExports) customNamedExports.forEach(addExport); const defaultExportPropertyAssignments = []; let hasDefaultExport = false; if (shouldWrap) { const args = `module${uses.exports ? ', exports' : ''}`; wrapperStart = `var ${moduleName} = ${HELPERS_NAME}.createCommonjsModule(function (${args}) {\n`; wrapperEnd = `\n});`; } else { const names = []; ast.body.forEach(node => { if (node.type === 'ExpressionStatement' && node.expression.type === 'AssignmentExpression') { const left = node.expression.left; const flattened = flatten(left); if (!flattened) return; const match = exportsPattern.exec(flattened.keypath); if (!match) return; if (flattened.keypath === 'module.exports') { hasDefaultExport = true; magicString.overwrite(left.start, left.end, `var ${moduleName}`); } else { const name = match[1]; const deconflicted = deconflict(scope, globals, name); names.push({ name, deconflicted }); magicString.overwrite(node.start, left.end, `var ${deconflicted}`); const declaration = name === deconflicted ? `export { ${name} };` : `export { ${deconflicted} as ${name} };`; if (name !== 'default') { namedExportDeclarations.push({ str: declaration, name }); delete namedExports[name]; } defaultExportPropertyAssignments.push(`${moduleName}.${name} = ${deconflicted};`); } } }); if (!hasDefaultExport) { wrapperEnd = `\n\nvar ${moduleName} = {\n${names .map(({ name, deconflicted }) => `\t${name}: ${deconflicted}`) .join(',\n')}\n};`; } } Object.keys(namedExports) .filter(key => !blacklist[key]) .forEach(addExport); const defaultExport = /__esModule/.test(code) ? `export default ${HELPERS_NAME}.unwrapExports(${moduleName});` : `export default ${moduleName};`; const named = namedExportDeclarations .filter(x => x.name !== 'default' || !hasDefaultExport) .map(x => x.str); const exportBlock = '\n\n' + [defaultExport] .concat(named) .concat(hasDefaultExport ? defaultExportPropertyAssignments : []) .join('\n'); magicString .trim() .prepend(importBlock + wrapperStart) .trim() .append(wrapperEnd + exportBlock); code = magicString.toString(); const map = sourceMap ? magicString.generateMap() : null; return { code, map }; }