diff --git a/src/compiler/transformers/es6.ts b/src/compiler/transformers/es6.ts index 613ee5031f3..84f58308e13 100644 --- a/src/compiler/transformers/es6.ts +++ b/src/compiler/transformers/es6.ts @@ -659,7 +659,7 @@ namespace ts { ); } - function visitVariableDeclaration(node: VariableDeclaration): OneOrMore { + function visitVariableDeclaration(node: VariableDeclaration): OneOrMany { const name = node.name; if (isBindingPattern(name)) { return createNodeArrayNode( @@ -1375,7 +1375,7 @@ namespace ts { const clone = cloneNode(node, node, node.flags, /*parent*/ undefined, node); const statements: Statement[] = []; startLexicalEnvironment(); - let statementOffset = addPrologueDirectives(statements, node.statements); + let statementOffset = copyPrologueDirectives(node.statements, statements); addCaptureThisForNodeIfNeeded(statements, node); addNodes(statements, visitNodes(node.statements, visitor, isStatement, statementOffset)); addNodes(statements, endLexicalEnvironment()); @@ -1383,21 +1383,6 @@ namespace ts { return clone; } - function addPrologueDirectives(to: Statement[], from: NodeArray): number { - for (let i = 0; i < from.length; ++i) { - if (isPrologueDirective(from[i])) { - addNode(to, from[i]); - } - else { - return i; - } - } - - return from.length; - } - - var inEmit: boolean; - function onBeforeEmitNode(node: Node) { previousOnBeforeEmitNode(node); diff --git a/src/compiler/transformers/module/es6.ts b/src/compiler/transformers/module/es6.ts index b49d0a5c83b..63b147b6e60 100644 --- a/src/compiler/transformers/module/es6.ts +++ b/src/compiler/transformers/module/es6.ts @@ -7,10 +7,6 @@ namespace ts { return transformSourceFile; function transformSourceFile(node: SourceFile) { - return visitEachChild(node, visitor, context); - } - - function visitor(node: Node): Node { return node; } } diff --git a/src/compiler/transformers/module/module.ts b/src/compiler/transformers/module/module.ts index 61c4a2cd880..c515a50ec8d 100644 --- a/src/compiler/transformers/module/module.ts +++ b/src/compiler/transformers/module/module.ts @@ -5,14 +5,704 @@ namespace ts { // TODO(rbuckton): CommonJS/AMD/UMD transformer export function transformModule(context: TransformationContext) { + const transformModuleDelegates: Map<(node: SourceFile) => Statement[]> = { + [ModuleKind.None]: transformCommonJSModule, + [ModuleKind.CommonJS]: transformCommonJSModule, + [ModuleKind.AMD]: transformAMDModule, + [ModuleKind.UMD]: transformUMDModule, + }; + + const { + getGeneratedNameForNode, + startLexicalEnvironment, + endLexicalEnvironment, + hoistVariableDeclaration, + setNodeEmitFlags + } = context; + + const compilerOptions = context.getCompilerOptions(); + const resolver = context.getEmitResolver(); + const languageVersion = getEmitScriptTarget(compilerOptions); + const moduleKind = getEmitModuleKind(compilerOptions); + const previousExpressionSubstitution = context.expressionSubstitution; + context.enableExpressionSubstitution(SyntaxKind.Identifier); + context.expressionSubstitution = substituteExpression; + + let currentSourceFile: SourceFile; + let externalImports: (ImportDeclaration | ImportEqualsDeclaration | ExportDeclaration)[]; + let exportSpecifiers: Map; + let exportEquals: ExportAssignment; + let hasExportStars: boolean; + return transformSourceFile; function transformSourceFile(node: SourceFile) { - return visitEachChild(node, visitor, context); - } + if (isExternalModule(node) || compilerOptions.isolatedModules) { + currentSourceFile = node; + + // collect information about the external module + ({ externalImports, exportSpecifiers, exportEquals, hasExportStars } = collectExternalModuleInfo(node, resolver)); + + const moduleTransformer = transformModuleDelegates[moduleKind]; + const statements = moduleTransformer(node); + const updated = cloneNode(node, node, node.flags, /*parent*/ undefined, node); + updated.statements = createNodeArray(statements, node.statements); + + if (hasExportStars && moduleTransformer === transformCommonJSModule) { + setNodeEmitFlags(updated, NodeEmitFlags.EmitExportStar); + } + + currentSourceFile = undefined; + externalImports = undefined; + exportSpecifiers = undefined; + exportEquals = undefined; + hasExportStars = false; + return updated; + } - function visitor(node: Node): Node { return node; } + + function transformCommonJSModule(node: SourceFile) { + const statements: Statement[] = []; + startLexicalEnvironment(); + + const statementOffset = copyPrologueDirectives(node.statements, statements); + addNodes(statements, visitNodes(node.statements, visitor, isStatement, statementOffset)); + addNodes(statements, endLexicalEnvironment()); + addNode(statements, tryCreateExportEquals(/*emitAsReturn*/ false)); + + return statements; + } + + function transformAMDModule(node: SourceFile) { + const define = createIdentifier("define"); + const moduleName = node.moduleName ? createLiteral(node.moduleName) : undefined; + return transformAsynchronousModule(node, define, moduleName, /*includeNonAmdDependencies*/ true); + } + + function transformUMDModule(node: SourceFile) { + const define = createIdentifier("define"); + setNodeEmitFlags(define, NodeEmitFlags.UMDDefine); + return transformAsynchronousModule(node, define, /*moduleName*/ undefined, /*includeNonAmdDependencies*/ false); + } + + function transformAsynchronousModule(node: SourceFile, define: Expression, moduleName: Expression, includeNonAmdDependencies: boolean) { + const statements: Statement[] = []; + startLexicalEnvironment(); + + const statementOffset = copyPrologueDirectives(node.statements, statements); + + // An AMD define function has the following shape: + // define(id?, dependencies?, factory); + // + // This has the shape of + // define(name, ["module1", "module2"], function (module1Alias) { + // The location of the alias in the parameter list in the factory function needs to + // match the position of the module name in the dependency list. + // + // To ensure this is true in cases of modules with no aliases, e.g.: + // `import "module"` or `` + // we need to add modules without alias names to the end of the dependencies list + + const defineArguments: Expression[] = []; + const unaliasedModuleNames: Expression[] = []; + const aliasedModuleNames: Expression[] = [createLiteral("require"), createLiteral("exports") ]; + const importAliasNames = [createParameter("require"), createParameter("exports")]; + + for (const amdDependency of node.amdDependencies) { + if (amdDependency.name) { + aliasedModuleNames.push(createLiteral(amdDependency.name)); + importAliasNames.push(createParameter(createIdentifier(amdDependency.name))); + } + else { + unaliasedModuleNames.push(createLiteral(amdDependency.path)); + } + } + + for (const importNode of externalImports) { + // Find the name of the external module + const externalModuleName = getExternalModuleNameLiteral(importNode); + // Find the name of the module alias, if there is one + const importAliasName = getLocalNameForExternalImport(importNode); + if (includeNonAmdDependencies && importAliasName) { + aliasedModuleNames.push(externalModuleName); + importAliasNames.push(createParameter(importAliasName)); + } + else { + unaliasedModuleNames.push(externalModuleName); + } + } + + // Add the module name. + addNode(defineArguments, moduleName); + + // Create the import names array. + addNode(defineArguments, createArrayLiteral(concatenate(aliasedModuleNames, unaliasedModuleNames))); + + // Create the body of the module. + const moduleBodyStatements: Statement[] = []; + + // Start the lexical environment for the module body. + startLexicalEnvironment(); + + // Pipe each statement of the source file through a visitor and out to the module body + addNodes(moduleBodyStatements, visitNodes(node.statements, visitor, isStatement, statementOffset)); + + // End the lexical environment for the module body. + addNodes(moduleBodyStatements, endLexicalEnvironment()); + + // Append the 'export =' statement if provided. + addNode(moduleBodyStatements, tryCreateExportEquals(/*emitAsReturn*/ true)); + + // Create the function for the module body. + const moduleBody = setMultiLine(createBlock(moduleBodyStatements), true); + + if (hasExportStars) { + setNodeEmitFlags(moduleBody, NodeEmitFlags.EmitExportStar); + } + + addNode(defineArguments, + createFunctionExpression( + /*asteriskToken*/ undefined, + /*name*/ undefined, + importAliasNames, + moduleBody + ) + ); + + addNode(statements, + createStatement( + createCall( + define, + defineArguments + ) + ) + ); + + return statements; + } + + function visitor(node: Node) { + switch (node.kind) { + case SyntaxKind.ImportDeclaration: + return visitImportDeclaration(node); + case SyntaxKind.ImportEqualsDeclaration: + return visitImportEqualsDeclaration(node); + case SyntaxKind.ExportDeclaration: + return visitExportDeclaration(node); + case SyntaxKind.ExportAssignment: + return visitExportAssignment(node); + case SyntaxKind.VariableStatement: + return visitVariableStatement(node); + case SyntaxKind.FunctionDeclaration: + return visitFunctionDeclaration(node); + case SyntaxKind.ClassDeclaration: + return visitClassDeclaration(node); + default: + return node; + } + } + + function visitImportDeclaration(node: ImportDeclaration): OneOrMany { + if (contains(externalImports, node)) { + const statements: Statement[] = []; + const namespaceDeclaration = getNamespaceDeclarationNode(node); + if (moduleKind !== ModuleKind.AMD) { + if (!node.importClause) { + // import "mod"; + addNode(statements, + createStatement( + createRequireCall(node), + /*location*/ node + ) + ); + } + else { + const variables: VariableDeclaration[] = []; + if (namespaceDeclaration && !isDefaultImport(node)) { + // import * as n from "mod"; + addNode(variables, + createVariableDeclaration( + getSynthesizedNode(namespaceDeclaration.name), + createRequireCall(node) + ) + ); + } + else { + // import d from "mod"; + // import { x, y } from "mod"; + // import d, { x, y } from "mod"; + // import d, * as n from "mod"; + addNode(variables, + createVariableDeclaration( + getGeneratedNameForNode(node), + createRequireCall(node) + ) + ); + + if (namespaceDeclaration && isDefaultImport(node)) { + addNode(variables, + createVariableDeclaration( + getSynthesizedNode(namespaceDeclaration.name), + getGeneratedNameForNode(node) + ) + ); + } + } + + addNode(statements, + createVariableStatement( + /*modifiers*/ undefined, + createVariableDeclarationList(variables), + /*location*/ node + ) + ); + } + } + else if (namespaceDeclaration && isDefaultImport(node)) { + // import d, * as n from "mod"; + addNode(statements, + createVariableStatement( + /*modifiers*/ undefined, + createVariableDeclarationList([ + createVariableDeclaration( + getSynthesizedNode(namespaceDeclaration.name), + getGeneratedNameForNode(node), + /*location*/ node + ) + ]) + ) + ); + } + + addExportImportAssignments(statements, node); + return createNodeArrayNode(statements); + } + + return undefined; + } + + function visitImportEqualsDeclaration(node: ImportEqualsDeclaration): OneOrMany { + if (contains(externalImports, node)) { + const statements: Statement[] = []; + if (moduleKind !== ModuleKind.AMD) { + if (node.flags & NodeFlags.Export) { + addNode(statements, + createStatement( + createExportAssignment( + node.name, + createRequireCall(node) + ), + /*location*/ node + ) + ); + } + else { + addNode(statements, + createVariableStatement( + /*modifiers*/ undefined, + createVariableDeclarationList([ + createVariableDeclaration( + getSynthesizedNode(node.name), + createRequireCall(node), + /*location*/ node + ) + ]) + ) + ); + } + } + else { + if (node.flags & NodeFlags.Export) { + addNode(statements, + createStatement( + createExportAssignment(node.name, node.name), + /*location*/ node + ) + ); + } + } + + addExportImportAssignments(statements, node); + return createNodeArrayNode(statements); + } + + return undefined; + } + + function visitExportDeclaration(node: ExportDeclaration): OneOrMany { + if (contains(externalImports, node)) { + let generatedName = getGeneratedNameForNode(node); + if (node.exportClause) { + const statements: Statement[] = []; + // export { x, y } from "mod"; + if (moduleKind !== ModuleKind.AMD) { + addNode(statements, + createVariableStatement( + /*modifiers*/ undefined, + createVariableDeclarationList([ + createVariableDeclaration( + generatedName, + createRequireCall(node), + /*location*/ node + ) + ]) + ) + ); + } + for (const specifier of node.exportClause.elements) { + if (resolver.isValueAliasDeclaration(specifier)) { + const exportedValue = createPropertyAccess( + generatedName, + specifier.propertyName || specifier.name + ); + addNode(statements, + createStatement( + createExportAssignment(specifier.name, exportedValue), + /*location*/ specifier + ) + ); + } + } + + return createNodeArrayNode(statements); + } + else { + // export * from "mod"; + return createStatement( + createCall( + createIdentifier("__export"), + [ + moduleKind !== ModuleKind.AMD + ? createRequireCall(node) + : generatedName + ] + ), + /*location*/ node + ); + } + } + + return undefined; + } + + function visitExportAssignment(node: ExportAssignment): OneOrMany { + if (!node.isExportEquals && resolver.isValueAliasDeclaration(node)) { + const statements: Statement[] = []; + addExportDefault(statements, node.expression, /*location*/ node); + return createNodeArrayNode(statements); + } + + return undefined; + } + + function addExportDefault(statements: Statement[], expression: Expression, location: TextRange): void { + addNode(statements, tryCreateExportDefaultCompat()); + addNode(statements, + createStatement( + createExportAssignment( + createIdentifier("default"), + expression + ), + location + ) + ); + } + + function tryCreateExportDefaultCompat(): Statement { + const original = getOriginalNode(currentSourceFile); + Debug.assert(original.kind === SyntaxKind.SourceFile); + + if (!(original).symbol.exports["___esModule"]) { + if (languageVersion === ScriptTarget.ES3) { + return createStatement( + createExportAssignment( + createIdentifier("__esModule"), + createLiteral(true) + ) + ); + } + else { + return createStatement( + createObjectDefineProperty( + createIdentifier("exports"), + createLiteral("_esModule"), + { + value: createLiteral(true) + } + ) + ); + } + } + } + + function addExportImportAssignments(statements: Statement[], node: Node) { + const names = reduceEachChild(node, collectExportMembers, []); + for (const name of names) { + addExportMemberAssignments(statements, name); + } + } + + function collectExportMembers(names: Identifier[], node: Node): Identifier[] { + if (isAliasSymbolDeclaration(node) && resolver.isValueAliasDeclaration(node) && isDeclaration(node)) { + const name = node.name; + if (isIdentifier(name)) { + names.push(name); + } + } + + return reduceEachChild(node, collectExportMembers, names); + } + + function addExportMemberAssignments(statements: Statement[], name: Identifier): void { + if (!exportEquals && exportSpecifiers && hasProperty(exportSpecifiers, name.text)) { + for (const specifier of exportSpecifiers[name.text]) { + addNode(statements, + createStatement( + createExportAssignment(specifier.name, name), + /*location*/ specifier.name + ) + ); + } + } + } + + function visitVariableStatement(node: VariableStatement): OneOrMany { + const variables = getInitializedVariables(node.declarationList); + if (variables.length === 0) { + // elide statement if there are no initialized variables + return undefined; + } + + return createStatement( + inlineExpressions( + map(variables, transformInitializedVariable) + ) + ); + } + + function transformInitializedVariable(node: VariableDeclaration): Expression { + const name = node.name; + if (isBindingPattern(name)) { + return flattenVariableDestructuringToExpression( + node, + hoistVariableDeclaration, + getModuleMemberName, + visitor + ); + } + else { + return createAssignment( + getModuleMemberName(name), + visitNode(node.initializer, visitor, isExpression) + ); + } + } + + function getModuleMemberName(name: Identifier) { + return createPropertyAccess( + createIdentifier("exports"), + name, + /*location*/ name + ); + } + + function visitFunctionDeclaration(node: FunctionDeclaration): OneOrMany { + const statements: Statement[] = []; + if (node.name) { + if (node.flags & NodeFlags.Export) { + addNode(statements, + createFunctionDeclaration( + /*modifiers*/ undefined, + /*asteriskToken*/ undefined, + node.name, + node.parameters, + node.body, + /*location*/ node + ) + ); + + if (node.flags & NodeFlags.Default) { + addExportDefault(statements, getGeneratedNameForNode(node.name), /*location*/ node); + } + } + else { + addNode(statements, node); + } + + addExportMemberAssignments(statements, node.name); + } + else { + Debug.assert((node.flags & NodeFlags.Default) !== 0); + addExportDefault(statements, + createFunctionExpression( + /*asteriskToken*/ undefined, + /*name*/ undefined, + node.parameters, + node.body, + /*location*/ node + ), + /*location*/ node + ); + } + return createNodeArrayNode(statements); + } + + function visitClassDeclaration(node: ClassDeclaration): OneOrMany { + const statements: Statement[] = []; + if (node.name) { + if (node.flags & NodeFlags.Export) { + addNode(statements, + createClassDeclaration( + /*modifiers*/ undefined, + node.name, + node.heritageClauses, + node.members, + /*location*/ node + ) + ); + + if (node.flags & NodeFlags.Default) { + addExportDefault(statements, getSynthesizedNode(node.name), /*location*/ node); + } + } + else { + addNode(statements, node); + } + + addExportMemberAssignments(statements, node.name); + } + else { + Debug.assert((node.flags & NodeFlags.Default) !== 0); + addExportDefault(statements, + createClassExpression( + /*name*/ undefined, + node.heritageClauses, + node.members, + /*location*/ node + ), + /*location*/ node + ); + } + + return createNodeArrayNode(statements); + } + + function substituteExpression(node: Expression) { + node = previousExpressionSubstitution(node); + if (isIdentifier(node)) { + return substituteExpressionIdentifier(node); + } + + return node; + } + + function substituteExpressionIdentifier(node: Identifier): Expression { + const container = resolver.getReferencedExportContainer(node); + if (container && container.kind === SyntaxKind.SourceFile) { + return createPropertyAccess( + createIdentifier("exports"), + getSynthesizedNode(node), + /*location*/ node + ); + } + + return node; + } + + function tryCreateExportEquals(emitAsReturn: boolean) { + if (exportEquals && resolver.isValueAliasDeclaration(exportEquals)) { + if (emitAsReturn) { + return createReturn(exportEquals.expression); + } + else { + return createStatement( + createAssignment( + createPropertyAccess( + createIdentifier("module"), + "exports" + ), + exportEquals.expression + ) + ); + } + } + return undefined; + } + + function getExternalModuleNameLiteral(importNode: ImportDeclaration | ExportDeclaration | ImportEqualsDeclaration) { + const moduleName = getExternalModuleName(importNode); + if (moduleName.kind === SyntaxKind.StringLiteral) { + return tryRenameExternalModule(moduleName) + || getSynthesizedNode(moduleName); + } + + return undefined; + } + + /** + * Some bundlers (SystemJS builder) sometimes want to rename dependencies. + * Here we check if alternative name was provided for a given moduleName and return it if possible. + */ + function tryRenameExternalModule(moduleName: LiteralExpression) { + if (currentSourceFile.renamedDependencies && hasProperty(currentSourceFile.renamedDependencies, moduleName.text)) { + return createLiteral(currentSourceFile.renamedDependencies[moduleName.text]); + } + return undefined; + } + + function getLocalNameForExternalImport(node: ImportDeclaration | ExportDeclaration | ImportEqualsDeclaration): Identifier { + let namespaceDeclaration = getNamespaceDeclarationNode(node); + if (namespaceDeclaration && !isDefaultImport(node)) { + return createIdentifier(getSourceTextOfNodeFromSourceFile(currentSourceFile, namespaceDeclaration.name)); + } + if (node.kind === SyntaxKind.ImportDeclaration && (node).importClause) { + return getGeneratedNameForNode(node); + } + if (node.kind === SyntaxKind.ExportDeclaration && (node).moduleSpecifier) { + return getGeneratedNameForNode(node); + } + } + + function getNamespaceDeclarationNode(node: ImportDeclaration | ImportEqualsDeclaration | ExportDeclaration) { + if (node.kind === SyntaxKind.ImportEqualsDeclaration) { + return node; + } + + let importClause = (node).importClause; + if (importClause && importClause.namedBindings && importClause.namedBindings.kind === SyntaxKind.NamespaceImport) { + return importClause.namedBindings; + } + } + + function isDefaultImport(node: ImportDeclaration | ImportEqualsDeclaration | ExportDeclaration) { + return node.kind === SyntaxKind.ImportDeclaration + && (node).importClause + && !!(node).importClause.name; + } + + function createRequireCall(importNode: ImportDeclaration | ImportEqualsDeclaration | ExportDeclaration) { + const moduleName = getExternalModuleNameLiteral(importNode); + return createCall( + createIdentifier("require"), + moduleName ? [moduleName] : [] + ); + } + + function createExportAssignment(name: Identifier, value: Expression) { + let exports = createIdentifier("exports"); + let exportMember: LeftHandSideExpression; + if (name.originalKeywordKind && languageVersion === ScriptTarget.ES3) { + let exportName = createLiteral(name.text); + exportMember = createElementAccess(exports, exportName); + } + else { + let exportName = getSynthesizedNode(name); + exportMember = createPropertyAccess(exports, exportName); + } + + return createAssignment(exportMember, value); + } } -} \ No newline at end of file +} diff --git a/src/compiler/transformers/ts.ts b/src/compiler/transformers/ts.ts index f7e7e128746..736b0406bed 100644 --- a/src/compiler/transformers/ts.ts +++ b/src/compiler/transformers/ts.ts @@ -1795,7 +1795,7 @@ namespace ts { * * @param node The function node. */ - function visitFunctionDeclaration(node: FunctionDeclaration): OneOrMore { + function visitFunctionDeclaration(node: FunctionDeclaration): OneOrMany { if (shouldElideFunctionLikeDeclaration(node)) { return undefined; } @@ -2081,14 +2081,6 @@ namespace ts { ); } - function getInitializedVariables(node: VariableDeclarationList) { - return filter(node.declarations, isInitializedVariable); - } - - function isInitializedVariable(node: VariableDeclaration) { - return node.initializer !== undefined; - } - function transformInitializedVariable(node: VariableDeclaration): Expression { const name = node.name; if (isBindingPattern(name)) { @@ -2446,7 +2438,7 @@ namespace ts { * * @param node The import equals declaration node. */ - function visitImportEqualsDeclaration(node: ImportEqualsDeclaration): OneOrMore { + function visitImportEqualsDeclaration(node: ImportEqualsDeclaration): OneOrMany { Debug.assert(!isExternalModuleImportEqualsDeclaration(node)); if (shouldElideImportEqualsDeclaration(node)) { return undefined; diff --git a/src/compiler/utilities.ts b/src/compiler/utilities.ts index aa7d0affab6..aa3c860a981 100644 --- a/src/compiler/utilities.ts +++ b/src/compiler/utilities.ts @@ -2847,6 +2847,90 @@ namespace ts { : { pos: range.end, end: range.end }; } + export function collectExternalModuleInfo(sourceFile: SourceFile, resolver: EmitResolver) { + const externalImports: (ImportDeclaration | ImportEqualsDeclaration | ExportDeclaration)[] = []; + const exportSpecifiers: Map = {}; + let exportEquals: ExportAssignment = undefined; + let hasExportStars = false; + for (let node of sourceFile.statements) { + switch (node.kind) { + case SyntaxKind.ImportDeclaration: + if (!(node).importClause || + resolver.isReferencedAliasDeclaration((node).importClause, /*checkChildren*/ true)) { + // import "mod" + // import x from "mod" where x is referenced + // import * as x from "mod" where x is referenced + // import { x, y } from "mod" where at least one import is referenced + externalImports.push(node); + } + break; + + case SyntaxKind.ImportEqualsDeclaration: + if ((node).moduleReference.kind === SyntaxKind.ExternalModuleReference && resolver.isReferencedAliasDeclaration(node)) { + // import x = require("mod") where x is referenced + externalImports.push(node); + } + break; + + case SyntaxKind.ExportDeclaration: + if ((node).moduleSpecifier) { + if (!(node).exportClause) { + // export * from "mod" + externalImports.push(node); + hasExportStars = true; + } + else if (resolver.isValueAliasDeclaration(node)) { + // export { x, y } from "mod" where at least one export is a value symbol + externalImports.push(node); + } + } + else { + // export { x, y } + for (const specifier of (node).exportClause.elements) { + const name = (specifier.propertyName || specifier.name).text; + if (!exportSpecifiers[name]) { + exportSpecifiers[name] = [specifier]; + } + else { + exportSpecifiers[name].push(specifier); + } + } + } + break; + + case SyntaxKind.ExportAssignment: + if ((node).isExportEquals && !exportEquals) { + // export = x + exportEquals = node; + } + break; + } + } + + return { externalImports, exportSpecifiers, exportEquals, hasExportStars }; + } + + export function copyPrologueDirectives(from: Statement[], to: Statement[]): number { + for (let i = 0; i < from.length; ++i) { + if (isPrologueDirective(from[i])) { + addNode(to, from[i]); + } + else { + return i; + } + } + + return from.length; + } + + export function getInitializedVariables(node: VariableDeclarationList) { + return filter(node.declarations, isInitializedVariable); + } + + function isInitializedVariable(node: VariableDeclaration) { + return node.initializer !== undefined; + } + // Node tests // // All node tests in the following list should *not* reference parent pointers so that diff --git a/src/compiler/visitor.ts b/src/compiler/visitor.ts index 3292881e014..8f5cb736dbf 100644 --- a/src/compiler/visitor.ts +++ b/src/compiler/visitor.ts @@ -2,7 +2,7 @@ /* @internal */ namespace ts { - export type OneOrMore = T | NodeArrayNode; + export type OneOrMany = T | NodeArrayNode; /** * Describes an edge of a Node, used when traversing a syntax tree. @@ -531,7 +531,7 @@ namespace ts { // Visit each original node. for (let i = 0; i < count; i++) { const node = nodes[i + start]; - const visited = node && >visitor(node); + const visited = node && >visitor(node); if (updated !== undefined || visited === undefined || visited !== node) { if (updated === undefined) { // Ensure we have a copy of `nodes`, up to the current index. @@ -660,7 +660,7 @@ namespace ts { /** * Flattens an array of nodes that could contain NodeArrayNodes. */ - export function flattenNodes(nodes: OneOrMore[]): T[] { + export function flattenNodes(nodes: OneOrMany[]): T[] { let result: T[]; if (nodes) { result = []; @@ -678,7 +678,7 @@ namespace ts { * @param to The destination array. * @param from The source Node or NodeArrayNode. */ - export function addNode(to: T[], from: OneOrMore, startOnNewLine?: boolean) { + export function addNode(to: T[], from: OneOrMany, startOnNewLine?: boolean) { addNodeWorker(to, from, startOnNewLine, /*test*/ undefined) } @@ -688,7 +688,7 @@ namespace ts { * @param to The destination NodeArray. * @param from The source array of Node or NodeArrayNode. */ - export function addNodes(to: T[], from: OneOrMore[], startOnNewLine?: boolean) { + export function addNodes(to: T[], from: OneOrMany[], startOnNewLine?: boolean) { addNodesWorker(to, from, startOnNewLine, /*test*/ undefined); } @@ -698,7 +698,7 @@ namespace ts { * @param to The destination array. * @param from The source Node or NodeArrayNode. */ - export function addLine(to: T[], from: OneOrMore) { + export function addLine(to: T[], from: OneOrMany) { addNodeWorker(to, from, /*addOnNewLine*/ true, /*test*/ undefined); } @@ -708,11 +708,11 @@ namespace ts { * @param to The destination NodeArray. * @param from The source array of Node or NodeArrayNode. */ - export function addLines(to: T[], from: OneOrMore[]) { + export function addLines(to: T[], from: OneOrMany[]) { addNodesWorker(to, from, /*addOnNewLine*/ true, /*test*/ undefined); } - function addNodeWorker(to: T[], from: OneOrMore, addOnNewLine: boolean, test: (node: Node) => boolean) { + function addNodeWorker(to: T[], from: OneOrMany, addOnNewLine: boolean, test: (node: Node) => boolean) { if (to && from) { if (isNodeArrayNode(from)) { addNodesWorker(to, from.nodes, addOnNewLine, test); @@ -728,7 +728,7 @@ namespace ts { } } - function addNodesWorker(to: T[], from: OneOrMore[], addOnNewLine: boolean, test: (node: Node) => boolean) { + function addNodesWorker(to: T[], from: OneOrMany[], addOnNewLine: boolean, test: (node: Node) => boolean) { if (to && from) { for (const node of from) { addNodeWorker(to, node, addOnNewLine, test);