Skip to content

Commit 55354b3

Browse files
authored
Merge pull request #172 from PepsRyuu/ExportFromExternal
Export from external
2 parents eee708d + a47e8c6 commit 55354b3

File tree

6 files changed

+519
-5
lines changed

6 files changed

+519
-5
lines changed

.travis.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
language: node_js
22
node_js:
3-
- "10"
3+
- "14"
44

55
script: xvfb-run npm test

lib/impl/CodeGenerator.js

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -57,11 +57,23 @@ function generateFile (context, filePath) {
5757
5858
${externalImports.map(i => {
5959
return i.specifiers.map(s => {
60+
let output = '';
61+
6062
if (s.imported === '*')
61-
return `var ${s.local} = ${i.importee};`;
63+
output += `var ${s.local} = ${i.importee};`;
6264
else
63-
return `var ${s.local} = ${i.importee}${s.imported}__;`;
64-
}).join(' ');
65+
output += `var ${s.local} = ${i.importee}${s.imported}__;`;
66+
67+
if (s.exportFrom) {
68+
if (s.imported === '*') {
69+
output += `for(var __k__ in ${s.local}){__k__ !== "default" && (__e__(__k__, ${s.local}[__k__]))}`
70+
} else {
71+
output += `__e__("${s.local.slice(3)}", ${s.local})`;
72+
}
73+
}
74+
75+
return output;
76+
}).join(';');
6577
}).join('; ')}
6678
6779
__d__(function () {

lib/impl/ImportExportResolver.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -194,7 +194,7 @@ async function resolveImport (context, input, output, node, currentpath) {
194194

195195
if (isExternal(context, node.source.value) || resolved === false || (typeof resolved === 'object' && resolved.external)) {
196196
dependency.importee = `__nollup__external__${node.source.value.replace(/[\W]/g, '_')}__`;
197-
dependency.source = node.source.value;
197+
dependency.source = (resolved && resolved.external && resolved.id) || node.source.value;
198198
output.externalImports.push(dependency);
199199
} else {
200200
dependency.importee = `_i${output.imports.length}`;

test/cases/external/cases.js

Lines changed: 310 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,310 @@
1+
let { nollup, fs, expect, rollup } = require('../../nollup');
2+
let { executeChunkedFiles } = require('./external-runtime.js');
3+
4+
describe('External', () => {
5+
['esm', 'cjs', 'iife'].forEach(format => {
6+
describe(format, () => {
7+
it ('should allow external array to work', async () => {
8+
fs.stub('./src/impl.js', () => `export default true;`)
9+
fs.stub('./src/main.js', () => `
10+
import result from './impl';
11+
import fs from "fs";
12+
if (fs.readFileSync) {
13+
self.result = result
14+
}
15+
`);
16+
17+
let bundle = await nollup({
18+
input: './src/main.js',
19+
external: ['fs']
20+
});
21+
22+
let { output } = await bundle.generate({ format });
23+
let result = await executeChunkedFiles(format, 'main.js', output);
24+
expect(result).to.equal(true);
25+
fs.reset();
26+
});
27+
28+
it ('should allow external function to work', async () => {
29+
fs.stub('./src/impl.js', () => `export default true;`)
30+
fs.stub('./src/main.js', () => `
31+
import result from './impl';
32+
import fs from "fs";
33+
if (fs.readFileSync) {
34+
self.result = result
35+
}
36+
`);
37+
38+
let bundle = await nollup({
39+
input: './src/main.js',
40+
external: id => id === 'fs'
41+
});
42+
43+
let { output } = await bundle.generate({ format });
44+
let result = await executeChunkedFiles(format, 'main.js', output);
45+
expect(result).to.equal(true);
46+
fs.reset();
47+
});
48+
49+
it ('should allow external from resolveId to work', async () => {
50+
fs.stub('./src/impl.js', () => `export default true;`)
51+
fs.stub('./src/main.js', () => `
52+
import result from './impl';
53+
import fs from "fs";
54+
if (fs.readFileSync) {
55+
self.result = result
56+
}
57+
`);
58+
59+
let bundle = await nollup({
60+
input: './src/main.js',
61+
plugins: [{
62+
resolveId (id, parent) {
63+
if (id === 'fs') {
64+
return {
65+
id: 'fs',
66+
external: true
67+
}
68+
}
69+
}
70+
}]
71+
});
72+
73+
let { output } = await bundle.generate({ format });
74+
let result = await executeChunkedFiles(format, 'main.js', output);
75+
expect(result).to.equal(true);
76+
fs.reset();
77+
});
78+
79+
it ('should allow default import for external', async () => {
80+
fs.stub('./src/main.js', () => `
81+
import Default from "DefaultModule";
82+
if (Default.prop) {
83+
self.result = true;
84+
}
85+
`);
86+
87+
let bundle = await nollup({
88+
input: './src/main.js',
89+
external: ['DefaultModule']
90+
});
91+
92+
let { output } = await bundle.generate({ format });
93+
let result = await executeChunkedFiles(format, 'main.js', output);
94+
expect(result).to.equal(true);
95+
fs.reset();
96+
});
97+
98+
it ('should allow named import for external', async () => {
99+
fs.stub('./src/main.js', () => `
100+
import { NamedExport1, NamedExport2 } from "NamedModule";
101+
if (NamedExport1 === 123 && NamedExport2 === 456) {
102+
self.result = true;
103+
}
104+
`);
105+
106+
let bundle = await nollup({
107+
input: './src/main.js',
108+
external: ['NamedModule']
109+
});
110+
111+
let { output } = await bundle.generate({ format });
112+
let result = await executeChunkedFiles(format, 'main.js', output);
113+
expect(result).to.equal(true);
114+
fs.reset();
115+
});
116+
117+
it ('should allow namespace import for external', async () => {
118+
fs.stub('./src/main.js', () => `
119+
import * as Namespace from "NamedModule";
120+
if (Namespace.NamedExport1 === 123 && Namespace.NamedExport2 === 456) {
121+
self.result = true;
122+
}
123+
`);
124+
125+
let bundle = await nollup({
126+
input: './src/main.js',
127+
external: ['NamedModule']
128+
});
129+
130+
let { output } = await bundle.generate({ format });
131+
let result = await executeChunkedFiles(format, 'main.js', output);
132+
expect(result).to.equal(true);
133+
fs.reset();
134+
});
135+
136+
it ('should allow bare import for external', async () => {
137+
fs.stub('./src/main.js', () => `
138+
import 'BareModule';
139+
if (self.BareModule.prop) {
140+
self.result = true;
141+
}
142+
`);
143+
144+
let bundle = await nollup({
145+
input: './src/main.js',
146+
external: ['BareModule']
147+
});
148+
149+
let { output } = await bundle.generate({ format });
150+
let result = await executeChunkedFiles(format, 'main.js', output);
151+
expect(result).to.equal(true);
152+
fs.reset();
153+
});
154+
155+
it ('should allow export from for default for external', async () => {
156+
fs.stub('./src/impl.js', () => `export { default } from 'DefaultModule';`)
157+
fs.stub('./src/main.js', () => `
158+
import Default from './impl';
159+
if (Default.prop) {
160+
self.result = true;
161+
}
162+
`);
163+
164+
let bundle = await nollup({
165+
input: './src/main.js',
166+
external: ['DefaultModule']
167+
});
168+
169+
let { output } = await bundle.generate({ format });
170+
let result = await executeChunkedFiles(format, 'main.js', output);
171+
expect(result).to.equal(true);
172+
fs.reset();
173+
});
174+
175+
it ('should allow export from for named for external', async () => {
176+
fs.stub('./src/impl.js', () => `export { NamedExport1, NamedExport2 as Other } from 'NamedModule';`)
177+
fs.stub('./src/main.js', () => `
178+
import { NamedExport1, Other } from './impl';
179+
if (NamedExport1 === 123 && Other === 456) {
180+
self.result = true;
181+
}
182+
`);
183+
184+
let bundle = await nollup({
185+
input: './src/main.js',
186+
external: ['NamedModule']
187+
});
188+
189+
let { output } = await bundle.generate({ format });
190+
let result = await executeChunkedFiles(format, 'main.js', output);
191+
expect(result).to.equal(true);
192+
fs.reset();
193+
});
194+
195+
it ('should allow export from for namespace for external', async () => {
196+
fs.stub('./src/impl.js', () => `export * from 'NamedModule';`)
197+
fs.stub('./src/main.js', () => `
198+
import { NamedExport1, NamedExport2 } from './impl';
199+
if (NamedExport1 === 123 && NamedExport2 === 456) {
200+
self.result = true;
201+
}
202+
`);
203+
204+
let bundle = await nollup({
205+
input: './src/main.js',
206+
external: ['NamedModule']
207+
});
208+
209+
let { output } = await bundle.generate({ format });
210+
let result = await executeChunkedFiles(format, 'main.js', output);
211+
expect(result).to.equal(true);
212+
fs.reset();
213+
});
214+
});
215+
});
216+
217+
describe ('Externals in Chunks', () => {
218+
['esm', 'cjs'].forEach(format => {
219+
it ('should allow external imports for chunks (' + format + ')', async () => {
220+
fs.stub('./src/chunk.js', () => `export { NamedExport1, NamedExport2 } from 'NamedModule';`)
221+
fs.stub('./src/main.js', () => `
222+
import('./chunk').then(mod => {
223+
if (mod.NamedExport1 === 123 && mod.NamedExport2 === 456) {
224+
self.result = true;
225+
}
226+
});
227+
`);
228+
229+
let bundle = await nollup({
230+
input: './src/main.js',
231+
external: ['NamedModule']
232+
});
233+
234+
let { output } = await bundle.generate({ format, chunkFileNames: '[name].js' });
235+
let result = await executeChunkedFiles(format, 'main.js', output, true);
236+
expect(result).to.equal(true);
237+
fs.reset();
238+
});
239+
})
240+
})
241+
242+
describe('Default Fallback', () => {
243+
['cjs', 'iife'].forEach(format => {
244+
it ('should fallback if default import not found for external (' + format + ')', async () => {
245+
fs.stub('./src/main.js', () => `
246+
import Default from "DefaultFallbackModule";
247+
if (Default.prop) {
248+
self.result = true;
249+
}
250+
`);
251+
252+
let bundle = await nollup({
253+
input: './src/main.js',
254+
external: ['DefaultFallbackModule']
255+
});
256+
257+
let { output } = await bundle.generate({ format });
258+
let result = await executeChunkedFiles(format, 'main.js', output);
259+
expect(result).to.equal(true);
260+
fs.reset();
261+
});
262+
})
263+
});
264+
265+
describe('IIFE Name Conversion', () => {
266+
it ('should convert special characters to underscore', async () => {
267+
fs.stub('./src/main.js', () => `
268+
import { NamedExport1, NamedExport2 } from "+IIFE-Special-Characters$";
269+
if (NamedExport1 === 123 && NamedExport2 === 456) {
270+
self.result = true;
271+
}
272+
`);
273+
274+
let bundle = await nollup({
275+
input: './src/main.js',
276+
external: ['+IIFE-Special-Characters$']
277+
});
278+
279+
let { output } = await bundle.generate({ format: 'iife' });
280+
let result = await executeChunkedFiles('iife', 'main.js', output);
281+
expect(result).to.equal(true);
282+
fs.reset();
283+
});
284+
285+
it ('should use global object to determine variable name', async () => {
286+
fs.stub('./src/main.js', () => `
287+
import { NamedExport1, NamedExport2 } from "+IIFE-Special-Characters$";
288+
if (NamedExport1 === 123 && NamedExport2 === 456) {
289+
self.result = true;
290+
}
291+
`);
292+
293+
let bundle = await nollup({
294+
input: './src/main.js',
295+
external: ['+IIFE-Special-Characters$'],
296+
output: {
297+
globals: {
298+
'+IIFE-Special-Characters$': '__globalModule'
299+
}
300+
}
301+
});
302+
303+
let { output } = await bundle.generate({ format: 'iife' });
304+
let result = await executeChunkedFiles('iife', 'main.js', output);
305+
expect(result).to.equal(true);
306+
fs.reset();
307+
});
308+
})
309+
310+
});
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
let { spawn } = require('child_process');
2+
3+
function executeChunkedFiles (format, entry, chunks, async) {
4+
return new Promise(resolve => {
5+
let forked = spawn('node', [
6+
'--experimental-vm-modules',
7+
process.cwd() + '/test/cases/external/fork-worker'
8+
], {
9+
stdio: [null, null, null, 'ipc']
10+
});
11+
12+
forked.stdout.on('data', d => console.log(d.toString()));
13+
forked.stderr.on('data', d => console.error(d.toString()));
14+
15+
forked.on('message', msg => {
16+
if (msg.ready) {
17+
forked.send({ format, entry, chunks, async });
18+
} else {
19+
resolve(msg.result);
20+
forked.kill();
21+
}
22+
});
23+
});
24+
}
25+
26+
module.exports = { executeChunkedFiles };

0 commit comments

Comments
 (0)