Skip to content

Commit

Permalink
Properly export destructured export declarations in SystemJS
Browse files Browse the repository at this point in the history
  • Loading branch information
lukastaegert committed Dec 11, 2018
1 parent f58d1e6 commit 27be520
Show file tree
Hide file tree
Showing 10 changed files with 128 additions and 27 deletions.
9 changes: 9 additions & 0 deletions src/ast/nodes/ArrayPattern.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { ExecutionPathOptions } from '../ExecutionPathOptions';
import { EMPTY_PATH, ObjectPath, UNKNOWN_EXPRESSION } from '../values';
import Variable from '../variables/Variable';
import * as NodeType from './NodeType';
import { ExpressionEntity } from './shared/Expression';
import { NodeBase } from './shared/Node';
Expand All @@ -9,6 +10,14 @@ export default class ArrayPattern extends NodeBase implements PatternNode {
type: NodeType.tArrayPattern;
elements: (PatternNode | null)[];

addExportedVariables(variables: Variable[]): void {
for (const element of this.elements) {
if (element !== null) {
element.addExportedVariables(variables);
}
}
}

declare(kind: string, _init: ExpressionEntity) {
for (const element of this.elements) {
if (element !== null) {
Expand Down
5 changes: 5 additions & 0 deletions src/ast/nodes/AssignmentPattern.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { BLANK } from '../../utils/blank';
import { NodeRenderOptions, RenderOptions } from '../../utils/renderHelpers';
import { ExecutionPathOptions } from '../ExecutionPathOptions';
import { EMPTY_PATH, ObjectPath, UNKNOWN_PATH } from '../values';
import Variable from '../variables/Variable';
import * as NodeType from './NodeType';
import { ExpressionEntity } from './shared/Expression';
import { ExpressionNode, NodeBase } from './shared/Node';
Expand All @@ -13,6 +14,10 @@ export default class AssignmentPattern extends NodeBase implements PatternNode {
left: PatternNode;
right: ExpressionNode;

addExportedVariables(variables: Variable[]): void {
this.left.addExportedVariables(variables);
}

bind() {
super.bind();
this.left.deoptimizePath(EMPTY_PATH);
Expand Down
9 changes: 8 additions & 1 deletion src/ast/nodes/Identifier.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,18 +13,25 @@ import Variable from '../variables/Variable';
import * as NodeType from './NodeType';
import { ExpressionEntity } from './shared/Expression';
import { Node, NodeBase } from './shared/Node';
import { PatternNode } from './shared/Pattern';

export function isIdentifier(node: Node): node is Identifier {
return node.type === NodeType.Identifier;
}

export default class Identifier extends NodeBase {
export default class Identifier extends NodeBase implements PatternNode {
type: NodeType.tIdentifier;
name: string;

variable: Variable;
private bound: boolean;

addExportedVariables(variables: Variable[]): void {
if (this.variable.exportName) {
variables.push(this.variable);
}
}

bind() {
if (this.bound) return;
this.bound = true;
Expand Down
11 changes: 11 additions & 0 deletions src/ast/nodes/ObjectPattern.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { ExecutionPathOptions } from '../ExecutionPathOptions';
import { EMPTY_PATH, ObjectPath } from '../values';
import Variable from '../variables/Variable';
import * as NodeType from './NodeType';
import Property from './Property';
import RestElement from './RestElement';
Expand All @@ -11,6 +12,16 @@ export default class ObjectPattern extends NodeBase implements PatternNode {
type: NodeType.tObjectPattern;
properties: (Property | RestElement)[];

addExportedVariables(variables: Variable[]): void {
for (const property of this.properties) {
if (property.type === NodeType.Property) {
(<PatternNode>(<unknown>property.value)).addExportedVariables(variables);
} else {
property.argument.addExportedVariables(variables);
}
}
}

declare(kind: string, init: ExpressionEntity) {
for (const property of this.properties) {
property.declare(kind, init);
Expand Down
5 changes: 5 additions & 0 deletions src/ast/nodes/RestElement.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { ExecutionPathOptions } from '../ExecutionPathOptions';
import { EMPTY_PATH, ObjectPath, UNKNOWN_EXPRESSION, UNKNOWN_KEY } from '../values';
import Variable from '../variables/Variable';
import * as NodeType from './NodeType';
import { ExpressionEntity } from './shared/Expression';
import { NodeBase } from './shared/Node';
Expand All @@ -11,6 +12,10 @@ export default class RestElement extends NodeBase implements PatternNode {

private declarationInit: ExpressionEntity | null = null;

addExportedVariables(variables: Variable[]): void {
this.argument.addExportedVariables(variables);
}

bind() {
super.bind();
if (this.declarationInit !== null) {
Expand Down
82 changes: 57 additions & 25 deletions src/ast/nodes/VariableDeclaration.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import { EMPTY_PATH, ObjectPath } from '../values';
import Variable from '../variables/Variable';
import { isIdentifier } from './Identifier';
import * as NodeType from './NodeType';
import { Node, NodeBase } from './shared/Node';
import { NodeBase } from './shared/Node';
import VariableDeclarator from './VariableDeclarator';

function isReassignedExportsMember(variable: Variable): boolean {
Expand All @@ -22,8 +22,41 @@ function isReassignedExportsMember(variable: Variable): boolean {
);
}

export function isVariableDeclaration(node: Node): node is VariableDeclaration {
return node.type === NodeType.VariableDeclaration;
function areAllDeclarationsIncludedAndNotExported(declarations: VariableDeclarator[]): boolean {
for (const declarator of declarations) {
if (!declarator.included) {
return false;
}
if (declarator.id.type === NodeType.Identifier) {
if (declarator.id.variable.exportName) return false;
} else {
const exportedVariables: Variable[] = [];
declarator.id.addExportedVariables(exportedVariables);
if (exportedVariables.length > 0) return false;
}
}
return true;
}

function renderSystemExports(
code: MagicString,
systemPatternExports: Variable[],
appendPosition: number
) {
if (systemPatternExports.length === 1) {
code.appendRight(
appendPosition,
` exports('${systemPatternExports[0].safeExportName ||
systemPatternExports[0].exportName}', ${systemPatternExports[0].getName()});`
);
} else {
code.appendRight(
appendPosition,
` exports({${systemPatternExports
.map(variable => `${variable.safeExportName || variable.exportName}: ${variable.getName()}`)
.join(', ')}});`
);
}
}

export default class VariableDeclaration extends NodeBase {
Expand Down Expand Up @@ -64,12 +97,7 @@ export default class VariableDeclaration extends NodeBase {
}

render(code: MagicString, options: RenderOptions, nodeRenderOptions: NodeRenderOptions = BLANK) {
if (
this.declarations.every(
declarator =>
declarator.included && (!declarator.id.variable || !declarator.id.variable.exportName)
)
) {
if (areAllDeclarationsIncludedAndNotExported(this.declarations)) {
for (const declarator of this.declarations) {
declarator.render(code, options);
}
Expand All @@ -88,7 +116,7 @@ export default class VariableDeclaration extends NodeBase {
code: MagicString,
options: RenderOptions,
{ start = this.start, end = this.end, isNoStatement }: NodeRenderOptions
) {
): void {
const separatedNodes = getCommaSeparatedNodesWithBoundaries(
this.declarations,
code,
Expand All @@ -108,6 +136,7 @@ export default class VariableDeclaration extends NodeBase {
let separatorString = '',
leadingString,
nextSeparatorString;
const systemPatternExports: Variable[] = [];
for (const { node, start, separator, contentEnd, end } of separatedNodes) {
if (
!node.included ||
Expand All @@ -124,17 +153,16 @@ export default class VariableDeclaration extends NodeBase {
}
isInDeclaration = false;
} else {
if (
options.format === 'system' &&
node.init !== null &&
isIdentifier(node.id) &&
node.id.variable.exportName
) {
code.prependLeft(
code.original.indexOf('=', node.id.end) + 1,
` exports('${node.id.variable.safeExportName || node.id.variable.exportName}',`
);
nextSeparatorString += ')';
if (options.format === 'system' && node.init !== null) {
if (node.id.type !== NodeType.Identifier) {
node.id.addExportedVariables(systemPatternExports);
} else if (node.id.variable.exportName) {
code.prependLeft(
code.original.indexOf('=', node.id.end) + 1,
` exports('${node.id.variable.safeExportName || node.id.variable.exportName}',`
);
nextSeparatorString += ')';
}
}
if (isInDeclaration) {
separatorString += ',';
Expand Down Expand Up @@ -166,7 +194,8 @@ export default class VariableDeclaration extends NodeBase {
lastSeparatorPos,
actualContentEnd,
renderedContentEnd,
!isNoStatement
!isNoStatement,
systemPatternExports
);
} else {
code.remove(start, end);
Expand All @@ -179,8 +208,9 @@ export default class VariableDeclaration extends NodeBase {
lastSeparatorPos: number,
actualContentEnd: number,
renderedContentEnd: number,
addSemicolon: boolean
) {
addSemicolon: boolean,
systemPatternExports: Variable[]
): void {
if (code.original.charCodeAt(this.end - 1) === 59 /*";"*/) {
code.remove(this.end - 1, this.end);
}
Expand All @@ -207,6 +237,8 @@ export default class VariableDeclaration extends NodeBase {
} else {
code.appendLeft(renderedContentEnd, separatorString);
}
return separatorString;
if (systemPatternExports.length > 0) {
renderSystemExports(code, systemPatternExports, renderedContentEnd);
}
}
}
5 changes: 4 additions & 1 deletion src/ast/nodes/shared/Pattern.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
import { WritableEntity } from '../../Entity';
import Variable from '../../variables/Variable';
import { Node } from './Node';

export interface PatternNode extends WritableEntity, Node {}
export interface PatternNode extends WritableEntity, Node {
addExportedVariables(variables: Variable[]): void;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
module.exports = {
description: 'Supports destructuring declarations and assignments for SystemJS',
options: {
output: {
format: 'system'
}
}
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
System.register([], function (exports, module) {
'use strict';
return {
execute: function () {

const {a = 1, ...b} = global1, c = exports('c', global2), {d} = global3; exports({a: a, b: b, d: d});
const [e, ...f] = global4; exports({e: e, f: f});
const {g, x: h = 2, y: {z: i}, a: [j ,k,, l]} = global5; exports({g: g, h: h, i: i, j: j, k: k, l: l});

var m = exports('m', 1);
var {m} = global6; exports('m', m);

}
};
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
export const {a = 1, ...b} = global1, c = global2, {d} = global3;
export const [e, ...f] = global4;
export const {g, x: h = 2, y: {z: i}, a: [j ,k,, l]} = global5;

export var m = 1;
var {m} = global6;

0 comments on commit 27be520

Please sign in to comment.