Skip to content

Commit b69afd1

Browse files
committed
PR Feedback
1 parent b8ee169 commit b69afd1

File tree

5 files changed

+148
-156
lines changed

5 files changed

+148
-156
lines changed

src/compiler/core.ts

Lines changed: 26 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -740,18 +740,34 @@ namespace ts {
740740
return to;
741741
}
742742

743+
/**
744+
* Gets the actual offset into an array for a relative offset. Negative offsets indicate a
745+
* position offset from the end of the array.
746+
*/
747+
function toOffset(array: any[], offset: number) {
748+
return offset < 0 ? array.length + offset : offset;
749+
}
750+
743751
/**
744752
* Appends a range of value to an array, returning the array.
745753
*
746754
* @param to The array to which `value` is to be appended. If `to` is `undefined`, a new array
747755
* is created if `value` was appended.
748756
* @param from The values to append to the array. If `from` is `undefined`, nothing is
749757
* appended. If an element of `from` is `undefined`, that element is not appended.
758+
* @param start The offset in `from` at which to start copying values.
759+
* @param end The offset in `from` at which to stop copying values (non-inclusive).
750760
*/
751-
export function addRange<T>(to: T[] | undefined, from: T[] | undefined): T[] | undefined {
761+
export function addRange<T>(to: T[] | undefined, from: T[] | undefined, start?: number, end?: number): T[] | undefined {
752762
if (from === undefined) return to;
753-
for (const v of from) {
754-
to = append(to, v);
763+
if (to === undefined) return from.slice(start, end);
764+
start = start === undefined ? 0 : toOffset(from, start);
765+
end = end === undefined ? from.length : toOffset(from, end);
766+
for (let i = start; i < end && i < from.length; i++) {
767+
const v = from[i];
768+
if (v !== undefined) {
769+
to.push(from[i]);
770+
}
755771
}
756772
return to;
757773
}
@@ -781,9 +797,13 @@ namespace ts {
781797
* A negative offset indicates the element should be retrieved from the end of the array.
782798
*/
783799
export function elementAt<T>(array: T[] | undefined, offset: number): T | undefined {
784-
return array && array.length > 0 && (offset < 0 ? ~offset : offset) < array.length
785-
? array[offset < 0 ? array.length + offset : offset]
786-
: undefined;
800+
if (array) {
801+
offset = toOffset(array, offset);
802+
if (offset < array.length) {
803+
return array[offset];
804+
}
805+
}
806+
return undefined;
787807
}
788808

789809
/**

src/compiler/factory.ts

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3736,14 +3736,15 @@ namespace ts {
37363736
All = Parentheses | Assertions | PartiallyEmittedExpressions
37373737
}
37383738

3739-
export type OuterExpression = ParenthesizedExpression | TypeAssertion | AsExpression | PartiallyEmittedExpression;
3739+
export type OuterExpression = ParenthesizedExpression | TypeAssertion | AsExpression | NonNullExpression | PartiallyEmittedExpression;
37403740

37413741
export function isOuterExpression(node: Node, kinds = OuterExpressionKinds.All): node is OuterExpression {
37423742
switch (node.kind) {
37433743
case SyntaxKind.ParenthesizedExpression:
37443744
return (kinds & OuterExpressionKinds.Parentheses) !== 0;
37453745
case SyntaxKind.TypeAssertionExpression:
37463746
case SyntaxKind.AsExpression:
3747+
case SyntaxKind.NonNullExpression:
37473748
return (kinds & OuterExpressionKinds.Assertions) !== 0;
37483749
case SyntaxKind.PartiallyEmittedExpression:
37493750
return (kinds & OuterExpressionKinds.PartiallyEmittedExpressions) !== 0;
@@ -3787,8 +3788,8 @@ namespace ts {
37873788
export function skipAssertions(node: Expression): Expression;
37883789
export function skipAssertions(node: Node): Node;
37893790
export function skipAssertions(node: Node): Node {
3790-
while (isAssertionExpression(node)) {
3791-
node = (<AssertionExpression>node).expression;
3791+
while (isAssertionExpression(node) || node.kind === SyntaxKind.NonNullExpression) {
3792+
node = (<AssertionExpression | NonNullExpression>node).expression;
37923793
}
37933794

37943795
return node;
@@ -3809,6 +3810,7 @@ namespace ts {
38093810
case SyntaxKind.ParenthesizedExpression: return updateParen(outerExpression, expression);
38103811
case SyntaxKind.TypeAssertionExpression: return updateTypeAssertion(outerExpression, outerExpression.type, expression);
38113812
case SyntaxKind.AsExpression: return updateAsExpression(outerExpression, expression, outerExpression.type);
3813+
case SyntaxKind.NonNullExpression: return updateNonNullExpression(outerExpression, expression);
38123814
case SyntaxKind.PartiallyEmittedExpression: return updatePartiallyEmittedExpression(outerExpression, expression);
38133815
}
38143816
}

src/compiler/transformers/es2015.ts

Lines changed: 77 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -339,6 +339,58 @@ namespace ts {
339339
&& !(<ReturnStatement>node).expression;
340340
}
341341

342+
function isClassLikeVariableStatement(node: Node) {
343+
if (!isVariableStatement(node)) return false;
344+
const variable = singleOrUndefined((<VariableStatement>node).declarationList.declarations);
345+
return variable
346+
&& variable.initializer
347+
&& isIdentifier(variable.name)
348+
&& (isClassLike(variable.initializer)
349+
|| (isAssignmentExpression(variable.initializer)
350+
&& isIdentifier(variable.initializer.left)
351+
&& isClassLike(variable.initializer.right)));
352+
}
353+
354+
function isTypeScriptClassWrapper(node: Node) {
355+
const call = tryCast(node, isCallExpression);
356+
if (!call || isParseTreeNode(call) ||
357+
some(call.typeArguments) ||
358+
some(call.arguments)) {
359+
return false;
360+
}
361+
362+
const func = tryCast(skipOuterExpressions(call.expression), isFunctionExpression);
363+
if (!func || isParseTreeNode(func) ||
364+
some(func.typeParameters) ||
365+
some(func.parameters) ||
366+
func.type ||
367+
!func.body) {
368+
return false;
369+
}
370+
371+
const statements = func.body.statements;
372+
if (statements.length < 2) {
373+
return false;
374+
}
375+
376+
const firstStatement = statements[0];
377+
if (isParseTreeNode(firstStatement) ||
378+
!isClassLike(firstStatement) &&
379+
!isClassLikeVariableStatement(firstStatement)) {
380+
return false;
381+
}
382+
383+
const lastStatement = elementAt(statements, -1);
384+
const returnStatement = tryCast(isVariableStatement(lastStatement) ? elementAt(statements, -2) : lastStatement, isReturnStatement);
385+
if (!returnStatement ||
386+
!returnStatement.expression ||
387+
!isIdentifier(skipOuterExpressions(returnStatement.expression))) {
388+
return false;
389+
}
390+
391+
return true;
392+
}
393+
342394
function shouldVisitNode(node: Node): boolean {
343395
return (node.transformFlags & TransformFlags.ContainsES2015) !== 0
344396
|| convertedLoopState !== undefined
@@ -3326,23 +3378,24 @@ namespace ts {
33263378
// var C_1;
33273379
// }())
33283380
//
3329-
const aliasAssignment = isAssignmentExpression(initializer) ? initializer : undefined;
3381+
const aliasAssignment = tryCast(initializer, isAssignmentExpression);
33303382

33313383
// The underlying call (3) is another IIFE that may contain a '_super' argument.
33323384
const call = cast(aliasAssignment ? skipOuterExpressions(aliasAssignment.right) : initializer, isCallExpression);
33333385
const func = cast(skipOuterExpressions(call.expression), isFunctionExpression);
33343386

3335-
// When we extract the statements of the inner IIFE, we exclude the 'return' statement (4)
3336-
// as we already have one that has been introduced by the 'ts' transformer.
3337-
const funcStatements = func.body.statements.slice(0, -1);
3387+
const funcStatements = func.body.statements;
3388+
let classBodyStart = 0;
3389+
let classBodyEnd = -1;
33383390

33393391
const statements: Statement[] = [];
33403392
if (aliasAssignment) {
33413393
// If we have a class alias assignment, we need to move it to the down-level constructor
33423394
// function we generated for the class.
3343-
const hasExtendsCall = isExpressionStatement(funcStatements[0]);
3344-
if (hasExtendsCall) {
3345-
statements.push(funcStatements[0]);
3395+
const extendsCall = tryCast(funcStatements[classBodyStart], isExpressionStatement);
3396+
if (extendsCall) {
3397+
statements.push(extendsCall);
3398+
classBodyStart++;
33463399
}
33473400

33483401
// We reuse the comment and source-map positions from the original variable statement
@@ -3359,25 +3412,37 @@ namespace ts {
33593412
updateBinary(aliasAssignment,
33603413
aliasAssignment.left,
33613414
convertFunctionDeclarationToExpression(
3362-
cast(funcStatements[hasExtendsCall ? 1 : 0], isFunctionDeclaration)
3415+
cast(funcStatements[classBodyStart], isFunctionDeclaration)
33633416
)
33643417
)
33653418
)
33663419
])
33673420
)
33683421
);
3422+
classBodyStart++;
3423+
}
33693424

3370-
addRange(statements, funcStatements.slice(hasExtendsCall ? 2 : 1));
3425+
// Find the trailing 'return' statement (4)
3426+
while (!isReturnStatement(elementAt(funcStatements, classBodyEnd))) {
3427+
classBodyEnd--;
33713428
}
3372-
else {
3373-
addRange(statements, funcStatements);
3429+
3430+
// When we extract the statements of the inner IIFE, we exclude the 'return' statement (4)
3431+
// as we already have one that has been introduced by the 'ts' transformer.
3432+
addRange(statements, funcStatements, classBodyStart, classBodyEnd);
3433+
3434+
if (classBodyEnd < -1) {
3435+
// If there were any hoisted declarations following the return statement, we should
3436+
// append them.
3437+
addRange(statements, funcStatements, classBodyEnd + 1);
33743438
}
33753439

3440+
// Add the remaining statements of the outer wrapper.
33763441
addRange(statements, remainingStatements);
33773442

33783443
// The 'es2015' class transform may add an end-of-declaration marker. If so we will add it
33793444
// after the remaining statements from the 'ts' transformer.
3380-
addRange(statements, classStatements.slice(1));
3445+
addRange(statements, classStatements, /*start*/ 1);
33813446

33823447
// Recreate any outer parentheses or partially-emitted expressions to preserve source map
33833448
// and comment locations.
@@ -3952,7 +4017,6 @@ namespace ts {
39524017
return false;
39534018
}
39544019
if (isClassElement(currentNode) && currentNode.parent === declaration) {
3955-
// we are in the class body, but we treat static fields as outside of the class body
39564020
return true;
39574021
}
39584022
currentNode = currentNode.parent;

src/compiler/transformers/ts.ts

Lines changed: 13 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -19,10 +19,11 @@ namespace ts {
1919
}
2020

2121
const enum ClassFacts {
22+
None = 0,
2223
HasStaticInitializedProperties = 1 << 0,
2324
HasConstructorDecorators = 1 << 1,
2425
HasMemberDecorators = 1 << 2,
25-
IsNamespaceExport = 1 << 3,
26+
IsExportOfNamespace = 1 << 3,
2627
IsNamedExternalExport = 1 << 4,
2728
IsDefaultExternalExport = 1 << 5,
2829
HasExtendsClause = 1 << 6,
@@ -31,7 +32,7 @@ namespace ts {
3132
HasAnyDecorators = HasConstructorDecorators | HasMemberDecorators,
3233
NeedsName = HasStaticInitializedProperties | HasMemberDecorators,
3334
MayNeedImmediatelyInvokedFunctionExpression = HasAnyDecorators | HasStaticInitializedProperties,
34-
IsExported = IsNamespaceExport | IsDefaultExternalExport | IsNamedExternalExport,
35+
IsExported = IsExportOfNamespace | IsDefaultExternalExport | IsNamedExternalExport,
3536
}
3637

3738
export function transformTypeScript(context: TransformationContext) {
@@ -519,14 +520,14 @@ namespace ts {
519520
}
520521

521522
function getClassFacts(node: ClassDeclaration, staticProperties: PropertyDeclaration[]) {
522-
let facts: ClassFacts = 0;
523+
let facts = ClassFacts.None;
523524
if (some(staticProperties)) facts |= ClassFacts.HasStaticInitializedProperties;
524525
if (getClassExtendsHeritageClauseElement(node)) facts |= ClassFacts.HasExtendsClause;
525526
if (shouldEmitDecorateCallForClass(node)) facts |= ClassFacts.HasConstructorDecorators;
526527
if (childIsDecorated(node)) facts |= ClassFacts.HasMemberDecorators;
527-
if (isNamespaceExport(node)) facts |= ClassFacts.IsNamespaceExport;
528-
if ((facts & ClassFacts.IsExported) === 0 && isDefaultExternalModuleExport(node)) facts |= ClassFacts.IsDefaultExternalExport;
529-
if ((facts & ClassFacts.IsExported) === 0 && isNamedExternalModuleExport(node)) facts |= ClassFacts.IsNamedExternalExport;
528+
if (isExportOfNamespace(node)) facts |= ClassFacts.IsExportOfNamespace;
529+
else if (isDefaultExternalModuleExport(node)) facts |= ClassFacts.IsDefaultExternalExport;
530+
else if (isNamedExternalModuleExport(node)) facts |= ClassFacts.IsNamedExternalExport;
530531
if (languageVersion <= ScriptTarget.ES5 && (facts & ClassFacts.MayNeedImmediatelyInvokedFunctionExpression)) facts |= ClassFacts.UseImmediatelyInvokedFunctionExpression;
531532
return facts;
532533
}
@@ -609,7 +610,7 @@ namespace ts {
609610
// If the class is exported as part of a TypeScript namespace, emit the namespace export.
610611
// Otherwise, if the class was exported at the top level and was decorated, emit an export
611612
// declaration or export default for the class.
612-
if (facts & ClassFacts.IsNamespaceExport) {
613+
if (facts & ClassFacts.IsExportOfNamespace) {
613614
addExportMemberAssignment(statements, node);
614615
}
615616
else if (facts & ClassFacts.UseImmediatelyInvokedFunctionExpression || facts & ClassFacts.HasConstructorDecorators) {
@@ -672,11 +673,6 @@ namespace ts {
672673
/**
673674
* Transforms a decorated class declaration and appends the resulting statements. If
674675
* the class requires an alias to avoid issues with double-binding, the alias is returned.
675-
*
676-
* @param statements A statement list to which to add the declaration.
677-
* @param node A ClassDeclaration node.
678-
* @param name The name of the class.
679-
* @param facts Precomputed facts about the clas.
680676
*/
681677
function createClassDeclarationHeadWithDecorators(node: ClassDeclaration, name: Identifier, facts: ClassFacts) {
682678
// When we emit an ES6 class that has a class decorator, we must tailor the
@@ -2223,7 +2219,7 @@ namespace ts {
22232219
/*type*/ undefined,
22242220
visitFunctionBody(node.body, visitor, context) || createBlock([])
22252221
);
2226-
if (isNamespaceExport(node)) {
2222+
if (isExportOfNamespace(node)) {
22272223
const statements: Statement[] = [updated];
22282224
addExportMemberAssignment(statements, node);
22292225
return statements;
@@ -2316,7 +2312,7 @@ namespace ts {
23162312
* - The node is exported from a TypeScript namespace.
23172313
*/
23182314
function visitVariableStatement(node: VariableStatement): Statement {
2319-
if (isNamespaceExport(node)) {
2315+
if (isExportOfNamespace(node)) {
23202316
const variables = getInitializedVariables(node.declarationList);
23212317
if (variables.length === 0) {
23222318
// elide statement if there are no initialized variables.
@@ -2620,7 +2616,7 @@ namespace ts {
26202616
* or `exports.x`).
26212617
*/
26222618
function hasNamespaceQualifiedExportName(node: Node) {
2623-
return isNamespaceExport(node)
2619+
return isExportOfNamespace(node)
26242620
|| (isExternalModuleExport(node)
26252621
&& moduleKind !== ModuleKind.ES2015
26262622
&& moduleKind !== ModuleKind.System);
@@ -3062,7 +3058,7 @@ namespace ts {
30623058
const moduleReference = createExpressionFromEntityName(<EntityName>node.moduleReference);
30633059
setEmitFlags(moduleReference, EmitFlags.NoComments | EmitFlags.NoNestedComments);
30643060

3065-
if (isNamedExternalModuleExport(node) || !isNamespaceExport(node)) {
3061+
if (isNamedExternalModuleExport(node) || !isExportOfNamespace(node)) {
30663062
// export var ${name} = ${moduleReference};
30673063
// var ${name} = ${moduleReference};
30683064
return setOriginalNode(
@@ -3103,7 +3099,7 @@ namespace ts {
31033099
*
31043100
* @param node The node to test.
31053101
*/
3106-
function isNamespaceExport(node: Node) {
3102+
function isExportOfNamespace(node: Node) {
31073103
return currentNamespace !== undefined && hasModifier(node, ModifierFlags.Export);
31083104
}
31093105

0 commit comments

Comments
 (0)