Browse Source

some basic functionality

declarations-and-references
Rich-Harris 9 years ago
parent
commit
24f4af72d9
  1. 8
      src/Bundle.js
  2. 87
      src/Module.js
  3. 10
      src/Statement.js
  4. 15
      src/ast/Scope.js
  5. 2
      test/form/self-contained-bundle/_config.js
  6. 10
      test/test.js

8
src/Bundle.js

@ -40,7 +40,9 @@ export default class Bundle {
this.internalNamespaceModules = []; this.internalNamespaceModules = [];
this.assumedGlobals = blank(); this.assumedGlobals = blank();
this.assumedGlobals.exports = true; // TODO strictly speaking, this only applies with non-ES6, non-default-only bundles
// TODO strictly speaking, this only applies with non-ES6, non-default-only bundles
[ 'module', 'exports' ].forEach( global => this.assumedGlobals[ global ] = true );
} }
build () { build () {
@ -50,9 +52,11 @@ export default class Bundle {
this.modules.forEach( module => { this.modules.forEach( module => {
module.bindImportSpecifiers(); module.bindImportSpecifiers();
module.bindReferences(); module.bindReferences();
module.markAllSideEffects();
}); });
this.modules.forEach( module => {
module.markAllSideEffects();
});
const defaultExport = entryModule.exports.default; const defaultExport = entryModule.exports.default;

87
src/Module.js

@ -2,7 +2,6 @@ import { parse } from 'acorn';
import MagicString from 'magic-string'; import MagicString from 'magic-string';
import Statement from './Statement'; import Statement from './Statement';
import walk from './ast/walk'; import walk from './ast/walk';
import Scope from './ast/Scope';
import { blank, keys } from './utils/object'; import { blank, keys } from './utils/object';
import { basename, extname } from './utils/path'; import { basename, extname } from './utils/path';
import getLocation from './utils/getLocation'; import getLocation from './utils/getLocation';
@ -45,8 +44,7 @@ export default class Module {
this.exportAlls = []; this.exportAlls = [];
this.scope = new Scope(); this.declarations = blank();
this.definitions = blank();
this.analyse(); this.analyse();
} }
@ -178,9 +176,8 @@ export default class Module {
statement.analyse(); statement.analyse();
keys( statement.scope.declarations ).forEach( name => { statement.scope.eachDeclaration( ( name, declaration ) => {
const declaration = statement.scope.declarations[ name ]; this.declarations[ name ] = declaration;
this.definitions[ name ] = { statement, declaration };
}); });
}); });
} }
@ -208,38 +205,20 @@ export default class Module {
} }
bindReferences () { bindReferences () {
this.statements.forEach( ( statement, i ) => { this.statements.forEach( statement => {
statement.references.forEach( reference => { statement.references.forEach( reference => {
let declaration; let declaration;
// find in local scope... // find in local scope...
declaration = reference.scope.findDeclaration( reference.name ); declaration = reference.scope.findDeclaration( reference.name ) ||
this.trace( reference.name );
if ( declaration ) { if ( declaration ) {
reference.declaration = declaration; reference.declaration = declaration;
reference.definingStatement = statement; declaration.references.push( reference );
return;
}
let definition;
// ...or in module...
definition = this.definitions[ reference.name ];
// ...or from import
if ( !definition ) {
const importDeclaration = this.imports[ reference.name ];
if ( importDeclaration ) {
definition = importDeclaration.module.traceExport( importDeclaration.name );
}
}
if ( definition ) {
reference.declaration = definition.declaration;
reference.definingStatement = definition.statement;
definition.declaration.references.push( reference );
} else { } else {
//console.log( 'TODO no declaration. global?' ); // TODO handle globals
this.bundle.assumedGlobals[ reference.name ] = true;
} }
}); });
}); });
@ -251,8 +230,8 @@ export default class Module {
this.statements.forEach( statement => { this.statements.forEach( statement => {
statement.references.forEach( reference => { statement.references.forEach( reference => {
if ( reference.definingStatement ) { if ( reference.declaration && reference.declaration.statement ) {
const module = reference.definingStatement.module; const module = reference.declaration.statement.module;
weakDependencies[ module.id ] = module; weakDependencies[ module.id ] = module;
} }
}); });
@ -273,17 +252,6 @@ export default class Module {
return this.replacements[ name ] || name; return this.replacements[ name ] || 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 );
}
getExports () { getExports () {
let exports = blank(); let exports = blank();
@ -547,16 +515,41 @@ export default class Module {
if ( !statement.isIncluded ) { if ( !statement.isIncluded ) {
magicString.remove( statement.start, statement.next ); magicString.remove( statement.start, statement.next );
} }
// modify exports as necessary
if ( statement.isExportDeclaration ) {
// remove `export` from `export class Foo {...}` or `export default Foo`
// TODO default exports need different treatment
if ( statement.node.declaration.id ) {
magicString.remove( statement.node.start, statement.node.declaration.start );
}
// else if ( statement.node.type === 'ExportDefaultDeclaration' ) {
// const defaultExport = this.exports.default;
//
// // anonymous functions should be converted into declarations
// if ( statement.node.declaration.type === 'FunctionExpression' ) {
// magicString.overwrite( statement.node.start, statement.node.declaration.start + 8, `function ${defaultExport.name}` );
// } else {
// magicString.overwrite( statement.node.start, statement.node.declaration.start, `var ${defaultExport.name} = ` );
// }
// }
else {
throw new Error( 'Unhandled export' );
}
}
}); });
return magicString.trim(); return magicString.trim();
} }
trace ( name ) { trace ( name ) {
if ( name in this.definitions ) return this.definitions[ name ]; if ( name in this.declarations ) return this.declarations[ name ];
if ( name in this.imports ) { if ( name in this.imports ) {
const otherModule = this.imports[ name ].module; const importDeclaration = this.imports[ name ];
return otherModule.traceExport( name ); const otherModule = importDeclaration.module;
return otherModule.traceExport( importDeclaration.name );
} }
return null; return null;
@ -584,6 +577,6 @@ export default class Module {
} }
// TODO export * // TODO export *
throw new Error( 'could not trace export', name ); return null;
} }
} }

10
src/Statement.js

@ -75,6 +75,12 @@ export default class Statement {
// attach scopes // attach scopes
attachScopes( this ); attachScopes( this );
// attach statement to each top-level declaration,
// so we can mark statements easily
this.scope.eachDeclaration( ( name, declaration ) => {
declaration.statement = this;
});
let references = this.references; let references = this.references;
// find references // find references
@ -102,8 +108,8 @@ export default class Statement {
this.isIncluded = true; this.isIncluded = true;
this.references.forEach( reference => { this.references.forEach( reference => {
if ( reference.definingStatement ) { if ( reference.declaration && reference.declaration.statement ) {
reference.definingStatement.mark(); reference.declaration.statement.mark();
} }
}); });
} }

15
src/ast/Scope.js

@ -1,4 +1,4 @@
import { blank } from '../utils/object'; import { blank, keys } from '../utils/object';
const extractors = { const extractors = {
Identifier ( names, param ) { Identifier ( names, param ) {
@ -34,8 +34,9 @@ function extractNames ( param ) {
} }
class Declaration { class Declaration {
constructor ( node ) { constructor () {
this.references = []; this.references = [];
this.statement = null;
} }
} }
@ -51,7 +52,7 @@ export default class Scope {
if ( options.params ) { if ( options.params ) {
options.params.forEach( param => { options.params.forEach( param => {
extractNames( param ).forEach( name => { extractNames( param ).forEach( name => {
this.declarations[ name ] = new Declaration( param ); this.declarations[ name ] = new Declaration();
}); });
}); });
} }
@ -64,7 +65,7 @@ export default class Scope {
this.parent.addDeclaration( node, isBlockDeclaration, isVar ); this.parent.addDeclaration( node, isBlockDeclaration, isVar );
} else { } else {
extractNames( node.id ).forEach( name => { extractNames( node.id ).forEach( name => {
this.declarations[ name ] = new Declaration( node ); this.declarations[ name ] = new Declaration();
}); });
} }
} }
@ -74,6 +75,12 @@ export default class Scope {
( this.parent ? this.parent.contains( name ) : false ); ( this.parent ? this.parent.contains( name ) : false );
} }
eachDeclaration ( fn ) {
keys( this.declarations ).forEach( key => {
fn( key, this.declarations[ key ] );
});
}
findDeclaration ( name ) { findDeclaration ( name ) {
return this.declarations[ name ] || return this.declarations[ name ] ||
( this.parent && this.parent.findDeclaration( name ) ); ( this.parent && this.parent.findDeclaration( name ) );

2
test/form/self-contained-bundle/_config.js

@ -1,5 +1,3 @@
module.exports = { module.exports = {
solo: true,
show: true,
description: 'self-contained bundle' description: 'self-contained bundle'
}; };

10
test/test.js

@ -15,11 +15,11 @@ var SOURCEMAPS = path.resolve( __dirname, 'sourcemaps' );
var CLI = path.resolve( __dirname, 'cli' ); var CLI = path.resolve( __dirname, 'cli' );
var PROFILES = [ var PROFILES = [
// { format: 'amd' }, { format: 'amd' },
{ format: 'cjs' }, { format: 'cjs' },
// { format: 'es6' }, { format: 'es6' },
// { format: 'iife' }, { format: 'iife' },
// { format: 'umd' } { format: 'umd' }
]; ];
function extend ( target ) { function extend ( target ) {
@ -228,7 +228,7 @@ describe( 'rollup', function () {
expectedMap.sourcesContent = expectedMap.sourcesContent.map( normaliseOutput ); expectedMap.sourcesContent = expectedMap.sourcesContent.map( normaliseOutput );
} catch ( err ) {} } catch ( err ) {}
if ( config.show || unintendedError ) { if ( config.show ) {
console.log( actualCode + '\n\n\n' ); console.log( actualCode + '\n\n\n' );
} }

Loading…
Cancel
Save