Huge News!Announcing our $40M Series B led by Abstract Ventures.Learn More
Socket
Sign inDemoInstall
Socket

rollup

Package Overview
Dependencies
Maintainers
3
Versions
834
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

rollup - npm Package Compare versions

Comparing version 0.15.0 to 0.16.0

src/Scope.js

6

CHANGELOG.md
# rollup changelog
## 0.16.0
* Internal refactoring ([#99](https://github.com/rollup/rollup/pull/99))
* Optimisation for statically-analysable namespace imports ([#99](https://github.com/rollup/rollup/pull/99))
* Windows support (theoretically!) ([#117](https://github.com/rollup/rollup/pull/117) / [#119](https://github.com/rollup/rollup/pull/119))
## 0.15.0

@@ -4,0 +10,0 @@

12

package.json
{
"name": "rollup",
"version": "0.15.0",
"version": "0.16.0",
"description": "Next-generation ES6 module bundler",

@@ -19,3 +19,3 @@ "main": "dist/rollup.js",

"type": "git",
"url": "https://github.com/rich-harris/rollup"
"url": "https://github.com/rollup/rollup"
},

@@ -30,8 +30,12 @@ "keywords": [

"author": "Rich Harris",
"contributors": [
"Oskar Segersvärd <victorystick@gmail.com>"
],
"license": "MIT",
"bugs": {
"url": "https://github.com/rich-harris/rollup/issues"
"url": "https://github.com/rollup/rollup/issues"
},
"homepage": "https://github.com/rich-harris/rollup",
"homepage": "https://github.com/rollup/rollup",
"devDependencies": {
"babel": "^5.8.21",
"babel-core": "^5.5.8",

@@ -38,0 +42,0 @@ "console-group": "^0.1.2",

@@ -1,2 +0,1 @@

import { basename, extname } from './utils/path';
import { Promise } from 'sander';

@@ -8,3 +7,2 @@ import MagicString from 'magic-string';

import finalisers from './finalisers/index';
import makeLegalIdentifier from './utils/makeLegalIdentifier';
import ensureArray from './utils/ensureArray';

@@ -16,2 +14,3 @@ import { defaultResolver, defaultExternalResolver } from './utils/resolveId';

import { unixizePath } from './utils/normalizePlatform.js';
import Scope from './Scope';

@@ -35,2 +34,14 @@ export default class Bundle {

// The global scope, and the bundle's internal scope.
this.globals = new Scope();
this.scope = new Scope( this.globals );
// TODO strictly speaking, this only applies with non-ES6, non-default-only bundles
// However, the deconfliction logic is greatly simplified by being the same for all formats.
this.globals.define( 'exports' );
this.scope.bind( 'exports', this.globals.reference( 'exports' ) );
// Alias for entryModule.exports.
this.exports = null;
this.toExport = null;

@@ -45,5 +56,2 @@

this.internalNamespaceModules = [];
this.assumedGlobals = blank();
this.assumedGlobals.exports = true; // TODO strictly speaking, this only applies with non-ES6, non-default-only bundles
}

@@ -55,121 +63,27 @@

.then( entryModule => {
entryModule.bindImportSpecifiers();
const defaultExport = entryModule.exports.default;
this.entryModule = entryModule;
this.exports = entryModule.exports;
if ( defaultExport ) {
entryModule.needsDefault = true;
// `export default function foo () {...}` -
// use the declared name for the export
if ( defaultExport.identifier ) {
entryModule.suggestName( 'default', defaultExport.identifier );
}
// `export default a + b` - generate an export name
// based on the id of the entry module
else {
let defaultExportName = makeLegalIdentifier( basename( this.entryModule.id ).slice( 0, -extname( this.entryModule.id ).length ) );
// deconflict
let topLevelNames = [];
entryModule.statements.forEach( statement => {
keys( statement.defines ).forEach( name => topLevelNames.push( name ) );
});
while ( ~topLevelNames.indexOf( defaultExportName ) ) {
defaultExportName = `_${defaultExportName}`;
}
entryModule.suggestName( 'default', defaultExportName );
}
}
entryModule.markAllStatements( true );
this.markAllModifierStatements();
this.orderedModules = this.sort();
});
}
// TODO would be better to deconflict once, rather than per-render
deconflict ( es6 ) {
let usedNames = blank();
this.exports.localIds().forEach( ([ , id ]) => {
// If the export is a module (namespace), we need
// all its exports dynamically accessible.
if ( id.module === id ) id.dynamicAccess();
});
// ensure no conflicts with globals
keys( this.assumedGlobals ).forEach( name => usedNames[ name ] = true );
// As a last step, deconflict all identifier names, once.
this.scope.deconflict();
let allReplacements = blank();
// Alias the default import to the external module named
// for external modules that don't need named imports.
this.externalModules.forEach( module => {
const externalDefault = module.exports.lookup( 'default' );
// Assign names to external modules
this.externalModules.forEach( module => {
// while we're here...
allReplacements[ module.id ] = blank();
// TODO is this necessary in the ES6 case?
let name = makeLegalIdentifier( module.suggestedNames['*'] || module.suggestedNames.default || module.id );
module.name = getSafeName( name );
});
// Discover conflicts (i.e. two statements in separate modules both define `foo`)
let i = this.orderedModules.length;
while ( i-- ) {
const module = this.orderedModules[i];
// while we're here...
allReplacements[ module.id ] = blank();
keys( module.definitions ).forEach( name => {
const safeName = getSafeName( name );
if ( safeName !== name ) {
module.rename( name, safeName );
allReplacements[ module.id ][ name ] = safeName;
}
if ( externalDefault && !( module.needsNamed || module.needsAll ) ) {
externalDefault.name = module.name;
}
});
});
}
// Assign non-conflicting names to internal default/namespace export
this.orderedModules.forEach( module => {
if ( !module.needsDefault && !module.needsAll ) return;
if ( module.needsAll ) {
const namespaceName = getSafeName( module.suggestedNames[ '*' ] );
module.replacements[ '*' ] = namespaceName;
}
if ( module.needsDefault || module.needsAll && module.exports.default ) {
const defaultExport = module.exports.default;
// only create a new name if either
// a) it's an expression (`export default 42`) or
// b) it's a name that is reassigned to (`export var a = 1; a = 2`)
if ( defaultExport && defaultExport.identifier && !defaultExport.isModified ) return; // TODO encapsulate check for whether we need synthetic default name
const defaultName = getSafeName( module.suggestedNames.default );
module.replacements.default = defaultName;
}
});
this.orderedModules.forEach( module => {
keys( module.imports ).forEach( localName => {
if ( !module.imports[ localName ].isUsed ) return;
const bundleName = this.trace( module, localName, es6 );
if ( bundleName !== localName ) {
allReplacements[ module.id ][ localName ] = bundleName;
}
});
});
function getSafeName ( name ) {
while ( usedNames[ name ] ) {
name = `_${name}`;
}
usedNames[ name ] = true;
return name;
}
return allReplacements;
}

@@ -201,3 +115,9 @@

return this.fetchAllDependencies( module ).then( () => module );
return this.fetchAllDependencies( module ).then( () => {
// Analyze the module once all its dependencies have been resolved.
// This means that any dependencies of a module has already been
// analysed when it's time for the module itself.
module.analyse();
return module;
});
});

@@ -215,3 +135,3 @@ }

if ( !this.moduleById[ source ] ) {
const module = new ExternalModule( source );
const module = new ExternalModule( { id: source, bundle: this } );
this.externalModules.push( module );

@@ -235,47 +155,4 @@ this.moduleById[ source ] = module;

markAllModifierStatements () {
let settled = true;
this.modules.forEach( module => {
module.statements.forEach( statement => {
if ( statement.isIncluded ) return;
keys( statement.modifies ).forEach( name => {
const definingStatement = module.definitions[ name ];
const exportDeclaration = module.exports[ name ] || module.reexports[ name ] || (
module.exports.default && module.exports.default.identifier === name && module.exports.default
);
const shouldMark = ( definingStatement && definingStatement.isIncluded ) ||
( exportDeclaration && exportDeclaration.isUsed );
if ( shouldMark ) {
settled = false;
statement.mark();
return;
}
// special case - https://github.com/rollup/rollup/pull/40
// TODO refactor this? it's a bit confusing
const importDeclaration = module.imports[ name ];
if ( !importDeclaration || importDeclaration.module.isExternal ) return;
const otherExportDeclaration = importDeclaration.module.exports[ importDeclaration.name ];
// TODO things like `export default a + b` don't apply here... right?
const otherDefiningStatement = module.findDefiningStatement( otherExportDeclaration.localName );
if ( !otherDefiningStatement ) return;
settled = false;
statement.mark();
});
});
});
if ( !settled ) this.markAllModifierStatements();
}
render ( options = {} ) {
const format = options.format || 'es6';
const allReplacements = this.deconflict( format === 'es6' );

@@ -307,3 +184,3 @@ // Determine export mode - 'default', 'named', 'none'

module.reassignments.forEach( name => {
isReassignedVarDeclaration[ module.replacements[ name ] || name ] = true;
isReassignedVarDeclaration[ module.locals.lookup( name ).name ] = true;
});

@@ -313,6 +190,5 @@ });

if ( format !== 'es6' && exportMode === 'named' ) {
keys( this.entryModule.exports )
.concat( keys( this.entryModule.reexports ) )
this.exports.getNames()
.forEach( name => {
const canonicalName = this.traceExport( this.entryModule, name );
const canonicalName = this.exports.lookup( name ).name;

@@ -335,4 +211,3 @@ if ( isReassignedVarDeclaration[ canonicalName ] ) {

// ensure we don't try and export them again at the bottom
this.toExport = keys( this.entryModule.exports )
.concat( keys( this.entryModule.reexports ) )
this.toExport = this.exports.getNames()
.filter( key => !varExports[ key ] );

@@ -343,3 +218,3 @@

this.orderedModules.forEach( module => {
const source = module.render( allBundleExports, allReplacements[ module.id ], format );
const source = module.render( allBundleExports, format === 'es6' );
if ( source.toString().length ) {

@@ -352,11 +227,8 @@ magicString.addSource( source );

const indentString = getIndentString( magicString, options );
const namespaceBlock = this.internalNamespaceModules.map( module => {
const exports = keys( module.exports )
.concat( keys( module.reexports ) )
.map( name => {
const canonicalName = this.traceExport( module, name );
return `${indentString}get ${name} () { return ${canonicalName}; }`;
});
const exports = module.exports.localIds().map( ( [ name, id ] ) =>
`${indentString}get ${name} () { return ${id.name}; }`);
return `var ${module.replacements['*']} = {\n` +
return `var ${module.name} = {\n` +
exports.join( ',\n' ) +

@@ -406,9 +278,14 @@ `\n};\n\n`;

sort () {
let seen = {};
// Set of visited module ids.
let seen = blank();
let ordered = [];
let hasCycles;
let strongDeps = {};
let stronglyDependsOn = {};
// Map from module id to list of modules.
let strongDeps = blank();
// Map from module id to boolean.
let stronglyDependsOn = blank();
function visit ( module ) {

@@ -489,36 +366,2 @@ seen[ module.id ] = true;

}
trace ( module, localName, es6 ) {
const importDeclaration = module.imports[ localName ];
// defined in this module
if ( !importDeclaration ) return module.replacements[ localName ] || localName;
// defined elsewhere
return this.traceExport( importDeclaration.module, importDeclaration.name, es6 );
}
traceExport ( module, name, es6 ) {
if ( module.isExternal ) {
if ( name === 'default' ) return module.needsNamed && !es6 ? `${module.name}__default` : module.name;
if ( name === '*' ) return module.name;
return es6 ? name : `${module.name}.${name}`;
}
const reexportDeclaration = module.reexports[ name ];
if ( reexportDeclaration ) {
return this.traceExport( reexportDeclaration.module, reexportDeclaration.localName );
}
if ( name === '*' ) return module.replacements[ '*' ];
if ( name === 'default' ) return module.defaultName();
const exportDeclaration = module.exports[ name ];
if ( exportDeclaration ) return this.trace( module, exportDeclaration.localName );
const exportDelegate = module.exportDelegates[ name ];
if ( exportDelegate ) return this.traceExport( exportDelegate.module, name, es6 );
throw new Error( `Could not trace binding '${name}' from ${module.id}` );
}
}
import { blank } from './utils/object';
import makeLegalIdentifier from './utils/makeLegalIdentifier';
// An external identifier.
class Id {
constructor ( module, name ) {
this.originalName = this.name = name;
this.module = module;
this.modifierStatements = [];
}
// Flags the identifier as imported by the bundle when marked.
mark () {
this.module.importedByBundle[ this.originalName ] = true;
this.modifierStatements.forEach( stmt => stmt.mark() );
}
}
export default class ExternalModule {
constructor ( id ) {
constructor ( { id, bundle } ) {
this.id = id;
this.name = null;
this.isExternal = true;
this.importedByBundle = [];
// Implement `Identifier` interface.
this.originalName = this.name = makeLegalIdentifier( id );
this.module = this;
this.isModule = true;
this.suggestedNames = blank();
// Define the external module's name in the bundle scope.
bundle.scope.define( id, this );
this.needsDefault = false;
this.isExternal = true;
this.importedByBundle = blank();

@@ -22,17 +42,26 @@ // Invariant: needsNamed and needsAll are never both true at once.

this.needsAll = false;
}
findDefiningStatement () {
return null;
}
this.exports = bundle.scope.virtual( false );
rename () {
// noop
const { reference } = this.exports;
// Override reference.
this.exports.reference = name => {
if ( name !== 'default' ) {
this.needsNamed = true;
}
if ( !this.exports.defines( name ) ) {
this.exports.define( name, new Id( this, name ) );
}
return reference.call( this.exports, name );
};
}
suggestName ( exportName, suggestion ) {
if ( !this.suggestedNames[ exportName ] ) {
this.suggestedNames[ exportName ] = suggestion;
}
// External modules are always marked for inclusion in the bundle.
// Marking an external module signals its use as a namespace.
mark () {
this.needsAll = true;
}
}

@@ -0,1 +1,2 @@

import getInteropBlock from './shared/getInteropBlock';
import getExportBlock from './shared/getExportBlock';

@@ -7,14 +8,11 @@

// TODO handle empty imports, once they're supported
const importBlock = bundle.externalModules
.map( module => {
let requireStatement = `var ${module.name} = require('${module.id}');`;
let importBlock = bundle.externalModules
.map( module => `var ${module.name} = require('${module.id}');`)
.join('\n');
if ( module.needsDefault ) {
requireStatement += '\n' + ( module.needsNamed ? `var ${module.name}__default = ` : `${module.name} = ` ) +
`'default' in ${module.name} ? ${module.name}['default'] : ${module.name};`;
}
const interopBlock = getInteropBlock( bundle );
return requireStatement;
})
.join( '\n' );
if ( interopBlock ) {
importBlock += '\n' + interopBlock;
}

@@ -21,0 +19,0 @@ if ( importBlock ) {

@@ -1,11 +0,12 @@

import { blank, keys } from '../utils/object';
import { keys } from '../utils/object';
function uniqueNames ( declarations ) {
let uniques = blank();
function specifiersFor ( externalModule ) {
return keys( externalModule.importedByBundle )
.filter( notDefault )
.sort()
.map( name => {
const id = externalModule.exports.lookup( name );
declarations
.filter( declaration => !/^(default|\*)$/.test( declaration.name ) )
.forEach( declaration => uniques[ declaration.name ] = true );
return keys( uniques );
return name !== id.name ? `${name} as ${id.name}` : name;
});
}

@@ -22,15 +23,14 @@

if ( module.needsDefault ) {
specifiers.push( module.importedByBundle.filter( declaration =>
declaration.name === 'default' )[0].localName );
const id = module.exports.lookup( 'default' );
if ( id ) {
specifiers.push( id.name );
}
if ( module.needsAll ) {
specifiers.push( '* as ' + module.importedByBundle.filter( declaration =>
declaration.name === '*' )[0].localName );
specifiers.push( '* as ' + module.name );
}
if ( module.needsNamed ) {
specifiers.push( '{ ' + uniqueNames( module.importedByBundle )
.join( ', ' ) + ' }' );
specifiers.push( '{ ' + specifiersFor( module ).join( ', ' ) + ' }' );
}

@@ -51,7 +51,7 @@

const specifiers = bundle.toExport.filter( notDefault ).map( name => {
const canonicalName = bundle.traceExport( module, name );
const id = bundle.exports.lookup( name );
return canonicalName === name ?
return id.name === name ?
name :
`${canonicalName} as ${name}`;
`${id.name} as ${name}`;
});

@@ -61,5 +61,5 @@

const defaultExport = module.exports.default || module.reexports.default;
const defaultExport = module.exports.lookup( 'default' );
if ( defaultExport ) {
exportBlock += `export default ${bundle.traceExport(module,'default')};`;
exportBlock += `export default ${ defaultExport.name };`;
}

@@ -66,0 +66,0 @@

@@ -0,9 +1,15 @@

function wrapAccess ( id ) {
return ( id.originalName !== 'default' && id.module && id.module.isExternal ) ?
id.module.name + propertyAccess( id.originalName ) : id.name;
}
function propertyAccess ( name ) {
return name === 'default' ? `['default']` : `.${name}`;
}
export default function getExportBlock ( bundle, exportMode, mechanism = 'return' ) {
if ( exportMode === 'default' ) {
const defaultExport = bundle.entryModule.exports.default;
const id = bundle.exports.lookup( 'default' );
const defaultExportName = bundle.entryModule.replacements.default ||
defaultExport.identifier;
return `${mechanism} ${defaultExportName};`;
return `${mechanism} ${wrapAccess( id )};`;
}

@@ -13,7 +19,7 @@

.map( name => {
const prop = name === 'default' ? `['default']` : `.${name}`;
name = bundle.traceExport( bundle.entryModule, name );
return `exports${prop} = ${name};`;
const id = bundle.exports.lookup( name );
return `exports${propertyAccess( name )} = ${wrapAccess( id )};`;
})
.join( '\n' );
}
export default function getInteropBlock ( bundle ) {
return bundle.externalModules
.map( module => {
return module.needsDefault ?
( module.needsNamed ?
`var ${module.name}__default = 'default' in ${module.name} ? ${module.name}['default'] : ${module.name};` :
`${module.name} = 'default' in ${module.name} ? ${module.name}['default'] : ${module.name};` ) :
null;
const def = module.exports.lookup( 'default' );
if ( !def ) return;
return ( module.needsNamed ? 'var ' : '' ) +
`${def.name} = 'default' in ${module.name} ? ${module.name}['default'] : ${module.name};`;
})

@@ -10,0 +11,0 @@ .filter( Boolean )

@@ -0,1 +1,2 @@

import { basename, extname } from './utils/path';
import { parse } from 'acorn';

@@ -9,17 +10,41 @@ import MagicString from 'magic-string';

function deconflict ( name, names ) {
while ( name in names ) {
name = `_${name}`;
function isEmptyExportedVarDeclaration ( node, exports, toExport ) {
if ( node.type !== 'VariableDeclaration' || node.declarations[0].init ) return false;
const name = node.declarations[0].id.name;
const id = exports.lookup( name );
return id && id.name in toExport;
}
function removeSourceMappingURLComments ( source, magicString ) {
const pattern = /\/\/#\s+sourceMappingURL=.+\n?/g;
let match;
while ( match = pattern.exec( source ) ) {
magicString.remove( match.index, match.index + match[0].length );
}
}
return name;
function assign ( target, source ) {
for ( var key in source ) target[ key ] = source[ key ];
}
function isEmptyExportedVarDeclaration ( node, allBundleExports, moduleReplacements ) {
if ( node.type !== 'VariableDeclaration' || node.declarations[0].init ) return false;
class Id {
constructor ( module, name, statement ) {
this.originalName = this.name = name;
this.module = module;
this.statement = statement;
const name = node.declarations[0].id.name;
const canonicalName = moduleReplacements[ name ] || name;
this.modifierStatements = [];
return canonicalName in allBundleExports;
// modifiers
this.isUsed = false;
}
mark () {
this.isUsed = true;
this.statement.mark();
this.modifierStatements.forEach( stmt => stmt.mark() );
}
}

@@ -33,3 +58,13 @@

this.id = id;
this.module = this;
this.isModule = true;
// Implement Identifier interface.
this.name = makeLegalIdentifier( basename( id ).slice( 0, -extname( id ).length ) );
// HACK: If `id` isn't a path, the above code yields the empty string.
if ( !this.name ) {
this.name = makeLegalIdentifier( id );
}
// By default, `id` is the filename. Custom resolvers and loaders

@@ -41,10 +76,4 @@ // can change that, but it makes sense to use it for the source filename

// remove existing sourceMappingURL comments
const pattern = /\/\/#\s+sourceMappingURL=.+\n?/g;
let match;
while ( match = pattern.exec( source ) ) {
this.magicString.remove( match.index, match.index + match[0].length );
}
removeSourceMappingURLComments( source, this.magicString );
this.suggestedNames = blank();
this.comments = [];

@@ -55,24 +84,47 @@

// all dependencies
this.dependencies = [];
this.resolvedIds = blank();
this.boundImportSpecifiers = false;
// imports and exports, indexed by local name
this.imports = blank();
this.exports = blank();
this.reexports = blank();
this.exportDelegates = blank();
// Virtual scopes for the local and exported names.
this.locals = bundle.scope.virtual( true );
this.exports = bundle.scope.virtual( false );
const { reference, inScope } = this.exports;
this.exports.reference = name => {
// If we have it, grab it.
if ( inScope.call( this.exports, name ) ) {
return reference.call( this.exports, name );
}
// ... otherwise search exportAlls
for ( let i = 0; i < this.exportAlls.length; i += 1 ) {
const module = this.exportAlls[i];
if ( module.exports.inScope( name ) ) {
return module.exports.reference( name );
}
}
// throw new Error( `The name "${name}" is never exported (from ${this.id})!` );
return reference.call( this.exports, name );
};
this.exports.inScope = name => {
if ( inScope.call( this.exports, name ) ) return true;
return this.exportAlls.some( module => module.exports.inScope( name ) );
};
// Create a unique virtual scope for references to the module.
// const unique = bundle.scope.virtual();
// unique.define( this.name, this );
// this.reference = unique.reference( this.name );
this.exportAlls = [];
this.replacements = blank();
this.reassignments = [];
this.marked = blank();
this.definitions = blank();
this.definitionPromises = blank();
this.modifications = blank();
// TODO: change to false, and detect when it's necessary.
this.needsDynamicAccess = false;
this.analyse();
this.dependencies = this.collectDependencies();
}

@@ -86,3 +138,3 @@

if ( source ) {
if ( !~this.dependencies.indexOf( source ) ) this.dependencies.push( source );
const module = this.getModule( source );

@@ -92,6 +144,8 @@ if ( node.type === 'ExportAllDeclaration' ) {

// When an unknown import is encountered, we see if one of them can satisfy it.
this.exportAlls.push({
statement,
source
});
if ( module.isExternal ) {
throw new Error( `Cannot trace 'export *' references through external modules.` );
}
this.exportAlls.push( module );
}

@@ -101,7 +155,5 @@

node.specifiers.forEach( specifier => {
this.reexports[ specifier.exported.name ] = {
source,
localName: specifier.local.name,
module: null // filled in later
};
// Bind the export of this module, to the export of the other.
this.exports.bind( specifier.exported.name,
module.exports.reference( specifier.local.name ) );
});

@@ -123,12 +175,14 @@ }

null;
const name = identifier || this.name;
this.exports.default = {
statement,
name: 'default',
localName: identifier || 'default',
identifier,
isDeclaration,
isAnonymous,
isModified: false // in case of `export default foo; foo = somethingElse`
};
// Always define a new `Identifier` for the default export.
const id = new Id( this, name, statement );
// Keep the identifier name, if one exists.
// We can optimize the newly created default `Identifier` away,
// if it is never modified.
// in case of `export default foo; foo = somethingElse`
assign( id, { isDeclaration, isAnonymous, identifier } );
this.exports.define( 'default', id );
}

@@ -146,7 +200,3 @@

this.exports[ exportedName ] = {
statement,
localName,
exportedName
};
this.exports.bind( exportedName, this.locals.reference( localName ) );
});

@@ -168,7 +218,4 @@ }

this.exports[ name ] = {
statement,
localName: name,
expression: declaration
};
this.locals.define( name, new Id( this, name, statement ) );
this.exports.bind( name, this.locals.reference( name ) );
}

@@ -180,6 +227,4 @@ }

const node = statement.node;
const source = node.source.value;
const module = this.getModule( node.source.value );
if ( !~this.dependencies.indexOf( source ) ) this.dependencies.push( source );
node.specifiers.forEach( specifier => {

@@ -190,5 +235,4 @@ const isDefault = specifier.type === 'ImportDefaultSpecifier';

const localName = specifier.local.name;
const name = isDefault ? 'default' : isNamespace ? '*' : specifier.imported.name;
if ( this.imports[ localName ] ) {
if ( this.locals.defines( localName ) ) {
const err = new Error( `Duplicated import '${localName}'` );

@@ -200,7 +244,22 @@ err.file = this.id;

this.imports[ localName ] = {
source,
name,
localName
};
if ( isNamespace ) {
// If it's a namespace import, we bind the localName to the module itself.
module.needsAll = true;
module.name = localName;
this.locals.bind( localName, module );
} else {
const name = isDefault ? 'default' : specifier.imported.name;
this.locals.bind( localName, module.exports.reference( name ) );
// For compliance with earlier Rollup versions.
// If the module is external, and we access the default.
// Rewrite the module name, and the default name to the
// `localName` we use for it.
if ( module.isExternal && isDefault ) {
const id = module.exports.lookup( name );
module.name = id.name = localName;
id.name += '__default';
}
}
});

@@ -219,8 +278,4 @@ }

keys( statement.defines ).forEach( name => {
this.definitions[ name ] = statement;
this.locals.define( name, new Id( this, name, statement ) );
});
keys( statement.modifies ).forEach( name => {
( this.modifications[ name ] || ( this.modifications[ name ] = [] ) ).push( statement );
});
});

@@ -253,35 +308,35 @@

keys( statement.dependsOn ).forEach( name => {
if ( !this.definitions[ name ] && !this.imports[ name ] ) {
this.bundle.assumedGlobals[ name ] = true;
// For each name we depend on that isn't in scope,
// add a new global and bind the local name to it.
if ( !this.locals.inScope( name ) ) {
this.bundle.globals.define( name, {
originalName: name,
name,
mark () {}
});
this.locals.bind( name, this.bundle.globals.reference( name ) );
}
});
});
// OPTIMIZATION!
// If we have a default export and it's value is never modified,
// bind to it directly.
const def = this.exports.lookup( 'default' );
if ( def && !def.isModified && def.identifier ) {
this.exports.bind( 'default', this.locals.reference( def.identifier ) );
}
}
bindImportSpecifiers () {
if ( this.boundImportSpecifiers ) return;
this.boundImportSpecifiers = true;
// Returns the set of imported module ids by going through all import/exports statements.
collectDependencies () {
const importedModules = blank();
[ this.imports, this.reexports ].forEach( specifiers => {
keys( specifiers ).forEach( name => {
const specifier = specifiers[ name ];
if ( specifier.module ) return;
const id = this.resolvedIds[ specifier.source ];
specifier.module = this.bundle.moduleById[ id ];
});
this.statements.forEach( statement => {
if ( statement.isImportDeclaration || ( statement.isExportDeclaration && statement.node.source ) ) {
importedModules[ statement.node.source.value ] = true;
}
});
this.exportAlls.forEach( delegate => {
const id = this.resolvedIds[ delegate.source ];
delegate.module = this.bundle.moduleById[ id ];
});
this.dependencies.forEach( source => {
const id = this.resolvedIds[ source ];
const module = this.bundle.moduleById[ id ];
if ( !module.isExternal ) module.bindImportSpecifiers();
});
return keys( importedModules );
}

@@ -302,4 +357,3 @@

// include module for its side-effects
const id = this.resolvedIds[ statement.node.source.value ];
const module = this.bundle.moduleById[ id ];
const module = this.getModule( statement.node.source.value );

@@ -312,13 +366,7 @@ if ( !module.isExternal ) strongDependencies[ module.id ] = module;

statement.node.specifiers.forEach( specifier => {
let reexport;
let module = this;
let name = specifier.exported.name;
while ( !module.isExternal && module.reexports[ name ] && module.reexports[ name ].isUsed ) {
reexport = module.reexports[ name ];
module = reexport.module;
name = reexport.localName;
}
addDependency( strongDependencies, reexport );
let id = this.exports.lookup( name );
addDependency( strongDependencies, id );
});

@@ -332,4 +380,3 @@ }

addDependency( strongDependencies, this.exportDelegates[ name ] ) ||
addDependency( strongDependencies, this.imports[ name ] );
addDependency( strongDependencies, this.locals.lookup( name ) );
});

@@ -345,94 +392,43 @@ }

addDependency( weakDependencies, this.exportDelegates[ name ] ) ||
addDependency( weakDependencies, this.imports[ name ] );
addDependency( weakDependencies, this.locals.lookup( name ) );
});
});
return { strongDependencies, weakDependencies };
}
this.locals.getNames().forEach( name => {
const id = this.locals.lookup( name );
defaultName () {
const defaultExport = this.exports.default;
if ( !id.modifierStatements ) return;
if ( !defaultExport ) return null;
id.modifierStatements.forEach( statement => {
const module = statement.module;
weakDependencies[ module.id ] = module;
});
});
const name = defaultExport.identifier && !defaultExport.isModified ?
defaultExport.identifier :
this.replacements.default;
// `Bundle.sort` gets stuck in an infinite loop if a module has
// `strongDependencies` to itself. Make sure it doesn't happen.
delete strongDependencies[ this.id ];
delete weakDependencies[ this.id ];
return this.replacements[ name ] || name;
return { strongDependencies, weakDependencies };
}
findDefiningStatement ( name ) {
if ( this.definitions[ name ] ) return this.definitions[ name ];
// Enforce dynamic access of the module's properties.
dynamicAccess () {
if ( this.needsDynamicAccess ) return;
// TODO what about `default`/`*`?
this.needsDynamicAccess = true;
this.markAllExportStatements();
const importDeclaration = this.imports[ name ];
if ( !importDeclaration ) return null;
if ( !~this.bundle.internalNamespaceModules.indexOf( this ) ) {
this.bundle.internalNamespaceModules.push( this );
}
}
return importDeclaration.module.findDefiningStatement( name );
getModule ( source ) {
return this.bundle.moduleById[ this.resolvedIds[ source ] ];
}
mark ( name ) {
// shortcut cycles
if ( this.marked[ name ] ) return;
this.marked[ name ] = true;
// The definition for this name is in a different module
if ( this.imports[ name ] ) {
const importDeclaration = this.imports[ name ];
importDeclaration.isUsed = true;
const module = importDeclaration.module;
// suggest names. TODO should this apply to non default/* imports?
if ( importDeclaration.name === 'default' ) {
// TODO this seems ropey
const localName = importDeclaration.localName;
let suggestion = this.suggestedNames[ localName ] || localName;
// special case - the module has its own import by this name
while ( !module.isExternal && module.imports[ suggestion ] ) {
suggestion = `_${suggestion}`;
}
module.suggestName( 'default', suggestion );
} else if ( importDeclaration.name === '*' ) {
const localName = importDeclaration.localName;
const suggestion = this.suggestedNames[ localName ] || localName;
module.suggestName( '*', suggestion );
module.suggestName( 'default', `${suggestion}__default` );
}
if ( importDeclaration.name === 'default' ) {
module.needsDefault = true;
} else if ( importDeclaration.name === '*' ) {
module.needsAll = true;
} else {
module.needsNamed = true;
}
if ( module.isExternal ) {
module.importedByBundle.push( importDeclaration );
}
else if ( importDeclaration.name === '*' ) {
// we need to create an internal namespace
if ( !~this.bundle.internalNamespaceModules.indexOf( module ) ) {
this.bundle.internalNamespaceModules.push( module );
}
module.markAllExportStatements();
}
else {
module.markExport( importDeclaration.name, name, this );
}
}
else {
const statement = name === 'default' ? this.exports.default.statement : this.definitions[ name ];
if ( statement ) statement.mark();
}
mark () {
this.dynamicAccess();
}

@@ -449,4 +445,3 @@

if ( !statement.node.specifiers.length ) {
const id = this.resolvedIds[ statement.node.source.value ];
const otherModule = this.bundle.moduleById[ id ];
const otherModule = this.getModule( statement.node.source.value );

@@ -465,2 +460,7 @@ if ( !otherModule.isExternal ) otherModule.markAllStatements();

else {
// Be sure to mark the default export for the entry module.
if ( isEntryModule && statement.node.type === 'ExportDefaultDeclaration' ) {
this.exports.lookup( 'default' ).mark();
}
statement.mark();

@@ -477,45 +477,2 @@ }

markExport ( name, suggestedName, importer ) {
const reexport = this.reexports[ name ];
const exportDeclaration = this.exports[ name ];
if ( reexport ) {
reexport.isUsed = true;
reexport.module.markExport( reexport.localName, suggestedName, this );
}
else if ( exportDeclaration ) {
exportDeclaration.isUsed = true;
if ( name === 'default' ) {
this.needsDefault = true;
this.suggestName( 'default', suggestedName );
return exportDeclaration.statement.mark();
}
this.mark( exportDeclaration.localName );
}
else {
// See if there exists an export delegate that defines `name`.
let i;
for ( i = 0; i < this.exportAlls.length; i += 1 ) {
const declaration = this.exportAlls[i];
if ( declaration.module.exports[ name ] ) {
// It's found! This module exports `name` through declaration.
// It is however not imported into this scope.
this.exportDelegates[ name ] = declaration;
declaration.module.markExport( name );
declaration.statement.dependsOn[ name ] =
declaration.statement.stronglyDependsOn[ name ] = true;
return;
}
}
throw new Error( `Module ${this.id} does not export ${name} (imported by ${importer.id})` );
}
}
parse ( ast ) {

@@ -604,7 +561,3 @@ // The ast can be supplied programmatically (but usually won't be)

rename ( name, replacement ) {
this.replacements[ name ] = replacement;
}
render ( allBundleExports, moduleReplacements ) {
render ( toExport, direct ) {
let magicString = this.magicString.clone();

@@ -627,3 +580,3 @@

// skip `export var foo;` if foo is exported
if ( isEmptyExportedVarDeclaration( statement.node.declaration, allBundleExports, moduleReplacements ) ) {
if ( isEmptyExportedVarDeclaration( statement.node.declaration, this.exports, toExport ) ) {
magicString.remove( statement.start, statement.next );

@@ -636,3 +589,3 @@ return;

// (otherwise we're left with `exports.foo;`, which is useless)
if ( isEmptyExportedVarDeclaration( statement.node, allBundleExports, moduleReplacements ) ) {
if ( isEmptyExportedVarDeclaration( statement.node, this.exports, toExport ) ) {
magicString.remove( statement.start, statement.next );

@@ -645,3 +598,3 @@ return;

// insert `var/let/const` if necessary
if ( !allBundleExports[ statement.node.declarations[0].id.name ] ) {
if ( !toExport[ statement.node.declarations[0].id.name ] ) {
magicString.insert( statement.start, `${statement.node.kind} ` );

@@ -656,10 +609,30 @@ }

// Indirect identifier access.
if ( !direct ) {
keys( statement.dependsOn )
.forEach( name => {
const id = this.locals.lookup( name );
// We shouldn't create a replacement for `id` if
// 1. `id` is a Global, in which case it has no module property
// 2. `id.module` isn't external, which means we have direct access
// 3. `id` is its own module, in the case of namespace imports
if ( id.module && id.module.isExternal && id.module !== id ) {
replacements[ name ] = id.originalName === 'default' ?
// default names are always directly accessed
id.name :
// other names are indirectly accessed
`${id.module.name}.${id.originalName}`;
}
});
}
keys( statement.dependsOn )
.concat( keys( statement.defines ) )
.forEach( name => {
const bundleName = moduleReplacements[ name ] || name;
const bundleName = this.locals.lookup( name ).name;
if ( allBundleExports[ bundleName ] ) {
bundleExports[ name ] = replacements[ name ] = allBundleExports[ bundleName ];
} else if ( bundleName !== name ) { // TODO weird structure
if ( toExport[ bundleName ] ) {
bundleExports[ name ] = replacements[ name ] = toExport[ bundleName ];
} else if ( bundleName !== name && !replacements[ name ] ) { // TODO weird structure
replacements[ name ] = bundleName;

@@ -685,5 +658,6 @@ }

else if ( statement.node.type === 'ExportDefaultDeclaration' ) {
const canonicalName = this.defaultName();
const def = this.exports.lookup( 'default' );
if ( statement.node.declaration.type === 'Identifier' && canonicalName === ( moduleReplacements[ statement.node.declaration.name ] || statement.node.declaration.name ) ) {
// FIXME: dunno what to do here yet.
if ( statement.node.declaration.type === 'Identifier' && def.name === ( replacements[ statement.node.declaration.name ] || statement.node.declaration.name ) ) {
magicString.remove( statement.start, statement.next );

@@ -694,3 +668,3 @@ return;

// prevent `var undefined = sideEffectyDefault(foo)`
if ( canonicalName === undefined ) {
if ( !def.isUsed ) {
magicString.remove( statement.start, statement.node.declaration.start );

@@ -702,5 +676,5 @@ return;

if ( statement.node.declaration.type === 'FunctionExpression' ) {
magicString.overwrite( statement.node.start, statement.node.declaration.start + 8, `function ${canonicalName}` );
magicString.overwrite( statement.node.start, statement.node.declaration.start + 8, `function ${def.name}` );
} else {
magicString.overwrite( statement.node.start, statement.node.declaration.start, `var ${canonicalName} = ` );
magicString.overwrite( statement.node.start, statement.node.declaration.start, `var ${def.name} = ` );
}

@@ -717,13 +691,2 @@ }

}
suggestName ( defaultOrBatch, suggestion ) {
// deconflict anonymous default exports with this module's definitions
const shouldDeconflict = this.exports.default && this.exports.default.isAnonymous;
if ( shouldDeconflict ) suggestion = deconflict( suggestion, this.definitions );
if ( !this.suggestedNames[ defaultOrBatch ] ) {
this.suggestedNames[ defaultOrBatch ] = makeLegalIdentifier( suggestion );
}
}
}

@@ -18,2 +18,10 @@ import { blank, keys } from './utils/object';

function chainedMemberExpression ( node ) {
if ( node.object.type === 'MemberExpression' ) {
return chainedMemberExpression( node.object ) + '.' + node.property.name;
}
return node.object.name + '.' + node.property.name;
}
export default class Statement {

@@ -29,3 +37,2 @@ constructor ( node, module, start, end ) {

this.defines = blank();
this.modifies = blank();
this.dependsOn = blank();

@@ -36,2 +43,6 @@ this.stronglyDependsOn = blank();

// TODO: make this more efficient
this.dependantIds = [];
this.namespaceReplacements = [];
this.isIncluded = false;

@@ -47,2 +58,15 @@

// `export { name } from './other'` is a special case
if ( this.isReexportDeclaration ) {
this.node.specifiers && this.node.specifiers.forEach( specifier => {
const id = this.module.exports.lookup( specifier.exported.name );
if ( !~this.dependantIds.indexOf( id ) ) {
this.dependantIds.push( id );
}
});
return;
}
let scope = this.scope;

@@ -133,2 +157,7 @@

// Used to track
let topName;
let currentMemberExpression = null;
let namespace = null;
if ( !this.isImportDeclaration ) {

@@ -150,2 +179,81 @@ walk( this.node, {

if ( node._scope ) scope = scope.parent;
// Optimize namespace lookups, which manifest as MemberExpressions.
if ( node.type === 'MemberExpression' && ( !currentMemberExpression || node.object === currentMemberExpression ) ) {
currentMemberExpression = node;
if ( !namespace ) {
topName = node.object.name;
const id = this.module.locals.lookup( topName );
if ( !id || !id.isModule || id.isExternal ) return;
namespace = id;
}
// If a namespace is the left hand side of an assignment, throw an error.
if ( parent.type === 'AssignmentExpression' && parent.left === node ||
parent.type === 'UpdateExpression' && parent.argument === node ) {
const err = new Error( `Illegal reassignment to import '${chainedMemberExpression( node )}'` );
err.file = this.module.id;
err.loc = getLocation( this.module.magicString.toString(), node.start );
throw err;
}
// Extract the name of the accessed property, from and Identifier or Literal.
// Any eventual Literal value is converted to a string.
const name = !node.computed ? node.property.name :
( node.property.type === 'Literal' ? String( node.property.value ) : null );
// If we can't resolve the name being accessed statically,
// we require the namespace to be dynamically accessible.
//
// // resolvable
// console.log( javascript.keywords.for )
// console.log( javascript.keywords[ 'for' ] )
// console.log( javascript.keywords[ 6 ] )
//
// // unresolvable
// console.log( javascript.keywords[ index ] )
// console.log( javascript.keywords[ 1 + 5 ] )
if ( name === null ) {
namespace.dynamicAccess();
namespace = null;
currentMemberExpression = null;
return;
}
const id = namespace.exports.lookup( name );
// If the namespace doesn't define the given name,
// we can throw an error (even for nested namespaces).
if ( !id ) {
throw new Error( `Module doesn't define "${name}"!` );
}
// We can't resolve deeper. Replace the member chain.
if ( parent.type !== 'MemberExpression' || !( id.isModule && !id.isExternal ) ) {
if ( !~this.dependantIds.indexOf( id ) ) {
this.dependantIds.push( id );
}
// FIXME: do this better
// If we depend on this name...
if ( this.dependsOn[ topName ] ) {
// ... decrement the count...
if ( !--this.dependsOn[ topName ] ) {
// ... and remove it if the count is 0.
delete this.dependsOn[ topName ];
}
}
this.namespaceReplacements.push( [ node, id ] );
namespace = null;
currentMemberExpression = null;
return;
}
namespace = id;
}
}

@@ -181,3 +289,7 @@ });

if ( !definingScope || definingScope.depth === 0 ) {
this.dependsOn[ node.name ] = true;
if ( !( node.name in this.dependsOn ) ) {
this.dependsOn[ node.name ] = 0;
}
this.dependsOn[ node.name ]++;
if ( strong ) this.stronglyDependsOn[ node.name ] = true;

@@ -199,5 +311,5 @@ }

if ( isAssignment ) {
const importSpecifier = this.module.imports[ node.name ];
const importSpecifier = this.module.locals.lookup( node.name );
if ( importSpecifier && !scope.contains( node.name ) ) {
if ( importSpecifier && importSpecifier.module !== this.module && !scope.contains( node.name ) ) {
const minDepth = importSpecifier.name === '*' ?

@@ -218,7 +330,8 @@ 2 : // cannot do e.g. `namespace.foo = bar`

// value is not updated by the second statement
if ( this.module.exports.default && depth === 0 && this.module.exports.default.identifier === node.name ) {
const def = this.module.exports.lookup( 'default' );
if ( def && depth === 0 && def.name === node.name ) {
// but only if this is a) inside a function body or
// b) after the export declaration
if ( !!scope.parent || node.start > this.module.exports.default.statement.node.start ) {
this.module.exports.default.isModified = true;
if ( !!scope.parent || node.start > def.statement.node.start ) {
def.isModified = true;
}

@@ -246,3 +359,7 @@ }

if ( writeDepth === 0 && node.type === 'Identifier' ) {
this.modifies[ node.name ] = true;
const id = this.module.locals.lookup( node.name );
if ( id && id.modifierStatements && !~id.modifierStatements.indexOf( this ) ) {
id.modifierStatements.push( this );
}
}

@@ -273,22 +390,8 @@ };

// `export { name } from './other'` is a special case
if ( this.isReexportDeclaration ) {
const id = this.module.resolvedIds[ this.node.source.value ];
const otherModule = this.module.bundle.moduleById[ id ];
this.dependantIds.forEach( id => id.mark() );
this.node.specifiers.forEach( specifier => {
const reexport = this.module.reexports[ specifier.exported.name ];
reexport.isUsed = true;
reexport.module = otherModule; // TODO still necessary?
if ( !otherModule.isExternal ) otherModule.markExport( specifier.local.name, specifier.exported.name, this.module );
});
return;
}
Object.keys( this.dependsOn ).forEach( name => {
// TODO: perhaps these could also be added?
keys( this.dependsOn ).forEach( name => {
if ( this.defines[ name ] ) return; // TODO maybe exclude from `this.dependsOn` in the first place?
this.module.mark( name );
this.module.locals.lookup( name ).mark();
});

@@ -298,3 +401,5 @@ }

replaceIdentifiers ( magicString, names, bundleExports ) {
const replacementStack = [ names ];
const statement = this;
const replacementStack = [];
const nameList = keys( names );

@@ -328,6 +433,8 @@

// needs to be rewritten, we replace the whole lot
const name = node.declarations[0].id.name;
const id = node.declarations[0].id;
const name = id.name;
if ( node.declarations.length === 1 && bundleExports[ name ] ) {
magicString.overwrite( node.start, node.declarations[0].id.end, bundleExports[ name ], true );
node.declarations[0].id._skip = true;
magicString.overwrite( node.start, id.end, bundleExports[ name ], true );
id._skip = true;
}

@@ -385,2 +492,14 @@

if ( node.type === 'MemberExpression' ) {
const replacements = statement.namespaceReplacements;
for ( let i = 0; i < replacements.length; i += 1 ) {
const [ top, id ] = replacements[ i ];
if ( node === top ) {
magicString.overwrite( node.start, node.end, id.name );
return this.skip();
}
}
}
if ( node.type !== 'Identifier' ) return;

@@ -416,4 +535,3 @@

if ( node._scope ) {
replacementStack.pop();
names = replacementStack[ replacementStack.length - 1 ];
names = replacementStack.pop();
}

@@ -420,0 +538,0 @@ }

@@ -8,3 +8,3 @@ import { keys } from './object';

export default function getExportMode ( bundle, exportMode ) {
const exportKeys = keys( bundle.entryModule.exports ).concat( keys( bundle.entryModule.reexports ) );
const exportKeys = keys( bundle.entryModule.exports.names );

@@ -11,0 +11,0 @@ if ( exportMode === 'default' ) {

@@ -11,3 +11,6 @@ import { blank } from './object';

export default function makeLegalIdentifier ( str ) {
str = str.replace( /[^$_a-zA-Z0-9]/g, '_' );
str = str
.replace( /-(\w)/g, ( _, letter ) => letter.toUpperCase() )
.replace( /[^$_a-zA-Z0-9]/g, '_' );
if ( /\d/.test( str[0] ) || blacklisted[ str ] ) str = `_${str}`;

@@ -14,0 +17,0 @@

// TODO does this all work on windows?
export const absolutePath = /^(?:\/|(?:[A-Za-z]:)?\\)/;
export const absolutePath = /^(?:\/|(?:[A-Za-z]:)?[\\|\/])/;

@@ -5,0 +5,0 @@ export function isAbsolute ( path ) {

@@ -27,3 +27,3 @@ import { absolutePath, dirname, isAbsolute, resolve } from './path';

while ( dir !== root ) {
while ( dir !== root && dir !== "." ) {
const pkgPath = resolve( dir, 'node_modules', id, 'package.json' );

@@ -30,0 +30,0 @@ let pkgJson;

Sorry, the diff of this file is too big to display

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is too big to display

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc