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 uninitialised exports turn up via .hasOwnProperty for non-ES formats #3957

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
46 changes: 13 additions & 33 deletions src/ast/nodes/VariableDeclaration.ts
@@ -1,5 +1,6 @@
import MagicString from 'magic-string';
import { BLANK } from '../../utils/blank';
import { isReassignedExportsMember } from '../../utils/reassignedExportsMember';
import {
findFirstOccurrenceOutsideComment,
findNonWhiteSpace,
Expand All @@ -19,15 +20,6 @@ import * as NodeType from './NodeType';
import { IncludeChildren, NodeBase } from './shared/Node';
import VariableDeclarator from './VariableDeclarator';

function isReassignedExportsMember(
variable: Variable,
exportNamesByVariable: Map<Variable, string[]>
): boolean {
return (
variable.renderBaseName !== null && exportNamesByVariable.has(variable) && variable.isReassigned
);
}

function areAllDeclarationsIncludedAndNotExported(
declarations: VariableDeclarator[],
exportNamesByVariable: Map<Variable, string[]>
Expand Down Expand Up @@ -149,7 +141,7 @@ export default class VariableDeclaration extends NodeBase {
private renderReplacedDeclarations(
code: MagicString,
options: RenderOptions,
{ start = this.start, end = this.end, isNoStatement }: NodeRenderOptions
{ isNoStatement }: NodeRenderOptions
): void {
const separatedNodes = getCommaSeparatedNodesWithBoundaries(
this.declarations,
Expand All @@ -168,15 +160,7 @@ export default class VariableDeclaration extends NodeBase {
nextSeparatorString;
const systemPatternExports: Variable[] = [];
for (const { node, start, separator, contentEnd, end } of separatedNodes) {
if (
!node.included ||
(node.id instanceof Identifier &&
isReassignedExportsMember(
(node.id as IdentifierWithVariable).variable,
options.exportNamesByVariable
) &&
node.init === null)
) {
if (!node.included) {
code.remove(start, end);
continue;
}
Expand Down Expand Up @@ -240,19 +224,15 @@ export default class VariableDeclaration extends NodeBase {
lastSeparatorPos = separator!;
separatorString = nextSeparatorString;
}
if (hasRenderedContent) {
this.renderDeclarationEnd(
code,
separatorString,
lastSeparatorPos,
actualContentEnd!,
renderedContentEnd,
systemPatternExports,
options,
isNoStatement
);
} else {
code.remove(start, end);
}
this.renderDeclarationEnd(
code,
separatorString,
lastSeparatorPos,
actualContentEnd!,
renderedContentEnd,
systemPatternExports,
options,
isNoStatement
);
}
}
8 changes: 8 additions & 0 deletions src/ast/nodes/VariableDeclarator.ts
@@ -1,5 +1,6 @@
import MagicString from 'magic-string';
import { BLANK } from '../../utils/blank';
import { isReassignedExportsMember } from '../../utils/reassignedExportsMember';
import {
findFirstOccurrenceOutsideComment,
findNonWhiteSpace,
Expand All @@ -8,6 +9,7 @@ import {
import { HasEffectsContext, InclusionContext } from '../ExecutionContext';
import { ObjectPath } from '../utils/PathTracker';
import { UNDEFINED_EXPRESSION } from '../values';
import Identifier from './Identifier';
import * as NodeType from './NodeType';
import { ExpressionNode, IncludeChildren, NodeBase } from './shared/Node';
import { PatternNode } from './shared/Pattern';
Expand Down Expand Up @@ -53,6 +55,12 @@ export default class VariableDeclarator extends NodeBase {
options,
renderId ? BLANK : { renderedParentType: NodeType.ExpressionStatement }
);
} else if (
this.id instanceof Identifier &&
isReassignedExportsMember(this.id.variable!, options.exportNamesByVariable)
) {
const _ = options.compact ? '' : ' ';
code.appendLeft(this.end, `${_}=${_}void 0`);
}
}
}
10 changes: 10 additions & 0 deletions src/utils/reassignedExportsMember.ts
@@ -0,0 +1,10 @@
import Variable from '../ast/variables/Variable';

export function isReassignedExportsMember(
variable: Variable,
exportNamesByVariable: Map<Variable, string[]>
): boolean {
return (
variable.renderBaseName !== null && exportNamesByVariable.has(variable) && variable.isReassigned
);
}
3 changes: 2 additions & 1 deletion test/form/samples/assignment-to-exports/_expected/amd.js
Expand Up @@ -5,6 +5,7 @@ define(['exports'], function (exports) { 'use strict';

// Reassigned uninitialised export
exports.bar1 = 1;
exports.bar1 = void 0;

// Reassigned initialised export
exports.baz1 = 1;
Expand All @@ -14,7 +15,7 @@ define(['exports'], function (exports) { 'use strict';
var kept1, foo2, kept2;

// Reassigned uninitialised export
var kept1, kept2;
var kept1; exports.bar2 = void 0; var kept2;
exports.bar2 = 1;

// Reassigned initialised export
Expand Down
3 changes: 2 additions & 1 deletion test/form/samples/assignment-to-exports/_expected/cjs.js
Expand Up @@ -7,6 +7,7 @@ var foo1;

// Reassigned uninitialised export
exports.bar1 = 1;
exports.bar1 = void 0;

// Reassigned initialised export
exports.baz1 = 1;
Expand All @@ -16,7 +17,7 @@ exports.baz1 = 2;
var kept1, foo2, kept2;

// Reassigned uninitialised export
var kept1, kept2;
var kept1; exports.bar2 = void 0; var kept2;
exports.bar2 = 1;

// Reassigned initialised export
Expand Down
2 changes: 1 addition & 1 deletion test/form/samples/assignment-to-exports/_expected/es.js
Expand Up @@ -3,7 +3,7 @@ var foo1;

// Reassigned uninitialised export
bar1 = 1;
var bar1; // this will be removed for non ES6 bundles
var bar1;

// Reassigned initialised export
var baz1 = 1;
Expand Down
3 changes: 2 additions & 1 deletion test/form/samples/assignment-to-exports/_expected/iife.js
Expand Up @@ -6,6 +6,7 @@ var bundle = (function (exports) {

// Reassigned uninitialised export
exports.bar1 = 1;
exports.bar1 = void 0;

// Reassigned initialised export
exports.baz1 = 1;
Expand All @@ -15,7 +16,7 @@ var bundle = (function (exports) {
var kept1, foo2, kept2;

// Reassigned uninitialised export
var kept1, kept2;
var kept1; exports.bar2 = void 0; var kept2;
exports.bar2 = 1;

// Reassigned initialised export
Expand Down
Expand Up @@ -15,7 +15,7 @@ System.register('bundle', [], function (exports) {

// Reassigned uninitialised export
bar1 = exports('bar1', 1);
var bar1; // this will be removed for non ES6 bundles
var bar1;

// Reassigned initialised export
var baz1 = exports('baz1', 1);
Expand Down
3 changes: 2 additions & 1 deletion test/form/samples/assignment-to-exports/_expected/umd.js
Expand Up @@ -9,6 +9,7 @@

// Reassigned uninitialised export
exports.bar1 = 1;
exports.bar1 = void 0;

// Reassigned initialised export
exports.baz1 = 1;
Expand All @@ -18,7 +19,7 @@
var kept1, foo2, kept2;

// Reassigned uninitialised export
var kept1, kept2;
var kept1; exports.bar2 = void 0; var kept2;
exports.bar2 = 1;

// Reassigned initialised export
Expand Down
2 changes: 1 addition & 1 deletion test/form/samples/assignment-to-exports/main.js
Expand Up @@ -3,7 +3,7 @@ export var foo1;

// Reassigned uninitialised export
bar1 = 1;
export var bar1; // this will be removed for non ES6 bundles
export var bar1;

// Reassigned initialised export
export var baz1 = 1;
Expand Down
@@ -1,9 +1,9 @@
define(['exports'], function (exports) { 'use strict';

var aFoo;
var aFoo; exports.aBar = void 0;
exports.aBar = 2;

var bBar;
exports.bFoo = void 0; var bBar;
exports.bFoo = 2;

var cFoo; exports.cBar = 1;
Expand Down
Expand Up @@ -2,10 +2,10 @@

Object.defineProperty(exports, '__esModule', { value: true });

var aFoo;
var aFoo; exports.aBar = void 0;
exports.aBar = 2;

var bBar;
exports.bFoo = void 0; var bBar;
exports.bFoo = 2;

var cFoo; exports.cBar = 1;
Expand Down
@@ -1,10 +1,10 @@
var bundle = (function (exports) {
'use strict';

var aFoo;
var aFoo; exports.aBar = void 0;
exports.aBar = 2;

var bBar;
exports.bFoo = void 0; var bBar;
exports.bFoo = 2;

var cFoo; exports.cBar = 1;
Expand Down
Expand Up @@ -4,10 +4,10 @@
(global = typeof globalThis !== 'undefined' ? globalThis : global || self, factory(global.bundle = {}));
}(this, (function (exports) { 'use strict';

var aFoo;
var aFoo; exports.aBar = void 0;
exports.aBar = 2;

var bBar;
exports.bFoo = void 0; var bBar;
exports.bFoo = 2;

var cFoo; exports.cBar = 1;
Expand Down
@@ -0,0 +1,17 @@
const assert = require('assert');

module.exports = {
description: 'creates an export as an exports property even if is has no initializer',
options: { output: { compact: true } },
exports(exports) {
assert.strictEqual(exports.foo, undefined);
assert.strictEqual(exports.bar, undefined);
assert.strictEqual(exports.baz, undefined);
assert.ok(exports.hasOwnProperty('foo'));
assert.ok(exports.hasOwnProperty('bar'));
assert.ok(exports.hasOwnProperty('baz'));
exports.defineFooBar();
assert.strictEqual(exports.foo, 'defined');
assert.strictEqual(exports.bar, 'defined');
}
};
@@ -0,0 +1,6 @@
export let foo;
export let bar, quux = 3, baz;
export function defineFooBar() {
foo = 'defined';
bar = 'defined';
}
16 changes: 16 additions & 0 deletions test/function/samples/create-undefined-export-property/_config.js
@@ -0,0 +1,16 @@
const assert = require('assert');

module.exports = {
description: 'creates an export as an exports property even if is has no initializer',
exports(exports) {
assert.strictEqual(exports.foo, undefined);
assert.strictEqual(exports.bar, undefined);
assert.strictEqual(exports.baz, undefined);
assert.ok(exports.hasOwnProperty('foo'));
assert.ok(exports.hasOwnProperty('bar'));
assert.ok(exports.hasOwnProperty('baz'));
exports.defineFooBar();
assert.strictEqual(exports.foo, 'defined');
assert.strictEqual(exports.bar, 'defined');
}
};
@@ -0,0 +1,6 @@
export let foo;
export let bar, quux = 3, baz;
export function defineFooBar() {
foo = 'defined';
bar = 'defined';
}
3 changes: 3 additions & 0 deletions test/function/samples/destructuring-loop/_config.js
@@ -0,0 +1,3 @@
module.exports = {
description: 'handles loops with destructuring declarations'
};
11 changes: 11 additions & 0 deletions test/function/samples/destructuring-loop/main.js
@@ -0,0 +1,11 @@
let result;

for (const [foo] of [['foo']]) {
result = foo;
}
assert.strictEqual(result, 'foo');

for (const { bar } of [{ bar: 'bar' }]) {
result = bar;
}
assert.strictEqual(result, 'bar');