Skip to content

Commit 895a021

Browse files
authored
Merge pull request #126 from PepsRyuu/ExportLiveBindings
Export Declaration Late Bindings
2 parents cf2fb66 + 7df5352 commit 895a021

File tree

5 files changed

+184
-0
lines changed

5 files changed

+184
-0
lines changed

lib/impl/ImportExportResolver.js

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,12 @@ async function resolveNamedExport (context, input, output, node, currentpath) {
7474
}).join(', ');
7575
} else {
7676
output.exports.push(node.id.name);
77+
78+
if (!node.init) {
79+
output.exportLiveBindings.push(node.id.name);
80+
return ``;
81+
}
82+
7783
return `__e__('${node.id.name}', ${node.id.name})`;
7884
}
7985
}).join(', ') + ';';
@@ -274,6 +280,36 @@ async function walk (context, input, output, nodes, currentpath) {
274280
}
275281
}
276282

283+
function walkLiveBindings (context, input, output, nodes, found, level) {
284+
for (let i = 0; i < nodes.length; i++) {
285+
let node = nodes[i];
286+
let locals = [];
287+
288+
if (!node) {
289+
continue;
290+
}
291+
292+
if (
293+
node.type === 'AssignmentExpression' &&
294+
node.left.type === 'Identifier' &&
295+
output.exportLiveBindings.indexOf(node.left.name) > -1
296+
) {
297+
if (found.indexOf(node.left.name) === -1) {
298+
found.push(node.left.name);
299+
}
300+
}
301+
302+
walkLiveBindings(context, input, output, findChildNodes(node), found, level + 1);
303+
304+
if (level === 0 && found.length > 0) {
305+
let transpiled = ';' + found.map(name => `__e__('${name}', typeof ${name} !== 'undefined' && ${name})`).join(';') + ';';
306+
output.transpiled.appendRight(node.end, transpiled);
307+
found = [];
308+
}
309+
}
310+
}
311+
312+
277313
/**
278314
* @method ImportExportResolver
279315
*/
@@ -286,12 +322,17 @@ module.exports = async function (context, input, currentpath) {
286322
dynamicImports: [],
287323
externalDynamicImports: [],
288324
metaProperties: [],
325+
exportLiveBindings: []
289326
};
290327

291328
let ast = AcornParser.parse(input);
292329

293330
await walk(context, input, output, ast.body, currentpath);
294331

332+
if (output.exportLiveBindings.length > 0) {
333+
walkLiveBindings(context, input, output, ast.body, [], 0);
334+
}
335+
295336
output.map = output.transpiled.generateMap({ source: currentpath });
296337
output.code = output.transpiled.toString();
297338

test/cases/es2cjs.js

Lines changed: 129 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -760,4 +760,133 @@ describe('misc transform issues', () => {
760760
'console.log(Hello, World)'
761761
].join('\n'));
762762
});
763+
});
764+
765+
describe ('Export Live Bindings', () => {
766+
it ('should only export when export is assigned for declarations', async () => {
767+
let res = await es_to_cjs({ plugins: [] }, [
768+
'export let hello;',
769+
'hello = 123;'
770+
].join('\n'), process.cwd() + '/_entry');
771+
expect(res.code).to.equal([
772+
'let hello;; ;',
773+
'hello = 123;;__e__(\'hello\', typeof hello !== \'undefined\' && hello);'
774+
].join('\n'));
775+
});
776+
777+
it ('should work for multiple exports', async () => {
778+
let res = await es_to_cjs({ plugins: [] }, [
779+
'export let hello;',
780+
'hello = 123;',
781+
'export let world;',
782+
'world = 456;'
783+
].join('\n'), process.cwd() + '/_entry');
784+
expect(res.code).to.equal([
785+
'let hello;; ;',
786+
'hello = 123;;__e__(\'hello\', typeof hello !== \'undefined\' && hello);',
787+
'let world;; ;',
788+
'world = 456;;__e__(\'world\', typeof world !== \'undefined\' && world);'
789+
].join('\n'));
790+
});
791+
792+
it ('should support inline assignments', async () => {
793+
let res = await es_to_cjs({ plugins: [] }, [
794+
'export let hello;',
795+
'(function () {})(hello || (hello = 123))'
796+
].join('\n'), process.cwd() + '/_entry');
797+
expect(res.code).to.equal([
798+
'let hello;; ;',
799+
'(function () {})(hello || (hello = 123));__e__(\'hello\', typeof hello !== \'undefined\' && hello);'
800+
].join('\n'));
801+
});
802+
803+
it ('should support inline assignments 2', async () => {
804+
let res = await es_to_cjs({ plugins: [] }, [
805+
'export let hello;',
806+
'(function () {})(hello || (hello = 123));'
807+
].join('\n'), process.cwd() + '/_entry');
808+
expect(res.code).to.equal([
809+
'let hello;; ;',
810+
'(function () {})(hello || (hello = 123));;__e__(\'hello\', typeof hello !== \'undefined\' && hello);'
811+
].join('\n'));
812+
});
813+
814+
it ('should not fail when found inside shadowing function expression', async () => {
815+
let res = await es_to_cjs({ plugins: [] }, [
816+
'export let hello;',
817+
'(function (hello) { hello = 123 })();',
818+
'hello = 123'
819+
].join('\n'), process.cwd() + '/_entry');
820+
expect(res.code).to.equal([
821+
'let hello;; ;',
822+
'(function (hello) { hello = 123 })();;__e__(\'hello\', typeof hello !== \'undefined\' && hello);',
823+
'hello = 123;__e__(\'hello\', typeof hello !== \'undefined\' && hello);'
824+
].join('\n'));
825+
});
826+
827+
it ('should not fail when found inside shadowing function expression for multiple exports', async () => {
828+
let res = await es_to_cjs({ plugins: [] }, [
829+
'export let hello;',
830+
'export let world;',
831+
'(function (hello) { hello = 123 })();',
832+
'(function (world) { world = 123 })();',
833+
'hello = 123',
834+
'world = 456'
835+
].join('\n'), process.cwd() + '/_entry');
836+
expect(res.code).to.equal([
837+
'let hello;; ;',
838+
'let world;; ;',
839+
'(function (hello) { hello = 123 })();;__e__(\'hello\', typeof hello !== \'undefined\' && hello);',
840+
'(function (world) { world = 123 })();;__e__(\'world\', typeof world !== \'undefined\' && world);',
841+
'hello = 123;__e__(\'hello\', typeof hello !== \'undefined\' && hello);',
842+
'world = 456;__e__(\'world\', typeof world !== \'undefined\' && world);'
843+
].join('\n'));
844+
});
845+
846+
it ('should not fail when exported after shadowed function statement', async () => {
847+
let res = await es_to_cjs({ plugins: [] }, [
848+
'function print (hello) { hello = 123 }',
849+
'export let hello;',
850+
'hello = 123'
851+
].join('\n'), process.cwd() + '/_entry');
852+
expect(res.code).to.equal([
853+
'function print (hello) { hello = 123 };__e__(\'hello\', typeof hello !== \'undefined\' && hello);',
854+
'let hello;; ;',
855+
'hello = 123;__e__(\'hello\', typeof hello !== \'undefined\' && hello);'
856+
].join('\n'));
857+
});
858+
859+
it ('should not fail when exported after shadowed arrow expression', async () => {
860+
let res = await es_to_cjs({ plugins: [] }, [
861+
'(hello => { hello = 123 })();',
862+
'export let hello;',
863+
'hello = 123'
864+
].join('\n'), process.cwd() + '/_entry');
865+
expect(res.code).to.equal([
866+
'(hello => { hello = 123 })();;__e__(\'hello\', typeof hello !== \'undefined\' && hello);',
867+
'let hello;; ;',
868+
'hello = 123;__e__(\'hello\', typeof hello !== \'undefined\' && hello);'
869+
].join('\n'));
870+
});
871+
872+
it ('should not fail when shadowed in nested functions', async () => {
873+
let res = await es_to_cjs({ plugins: [] }, [
874+
'function parent (hello) {',
875+
' function nested (hello) {',
876+
' hello = 123;',
877+
' }',
878+
'}',
879+
'export let hello;',
880+
'hello = 123'
881+
].join('\n'), process.cwd() + '/_entry');
882+
expect(res.code).to.equal([
883+
'function parent (hello) {',
884+
' function nested (hello) {',
885+
' hello = 123;',
886+
' }',
887+
'};__e__(\'hello\', typeof hello !== \'undefined\' && hello);',
888+
'let hello;; ;',
889+
'hello = 123;__e__(\'hello\', typeof hello !== \'undefined\' && hello);'
890+
].join('\n'));
891+
});
763892
});

test/cases/scenarios/simple.js

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -290,4 +290,10 @@ describe('Nollup', function () {
290290
let entry = await bundle.generate();
291291
expect(entry.default).to.equal('A');
292292
});
293+
294+
it ('Scenario: Export Declaration Late Binding', async function () {
295+
let bundle = await createNollup('export-declaration-late-binding');
296+
let entry = await bundle.generate();
297+
expect(entry.default).to.equal('hello world');
298+
});
293299
});
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
import { messages } from './messages';
2+
3+
export default messages.greeting;
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
export var messages;
2+
3+
(function (messages) {
4+
messages['greeting'] = 'hello world';
5+
})(messages || (messages = {}));

0 commit comments

Comments
 (0)