modules.concat( externalModules ).forEach( mod => { // is this already named? if ( hasOwnProp.call( names, mod.id ) ) { mod.name = names[ mod.id ]; return; } let name; let parts = splitPath( mod.id ); let i = parts.length; while ( i-- ) { name = sanitize( parts.slice( i ).join( '_' ) ); if ( !hasOwnProp.call( used, name ) ) { break; } } while ( hasOwnProp.call( used, name ) ) { name = '_' + name; } used[ name ] = true; mod.name = name; });
x.specifiers.forEach( s => { var moduleId, mod, moduleName, specifierName, replacement, hash, isChained, separatorIndex; moduleId = x.id; if ( s.isBatch ) { replacement = ( bundle.moduleLookup[ moduleId ] || bundle.externalModuleLookup[ moduleId ] ).name; } else { specifierName = s.name; // If this is a chained import, get the origin hash = `${moduleId}@${specifierName}`; while ( hasOwnProp.call( bundle.chains, hash ) ) { hash = bundle.chains[ hash ]; isChained = true; } if ( isChained ) { separatorIndex = hash.indexOf( '@' ); moduleId = hash.substr( 0, separatorIndex ); specifierName = hash.substring( separatorIndex + 1 ); } mod = ( bundle.moduleLookup[ moduleId ] || bundle.externalModuleLookup[ moduleId ] ); moduleName = mod && mod.name; if ( specifierName === 'default' ) { // if it's an external module, always use __default if the // bundle also uses named imports if ( !!externalModule ) { replacement = externalModule.needsNamed ? `${moduleName}__default` : moduleName; } // TODO We currently need to check for the existence of `mod`, because modules // can be skipped. Would be better to replace skipped modules with dummies // - see https://github.com/Rich-Harris/esperanto/issues/32 else if ( mod ) { replacement = mod.identifierReplacements.default; } } else if ( !externalModule ) { replacement = hasOwnProp.call( conflicts, specifierName ) ? `${moduleName}__${specifierName}` : specifierName; } else { replacement = moduleName + '.' + specifierName; } } if ( replacement !== s.as ) { moduleIdentifiers[ s.as ] = replacement; } });
imports.forEach( x => { let moduleId = x.path; let name; moduleId = x.path; // use existing value if ( hasOwnProp.call( nameById, moduleId ) ) { x.name = nameById[ moduleId ]; return; } // if user supplied a function, defer to it if ( userFn && ( name = userFn( moduleId ) ) ) { name = sanitize( name ); if ( hasOwnProp.call( usedNames, name ) ) { // TODO write a test for this throw new Error( `Naming collision: module ${moduleId} cannot be called ${name}` ); } } else { let parts = splitPath( moduleId ); let i; let prefix = ''; let candidate; do { i = parts.length; while ( i-- > 0 ) { candidate = prefix + sanitize( parts.slice( i ).join( '__' ) ); if ( !hasOwnProp.call( usedNames, candidate ) ) { name = candidate; break; } } prefix += '_'; } while ( !name ); } usedNames[ name ] = true; nameById[ moduleId ] = name; x.name = name; });
function deconflict ( name, declared ) { while ( hasOwnProp.call( declared, name ) ) { name = '_' + name; } return name; }
function tryPath ( base, filename, userModules ) { const absolutePath = path.resolve( base, filename ); if ( hasOwnProp.call( userModules, absolutePath ) ) { return Promise.resolve( absolutePath ); } return sander.stat( absolutePath ).then( () => absolutePath ); }
mod.imports.forEach( x => { if ( !hasOwnProp.call( seen, x.path ) ) { let replacement = x.isEmpty ? `${req(x.path)};` : `var ${x.as} = ${req(x.path)};`; mod.body.replace( x.start, x.end, replacement ); seen[ x.path ] = true; } else { mod.body.remove( x.start, x.next ); } });
bundle.modules.forEach( mod => { let x = mod.defaultExport; if ( x ) { let result; if ( x.hasDeclaration && x.name ) { result = hasOwnProp.call( conflicts, x.name ) || otherModulesDeclare( mod, x.name ) ? `${mod.name}__${x.name}` : x.name; } else { result = hasOwnProp.call( conflicts, mod.name ) || ( x.value !== mod.name && ~mod.ast._topLevelNames.indexOf( mod.name )) || otherModulesDeclare( mod, mod.name ) ? `${mod.name}__default` : mod.name; } mod.identifierReplacements.default = result; } });
function otherModulesDeclare ( mod, replacement ) { var i, otherMod; i = bundle.modules.length; while ( i-- ) { otherMod = bundle.modules[i]; if ( mod === otherMod ) { continue; } if ( hasOwnProp.call( otherMod.ast._declared, replacement ) ) { return true; } } }
mod.exports.forEach( x => { var name; if ( x.isDefault ) { if ( x.type === 'namedFunction' || x.type === 'namedClass' ) { // if you have a default export like // // export default function foo () {...} // // you need to rewrite it as // // function foo () {...} // exports.default = foo; // // as the `foo` reference may be used elsewhere // remove the `export default `, keep the rest body.remove( x.start, x.valueStart ); } else if ( x.node.declaration && ( name = x.node.declaration.name ) ) { if ( name === identifierReplacements.default ) { body.remove( x.start, x.end ); } else { let original = hasOwnProp.call( identifierReplacements, name ) ? identifierReplacements[ name ] : name; body.replace( x.start, x.end, `var ${identifierReplacements.default} = ${original};` ); } } else { body.replace( x.start, x.valueStart, `var ${identifierReplacements.default} = ` ); } return; } if ( x.hasDeclaration ) { if ( x.type === 'namedFunction' ) { shouldExportEarly[ x.name ] = true; // TODO what about `function foo () {}; export { foo }`? } body.remove( x.start, x.valueStart ); } else { body.remove( x.start, x.next ); } });
return resolvePath( base, userModules, id, absolutePath, options.resolvePath ).then( absolutePath => { let promise = hasOwnProp.call( promiseByPath, absolutePath ) && promiseByPath[ absolutePath ]; let cyclical = !!promise; if ( cyclical ) { // ensure all modules are set before we // create the bundle... cyclicalModules.push( promise.then( module => x.module = module ) ); // ...then short-circuit return; } return fetchModule( id, absolutePath ).then( module => x.module = module ); }, function handleError ( err ) {
}, function handleError ( err ) { if ( err.code === 'ENOENT' ) { // Most likely an external module let externalModule = hasOwnProp.call( externalModuleLookup, id ) && externalModuleLookup[ id ]; if ( !externalModule ) { externalModule = { id, isExternal: true }; externalModules.push( externalModule ); externalModuleLookup[ id ] = externalModule; } x.module = externalModule; } else { throw err; } } );
// infer names from default imports - e.g. with `import _ from './utils'`, // use '_' instead of generating a name from 'utils' function inferName ( x ) { if ( x.isDefault && !hasOwnProp.call( names, x.module.id ) && !hasOwnProp.call( used, x.as ) ) { names[ x.module.id ] = x.as; used[ x.as ] = true; } }
imports.forEach( x => { if ( hasOwnProp.call( inferredNames, x.path ) ) { x.name = inferredNames[ x.path ]; } });
imports.forEach( x => { if ( x.as && !hasOwnProp.call( usedNames, x.as ) ) { inferredNames[ x.path ] = x.as; } });
export default function transformBody ( bundle, mod, body ) { let identifierReplacements = mod.identifierReplacements; let [ importedBindings, importedNamespaces ] = getReadOnlyIdentifiers( mod.imports ); let exportNames = hasOwnProp.call( bundle.exports, mod.id ) && bundle.exports[ mod.id ]; traverseAst( mod.ast, body, identifierReplacements, importedBindings, importedNamespaces, exportNames ); // Remove import statements mod.imports.forEach( x => { if ( !x.passthrough ) { body.remove( x.start, x.next ); } }); let shouldExportEarly = {}; // Remove export statements mod.exports.forEach( x => { var name; if ( x.isDefault ) { if ( x.type === 'namedFunction' || x.type === 'namedClass' ) { // if you have a default export like // // export default function foo () {...} // // you need to rewrite it as // // function foo () {...} // exports.default = foo; // // as the `foo` reference may be used elsewhere // remove the `export default `, keep the rest body.remove( x.start, x.valueStart ); } else if ( x.node.declaration && ( name = x.node.declaration.name ) ) { if ( name === identifierReplacements.default ) { body.remove( x.start, x.end ); } else { let original = hasOwnProp.call( identifierReplacements, name ) ? identifierReplacements[ name ] : name; body.replace( x.start, x.end, `var ${identifierReplacements.default} = ${original};` ); } } else { body.replace( x.start, x.valueStart, `var ${identifierReplacements.default} = ` ); } return; } if ( x.hasDeclaration ) { if ( x.type === 'namedFunction' ) { shouldExportEarly[ x.name ] = true; // TODO what about `function foo () {}; export { foo }`? } body.remove( x.start, x.valueStart ); } else { body.remove( x.start, x.next ); } }); // If this module exports a namespace - i.e. another module does // `import * from 'foo'` - then we need to make all this module's // exports available, using Object.defineProperty var indentStr = body.getIndentString(); if ( mod._exportsNamespace ) { let namespaceExportBlock = `var ${mod.name} = {\n`, namespaceExports = []; mod.exports.forEach( x => { if ( x.hasDeclaration ) { namespaceExports.push( indentStr + `get ${x.name} () { return ${identifierReplacements[x.name]}; }` ); } else if ( x.isDefault ) { namespaceExports.push( indentStr + `get default () { return ${identifierReplacements.default}; }` ); } else { x.specifiers.forEach( s => { namespaceExports.push( indentStr + `get ${s.name} () { return ${s.name}; }` ); }); } }); namespaceExportBlock += namespaceExports.join( ',\n' ) + '\n};\n\n'; body.prepend( namespaceExportBlock ); } // If this module is responsible for one of the bundle's exports // (it doesn't have to be the entry module, which could re-export // a binding from another module), we write exports here if ( exportNames ) { let exportBlock = []; Object.keys( exportNames ).forEach( name => { var exportAs = exportNames[ name ]; exportBlock.push( `exports.${exportAs} = ${identifierReplacements[name]};` ); }); if ( exportBlock.length ) { body.trim().append( '\n\n' + exportBlock.join( '\n' ) ); } } return body.trim(); }
mod.ast._topLevelNames.forEach( n => { moduleIdentifiers[n] = hasOwnProp.call( conflicts, n ) ? `${mod.name}__${n}` : n; });
function fetchModule ( moduleId, absolutePath ) { if ( !hasOwnProp.call( promiseByPath, absolutePath ) ) { promiseByPath[ absolutePath ] = ( hasOwnProp.call( userModules, absolutePath ) ? Promise.resolve( userModules[ absolutePath ] ) : sander.readFile( absolutePath ).then( String ) ).then( function ( source ) { let code, ast; // normalise if ( typeof source === 'object' ) { code = source.code; ast = source.ast; } else { code = source; ast = null; } if ( options.transform ) { code = options.transform( code, absolutePath ); if ( typeof code !== 'string' && !isThenable( code ) ) { throw new Error( 'transform should return String or Promise' ); } } let module = getModule({ id: moduleId, path: absolutePath, code, ast, // TODO should not need this relativePath: path.relative( base, absolutePath ) }); modules.push( module ); moduleLookup[ moduleId ] = module; let promises = module.imports.map( x => { // TODO remove this, use x.module instead. more flexible, no lookups involved const id = resolveId( x.path, module.relativePath ); if ( id === moduleId ) { throw new Error( 'A module (' + moduleId + ') cannot import itself' ); } // Some modules can be skipped if ( skip && ~skip.indexOf( id ) ) { const skippedModule = { id, isSkipped: true }; x.module = skippedModule; return skippedModule; } return resolvePath( base, userModules, id, absolutePath, options.resolvePath ).then( absolutePath => { let promise = hasOwnProp.call( promiseByPath, absolutePath ) && promiseByPath[ absolutePath ]; let cyclical = !!promise; if ( cyclical ) { // ensure all modules are set before we // create the bundle... cyclicalModules.push( promise.then( module => x.module = module ) ); // ...then short-circuit return; } return fetchModule( id, absolutePath ).then( module => x.module = module ); }, function handleError ( err ) { if ( err.code === 'ENOENT' ) { // Most likely an external module let externalModule = hasOwnProp.call( externalModuleLookup, id ) && externalModuleLookup[ id ]; if ( !externalModule ) { externalModule = { id, isExternal: true }; externalModules.push( externalModule ); externalModuleLookup[ id ] = externalModule; } x.module = externalModule; } else { throw err; } } ); }); return Promise.all( promises ) .then( () => module ); }); } return promiseByPath[ absolutePath ]; }