forked from import-js/eslint-plugin-import
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathexportMap.js
178 lines (146 loc) · 5.03 KB
/
exportMap.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
export default class ExportMap {
constructor(path) {
this.path = path;
this.namespace = new Map();
// todo: restructure to key on path, value is resolver + map of names
this.reexports = new Map();
/**
* star-exports
* @type {Set<() => ExportMap>}
*/
this.dependencies = new Set();
/**
* dependencies of this module that are not explicitly re-exported
* @type {Map<string, () => ExportMap>}
*/
this.imports = new Map();
this.errors = [];
/**
* type {'ambiguous' | 'Module' | 'Script'}
*/
this.parseGoal = 'ambiguous';
}
get hasDefault() { return this.get('default') != null; } // stronger than this.has
get size() {
let size = this.namespace.size + this.reexports.size;
this.dependencies.forEach((dep) => {
const d = dep();
// CJS / ignored dependencies won't exist (#717)
if (d == null) { return; }
size += d.size;
});
return size;
}
/**
* Note that this does not check explicitly re-exported names for existence
* in the base namespace, but it will expand all `export * from '...'` exports
* if not found in the explicit namespace.
* @param {string} name
* @return {boolean} true if `name` is exported by this module.
*/
has(name) {
if (this.namespace.has(name)) { return true; }
if (this.reexports.has(name)) { return true; }
// default exports must be explicitly re-exported (#328)
if (name !== 'default') {
for (const dep of this.dependencies) {
const innerMap = dep();
// todo: report as unresolved?
if (!innerMap) { continue; }
if (innerMap.has(name)) { return true; }
}
}
return false;
}
/**
* ensure that imported name fully resolves.
* @param {string} name
* @return {{ found: boolean, path: ExportMap[] }}
*/
hasDeep(name) {
if (this.namespace.has(name)) { return { found: true, path: [this] }; }
if (this.reexports.has(name)) {
const reexports = this.reexports.get(name);
const imported = reexports.getImport();
// if import is ignored, return explicit 'null'
if (imported == null) { return { found: true, path: [this] }; }
// safeguard against cycles, only if name matches
if (imported.path === this.path && reexports.local === name) {
return { found: false, path: [this] };
}
const deep = imported.hasDeep(reexports.local);
deep.path.unshift(this);
return deep;
}
// default exports must be explicitly re-exported (#328)
if (name !== 'default') {
for (const dep of this.dependencies) {
const innerMap = dep();
if (innerMap == null) { return { found: true, path: [this] }; }
// todo: report as unresolved?
if (!innerMap) { continue; }
// safeguard against cycles
if (innerMap.path === this.path) { continue; }
const innerValue = innerMap.hasDeep(name);
if (innerValue.found) {
innerValue.path.unshift(this);
return innerValue;
}
}
}
return { found: false, path: [this] };
}
get(name) {
if (this.namespace.has(name)) { return this.namespace.get(name); }
if (this.reexports.has(name)) {
const reexports = this.reexports.get(name);
const imported = reexports.getImport();
// if import is ignored, return explicit 'null'
if (imported == null) { return null; }
// safeguard against cycles, only if name matches
if (imported.path === this.path && reexports.local === name) { return undefined; }
return imported.get(reexports.local);
}
// default exports must be explicitly re-exported (#328)
if (name !== 'default') {
for (const dep of this.dependencies) {
const innerMap = dep();
// todo: report as unresolved?
if (!innerMap) { continue; }
// safeguard against cycles
if (innerMap.path === this.path) { continue; }
const innerValue = innerMap.get(name);
if (innerValue !== undefined) { return innerValue; }
}
}
return undefined;
}
forEach(callback, thisArg) {
this.namespace.forEach((v, n) => { callback.call(thisArg, v, n, this); });
this.reexports.forEach((reexports, name) => {
const reexported = reexports.getImport();
// can't look up meta for ignored re-exports (#348)
callback.call(thisArg, reexported && reexported.get(reexports.local), name, this);
});
this.dependencies.forEach((dep) => {
const d = dep();
// CJS / ignored dependencies won't exist (#717)
if (d == null) { return; }
d.forEach((v, n) => {
if (n !== 'default') {
callback.call(thisArg, v, n, this);
}
});
});
}
// todo: keys, values, entries?
reportErrors(context, declaration) {
const msg = this.errors
.map((e) => `${e.message} (${e.lineNumber}:${e.column})`)
.join(', ');
context.report({
node: declaration.source,
message: `Parse errors in imported module '${declaration.source.value}': ${msg}`,
});
}
}