Browse Source

Simplified some of Module's logic. Started passing tests. :)

gh-109
Oskar Segersvärd 10 years ago
parent
commit
0c79692f26
  1. 8
      src/Bundle.js
  2. 16
      src/ExternalModule.js
  3. 70
      src/Module.js
  4. 11
      src/Statement.js

8
src/Bundle.js

@ -31,7 +31,8 @@ export default class Bundle {
transform: ensureArray( options.transform )
};
this.scope = new Scope();
this.globals = new Scope();
this.scope = new Scope( this.globals );
this.toExport = null;
@ -120,6 +121,8 @@ export default class Bundle {
// 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.
//
// FIXME: Except for cyclic dependencies...
module.analyse();
return module;
});
@ -198,7 +201,6 @@ export default class Bundle {
render ( options = {} ) {
const format = options.format || 'es6';
const allReplacements = blank();
// Determine export mode - 'default', 'named', 'none'
const exportMode = getExportMode( this, options.exports );
@ -225,7 +227,7 @@ export default class Bundle {
this.orderedModules.forEach( module => {
module.reassignments.forEach( name => {
isReassignedVarDeclaration[ module.replacements[ name ] || name ] = true;
isReassignedVarDeclaration[ module.exports.lookup( name ) ] = true;
});
});

16
src/ExternalModule.js

@ -18,6 +18,22 @@ export default class ExternalModule {
this.needsAll = false;
this.exports = bundle.scope.virtual();
const ref = this.exports.reference;
// Override reference.
this.exports.reference = name => {
if ( !this.exports.defines( name ) ) {
this.exports.define({
originalName: name,
name,
module: this
});
}
return ref.call( this.exports, name );
};
}
findDefiningStatement () {

70
src/Module.js

@ -4,6 +4,7 @@ import Statement from './Statement';
import walk from './ast/walk';
import { blank, keys } from './utils/object';
import getLocation from './utils/getLocation';
import makeLegalIdentifier from './utils/makeLegalIdentifier';
function isEmptyExportedVarDeclaration ( node, exports, toExport ) {
if ( node.type !== 'VariableDeclaration' || node.declarations[0].init ) return false;
@ -15,6 +16,14 @@ function isEmptyExportedVarDeclaration ( node, exports, toExport ) {
return !~toExport.indexOf( id.name );
}
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 );
}
}
export default class Module {
constructor ({ id, source, ast, bundle }) {
this.source = source;
@ -23,7 +32,7 @@ export default class Module {
this.id = id;
// Implement Identifier interface.
this.name = id;
this.name = makeLegalIdentifier( id );
// By default, `id` is the filename. Custom resolvers and loaders
// can change that, but it makes sense to use it for the source filename
@ -31,32 +40,21 @@ export default class Module {
filename: id
});
// 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 = [];
this.statements = this.parse( ast );
// all dependencies
this.resolvedIds = blank();
this.boundImportSpecifiers = false;
// imports and exports, indexed by local name
this.imports = blank();
// Virtual scopes for the local and exported names.
this.locals = bundle.scope.virtual();
this.exports = bundle.scope.virtual();
this.exportAlls = [];
this.replacements = blank();
this.reassignments = [];
this.marked = blank();
@ -107,7 +105,8 @@ export default class Module {
// If the default export has an identifier, bind to it.
this.exports.bind( 'default', this.locals.reference( identifier ) );
} else {
this.exports.define({
// Define the default identifier.
const id = {
originalName: 'default',
name: 'default',
@ -117,7 +116,13 @@ export default class Module {
isDeclaration,
isAnonymous,
isModified: false // in case of `export default foo; foo = somethingElse`
});
};
this.exports.define( id );
// Rename it to avoid generating the `default` idenntifier,
// which is invalid.
id.name = this.name;
}
}
@ -149,7 +154,7 @@ export default class Module {
name = declaration.id.name;
}
this.exports.bind({
this.locals.define({
originalName: name,
name,
@ -157,6 +162,8 @@ export default class Module {
localName: name,
expression: declaration
});
this.exports.bind( name, this.locals.reference( name ) );
}
}
}
@ -242,8 +249,8 @@ export default class Module {
});
keys( statement.dependsOn ).forEach( name => {
if ( !this.definitions[ name ] && !this.imports[ name ] ) {
this.bundle.assumedGlobals[ name ] = true;
if ( !this.locals.inScope( name ) ) {
this.bundle.globals.define( name );
}
});
});
@ -315,26 +322,13 @@ export default class Module {
}
defaultName () {
const defaultExport = this.exports.default;
if ( !defaultExport ) return null;
const name = defaultExport.identifier && !defaultExport.isModified ?
defaultExport.identifier :
this.replacements.default;
return this.replacements[ name ] || name;
return this.name;
}
findDefiningStatement ( name ) {
if ( this.definitions[ name ] ) return this.definitions[ name ];
// TODO what about `default`/`*`?
const importDeclaration = this.imports[ name ];
if ( !importDeclaration ) return null;
return importDeclaration.module.findDefiningStatement( name );
return null;
}
getModule ( source ) {
@ -344,9 +338,6 @@ export default class Module {
mark ( name ) {
const id = this.locals.lookup( name );
if ( id && !id.statement)
console.log(id)
if ( id && id.statement ) {
// Assert that statement is defined. It isn't for external modules.
id.statement.mark();
@ -499,7 +490,6 @@ export default class Module {
this.statements.forEach( statement => {
if ( !statement.isIncluded ) {
console.log( 'removing definer of', keys( statement.defines ) );
magicString.remove( statement.start, statement.next );
return;
}
@ -543,11 +533,9 @@ export default class Module {
keys( statement.dependsOn )
.concat( keys( statement.defines ) )
.forEach( name => {
// console.log ( name, statement.node );
// console.log( this.locals );
const bundleName = this.locals.lookup( name ).name;
if ( !~toExport.indexOf( bundleName ) ) {
if ( ~toExport.indexOf( bundleName ) ) {
bundleExports[ name ] = replacements[ name ] = bundleName;
} else if ( bundleName !== name ) { // TODO weird structure
replacements[ name ] = bundleName;

11
src/Statement.js

@ -262,8 +262,6 @@ export default class Statement {
if ( this.isIncluded ) return; // prevent infinite loops
this.isIncluded = true;
console.log( 'marking definer of', keys( this.defines ) );
// `export { name } from './other'` is a special case
if ( this.isReexportDeclaration ) {
const otherModule = this.module.getModule( this.node.source.value );
@ -282,7 +280,6 @@ export default class Statement {
}
replaceIdentifiers ( magicString, names, bundleExports ) {
console.log( magicString.slice(this.start, this.end) );
const replacementStack = [ names ];
const nameList = keys( names );
@ -312,10 +309,12 @@ export default class Statement {
if ( node.type === 'VariableDeclaration' ) {
// if this contains a single declarator, and it's one that
// 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;
}
// otherwise, we insert the `exports.foo = foo` after the declaration

Loading…
Cancel
Save