• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1/* @internal */
2namespace ts {
3
4    // Compound nodes
5
6    export function createEmptyExports(factory: NodeFactory) {
7        return factory.createExportDeclaration(/*decorators*/ undefined, /*modifiers*/ undefined, /*isTypeOnly*/ false, factory.createNamedExports([]), /*moduleSpecifier*/ undefined);
8    }
9
10    export function createMemberAccessForPropertyName(factory: NodeFactory, target: Expression, memberName: PropertyName, location?: TextRange): MemberExpression {
11        if (isComputedPropertyName(memberName)) {
12             return setTextRange(factory.createElementAccessExpression(target, memberName.expression), location);
13        }
14        else {
15            const expression = setTextRange(
16                isIdentifierOrPrivateIdentifier(memberName)
17                    ? factory.createPropertyAccessExpression(target, memberName)
18                    : factory.createElementAccessExpression(target, memberName),
19                memberName
20            );
21            getOrCreateEmitNode(expression).flags |= EmitFlags.NoNestedSourceMaps;
22            return expression;
23        }
24    }
25
26    function createReactNamespace(reactNamespace: string, parent: JsxOpeningLikeElement | JsxOpeningFragment) {
27        // To ensure the emit resolver can properly resolve the namespace, we need to
28        // treat this identifier as if it were a source tree node by clearing the `Synthesized`
29        // flag and setting a parent node.
30        const react = parseNodeFactory.createIdentifier(reactNamespace || "React");
31        // Set the parent that is in parse tree
32        // this makes sure that parent chain is intact for checker to traverse complete scope tree
33        setParent(react, getParseTreeNode(parent));
34        return react;
35    }
36
37    function createJsxFactoryExpressionFromEntityName(factory: NodeFactory, jsxFactory: EntityName, parent: JsxOpeningLikeElement | JsxOpeningFragment): Expression {
38        if (isQualifiedName(jsxFactory)) {
39            const left = createJsxFactoryExpressionFromEntityName(factory, jsxFactory.left, parent);
40            const right = factory.createIdentifier(idText(jsxFactory.right)) as Mutable<Identifier>;
41            right.escapedText = jsxFactory.right.escapedText;
42            return factory.createPropertyAccessExpression(left, right);
43        }
44        else {
45            return createReactNamespace(idText(jsxFactory), parent);
46        }
47    }
48
49    export function createJsxFactoryExpression(factory: NodeFactory, jsxFactoryEntity: EntityName | undefined, reactNamespace: string, parent: JsxOpeningLikeElement | JsxOpeningFragment): Expression {
50        return jsxFactoryEntity ?
51            createJsxFactoryExpressionFromEntityName(factory, jsxFactoryEntity, parent) :
52            factory.createPropertyAccessExpression(
53                createReactNamespace(reactNamespace, parent),
54                "createElement"
55            );
56    }
57
58    function createJsxFragmentFactoryExpression(factory: NodeFactory, jsxFragmentFactoryEntity: EntityName | undefined, reactNamespace: string, parent: JsxOpeningLikeElement | JsxOpeningFragment): Expression {
59        return jsxFragmentFactoryEntity ?
60            createJsxFactoryExpressionFromEntityName(factory, jsxFragmentFactoryEntity, parent) :
61            factory.createPropertyAccessExpression(
62                createReactNamespace(reactNamespace, parent),
63                "Fragment"
64            );
65    }
66
67    export function createExpressionForJsxElement(factory: NodeFactory, callee: Expression, tagName: Expression, props: Expression | undefined, children: readonly Expression[] | undefined, location: TextRange): LeftHandSideExpression {
68        const argumentsList = [tagName];
69        if (props) {
70            argumentsList.push(props);
71        }
72
73        if (children && children.length > 0) {
74            if (!props) {
75                argumentsList.push(factory.createNull());
76            }
77
78            if (children.length > 1) {
79                for (const child of children) {
80                    startOnNewLine(child);
81                    argumentsList.push(child);
82                }
83            }
84            else {
85                argumentsList.push(children[0]);
86            }
87        }
88
89        return setTextRange(
90            factory.createCallExpression(
91                callee,
92                /*typeArguments*/ undefined,
93                argumentsList
94            ),
95            location
96        );
97    }
98
99    export function createExpressionForJsxFragment(factory: NodeFactory, jsxFactoryEntity: EntityName | undefined, jsxFragmentFactoryEntity: EntityName | undefined, reactNamespace: string, children: readonly Expression[], parentElement: JsxOpeningFragment, location: TextRange): LeftHandSideExpression {
100        const tagName = createJsxFragmentFactoryExpression(factory, jsxFragmentFactoryEntity, reactNamespace, parentElement);
101        const argumentsList = [tagName, factory.createNull()];
102
103        if (children && children.length > 0) {
104            if (children.length > 1) {
105                for (const child of children) {
106                    startOnNewLine(child);
107                    argumentsList.push(child);
108                }
109            }
110            else {
111                argumentsList.push(children[0]);
112            }
113        }
114
115        return setTextRange(
116            factory.createCallExpression(
117                createJsxFactoryExpression(factory, jsxFactoryEntity, reactNamespace, parentElement),
118                /*typeArguments*/ undefined,
119                argumentsList
120            ),
121            location
122        );
123    }
124
125    // Utilities
126
127    export function createForOfBindingStatement(factory: NodeFactory, node: ForInitializer, boundValue: Expression): Statement {
128        if (isVariableDeclarationList(node)) {
129            const firstDeclaration = first(node.declarations);
130            const updatedDeclaration = factory.updateVariableDeclaration(
131                firstDeclaration,
132                firstDeclaration.name,
133                /*exclamationToken*/ undefined,
134                /*type*/ undefined,
135                boundValue
136            );
137            return setTextRange(
138                factory.createVariableStatement(
139                    /*modifiers*/ undefined,
140                    factory.updateVariableDeclarationList(node, [updatedDeclaration])
141                ),
142                /*location*/ node
143            );
144        }
145        else {
146            const updatedExpression = setTextRange(factory.createAssignment(node, boundValue), /*location*/ node);
147            return setTextRange(factory.createExpressionStatement(updatedExpression), /*location*/ node);
148        }
149    }
150
151    export function insertLeadingStatement(factory: NodeFactory, dest: Statement, source: Statement) {
152        if (isBlock(dest)) {
153            return factory.updateBlock(dest, setTextRange(factory.createNodeArray([source, ...dest.statements]), dest.statements));
154        }
155        else {
156            return factory.createBlock(factory.createNodeArray([dest, source]), /*multiLine*/ true);
157        }
158    }
159
160    export function createExpressionFromEntityName(factory: NodeFactory, node: EntityName | Expression): Expression {
161        if (isQualifiedName(node)) {
162            const left = createExpressionFromEntityName(factory, node.left);
163            // TODO(rbuckton): Does this need to be parented?
164            const right = setParent(setTextRange(factory.cloneNode(node.right), node.right), node.right.parent);
165            return setTextRange(factory.createPropertyAccessExpression(left, right), node);
166        }
167        else {
168            // TODO(rbuckton): Does this need to be parented?
169            return setParent(setTextRange(factory.cloneNode(node), node), node.parent);
170        }
171    }
172
173    export function createExpressionForPropertyName(factory: NodeFactory, memberName: Exclude<PropertyName, PrivateIdentifier>): Expression {
174        if (isIdentifier(memberName)) {
175            return factory.createStringLiteralFromNode(memberName);
176        }
177        else if (isComputedPropertyName(memberName)) {
178            // TODO(rbuckton): Does this need to be parented?
179            return setParent(setTextRange(factory.cloneNode(memberName.expression), memberName.expression), memberName.expression.parent);
180        }
181        else {
182            // TODO(rbuckton): Does this need to be parented?
183            return setParent(setTextRange(factory.cloneNode(memberName), memberName), memberName.parent);
184        }
185    }
186
187    function createExpressionForAccessorDeclaration(factory: NodeFactory, properties: NodeArray<Declaration>, property: AccessorDeclaration & { readonly name: Exclude<PropertyName, PrivateIdentifier>; }, receiver: Expression, multiLine: boolean) {
188        const { firstAccessor, getAccessor, setAccessor } = getAllAccessorDeclarations(properties, property);
189        if (property === firstAccessor) {
190            return setTextRange(
191                factory.createObjectDefinePropertyCall(
192                    receiver,
193                    createExpressionForPropertyName(factory, property.name),
194                    factory.createPropertyDescriptor({
195                        enumerable: factory.createFalse(),
196                        configurable: true,
197                        get: getAccessor && setTextRange(
198                            setOriginalNode(
199                                factory.createFunctionExpression(
200                                    getAccessor.modifiers,
201                                    /*asteriskToken*/ undefined,
202                                    /*name*/ undefined,
203                                    /*typeParameters*/ undefined,
204                                    getAccessor.parameters,
205                                    /*type*/ undefined,
206                                    getAccessor.body! // TODO: GH#18217
207                                ),
208                                getAccessor
209                            ),
210                            getAccessor
211                        ),
212                        set: setAccessor && setTextRange(
213                            setOriginalNode(
214                                factory.createFunctionExpression(
215                                    setAccessor.modifiers,
216                                    /*asteriskToken*/ undefined,
217                                    /*name*/ undefined,
218                                    /*typeParameters*/ undefined,
219                                    setAccessor.parameters,
220                                    /*type*/ undefined,
221                                    setAccessor.body! // TODO: GH#18217
222                                ),
223                                setAccessor
224                            ),
225                            setAccessor
226                        )
227                    }, !multiLine)
228                ),
229                firstAccessor
230            );
231        }
232
233        return undefined;
234    }
235
236    function createExpressionForPropertyAssignment(factory: NodeFactory, property: PropertyAssignment, receiver: Expression) {
237        return setOriginalNode(
238            setTextRange(
239                factory.createAssignment(
240                    createMemberAccessForPropertyName(factory, receiver, property.name, /*location*/ property.name),
241                    property.initializer
242                ),
243                property
244            ),
245            property
246        );
247    }
248
249    function createExpressionForShorthandPropertyAssignment(factory: NodeFactory, property: ShorthandPropertyAssignment, receiver: Expression) {
250        return setOriginalNode(
251            setTextRange(
252                factory.createAssignment(
253                    createMemberAccessForPropertyName(factory, receiver, property.name, /*location*/ property.name),
254                    factory.cloneNode(property.name)
255                ),
256                /*location*/ property
257            ),
258            /*original*/ property
259        );
260    }
261
262    function createExpressionForMethodDeclaration(factory: NodeFactory, method: MethodDeclaration, receiver: Expression) {
263        return setOriginalNode(
264            setTextRange(
265                factory.createAssignment(
266                    createMemberAccessForPropertyName(factory, receiver, method.name, /*location*/ method.name),
267                    setOriginalNode(
268                        setTextRange(
269                            factory.createFunctionExpression(
270                                method.modifiers,
271                                method.asteriskToken,
272                                /*name*/ undefined,
273                                /*typeParameters*/ undefined,
274                                method.parameters,
275                                /*type*/ undefined,
276                                method.body! // TODO: GH#18217
277                            ),
278                            /*location*/ method
279                        ),
280                        /*original*/ method
281                    )
282                ),
283                /*location*/ method
284            ),
285            /*original*/ method
286        );
287    }
288
289    export function createExpressionForObjectLiteralElementLike(factory: NodeFactory, node: ObjectLiteralExpression, property: ObjectLiteralElementLike, receiver: Expression): Expression | undefined {
290        if (property.name && isPrivateIdentifier(property.name)) {
291            Debug.failBadSyntaxKind(property.name, "Private identifiers are not allowed in object literals.");
292        }
293        switch (property.kind) {
294            case SyntaxKind.GetAccessor:
295            case SyntaxKind.SetAccessor:
296                return createExpressionForAccessorDeclaration(factory, node.properties, property as typeof property & { readonly name: Exclude<PropertyName, PrivateIdentifier> }, receiver, !!node.multiLine);
297            case SyntaxKind.PropertyAssignment:
298                return createExpressionForPropertyAssignment(factory, property, receiver);
299            case SyntaxKind.ShorthandPropertyAssignment:
300                return createExpressionForShorthandPropertyAssignment(factory, property, receiver);
301            case SyntaxKind.MethodDeclaration:
302                return createExpressionForMethodDeclaration(factory, property, receiver);
303        }
304    }
305
306    /**
307     * Gets whether an identifier should only be referred to by its internal name.
308     */
309    export function isInternalName(node: Identifier) {
310        return (getEmitFlags(node) & EmitFlags.InternalName) !== 0;
311    }
312
313    /**
314     * Gets whether an identifier should only be referred to by its local name.
315     */
316    export function isLocalName(node: Identifier) {
317        return (getEmitFlags(node) & EmitFlags.LocalName) !== 0;
318    }
319
320    /**
321     * Gets whether an identifier should only be referred to by its export representation if the
322     * name points to an exported symbol.
323     */
324    export function isExportName(node: Identifier) {
325        return (getEmitFlags(node) & EmitFlags.ExportName) !== 0;
326    }
327
328    function isUseStrictPrologue(node: ExpressionStatement): boolean {
329        return isStringLiteral(node.expression) && node.expression.text === "use strict";
330    }
331
332    export function findUseStrictPrologue(statements: readonly Statement[]): Statement | undefined {
333        for (const statement of statements) {
334            if (isPrologueDirective(statement)) {
335                if (isUseStrictPrologue(statement)) {
336                    return statement;
337                }
338            }
339            else {
340                break;
341            }
342        }
343        return undefined;
344    }
345
346    export function startsWithUseStrict(statements: readonly Statement[]) {
347        const firstStatement = firstOrUndefined(statements);
348        return firstStatement !== undefined
349            && isPrologueDirective(firstStatement)
350            && isUseStrictPrologue(firstStatement);
351    }
352
353    export function isCommaSequence(node: Expression): node is BinaryExpression & {operatorToken: Token<SyntaxKind.CommaToken>} | CommaListExpression {
354        return node.kind === SyntaxKind.BinaryExpression && (<BinaryExpression>node).operatorToken.kind === SyntaxKind.CommaToken ||
355            node.kind === SyntaxKind.CommaListExpression;
356    }
357
358    export function isOuterExpression(node: Node, kinds = OuterExpressionKinds.All): node is OuterExpression {
359        switch (node.kind) {
360            case SyntaxKind.ParenthesizedExpression:
361                return (kinds & OuterExpressionKinds.Parentheses) !== 0;
362            case SyntaxKind.TypeAssertionExpression:
363            case SyntaxKind.AsExpression:
364                return (kinds & OuterExpressionKinds.TypeAssertions) !== 0;
365            case SyntaxKind.NonNullExpression:
366                return (kinds & OuterExpressionKinds.NonNullAssertions) !== 0;
367            case SyntaxKind.PartiallyEmittedExpression:
368                return (kinds & OuterExpressionKinds.PartiallyEmittedExpressions) !== 0;
369        }
370        return false;
371    }
372
373    export function skipOuterExpressions(node: Expression, kinds?: OuterExpressionKinds): Expression;
374    export function skipOuterExpressions(node: Node, kinds?: OuterExpressionKinds): Node;
375    export function skipOuterExpressions(node: Node, kinds = OuterExpressionKinds.All) {
376        while (isOuterExpression(node, kinds)) {
377            node = node.expression;
378        }
379        return node;
380    }
381
382    export function skipAssertions(node: Expression): Expression;
383    export function skipAssertions(node: Node): Node;
384    export function skipAssertions(node: Node): Node {
385        return skipOuterExpressions(node, OuterExpressionKinds.Assertions);
386    }
387
388    export function startOnNewLine<T extends Node>(node: T): T {
389        return setStartsOnNewLine(node, /*newLine*/ true);
390    }
391
392    export function getExternalHelpersModuleName(node: SourceFile) {
393        const parseNode = getOriginalNode(node, isSourceFile);
394        const emitNode = parseNode && parseNode.emitNode;
395        return emitNode && emitNode.externalHelpersModuleName;
396    }
397
398    export function hasRecordedExternalHelpers(sourceFile: SourceFile) {
399        const parseNode = getOriginalNode(sourceFile, isSourceFile);
400        const emitNode = parseNode && parseNode.emitNode;
401        return !!emitNode && (!!emitNode.externalHelpersModuleName || !!emitNode.externalHelpers);
402    }
403
404    export function createExternalHelpersImportDeclarationIfNeeded(nodeFactory: NodeFactory, helperFactory: EmitHelperFactory, sourceFile: SourceFile, compilerOptions: CompilerOptions, hasExportStarsToExportValues?: boolean, hasImportStar?: boolean, hasImportDefault?: boolean) {
405        if (compilerOptions.importHelpers && isEffectiveExternalModule(sourceFile, compilerOptions)) {
406            let namedBindings: NamedImportBindings | undefined;
407            const moduleKind = getEmitModuleKind(compilerOptions);
408            if (moduleKind >= ModuleKind.ES2015 && moduleKind <= ModuleKind.ESNext) {
409                // use named imports
410                const helpers = getEmitHelpers(sourceFile);
411                if (helpers) {
412                    const helperNames: string[] = [];
413                    for (const helper of helpers) {
414                        if (!helper.scoped) {
415                            const importName = (helper as UnscopedEmitHelper).importName;
416                            if (importName) {
417                                pushIfUnique(helperNames, importName);
418                            }
419                        }
420                    }
421                    if (some(helperNames)) {
422                        helperNames.sort(compareStringsCaseSensitive);
423                        // Alias the imports if the names are used somewhere in the file.
424                        // NOTE: We don't need to care about global import collisions as this is a module.
425                        namedBindings = nodeFactory.createNamedImports(
426                            map(helperNames, name => isFileLevelUniqueName(sourceFile, name)
427                                ? nodeFactory.createImportSpecifier(/*propertyName*/ undefined, nodeFactory.createIdentifier(name))
428                                : nodeFactory.createImportSpecifier(nodeFactory.createIdentifier(name), helperFactory.getUnscopedHelperName(name))
429                            )
430                        );
431                        const parseNode = getOriginalNode(sourceFile, isSourceFile);
432                        const emitNode = getOrCreateEmitNode(parseNode);
433                        emitNode.externalHelpers = true;
434                    }
435                }
436            }
437            else {
438                // use a namespace import
439                const externalHelpersModuleName = getOrCreateExternalHelpersModuleNameIfNeeded(nodeFactory, sourceFile, compilerOptions, hasExportStarsToExportValues, hasImportStar || hasImportDefault);
440                if (externalHelpersModuleName) {
441                    namedBindings = nodeFactory.createNamespaceImport(externalHelpersModuleName);
442                }
443            }
444            if (namedBindings) {
445                const externalHelpersImportDeclaration = nodeFactory.createImportDeclaration(
446                    /*decorators*/ undefined,
447                    /*modifiers*/ undefined,
448                    nodeFactory.createImportClause(/*isTypeOnly*/ false, /*name*/ undefined, namedBindings),
449                    nodeFactory.createStringLiteral(externalHelpersModuleNameText)
450                );
451                addEmitFlags(externalHelpersImportDeclaration, EmitFlags.NeverApplyImportHelper);
452                return externalHelpersImportDeclaration;
453            }
454        }
455    }
456
457    export function getOrCreateExternalHelpersModuleNameIfNeeded(factory: NodeFactory, node: SourceFile, compilerOptions: CompilerOptions, hasExportStarsToExportValues?: boolean, hasImportStarOrImportDefault?: boolean) {
458        if (compilerOptions.importHelpers && isEffectiveExternalModule(node, compilerOptions)) {
459            const externalHelpersModuleName = getExternalHelpersModuleName(node);
460            if (externalHelpersModuleName) {
461                return externalHelpersModuleName;
462            }
463
464            const moduleKind = getEmitModuleKind(compilerOptions);
465            let create = (hasExportStarsToExportValues || (compilerOptions.esModuleInterop && hasImportStarOrImportDefault))
466                && moduleKind !== ModuleKind.System
467                && moduleKind < ModuleKind.ES2015;
468            if (!create) {
469                const helpers = getEmitHelpers(node);
470                if (helpers) {
471                    for (const helper of helpers) {
472                        if (!helper.scoped) {
473                            create = true;
474                            break;
475                        }
476                    }
477                }
478            }
479
480            if (create) {
481                const parseNode = getOriginalNode(node, isSourceFile);
482                const emitNode = getOrCreateEmitNode(parseNode);
483                return emitNode.externalHelpersModuleName || (emitNode.externalHelpersModuleName = factory.createUniqueName(externalHelpersModuleNameText));
484            }
485        }
486    }
487
488    /**
489     * Get the name of that target module from an import or export declaration
490     */
491    export function getLocalNameForExternalImport(factory: NodeFactory, node: ImportDeclaration | ExportDeclaration | ImportEqualsDeclaration, sourceFile: SourceFile): Identifier | undefined {
492        const namespaceDeclaration = getNamespaceDeclarationNode(node);
493        if (namespaceDeclaration && !isDefaultImport(node) && !isExportNamespaceAsDefaultDeclaration(node)) {
494            const name = namespaceDeclaration.name;
495            return isGeneratedIdentifier(name) ? name : factory.createIdentifier(getSourceTextOfNodeFromSourceFile(sourceFile, name) || idText(name));
496        }
497        if (node.kind === SyntaxKind.ImportDeclaration && node.importClause) {
498            return factory.getGeneratedNameForNode(node);
499        }
500        if (node.kind === SyntaxKind.ExportDeclaration && node.moduleSpecifier) {
501            return factory.getGeneratedNameForNode(node);
502        }
503        return undefined;
504    }
505
506    /**
507     * Get the name of a target module from an import/export declaration as should be written in the emitted output.
508     * The emitted output name can be different from the input if:
509     *  1. The module has a /// <amd-module name="<new name>" />
510     *  2. --out or --outFile is used, making the name relative to the rootDir
511     *  3- The containing SourceFile has an entry in renamedDependencies for the import as requested by some module loaders (e.g. System).
512     * Otherwise, a new StringLiteral node representing the module name will be returned.
513     */
514    export function getExternalModuleNameLiteral(factory: NodeFactory, importNode: ImportDeclaration | ExportDeclaration | ImportEqualsDeclaration | ImportCall, sourceFile: SourceFile, host: EmitHost, resolver: EmitResolver, compilerOptions: CompilerOptions) {
515        const moduleName = getExternalModuleName(importNode);
516        if (moduleName && isStringLiteral(moduleName)) {
517            return tryGetModuleNameFromDeclaration(importNode, host, factory, resolver, compilerOptions)
518                || tryRenameExternalModule(factory, moduleName, sourceFile)
519                || factory.cloneNode(moduleName);
520        }
521
522        return undefined;
523    }
524
525    /**
526     * Some bundlers (SystemJS builder) sometimes want to rename dependencies.
527     * Here we check if alternative name was provided for a given moduleName and return it if possible.
528     */
529    function tryRenameExternalModule(factory: NodeFactory, moduleName: LiteralExpression, sourceFile: SourceFile) {
530        const rename = sourceFile.renamedDependencies && sourceFile.renamedDependencies.get(moduleName.text);
531        return rename ? factory.createStringLiteral(rename) : undefined;
532    }
533
534    /**
535     * Get the name of a module as should be written in the emitted output.
536     * The emitted output name can be different from the input if:
537     *  1. The module has a /// <amd-module name="<new name>" />
538     *  2. --out or --outFile is used, making the name relative to the rootDir
539     * Otherwise, a new StringLiteral node representing the module name will be returned.
540     */
541    export function tryGetModuleNameFromFile(factory: NodeFactory, file: SourceFile | undefined, host: EmitHost, options: CompilerOptions): StringLiteral | undefined {
542        if (!file) {
543            return undefined;
544        }
545        if (file.moduleName) {
546            return factory.createStringLiteral(file.moduleName);
547        }
548        if (!file.isDeclarationFile && outFile(options)) {
549            return factory.createStringLiteral(getExternalModuleNameFromPath(host, file.fileName));
550        }
551        return undefined;
552    }
553
554    function tryGetModuleNameFromDeclaration(declaration: ImportEqualsDeclaration | ImportDeclaration | ExportDeclaration | ImportCall, host: EmitHost, factory: NodeFactory, resolver: EmitResolver, compilerOptions: CompilerOptions) {
555        return tryGetModuleNameFromFile(factory, resolver.getExternalModuleFileFromDeclaration(declaration), host, compilerOptions);
556    }
557
558    /**
559     * Gets the initializer of an BindingOrAssignmentElement.
560     */
561    export function getInitializerOfBindingOrAssignmentElement(bindingElement: BindingOrAssignmentElement): Expression | undefined {
562        if (isDeclarationBindingElement(bindingElement)) {
563            // `1` in `let { a = 1 } = ...`
564            // `1` in `let { a: b = 1 } = ...`
565            // `1` in `let { a: {b} = 1 } = ...`
566            // `1` in `let { a: [b] = 1 } = ...`
567            // `1` in `let [a = 1] = ...`
568            // `1` in `let [{a} = 1] = ...`
569            // `1` in `let [[a] = 1] = ...`
570            return bindingElement.initializer;
571        }
572
573        if (isPropertyAssignment(bindingElement)) {
574            // `1` in `({ a: b = 1 } = ...)`
575            // `1` in `({ a: {b} = 1 } = ...)`
576            // `1` in `({ a: [b] = 1 } = ...)`
577            const initializer = bindingElement.initializer;
578            return isAssignmentExpression(initializer, /*excludeCompoundAssignment*/ true)
579                ? initializer.right
580                : undefined;
581        }
582
583        if (isShorthandPropertyAssignment(bindingElement)) {
584            // `1` in `({ a = 1 } = ...)`
585            return bindingElement.objectAssignmentInitializer;
586        }
587
588        if (isAssignmentExpression(bindingElement, /*excludeCompoundAssignment*/ true)) {
589            // `1` in `[a = 1] = ...`
590            // `1` in `[{a} = 1] = ...`
591            // `1` in `[[a] = 1] = ...`
592            return bindingElement.right;
593        }
594
595        if (isSpreadElement(bindingElement)) {
596            // Recovery consistent with existing emit.
597            return getInitializerOfBindingOrAssignmentElement(<BindingOrAssignmentElement>bindingElement.expression);
598        }
599    }
600
601    /**
602     * Gets the name of an BindingOrAssignmentElement.
603     */
604    export function getTargetOfBindingOrAssignmentElement(bindingElement: BindingOrAssignmentElement): BindingOrAssignmentElementTarget | undefined {
605        if (isDeclarationBindingElement(bindingElement)) {
606            // `a` in `let { a } = ...`
607            // `a` in `let { a = 1 } = ...`
608            // `b` in `let { a: b } = ...`
609            // `b` in `let { a: b = 1 } = ...`
610            // `a` in `let { ...a } = ...`
611            // `{b}` in `let { a: {b} } = ...`
612            // `{b}` in `let { a: {b} = 1 } = ...`
613            // `[b]` in `let { a: [b] } = ...`
614            // `[b]` in `let { a: [b] = 1 } = ...`
615            // `a` in `let [a] = ...`
616            // `a` in `let [a = 1] = ...`
617            // `a` in `let [...a] = ...`
618            // `{a}` in `let [{a}] = ...`
619            // `{a}` in `let [{a} = 1] = ...`
620            // `[a]` in `let [[a]] = ...`
621            // `[a]` in `let [[a] = 1] = ...`
622            return bindingElement.name;
623        }
624
625        if (isObjectLiteralElementLike(bindingElement)) {
626            switch (bindingElement.kind) {
627                case SyntaxKind.PropertyAssignment:
628                    // `b` in `({ a: b } = ...)`
629                    // `b` in `({ a: b = 1 } = ...)`
630                    // `{b}` in `({ a: {b} } = ...)`
631                    // `{b}` in `({ a: {b} = 1 } = ...)`
632                    // `[b]` in `({ a: [b] } = ...)`
633                    // `[b]` in `({ a: [b] = 1 } = ...)`
634                    // `b.c` in `({ a: b.c } = ...)`
635                    // `b.c` in `({ a: b.c = 1 } = ...)`
636                    // `b[0]` in `({ a: b[0] } = ...)`
637                    // `b[0]` in `({ a: b[0] = 1 } = ...)`
638                    return getTargetOfBindingOrAssignmentElement(<BindingOrAssignmentElement>bindingElement.initializer);
639
640                case SyntaxKind.ShorthandPropertyAssignment:
641                    // `a` in `({ a } = ...)`
642                    // `a` in `({ a = 1 } = ...)`
643                    return bindingElement.name;
644
645                case SyntaxKind.SpreadAssignment:
646                    // `a` in `({ ...a } = ...)`
647                    return getTargetOfBindingOrAssignmentElement(<BindingOrAssignmentElement>bindingElement.expression);
648            }
649
650            // no target
651            return undefined;
652        }
653
654        if (isAssignmentExpression(bindingElement, /*excludeCompoundAssignment*/ true)) {
655            // `a` in `[a = 1] = ...`
656            // `{a}` in `[{a} = 1] = ...`
657            // `[a]` in `[[a] = 1] = ...`
658            // `a.b` in `[a.b = 1] = ...`
659            // `a[0]` in `[a[0] = 1] = ...`
660            return getTargetOfBindingOrAssignmentElement(<BindingOrAssignmentElement>bindingElement.left);
661        }
662
663        if (isSpreadElement(bindingElement)) {
664            // `a` in `[...a] = ...`
665            return getTargetOfBindingOrAssignmentElement(<BindingOrAssignmentElement>bindingElement.expression);
666        }
667
668        // `a` in `[a] = ...`
669        // `{a}` in `[{a}] = ...`
670        // `[a]` in `[[a]] = ...`
671        // `a.b` in `[a.b] = ...`
672        // `a[0]` in `[a[0]] = ...`
673        return bindingElement;
674    }
675
676    /**
677     * Determines whether an BindingOrAssignmentElement is a rest element.
678     */
679    export function getRestIndicatorOfBindingOrAssignmentElement(bindingElement: BindingOrAssignmentElement): BindingOrAssignmentElementRestIndicator | undefined {
680        switch (bindingElement.kind) {
681            case SyntaxKind.Parameter:
682            case SyntaxKind.BindingElement:
683                // `...` in `let [...a] = ...`
684                return bindingElement.dotDotDotToken;
685
686            case SyntaxKind.SpreadElement:
687            case SyntaxKind.SpreadAssignment:
688                // `...` in `[...a] = ...`
689                return bindingElement;
690        }
691
692        return undefined;
693    }
694
695    /**
696     * Gets the property name of a BindingOrAssignmentElement
697     */
698    export function getPropertyNameOfBindingOrAssignmentElement(bindingElement: BindingOrAssignmentElement): Exclude<PropertyName, PrivateIdentifier> | undefined {
699        const propertyName = tryGetPropertyNameOfBindingOrAssignmentElement(bindingElement);
700        Debug.assert(!!propertyName || isSpreadAssignment(bindingElement), "Invalid property name for binding element.");
701        return propertyName;
702    }
703
704    export function tryGetPropertyNameOfBindingOrAssignmentElement(bindingElement: BindingOrAssignmentElement): Exclude<PropertyName, PrivateIdentifier> | undefined {
705        switch (bindingElement.kind) {
706            case SyntaxKind.BindingElement:
707                // `a` in `let { a: b } = ...`
708                // `[a]` in `let { [a]: b } = ...`
709                // `"a"` in `let { "a": b } = ...`
710                // `1` in `let { 1: b } = ...`
711                if (bindingElement.propertyName) {
712                    const propertyName = bindingElement.propertyName;
713                    if (isPrivateIdentifier(propertyName)) {
714                        return Debug.failBadSyntaxKind(propertyName);
715                    }
716                    return isComputedPropertyName(propertyName) && isStringOrNumericLiteral(propertyName.expression)
717                        ? propertyName.expression
718                        : propertyName;
719                }
720
721                break;
722
723            case SyntaxKind.PropertyAssignment:
724                // `a` in `({ a: b } = ...)`
725                // `[a]` in `({ [a]: b } = ...)`
726                // `"a"` in `({ "a": b } = ...)`
727                // `1` in `({ 1: b } = ...)`
728                if (bindingElement.name) {
729                    const propertyName = bindingElement.name;
730                    if (isPrivateIdentifier(propertyName)) {
731                        return Debug.failBadSyntaxKind(propertyName);
732                    }
733                    return isComputedPropertyName(propertyName) && isStringOrNumericLiteral(propertyName.expression)
734                        ? propertyName.expression
735                        : propertyName;
736                }
737
738                break;
739
740            case SyntaxKind.SpreadAssignment:
741                // `a` in `({ ...a } = ...)`
742                if (bindingElement.name && isPrivateIdentifier(bindingElement.name)) {
743                    return Debug.failBadSyntaxKind(bindingElement.name);
744                }
745                return bindingElement.name;
746        }
747
748        const target = getTargetOfBindingOrAssignmentElement(bindingElement);
749        if (target && isPropertyName(target)) {
750            return target;
751        }
752    }
753
754    function isStringOrNumericLiteral(node: Node): node is StringLiteral | NumericLiteral {
755        const kind = node.kind;
756        return kind === SyntaxKind.StringLiteral
757            || kind === SyntaxKind.NumericLiteral;
758    }
759
760    /**
761     * Gets the elements of a BindingOrAssignmentPattern
762     */
763    export function getElementsOfBindingOrAssignmentPattern(name: BindingOrAssignmentPattern): readonly BindingOrAssignmentElement[] {
764        switch (name.kind) {
765            case SyntaxKind.ObjectBindingPattern:
766            case SyntaxKind.ArrayBindingPattern:
767            case SyntaxKind.ArrayLiteralExpression:
768                // `a` in `{a}`
769                // `a` in `[a]`
770                return <readonly BindingOrAssignmentElement[]>name.elements;
771
772            case SyntaxKind.ObjectLiteralExpression:
773                // `a` in `{a}`
774                return <readonly BindingOrAssignmentElement[]>name.properties;
775        }
776    }
777
778    /* @internal */
779    export function getJSDocTypeAliasName(fullName: JSDocNamespaceBody | undefined) {
780        if (fullName) {
781            let rightNode = fullName;
782            while (true) {
783                if (isIdentifier(rightNode) || !rightNode.body) {
784                    return isIdentifier(rightNode) ? rightNode : rightNode.name;
785                }
786                rightNode = rightNode.body;
787            }
788        }
789    }
790
791    export function canHaveModifiers(node: Node): node is HasModifiers {
792        const kind = node.kind;
793        return kind === SyntaxKind.Parameter
794            || kind === SyntaxKind.PropertySignature
795            || kind === SyntaxKind.PropertyDeclaration
796            || kind === SyntaxKind.MethodSignature
797            || kind === SyntaxKind.MethodDeclaration
798            || kind === SyntaxKind.Constructor
799            || kind === SyntaxKind.GetAccessor
800            || kind === SyntaxKind.SetAccessor
801            || kind === SyntaxKind.IndexSignature
802            || kind === SyntaxKind.FunctionExpression
803            || kind === SyntaxKind.ArrowFunction
804            || kind === SyntaxKind.ClassExpression
805            || kind === SyntaxKind.VariableStatement
806            || kind === SyntaxKind.FunctionDeclaration
807            || kind === SyntaxKind.ClassDeclaration
808            || kind === SyntaxKind.StructDeclaration
809            || kind === SyntaxKind.InterfaceDeclaration
810            || kind === SyntaxKind.TypeAliasDeclaration
811            || kind === SyntaxKind.EnumDeclaration
812            || kind === SyntaxKind.ModuleDeclaration
813            || kind === SyntaxKind.ImportEqualsDeclaration
814            || kind === SyntaxKind.ImportDeclaration
815            || kind === SyntaxKind.ExportAssignment
816            || kind === SyntaxKind.ExportDeclaration;
817    }
818
819    /* @internal */
820    export function isExportModifier(node: Modifier): node is ExportKeyword {
821        return node.kind === SyntaxKind.ExportKeyword;
822    }
823
824    /* @internal */
825    export function isAsyncModifier(node: Modifier): node is AsyncKeyword {
826        return node.kind === SyntaxKind.AsyncKeyword;
827    }
828
829    /* @internal */
830    export function isStaticModifier(node: Modifier): node is StaticKeyword {
831        return node.kind === SyntaxKind.StaticKeyword;
832    }
833}
834