Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Make sure system exports are valid JavaScript #3960

Merged
merged 1 commit into from Feb 12, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
2 changes: 1 addition & 1 deletion cli/run/batchWarnings.ts
Expand Up @@ -41,7 +41,7 @@ export default function batchWarnings() {
const id = (warning.loc && warning.loc.file) || warning.id;
if (id) {
const loc = warning.loc
? `${relativeId(id)}: (${warning.loc.line}:${warning.loc.column})`
? `${relativeId(id)} (${warning.loc.line}:${warning.loc.column})`
: relativeId(id);

stderr(bold(relativeId(loc)));
Expand Down
2 changes: 2 additions & 0 deletions docs/999-big-list-of-options.md
Expand Up @@ -931,6 +931,8 @@ Default: `false`

Re-parses each generated chunk to detect if the generated code is valid JavaScript. This can be useful to debug output generated by plugins that use the [`renderChunk`](guide/en/#renderchunk) hook to transform code.

If the code is invalid, a warning will be issued. Note that no error is thrown so that you can still inspect the generated output. To promote this warning to an error, you can watch for it in an [`onwarn`](guide/en/#onwarn) handler.

#### preserveEntrySignatures
Type: `"strict" | "allow-extension" | "exports-only" | false`<br>
CLI: `--preserveEntrySignatures <strict|allow-extension>`/`--no-preserveEntrySignatures`<br>
Expand Down
2 changes: 1 addition & 1 deletion src/Bundle.ts
Expand Up @@ -186,7 +186,7 @@ export default class Bundle {
ecmaVersion: 'latest'
});
} catch (exception) {
return error(errChunkInvalid(file, exception));
this.inputOptions.onwarn(errChunkInvalid(file, exception));
}
}
}
Expand Down
4 changes: 2 additions & 2 deletions src/ast/nodes/AssignmentExpression.ts
Expand Up @@ -114,9 +114,9 @@ export default class AssignmentExpression extends NodeBase {
if (systemPatternExports.length > 0) {
code.prependRight(
this.start,
getSystemExportFunctionLeft(systemPatternExports, true, options)
`(${getSystemExportFunctionLeft(systemPatternExports, true, options)}`
);
code.appendLeft(this.end, ')');
code.appendLeft(this.end, '))');
}
}
}
Expand Down
7 changes: 2 additions & 5 deletions src/ast/nodes/UpdateExpression.ts
@@ -1,9 +1,6 @@
import MagicString from 'magic-string';
import { RenderOptions } from '../../utils/renderHelpers';
import {
getSystemExportFunctionLeft,
getSystemExportStatement
} from '../../utils/systemJsRendering';
import { getSystemExportFunctionLeft, getSystemExportStatement } from '../../utils/systemJsRendering';
import { HasEffectsContext } from '../ExecutionContext';
import { EMPTY_PATH, ObjectPath } from '../utils/PathTracker';
import Identifier from './Identifier';
Expand Down Expand Up @@ -65,7 +62,7 @@ export default class UpdateExpression extends NodeBase {
code.overwrite(
this.start,
this.end,
`${getSystemExportFunctionLeft([variable!], false, options)}${this.operator}${name})`
`(${getSystemExportFunctionLeft([variable!], false, options)}${this.operator}${name}))`
);
} else {
let op;
Expand Down
8 changes: 5 additions & 3 deletions src/utils/pureComments.ts
@@ -1,5 +1,6 @@
import * as acorn from 'acorn';
import { base as basicWalker, BaseWalker } from 'acorn-walk';
import { CallExpression, ExpressionStatement, NewExpression } from '../ast/nodes/NodeType';
import { CommentDescription } from '../Module';

// patch up acorn-walk until class-fields are officially supported
Expand All @@ -13,7 +14,8 @@ basicWalker.PropertyDefinition = function (node: any, st: any, c: any) {
};

interface CommentState {
commentIndex: number; commentNodes: CommentDescription[]
commentIndex: number;
commentNodes: CommentDescription[];
}

function handlePureAnnotationsOfNode(
Expand All @@ -40,10 +42,10 @@ function markPureNode(
} else {
node.annotations = [comment];
}
if (node.type === 'ExpressionStatement') {
if (node.type === ExpressionStatement) {
node = (node as any).expression;
}
if (node.type === 'CallExpression' || node.type === 'NewExpression') {
if (node.type === CallExpression || node.type === NewExpression) {
(node as any).annotatedPure = true;
}
}
Expand Down
2 changes: 1 addition & 1 deletion test/cli/samples/validate/_config.js
Expand Up @@ -8,7 +8,7 @@ module.exports = {
stderr: stderr =>
assertIncludes(
stderr,
`[!] Error: Chunk "out.js" is not valid JavaScript: Unterminated comment (3:20).
`(!) Chunk "out.js" is not valid JavaScript: Unterminated comment (3:20).
out.js (3:20)
1: console.log(2 );
2:
Expand Down
2 changes: 1 addition & 1 deletion test/cli/samples/warn-multiple/_config.js
Expand Up @@ -25,7 +25,7 @@ module.exports = {
stderr,

"(!) Module level directives cause errors when bundled, 'use stuff' was ignored.\n" +
'main.js: (1:0)\n' +
'main.js (1:0)\n' +
"1: 'use stuff';\n" +
' ^\n' +
'2: \n' +
Expand Down
19 changes: 14 additions & 5 deletions test/form/index.js
Expand Up @@ -14,6 +14,7 @@ runTestSuiteWithSamples('form', path.resolve(__dirname, 'samples'), (dir, config
() => {
let bundle;
const runRollupTest = async (inputFile, bundleFile, defaultFormat) => {
const warnings = [];
if (config.before) config.before();
try {
process.chdir(dir);
Expand All @@ -30,10 +31,7 @@ runTestSuiteWithSamples('form', path.resolve(__dirname, 'samples'), (dir, config
config.expectedWarnings.indexOf(warning.code) >= 0
)
) {
throw new Error(
`Unexpected warnings (${warning.code}): ${warning.message}\n` +
'If you expect warnings, list their codes in config.expectedWarnings'
);
warnings.push(warning);
}
},
strictDeprecations: true
Expand All @@ -48,7 +46,7 @@ runTestSuiteWithSamples('form', path.resolve(__dirname, 'samples'), (dir, config
exports: 'auto',
file: inputFile,
format: defaultFormat,
// validate: true // add when systemjs bugs fixed
validate: true
},
(config.options || {}).output || {}
),
Expand All @@ -58,6 +56,17 @@ runTestSuiteWithSamples('form', path.resolve(__dirname, 'samples'), (dir, config
} finally {
if (config.after) config.after();
}
if (warnings.length > 0) {
const codes = new Set();
for (const { code } of warnings) {
codes.add(code);
}
throw new Error(
`Unexpected warnings (${[...codes].join(', ')}): \n${warnings
.map(({ message }) => `${message}\n\n`)
.join('')}` + 'If you expect warnings, list their codes in config.expectedWarnings'
);
}
};

if (isSingleFormatTest) {
Expand Down
2 changes: 1 addition & 1 deletion test/form/samples/system-export-compact/_expected.js
@@ -1,3 +1,3 @@
System.register([],function(exports){'use strict';return{execute:function(){exports({a:void 0,b:void 0,c:void 0});let a, b;

function(v){return exports({a:a}),v}([{ b: a = (exports('a',a-1),a--) }] = { b: function(v){return exports({b:v,c:v}),v}(--b) });}}});
(function(v){return exports({a:a}),v}([{ b: a = (exports('a',a-1),a--) }] = { b: (function(v){return exports({b:v,c:v}),v}(--b)) })), (function(v){return exports({a:a}),v}([a] = [1]));}}});
4 changes: 2 additions & 2 deletions test/form/samples/system-export-compact/main.js
@@ -1,5 +1,5 @@
export let a, b;

[{ b: a = a-- }] = { b: b-- };
[{ b: a = a-- }] = { b: b-- }, [a] = [1];

export { b as c}
export { b as c };
Expand Up @@ -11,8 +11,8 @@ System.register([], function (exports) {

let a, b, c;

console.log(function (v) { return exports({ a: a }), v; }({a} = someObject));
(function (v) { return exports({ b: b, c: c }), v; }({b, c} = someObject));
console.log((function (v) { return exports({ a: a }), v; }({a} = someObject)));
((function (v) { return exports({ b: b, c: c }), v; }({b, c} = someObject)));

}
};
Expand Down
Expand Up @@ -30,12 +30,12 @@ System.register([], function (exports) {
a = function (v) { return exports({ a: v, a2: v }), v; }(b = exports('b', c = function (v) { return exports({ c: v, c2: v }), v; }(0)));

// Destructing Assignment Expression
(function (v) { return exports({ a: a, a2: a, b: b, c: c, c2: c }), v; }({ a, b, c } = { c: 4, b: 5, a: 6 }));
((function (v) { return exports({ a: a, a2: a, b: b, c: c, c2: c }), v; }({ a, b, c } = { c: 4, b: 5, a: 6 })));

// Destructuring Defaults
var p = function (v) { return exports({ p: v, pp: v }), v; }(5);
var q = function (v) { return exports({ q: v, qq: v }), v; }(10);
(function (v) { return exports({ p: p, pp: p }), v; }({ p = q = function (v) { return exports({ q: v, qq: v }), v; }(20) } = {}));
((function (v) { return exports({ p: p, pp: p }), v; }({ p = q = function (v) { return exports({ q: v, qq: v }), v; }(20) } = {})));

// Function Assignment
function fn () {
Expand All @@ -44,7 +44,8 @@ System.register([], function (exports) {
fn = function (v) { return exports({ fn: v, fn2: v }), v; }(5);

// Update Expression
function (v) { return exports({ a: v, a2: v }), v; }(++a), (exports('b', b + 1), b++), (++c, exports({ c: c, c2: c }), c);
(function (v) { return exports({ a: v, a2: v }), v; }(++a)), (exports('b', b + 1), b++), (++c, exports({ c: c, c2: c }), c);
(function (v) { return exports({ a: v, a2: v }), v; }(++a));

// Class Declaration
class A {} exports({ A: A, B: A });
Expand Down
1 change: 1 addition & 0 deletions test/form/samples/system-multiple-export-bindings/main.js
Expand Up @@ -31,6 +31,7 @@ export { fn as fn2 }

// Update Expression
a++, b++, ++c;
true && a++;

// Class Declaration
export class A {}
Expand Down
3 changes: 3 additions & 0 deletions test/function/samples/validate-output/_config.js
@@ -1,6 +1,9 @@
module.exports = {
description: 'handles validate failure',
options: {
onwarn(warning) {
throw warning;
},
output: {
outro: '/*',
validate: true
Expand Down
1 change: 1 addition & 0 deletions test/utils.js
Expand Up @@ -17,6 +17,7 @@ exports.assertIncludes = assertIncludes;

function normaliseError(error) {
delete error.stack;
delete error.toString;
return Object.assign({}, error, {
message: error.message
});
Expand Down