UNPKG

2.58 kBJavaScriptView Raw
1import { blank, keys } from '../utils/object.js';
2
3const extractors = {
4 Identifier ( names, param ) {
5 names.push( param.name );
6 },
7
8 ObjectPattern ( names, param ) {
9 param.properties.forEach( prop => {
10 extractors[ prop.key.type ]( names, prop.key );
11 });
12 },
13
14 ArrayPattern ( names, param ) {
15 param.elements.forEach( element => {
16 if ( element ) extractors[ element.type ]( names, element );
17 });
18 },
19
20 RestElement ( names, param ) {
21 extractors[ param.argument.type ]( names, param.argument );
22 },
23
24 AssignmentPattern ( names, param ) {
25 return extractors[ param.left.type ]( names, param.left );
26 }
27};
28
29function extractNames ( param ) {
30 let names = [];
31
32 extractors[ param.type ]( names, param );
33 return names;
34}
35
36class Declaration {
37 constructor () {
38 this.statement = null;
39 this.name = null;
40
41 this.isReassigned = false;
42 this.aliases = [];
43 }
44
45 addAlias ( declaration ) {
46 this.aliases.push( declaration );
47 }
48
49 addReference ( reference ) {
50 reference.declaration = this;
51 this.name = reference.name; // TODO handle differences of opinion
52
53 if ( reference.isReassignment ) this.isReassigned = true;
54 }
55
56 render ( es6 ) {
57 if ( es6 ) return this.name;
58 if ( !this.isReassigned || !this.isExported ) return this.name;
59
60 return `exports.${this.name}`;
61 }
62
63 use () {
64 this.isUsed = true;
65 if ( this.statement ) this.statement.mark();
66
67 this.aliases.forEach( alias => alias.use() );
68 }
69}
70
71export default class Scope {
72 constructor ( options ) {
73 options = options || {};
74
75 this.parent = options.parent;
76 this.isBlockScope = !!options.block;
77
78 this.declarations = blank();
79
80 if ( options.params ) {
81 options.params.forEach( param => {
82 extractNames( param ).forEach( name => {
83 this.declarations[ name ] = new Declaration( name );
84 });
85 });
86 }
87 }
88
89 addDeclaration ( node, isBlockDeclaration, isVar ) {
90 if ( !isBlockDeclaration && this.isBlockScope ) {
91 // it's a `var` or function node, and this
92 // is a block scope, so we need to go up
93 this.parent.addDeclaration( node, isBlockDeclaration, isVar );
94 } else {
95 extractNames( node.id ).forEach( name => {
96 this.declarations[ name ] = new Declaration( name );
97 });
98 }
99 }
100
101 contains ( name ) {
102 return this.declarations[ name ] ||
103 ( this.parent ? this.parent.contains( name ) : false );
104 }
105
106 eachDeclaration ( fn ) {
107 keys( this.declarations ).forEach( key => {
108 fn( key, this.declarations[ key ] );
109 });
110 }
111
112 findDeclaration ( name ) {
113 return this.declarations[ name ] ||
114 ( this.parent && this.parent.findDeclaration( name ) );
115 }
116}