1 | import { walk } from 'estree-walker';
|
2 | import Scope from './ast/Scope.js';
|
3 | import attachScopes from './ast/attachScopes.js';
|
4 | import modifierNodes, { isModifierNode } from './ast/modifierNodes.js';
|
5 | import isFunctionDeclaration from './ast/isFunctionDeclaration.js';
|
6 | import isReference from './ast/isReference.js';
|
7 | import getLocation from './utils/getLocation.js';
|
8 | import run from './utils/run.js';
|
9 | import { Reference } from './Reference.js';
|
10 |
|
11 | export default class Statement {
|
12 | constructor ( node, module, start, end ) {
|
13 | this.node = node;
|
14 | this.module = module;
|
15 | this.start = start;
|
16 | this.end = end;
|
17 | this.next = null;
|
18 |
|
19 | this.scope = new Scope({ statement: this });
|
20 |
|
21 | this.references = [];
|
22 | this.stringLiteralRanges = [];
|
23 |
|
24 | this.isIncluded = false;
|
25 | this.ran = false;
|
26 |
|
27 | this.isImportDeclaration = node.type === 'ImportDeclaration';
|
28 | this.isExportDeclaration = /^Export/.test( node.type );
|
29 | this.isReexportDeclaration = this.isExportDeclaration && !!node.source;
|
30 |
|
31 | this.isFunctionDeclaration = isFunctionDeclaration( node ) ||
|
32 | this.isExportDeclaration && isFunctionDeclaration( node.declaration );
|
33 | }
|
34 |
|
35 | firstPass () {
|
36 | if ( this.isImportDeclaration ) return;
|
37 |
|
38 |
|
39 | attachScopes( this );
|
40 |
|
41 |
|
42 | const statement = this;
|
43 | let { module, references, scope, stringLiteralRanges } = this;
|
44 | let readDepth = 0;
|
45 |
|
46 | walk( this.node, {
|
47 | enter ( node, parent ) {
|
48 |
|
49 | if ( node.type === 'CallExpression' && node.callee.name === 'eval' && !scope.contains( 'eval' ) ) {
|
50 | 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` );
|
51 | }
|
52 |
|
53 |
|
54 | if ( node.type === 'ExportNamedDeclaration' && node.source ) return this.skip();
|
55 |
|
56 | if ( node.type === 'TemplateElement' ) stringLiteralRanges.push([ node.start, node.end ]);
|
57 | if ( node.type === 'Literal' && typeof node.value === 'string' && /\n/.test( node.raw ) ) {
|
58 | stringLiteralRanges.push([ node.start + 1, node.end - 1 ]);
|
59 | }
|
60 |
|
61 | if ( node._scope ) scope = node._scope;
|
62 | if ( /Function/.test( node.type ) ) readDepth += 1;
|
63 |
|
64 |
|
65 |
|
66 | if ( node.type === 'Property' && node.shorthand && parent.type !== 'ObjectPattern' ) {
|
67 | const reference = new Reference( node.key, scope );
|
68 | reference.isShorthandProperty = true;
|
69 | references.push( reference );
|
70 | return this.skip();
|
71 | }
|
72 |
|
73 | let isReassignment;
|
74 |
|
75 | if ( parent && isModifierNode( parent ) ) {
|
76 | let subject = parent[ modifierNodes[ parent.type ] ];
|
77 | let depth = 0;
|
78 |
|
79 | while ( subject.type === 'MemberExpression' ) {
|
80 | subject = subject.object;
|
81 | depth += 1;
|
82 | }
|
83 |
|
84 | const importDeclaration = module.imports[ subject.name ];
|
85 |
|
86 | if ( !scope.contains( subject.name ) && importDeclaration ) {
|
87 | const minDepth = importDeclaration.name === '*' ?
|
88 | 2 :
|
89 | 1;
|
90 |
|
91 | if ( depth < minDepth ) {
|
92 | const err = new Error( `Illegal reassignment to import '${subject.name}'` );
|
93 | err.file = module.id;
|
94 | err.loc = getLocation( module.magicString.toString(), subject.start );
|
95 | throw err;
|
96 | }
|
97 | }
|
98 |
|
99 | isReassignment = !depth;
|
100 | }
|
101 |
|
102 | if ( isReference( node, parent ) ) {
|
103 |
|
104 |
|
105 | const referenceScope = parent.type === 'FunctionDeclaration' && node === parent.id ?
|
106 | scope.parent :
|
107 | scope;
|
108 |
|
109 | const reference = new Reference( node, referenceScope, statement );
|
110 | reference.isReassignment = isReassignment;
|
111 |
|
112 | references.push( reference );
|
113 |
|
114 | this.skip();
|
115 | }
|
116 | },
|
117 | leave ( node ) {
|
118 | if ( node._scope ) scope = scope.parent;
|
119 | if ( /Function/.test( node.type ) ) readDepth -= 1;
|
120 | }
|
121 | });
|
122 | }
|
123 |
|
124 | mark () {
|
125 | if ( this.isIncluded ) return;
|
126 | this.isIncluded = true;
|
127 |
|
128 | this.references.forEach( reference => {
|
129 | if ( reference.declaration ) reference.declaration.use();
|
130 | });
|
131 | }
|
132 |
|
133 | run ( strongDependencies ) {
|
134 | if ( ( this.ran && this.isIncluded ) || this.isImportDeclaration || this.isFunctionDeclaration ) return;
|
135 | this.ran = true;
|
136 |
|
137 | if ( run( this.node, this.scope, this, strongDependencies, false ) ) {
|
138 | this.mark();
|
139 | return true;
|
140 | }
|
141 | }
|
142 |
|
143 | source () {
|
144 | return this.module.source.slice( this.start, this.end );
|
145 | }
|
146 |
|
147 | toString () {
|
148 | return this.module.magicString.slice( this.start, this.end );
|
149 | }
|
150 | }
|