Skip to content

Commit

Permalink
Prevent anonymous functions getting named by export default [fix]
Browse files Browse the repository at this point in the history
Fixes #99.
  • Loading branch information
overlookmotel committed Mar 25, 2021
1 parent 6545722 commit 09a9d69
Show file tree
Hide file tree
Showing 4 changed files with 78 additions and 45 deletions.
33 changes: 21 additions & 12 deletions lib/serialize/output.js
Original file line number Diff line number Diff line change
Expand Up @@ -472,17 +472,26 @@ module.exports = {
// Only used once - substitute content in place of var
const {node: parentNode, key} = dependents[0];

// Wrap unnamed functions used as object properties in `(0, fn)` to prevent them getting named
// and unwrap `{x: {x() {}}.x}` to `{x() {}}`
if (t.isObjectProperty(parentNode) && key === 'value') {
if ((t.isFunction(node) || t.isClass(node)) && !node.id) {
// Wrap unnamed function
node = t.sequenceExpression([t.numericLiteral(0), node]);
} else if (!parentNode.computed && isWrappingMethod(node, parentNode.key)) {
// Unwrap `{x: {x() {}}.x}` to `{x() {}}`
replaceNode(parentNode, node.object.properties[0]);
node = null;
}
// Wrap unnamed functions used as object properties or `export default`
// in `(0, fn)` to prevent them getting implicitly named.
// NB `valRecord.scope` check is to avoid doing this for `createScope` functions.
if (
((t.isFunction(node) || t.isClass(node)) && !node.id && valRecord.scope)
&& (
(t.isObjectProperty(parentNode) && key === 'value')
|| (t.isExportDefaultDeclaration(parentNode) && key === 'declaration')
)
) {
node = t.sequenceExpression([t.numericLiteral(0), node]);
}

// Unwrap `{x: {x() {}}.x}` to `{x() {}}`
if (
(t.isObjectProperty(parentNode) && key === 'value')
&& !parentNode.computed && isWrappingMethod(node, parentNode.key)
) {
replaceNode(parentNode, node.object.properties[0]);
node = null;
}

if (node) parentNode[key] = node;
Expand All @@ -492,7 +501,7 @@ module.exports = {
varNode.name = transformVarName(mangle ? null : toJsIdentifier(varNode.name));

if (!nodeIsImport) {
// Wrap unnamed functions in `(0, fn)` to prevent them getting named.
// Wrap unnamed functions in `(0, fn)` to prevent them getting implicitly named.
// NB `valRecord.scope` check is to avoid doing this for `createScope` functions.
if ((t.isFunction(node) || t.isClass(node)) && !node.id && valRecord.scope) {
node = t.sequenceExpression([t.numericLiteral(0), node]);
Expand Down
12 changes: 12 additions & 0 deletions test/classes.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -3007,6 +3007,18 @@ describe('Classes', () => {
}
});

itSerializes('unnamed class as default export', {
in() {
return class {};
},
format: 'esm',
out: 'export default(0,class{})',
validate(Klass) {
expect(Klass).toBeFunction();
expect(Klass.name).toBe('');
}
});

itSerializes('not valid JS identifier', {
in: () => ({'0a': class {}}['0a']),
out: 'Object.defineProperties(class{},{name:{value:"0a",configurable:true}})',
Expand Down
12 changes: 12 additions & 0 deletions test/functions.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -4683,6 +4683,18 @@ describe('Functions', () => {
}
});

itSerializes('unnamed function as default export', {
in() {
return function() {};
},
format: 'esm',
out: 'export default(0,function(){})',
validate(fn) {
expect(fn).toBeFunction();
expect(fn.name).toBe('');
}
});

itSerializes('not valid JS identifier', {
in: () => ({'0a': function() {}}['0a']),
out: 'Object.defineProperties(function(){},{name:{value:"0a"}})',
Expand Down
66 changes: 33 additions & 33 deletions test/split.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -362,7 +362,7 @@ describe('Code splitting', () => {
'two.js': 'module.exports=require("./one.js")'
},
outEsm: {
'one.js': 'export default(()=>1)',
'one.js': 'export default(0,()=>1)',
'two.js': 'import a from"./one.js";export default a'
},
outJs: {
Expand All @@ -387,8 +387,8 @@ describe('Code splitting', () => {
'two.js': 'module.exports=()=>2'
},
outEsm: {
'one.js': 'export default(()=>1)',
'two.js': 'export default(()=>2)'
'one.js': 'export default(0,()=>1)',
'two.js': 'export default(0,()=>2)'
},
outJs: {
'one.js': '()=>1',
Expand Down Expand Up @@ -2014,19 +2014,19 @@ describe('Code splitting', () => {
type: 'entry',
name: 'one',
filename: 'one.js',
content: 'import a from"./common.BV7UVVU7.js";a()'
content: 'import a from"./common.JWCKETAR.js";a()'
},
{
type: 'entry',
name: 'two',
filename: 'two.js',
content: 'import a from"./common.BV7UVVU7.js";a()'
content: 'import a from"./common.JWCKETAR.js";a()'
},
{
type: 'common',
name: null,
filename: 'common.BV7UVVU7.js',
content: 'export default(()=>console.log("shared"))'
filename: 'common.JWCKETAR.js',
content: 'export default(0,()=>console.log("shared"))'
}
]);
});
Expand All @@ -2050,19 +2050,19 @@ describe('Code splitting', () => {
type: 'entry',
name: 'one',
filename: 'one.js',
content: 'import a from"./common.BV7UVVU7.js";(a=>()=>{a();console.log("one")})(a)()'
content: 'import a from"./common.JWCKETAR.js";(a=>()=>{a();console.log("one")})(a)()'
},
{
type: 'entry',
name: 'two',
filename: 'two.js',
content: 'import a from"./common.BV7UVVU7.js";a()'
content: 'import a from"./common.JWCKETAR.js";a()'
},
{
type: 'common',
name: null,
filename: 'common.BV7UVVU7.js',
content: 'export default(()=>console.log("shared"))'
filename: 'common.JWCKETAR.js',
content: 'export default(0,()=>console.log("shared"))'
}
]);
});
Expand All @@ -2086,19 +2086,19 @@ describe('Code splitting', () => {
type: 'entry',
name: 'one',
filename: 'one.js',
content: 'import a from"./common.BV7UVVU7.js";a()'
content: 'import a from"./common.JWCKETAR.js";a()'
},
{
type: 'entry',
name: 'two',
filename: 'two.js',
content: 'import a from"./common.BV7UVVU7.js";(a=>()=>{a();console.log("two")})(a)()'
content: 'import a from"./common.JWCKETAR.js";(a=>()=>{a();console.log("two")})(a)()'
},
{
type: 'common',
name: null,
filename: 'common.BV7UVVU7.js',
content: 'export default(()=>console.log("shared"))'
filename: 'common.JWCKETAR.js',
content: 'export default(0,()=>console.log("shared"))'
}
]);
});
Expand Down Expand Up @@ -2628,7 +2628,7 @@ describe('Code splitting', () => {
'split.QEIVLBZW.js': 'module.exports={x:1}'
},
outEsm: {
'one.js': 'export default(()=>import("./split.UQMAZ4OK.js"))',
'one.js': 'export default(0,()=>import("./split.UQMAZ4OK.js"))',
'split.UQMAZ4OK.js': 'export default{x:1}'
},
outJs: {
Expand All @@ -2651,7 +2651,7 @@ describe('Code splitting', () => {
'imported.QEIVLBZW.js': 'module.exports={x:1}'
},
outEsm: {
'one.js': 'export default(()=>import("./imported.UQMAZ4OK.js"))',
'one.js': 'export default(0,()=>import("./imported.UQMAZ4OK.js"))',
'imported.UQMAZ4OK.js': 'export default{x:1}'
},
outJs: {
Expand All @@ -2675,7 +2675,7 @@ describe('Code splitting', () => {
'split.QEIVLBZW.js': 'module.exports={x:1}'
},
outEsm: {
'sub/subSub/one.js': 'export default(()=>import("../../split.UQMAZ4OK.js"))',
'sub/subSub/one.js': 'export default(0,()=>import("../../split.UQMAZ4OK.js"))',
'split.UQMAZ4OK.js': 'export default{x:1}'
},
outJs: {
Expand All @@ -2697,7 +2697,7 @@ describe('Code splitting', () => {
'imported.QEIVLBZW.js': 'module.exports={x:1}'
},
outEsm: {
'sub/subSub/one.js': 'export default(()=>import("../../imported.UQMAZ4OK.js"))',
'sub/subSub/one.js': 'export default(0,()=>import("../../imported.UQMAZ4OK.js"))',
'imported.UQMAZ4OK.js': 'export default{x:1}'
},
outJs: {
Expand All @@ -2719,7 +2719,7 @@ describe('Code splitting', () => {
'sub/subSub/imported.QEIVLBZW.js': 'module.exports={x:1}'
},
outEsm: {
'one.js': 'export default(()=>import("./sub/subSub/imported.UQMAZ4OK.js"))',
'one.js': 'export default(0,()=>import("./sub/subSub/imported.UQMAZ4OK.js"))',
'sub/subSub/imported.UQMAZ4OK.js': 'export default{x:1}'
},
outJs: {
Expand All @@ -2741,7 +2741,7 @@ describe('Code splitting', () => {
'sub/subSub/imported.QEIVLBZW.js': 'module.exports={x:1}'
},
outEsm: {
'sub/one.js': 'export default(()=>import("./subSub/imported.UQMAZ4OK.js"))',
'sub/one.js': 'export default(0,()=>import("./subSub/imported.UQMAZ4OK.js"))',
'sub/subSub/imported.UQMAZ4OK.js': 'export default{x:1}'
},
outJs: {
Expand Down Expand Up @@ -2806,7 +2806,7 @@ describe('Code splitting', () => {
'imported.QEIVLBZW.js': 'module.exports={x:1}'
},
outEsm: {
'one.js': 'export default(()=>import("./imported.UQMAZ4OK.js"))',
'one.js': 'export default(0,()=>import("./imported.UQMAZ4OK.js"))',
'imported.UQMAZ4OK.js': 'export default{x:1}'
},
outJs: {
Expand Down Expand Up @@ -2894,7 +2894,7 @@ describe('Code splitting', () => {
outEsm: {
'one.js': 'import a from"./split.WSDEFBGJ.js";export default{sharedInner:a[1],sharedOuter:a[0]}',
'two.js': 'import a from"./split.WSDEFBGJ.js";export default{sharedInner:a[1],sharedOuter:a[0]}',
'three.js': 'export default(()=>import("./sharedOuter.ULKZKTSE.js"))',
'three.js': 'export default(0,()=>import("./sharedOuter.ULKZKTSE.js"))',
'split.WSDEFBGJ.js': 'const a={isSharedInner:true};export default[{isSharedOuter:true,sharedInner:a},a]',
'sharedOuter.ULKZKTSE.js': 'import a from"./split.WSDEFBGJ.js";export default a[0]'
},
Expand Down Expand Up @@ -2997,7 +2997,7 @@ describe('Code splitting', () => {
'imported.QEIVLBZW.js': 'module.exports={x:1}'
},
outEsm: {
'one.js': 'export default(()=>import("./imported.UQMAZ4OK.js"))',
'one.js': 'export default(0,()=>import("./imported.UQMAZ4OK.js"))',
'two.js': 'import a from"./one.js";export default a',
'imported.UQMAZ4OK.js': 'export default{x:1}'
},
Expand Down Expand Up @@ -3028,8 +3028,8 @@ describe('Code splitting', () => {
'imported.QEIVLBZW.js': 'module.exports={x:1}'
},
outEsm: {
'one.js': 'export default(()=>import("./imported.UQMAZ4OK.js"))',
'two.js': 'export default(()=>import("./imported.UQMAZ4OK.js"))',
'one.js': 'export default(0,()=>import("./imported.UQMAZ4OK.js"))',
'two.js': 'export default(0,()=>import("./imported.UQMAZ4OK.js"))',
'imported.UQMAZ4OK.js': 'export default{x:1}'
},
outJs: {
Expand Down Expand Up @@ -3061,9 +3061,9 @@ describe('Code splitting', () => {
'imported2.IXXBWE7H.js': 'module.exports=()=>import("./imported1.QEIVLBZW.js")'
},
outEsm: {
'one.js': 'export default(()=>import("./imported2.ZBOMFFWE.js"))',
'one.js': 'export default(0,()=>import("./imported2.UVSVXFBR.js"))',
'imported1.UQMAZ4OK.js': 'export default{x:1}',
'imported2.ZBOMFFWE.js': 'export default(()=>import("./imported1.UQMAZ4OK.js"))'
'imported2.UVSVXFBR.js': 'export default(0,()=>import("./imported1.UQMAZ4OK.js"))'
},
outJs: {
'one.js': '()=>import("./imported2.IXXBWE7H.js")',
Expand Down Expand Up @@ -3506,7 +3506,7 @@ describe('Code splitting', () => {
'split-Y4EZPJ2P.js': 'module.exports={isSplit:true}'
},
outEsm: {
'one.js': 'export default(()=>import("./split-L22RYOUC.js"))',
'one.js': 'export default(0,()=>import("./split-L22RYOUC.js"))',
'split-L22RYOUC.js': 'export default{isSplit:true}'
},
outJs: {
Expand All @@ -3529,7 +3529,7 @@ describe('Code splitting', () => {
'Y4EZPJ2P.js': 'module.exports={isSplit:true}'
},
outEsm: {
'one.js': 'export default(()=>import("./L22RYOUC.js"))',
'one.js': 'export default(0,()=>import("./L22RYOUC.js"))',
'L22RYOUC.js': 'export default{isSplit:true}'
},
outJs: {
Expand All @@ -3552,7 +3552,7 @@ describe('Code splitting', () => {
'splits/Y4EZPJ2P.js': 'module.exports={isSplit:true}'
},
outEsm: {
'one.js': 'export default(()=>import("./splits/L22RYOUC.js"))',
'one.js': 'export default(0,()=>import("./splits/L22RYOUC.js"))',
'splits/L22RYOUC.js': 'export default{isSplit:true}'
},
outJs: {
Expand All @@ -3576,7 +3576,7 @@ describe('Code splitting', () => {
'splits/Y4EZPJ2P.js': 'module.exports={isSplit:true}'
},
outEsm: {
'entries/one.js': 'export default(()=>import("../splits/L22RYOUC.js"))',
'entries/one.js': 'export default(0,()=>import("../splits/L22RYOUC.js"))',
'splits/L22RYOUC.js': 'export default{isSplit:true}'
},
outJs: {
Expand Down Expand Up @@ -3646,7 +3646,7 @@ describe('Code splitting', () => {
type: 'entry',
name: 'one',
filename: 'one.mjs',
content: 'export default(()=>import("./split.P4XPXXFO.mjs"))'
content: 'export default(0,()=>import("./split.P4XPXXFO.mjs"))'
},
{
type: 'split',
Expand Down

0 comments on commit 09a9d69

Please sign in to comment.