mirror of https://github.com/lukechilds/rollup.git
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
151 lines
4.8 KiB
151 lines
4.8 KiB
import { walk } from 'estree-walker';
|
|
import Scope from './ast/Scope.js';
|
|
import attachScopes from './ast/attachScopes.js';
|
|
import modifierNodes, { isModifierNode } from './ast/modifierNodes.js';
|
|
import isFunctionDeclaration from './ast/isFunctionDeclaration.js';
|
|
import isReference from './ast/isReference.js';
|
|
import getLocation from './utils/getLocation.js';
|
|
import run from './utils/run.js';
|
|
import { Reference } from './Reference.js';
|
|
|
|
export default class Statement {
|
|
constructor ( node, module, start, end ) {
|
|
this.node = node;
|
|
this.module = module;
|
|
this.start = start;
|
|
this.end = end;
|
|
this.next = null; // filled in later
|
|
|
|
this.scope = new Scope({ statement: this });
|
|
|
|
this.references = [];
|
|
this.stringLiteralRanges = [];
|
|
|
|
this.isIncluded = false;
|
|
this.ran = false;
|
|
|
|
this.isImportDeclaration = node.type === 'ImportDeclaration';
|
|
this.isExportDeclaration = /^Export/.test( node.type );
|
|
this.isReexportDeclaration = this.isExportDeclaration && !!node.source;
|
|
|
|
this.isFunctionDeclaration = isFunctionDeclaration( node ) ||
|
|
this.isExportDeclaration && isFunctionDeclaration( node.declaration );
|
|
}
|
|
|
|
firstPass () {
|
|
if ( this.isImportDeclaration ) return; // nothing to analyse
|
|
|
|
// attach scopes
|
|
attachScopes( this );
|
|
|
|
// find references
|
|
const statement = this;
|
|
let { module, references, scope, stringLiteralRanges } = this;
|
|
let readDepth = 0;
|
|
|
|
walk( this.node, {
|
|
enter ( node, parent, prop ) {
|
|
// warn about eval
|
|
if ( node.type === 'CallExpression' && node.callee.name === 'eval' && !scope.contains( 'eval' ) ) {
|
|
module.bundle.onwarn( `Use of \`eval\` (in ${module.id}) is discouraged, as it may cause issues with minification. See https://github.com/rollup/rollup/wiki/Troubleshooting#avoiding-eval for more details` );
|
|
}
|
|
|
|
// skip re-export declarations
|
|
if ( node.type === 'ExportNamedDeclaration' && node.source ) return this.skip();
|
|
|
|
if ( node.type === 'TemplateElement' ) stringLiteralRanges.push([ node.start, node.end ]);
|
|
if ( node.type === 'Literal' && typeof node.value === 'string' && /\n/.test( node.raw ) ) {
|
|
stringLiteralRanges.push([ node.start + 1, node.end - 1 ]);
|
|
}
|
|
|
|
if ( node._scope ) scope = node._scope;
|
|
if ( /Function/.test( node.type ) ) readDepth += 1;
|
|
|
|
let isReassignment;
|
|
|
|
if ( parent && isModifierNode( parent ) ) {
|
|
let subject = parent[ modifierNodes[ parent.type ] ];
|
|
let depth = 0;
|
|
|
|
while ( subject.type === 'MemberExpression' ) {
|
|
subject = subject.object;
|
|
depth += 1;
|
|
}
|
|
|
|
const importDeclaration = module.imports[ subject.name ];
|
|
|
|
if ( !scope.contains( subject.name ) && importDeclaration ) {
|
|
const minDepth = importDeclaration.name === '*' ?
|
|
2 : // cannot do e.g. `namespace.foo = bar`
|
|
1; // cannot do e.g. `foo = bar`, but `foo.bar = bar` is fine
|
|
|
|
if ( depth < minDepth ) {
|
|
const err = new Error( `Illegal reassignment to import '${subject.name}'` );
|
|
err.file = module.id;
|
|
err.loc = getLocation( module.magicString.toString(), subject.start );
|
|
throw err;
|
|
}
|
|
}
|
|
|
|
isReassignment = !depth;
|
|
}
|
|
|
|
if ( isReference( node, parent ) ) {
|
|
// function declaration IDs are a special case – they're associated
|
|
// with the parent scope
|
|
const referenceScope = parent.type === 'FunctionDeclaration' && node === parent.id ?
|
|
scope.parent :
|
|
scope;
|
|
|
|
const isShorthandProperty = parent.type === 'Property' && parent.shorthand;
|
|
|
|
// Since `node.key` can equal `node.value` for shorthand properties
|
|
// we must use the `prop` argument provided by `estree-walker` to determine
|
|
// if we're looking at the key or the value.
|
|
// If they are equal, we'll return to not create duplicate references.
|
|
if ( isShorthandProperty && parent.value === parent.key && prop === 'value' ) {
|
|
return;
|
|
}
|
|
|
|
const reference = new Reference( node, referenceScope, statement );
|
|
reference.isReassignment = isReassignment;
|
|
reference.isShorthandProperty = isShorthandProperty;
|
|
references.push( reference );
|
|
|
|
this.skip(); // don't descend from `foo.bar.baz` into `foo.bar`
|
|
}
|
|
},
|
|
leave ( node ) {
|
|
if ( node._scope ) scope = scope.parent;
|
|
if ( /Function/.test( node.type ) ) readDepth -= 1;
|
|
}
|
|
});
|
|
}
|
|
|
|
mark () {
|
|
if ( this.isIncluded ) return; // prevent infinite loops
|
|
this.isIncluded = true;
|
|
|
|
this.references.forEach( reference => {
|
|
if ( reference.declaration ) reference.declaration.use();
|
|
});
|
|
}
|
|
|
|
run ( strongDependencies ) {
|
|
if ( ( this.ran && this.isIncluded ) || this.isImportDeclaration || this.isFunctionDeclaration ) return;
|
|
this.ran = true;
|
|
|
|
if ( run( this.node, this.scope, this, strongDependencies, false ) ) {
|
|
this.mark();
|
|
return true;
|
|
}
|
|
}
|
|
|
|
source () {
|
|
return this.module.source.slice( this.start, this.end );
|
|
}
|
|
|
|
toString () {
|
|
return this.module.magicString.slice( this.start, this.end );
|
|
}
|
|
}
|
|
|