From dfbd4f36325b3d8cf74f25fbcb0e180fbd553822 Mon Sep 17 00:00:00 2001 From: Ryan Cavanaugh Date: Thu, 8 Mar 2018 15:44:05 -0800 Subject: [PATCH 01/28] Project References --- Jakefile.js | 1 + src/compiler/checker.ts | 17 +- src/compiler/commandLineParser.ts | 43 +- src/compiler/core.ts | 19 + src/compiler/declarationEmitter.ts | 2058 +++++++++++++++++ src/compiler/diagnosticMessages.json | 34 + src/compiler/emitter.ts | 105 +- src/compiler/factory.ts | 14 +- src/compiler/program.ts | 233 +- src/compiler/types.ts | 44 +- src/compiler/utilities.ts | 1 + .../convertCompilerOptionsFromJson.ts | 2 +- src/harness/unittests/projectReferences.ts | 275 +++ src/harness/virtualFileSystem.ts | 74 + src/server/protocol.ts | 1 + .../reference/api/tsserverlibrary.d.ts | 91 +- tests/baselines/reference/api/typescript.d.ts | 90 +- ...LineContextDiagnosticWithPretty.errors.txt | 3 +- .../prettyContextNotDebugAssertion.errors.txt | 3 +- .../tsconfig.json | 2 + .../tsconfig.json | 2 + .../tsconfig.json | 2 + .../tsconfig.json | 2 + .../tsconfig.json | 2 + .../tsconfig.json | 2 + .../tsconfig.json | 2 + .../tsconfig.json | 2 + 27 files changed, 3035 insertions(+), 89 deletions(-) create mode 100644 src/compiler/declarationEmitter.ts create mode 100644 src/harness/unittests/projectReferences.ts diff --git a/Jakefile.js b/Jakefile.js index f2821a0168642..f7c9db9efa9c5 100644 --- a/Jakefile.js +++ b/Jakefile.js @@ -133,6 +133,7 @@ var harnessSources = harnessCoreSources.concat([ "asserts.ts", "builder.ts", "commandLineParsing.ts", + "projectReferences.ts", "configurationExtension.ts", "convertCompilerOptionsFromJson.ts", "convertTypeAcquisitionFromJson.ts", diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 0ccaccb546b6f..3fb1ca794098f 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -2148,7 +2148,22 @@ namespace ts { } if (moduleNotFoundError) { - // report errors only if it was requested + // For relative paths, see if this was possibly a projectReference redirect + if (pathIsRelative(moduleReference)) { + const sourceFile = getSourceFileOfNode(location); + const redirects = sourceFile.redirectedReferences; + if (redirects) { + const normalizedTargetPath = getNormalizedAbsolutePath(moduleReference, getDirectoryPath(sourceFile.fileName)); + for (const ext of [Extension.Ts, Extension.Tsx]) { + const probePath = normalizedTargetPath + ext; + if (redirects.indexOf(probePath) >= 0) { + error(errorNode, Diagnostics.Output_file_0_has_not_been_built_from_source_file_1, moduleReference, probePath); + return undefined; + } + } + } + } + if (resolutionDiagnostic) { error(errorNode, resolutionDiagnostic, moduleReference, resolvedModule.resolvedFileName); } diff --git a/src/compiler/commandLineParser.ts b/src/compiler/commandLineParser.ts index e15cd5df7db0a..5515979791a81 100644 --- a/src/compiler/commandLineParser.ts +++ b/src/compiler/commandLineParser.ts @@ -240,6 +240,12 @@ namespace ts { category: Diagnostics.Basic_Options, description: Diagnostics.Specify_the_root_directory_of_input_files_Use_to_control_the_output_directory_structure_with_outDir, }, + { + name: "composite", + type: "boolean", + category: Diagnostics.Basic_Options, + description: Diagnostics.Enable_project_compilation, + }, { name: "removeComments", type: "boolean", @@ -417,6 +423,17 @@ namespace ts { category: Diagnostics.Module_Resolution_Options, description: Diagnostics.Type_declaration_files_to_be_included_in_compilation }, + { + name: "references", + type: "list", + element: { + name: "references", + type: "object" + }, + showInSimplifiedHelpView: true, + category: Diagnostics.Module_Resolution_Options, + description: Diagnostics.Projects_to_reference + }, { name: "allowSyntheticDefaultImports", type: "boolean", @@ -942,8 +959,9 @@ namespace ts { */ export function parseConfigFileTextToJson(fileName: string, jsonText: string): { config?: any; error?: Diagnostic } { const jsonSourceFile = parseJsonText(fileName, jsonText); + const config = convertToObject(jsonSourceFile, jsonSourceFile.parseDiagnostics); return { - config: convertToObject(jsonSourceFile, jsonSourceFile.parseDiagnostics), + config, error: jsonSourceFile.parseDiagnostics.length ? jsonSourceFile.parseDiagnostics[0] : undefined }; } @@ -1006,6 +1024,22 @@ namespace ts { type: "string" } }, + { + name: "references", + type: "list", + element: { + name: "references", + type: "object" + } + }, + { + name: "projects", + type: "list", + element: { + name: "projects", + type: "string" + } + }, { name: "include", type: "list", @@ -1470,7 +1504,7 @@ namespace ts { const parsedConfig = parseConfig(json, sourceFile, host, basePath, configFileName, resolutionStack, errors); const { raw } = parsedConfig; const options = extend(existingOptions, parsedConfig.options || {}); - options.configFilePath = configFileName; + options.configFilePath = configFileName && normalizeSlashes(configFileName); setConfigFileInOptions(options, sourceFile); const { fileNames, wildcardDirectories, spec } = getFileNames(); return { @@ -1529,7 +1563,7 @@ namespace ts { } const result = matchFileNames(filesSpecs, includeSpecs, excludeSpecs, configFileName ? directoryOfCombinedPath(configFileName, basePath) : basePath, options, host, errors, extraFileExtensions, sourceFile); - if (result.fileNames.length === 0 && !hasProperty(raw, "files") && resolutionStack.length === 0) { + if (result.fileNames.length === 0 && !hasProperty(raw, "files") && resolutionStack.length === 0 && !options.references) { errors.push(getErrorForNoInputFiles(result.spec, configFileName)); } @@ -1821,6 +1855,9 @@ namespace ts { const options = getDefaultCompilerOptions(configFileName); convertOptionsFromJson(optionDeclarations, jsonOptions, basePath, options, Diagnostics.Unknown_compiler_option_0, errors); + if (configFileName) { + options.configFilePath = configFileName; + } return options; } diff --git a/src/compiler/core.ts b/src/compiler/core.ts index b08d897b35c23..cf1974841e68d 100644 --- a/src/compiler/core.ts +++ b/src/compiler/core.ts @@ -1988,6 +1988,18 @@ namespace ts { return normalized; } + /** + * Normalizes a path including its leading drive letter (if any) + */ + export function normalizePathAndRoot(path: string): string { + const normalized = normalizePathAndParts(path).path; + const rootLength = getRootLength(normalized); + if (rootLength === 0) { + return normalized; + } + return path.substr(0, rootLength).toLowerCase() + path.substr(rootLength); + } + export function normalizePath(path: string): string { return normalizePathAndParts(path).path; } @@ -2061,6 +2073,13 @@ namespace ts { : moduleKind === ModuleKind.System; } + export function getEmitDeclarations(compilerOptions: CompilerOptions): boolean { + if (compilerOptions.composite) { + return true; + } + return !!compilerOptions.declaration; + } + export type StrictOptionName = "noImplicitAny" | "noImplicitThis" | "strictNullChecks" | "strictFunctionTypes" | "strictPropertyInitialization" | "alwaysStrict"; export function getStrictOptionValue(compilerOptions: CompilerOptions, flag: StrictOptionName): boolean { diff --git a/src/compiler/declarationEmitter.ts b/src/compiler/declarationEmitter.ts new file mode 100644 index 0000000000000..82933d8254d0f --- /dev/null +++ b/src/compiler/declarationEmitter.ts @@ -0,0 +1,2058 @@ +/// + +/* @internal */ +namespace ts { + interface ModuleElementDeclarationEmitInfo { + node: Node; + outputPos: number; + indent: number; + asynchronousOutput?: string; // If the output for alias was written asynchronously, the corresponding output + subModuleElementDeclarationEmitInfo?: ModuleElementDeclarationEmitInfo[]; + isVisible?: boolean; + } + + interface DeclarationEmit { + reportedDeclarationError: boolean; + moduleElementDeclarationEmitInfo: ModuleElementDeclarationEmitInfo[]; + synchronousDeclarationOutput: string; + referencesOutput: string; + } + + type GetSymbolAccessibilityDiagnostic = (symbolAccessibilityResult: SymbolAccessibilityResult) => SymbolAccessibilityDiagnostic; + + interface EmitTextWriterWithSymbolWriter extends EmitTextWriter { + getSymbolAccessibilityDiagnostic: GetSymbolAccessibilityDiagnostic; + } + + interface SymbolAccessibilityDiagnostic { + errorNode: Node; + diagnosticMessage: DiagnosticMessage; + typeName?: DeclarationName | QualifiedName; + } + + export function getDeclarationDiagnostics(host: EmitHost, resolver: EmitResolver, targetSourceFile: SourceFile): Diagnostic[] { + const declarationDiagnostics = createDiagnosticCollection(); + forEachEmittedFile(host, getDeclarationDiagnosticsFromFile, targetSourceFile); + return declarationDiagnostics.getDiagnostics(targetSourceFile ? targetSourceFile.fileName : undefined); + + function getDeclarationDiagnosticsFromFile({ declarationFilePath }: EmitFileNames, sourceFileOrBundle: SourceFile | Bundle) { + emitDeclarations(host, resolver, declarationDiagnostics, declarationFilePath, sourceFileOrBundle, /*emitOnlyDtsFiles*/ false); + } + } + + function emitDeclarations(host: EmitHost, resolver: EmitResolver, emitterDiagnostics: DiagnosticCollection, declarationFilePath: string, + sourceFileOrBundle: SourceFile | Bundle, emitOnlyDtsFiles: boolean): DeclarationEmit { + const sourceFiles = sourceFileOrBundle.kind === SyntaxKind.Bundle ? [...sourceFileOrBundle.prepends, ...sourceFileOrBundle.sourceFiles] : [sourceFileOrBundle]; + const isBundledEmit = sourceFileOrBundle.kind === SyntaxKind.Bundle; + const newLine = host.getNewLine(); + const compilerOptions = host.getCompilerOptions(); + + let write: (s: string) => void; + let writeLine: () => void; + let increaseIndent: () => void; + let decreaseIndent: () => void; + let writeTextOfNode: (text: string, node: Node) => void; + + let writer: EmitTextWriterWithSymbolWriter; + + createAndSetNewTextWriterWithSymbolWriter(); + + let enclosingDeclaration: Node; + let resultHasExternalModuleIndicator: boolean; + let currentText: string; + let currentLineMap: ReadonlyArray; + let currentIdentifiers: Map; + let isCurrentFileExternalModule: boolean; + let reportedDeclarationError = false; + let errorNameNode: DeclarationName | QualifiedName; + const emitJsDocComments = compilerOptions.removeComments ? noop : writeJsDocComments; + const emit = compilerOptions.stripInternal ? stripInternal : emitNode; + let needsDeclare = true; + + let moduleElementDeclarationEmitInfo: ModuleElementDeclarationEmitInfo[] = []; + let asynchronousSubModuleDeclarationEmitInfo: ModuleElementDeclarationEmitInfo[]; + + // Contains the reference paths that needs to go in the declaration file. + // Collecting this separately because reference paths need to be first thing in the declaration file + // and we could be collecting these paths from multiple files into single one with --out option + let referencesOutput = ""; + + let usedTypeDirectiveReferences: Map; + + // Emit references corresponding to each file + const emittedReferencedFiles: SourceFile[] = []; + let addedGlobalFileReference = false; + let allSourcesModuleElementDeclarationEmitInfo: ModuleElementDeclarationEmitInfo[] = []; + forEach(sourceFiles, sourceFile => { + if (sourceFile.kind === SyntaxKind.Prepend) { + emitPrepend(sourceFile); + return; + } + + // Dont emit for javascript file + if (isSourceFileJavaScript(sourceFile)) { + return; + } + + // Check what references need to be added + if (!compilerOptions.noResolve) { + forEach(sourceFile.referencedFiles, fileReference => { + const referencedFile = tryResolveScriptReference(host, sourceFile, fileReference); + + // Emit reference in dts, if the file reference was not already emitted + if (referencedFile && !contains(emittedReferencedFiles, referencedFile)) { + // Add a reference to generated dts file, + // global file reference is added only + // - if it is not bundled emit (because otherwise it would be self reference) + // - and it is not already added + if (writeReferencePath(referencedFile, !isBundledEmit && !addedGlobalFileReference, emitOnlyDtsFiles)) { + addedGlobalFileReference = true; + } + emittedReferencedFiles.push(referencedFile); + } + }); + } + + resultHasExternalModuleIndicator = false; + if (!isBundledEmit || !isExternalModule(sourceFile)) { + needsDeclare = true; + emitSourceFile(sourceFile); + } + else if (isExternalModule(sourceFile)) { + needsDeclare = false; + write(`declare module "${getResolvedExternalModuleName(host, sourceFile)}" {`); + writeLine(); + increaseIndent(); + emitSourceFile(sourceFile); + decreaseIndent(); + write("}"); + writeLine(); + } + + // create asynchronous output for the importDeclarations + if (moduleElementDeclarationEmitInfo.length) { + const oldWriter = writer; + forEach(moduleElementDeclarationEmitInfo, aliasEmitInfo => { + if (aliasEmitInfo.isVisible && !aliasEmitInfo.asynchronousOutput) { + Debug.assert(aliasEmitInfo.node.kind === SyntaxKind.ImportDeclaration); + createAndSetNewTextWriterWithSymbolWriter(); + Debug.assert(aliasEmitInfo.indent === 0 || (aliasEmitInfo.indent === 1 && isBundledEmit)); + for (let i = 0; i < aliasEmitInfo.indent; i++) { + increaseIndent(); + } + writeImportDeclaration(aliasEmitInfo.node); + aliasEmitInfo.asynchronousOutput = writer.getText(); + for (let i = 0; i < aliasEmitInfo.indent; i++) { + decreaseIndent(); + } + } + }); + setWriter(oldWriter); + + allSourcesModuleElementDeclarationEmitInfo = allSourcesModuleElementDeclarationEmitInfo.concat(moduleElementDeclarationEmitInfo); + moduleElementDeclarationEmitInfo = []; + } + + if (!isBundledEmit && isExternalModule(sourceFile) && !resultHasExternalModuleIndicator) { + // if file was external module this fact should be preserved in .d.ts as well. + // in case if we didn't write any external module specifiers in .d.ts we need to emit something + // that will force compiler to think that this file is an external module - 'export {}' is a reasonable choice here. + write("export {};"); + writeLine(); + } + }); + + if (usedTypeDirectiveReferences) { + forEachKey(usedTypeDirectiveReferences, directive => { + referencesOutput += `/// ${newLine}`; + }); + } + + return { + reportedDeclarationError, + moduleElementDeclarationEmitInfo: allSourcesModuleElementDeclarationEmitInfo, + synchronousDeclarationOutput: writer.getText(), + referencesOutput, + }; + + function hasInternalAnnotation(range: CommentRange) { + const comment = currentText.substring(range.pos, range.end); + return stringContains(comment, "@internal"); + } + + function stripInternal(node: Node) { + if (node) { + const leadingCommentRanges = getLeadingCommentRanges(currentText, node.pos); + if (forEach(leadingCommentRanges, hasInternalAnnotation)) { + return; + } + + emitNode(node); + } + } + + function createAndSetNewTextWriterWithSymbolWriter(): void { + const writer = createTextWriter(newLine); + writer.trackSymbol = trackSymbol; + writer.reportInaccessibleThisError = reportInaccessibleThisError; + writer.reportInaccessibleUniqueSymbolError = reportInaccessibleUniqueSymbolError; + writer.reportPrivateInBaseOfClassExpression = reportPrivateInBaseOfClassExpression; + writer.writeKeyword = writer.write; + writer.writeOperator = writer.write; + writer.writePunctuation = writer.write; + writer.writeSpace = writer.write; + writer.writeStringLiteral = writer.writeLiteral; + writer.writeParameter = writer.write; + writer.writeProperty = writer.write; + writer.writeSymbol = writer.write; + setWriter(writer); + } + + function setWriter(newWriter: EmitTextWriterWithSymbolWriter) { + writer = newWriter; + write = newWriter.write; + writeTextOfNode = newWriter.writeTextOfNode; + writeLine = newWriter.writeLine; + increaseIndent = newWriter.increaseIndent; + decreaseIndent = newWriter.decreaseIndent; + } + + function writeAsynchronousModuleElements(nodes: ReadonlyArray) { + const oldWriter = writer; + forEach(nodes, declaration => { + let nodeToCheck: Node; + if (declaration.kind === SyntaxKind.VariableDeclaration) { + nodeToCheck = declaration.parent.parent; + } + else if (declaration.kind === SyntaxKind.NamedImports || declaration.kind === SyntaxKind.ImportSpecifier || declaration.kind === SyntaxKind.ImportClause) { + Debug.fail("We should be getting ImportDeclaration instead to write"); + } + else { + nodeToCheck = declaration; + } + + let moduleElementEmitInfo = forEach(moduleElementDeclarationEmitInfo, declEmitInfo => declEmitInfo.node === nodeToCheck ? declEmitInfo : undefined); + if (!moduleElementEmitInfo && asynchronousSubModuleDeclarationEmitInfo) { + moduleElementEmitInfo = forEach(asynchronousSubModuleDeclarationEmitInfo, declEmitInfo => declEmitInfo.node === nodeToCheck ? declEmitInfo : undefined); + } + + // If the alias was marked as not visible when we saw its declaration, we would have saved the aliasEmitInfo, but if we haven't yet visited the alias declaration + // then we don't need to write it at this point. We will write it when we actually see its declaration + // Eg. + // export function bar(a: foo.Foo) { } + // import foo = require("foo"); + // Writing of function bar would mark alias declaration foo as visible but we haven't yet visited that declaration so do nothing, + // we would write alias foo declaration when we visit it since it would now be marked as visible + if (moduleElementEmitInfo) { + if (moduleElementEmitInfo.node.kind === SyntaxKind.ImportDeclaration) { + // we have to create asynchronous output only after we have collected complete information + // because it is possible to enable multiple bindings as asynchronously visible + moduleElementEmitInfo.isVisible = true; + } + else { + createAndSetNewTextWriterWithSymbolWriter(); + for (let declarationIndent = moduleElementEmitInfo.indent; declarationIndent; declarationIndent--) { + increaseIndent(); + } + + if (nodeToCheck.kind === SyntaxKind.ModuleDeclaration) { + Debug.assert(asynchronousSubModuleDeclarationEmitInfo === undefined); + asynchronousSubModuleDeclarationEmitInfo = []; + } + writeModuleElement(nodeToCheck); + if (nodeToCheck.kind === SyntaxKind.ModuleDeclaration) { + moduleElementEmitInfo.subModuleElementDeclarationEmitInfo = asynchronousSubModuleDeclarationEmitInfo; + asynchronousSubModuleDeclarationEmitInfo = undefined; + } + moduleElementEmitInfo.asynchronousOutput = writer.getText(); + } + } + }); + setWriter(oldWriter); + } + + function recordTypeReferenceDirectivesIfNecessary(typeReferenceDirectives: string[]): void { + if (!typeReferenceDirectives) { + return; + } + + if (!usedTypeDirectiveReferences) { + usedTypeDirectiveReferences = createMap(); + } + for (const directive of typeReferenceDirectives) { + if (!usedTypeDirectiveReferences.has(directive)) { + usedTypeDirectiveReferences.set(directive, directive); + } + } + } + + function handleSymbolAccessibilityError(symbolAccessibilityResult: SymbolAccessibilityResult) { + if (symbolAccessibilityResult.accessibility === SymbolAccessibility.Accessible) { + // write the aliases + if (symbolAccessibilityResult && symbolAccessibilityResult.aliasesToMakeVisible) { + writeAsynchronousModuleElements(symbolAccessibilityResult.aliasesToMakeVisible); + } + } + else { + // Report error + reportedDeclarationError = true; + const errorInfo = writer.getSymbolAccessibilityDiagnostic(symbolAccessibilityResult); + if (errorInfo) { + if (errorInfo.typeName) { + emitterDiagnostics.add(createDiagnosticForNode(symbolAccessibilityResult.errorNode || errorInfo.errorNode, + errorInfo.diagnosticMessage, + getTextOfNodeFromSourceText(currentText, errorInfo.typeName), + symbolAccessibilityResult.errorSymbolName, + symbolAccessibilityResult.errorModuleName)); + } + else { + emitterDiagnostics.add(createDiagnosticForNode(symbolAccessibilityResult.errorNode || errorInfo.errorNode, + errorInfo.diagnosticMessage, + symbolAccessibilityResult.errorSymbolName, + symbolAccessibilityResult.errorModuleName)); + } + } + } + } + + function trackSymbol(symbol: Symbol, enclosingDeclaration?: Node, meaning?: SymbolFlags) { + handleSymbolAccessibilityError(resolver.isSymbolAccessible(symbol, enclosingDeclaration, meaning, /*shouldComputeAliasesToMakeVisible*/ true)); + recordTypeReferenceDirectivesIfNecessary(resolver.getTypeReferenceDirectivesForSymbol(symbol, meaning)); + } + + function reportPrivateInBaseOfClassExpression(propertyName: string) { + if (errorNameNode) { + reportedDeclarationError = true; + emitterDiagnostics.add( + createDiagnosticForNode(errorNameNode, Diagnostics.Property_0_of_exported_class_expression_may_not_be_private_or_protected, propertyName)); + } + } + + function reportInaccessibleUniqueSymbolError() { + if (errorNameNode) { + reportedDeclarationError = true; + emitterDiagnostics.add(createDiagnosticForNode(errorNameNode, Diagnostics.The_inferred_type_of_0_references_an_inaccessible_1_type_A_type_annotation_is_necessary, + declarationNameToString(errorNameNode), + "unique symbol")); + } + } + + function reportInaccessibleThisError() { + if (errorNameNode) { + reportedDeclarationError = true; + emitterDiagnostics.add(createDiagnosticForNode(errorNameNode, Diagnostics.The_inferred_type_of_0_references_an_inaccessible_1_type_A_type_annotation_is_necessary, + declarationNameToString(errorNameNode), + "this")); + } + } + + function writeTypeOfDeclaration(declaration: AccessorDeclaration | VariableLikeDeclaration, type: TypeNode, getSymbolAccessibilityDiagnostic: GetSymbolAccessibilityDiagnostic) { + writer.getSymbolAccessibilityDiagnostic = getSymbolAccessibilityDiagnostic; + write(": "); + + // use the checker's type, not the declared type, + // for optional parameter properties + // and also for non-optional initialized parameters that aren't a parameter property + // these types may need to add `undefined`. + const shouldUseResolverType = declaration.kind === SyntaxKind.Parameter && + (resolver.isRequiredInitializedParameter(declaration) || + resolver.isOptionalUninitializedParameterProperty(declaration)); + if (type && !shouldUseResolverType) { + // Write the type + emitType(type); + } + else { + errorNameNode = declaration.name; + const format = TypeFormatFlags.UseTypeOfFunction | TypeFormatFlags.UseStructuralFallback | + TypeFormatFlags.WriteClassExpressionAsTypeLiteral | + (shouldUseResolverType ? TypeFormatFlags.AddUndefined : 0); + resolver.writeTypeOfDeclaration(declaration, enclosingDeclaration, format, writer); + errorNameNode = undefined; + } + } + + function writeReturnTypeAtSignature(signature: SignatureDeclaration, getSymbolAccessibilityDiagnostic: GetSymbolAccessibilityDiagnostic) { + writer.getSymbolAccessibilityDiagnostic = getSymbolAccessibilityDiagnostic; + write(": "); + if (signature.type) { + // Write the type + emitType(signature.type); + } + else { + errorNameNode = signature.name; + resolver.writeReturnTypeOfSignatureDeclaration( + signature, + enclosingDeclaration, + TypeFormatFlags.UseTypeOfFunction | TypeFormatFlags.UseStructuralFallback | TypeFormatFlags.WriteClassExpressionAsTypeLiteral, + writer); + errorNameNode = undefined; + } + } + + function emitLines(nodes: ReadonlyArray) { + for (const node of nodes) { + emit(node); + } + } + + function emitSeparatedList(nodes: ReadonlyArray, separator: string, eachNodeEmitFn: (node: Node) => void, canEmitFn?: (node: Node) => boolean) { + let currentWriterPos = writer.getTextPos(); + for (const node of nodes) { + if (!canEmitFn || canEmitFn(node)) { + if (currentWriterPos !== writer.getTextPos()) { + write(separator); + } + currentWriterPos = writer.getTextPos(); + eachNodeEmitFn(node); + } + } + } + + function emitCommaList(nodes: ReadonlyArray, eachNodeEmitFn: (node: Node) => void, canEmitFn?: (node: Node) => boolean) { + emitSeparatedList(nodes, ", ", eachNodeEmitFn, canEmitFn); + } + + function writeJsDocComments(declaration: Node) { + if (declaration) { + const jsDocComments = getJSDocCommentRanges(declaration, currentText); + emitNewLineBeforeLeadingComments(currentLineMap, writer, declaration, jsDocComments); + // jsDoc comments are emitted at /*leading comment1 */space/*leading comment*/space + emitComments(currentText, currentLineMap, writer, jsDocComments, /*leadingSeparator*/ false, /*trailingSeparator*/ true, newLine, writeCommentRange); + } + } + + function emitTypeWithNewGetSymbolAccessibilityDiagnostic(type: TypeNode | EntityName, getSymbolAccessibilityDiagnostic: GetSymbolAccessibilityDiagnostic) { + writer.getSymbolAccessibilityDiagnostic = getSymbolAccessibilityDiagnostic; + emitType(type); + } + + function emitType(type: TypeNode | Identifier | QualifiedName) { + switch (type.kind) { + case SyntaxKind.AnyKeyword: + case SyntaxKind.StringKeyword: + case SyntaxKind.NumberKeyword: + case SyntaxKind.BooleanKeyword: + case SyntaxKind.ObjectKeyword: + case SyntaxKind.SymbolKeyword: + case SyntaxKind.VoidKeyword: + case SyntaxKind.UndefinedKeyword: + case SyntaxKind.NullKeyword: + case SyntaxKind.NeverKeyword: + case SyntaxKind.ThisType: + case SyntaxKind.LiteralType: + return writeTextOfNode(currentText, type); + case SyntaxKind.ExpressionWithTypeArguments: + return emitExpressionWithTypeArguments(type); + case SyntaxKind.TypeReference: + return emitTypeReference(type); + case SyntaxKind.TypeQuery: + return emitTypeQuery(type); + case SyntaxKind.ArrayType: + return emitArrayType(type); + case SyntaxKind.TupleType: + return emitTupleType(type); + case SyntaxKind.UnionType: + return emitUnionType(type); + case SyntaxKind.IntersectionType: + return emitIntersectionType(type); + case SyntaxKind.ConditionalType: + return emitConditionalType(type); + case SyntaxKind.InferType: + return emitInferType(type); + case SyntaxKind.ParenthesizedType: + return emitParenType(type); + case SyntaxKind.TypeOperator: + return emitTypeOperator(type); + case SyntaxKind.IndexedAccessType: + return emitIndexedAccessType(type); + case SyntaxKind.MappedType: + return emitMappedType(type); + case SyntaxKind.FunctionType: + case SyntaxKind.ConstructorType: + return emitSignatureDeclarationWithJsDocComments(type); + case SyntaxKind.TypeLiteral: + return emitTypeLiteral(type); + case SyntaxKind.Identifier: + return emitEntityName(type); + case SyntaxKind.QualifiedName: + return emitEntityName(type); + case SyntaxKind.TypePredicate: + return emitTypePredicate(type); + } + + function writeEntityName(entityName: EntityName | Expression) { + if (entityName.kind === SyntaxKind.Identifier) { + writeTextOfNode(currentText, entityName); + } + else { + const left = entityName.kind === SyntaxKind.QualifiedName ? (entityName).left : (entityName).expression; + const right = entityName.kind === SyntaxKind.QualifiedName ? (entityName).right : (entityName).name; + writeEntityName(left); + write("."); + writeTextOfNode(currentText, right); + } + } + + function emitEntityName(entityName: EntityNameOrEntityNameExpression) { + const visibilityResult = resolver.isEntityNameVisible(entityName, + // Aliases can be written asynchronously so use correct enclosing declaration + entityName.parent.kind === SyntaxKind.ImportEqualsDeclaration ? entityName.parent : enclosingDeclaration); + + handleSymbolAccessibilityError(visibilityResult); + recordTypeReferenceDirectivesIfNecessary(resolver.getTypeReferenceDirectivesForEntityName(entityName)); + writeEntityName(entityName); + } + + function emitExpressionWithTypeArguments(node: ExpressionWithTypeArguments) { + if (isEntityNameExpression(node.expression)) { + Debug.assert(node.expression.kind === SyntaxKind.Identifier || node.expression.kind === SyntaxKind.PropertyAccessExpression); + emitEntityName(node.expression); + if (node.typeArguments) { + write("<"); + emitCommaList(node.typeArguments, emitType); + write(">"); + } + } + } + + function emitTypeReference(type: TypeReferenceNode) { + emitEntityName(type.typeName); + if (type.typeArguments) { + write("<"); + emitCommaList(type.typeArguments, emitType); + write(">"); + } + } + + function emitTypePredicate(type: TypePredicateNode) { + writeTextOfNode(currentText, type.parameterName); + write(" is "); + emitType(type.type); + } + + function emitTypeQuery(type: TypeQueryNode) { + write("typeof "); + emitEntityName(type.exprName); + } + + function emitArrayType(type: ArrayTypeNode) { + emitType(type.elementType); + write("[]"); + } + + function emitTupleType(type: TupleTypeNode) { + write("["); + emitCommaList(type.elementTypes, emitType); + write("]"); + } + + function emitUnionType(type: UnionTypeNode) { + emitSeparatedList(type.types, " | ", emitType); + } + + function emitIntersectionType(type: IntersectionTypeNode) { + emitSeparatedList(type.types, " & ", emitType); + } + + function emitConditionalType(node: ConditionalTypeNode) { + emitType(node.checkType); + write(" extends "); + emitType(node.extendsType); + write(" ? "); + const prevEnclosingDeclaration = enclosingDeclaration; + enclosingDeclaration = node.trueType; + emitType(node.trueType); + enclosingDeclaration = prevEnclosingDeclaration; + write(" : "); + emitType(node.falseType); + } + + function emitInferType(node: InferTypeNode) { + write("infer "); + writeTextOfNode(currentText, node.typeParameter.name); + } + + function emitParenType(type: ParenthesizedTypeNode) { + write("("); + emitType(type.type); + write(")"); + } + + function emitTypeOperator(type: TypeOperatorNode) { + write(tokenToString(type.operator)); + write(" "); + emitType(type.type); + } + + function emitIndexedAccessType(node: IndexedAccessTypeNode) { + emitType(node.objectType); + write("["); + emitType(node.indexType); + write("]"); + } + + function emitMappedType(node: MappedTypeNode) { + const prevEnclosingDeclaration = enclosingDeclaration; + enclosingDeclaration = node; + write("{"); + writeLine(); + increaseIndent(); + if (node.readonlyToken) { + write(node.readonlyToken.kind === SyntaxKind.PlusToken ? "+readonly " : + node.readonlyToken.kind === SyntaxKind.MinusToken ? "-readonly " : + "readonly "); + } + write("["); + writeEntityName(node.typeParameter.name); + write(" in "); + emitType(node.typeParameter.constraint); + write("]"); + if (node.questionToken) { + write(node.questionToken.kind === SyntaxKind.PlusToken ? "+?" : + node.questionToken.kind === SyntaxKind.MinusToken ? "-?" : + "?"); + } + write(": "); + if (node.type) { + emitType(node.type); + } + else { + write("any"); + } + write(";"); + writeLine(); + decreaseIndent(); + write("}"); + enclosingDeclaration = prevEnclosingDeclaration; + } + + function emitTypeLiteral(type: TypeLiteralNode) { + write("{"); + if (type.members.length) { + writeLine(); + increaseIndent(); + // write members + emitLines(type.members); + decreaseIndent(); + } + write("}"); + } + } + + function emitPrepend(node: PrependNode) { + write(node.declarationText); + } + + function emitSourceFile(node: SourceFile) { + currentText = node.text; + currentLineMap = getLineStarts(node); + currentIdentifiers = node.identifiers; + isCurrentFileExternalModule = isExternalModule(node); + enclosingDeclaration = node; + emitDetachedComments(currentText, currentLineMap, writer, writeCommentRange, node, newLine, /*removeComments*/ true); + emitLines(node.statements); + } + + // Return a temp variable name to be used in `export default`/`export class ... extends` statements. + // The temp name will be of the form _default_counter. + // Note that export default is only allowed at most once in a module, so we + // do not need to keep track of created temp names. + function getExportTempVariableName(baseName: string): string { + if (!currentIdentifiers.has(baseName)) { + return baseName; + } + let count = 0; + while (true) { + count++; + const name = baseName + "_" + count; + if (!currentIdentifiers.has(name)) { + return name; + } + } + } + + function emitTempVariableDeclaration(expr: Expression, baseName: string, diagnostic: SymbolAccessibilityDiagnostic, needsDeclare: boolean): string { + const tempVarName = getExportTempVariableName(baseName); + if (needsDeclare) { + write("declare "); + } + write("const "); + write(tempVarName); + write(": "); + writer.getSymbolAccessibilityDiagnostic = () => diagnostic; + resolver.writeTypeOfExpression( + expr, + enclosingDeclaration, + TypeFormatFlags.UseTypeOfFunction | TypeFormatFlags.UseStructuralFallback | TypeFormatFlags.WriteClassExpressionAsTypeLiteral, + writer); + write(";"); + writeLine(); + return tempVarName; + } + + function emitExportAssignment(node: ExportAssignment) { + if (isSourceFile(node.parent)) { + resultHasExternalModuleIndicator = true; // Top-level exports are external module indicators + } + if (node.expression.kind === SyntaxKind.Identifier) { + write(node.isExportEquals ? "export = " : "export default "); + writeTextOfNode(currentText, node.expression); + } + else { + const tempVarName = emitTempVariableDeclaration(node.expression, "_default", { + diagnosticMessage: Diagnostics.Default_export_of_the_module_has_or_is_using_private_name_0, + errorNode: node + }, needsDeclare); + write(node.isExportEquals ? "export = " : "export default "); + write(tempVarName); + } + write(";"); + writeLine(); + + // Make all the declarations visible for the export name + if (node.expression.kind === SyntaxKind.Identifier) { + const nodes = resolver.collectLinkedAliases(node.expression); + + // write each of these declarations asynchronously + writeAsynchronousModuleElements(nodes); + } + } + + function isModuleElementVisible(node: Declaration) { + return resolver.isDeclarationVisible(node); + } + + function emitModuleElement(node: Node, isModuleElementVisible: boolean) { + if (isModuleElementVisible) { + writeModuleElement(node); + } + // Import equals declaration in internal module can become visible as part of any emit so lets make sure we add these irrespective + else if (node.kind === SyntaxKind.ImportEqualsDeclaration || + (node.parent.kind === SyntaxKind.SourceFile && isCurrentFileExternalModule)) { + let isVisible: boolean; + if (asynchronousSubModuleDeclarationEmitInfo && node.parent.kind !== SyntaxKind.SourceFile) { + // Import declaration of another module that is visited async so lets put it in right spot + asynchronousSubModuleDeclarationEmitInfo.push({ + node, + outputPos: writer.getTextPos(), + indent: writer.getIndent(), + isVisible + }); + } + else { + if (node.kind === SyntaxKind.ImportDeclaration) { + const importDeclaration = node; + if (importDeclaration.importClause) { + isVisible = (importDeclaration.importClause.name && resolver.isDeclarationVisible(importDeclaration.importClause)) || + isVisibleNamedBinding(importDeclaration.importClause.namedBindings); + } + } + moduleElementDeclarationEmitInfo.push({ + node, + outputPos: writer.getTextPos(), + indent: writer.getIndent(), + isVisible + }); + } + } + } + + function writeModuleElement(node: Node) { + switch (node.kind) { + case SyntaxKind.FunctionDeclaration: + return writeFunctionDeclaration(node); + case SyntaxKind.VariableStatement: + return writeVariableStatement(node); + case SyntaxKind.InterfaceDeclaration: + return writeInterfaceDeclaration(node); + case SyntaxKind.ClassDeclaration: + return writeClassDeclaration(node); + case SyntaxKind.TypeAliasDeclaration: + return writeTypeAliasDeclaration(node); + case SyntaxKind.EnumDeclaration: + return writeEnumDeclaration(node); + case SyntaxKind.ModuleDeclaration: + return writeModuleDeclaration(node); + case SyntaxKind.ImportEqualsDeclaration: + return writeImportEqualsDeclaration(node); + case SyntaxKind.ImportDeclaration: + return writeImportDeclaration(node); + default: + Debug.fail("Unknown symbol kind"); + } + } + + function emitModuleElementDeclarationFlags(node: Node) { + // If the node is parented in the current source file we need to emit export declare or just export + if (node.parent.kind === SyntaxKind.SourceFile) { + const modifiers = getModifierFlags(node); + // If the node is exported + if (modifiers & ModifierFlags.Export) { + resultHasExternalModuleIndicator = true; // Top-level exports are external module indicators + write("export "); + } + + if (modifiers & ModifierFlags.Default) { + write("default "); + } + else if (node.kind !== SyntaxKind.InterfaceDeclaration && needsDeclare) { + write("declare "); + } + } + } + + function emitClassMemberDeclarationFlags(flags: ModifierFlags) { + if (flags & ModifierFlags.Private) { + write("private "); + } + else if (flags & ModifierFlags.Protected) { + write("protected "); + } + + if (flags & ModifierFlags.Static) { + write("static "); + } + if (flags & ModifierFlags.Readonly) { + write("readonly "); + } + if (flags & ModifierFlags.Abstract) { + write("abstract "); + } + } + + function writeImportEqualsDeclaration(node: ImportEqualsDeclaration) { + // note usage of writer. methods instead of aliases created, just to make sure we are using + // correct writer especially to handle asynchronous alias writing + emitJsDocComments(node); + if (hasModifier(node, ModifierFlags.Export)) { + write("export "); + } + write("import "); + writeTextOfNode(currentText, node.name); + write(" = "); + if (isInternalModuleImportEqualsDeclaration(node)) { + emitTypeWithNewGetSymbolAccessibilityDiagnostic(node.moduleReference, getImportEntityNameVisibilityError); + write(";"); + } + else { + write("require("); + emitExternalModuleSpecifier(node); + write(");"); + } + writer.writeLine(); + + function getImportEntityNameVisibilityError(): SymbolAccessibilityDiagnostic { + return { + diagnosticMessage: Diagnostics.Import_declaration_0_is_using_private_name_1, + errorNode: node, + typeName: node.name + }; + } + } + + function isVisibleNamedBinding(namedBindings: NamespaceImport | NamedImports): boolean { + if (namedBindings) { + if (namedBindings.kind === SyntaxKind.NamespaceImport) { + return resolver.isDeclarationVisible(namedBindings); + } + else { + return namedBindings.elements.some(namedImport => resolver.isDeclarationVisible(namedImport)); + } + } + } + + function writeImportDeclaration(node: ImportDeclaration) { + emitJsDocComments(node); + if (hasModifier(node, ModifierFlags.Export)) { + write("export "); + } + write("import "); + if (node.importClause) { + const currentWriterPos = writer.getTextPos(); + if (node.importClause.name && resolver.isDeclarationVisible(node.importClause)) { + writeTextOfNode(currentText, node.importClause.name); + } + if (node.importClause.namedBindings && isVisibleNamedBinding(node.importClause.namedBindings)) { + if (currentWriterPos !== writer.getTextPos()) { + // If the default binding was emitted, write the separated + write(", "); + } + if (node.importClause.namedBindings.kind === SyntaxKind.NamespaceImport) { + write("* as "); + writeTextOfNode(currentText, node.importClause.namedBindings.name); + } + else { + write("{ "); + emitCommaList(node.importClause.namedBindings.elements, emitImportOrExportSpecifier, resolver.isDeclarationVisible); + write(" }"); + } + } + write(" from "); + } + emitExternalModuleSpecifier(node); + write(";"); + writer.writeLine(); + } + + function emitExternalModuleSpecifier(parent: ImportEqualsDeclaration | ImportDeclaration | ExportDeclaration | ModuleDeclaration) { + // emitExternalModuleSpecifier is usually called when we emit something in the.d.ts file that will make it an external module (i.e. import/export declarations). + // the only case when it is not true is when we call it to emit correct name for module augmentation - d.ts files with just module augmentations are not considered + // external modules since they are indistinguishable from script files with ambient modules. To fix this in such d.ts files we'll emit top level 'export {}' + // so compiler will treat them as external modules. + resultHasExternalModuleIndicator = resultHasExternalModuleIndicator || parent.kind !== SyntaxKind.ModuleDeclaration; + const moduleSpecifier = parent.kind === SyntaxKind.ImportEqualsDeclaration ? getExternalModuleImportEqualsDeclarationExpression(parent) : + parent.kind === SyntaxKind.ModuleDeclaration ? parent.name : parent.moduleSpecifier; + + if (moduleSpecifier.kind === SyntaxKind.StringLiteral && isBundledEmit && (compilerOptions.out || compilerOptions.outFile)) { + const moduleName = getExternalModuleNameFromDeclaration(host, resolver, parent); + if (moduleName) { + write('"'); + write(moduleName); + write('"'); + return; + } + } + + writeTextOfNode(currentText, moduleSpecifier); + } + + function emitImportOrExportSpecifier(node: ImportOrExportSpecifier) { + if (node.propertyName) { + writeTextOfNode(currentText, node.propertyName); + write(" as "); + } + writeTextOfNode(currentText, node.name); + } + + function emitExportSpecifier(node: ExportSpecifier) { + emitImportOrExportSpecifier(node); + + // Make all the declarations visible for the export name + const nodes = resolver.collectLinkedAliases(node.propertyName || node.name); + + // write each of these declarations asynchronously + writeAsynchronousModuleElements(nodes); + } + + function emitExportDeclaration(node: ExportDeclaration) { + resultHasExternalModuleIndicator = true; // Top-level exports are external module indicators + emitJsDocComments(node); + write("export "); + if (node.exportClause) { + write("{ "); + emitCommaList(node.exportClause.elements, emitExportSpecifier); + write(" }"); + } + else { + write("*"); + } + if (node.moduleSpecifier) { + write(" from "); + emitExternalModuleSpecifier(node); + } + write(";"); + writer.writeLine(); + } + + function writeModuleDeclaration(node: ModuleDeclaration) { + emitJsDocComments(node); + emitModuleElementDeclarationFlags(node); + if (isGlobalScopeAugmentation(node)) { + write("global "); + } + else { + if (node.flags & NodeFlags.Namespace) { + write("namespace "); + } + else { + write("module "); + } + if (isExternalModuleAugmentation(node)) { + emitExternalModuleSpecifier(node); + } + else { + writeTextOfNode(currentText, node.name); + } + } + while (node.body && node.body.kind !== SyntaxKind.ModuleBlock) { + node = node.body; + write("."); + writeTextOfNode(currentText, node.name); + } + const prevEnclosingDeclaration = enclosingDeclaration; + if (node.body) { + enclosingDeclaration = node; + write(" {"); + writeLine(); + increaseIndent(); + emitLines((node.body).statements); + decreaseIndent(); + write("}"); + writeLine(); + enclosingDeclaration = prevEnclosingDeclaration; + } + else { + write(";"); + } + } + + function writeTypeAliasDeclaration(node: TypeAliasDeclaration) { + const prevEnclosingDeclaration = enclosingDeclaration; + enclosingDeclaration = node; + emitJsDocComments(node); + emitModuleElementDeclarationFlags(node); + write("type "); + writeTextOfNode(currentText, node.name); + emitTypeParameters(node.typeParameters); + write(" = "); + emitTypeWithNewGetSymbolAccessibilityDiagnostic(node.type, getTypeAliasDeclarationVisibilityError); + write(";"); + writeLine(); + enclosingDeclaration = prevEnclosingDeclaration; + + function getTypeAliasDeclarationVisibilityError(): SymbolAccessibilityDiagnostic { + return { + diagnosticMessage: Diagnostics.Exported_type_alias_0_has_or_is_using_private_name_1, + errorNode: node.type, + typeName: node.name + }; + } + } + + function writeEnumDeclaration(node: EnumDeclaration) { + emitJsDocComments(node); + emitModuleElementDeclarationFlags(node); + if (isConst(node)) { + write("const "); + } + write("enum "); + writeTextOfNode(currentText, node.name); + write(" {"); + writeLine(); + increaseIndent(); + emitLines(node.members); + decreaseIndent(); + write("}"); + writeLine(); + } + + function emitEnumMemberDeclaration(node: EnumMember) { + emitJsDocComments(node); + writeTextOfNode(currentText, node.name); + const enumMemberValue = resolver.getConstantValue(node); + if (enumMemberValue !== undefined) { + write(" = "); + write(getTextOfConstantValue(enumMemberValue)); + } + write(","); + writeLine(); + } + + function isPrivateMethodTypeParameter(node: TypeParameterDeclaration) { + return node.parent.kind === SyntaxKind.MethodDeclaration && hasModifier(node.parent, ModifierFlags.Private); + } + + function emitTypeParameters(typeParameters: ReadonlyArray) { + function emitTypeParameter(node: TypeParameterDeclaration) { + increaseIndent(); + emitJsDocComments(node); + decreaseIndent(); + writeTextOfNode(currentText, node.name); + // If there is constraint present and this is not a type parameter of the private method emit the constraint + if (node.constraint && !isPrivateMethodTypeParameter(node)) { + write(" extends "); + if (node.parent.kind === SyntaxKind.FunctionType || + node.parent.kind === SyntaxKind.ConstructorType || + (node.parent.parent && node.parent.parent.kind === SyntaxKind.TypeLiteral)) { + Debug.assert(node.parent.kind === SyntaxKind.MethodDeclaration || + node.parent.kind === SyntaxKind.MethodSignature || + node.parent.kind === SyntaxKind.FunctionType || + node.parent.kind === SyntaxKind.ConstructorType || + node.parent.kind === SyntaxKind.CallSignature || + node.parent.kind === SyntaxKind.ConstructSignature); + emitType(node.constraint); + } + else { + emitTypeWithNewGetSymbolAccessibilityDiagnostic(node.constraint, getTypeParameterConstraintVisibilityError); + } + } + if (node.default && !isPrivateMethodTypeParameter(node)) { + write(" = "); + if (node.parent.kind === SyntaxKind.FunctionType || + node.parent.kind === SyntaxKind.ConstructorType || + (node.parent.parent && node.parent.parent.kind === SyntaxKind.TypeLiteral)) { + Debug.assert(node.parent.kind === SyntaxKind.MethodDeclaration || + node.parent.kind === SyntaxKind.MethodSignature || + node.parent.kind === SyntaxKind.FunctionType || + node.parent.kind === SyntaxKind.ConstructorType || + node.parent.kind === SyntaxKind.CallSignature || + node.parent.kind === SyntaxKind.ConstructSignature); + emitType(node.default); + } + else { + emitTypeWithNewGetSymbolAccessibilityDiagnostic(node.default, getTypeParameterConstraintVisibilityError); + } + } + + function getTypeParameterConstraintVisibilityError(): SymbolAccessibilityDiagnostic { + // Type parameter constraints are named by user so we should always be able to name it + let diagnosticMessage: DiagnosticMessage; + switch (node.parent.kind) { + case SyntaxKind.ClassDeclaration: + diagnosticMessage = Diagnostics.Type_parameter_0_of_exported_class_has_or_is_using_private_name_1; + break; + + case SyntaxKind.InterfaceDeclaration: + diagnosticMessage = Diagnostics.Type_parameter_0_of_exported_interface_has_or_is_using_private_name_1; + break; + + case SyntaxKind.ConstructSignature: + diagnosticMessage = Diagnostics.Type_parameter_0_of_constructor_signature_from_exported_interface_has_or_is_using_private_name_1; + break; + + case SyntaxKind.CallSignature: + diagnosticMessage = Diagnostics.Type_parameter_0_of_call_signature_from_exported_interface_has_or_is_using_private_name_1; + break; + + case SyntaxKind.MethodDeclaration: + case SyntaxKind.MethodSignature: + if (hasModifier(node.parent, ModifierFlags.Static)) { + diagnosticMessage = Diagnostics.Type_parameter_0_of_public_static_method_from_exported_class_has_or_is_using_private_name_1; + } + else if (node.parent.parent.kind === SyntaxKind.ClassDeclaration) { + diagnosticMessage = Diagnostics.Type_parameter_0_of_public_method_from_exported_class_has_or_is_using_private_name_1; + } + else { + diagnosticMessage = Diagnostics.Type_parameter_0_of_method_from_exported_interface_has_or_is_using_private_name_1; + } + break; + + case SyntaxKind.FunctionDeclaration: + diagnosticMessage = Diagnostics.Type_parameter_0_of_exported_function_has_or_is_using_private_name_1; + break; + + case SyntaxKind.TypeAliasDeclaration: + diagnosticMessage = Diagnostics.Type_parameter_0_of_exported_type_alias_has_or_is_using_private_name_1; + break; + + default: + Debug.fail("This is unknown parent for type parameter: " + node.parent.kind); + } + + return { + diagnosticMessage, + errorNode: node, + typeName: node.name + }; + } + } + + if (typeParameters) { + write("<"); + emitCommaList(typeParameters, emitTypeParameter); + write(">"); + } + } + + function emitHeritageClause(typeReferences: ReadonlyArray, isImplementsList: boolean) { + if (typeReferences) { + write(isImplementsList ? " implements " : " extends "); + emitCommaList(typeReferences, emitTypeOfTypeReference); + } + + function emitTypeOfTypeReference(node: ExpressionWithTypeArguments) { + if (isEntityNameExpression(node.expression)) { + emitTypeWithNewGetSymbolAccessibilityDiagnostic(node, getHeritageClauseVisibilityError); + } + else if (!isImplementsList && node.expression.kind === SyntaxKind.NullKeyword) { + write("null"); + } + + function getHeritageClauseVisibilityError(): SymbolAccessibilityDiagnostic { + let diagnosticMessage: DiagnosticMessage; + // Heritage clause is written by user so it can always be named + if (node.parent.parent.kind === SyntaxKind.ClassDeclaration) { + // Class or Interface implemented/extended is inaccessible + diagnosticMessage = isImplementsList ? + Diagnostics.Implements_clause_of_exported_class_0_has_or_is_using_private_name_1 : + Diagnostics.extends_clause_of_exported_class_0_has_or_is_using_private_name_1; + } + else { + // interface is inaccessible + diagnosticMessage = Diagnostics.extends_clause_of_exported_interface_0_has_or_is_using_private_name_1; + } + + return { + diagnosticMessage, + errorNode: node, + typeName: getNameOfDeclaration(node.parent.parent) + }; + } + } + } + + function writeClassDeclaration(node: ClassDeclaration) { + function emitParameterProperties(constructorDeclaration: ConstructorDeclaration) { + if (constructorDeclaration) { + forEach(constructorDeclaration.parameters, param => { + if (hasModifier(param, ModifierFlags.ParameterPropertyModifier)) { + emitPropertyDeclaration(param); + } + }); + } + } + + const prevEnclosingDeclaration = enclosingDeclaration; + enclosingDeclaration = node; + const baseTypeNode = getClassExtendsHeritageClauseElement(node); + let tempVarName: string; + if (baseTypeNode && !isEntityNameExpression(baseTypeNode.expression)) { + tempVarName = baseTypeNode.expression.kind === SyntaxKind.NullKeyword ? + "null" : + emitTempVariableDeclaration(baseTypeNode.expression, `${node.name.escapedText}_base`, { + diagnosticMessage: Diagnostics.extends_clause_of_exported_class_0_has_or_is_using_private_name_1, + errorNode: baseTypeNode, + typeName: node.name + }, !findAncestor(node, n => n.kind === SyntaxKind.ModuleDeclaration)); + } + + emitJsDocComments(node); + emitModuleElementDeclarationFlags(node); + if (hasModifier(node, ModifierFlags.Abstract)) { + write("abstract "); + } + write("class "); + writeTextOfNode(currentText, node.name); + emitTypeParameters(node.typeParameters); + if (baseTypeNode) { + if (!isEntityNameExpression(baseTypeNode.expression)) { + write(" extends "); + write(tempVarName); + if (baseTypeNode.typeArguments) { + write("<"); + emitCommaList(baseTypeNode.typeArguments, emitType); + write(">"); + } + } + else { + emitHeritageClause([baseTypeNode], /*isImplementsList*/ false); + } + } + emitHeritageClause(getClassImplementsHeritageClauseElements(node), /*isImplementsList*/ true); + write(" {"); + writeLine(); + increaseIndent(); + emitParameterProperties(getFirstConstructorWithBody(node)); + emitLines(node.members); + decreaseIndent(); + write("}"); + writeLine(); + enclosingDeclaration = prevEnclosingDeclaration; + } + + function writeInterfaceDeclaration(node: InterfaceDeclaration) { + emitJsDocComments(node); + emitModuleElementDeclarationFlags(node); + write("interface "); + writeTextOfNode(currentText, node.name); + const prevEnclosingDeclaration = enclosingDeclaration; + enclosingDeclaration = node; + emitTypeParameters(node.typeParameters); + const interfaceExtendsTypes = filter(getInterfaceBaseTypeNodes(node), base => isEntityNameExpression(base.expression)); + if (interfaceExtendsTypes && interfaceExtendsTypes.length) { + emitHeritageClause(interfaceExtendsTypes, /*isImplementsList*/ false); + } + write(" {"); + writeLine(); + increaseIndent(); + emitLines(node.members); + decreaseIndent(); + write("}"); + writeLine(); + enclosingDeclaration = prevEnclosingDeclaration; + } + + function emitPropertyDeclaration(node: Declaration) { + if (hasDynamicName(node) && !resolver.isLateBound(node)) { + return; + } + + emitJsDocComments(node); + emitClassMemberDeclarationFlags(getModifierFlags(node)); + emitVariableDeclaration(node); + write(";"); + writeLine(); + } + + function bindingNameContainsVisibleBindingElement(node: BindingName): boolean { + return !!node && isBindingPattern(node) && some(node.elements, elem => !isOmittedExpression(elem) && isVariableDeclarationVisible(elem)); + } + + function isVariableDeclarationVisible(node: VariableDeclaration | BindingElement) { + return resolver.isDeclarationVisible(node) || bindingNameContainsVisibleBindingElement(node.name); + } + + function emitVariableDeclaration(node: VariableDeclaration | PropertyDeclaration | PropertySignature | ParameterDeclaration) { + // If we are emitting property it isn't moduleElement and hence we already know it needs to be emitted + // so there is no check needed to see if declaration is visible + if (node.kind !== SyntaxKind.VariableDeclaration || isVariableDeclarationVisible(node)) { + if (isBindingPattern(node.name)) { + emitBindingPattern(node.name); + } + else { + writeNameOfDeclaration(node, getVariableDeclarationTypeVisibilityError); + + // If optional property emit ? but in the case of parameterProperty declaration with "?" indicating optional parameter for the constructor + // we don't want to emit property declaration with "?" + if ((node.kind === SyntaxKind.PropertyDeclaration || node.kind === SyntaxKind.PropertySignature || + (node.kind === SyntaxKind.Parameter && !isParameterPropertyDeclaration(node))) && hasQuestionToken(node)) { + write("?"); + } + if ((node.kind === SyntaxKind.PropertyDeclaration || node.kind === SyntaxKind.PropertySignature) && node.parent.kind === SyntaxKind.TypeLiteral) { + emitTypeOfVariableDeclarationFromTypeLiteral(node); + } + else if (resolver.isLiteralConstDeclaration(node)) { + write(" = "); + resolver.writeLiteralConstValue(node, writer); + } + else if (!hasModifier(node, ModifierFlags.Private)) { + writeTypeOfDeclaration(node, node.type, getVariableDeclarationTypeVisibilityError); + } + } + } + + function getVariableDeclarationTypeVisibilityDiagnosticMessage(symbolAccessibilityResult: SymbolAccessibilityResult) { + if (node.kind === SyntaxKind.VariableDeclaration) { + return symbolAccessibilityResult.errorModuleName ? + symbolAccessibilityResult.accessibility === SymbolAccessibility.CannotBeNamed ? + Diagnostics.Exported_variable_0_has_or_is_using_name_1_from_external_module_2_but_cannot_be_named : + Diagnostics.Exported_variable_0_has_or_is_using_name_1_from_private_module_2 : + Diagnostics.Exported_variable_0_has_or_is_using_private_name_1; + } + // This check is to ensure we don't report error on constructor parameter property as that error would be reported during parameter emit + // The only exception here is if the constructor was marked as private. we are not emitting the constructor parameters at all. + else if (node.kind === SyntaxKind.PropertyDeclaration || node.kind === SyntaxKind.PropertySignature || + (node.kind === SyntaxKind.Parameter && hasModifier(node.parent, ModifierFlags.Private))) { + // TODO(jfreeman): Deal with computed properties in error reporting. + if (hasModifier(node, ModifierFlags.Static)) { + return symbolAccessibilityResult.errorModuleName ? + symbolAccessibilityResult.accessibility === SymbolAccessibility.CannotBeNamed ? + Diagnostics.Public_static_property_0_of_exported_class_has_or_is_using_name_1_from_external_module_2_but_cannot_be_named : + Diagnostics.Public_static_property_0_of_exported_class_has_or_is_using_name_1_from_private_module_2 : + Diagnostics.Public_static_property_0_of_exported_class_has_or_is_using_private_name_1; + } + else if (node.parent.kind === SyntaxKind.ClassDeclaration || node.kind === SyntaxKind.Parameter) { + return symbolAccessibilityResult.errorModuleName ? + symbolAccessibilityResult.accessibility === SymbolAccessibility.CannotBeNamed ? + Diagnostics.Public_property_0_of_exported_class_has_or_is_using_name_1_from_external_module_2_but_cannot_be_named : + Diagnostics.Public_property_0_of_exported_class_has_or_is_using_name_1_from_private_module_2 : + Diagnostics.Public_property_0_of_exported_class_has_or_is_using_private_name_1; + } + else { + // Interfaces cannot have types that cannot be named + return symbolAccessibilityResult.errorModuleName ? + Diagnostics.Property_0_of_exported_interface_has_or_is_using_name_1_from_private_module_2 : + Diagnostics.Property_0_of_exported_interface_has_or_is_using_private_name_1; + } + } + } + + function getVariableDeclarationTypeVisibilityError(symbolAccessibilityResult: SymbolAccessibilityResult): SymbolAccessibilityDiagnostic { + const diagnosticMessage = getVariableDeclarationTypeVisibilityDiagnosticMessage(symbolAccessibilityResult); + return diagnosticMessage !== undefined ? { + diagnosticMessage, + errorNode: node, + typeName: node.name + } : undefined; + } + + function emitBindingPattern(bindingPattern: BindingPattern) { + // Only select visible, non-omitted expression from the bindingPattern's elements. + // We have to do this to avoid emitting trailing commas. + // For example: + // original: var [, c,,] = [ 2,3,4] + // emitted: declare var c: number; // instead of declare var c:number, ; + const elements: Node[] = []; + for (const element of bindingPattern.elements) { + if (element.kind !== SyntaxKind.OmittedExpression && isVariableDeclarationVisible(element)) { + elements.push(element); + } + } + emitCommaList(elements, emitBindingElement); + } + + function emitBindingElement(bindingElement: BindingElement) { + function getBindingElementTypeVisibilityError(symbolAccessibilityResult: SymbolAccessibilityResult): SymbolAccessibilityDiagnostic { + const diagnosticMessage = getVariableDeclarationTypeVisibilityDiagnosticMessage(symbolAccessibilityResult); + return diagnosticMessage !== undefined ? { + diagnosticMessage, + errorNode: bindingElement, + typeName: bindingElement.name + } : undefined; + } + + if (bindingElement.name) { + if (isBindingPattern(bindingElement.name)) { + emitBindingPattern(bindingElement.name); + } + else { + writeTextOfNode(currentText, bindingElement.name); + writeTypeOfDeclaration(bindingElement, /*type*/ undefined, getBindingElementTypeVisibilityError); + } + } + } + } + + function emitTypeOfVariableDeclarationFromTypeLiteral(node: VariableLikeDeclaration) { + // if this is property of type literal, + // or is parameter of method/call/construct/index signature of type literal + // emit only if type is specified + if (hasType(node)) { + write(": "); + emitType(node.type); + } + } + + function isVariableStatementVisible(node: VariableStatement) { + return forEach(node.declarationList.declarations, varDeclaration => isVariableDeclarationVisible(varDeclaration)); + } + + function writeVariableStatement(node: VariableStatement) { + // If binding pattern doesn't have name, then there is nothing to be emitted for declaration file i.e. const [,] = [1,2]. + if (every(node.declarationList && node.declarationList.declarations, decl => decl.name && isEmptyBindingPattern(decl.name))) { + return; + } + emitJsDocComments(node); + emitModuleElementDeclarationFlags(node); + if (isLet(node.declarationList)) { + write("let "); + } + else if (isConst(node.declarationList)) { + write("const "); + } + else { + write("var "); + } + emitCommaList(node.declarationList.declarations, emitVariableDeclaration, isVariableDeclarationVisible); + write(";"); + writeLine(); + } + + function emitAccessorDeclaration(node: AccessorDeclaration) { + if (hasDynamicName(node) && !resolver.isLateBound(node)) { + return; + } + + const accessors = getAllAccessorDeclarations((node.parent).members, node); + let accessorWithTypeAnnotation: AccessorDeclaration; + + if (node === accessors.firstAccessor) { + emitJsDocComments(accessors.getAccessor); + emitJsDocComments(accessors.setAccessor); + emitClassMemberDeclarationFlags(getModifierFlags(node) | (accessors.setAccessor ? 0 : ModifierFlags.Readonly)); + writeNameOfDeclaration(node, getAccessorNameVisibilityError); + if (!hasModifier(node, ModifierFlags.Private)) { + accessorWithTypeAnnotation = node; + let type = getTypeAnnotationFromAccessor(node); + if (!type) { + // couldn't get type for the first accessor, try the another one + const anotherAccessor = node.kind === SyntaxKind.GetAccessor ? accessors.setAccessor : accessors.getAccessor; + type = getTypeAnnotationFromAccessor(anotherAccessor); + if (type) { + accessorWithTypeAnnotation = anotherAccessor; + } + } + writeTypeOfDeclaration(node, type, getAccessorDeclarationTypeVisibilityError); + } + write(";"); + writeLine(); + } + + function getTypeAnnotationFromAccessor(accessor: AccessorDeclaration): TypeNode { + if (accessor) { + return accessor.kind === SyntaxKind.GetAccessor + ? accessor.type // Getter - return type + : accessor.parameters.length > 0 + ? accessor.parameters[0].type // Setter parameter type + : undefined; + } + } + + function getAccessorNameVisibilityError(symbolAccessibilityResult: SymbolAccessibilityResult) { + const diagnosticMessage = getAccessorNameVisibilityDiagnosticMessage(symbolAccessibilityResult); + return diagnosticMessage !== undefined ? { + diagnosticMessage, + errorNode: node, + typeName: node.name + } : undefined; + } + + function getAccessorNameVisibilityDiagnosticMessage(symbolAccessibilityResult: SymbolAccessibilityResult) { + if (hasModifier(node, ModifierFlags.Static)) { + return symbolAccessibilityResult.errorModuleName ? + symbolAccessibilityResult.accessibility === SymbolAccessibility.CannotBeNamed ? + Diagnostics.Public_static_property_0_of_exported_class_has_or_is_using_name_1_from_external_module_2_but_cannot_be_named : + Diagnostics.Public_static_property_0_of_exported_class_has_or_is_using_name_1_from_private_module_2 : + Diagnostics.Public_static_property_0_of_exported_class_has_or_is_using_private_name_1; + } + else if (node.parent.kind === SyntaxKind.ClassDeclaration) { + return symbolAccessibilityResult.errorModuleName ? + symbolAccessibilityResult.accessibility === SymbolAccessibility.CannotBeNamed ? + Diagnostics.Public_property_0_of_exported_class_has_or_is_using_name_1_from_external_module_2_but_cannot_be_named : + Diagnostics.Public_property_0_of_exported_class_has_or_is_using_name_1_from_private_module_2 : + Diagnostics.Public_property_0_of_exported_class_has_or_is_using_private_name_1; + } + else { + return symbolAccessibilityResult.errorModuleName ? + Diagnostics.Property_0_of_exported_interface_has_or_is_using_name_1_from_private_module_2 : + Diagnostics.Property_0_of_exported_interface_has_or_is_using_private_name_1; + } + } + + function getAccessorDeclarationTypeVisibilityError(symbolAccessibilityResult: SymbolAccessibilityResult): SymbolAccessibilityDiagnostic { + let diagnosticMessage: DiagnosticMessage; + if (accessorWithTypeAnnotation.kind === SyntaxKind.SetAccessor) { + // Getters can infer the return type from the returned expression, but setters cannot, so the + // "_from_external_module_1_but_cannot_be_named" case cannot occur. + if (hasModifier(accessorWithTypeAnnotation, ModifierFlags.Static)) { + diagnosticMessage = symbolAccessibilityResult.errorModuleName ? + Diagnostics.Parameter_type_of_public_static_setter_0_from_exported_class_has_or_is_using_name_1_from_private_module_2 : + Diagnostics.Parameter_type_of_public_static_setter_0_from_exported_class_has_or_is_using_private_name_1; + } + else { + diagnosticMessage = symbolAccessibilityResult.errorModuleName ? + Diagnostics.Parameter_type_of_public_setter_0_from_exported_class_has_or_is_using_name_1_from_private_module_2 : + Diagnostics.Parameter_type_of_public_setter_0_from_exported_class_has_or_is_using_private_name_1; + } + } + else { + if (hasModifier(accessorWithTypeAnnotation, ModifierFlags.Static)) { + diagnosticMessage = symbolAccessibilityResult.errorModuleName ? + symbolAccessibilityResult.accessibility === SymbolAccessibility.CannotBeNamed ? + Diagnostics.Return_type_of_public_static_getter_0_from_exported_class_has_or_is_using_name_1_from_external_module_2_but_cannot_be_named : + Diagnostics.Return_type_of_public_static_getter_0_from_exported_class_has_or_is_using_name_1_from_private_module_2 : + Diagnostics.Return_type_of_public_static_getter_0_from_exported_class_has_or_is_using_private_name_1; + } + else { + diagnosticMessage = symbolAccessibilityResult.errorModuleName ? + symbolAccessibilityResult.accessibility === SymbolAccessibility.CannotBeNamed ? + Diagnostics.Return_type_of_public_getter_0_from_exported_class_has_or_is_using_name_1_from_external_module_2_but_cannot_be_named : + Diagnostics.Return_type_of_public_getter_0_from_exported_class_has_or_is_using_name_1_from_private_module_2 : + Diagnostics.Return_type_of_public_getter_0_from_exported_class_has_or_is_using_private_name_1; + } + } + return { + diagnosticMessage, + errorNode: accessorWithTypeAnnotation.name, + typeName: accessorWithTypeAnnotation.name + }; + } + } + + function writeFunctionDeclaration(node: FunctionLikeDeclaration) { + if (hasDynamicName(node) && !resolver.isLateBound(node)) { + return; + } + + // If we are emitting Method/Constructor it isn't moduleElement and hence already determined to be emitting + // so no need to verify if the declaration is visible + if (!resolver.isImplementationOfOverload(node)) { + emitJsDocComments(node); + if (node.kind === SyntaxKind.FunctionDeclaration) { + emitModuleElementDeclarationFlags(node); + } + else if (node.kind === SyntaxKind.MethodDeclaration || node.kind === SyntaxKind.Constructor) { + emitClassMemberDeclarationFlags(getModifierFlags(node)); + } + if (node.kind === SyntaxKind.FunctionDeclaration) { + write("function "); + writeTextOfNode(currentText, node.name); + } + else if (node.kind === SyntaxKind.Constructor) { + write("constructor"); + } + else { + writeNameOfDeclaration(node, getMethodNameVisibilityError); + if (hasQuestionToken(node)) { + write("?"); + } + } + emitSignatureDeclaration(node); + } + + function getMethodNameVisibilityError(symbolAccessibilityResult: SymbolAccessibilityResult): SymbolAccessibilityDiagnostic { + const diagnosticMessage = getMethodNameVisibilityDiagnosticMessage(symbolAccessibilityResult); + return diagnosticMessage !== undefined ? { + diagnosticMessage, + errorNode: node, + typeName: node.name + } : undefined; + } + + function getMethodNameVisibilityDiagnosticMessage(symbolAccessibilityResult: SymbolAccessibilityResult) { + if (hasModifier(node, ModifierFlags.Static)) { + return symbolAccessibilityResult.errorModuleName ? + symbolAccessibilityResult.accessibility === SymbolAccessibility.CannotBeNamed ? + Diagnostics.Public_static_method_0_of_exported_class_has_or_is_using_name_1_from_external_module_2_but_cannot_be_named : + Diagnostics.Public_static_method_0_of_exported_class_has_or_is_using_name_1_from_private_module_2 : + Diagnostics.Public_static_method_0_of_exported_class_has_or_is_using_private_name_1; + } + else if (node.parent.kind === SyntaxKind.ClassDeclaration) { + return symbolAccessibilityResult.errorModuleName ? + symbolAccessibilityResult.accessibility === SymbolAccessibility.CannotBeNamed ? + Diagnostics.Public_method_0_of_exported_class_has_or_is_using_name_1_from_external_module_2_but_cannot_be_named : + Diagnostics.Public_method_0_of_exported_class_has_or_is_using_name_1_from_private_module_2 : + Diagnostics.Public_method_0_of_exported_class_has_or_is_using_private_name_1; + } + else { + return symbolAccessibilityResult.errorModuleName ? + Diagnostics.Method_0_of_exported_interface_has_or_is_using_name_1_from_private_module_2 : + Diagnostics.Method_0_of_exported_interface_has_or_is_using_private_name_1; + } + } + } + + function writeNameOfDeclaration(node: NamedDeclaration, getSymbolAccessibilityDiagnostic: GetSymbolAccessibilityDiagnostic) { + if (hasDynamicName(node)) { + // If this node has a dynamic name, it can only be an identifier or property access because + // we've already skipped it otherwise. + Debug.assert(resolver.isLateBound(node)); + + writeLateBoundNameOfDeclaration(node as LateBoundDeclaration, getSymbolAccessibilityDiagnostic); + } + else { + // If this node is a computed name, it can only be a symbol, because we've already skipped + // it if it's not a well known symbol. In that case, the text of the name will be exactly + // what we want, namely the name expression enclosed in brackets. + writeTextOfNode(currentText, node.name); + } + } + + function writeLateBoundNameOfDeclaration(node: LateBoundDeclaration, getSymbolAccessibilityDiagnostic: GetSymbolAccessibilityDiagnostic) { + writer.getSymbolAccessibilityDiagnostic = getSymbolAccessibilityDiagnostic; + const entityName = node.name.expression; + const visibilityResult = resolver.isEntityNameVisible(entityName, enclosingDeclaration); + handleSymbolAccessibilityError(visibilityResult); + recordTypeReferenceDirectivesIfNecessary(resolver.getTypeReferenceDirectivesForEntityName(entityName)); + writeTextOfNode(currentText, node.name); + } + + function emitSignatureDeclarationWithJsDocComments(node: SignatureDeclaration) { + emitJsDocComments(node); + emitSignatureDeclaration(node); + } + + function emitSignatureDeclaration(node: SignatureDeclaration) { + const prevEnclosingDeclaration = enclosingDeclaration; + enclosingDeclaration = node; + let closeParenthesizedFunctionType = false; + + if (node.kind === SyntaxKind.IndexSignature) { + // Index signature can have readonly modifier + emitClassMemberDeclarationFlags(getModifierFlags(node)); + write("["); + } + else { + if (node.kind === SyntaxKind.Constructor && hasModifier(node, ModifierFlags.Private)) { + write("();"); + writeLine(); + return; + } + // Construct signature or constructor type write new Signature + if (node.kind === SyntaxKind.ConstructSignature || node.kind === SyntaxKind.ConstructorType) { + write("new "); + } + else if (node.kind === SyntaxKind.FunctionType) { + const currentOutput = writer.getText(); + // Do not generate incorrect type when function type with type parameters is type argument + // This could happen if user used space between two '<' making it error free + // e.g var x: A< (a: Tany)=>Tany>; + if (node.typeParameters && currentOutput.charAt(currentOutput.length - 1) === "<") { + closeParenthesizedFunctionType = true; + write("("); + } + } + emitTypeParameters(node.typeParameters); + write("("); + } + + // Parameters + emitCommaList(node.parameters, emitParameterDeclaration); + + if (node.kind === SyntaxKind.IndexSignature) { + write("]"); + } + else { + write(")"); + } + + // If this is not a constructor and is not private, emit the return type + const isFunctionTypeOrConstructorType = node.kind === SyntaxKind.FunctionType || node.kind === SyntaxKind.ConstructorType; + if (isFunctionTypeOrConstructorType || node.parent.kind === SyntaxKind.TypeLiteral) { + // Emit type literal signature return type only if specified + if (node.type) { + write(isFunctionTypeOrConstructorType ? " => " : ": "); + emitType(node.type); + } + } + else if (node.kind !== SyntaxKind.Constructor && !hasModifier(node, ModifierFlags.Private)) { + writeReturnTypeAtSignature(node, getReturnTypeVisibilityError); + } + + enclosingDeclaration = prevEnclosingDeclaration; + + if (!isFunctionTypeOrConstructorType) { + write(";"); + writeLine(); + } + else if (closeParenthesizedFunctionType) { + write(")"); + } + + function getReturnTypeVisibilityError(symbolAccessibilityResult: SymbolAccessibilityResult): SymbolAccessibilityDiagnostic { + let diagnosticMessage: DiagnosticMessage; + switch (node.kind) { + case SyntaxKind.ConstructSignature: + // Interfaces cannot have return types that cannot be named + diagnosticMessage = symbolAccessibilityResult.errorModuleName ? + Diagnostics.Return_type_of_constructor_signature_from_exported_interface_has_or_is_using_name_0_from_private_module_1 : + Diagnostics.Return_type_of_constructor_signature_from_exported_interface_has_or_is_using_private_name_0; + break; + + case SyntaxKind.CallSignature: + // Interfaces cannot have return types that cannot be named + diagnosticMessage = symbolAccessibilityResult.errorModuleName ? + Diagnostics.Return_type_of_call_signature_from_exported_interface_has_or_is_using_name_0_from_private_module_1 : + Diagnostics.Return_type_of_call_signature_from_exported_interface_has_or_is_using_private_name_0; + break; + + case SyntaxKind.IndexSignature: + // Interfaces cannot have return types that cannot be named + diagnosticMessage = symbolAccessibilityResult.errorModuleName ? + Diagnostics.Return_type_of_index_signature_from_exported_interface_has_or_is_using_name_0_from_private_module_1 : + Diagnostics.Return_type_of_index_signature_from_exported_interface_has_or_is_using_private_name_0; + break; + + case SyntaxKind.MethodDeclaration: + case SyntaxKind.MethodSignature: + if (hasModifier(node, ModifierFlags.Static)) { + diagnosticMessage = symbolAccessibilityResult.errorModuleName ? + symbolAccessibilityResult.accessibility === SymbolAccessibility.CannotBeNamed ? + Diagnostics.Return_type_of_public_static_method_from_exported_class_has_or_is_using_name_0_from_external_module_1_but_cannot_be_named : + Diagnostics.Return_type_of_public_static_method_from_exported_class_has_or_is_using_name_0_from_private_module_1 : + Diagnostics.Return_type_of_public_static_method_from_exported_class_has_or_is_using_private_name_0; + } + else if (node.parent.kind === SyntaxKind.ClassDeclaration) { + diagnosticMessage = symbolAccessibilityResult.errorModuleName ? + symbolAccessibilityResult.accessibility === SymbolAccessibility.CannotBeNamed ? + Diagnostics.Return_type_of_public_method_from_exported_class_has_or_is_using_name_0_from_external_module_1_but_cannot_be_named : + Diagnostics.Return_type_of_public_method_from_exported_class_has_or_is_using_name_0_from_private_module_1 : + Diagnostics.Return_type_of_public_method_from_exported_class_has_or_is_using_private_name_0; + } + else { + // Interfaces cannot have return types that cannot be named + diagnosticMessage = symbolAccessibilityResult.errorModuleName ? + Diagnostics.Return_type_of_method_from_exported_interface_has_or_is_using_name_0_from_private_module_1 : + Diagnostics.Return_type_of_method_from_exported_interface_has_or_is_using_private_name_0; + } + break; + + case SyntaxKind.FunctionDeclaration: + diagnosticMessage = symbolAccessibilityResult.errorModuleName ? + symbolAccessibilityResult.accessibility === SymbolAccessibility.CannotBeNamed ? + Diagnostics.Return_type_of_exported_function_has_or_is_using_name_0_from_external_module_1_but_cannot_be_named : + Diagnostics.Return_type_of_exported_function_has_or_is_using_name_0_from_private_module_1 : + Diagnostics.Return_type_of_exported_function_has_or_is_using_private_name_0; + break; + + default: + Debug.fail("This is unknown kind for signature: " + node.kind); + } + + return { + diagnosticMessage, + errorNode: node.name || node + }; + } + } + + function emitParameterDeclaration(node: ParameterDeclaration) { + increaseIndent(); + emitJsDocComments(node); + if (node.dotDotDotToken) { + write("..."); + } + if (isBindingPattern(node.name)) { + // For bindingPattern, we can't simply writeTextOfNode from the source file + // because we want to omit the initializer and using writeTextOfNode will result in initializer get emitted. + // Therefore, we will have to recursively emit each element in the bindingPattern. + emitBindingPattern(node.name); + } + else { + writeTextOfNode(currentText, node.name); + } + if (resolver.isOptionalParameter(node)) { + write("?"); + } + decreaseIndent(); + + if (node.parent.kind === SyntaxKind.FunctionType || + node.parent.kind === SyntaxKind.ConstructorType || + node.parent.parent.kind === SyntaxKind.TypeLiteral) { + emitTypeOfVariableDeclarationFromTypeLiteral(node); + } + else if (!hasModifier(node.parent, ModifierFlags.Private)) { + writeTypeOfDeclaration(node, node.type, getParameterDeclarationTypeVisibilityError); + } + + function getParameterDeclarationTypeVisibilityError(symbolAccessibilityResult: SymbolAccessibilityResult): SymbolAccessibilityDiagnostic { + const diagnosticMessage: DiagnosticMessage = getParameterDeclarationTypeVisibilityDiagnosticMessage(symbolAccessibilityResult); + return diagnosticMessage !== undefined ? { + diagnosticMessage, + errorNode: node, + typeName: node.name + } : undefined; + } + + function getParameterDeclarationTypeVisibilityDiagnosticMessage(symbolAccessibilityResult: SymbolAccessibilityResult): DiagnosticMessage { + switch (node.parent.kind) { + case SyntaxKind.Constructor: + return symbolAccessibilityResult.errorModuleName ? + symbolAccessibilityResult.accessibility === SymbolAccessibility.CannotBeNamed ? + Diagnostics.Parameter_0_of_constructor_from_exported_class_has_or_is_using_name_1_from_external_module_2_but_cannot_be_named : + Diagnostics.Parameter_0_of_constructor_from_exported_class_has_or_is_using_name_1_from_private_module_2 : + Diagnostics.Parameter_0_of_constructor_from_exported_class_has_or_is_using_private_name_1; + + case SyntaxKind.ConstructSignature: + // Interfaces cannot have parameter types that cannot be named + return symbolAccessibilityResult.errorModuleName ? + Diagnostics.Parameter_0_of_constructor_signature_from_exported_interface_has_or_is_using_name_1_from_private_module_2 : + Diagnostics.Parameter_0_of_constructor_signature_from_exported_interface_has_or_is_using_private_name_1; + + case SyntaxKind.CallSignature: + // Interfaces cannot have parameter types that cannot be named + return symbolAccessibilityResult.errorModuleName ? + Diagnostics.Parameter_0_of_call_signature_from_exported_interface_has_or_is_using_name_1_from_private_module_2 : + Diagnostics.Parameter_0_of_call_signature_from_exported_interface_has_or_is_using_private_name_1; + + case SyntaxKind.IndexSignature: + // Interfaces cannot have parameter types that cannot be named + return symbolAccessibilityResult.errorModuleName ? + Diagnostics.Parameter_0_of_index_signature_from_exported_interface_has_or_is_using_name_1_from_private_module_2 : + Diagnostics.Parameter_0_of_index_signature_from_exported_interface_has_or_is_using_private_name_1; + + case SyntaxKind.MethodDeclaration: + case SyntaxKind.MethodSignature: + if (hasModifier(node.parent, ModifierFlags.Static)) { + return symbolAccessibilityResult.errorModuleName ? + symbolAccessibilityResult.accessibility === SymbolAccessibility.CannotBeNamed ? + Diagnostics.Parameter_0_of_public_static_method_from_exported_class_has_or_is_using_name_1_from_external_module_2_but_cannot_be_named : + Diagnostics.Parameter_0_of_public_static_method_from_exported_class_has_or_is_using_name_1_from_private_module_2 : + Diagnostics.Parameter_0_of_public_static_method_from_exported_class_has_or_is_using_private_name_1; + } + else if (node.parent.parent.kind === SyntaxKind.ClassDeclaration) { + return symbolAccessibilityResult.errorModuleName ? + symbolAccessibilityResult.accessibility === SymbolAccessibility.CannotBeNamed ? + Diagnostics.Parameter_0_of_public_method_from_exported_class_has_or_is_using_name_1_from_external_module_2_but_cannot_be_named : + Diagnostics.Parameter_0_of_public_method_from_exported_class_has_or_is_using_name_1_from_private_module_2 : + Diagnostics.Parameter_0_of_public_method_from_exported_class_has_or_is_using_private_name_1; + } + else { + // Interfaces cannot have parameter types that cannot be named + return symbolAccessibilityResult.errorModuleName ? + Diagnostics.Parameter_0_of_method_from_exported_interface_has_or_is_using_name_1_from_private_module_2 : + Diagnostics.Parameter_0_of_method_from_exported_interface_has_or_is_using_private_name_1; + } + + case SyntaxKind.FunctionDeclaration: + return symbolAccessibilityResult.errorModuleName ? + symbolAccessibilityResult.accessibility === SymbolAccessibility.CannotBeNamed ? + Diagnostics.Parameter_0_of_exported_function_has_or_is_using_name_1_from_external_module_2_but_cannot_be_named : + Diagnostics.Parameter_0_of_exported_function_has_or_is_using_name_1_from_private_module_2 : + Diagnostics.Parameter_0_of_exported_function_has_or_is_using_private_name_1; + + default: + Debug.fail("This is unknown parent for parameter: " + node.parent.kind); + } + } + + function emitBindingPattern(bindingPattern: BindingPattern) { + // We have to explicitly emit square bracket and bracket because these tokens are not store inside the node. + if (bindingPattern.kind === SyntaxKind.ObjectBindingPattern) { + write("{"); + emitCommaList(bindingPattern.elements, emitBindingElement); + write("}"); + } + else if (bindingPattern.kind === SyntaxKind.ArrayBindingPattern) { + write("["); + const elements = bindingPattern.elements; + emitCommaList(elements, emitBindingElement); + if (elements && elements.hasTrailingComma) { + write(", "); + } + write("]"); + } + } + + function emitBindingElement(bindingElement: BindingElement | OmittedExpression) { + if (bindingElement.kind === SyntaxKind.OmittedExpression) { + // If bindingElement is an omittedExpression (i.e. containing elision), + // we will emit blank space (although this may differ from users' original code, + // it allows emitSeparatedList to write separator appropriately) + // Example: + // original: function foo([, x, ,]) {} + // tslint:disable-next-line no-double-space + // emit : function foo([ , x, , ]) {} + write(" "); + } + else if (bindingElement.kind === SyntaxKind.BindingElement) { + if (bindingElement.propertyName) { + // bindingElement has propertyName property in the following case: + // { y: [a,b,c] ...} -> bindingPattern will have a property called propertyName for "y" + // We have to explicitly emit the propertyName before descending into its binding elements. + // Example: + // original: function foo({y: [a,b,c]}) {} + // emit : declare function foo({y: [a, b, c]}: { y: [any, any, any] }) void; + writeTextOfNode(currentText, bindingElement.propertyName); + write(": "); + } + if (bindingElement.name) { + if (isBindingPattern(bindingElement.name)) { + // If it is a nested binding pattern, we will recursively descend into each element and emit each one separately. + // In the case of rest element, we will omit rest element. + // Example: + // original: function foo([a, [[b]], c] = [1,[["string"]], 3]) {} + // emit : declare function foo([a, [[b]], c]: [number, [[string]], number]): void; + // original with rest: function foo([a, ...c]) {} + // emit : declare function foo([a, ...c]): void; + emitBindingPattern(bindingElement.name); + } + else { + Debug.assert(bindingElement.name.kind === SyntaxKind.Identifier); + // If the node is just an identifier, we will simply emit the text associated with the node's name + // Example: + // original: function foo({y = 10, x}) {} + // emit : declare function foo({y, x}: {number, any}): void; + if (bindingElement.dotDotDotToken) { + write("..."); + } + writeTextOfNode(currentText, bindingElement.name); + } + } + } + } + } + + function emitNode(node: Node) { + switch (node.kind) { + case SyntaxKind.FunctionDeclaration: + case SyntaxKind.ModuleDeclaration: + case SyntaxKind.ImportEqualsDeclaration: + case SyntaxKind.InterfaceDeclaration: + case SyntaxKind.ClassDeclaration: + case SyntaxKind.TypeAliasDeclaration: + case SyntaxKind.EnumDeclaration: + return emitModuleElement(node, isModuleElementVisible(node)); + case SyntaxKind.VariableStatement: + return emitModuleElement(node, isVariableStatementVisible(node)); + case SyntaxKind.ImportDeclaration: + // Import declaration without import clause is visible, otherwise it is not visible + return emitModuleElement(node, /*isModuleElementVisible*/!(node).importClause); + case SyntaxKind.ExportDeclaration: + return emitExportDeclaration(node); + case SyntaxKind.Constructor: + case SyntaxKind.MethodDeclaration: + case SyntaxKind.MethodSignature: + return writeFunctionDeclaration(node); + case SyntaxKind.ConstructSignature: + case SyntaxKind.CallSignature: + case SyntaxKind.IndexSignature: + return emitSignatureDeclarationWithJsDocComments(node); + case SyntaxKind.GetAccessor: + case SyntaxKind.SetAccessor: + return emitAccessorDeclaration(node); + case SyntaxKind.PropertyDeclaration: + case SyntaxKind.PropertySignature: + return emitPropertyDeclaration(node); + case SyntaxKind.EnumMember: + return emitEnumMemberDeclaration(node); + case SyntaxKind.ExportAssignment: + return emitExportAssignment(node); + case SyntaxKind.SourceFile: + return emitSourceFile(node); + case SyntaxKind.Prepend: + return emitPrepend(node); + } + } + + /** + * Adds the reference to referenced file, returns true if global file reference was emitted + * @param referencedFile + * @param addBundledFileReference Determines if global file reference corresponding to bundled file should be emitted or not + */ + function writeReferencePath(referencedFile: SourceFile, addBundledFileReference: boolean, emitOnlyDtsFiles: boolean): boolean { + let declFileName: string; + let addedBundledEmitReference = false; + if (referencedFile.isDeclarationFile) { + // Declaration file, use declaration file name + declFileName = referencedFile.fileName; + } + else { + // Get the declaration file path + forEachEmittedFile(host, getDeclFileName, referencedFile, emitOnlyDtsFiles); + } + + if (declFileName) { + declFileName = getRelativePathToDirectoryOrUrl( + getDirectoryPath(normalizeSlashes(declarationFilePath)), + declFileName, + host.getCurrentDirectory(), + host.getCanonicalFileName, + /*isAbsolutePathAnUrl*/ false); + + referencesOutput += `/// ${newLine}`; + } + return addedBundledEmitReference; + + function getDeclFileName(emitFileNames: EmitFileNames, sourceFileOrBundle: SourceFile | Bundle) { + // Dont add reference path to this file if it is a bundled emit and caller asked not emit bundled file path + const isBundledEmit = sourceFileOrBundle.kind === SyntaxKind.Bundle; + if (isBundledEmit && !addBundledFileReference) { + return; + } + + Debug.assert(!!emitFileNames.declarationFilePath || isSourceFileJavaScript(referencedFile), "Declaration file is not present only for javascript files"); + declFileName = emitFileNames.declarationFilePath || emitFileNames.jsFilePath; + addedBundledEmitReference = isBundledEmit; + } + } + } + + /* @internal */ + export function writeDeclarationFile(declarationFilePath: string, sourceFileOrBundle: SourceFile | Bundle, host: EmitHost, resolver: EmitResolver, emitterDiagnostics: DiagnosticCollection, emitOnlyDtsFiles: boolean) { + const emitDeclarationResult = emitDeclarations(host, resolver, emitterDiagnostics, declarationFilePath, sourceFileOrBundle, emitOnlyDtsFiles); + const emitSkipped = emitDeclarationResult.reportedDeclarationError || host.isEmitBlocked(declarationFilePath) || host.getCompilerOptions().noEmit; + if (!emitSkipped || emitOnlyDtsFiles) { + const sourceFiles = sourceFileOrBundle.kind === SyntaxKind.Bundle ? sourceFileOrBundle.sourceFiles : [sourceFileOrBundle]; + const declarationOutput = emitDeclarationResult.referencesOutput + + getDeclarationOutput(emitDeclarationResult.synchronousDeclarationOutput, emitDeclarationResult.moduleElementDeclarationEmitInfo); + writeFile(host, emitterDiagnostics, declarationFilePath, declarationOutput, host.getCompilerOptions().emitBOM, sourceFiles); + } + return emitSkipped; + + function getDeclarationOutput(synchronousDeclarationOutput: string, moduleElementDeclarationEmitInfo: ModuleElementDeclarationEmitInfo[]) { + let appliedSyncOutputPos = 0; + let declarationOutput = ""; + // apply asynchronous additions to the synchronous output + forEach(moduleElementDeclarationEmitInfo, aliasEmitInfo => { + if (aliasEmitInfo.asynchronousOutput) { + declarationOutput += synchronousDeclarationOutput.substring(appliedSyncOutputPos, aliasEmitInfo.outputPos); + declarationOutput += getDeclarationOutput(aliasEmitInfo.asynchronousOutput, aliasEmitInfo.subModuleElementDeclarationEmitInfo); + appliedSyncOutputPos = aliasEmitInfo.outputPos; + } + }); + declarationOutput += synchronousDeclarationOutput.substring(appliedSyncOutputPos); + return declarationOutput; + } + } +} diff --git a/src/compiler/diagnosticMessages.json b/src/compiler/diagnosticMessages.json index 2e3377b08eb28..50fa752a737f6 100644 --- a/src/compiler/diagnosticMessages.json +++ b/src/compiler/diagnosticMessages.json @@ -3496,6 +3496,40 @@ "category": "Error", "code": 6192 }, + + "Projects to reference": { + "category": "Message", + "code": 6300 + }, + "Referenced project '{0}' must have 'declaration': true": { + "category": "Error", + "code": 6301 + }, + "Enable project compilation": { + "category": "Message", + "code": 6302 + }, + "Project references may not form a circular graph. Cycle detected: {0}": { + "category": "Error", + "code": 6202 + }, + "Projects may not disable declaration emit.": { + "category": "Error", + "code": 6304 + }, + "Output file '{0}' has not been built from source file '{1}'.": { + "category": "Error", + "code": 6305 + }, + "Referenced project '{0}' must have setting \"composite\": true.": { + "category": "Error", + "code": 6306 + }, + "File '{0}' is not in project file list. Projects must list all files or use an 'include' pattern.": { + "category": "Error", + "code": 6307 + }, + "Variable '{0}' implicitly has an '{1}' type.": { "category": "Error", "code": 7005 diff --git a/src/compiler/emitter.ts b/src/compiler/emitter.ts index b46ca85213557..837e48882c66d 100644 --- a/src/compiler/emitter.ts +++ b/src/compiler/emitter.ts @@ -17,16 +17,19 @@ namespace ts { * Else, calls `getSourceFilesToEmit` with the (optional) target source file to determine the list of source files to emit. */ export function forEachEmittedFile( - host: EmitHost, action: (emitFileNames: EmitFileNames, sourceFileOrBundle: SourceFile | Bundle) => T, + host: EmitHost, action: (emitFileNames: EmitFileNames, sourceFileOrBundle: SourceFile | Bundle, emitOnlyDtsFiles: boolean) => T, sourceFilesOrTargetSourceFile?: ReadonlyArray | SourceFile, emitOnlyDtsFiles?: boolean) { - const sourceFiles = isArray(sourceFilesOrTargetSourceFile) ? sourceFilesOrTargetSourceFile : getSourceFilesToEmit(host, sourceFilesOrTargetSourceFile); const options = host.getCompilerOptions(); if (options.outFile || options.out) { if (sourceFiles.length) { const bundle = createBundle(sourceFiles); - const result = action(getOutputPathsFor(bundle, host, emitOnlyDtsFiles), bundle); + const jsFilePath = options.outFile || options.out; + const sourceMapFilePath = getSourceMapFilePath(jsFilePath, options); + const declarationFilePath = options.declaration ? removeFileExtension(jsFilePath) + Extension.Dts : ""; + const bundleInfoPath = options.references && jsFilePath && (removeFileExtension(jsFilePath) + ".bundle_info"); + const result = action({ jsFilePath, sourceMapFilePath, declarationFilePath, declarationMapPath: undefined, bundleInfoPath }, createBundle(sourceFiles, host.getPrependNodes()), emitOnlyDtsFiles); if (result) { return result; } @@ -34,7 +37,10 @@ namespace ts { } else { for (const sourceFile of sourceFiles) { - const result = action(getOutputPathsFor(sourceFile, host, emitOnlyDtsFiles), sourceFile); + const jsFilePath = getOwnEmitOutputFilePath(sourceFile, host, getOutputExtension(sourceFile, options)); + const sourceMapFilePath = getSourceMapFilePath(jsFilePath, options); + const declarationFilePath = !isSourceFileJavaScript(sourceFile) && (emitOnlyDtsFiles || options.declaration || options.composite) ? getDeclarationEmitOutputFilePath(sourceFile, host) : undefined; + const result = action({ jsFilePath, sourceMapFilePath, declarationFilePath, declarationMapPath: undefined, bundleInfoPath: undefined }, sourceFile, emitOnlyDtsFiles); if (result) { return result; } @@ -67,6 +73,13 @@ namespace ts { return (options.sourceMap && !options.inlineSourceMap) ? jsFilePath + ".map" : undefined; } + function createDefaultBundleInfo(): BundleInfo { + return { + originalOffset: -1, + totalLength: -1 + }; + } + // JavaScript files are always LanguageVariant.JSX, as JSX syntax is allowed in .js files also. // So for JavaScript files, '.jsx' is only emitted if the input was '.jsx', and JsxEmit.Preserve. // For TypeScript, the only time to emit with a '.jsx' extension, is on JSX input, and JsxEmit.Preserve @@ -85,6 +98,16 @@ namespace ts { return Extension.Js; } +<<<<<<< HEAD +======= + function getOriginalSourceFileOrBundle(sourceFileOrBundle: SourceFile | Bundle) { + if (sourceFileOrBundle.kind === SyntaxKind.Bundle) { + return updateBundle(sourceFileOrBundle, sameMap(sourceFileOrBundle.sourceFiles, getOriginalSourceFile), sourceFileOrBundle.prepends); + } + return getOriginalSourceFile(sourceFileOrBundle); + } + +>>>>>>> Project References /*@internal*/ // targetSourceFile is when users only want one file in entire project to be emitted. This is used in compileOnSave feature export function emitFiles(resolver: EmitResolver, host: EmitHost, targetSourceFile: SourceFile, emitOnlyDtsFiles?: boolean, transformers?: TransformerFactory[]): EmitResult { @@ -104,6 +127,7 @@ namespace ts { // Explicitly do not passthru either `inline` option }); + let bundleInfo: BundleInfo = createDefaultBundleInfo(); let currentSourceFile: SourceFile; let bundledHelpers: Map; let isOwnFileEmit: boolean; @@ -122,9 +146,26 @@ namespace ts { sourceMaps: sourceMapDataList }; +<<<<<<< HEAD function emitSourceFileOrBundle({ jsFilePath, sourceMapFilePath, declarationFilePath, declarationMapPath }: EmitFileNames, sourceFileOrBundle: SourceFile | Bundle) { emitJsFileOrBundle(sourceFileOrBundle, jsFilePath, sourceMapFilePath); emitDeclarationFileOrBundle(sourceFileOrBundle, declarationFilePath, declarationMapPath); +======= + function emitSourceFileOrBundle({ jsFilePath, sourceMapFilePath, declarationFilePath, bundleInfoPath }: EmitFileNames, sourceFileOrBundle: SourceFile | Bundle) { + // Make sure not to write js file and source map file if any of them cannot be written + if (!host.isEmitBlocked(jsFilePath) && !compilerOptions.noEmit && !compilerOptions.emitDeclarationOnly) { + if (!emitOnlyDtsFiles) { + printSourceFileOrBundle(jsFilePath, sourceMapFilePath, bundleInfoPath, sourceFileOrBundle); + } + } + else { + emitSkipped = true; + } + + if (declarationFilePath) { + emitSkipped = writeDeclarationFile(declarationFilePath, getOriginalSourceFileOrBundle(sourceFileOrBundle), host, resolver, emitterDiagnostics, emitOnlyDtsFiles) || emitSkipped; + } +>>>>>>> Project References if (!emitSkipped && emittedFilesList) { if (!emitOnlyDtsFiles) { @@ -136,9 +177,13 @@ namespace ts { if (declarationFilePath) { emittedFilesList.push(declarationFilePath); } + if (bundleInfoPath) { + emittedFilesList.push(bundleInfoPath); + } } } +<<<<<<< HEAD function emitJsFileOrBundle(sourceFileOrBundle: SourceFile | Bundle, jsFilePath: string, sourceMapFilePath: string) { const sourceFiles = isSourceFile(sourceFileOrBundle) ? [sourceFileOrBundle] : sourceFileOrBundle.sourceFiles; // Make sure not to write js file and source map file if any of them cannot be written @@ -214,6 +259,9 @@ namespace ts { } function printSourceFileOrBundle(jsFilePath: string, sourceMapFilePath: string | undefined, sourceFileOrBundle: SourceFile | Bundle, printer: Printer, mapRecorder: SourceMapWriter) { +======= + function printSourceFileOrBundle(jsFilePath: string, sourceMapFilePath: string, bundleInfoPath: string | undefined, sourceFileOrBundle: SourceFile | Bundle) { +>>>>>>> Project References const bundle = sourceFileOrBundle.kind === SyntaxKind.Bundle ? sourceFileOrBundle : undefined; const sourceFile = sourceFileOrBundle.kind === SyntaxKind.SourceFile ? sourceFileOrBundle : undefined; const sourceFiles = bundle ? bundle.sourceFiles : [sourceFile]; @@ -222,7 +270,7 @@ namespace ts { if (bundle) { bundledHelpers = createMap(); isOwnFileEmit = false; - printer.writeBundle(bundle, writer); + printer.writeBundle(bundle, writer, bundleInfo); } else { isOwnFileEmit = true; @@ -244,6 +292,12 @@ namespace ts { // Write the output file writeFile(host, emitterDiagnostics, jsFilePath, writer.getText(), compilerOptions.emitBOM, sourceFiles); + // Write bundled offset information if applicable + if (bundleInfoPath) { + bundleInfo.totalLength = writer.getTextPos(); + writeFile(host, emitterDiagnostics, bundleInfoPath, JSON.stringify(bundleInfo, undefined, 2), /*writeByteOrderMark*/ false); + } + // Reset state mapRecorder.reset(); writer.clear(); @@ -251,6 +305,7 @@ namespace ts { currentSourceFile = undefined; bundledHelpers = undefined; isOwnFileEmit = false; + bundleInfo = createDefaultBundleInfo(); } function setSourceFile(node: SourceFile) { @@ -382,10 +437,14 @@ namespace ts { case EmitHint.Expression: Debug.assert(isExpression(node), "Expected an Expression node."); break; + case EmitHint.Prepend: + Debug.assert(node.kind === SyntaxKind.Prepend, "Expected an Prepend node."); + break; } switch (node.kind) { case SyntaxKind.SourceFile: return printFile(node); case SyntaxKind.Bundle: return printBundle(node); + case SyntaxKind.Prepend: return printPrepend(node); } writeNode(hint, node, sourceFile, beginPrint()); return endPrint(); @@ -406,6 +465,11 @@ namespace ts { return endPrint(); } + function printPrepend(prepend: PrependNode): string { + writePrepend(prepend, beginPrint()); + return endPrint(); + } + /** * If `sourceFile` is `undefined`, `node` must be a synthesized `TypeNode`. */ @@ -430,13 +494,24 @@ namespace ts { writer = previousWriter; } - function writeBundle(bundle: Bundle, output: EmitTextWriter) { + function writeBundle(bundle: Bundle, output: EmitTextWriter, bundleInfo?: BundleInfo) { const previousWriter = writer; setWriter(output); emitShebangIfNeeded(bundle); emitPrologueDirectivesIfNeeded(bundle); + for (const prepend of bundle.prepends) { + print(EmitHint.Prepend, prepend, /*sourceFile*/ undefined); + } + + if (bundleInfo) { + bundleInfo.originalOffset = writer.getTextPos(); + } emitHelpersIndirect(bundle); +<<<<<<< HEAD emitSyntheticTripleSlashReferencesIfNeeded(bundle); +======= + +>>>>>>> Project References for (const sourceFile of bundle.sourceFiles) { print(EmitHint.SourceFile, sourceFile, sourceFile); } @@ -444,6 +519,14 @@ namespace ts { writer = previousWriter; } + function writePrepend(prepend: PrependNode, output: EmitTextWriter) { + const previousWriter = writer; + setWriter(output); + print(EmitHint.Prepend, prepend, /*sourceFile*/ undefined); + reset(); + writer = previousWriter; + } + function writeFile(sourceFile: SourceFile, output: EmitTextWriter) { const previousWriter = writer; setWriter(output); @@ -549,7 +632,10 @@ namespace ts { case EmitHint.IdentifierName: return pipelineEmitIdentifierName(node); case EmitHint.Expression: return pipelineEmitExpression(node); case EmitHint.MappedTypeParameter: return emitMappedTypeParameter(cast(node, isTypeParameterDeclaration)); + case EmitHint.Prepend: case EmitHint.Unspecified: return pipelineEmitUnspecified(node); + default: + assertTypeIsNever(hint); } } @@ -588,6 +674,8 @@ namespace ts { case SyntaxKind.TemplateMiddle: case SyntaxKind.TemplateTail: return emitLiteral(node); + case SyntaxKind.Prepend: + return emitPrepend(node); // Identifiers case SyntaxKind.Identifier: @@ -982,6 +1070,11 @@ namespace ts { } } + // SyntaxKind.Prepend + function emitPrepend(prepend: PrependNode) { + write(prepend.javascriptText); + } + // // Identifiers // diff --git a/src/compiler/factory.ts b/src/compiler/factory.ts index c64ce44dc6d6b..f62d37c81a295 100644 --- a/src/compiler/factory.ts +++ b/src/compiler/factory.ts @@ -2531,15 +2531,23 @@ namespace ts { : node; } - export function createBundle(sourceFiles: ReadonlyArray) { + export function createBundle(sourceFiles: ReadonlyArray, prepends: ReadonlyArray = emptyArray) { const node = createNode(SyntaxKind.Bundle); + node.prepends = prepends; node.sourceFiles = sourceFiles; return node; } - export function updateBundle(node: Bundle, sourceFiles: ReadonlyArray) { + export function createPrepend(javascript: string, declaration: string): PrependNode { + const node = createNode(SyntaxKind.Prepend); + node.javascriptText = javascript; + node.declarationText = declaration; + return node; + } + + export function updateBundle(node: Bundle, sourceFiles: ReadonlyArray, prepends: ReadonlyArray = emptyArray) { if (node.sourceFiles !== sourceFiles) { - return createBundle(sourceFiles); + return createBundle(sourceFiles, prepends); } return node; } diff --git a/src/compiler/program.ts b/src/compiler/program.ts index 4a7a5580dbe34..791268f429a09 100755 --- a/src/compiler/program.ts +++ b/src/compiler/program.ts @@ -330,15 +330,13 @@ namespace ts { context += resetEscapeSequence; } - output += formatColorAndReset(relativeFileName, ForegroundColorEscapeSequences.Cyan); - output += ":"; - output += formatColorAndReset(`${ firstLine + 1 }`, ForegroundColorEscapeSequences.Yellow); - output += ":"; - output += formatColorAndReset(`${ firstLineChar + 1 }`, ForegroundColorEscapeSequences.Yellow); - output += " - "; + output += host.getNewLine(); + output += `${relativeFileName}(${firstLine + 1},${firstLineChar + 1}): `; } - output += formatColorAndReset(diagnosticCategoryName(diagnostic), getCategoryFormat(diagnostic.category)); + const categoryColor = getCategoryFormat(diagnostic.category); + const category = DiagnosticCategory[diagnostic.category].toLowerCase(); + output += formatColorAndReset(category, categoryColor); output += formatColorAndReset(` TS${ diagnostic.code }: `, ForegroundColorEscapeSequences.Grey); output += flattenDiagnosticMessageText(diagnostic.messageText, host.getNewLine()); @@ -586,6 +584,13 @@ namespace ts { // used to track cases when two file names differ only in casing const filesByNameIgnoreCase = host.useCaseSensitiveFileNames() ? createMap() : undefined; + const referencedProjectOutFiles: Path[] = []; + const projectReferenceRedirects = createProjectReferenceRedirects(options); + checkProjectReferenceGraph(); + for (const referencedProjectFile of referencedProjectOutFiles) { + processSourceFile(referencedProjectFile, /*isDefaultLib*/ false, /*packageId*/ undefined); + } + const shouldCreateNewSourceFile = shouldProgramCreateNewSourceFiles(oldProgram, options); const structuralIsReused = tryReuseStructureFromOldProgram(); if (structuralIsReused !== StructureIsReused.Completely) { @@ -626,6 +631,17 @@ namespace ts { Debug.assert(!!missingFilePaths); + // List of collected files is complete; validate exhautiveness if this is a project with a file list + if (options.composite && rootNames.length < files.length) { + const normalizedRootNames = rootNames.map(r => normalizePathAndRoot(r).toLowerCase()); + const sourceFiles = files.filter(f => !f.isDeclarationFile).map(f => normalizePathAndRoot(f.path).toLowerCase()); + for (const file of sourceFiles) { + if (normalizedRootNames.every(r => r !== file)) { + programDiagnostics.add(createCompilerDiagnostic(Diagnostics.File_0_is_not_in_project_file_list_Projects_must_list_all_files_or_use_an_include_pattern, file)); + } + } + } + // unconditionally set moduleResolutionCache to undefined to avoid unnecessary leaks moduleResolutionCache = undefined; @@ -690,7 +706,11 @@ namespace ts { function getCommonSourceDirectory() { if (commonSourceDirectory === undefined) { const emittedFiles = filter(files, file => sourceFileMayBeEmitted(file, options, isSourceFileFromExternalLibrary)); - if (options.rootDir && checkSourceFilesBelongToPath(emittedFiles, options.rootDir)) { + if (options.composite) { + // Project compilations never infer their root from the input source paths + commonSourceDirectory = getNormalizedAbsolutePath(options.rootDir || getDirectoryPath(normalizeSlashes(options.configFilePath)), currentDirectory); + } + else if (options.rootDir && checkSourceFilesBelongToPath(emittedFiles, options.rootDir)) { // If a rootDir is specified and is valid use it as the commonSourceDirectory commonSourceDirectory = getNormalizedAbsolutePath(options.rootDir, currentDirectory); } @@ -1085,6 +1105,7 @@ namespace ts { function getEmitHost(writeFileCallback?: WriteFileCallback): EmitHost { return { + getPrependNodes, getCanonicalFileName, getCommonSourceDirectory: program.getCommonSourceDirectory, getCompilerOptions: program.getCompilerOptions, @@ -1100,6 +1121,24 @@ namespace ts { }; } + function getPrependNodes(): PrependNode[] { + if (!options.references) { + return emptyArray; + } + + const nodes: PrependNode[] = []; + walkProjectReferenceGraph(host, options, (_file, proj, opts) => { + if (opts.prepend) { + const dtsFilename = changeExtension(proj.outFile, ".d.ts"); + const js = host.readFile(proj.outFile) || `/* Input file ${proj.outFile} was missing */\r\n`; + const dts = host.readFile(dtsFilename) || `/* Input file ${proj.dtsFilename} was missing */\r\n`; + const node = createPrepend(js, dts); + nodes.push(node); + } + }); + return nodes; + } + function isSourceFileFromExternalLibrary(file: SourceFile): boolean { return sourceFilesFoundSearchingNodeModules.get(file.path); } @@ -1117,7 +1156,7 @@ namespace ts { // otherwise, using options specified in '--lib' instead of '--target' default library file const equalityComparer = host.useCaseSensitiveFileNames() ? equateStringsCaseSensitive : equateStringsCaseInsensitive; if (!options.lib) { - return equalityComparer(file.fileName, getDefaultLibraryFileName()); + return equalityComparer(file.fileName, getDefaultLibraryFileName()); } else { return forEach(options.lib, libFileName => equalityComparer(file.fileName, combinePaths(defaultLibraryPath, libFileName))); @@ -1712,7 +1751,13 @@ namespace ts { const sourceFile = getSourceFile(fileName); if (fail) { if (!sourceFile) { - fail(Diagnostics.File_0_not_found, fileName); + const redirect = getProjectReferenceRedirect(fileName); + if (redirect) { + fail(Diagnostics.Output_file_0_has_not_been_built_from_source_file_1, redirect, fileName); + } + else { + fail(Diagnostics.File_0_not_found, fileName); + } } else if (refFile && host.getCanonicalFileName(fileName) === host.getCanonicalFileName(refFile.fileName)) { fail(Diagnostics.A_file_cannot_have_a_reference_to_itself); @@ -1808,6 +1853,14 @@ namespace ts { return file; } + if (refFile) { + const redirect = getProjectReferenceRedirect(fileName); + if (redirect) { + ((refFile.redirectedReferences || (refFile.redirectedReferences = [])) as string[]).push(fileName); + } + fileName = redirect || fileName; + } + // We haven't looked for this file, do so now and cache result const file = host.getSourceFile(fileName, options.target, hostErrorMessage => { if (refFile !== undefined && refPos !== undefined && refEnd !== undefined) { @@ -1877,6 +1930,23 @@ namespace ts { return file; } + function getProjectReferenceRedirect(fileName: string): string | undefined { + const path = toPath(fileName); + // If this file is produced by a referenced project, we need to rewrite it to + // look in the output folder of the referenced project rather than the input + const normalized = getNormalizedAbsolutePath(fileName, path); + let result: string | undefined; + projectReferenceRedirects.forEach((v, k) => { + if (result !== undefined) { + return undefined; + } + if (normalized.indexOf(k) === 0) { + result = changeExtension(fileName.replace(k, v), ".d.ts"); + } + }); + return result; + } + function processReferencedFiles(file: SourceFile, isDefaultLib: boolean) { forEach(file.referencedFiles, ref => { const referencedFileName = resolveTripleslashReference(ref.fileName, file.fileName); @@ -2049,6 +2119,69 @@ namespace ts { return allFilesBelongToPath; } + function createProjectReferenceRedirects(rootOptions: CompilerOptions): Map { + const result = createMap(); + const handledProjects = createMap(); + + walkProjectReferenceGraph(host, rootOptions, createMapping); + + function createMapping(resolvedFile: string, referencedProject: CompilerOptions, reference: ProjectReference) { + const rootDir = normalizePath(referencedProject.rootDir || getDirectoryPath(resolvedFile)); + result.set(rootDir, referencedProject.outDir); + // If this project uses outFile, add the outFile .d.ts to our compilation + // Note: We don't enumerate the input files to try to find corresponding .d.ts + // files because we can't know from the outside whether they're modules or not + if (referencedProject.outFile) { + const outFile = combinePaths(referencedProject.outDir, changeExtension(referencedProject.outFile, ".d.ts")); + referencedProjectOutFiles.push(toPath(outFile)); + if (reference.prepend) { + // Don't recurse - it'll cause multi-level outfile compilations + // to get duplicate definitions from the up-up-stream .d.ts! + return; + } + } + + // Circularity check + resolvedFile = normalizePath(resolvedFile); + if (!handledProjects.has(resolvedFile)) { + handledProjects.set(resolvedFile, true); + walkProjectReferenceGraph(host, referencedProject, createMapping); + } + } + return result; + } + + function checkProjectReferenceGraph() { + // Checks the following conditions: + // * Any referenced project has project: true + // * No circularities exist + + const illegalRefs = createMap(); + const cycleName: string[] = [options.configFilePath || host.getCurrentDirectory()]; + + walkProjectReferenceGraph(host, options, checkReference); + + function checkReference(fileName: string, opts: CompilerOptions) { + const normalizedPath = normalizePath(fileName); + if (illegalRefs.has(normalizedPath)) { + createDiagnosticForOptionName(Diagnostics.Project_references_may_not_form_a_circular_graph_Cycle_detected_Colon_0, cycleName.map(normalizePath).map(s => host.getNewLine() + " " + s).join(" -> ")); + return; + } + if (opts === undefined) { + Debug.fail("Options cannot be undefined"); + return; + } + if (!opts.composite) { + createDiagnosticForOptionName(Diagnostics.Referenced_project_0_must_have_setting_composite_Colon_true, fileName); + } + illegalRefs.set(normalizedPath, true); + cycleName.push(normalizedPath); + walkProjectReferenceGraph(host, opts, checkReference); + cycleName.pop(); + illegalRefs.delete(normalizedPath); + } + } + function verifyCompilerOptions() { if (options.isolatedModules) { if (options.declaration) { @@ -2081,6 +2214,12 @@ namespace ts { createDiagnosticForOptionName(Diagnostics.Option_paths_cannot_be_used_without_specifying_baseUrl_option, "paths"); } + if (options.composite) { + if (options.declaration === false) { + createDiagnosticForOptionName(Diagnostics.Projects_may_not_disable_declaration_emit, "declaration"); + } + } + if (options.paths) { for (const key in options.paths) { if (!hasProperty(options.paths, key)) { @@ -2310,12 +2449,16 @@ namespace ts { } } - function getOptionPathsSyntax() { + function getOptionsSyntaxByName(name: string): object | undefined { const compilerOptionsObjectLiteralSyntax = getCompilerOptionsObjectLiteralSyntax(); if (compilerOptionsObjectLiteralSyntax) { - return getPropertyAssignment(compilerOptionsObjectLiteralSyntax, "paths"); + return getPropertyAssignment(compilerOptionsObjectLiteralSyntax, name); } - return emptyArray; + return undefined; + } + + function getOptionPathsSyntax(): PropertyAssignment[] { + return getOptionsSyntaxByName("paths") as PropertyAssignment[] || emptyArray; } function createDiagnosticForOptionName(message: DiagnosticMessage, option1: string, option2?: string, option3?: string) { @@ -2400,6 +2543,70 @@ namespace ts { } } + function parseConfigHostFromCompilerHost(host: CompilerHost): ParseConfigHost { + return { + fileExists: host.fileExists, + readDirectory: () => [], + readFile: host.readFile, + useCaseSensitiveFileNames: host.useCaseSensitiveFileNames() + }; + } + + export function getProjectReferenceFileNames(host: CompilerHost, rootOptions: CompilerOptions): string[] | undefined { + if (rootOptions.references === undefined) { + return []; + } + + const result: string[] = []; + for (const ref of rootOptions.references) { + const refPath = resolveProjectReferencePath(host, rootOptions.configFilePath, ref); + if (!refPath || !host.fileExists(refPath)) { + return undefined; + } + result.push(refPath); + } + return result; + } + + function resolveProjectReferencePath(host: CompilerHost, referencingConfigFilePath: string, ref: ProjectReference): string | undefined { + const rootPath = referencingConfigFilePath ? getDirectoryPath(normalizeSlashes(referencingConfigFilePath)) : host.getCurrentDirectory(); + let refPath = combinePaths(rootPath, ref.path); + if (!host.fileExists(refPath)) { + refPath = combinePaths(refPath, "tsconfig.json"); + } + return refPath; + } + + export function walkProjectReferenceGraph(host: CompilerHost, rootOptions: CompilerOptions, + callback: (resolvedFile: string, referencedProject: CompilerOptions, settings: ProjectReference) => void, + errorCallback?: (failedLocation: string) => void) { + if (rootOptions.references === undefined) return; + + const references = getProjectReferenceFileNames(host, rootOptions); + if (references === undefined) { + return; + } + + const configHost = parseConfigHostFromCompilerHost(host); + for (const ref of rootOptions.references) { + const refPath = resolveProjectReferencePath(host, rootOptions.configFilePath, ref); + const text = host.readFile(refPath); + if (!text) { + // Failed to read a referenced tsconfig file + if (errorCallback) { + errorCallback(refPath); + } + continue; + } + const referenceJsonSource = parseJsonText(refPath, text); + const cmdLine = parseJsonSourceFileConfigFileContent(referenceJsonSource, configHost, getDirectoryPath(refPath), /*existingOptions*/ undefined, refPath); + cmdLine.options.configFilePath = refPath; + if (cmdLine.options) { + callback(refPath, cmdLine.options, ref); + } + } + } + /* @internal */ /** * Returns a DiagnosticMessage if we won't include a resolved module due to its extension. diff --git a/src/compiler/types.ts b/src/compiler/types.ts index d7f14f729f2be..ed73af60f18c0 100644 --- a/src/compiler/types.ts +++ b/src/compiler/types.ts @@ -396,6 +396,7 @@ namespace ts { // Top-level nodes SourceFile, Bundle, + Prepend, // JSDoc nodes JSDocTypeExpression, @@ -2574,6 +2575,12 @@ namespace ts { /* @internal */ resolvedModules: Map; /* @internal */ resolvedTypeReferenceDirectiveNames: Map; /* @internal */ imports: ReadonlyArray; + /** + * When a file's references are redirected due to project reference directives, + * the original names of the references are stored in this array + */ + /* @internal*/ + redirectedReferences?: ReadonlyArray; // Identifier only if `declare global` /* @internal */ moduleAugmentations: ReadonlyArray; /* @internal */ patternAmbientModules?: PatternAmbientModule[]; @@ -2587,11 +2594,18 @@ namespace ts { export interface Bundle extends Node { kind: SyntaxKind.Bundle; + prepends: ReadonlyArray; sourceFiles: ReadonlyArray; /* @internal */ syntheticFileReferences?: ReadonlyArray; /* @internal */ syntheticTypeReferences?: ReadonlyArray; } + export interface PrependNode extends Node { + kind: SyntaxKind.Prepend; + javascriptText: string; + declarationText: string; + } + export interface JsonSourceFile extends SourceFile { jsonObject?: ObjectLiteralExpression; extendedSourceFiles?: string[]; @@ -4093,7 +4107,12 @@ namespace ts { name: string; } - export type CompilerOptionsValue = string | number | boolean | (string | number)[] | string[] | MapLike | PluginImport[] | null | undefined; + export interface ProjectReference { + path: string; + prepend?: boolean; + } + + export type CompilerOptionsValue = string | number | boolean | (string | number)[] | string[] | MapLike | PluginImport[] | ProjectReference[] | null | undefined; export interface CompilerOptions { /*@internal*/ all?: boolean; @@ -4164,6 +4183,8 @@ namespace ts { /* @internal */ pretty?: DiagnosticStyle; reactNamespace?: string; jsxFactory?: string; + references?: ProjectReference[]; + composite?: boolean; removeComments?: boolean; rootDir?: string; rootDirs?: string[]; @@ -4807,6 +4828,7 @@ namespace ts { Expression, // Emitting an Expression IdentifierName, // Emitting an IdentifierName MappedTypeParameter, // Emitting a TypeParameterDeclaration inside of a MappedTypeNode + Prepend, // Emitting a literal node from a prior emit output of a referenced project Unspecified, // Emitting an otherwise unspecified node } @@ -4823,6 +4845,8 @@ namespace ts { isEmitBlocked(emitFileName: string): boolean; + getPrependNodes(): PrependNode[]; + writeFile: WriteFileCallback; } @@ -4974,7 +4998,23 @@ namespace ts { /*@internal*/ writeNode(hint: EmitHint, node: Node, sourceFile: SourceFile | undefined, writer: EmitTextWriter): void; /*@internal*/ writeList(format: ListFormat, list: NodeArray, sourceFile: SourceFile | undefined, writer: EmitTextWriter): void; /*@internal*/ writeFile(sourceFile: SourceFile, writer: EmitTextWriter): void; - /*@internal*/ writeBundle(bundle: Bundle, writer: EmitTextWriter): void; + /*@internal*/ writeBundle(bundle: Bundle, writer: EmitTextWriter, info?: BundleInfo): void; + } + + /** + * When a bundle contains prepended content, we store a file on disk indicating where the non-prepended + * content of that file starts. On a subsequent build where there are no upstream .d.ts changes, we + * read the bundle info file and the original .js file to quickly re-use portion of the file + * that didn't originate in prepended content. + */ + /* @internal */ + export interface BundleInfo { + // The offset (in characters, i.e. suitable for .substr) at which the + // non-prepended portion of the emitted file starts. + originalOffset: number; + // The total length of this bundle. Used to ensure we're pulling from + // the same source as we originally wrote. + totalLength: number; } export interface PrintHandlers { diff --git a/src/compiler/utilities.ts b/src/compiler/utilities.ts index 1cc3add43e326..c24769b8d8bd8 100644 --- a/src/compiler/utilities.ts +++ b/src/compiler/utilities.ts @@ -2842,6 +2842,7 @@ namespace ts { sourceMapFilePath: string | undefined; declarationFilePath: string | undefined; declarationMapPath: string | undefined; + bundleInfoPath: string | undefined; } /** diff --git a/src/harness/unittests/convertCompilerOptionsFromJson.ts b/src/harness/unittests/convertCompilerOptionsFromJson.ts index 8178d4d05290d..3174e63a56614 100644 --- a/src/harness/unittests/convertCompilerOptionsFromJson.ts +++ b/src/harness/unittests/convertCompilerOptionsFromJson.ts @@ -12,7 +12,7 @@ namespace ts { const { options: actualCompilerOptions, errors: actualErrors} = convertCompilerOptionsFromJson(json.compilerOptions, "/apath/", configFileName); const parsedCompilerOptions = JSON.stringify(actualCompilerOptions); - const expectedCompilerOptions = JSON.stringify(expectedResult.compilerOptions); + const expectedCompilerOptions = JSON.stringify({ ...expectedResult.compilerOptions, configFilePath: configFileName }); assert.equal(parsedCompilerOptions, expectedCompilerOptions); const expectedErrors = expectedResult.errors; diff --git a/src/harness/unittests/projectReferences.ts b/src/harness/unittests/projectReferences.ts new file mode 100644 index 0000000000000..cd200d3a241df --- /dev/null +++ b/src/harness/unittests/projectReferences.ts @@ -0,0 +1,275 @@ +/// +/// + +namespace ts { + interface TestProjectSpecification { + configFileName?: string; + references?: string[]; + files: { [fileName: string]: string }; + outputFiles?: { [fileName: string]: string }; + config?: object; + options?: Partial; + } + interface TestSpecification { + [path: string]: TestProjectSpecification; + } + + function assertHasError(message: string, errors: ReadonlyArray, diag: DiagnosticMessage) { + if (!errors.some(e => e.code === diag.code)) { + const errorString = errors.map(e => ` ${e.file ? e.file.fileName : "[global]"}: ${e.messageText}`).join("\r\n"); + assert(false, `${message}: Did not find any diagnostic for ${diag.message} in:\r\n${errorString}`); + } + } + + function assertNoErrors(message: string, errors: ReadonlyArray) { + if (errors && errors.length > 0) { + assert(false, `${message}: Expected no errors, but found:\r\n${errors.map(e => ` ${e.messageText}`).join("\r\n")}`); + } + } + + function combineAllPaths(...paths: string[]) { + let result = paths[0]; + for (let i = 1; i < paths.length; i++) { + result = combinePaths(result, paths[i]); + } + return result; + } + + const emptyModule = "export { };"; + + /** + * Produces the text of a source file which imports all of the + * specified module names + */ + function moduleImporting(...names: string[]) { + return names.map((n, i) => `import * as mod_${i} from ${n}`).join("\r\n"); + } + + function testProjectReferences(spec: TestSpecification, entryPointConfigFileName: string, checkResult: (prog: Program, host: Utils.MockProjectReferenceCompilerHost) => void) { + const files = createMap(); + for (const key in spec) { + const sp = spec[key]; + const configFileName = combineAllPaths("/", key, sp.configFileName || "tsconfig.json"); + const options = { + compilerOptions: { + references: (sp.references || []).map(r => ({ path: r })), + composite: true, + outDir: "bin", + ...sp.options + }, + ...sp.config + }; + const configContent = JSON.stringify(options); + const outDir = options.compilerOptions.outDir; + files.set(configFileName, configContent); + for (const sourceFile of Object.keys(sp.files)) { + files.set(sourceFile, sp.files[sourceFile]); + } + if (sp.outputFiles) { + for (const outFile of Object.keys(sp.outputFiles)) { + files.set(combineAllPaths("/", key, outDir, outFile), sp.outputFiles[outFile]); + } + } + } + + const host = new Utils.MockProjectReferenceCompilerHost("/", /*useCaseSensitiveFileNames*/ true, files); + + const { config, error } = readConfigFile(entryPointConfigFileName, name => host.readFile(name)); + + // We shouldn't have any errors about invalid tsconfig files in these tests + assert(config && !error, flattenDiagnosticMessageText(error && error.messageText, "\n")); + const file = parseJsonConfigFileContent(config, host.configHost, getDirectoryPath(entryPointConfigFileName), {}, entryPointConfigFileName); + file.options.configFilePath = entryPointConfigFileName; + const prog = createProgram(file.fileNames, file.options, host); + checkResult(prog, host); + } + + describe("project-references meta check", () => { + it("default setup was created correctly", () => { + const spec: TestSpecification = { + "/primary": { + files: { "/primary/a.ts": emptyModule }, + references: [] + }, + "/reference": { + files: { "/secondary/b.ts": moduleImporting("../primary/a") }, + references: ["../primary"] + } + }; + testProjectReferences(spec, "/primary/tsconfig.json", prog => { + assert.isTrue(!!prog, "Program should exist"); + assertNoErrors("Sanity check should not produce errors", prog.getOptionsDiagnostics()); + }); + }); + + it("can detect a circularity error", () => { + const spec: TestSpecification = { + "/primary": { + files: { "/primary/a.ts": emptyModule }, + references: ["../secondary"] + }, + "/secondary": { + files: { "/secondary/b.ts": moduleImporting("../primary/a") }, + references: ["../primary"] + } + }; + testProjectReferences(spec, "/primary/tsconfig.json", prog => { + assert.isTrue(!!prog, "Program should exist"); + assertHasError("Should detect a circular error", prog.getOptionsDiagnostics(), Diagnostics.Project_references_may_not_form_a_circular_graph_Cycle_detected_Colon_0); + }); + }); + }); + + /** + * Validate that we enforce the basic settings constraints for referenced projects + */ + describe("project-references constraint checking for settings", () => { + it("errors when declaration = false", () => { + const spec: TestSpecification = { + "/primary": { + files: { "/primary/a.ts": emptyModule }, + references: [], + options: { + declaration: false + } + } + }; + + testProjectReferences(spec, "/primary/tsconfig.json", program => { + const errs = program.getOptionsDiagnostics(); + assertHasError("Reports an error about the wrong decl setting", errs, Diagnostics.Projects_may_not_disable_declaration_emit); + }); + }); + + it("errors when the referenced project doesn't have composite:true", () => { + const spec: TestSpecification = { + "/primary": { + files: { "/primary/a.ts": emptyModule }, + references: [], + options: { + composite: false + } + }, + "/reference": { + files: { "/secondary/b.ts": moduleImporting("../primary/a") }, + references: ["../primary"] + } + }; + testProjectReferences(spec, "/reference/tsconfig.json", program => { + const errs = program.getOptionsDiagnostics(); + assertHasError("Reports an error about 'composite' not being set", errs, Diagnostics.Referenced_project_0_must_have_setting_composite_Colon_true); + }); + }); + + it("errors when the file list is not exhaustive", () => { + const spec: TestSpecification = { + "/primary": { + files: { + "/primary/a.ts": "import * as b from './b'", + "/primary/b.ts": "export {}" + }, + config: { + files: ["a.ts"] + } + } + }; + + testProjectReferences(spec, "/primary/tsconfig.json", program => { + const errs = program.getOptionsDiagnostics(); + assertHasError("Reports an error about b.ts not being in the list", errs, Diagnostics.File_0_is_not_in_project_file_list_Projects_must_list_all_files_or_use_an_include_pattern); + }); + }); + }); + + /** + * Circularity checking + */ + describe("project-references circularity checking", () => { + // Bare cycle with relative paths tested in sanity check block + it("detects an indirected cycle", () => { + const spec: TestSpecification = { + "/alpha": { + files: { "/alpha/a.ts": emptyModule }, + references: ["../beta"] + }, + "/beta": { + files: { "/beta/b.ts": moduleImporting("../alpha/a") }, + references: ["../gamma"] + }, + "/gamma": { + files: { "/gamma/a.ts": emptyModule }, + references: ["../alpha"], + + } + }; + + testProjectReferences(spec, "/alpha/tsconfig.json", program => { + const errs = program.getOptionsDiagnostics(); + assertHasError("Reports an error about the circular diagnsotic", errs, Diagnostics.Project_references_may_not_form_a_circular_graph_Cycle_detected_Colon_0); + }); + }); + }); + + /** + * Path mapping behavior + */ + describe("project-references path mapping", () => { + it("redirects to the output .d.ts file", () => { + const spec: TestSpecification = { + "/alpha": { + files: { "/alpha/a.ts": "export const m: number = 3;" }, + references: [], + outputFiles: { "a.d.ts": emptyModule } + }, + "/beta": { + files: { "/beta/b.ts": "import { m } from '../alpha/a'" }, + references: ["../alpha"] + } + }; + testProjectReferences(spec, "/beta/tsconfig.json", program => { + assertNoErrors("File setup should be correct", program.getOptionsDiagnostics()); + assertHasError("Found a type error", program.getSemanticDiagnostics(), Diagnostics.Module_0_has_no_exported_member_1); + }); + }); + }); + + describe("project-references nice-behavior", () => { + it("issues a nice error when the input file is missing", () => { + const spec: TestSpecification = { + "/alpha": { + files: { "/alpha/a.ts": "export const m: number = 3;" }, + references: [] + }, + "/beta": { + files: { "/beta/b.ts": "import { m } from '../alpha/a'" }, + references: ["../alpha"] + } + }; + testProjectReferences(spec, "/beta/tsconfig.json", program => { + assertHasError("Issues a useful error", program.getSemanticDiagnostics(), Diagnostics.Output_file_0_has_not_been_built_from_source_file_1); + }); + }); + }); + + /** + * 'composite' behavior + */ + describe("project-references behavior changes under composite: true", () => { + it("doesn't infer the rootDir from source paths", () => { + const spec: TestSpecification = { + "/alpha": { + files: { "/alpha/src/a.ts": "export const m: number = 3;" }, + options: { + outDir: "bin" + }, + references: [] + } + }; + testProjectReferences(spec, "/alpha/tsconfig.json", (program, host) => { + program.emit(); + assert.deepEqual(host.emittedFiles.map(e => e.fileName).sort(), ["/alpha/bin/src/a.d.ts", "/alpha/bin/src/a.js"]); + }); + }); + }); + +} diff --git a/src/harness/virtualFileSystem.ts b/src/harness/virtualFileSystem.ts index 16267a092fce4..300f563c698dd 100644 --- a/src/harness/virtualFileSystem.ts +++ b/src/harness/virtualFileSystem.ts @@ -219,4 +219,78 @@ namespace Utils { return ts.matchFiles(path, extensions, excludes, includes, this.useCaseSensitiveFileNames, this.currentDirectory, depth, (path: string) => this.getAccessibleFileSystemEntries(path)); } } + + export class MockProjectReferenceCompilerHost implements ts.CompilerHost { + public emittedFiles: {fileName: string, contents: string}[] = []; + public configHost: ts.ParseConfigHost; + private readonly getCanonicalFileNameImpl = ts.createGetCanonicalFileName(!this.ignoreCase); + constructor(private currentDirectory: string, private ignoreCase: boolean, private files: ts.Map | string[]) { + this.reset(); + } + + reset() { + this.configHost = new MockParseConfigHost(this.currentDirectory, this.ignoreCase, this.files); + this.emittedFiles = []; + } + + getCanonicalFileName = (fileName: string): string => { + return this.getCanonicalFileNameImpl(fileName); + } + fileExists = (fileName: string): boolean => { + return this.configHost.fileExists(fileName); + } + + // TODO try deleting this + directoryExists = (dirName: string): boolean => { + const fullName = this.getCanonicalFileName(dirName); + let exists = false; + if (Array.isArray(this.files)) { + for (const k of this.files) { + if (this.getCanonicalFileName(k).indexOf(fullName) === 0) { + exists = true; + } + } + } + else { + this.files.forEach((_v, k) => { + if (this.getCanonicalFileName(k).indexOf(fullName) === 0) { + exists = true; + } + }); + } + return exists; + } + readFile = (fileName: string): string => { + if (fileName === "lib.d.ts") return "declare var window: any;"; + + return this.configHost.readFile(fileName); + } + getSourceFile = (fileName: string, languageVersion: ts.ScriptTarget): ts.SourceFile => { + const content = this.readFile(fileName); + if (content === undefined) { + return undefined; + } + return ts.createSourceFile(fileName, content, languageVersion); + } + getDefaultLibFileName(options: ts.CompilerOptions): string { + return ts.getDefaultLibFileName(options); + } + + writeFile: ts.WriteFileCallback = (fileName, contents) => { + this.emittedFiles.push({ fileName, contents }); + } + + getCurrentDirectory = (): string => { + return this.currentDirectory; + } + getNewLine(): string { + return "\r\n"; + } + getDirectories(): string[] { + return []; + } + useCaseSensitiveFileNames = () => { + return this.ignoreCase; + } + } } \ No newline at end of file diff --git a/src/server/protocol.ts b/src/server/protocol.ts index 3f0ce3b6d4080..d092506ef7785 100644 --- a/src/server/protocol.ts +++ b/src/server/protocol.ts @@ -2702,6 +2702,7 @@ namespace ts.server.protocol { project?: string; reactNamespace?: string; removeComments?: boolean; + references?: ProjectReference[]; rootDir?: string; rootDirs?: string[]; skipLibCheck?: boolean; diff --git a/tests/baselines/reference/api/tsserverlibrary.d.ts b/tests/baselines/reference/api/tsserverlibrary.d.ts index 10d6f31408b40..e41473b8c48b7 100644 --- a/tests/baselines/reference/api/tsserverlibrary.d.ts +++ b/tests/baselines/reference/api/tsserverlibrary.d.ts @@ -336,32 +336,33 @@ declare namespace ts { EnumMember = 271, SourceFile = 272, Bundle = 273, - JSDocTypeExpression = 274, - JSDocAllType = 275, - JSDocUnknownType = 276, - JSDocNullableType = 277, - JSDocNonNullableType = 278, - JSDocOptionalType = 279, - JSDocFunctionType = 280, - JSDocVariadicType = 281, - JSDocComment = 282, - JSDocTypeLiteral = 283, - JSDocTag = 284, - JSDocAugmentsTag = 285, - JSDocClassTag = 286, - JSDocParameterTag = 287, - JSDocReturnTag = 288, - JSDocTypeTag = 289, - JSDocTemplateTag = 290, - JSDocTypedefTag = 291, - JSDocPropertyTag = 292, - SyntaxList = 293, - NotEmittedStatement = 294, - PartiallyEmittedExpression = 295, - CommaListExpression = 296, - MergeDeclarationMarker = 297, - EndOfDeclarationMarker = 298, - Count = 299, + Prepend = 274, + JSDocTypeExpression = 275, + JSDocAllType = 276, + JSDocUnknownType = 277, + JSDocNullableType = 278, + JSDocNonNullableType = 279, + JSDocOptionalType = 280, + JSDocFunctionType = 281, + JSDocVariadicType = 282, + JSDocComment = 283, + JSDocTypeLiteral = 284, + JSDocTag = 285, + JSDocAugmentsTag = 286, + JSDocClassTag = 287, + JSDocParameterTag = 288, + JSDocReturnTag = 289, + JSDocTypeTag = 290, + JSDocTemplateTag = 291, + JSDocTypedefTag = 292, + JSDocPropertyTag = 293, + SyntaxList = 294, + NotEmittedStatement = 295, + PartiallyEmittedExpression = 296, + CommaListExpression = 297, + MergeDeclarationMarker = 298, + EndOfDeclarationMarker = 299, + Count = 300, FirstAssignment = 58, LastAssignment = 70, FirstCompoundAssignment = 59, @@ -387,10 +388,17 @@ declare namespace ts { FirstBinaryOperator = 27, LastBinaryOperator = 70, FirstNode = 145, +<<<<<<< HEAD FirstJSDocNode = 274, LastJSDocNode = 292, FirstJSDocTagNode = 284, LastJSDocTagNode = 292 +======= + FirstJSDocNode = 275, + LastJSDocNode = 293, + FirstJSDocTagNode = 285, + LastJSDocTagNode = 293, +>>>>>>> Project References } enum NodeFlags { None = 0, @@ -1634,8 +1642,14 @@ declare namespace ts { } interface Bundle extends Node { kind: SyntaxKind.Bundle; + prepends: ReadonlyArray; sourceFiles: ReadonlyArray; } + interface PrependNode extends Node { + kind: SyntaxKind.Prepend; + javascriptText: string; + declarationText: string; + } interface JsonSourceFile extends SourceFile { jsonObject?: ObjectLiteralExpression; extendedSourceFiles?: string[]; @@ -2297,7 +2311,11 @@ declare namespace ts { interface PluginImport { name: string; } - type CompilerOptionsValue = string | number | boolean | (string | number)[] | string[] | MapLike | PluginImport[] | null | undefined; + interface ProjectReference { + path: string; + prepend?: boolean; + } + type CompilerOptionsValue = string | number | boolean | (string | number)[] | string[] | MapLike | PluginImport[] | ProjectReference[] | null | undefined; interface CompilerOptions { allowJs?: boolean; allowSyntheticDefaultImports?: boolean; @@ -2352,6 +2370,8 @@ declare namespace ts { project?: string; reactNamespace?: string; jsxFactory?: string; + references?: ProjectReference[]; + composite?: boolean; removeComments?: boolean; rootDir?: string; rootDirs?: string[]; @@ -2592,7 +2612,12 @@ declare namespace ts { Expression = 1, IdentifierName = 2, MappedTypeParameter = 3, +<<<<<<< HEAD Unspecified = 4 +======= + Prepend = 4, + Unspecified = 5, +>>>>>>> Project References } interface TransformationContext { /** Gets the compiler options supplied to the transformer. */ @@ -3738,8 +3763,9 @@ declare namespace ts { function updatePartiallyEmittedExpression(node: PartiallyEmittedExpression, expression: Expression): PartiallyEmittedExpression; function createCommaList(elements: ReadonlyArray): CommaListExpression; function updateCommaList(node: CommaListExpression, elements: ReadonlyArray): CommaListExpression; - function createBundle(sourceFiles: ReadonlyArray): Bundle; - function updateBundle(node: Bundle, sourceFiles: ReadonlyArray): Bundle; + function createBundle(sourceFiles: ReadonlyArray, prepends?: ReadonlyArray): Bundle; + function createPrepend(javascript: string, declaration: string): PrependNode; + function updateBundle(node: Bundle, sourceFiles: ReadonlyArray, prepends?: ReadonlyArray): Bundle; function createImmediatelyInvokedFunctionExpression(statements: Statement[]): CallExpression; function createImmediatelyInvokedFunctionExpression(statements: Statement[], param: ParameterDeclaration, paramValue: Expression): CallExpression; function createImmediatelyInvokedArrowFunction(statements: Statement[]): CallExpression; @@ -3946,7 +3972,13 @@ declare namespace ts { * @param configFileParsingDiagnostics - error during config file parsing * @returns A 'Program' object. */ +<<<<<<< HEAD function createProgram(rootNames: ReadonlyArray, options: CompilerOptions, host?: CompilerHost, oldProgram?: Program, configFileParsingDiagnostics?: ReadonlyArray): Program; +======= + function createProgram(rootNames: ReadonlyArray, options: CompilerOptions, host?: CompilerHost, oldProgram?: Program): Program; + function getProjectReferenceFileNames(host: CompilerHost, rootOptions: CompilerOptions): string[] | undefined; + function walkProjectReferenceGraph(host: CompilerHost, rootOptions: CompilerOptions, callback: (resolvedFile: string, referencedProject: CompilerOptions, settings: ProjectReference) => void, errorCallback?: (failedLocation: string) => void): void; +>>>>>>> Project References } declare namespace ts { interface Node { @@ -7186,6 +7218,7 @@ declare namespace ts.server.protocol { project?: string; reactNamespace?: string; removeComments?: boolean; + references?: ProjectReference[]; rootDir?: string; rootDirs?: string[]; skipLibCheck?: boolean; diff --git a/tests/baselines/reference/api/typescript.d.ts b/tests/baselines/reference/api/typescript.d.ts index 26b942f6d813f..3f120f5f2ac26 100644 --- a/tests/baselines/reference/api/typescript.d.ts +++ b/tests/baselines/reference/api/typescript.d.ts @@ -336,32 +336,33 @@ declare namespace ts { EnumMember = 271, SourceFile = 272, Bundle = 273, - JSDocTypeExpression = 274, - JSDocAllType = 275, - JSDocUnknownType = 276, - JSDocNullableType = 277, - JSDocNonNullableType = 278, - JSDocOptionalType = 279, - JSDocFunctionType = 280, - JSDocVariadicType = 281, - JSDocComment = 282, - JSDocTypeLiteral = 283, - JSDocTag = 284, - JSDocAugmentsTag = 285, - JSDocClassTag = 286, - JSDocParameterTag = 287, - JSDocReturnTag = 288, - JSDocTypeTag = 289, - JSDocTemplateTag = 290, - JSDocTypedefTag = 291, - JSDocPropertyTag = 292, - SyntaxList = 293, - NotEmittedStatement = 294, - PartiallyEmittedExpression = 295, - CommaListExpression = 296, - MergeDeclarationMarker = 297, - EndOfDeclarationMarker = 298, - Count = 299, + Prepend = 274, + JSDocTypeExpression = 275, + JSDocAllType = 276, + JSDocUnknownType = 277, + JSDocNullableType = 278, + JSDocNonNullableType = 279, + JSDocOptionalType = 280, + JSDocFunctionType = 281, + JSDocVariadicType = 282, + JSDocComment = 283, + JSDocTypeLiteral = 284, + JSDocTag = 285, + JSDocAugmentsTag = 286, + JSDocClassTag = 287, + JSDocParameterTag = 288, + JSDocReturnTag = 289, + JSDocTypeTag = 290, + JSDocTemplateTag = 291, + JSDocTypedefTag = 292, + JSDocPropertyTag = 293, + SyntaxList = 294, + NotEmittedStatement = 295, + PartiallyEmittedExpression = 296, + CommaListExpression = 297, + MergeDeclarationMarker = 298, + EndOfDeclarationMarker = 299, + Count = 300, FirstAssignment = 58, LastAssignment = 70, FirstCompoundAssignment = 59, @@ -387,10 +388,17 @@ declare namespace ts { FirstBinaryOperator = 27, LastBinaryOperator = 70, FirstNode = 145, +<<<<<<< HEAD FirstJSDocNode = 274, LastJSDocNode = 292, FirstJSDocTagNode = 284, LastJSDocTagNode = 292 +======= + FirstJSDocNode = 275, + LastJSDocNode = 293, + FirstJSDocTagNode = 285, + LastJSDocTagNode = 293, +>>>>>>> Project References } enum NodeFlags { None = 0, @@ -1634,8 +1642,14 @@ declare namespace ts { } interface Bundle extends Node { kind: SyntaxKind.Bundle; + prepends: ReadonlyArray; sourceFiles: ReadonlyArray; } + interface PrependNode extends Node { + kind: SyntaxKind.Prepend; + javascriptText: string; + declarationText: string; + } interface JsonSourceFile extends SourceFile { jsonObject?: ObjectLiteralExpression; extendedSourceFiles?: string[]; @@ -2297,7 +2311,11 @@ declare namespace ts { interface PluginImport { name: string; } - type CompilerOptionsValue = string | number | boolean | (string | number)[] | string[] | MapLike | PluginImport[] | null | undefined; + interface ProjectReference { + path: string; + prepend?: boolean; + } + type CompilerOptionsValue = string | number | boolean | (string | number)[] | string[] | MapLike | PluginImport[] | ProjectReference[] | null | undefined; interface CompilerOptions { allowJs?: boolean; allowSyntheticDefaultImports?: boolean; @@ -2352,6 +2370,8 @@ declare namespace ts { project?: string; reactNamespace?: string; jsxFactory?: string; + references?: ProjectReference[]; + composite?: boolean; removeComments?: boolean; rootDir?: string; rootDirs?: string[]; @@ -2592,7 +2612,12 @@ declare namespace ts { Expression = 1, IdentifierName = 2, MappedTypeParameter = 3, +<<<<<<< HEAD Unspecified = 4 +======= + Prepend = 4, + Unspecified = 5, +>>>>>>> Project References } interface TransformationContext { /** Gets the compiler options supplied to the transformer. */ @@ -3685,8 +3710,9 @@ declare namespace ts { function updatePartiallyEmittedExpression(node: PartiallyEmittedExpression, expression: Expression): PartiallyEmittedExpression; function createCommaList(elements: ReadonlyArray): CommaListExpression; function updateCommaList(node: CommaListExpression, elements: ReadonlyArray): CommaListExpression; - function createBundle(sourceFiles: ReadonlyArray): Bundle; - function updateBundle(node: Bundle, sourceFiles: ReadonlyArray): Bundle; + function createBundle(sourceFiles: ReadonlyArray, prepends?: ReadonlyArray): Bundle; + function createPrepend(javascript: string, declaration: string): PrependNode; + function updateBundle(node: Bundle, sourceFiles: ReadonlyArray, prepends?: ReadonlyArray): Bundle; function createImmediatelyInvokedFunctionExpression(statements: Statement[]): CallExpression; function createImmediatelyInvokedFunctionExpression(statements: Statement[], param: ParameterDeclaration, paramValue: Expression): CallExpression; function createImmediatelyInvokedArrowFunction(statements: Statement[]): CallExpression; @@ -3893,7 +3919,13 @@ declare namespace ts { * @param configFileParsingDiagnostics - error during config file parsing * @returns A 'Program' object. */ +<<<<<<< HEAD function createProgram(rootNames: ReadonlyArray, options: CompilerOptions, host?: CompilerHost, oldProgram?: Program, configFileParsingDiagnostics?: ReadonlyArray): Program; +======= + function createProgram(rootNames: ReadonlyArray, options: CompilerOptions, host?: CompilerHost, oldProgram?: Program): Program; + function getProjectReferenceFileNames(host: CompilerHost, rootOptions: CompilerOptions): string[] | undefined; + function walkProjectReferenceGraph(host: CompilerHost, rootOptions: CompilerOptions, callback: (resolvedFile: string, referencedProject: CompilerOptions, settings: ProjectReference) => void, errorCallback?: (failedLocation: string) => void): void; +>>>>>>> Project References } declare namespace ts { interface EmitOutput { diff --git a/tests/baselines/reference/multiLineContextDiagnosticWithPretty.errors.txt b/tests/baselines/reference/multiLineContextDiagnosticWithPretty.errors.txt index b0cea3122bebe..b0358fa52c646 100644 --- a/tests/baselines/reference/multiLineContextDiagnosticWithPretty.errors.txt +++ b/tests/baselines/reference/multiLineContextDiagnosticWithPretty.errors.txt @@ -1,4 +1,5 @@ -tests/cases/compiler/multiLineContextDiagnosticWithPretty.ts:2:5 - error TS2322: Type '{ a: { b: string; }; }' is not assignable to type '{ c: string; }'. + +tests/cases/compiler/multiLineContextDiagnosticWithPretty.ts(2,5): error TS2322: Type '{ a: { b: string; }; }' is not assignable to type '{ c: string; }'. Object literal may only specify known properties, and 'a' does not exist in type '{ c: string; }'. 2 a: { diff --git a/tests/baselines/reference/prettyContextNotDebugAssertion.errors.txt b/tests/baselines/reference/prettyContextNotDebugAssertion.errors.txt index 3c94d3bd0b8f3..901f11eec90f3 100644 --- a/tests/baselines/reference/prettyContextNotDebugAssertion.errors.txt +++ b/tests/baselines/reference/prettyContextNotDebugAssertion.errors.txt @@ -1,4 +1,5 @@ -tests/cases/compiler/index.ts:2:1 - error TS1005: '}' expected. + +tests/cases/compiler/index.ts(2,1): error TS1005: '}' expected. 2    diff --git a/tests/baselines/reference/tsConfig/Default initialized TSConfig/tsconfig.json b/tests/baselines/reference/tsConfig/Default initialized TSConfig/tsconfig.json index 28ccc78540c7a..c1bd336691902 100644 --- a/tests/baselines/reference/tsConfig/Default initialized TSConfig/tsconfig.json +++ b/tests/baselines/reference/tsConfig/Default initialized TSConfig/tsconfig.json @@ -13,6 +13,7 @@ // "outFile": "./", /* Concatenate and emit output to single file. */ // "outDir": "./", /* Redirect output structure to the directory. */ // "rootDir": "./", /* Specify the root directory of input files. Use to control the output directory structure with --outDir. */ + // "composite": true, /* Enable project compilation */ // "removeComments": true, /* Do not emit comments to output. */ // "noEmit": true, /* Do not emit outputs. */ // "importHelpers": true, /* Import emit helpers from 'tslib'. */ @@ -41,6 +42,7 @@ // "rootDirs": [], /* List of root folders whose combined content represents the structure of the project at runtime. */ // "typeRoots": [], /* List of folders to include type definitions from. */ // "types": [], /* Type declaration files to be included in compilation. */ + // "references": [], /* Projects to reference */ // "allowSyntheticDefaultImports": true, /* Allow default imports from modules with no default export. This does not affect code emit, just typechecking. */ "esModuleInterop": true /* Enables emit interoperability between CommonJS and ES Modules via creation of namespace objects for all imports. Implies 'allowSyntheticDefaultImports'. */ // "preserveSymlinks": true, /* Do not resolve the real path of symlinks. */ diff --git a/tests/baselines/reference/tsConfig/Initialized TSConfig with boolean value compiler options/tsconfig.json b/tests/baselines/reference/tsConfig/Initialized TSConfig with boolean value compiler options/tsconfig.json index 25c75a4b55699..5bf4b7b0e3469 100644 --- a/tests/baselines/reference/tsConfig/Initialized TSConfig with boolean value compiler options/tsconfig.json +++ b/tests/baselines/reference/tsConfig/Initialized TSConfig with boolean value compiler options/tsconfig.json @@ -13,6 +13,7 @@ // "outFile": "./", /* Concatenate and emit output to single file. */ // "outDir": "./", /* Redirect output structure to the directory. */ // "rootDir": "./", /* Specify the root directory of input files. Use to control the output directory structure with --outDir. */ + // "composite": true, /* Enable project compilation */ // "removeComments": true, /* Do not emit comments to output. */ // "noEmit": true, /* Do not emit outputs. */ // "importHelpers": true, /* Import emit helpers from 'tslib'. */ @@ -41,6 +42,7 @@ // "rootDirs": [], /* List of root folders whose combined content represents the structure of the project at runtime. */ // "typeRoots": [], /* List of folders to include type definitions from. */ // "types": [], /* Type declaration files to be included in compilation. */ + // "references": [], /* Projects to reference */ // "allowSyntheticDefaultImports": true, /* Allow default imports from modules with no default export. This does not affect code emit, just typechecking. */ "esModuleInterop": true /* Enables emit interoperability between CommonJS and ES Modules via creation of namespace objects for all imports. Implies 'allowSyntheticDefaultImports'. */ // "preserveSymlinks": true, /* Do not resolve the real path of symlinks. */ diff --git a/tests/baselines/reference/tsConfig/Initialized TSConfig with enum value compiler options/tsconfig.json b/tests/baselines/reference/tsConfig/Initialized TSConfig with enum value compiler options/tsconfig.json index 168fcc6dfe637..e79b837a4f657 100644 --- a/tests/baselines/reference/tsConfig/Initialized TSConfig with enum value compiler options/tsconfig.json +++ b/tests/baselines/reference/tsConfig/Initialized TSConfig with enum value compiler options/tsconfig.json @@ -13,6 +13,7 @@ // "outFile": "./", /* Concatenate and emit output to single file. */ // "outDir": "./", /* Redirect output structure to the directory. */ // "rootDir": "./", /* Specify the root directory of input files. Use to control the output directory structure with --outDir. */ + // "composite": true, /* Enable project compilation */ // "removeComments": true, /* Do not emit comments to output. */ // "noEmit": true, /* Do not emit outputs. */ // "importHelpers": true, /* Import emit helpers from 'tslib'. */ @@ -41,6 +42,7 @@ // "rootDirs": [], /* List of root folders whose combined content represents the structure of the project at runtime. */ // "typeRoots": [], /* List of folders to include type definitions from. */ // "types": [], /* Type declaration files to be included in compilation. */ + // "references": [], /* Projects to reference */ // "allowSyntheticDefaultImports": true, /* Allow default imports from modules with no default export. This does not affect code emit, just typechecking. */ "esModuleInterop": true /* Enables emit interoperability between CommonJS and ES Modules via creation of namespace objects for all imports. Implies 'allowSyntheticDefaultImports'. */ // "preserveSymlinks": true, /* Do not resolve the real path of symlinks. */ diff --git a/tests/baselines/reference/tsConfig/Initialized TSConfig with files options/tsconfig.json b/tests/baselines/reference/tsConfig/Initialized TSConfig with files options/tsconfig.json index e87936a34fc8d..b867fd0c6ac99 100644 --- a/tests/baselines/reference/tsConfig/Initialized TSConfig with files options/tsconfig.json +++ b/tests/baselines/reference/tsConfig/Initialized TSConfig with files options/tsconfig.json @@ -13,6 +13,7 @@ // "outFile": "./", /* Concatenate and emit output to single file. */ // "outDir": "./", /* Redirect output structure to the directory. */ // "rootDir": "./", /* Specify the root directory of input files. Use to control the output directory structure with --outDir. */ + // "composite": true, /* Enable project compilation */ // "removeComments": true, /* Do not emit comments to output. */ // "noEmit": true, /* Do not emit outputs. */ // "importHelpers": true, /* Import emit helpers from 'tslib'. */ @@ -41,6 +42,7 @@ // "rootDirs": [], /* List of root folders whose combined content represents the structure of the project at runtime. */ // "typeRoots": [], /* List of folders to include type definitions from. */ // "types": [], /* Type declaration files to be included in compilation. */ + // "references": [], /* Projects to reference */ // "allowSyntheticDefaultImports": true, /* Allow default imports from modules with no default export. This does not affect code emit, just typechecking. */ "esModuleInterop": true /* Enables emit interoperability between CommonJS and ES Modules via creation of namespace objects for all imports. Implies 'allowSyntheticDefaultImports'. */ // "preserveSymlinks": true, /* Do not resolve the real path of symlinks. */ diff --git a/tests/baselines/reference/tsConfig/Initialized TSConfig with incorrect compiler option value/tsconfig.json b/tests/baselines/reference/tsConfig/Initialized TSConfig with incorrect compiler option value/tsconfig.json index c00426a373b6a..c30897a71403d 100644 --- a/tests/baselines/reference/tsConfig/Initialized TSConfig with incorrect compiler option value/tsconfig.json +++ b/tests/baselines/reference/tsConfig/Initialized TSConfig with incorrect compiler option value/tsconfig.json @@ -13,6 +13,7 @@ // "outFile": "./", /* Concatenate and emit output to single file. */ // "outDir": "./", /* Redirect output structure to the directory. */ // "rootDir": "./", /* Specify the root directory of input files. Use to control the output directory structure with --outDir. */ + // "composite": true, /* Enable project compilation */ // "removeComments": true, /* Do not emit comments to output. */ // "noEmit": true, /* Do not emit outputs. */ // "importHelpers": true, /* Import emit helpers from 'tslib'. */ @@ -41,6 +42,7 @@ // "rootDirs": [], /* List of root folders whose combined content represents the structure of the project at runtime. */ // "typeRoots": [], /* List of folders to include type definitions from. */ // "types": [], /* Type declaration files to be included in compilation. */ + // "references": [], /* Projects to reference */ // "allowSyntheticDefaultImports": true, /* Allow default imports from modules with no default export. This does not affect code emit, just typechecking. */ "esModuleInterop": true /* Enables emit interoperability between CommonJS and ES Modules via creation of namespace objects for all imports. Implies 'allowSyntheticDefaultImports'. */ // "preserveSymlinks": true, /* Do not resolve the real path of symlinks. */ diff --git a/tests/baselines/reference/tsConfig/Initialized TSConfig with incorrect compiler option/tsconfig.json b/tests/baselines/reference/tsConfig/Initialized TSConfig with incorrect compiler option/tsconfig.json index 28ccc78540c7a..c1bd336691902 100644 --- a/tests/baselines/reference/tsConfig/Initialized TSConfig with incorrect compiler option/tsconfig.json +++ b/tests/baselines/reference/tsConfig/Initialized TSConfig with incorrect compiler option/tsconfig.json @@ -13,6 +13,7 @@ // "outFile": "./", /* Concatenate and emit output to single file. */ // "outDir": "./", /* Redirect output structure to the directory. */ // "rootDir": "./", /* Specify the root directory of input files. Use to control the output directory structure with --outDir. */ + // "composite": true, /* Enable project compilation */ // "removeComments": true, /* Do not emit comments to output. */ // "noEmit": true, /* Do not emit outputs. */ // "importHelpers": true, /* Import emit helpers from 'tslib'. */ @@ -41,6 +42,7 @@ // "rootDirs": [], /* List of root folders whose combined content represents the structure of the project at runtime. */ // "typeRoots": [], /* List of folders to include type definitions from. */ // "types": [], /* Type declaration files to be included in compilation. */ + // "references": [], /* Projects to reference */ // "allowSyntheticDefaultImports": true, /* Allow default imports from modules with no default export. This does not affect code emit, just typechecking. */ "esModuleInterop": true /* Enables emit interoperability between CommonJS and ES Modules via creation of namespace objects for all imports. Implies 'allowSyntheticDefaultImports'. */ // "preserveSymlinks": true, /* Do not resolve the real path of symlinks. */ diff --git a/tests/baselines/reference/tsConfig/Initialized TSConfig with list compiler options with enum value/tsconfig.json b/tests/baselines/reference/tsConfig/Initialized TSConfig with list compiler options with enum value/tsconfig.json index a4d7d4ffc2c90..8f8a22fb16ebe 100644 --- a/tests/baselines/reference/tsConfig/Initialized TSConfig with list compiler options with enum value/tsconfig.json +++ b/tests/baselines/reference/tsConfig/Initialized TSConfig with list compiler options with enum value/tsconfig.json @@ -13,6 +13,7 @@ // "outFile": "./", /* Concatenate and emit output to single file. */ // "outDir": "./", /* Redirect output structure to the directory. */ // "rootDir": "./", /* Specify the root directory of input files. Use to control the output directory structure with --outDir. */ + // "composite": true, /* Enable project compilation */ // "removeComments": true, /* Do not emit comments to output. */ // "noEmit": true, /* Do not emit outputs. */ // "importHelpers": true, /* Import emit helpers from 'tslib'. */ @@ -41,6 +42,7 @@ // "rootDirs": [], /* List of root folders whose combined content represents the structure of the project at runtime. */ // "typeRoots": [], /* List of folders to include type definitions from. */ // "types": [], /* Type declaration files to be included in compilation. */ + // "references": [], /* Projects to reference */ // "allowSyntheticDefaultImports": true, /* Allow default imports from modules with no default export. This does not affect code emit, just typechecking. */ "esModuleInterop": true /* Enables emit interoperability between CommonJS and ES Modules via creation of namespace objects for all imports. Implies 'allowSyntheticDefaultImports'. */ // "preserveSymlinks": true, /* Do not resolve the real path of symlinks. */ diff --git a/tests/baselines/reference/tsConfig/Initialized TSConfig with list compiler options/tsconfig.json b/tests/baselines/reference/tsConfig/Initialized TSConfig with list compiler options/tsconfig.json index d01ba76d82237..1f7ba4dc581e2 100644 --- a/tests/baselines/reference/tsConfig/Initialized TSConfig with list compiler options/tsconfig.json +++ b/tests/baselines/reference/tsConfig/Initialized TSConfig with list compiler options/tsconfig.json @@ -13,6 +13,7 @@ // "outFile": "./", /* Concatenate and emit output to single file. */ // "outDir": "./", /* Redirect output structure to the directory. */ // "rootDir": "./", /* Specify the root directory of input files. Use to control the output directory structure with --outDir. */ + // "composite": true, /* Enable project compilation */ // "removeComments": true, /* Do not emit comments to output. */ // "noEmit": true, /* Do not emit outputs. */ // "importHelpers": true, /* Import emit helpers from 'tslib'. */ @@ -41,6 +42,7 @@ // "rootDirs": [], /* List of root folders whose combined content represents the structure of the project at runtime. */ // "typeRoots": [], /* List of folders to include type definitions from. */ "types": ["jquery","mocha"], /* Type declaration files to be included in compilation. */ + // "references": [], /* Projects to reference */ // "allowSyntheticDefaultImports": true, /* Allow default imports from modules with no default export. This does not affect code emit, just typechecking. */ "esModuleInterop": true /* Enables emit interoperability between CommonJS and ES Modules via creation of namespace objects for all imports. Implies 'allowSyntheticDefaultImports'. */ // "preserveSymlinks": true, /* Do not resolve the real path of symlinks. */ From f83ad4c2b906b6e3001b3faa8bdd4c903d79f83f Mon Sep 17 00:00:00 2001 From: Ryan Cavanaugh Date: Thu, 29 Mar 2018 15:40:35 -0700 Subject: [PATCH 02/28] Merge with master --- src/compiler/emitter.ts | 66 ++++--------------- src/harness/unittests/projectReferences.ts | 1 + .../reference/api/tsserverlibrary.d.ts | 19 +----- tests/baselines/reference/api/typescript.d.ts | 19 +----- 4 files changed, 18 insertions(+), 87 deletions(-) diff --git a/src/compiler/emitter.ts b/src/compiler/emitter.ts index 837e48882c66d..a0ca5d327dde0 100644 --- a/src/compiler/emitter.ts +++ b/src/compiler/emitter.ts @@ -17,19 +17,15 @@ namespace ts { * Else, calls `getSourceFilesToEmit` with the (optional) target source file to determine the list of source files to emit. */ export function forEachEmittedFile( - host: EmitHost, action: (emitFileNames: EmitFileNames, sourceFileOrBundle: SourceFile | Bundle, emitOnlyDtsFiles: boolean) => T, + host: EmitHost, action: (emitFileNames: EmitFileNames, sourceFileOrBundle: SourceFile | Bundle) => T, sourceFilesOrTargetSourceFile?: ReadonlyArray | SourceFile, emitOnlyDtsFiles?: boolean) { const sourceFiles = isArray(sourceFilesOrTargetSourceFile) ? sourceFilesOrTargetSourceFile : getSourceFilesToEmit(host, sourceFilesOrTargetSourceFile); const options = host.getCompilerOptions(); if (options.outFile || options.out) { if (sourceFiles.length) { - const bundle = createBundle(sourceFiles); - const jsFilePath = options.outFile || options.out; - const sourceMapFilePath = getSourceMapFilePath(jsFilePath, options); - const declarationFilePath = options.declaration ? removeFileExtension(jsFilePath) + Extension.Dts : ""; - const bundleInfoPath = options.references && jsFilePath && (removeFileExtension(jsFilePath) + ".bundle_info"); - const result = action({ jsFilePath, sourceMapFilePath, declarationFilePath, declarationMapPath: undefined, bundleInfoPath }, createBundle(sourceFiles, host.getPrependNodes()), emitOnlyDtsFiles); + const bundle = createBundle(sourceFiles, host.getPrependNodes()); + const result = action(getOutputPathsFor(bundle, host, emitOnlyDtsFiles), bundle); if (result) { return result; } @@ -37,10 +33,7 @@ namespace ts { } else { for (const sourceFile of sourceFiles) { - const jsFilePath = getOwnEmitOutputFilePath(sourceFile, host, getOutputExtension(sourceFile, options)); - const sourceMapFilePath = getSourceMapFilePath(jsFilePath, options); - const declarationFilePath = !isSourceFileJavaScript(sourceFile) && (emitOnlyDtsFiles || options.declaration || options.composite) ? getDeclarationEmitOutputFilePath(sourceFile, host) : undefined; - const result = action({ jsFilePath, sourceMapFilePath, declarationFilePath, declarationMapPath: undefined, bundleInfoPath: undefined }, sourceFile, emitOnlyDtsFiles); + const result = action(getOutputPathsFor(sourceFile, host, emitOnlyDtsFiles), sourceFile); if (result) { return result; } @@ -56,7 +49,8 @@ namespace ts { const sourceMapFilePath = getSourceMapFilePath(jsFilePath, options); const declarationFilePath = (forceDtsPaths || options.declaration) ? removeFileExtension(jsFilePath) + Extension.Dts : undefined; const declarationMapPath = getAreDeclarationMapsEnabled(options) ? declarationFilePath + ".map" : undefined; - return { jsFilePath, sourceMapFilePath, declarationFilePath, declarationMapPath }; + const bundleInfoPath = options.references && jsFilePath && (removeFileExtension(jsFilePath) + ".bundle_info"); + return { jsFilePath, sourceMapFilePath, declarationFilePath, declarationMapPath, bundleInfoPath }; } else { const jsFilePath = getOwnEmitOutputFilePath(sourceFile, host, getOutputExtension(sourceFile, options)); @@ -65,7 +59,7 @@ namespace ts { const isJs = isSourceFileJavaScript(sourceFile); const declarationFilePath = ((forceDtsPaths || options.declaration) && !isJs) ? getDeclarationEmitOutputFilePath(sourceFile, host) : undefined; const declarationMapPath = getAreDeclarationMapsEnabled(options) ? declarationFilePath + ".map" : undefined; - return { jsFilePath, sourceMapFilePath, declarationFilePath, declarationMapPath }; + return { jsFilePath, sourceMapFilePath, declarationFilePath, declarationMapPath, bundleInfoPath: undefined }; } } @@ -98,16 +92,6 @@ namespace ts { return Extension.Js; } -<<<<<<< HEAD -======= - function getOriginalSourceFileOrBundle(sourceFileOrBundle: SourceFile | Bundle) { - if (sourceFileOrBundle.kind === SyntaxKind.Bundle) { - return updateBundle(sourceFileOrBundle, sameMap(sourceFileOrBundle.sourceFiles, getOriginalSourceFile), sourceFileOrBundle.prepends); - } - return getOriginalSourceFile(sourceFileOrBundle); - } - ->>>>>>> Project References /*@internal*/ // targetSourceFile is when users only want one file in entire project to be emitted. This is used in compileOnSave feature export function emitFiles(resolver: EmitResolver, host: EmitHost, targetSourceFile: SourceFile, emitOnlyDtsFiles?: boolean, transformers?: TransformerFactory[]): EmitResult { @@ -146,26 +130,9 @@ namespace ts { sourceMaps: sourceMapDataList }; -<<<<<<< HEAD - function emitSourceFileOrBundle({ jsFilePath, sourceMapFilePath, declarationFilePath, declarationMapPath }: EmitFileNames, sourceFileOrBundle: SourceFile | Bundle) { - emitJsFileOrBundle(sourceFileOrBundle, jsFilePath, sourceMapFilePath); + function emitSourceFileOrBundle({ jsFilePath, sourceMapFilePath, declarationFilePath, declarationMapPath, bundleInfoPath }: EmitFileNames, sourceFileOrBundle: SourceFile | Bundle) { + emitJsFileOrBundle(sourceFileOrBundle, jsFilePath, sourceMapFilePath, bundleInfoPath); emitDeclarationFileOrBundle(sourceFileOrBundle, declarationFilePath, declarationMapPath); -======= - function emitSourceFileOrBundle({ jsFilePath, sourceMapFilePath, declarationFilePath, bundleInfoPath }: EmitFileNames, sourceFileOrBundle: SourceFile | Bundle) { - // Make sure not to write js file and source map file if any of them cannot be written - if (!host.isEmitBlocked(jsFilePath) && !compilerOptions.noEmit && !compilerOptions.emitDeclarationOnly) { - if (!emitOnlyDtsFiles) { - printSourceFileOrBundle(jsFilePath, sourceMapFilePath, bundleInfoPath, sourceFileOrBundle); - } - } - else { - emitSkipped = true; - } - - if (declarationFilePath) { - emitSkipped = writeDeclarationFile(declarationFilePath, getOriginalSourceFileOrBundle(sourceFileOrBundle), host, resolver, emitterDiagnostics, emitOnlyDtsFiles) || emitSkipped; - } ->>>>>>> Project References if (!emitSkipped && emittedFilesList) { if (!emitOnlyDtsFiles) { @@ -183,8 +150,7 @@ namespace ts { } } -<<<<<<< HEAD - function emitJsFileOrBundle(sourceFileOrBundle: SourceFile | Bundle, jsFilePath: string, sourceMapFilePath: string) { + function emitJsFileOrBundle(sourceFileOrBundle: SourceFile | Bundle, jsFilePath: string, sourceMapFilePath: string, bundleInfoPath: string | undefined) { const sourceFiles = isSourceFile(sourceFileOrBundle) ? [sourceFileOrBundle] : sourceFileOrBundle.sourceFiles; // Make sure not to write js file and source map file if any of them cannot be written if (host.isEmitBlocked(jsFilePath) || compilerOptions.noEmit || compilerOptions.emitDeclarationOnly) { @@ -216,7 +182,7 @@ namespace ts { onSetSourceFile: setSourceFile, }); - printSourceFileOrBundle(jsFilePath, sourceMapFilePath, isSourceFile(sourceFileOrBundle) ? transform.transformed[0] : createBundle(transform.transformed), printer, sourceMap); + printSourceFileOrBundle(jsFilePath, sourceMapFilePath, isSourceFile(sourceFileOrBundle) ? transform.transformed[0] : createBundle(transform.transformed), bundleInfoPath, printer, sourceMap); // Clean up emit nodes on parse tree transform.dispose(); @@ -253,15 +219,12 @@ namespace ts { const declBlocked = (!!declarationTransform.diagnostics && !!declarationTransform.diagnostics.length) || !!host.isEmitBlocked(declarationFilePath) || !!compilerOptions.noEmit; emitSkipped = emitSkipped || declBlocked; if (!declBlocked || emitOnlyDtsFiles) { - printSourceFileOrBundle(declarationFilePath, declarationMapPath, declarationTransform.transformed[0], declarationPrinter, declarationSourceMap); + printSourceFileOrBundle(declarationFilePath, declarationMapPath, declarationTransform.transformed[0], /* bundleInfopath*/ undefined, declarationPrinter, declarationSourceMap); } declarationTransform.dispose(); } - function printSourceFileOrBundle(jsFilePath: string, sourceMapFilePath: string | undefined, sourceFileOrBundle: SourceFile | Bundle, printer: Printer, mapRecorder: SourceMapWriter) { -======= - function printSourceFileOrBundle(jsFilePath: string, sourceMapFilePath: string, bundleInfoPath: string | undefined, sourceFileOrBundle: SourceFile | Bundle) { ->>>>>>> Project References + function printSourceFileOrBundle(jsFilePath: string, sourceMapFilePath: string | undefined, sourceFileOrBundle: SourceFile | Bundle, bundleInfoPath: string | undefined, printer: Printer, mapRecorder: SourceMapWriter) { const bundle = sourceFileOrBundle.kind === SyntaxKind.Bundle ? sourceFileOrBundle : undefined; const sourceFile = sourceFileOrBundle.kind === SyntaxKind.SourceFile ? sourceFileOrBundle : undefined; const sourceFiles = bundle ? bundle.sourceFiles : [sourceFile]; @@ -507,11 +470,8 @@ namespace ts { bundleInfo.originalOffset = writer.getTextPos(); } emitHelpersIndirect(bundle); -<<<<<<< HEAD emitSyntheticTripleSlashReferencesIfNeeded(bundle); -======= ->>>>>>> Project References for (const sourceFile of bundle.sourceFiles) { print(EmitHint.SourceFile, sourceFile, sourceFile); } diff --git a/src/harness/unittests/projectReferences.ts b/src/harness/unittests/projectReferences.ts index cd200d3a241df..4a1e1bdf0501f 100644 --- a/src/harness/unittests/projectReferences.ts +++ b/src/harness/unittests/projectReferences.ts @@ -260,6 +260,7 @@ namespace ts { "/alpha": { files: { "/alpha/src/a.ts": "export const m: number = 3;" }, options: { + declaration: true, outDir: "bin" }, references: [] diff --git a/tests/baselines/reference/api/tsserverlibrary.d.ts b/tests/baselines/reference/api/tsserverlibrary.d.ts index e41473b8c48b7..756e5a75f52aa 100644 --- a/tests/baselines/reference/api/tsserverlibrary.d.ts +++ b/tests/baselines/reference/api/tsserverlibrary.d.ts @@ -388,17 +388,10 @@ declare namespace ts { FirstBinaryOperator = 27, LastBinaryOperator = 70, FirstNode = 145, -<<<<<<< HEAD - FirstJSDocNode = 274, - LastJSDocNode = 292, - FirstJSDocTagNode = 284, - LastJSDocTagNode = 292 -======= FirstJSDocNode = 275, LastJSDocNode = 293, FirstJSDocTagNode = 285, - LastJSDocTagNode = 293, ->>>>>>> Project References + LastJSDocTagNode = 293 } enum NodeFlags { None = 0, @@ -2612,12 +2605,8 @@ declare namespace ts { Expression = 1, IdentifierName = 2, MappedTypeParameter = 3, -<<<<<<< HEAD - Unspecified = 4 -======= Prepend = 4, - Unspecified = 5, ->>>>>>> Project References + Unspecified = 5 } interface TransformationContext { /** Gets the compiler options supplied to the transformer. */ @@ -3972,13 +3961,9 @@ declare namespace ts { * @param configFileParsingDiagnostics - error during config file parsing * @returns A 'Program' object. */ -<<<<<<< HEAD function createProgram(rootNames: ReadonlyArray, options: CompilerOptions, host?: CompilerHost, oldProgram?: Program, configFileParsingDiagnostics?: ReadonlyArray): Program; -======= - function createProgram(rootNames: ReadonlyArray, options: CompilerOptions, host?: CompilerHost, oldProgram?: Program): Program; function getProjectReferenceFileNames(host: CompilerHost, rootOptions: CompilerOptions): string[] | undefined; function walkProjectReferenceGraph(host: CompilerHost, rootOptions: CompilerOptions, callback: (resolvedFile: string, referencedProject: CompilerOptions, settings: ProjectReference) => void, errorCallback?: (failedLocation: string) => void): void; ->>>>>>> Project References } declare namespace ts { interface Node { diff --git a/tests/baselines/reference/api/typescript.d.ts b/tests/baselines/reference/api/typescript.d.ts index 3f120f5f2ac26..5ac93ded97110 100644 --- a/tests/baselines/reference/api/typescript.d.ts +++ b/tests/baselines/reference/api/typescript.d.ts @@ -388,17 +388,10 @@ declare namespace ts { FirstBinaryOperator = 27, LastBinaryOperator = 70, FirstNode = 145, -<<<<<<< HEAD - FirstJSDocNode = 274, - LastJSDocNode = 292, - FirstJSDocTagNode = 284, - LastJSDocTagNode = 292 -======= FirstJSDocNode = 275, LastJSDocNode = 293, FirstJSDocTagNode = 285, - LastJSDocTagNode = 293, ->>>>>>> Project References + LastJSDocTagNode = 293 } enum NodeFlags { None = 0, @@ -2612,12 +2605,8 @@ declare namespace ts { Expression = 1, IdentifierName = 2, MappedTypeParameter = 3, -<<<<<<< HEAD - Unspecified = 4 -======= Prepend = 4, - Unspecified = 5, ->>>>>>> Project References + Unspecified = 5 } interface TransformationContext { /** Gets the compiler options supplied to the transformer. */ @@ -3919,13 +3908,9 @@ declare namespace ts { * @param configFileParsingDiagnostics - error during config file parsing * @returns A 'Program' object. */ -<<<<<<< HEAD function createProgram(rootNames: ReadonlyArray, options: CompilerOptions, host?: CompilerHost, oldProgram?: Program, configFileParsingDiagnostics?: ReadonlyArray): Program; -======= - function createProgram(rootNames: ReadonlyArray, options: CompilerOptions, host?: CompilerHost, oldProgram?: Program): Program; function getProjectReferenceFileNames(host: CompilerHost, rootOptions: CompilerOptions): string[] | undefined; function walkProjectReferenceGraph(host: CompilerHost, rootOptions: CompilerOptions, callback: (resolvedFile: string, referencedProject: CompilerOptions, settings: ProjectReference) => void, errorCallback?: (failedLocation: string) => void): void; ->>>>>>> Project References } declare namespace ts { interface EmitOutput { From b0d49f99e7430f10c48420102bf863add8d120fa Mon Sep 17 00:00:00 2001 From: Ryan Cavanaugh Date: Fri, 6 Apr 2018 15:31:32 -0700 Subject: [PATCH 03/28] Move 'references' up to the 'files' level --- src/compiler/commandLineParser.ts | 118 ++++++++---- src/compiler/program.ts | 198 ++++++++++++--------- src/compiler/types.ts | 21 ++- src/compiler/watch.ts | 42 ----- src/harness/unittests/projectReferences.ts | 56 +----- 5 files changed, 229 insertions(+), 206 deletions(-) diff --git a/src/compiler/commandLineParser.ts b/src/compiler/commandLineParser.ts index 5515979791a81..986574cc91511 100644 --- a/src/compiler/commandLineParser.ts +++ b/src/compiler/commandLineParser.ts @@ -423,17 +423,6 @@ namespace ts { category: Diagnostics.Module_Resolution_Options, description: Diagnostics.Type_declaration_files_to_be_included_in_compilation }, - { - name: "references", - type: "list", - element: { - name: "references", - type: "object" - }, - showInSimplifiedHelpView: true, - category: Diagnostics.Module_Resolution_Options, - description: Diagnostics.Projects_to_reference - }, { name: "allowSyntheticDefaultImports", type: "boolean", @@ -824,12 +813,14 @@ namespace ts { export function parseCommandLine(commandLine: ReadonlyArray, readFile?: (path: string) => string | undefined): ParsedCommandLine { const options: CompilerOptions = {}; const fileNames: string[] = []; + const projectReferences: ProjectReference[] | undefined = undefined; const errors: Diagnostic[] = []; parseStrings(commandLine); return { options, fileNames, + projectReferences, errors }; @@ -943,6 +934,49 @@ namespace ts { return optionNameMap.get(optionName); } + + export type DiagnosticReporter = (diagnostic: Diagnostic) => void; + /** + * Reports config file diagnostics + */ + export interface ConfigFileDiagnosticsReporter { + /** + * Reports unrecoverable error when parsing config file + */ + onUnRecoverableConfigFileDiagnostic: DiagnosticReporter; + } + + /** + * Interface extending ParseConfigHost to support ParseConfigFile that reads config file and reports errors + */ + export interface ParseConfigFileHost extends ParseConfigHost, ConfigFileDiagnosticsReporter { + getCurrentDirectory(): string; + } + + /** + * Reads the config file, reports errors if any and exits if the config file cannot be found + */ + export function getParsedCommandLineOfConfigFile(configFileName: string, optionsToExtend: CompilerOptions, host: ParseConfigFileHost): ParsedCommandLine | undefined { + let configFileText: string; + try { + configFileText = host.readFile(configFileName); + } + catch (e) { + const error = createCompilerDiagnostic(Diagnostics.Cannot_read_file_0_Colon_1, configFileName, e.message); + host.onUnRecoverableConfigFileDiagnostic(error); + return undefined; + } + if (!configFileText) { + const error = createCompilerDiagnostic(Diagnostics.File_0_not_found, configFileName); + host.onUnRecoverableConfigFileDiagnostic(error); + return undefined; + } + + const result = parseJsonText(configFileName, configFileText); + const cwd = host.getCurrentDirectory(); + return parseJsonSourceFileConfigFileContent(result, host, getNormalizedAbsolutePath(getDirectoryPath(configFileName), cwd), optionsToExtend, getNormalizedAbsolutePath(configFileName, cwd)); + } + /** * Read tsconfig.json file * @param fileName The path to the config file @@ -1032,14 +1066,6 @@ namespace ts { type: "object" } }, - { - name: "projects", - type: "list", - element: { - name: "projects", - type: "string" - } - }, { name: "include", type: "list", @@ -1420,7 +1446,7 @@ namespace ts { for (let i = 0; i < nameColumn.length; i++) { const optionName = nameColumn[i]; const description = descriptionColumn[i]; - result.push(optionName && `${tab}${tab}${optionName}${ description && (makePadding(marginLength - optionName.length + 2) + description)}`); + result.push(optionName && `${tab}${tab}${optionName}${description && (makePadding(marginLength - optionName.length + 2) + description)}`); } if (fileNames.length) { result.push(`${tab}},`); @@ -1506,10 +1532,11 @@ namespace ts { const options = extend(existingOptions, parsedConfig.options || {}); options.configFilePath = configFileName && normalizeSlashes(configFileName); setConfigFileInOptions(options, sourceFile); - const { fileNames, wildcardDirectories, spec } = getFileNames(); + const { fileNames, wildcardDirectories, spec, projectReferences } = getFileNames(); return { options, fileNames, + projectReferences, typeAcquisition: parsedConfig.typeAcquisition || getDefaultTypeAcquisition(), raw, errors, @@ -1563,10 +1590,33 @@ namespace ts { } const result = matchFileNames(filesSpecs, includeSpecs, excludeSpecs, configFileName ? directoryOfCombinedPath(configFileName, basePath) : basePath, options, host, errors, extraFileExtensions, sourceFile); - if (result.fileNames.length === 0 && !hasProperty(raw, "files") && resolutionStack.length === 0 && !options.references) { + if (result.fileNames.length === 0 && !hasProperty(raw, "files") && resolutionStack.length === 0 && !hasProperty(raw, "references")) { errors.push(getErrorForNoInputFiles(result.spec, configFileName)); } + if (hasProperty(raw, "references") && !isNullOrUndefined(raw.references)) { + if (isArray(raw.references)) { + const references: ProjectReference[] = []; + for (const ref of raw.references) { + if (typeof ref.path !== "string") { + createCompilerDiagnosticOnlyIfJson(Diagnostics.Compiler_option_0_requires_a_value_of_type_1, "reference.path", "string"); + } + else { + references.push({ + path: getNormalizedAbsolutePath(ref.path, basePath), + originalPath: ref.path, + prepend: ref.prepend || false, + circular: ref.circular || false + }); + } + } + result.projectReferences = references; + } + else { + createCompilerDiagnosticOnlyIfJson(Diagnostics.Compiler_option_0_requires_a_value_of_type_1, "references", "Array"); + } + } + return result; } @@ -1610,13 +1660,13 @@ namespace ts { * It does *not* resolve the included files. */ function parseConfig( - json: any, - sourceFile: JsonSourceFile, - host: ParseConfigHost, - basePath: string, - configFileName: string, - resolutionStack: string[], - errors: Push, + json: any, + sourceFile: JsonSourceFile, + host: ParseConfigHost, + basePath: string, + configFileName: string, + resolutionStack: string[], + errors: Push, ): ParsedTsconfig { basePath = normalizeSlashes(basePath); const resolvedPath = getNormalizedAbsolutePath(configFileName || "", basePath); @@ -2056,7 +2106,7 @@ namespace ts { // new entries in these paths. const wildcardDirectories = getWildcardDirectories(validatedIncludeSpecs, validatedExcludeSpecs, basePath, host.useCaseSensitiveFileNames); - const spec: ConfigFileSpecs = { filesSpecs, includeSpecs, excludeSpecs, validatedIncludeSpecs, validatedExcludeSpecs, wildcardDirectories }; + const spec: ConfigFileSpecs = { filesSpecs, referencesSpecs: undefined, includeSpecs, excludeSpecs, validatedIncludeSpecs, validatedExcludeSpecs, wildcardDirectories }; return getFileNamesFromConfigSpecs(spec, basePath, options, host, extraFileExtensions); } @@ -2127,8 +2177,16 @@ namespace ts { const literalFiles = arrayFrom(literalFileMap.values()); const wildcardFiles = arrayFrom(wildcardFileMap.values()); + const projectReferences = spec.referencesSpecs && spec.referencesSpecs.map((r): ProjectReference => { + return { + ...r, + path: getNormalizedAbsolutePath(r.path, basePath) + }; + }); + return { fileNames: literalFiles.concat(wildcardFiles), + projectReferences, wildcardDirectories, spec }; diff --git a/src/compiler/program.ts b/src/compiler/program.ts index 791268f429a09..e9bdd36252039 100755 --- a/src/compiler/program.ts +++ b/src/compiler/program.ts @@ -337,7 +337,7 @@ namespace ts { const categoryColor = getCategoryFormat(diagnostic.category); const category = DiagnosticCategory[diagnostic.category].toLowerCase(); output += formatColorAndReset(category, categoryColor); - output += formatColorAndReset(` TS${ diagnostic.code }: `, ForegroundColorEscapeSequences.Grey); + output += formatColorAndReset(` TS${diagnostic.code}: `, ForegroundColorEscapeSequences.Grey); output += flattenDiagnosticMessageText(diagnostic.messageText, host.getNewLine()); if (diagnostic.file) { @@ -479,6 +479,16 @@ namespace ts { ); } + function createCreateProgramOptions(rootNames: ReadonlyArray, options: CompilerOptions, host?: CompilerHost, oldProgram?: Program, configFileParsingDiagnostics?: ReadonlyArray): CreateProgramOptions { + return { + rootNames, + options, + host, + oldProgram, + configFileParsingDiagnostics + }; + } + /** * Create a new 'Program' instance. A Program is an immutable collection of 'SourceFile's and a 'CompilerOptions' * that represent a compilation unit. @@ -493,7 +503,13 @@ namespace ts { * @param configFileParsingDiagnostics - error during config file parsing * @returns A 'Program' object. */ - export function createProgram(rootNames: ReadonlyArray, options: CompilerOptions, host?: CompilerHost, oldProgram?: Program, configFileParsingDiagnostics?: ReadonlyArray): Program { + export function createProgram(createProgramOptions: CreateProgramOptions): Program; + export function createProgram(rootNames: ReadonlyArray, options: CompilerOptions, host?: CompilerHost, oldProgram?: Program, configFileParsingDiagnostics?: ReadonlyArray): Program; + export function createProgram(rootNamesOrOptions: ReadonlyArray | CreateProgramOptions, _options?: CompilerOptions, _host?: CompilerHost, _oldProgram?: Program, _configFileParsingDiagnostics?: ReadonlyArray): Program { + const createProgramOptions = isArray(rootNamesOrOptions) ? createCreateProgramOptions(rootNamesOrOptions, _options, _host, _oldProgram, _configFileParsingDiagnostics) : rootNamesOrOptions; + const { rootNames, options, configFileParsingDiagnostics, projectReferences } = createProgramOptions; + let { host, oldProgram } = createProgramOptions; + let program: Program; let files: SourceFile[] = []; let commonSourceDirectory: string; @@ -539,6 +555,7 @@ namespace ts { // Map storing if there is emit blocking diagnostics for given input const hasEmitBlockingDiagnostics = createMap(); let _compilerOptionsObjectLiteralSyntax: ObjectLiteralExpression; + let _referencesArrayLiteralSyntax: ArrayLiteralExpression; let moduleResolutionCache: ModuleResolutionCache; let resolveModuleNamesWorker: (moduleNames: string[], containingFile: string, reusedNames?: string[]) => ResolvedModuleFull[]; @@ -584,11 +601,18 @@ namespace ts { // used to track cases when two file names differ only in casing const filesByNameIgnoreCase = host.useCaseSensitiveFileNames() ? createMap() : undefined; - const referencedProjectOutFiles: Path[] = []; - const projectReferenceRedirects = createProjectReferenceRedirects(options); - checkProjectReferenceGraph(); - for (const referencedProjectFile of referencedProjectOutFiles) { - processSourceFile(referencedProjectFile, /*isDefaultLib*/ false, /*packageId*/ undefined); + // A parallel array to projectReferences storing the results of reading in the referenced tsconfig files + const resolvedProjectReferences: ParsedCommandLine[] = []; + const projectReferenceRedirects: Map = createMap(); + if (projectReferences) { + for (const ref of projectReferences) { + const parsedRef = parseProjectReferenceConfigFile(ref); + resolvedProjectReferences.push(parsedRef); + if (parsedRef.options.outFile) { + processSourceFile(parsedRef.options.outFile, /*isDefaultLib*/ false, /*packageId*/ undefined); + } + addProjectReferenceRedirects(parsedRef, projectReferenceRedirects); + } } const shouldCreateNewSourceFile = shouldProgramCreateNewSourceFiles(oldProgram, options); @@ -1122,20 +1146,22 @@ namespace ts { } function getPrependNodes(): PrependNode[] { - if (!options.references) { + if (!projectReferences) { return emptyArray; } const nodes: PrependNode[] = []; - walkProjectReferenceGraph(host, options, (_file, proj, opts) => { - if (opts.prepend) { - const dtsFilename = changeExtension(proj.outFile, ".d.ts"); - const js = host.readFile(proj.outFile) || `/* Input file ${proj.outFile} was missing */\r\n`; - const dts = host.readFile(dtsFilename) || `/* Input file ${proj.dtsFilename} was missing */\r\n`; + for (let i = 0; i < projectReferences.length; i++) { + const ref = projectReferences[i]; + const resolvedRef = resolvedProjectReferences[i]; + if (ref.prepend) { + const dtsFilename = changeExtension(resolvedRef.options.outFile, ".d.ts"); + const js = host.readFile(resolvedRef.options.outFile) || `/* Input file ${resolvedRef.options.outFile} was missing */\r\n`; + const dts = host.readFile(dtsFilename) || `/* Input file ${dtsFilename} was missing */\r\n`; const node = createPrepend(js, dts); nodes.push(node); } - }); + } return nodes; } @@ -2119,67 +2145,14 @@ namespace ts { return allFilesBelongToPath; } - function createProjectReferenceRedirects(rootOptions: CompilerOptions): Map { - const result = createMap(); - const handledProjects = createMap(); - - walkProjectReferenceGraph(host, rootOptions, createMapping); - - function createMapping(resolvedFile: string, referencedProject: CompilerOptions, reference: ProjectReference) { - const rootDir = normalizePath(referencedProject.rootDir || getDirectoryPath(resolvedFile)); - result.set(rootDir, referencedProject.outDir); - // If this project uses outFile, add the outFile .d.ts to our compilation - // Note: We don't enumerate the input files to try to find corresponding .d.ts - // files because we can't know from the outside whether they're modules or not - if (referencedProject.outFile) { - const outFile = combinePaths(referencedProject.outDir, changeExtension(referencedProject.outFile, ".d.ts")); - referencedProjectOutFiles.push(toPath(outFile)); - if (reference.prepend) { - // Don't recurse - it'll cause multi-level outfile compilations - // to get duplicate definitions from the up-up-stream .d.ts! - return; - } - } - - // Circularity check - resolvedFile = normalizePath(resolvedFile); - if (!handledProjects.has(resolvedFile)) { - handledProjects.set(resolvedFile, true); - walkProjectReferenceGraph(host, referencedProject, createMapping); - } - } - return result; + function parseProjectReferenceConfigFile(ref: ProjectReference): ParsedCommandLine { + const refPath = resolveProjectReferencePath(host, ref); + return getParsedCommandLineOfConfigFile(refPath, {}, parseConfigHostFromCompilerHost(host)); } - function checkProjectReferenceGraph() { - // Checks the following conditions: - // * Any referenced project has project: true - // * No circularities exist - - const illegalRefs = createMap(); - const cycleName: string[] = [options.configFilePath || host.getCurrentDirectory()]; - - walkProjectReferenceGraph(host, options, checkReference); - - function checkReference(fileName: string, opts: CompilerOptions) { - const normalizedPath = normalizePath(fileName); - if (illegalRefs.has(normalizedPath)) { - createDiagnosticForOptionName(Diagnostics.Project_references_may_not_form_a_circular_graph_Cycle_detected_Colon_0, cycleName.map(normalizePath).map(s => host.getNewLine() + " " + s).join(" -> ")); - return; - } - if (opts === undefined) { - Debug.fail("Options cannot be undefined"); - return; - } - if (!opts.composite) { - createDiagnosticForOptionName(Diagnostics.Referenced_project_0_must_have_setting_composite_Colon_true, fileName); - } - illegalRefs.set(normalizedPath, true); - cycleName.push(normalizedPath); - walkProjectReferenceGraph(host, opts, checkReference); - cycleName.pop(); - illegalRefs.delete(normalizedPath); - } + function addProjectReferenceRedirects(referencedProject: ParsedCommandLine, target: Map) { + const rootDir = normalizePath(referencedProject.options.rootDir || getDirectoryPath(referencedProject.options.configFilePath)); + target.set(rootDir, referencedProject.options.outDir); } function verifyCompilerOptions() { @@ -2220,6 +2193,23 @@ namespace ts { } } + if (projectReferences) { + for (let i = 0; i < projectReferences.length; i++) { + const ref = projectReferences[i]; + const resolvedRef = resolvedProjectReferences[i]; + if (resolvedRef === undefined) { + createDiagnosticForReference(i, Diagnostics.File_0_does_not_exist, ref.path); + continue; + } + if (!resolvedRef.options.composite) { + createDiagnosticForReference(i, Diagnostics.Referenced_project_0_must_have_setting_composite_Colon_true, ref.path); + } + if (resolvedRef.options.declaration === false) { + createDiagnosticForReference(i, Diagnostics.Referenced_project_0_must_have_declaration_Colon_true, ref.path); + } + } + } + if (options.paths) { for (const key in options.paths) { if (!hasProperty(options.paths, key)) { @@ -2469,6 +2459,16 @@ namespace ts { createDiagnosticForOption(/*onKey*/ false, option1, /*option2*/ undefined, message, arg0); } + function createDiagnosticForReference(index: number, message: DiagnosticMessage, arg0?: string | number) { + const referencesSyntax = getProjectReferencesSyntax(); + if (referencesSyntax) { + if (createOptionDiagnosticInArrayLiteralSyntax(referencesSyntax, index, message, arg0)) { + return; + } + } + programDiagnostics.add(createCompilerDiagnostic(message, arg0)); + } + function createDiagnosticForOption(onKey: boolean, option1: string, option2: string, message: DiagnosticMessage, arg0: string | number, arg1?: string | number, arg2?: string | number) { const compilerOptionsObjectLiteralSyntax = getCompilerOptionsObjectLiteralSyntax(); const needCompilerDiagnostic = !compilerOptionsObjectLiteralSyntax || @@ -2479,6 +2479,21 @@ namespace ts { } } + function getProjectReferencesSyntax(): ArrayLiteralExpression | null { + if (_referencesArrayLiteralSyntax === undefined) { + _referencesArrayLiteralSyntax = null; // tslint:disable-line:no-null-keyword + if (options.configFile && options.configFile.jsonObject) { + for (const prop of getPropertyAssignment(options.configFile.jsonObject, "references")) { + if (isArrayLiteralExpression(prop.initializer)) { + _referencesArrayLiteralSyntax = prop.initializer; + break; + } + } + } + } + return _referencesArrayLiteralSyntax; + } + function getCompilerOptionsObjectLiteralSyntax() { if (_compilerOptionsObjectLiteralSyntax === undefined) { _compilerOptionsObjectLiteralSyntax = null; // tslint:disable-line:no-null-keyword @@ -2502,6 +2517,14 @@ namespace ts { return !!props.length; } + function createOptionDiagnosticInArrayLiteralSyntax(arrayLiteral: ArrayLiteralExpression, index: number, message: DiagnosticMessage, arg0: string | number, arg1?: string | number, arg2?: string | number): boolean { + if (arrayLiteral.elements.length <= index) { + // Out-of-bounds + return false; + } + programDiagnostics.add(createDiagnosticForNodeInSourceFile(options.configFile, arrayLiteral.elements[index], message, arg0, arg1, arg2)); + } + function blockEmittingOfFile(emitFileName: string, diag: Diagnostic) { hasEmitBlockingDiagnostics.set(toPath(emitFileName), true); programDiagnostics.add(diag); @@ -2543,22 +2566,25 @@ namespace ts { } } - function parseConfigHostFromCompilerHost(host: CompilerHost): ParseConfigHost { + function parseConfigHostFromCompilerHost(host: CompilerHost): ParseConfigFileHost { return { fileExists: host.fileExists, readDirectory: () => [], readFile: host.readFile, - useCaseSensitiveFileNames: host.useCaseSensitiveFileNames() + useCaseSensitiveFileNames: host.useCaseSensitiveFileNames(), + getCurrentDirectory: host.getCurrentDirectory, + onUnRecoverableConfigFileDiagnostic: () => undefined }; } + /* export function getProjectReferenceFileNames(host: CompilerHost, rootOptions: CompilerOptions): string[] | undefined { - if (rootOptions.references === undefined) { + if (rootOptions.projectReferences === undefined) { return []; } const result: string[] = []; - for (const ref of rootOptions.references) { + for (const ref of rootOptions.projectReferences) { const refPath = resolveProjectReferencePath(host, rootOptions.configFilePath, ref); if (!refPath || !host.fileExists(refPath)) { return undefined; @@ -2567,16 +2593,19 @@ namespace ts { } return result; } + */ - function resolveProjectReferencePath(host: CompilerHost, referencingConfigFilePath: string, ref: ProjectReference): string | undefined { - const rootPath = referencingConfigFilePath ? getDirectoryPath(normalizeSlashes(referencingConfigFilePath)) : host.getCurrentDirectory(); - let refPath = combinePaths(rootPath, ref.path); - if (!host.fileExists(refPath)) { - refPath = combinePaths(refPath, "tsconfig.json"); + /** + * Returns the target config filename of a project reference + */ + function resolveProjectReferencePath(host: CompilerHost, ref: ProjectReference): string | undefined { + if (!host.fileExists(ref.path)) { + return combinePaths(ref.path, "tsconfig.json"); } - return refPath; + return ref.path; } + /* export function walkProjectReferenceGraph(host: CompilerHost, rootOptions: CompilerOptions, callback: (resolvedFile: string, referencedProject: CompilerOptions, settings: ProjectReference) => void, errorCallback?: (failedLocation: string) => void) { @@ -2599,13 +2628,14 @@ namespace ts { continue; } const referenceJsonSource = parseJsonText(refPath, text); - const cmdLine = parseJsonSourceFileConfigFileContent(referenceJsonSource, configHost, getDirectoryPath(refPath), /*existingOptions*/ undefined, refPath); + const cmdLine = parseJsonSourceFileConfigFileContent(referenceJsonSource, configHost, getDirectoryPath(refPath), undefined, refPath); cmdLine.options.configFilePath = refPath; if (cmdLine.options) { callback(refPath, cmdLine.options, ref); } } } + */ /* @internal */ /** diff --git a/src/compiler/types.ts b/src/compiler/types.ts index ed73af60f18c0..f8fcbee926c86 100644 --- a/src/compiler/types.ts +++ b/src/compiler/types.ts @@ -4108,8 +4108,14 @@ namespace ts { } export interface ProjectReference { + /** A normalized path on disk */ path: string; + /** The path as the user originally wrote it */ + originalPath: string; + /** True if the output of this reference should be prepended to the output of this project. Only valid for --outFile compilations */ prepend?: boolean; + /** True if it is intended that this reference form a circularity */ + circular?: boolean; } export type CompilerOptionsValue = string | number | boolean | (string | number)[] | string[] | MapLike | PluginImport[] | ProjectReference[] | null | undefined; @@ -4183,7 +4189,6 @@ namespace ts { /* @internal */ pretty?: DiagnosticStyle; reactNamespace?: string; jsxFactory?: string; - references?: ProjectReference[]; composite?: boolean; removeComments?: boolean; rootDir?: string; @@ -4291,6 +4296,7 @@ namespace ts { options: CompilerOptions; typeAcquisition?: TypeAcquisition; fileNames: string[]; + projectReferences?: ReadonlyArray; raw?: any; errors: Diagnostic[]; wildcardDirectories?: MapLike; @@ -4306,6 +4312,7 @@ namespace ts { /* @internal */ export interface ConfigFileSpecs { filesSpecs: ReadonlyArray; + referencesSpecs: ReadonlyArray | undefined; /** * Present to report errors (user specified specs), validatedIncludeSpecs are used for file name matching */ @@ -4321,10 +4328,20 @@ namespace ts { export interface ExpandResult { fileNames: string[]; + projectReferences: ReadonlyArray | undefined; wildcardDirectories: MapLike; /* @internal */ spec: ConfigFileSpecs; } + export interface CreateProgramOptions { + rootNames: ReadonlyArray; + options: CompilerOptions; + projectReferences?: ReadonlyArray; + host?: CompilerHost; + oldProgram?: Program; + configFileParsingDiagnostics?: ReadonlyArray; + } + /* @internal */ export interface CommandLineOptionBase { name: string; @@ -4845,7 +4862,7 @@ namespace ts { isEmitBlocked(emitFileName: string): boolean; - getPrependNodes(): PrependNode[]; + getPrependNodes(): ReadonlyArray; writeFile: WriteFileCallback; } diff --git a/src/compiler/watch.ts b/src/compiler/watch.ts index ed5276e49a408..a510953090d52 100644 --- a/src/compiler/watch.ts +++ b/src/compiler/watch.ts @@ -60,13 +60,6 @@ namespace ts { }; } - /** - * Interface extending ParseConfigHost to support ParseConfigFile that reads config file and reports errors - */ - export interface ParseConfigFileHost extends ParseConfigHost, ConfigFileDiagnosticsReporter { - getCurrentDirectory(): string; - } - /** Parses config file using System interface */ export function parseConfigFileWithSystem(configFileName: string, optionsToExtend: CompilerOptions, system: System, reportDiagnostic: DiagnosticReporter) { const host: ParseConfigFileHost = system; @@ -76,30 +69,6 @@ namespace ts { return result; } - /** - * Reads the config file, reports errors if any and exits if the config file cannot be found - */ - export function getParsedCommandLineOfConfigFile(configFileName: string, optionsToExtend: CompilerOptions, host: ParseConfigFileHost): ParsedCommandLine | undefined { - let configFileText: string; - try { - configFileText = host.readFile(configFileName); - } - catch (e) { - const error = createCompilerDiagnostic(Diagnostics.Cannot_read_file_0_Colon_1, configFileName, e.message); - host.onUnRecoverableConfigFileDiagnostic(error); - return undefined; - } - if (!configFileText) { - const error = createCompilerDiagnostic(Diagnostics.File_0_not_found, configFileName); - host.onUnRecoverableConfigFileDiagnostic(error); - return undefined; - } - - const result = parseJsonText(configFileName, configFileText); - const cwd = host.getCurrentDirectory(); - return parseJsonSourceFileConfigFileContent(result, host, getNormalizedAbsolutePath(getDirectoryPath(configFileName), cwd), optionsToExtend, getNormalizedAbsolutePath(configFileName, cwd)); - } - /** * Program structure needed to emit the files and report diagnostics */ @@ -252,7 +221,6 @@ namespace ts { } namespace ts { - export type DiagnosticReporter = (diagnostic: Diagnostic) => void; export type WatchStatusReporter = (diagnostic: Diagnostic, newLine: string, options: CompilerOptions) => void; /** Create the program with rootNames and options, if they are undefined, oldProgram and new configFile diagnostics create new program */ export type CreateProgram = (rootNames: ReadonlyArray | undefined, options: CompilerOptions | undefined, host?: CompilerHost, oldProgram?: T, configFileParsingDiagnostics?: ReadonlyArray) => T; @@ -337,16 +305,6 @@ namespace ts { options: CompilerOptions; } - /** - * Reports config file diagnostics - */ - export interface ConfigFileDiagnosticsReporter { - /** - * Reports unrecoverable error when parsing config file - */ - onUnRecoverableConfigFileDiagnostic: DiagnosticReporter; - } - /** * Host to create watch with config file */ diff --git a/src/harness/unittests/projectReferences.ts b/src/harness/unittests/projectReferences.ts index 4a1e1bdf0501f..9008ad2cb5e6c 100644 --- a/src/harness/unittests/projectReferences.ts +++ b/src/harness/unittests/projectReferences.ts @@ -52,11 +52,11 @@ namespace ts { const configFileName = combineAllPaths("/", key, sp.configFileName || "tsconfig.json"); const options = { compilerOptions: { - references: (sp.references || []).map(r => ({ path: r })), composite: true, outDir: "bin", ...sp.options }, + references: (sp.references || []).map(r => ({ path: r })), ...sp.config }; const configContent = JSON.stringify(options); @@ -80,7 +80,12 @@ namespace ts { assert(config && !error, flattenDiagnosticMessageText(error && error.messageText, "\n")); const file = parseJsonConfigFileContent(config, host.configHost, getDirectoryPath(entryPointConfigFileName), {}, entryPointConfigFileName); file.options.configFilePath = entryPointConfigFileName; - const prog = createProgram(file.fileNames, file.options, host); + const prog = createProgram({ + rootNames: file.fileNames, + options: file.options, + host, + projectReferences: file.projectReferences + }); checkResult(prog, host); } @@ -101,23 +106,6 @@ namespace ts { assertNoErrors("Sanity check should not produce errors", prog.getOptionsDiagnostics()); }); }); - - it("can detect a circularity error", () => { - const spec: TestSpecification = { - "/primary": { - files: { "/primary/a.ts": emptyModule }, - references: ["../secondary"] - }, - "/secondary": { - files: { "/secondary/b.ts": moduleImporting("../primary/a") }, - references: ["../primary"] - } - }; - testProjectReferences(spec, "/primary/tsconfig.json", prog => { - assert.isTrue(!!prog, "Program should exist"); - assertHasError("Should detect a circular error", prog.getOptionsDiagnostics(), Diagnostics.Project_references_may_not_form_a_circular_graph_Cycle_detected_Colon_0); - }); - }); }); /** @@ -155,6 +143,7 @@ namespace ts { references: ["../primary"] } }; + debugger; testProjectReferences(spec, "/reference/tsconfig.json", program => { const errs = program.getOptionsDiagnostics(); assertHasError("Reports an error about 'composite' not being set", errs, Diagnostics.Referenced_project_0_must_have_setting_composite_Colon_true); @@ -181,35 +170,6 @@ namespace ts { }); }); - /** - * Circularity checking - */ - describe("project-references circularity checking", () => { - // Bare cycle with relative paths tested in sanity check block - it("detects an indirected cycle", () => { - const spec: TestSpecification = { - "/alpha": { - files: { "/alpha/a.ts": emptyModule }, - references: ["../beta"] - }, - "/beta": { - files: { "/beta/b.ts": moduleImporting("../alpha/a") }, - references: ["../gamma"] - }, - "/gamma": { - files: { "/gamma/a.ts": emptyModule }, - references: ["../alpha"], - - } - }; - - testProjectReferences(spec, "/alpha/tsconfig.json", program => { - const errs = program.getOptionsDiagnostics(); - assertHasError("Reports an error about the circular diagnsotic", errs, Diagnostics.Project_references_may_not_form_a_circular_graph_Cycle_detected_Colon_0); - }); - }); - }); - /** * Path mapping behavior */ From df8df9f324452661ffed22f17ad9e8fd6698dcb0 Mon Sep 17 00:00:00 2001 From: Ryan Cavanaugh Date: Fri, 6 Apr 2018 15:31:54 -0700 Subject: [PATCH 04/28] baseline-accept --- .../reference/api/tsserverlibrary.d.ts | 40 +++++++++++++-- tests/baselines/reference/api/typescript.d.ts | 50 ++++++++++++++----- .../tsconfig.json | 1 - .../tsconfig.json | 1 - .../tsconfig.json | 1 - .../tsconfig.json | 1 - .../tsconfig.json | 1 - .../tsconfig.json | 1 - .../tsconfig.json | 1 - .../tsconfig.json | 1 - 10 files changed, 74 insertions(+), 24 deletions(-) diff --git a/tests/baselines/reference/api/tsserverlibrary.d.ts b/tests/baselines/reference/api/tsserverlibrary.d.ts index 756e5a75f52aa..3429b77ca604f 100644 --- a/tests/baselines/reference/api/tsserverlibrary.d.ts +++ b/tests/baselines/reference/api/tsserverlibrary.d.ts @@ -2305,8 +2305,14 @@ declare namespace ts { name: string; } interface ProjectReference { + /** A normalized path on disk */ path: string; + /** The path as the user originally wrote it */ + originalPath: string; + /** True if the output of this reference should be prepended to the output of this project. Only valid for --outFile compilations */ prepend?: boolean; + /** True if it is intended that this reference form a circularity */ + circular?: boolean; } type CompilerOptionsValue = string | number | boolean | (string | number)[] | string[] | MapLike | PluginImport[] | ProjectReference[] | null | undefined; interface CompilerOptions { @@ -2363,7 +2369,6 @@ declare namespace ts { project?: string; reactNamespace?: string; jsxFactory?: string; - references?: ProjectReference[]; composite?: boolean; removeComments?: boolean; rootDir?: string; @@ -2445,6 +2450,7 @@ declare namespace ts { options: CompilerOptions; typeAcquisition?: TypeAcquisition; fileNames: string[]; + projectReferences?: ReadonlyArray; raw?: any; errors: Diagnostic[]; wildcardDirectories?: MapLike; @@ -2456,8 +2462,17 @@ declare namespace ts { } interface ExpandResult { fileNames: string[]; + projectReferences: ReadonlyArray | undefined; wildcardDirectories: MapLike; } + interface CreateProgramOptions { + rootNames: ReadonlyArray; + options: CompilerOptions; + projectReferences?: ReadonlyArray; + host?: CompilerHost; + oldProgram?: Program; + configFileParsingDiagnostics?: ReadonlyArray; + } interface ModuleResolutionHost { fileExists(fileName: string): boolean; readFile(fileName: string): string | undefined; @@ -3351,6 +3366,26 @@ declare namespace ts { } declare namespace ts { function parseCommandLine(commandLine: ReadonlyArray, readFile?: (path: string) => string | undefined): ParsedCommandLine; + type DiagnosticReporter = (diagnostic: Diagnostic) => void; + /** + * Reports config file diagnostics + */ + interface ConfigFileDiagnosticsReporter { + /** + * Reports unrecoverable error when parsing config file + */ + onUnRecoverableConfigFileDiagnostic: DiagnosticReporter; + } + /** + * Interface extending ParseConfigHost to support ParseConfigFile that reads config file and reports errors + */ + interface ParseConfigFileHost extends ParseConfigHost, ConfigFileDiagnosticsReporter { + getCurrentDirectory(): string; + } + /** + * Reads the config file, reports errors if any and exits if the config file cannot be found + */ + function getParsedCommandLineOfConfigFile(configFileName: string, optionsToExtend: CompilerOptions, host: ParseConfigFileHost): ParsedCommandLine | undefined; /** * Read tsconfig.json file * @param fileName The path to the config file @@ -3961,9 +3996,8 @@ declare namespace ts { * @param configFileParsingDiagnostics - error during config file parsing * @returns A 'Program' object. */ + function createProgram(createProgramOptions: CreateProgramOptions): Program; function createProgram(rootNames: ReadonlyArray, options: CompilerOptions, host?: CompilerHost, oldProgram?: Program, configFileParsingDiagnostics?: ReadonlyArray): Program; - function getProjectReferenceFileNames(host: CompilerHost, rootOptions: CompilerOptions): string[] | undefined; - function walkProjectReferenceGraph(host: CompilerHost, rootOptions: CompilerOptions, callback: (resolvedFile: string, referencedProject: CompilerOptions, settings: ProjectReference) => void, errorCallback?: (failedLocation: string) => void): void; } declare namespace ts { interface Node { diff --git a/tests/baselines/reference/api/typescript.d.ts b/tests/baselines/reference/api/typescript.d.ts index 5ac93ded97110..c2c80b2add74b 100644 --- a/tests/baselines/reference/api/typescript.d.ts +++ b/tests/baselines/reference/api/typescript.d.ts @@ -2305,8 +2305,14 @@ declare namespace ts { name: string; } interface ProjectReference { + /** A normalized path on disk */ path: string; + /** The path as the user originally wrote it */ + originalPath: string; + /** True if the output of this reference should be prepended to the output of this project. Only valid for --outFile compilations */ prepend?: boolean; + /** True if it is intended that this reference form a circularity */ + circular?: boolean; } type CompilerOptionsValue = string | number | boolean | (string | number)[] | string[] | MapLike | PluginImport[] | ProjectReference[] | null | undefined; interface CompilerOptions { @@ -2363,7 +2369,6 @@ declare namespace ts { project?: string; reactNamespace?: string; jsxFactory?: string; - references?: ProjectReference[]; composite?: boolean; removeComments?: boolean; rootDir?: string; @@ -2445,6 +2450,7 @@ declare namespace ts { options: CompilerOptions; typeAcquisition?: TypeAcquisition; fileNames: string[]; + projectReferences?: ReadonlyArray; raw?: any; errors: Diagnostic[]; wildcardDirectories?: MapLike; @@ -2456,8 +2462,17 @@ declare namespace ts { } interface ExpandResult { fileNames: string[]; + projectReferences: ReadonlyArray | undefined; wildcardDirectories: MapLike; } + interface CreateProgramOptions { + rootNames: ReadonlyArray; + options: CompilerOptions; + projectReferences?: ReadonlyArray; + host?: CompilerHost; + oldProgram?: Program; + configFileParsingDiagnostics?: ReadonlyArray; + } interface ModuleResolutionHost { fileExists(fileName: string): boolean; readFile(fileName: string): string | undefined; @@ -3908,9 +3923,8 @@ declare namespace ts { * @param configFileParsingDiagnostics - error during config file parsing * @returns A 'Program' object. */ + function createProgram(createProgramOptions: CreateProgramOptions): Program; function createProgram(rootNames: ReadonlyArray, options: CompilerOptions, host?: CompilerHost, oldProgram?: Program, configFileParsingDiagnostics?: ReadonlyArray): Program; - function getProjectReferenceFileNames(host: CompilerHost, rootOptions: CompilerOptions): string[] | undefined; - function walkProjectReferenceGraph(host: CompilerHost, rootOptions: CompilerOptions, callback: (resolvedFile: string, referencedProject: CompilerOptions, settings: ProjectReference) => void, errorCallback?: (failedLocation: string) => void): void; } declare namespace ts { interface EmitOutput { @@ -4049,7 +4063,6 @@ declare namespace ts { function createAbstractBuilder(rootNames: ReadonlyArray, options: CompilerOptions, host?: CompilerHost, oldProgram?: BuilderProgram, configFileParsingDiagnostics?: ReadonlyArray): BuilderProgram; } declare namespace ts { - type DiagnosticReporter = (diagnostic: Diagnostic) => void; type WatchStatusReporter = (diagnostic: Diagnostic, newLine: string, options: CompilerOptions) => void; /** Create the program with rootNames and options, if they are undefined, oldProgram and new configFile diagnostics create new program */ type CreateProgram = (rootNames: ReadonlyArray | undefined, options: CompilerOptions | undefined, host?: CompilerHost, oldProgram?: T, configFileParsingDiagnostics?: ReadonlyArray) => T; @@ -4112,15 +4125,6 @@ declare namespace ts { /** Compiler options */ options: CompilerOptions; } - /** - * Reports config file diagnostics - */ - interface ConfigFileDiagnosticsReporter { - /** - * Reports unrecoverable error when parsing config file - */ - onUnRecoverableConfigFileDiagnostic: DiagnosticReporter; - } /** * Host to create watch with config file */ @@ -4167,6 +4171,26 @@ declare namespace ts { } declare namespace ts { function parseCommandLine(commandLine: ReadonlyArray, readFile?: (path: string) => string | undefined): ParsedCommandLine; + type DiagnosticReporter = (diagnostic: Diagnostic) => void; + /** + * Reports config file diagnostics + */ + interface ConfigFileDiagnosticsReporter { + /** + * Reports unrecoverable error when parsing config file + */ + onUnRecoverableConfigFileDiagnostic: DiagnosticReporter; + } + /** + * Interface extending ParseConfigHost to support ParseConfigFile that reads config file and reports errors + */ + interface ParseConfigFileHost extends ParseConfigHost, ConfigFileDiagnosticsReporter { + getCurrentDirectory(): string; + } + /** + * Reads the config file, reports errors if any and exits if the config file cannot be found + */ + function getParsedCommandLineOfConfigFile(configFileName: string, optionsToExtend: CompilerOptions, host: ParseConfigFileHost): ParsedCommandLine | undefined; /** * Read tsconfig.json file * @param fileName The path to the config file diff --git a/tests/baselines/reference/tsConfig/Default initialized TSConfig/tsconfig.json b/tests/baselines/reference/tsConfig/Default initialized TSConfig/tsconfig.json index c1bd336691902..0b72c3fd8a779 100644 --- a/tests/baselines/reference/tsConfig/Default initialized TSConfig/tsconfig.json +++ b/tests/baselines/reference/tsConfig/Default initialized TSConfig/tsconfig.json @@ -42,7 +42,6 @@ // "rootDirs": [], /* List of root folders whose combined content represents the structure of the project at runtime. */ // "typeRoots": [], /* List of folders to include type definitions from. */ // "types": [], /* Type declaration files to be included in compilation. */ - // "references": [], /* Projects to reference */ // "allowSyntheticDefaultImports": true, /* Allow default imports from modules with no default export. This does not affect code emit, just typechecking. */ "esModuleInterop": true /* Enables emit interoperability between CommonJS and ES Modules via creation of namespace objects for all imports. Implies 'allowSyntheticDefaultImports'. */ // "preserveSymlinks": true, /* Do not resolve the real path of symlinks. */ diff --git a/tests/baselines/reference/tsConfig/Initialized TSConfig with boolean value compiler options/tsconfig.json b/tests/baselines/reference/tsConfig/Initialized TSConfig with boolean value compiler options/tsconfig.json index 5bf4b7b0e3469..f84c7f3abf3f2 100644 --- a/tests/baselines/reference/tsConfig/Initialized TSConfig with boolean value compiler options/tsconfig.json +++ b/tests/baselines/reference/tsConfig/Initialized TSConfig with boolean value compiler options/tsconfig.json @@ -42,7 +42,6 @@ // "rootDirs": [], /* List of root folders whose combined content represents the structure of the project at runtime. */ // "typeRoots": [], /* List of folders to include type definitions from. */ // "types": [], /* Type declaration files to be included in compilation. */ - // "references": [], /* Projects to reference */ // "allowSyntheticDefaultImports": true, /* Allow default imports from modules with no default export. This does not affect code emit, just typechecking. */ "esModuleInterop": true /* Enables emit interoperability between CommonJS and ES Modules via creation of namespace objects for all imports. Implies 'allowSyntheticDefaultImports'. */ // "preserveSymlinks": true, /* Do not resolve the real path of symlinks. */ diff --git a/tests/baselines/reference/tsConfig/Initialized TSConfig with enum value compiler options/tsconfig.json b/tests/baselines/reference/tsConfig/Initialized TSConfig with enum value compiler options/tsconfig.json index e79b837a4f657..b65582c7841d2 100644 --- a/tests/baselines/reference/tsConfig/Initialized TSConfig with enum value compiler options/tsconfig.json +++ b/tests/baselines/reference/tsConfig/Initialized TSConfig with enum value compiler options/tsconfig.json @@ -42,7 +42,6 @@ // "rootDirs": [], /* List of root folders whose combined content represents the structure of the project at runtime. */ // "typeRoots": [], /* List of folders to include type definitions from. */ // "types": [], /* Type declaration files to be included in compilation. */ - // "references": [], /* Projects to reference */ // "allowSyntheticDefaultImports": true, /* Allow default imports from modules with no default export. This does not affect code emit, just typechecking. */ "esModuleInterop": true /* Enables emit interoperability between CommonJS and ES Modules via creation of namespace objects for all imports. Implies 'allowSyntheticDefaultImports'. */ // "preserveSymlinks": true, /* Do not resolve the real path of symlinks. */ diff --git a/tests/baselines/reference/tsConfig/Initialized TSConfig with files options/tsconfig.json b/tests/baselines/reference/tsConfig/Initialized TSConfig with files options/tsconfig.json index b867fd0c6ac99..86d25e2362ac7 100644 --- a/tests/baselines/reference/tsConfig/Initialized TSConfig with files options/tsconfig.json +++ b/tests/baselines/reference/tsConfig/Initialized TSConfig with files options/tsconfig.json @@ -42,7 +42,6 @@ // "rootDirs": [], /* List of root folders whose combined content represents the structure of the project at runtime. */ // "typeRoots": [], /* List of folders to include type definitions from. */ // "types": [], /* Type declaration files to be included in compilation. */ - // "references": [], /* Projects to reference */ // "allowSyntheticDefaultImports": true, /* Allow default imports from modules with no default export. This does not affect code emit, just typechecking. */ "esModuleInterop": true /* Enables emit interoperability between CommonJS and ES Modules via creation of namespace objects for all imports. Implies 'allowSyntheticDefaultImports'. */ // "preserveSymlinks": true, /* Do not resolve the real path of symlinks. */ diff --git a/tests/baselines/reference/tsConfig/Initialized TSConfig with incorrect compiler option value/tsconfig.json b/tests/baselines/reference/tsConfig/Initialized TSConfig with incorrect compiler option value/tsconfig.json index c30897a71403d..b623b1198c232 100644 --- a/tests/baselines/reference/tsConfig/Initialized TSConfig with incorrect compiler option value/tsconfig.json +++ b/tests/baselines/reference/tsConfig/Initialized TSConfig with incorrect compiler option value/tsconfig.json @@ -42,7 +42,6 @@ // "rootDirs": [], /* List of root folders whose combined content represents the structure of the project at runtime. */ // "typeRoots": [], /* List of folders to include type definitions from. */ // "types": [], /* Type declaration files to be included in compilation. */ - // "references": [], /* Projects to reference */ // "allowSyntheticDefaultImports": true, /* Allow default imports from modules with no default export. This does not affect code emit, just typechecking. */ "esModuleInterop": true /* Enables emit interoperability between CommonJS and ES Modules via creation of namespace objects for all imports. Implies 'allowSyntheticDefaultImports'. */ // "preserveSymlinks": true, /* Do not resolve the real path of symlinks. */ diff --git a/tests/baselines/reference/tsConfig/Initialized TSConfig with incorrect compiler option/tsconfig.json b/tests/baselines/reference/tsConfig/Initialized TSConfig with incorrect compiler option/tsconfig.json index c1bd336691902..0b72c3fd8a779 100644 --- a/tests/baselines/reference/tsConfig/Initialized TSConfig with incorrect compiler option/tsconfig.json +++ b/tests/baselines/reference/tsConfig/Initialized TSConfig with incorrect compiler option/tsconfig.json @@ -42,7 +42,6 @@ // "rootDirs": [], /* List of root folders whose combined content represents the structure of the project at runtime. */ // "typeRoots": [], /* List of folders to include type definitions from. */ // "types": [], /* Type declaration files to be included in compilation. */ - // "references": [], /* Projects to reference */ // "allowSyntheticDefaultImports": true, /* Allow default imports from modules with no default export. This does not affect code emit, just typechecking. */ "esModuleInterop": true /* Enables emit interoperability between CommonJS and ES Modules via creation of namespace objects for all imports. Implies 'allowSyntheticDefaultImports'. */ // "preserveSymlinks": true, /* Do not resolve the real path of symlinks. */ diff --git a/tests/baselines/reference/tsConfig/Initialized TSConfig with list compiler options with enum value/tsconfig.json b/tests/baselines/reference/tsConfig/Initialized TSConfig with list compiler options with enum value/tsconfig.json index 8f8a22fb16ebe..ea9df560138df 100644 --- a/tests/baselines/reference/tsConfig/Initialized TSConfig with list compiler options with enum value/tsconfig.json +++ b/tests/baselines/reference/tsConfig/Initialized TSConfig with list compiler options with enum value/tsconfig.json @@ -42,7 +42,6 @@ // "rootDirs": [], /* List of root folders whose combined content represents the structure of the project at runtime. */ // "typeRoots": [], /* List of folders to include type definitions from. */ // "types": [], /* Type declaration files to be included in compilation. */ - // "references": [], /* Projects to reference */ // "allowSyntheticDefaultImports": true, /* Allow default imports from modules with no default export. This does not affect code emit, just typechecking. */ "esModuleInterop": true /* Enables emit interoperability between CommonJS and ES Modules via creation of namespace objects for all imports. Implies 'allowSyntheticDefaultImports'. */ // "preserveSymlinks": true, /* Do not resolve the real path of symlinks. */ diff --git a/tests/baselines/reference/tsConfig/Initialized TSConfig with list compiler options/tsconfig.json b/tests/baselines/reference/tsConfig/Initialized TSConfig with list compiler options/tsconfig.json index 1f7ba4dc581e2..bab4947a8e51b 100644 --- a/tests/baselines/reference/tsConfig/Initialized TSConfig with list compiler options/tsconfig.json +++ b/tests/baselines/reference/tsConfig/Initialized TSConfig with list compiler options/tsconfig.json @@ -42,7 +42,6 @@ // "rootDirs": [], /* List of root folders whose combined content represents the structure of the project at runtime. */ // "typeRoots": [], /* List of folders to include type definitions from. */ "types": ["jquery","mocha"], /* Type declaration files to be included in compilation. */ - // "references": [], /* Projects to reference */ // "allowSyntheticDefaultImports": true, /* Allow default imports from modules with no default export. This does not affect code emit, just typechecking. */ "esModuleInterop": true /* Enables emit interoperability between CommonJS and ES Modules via creation of namespace objects for all imports. Implies 'allowSyntheticDefaultImports'. */ // "preserveSymlinks": true, /* Do not resolve the real path of symlinks. */ From 4dfe4ee57d00ba3baad5a2625c79b72ef19087d2 Mon Sep 17 00:00:00 2001 From: Ryan Cavanaugh Date: Fri, 6 Apr 2018 15:56:08 -0700 Subject: [PATCH 05/28] Remove bad file --- src/compiler/declarationEmitter.ts | 2058 ---------------------------- 1 file changed, 2058 deletions(-) delete mode 100644 src/compiler/declarationEmitter.ts diff --git a/src/compiler/declarationEmitter.ts b/src/compiler/declarationEmitter.ts deleted file mode 100644 index 82933d8254d0f..0000000000000 --- a/src/compiler/declarationEmitter.ts +++ /dev/null @@ -1,2058 +0,0 @@ -/// - -/* @internal */ -namespace ts { - interface ModuleElementDeclarationEmitInfo { - node: Node; - outputPos: number; - indent: number; - asynchronousOutput?: string; // If the output for alias was written asynchronously, the corresponding output - subModuleElementDeclarationEmitInfo?: ModuleElementDeclarationEmitInfo[]; - isVisible?: boolean; - } - - interface DeclarationEmit { - reportedDeclarationError: boolean; - moduleElementDeclarationEmitInfo: ModuleElementDeclarationEmitInfo[]; - synchronousDeclarationOutput: string; - referencesOutput: string; - } - - type GetSymbolAccessibilityDiagnostic = (symbolAccessibilityResult: SymbolAccessibilityResult) => SymbolAccessibilityDiagnostic; - - interface EmitTextWriterWithSymbolWriter extends EmitTextWriter { - getSymbolAccessibilityDiagnostic: GetSymbolAccessibilityDiagnostic; - } - - interface SymbolAccessibilityDiagnostic { - errorNode: Node; - diagnosticMessage: DiagnosticMessage; - typeName?: DeclarationName | QualifiedName; - } - - export function getDeclarationDiagnostics(host: EmitHost, resolver: EmitResolver, targetSourceFile: SourceFile): Diagnostic[] { - const declarationDiagnostics = createDiagnosticCollection(); - forEachEmittedFile(host, getDeclarationDiagnosticsFromFile, targetSourceFile); - return declarationDiagnostics.getDiagnostics(targetSourceFile ? targetSourceFile.fileName : undefined); - - function getDeclarationDiagnosticsFromFile({ declarationFilePath }: EmitFileNames, sourceFileOrBundle: SourceFile | Bundle) { - emitDeclarations(host, resolver, declarationDiagnostics, declarationFilePath, sourceFileOrBundle, /*emitOnlyDtsFiles*/ false); - } - } - - function emitDeclarations(host: EmitHost, resolver: EmitResolver, emitterDiagnostics: DiagnosticCollection, declarationFilePath: string, - sourceFileOrBundle: SourceFile | Bundle, emitOnlyDtsFiles: boolean): DeclarationEmit { - const sourceFiles = sourceFileOrBundle.kind === SyntaxKind.Bundle ? [...sourceFileOrBundle.prepends, ...sourceFileOrBundle.sourceFiles] : [sourceFileOrBundle]; - const isBundledEmit = sourceFileOrBundle.kind === SyntaxKind.Bundle; - const newLine = host.getNewLine(); - const compilerOptions = host.getCompilerOptions(); - - let write: (s: string) => void; - let writeLine: () => void; - let increaseIndent: () => void; - let decreaseIndent: () => void; - let writeTextOfNode: (text: string, node: Node) => void; - - let writer: EmitTextWriterWithSymbolWriter; - - createAndSetNewTextWriterWithSymbolWriter(); - - let enclosingDeclaration: Node; - let resultHasExternalModuleIndicator: boolean; - let currentText: string; - let currentLineMap: ReadonlyArray; - let currentIdentifiers: Map; - let isCurrentFileExternalModule: boolean; - let reportedDeclarationError = false; - let errorNameNode: DeclarationName | QualifiedName; - const emitJsDocComments = compilerOptions.removeComments ? noop : writeJsDocComments; - const emit = compilerOptions.stripInternal ? stripInternal : emitNode; - let needsDeclare = true; - - let moduleElementDeclarationEmitInfo: ModuleElementDeclarationEmitInfo[] = []; - let asynchronousSubModuleDeclarationEmitInfo: ModuleElementDeclarationEmitInfo[]; - - // Contains the reference paths that needs to go in the declaration file. - // Collecting this separately because reference paths need to be first thing in the declaration file - // and we could be collecting these paths from multiple files into single one with --out option - let referencesOutput = ""; - - let usedTypeDirectiveReferences: Map; - - // Emit references corresponding to each file - const emittedReferencedFiles: SourceFile[] = []; - let addedGlobalFileReference = false; - let allSourcesModuleElementDeclarationEmitInfo: ModuleElementDeclarationEmitInfo[] = []; - forEach(sourceFiles, sourceFile => { - if (sourceFile.kind === SyntaxKind.Prepend) { - emitPrepend(sourceFile); - return; - } - - // Dont emit for javascript file - if (isSourceFileJavaScript(sourceFile)) { - return; - } - - // Check what references need to be added - if (!compilerOptions.noResolve) { - forEach(sourceFile.referencedFiles, fileReference => { - const referencedFile = tryResolveScriptReference(host, sourceFile, fileReference); - - // Emit reference in dts, if the file reference was not already emitted - if (referencedFile && !contains(emittedReferencedFiles, referencedFile)) { - // Add a reference to generated dts file, - // global file reference is added only - // - if it is not bundled emit (because otherwise it would be self reference) - // - and it is not already added - if (writeReferencePath(referencedFile, !isBundledEmit && !addedGlobalFileReference, emitOnlyDtsFiles)) { - addedGlobalFileReference = true; - } - emittedReferencedFiles.push(referencedFile); - } - }); - } - - resultHasExternalModuleIndicator = false; - if (!isBundledEmit || !isExternalModule(sourceFile)) { - needsDeclare = true; - emitSourceFile(sourceFile); - } - else if (isExternalModule(sourceFile)) { - needsDeclare = false; - write(`declare module "${getResolvedExternalModuleName(host, sourceFile)}" {`); - writeLine(); - increaseIndent(); - emitSourceFile(sourceFile); - decreaseIndent(); - write("}"); - writeLine(); - } - - // create asynchronous output for the importDeclarations - if (moduleElementDeclarationEmitInfo.length) { - const oldWriter = writer; - forEach(moduleElementDeclarationEmitInfo, aliasEmitInfo => { - if (aliasEmitInfo.isVisible && !aliasEmitInfo.asynchronousOutput) { - Debug.assert(aliasEmitInfo.node.kind === SyntaxKind.ImportDeclaration); - createAndSetNewTextWriterWithSymbolWriter(); - Debug.assert(aliasEmitInfo.indent === 0 || (aliasEmitInfo.indent === 1 && isBundledEmit)); - for (let i = 0; i < aliasEmitInfo.indent; i++) { - increaseIndent(); - } - writeImportDeclaration(aliasEmitInfo.node); - aliasEmitInfo.asynchronousOutput = writer.getText(); - for (let i = 0; i < aliasEmitInfo.indent; i++) { - decreaseIndent(); - } - } - }); - setWriter(oldWriter); - - allSourcesModuleElementDeclarationEmitInfo = allSourcesModuleElementDeclarationEmitInfo.concat(moduleElementDeclarationEmitInfo); - moduleElementDeclarationEmitInfo = []; - } - - if (!isBundledEmit && isExternalModule(sourceFile) && !resultHasExternalModuleIndicator) { - // if file was external module this fact should be preserved in .d.ts as well. - // in case if we didn't write any external module specifiers in .d.ts we need to emit something - // that will force compiler to think that this file is an external module - 'export {}' is a reasonable choice here. - write("export {};"); - writeLine(); - } - }); - - if (usedTypeDirectiveReferences) { - forEachKey(usedTypeDirectiveReferences, directive => { - referencesOutput += `/// ${newLine}`; - }); - } - - return { - reportedDeclarationError, - moduleElementDeclarationEmitInfo: allSourcesModuleElementDeclarationEmitInfo, - synchronousDeclarationOutput: writer.getText(), - referencesOutput, - }; - - function hasInternalAnnotation(range: CommentRange) { - const comment = currentText.substring(range.pos, range.end); - return stringContains(comment, "@internal"); - } - - function stripInternal(node: Node) { - if (node) { - const leadingCommentRanges = getLeadingCommentRanges(currentText, node.pos); - if (forEach(leadingCommentRanges, hasInternalAnnotation)) { - return; - } - - emitNode(node); - } - } - - function createAndSetNewTextWriterWithSymbolWriter(): void { - const writer = createTextWriter(newLine); - writer.trackSymbol = trackSymbol; - writer.reportInaccessibleThisError = reportInaccessibleThisError; - writer.reportInaccessibleUniqueSymbolError = reportInaccessibleUniqueSymbolError; - writer.reportPrivateInBaseOfClassExpression = reportPrivateInBaseOfClassExpression; - writer.writeKeyword = writer.write; - writer.writeOperator = writer.write; - writer.writePunctuation = writer.write; - writer.writeSpace = writer.write; - writer.writeStringLiteral = writer.writeLiteral; - writer.writeParameter = writer.write; - writer.writeProperty = writer.write; - writer.writeSymbol = writer.write; - setWriter(writer); - } - - function setWriter(newWriter: EmitTextWriterWithSymbolWriter) { - writer = newWriter; - write = newWriter.write; - writeTextOfNode = newWriter.writeTextOfNode; - writeLine = newWriter.writeLine; - increaseIndent = newWriter.increaseIndent; - decreaseIndent = newWriter.decreaseIndent; - } - - function writeAsynchronousModuleElements(nodes: ReadonlyArray) { - const oldWriter = writer; - forEach(nodes, declaration => { - let nodeToCheck: Node; - if (declaration.kind === SyntaxKind.VariableDeclaration) { - nodeToCheck = declaration.parent.parent; - } - else if (declaration.kind === SyntaxKind.NamedImports || declaration.kind === SyntaxKind.ImportSpecifier || declaration.kind === SyntaxKind.ImportClause) { - Debug.fail("We should be getting ImportDeclaration instead to write"); - } - else { - nodeToCheck = declaration; - } - - let moduleElementEmitInfo = forEach(moduleElementDeclarationEmitInfo, declEmitInfo => declEmitInfo.node === nodeToCheck ? declEmitInfo : undefined); - if (!moduleElementEmitInfo && asynchronousSubModuleDeclarationEmitInfo) { - moduleElementEmitInfo = forEach(asynchronousSubModuleDeclarationEmitInfo, declEmitInfo => declEmitInfo.node === nodeToCheck ? declEmitInfo : undefined); - } - - // If the alias was marked as not visible when we saw its declaration, we would have saved the aliasEmitInfo, but if we haven't yet visited the alias declaration - // then we don't need to write it at this point. We will write it when we actually see its declaration - // Eg. - // export function bar(a: foo.Foo) { } - // import foo = require("foo"); - // Writing of function bar would mark alias declaration foo as visible but we haven't yet visited that declaration so do nothing, - // we would write alias foo declaration when we visit it since it would now be marked as visible - if (moduleElementEmitInfo) { - if (moduleElementEmitInfo.node.kind === SyntaxKind.ImportDeclaration) { - // we have to create asynchronous output only after we have collected complete information - // because it is possible to enable multiple bindings as asynchronously visible - moduleElementEmitInfo.isVisible = true; - } - else { - createAndSetNewTextWriterWithSymbolWriter(); - for (let declarationIndent = moduleElementEmitInfo.indent; declarationIndent; declarationIndent--) { - increaseIndent(); - } - - if (nodeToCheck.kind === SyntaxKind.ModuleDeclaration) { - Debug.assert(asynchronousSubModuleDeclarationEmitInfo === undefined); - asynchronousSubModuleDeclarationEmitInfo = []; - } - writeModuleElement(nodeToCheck); - if (nodeToCheck.kind === SyntaxKind.ModuleDeclaration) { - moduleElementEmitInfo.subModuleElementDeclarationEmitInfo = asynchronousSubModuleDeclarationEmitInfo; - asynchronousSubModuleDeclarationEmitInfo = undefined; - } - moduleElementEmitInfo.asynchronousOutput = writer.getText(); - } - } - }); - setWriter(oldWriter); - } - - function recordTypeReferenceDirectivesIfNecessary(typeReferenceDirectives: string[]): void { - if (!typeReferenceDirectives) { - return; - } - - if (!usedTypeDirectiveReferences) { - usedTypeDirectiveReferences = createMap(); - } - for (const directive of typeReferenceDirectives) { - if (!usedTypeDirectiveReferences.has(directive)) { - usedTypeDirectiveReferences.set(directive, directive); - } - } - } - - function handleSymbolAccessibilityError(symbolAccessibilityResult: SymbolAccessibilityResult) { - if (symbolAccessibilityResult.accessibility === SymbolAccessibility.Accessible) { - // write the aliases - if (symbolAccessibilityResult && symbolAccessibilityResult.aliasesToMakeVisible) { - writeAsynchronousModuleElements(symbolAccessibilityResult.aliasesToMakeVisible); - } - } - else { - // Report error - reportedDeclarationError = true; - const errorInfo = writer.getSymbolAccessibilityDiagnostic(symbolAccessibilityResult); - if (errorInfo) { - if (errorInfo.typeName) { - emitterDiagnostics.add(createDiagnosticForNode(symbolAccessibilityResult.errorNode || errorInfo.errorNode, - errorInfo.diagnosticMessage, - getTextOfNodeFromSourceText(currentText, errorInfo.typeName), - symbolAccessibilityResult.errorSymbolName, - symbolAccessibilityResult.errorModuleName)); - } - else { - emitterDiagnostics.add(createDiagnosticForNode(symbolAccessibilityResult.errorNode || errorInfo.errorNode, - errorInfo.diagnosticMessage, - symbolAccessibilityResult.errorSymbolName, - symbolAccessibilityResult.errorModuleName)); - } - } - } - } - - function trackSymbol(symbol: Symbol, enclosingDeclaration?: Node, meaning?: SymbolFlags) { - handleSymbolAccessibilityError(resolver.isSymbolAccessible(symbol, enclosingDeclaration, meaning, /*shouldComputeAliasesToMakeVisible*/ true)); - recordTypeReferenceDirectivesIfNecessary(resolver.getTypeReferenceDirectivesForSymbol(symbol, meaning)); - } - - function reportPrivateInBaseOfClassExpression(propertyName: string) { - if (errorNameNode) { - reportedDeclarationError = true; - emitterDiagnostics.add( - createDiagnosticForNode(errorNameNode, Diagnostics.Property_0_of_exported_class_expression_may_not_be_private_or_protected, propertyName)); - } - } - - function reportInaccessibleUniqueSymbolError() { - if (errorNameNode) { - reportedDeclarationError = true; - emitterDiagnostics.add(createDiagnosticForNode(errorNameNode, Diagnostics.The_inferred_type_of_0_references_an_inaccessible_1_type_A_type_annotation_is_necessary, - declarationNameToString(errorNameNode), - "unique symbol")); - } - } - - function reportInaccessibleThisError() { - if (errorNameNode) { - reportedDeclarationError = true; - emitterDiagnostics.add(createDiagnosticForNode(errorNameNode, Diagnostics.The_inferred_type_of_0_references_an_inaccessible_1_type_A_type_annotation_is_necessary, - declarationNameToString(errorNameNode), - "this")); - } - } - - function writeTypeOfDeclaration(declaration: AccessorDeclaration | VariableLikeDeclaration, type: TypeNode, getSymbolAccessibilityDiagnostic: GetSymbolAccessibilityDiagnostic) { - writer.getSymbolAccessibilityDiagnostic = getSymbolAccessibilityDiagnostic; - write(": "); - - // use the checker's type, not the declared type, - // for optional parameter properties - // and also for non-optional initialized parameters that aren't a parameter property - // these types may need to add `undefined`. - const shouldUseResolverType = declaration.kind === SyntaxKind.Parameter && - (resolver.isRequiredInitializedParameter(declaration) || - resolver.isOptionalUninitializedParameterProperty(declaration)); - if (type && !shouldUseResolverType) { - // Write the type - emitType(type); - } - else { - errorNameNode = declaration.name; - const format = TypeFormatFlags.UseTypeOfFunction | TypeFormatFlags.UseStructuralFallback | - TypeFormatFlags.WriteClassExpressionAsTypeLiteral | - (shouldUseResolverType ? TypeFormatFlags.AddUndefined : 0); - resolver.writeTypeOfDeclaration(declaration, enclosingDeclaration, format, writer); - errorNameNode = undefined; - } - } - - function writeReturnTypeAtSignature(signature: SignatureDeclaration, getSymbolAccessibilityDiagnostic: GetSymbolAccessibilityDiagnostic) { - writer.getSymbolAccessibilityDiagnostic = getSymbolAccessibilityDiagnostic; - write(": "); - if (signature.type) { - // Write the type - emitType(signature.type); - } - else { - errorNameNode = signature.name; - resolver.writeReturnTypeOfSignatureDeclaration( - signature, - enclosingDeclaration, - TypeFormatFlags.UseTypeOfFunction | TypeFormatFlags.UseStructuralFallback | TypeFormatFlags.WriteClassExpressionAsTypeLiteral, - writer); - errorNameNode = undefined; - } - } - - function emitLines(nodes: ReadonlyArray) { - for (const node of nodes) { - emit(node); - } - } - - function emitSeparatedList(nodes: ReadonlyArray, separator: string, eachNodeEmitFn: (node: Node) => void, canEmitFn?: (node: Node) => boolean) { - let currentWriterPos = writer.getTextPos(); - for (const node of nodes) { - if (!canEmitFn || canEmitFn(node)) { - if (currentWriterPos !== writer.getTextPos()) { - write(separator); - } - currentWriterPos = writer.getTextPos(); - eachNodeEmitFn(node); - } - } - } - - function emitCommaList(nodes: ReadonlyArray, eachNodeEmitFn: (node: Node) => void, canEmitFn?: (node: Node) => boolean) { - emitSeparatedList(nodes, ", ", eachNodeEmitFn, canEmitFn); - } - - function writeJsDocComments(declaration: Node) { - if (declaration) { - const jsDocComments = getJSDocCommentRanges(declaration, currentText); - emitNewLineBeforeLeadingComments(currentLineMap, writer, declaration, jsDocComments); - // jsDoc comments are emitted at /*leading comment1 */space/*leading comment*/space - emitComments(currentText, currentLineMap, writer, jsDocComments, /*leadingSeparator*/ false, /*trailingSeparator*/ true, newLine, writeCommentRange); - } - } - - function emitTypeWithNewGetSymbolAccessibilityDiagnostic(type: TypeNode | EntityName, getSymbolAccessibilityDiagnostic: GetSymbolAccessibilityDiagnostic) { - writer.getSymbolAccessibilityDiagnostic = getSymbolAccessibilityDiagnostic; - emitType(type); - } - - function emitType(type: TypeNode | Identifier | QualifiedName) { - switch (type.kind) { - case SyntaxKind.AnyKeyword: - case SyntaxKind.StringKeyword: - case SyntaxKind.NumberKeyword: - case SyntaxKind.BooleanKeyword: - case SyntaxKind.ObjectKeyword: - case SyntaxKind.SymbolKeyword: - case SyntaxKind.VoidKeyword: - case SyntaxKind.UndefinedKeyword: - case SyntaxKind.NullKeyword: - case SyntaxKind.NeverKeyword: - case SyntaxKind.ThisType: - case SyntaxKind.LiteralType: - return writeTextOfNode(currentText, type); - case SyntaxKind.ExpressionWithTypeArguments: - return emitExpressionWithTypeArguments(type); - case SyntaxKind.TypeReference: - return emitTypeReference(type); - case SyntaxKind.TypeQuery: - return emitTypeQuery(type); - case SyntaxKind.ArrayType: - return emitArrayType(type); - case SyntaxKind.TupleType: - return emitTupleType(type); - case SyntaxKind.UnionType: - return emitUnionType(type); - case SyntaxKind.IntersectionType: - return emitIntersectionType(type); - case SyntaxKind.ConditionalType: - return emitConditionalType(type); - case SyntaxKind.InferType: - return emitInferType(type); - case SyntaxKind.ParenthesizedType: - return emitParenType(type); - case SyntaxKind.TypeOperator: - return emitTypeOperator(type); - case SyntaxKind.IndexedAccessType: - return emitIndexedAccessType(type); - case SyntaxKind.MappedType: - return emitMappedType(type); - case SyntaxKind.FunctionType: - case SyntaxKind.ConstructorType: - return emitSignatureDeclarationWithJsDocComments(type); - case SyntaxKind.TypeLiteral: - return emitTypeLiteral(type); - case SyntaxKind.Identifier: - return emitEntityName(type); - case SyntaxKind.QualifiedName: - return emitEntityName(type); - case SyntaxKind.TypePredicate: - return emitTypePredicate(type); - } - - function writeEntityName(entityName: EntityName | Expression) { - if (entityName.kind === SyntaxKind.Identifier) { - writeTextOfNode(currentText, entityName); - } - else { - const left = entityName.kind === SyntaxKind.QualifiedName ? (entityName).left : (entityName).expression; - const right = entityName.kind === SyntaxKind.QualifiedName ? (entityName).right : (entityName).name; - writeEntityName(left); - write("."); - writeTextOfNode(currentText, right); - } - } - - function emitEntityName(entityName: EntityNameOrEntityNameExpression) { - const visibilityResult = resolver.isEntityNameVisible(entityName, - // Aliases can be written asynchronously so use correct enclosing declaration - entityName.parent.kind === SyntaxKind.ImportEqualsDeclaration ? entityName.parent : enclosingDeclaration); - - handleSymbolAccessibilityError(visibilityResult); - recordTypeReferenceDirectivesIfNecessary(resolver.getTypeReferenceDirectivesForEntityName(entityName)); - writeEntityName(entityName); - } - - function emitExpressionWithTypeArguments(node: ExpressionWithTypeArguments) { - if (isEntityNameExpression(node.expression)) { - Debug.assert(node.expression.kind === SyntaxKind.Identifier || node.expression.kind === SyntaxKind.PropertyAccessExpression); - emitEntityName(node.expression); - if (node.typeArguments) { - write("<"); - emitCommaList(node.typeArguments, emitType); - write(">"); - } - } - } - - function emitTypeReference(type: TypeReferenceNode) { - emitEntityName(type.typeName); - if (type.typeArguments) { - write("<"); - emitCommaList(type.typeArguments, emitType); - write(">"); - } - } - - function emitTypePredicate(type: TypePredicateNode) { - writeTextOfNode(currentText, type.parameterName); - write(" is "); - emitType(type.type); - } - - function emitTypeQuery(type: TypeQueryNode) { - write("typeof "); - emitEntityName(type.exprName); - } - - function emitArrayType(type: ArrayTypeNode) { - emitType(type.elementType); - write("[]"); - } - - function emitTupleType(type: TupleTypeNode) { - write("["); - emitCommaList(type.elementTypes, emitType); - write("]"); - } - - function emitUnionType(type: UnionTypeNode) { - emitSeparatedList(type.types, " | ", emitType); - } - - function emitIntersectionType(type: IntersectionTypeNode) { - emitSeparatedList(type.types, " & ", emitType); - } - - function emitConditionalType(node: ConditionalTypeNode) { - emitType(node.checkType); - write(" extends "); - emitType(node.extendsType); - write(" ? "); - const prevEnclosingDeclaration = enclosingDeclaration; - enclosingDeclaration = node.trueType; - emitType(node.trueType); - enclosingDeclaration = prevEnclosingDeclaration; - write(" : "); - emitType(node.falseType); - } - - function emitInferType(node: InferTypeNode) { - write("infer "); - writeTextOfNode(currentText, node.typeParameter.name); - } - - function emitParenType(type: ParenthesizedTypeNode) { - write("("); - emitType(type.type); - write(")"); - } - - function emitTypeOperator(type: TypeOperatorNode) { - write(tokenToString(type.operator)); - write(" "); - emitType(type.type); - } - - function emitIndexedAccessType(node: IndexedAccessTypeNode) { - emitType(node.objectType); - write("["); - emitType(node.indexType); - write("]"); - } - - function emitMappedType(node: MappedTypeNode) { - const prevEnclosingDeclaration = enclosingDeclaration; - enclosingDeclaration = node; - write("{"); - writeLine(); - increaseIndent(); - if (node.readonlyToken) { - write(node.readonlyToken.kind === SyntaxKind.PlusToken ? "+readonly " : - node.readonlyToken.kind === SyntaxKind.MinusToken ? "-readonly " : - "readonly "); - } - write("["); - writeEntityName(node.typeParameter.name); - write(" in "); - emitType(node.typeParameter.constraint); - write("]"); - if (node.questionToken) { - write(node.questionToken.kind === SyntaxKind.PlusToken ? "+?" : - node.questionToken.kind === SyntaxKind.MinusToken ? "-?" : - "?"); - } - write(": "); - if (node.type) { - emitType(node.type); - } - else { - write("any"); - } - write(";"); - writeLine(); - decreaseIndent(); - write("}"); - enclosingDeclaration = prevEnclosingDeclaration; - } - - function emitTypeLiteral(type: TypeLiteralNode) { - write("{"); - if (type.members.length) { - writeLine(); - increaseIndent(); - // write members - emitLines(type.members); - decreaseIndent(); - } - write("}"); - } - } - - function emitPrepend(node: PrependNode) { - write(node.declarationText); - } - - function emitSourceFile(node: SourceFile) { - currentText = node.text; - currentLineMap = getLineStarts(node); - currentIdentifiers = node.identifiers; - isCurrentFileExternalModule = isExternalModule(node); - enclosingDeclaration = node; - emitDetachedComments(currentText, currentLineMap, writer, writeCommentRange, node, newLine, /*removeComments*/ true); - emitLines(node.statements); - } - - // Return a temp variable name to be used in `export default`/`export class ... extends` statements. - // The temp name will be of the form _default_counter. - // Note that export default is only allowed at most once in a module, so we - // do not need to keep track of created temp names. - function getExportTempVariableName(baseName: string): string { - if (!currentIdentifiers.has(baseName)) { - return baseName; - } - let count = 0; - while (true) { - count++; - const name = baseName + "_" + count; - if (!currentIdentifiers.has(name)) { - return name; - } - } - } - - function emitTempVariableDeclaration(expr: Expression, baseName: string, diagnostic: SymbolAccessibilityDiagnostic, needsDeclare: boolean): string { - const tempVarName = getExportTempVariableName(baseName); - if (needsDeclare) { - write("declare "); - } - write("const "); - write(tempVarName); - write(": "); - writer.getSymbolAccessibilityDiagnostic = () => diagnostic; - resolver.writeTypeOfExpression( - expr, - enclosingDeclaration, - TypeFormatFlags.UseTypeOfFunction | TypeFormatFlags.UseStructuralFallback | TypeFormatFlags.WriteClassExpressionAsTypeLiteral, - writer); - write(";"); - writeLine(); - return tempVarName; - } - - function emitExportAssignment(node: ExportAssignment) { - if (isSourceFile(node.parent)) { - resultHasExternalModuleIndicator = true; // Top-level exports are external module indicators - } - if (node.expression.kind === SyntaxKind.Identifier) { - write(node.isExportEquals ? "export = " : "export default "); - writeTextOfNode(currentText, node.expression); - } - else { - const tempVarName = emitTempVariableDeclaration(node.expression, "_default", { - diagnosticMessage: Diagnostics.Default_export_of_the_module_has_or_is_using_private_name_0, - errorNode: node - }, needsDeclare); - write(node.isExportEquals ? "export = " : "export default "); - write(tempVarName); - } - write(";"); - writeLine(); - - // Make all the declarations visible for the export name - if (node.expression.kind === SyntaxKind.Identifier) { - const nodes = resolver.collectLinkedAliases(node.expression); - - // write each of these declarations asynchronously - writeAsynchronousModuleElements(nodes); - } - } - - function isModuleElementVisible(node: Declaration) { - return resolver.isDeclarationVisible(node); - } - - function emitModuleElement(node: Node, isModuleElementVisible: boolean) { - if (isModuleElementVisible) { - writeModuleElement(node); - } - // Import equals declaration in internal module can become visible as part of any emit so lets make sure we add these irrespective - else if (node.kind === SyntaxKind.ImportEqualsDeclaration || - (node.parent.kind === SyntaxKind.SourceFile && isCurrentFileExternalModule)) { - let isVisible: boolean; - if (asynchronousSubModuleDeclarationEmitInfo && node.parent.kind !== SyntaxKind.SourceFile) { - // Import declaration of another module that is visited async so lets put it in right spot - asynchronousSubModuleDeclarationEmitInfo.push({ - node, - outputPos: writer.getTextPos(), - indent: writer.getIndent(), - isVisible - }); - } - else { - if (node.kind === SyntaxKind.ImportDeclaration) { - const importDeclaration = node; - if (importDeclaration.importClause) { - isVisible = (importDeclaration.importClause.name && resolver.isDeclarationVisible(importDeclaration.importClause)) || - isVisibleNamedBinding(importDeclaration.importClause.namedBindings); - } - } - moduleElementDeclarationEmitInfo.push({ - node, - outputPos: writer.getTextPos(), - indent: writer.getIndent(), - isVisible - }); - } - } - } - - function writeModuleElement(node: Node) { - switch (node.kind) { - case SyntaxKind.FunctionDeclaration: - return writeFunctionDeclaration(node); - case SyntaxKind.VariableStatement: - return writeVariableStatement(node); - case SyntaxKind.InterfaceDeclaration: - return writeInterfaceDeclaration(node); - case SyntaxKind.ClassDeclaration: - return writeClassDeclaration(node); - case SyntaxKind.TypeAliasDeclaration: - return writeTypeAliasDeclaration(node); - case SyntaxKind.EnumDeclaration: - return writeEnumDeclaration(node); - case SyntaxKind.ModuleDeclaration: - return writeModuleDeclaration(node); - case SyntaxKind.ImportEqualsDeclaration: - return writeImportEqualsDeclaration(node); - case SyntaxKind.ImportDeclaration: - return writeImportDeclaration(node); - default: - Debug.fail("Unknown symbol kind"); - } - } - - function emitModuleElementDeclarationFlags(node: Node) { - // If the node is parented in the current source file we need to emit export declare or just export - if (node.parent.kind === SyntaxKind.SourceFile) { - const modifiers = getModifierFlags(node); - // If the node is exported - if (modifiers & ModifierFlags.Export) { - resultHasExternalModuleIndicator = true; // Top-level exports are external module indicators - write("export "); - } - - if (modifiers & ModifierFlags.Default) { - write("default "); - } - else if (node.kind !== SyntaxKind.InterfaceDeclaration && needsDeclare) { - write("declare "); - } - } - } - - function emitClassMemberDeclarationFlags(flags: ModifierFlags) { - if (flags & ModifierFlags.Private) { - write("private "); - } - else if (flags & ModifierFlags.Protected) { - write("protected "); - } - - if (flags & ModifierFlags.Static) { - write("static "); - } - if (flags & ModifierFlags.Readonly) { - write("readonly "); - } - if (flags & ModifierFlags.Abstract) { - write("abstract "); - } - } - - function writeImportEqualsDeclaration(node: ImportEqualsDeclaration) { - // note usage of writer. methods instead of aliases created, just to make sure we are using - // correct writer especially to handle asynchronous alias writing - emitJsDocComments(node); - if (hasModifier(node, ModifierFlags.Export)) { - write("export "); - } - write("import "); - writeTextOfNode(currentText, node.name); - write(" = "); - if (isInternalModuleImportEqualsDeclaration(node)) { - emitTypeWithNewGetSymbolAccessibilityDiagnostic(node.moduleReference, getImportEntityNameVisibilityError); - write(";"); - } - else { - write("require("); - emitExternalModuleSpecifier(node); - write(");"); - } - writer.writeLine(); - - function getImportEntityNameVisibilityError(): SymbolAccessibilityDiagnostic { - return { - diagnosticMessage: Diagnostics.Import_declaration_0_is_using_private_name_1, - errorNode: node, - typeName: node.name - }; - } - } - - function isVisibleNamedBinding(namedBindings: NamespaceImport | NamedImports): boolean { - if (namedBindings) { - if (namedBindings.kind === SyntaxKind.NamespaceImport) { - return resolver.isDeclarationVisible(namedBindings); - } - else { - return namedBindings.elements.some(namedImport => resolver.isDeclarationVisible(namedImport)); - } - } - } - - function writeImportDeclaration(node: ImportDeclaration) { - emitJsDocComments(node); - if (hasModifier(node, ModifierFlags.Export)) { - write("export "); - } - write("import "); - if (node.importClause) { - const currentWriterPos = writer.getTextPos(); - if (node.importClause.name && resolver.isDeclarationVisible(node.importClause)) { - writeTextOfNode(currentText, node.importClause.name); - } - if (node.importClause.namedBindings && isVisibleNamedBinding(node.importClause.namedBindings)) { - if (currentWriterPos !== writer.getTextPos()) { - // If the default binding was emitted, write the separated - write(", "); - } - if (node.importClause.namedBindings.kind === SyntaxKind.NamespaceImport) { - write("* as "); - writeTextOfNode(currentText, node.importClause.namedBindings.name); - } - else { - write("{ "); - emitCommaList(node.importClause.namedBindings.elements, emitImportOrExportSpecifier, resolver.isDeclarationVisible); - write(" }"); - } - } - write(" from "); - } - emitExternalModuleSpecifier(node); - write(";"); - writer.writeLine(); - } - - function emitExternalModuleSpecifier(parent: ImportEqualsDeclaration | ImportDeclaration | ExportDeclaration | ModuleDeclaration) { - // emitExternalModuleSpecifier is usually called when we emit something in the.d.ts file that will make it an external module (i.e. import/export declarations). - // the only case when it is not true is when we call it to emit correct name for module augmentation - d.ts files with just module augmentations are not considered - // external modules since they are indistinguishable from script files with ambient modules. To fix this in such d.ts files we'll emit top level 'export {}' - // so compiler will treat them as external modules. - resultHasExternalModuleIndicator = resultHasExternalModuleIndicator || parent.kind !== SyntaxKind.ModuleDeclaration; - const moduleSpecifier = parent.kind === SyntaxKind.ImportEqualsDeclaration ? getExternalModuleImportEqualsDeclarationExpression(parent) : - parent.kind === SyntaxKind.ModuleDeclaration ? parent.name : parent.moduleSpecifier; - - if (moduleSpecifier.kind === SyntaxKind.StringLiteral && isBundledEmit && (compilerOptions.out || compilerOptions.outFile)) { - const moduleName = getExternalModuleNameFromDeclaration(host, resolver, parent); - if (moduleName) { - write('"'); - write(moduleName); - write('"'); - return; - } - } - - writeTextOfNode(currentText, moduleSpecifier); - } - - function emitImportOrExportSpecifier(node: ImportOrExportSpecifier) { - if (node.propertyName) { - writeTextOfNode(currentText, node.propertyName); - write(" as "); - } - writeTextOfNode(currentText, node.name); - } - - function emitExportSpecifier(node: ExportSpecifier) { - emitImportOrExportSpecifier(node); - - // Make all the declarations visible for the export name - const nodes = resolver.collectLinkedAliases(node.propertyName || node.name); - - // write each of these declarations asynchronously - writeAsynchronousModuleElements(nodes); - } - - function emitExportDeclaration(node: ExportDeclaration) { - resultHasExternalModuleIndicator = true; // Top-level exports are external module indicators - emitJsDocComments(node); - write("export "); - if (node.exportClause) { - write("{ "); - emitCommaList(node.exportClause.elements, emitExportSpecifier); - write(" }"); - } - else { - write("*"); - } - if (node.moduleSpecifier) { - write(" from "); - emitExternalModuleSpecifier(node); - } - write(";"); - writer.writeLine(); - } - - function writeModuleDeclaration(node: ModuleDeclaration) { - emitJsDocComments(node); - emitModuleElementDeclarationFlags(node); - if (isGlobalScopeAugmentation(node)) { - write("global "); - } - else { - if (node.flags & NodeFlags.Namespace) { - write("namespace "); - } - else { - write("module "); - } - if (isExternalModuleAugmentation(node)) { - emitExternalModuleSpecifier(node); - } - else { - writeTextOfNode(currentText, node.name); - } - } - while (node.body && node.body.kind !== SyntaxKind.ModuleBlock) { - node = node.body; - write("."); - writeTextOfNode(currentText, node.name); - } - const prevEnclosingDeclaration = enclosingDeclaration; - if (node.body) { - enclosingDeclaration = node; - write(" {"); - writeLine(); - increaseIndent(); - emitLines((node.body).statements); - decreaseIndent(); - write("}"); - writeLine(); - enclosingDeclaration = prevEnclosingDeclaration; - } - else { - write(";"); - } - } - - function writeTypeAliasDeclaration(node: TypeAliasDeclaration) { - const prevEnclosingDeclaration = enclosingDeclaration; - enclosingDeclaration = node; - emitJsDocComments(node); - emitModuleElementDeclarationFlags(node); - write("type "); - writeTextOfNode(currentText, node.name); - emitTypeParameters(node.typeParameters); - write(" = "); - emitTypeWithNewGetSymbolAccessibilityDiagnostic(node.type, getTypeAliasDeclarationVisibilityError); - write(";"); - writeLine(); - enclosingDeclaration = prevEnclosingDeclaration; - - function getTypeAliasDeclarationVisibilityError(): SymbolAccessibilityDiagnostic { - return { - diagnosticMessage: Diagnostics.Exported_type_alias_0_has_or_is_using_private_name_1, - errorNode: node.type, - typeName: node.name - }; - } - } - - function writeEnumDeclaration(node: EnumDeclaration) { - emitJsDocComments(node); - emitModuleElementDeclarationFlags(node); - if (isConst(node)) { - write("const "); - } - write("enum "); - writeTextOfNode(currentText, node.name); - write(" {"); - writeLine(); - increaseIndent(); - emitLines(node.members); - decreaseIndent(); - write("}"); - writeLine(); - } - - function emitEnumMemberDeclaration(node: EnumMember) { - emitJsDocComments(node); - writeTextOfNode(currentText, node.name); - const enumMemberValue = resolver.getConstantValue(node); - if (enumMemberValue !== undefined) { - write(" = "); - write(getTextOfConstantValue(enumMemberValue)); - } - write(","); - writeLine(); - } - - function isPrivateMethodTypeParameter(node: TypeParameterDeclaration) { - return node.parent.kind === SyntaxKind.MethodDeclaration && hasModifier(node.parent, ModifierFlags.Private); - } - - function emitTypeParameters(typeParameters: ReadonlyArray) { - function emitTypeParameter(node: TypeParameterDeclaration) { - increaseIndent(); - emitJsDocComments(node); - decreaseIndent(); - writeTextOfNode(currentText, node.name); - // If there is constraint present and this is not a type parameter of the private method emit the constraint - if (node.constraint && !isPrivateMethodTypeParameter(node)) { - write(" extends "); - if (node.parent.kind === SyntaxKind.FunctionType || - node.parent.kind === SyntaxKind.ConstructorType || - (node.parent.parent && node.parent.parent.kind === SyntaxKind.TypeLiteral)) { - Debug.assert(node.parent.kind === SyntaxKind.MethodDeclaration || - node.parent.kind === SyntaxKind.MethodSignature || - node.parent.kind === SyntaxKind.FunctionType || - node.parent.kind === SyntaxKind.ConstructorType || - node.parent.kind === SyntaxKind.CallSignature || - node.parent.kind === SyntaxKind.ConstructSignature); - emitType(node.constraint); - } - else { - emitTypeWithNewGetSymbolAccessibilityDiagnostic(node.constraint, getTypeParameterConstraintVisibilityError); - } - } - if (node.default && !isPrivateMethodTypeParameter(node)) { - write(" = "); - if (node.parent.kind === SyntaxKind.FunctionType || - node.parent.kind === SyntaxKind.ConstructorType || - (node.parent.parent && node.parent.parent.kind === SyntaxKind.TypeLiteral)) { - Debug.assert(node.parent.kind === SyntaxKind.MethodDeclaration || - node.parent.kind === SyntaxKind.MethodSignature || - node.parent.kind === SyntaxKind.FunctionType || - node.parent.kind === SyntaxKind.ConstructorType || - node.parent.kind === SyntaxKind.CallSignature || - node.parent.kind === SyntaxKind.ConstructSignature); - emitType(node.default); - } - else { - emitTypeWithNewGetSymbolAccessibilityDiagnostic(node.default, getTypeParameterConstraintVisibilityError); - } - } - - function getTypeParameterConstraintVisibilityError(): SymbolAccessibilityDiagnostic { - // Type parameter constraints are named by user so we should always be able to name it - let diagnosticMessage: DiagnosticMessage; - switch (node.parent.kind) { - case SyntaxKind.ClassDeclaration: - diagnosticMessage = Diagnostics.Type_parameter_0_of_exported_class_has_or_is_using_private_name_1; - break; - - case SyntaxKind.InterfaceDeclaration: - diagnosticMessage = Diagnostics.Type_parameter_0_of_exported_interface_has_or_is_using_private_name_1; - break; - - case SyntaxKind.ConstructSignature: - diagnosticMessage = Diagnostics.Type_parameter_0_of_constructor_signature_from_exported_interface_has_or_is_using_private_name_1; - break; - - case SyntaxKind.CallSignature: - diagnosticMessage = Diagnostics.Type_parameter_0_of_call_signature_from_exported_interface_has_or_is_using_private_name_1; - break; - - case SyntaxKind.MethodDeclaration: - case SyntaxKind.MethodSignature: - if (hasModifier(node.parent, ModifierFlags.Static)) { - diagnosticMessage = Diagnostics.Type_parameter_0_of_public_static_method_from_exported_class_has_or_is_using_private_name_1; - } - else if (node.parent.parent.kind === SyntaxKind.ClassDeclaration) { - diagnosticMessage = Diagnostics.Type_parameter_0_of_public_method_from_exported_class_has_or_is_using_private_name_1; - } - else { - diagnosticMessage = Diagnostics.Type_parameter_0_of_method_from_exported_interface_has_or_is_using_private_name_1; - } - break; - - case SyntaxKind.FunctionDeclaration: - diagnosticMessage = Diagnostics.Type_parameter_0_of_exported_function_has_or_is_using_private_name_1; - break; - - case SyntaxKind.TypeAliasDeclaration: - diagnosticMessage = Diagnostics.Type_parameter_0_of_exported_type_alias_has_or_is_using_private_name_1; - break; - - default: - Debug.fail("This is unknown parent for type parameter: " + node.parent.kind); - } - - return { - diagnosticMessage, - errorNode: node, - typeName: node.name - }; - } - } - - if (typeParameters) { - write("<"); - emitCommaList(typeParameters, emitTypeParameter); - write(">"); - } - } - - function emitHeritageClause(typeReferences: ReadonlyArray, isImplementsList: boolean) { - if (typeReferences) { - write(isImplementsList ? " implements " : " extends "); - emitCommaList(typeReferences, emitTypeOfTypeReference); - } - - function emitTypeOfTypeReference(node: ExpressionWithTypeArguments) { - if (isEntityNameExpression(node.expression)) { - emitTypeWithNewGetSymbolAccessibilityDiagnostic(node, getHeritageClauseVisibilityError); - } - else if (!isImplementsList && node.expression.kind === SyntaxKind.NullKeyword) { - write("null"); - } - - function getHeritageClauseVisibilityError(): SymbolAccessibilityDiagnostic { - let diagnosticMessage: DiagnosticMessage; - // Heritage clause is written by user so it can always be named - if (node.parent.parent.kind === SyntaxKind.ClassDeclaration) { - // Class or Interface implemented/extended is inaccessible - diagnosticMessage = isImplementsList ? - Diagnostics.Implements_clause_of_exported_class_0_has_or_is_using_private_name_1 : - Diagnostics.extends_clause_of_exported_class_0_has_or_is_using_private_name_1; - } - else { - // interface is inaccessible - diagnosticMessage = Diagnostics.extends_clause_of_exported_interface_0_has_or_is_using_private_name_1; - } - - return { - diagnosticMessage, - errorNode: node, - typeName: getNameOfDeclaration(node.parent.parent) - }; - } - } - } - - function writeClassDeclaration(node: ClassDeclaration) { - function emitParameterProperties(constructorDeclaration: ConstructorDeclaration) { - if (constructorDeclaration) { - forEach(constructorDeclaration.parameters, param => { - if (hasModifier(param, ModifierFlags.ParameterPropertyModifier)) { - emitPropertyDeclaration(param); - } - }); - } - } - - const prevEnclosingDeclaration = enclosingDeclaration; - enclosingDeclaration = node; - const baseTypeNode = getClassExtendsHeritageClauseElement(node); - let tempVarName: string; - if (baseTypeNode && !isEntityNameExpression(baseTypeNode.expression)) { - tempVarName = baseTypeNode.expression.kind === SyntaxKind.NullKeyword ? - "null" : - emitTempVariableDeclaration(baseTypeNode.expression, `${node.name.escapedText}_base`, { - diagnosticMessage: Diagnostics.extends_clause_of_exported_class_0_has_or_is_using_private_name_1, - errorNode: baseTypeNode, - typeName: node.name - }, !findAncestor(node, n => n.kind === SyntaxKind.ModuleDeclaration)); - } - - emitJsDocComments(node); - emitModuleElementDeclarationFlags(node); - if (hasModifier(node, ModifierFlags.Abstract)) { - write("abstract "); - } - write("class "); - writeTextOfNode(currentText, node.name); - emitTypeParameters(node.typeParameters); - if (baseTypeNode) { - if (!isEntityNameExpression(baseTypeNode.expression)) { - write(" extends "); - write(tempVarName); - if (baseTypeNode.typeArguments) { - write("<"); - emitCommaList(baseTypeNode.typeArguments, emitType); - write(">"); - } - } - else { - emitHeritageClause([baseTypeNode], /*isImplementsList*/ false); - } - } - emitHeritageClause(getClassImplementsHeritageClauseElements(node), /*isImplementsList*/ true); - write(" {"); - writeLine(); - increaseIndent(); - emitParameterProperties(getFirstConstructorWithBody(node)); - emitLines(node.members); - decreaseIndent(); - write("}"); - writeLine(); - enclosingDeclaration = prevEnclosingDeclaration; - } - - function writeInterfaceDeclaration(node: InterfaceDeclaration) { - emitJsDocComments(node); - emitModuleElementDeclarationFlags(node); - write("interface "); - writeTextOfNode(currentText, node.name); - const prevEnclosingDeclaration = enclosingDeclaration; - enclosingDeclaration = node; - emitTypeParameters(node.typeParameters); - const interfaceExtendsTypes = filter(getInterfaceBaseTypeNodes(node), base => isEntityNameExpression(base.expression)); - if (interfaceExtendsTypes && interfaceExtendsTypes.length) { - emitHeritageClause(interfaceExtendsTypes, /*isImplementsList*/ false); - } - write(" {"); - writeLine(); - increaseIndent(); - emitLines(node.members); - decreaseIndent(); - write("}"); - writeLine(); - enclosingDeclaration = prevEnclosingDeclaration; - } - - function emitPropertyDeclaration(node: Declaration) { - if (hasDynamicName(node) && !resolver.isLateBound(node)) { - return; - } - - emitJsDocComments(node); - emitClassMemberDeclarationFlags(getModifierFlags(node)); - emitVariableDeclaration(node); - write(";"); - writeLine(); - } - - function bindingNameContainsVisibleBindingElement(node: BindingName): boolean { - return !!node && isBindingPattern(node) && some(node.elements, elem => !isOmittedExpression(elem) && isVariableDeclarationVisible(elem)); - } - - function isVariableDeclarationVisible(node: VariableDeclaration | BindingElement) { - return resolver.isDeclarationVisible(node) || bindingNameContainsVisibleBindingElement(node.name); - } - - function emitVariableDeclaration(node: VariableDeclaration | PropertyDeclaration | PropertySignature | ParameterDeclaration) { - // If we are emitting property it isn't moduleElement and hence we already know it needs to be emitted - // so there is no check needed to see if declaration is visible - if (node.kind !== SyntaxKind.VariableDeclaration || isVariableDeclarationVisible(node)) { - if (isBindingPattern(node.name)) { - emitBindingPattern(node.name); - } - else { - writeNameOfDeclaration(node, getVariableDeclarationTypeVisibilityError); - - // If optional property emit ? but in the case of parameterProperty declaration with "?" indicating optional parameter for the constructor - // we don't want to emit property declaration with "?" - if ((node.kind === SyntaxKind.PropertyDeclaration || node.kind === SyntaxKind.PropertySignature || - (node.kind === SyntaxKind.Parameter && !isParameterPropertyDeclaration(node))) && hasQuestionToken(node)) { - write("?"); - } - if ((node.kind === SyntaxKind.PropertyDeclaration || node.kind === SyntaxKind.PropertySignature) && node.parent.kind === SyntaxKind.TypeLiteral) { - emitTypeOfVariableDeclarationFromTypeLiteral(node); - } - else if (resolver.isLiteralConstDeclaration(node)) { - write(" = "); - resolver.writeLiteralConstValue(node, writer); - } - else if (!hasModifier(node, ModifierFlags.Private)) { - writeTypeOfDeclaration(node, node.type, getVariableDeclarationTypeVisibilityError); - } - } - } - - function getVariableDeclarationTypeVisibilityDiagnosticMessage(symbolAccessibilityResult: SymbolAccessibilityResult) { - if (node.kind === SyntaxKind.VariableDeclaration) { - return symbolAccessibilityResult.errorModuleName ? - symbolAccessibilityResult.accessibility === SymbolAccessibility.CannotBeNamed ? - Diagnostics.Exported_variable_0_has_or_is_using_name_1_from_external_module_2_but_cannot_be_named : - Diagnostics.Exported_variable_0_has_or_is_using_name_1_from_private_module_2 : - Diagnostics.Exported_variable_0_has_or_is_using_private_name_1; - } - // This check is to ensure we don't report error on constructor parameter property as that error would be reported during parameter emit - // The only exception here is if the constructor was marked as private. we are not emitting the constructor parameters at all. - else if (node.kind === SyntaxKind.PropertyDeclaration || node.kind === SyntaxKind.PropertySignature || - (node.kind === SyntaxKind.Parameter && hasModifier(node.parent, ModifierFlags.Private))) { - // TODO(jfreeman): Deal with computed properties in error reporting. - if (hasModifier(node, ModifierFlags.Static)) { - return symbolAccessibilityResult.errorModuleName ? - symbolAccessibilityResult.accessibility === SymbolAccessibility.CannotBeNamed ? - Diagnostics.Public_static_property_0_of_exported_class_has_or_is_using_name_1_from_external_module_2_but_cannot_be_named : - Diagnostics.Public_static_property_0_of_exported_class_has_or_is_using_name_1_from_private_module_2 : - Diagnostics.Public_static_property_0_of_exported_class_has_or_is_using_private_name_1; - } - else if (node.parent.kind === SyntaxKind.ClassDeclaration || node.kind === SyntaxKind.Parameter) { - return symbolAccessibilityResult.errorModuleName ? - symbolAccessibilityResult.accessibility === SymbolAccessibility.CannotBeNamed ? - Diagnostics.Public_property_0_of_exported_class_has_or_is_using_name_1_from_external_module_2_but_cannot_be_named : - Diagnostics.Public_property_0_of_exported_class_has_or_is_using_name_1_from_private_module_2 : - Diagnostics.Public_property_0_of_exported_class_has_or_is_using_private_name_1; - } - else { - // Interfaces cannot have types that cannot be named - return symbolAccessibilityResult.errorModuleName ? - Diagnostics.Property_0_of_exported_interface_has_or_is_using_name_1_from_private_module_2 : - Diagnostics.Property_0_of_exported_interface_has_or_is_using_private_name_1; - } - } - } - - function getVariableDeclarationTypeVisibilityError(symbolAccessibilityResult: SymbolAccessibilityResult): SymbolAccessibilityDiagnostic { - const diagnosticMessage = getVariableDeclarationTypeVisibilityDiagnosticMessage(symbolAccessibilityResult); - return diagnosticMessage !== undefined ? { - diagnosticMessage, - errorNode: node, - typeName: node.name - } : undefined; - } - - function emitBindingPattern(bindingPattern: BindingPattern) { - // Only select visible, non-omitted expression from the bindingPattern's elements. - // We have to do this to avoid emitting trailing commas. - // For example: - // original: var [, c,,] = [ 2,3,4] - // emitted: declare var c: number; // instead of declare var c:number, ; - const elements: Node[] = []; - for (const element of bindingPattern.elements) { - if (element.kind !== SyntaxKind.OmittedExpression && isVariableDeclarationVisible(element)) { - elements.push(element); - } - } - emitCommaList(elements, emitBindingElement); - } - - function emitBindingElement(bindingElement: BindingElement) { - function getBindingElementTypeVisibilityError(symbolAccessibilityResult: SymbolAccessibilityResult): SymbolAccessibilityDiagnostic { - const diagnosticMessage = getVariableDeclarationTypeVisibilityDiagnosticMessage(symbolAccessibilityResult); - return diagnosticMessage !== undefined ? { - diagnosticMessage, - errorNode: bindingElement, - typeName: bindingElement.name - } : undefined; - } - - if (bindingElement.name) { - if (isBindingPattern(bindingElement.name)) { - emitBindingPattern(bindingElement.name); - } - else { - writeTextOfNode(currentText, bindingElement.name); - writeTypeOfDeclaration(bindingElement, /*type*/ undefined, getBindingElementTypeVisibilityError); - } - } - } - } - - function emitTypeOfVariableDeclarationFromTypeLiteral(node: VariableLikeDeclaration) { - // if this is property of type literal, - // or is parameter of method/call/construct/index signature of type literal - // emit only if type is specified - if (hasType(node)) { - write(": "); - emitType(node.type); - } - } - - function isVariableStatementVisible(node: VariableStatement) { - return forEach(node.declarationList.declarations, varDeclaration => isVariableDeclarationVisible(varDeclaration)); - } - - function writeVariableStatement(node: VariableStatement) { - // If binding pattern doesn't have name, then there is nothing to be emitted for declaration file i.e. const [,] = [1,2]. - if (every(node.declarationList && node.declarationList.declarations, decl => decl.name && isEmptyBindingPattern(decl.name))) { - return; - } - emitJsDocComments(node); - emitModuleElementDeclarationFlags(node); - if (isLet(node.declarationList)) { - write("let "); - } - else if (isConst(node.declarationList)) { - write("const "); - } - else { - write("var "); - } - emitCommaList(node.declarationList.declarations, emitVariableDeclaration, isVariableDeclarationVisible); - write(";"); - writeLine(); - } - - function emitAccessorDeclaration(node: AccessorDeclaration) { - if (hasDynamicName(node) && !resolver.isLateBound(node)) { - return; - } - - const accessors = getAllAccessorDeclarations((node.parent).members, node); - let accessorWithTypeAnnotation: AccessorDeclaration; - - if (node === accessors.firstAccessor) { - emitJsDocComments(accessors.getAccessor); - emitJsDocComments(accessors.setAccessor); - emitClassMemberDeclarationFlags(getModifierFlags(node) | (accessors.setAccessor ? 0 : ModifierFlags.Readonly)); - writeNameOfDeclaration(node, getAccessorNameVisibilityError); - if (!hasModifier(node, ModifierFlags.Private)) { - accessorWithTypeAnnotation = node; - let type = getTypeAnnotationFromAccessor(node); - if (!type) { - // couldn't get type for the first accessor, try the another one - const anotherAccessor = node.kind === SyntaxKind.GetAccessor ? accessors.setAccessor : accessors.getAccessor; - type = getTypeAnnotationFromAccessor(anotherAccessor); - if (type) { - accessorWithTypeAnnotation = anotherAccessor; - } - } - writeTypeOfDeclaration(node, type, getAccessorDeclarationTypeVisibilityError); - } - write(";"); - writeLine(); - } - - function getTypeAnnotationFromAccessor(accessor: AccessorDeclaration): TypeNode { - if (accessor) { - return accessor.kind === SyntaxKind.GetAccessor - ? accessor.type // Getter - return type - : accessor.parameters.length > 0 - ? accessor.parameters[0].type // Setter parameter type - : undefined; - } - } - - function getAccessorNameVisibilityError(symbolAccessibilityResult: SymbolAccessibilityResult) { - const diagnosticMessage = getAccessorNameVisibilityDiagnosticMessage(symbolAccessibilityResult); - return diagnosticMessage !== undefined ? { - diagnosticMessage, - errorNode: node, - typeName: node.name - } : undefined; - } - - function getAccessorNameVisibilityDiagnosticMessage(symbolAccessibilityResult: SymbolAccessibilityResult) { - if (hasModifier(node, ModifierFlags.Static)) { - return symbolAccessibilityResult.errorModuleName ? - symbolAccessibilityResult.accessibility === SymbolAccessibility.CannotBeNamed ? - Diagnostics.Public_static_property_0_of_exported_class_has_or_is_using_name_1_from_external_module_2_but_cannot_be_named : - Diagnostics.Public_static_property_0_of_exported_class_has_or_is_using_name_1_from_private_module_2 : - Diagnostics.Public_static_property_0_of_exported_class_has_or_is_using_private_name_1; - } - else if (node.parent.kind === SyntaxKind.ClassDeclaration) { - return symbolAccessibilityResult.errorModuleName ? - symbolAccessibilityResult.accessibility === SymbolAccessibility.CannotBeNamed ? - Diagnostics.Public_property_0_of_exported_class_has_or_is_using_name_1_from_external_module_2_but_cannot_be_named : - Diagnostics.Public_property_0_of_exported_class_has_or_is_using_name_1_from_private_module_2 : - Diagnostics.Public_property_0_of_exported_class_has_or_is_using_private_name_1; - } - else { - return symbolAccessibilityResult.errorModuleName ? - Diagnostics.Property_0_of_exported_interface_has_or_is_using_name_1_from_private_module_2 : - Diagnostics.Property_0_of_exported_interface_has_or_is_using_private_name_1; - } - } - - function getAccessorDeclarationTypeVisibilityError(symbolAccessibilityResult: SymbolAccessibilityResult): SymbolAccessibilityDiagnostic { - let diagnosticMessage: DiagnosticMessage; - if (accessorWithTypeAnnotation.kind === SyntaxKind.SetAccessor) { - // Getters can infer the return type from the returned expression, but setters cannot, so the - // "_from_external_module_1_but_cannot_be_named" case cannot occur. - if (hasModifier(accessorWithTypeAnnotation, ModifierFlags.Static)) { - diagnosticMessage = symbolAccessibilityResult.errorModuleName ? - Diagnostics.Parameter_type_of_public_static_setter_0_from_exported_class_has_or_is_using_name_1_from_private_module_2 : - Diagnostics.Parameter_type_of_public_static_setter_0_from_exported_class_has_or_is_using_private_name_1; - } - else { - diagnosticMessage = symbolAccessibilityResult.errorModuleName ? - Diagnostics.Parameter_type_of_public_setter_0_from_exported_class_has_or_is_using_name_1_from_private_module_2 : - Diagnostics.Parameter_type_of_public_setter_0_from_exported_class_has_or_is_using_private_name_1; - } - } - else { - if (hasModifier(accessorWithTypeAnnotation, ModifierFlags.Static)) { - diagnosticMessage = symbolAccessibilityResult.errorModuleName ? - symbolAccessibilityResult.accessibility === SymbolAccessibility.CannotBeNamed ? - Diagnostics.Return_type_of_public_static_getter_0_from_exported_class_has_or_is_using_name_1_from_external_module_2_but_cannot_be_named : - Diagnostics.Return_type_of_public_static_getter_0_from_exported_class_has_or_is_using_name_1_from_private_module_2 : - Diagnostics.Return_type_of_public_static_getter_0_from_exported_class_has_or_is_using_private_name_1; - } - else { - diagnosticMessage = symbolAccessibilityResult.errorModuleName ? - symbolAccessibilityResult.accessibility === SymbolAccessibility.CannotBeNamed ? - Diagnostics.Return_type_of_public_getter_0_from_exported_class_has_or_is_using_name_1_from_external_module_2_but_cannot_be_named : - Diagnostics.Return_type_of_public_getter_0_from_exported_class_has_or_is_using_name_1_from_private_module_2 : - Diagnostics.Return_type_of_public_getter_0_from_exported_class_has_or_is_using_private_name_1; - } - } - return { - diagnosticMessage, - errorNode: accessorWithTypeAnnotation.name, - typeName: accessorWithTypeAnnotation.name - }; - } - } - - function writeFunctionDeclaration(node: FunctionLikeDeclaration) { - if (hasDynamicName(node) && !resolver.isLateBound(node)) { - return; - } - - // If we are emitting Method/Constructor it isn't moduleElement and hence already determined to be emitting - // so no need to verify if the declaration is visible - if (!resolver.isImplementationOfOverload(node)) { - emitJsDocComments(node); - if (node.kind === SyntaxKind.FunctionDeclaration) { - emitModuleElementDeclarationFlags(node); - } - else if (node.kind === SyntaxKind.MethodDeclaration || node.kind === SyntaxKind.Constructor) { - emitClassMemberDeclarationFlags(getModifierFlags(node)); - } - if (node.kind === SyntaxKind.FunctionDeclaration) { - write("function "); - writeTextOfNode(currentText, node.name); - } - else if (node.kind === SyntaxKind.Constructor) { - write("constructor"); - } - else { - writeNameOfDeclaration(node, getMethodNameVisibilityError); - if (hasQuestionToken(node)) { - write("?"); - } - } - emitSignatureDeclaration(node); - } - - function getMethodNameVisibilityError(symbolAccessibilityResult: SymbolAccessibilityResult): SymbolAccessibilityDiagnostic { - const diagnosticMessage = getMethodNameVisibilityDiagnosticMessage(symbolAccessibilityResult); - return diagnosticMessage !== undefined ? { - diagnosticMessage, - errorNode: node, - typeName: node.name - } : undefined; - } - - function getMethodNameVisibilityDiagnosticMessage(symbolAccessibilityResult: SymbolAccessibilityResult) { - if (hasModifier(node, ModifierFlags.Static)) { - return symbolAccessibilityResult.errorModuleName ? - symbolAccessibilityResult.accessibility === SymbolAccessibility.CannotBeNamed ? - Diagnostics.Public_static_method_0_of_exported_class_has_or_is_using_name_1_from_external_module_2_but_cannot_be_named : - Diagnostics.Public_static_method_0_of_exported_class_has_or_is_using_name_1_from_private_module_2 : - Diagnostics.Public_static_method_0_of_exported_class_has_or_is_using_private_name_1; - } - else if (node.parent.kind === SyntaxKind.ClassDeclaration) { - return symbolAccessibilityResult.errorModuleName ? - symbolAccessibilityResult.accessibility === SymbolAccessibility.CannotBeNamed ? - Diagnostics.Public_method_0_of_exported_class_has_or_is_using_name_1_from_external_module_2_but_cannot_be_named : - Diagnostics.Public_method_0_of_exported_class_has_or_is_using_name_1_from_private_module_2 : - Diagnostics.Public_method_0_of_exported_class_has_or_is_using_private_name_1; - } - else { - return symbolAccessibilityResult.errorModuleName ? - Diagnostics.Method_0_of_exported_interface_has_or_is_using_name_1_from_private_module_2 : - Diagnostics.Method_0_of_exported_interface_has_or_is_using_private_name_1; - } - } - } - - function writeNameOfDeclaration(node: NamedDeclaration, getSymbolAccessibilityDiagnostic: GetSymbolAccessibilityDiagnostic) { - if (hasDynamicName(node)) { - // If this node has a dynamic name, it can only be an identifier or property access because - // we've already skipped it otherwise. - Debug.assert(resolver.isLateBound(node)); - - writeLateBoundNameOfDeclaration(node as LateBoundDeclaration, getSymbolAccessibilityDiagnostic); - } - else { - // If this node is a computed name, it can only be a symbol, because we've already skipped - // it if it's not a well known symbol. In that case, the text of the name will be exactly - // what we want, namely the name expression enclosed in brackets. - writeTextOfNode(currentText, node.name); - } - } - - function writeLateBoundNameOfDeclaration(node: LateBoundDeclaration, getSymbolAccessibilityDiagnostic: GetSymbolAccessibilityDiagnostic) { - writer.getSymbolAccessibilityDiagnostic = getSymbolAccessibilityDiagnostic; - const entityName = node.name.expression; - const visibilityResult = resolver.isEntityNameVisible(entityName, enclosingDeclaration); - handleSymbolAccessibilityError(visibilityResult); - recordTypeReferenceDirectivesIfNecessary(resolver.getTypeReferenceDirectivesForEntityName(entityName)); - writeTextOfNode(currentText, node.name); - } - - function emitSignatureDeclarationWithJsDocComments(node: SignatureDeclaration) { - emitJsDocComments(node); - emitSignatureDeclaration(node); - } - - function emitSignatureDeclaration(node: SignatureDeclaration) { - const prevEnclosingDeclaration = enclosingDeclaration; - enclosingDeclaration = node; - let closeParenthesizedFunctionType = false; - - if (node.kind === SyntaxKind.IndexSignature) { - // Index signature can have readonly modifier - emitClassMemberDeclarationFlags(getModifierFlags(node)); - write("["); - } - else { - if (node.kind === SyntaxKind.Constructor && hasModifier(node, ModifierFlags.Private)) { - write("();"); - writeLine(); - return; - } - // Construct signature or constructor type write new Signature - if (node.kind === SyntaxKind.ConstructSignature || node.kind === SyntaxKind.ConstructorType) { - write("new "); - } - else if (node.kind === SyntaxKind.FunctionType) { - const currentOutput = writer.getText(); - // Do not generate incorrect type when function type with type parameters is type argument - // This could happen if user used space between two '<' making it error free - // e.g var x: A< (a: Tany)=>Tany>; - if (node.typeParameters && currentOutput.charAt(currentOutput.length - 1) === "<") { - closeParenthesizedFunctionType = true; - write("("); - } - } - emitTypeParameters(node.typeParameters); - write("("); - } - - // Parameters - emitCommaList(node.parameters, emitParameterDeclaration); - - if (node.kind === SyntaxKind.IndexSignature) { - write("]"); - } - else { - write(")"); - } - - // If this is not a constructor and is not private, emit the return type - const isFunctionTypeOrConstructorType = node.kind === SyntaxKind.FunctionType || node.kind === SyntaxKind.ConstructorType; - if (isFunctionTypeOrConstructorType || node.parent.kind === SyntaxKind.TypeLiteral) { - // Emit type literal signature return type only if specified - if (node.type) { - write(isFunctionTypeOrConstructorType ? " => " : ": "); - emitType(node.type); - } - } - else if (node.kind !== SyntaxKind.Constructor && !hasModifier(node, ModifierFlags.Private)) { - writeReturnTypeAtSignature(node, getReturnTypeVisibilityError); - } - - enclosingDeclaration = prevEnclosingDeclaration; - - if (!isFunctionTypeOrConstructorType) { - write(";"); - writeLine(); - } - else if (closeParenthesizedFunctionType) { - write(")"); - } - - function getReturnTypeVisibilityError(symbolAccessibilityResult: SymbolAccessibilityResult): SymbolAccessibilityDiagnostic { - let diagnosticMessage: DiagnosticMessage; - switch (node.kind) { - case SyntaxKind.ConstructSignature: - // Interfaces cannot have return types that cannot be named - diagnosticMessage = symbolAccessibilityResult.errorModuleName ? - Diagnostics.Return_type_of_constructor_signature_from_exported_interface_has_or_is_using_name_0_from_private_module_1 : - Diagnostics.Return_type_of_constructor_signature_from_exported_interface_has_or_is_using_private_name_0; - break; - - case SyntaxKind.CallSignature: - // Interfaces cannot have return types that cannot be named - diagnosticMessage = symbolAccessibilityResult.errorModuleName ? - Diagnostics.Return_type_of_call_signature_from_exported_interface_has_or_is_using_name_0_from_private_module_1 : - Diagnostics.Return_type_of_call_signature_from_exported_interface_has_or_is_using_private_name_0; - break; - - case SyntaxKind.IndexSignature: - // Interfaces cannot have return types that cannot be named - diagnosticMessage = symbolAccessibilityResult.errorModuleName ? - Diagnostics.Return_type_of_index_signature_from_exported_interface_has_or_is_using_name_0_from_private_module_1 : - Diagnostics.Return_type_of_index_signature_from_exported_interface_has_or_is_using_private_name_0; - break; - - case SyntaxKind.MethodDeclaration: - case SyntaxKind.MethodSignature: - if (hasModifier(node, ModifierFlags.Static)) { - diagnosticMessage = symbolAccessibilityResult.errorModuleName ? - symbolAccessibilityResult.accessibility === SymbolAccessibility.CannotBeNamed ? - Diagnostics.Return_type_of_public_static_method_from_exported_class_has_or_is_using_name_0_from_external_module_1_but_cannot_be_named : - Diagnostics.Return_type_of_public_static_method_from_exported_class_has_or_is_using_name_0_from_private_module_1 : - Diagnostics.Return_type_of_public_static_method_from_exported_class_has_or_is_using_private_name_0; - } - else if (node.parent.kind === SyntaxKind.ClassDeclaration) { - diagnosticMessage = symbolAccessibilityResult.errorModuleName ? - symbolAccessibilityResult.accessibility === SymbolAccessibility.CannotBeNamed ? - Diagnostics.Return_type_of_public_method_from_exported_class_has_or_is_using_name_0_from_external_module_1_but_cannot_be_named : - Diagnostics.Return_type_of_public_method_from_exported_class_has_or_is_using_name_0_from_private_module_1 : - Diagnostics.Return_type_of_public_method_from_exported_class_has_or_is_using_private_name_0; - } - else { - // Interfaces cannot have return types that cannot be named - diagnosticMessage = symbolAccessibilityResult.errorModuleName ? - Diagnostics.Return_type_of_method_from_exported_interface_has_or_is_using_name_0_from_private_module_1 : - Diagnostics.Return_type_of_method_from_exported_interface_has_or_is_using_private_name_0; - } - break; - - case SyntaxKind.FunctionDeclaration: - diagnosticMessage = symbolAccessibilityResult.errorModuleName ? - symbolAccessibilityResult.accessibility === SymbolAccessibility.CannotBeNamed ? - Diagnostics.Return_type_of_exported_function_has_or_is_using_name_0_from_external_module_1_but_cannot_be_named : - Diagnostics.Return_type_of_exported_function_has_or_is_using_name_0_from_private_module_1 : - Diagnostics.Return_type_of_exported_function_has_or_is_using_private_name_0; - break; - - default: - Debug.fail("This is unknown kind for signature: " + node.kind); - } - - return { - diagnosticMessage, - errorNode: node.name || node - }; - } - } - - function emitParameterDeclaration(node: ParameterDeclaration) { - increaseIndent(); - emitJsDocComments(node); - if (node.dotDotDotToken) { - write("..."); - } - if (isBindingPattern(node.name)) { - // For bindingPattern, we can't simply writeTextOfNode from the source file - // because we want to omit the initializer and using writeTextOfNode will result in initializer get emitted. - // Therefore, we will have to recursively emit each element in the bindingPattern. - emitBindingPattern(node.name); - } - else { - writeTextOfNode(currentText, node.name); - } - if (resolver.isOptionalParameter(node)) { - write("?"); - } - decreaseIndent(); - - if (node.parent.kind === SyntaxKind.FunctionType || - node.parent.kind === SyntaxKind.ConstructorType || - node.parent.parent.kind === SyntaxKind.TypeLiteral) { - emitTypeOfVariableDeclarationFromTypeLiteral(node); - } - else if (!hasModifier(node.parent, ModifierFlags.Private)) { - writeTypeOfDeclaration(node, node.type, getParameterDeclarationTypeVisibilityError); - } - - function getParameterDeclarationTypeVisibilityError(symbolAccessibilityResult: SymbolAccessibilityResult): SymbolAccessibilityDiagnostic { - const diagnosticMessage: DiagnosticMessage = getParameterDeclarationTypeVisibilityDiagnosticMessage(symbolAccessibilityResult); - return diagnosticMessage !== undefined ? { - diagnosticMessage, - errorNode: node, - typeName: node.name - } : undefined; - } - - function getParameterDeclarationTypeVisibilityDiagnosticMessage(symbolAccessibilityResult: SymbolAccessibilityResult): DiagnosticMessage { - switch (node.parent.kind) { - case SyntaxKind.Constructor: - return symbolAccessibilityResult.errorModuleName ? - symbolAccessibilityResult.accessibility === SymbolAccessibility.CannotBeNamed ? - Diagnostics.Parameter_0_of_constructor_from_exported_class_has_or_is_using_name_1_from_external_module_2_but_cannot_be_named : - Diagnostics.Parameter_0_of_constructor_from_exported_class_has_or_is_using_name_1_from_private_module_2 : - Diagnostics.Parameter_0_of_constructor_from_exported_class_has_or_is_using_private_name_1; - - case SyntaxKind.ConstructSignature: - // Interfaces cannot have parameter types that cannot be named - return symbolAccessibilityResult.errorModuleName ? - Diagnostics.Parameter_0_of_constructor_signature_from_exported_interface_has_or_is_using_name_1_from_private_module_2 : - Diagnostics.Parameter_0_of_constructor_signature_from_exported_interface_has_or_is_using_private_name_1; - - case SyntaxKind.CallSignature: - // Interfaces cannot have parameter types that cannot be named - return symbolAccessibilityResult.errorModuleName ? - Diagnostics.Parameter_0_of_call_signature_from_exported_interface_has_or_is_using_name_1_from_private_module_2 : - Diagnostics.Parameter_0_of_call_signature_from_exported_interface_has_or_is_using_private_name_1; - - case SyntaxKind.IndexSignature: - // Interfaces cannot have parameter types that cannot be named - return symbolAccessibilityResult.errorModuleName ? - Diagnostics.Parameter_0_of_index_signature_from_exported_interface_has_or_is_using_name_1_from_private_module_2 : - Diagnostics.Parameter_0_of_index_signature_from_exported_interface_has_or_is_using_private_name_1; - - case SyntaxKind.MethodDeclaration: - case SyntaxKind.MethodSignature: - if (hasModifier(node.parent, ModifierFlags.Static)) { - return symbolAccessibilityResult.errorModuleName ? - symbolAccessibilityResult.accessibility === SymbolAccessibility.CannotBeNamed ? - Diagnostics.Parameter_0_of_public_static_method_from_exported_class_has_or_is_using_name_1_from_external_module_2_but_cannot_be_named : - Diagnostics.Parameter_0_of_public_static_method_from_exported_class_has_or_is_using_name_1_from_private_module_2 : - Diagnostics.Parameter_0_of_public_static_method_from_exported_class_has_or_is_using_private_name_1; - } - else if (node.parent.parent.kind === SyntaxKind.ClassDeclaration) { - return symbolAccessibilityResult.errorModuleName ? - symbolAccessibilityResult.accessibility === SymbolAccessibility.CannotBeNamed ? - Diagnostics.Parameter_0_of_public_method_from_exported_class_has_or_is_using_name_1_from_external_module_2_but_cannot_be_named : - Diagnostics.Parameter_0_of_public_method_from_exported_class_has_or_is_using_name_1_from_private_module_2 : - Diagnostics.Parameter_0_of_public_method_from_exported_class_has_or_is_using_private_name_1; - } - else { - // Interfaces cannot have parameter types that cannot be named - return symbolAccessibilityResult.errorModuleName ? - Diagnostics.Parameter_0_of_method_from_exported_interface_has_or_is_using_name_1_from_private_module_2 : - Diagnostics.Parameter_0_of_method_from_exported_interface_has_or_is_using_private_name_1; - } - - case SyntaxKind.FunctionDeclaration: - return symbolAccessibilityResult.errorModuleName ? - symbolAccessibilityResult.accessibility === SymbolAccessibility.CannotBeNamed ? - Diagnostics.Parameter_0_of_exported_function_has_or_is_using_name_1_from_external_module_2_but_cannot_be_named : - Diagnostics.Parameter_0_of_exported_function_has_or_is_using_name_1_from_private_module_2 : - Diagnostics.Parameter_0_of_exported_function_has_or_is_using_private_name_1; - - default: - Debug.fail("This is unknown parent for parameter: " + node.parent.kind); - } - } - - function emitBindingPattern(bindingPattern: BindingPattern) { - // We have to explicitly emit square bracket and bracket because these tokens are not store inside the node. - if (bindingPattern.kind === SyntaxKind.ObjectBindingPattern) { - write("{"); - emitCommaList(bindingPattern.elements, emitBindingElement); - write("}"); - } - else if (bindingPattern.kind === SyntaxKind.ArrayBindingPattern) { - write("["); - const elements = bindingPattern.elements; - emitCommaList(elements, emitBindingElement); - if (elements && elements.hasTrailingComma) { - write(", "); - } - write("]"); - } - } - - function emitBindingElement(bindingElement: BindingElement | OmittedExpression) { - if (bindingElement.kind === SyntaxKind.OmittedExpression) { - // If bindingElement is an omittedExpression (i.e. containing elision), - // we will emit blank space (although this may differ from users' original code, - // it allows emitSeparatedList to write separator appropriately) - // Example: - // original: function foo([, x, ,]) {} - // tslint:disable-next-line no-double-space - // emit : function foo([ , x, , ]) {} - write(" "); - } - else if (bindingElement.kind === SyntaxKind.BindingElement) { - if (bindingElement.propertyName) { - // bindingElement has propertyName property in the following case: - // { y: [a,b,c] ...} -> bindingPattern will have a property called propertyName for "y" - // We have to explicitly emit the propertyName before descending into its binding elements. - // Example: - // original: function foo({y: [a,b,c]}) {} - // emit : declare function foo({y: [a, b, c]}: { y: [any, any, any] }) void; - writeTextOfNode(currentText, bindingElement.propertyName); - write(": "); - } - if (bindingElement.name) { - if (isBindingPattern(bindingElement.name)) { - // If it is a nested binding pattern, we will recursively descend into each element and emit each one separately. - // In the case of rest element, we will omit rest element. - // Example: - // original: function foo([a, [[b]], c] = [1,[["string"]], 3]) {} - // emit : declare function foo([a, [[b]], c]: [number, [[string]], number]): void; - // original with rest: function foo([a, ...c]) {} - // emit : declare function foo([a, ...c]): void; - emitBindingPattern(bindingElement.name); - } - else { - Debug.assert(bindingElement.name.kind === SyntaxKind.Identifier); - // If the node is just an identifier, we will simply emit the text associated with the node's name - // Example: - // original: function foo({y = 10, x}) {} - // emit : declare function foo({y, x}: {number, any}): void; - if (bindingElement.dotDotDotToken) { - write("..."); - } - writeTextOfNode(currentText, bindingElement.name); - } - } - } - } - } - - function emitNode(node: Node) { - switch (node.kind) { - case SyntaxKind.FunctionDeclaration: - case SyntaxKind.ModuleDeclaration: - case SyntaxKind.ImportEqualsDeclaration: - case SyntaxKind.InterfaceDeclaration: - case SyntaxKind.ClassDeclaration: - case SyntaxKind.TypeAliasDeclaration: - case SyntaxKind.EnumDeclaration: - return emitModuleElement(node, isModuleElementVisible(node)); - case SyntaxKind.VariableStatement: - return emitModuleElement(node, isVariableStatementVisible(node)); - case SyntaxKind.ImportDeclaration: - // Import declaration without import clause is visible, otherwise it is not visible - return emitModuleElement(node, /*isModuleElementVisible*/!(node).importClause); - case SyntaxKind.ExportDeclaration: - return emitExportDeclaration(node); - case SyntaxKind.Constructor: - case SyntaxKind.MethodDeclaration: - case SyntaxKind.MethodSignature: - return writeFunctionDeclaration(node); - case SyntaxKind.ConstructSignature: - case SyntaxKind.CallSignature: - case SyntaxKind.IndexSignature: - return emitSignatureDeclarationWithJsDocComments(node); - case SyntaxKind.GetAccessor: - case SyntaxKind.SetAccessor: - return emitAccessorDeclaration(node); - case SyntaxKind.PropertyDeclaration: - case SyntaxKind.PropertySignature: - return emitPropertyDeclaration(node); - case SyntaxKind.EnumMember: - return emitEnumMemberDeclaration(node); - case SyntaxKind.ExportAssignment: - return emitExportAssignment(node); - case SyntaxKind.SourceFile: - return emitSourceFile(node); - case SyntaxKind.Prepend: - return emitPrepend(node); - } - } - - /** - * Adds the reference to referenced file, returns true if global file reference was emitted - * @param referencedFile - * @param addBundledFileReference Determines if global file reference corresponding to bundled file should be emitted or not - */ - function writeReferencePath(referencedFile: SourceFile, addBundledFileReference: boolean, emitOnlyDtsFiles: boolean): boolean { - let declFileName: string; - let addedBundledEmitReference = false; - if (referencedFile.isDeclarationFile) { - // Declaration file, use declaration file name - declFileName = referencedFile.fileName; - } - else { - // Get the declaration file path - forEachEmittedFile(host, getDeclFileName, referencedFile, emitOnlyDtsFiles); - } - - if (declFileName) { - declFileName = getRelativePathToDirectoryOrUrl( - getDirectoryPath(normalizeSlashes(declarationFilePath)), - declFileName, - host.getCurrentDirectory(), - host.getCanonicalFileName, - /*isAbsolutePathAnUrl*/ false); - - referencesOutput += `/// ${newLine}`; - } - return addedBundledEmitReference; - - function getDeclFileName(emitFileNames: EmitFileNames, sourceFileOrBundle: SourceFile | Bundle) { - // Dont add reference path to this file if it is a bundled emit and caller asked not emit bundled file path - const isBundledEmit = sourceFileOrBundle.kind === SyntaxKind.Bundle; - if (isBundledEmit && !addBundledFileReference) { - return; - } - - Debug.assert(!!emitFileNames.declarationFilePath || isSourceFileJavaScript(referencedFile), "Declaration file is not present only for javascript files"); - declFileName = emitFileNames.declarationFilePath || emitFileNames.jsFilePath; - addedBundledEmitReference = isBundledEmit; - } - } - } - - /* @internal */ - export function writeDeclarationFile(declarationFilePath: string, sourceFileOrBundle: SourceFile | Bundle, host: EmitHost, resolver: EmitResolver, emitterDiagnostics: DiagnosticCollection, emitOnlyDtsFiles: boolean) { - const emitDeclarationResult = emitDeclarations(host, resolver, emitterDiagnostics, declarationFilePath, sourceFileOrBundle, emitOnlyDtsFiles); - const emitSkipped = emitDeclarationResult.reportedDeclarationError || host.isEmitBlocked(declarationFilePath) || host.getCompilerOptions().noEmit; - if (!emitSkipped || emitOnlyDtsFiles) { - const sourceFiles = sourceFileOrBundle.kind === SyntaxKind.Bundle ? sourceFileOrBundle.sourceFiles : [sourceFileOrBundle]; - const declarationOutput = emitDeclarationResult.referencesOutput - + getDeclarationOutput(emitDeclarationResult.synchronousDeclarationOutput, emitDeclarationResult.moduleElementDeclarationEmitInfo); - writeFile(host, emitterDiagnostics, declarationFilePath, declarationOutput, host.getCompilerOptions().emitBOM, sourceFiles); - } - return emitSkipped; - - function getDeclarationOutput(synchronousDeclarationOutput: string, moduleElementDeclarationEmitInfo: ModuleElementDeclarationEmitInfo[]) { - let appliedSyncOutputPos = 0; - let declarationOutput = ""; - // apply asynchronous additions to the synchronous output - forEach(moduleElementDeclarationEmitInfo, aliasEmitInfo => { - if (aliasEmitInfo.asynchronousOutput) { - declarationOutput += synchronousDeclarationOutput.substring(appliedSyncOutputPos, aliasEmitInfo.outputPos); - declarationOutput += getDeclarationOutput(aliasEmitInfo.asynchronousOutput, aliasEmitInfo.subModuleElementDeclarationEmitInfo); - appliedSyncOutputPos = aliasEmitInfo.outputPos; - } - }); - declarationOutput += synchronousDeclarationOutput.substring(appliedSyncOutputPos); - return declarationOutput; - } - } -} From f237834ab854dac1a1055e7e6e69d24078a7b820 Mon Sep 17 00:00:00 2001 From: Ryan Cavanaugh Date: Fri, 6 Apr 2018 15:56:18 -0700 Subject: [PATCH 06/28] Remove unuseful error --- src/compiler/diagnosticMessages.json | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/src/compiler/diagnosticMessages.json b/src/compiler/diagnosticMessages.json index 50fa752a737f6..0f79c87da8c6f 100644 --- a/src/compiler/diagnosticMessages.json +++ b/src/compiler/diagnosticMessages.json @@ -3501,10 +3501,7 @@ "category": "Message", "code": 6300 }, - "Referenced project '{0}' must have 'declaration': true": { - "category": "Error", - "code": 6301 - }, + "Enable project compilation": { "category": "Message", "code": 6302 From d9a354fed98745ef7902d40ff150ab89634d713a Mon Sep 17 00:00:00 2001 From: Ryan Cavanaugh Date: Fri, 6 Apr 2018 15:56:46 -0700 Subject: [PATCH 07/28] Prepend -> UnparsedSource; fix other emitter bugs; bundle_info -> ?? --- src/compiler/emitter.ts | 31 ++++++++++++++++--------------- src/compiler/factory.ts | 10 +++++----- src/compiler/program.ts | 7 ++----- src/compiler/types.ts | 12 ++++++------ 4 files changed, 29 insertions(+), 31 deletions(-) diff --git a/src/compiler/emitter.ts b/src/compiler/emitter.ts index a0ca5d327dde0..bf73d25f6a378 100644 --- a/src/compiler/emitter.ts +++ b/src/compiler/emitter.ts @@ -4,6 +4,7 @@ /// namespace ts { + const infoExtension = ".tsbundleinfo"; const brackets = createBracketsMap(); /*@internal*/ @@ -49,7 +50,7 @@ namespace ts { const sourceMapFilePath = getSourceMapFilePath(jsFilePath, options); const declarationFilePath = (forceDtsPaths || options.declaration) ? removeFileExtension(jsFilePath) + Extension.Dts : undefined; const declarationMapPath = getAreDeclarationMapsEnabled(options) ? declarationFilePath + ".map" : undefined; - const bundleInfoPath = options.references && jsFilePath && (removeFileExtension(jsFilePath) + ".bundle_info"); + const bundleInfoPath = options.references && jsFilePath && (removeFileExtension(jsFilePath) + infoExtension); return { jsFilePath, sourceMapFilePath, declarationFilePath, declarationMapPath, bundleInfoPath }; } else { @@ -400,14 +401,14 @@ namespace ts { case EmitHint.Expression: Debug.assert(isExpression(node), "Expected an Expression node."); break; - case EmitHint.Prepend: - Debug.assert(node.kind === SyntaxKind.Prepend, "Expected an Prepend node."); + case EmitHint.UnparsedSource: + Debug.assert(node.kind === SyntaxKind.UnparsedSource, "Expected an UnparsedSource node."); break; } switch (node.kind) { case SyntaxKind.SourceFile: return printFile(node); case SyntaxKind.Bundle: return printBundle(node); - case SyntaxKind.Prepend: return printPrepend(node); + case SyntaxKind.UnparsedSource: return printUnparsedSource(node); } writeNode(hint, node, sourceFile, beginPrint()); return endPrint(); @@ -428,8 +429,8 @@ namespace ts { return endPrint(); } - function printPrepend(prepend: PrependNode): string { - writePrepend(prepend, beginPrint()); + function printUnparsedSource(unparsed: UnparsedSource): string { + writeUnparsedSource(unparsed, beginPrint()); return endPrint(); } @@ -463,7 +464,7 @@ namespace ts { emitShebangIfNeeded(bundle); emitPrologueDirectivesIfNeeded(bundle); for (const prepend of bundle.prepends) { - print(EmitHint.Prepend, prepend, /*sourceFile*/ undefined); + print(EmitHint.UnparsedSource, prepend, /*sourceFile*/ undefined); } if (bundleInfo) { @@ -479,10 +480,10 @@ namespace ts { writer = previousWriter; } - function writePrepend(prepend: PrependNode, output: EmitTextWriter) { + function writeUnparsedSource(unparsed: UnparsedSource, output: EmitTextWriter) { const previousWriter = writer; setWriter(output); - print(EmitHint.Prepend, prepend, /*sourceFile*/ undefined); + print(EmitHint.UnparsedSource, unparsed, /*sourceFile*/ undefined); reset(); writer = previousWriter; } @@ -592,7 +593,7 @@ namespace ts { case EmitHint.IdentifierName: return pipelineEmitIdentifierName(node); case EmitHint.Expression: return pipelineEmitExpression(node); case EmitHint.MappedTypeParameter: return emitMappedTypeParameter(cast(node, isTypeParameterDeclaration)); - case EmitHint.Prepend: + case EmitHint.UnparsedSource: case EmitHint.Unspecified: return pipelineEmitUnspecified(node); default: assertTypeIsNever(hint); @@ -634,8 +635,8 @@ namespace ts { case SyntaxKind.TemplateMiddle: case SyntaxKind.TemplateTail: return emitLiteral(node); - case SyntaxKind.Prepend: - return emitPrepend(node); + case SyntaxKind.UnparsedSource: + return emitUnparsedSource(node); // Identifiers case SyntaxKind.Identifier: @@ -1030,9 +1031,9 @@ namespace ts { } } - // SyntaxKind.Prepend - function emitPrepend(prepend: PrependNode) { - write(prepend.javascriptText); + // SyntaxKind.UnparsedSource + function emitUnparsedSource(unparsed: UnparsedSource) { + write(unparsed.javascriptText); } // diff --git a/src/compiler/factory.ts b/src/compiler/factory.ts index f62d37c81a295..7ec0a9cbc22a2 100644 --- a/src/compiler/factory.ts +++ b/src/compiler/factory.ts @@ -2531,22 +2531,22 @@ namespace ts { : node; } - export function createBundle(sourceFiles: ReadonlyArray, prepends: ReadonlyArray = emptyArray) { + export function createBundle(sourceFiles: ReadonlyArray, prepends: ReadonlyArray = emptyArray) { const node = createNode(SyntaxKind.Bundle); node.prepends = prepends; node.sourceFiles = sourceFiles; return node; } - export function createPrepend(javascript: string, declaration: string): PrependNode { - const node = createNode(SyntaxKind.Prepend); + export function createPrepend(javascript: string, declaration: string): UnparsedSource { + const node = createNode(SyntaxKind.UnparsedSource); node.javascriptText = javascript; node.declarationText = declaration; return node; } - export function updateBundle(node: Bundle, sourceFiles: ReadonlyArray, prepends: ReadonlyArray = emptyArray) { - if (node.sourceFiles !== sourceFiles) { + export function updateBundle(node: Bundle, sourceFiles: ReadonlyArray, prepends: ReadonlyArray = emptyArray) { + if (node.sourceFiles !== sourceFiles || node.prepends !== prepends) { return createBundle(sourceFiles, prepends); } return node; diff --git a/src/compiler/program.ts b/src/compiler/program.ts index e9bdd36252039..5b941b66fbde7 100755 --- a/src/compiler/program.ts +++ b/src/compiler/program.ts @@ -1145,12 +1145,12 @@ namespace ts { }; } - function getPrependNodes(): PrependNode[] { + function getPrependNodes(): UnparsedSource[] { if (!projectReferences) { return emptyArray; } - const nodes: PrependNode[] = []; + const nodes: UnparsedSource[] = []; for (let i = 0; i < projectReferences.length; i++) { const ref = projectReferences[i]; const resolvedRef = resolvedProjectReferences[i]; @@ -2204,9 +2204,6 @@ namespace ts { if (!resolvedRef.options.composite) { createDiagnosticForReference(i, Diagnostics.Referenced_project_0_must_have_setting_composite_Colon_true, ref.path); } - if (resolvedRef.options.declaration === false) { - createDiagnosticForReference(i, Diagnostics.Referenced_project_0_must_have_declaration_Colon_true, ref.path); - } } } diff --git a/src/compiler/types.ts b/src/compiler/types.ts index f8fcbee926c86..53d15ef2e5c2f 100644 --- a/src/compiler/types.ts +++ b/src/compiler/types.ts @@ -396,7 +396,7 @@ namespace ts { // Top-level nodes SourceFile, Bundle, - Prepend, + UnparsedSource, // JSDoc nodes JSDocTypeExpression, @@ -2594,14 +2594,14 @@ namespace ts { export interface Bundle extends Node { kind: SyntaxKind.Bundle; - prepends: ReadonlyArray; + prepends: ReadonlyArray; sourceFiles: ReadonlyArray; /* @internal */ syntheticFileReferences?: ReadonlyArray; /* @internal */ syntheticTypeReferences?: ReadonlyArray; } - export interface PrependNode extends Node { - kind: SyntaxKind.Prepend; + export interface UnparsedSource extends Node { + kind: SyntaxKind.UnparsedSource; javascriptText: string; declarationText: string; } @@ -4845,7 +4845,7 @@ namespace ts { Expression, // Emitting an Expression IdentifierName, // Emitting an IdentifierName MappedTypeParameter, // Emitting a TypeParameterDeclaration inside of a MappedTypeNode - Prepend, // Emitting a literal node from a prior emit output of a referenced project + UnparsedSource, // Emitting a literal node from a prior emit output of a referenced project Unspecified, // Emitting an otherwise unspecified node } @@ -4862,7 +4862,7 @@ namespace ts { isEmitBlocked(emitFileName: string): boolean; - getPrependNodes(): ReadonlyArray; + getPrependNodes(): ReadonlyArray; writeFile: WriteFileCallback; } From cdaf625fbbab76be4f2851c80830fcc4c36ddaa1 Mon Sep 17 00:00:00 2001 From: Ryan Cavanaugh Date: Fri, 6 Apr 2018 17:00:55 -0700 Subject: [PATCH 08/28] Proj reference PR cleanup WIP --- src/compiler/diagnosticMessages.json | 8 +++ src/compiler/program.ts | 52 +++++++++++------ src/compiler/types.ts | 2 +- src/harness/unittests/projectReferences.ts | 56 ++++++++++++++++++- .../reference/api/tsserverlibrary.d.ts | 18 +++--- tests/baselines/reference/api/typescript.d.ts | 18 +++--- ...LineContextDiagnosticWithPretty.errors.txt | 3 +- .../prettyContextNotDebugAssertion.errors.txt | 3 +- .../amd/FolderA/FolderB/fileB.d.ts | 4 ++ .../amd/FolderA/FolderB/fileB.js | 6 ++ .../amd/outdir/simple/fileC.d.ts | 2 + .../amd/outdir/simple/fileC.js | 5 ++ .../amd/rootDirectoryErrors.json | 8 +-- .../node/FolderA/FolderB/fileB.d.ts | 4 ++ .../node/FolderA/FolderB/fileB.js | 6 ++ .../node/outdir/simple/fileC.d.ts | 2 + .../node/outdir/simple/fileC.js | 5 ++ .../node/rootDirectoryErrors.json | 8 +-- 18 files changed, 160 insertions(+), 50 deletions(-) create mode 100644 tests/baselines/reference/project/rootDirectoryErrors/amd/FolderA/FolderB/fileB.d.ts create mode 100644 tests/baselines/reference/project/rootDirectoryErrors/amd/FolderA/FolderB/fileB.js create mode 100644 tests/baselines/reference/project/rootDirectoryErrors/amd/outdir/simple/fileC.d.ts create mode 100644 tests/baselines/reference/project/rootDirectoryErrors/amd/outdir/simple/fileC.js create mode 100644 tests/baselines/reference/project/rootDirectoryErrors/node/FolderA/FolderB/fileB.d.ts create mode 100644 tests/baselines/reference/project/rootDirectoryErrors/node/FolderA/FolderB/fileB.js create mode 100644 tests/baselines/reference/project/rootDirectoryErrors/node/outdir/simple/fileC.d.ts create mode 100644 tests/baselines/reference/project/rootDirectoryErrors/node/outdir/simple/fileC.js diff --git a/src/compiler/diagnosticMessages.json b/src/compiler/diagnosticMessages.json index 0f79c87da8c6f..5a1a0bff88a23 100644 --- a/src/compiler/diagnosticMessages.json +++ b/src/compiler/diagnosticMessages.json @@ -3526,6 +3526,14 @@ "category": "Error", "code": 6307 }, + "Cannot prepend project '{0}' because it does not have 'outFile' set": { + "category": "Error", + "code": 6308 + }, + "Output file '{0}' from project '{1}' does not exist": { + "category": "Error", + "code": 6309 + }, "Variable '{0}' implicitly has an '{1}' type.": { "category": "Error", diff --git a/src/compiler/program.ts b/src/compiler/program.ts index 5b941b66fbde7..04452e7f41059 100755 --- a/src/compiler/program.ts +++ b/src/compiler/program.ts @@ -330,13 +330,15 @@ namespace ts { context += resetEscapeSequence; } - output += host.getNewLine(); - output += `${relativeFileName}(${firstLine + 1},${firstLineChar + 1}): `; + output += formatColorAndReset(relativeFileName, ForegroundColorEscapeSequences.Cyan); + output += ":"; + output += formatColorAndReset(`${firstLine + 1}`, ForegroundColorEscapeSequences.Yellow); + output += ":"; + output += formatColorAndReset(`${firstLineChar + 1}`, ForegroundColorEscapeSequences.Yellow); + output += " - "; } - const categoryColor = getCategoryFormat(diagnostic.category); - const category = DiagnosticCategory[diagnostic.category].toLowerCase(); - output += formatColorAndReset(category, categoryColor); + output += formatColorAndReset(diagnosticCategoryName(diagnostic), getCategoryFormat(diagnostic.category)); output += formatColorAndReset(` TS${diagnostic.code}: `, ForegroundColorEscapeSequences.Grey); output += flattenDiagnosticMessageText(diagnostic.messageText, host.getNewLine()); @@ -608,10 +610,12 @@ namespace ts { for (const ref of projectReferences) { const parsedRef = parseProjectReferenceConfigFile(ref); resolvedProjectReferences.push(parsedRef); - if (parsedRef.options.outFile) { - processSourceFile(parsedRef.options.outFile, /*isDefaultLib*/ false, /*packageId*/ undefined); + if (parsedRef) { + if (parsedRef.options.outFile) { + processSourceFile(parsedRef.options.outFile, /*isDefaultLib*/ false, /*packageId*/ undefined); + } + addProjectReferenceRedirects(parsedRef, projectReferenceRedirects); } - addProjectReferenceRedirects(parsedRef, projectReferenceRedirects); } } @@ -730,17 +734,23 @@ namespace ts { function getCommonSourceDirectory() { if (commonSourceDirectory === undefined) { const emittedFiles = filter(files, file => sourceFileMayBeEmitted(file, options, isSourceFileFromExternalLibrary)); - if (options.composite) { + if (options.rootDir) { + // If a rootDir is specified use it as the commonSourceDirectory + commonSourceDirectory = getNormalizedAbsolutePath(options.rootDir, currentDirectory); + } + else if (options.composite) { // Project compilations never infer their root from the input source paths - commonSourceDirectory = getNormalizedAbsolutePath(options.rootDir || getDirectoryPath(normalizeSlashes(options.configFilePath)), currentDirectory); + Debug.assert(!!options.configFilePath); + commonSourceDirectory = getDirectoryPath(normalizeSlashes(options.configFilePath)); } - else if (options.rootDir && checkSourceFilesBelongToPath(emittedFiles, options.rootDir)) { - // If a rootDir is specified and is valid use it as the commonSourceDirectory - commonSourceDirectory = getNormalizedAbsolutePath(options.rootDir, currentDirectory); + + if (commonSourceDirectory) { + checkSourceFilesBelongToPath(emittedFiles, commonSourceDirectory); } else { commonSourceDirectory = computeCommonSourceDirectory(emittedFiles); } + if (commonSourceDirectory && commonSourceDirectory[commonSourceDirectory.length - 1] !== directorySeparator) { // Make sure directory path ends with directory separator so this string can directly // used to replace with "" to get the relative path of the source file and the relative path doesn't @@ -2204,6 +2214,16 @@ namespace ts { if (!resolvedRef.options.composite) { createDiagnosticForReference(i, Diagnostics.Referenced_project_0_must_have_setting_composite_Colon_true, ref.path); } + if (ref.prepend) { + if (resolvedRef.options.outFile) { + if (!host.fileExists(resolvedRef.options.outFile)) { + createDiagnosticForReference(i, Diagnostics.Output_file_0_from_project_1_does_not_exist, resolvedRef.options.outFile, ref.path); + } + } + else { + createDiagnosticForReference(i, Diagnostics.Cannot_prepend_project_0_because_it_does_not_have_outFile_set, ref.path); + } + } } } @@ -2456,14 +2476,14 @@ namespace ts { createDiagnosticForOption(/*onKey*/ false, option1, /*option2*/ undefined, message, arg0); } - function createDiagnosticForReference(index: number, message: DiagnosticMessage, arg0?: string | number) { + function createDiagnosticForReference(index: number, message: DiagnosticMessage, arg0?: string | number, arg1?: string | number) { const referencesSyntax = getProjectReferencesSyntax(); if (referencesSyntax) { - if (createOptionDiagnosticInArrayLiteralSyntax(referencesSyntax, index, message, arg0)) { + if (createOptionDiagnosticInArrayLiteralSyntax(referencesSyntax, index, message, arg0, arg1)) { return; } } - programDiagnostics.add(createCompilerDiagnostic(message, arg0)); + programDiagnostics.add(createCompilerDiagnostic(message, arg0, arg1)); } function createDiagnosticForOption(onKey: boolean, option1: string, option2: string, message: DiagnosticMessage, arg0: string | number, arg1?: string | number, arg2?: string | number) { diff --git a/src/compiler/types.ts b/src/compiler/types.ts index 53d15ef2e5c2f..448ff3b976227 100644 --- a/src/compiler/types.ts +++ b/src/compiler/types.ts @@ -4111,7 +4111,7 @@ namespace ts { /** A normalized path on disk */ path: string; /** The path as the user originally wrote it */ - originalPath: string; + originalPath?: string; /** True if the output of this reference should be prepended to the output of this project. Only valid for --outFile compilations */ prepend?: boolean; /** True if it is intended that this reference form a circularity */ diff --git a/src/harness/unittests/projectReferences.ts b/src/harness/unittests/projectReferences.ts index 9008ad2cb5e6c..cdc4ef62b41c6 100644 --- a/src/harness/unittests/projectReferences.ts +++ b/src/harness/unittests/projectReferences.ts @@ -4,7 +4,7 @@ namespace ts { interface TestProjectSpecification { configFileName?: string; - references?: string[]; + references?: ReadonlyArray; files: { [fileName: string]: string }; outputFiles?: { [fileName: string]: string }; config?: object; @@ -56,7 +56,12 @@ namespace ts { outDir: "bin", ...sp.options }, - references: (sp.references || []).map(r => ({ path: r })), + references: (sp.references || []).map(r => { + if (typeof r === "string") { + return { path: r }; + } + return r; + }), ...sp.config }; const configContent = JSON.stringify(options); @@ -143,7 +148,6 @@ namespace ts { references: ["../primary"] } }; - debugger; testProjectReferences(spec, "/reference/tsconfig.json", program => { const errs = program.getOptionsDiagnostics(); assertHasError("Reports an error about 'composite' not being set", errs, Diagnostics.Referenced_project_0_must_have_setting_composite_Colon_true); @@ -168,6 +172,52 @@ namespace ts { assertHasError("Reports an error about b.ts not being in the list", errs, Diagnostics.File_0_is_not_in_project_file_list_Projects_must_list_all_files_or_use_an_include_pattern); }); }); + + it("errors when the referenced project doesn't exist", () => { + const spec: TestSpecification = { + "/primary": { + files: { "/primary/a.ts": emptyModule }, + references: ["../foo"] + } + }; + testProjectReferences(spec, "/primary/tsconfig.json", program => { + const errs = program.getOptionsDiagnostics(); + assertHasError("Reports an error about a missing file", errs, Diagnostics.File_0_does_not_exist); + }); + }); + + it("errors when a prepended project reference doesn't set outFile", () => { + const spec: TestSpecification = { + "/primary": { + files: { "/primary/a.ts": emptyModule }, + references: [{ path: "../someProj", prepend: true }] + }, + "/someProj": { + files: { "/someProj/b.ts": "const x = 100;" } + } + }; + testProjectReferences(spec, "/primary/tsconfig.json", program => { + const errs = program.getOptionsDiagnostics(); + assertHasError("Reports an error about outFile not being set", errs, Diagnostics.Cannot_prepend_project_0_because_it_does_not_have_outFile_set); + }); + }); + + it("errors when a prepended project reference output doesn't exist", () => { + const spec: TestSpecification = { + "/primary": { + files: { "/primary/a.ts": "const y = x;" }, + references: [{ path: "../someProj", prepend: true }] + }, + "/someProj": { + files: { "/someProj/b.ts": "const x = 100;" }, + options: { outFile: "foo.js" } + } + }; + testProjectReferences(spec, "/primary/tsconfig.json", program => { + const errs = program.getOptionsDiagnostics(); + assertHasError("Reports an error about outFile being missing", errs, Diagnostics.Output_file_0_from_project_1_does_not_exist); + }); + }); }); /** diff --git a/tests/baselines/reference/api/tsserverlibrary.d.ts b/tests/baselines/reference/api/tsserverlibrary.d.ts index 3429b77ca604f..97d82027d13e3 100644 --- a/tests/baselines/reference/api/tsserverlibrary.d.ts +++ b/tests/baselines/reference/api/tsserverlibrary.d.ts @@ -336,7 +336,7 @@ declare namespace ts { EnumMember = 271, SourceFile = 272, Bundle = 273, - Prepend = 274, + UnparsedSource = 274, JSDocTypeExpression = 275, JSDocAllType = 276, JSDocUnknownType = 277, @@ -1635,11 +1635,11 @@ declare namespace ts { } interface Bundle extends Node { kind: SyntaxKind.Bundle; - prepends: ReadonlyArray; + prepends: ReadonlyArray; sourceFiles: ReadonlyArray; } - interface PrependNode extends Node { - kind: SyntaxKind.Prepend; + interface UnparsedSource extends Node { + kind: SyntaxKind.UnparsedSource; javascriptText: string; declarationText: string; } @@ -2308,7 +2308,7 @@ declare namespace ts { /** A normalized path on disk */ path: string; /** The path as the user originally wrote it */ - originalPath: string; + originalPath?: string; /** True if the output of this reference should be prepended to the output of this project. Only valid for --outFile compilations */ prepend?: boolean; /** True if it is intended that this reference form a circularity */ @@ -2620,7 +2620,7 @@ declare namespace ts { Expression = 1, IdentifierName = 2, MappedTypeParameter = 3, - Prepend = 4, + UnparsedSource = 4, Unspecified = 5 } interface TransformationContext { @@ -3787,9 +3787,9 @@ declare namespace ts { function updatePartiallyEmittedExpression(node: PartiallyEmittedExpression, expression: Expression): PartiallyEmittedExpression; function createCommaList(elements: ReadonlyArray): CommaListExpression; function updateCommaList(node: CommaListExpression, elements: ReadonlyArray): CommaListExpression; - function createBundle(sourceFiles: ReadonlyArray, prepends?: ReadonlyArray): Bundle; - function createPrepend(javascript: string, declaration: string): PrependNode; - function updateBundle(node: Bundle, sourceFiles: ReadonlyArray, prepends?: ReadonlyArray): Bundle; + function createBundle(sourceFiles: ReadonlyArray, prepends?: ReadonlyArray): Bundle; + function createPrepend(javascript: string, declaration: string): UnparsedSource; + function updateBundle(node: Bundle, sourceFiles: ReadonlyArray, prepends?: ReadonlyArray): Bundle; function createImmediatelyInvokedFunctionExpression(statements: Statement[]): CallExpression; function createImmediatelyInvokedFunctionExpression(statements: Statement[], param: ParameterDeclaration, paramValue: Expression): CallExpression; function createImmediatelyInvokedArrowFunction(statements: Statement[]): CallExpression; diff --git a/tests/baselines/reference/api/typescript.d.ts b/tests/baselines/reference/api/typescript.d.ts index c2c80b2add74b..01965a63323a0 100644 --- a/tests/baselines/reference/api/typescript.d.ts +++ b/tests/baselines/reference/api/typescript.d.ts @@ -336,7 +336,7 @@ declare namespace ts { EnumMember = 271, SourceFile = 272, Bundle = 273, - Prepend = 274, + UnparsedSource = 274, JSDocTypeExpression = 275, JSDocAllType = 276, JSDocUnknownType = 277, @@ -1635,11 +1635,11 @@ declare namespace ts { } interface Bundle extends Node { kind: SyntaxKind.Bundle; - prepends: ReadonlyArray; + prepends: ReadonlyArray; sourceFiles: ReadonlyArray; } - interface PrependNode extends Node { - kind: SyntaxKind.Prepend; + interface UnparsedSource extends Node { + kind: SyntaxKind.UnparsedSource; javascriptText: string; declarationText: string; } @@ -2308,7 +2308,7 @@ declare namespace ts { /** A normalized path on disk */ path: string; /** The path as the user originally wrote it */ - originalPath: string; + originalPath?: string; /** True if the output of this reference should be prepended to the output of this project. Only valid for --outFile compilations */ prepend?: boolean; /** True if it is intended that this reference form a circularity */ @@ -2620,7 +2620,7 @@ declare namespace ts { Expression = 1, IdentifierName = 2, MappedTypeParameter = 3, - Prepend = 4, + UnparsedSource = 4, Unspecified = 5 } interface TransformationContext { @@ -3714,9 +3714,9 @@ declare namespace ts { function updatePartiallyEmittedExpression(node: PartiallyEmittedExpression, expression: Expression): PartiallyEmittedExpression; function createCommaList(elements: ReadonlyArray): CommaListExpression; function updateCommaList(node: CommaListExpression, elements: ReadonlyArray): CommaListExpression; - function createBundle(sourceFiles: ReadonlyArray, prepends?: ReadonlyArray): Bundle; - function createPrepend(javascript: string, declaration: string): PrependNode; - function updateBundle(node: Bundle, sourceFiles: ReadonlyArray, prepends?: ReadonlyArray): Bundle; + function createBundle(sourceFiles: ReadonlyArray, prepends?: ReadonlyArray): Bundle; + function createPrepend(javascript: string, declaration: string): UnparsedSource; + function updateBundle(node: Bundle, sourceFiles: ReadonlyArray, prepends?: ReadonlyArray): Bundle; function createImmediatelyInvokedFunctionExpression(statements: Statement[]): CallExpression; function createImmediatelyInvokedFunctionExpression(statements: Statement[], param: ParameterDeclaration, paramValue: Expression): CallExpression; function createImmediatelyInvokedArrowFunction(statements: Statement[]): CallExpression; diff --git a/tests/baselines/reference/multiLineContextDiagnosticWithPretty.errors.txt b/tests/baselines/reference/multiLineContextDiagnosticWithPretty.errors.txt index b0358fa52c646..b0cea3122bebe 100644 --- a/tests/baselines/reference/multiLineContextDiagnosticWithPretty.errors.txt +++ b/tests/baselines/reference/multiLineContextDiagnosticWithPretty.errors.txt @@ -1,5 +1,4 @@ - -tests/cases/compiler/multiLineContextDiagnosticWithPretty.ts(2,5): error TS2322: Type '{ a: { b: string; }; }' is not assignable to type '{ c: string; }'. +tests/cases/compiler/multiLineContextDiagnosticWithPretty.ts:2:5 - error TS2322: Type '{ a: { b: string; }; }' is not assignable to type '{ c: string; }'. Object literal may only specify known properties, and 'a' does not exist in type '{ c: string; }'. 2 a: { diff --git a/tests/baselines/reference/prettyContextNotDebugAssertion.errors.txt b/tests/baselines/reference/prettyContextNotDebugAssertion.errors.txt index 901f11eec90f3..3c94d3bd0b8f3 100644 --- a/tests/baselines/reference/prettyContextNotDebugAssertion.errors.txt +++ b/tests/baselines/reference/prettyContextNotDebugAssertion.errors.txt @@ -1,5 +1,4 @@ - -tests/cases/compiler/index.ts(2,1): error TS1005: '}' expected. +tests/cases/compiler/index.ts:2:1 - error TS1005: '}' expected. 2    diff --git a/tests/baselines/reference/project/rootDirectoryErrors/amd/FolderA/FolderB/fileB.d.ts b/tests/baselines/reference/project/rootDirectoryErrors/amd/FolderA/FolderB/fileB.d.ts new file mode 100644 index 0000000000000..289bf1291191b --- /dev/null +++ b/tests/baselines/reference/project/rootDirectoryErrors/amd/FolderA/FolderB/fileB.d.ts @@ -0,0 +1,4 @@ +/// +declare class B { + c: C; +} diff --git a/tests/baselines/reference/project/rootDirectoryErrors/amd/FolderA/FolderB/fileB.js b/tests/baselines/reference/project/rootDirectoryErrors/amd/FolderA/FolderB/fileB.js new file mode 100644 index 0000000000000..e3580f23910ef --- /dev/null +++ b/tests/baselines/reference/project/rootDirectoryErrors/amd/FolderA/FolderB/fileB.js @@ -0,0 +1,6 @@ +/// +var B = /** @class */ (function () { + function B() { + } + return B; +}()); diff --git a/tests/baselines/reference/project/rootDirectoryErrors/amd/outdir/simple/fileC.d.ts b/tests/baselines/reference/project/rootDirectoryErrors/amd/outdir/simple/fileC.d.ts new file mode 100644 index 0000000000000..8147620b2111b --- /dev/null +++ b/tests/baselines/reference/project/rootDirectoryErrors/amd/outdir/simple/fileC.d.ts @@ -0,0 +1,2 @@ +declare class C { +} diff --git a/tests/baselines/reference/project/rootDirectoryErrors/amd/outdir/simple/fileC.js b/tests/baselines/reference/project/rootDirectoryErrors/amd/outdir/simple/fileC.js new file mode 100644 index 0000000000000..35b4a697f307e --- /dev/null +++ b/tests/baselines/reference/project/rootDirectoryErrors/amd/outdir/simple/fileC.js @@ -0,0 +1,5 @@ +var C = /** @class */ (function () { + function C() { + } + return C; +}()); diff --git a/tests/baselines/reference/project/rootDirectoryErrors/amd/rootDirectoryErrors.json b/tests/baselines/reference/project/rootDirectoryErrors/amd/rootDirectoryErrors.json index 42f348a4b56d6..bbcde1907eaa0 100644 --- a/tests/baselines/reference/project/rootDirectoryErrors/amd/rootDirectoryErrors.json +++ b/tests/baselines/reference/project/rootDirectoryErrors/amd/rootDirectoryErrors.json @@ -14,9 +14,9 @@ "FolderA/FolderB/fileB.ts" ], "emittedFiles": [ - "outdir/simple/FolderC/fileC.js", - "outdir/simple/FolderC/fileC.d.ts", - "outdir/simple/fileB.js", - "outdir/simple/fileB.d.ts" + "outdir/simple/fileC.js", + "outdir/simple/fileC.d.ts", + "FolderA/FolderB/fileB.js", + "FolderA/FolderB/fileB.d.ts" ] } \ No newline at end of file diff --git a/tests/baselines/reference/project/rootDirectoryErrors/node/FolderA/FolderB/fileB.d.ts b/tests/baselines/reference/project/rootDirectoryErrors/node/FolderA/FolderB/fileB.d.ts new file mode 100644 index 0000000000000..289bf1291191b --- /dev/null +++ b/tests/baselines/reference/project/rootDirectoryErrors/node/FolderA/FolderB/fileB.d.ts @@ -0,0 +1,4 @@ +/// +declare class B { + c: C; +} diff --git a/tests/baselines/reference/project/rootDirectoryErrors/node/FolderA/FolderB/fileB.js b/tests/baselines/reference/project/rootDirectoryErrors/node/FolderA/FolderB/fileB.js new file mode 100644 index 0000000000000..e3580f23910ef --- /dev/null +++ b/tests/baselines/reference/project/rootDirectoryErrors/node/FolderA/FolderB/fileB.js @@ -0,0 +1,6 @@ +/// +var B = /** @class */ (function () { + function B() { + } + return B; +}()); diff --git a/tests/baselines/reference/project/rootDirectoryErrors/node/outdir/simple/fileC.d.ts b/tests/baselines/reference/project/rootDirectoryErrors/node/outdir/simple/fileC.d.ts new file mode 100644 index 0000000000000..8147620b2111b --- /dev/null +++ b/tests/baselines/reference/project/rootDirectoryErrors/node/outdir/simple/fileC.d.ts @@ -0,0 +1,2 @@ +declare class C { +} diff --git a/tests/baselines/reference/project/rootDirectoryErrors/node/outdir/simple/fileC.js b/tests/baselines/reference/project/rootDirectoryErrors/node/outdir/simple/fileC.js new file mode 100644 index 0000000000000..35b4a697f307e --- /dev/null +++ b/tests/baselines/reference/project/rootDirectoryErrors/node/outdir/simple/fileC.js @@ -0,0 +1,5 @@ +var C = /** @class */ (function () { + function C() { + } + return C; +}()); diff --git a/tests/baselines/reference/project/rootDirectoryErrors/node/rootDirectoryErrors.json b/tests/baselines/reference/project/rootDirectoryErrors/node/rootDirectoryErrors.json index 42f348a4b56d6..bbcde1907eaa0 100644 --- a/tests/baselines/reference/project/rootDirectoryErrors/node/rootDirectoryErrors.json +++ b/tests/baselines/reference/project/rootDirectoryErrors/node/rootDirectoryErrors.json @@ -14,9 +14,9 @@ "FolderA/FolderB/fileB.ts" ], "emittedFiles": [ - "outdir/simple/FolderC/fileC.js", - "outdir/simple/FolderC/fileC.d.ts", - "outdir/simple/fileB.js", - "outdir/simple/fileB.d.ts" + "outdir/simple/fileC.js", + "outdir/simple/fileC.d.ts", + "FolderA/FolderB/fileB.js", + "FolderA/FolderB/fileB.d.ts" ] } \ No newline at end of file From a0a3a09ba0c21aa782bcbf128157a5912547238a Mon Sep 17 00:00:00 2001 From: Ryan Cavanaugh Date: Tue, 10 Apr 2018 11:11:14 -0700 Subject: [PATCH 09/28] Add undefined check for missing target project --- src/compiler/program.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/compiler/program.ts b/src/compiler/program.ts index 04452e7f41059..dda6f628dbc05 100755 --- a/src/compiler/program.ts +++ b/src/compiler/program.ts @@ -1164,7 +1164,7 @@ namespace ts { for (let i = 0; i < projectReferences.length; i++) { const ref = projectReferences[i]; const resolvedRef = resolvedProjectReferences[i]; - if (ref.prepend) { + if (ref.prepend && resolvedRef && resolvedRef.options) { const dtsFilename = changeExtension(resolvedRef.options.outFile, ".d.ts"); const js = host.readFile(resolvedRef.options.outFile) || `/* Input file ${resolvedRef.options.outFile} was missing */\r\n`; const dts = host.readFile(dtsFilename) || `/* Input file ${dtsFilename} was missing */\r\n`; From 2e0d200b5478826250b84b2bd74b1823a71354f5 Mon Sep 17 00:00:00 2001 From: Ryan Cavanaugh Date: Tue, 10 Apr 2018 11:35:08 -0700 Subject: [PATCH 10/28] More fixes --- src/compiler/commandLineParser.ts | 1 + src/compiler/emitter.ts | 8 ++--- src/compiler/program.ts | 58 ++++--------------------------- src/compiler/types.ts | 1 - 4 files changed, 10 insertions(+), 58 deletions(-) diff --git a/src/compiler/commandLineParser.ts b/src/compiler/commandLineParser.ts index 986574cc91511..6cd03d1ca888b 100644 --- a/src/compiler/commandLineParser.ts +++ b/src/compiler/commandLineParser.ts @@ -243,6 +243,7 @@ namespace ts { { name: "composite", type: "boolean", + isTSConfigOnly: true, category: Diagnostics.Basic_Options, description: Diagnostics.Enable_project_compilation, }, diff --git a/src/compiler/emitter.ts b/src/compiler/emitter.ts index bf73d25f6a378..b89e6efa5e4cb 100644 --- a/src/compiler/emitter.ts +++ b/src/compiler/emitter.ts @@ -401,9 +401,6 @@ namespace ts { case EmitHint.Expression: Debug.assert(isExpression(node), "Expected an Expression node."); break; - case EmitHint.UnparsedSource: - Debug.assert(node.kind === SyntaxKind.UnparsedSource, "Expected an UnparsedSource node."); - break; } switch (node.kind) { case SyntaxKind.SourceFile: return printFile(node); @@ -464,7 +461,7 @@ namespace ts { emitShebangIfNeeded(bundle); emitPrologueDirectivesIfNeeded(bundle); for (const prepend of bundle.prepends) { - print(EmitHint.UnparsedSource, prepend, /*sourceFile*/ undefined); + print(EmitHint.Unspecified, prepend, /*sourceFile*/ undefined); } if (bundleInfo) { @@ -483,7 +480,7 @@ namespace ts { function writeUnparsedSource(unparsed: UnparsedSource, output: EmitTextWriter) { const previousWriter = writer; setWriter(output); - print(EmitHint.UnparsedSource, unparsed, /*sourceFile*/ undefined); + print(EmitHint.Unspecified, unparsed, /*sourceFile*/ undefined); reset(); writer = previousWriter; } @@ -593,7 +590,6 @@ namespace ts { case EmitHint.IdentifierName: return pipelineEmitIdentifierName(node); case EmitHint.Expression: return pipelineEmitExpression(node); case EmitHint.MappedTypeParameter: return emitMappedTypeParameter(cast(node, isTypeParameterDeclaration)); - case EmitHint.UnparsedSource: case EmitHint.Unspecified: return pipelineEmitUnspecified(node); default: assertTypeIsNever(hint); diff --git a/src/compiler/program.ts b/src/compiler/program.ts index dda6f628dbc05..37768abd43d16 100755 --- a/src/compiler/program.ts +++ b/src/compiler/program.ts @@ -2162,7 +2162,13 @@ namespace ts { function addProjectReferenceRedirects(referencedProject: ParsedCommandLine, target: Map) { const rootDir = normalizePath(referencedProject.options.rootDir || getDirectoryPath(referencedProject.options.configFilePath)); - target.set(rootDir, referencedProject.options.outDir); + target.set(rootDir, getDeclarationOutputDirectory(referencedProject)); + } + + function getDeclarationOutputDirectory(proj: ParsedCommandLine) { + return proj.options.declarationDir || + proj.options.outDir || + getDirectoryPath(proj.options.configFilePath); } function verifyCompilerOptions() { @@ -2594,24 +2600,6 @@ namespace ts { }; } - /* - export function getProjectReferenceFileNames(host: CompilerHost, rootOptions: CompilerOptions): string[] | undefined { - if (rootOptions.projectReferences === undefined) { - return []; - } - - const result: string[] = []; - for (const ref of rootOptions.projectReferences) { - const refPath = resolveProjectReferencePath(host, rootOptions.configFilePath, ref); - if (!refPath || !host.fileExists(refPath)) { - return undefined; - } - result.push(refPath); - } - return result; - } - */ - /** * Returns the target config filename of a project reference */ @@ -2622,38 +2610,6 @@ namespace ts { return ref.path; } - /* - export function walkProjectReferenceGraph(host: CompilerHost, rootOptions: CompilerOptions, - callback: (resolvedFile: string, referencedProject: CompilerOptions, settings: ProjectReference) => void, - errorCallback?: (failedLocation: string) => void) { - if (rootOptions.references === undefined) return; - - const references = getProjectReferenceFileNames(host, rootOptions); - if (references === undefined) { - return; - } - - const configHost = parseConfigHostFromCompilerHost(host); - for (const ref of rootOptions.references) { - const refPath = resolveProjectReferencePath(host, rootOptions.configFilePath, ref); - const text = host.readFile(refPath); - if (!text) { - // Failed to read a referenced tsconfig file - if (errorCallback) { - errorCallback(refPath); - } - continue; - } - const referenceJsonSource = parseJsonText(refPath, text); - const cmdLine = parseJsonSourceFileConfigFileContent(referenceJsonSource, configHost, getDirectoryPath(refPath), undefined, refPath); - cmdLine.options.configFilePath = refPath; - if (cmdLine.options) { - callback(refPath, cmdLine.options, ref); - } - } - } - */ - /* @internal */ /** * Returns a DiagnosticMessage if we won't include a resolved module due to its extension. diff --git a/src/compiler/types.ts b/src/compiler/types.ts index 448ff3b976227..c9c9810340b0f 100644 --- a/src/compiler/types.ts +++ b/src/compiler/types.ts @@ -4845,7 +4845,6 @@ namespace ts { Expression, // Emitting an Expression IdentifierName, // Emitting an IdentifierName MappedTypeParameter, // Emitting a TypeParameterDeclaration inside of a MappedTypeNode - UnparsedSource, // Emitting a literal node from a prior emit output of a referenced project Unspecified, // Emitting an otherwise unspecified node } From ce1082e18e78bfc0f21556ac49f5a4712f08176b Mon Sep 17 00:00:00 2001 From: Ryan Cavanaugh Date: Tue, 10 Apr 2018 13:08:24 -0700 Subject: [PATCH 11/28] Merge updates --- src/compiler/emitter.ts | 8 - .../reference/api/tsserverlibrary.d.ts | 341 ++++++++++-------- tests/baselines/reference/api/typescript.d.ts | 330 +++++++++-------- .../tsconfig.json | 1 + 4 files changed, 372 insertions(+), 308 deletions(-) diff --git a/src/compiler/emitter.ts b/src/compiler/emitter.ts index 419c5ee5fc25b..55a453162cd62 100644 --- a/src/compiler/emitter.ts +++ b/src/compiler/emitter.ts @@ -112,9 +112,6 @@ namespace ts { }); let bundleInfo: BundleInfo = createDefaultBundleInfo(); - let currentSourceFile: SourceFile; - let bundledHelpers: Map; - let isOwnFileEmit: boolean; let emitSkipped = false; // Emit each output file @@ -230,8 +227,6 @@ namespace ts { mapRecorder.initialize(jsFilePath, sourceMapFilePath || "", sourceFileOrBundle, sourceMapDataList); if (bundle) { - bundledHelpers = createMap(); - isOwnFileEmit = false; printer.writeBundle(bundle, writer, bundleInfo); } else { @@ -263,9 +258,6 @@ namespace ts { mapRecorder.reset(); writer.clear(); - currentSourceFile = undefined; - bundledHelpers = undefined; - isOwnFileEmit = false; bundleInfo = createDefaultBundleInfo(); } diff --git a/tests/baselines/reference/api/tsserverlibrary.d.ts b/tests/baselines/reference/api/tsserverlibrary.d.ts index 97d82027d13e3..7573e7113bc0a 100644 --- a/tests/baselines/reference/api/tsserverlibrary.d.ts +++ b/tests/baselines/reference/api/tsserverlibrary.d.ts @@ -240,129 +240,130 @@ declare namespace ts { IndexedAccessType = 175, MappedType = 176, LiteralType = 177, - ObjectBindingPattern = 178, - ArrayBindingPattern = 179, - BindingElement = 180, - ArrayLiteralExpression = 181, - ObjectLiteralExpression = 182, - PropertyAccessExpression = 183, - ElementAccessExpression = 184, - CallExpression = 185, - NewExpression = 186, - TaggedTemplateExpression = 187, - TypeAssertionExpression = 188, - ParenthesizedExpression = 189, - FunctionExpression = 190, - ArrowFunction = 191, - DeleteExpression = 192, - TypeOfExpression = 193, - VoidExpression = 194, - AwaitExpression = 195, - PrefixUnaryExpression = 196, - PostfixUnaryExpression = 197, - BinaryExpression = 198, - ConditionalExpression = 199, - TemplateExpression = 200, - YieldExpression = 201, - SpreadElement = 202, - ClassExpression = 203, - OmittedExpression = 204, - ExpressionWithTypeArguments = 205, - AsExpression = 206, - NonNullExpression = 207, - MetaProperty = 208, - TemplateSpan = 209, - SemicolonClassElement = 210, - Block = 211, - VariableStatement = 212, - EmptyStatement = 213, - ExpressionStatement = 214, - IfStatement = 215, - DoStatement = 216, - WhileStatement = 217, - ForStatement = 218, - ForInStatement = 219, - ForOfStatement = 220, - ContinueStatement = 221, - BreakStatement = 222, - ReturnStatement = 223, - WithStatement = 224, - SwitchStatement = 225, - LabeledStatement = 226, - ThrowStatement = 227, - TryStatement = 228, - DebuggerStatement = 229, - VariableDeclaration = 230, - VariableDeclarationList = 231, - FunctionDeclaration = 232, - ClassDeclaration = 233, - InterfaceDeclaration = 234, - TypeAliasDeclaration = 235, - EnumDeclaration = 236, - ModuleDeclaration = 237, - ModuleBlock = 238, - CaseBlock = 239, - NamespaceExportDeclaration = 240, - ImportEqualsDeclaration = 241, - ImportDeclaration = 242, - ImportClause = 243, - NamespaceImport = 244, - NamedImports = 245, - ImportSpecifier = 246, - ExportAssignment = 247, - ExportDeclaration = 248, - NamedExports = 249, - ExportSpecifier = 250, - MissingDeclaration = 251, - ExternalModuleReference = 252, - JsxElement = 253, - JsxSelfClosingElement = 254, - JsxOpeningElement = 255, - JsxClosingElement = 256, - JsxFragment = 257, - JsxOpeningFragment = 258, - JsxClosingFragment = 259, - JsxAttribute = 260, - JsxAttributes = 261, - JsxSpreadAttribute = 262, - JsxExpression = 263, - CaseClause = 264, - DefaultClause = 265, - HeritageClause = 266, - CatchClause = 267, - PropertyAssignment = 268, - ShorthandPropertyAssignment = 269, - SpreadAssignment = 270, - EnumMember = 271, - SourceFile = 272, - Bundle = 273, - UnparsedSource = 274, - JSDocTypeExpression = 275, - JSDocAllType = 276, - JSDocUnknownType = 277, - JSDocNullableType = 278, - JSDocNonNullableType = 279, - JSDocOptionalType = 280, - JSDocFunctionType = 281, - JSDocVariadicType = 282, - JSDocComment = 283, - JSDocTypeLiteral = 284, - JSDocTag = 285, - JSDocAugmentsTag = 286, - JSDocClassTag = 287, - JSDocParameterTag = 288, - JSDocReturnTag = 289, - JSDocTypeTag = 290, - JSDocTemplateTag = 291, - JSDocTypedefTag = 292, - JSDocPropertyTag = 293, - SyntaxList = 294, - NotEmittedStatement = 295, - PartiallyEmittedExpression = 296, - CommaListExpression = 297, - MergeDeclarationMarker = 298, - EndOfDeclarationMarker = 299, - Count = 300, + ImportType = 178, + ObjectBindingPattern = 179, + ArrayBindingPattern = 180, + BindingElement = 181, + ArrayLiteralExpression = 182, + ObjectLiteralExpression = 183, + PropertyAccessExpression = 184, + ElementAccessExpression = 185, + CallExpression = 186, + NewExpression = 187, + TaggedTemplateExpression = 188, + TypeAssertionExpression = 189, + ParenthesizedExpression = 190, + FunctionExpression = 191, + ArrowFunction = 192, + DeleteExpression = 193, + TypeOfExpression = 194, + VoidExpression = 195, + AwaitExpression = 196, + PrefixUnaryExpression = 197, + PostfixUnaryExpression = 198, + BinaryExpression = 199, + ConditionalExpression = 200, + TemplateExpression = 201, + YieldExpression = 202, + SpreadElement = 203, + ClassExpression = 204, + OmittedExpression = 205, + ExpressionWithTypeArguments = 206, + AsExpression = 207, + NonNullExpression = 208, + MetaProperty = 209, + TemplateSpan = 210, + SemicolonClassElement = 211, + Block = 212, + VariableStatement = 213, + EmptyStatement = 214, + ExpressionStatement = 215, + IfStatement = 216, + DoStatement = 217, + WhileStatement = 218, + ForStatement = 219, + ForInStatement = 220, + ForOfStatement = 221, + ContinueStatement = 222, + BreakStatement = 223, + ReturnStatement = 224, + WithStatement = 225, + SwitchStatement = 226, + LabeledStatement = 227, + ThrowStatement = 228, + TryStatement = 229, + DebuggerStatement = 230, + VariableDeclaration = 231, + VariableDeclarationList = 232, + FunctionDeclaration = 233, + ClassDeclaration = 234, + InterfaceDeclaration = 235, + TypeAliasDeclaration = 236, + EnumDeclaration = 237, + ModuleDeclaration = 238, + ModuleBlock = 239, + CaseBlock = 240, + NamespaceExportDeclaration = 241, + ImportEqualsDeclaration = 242, + ImportDeclaration = 243, + ImportClause = 244, + NamespaceImport = 245, + NamedImports = 246, + ImportSpecifier = 247, + ExportAssignment = 248, + ExportDeclaration = 249, + NamedExports = 250, + ExportSpecifier = 251, + MissingDeclaration = 252, + ExternalModuleReference = 253, + JsxElement = 254, + JsxSelfClosingElement = 255, + JsxOpeningElement = 256, + JsxClosingElement = 257, + JsxFragment = 258, + JsxOpeningFragment = 259, + JsxClosingFragment = 260, + JsxAttribute = 261, + JsxAttributes = 262, + JsxSpreadAttribute = 263, + JsxExpression = 264, + CaseClause = 265, + DefaultClause = 266, + HeritageClause = 267, + CatchClause = 268, + PropertyAssignment = 269, + ShorthandPropertyAssignment = 270, + SpreadAssignment = 271, + EnumMember = 272, + SourceFile = 273, + Bundle = 274, + UnparsedSource = 275, + JSDocTypeExpression = 276, + JSDocAllType = 277, + JSDocUnknownType = 278, + JSDocNullableType = 279, + JSDocNonNullableType = 280, + JSDocOptionalType = 281, + JSDocFunctionType = 282, + JSDocVariadicType = 283, + JSDocComment = 284, + JSDocTypeLiteral = 285, + JSDocTag = 286, + JSDocAugmentsTag = 287, + JSDocClassTag = 288, + JSDocParameterTag = 289, + JSDocReturnTag = 290, + JSDocTypeTag = 291, + JSDocTemplateTag = 292, + JSDocTypedefTag = 293, + JSDocPropertyTag = 294, + SyntaxList = 295, + NotEmittedStatement = 296, + PartiallyEmittedExpression = 297, + CommaListExpression = 298, + MergeDeclarationMarker = 299, + EndOfDeclarationMarker = 300, + Count = 301, FirstAssignment = 58, LastAssignment = 70, FirstCompoundAssignment = 59, @@ -374,7 +375,7 @@ declare namespace ts { FirstFutureReservedWord = 108, LastFutureReservedWord = 116, FirstTypeNode = 160, - LastTypeNode = 177, + LastTypeNode = 178, FirstPunctuation = 17, LastPunctuation = 70, FirstToken = 0, @@ -388,10 +389,10 @@ declare namespace ts { FirstBinaryOperator = 27, LastBinaryOperator = 70, FirstNode = 145, - FirstJSDocNode = 275, - LastJSDocNode = 293, - FirstJSDocTagNode = 285, - LastJSDocTagNode = 293 + FirstJSDocNode = 276, + LastJSDocNode = 294, + FirstJSDocTagNode = 286, + LastJSDocTagNode = 294 } enum NodeFlags { None = 0, @@ -698,6 +699,12 @@ declare namespace ts { interface KeywordTypeNode extends TypeNode { kind: SyntaxKind.AnyKeyword | SyntaxKind.NumberKeyword | SyntaxKind.ObjectKeyword | SyntaxKind.BooleanKeyword | SyntaxKind.StringKeyword | SyntaxKind.SymbolKeyword | SyntaxKind.ThisKeyword | SyntaxKind.VoidKeyword | SyntaxKind.UndefinedKeyword | SyntaxKind.NullKeyword | SyntaxKind.NeverKeyword; } + interface ImportTypeNode extends NodeWithTypeArguments { + kind: SyntaxKind.ImportType; + isTypeOf?: boolean; + argument: TypeNode; + qualifier?: EntityName; + } interface ThisTypeNode extends TypeNode { kind: SyntaxKind.ThisType; } @@ -708,11 +715,13 @@ declare namespace ts { interface ConstructorTypeNode extends TypeNode, SignatureDeclarationBase { kind: SyntaxKind.ConstructorType; } + interface NodeWithTypeArguments extends TypeNode { + typeArguments?: NodeArray; + } type TypeReferenceType = TypeReferenceNode | ExpressionWithTypeArguments; - interface TypeReferenceNode extends TypeNode { + interface TypeReferenceNode extends NodeWithTypeArguments { kind: SyntaxKind.TypeReference; typeName: EntityName; - typeArguments?: NodeArray; } interface TypePredicateNode extends TypeNode { kind: SyntaxKind.TypePredicate; @@ -1028,11 +1037,10 @@ declare namespace ts { interface ImportCall extends CallExpression { expression: ImportExpression; } - interface ExpressionWithTypeArguments extends TypeNode { + interface ExpressionWithTypeArguments extends NodeWithTypeArguments { kind: SyntaxKind.ExpressionWithTypeArguments; parent?: HeritageClause; expression: LeftHandSideExpression; - typeArguments?: NodeArray; } interface NewExpression extends PrimaryExpression, Declaration { kind: SyntaxKind.NewExpression; @@ -1782,6 +1790,10 @@ declare namespace ts { getSymbolsInScope(location: Node, meaning: SymbolFlags): Symbol[]; getSymbolAtLocation(node: Node): Symbol | undefined; getSymbolsOfParameterPropertyDeclaration(parameter: ParameterDeclaration, parameterName: string): Symbol[]; + /** + * The function returns the value (local variable) symbol of an identifier in the short-hand property assignment. + * This is necessary as an identifier in short-hand property assignment can contains two meaning: property name and property value. + */ getShorthandAssignmentValueSymbol(location: Node): Symbol | undefined; getExportSpecifierLocalTargetSymbol(location: ExportSpecifier): Symbol | undefined; /** @@ -1820,7 +1832,7 @@ declare namespace ts { isArgumentsSymbol(symbol: Symbol): boolean; isUnknownSymbol(symbol: Symbol): boolean; getConstantValue(node: EnumMember | PropertyAccessExpression | ElementAccessExpression): string | number | undefined; - isValidPropertyAccess(node: PropertyAccessExpression | QualifiedName, propertyName: string): boolean; + isValidPropertyAccess(node: PropertyAccessExpression | QualifiedName | ImportTypeNode, propertyName: string): boolean; /** Follow all aliases to get the original symbol. */ getAliasedSymbol(symbol: Symbol): Symbol; getExportsOfModule(moduleSymbol: Symbol): Symbol[]; @@ -1832,6 +1844,7 @@ declare namespace ts { getApparentType(type: Type): Type; getSuggestionForNonexistentProperty(node: Identifier, containingType: Type): string | undefined; getSuggestionForNonexistentSymbol(location: Node, name: string, meaning: SymbolFlags): string | undefined; + getSuggestionForNonexistentModule(node: Identifier, target: Symbol): string | undefined; getBaseConstraintOfType(type: Type): Type | undefined; getDefaultFromTypeParameter(type: Type): Type | undefined; } @@ -2247,7 +2260,7 @@ declare namespace ts { interface IndexInfo { type: Type; isReadonly: boolean; - declaration?: SignatureDeclaration; + declaration?: IndexSignatureDeclaration; } enum InferencePriority { NakedTypeVariable = 1, @@ -2269,6 +2282,7 @@ declare namespace ts { category: DiagnosticCategory; code: number; message: string; + reportsUnnecessary?: {}; } /** * A linked list of formatted diagnostic messages to be used as part of a multiline message. @@ -2288,6 +2302,8 @@ declare namespace ts { length: number | undefined; messageText: string | DiagnosticMessageChain; category: DiagnosticCategory; + /** May store more in future. For now, this will simply be `true` to indicate when a diagnostic is an unused-identifier diagnostic. */ + reportsUnnecessary?: {}; code: number; source?: string; } @@ -2612,16 +2628,16 @@ declare namespace ts { interface EmitHelper { readonly name: string; readonly scoped: boolean; - readonly text: string; + readonly text: string | ((node: EmitHelperUniqueNameCallback) => string); readonly priority?: number; } + type EmitHelperUniqueNameCallback = (name: string) => string; enum EmitHint { SourceFile = 0, Expression = 1, IdentifierName = 2, MappedTypeParameter = 3, - UnparsedSource = 4, - Unspecified = 5 + Unspecified = 4 } interface TransformationContext { /** Gets the compiler options supplied to the transformer. */ @@ -2787,6 +2803,7 @@ declare namespace ts { removeComments?: boolean; newLine?: NewLineKind; omitTrailingSemicolon?: boolean; + noEmitHelpers?: boolean; } /** @deprecated See comment on SymbolWriter */ interface SymbolTracker { @@ -3258,6 +3275,7 @@ declare namespace ts { function isClassLike(node: Node): node is ClassLikeDeclaration; function isAccessor(node: Node): node is AccessorDeclaration; function isTypeElement(node: Node): node is TypeElement; + function isClassOrTypeElement(node: Node): node is ClassElement | TypeElement; function isObjectLiteralElementLike(node: Node): node is ObjectLiteralElementLike; /** * Node test that determines whether a node is a valid type node. @@ -3499,6 +3517,8 @@ declare namespace ts { function createUniqueName(text: string): Identifier; /** Create a unique name based on the supplied text. */ function createOptimisticUniqueName(text: string): Identifier; + /** Create a unique name based on the supplied text. This does not consider names injected by the transformer. */ + function createFileLevelUniqueName(text: string): Identifier; /** Create a unique name generated for a node. */ function getGeneratedNameForNode(node: Node): Identifier; function createToken(token: TKind): Token; @@ -3533,9 +3553,9 @@ declare namespace ts { function updateGetAccessor(node: GetAccessorDeclaration, decorators: ReadonlyArray | undefined, modifiers: ReadonlyArray | undefined, name: PropertyName, parameters: ReadonlyArray, type: TypeNode | undefined, body: Block | undefined): GetAccessorDeclaration; function createSetAccessor(decorators: ReadonlyArray | undefined, modifiers: ReadonlyArray | undefined, name: string | PropertyName, parameters: ReadonlyArray, body: Block | undefined): SetAccessorDeclaration; function updateSetAccessor(node: SetAccessorDeclaration, decorators: ReadonlyArray | undefined, modifiers: ReadonlyArray | undefined, name: PropertyName, parameters: ReadonlyArray, body: Block | undefined): SetAccessorDeclaration; - function createCallSignature(typeParameters: TypeParameterDeclaration[] | undefined, parameters: ParameterDeclaration[], type: TypeNode | undefined): CallSignatureDeclaration; + function createCallSignature(typeParameters: ReadonlyArray | undefined, parameters: ReadonlyArray, type: TypeNode | undefined): CallSignatureDeclaration; function updateCallSignature(node: CallSignatureDeclaration, typeParameters: NodeArray | undefined, parameters: NodeArray, type: TypeNode | undefined): CallSignatureDeclaration; - function createConstructSignature(typeParameters: TypeParameterDeclaration[] | undefined, parameters: ParameterDeclaration[], type: TypeNode | undefined): ConstructSignatureDeclaration; + function createConstructSignature(typeParameters: ReadonlyArray | undefined, parameters: ReadonlyArray, type: TypeNode | undefined): ConstructSignatureDeclaration; function updateConstructSignature(node: ConstructSignatureDeclaration, typeParameters: NodeArray | undefined, parameters: NodeArray, type: TypeNode | undefined): ConstructSignatureDeclaration; function createIndexSignature(decorators: ReadonlyArray | undefined, modifiers: ReadonlyArray | undefined, parameters: ReadonlyArray, type: TypeNode): IndexSignatureDeclaration; function updateIndexSignature(node: IndexSignatureDeclaration, decorators: ReadonlyArray | undefined, modifiers: ReadonlyArray | undefined, parameters: ReadonlyArray, type: TypeNode): IndexSignatureDeclaration; @@ -3544,9 +3564,9 @@ declare namespace ts { function updateTypePredicateNode(node: TypePredicateNode, parameterName: Identifier | ThisTypeNode, type: TypeNode): TypePredicateNode; function createTypeReferenceNode(typeName: string | EntityName, typeArguments: ReadonlyArray | undefined): TypeReferenceNode; function updateTypeReferenceNode(node: TypeReferenceNode, typeName: EntityName, typeArguments: NodeArray | undefined): TypeReferenceNode; - function createFunctionTypeNode(typeParameters: TypeParameterDeclaration[] | undefined, parameters: ParameterDeclaration[], type: TypeNode | undefined): FunctionTypeNode; + function createFunctionTypeNode(typeParameters: ReadonlyArray | undefined, parameters: ReadonlyArray, type: TypeNode | undefined): FunctionTypeNode; function updateFunctionTypeNode(node: FunctionTypeNode, typeParameters: NodeArray | undefined, parameters: NodeArray, type: TypeNode | undefined): FunctionTypeNode; - function createConstructorTypeNode(typeParameters: TypeParameterDeclaration[] | undefined, parameters: ParameterDeclaration[], type: TypeNode | undefined): ConstructorTypeNode; + function createConstructorTypeNode(typeParameters: ReadonlyArray | undefined, parameters: ReadonlyArray, type: TypeNode | undefined): ConstructorTypeNode; function updateConstructorTypeNode(node: ConstructorTypeNode, typeParameters: NodeArray | undefined, parameters: NodeArray, type: TypeNode | undefined): ConstructorTypeNode; function createTypeQueryNode(exprName: EntityName): TypeQueryNode; function updateTypeQueryNode(node: TypeQueryNode, exprName: EntityName): TypeQueryNode; @@ -3556,15 +3576,17 @@ declare namespace ts { function updateArrayTypeNode(node: ArrayTypeNode, elementType: TypeNode): ArrayTypeNode; function createTupleTypeNode(elementTypes: ReadonlyArray): TupleTypeNode; function updateTypleTypeNode(node: TupleTypeNode, elementTypes: ReadonlyArray): TupleTypeNode; - function createUnionTypeNode(types: TypeNode[]): UnionTypeNode; + function createUnionTypeNode(types: ReadonlyArray): UnionTypeNode; function updateUnionTypeNode(node: UnionTypeNode, types: NodeArray): UnionTypeNode; - function createIntersectionTypeNode(types: TypeNode[]): IntersectionTypeNode; + function createIntersectionTypeNode(types: ReadonlyArray): IntersectionTypeNode; function updateIntersectionTypeNode(node: IntersectionTypeNode, types: NodeArray): IntersectionTypeNode; function createUnionOrIntersectionTypeNode(kind: SyntaxKind.UnionType | SyntaxKind.IntersectionType, types: ReadonlyArray): UnionOrIntersectionTypeNode; function createConditionalTypeNode(checkType: TypeNode, extendsType: TypeNode, trueType: TypeNode, falseType: TypeNode): ConditionalTypeNode; function updateConditionalTypeNode(node: ConditionalTypeNode, checkType: TypeNode, extendsType: TypeNode, trueType: TypeNode, falseType: TypeNode): ConditionalTypeNode; function createInferTypeNode(typeParameter: TypeParameterDeclaration): InferTypeNode; function updateInferTypeNode(node: InferTypeNode, typeParameter: TypeParameterDeclaration): InferTypeNode; + function createImportTypeNode(argument: TypeNode, qualifier?: EntityName, typeArguments?: ReadonlyArray, isTypeOf?: boolean): ImportTypeNode; + function updateImportTypeNode(node: ImportTypeNode, argument: TypeNode, qualifier?: EntityName, typeArguments?: ReadonlyArray, isTypeOf?: boolean): ImportTypeNode; function createParenthesizedType(type: TypeNode): ParenthesizedTypeNode; function updateParenthesizedType(node: ParenthesizedTypeNode, type: TypeNode): ParenthesizedTypeNode; function createThisTypeNode(): ThisTypeNode; @@ -3763,7 +3785,7 @@ declare namespace ts { function updateSpreadAssignment(node: SpreadAssignment, expression: Expression): SpreadAssignment; function createEnumMember(name: string | PropertyName, initializer?: Expression): EnumMember; function updateEnumMember(node: EnumMember, name: PropertyName, initializer: Expression | undefined): EnumMember; - function updateSourceFileNode(node: SourceFile, statements: ReadonlyArray, isDeclarationFile?: boolean, referencedFiles?: SourceFile["referencedFiles"], typeReferences?: SourceFile["typeReferenceDirectives"]): SourceFile; + function updateSourceFileNode(node: SourceFile, statements: ReadonlyArray, isDeclarationFile?: boolean, referencedFiles?: SourceFile["referencedFiles"], typeReferences?: SourceFile["typeReferenceDirectives"], hasNoDefaultLib?: boolean): SourceFile; /** * Creates a shallow, memberwise clone of a node for mutation. */ @@ -3790,10 +3812,10 @@ declare namespace ts { function createBundle(sourceFiles: ReadonlyArray, prepends?: ReadonlyArray): Bundle; function createPrepend(javascript: string, declaration: string): UnparsedSource; function updateBundle(node: Bundle, sourceFiles: ReadonlyArray, prepends?: ReadonlyArray): Bundle; - function createImmediatelyInvokedFunctionExpression(statements: Statement[]): CallExpression; - function createImmediatelyInvokedFunctionExpression(statements: Statement[], param: ParameterDeclaration, paramValue: Expression): CallExpression; - function createImmediatelyInvokedArrowFunction(statements: Statement[]): CallExpression; - function createImmediatelyInvokedArrowFunction(statements: Statement[], param: ParameterDeclaration, paramValue: Expression): CallExpression; + function createImmediatelyInvokedFunctionExpression(statements: ReadonlyArray): CallExpression; + function createImmediatelyInvokedFunctionExpression(statements: ReadonlyArray, param: ParameterDeclaration, paramValue: Expression): CallExpression; + function createImmediatelyInvokedArrowFunction(statements: ReadonlyArray): CallExpression; + function createImmediatelyInvokedArrowFunction(statements: ReadonlyArray, param: ParameterDeclaration, paramValue: Expression): CallExpression; function createComma(left: Expression, right: Expression): Expression; function createLessThan(left: Expression, right: Expression): Expression; function createAssignment(left: ObjectLiteralExpression | ArrayLiteralExpression, right: Expression): DestructuringAssignment; @@ -4043,6 +4065,15 @@ declare namespace ts { getNonNullableType(): Type; getConstraint(): Type | undefined; getDefault(): Type | undefined; + isUnion(): this is UnionType; + isIntersection(): this is IntersectionType; + isUnionOrIntersection(): this is UnionOrIntersectionType; + isLiteral(): this is LiteralType; + isStringLiteral(): this is StringLiteralType; + isNumberLiteral(): this is NumberLiteralType; + isTypeParameter(): this is TypeParameter; + isClassOrInterface(): this is InterfaceType; + isClass(): this is InterfaceType; } interface Signature { getDeclaration(): SignatureDeclaration; @@ -4457,6 +4488,7 @@ declare namespace ts { placeOpenBraceOnNewLineForFunctions?: boolean; placeOpenBraceOnNewLineForControlBlocks?: boolean; insertSpaceBeforeTypeAnnotation?: boolean; + indentMultiLineObjectLiteralBeginningOnBlankLine?: boolean; } interface DefinitionInfo { fileName: string; @@ -4893,7 +4925,7 @@ declare namespace ts { function createLanguageServiceSourceFile(fileName: string, scriptSnapshot: IScriptSnapshot, scriptTarget: ScriptTarget, version: string, setNodeParents: boolean, scriptKind?: ScriptKind): SourceFile; let disableIncrementalParsing: boolean; function updateLanguageServiceSourceFile(sourceFile: SourceFile, scriptSnapshot: IScriptSnapshot, version: string, textChangeRange: TextChangeRange, aggressiveChecks?: boolean): SourceFile; - function createLanguageService(host: LanguageServiceHost, documentRegistry?: DocumentRegistry): LanguageService; + function createLanguageService(host: LanguageServiceHost, documentRegistry?: DocumentRegistry, syntaxOnly?: boolean): LanguageService; /** * Get the path of the default library files (lib.d.ts) as distributed with the typescript * node package. @@ -5424,6 +5456,8 @@ declare namespace ts.server.protocol { endLocation: Location; category: string; code: number; + /** May store more in future. For now, this will simply be `true` to indicate when a diagnostic is an unused-identifier diagnostic. */ + reportsUnnecessary?: {}; } /** * Response message for "projectInfo" request @@ -6774,6 +6808,7 @@ declare namespace ts.server.protocol { * The category of the diagnostic message, e.g. "error", "warning", or "suggestion". */ category: string; + reportsUnnecessary?: {}; /** * The error code of the diagnostic message. */ @@ -7322,6 +7357,7 @@ declare namespace ts.server { eventHandler?: ProjectServiceEventHandler; /** Has no effect if eventHandler is also specified. */ suppressDiagnosticEvents?: boolean; + syntaxOnly?: boolean; throttleWaitMilliseconds?: number; globalPlugins?: ReadonlyArray; pluginProbeLocations?: ReadonlyArray; @@ -7354,7 +7390,7 @@ declare namespace ts.server { private doOutput; private semanticCheck; private syntacticCheck; - private infoCheck; + private suggestionCheck; private sendDiagnosticsEvent; /** It is the caller's responsibility to verify that `!this.suppressDiagnosticEvents`. */ private updateErrorCheck; @@ -7891,6 +7927,7 @@ declare namespace ts.server { pluginProbeLocations?: ReadonlyArray; allowLocalPluginLoads?: boolean; typesMapLocation?: string; + syntaxOnly?: boolean; } class ProjectService { readonly typingsCache: TypingsCache; @@ -7950,6 +7987,7 @@ declare namespace ts.server { readonly useSingleInferredProject: boolean; readonly useInferredProjectPerProjectRoot: boolean; readonly typingsInstaller: ITypingsInstaller; + private readonly globalCacheLocationDirectoryPath; readonly throttleWaitMilliseconds?: number; private readonly eventHandler?; private readonly suppressDiagnosticEvents?; @@ -7957,6 +7995,7 @@ declare namespace ts.server { readonly pluginProbeLocations: ReadonlyArray; readonly allowLocalPluginLoads: boolean; readonly typesMapLocation: string | undefined; + readonly syntaxOnly?: boolean; /** Tracks projects that we have already sent telemetry for. */ private readonly seenProjects; constructor(opts: ProjectServiceOptions); @@ -8101,7 +8140,7 @@ declare namespace ts.server { * @param fileContent is a known version of the file content that is more up to date than the one on disk */ openClientFile(fileName: string, fileContent?: string, scriptKind?: ScriptKind, projectRootPath?: string): OpenConfiguredProjectResult; - private findExternalProjetContainingOpenScriptInfo; + private findExternalProjectContainingOpenScriptInfo; openClientFileWithNormalizedPath(fileName: NormalizedPath, fileContent?: string, scriptKind?: ScriptKind, hasMixedContent?: boolean, projectRootPath?: NormalizedPath): OpenConfiguredProjectResult; /** * Close file whose contents is managed by the client diff --git a/tests/baselines/reference/api/typescript.d.ts b/tests/baselines/reference/api/typescript.d.ts index 01965a63323a0..29382d143b7ce 100644 --- a/tests/baselines/reference/api/typescript.d.ts +++ b/tests/baselines/reference/api/typescript.d.ts @@ -240,129 +240,130 @@ declare namespace ts { IndexedAccessType = 175, MappedType = 176, LiteralType = 177, - ObjectBindingPattern = 178, - ArrayBindingPattern = 179, - BindingElement = 180, - ArrayLiteralExpression = 181, - ObjectLiteralExpression = 182, - PropertyAccessExpression = 183, - ElementAccessExpression = 184, - CallExpression = 185, - NewExpression = 186, - TaggedTemplateExpression = 187, - TypeAssertionExpression = 188, - ParenthesizedExpression = 189, - FunctionExpression = 190, - ArrowFunction = 191, - DeleteExpression = 192, - TypeOfExpression = 193, - VoidExpression = 194, - AwaitExpression = 195, - PrefixUnaryExpression = 196, - PostfixUnaryExpression = 197, - BinaryExpression = 198, - ConditionalExpression = 199, - TemplateExpression = 200, - YieldExpression = 201, - SpreadElement = 202, - ClassExpression = 203, - OmittedExpression = 204, - ExpressionWithTypeArguments = 205, - AsExpression = 206, - NonNullExpression = 207, - MetaProperty = 208, - TemplateSpan = 209, - SemicolonClassElement = 210, - Block = 211, - VariableStatement = 212, - EmptyStatement = 213, - ExpressionStatement = 214, - IfStatement = 215, - DoStatement = 216, - WhileStatement = 217, - ForStatement = 218, - ForInStatement = 219, - ForOfStatement = 220, - ContinueStatement = 221, - BreakStatement = 222, - ReturnStatement = 223, - WithStatement = 224, - SwitchStatement = 225, - LabeledStatement = 226, - ThrowStatement = 227, - TryStatement = 228, - DebuggerStatement = 229, - VariableDeclaration = 230, - VariableDeclarationList = 231, - FunctionDeclaration = 232, - ClassDeclaration = 233, - InterfaceDeclaration = 234, - TypeAliasDeclaration = 235, - EnumDeclaration = 236, - ModuleDeclaration = 237, - ModuleBlock = 238, - CaseBlock = 239, - NamespaceExportDeclaration = 240, - ImportEqualsDeclaration = 241, - ImportDeclaration = 242, - ImportClause = 243, - NamespaceImport = 244, - NamedImports = 245, - ImportSpecifier = 246, - ExportAssignment = 247, - ExportDeclaration = 248, - NamedExports = 249, - ExportSpecifier = 250, - MissingDeclaration = 251, - ExternalModuleReference = 252, - JsxElement = 253, - JsxSelfClosingElement = 254, - JsxOpeningElement = 255, - JsxClosingElement = 256, - JsxFragment = 257, - JsxOpeningFragment = 258, - JsxClosingFragment = 259, - JsxAttribute = 260, - JsxAttributes = 261, - JsxSpreadAttribute = 262, - JsxExpression = 263, - CaseClause = 264, - DefaultClause = 265, - HeritageClause = 266, - CatchClause = 267, - PropertyAssignment = 268, - ShorthandPropertyAssignment = 269, - SpreadAssignment = 270, - EnumMember = 271, - SourceFile = 272, - Bundle = 273, - UnparsedSource = 274, - JSDocTypeExpression = 275, - JSDocAllType = 276, - JSDocUnknownType = 277, - JSDocNullableType = 278, - JSDocNonNullableType = 279, - JSDocOptionalType = 280, - JSDocFunctionType = 281, - JSDocVariadicType = 282, - JSDocComment = 283, - JSDocTypeLiteral = 284, - JSDocTag = 285, - JSDocAugmentsTag = 286, - JSDocClassTag = 287, - JSDocParameterTag = 288, - JSDocReturnTag = 289, - JSDocTypeTag = 290, - JSDocTemplateTag = 291, - JSDocTypedefTag = 292, - JSDocPropertyTag = 293, - SyntaxList = 294, - NotEmittedStatement = 295, - PartiallyEmittedExpression = 296, - CommaListExpression = 297, - MergeDeclarationMarker = 298, - EndOfDeclarationMarker = 299, - Count = 300, + ImportType = 178, + ObjectBindingPattern = 179, + ArrayBindingPattern = 180, + BindingElement = 181, + ArrayLiteralExpression = 182, + ObjectLiteralExpression = 183, + PropertyAccessExpression = 184, + ElementAccessExpression = 185, + CallExpression = 186, + NewExpression = 187, + TaggedTemplateExpression = 188, + TypeAssertionExpression = 189, + ParenthesizedExpression = 190, + FunctionExpression = 191, + ArrowFunction = 192, + DeleteExpression = 193, + TypeOfExpression = 194, + VoidExpression = 195, + AwaitExpression = 196, + PrefixUnaryExpression = 197, + PostfixUnaryExpression = 198, + BinaryExpression = 199, + ConditionalExpression = 200, + TemplateExpression = 201, + YieldExpression = 202, + SpreadElement = 203, + ClassExpression = 204, + OmittedExpression = 205, + ExpressionWithTypeArguments = 206, + AsExpression = 207, + NonNullExpression = 208, + MetaProperty = 209, + TemplateSpan = 210, + SemicolonClassElement = 211, + Block = 212, + VariableStatement = 213, + EmptyStatement = 214, + ExpressionStatement = 215, + IfStatement = 216, + DoStatement = 217, + WhileStatement = 218, + ForStatement = 219, + ForInStatement = 220, + ForOfStatement = 221, + ContinueStatement = 222, + BreakStatement = 223, + ReturnStatement = 224, + WithStatement = 225, + SwitchStatement = 226, + LabeledStatement = 227, + ThrowStatement = 228, + TryStatement = 229, + DebuggerStatement = 230, + VariableDeclaration = 231, + VariableDeclarationList = 232, + FunctionDeclaration = 233, + ClassDeclaration = 234, + InterfaceDeclaration = 235, + TypeAliasDeclaration = 236, + EnumDeclaration = 237, + ModuleDeclaration = 238, + ModuleBlock = 239, + CaseBlock = 240, + NamespaceExportDeclaration = 241, + ImportEqualsDeclaration = 242, + ImportDeclaration = 243, + ImportClause = 244, + NamespaceImport = 245, + NamedImports = 246, + ImportSpecifier = 247, + ExportAssignment = 248, + ExportDeclaration = 249, + NamedExports = 250, + ExportSpecifier = 251, + MissingDeclaration = 252, + ExternalModuleReference = 253, + JsxElement = 254, + JsxSelfClosingElement = 255, + JsxOpeningElement = 256, + JsxClosingElement = 257, + JsxFragment = 258, + JsxOpeningFragment = 259, + JsxClosingFragment = 260, + JsxAttribute = 261, + JsxAttributes = 262, + JsxSpreadAttribute = 263, + JsxExpression = 264, + CaseClause = 265, + DefaultClause = 266, + HeritageClause = 267, + CatchClause = 268, + PropertyAssignment = 269, + ShorthandPropertyAssignment = 270, + SpreadAssignment = 271, + EnumMember = 272, + SourceFile = 273, + Bundle = 274, + UnparsedSource = 275, + JSDocTypeExpression = 276, + JSDocAllType = 277, + JSDocUnknownType = 278, + JSDocNullableType = 279, + JSDocNonNullableType = 280, + JSDocOptionalType = 281, + JSDocFunctionType = 282, + JSDocVariadicType = 283, + JSDocComment = 284, + JSDocTypeLiteral = 285, + JSDocTag = 286, + JSDocAugmentsTag = 287, + JSDocClassTag = 288, + JSDocParameterTag = 289, + JSDocReturnTag = 290, + JSDocTypeTag = 291, + JSDocTemplateTag = 292, + JSDocTypedefTag = 293, + JSDocPropertyTag = 294, + SyntaxList = 295, + NotEmittedStatement = 296, + PartiallyEmittedExpression = 297, + CommaListExpression = 298, + MergeDeclarationMarker = 299, + EndOfDeclarationMarker = 300, + Count = 301, FirstAssignment = 58, LastAssignment = 70, FirstCompoundAssignment = 59, @@ -374,7 +375,7 @@ declare namespace ts { FirstFutureReservedWord = 108, LastFutureReservedWord = 116, FirstTypeNode = 160, - LastTypeNode = 177, + LastTypeNode = 178, FirstPunctuation = 17, LastPunctuation = 70, FirstToken = 0, @@ -388,10 +389,10 @@ declare namespace ts { FirstBinaryOperator = 27, LastBinaryOperator = 70, FirstNode = 145, - FirstJSDocNode = 275, - LastJSDocNode = 293, - FirstJSDocTagNode = 285, - LastJSDocTagNode = 293 + FirstJSDocNode = 276, + LastJSDocNode = 294, + FirstJSDocTagNode = 286, + LastJSDocTagNode = 294 } enum NodeFlags { None = 0, @@ -698,6 +699,12 @@ declare namespace ts { interface KeywordTypeNode extends TypeNode { kind: SyntaxKind.AnyKeyword | SyntaxKind.NumberKeyword | SyntaxKind.ObjectKeyword | SyntaxKind.BooleanKeyword | SyntaxKind.StringKeyword | SyntaxKind.SymbolKeyword | SyntaxKind.ThisKeyword | SyntaxKind.VoidKeyword | SyntaxKind.UndefinedKeyword | SyntaxKind.NullKeyword | SyntaxKind.NeverKeyword; } + interface ImportTypeNode extends NodeWithTypeArguments { + kind: SyntaxKind.ImportType; + isTypeOf?: boolean; + argument: TypeNode; + qualifier?: EntityName; + } interface ThisTypeNode extends TypeNode { kind: SyntaxKind.ThisType; } @@ -708,11 +715,13 @@ declare namespace ts { interface ConstructorTypeNode extends TypeNode, SignatureDeclarationBase { kind: SyntaxKind.ConstructorType; } + interface NodeWithTypeArguments extends TypeNode { + typeArguments?: NodeArray; + } type TypeReferenceType = TypeReferenceNode | ExpressionWithTypeArguments; - interface TypeReferenceNode extends TypeNode { + interface TypeReferenceNode extends NodeWithTypeArguments { kind: SyntaxKind.TypeReference; typeName: EntityName; - typeArguments?: NodeArray; } interface TypePredicateNode extends TypeNode { kind: SyntaxKind.TypePredicate; @@ -1028,11 +1037,10 @@ declare namespace ts { interface ImportCall extends CallExpression { expression: ImportExpression; } - interface ExpressionWithTypeArguments extends TypeNode { + interface ExpressionWithTypeArguments extends NodeWithTypeArguments { kind: SyntaxKind.ExpressionWithTypeArguments; parent?: HeritageClause; expression: LeftHandSideExpression; - typeArguments?: NodeArray; } interface NewExpression extends PrimaryExpression, Declaration { kind: SyntaxKind.NewExpression; @@ -1782,6 +1790,10 @@ declare namespace ts { getSymbolsInScope(location: Node, meaning: SymbolFlags): Symbol[]; getSymbolAtLocation(node: Node): Symbol | undefined; getSymbolsOfParameterPropertyDeclaration(parameter: ParameterDeclaration, parameterName: string): Symbol[]; + /** + * The function returns the value (local variable) symbol of an identifier in the short-hand property assignment. + * This is necessary as an identifier in short-hand property assignment can contains two meaning: property name and property value. + */ getShorthandAssignmentValueSymbol(location: Node): Symbol | undefined; getExportSpecifierLocalTargetSymbol(location: ExportSpecifier): Symbol | undefined; /** @@ -1820,7 +1832,7 @@ declare namespace ts { isArgumentsSymbol(symbol: Symbol): boolean; isUnknownSymbol(symbol: Symbol): boolean; getConstantValue(node: EnumMember | PropertyAccessExpression | ElementAccessExpression): string | number | undefined; - isValidPropertyAccess(node: PropertyAccessExpression | QualifiedName, propertyName: string): boolean; + isValidPropertyAccess(node: PropertyAccessExpression | QualifiedName | ImportTypeNode, propertyName: string): boolean; /** Follow all aliases to get the original symbol. */ getAliasedSymbol(symbol: Symbol): Symbol; getExportsOfModule(moduleSymbol: Symbol): Symbol[]; @@ -1832,6 +1844,7 @@ declare namespace ts { getApparentType(type: Type): Type; getSuggestionForNonexistentProperty(node: Identifier, containingType: Type): string | undefined; getSuggestionForNonexistentSymbol(location: Node, name: string, meaning: SymbolFlags): string | undefined; + getSuggestionForNonexistentModule(node: Identifier, target: Symbol): string | undefined; getBaseConstraintOfType(type: Type): Type | undefined; getDefaultFromTypeParameter(type: Type): Type | undefined; } @@ -2247,7 +2260,7 @@ declare namespace ts { interface IndexInfo { type: Type; isReadonly: boolean; - declaration?: SignatureDeclaration; + declaration?: IndexSignatureDeclaration; } enum InferencePriority { NakedTypeVariable = 1, @@ -2269,6 +2282,7 @@ declare namespace ts { category: DiagnosticCategory; code: number; message: string; + reportsUnnecessary?: {}; } /** * A linked list of formatted diagnostic messages to be used as part of a multiline message. @@ -2288,6 +2302,8 @@ declare namespace ts { length: number | undefined; messageText: string | DiagnosticMessageChain; category: DiagnosticCategory; + /** May store more in future. For now, this will simply be `true` to indicate when a diagnostic is an unused-identifier diagnostic. */ + reportsUnnecessary?: {}; code: number; source?: string; } @@ -2612,16 +2628,16 @@ declare namespace ts { interface EmitHelper { readonly name: string; readonly scoped: boolean; - readonly text: string; + readonly text: string | ((node: EmitHelperUniqueNameCallback) => string); readonly priority?: number; } + type EmitHelperUniqueNameCallback = (name: string) => string; enum EmitHint { SourceFile = 0, Expression = 1, IdentifierName = 2, MappedTypeParameter = 3, - UnparsedSource = 4, - Unspecified = 5 + Unspecified = 4 } interface TransformationContext { /** Gets the compiler options supplied to the transformer. */ @@ -2787,6 +2803,7 @@ declare namespace ts { removeComments?: boolean; newLine?: NewLineKind; omitTrailingSemicolon?: boolean; + noEmitHelpers?: boolean; } /** @deprecated See comment on SymbolWriter */ interface SymbolTracker { @@ -3313,6 +3330,7 @@ declare namespace ts { function isClassLike(node: Node): node is ClassLikeDeclaration; function isAccessor(node: Node): node is AccessorDeclaration; function isTypeElement(node: Node): node is TypeElement; + function isClassOrTypeElement(node: Node): node is ClassElement | TypeElement; function isObjectLiteralElementLike(node: Node): node is ObjectLiteralElementLike; /** * Node test that determines whether a node is a valid type node. @@ -3426,6 +3444,8 @@ declare namespace ts { function createUniqueName(text: string): Identifier; /** Create a unique name based on the supplied text. */ function createOptimisticUniqueName(text: string): Identifier; + /** Create a unique name based on the supplied text. This does not consider names injected by the transformer. */ + function createFileLevelUniqueName(text: string): Identifier; /** Create a unique name generated for a node. */ function getGeneratedNameForNode(node: Node): Identifier; function createToken(token: TKind): Token; @@ -3460,9 +3480,9 @@ declare namespace ts { function updateGetAccessor(node: GetAccessorDeclaration, decorators: ReadonlyArray | undefined, modifiers: ReadonlyArray | undefined, name: PropertyName, parameters: ReadonlyArray, type: TypeNode | undefined, body: Block | undefined): GetAccessorDeclaration; function createSetAccessor(decorators: ReadonlyArray | undefined, modifiers: ReadonlyArray | undefined, name: string | PropertyName, parameters: ReadonlyArray, body: Block | undefined): SetAccessorDeclaration; function updateSetAccessor(node: SetAccessorDeclaration, decorators: ReadonlyArray | undefined, modifiers: ReadonlyArray | undefined, name: PropertyName, parameters: ReadonlyArray, body: Block | undefined): SetAccessorDeclaration; - function createCallSignature(typeParameters: TypeParameterDeclaration[] | undefined, parameters: ParameterDeclaration[], type: TypeNode | undefined): CallSignatureDeclaration; + function createCallSignature(typeParameters: ReadonlyArray | undefined, parameters: ReadonlyArray, type: TypeNode | undefined): CallSignatureDeclaration; function updateCallSignature(node: CallSignatureDeclaration, typeParameters: NodeArray | undefined, parameters: NodeArray, type: TypeNode | undefined): CallSignatureDeclaration; - function createConstructSignature(typeParameters: TypeParameterDeclaration[] | undefined, parameters: ParameterDeclaration[], type: TypeNode | undefined): ConstructSignatureDeclaration; + function createConstructSignature(typeParameters: ReadonlyArray | undefined, parameters: ReadonlyArray, type: TypeNode | undefined): ConstructSignatureDeclaration; function updateConstructSignature(node: ConstructSignatureDeclaration, typeParameters: NodeArray | undefined, parameters: NodeArray, type: TypeNode | undefined): ConstructSignatureDeclaration; function createIndexSignature(decorators: ReadonlyArray | undefined, modifiers: ReadonlyArray | undefined, parameters: ReadonlyArray, type: TypeNode): IndexSignatureDeclaration; function updateIndexSignature(node: IndexSignatureDeclaration, decorators: ReadonlyArray | undefined, modifiers: ReadonlyArray | undefined, parameters: ReadonlyArray, type: TypeNode): IndexSignatureDeclaration; @@ -3471,9 +3491,9 @@ declare namespace ts { function updateTypePredicateNode(node: TypePredicateNode, parameterName: Identifier | ThisTypeNode, type: TypeNode): TypePredicateNode; function createTypeReferenceNode(typeName: string | EntityName, typeArguments: ReadonlyArray | undefined): TypeReferenceNode; function updateTypeReferenceNode(node: TypeReferenceNode, typeName: EntityName, typeArguments: NodeArray | undefined): TypeReferenceNode; - function createFunctionTypeNode(typeParameters: TypeParameterDeclaration[] | undefined, parameters: ParameterDeclaration[], type: TypeNode | undefined): FunctionTypeNode; + function createFunctionTypeNode(typeParameters: ReadonlyArray | undefined, parameters: ReadonlyArray, type: TypeNode | undefined): FunctionTypeNode; function updateFunctionTypeNode(node: FunctionTypeNode, typeParameters: NodeArray | undefined, parameters: NodeArray, type: TypeNode | undefined): FunctionTypeNode; - function createConstructorTypeNode(typeParameters: TypeParameterDeclaration[] | undefined, parameters: ParameterDeclaration[], type: TypeNode | undefined): ConstructorTypeNode; + function createConstructorTypeNode(typeParameters: ReadonlyArray | undefined, parameters: ReadonlyArray, type: TypeNode | undefined): ConstructorTypeNode; function updateConstructorTypeNode(node: ConstructorTypeNode, typeParameters: NodeArray | undefined, parameters: NodeArray, type: TypeNode | undefined): ConstructorTypeNode; function createTypeQueryNode(exprName: EntityName): TypeQueryNode; function updateTypeQueryNode(node: TypeQueryNode, exprName: EntityName): TypeQueryNode; @@ -3483,15 +3503,17 @@ declare namespace ts { function updateArrayTypeNode(node: ArrayTypeNode, elementType: TypeNode): ArrayTypeNode; function createTupleTypeNode(elementTypes: ReadonlyArray): TupleTypeNode; function updateTypleTypeNode(node: TupleTypeNode, elementTypes: ReadonlyArray): TupleTypeNode; - function createUnionTypeNode(types: TypeNode[]): UnionTypeNode; + function createUnionTypeNode(types: ReadonlyArray): UnionTypeNode; function updateUnionTypeNode(node: UnionTypeNode, types: NodeArray): UnionTypeNode; - function createIntersectionTypeNode(types: TypeNode[]): IntersectionTypeNode; + function createIntersectionTypeNode(types: ReadonlyArray): IntersectionTypeNode; function updateIntersectionTypeNode(node: IntersectionTypeNode, types: NodeArray): IntersectionTypeNode; function createUnionOrIntersectionTypeNode(kind: SyntaxKind.UnionType | SyntaxKind.IntersectionType, types: ReadonlyArray): UnionOrIntersectionTypeNode; function createConditionalTypeNode(checkType: TypeNode, extendsType: TypeNode, trueType: TypeNode, falseType: TypeNode): ConditionalTypeNode; function updateConditionalTypeNode(node: ConditionalTypeNode, checkType: TypeNode, extendsType: TypeNode, trueType: TypeNode, falseType: TypeNode): ConditionalTypeNode; function createInferTypeNode(typeParameter: TypeParameterDeclaration): InferTypeNode; function updateInferTypeNode(node: InferTypeNode, typeParameter: TypeParameterDeclaration): InferTypeNode; + function createImportTypeNode(argument: TypeNode, qualifier?: EntityName, typeArguments?: ReadonlyArray, isTypeOf?: boolean): ImportTypeNode; + function updateImportTypeNode(node: ImportTypeNode, argument: TypeNode, qualifier?: EntityName, typeArguments?: ReadonlyArray, isTypeOf?: boolean): ImportTypeNode; function createParenthesizedType(type: TypeNode): ParenthesizedTypeNode; function updateParenthesizedType(node: ParenthesizedTypeNode, type: TypeNode): ParenthesizedTypeNode; function createThisTypeNode(): ThisTypeNode; @@ -3690,7 +3712,7 @@ declare namespace ts { function updateSpreadAssignment(node: SpreadAssignment, expression: Expression): SpreadAssignment; function createEnumMember(name: string | PropertyName, initializer?: Expression): EnumMember; function updateEnumMember(node: EnumMember, name: PropertyName, initializer: Expression | undefined): EnumMember; - function updateSourceFileNode(node: SourceFile, statements: ReadonlyArray, isDeclarationFile?: boolean, referencedFiles?: SourceFile["referencedFiles"], typeReferences?: SourceFile["typeReferenceDirectives"]): SourceFile; + function updateSourceFileNode(node: SourceFile, statements: ReadonlyArray, isDeclarationFile?: boolean, referencedFiles?: SourceFile["referencedFiles"], typeReferences?: SourceFile["typeReferenceDirectives"], hasNoDefaultLib?: boolean): SourceFile; /** * Creates a shallow, memberwise clone of a node for mutation. */ @@ -3717,10 +3739,10 @@ declare namespace ts { function createBundle(sourceFiles: ReadonlyArray, prepends?: ReadonlyArray): Bundle; function createPrepend(javascript: string, declaration: string): UnparsedSource; function updateBundle(node: Bundle, sourceFiles: ReadonlyArray, prepends?: ReadonlyArray): Bundle; - function createImmediatelyInvokedFunctionExpression(statements: Statement[]): CallExpression; - function createImmediatelyInvokedFunctionExpression(statements: Statement[], param: ParameterDeclaration, paramValue: Expression): CallExpression; - function createImmediatelyInvokedArrowFunction(statements: Statement[]): CallExpression; - function createImmediatelyInvokedArrowFunction(statements: Statement[], param: ParameterDeclaration, paramValue: Expression): CallExpression; + function createImmediatelyInvokedFunctionExpression(statements: ReadonlyArray): CallExpression; + function createImmediatelyInvokedFunctionExpression(statements: ReadonlyArray, param: ParameterDeclaration, paramValue: Expression): CallExpression; + function createImmediatelyInvokedArrowFunction(statements: ReadonlyArray): CallExpression; + function createImmediatelyInvokedArrowFunction(statements: ReadonlyArray, param: ParameterDeclaration, paramValue: Expression): CallExpression; function createComma(left: Expression, right: Expression): Expression; function createLessThan(left: Expression, right: Expression): Expression; function createAssignment(left: ObjectLiteralExpression | ArrayLiteralExpression, right: Expression): DestructuringAssignment; @@ -4286,6 +4308,15 @@ declare namespace ts { getNonNullableType(): Type; getConstraint(): Type | undefined; getDefault(): Type | undefined; + isUnion(): this is UnionType; + isIntersection(): this is IntersectionType; + isUnionOrIntersection(): this is UnionOrIntersectionType; + isLiteral(): this is LiteralType; + isStringLiteral(): this is StringLiteralType; + isNumberLiteral(): this is NumberLiteralType; + isTypeParameter(): this is TypeParameter; + isClassOrInterface(): this is InterfaceType; + isClass(): this is InterfaceType; } interface Signature { getDeclaration(): SignatureDeclaration; @@ -4700,6 +4731,7 @@ declare namespace ts { placeOpenBraceOnNewLineForFunctions?: boolean; placeOpenBraceOnNewLineForControlBlocks?: boolean; insertSpaceBeforeTypeAnnotation?: boolean; + indentMultiLineObjectLiteralBeginningOnBlankLine?: boolean; } interface DefinitionInfo { fileName: string; @@ -5136,7 +5168,7 @@ declare namespace ts { function createLanguageServiceSourceFile(fileName: string, scriptSnapshot: IScriptSnapshot, scriptTarget: ScriptTarget, version: string, setNodeParents: boolean, scriptKind?: ScriptKind): SourceFile; let disableIncrementalParsing: boolean; function updateLanguageServiceSourceFile(sourceFile: SourceFile, scriptSnapshot: IScriptSnapshot, version: string, textChangeRange: TextChangeRange, aggressiveChecks?: boolean): SourceFile; - function createLanguageService(host: LanguageServiceHost, documentRegistry?: DocumentRegistry): LanguageService; + function createLanguageService(host: LanguageServiceHost, documentRegistry?: DocumentRegistry, syntaxOnly?: boolean): LanguageService; /** * Get the path of the default library files (lib.d.ts) as distributed with the typescript * node package. diff --git a/tests/baselines/reference/tsConfig/Initialized TSConfig with advanced options/tsconfig.json b/tests/baselines/reference/tsConfig/Initialized TSConfig with advanced options/tsconfig.json index c75fe3c9dc6ef..4d5c6053bcc76 100644 --- a/tests/baselines/reference/tsConfig/Initialized TSConfig with advanced options/tsconfig.json +++ b/tests/baselines/reference/tsConfig/Initialized TSConfig with advanced options/tsconfig.json @@ -13,6 +13,7 @@ // "outFile": "./", /* Concatenate and emit output to single file. */ // "outDir": "./", /* Redirect output structure to the directory. */ // "rootDir": "./", /* Specify the root directory of input files. Use to control the output directory structure with --outDir. */ + // "composite": true, /* Enable project compilation */ // "removeComments": true, /* Do not emit comments to output. */ // "noEmit": true, /* Do not emit outputs. */ // "importHelpers": true, /* Import emit helpers from 'tslib'. */ From d6099214cf7534ae94b4d1861566b695254c3e86 Mon Sep 17 00:00:00 2001 From: Ryan Cavanaugh Date: Fri, 20 Apr 2018 14:24:29 -0700 Subject: [PATCH 12/28] Switch to using SourceFiles to represent parsed config references --- src/compiler/commandLineParser.ts | 7 ++-- src/compiler/program.ts | 61 ++++++++++++++++++++++--------- src/harness/virtualFileSystem.ts | 4 ++ 3 files changed, 51 insertions(+), 21 deletions(-) diff --git a/src/compiler/commandLineParser.ts b/src/compiler/commandLineParser.ts index 06f64d2ee1199..11f2da47ab97e 100644 --- a/src/compiler/commandLineParser.ts +++ b/src/compiler/commandLineParser.ts @@ -994,9 +994,8 @@ namespace ts { */ export function parseConfigFileTextToJson(fileName: string, jsonText: string): { config?: any; error?: Diagnostic } { const jsonSourceFile = parseJsonText(fileName, jsonText); - const config = convertToObject(jsonSourceFile, jsonSourceFile.parseDiagnostics); return { - config, + config: convertToObject(jsonSourceFile, jsonSourceFile.parseDiagnostics), error: jsonSourceFile.parseDiagnostics.length ? jsonSourceFile.parseDiagnostics[0] : undefined }; } @@ -1615,8 +1614,8 @@ namespace ts { references.push({ path: getNormalizedAbsolutePath(ref.path, basePath), originalPath: ref.path, - prepend: ref.prepend || false, - circular: ref.circular || false + prepend: ref.prepend, + circular: ref.circular }); } } diff --git a/src/compiler/program.ts b/src/compiler/program.ts index 020e45f21d9a5..e82f829979682 100755 --- a/src/compiler/program.ts +++ b/src/compiler/program.ts @@ -604,17 +604,17 @@ namespace ts { const filesByNameIgnoreCase = host.useCaseSensitiveFileNames() ? createMap() : undefined; // A parallel array to projectReferences storing the results of reading in the referenced tsconfig files - const resolvedProjectReferences: ParsedCommandLine[] = []; + const resolvedProjectReferences: { commandLine: ParsedCommandLine; sourceFile: SourceFile }[] = []; const projectReferenceRedirects: Map = createMap(); if (projectReferences) { for (const ref of projectReferences) { const parsedRef = parseProjectReferenceConfigFile(ref); resolvedProjectReferences.push(parsedRef); if (parsedRef) { - if (parsedRef.options.outFile) { - processSourceFile(parsedRef.options.outFile, /*isDefaultLib*/ false, /*packageId*/ undefined); + if (parsedRef.commandLine.options.outFile) { + processSourceFile(parsedRef.commandLine.options.outFile, /*isDefaultLib*/ false, /*packageId*/ undefined); } - addProjectReferenceRedirects(parsedRef, projectReferenceRedirects); + addProjectReferenceRedirects(parsedRef.commandLine, projectReferenceRedirects); } } } @@ -741,7 +741,6 @@ namespace ts { } else if (options.composite) { // Project compilations never infer their root from the input source paths - Debug.assert(!!options.configFilePath); commonSourceDirectory = getDirectoryPath(normalizeSlashes(options.configFilePath)); } @@ -953,6 +952,26 @@ namespace ts { return oldProgram.structureIsReused = StructureIsReused.Not; } + // Check if any referenced project tsconfig files are different + if (createProgramOptions.projectReferences) { + for (let i = 0; i < createProgramOptions.projectReferences.length; i++) { + const oldRef = resolvedProjectReferences[i]; + if (oldRef) { + const newRef = parseProjectReferenceConfigFile(createProgramOptions.projectReferences[i]); + if (!newRef || newRef.sourceFile !== oldRef.sourceFile) { + // Resolved project reference has gone missing or changed + return oldProgram.structureIsReused = StructureIsReused.Not; + } + } + else { + // A previously-unresolved reference may be resolved now + if (parseProjectReferenceConfigFile(createProgramOptions.projectReferences[i]) !== undefined) { + return oldProgram.structureIsReused = StructureIsReused.Not; + } + } + } + } + // check if program source files has changed in the way that can affect structure of the program const newSourceFiles: SourceFile[] = []; const filePaths: Path[] = []; @@ -1164,10 +1183,10 @@ namespace ts { const nodes: UnparsedSource[] = []; for (let i = 0; i < projectReferences.length; i++) { const ref = projectReferences[i]; - const resolvedRef = resolvedProjectReferences[i]; - if (ref.prepend && resolvedRef && resolvedRef.options) { - const dtsFilename = changeExtension(resolvedRef.options.outFile, ".d.ts"); - const js = host.readFile(resolvedRef.options.outFile) || `/* Input file ${resolvedRef.options.outFile} was missing */\r\n`; + const resolvedRefOpts = resolvedProjectReferences[i].commandLine; + if (ref.prepend && resolvedRefOpts && resolvedRefOpts.options) { + const dtsFilename = changeExtension(resolvedRefOpts.options.outFile, ".d.ts"); + const js = host.readFile(resolvedRefOpts.options.outFile) || `/* Input file ${resolvedRefOpts.options.outFile} was missing */\r\n`; const dts = host.readFile(dtsFilename) || `/* Input file ${dtsFilename} was missing */\r\n`; const node = createPrepend(js, dts); nodes.push(node); @@ -2166,9 +2185,17 @@ namespace ts { return allFilesBelongToPath; } - function parseProjectReferenceConfigFile(ref: ProjectReference): ParsedCommandLine { + function parseProjectReferenceConfigFile(ref: ProjectReference): { commandLine: ParsedCommandLine, sourceFile: SourceFile } | undefined { + // The actual filename (i.e. add "/tsconfig.json" if necessary) const refPath = resolveProjectReferencePath(host, ref); - return getParsedCommandLineOfConfigFile(refPath, {}, parseConfigHostFromCompilerHost(host)); + // An absolute path pointing to the containing directory of the config file + const basePath = getNormalizedAbsolutePath(getDirectoryPath(refPath), host.getCurrentDirectory()); + const sourceFile = host.getSourceFile(refPath, ScriptTarget.Latest); + if (sourceFile === undefined) { + return undefined; + } + const commandLine = parseJsonSourceFileConfigFileContent(sourceFile, parseConfigHostFromCompilerHost(host), basePath, /*existingOptions*/ undefined, refPath); + return { commandLine, sourceFile }; } function addProjectReferenceRedirects(referencedProject: ParsedCommandLine, target: Map) { @@ -2223,18 +2250,18 @@ namespace ts { if (projectReferences) { for (let i = 0; i < projectReferences.length; i++) { const ref = projectReferences[i]; - const resolvedRef = resolvedProjectReferences[i]; - if (resolvedRef === undefined) { + const resolvedRefOpts = resolvedProjectReferences[i] && resolvedProjectReferences[i].commandLine.options; + if (resolvedRefOpts === undefined) { createDiagnosticForReference(i, Diagnostics.File_0_does_not_exist, ref.path); continue; } - if (!resolvedRef.options.composite) { + if (!resolvedRefOpts.composite) { createDiagnosticForReference(i, Diagnostics.Referenced_project_0_must_have_setting_composite_Colon_true, ref.path); } if (ref.prepend) { - if (resolvedRef.options.outFile) { - if (!host.fileExists(resolvedRef.options.outFile)) { - createDiagnosticForReference(i, Diagnostics.Output_file_0_from_project_1_does_not_exist, resolvedRef.options.outFile, ref.path); + if (resolvedRefOpts.outFile) { + if (!host.fileExists(resolvedRefOpts.outFile)) { + createDiagnosticForReference(i, Diagnostics.Output_file_0_from_project_1_does_not_exist, resolvedRefOpts.outFile, ref.path); } } else { diff --git a/src/harness/virtualFileSystem.ts b/src/harness/virtualFileSystem.ts index 300f563c698dd..4779b279eba71 100644 --- a/src/harness/virtualFileSystem.ts +++ b/src/harness/virtualFileSystem.ts @@ -270,6 +270,10 @@ namespace Utils { if (content === undefined) { return undefined; } + if (fileName.endsWith(".json")) { + return ts.parseJsonText(fileName, content); + } + return ts.createSourceFile(fileName, content, languageVersion); } getDefaultLibFileName(options: ts.CompilerOptions): string { From 6ce12855caf1414efa56a4ea0c6cdffd3611d8c9 Mon Sep 17 00:00:00 2001 From: Ryan Cavanaugh Date: Fri, 20 Apr 2018 15:42:25 -0700 Subject: [PATCH 13/28] Merge fixes --- src/compiler/program.ts | 3 --- src/services/getEditsForFileRename.ts | 2 +- 2 files changed, 1 insertion(+), 4 deletions(-) diff --git a/src/compiler/program.ts b/src/compiler/program.ts index fb9b566f254d0..bcd00836e22d4 100644 --- a/src/compiler/program.ts +++ b/src/compiler/program.ts @@ -666,9 +666,6 @@ namespace ts { } } - // unconditionally set moduleResolutionCache to undefined to avoid unnecessary leaks - moduleResolutionCache = undefined; - // Release any files we have acquired in the old program but are // not part of the new program. if (oldProgram && host.onReleaseOldSourceFile) { diff --git a/src/services/getEditsForFileRename.ts b/src/services/getEditsForFileRename.ts index 60fbd5b70f3cc..c4b82688aafac 100644 --- a/src/services/getEditsForFileRename.ts +++ b/src/services/getEditsForFileRename.ts @@ -36,7 +36,7 @@ namespace ts { if (checker.getSymbolAtLocation(importStringLiteral)) continue; const resolved = program.getResolvedModuleWithFailedLookupLocationsFromCache(importStringLiteral.text, sourceFile.fileName); - if (contains(resolved.failedLookupLocations, oldFilePath)) { + if (resolved && contains(resolved.failedLookupLocations, oldFilePath)) { result.push({ sourceFile, toUpdate: importStringLiteral }); } } From 9766ad82afa9e0e7dc6e5895e3fe5f8252c60915 Mon Sep 17 00:00:00 2001 From: Ryan Cavanaugh Date: Fri, 20 Apr 2018 16:16:16 -0700 Subject: [PATCH 14/28] Retain old logic about discarding invalid rootDir settings --- src/compiler/program.ts | 5 +---- .../rootDirectoryErrors/amd/rootDirectoryErrors.json | 8 ++++---- .../rootDirectoryErrors/node/rootDirectoryErrors.json | 8 ++++---- 3 files changed, 9 insertions(+), 12 deletions(-) diff --git a/src/compiler/program.ts b/src/compiler/program.ts index bcd00836e22d4..70bfd18da0ddd 100644 --- a/src/compiler/program.ts +++ b/src/compiler/program.ts @@ -732,16 +732,13 @@ namespace ts { function getCommonSourceDirectory() { if (commonSourceDirectory === undefined) { const emittedFiles = filter(files, file => sourceFileMayBeEmitted(file, options, isSourceFileFromExternalLibrary)); - if (options.rootDir) { + if (options.rootDir && checkSourceFilesBelongToPath(emittedFiles, options.rootDir)) { // If a rootDir is specified use it as the commonSourceDirectory commonSourceDirectory = getNormalizedAbsolutePath(options.rootDir, currentDirectory); } else if (options.composite) { // Project compilations never infer their root from the input source paths commonSourceDirectory = getDirectoryPath(normalizeSlashes(options.configFilePath)); - } - - if (commonSourceDirectory) { checkSourceFilesBelongToPath(emittedFiles, commonSourceDirectory); } else { diff --git a/tests/baselines/reference/project/rootDirectoryErrors/amd/rootDirectoryErrors.json b/tests/baselines/reference/project/rootDirectoryErrors/amd/rootDirectoryErrors.json index bbcde1907eaa0..42f348a4b56d6 100644 --- a/tests/baselines/reference/project/rootDirectoryErrors/amd/rootDirectoryErrors.json +++ b/tests/baselines/reference/project/rootDirectoryErrors/amd/rootDirectoryErrors.json @@ -14,9 +14,9 @@ "FolderA/FolderB/fileB.ts" ], "emittedFiles": [ - "outdir/simple/fileC.js", - "outdir/simple/fileC.d.ts", - "FolderA/FolderB/fileB.js", - "FolderA/FolderB/fileB.d.ts" + "outdir/simple/FolderC/fileC.js", + "outdir/simple/FolderC/fileC.d.ts", + "outdir/simple/fileB.js", + "outdir/simple/fileB.d.ts" ] } \ No newline at end of file diff --git a/tests/baselines/reference/project/rootDirectoryErrors/node/rootDirectoryErrors.json b/tests/baselines/reference/project/rootDirectoryErrors/node/rootDirectoryErrors.json index bbcde1907eaa0..42f348a4b56d6 100644 --- a/tests/baselines/reference/project/rootDirectoryErrors/node/rootDirectoryErrors.json +++ b/tests/baselines/reference/project/rootDirectoryErrors/node/rootDirectoryErrors.json @@ -14,9 +14,9 @@ "FolderA/FolderB/fileB.ts" ], "emittedFiles": [ - "outdir/simple/fileC.js", - "outdir/simple/fileC.d.ts", - "FolderA/FolderB/fileB.js", - "FolderA/FolderB/fileB.d.ts" + "outdir/simple/FolderC/fileC.js", + "outdir/simple/FolderC/fileC.d.ts", + "outdir/simple/fileB.js", + "outdir/simple/fileB.d.ts" ] } \ No newline at end of file From e007de4d3c2e03c7a0d73f359f0e7c8322bca37c Mon Sep 17 00:00:00 2001 From: Ryan Cavanaugh Date: Fri, 20 Apr 2018 16:19:03 -0700 Subject: [PATCH 15/28] Styly --- src/compiler/core.ts | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/src/compiler/core.ts b/src/compiler/core.ts index 2469f4de2ec4f..199d37cc4bf23 100644 --- a/src/compiler/core.ts +++ b/src/compiler/core.ts @@ -2076,10 +2076,7 @@ namespace ts { } export function getEmitDeclarations(compilerOptions: CompilerOptions): boolean { - if (compilerOptions.composite) { - return true; - } - return !!compilerOptions.declaration; + return !!(compilerOptions.declaration || compilerOptions.composite); } export type StrictOptionName = "noImplicitAny" | "noImplicitThis" | "strictNullChecks" | "strictFunctionTypes" | "strictPropertyInitialization" | "alwaysStrict"; From 581a96f777ceb3e3779cb984a455eb9907d3436f Mon Sep 17 00:00:00 2001 From: Ryan Cavanaugh Date: Mon, 30 Apr 2018 10:01:48 -0700 Subject: [PATCH 16/28] Add JSON ScriptTarget + other fixes --- src/compiler/commandLineParser.ts | 2 +- src/compiler/emitter.ts | 2 +- src/compiler/parser.ts | 8 +++++++- src/compiler/program.ts | 15 +++++++++++++-- src/compiler/tsc.ts | 19 +++++++++++++------ src/compiler/types.ts | 3 ++- .../reference/api/tsserverlibrary.d.ts | 1 + tests/baselines/reference/api/typescript.d.ts | 1 + 8 files changed, 39 insertions(+), 12 deletions(-) diff --git a/src/compiler/commandLineParser.ts b/src/compiler/commandLineParser.ts index 5a5a217db7c1f..8277cc0d2a874 100644 --- a/src/compiler/commandLineParser.ts +++ b/src/compiler/commandLineParser.ts @@ -1909,7 +1909,7 @@ namespace ts { const options = getDefaultCompilerOptions(configFileName); convertOptionsFromJson(optionDeclarations, jsonOptions, basePath, options, Diagnostics.Unknown_compiler_option_0, errors); if (configFileName) { - options.configFilePath = configFileName; + options.configFilePath = normalizeSlashes(configFileName); } return options; } diff --git a/src/compiler/emitter.ts b/src/compiler/emitter.ts index 697a4e53627b9..23b13e9c77559 100644 --- a/src/compiler/emitter.ts +++ b/src/compiler/emitter.ts @@ -532,7 +532,7 @@ namespace ts { case EmitHint.MappedTypeParameter: return emitMappedTypeParameter(cast(node, isTypeParameterDeclaration)); case EmitHint.Unspecified: return pipelineEmitUnspecified(node); default: - assertTypeIsNever(hint); + return Debug.assertNever(hint, `Unhandled EmitHint: ${(ts as any).EmitHint ? (ts as any).EmitHint[hint] : hint }`); } } diff --git a/src/compiler/parser.ts b/src/compiler/parser.ts index 4cf4c6e4c6709..1c37fb3f65ec5 100644 --- a/src/compiler/parser.ts +++ b/src/compiler/parser.ts @@ -503,7 +503,13 @@ namespace ts { export function createSourceFile(fileName: string, sourceText: string, languageVersion: ScriptTarget, setParentNodes = false, scriptKind?: ScriptKind): SourceFile { performance.mark("beforeParse"); - const result = Parser.parseSourceFile(fileName, sourceText, languageVersion, /*syntaxCursor*/ undefined, setParentNodes, scriptKind); + let result: SourceFile; + if (languageVersion === ScriptTarget.JSON) { + result = Parser.parseJsonText(fileName, sourceText); + } + else { + result = Parser.parseSourceFile(fileName, sourceText, languageVersion, /*syntaxCursor*/ undefined, setParentNodes, scriptKind); + } performance.mark("afterParse"); performance.measure("Parse", "beforeParse", "afterParse"); return result; diff --git a/src/compiler/program.ts b/src/compiler/program.ts index 70bfd18da0ddd..bc9f72215d245 100644 --- a/src/compiler/program.ts +++ b/src/compiler/program.ts @@ -1916,12 +1916,19 @@ namespace ts { return file; } + let redirectedPath: string | undefined; if (refFile) { const redirect = getProjectReferenceRedirect(fileName); if (redirect) { ((refFile.redirectedReferences || (refFile.redirectedReferences = [])) as string[]).push(fileName); + fileName = redirect; + // Once we start redirecting to a file, we can potentially come back to it + // via a back-reference from another file in the .d.ts folder. If that happens we'll + // end up trying to add it to the program *again* because we were tracking it via its + // original (un-redirected) name. So we have to map both the original path and the redirected path + // to the source file we're about to find/create + redirectedPath = toPath(redirect); } - fileName = redirect || fileName; } // We haven't looked for this file, do so now and cache result @@ -1956,6 +1963,10 @@ namespace ts { } filesByName.set(path, file); + if (redirectedPath) { + filesByName.set(redirectedPath, file); + } + if (file) { sourceFilesFoundSearchingNodeModules.set(path, currentNodeModulesDepth > 0); file.path = path; @@ -2188,7 +2199,7 @@ namespace ts { const refPath = resolveProjectReferencePath(host, ref); // An absolute path pointing to the containing directory of the config file const basePath = getNormalizedAbsolutePath(getDirectoryPath(refPath), host.getCurrentDirectory()); - const sourceFile = host.getSourceFile(refPath, ScriptTarget.Latest); + const sourceFile = host.getSourceFile(refPath, ScriptTarget.JSON); if (sourceFile === undefined) { return undefined; } diff --git a/src/compiler/tsc.ts b/src/compiler/tsc.ts index 3c73eba86ef1a..71eeec6979a07 100644 --- a/src/compiler/tsc.ts +++ b/src/compiler/tsc.ts @@ -120,7 +120,7 @@ namespace ts { createWatchOfConfigFile(configParseResult, commandLineOptions); } else { - performCompilation(configParseResult.fileNames, configParseResult.options, getConfigFileParsingDiagnostics(configParseResult)); + performCompilation(configParseResult.fileNames, configParseResult.projectReferences, configParseResult.options, getConfigFileParsingDiagnostics(configParseResult)); } } else { @@ -130,7 +130,7 @@ namespace ts { createWatchOfFilesAndCompilerOptions(commandLine.fileNames, commandLineOptions); } else { - performCompilation(commandLine.fileNames, commandLineOptions); + performCompilation(commandLine.fileNames, /*references*/ undefined, commandLineOptions); } } } @@ -142,11 +142,18 @@ namespace ts { } } - function performCompilation(rootFileNames: string[], compilerOptions: CompilerOptions, configFileParsingDiagnostics?: ReadonlyArray) { - const compilerHost = createCompilerHost(compilerOptions); - enableStatistics(compilerOptions); + function performCompilation(rootNames: string[], projectReferences: ReadonlyArray | undefined, options: CompilerOptions, configFileParsingDiagnostics?: ReadonlyArray) { + const host = createCompilerHost(options); + enableStatistics(options); - const program = createProgram(rootFileNames, compilerOptions, compilerHost, /*oldProgram*/ undefined, configFileParsingDiagnostics); + const programOptions: CreateProgramOptions = { + rootNames, + options, + projectReferences, + host, + configFileParsingDiagnostics + }; + const program = createProgram(programOptions); const exitStatus = emitFilesAndReportErrors(program, reportDiagnostic, s => sys.write(s + sys.newLine)); reportStatistics(program); return sys.exit(exitStatus); diff --git a/src/compiler/types.ts b/src/compiler/types.ts index 817606e5c44f1..30d301ffafa84 100644 --- a/src/compiler/types.ts +++ b/src/compiler/types.ts @@ -4314,12 +4314,13 @@ namespace ts { ES2017 = 4, ES2018 = 5, ESNext = 6, + JSON = 100, Latest = ESNext, } export const enum LanguageVariant { Standard, - JSX, + JSX } /** Either a parsed command line or a parsed tsconfig.json */ diff --git a/tests/baselines/reference/api/tsserverlibrary.d.ts b/tests/baselines/reference/api/tsserverlibrary.d.ts index 292ee932d6411..4e86eb1415c15 100644 --- a/tests/baselines/reference/api/tsserverlibrary.d.ts +++ b/tests/baselines/reference/api/tsserverlibrary.d.ts @@ -2457,6 +2457,7 @@ declare namespace ts { ES2017 = 4, ES2018 = 5, ESNext = 6, + JSON = 100, Latest = 6 } enum LanguageVariant { diff --git a/tests/baselines/reference/api/typescript.d.ts b/tests/baselines/reference/api/typescript.d.ts index 16d74c6cbb448..44c0e8a1075a5 100644 --- a/tests/baselines/reference/api/typescript.d.ts +++ b/tests/baselines/reference/api/typescript.d.ts @@ -2457,6 +2457,7 @@ declare namespace ts { ES2017 = 4, ES2018 = 5, ESNext = 6, + JSON = 100, Latest = 6 } enum LanguageVariant { From 2f9c51ecf65e46bd87283afd96a333ab5dfe07e6 Mon Sep 17 00:00:00 2001 From: Ryan Cavanaugh Date: Mon, 30 Apr 2018 13:32:47 -0700 Subject: [PATCH 17/28] Handle JSON files properly in document registry and host cache --- src/compiler/program.ts | 13 ++++++++-- src/server/editorServices.ts | 7 +++-- src/server/project.ts | 15 ++++++++++- src/server/utilities.ts | 2 ++ src/services/documentRegistry.ts | 3 ++- src/services/services.ts | 26 ++++++++++++++++--- src/services/types.ts | 1 + .../reference/api/tsserverlibrary.d.ts | 6 +++++ tests/baselines/reference/api/typescript.d.ts | 1 + 9 files changed, 65 insertions(+), 9 deletions(-) diff --git a/src/compiler/program.ts b/src/compiler/program.ts index bc9f72215d245..d534e357ded1b 100644 --- a/src/compiler/program.ts +++ b/src/compiler/program.ts @@ -607,7 +607,8 @@ namespace ts { resolvedProjectReferences.push(parsedRef); if (parsedRef) { if (parsedRef.commandLine.options.outFile) { - processSourceFile(parsedRef.commandLine.options.outFile, /*isDefaultLib*/ false, /*packageId*/ undefined); + const dtsOutfile = changeExtension(parsedRef.commandLine.options.outFile, ".d.ts"); + processSourceFile(dtsOutfile, /*isDefaultLib*/ false, /*packageId*/ undefined); } addProjectReferenceRedirects(parsedRef.commandLine, projectReferenceRedirects); } @@ -1179,6 +1180,9 @@ namespace ts { const ref = projectReferences[i]; const resolvedRefOpts = resolvedProjectReferences[i].commandLine; if (ref.prepend && resolvedRefOpts && resolvedRefOpts.options) { + // Upstream project didn't have outFile set -- skip (error will have been issued earlier) + if (!resolvedRefOpts.options.outFile) continue; + const dtsFilename = changeExtension(resolvedRefOpts.options.outFile, ".d.ts"); const js = host.readFile(resolvedRefOpts.options.outFile) || `/* Input file ${resolvedRefOpts.options.outFile} was missing */\r\n`; const dts = host.readFile(dtsFilename) || `/* Input file ${dtsFilename} was missing */\r\n`; @@ -2031,6 +2035,10 @@ namespace ts { function processTypeReferenceDirectives(file: SourceFile) { // We lower-case all type references because npm automatically lowercases all packages. See GH#9824. const typeDirectives = map(file.typeReferenceDirectives, ref => ref.fileName.toLocaleLowerCase()); + if (!typeDirectives) { + return; + } + const resolutions = resolveTypeReferenceDirectiveNamesWorker(typeDirectives, file.fileName); for (let i = 0; i < typeDirectives.length; i++) { @@ -2199,10 +2207,11 @@ namespace ts { const refPath = resolveProjectReferencePath(host, ref); // An absolute path pointing to the containing directory of the config file const basePath = getNormalizedAbsolutePath(getDirectoryPath(refPath), host.getCurrentDirectory()); - const sourceFile = host.getSourceFile(refPath, ScriptTarget.JSON); + const sourceFile = host.getSourceFile(refPath, ScriptTarget.JSON) as JsonSourceFile; if (sourceFile === undefined) { return undefined; } + const commandLine = parseJsonSourceFileConfigFileContent(sourceFile, parseConfigHostFromCompilerHost(host), basePath, /*existingOptions*/ undefined, refPath); return { commandLine, sourceFile }; } diff --git a/src/server/editorServices.ts b/src/server/editorServices.ts index 1271b98c1c78a..c4496f2f7b17b 100644 --- a/src/server/editorServices.ts +++ b/src/server/editorServices.ts @@ -1334,7 +1334,8 @@ namespace ts.server { configHasExcludeProperty: parsedCommandLine.raw.exclude !== undefined, wildcardDirectories: createMapFromTemplate(parsedCommandLine.wildcardDirectories), typeAcquisition: parsedCommandLine.typeAcquisition, - compileOnSave: parsedCommandLine.compileOnSave + compileOnSave: parsedCommandLine.compileOnSave, + projectReferences: parsedCommandLine.projectReferences }; return { projectOptions, configFileErrors: errors, configFileSpecs: parsedCommandLine.configFileSpecs }; @@ -1467,7 +1468,8 @@ namespace ts.server { projectOptions.compilerOptions, lastFileExceededProgramSize, projectOptions.compileOnSave === undefined ? false : projectOptions.compileOnSave, - cachedDirectoryStructureHost); + cachedDirectoryStructureHost, + projectOptions.projectReferences); project.configFileSpecs = configFileSpecs; // TODO: We probably should also watch the configFiles that are extended @@ -1592,6 +1594,7 @@ namespace ts.server { // Update the project project.configFileSpecs = configFileSpecs; project.setProjectErrors(configFileErrors); + project.updateReferences(projectOptions.projectReferences); const lastFileExceededProgramSize = this.getFilenameForExceededTotalSizeLimitForNonTsFiles(project.canonicalConfigFilePath, projectOptions.compilerOptions, projectOptions.files, fileNamePropertyReader); if (lastFileExceededProgramSize) { project.disableLanguageService(lastFileExceededProgramSize); diff --git a/src/server/project.ts b/src/server/project.ts index 92645cfca5248..4ef9b881f00a0 100644 --- a/src/server/project.ts +++ b/src/server/project.ts @@ -265,6 +265,10 @@ namespace ts.server { return this.projectStateVersion.toString(); } + getProjectReferences(): ReadonlyArray | undefined { + return undefined; + } + getScriptFileNames() { if (!this.rootFiles) { return ts.emptyArray; @@ -1262,7 +1266,8 @@ namespace ts.server { compilerOptions: CompilerOptions, lastFileExceededProgramSize: string | undefined, public compileOnSaveEnabled: boolean, - cachedDirectoryStructureHost: CachedDirectoryStructureHost) { + cachedDirectoryStructureHost: CachedDirectoryStructureHost, + private projectReferences: ReadonlyArray | undefined) { super(configFileName, ProjectKind.Configured, projectService, @@ -1304,6 +1309,14 @@ namespace ts.server { return asNormalizedPath(this.getProjectName()); } + getProjectReferences(): ReadonlyArray | undefined { + return this.projectReferences; + } + + updateReferences(refs: ReadonlyArray | undefined) { + this.projectReferences = refs; + } + enablePlugins() { const host = this.projectService.host; const options = this.getCompilationSettings(); diff --git a/src/server/utilities.ts b/src/server/utilities.ts index 363393d401eeb..5705960f3d255 100644 --- a/src/server/utilities.ts +++ b/src/server/utilities.ts @@ -128,6 +128,8 @@ namespace ts.server { configHasFilesProperty: boolean; configHasIncludeProperty: boolean; configHasExcludeProperty: boolean; + + projectReferences: ReadonlyArray | undefined; /** * these fields can be present in the project file */ diff --git a/src/services/documentRegistry.ts b/src/services/documentRegistry.ts index 81742018fb7dd..d464f7a408acc 100644 --- a/src/services/documentRegistry.ts +++ b/src/services/documentRegistry.ts @@ -172,9 +172,10 @@ namespace ts { const bucket = getBucketForCompilationSettings(key, /*createIfMissing*/ true); let entry = bucket.get(path); + const scriptTarget = scriptKind === ScriptKind.JSON ? ScriptTarget.JSON : compilationSettings.target; if (!entry) { // Have never seen this file with these settings. Create a new source file for it. - const sourceFile = createLanguageServiceSourceFile(fileName, scriptSnapshot, compilationSettings.target, version, /*setNodeParents*/ false, scriptKind); + const sourceFile = createLanguageServiceSourceFile(fileName, scriptSnapshot, scriptTarget, version, /*setNodeParents*/ false, scriptKind); entry = { sourceFile, diff --git a/src/services/services.ts b/src/services/services.ts index 08e3c960d7ae3..bd4e8d5cd734a 100644 --- a/src/services/services.ts +++ b/src/services/services.ts @@ -879,6 +879,10 @@ namespace ts { return this._compilationSettings; } + public getProjectReferences(): ReadonlyArray | undefined { + return this.host.getProjectReferences && this.host.getProjectReferences(); + } + private createEntry(fileName: string, path: Path) { let entry: CachedHostFileInformation; const scriptSnapshot = this.host.getScriptSnapshot(fileName); @@ -913,9 +917,18 @@ namespace ts { } public getRootFileNames(): string[] { - return arrayFrom(this.fileNameToEntry.values(), entry => { - return isString(entry) ? entry : entry.hostFileName; + const names: string[] = []; + this.fileNameToEntry.forEach(entry => { + if (isString(entry)) { + names.push(entry); + } + else { + if (entry.scriptKind !== ScriptKind.JSON) { + names.push(entry.hostFileName); + } + } }); + return names; } public getVersion(path: Path): string { @@ -1242,7 +1255,14 @@ namespace ts { } const documentRegistryBucketKey = documentRegistry.getKeyForCompilationSettings(newSettings); - program = createProgram(rootFileNames, newSettings, compilerHost, program); + const options: CreateProgramOptions = { + rootNames: rootFileNames, + options: newSettings, + host: compilerHost, + oldProgram: program, + projectReferences: hostCache.getProjectReferences() + }; + program = createProgram(options); // hostCache is captured in the closure for 'getOrCreateSourceFile' but it should not be used past this point. // It needs to be cleared to allow all collected snapshots to be released diff --git a/src/services/types.ts b/src/services/types.ts index e835d0cdef654..5e0cb85965779 100644 --- a/src/services/types.ts +++ b/src/services/types.ts @@ -179,6 +179,7 @@ namespace ts { getScriptKind?(fileName: string): ScriptKind; getScriptVersion(fileName: string): string; getScriptSnapshot(fileName: string): IScriptSnapshot | undefined; + getProjectReferences?(): ReadonlyArray | undefined; getLocalizedDiagnosticMessages?(): any; getCancellationToken?(): HostCancellationToken; getCurrentDirectory(): string; diff --git a/tests/baselines/reference/api/tsserverlibrary.d.ts b/tests/baselines/reference/api/tsserverlibrary.d.ts index 4e86eb1415c15..81ace3272e143 100644 --- a/tests/baselines/reference/api/tsserverlibrary.d.ts +++ b/tests/baselines/reference/api/tsserverlibrary.d.ts @@ -4397,6 +4397,7 @@ declare namespace ts { getScriptKind?(fileName: string): ScriptKind; getScriptVersion(fileName: string): string; getScriptSnapshot(fileName: string): IScriptSnapshot | undefined; + getProjectReferences?(): ReadonlyArray | undefined; getLocalizedDiagnosticMessages?(): any; getCancellationToken?(): HostCancellationToken; getCurrentDirectory(): string; @@ -5381,6 +5382,7 @@ declare namespace ts.server { configHasFilesProperty: boolean; configHasIncludeProperty: boolean; configHasExcludeProperty: boolean; + projectReferences: ReadonlyArray | undefined; /** * these fields can be present in the project file */ @@ -7752,6 +7754,7 @@ declare namespace ts.server { getCompilerOptions(): CompilerOptions; getNewLine(): string; getProjectVersion(): string; + getProjectReferences(): ReadonlyArray | undefined; getScriptFileNames(): string[]; private getOrCreateScriptInfoAndAttachToProject; getScriptKind(fileName: string): ScriptKind; @@ -7851,6 +7854,7 @@ declare namespace ts.server { */ class ConfiguredProject extends Project { compileOnSaveEnabled: boolean; + private projectReferences; private typeAcquisition; private directoriesWatchedForWildcards; readonly canonicalConfigFilePath: NormalizedPath; @@ -7863,6 +7867,8 @@ declare namespace ts.server { */ updateGraph(): boolean; getConfigFilePath(): NormalizedPath; + getProjectReferences(): ReadonlyArray | undefined; + updateReferences(refs: ReadonlyArray | undefined): void; enablePlugins(): void; /** * Get the errors that dont have any file name associated diff --git a/tests/baselines/reference/api/typescript.d.ts b/tests/baselines/reference/api/typescript.d.ts index 44c0e8a1075a5..6ba8f04d8b3f1 100644 --- a/tests/baselines/reference/api/typescript.d.ts +++ b/tests/baselines/reference/api/typescript.d.ts @@ -4397,6 +4397,7 @@ declare namespace ts { getScriptKind?(fileName: string): ScriptKind; getScriptVersion(fileName: string): string; getScriptSnapshot(fileName: string): IScriptSnapshot | undefined; + getProjectReferences?(): ReadonlyArray | undefined; getLocalizedDiagnosticMessages?(): any; getCancellationToken?(): HostCancellationToken; getCurrentDirectory(): string; From 8bcea6072b6956e7697de90139f95285a6acca9d Mon Sep 17 00:00:00 2001 From: Ryan Cavanaugh Date: Tue, 1 May 2018 11:29:23 -0700 Subject: [PATCH 18/28] Properly handle "simplified" results in most places --- src/server/session.ts | 8 +++++--- src/services/services.ts | 7 +++++++ src/services/types.ts | 2 ++ 3 files changed, 14 insertions(+), 3 deletions(-) diff --git a/src/server/session.ts b/src/server/session.ts index baed5a2dbe3e0..36dcaa9bbf248 100644 --- a/src/server/session.ts +++ b/src/server/session.ts @@ -699,12 +699,14 @@ namespace ts.server { } private toFileSpan(fileName: string, textSpan: TextSpan, project: Project): protocol.FileSpan { - const scriptInfo = project.getScriptInfo(fileName); + const ls = project.getLanguageService(); + const start = ls.toLineColumnOffset(fileName, textSpan.start); + const end = ls.toLineColumnOffset(fileName, textSpanEnd(textSpan)); return { file: fileName, - start: scriptInfo.positionToLineOffset(textSpan.start), - end: scriptInfo.positionToLineOffset(textSpanEnd(textSpan)) + start: { line: start.line + 1, offset: start.character + 1 }, + end: { line: end.line + 1, offset: end.character + 1 } }; } diff --git a/src/services/services.ts b/src/services/services.ts index 20f6f9841430a..700b07085a06e 100644 --- a/src/services/services.ts +++ b/src/services/services.ts @@ -1521,6 +1521,12 @@ namespace ts { return checker.getSymbolAtLocation(node); } + function toLineColumnOffset(fileName: string, position: number) { + const path = toPath(fileName, currentDirectory, getCanonicalFileName); + const file = program.getSourceFile(path) || sourcemappedFileCache.get(path); + return file.getLineAndCharacterOfPosition(position); + } + // Sometimes tools can sometimes see the following line as a source mapping url comment, so we mangle it a bit (the [M]) const sourceMapCommentRegExp = /^\/\/[@#] source[M]appingURL=(.+)$/gm; const base64UrlRegExp = /^data:(?:application\/json(?:;charset=[uU][tT][fF]-8);base64,([A-Za-z0-9+\/=]+)$)?/; @@ -2285,6 +2291,7 @@ namespace ts { getProgram, getApplicableRefactors, getEditsForRefactor, + toLineColumnOffset }; } diff --git a/src/services/types.ts b/src/services/types.ts index 7f3377d685df7..76fee927baf57 100644 --- a/src/services/types.ts +++ b/src/services/types.ts @@ -321,6 +321,8 @@ namespace ts { getSpanOfEnclosingComment(fileName: string, position: number, onlyMultiLine: boolean): TextSpan; + toLineColumnOffset(fileName: string, position: number): LineAndCharacter; + getCodeFixesAtPosition(fileName: string, start: number, end: number, errorCodes: ReadonlyArray, formatOptions: FormatCodeSettings, preferences: UserPreferences): ReadonlyArray; getCombinedCodeFix(scope: CombinedCodeFixScope, fixId: {}, formatOptions: FormatCodeSettings, preferences: UserPreferences): CombinedCodeActions; applyCodeActionCommand(action: CodeActionCommand): Promise; From c63de04ed211556d07958240c13166173f143541 Mon Sep 17 00:00:00 2001 From: Ryan Cavanaugh Date: Tue, 1 May 2018 11:29:37 -0700 Subject: [PATCH 19/28] Write newlines between prepends; re-order helpers --- src/compiler/emitter.ts | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/compiler/emitter.ts b/src/compiler/emitter.ts index 2a57318ebfaef..25794c5a6a362 100644 --- a/src/compiler/emitter.ts +++ b/src/compiler/emitter.ts @@ -399,6 +399,9 @@ namespace ts { setWriter(output); emitShebangIfNeeded(bundle); emitPrologueDirectivesIfNeeded(bundle); + emitHelpers(bundle); + emitSyntheticTripleSlashReferencesIfNeeded(bundle); + for (const prepend of bundle.prepends) { print(EmitHint.Unspecified, prepend, /*sourceFile*/ undefined); } @@ -406,10 +409,9 @@ namespace ts { if (bundleInfo) { bundleInfo.originalOffset = writer.getTextPos(); } - emitHelpers(bundle); - emitSyntheticTripleSlashReferencesIfNeeded(bundle); for (const sourceFile of bundle.sourceFiles) { + write("\n"); print(EmitHint.SourceFile, sourceFile, sourceFile); } reset(); From f79c496ed2806808df0d6412109e1d1081a9cb2b Mon Sep 17 00:00:00 2001 From: Ryan Cavanaugh Date: Tue, 1 May 2018 13:49:27 -0700 Subject: [PATCH 20/28] Chain through bundling logic in emitter --- src/compiler/emitter.ts | 15 ++-- src/compiler/factory.ts | 10 ++- src/compiler/program.ts | 6 +- src/compiler/transformer.ts | 4 +- src/compiler/transformers/declarations.ts | 6 +- src/compiler/transformers/es2015.ts | 2 +- src/compiler/transformers/es2016.ts | 2 +- src/compiler/transformers/es2017.ts | 2 +- src/compiler/transformers/es5.ts | 2 +- src/compiler/transformers/esnext.ts | 2 +- src/compiler/transformers/generators.ts | 2 +- src/compiler/transformers/jsx.ts | 2 +- src/compiler/transformers/module/es2015.ts | 2 +- src/compiler/transformers/module/module.ts | 2 +- src/compiler/transformers/module/system.ts | 2 +- src/compiler/transformers/ts.ts | 18 ++++- src/compiler/transformers/utilities.ts | 12 +++ src/compiler/types.ts | 14 +++- src/server/client.ts | 5 ++ src/services/types.ts | 2 +- .../reference/api/tsserverlibrary.d.ts | 77 ++++++++++--------- tests/baselines/reference/api/typescript.d.ts | 77 ++++++++++--------- .../reference/declarationMapsOutFile.js.map | 2 +- .../declarationMapsOutFile.sourcemap.txt | 11 +-- .../reference/declarationMapsOutFile2.js.map | 2 +- .../declarationMapsOutFile2.sourcemap.txt | 11 +-- .../declarationMapsWithSourceMap.js.map | 2 +- ...declarationMapsWithSourceMap.sourcemap.txt | 11 +-- 28 files changed, 180 insertions(+), 125 deletions(-) diff --git a/src/compiler/emitter.ts b/src/compiler/emitter.ts index 25794c5a6a362..a6667bbd7b177 100644 --- a/src/compiler/emitter.ts +++ b/src/compiler/emitter.ts @@ -90,7 +90,7 @@ namespace ts { /*@internal*/ // targetSourceFile is when users only want one file in entire project to be emitted. This is used in compileOnSave feature - export function emitFiles(resolver: EmitResolver, host: EmitHost, targetSourceFile: SourceFile, emitOnlyDtsFiles?: boolean, transformers?: TransformerFactory[]): EmitResult { + export function emitFiles(resolver: EmitResolver, host: EmitHost, targetSourceFile: SourceFile, emitOnlyDtsFiles?: boolean, transformers?: TransformerFactory[]): EmitResult { const compilerOptions = host.getCompilerOptions(); const sourceMapDataList: SourceMapData[] = (compilerOptions.sourceMap || compilerOptions.inlineSourceMap || getAreDeclarationMapsEnabled(compilerOptions)) ? [] : undefined; const emittedFilesList: string[] = compilerOptions.listEmittedFiles ? [] : undefined; @@ -143,7 +143,6 @@ namespace ts { } function emitJsFileOrBundle(sourceFileOrBundle: SourceFile | Bundle, jsFilePath: string, sourceMapFilePath: string, bundleInfoPath: string | undefined) { - const sourceFiles = isSourceFile(sourceFileOrBundle) ? [sourceFileOrBundle] : sourceFileOrBundle.sourceFiles; // Make sure not to write js file and source map file if any of them cannot be written if (host.isEmitBlocked(jsFilePath) || compilerOptions.noEmit || compilerOptions.emitDeclarationOnly) { emitSkipped = true; @@ -153,7 +152,7 @@ namespace ts { return; } // Transform the source files - const transform = transformNodes(resolver, host, compilerOptions, sourceFiles, transformers, /*allowDtsFiles*/ false); + const transform = transformNodes(resolver, host, compilerOptions, [sourceFileOrBundle], transformers, /*allowDtsFiles*/ false); // Create a printer to print the nodes const printer = createPrinter({ ...compilerOptions, noEmitHelpers: compilerOptions.noEmitHelpers } as PrinterOptions, { @@ -173,7 +172,8 @@ namespace ts { onSetSourceFile: setSourceFile, }); - printSourceFileOrBundle(jsFilePath, sourceMapFilePath, isSourceFile(sourceFileOrBundle) ? transform.transformed[0] : createBundle(transform.transformed), bundleInfoPath, printer, sourceMap); + Debug.assert(transform.transformed.length === 1, "Should only see one output from the transform"); + printSourceFileOrBundle(jsFilePath, sourceMapFilePath, transform.transformed[0], bundleInfoPath, printer, sourceMap); // Clean up emit nodes on parse tree transform.dispose(); @@ -186,7 +186,7 @@ namespace ts { const sourceFiles = isSourceFile(sourceFileOrBundle) ? [sourceFileOrBundle] : sourceFileOrBundle.sourceFiles; // Setup and perform the transformation to retrieve declarations from the input files const nonJsFiles = filter(sourceFiles, isSourceFileNotJavaScript); - const inputListOrBundle = (compilerOptions.outFile || compilerOptions.out) ? [createBundle(nonJsFiles)] : nonJsFiles; + const inputListOrBundle = (compilerOptions.outFile || compilerOptions.out) ? [createBundle(nonJsFiles, !isSourceFile(sourceFileOrBundle) ? sourceFileOrBundle.prepends : undefined)] : nonJsFiles; const declarationTransform = transformNodes(resolver, host, compilerOptions, inputListOrBundle, [transformDeclarations], /*allowDtsFiles*/ false); if (length(declarationTransform.diagnostics)) { for (const diagnostic of declarationTransform.diagnostics) { @@ -210,6 +210,7 @@ namespace ts { const declBlocked = (!!declarationTransform.diagnostics && !!declarationTransform.diagnostics.length) || !!host.isEmitBlocked(declarationFilePath) || !!compilerOptions.noEmit; emitSkipped = emitSkipped || declBlocked; if (!declBlocked || emitOnlyDtsFiles) { + Debug.assert(declarationTransform.transformed.length === 1, "Should only see one output from the decl transform"); printSourceFileOrBundle(declarationFilePath, declarationMapPath, declarationTransform.transformed[0], /* bundleInfopath*/ undefined, declarationPrinter, declarationSourceMap); } declarationTransform.dispose(); @@ -403,6 +404,7 @@ namespace ts { emitSyntheticTripleSlashReferencesIfNeeded(bundle); for (const prepend of bundle.prepends) { + write("\n"); print(EmitHint.Unspecified, prepend, /*sourceFile*/ undefined); } @@ -411,7 +413,6 @@ namespace ts { } for (const sourceFile of bundle.sourceFiles) { - write("\n"); print(EmitHint.SourceFile, sourceFile, sourceFile); } reset(); @@ -1018,7 +1019,7 @@ namespace ts { // SyntaxKind.UnparsedSource function emitUnparsedSource(unparsed: UnparsedSource) { - write(unparsed.javascriptText); + write(unparsed.text); } // diff --git a/src/compiler/factory.ts b/src/compiler/factory.ts index e0d70eac82ef5..b79e38e01148d 100644 --- a/src/compiler/factory.ts +++ b/src/compiler/factory.ts @@ -2569,15 +2569,21 @@ namespace ts { : node; } - export function createBundle(sourceFiles: ReadonlyArray, prepends: ReadonlyArray = emptyArray) { + export function createBundle(sourceFiles: ReadonlyArray, prepends: ReadonlyArray = emptyArray) { const node = createNode(SyntaxKind.Bundle); node.prepends = prepends; node.sourceFiles = sourceFiles; return node; } - export function createPrepend(javascript: string, declaration: string): UnparsedSource { + export function createUnparsedSourceFile(text: string): UnparsedSource { const node = createNode(SyntaxKind.UnparsedSource); + node.text = text; + return node; + } + + export function createInputFiles(javascript: string, declaration: string): InputFiles { + const node = createNode(SyntaxKind.InputFiles); node.javascriptText = javascript; node.declarationText = declaration; return node; diff --git a/src/compiler/program.ts b/src/compiler/program.ts index 9aa90fd98c403..7c523c025088a 100644 --- a/src/compiler/program.ts +++ b/src/compiler/program.ts @@ -1170,12 +1170,12 @@ namespace ts { }; } - function getPrependNodes(): UnparsedSource[] { + function getPrependNodes(): InputFiles[] { if (!projectReferences) { return emptyArray; } - const nodes: UnparsedSource[] = []; + const nodes: InputFiles[] = []; for (let i = 0; i < projectReferences.length; i++) { const ref = projectReferences[i]; const resolvedRefOpts = resolvedProjectReferences[i].commandLine; @@ -1186,7 +1186,7 @@ namespace ts { const dtsFilename = changeExtension(resolvedRefOpts.options.outFile, ".d.ts"); const js = host.readFile(resolvedRefOpts.options.outFile) || `/* Input file ${resolvedRefOpts.options.outFile} was missing */\r\n`; const dts = host.readFile(dtsFilename) || `/* Input file ${dtsFilename} was missing */\r\n`; - const node = createPrepend(js, dts); + const node = createInputFiles(js, dts); nodes.push(node); } } diff --git a/src/compiler/transformer.ts b/src/compiler/transformer.ts index 8cadba453d8d1..16a6912757f40 100644 --- a/src/compiler/transformer.ts +++ b/src/compiler/transformer.ts @@ -1,6 +1,6 @@ /* @internal */ namespace ts { - function getModuleTransformer(moduleKind: ModuleKind): TransformerFactory { + function getModuleTransformer(moduleKind: ModuleKind): TransformerFactory { switch (moduleKind) { case ModuleKind.ESNext: case ModuleKind.ES2015: @@ -28,7 +28,7 @@ namespace ts { const jsx = compilerOptions.jsx; const languageVersion = getEmitScriptTarget(compilerOptions); const moduleKind = getEmitModuleKind(compilerOptions); - const transformers: TransformerFactory[] = []; + const transformers: TransformerFactory[] = []; addRange(transformers, customTransformers && customTransformers.before); diff --git a/src/compiler/transformers/declarations.ts b/src/compiler/transformers/declarations.ts index 67e4fa31cb249..1a9d8c11dc411 100644 --- a/src/compiler/transformers/declarations.ts +++ b/src/compiler/transformers/declarations.ts @@ -163,7 +163,11 @@ namespace ts { const updated = visitNodes(sourceFile.statements, visitDeclarationStatements); return updateSourceFileNode(sourceFile, filterCandidateImports(updated), /*isDeclarationFile*/ true, /*referencedFiles*/ [], /*typeReferences*/ [], /*hasNoDefaultLib*/ false); } - )); + ), ts.mapDefined(node.prepends, prepend => { + if (prepend.kind === SyntaxKind.InputFiles) { + return createUnparsedSourceFile(prepend.declarationText); + } + })); bundle.syntheticFileReferences = []; bundle.syntheticTypeReferences = getFileReferencesForUsedTypeReferences(); bundle.hasNoDefaultLib = hasNoDefaultLib; diff --git a/src/compiler/transformers/es2015.ts b/src/compiler/transformers/es2015.ts index 0a607494bb5ef..76a7219b75819 100644 --- a/src/compiler/transformers/es2015.ts +++ b/src/compiler/transformers/es2015.ts @@ -295,7 +295,7 @@ namespace ts { */ let enabledSubstitutions: ES2015SubstitutionFlags; - return transformSourceFile; + return chainBundle(transformSourceFile); function transformSourceFile(node: SourceFile) { if (node.isDeclarationFile) { diff --git a/src/compiler/transformers/es2016.ts b/src/compiler/transformers/es2016.ts index 22b2f11b6122e..93b5d45714ff2 100644 --- a/src/compiler/transformers/es2016.ts +++ b/src/compiler/transformers/es2016.ts @@ -3,7 +3,7 @@ namespace ts { export function transformES2016(context: TransformationContext) { const { hoistVariableDeclaration } = context; - return transformSourceFile; + return chainBundle(transformSourceFile); function transformSourceFile(node: SourceFile) { if (node.isDeclarationFile) { diff --git a/src/compiler/transformers/es2017.ts b/src/compiler/transformers/es2017.ts index 6689f83ed5b59..c61809b64e018 100644 --- a/src/compiler/transformers/es2017.ts +++ b/src/compiler/transformers/es2017.ts @@ -40,7 +40,7 @@ namespace ts { context.onEmitNode = onEmitNode; context.onSubstituteNode = onSubstituteNode; - return transformSourceFile; + return chainBundle(transformSourceFile); function transformSourceFile(node: SourceFile) { if (node.isDeclarationFile) { diff --git a/src/compiler/transformers/es5.ts b/src/compiler/transformers/es5.ts index 92ecce6a15c7b..bace315e95338 100644 --- a/src/compiler/transformers/es5.ts +++ b/src/compiler/transformers/es5.ts @@ -24,7 +24,7 @@ namespace ts { context.onSubstituteNode = onSubstituteNode; context.enableSubstitution(SyntaxKind.PropertyAccessExpression); context.enableSubstitution(SyntaxKind.PropertyAssignment); - return transformSourceFile; + return chainBundle(transformSourceFile); /** * Transforms an ES5 source file to ES3. diff --git a/src/compiler/transformers/esnext.ts b/src/compiler/transformers/esnext.ts index 68861a5bb43f5..d2e01b4fbbf7e 100644 --- a/src/compiler/transformers/esnext.ts +++ b/src/compiler/transformers/esnext.ts @@ -26,7 +26,7 @@ namespace ts { let enclosingFunctionFlags: FunctionFlags; let enclosingSuperContainerFlags: NodeCheckFlags = 0; - return transformSourceFile; + return chainBundle(transformSourceFile); function transformSourceFile(node: SourceFile) { if (node.isDeclarationFile) { diff --git a/src/compiler/transformers/generators.ts b/src/compiler/transformers/generators.ts index 15f0d2d0f819c..a1d610280c043 100644 --- a/src/compiler/transformers/generators.ts +++ b/src/compiler/transformers/generators.ts @@ -289,7 +289,7 @@ namespace ts { let currentExceptionBlock: ExceptionBlock; // The current exception block. let withBlockStack: WithBlock[]; // A stack containing `with` blocks. - return transformSourceFile; + return chainBundle(transformSourceFile); function transformSourceFile(node: SourceFile) { if (node.isDeclarationFile || (node.transformFlags & TransformFlags.ContainsGenerator) === 0) { diff --git a/src/compiler/transformers/jsx.ts b/src/compiler/transformers/jsx.ts index 4acdf63b2756f..48d3bee42d6a8 100644 --- a/src/compiler/transformers/jsx.ts +++ b/src/compiler/transformers/jsx.ts @@ -4,7 +4,7 @@ namespace ts { const compilerOptions = context.getCompilerOptions(); let currentSourceFile: SourceFile; - return transformSourceFile; + return chainBundle(transformSourceFile); /** * Transform JSX-specific syntax in a SourceFile. diff --git a/src/compiler/transformers/module/es2015.ts b/src/compiler/transformers/module/es2015.ts index a331585263273..070980df5de15 100644 --- a/src/compiler/transformers/module/es2015.ts +++ b/src/compiler/transformers/module/es2015.ts @@ -10,7 +10,7 @@ namespace ts { context.enableSubstitution(SyntaxKind.Identifier); let currentSourceFile: SourceFile; - return transformSourceFile; + return chainBundle(transformSourceFile); function transformSourceFile(node: SourceFile) { if (node.isDeclarationFile) { diff --git a/src/compiler/transformers/module/module.ts b/src/compiler/transformers/module/module.ts index f9fb47f6a27ed..584b217c96745 100644 --- a/src/compiler/transformers/module/module.ts +++ b/src/compiler/transformers/module/module.ts @@ -45,7 +45,7 @@ namespace ts { let noSubstitution: boolean[]; // Set of nodes for which substitution rules should be ignored. let needUMDDynamicImportHelper: boolean; - return transformSourceFile; + return chainBundle(transformSourceFile); /** * Transforms the module aspects of a SourceFile. diff --git a/src/compiler/transformers/module/system.ts b/src/compiler/transformers/module/system.ts index 5cde69e2ef65a..047b552415d7b 100644 --- a/src/compiler/transformers/module/system.ts +++ b/src/compiler/transformers/module/system.ts @@ -39,7 +39,7 @@ namespace ts { let enclosingBlockScopedContainer: Node; let noSubstitution: boolean[]; // Set of nodes for which substitution rules should be ignored. - return transformSourceFile; + return chainBundle(transformSourceFile); /** * Transforms the module aspects of a SourceFile. diff --git a/src/compiler/transformers/ts.ts b/src/compiler/transformers/ts.ts index f29829d46078e..0881a84ab4e29 100644 --- a/src/compiler/transformers/ts.ts +++ b/src/compiler/transformers/ts.ts @@ -88,7 +88,23 @@ namespace ts { */ let pendingExpressions: Expression[] | undefined; - return transformSourceFile; + return transformSourceFileOrBundle; + + function transformSourceFileOrBundle(node: SourceFile | Bundle) { + if (node.kind === SyntaxKind.Bundle) { + return transformBundle(node); + } + return transformSourceFile(node); + } + + function transformBundle(node: Bundle) { + return createBundle(node.sourceFiles.map(transformSourceFile), mapDefined(node.prepends, prepend => { + if (prepend.kind === SyntaxKind.InputFiles) { + return createUnparsedSourceFile(prepend.javascriptText); + } + return prepend; + })); + } /** * Transform TypeScript-specific syntax in a SourceFile. diff --git a/src/compiler/transformers/utilities.ts b/src/compiler/transformers/utilities.ts index 935f5b0ffd4e8..5265b09b082b6 100644 --- a/src/compiler/transformers/utilities.ts +++ b/src/compiler/transformers/utilities.ts @@ -33,6 +33,18 @@ namespace ts { return e.propertyName && e.propertyName.escapedText === InternalSymbolName.Default; } + export function chainBundle(transformSourceFile: (x: SourceFile) => SourceFile): (x: SourceFile | Bundle) => SourceFile | Bundle { + return transformSourceFileOrBundle; + + function transformSourceFileOrBundle(node: SourceFile | Bundle) { + return node.kind === SyntaxKind.SourceFile ? transformSourceFile(node) : transformBundle(node); + } + + function transformBundle(node: Bundle) { + return createBundle(node.sourceFiles.map(transformSourceFile), node.prepends); + } + } + export function getImportNeedsImportStarHelper(node: ImportDeclaration) { return !!getNamespaceDeclarationNode(node) || (getNamedImportCount(node) > 1 && containsDefaultReference(node.importClause.namedBindings)); } diff --git a/src/compiler/types.ts b/src/compiler/types.ts index 8dd9882ca0042..988f02bd3b6ce 100644 --- a/src/compiler/types.ts +++ b/src/compiler/types.ts @@ -398,6 +398,7 @@ namespace ts { SourceFile, Bundle, UnparsedSource, + InputFiles, // JSDoc nodes JSDocTypeExpression, @@ -2621,19 +2622,24 @@ namespace ts { export interface Bundle extends Node { kind: SyntaxKind.Bundle; - prepends: ReadonlyArray; + prepends: ReadonlyArray; sourceFiles: ReadonlyArray; /* @internal */ syntheticFileReferences?: ReadonlyArray; /* @internal */ syntheticTypeReferences?: ReadonlyArray; /* @internal */ hasNoDefaultLib?: boolean; } - export interface UnparsedSource extends Node { - kind: SyntaxKind.UnparsedSource; + export interface InputFiles extends Node { + kind: SyntaxKind.InputFiles; javascriptText: string; declarationText: string; } + export interface UnparsedSource extends Node { + kind: SyntaxKind.UnparsedSource; + text: string; + } + export interface JsonSourceFile extends SourceFile { jsonObject?: ObjectLiteralExpression; extendedSourceFiles?: string[]; @@ -4928,7 +4934,7 @@ namespace ts { isEmitBlocked(emitFileName: string): boolean; - getPrependNodes(): ReadonlyArray; + getPrependNodes(): ReadonlyArray; writeFile: WriteFileCallback; } diff --git a/src/server/client.ts b/src/server/client.ts index d7a83ee532094..4033b8596d52e 100644 --- a/src/server/client.ts +++ b/src/server/client.ts @@ -139,6 +139,11 @@ namespace ts.server { this.processRequest(CommandNames.Change, args); } + toLineColumnOffset(fileName: string, position: number) { + const { line, offset } = this.positionToOneBasedLineOffset(fileName, position); + return { line, character: offset }; + } + getQuickInfoAtPosition(fileName: string, position: number): QuickInfo { const args = this.createFileLocationRequestArgs(fileName, position); diff --git a/src/services/types.ts b/src/services/types.ts index 76fee927baf57..b92daac7339e3 100644 --- a/src/services/types.ts +++ b/src/services/types.ts @@ -321,7 +321,7 @@ namespace ts { getSpanOfEnclosingComment(fileName: string, position: number, onlyMultiLine: boolean): TextSpan; - toLineColumnOffset(fileName: string, position: number): LineAndCharacter; + toLineColumnOffset?(fileName: string, position: number): LineAndCharacter; getCodeFixesAtPosition(fileName: string, start: number, end: number, errorCodes: ReadonlyArray, formatOptions: FormatCodeSettings, preferences: UserPreferences): ReadonlyArray; getCombinedCodeFix(scope: CombinedCodeFixScope, fixId: {}, formatOptions: FormatCodeSettings, preferences: UserPreferences): CombinedCodeActions; diff --git a/tests/baselines/reference/api/tsserverlibrary.d.ts b/tests/baselines/reference/api/tsserverlibrary.d.ts index 9b141d931d5de..48ff19744e1dc 100644 --- a/tests/baselines/reference/api/tsserverlibrary.d.ts +++ b/tests/baselines/reference/api/tsserverlibrary.d.ts @@ -338,32 +338,33 @@ declare namespace ts { SourceFile = 273, Bundle = 274, UnparsedSource = 275, - JSDocTypeExpression = 276, - JSDocAllType = 277, - JSDocUnknownType = 278, - JSDocNullableType = 279, - JSDocNonNullableType = 280, - JSDocOptionalType = 281, - JSDocFunctionType = 282, - JSDocVariadicType = 283, - JSDocComment = 284, - JSDocTypeLiteral = 285, - JSDocTag = 286, - JSDocAugmentsTag = 287, - JSDocClassTag = 288, - JSDocParameterTag = 289, - JSDocReturnTag = 290, - JSDocTypeTag = 291, - JSDocTemplateTag = 292, - JSDocTypedefTag = 293, - JSDocPropertyTag = 294, - SyntaxList = 295, - NotEmittedStatement = 296, - PartiallyEmittedExpression = 297, - CommaListExpression = 298, - MergeDeclarationMarker = 299, - EndOfDeclarationMarker = 300, - Count = 301, + InputFiles = 276, + JSDocTypeExpression = 277, + JSDocAllType = 278, + JSDocUnknownType = 279, + JSDocNullableType = 280, + JSDocNonNullableType = 281, + JSDocOptionalType = 282, + JSDocFunctionType = 283, + JSDocVariadicType = 284, + JSDocComment = 285, + JSDocTypeLiteral = 286, + JSDocTag = 287, + JSDocAugmentsTag = 288, + JSDocClassTag = 289, + JSDocParameterTag = 290, + JSDocReturnTag = 291, + JSDocTypeTag = 292, + JSDocTemplateTag = 293, + JSDocTypedefTag = 294, + JSDocPropertyTag = 295, + SyntaxList = 296, + NotEmittedStatement = 297, + PartiallyEmittedExpression = 298, + CommaListExpression = 299, + MergeDeclarationMarker = 300, + EndOfDeclarationMarker = 301, + Count = 302, FirstAssignment = 58, LastAssignment = 70, FirstCompoundAssignment = 59, @@ -389,10 +390,10 @@ declare namespace ts { FirstBinaryOperator = 27, LastBinaryOperator = 70, FirstNode = 145, - FirstJSDocNode = 276, - LastJSDocNode = 294, - FirstJSDocTagNode = 286, - LastJSDocTagNode = 294 + FirstJSDocNode = 277, + LastJSDocNode = 295, + FirstJSDocTagNode = 287, + LastJSDocTagNode = 295 } enum NodeFlags { None = 0, @@ -1645,14 +1646,18 @@ declare namespace ts { } interface Bundle extends Node { kind: SyntaxKind.Bundle; - prepends: ReadonlyArray; + prepends: ReadonlyArray; sourceFiles: ReadonlyArray; } - interface UnparsedSource extends Node { - kind: SyntaxKind.UnparsedSource; + interface InputFiles extends Node { + kind: SyntaxKind.InputFiles; javascriptText: string; declarationText: string; } + interface UnparsedSource extends Node { + kind: SyntaxKind.UnparsedSource; + text: string; + } interface JsonSourceFile extends SourceFile { jsonObject?: ObjectLiteralExpression; extendedSourceFiles?: string[]; @@ -3756,8 +3761,9 @@ declare namespace ts { function updatePartiallyEmittedExpression(node: PartiallyEmittedExpression, expression: Expression): PartiallyEmittedExpression; function createCommaList(elements: ReadonlyArray): CommaListExpression; function updateCommaList(node: CommaListExpression, elements: ReadonlyArray): CommaListExpression; - function createBundle(sourceFiles: ReadonlyArray, prepends?: ReadonlyArray): Bundle; - function createPrepend(javascript: string, declaration: string): UnparsedSource; + function createBundle(sourceFiles: ReadonlyArray, prepends?: ReadonlyArray): Bundle; + function createUnparsedSourceFile(text: string): UnparsedSource; + function createInputFiles(javascript: string, declaration: string): InputFiles; function updateBundle(node: Bundle, sourceFiles: ReadonlyArray, prepends?: ReadonlyArray): Bundle; function createImmediatelyInvokedFunctionExpression(statements: ReadonlyArray): CallExpression; function createImmediatelyInvokedFunctionExpression(statements: ReadonlyArray, param: ParameterDeclaration, paramValue: Expression): CallExpression; @@ -4483,6 +4489,7 @@ declare namespace ts { getDocCommentTemplateAtPosition(fileName: string, position: number): TextInsertion; isValidBraceCompletionAtPosition(fileName: string, position: number, openingBrace: number): boolean; getSpanOfEnclosingComment(fileName: string, position: number, onlyMultiLine: boolean): TextSpan; + toLineColumnOffset?(fileName: string, position: number): LineAndCharacter; getCodeFixesAtPosition(fileName: string, start: number, end: number, errorCodes: ReadonlyArray, formatOptions: FormatCodeSettings, preferences: UserPreferences): ReadonlyArray; getCombinedCodeFix(scope: CombinedCodeFixScope, fixId: {}, formatOptions: FormatCodeSettings, preferences: UserPreferences): CombinedCodeActions; applyCodeActionCommand(action: CodeActionCommand): Promise; diff --git a/tests/baselines/reference/api/typescript.d.ts b/tests/baselines/reference/api/typescript.d.ts index 94f343cd69972..bde3a7db23790 100644 --- a/tests/baselines/reference/api/typescript.d.ts +++ b/tests/baselines/reference/api/typescript.d.ts @@ -338,32 +338,33 @@ declare namespace ts { SourceFile = 273, Bundle = 274, UnparsedSource = 275, - JSDocTypeExpression = 276, - JSDocAllType = 277, - JSDocUnknownType = 278, - JSDocNullableType = 279, - JSDocNonNullableType = 280, - JSDocOptionalType = 281, - JSDocFunctionType = 282, - JSDocVariadicType = 283, - JSDocComment = 284, - JSDocTypeLiteral = 285, - JSDocTag = 286, - JSDocAugmentsTag = 287, - JSDocClassTag = 288, - JSDocParameterTag = 289, - JSDocReturnTag = 290, - JSDocTypeTag = 291, - JSDocTemplateTag = 292, - JSDocTypedefTag = 293, - JSDocPropertyTag = 294, - SyntaxList = 295, - NotEmittedStatement = 296, - PartiallyEmittedExpression = 297, - CommaListExpression = 298, - MergeDeclarationMarker = 299, - EndOfDeclarationMarker = 300, - Count = 301, + InputFiles = 276, + JSDocTypeExpression = 277, + JSDocAllType = 278, + JSDocUnknownType = 279, + JSDocNullableType = 280, + JSDocNonNullableType = 281, + JSDocOptionalType = 282, + JSDocFunctionType = 283, + JSDocVariadicType = 284, + JSDocComment = 285, + JSDocTypeLiteral = 286, + JSDocTag = 287, + JSDocAugmentsTag = 288, + JSDocClassTag = 289, + JSDocParameterTag = 290, + JSDocReturnTag = 291, + JSDocTypeTag = 292, + JSDocTemplateTag = 293, + JSDocTypedefTag = 294, + JSDocPropertyTag = 295, + SyntaxList = 296, + NotEmittedStatement = 297, + PartiallyEmittedExpression = 298, + CommaListExpression = 299, + MergeDeclarationMarker = 300, + EndOfDeclarationMarker = 301, + Count = 302, FirstAssignment = 58, LastAssignment = 70, FirstCompoundAssignment = 59, @@ -389,10 +390,10 @@ declare namespace ts { FirstBinaryOperator = 27, LastBinaryOperator = 70, FirstNode = 145, - FirstJSDocNode = 276, - LastJSDocNode = 294, - FirstJSDocTagNode = 286, - LastJSDocTagNode = 294 + FirstJSDocNode = 277, + LastJSDocNode = 295, + FirstJSDocTagNode = 287, + LastJSDocTagNode = 295 } enum NodeFlags { None = 0, @@ -1645,14 +1646,18 @@ declare namespace ts { } interface Bundle extends Node { kind: SyntaxKind.Bundle; - prepends: ReadonlyArray; + prepends: ReadonlyArray; sourceFiles: ReadonlyArray; } - interface UnparsedSource extends Node { - kind: SyntaxKind.UnparsedSource; + interface InputFiles extends Node { + kind: SyntaxKind.InputFiles; javascriptText: string; declarationText: string; } + interface UnparsedSource extends Node { + kind: SyntaxKind.UnparsedSource; + text: string; + } interface JsonSourceFile extends SourceFile { jsonObject?: ObjectLiteralExpression; extendedSourceFiles?: string[]; @@ -3756,8 +3761,9 @@ declare namespace ts { function updatePartiallyEmittedExpression(node: PartiallyEmittedExpression, expression: Expression): PartiallyEmittedExpression; function createCommaList(elements: ReadonlyArray): CommaListExpression; function updateCommaList(node: CommaListExpression, elements: ReadonlyArray): CommaListExpression; - function createBundle(sourceFiles: ReadonlyArray, prepends?: ReadonlyArray): Bundle; - function createPrepend(javascript: string, declaration: string): UnparsedSource; + function createBundle(sourceFiles: ReadonlyArray, prepends?: ReadonlyArray): Bundle; + function createUnparsedSourceFile(text: string): UnparsedSource; + function createInputFiles(javascript: string, declaration: string): InputFiles; function updateBundle(node: Bundle, sourceFiles: ReadonlyArray, prepends?: ReadonlyArray): Bundle; function createImmediatelyInvokedFunctionExpression(statements: ReadonlyArray): CallExpression; function createImmediatelyInvokedFunctionExpression(statements: ReadonlyArray, param: ParameterDeclaration, paramValue: Expression): CallExpression; @@ -4483,6 +4489,7 @@ declare namespace ts { getDocCommentTemplateAtPosition(fileName: string, position: number): TextInsertion; isValidBraceCompletionAtPosition(fileName: string, position: number, openingBrace: number): boolean; getSpanOfEnclosingComment(fileName: string, position: number, onlyMultiLine: boolean): TextSpan; + toLineColumnOffset?(fileName: string, position: number): LineAndCharacter; getCodeFixesAtPosition(fileName: string, start: number, end: number, errorCodes: ReadonlyArray, formatOptions: FormatCodeSettings, preferences: UserPreferences): ReadonlyArray; getCombinedCodeFix(scope: CombinedCodeFixScope, fixId: {}, formatOptions: FormatCodeSettings, preferences: UserPreferences): CombinedCodeActions; applyCodeActionCommand(action: CodeActionCommand): Promise; diff --git a/tests/baselines/reference/declarationMapsOutFile.js.map b/tests/baselines/reference/declarationMapsOutFile.js.map index 051318f806401..0018f3f9f31d7 100644 --- a/tests/baselines/reference/declarationMapsOutFile.js.map +++ b/tests/baselines/reference/declarationMapsOutFile.js.map @@ -1,2 +1,2 @@ //// [bundle.d.ts.map] -{"version":3,"file":"bundle.d.ts","sourceRoot":"","sources":["tests/cases/compiler/a.ts","tests/cases/compiler/index.ts"],"names":[],"mappings":";IAAA,MAAM;QACF,OAAO,CAAC,CAAC,EAAE;YAAC,CAAC,EAAE,MAAM,CAAA;SAAC;;;QAGtB,MAAM,CAAC,IAAI;KAGd;;;ICPD,OAAO,EAAC,GAAG,EAAC,UAAY;IAExB,MAAM,CAAC,KAAY,CAAC;IAGpB,MAAM,CAAC,IAAI,CAAC;;KAAqB,CAAC;IAClC,OAAO,EAAE,CAAC,EAAE,GAAG,EAAE,CAAC"} \ No newline at end of file +{"version":3,"file":"bundle.d.ts","sourceRoot":"","sources":["tests/cases/compiler/a.ts","tests/cases/compiler/index.ts"],"names":[],"mappings":";IAAA,MAAM;QACF,OAAO,CAAC,GAAG;YAAC,CAAC,EAAE,MAAM,CAAA;SAAC;;;QAGtB,MAAM,CAAC,IAAI;KAGd;;;ICPD,OAAO,EAAC,GAAG,EAAC,UAAY;IAExB,MAAM,CAAC,KAAY,CAAC;IAGpB,MAAM,CAAC,IAAI,CAAC;;KAAqB,CAAC;IAClC,OAAO,EAAE,CAAC,EAAE,GAAG,EAAE,CAAC"} \ No newline at end of file diff --git a/tests/baselines/reference/declarationMapsOutFile.sourcemap.txt b/tests/baselines/reference/declarationMapsOutFile.sourcemap.txt index 39d83719b20f3..f1090c4f765a2 100644 --- a/tests/baselines/reference/declarationMapsOutFile.sourcemap.txt +++ b/tests/baselines/reference/declarationMapsOutFile.sourcemap.txt @@ -22,20 +22,17 @@ sourceFile:tests/cases/compiler/a.ts 1->^^^^^^^^ 2 > ^^^^^^^ 3 > ^ -4 > ^ -5 > ^^ -6 > ^^^^-> +4 > ^^^ +5 > ^^^^-> 1-> class Foo { > 2 > doThing 3 > ( -4 > x -5 > : +4 > x: 1->Emitted(3, 9) Source(2, 5) + SourceIndex(0) 2 >Emitted(3, 16) Source(2, 12) + SourceIndex(0) 3 >Emitted(3, 17) Source(2, 13) + SourceIndex(0) -4 >Emitted(3, 18) Source(2, 14) + SourceIndex(0) -5 >Emitted(3, 20) Source(2, 16) + SourceIndex(0) +4 >Emitted(3, 20) Source(2, 16) + SourceIndex(0) --- >>> a: number; 1->^^^^^^^^^^^^ diff --git a/tests/baselines/reference/declarationMapsOutFile2.js.map b/tests/baselines/reference/declarationMapsOutFile2.js.map index 22845f90fb202..579077e0679cf 100644 --- a/tests/baselines/reference/declarationMapsOutFile2.js.map +++ b/tests/baselines/reference/declarationMapsOutFile2.js.map @@ -1,2 +1,2 @@ //// [bundle.d.ts.map] -{"version":3,"file":"bundle.d.ts","sourceRoot":"","sources":["tests/cases/compiler/a.ts","tests/cases/compiler/index.ts"],"names":[],"mappings":"AAAA;IACI,OAAO,CAAC,CAAC,EAAE;QAAC,CAAC,EAAE,MAAM,CAAA;KAAC;;;IAGtB,MAAM,CAAC,IAAI;CAGd;ACPD,QAAA,MAAM,CAAC,KAAY,CAAC;AAGpB,QAAA,IAAI,CAAC;;CAAqB,CAAC"} \ No newline at end of file +{"version":3,"file":"bundle.d.ts","sourceRoot":"","sources":["tests/cases/compiler/a.ts","tests/cases/compiler/index.ts"],"names":[],"mappings":"AAAA;IACI,OAAO,CAAC,GAAG;QAAC,CAAC,EAAE,MAAM,CAAA;KAAC;;;IAGtB,MAAM,CAAC,IAAI;CAGd;ACPD,QAAA,MAAM,CAAC,KAAY,CAAC;AAGpB,QAAA,IAAI,CAAC;;CAAqB,CAAC"} \ No newline at end of file diff --git a/tests/baselines/reference/declarationMapsOutFile2.sourcemap.txt b/tests/baselines/reference/declarationMapsOutFile2.sourcemap.txt index d7a829917e653..469e5ff2bd992 100644 --- a/tests/baselines/reference/declarationMapsOutFile2.sourcemap.txt +++ b/tests/baselines/reference/declarationMapsOutFile2.sourcemap.txt @@ -18,20 +18,17 @@ sourceFile:tests/cases/compiler/a.ts 1->^^^^ 2 > ^^^^^^^ 3 > ^ -4 > ^ -5 > ^^ -6 > ^^^^-> +4 > ^^^ +5 > ^^^^-> 1->class Foo { > 2 > doThing 3 > ( -4 > x -5 > : +4 > x: 1->Emitted(2, 5) Source(2, 5) + SourceIndex(0) 2 >Emitted(2, 12) Source(2, 12) + SourceIndex(0) 3 >Emitted(2, 13) Source(2, 13) + SourceIndex(0) -4 >Emitted(2, 14) Source(2, 14) + SourceIndex(0) -5 >Emitted(2, 16) Source(2, 16) + SourceIndex(0) +4 >Emitted(2, 16) Source(2, 16) + SourceIndex(0) --- >>> a: number; 1->^^^^^^^^ diff --git a/tests/baselines/reference/declarationMapsWithSourceMap.js.map b/tests/baselines/reference/declarationMapsWithSourceMap.js.map index 17ef6a6004ccc..9e27f4408187f 100644 --- a/tests/baselines/reference/declarationMapsWithSourceMap.js.map +++ b/tests/baselines/reference/declarationMapsWithSourceMap.js.map @@ -1,3 +1,3 @@ //// [bundle.js.map] {"version":3,"file":"bundle.js","sourceRoot":"","sources":["tests/cases/compiler/a.ts","tests/cases/compiler/index.ts"],"names":[],"mappings":"AAAA;IAAA;IAOA,CAAC;IANG,qBAAO,GAAP,UAAQ,CAAc;QAClB,OAAO,EAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAC,CAAC;IACpB,CAAC;IACM,QAAI,GAAX;QACI,OAAO,IAAI,GAAG,EAAE,CAAC;IACrB,CAAC;IACL,UAAC;AAAD,CAAC,AAPD,IAOC;ACPD,IAAM,CAAC,GAAG,IAAI,GAAG,EAAE,CAAC;AACpB,CAAC,CAAC,OAAO,CAAC,EAAC,CAAC,EAAE,EAAE,EAAC,CAAC,CAAC;AAEnB,IAAI,CAAC,GAAG,CAAC,CAAC,OAAO,CAAC,EAAC,CAAC,EAAE,EAAE,EAAC,CAAC,CAAC"}//// [bundle.d.ts.map] -{"version":3,"file":"bundle.d.ts","sourceRoot":"","sources":["tests/cases/compiler/a.ts","tests/cases/compiler/index.ts"],"names":[],"mappings":"AAAA;IACI,OAAO,CAAC,CAAC,EAAE;QAAC,CAAC,EAAE,MAAM,CAAA;KAAC;;;IAGtB,MAAM,CAAC,IAAI;CAGd;ACPD,QAAA,MAAM,CAAC,KAAY,CAAC;AAGpB,QAAA,IAAI,CAAC;;CAAqB,CAAC"} \ No newline at end of file +{"version":3,"file":"bundle.d.ts","sourceRoot":"","sources":["tests/cases/compiler/a.ts","tests/cases/compiler/index.ts"],"names":[],"mappings":"AAAA;IACI,OAAO,CAAC,GAAG;QAAC,CAAC,EAAE,MAAM,CAAA;KAAC;;;IAGtB,MAAM,CAAC,IAAI;CAGd;ACPD,QAAA,MAAM,CAAC,KAAY,CAAC;AAGpB,QAAA,IAAI,CAAC;;CAAqB,CAAC"} \ No newline at end of file diff --git a/tests/baselines/reference/declarationMapsWithSourceMap.sourcemap.txt b/tests/baselines/reference/declarationMapsWithSourceMap.sourcemap.txt index 0f9df308ef6cb..86d306d00fbe9 100644 --- a/tests/baselines/reference/declarationMapsWithSourceMap.sourcemap.txt +++ b/tests/baselines/reference/declarationMapsWithSourceMap.sourcemap.txt @@ -312,20 +312,17 @@ sourceFile:tests/cases/compiler/a.ts 1->^^^^ 2 > ^^^^^^^ 3 > ^ -4 > ^ -5 > ^^ -6 > ^^^^-> +4 > ^^^ +5 > ^^^^-> 1->class Foo { > 2 > doThing 3 > ( -4 > x -5 > : +4 > x: 1->Emitted(2, 5) Source(2, 5) + SourceIndex(0) 2 >Emitted(2, 12) Source(2, 12) + SourceIndex(0) 3 >Emitted(2, 13) Source(2, 13) + SourceIndex(0) -4 >Emitted(2, 14) Source(2, 14) + SourceIndex(0) -5 >Emitted(2, 16) Source(2, 16) + SourceIndex(0) +4 >Emitted(2, 16) Source(2, 16) + SourceIndex(0) --- >>> a: number; 1->^^^^^^^^ From ad3d50e4abbb556979422a45580c7933e962916b Mon Sep 17 00:00:00 2001 From: Ryan Cavanaugh Date: Tue, 1 May 2018 13:52:17 -0700 Subject: [PATCH 21/28] Move newline --- src/compiler/emitter.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/compiler/emitter.ts b/src/compiler/emitter.ts index a6667bbd7b177..daaa6aaeec954 100644 --- a/src/compiler/emitter.ts +++ b/src/compiler/emitter.ts @@ -404,8 +404,8 @@ namespace ts { emitSyntheticTripleSlashReferencesIfNeeded(bundle); for (const prepend of bundle.prepends) { - write("\n"); print(EmitHint.Unspecified, prepend, /*sourceFile*/ undefined); + write("\n"); } if (bundleInfo) { From 431c02d6b9e9462eba5a17a5832edccff17f6cec Mon Sep 17 00:00:00 2001 From: Ryan Cavanaugh Date: Tue, 1 May 2018 14:03:28 -0700 Subject: [PATCH 22/28] Lint --- src/compiler/transformers/declarations.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/compiler/transformers/declarations.ts b/src/compiler/transformers/declarations.ts index 1a9d8c11dc411..0a7cf397dddef 100644 --- a/src/compiler/transformers/declarations.ts +++ b/src/compiler/transformers/declarations.ts @@ -163,7 +163,7 @@ namespace ts { const updated = visitNodes(sourceFile.statements, visitDeclarationStatements); return updateSourceFileNode(sourceFile, filterCandidateImports(updated), /*isDeclarationFile*/ true, /*referencedFiles*/ [], /*typeReferences*/ [], /*hasNoDefaultLib*/ false); } - ), ts.mapDefined(node.prepends, prepend => { + ), mapDefined(node.prepends, prepend => { if (prepend.kind === SyntaxKind.InputFiles) { return createUnparsedSourceFile(prepend.declarationText); } From 4bb2e5aaad683fd194d7a8e3a6edef16e336e0ca Mon Sep 17 00:00:00 2001 From: Ryan Cavanaugh Date: Tue, 1 May 2018 14:11:57 -0700 Subject: [PATCH 23/28] Move file check to verifyCompilerOptions --- src/compiler/program.ts | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/src/compiler/program.ts b/src/compiler/program.ts index 7c523c025088a..d20ca7a76988a 100644 --- a/src/compiler/program.ts +++ b/src/compiler/program.ts @@ -656,17 +656,6 @@ namespace ts { Debug.assert(!!missingFilePaths); - // List of collected files is complete; validate exhautiveness if this is a project with a file list - if (options.composite && rootNames.length < files.length) { - const normalizedRootNames = rootNames.map(r => normalizePathAndRoot(r).toLowerCase()); - const sourceFiles = files.filter(f => !f.isDeclarationFile).map(f => normalizePathAndRoot(f.path).toLowerCase()); - for (const file of sourceFiles) { - if (normalizedRootNames.every(r => r !== file)) { - programDiagnostics.add(createCompilerDiagnostic(Diagnostics.File_0_is_not_in_project_file_list_Projects_must_list_all_files_or_use_an_include_pattern, file)); - } - } - } - // Release any files we have acquired in the old program but are // not part of the new program. if (oldProgram && host.onReleaseOldSourceFile) { @@ -2293,6 +2282,17 @@ namespace ts { } } + // List of collected files is complete; validate exhautiveness if this is a project with a file list + if (options.composite && rootNames.length < files.length) { + const normalizedRootNames = rootNames.map(r => normalizePathAndRoot(r).toLowerCase()); + const sourceFiles = files.filter(f => !f.isDeclarationFile).map(f => normalizePathAndRoot(f.path).toLowerCase()); + for (const file of sourceFiles) { + if (normalizedRootNames.every(r => r !== file)) { + programDiagnostics.add(createCompilerDiagnostic(Diagnostics.File_0_is_not_in_project_file_list_Projects_must_list_all_files_or_use_an_include_pattern, file)); + } + } + } + if (options.paths) { for (const key in options.paths) { if (!hasProperty(options.paths, key)) { From 8e765441aa79a0b8ada3447c9d3c060f80ff2890 Mon Sep 17 00:00:00 2001 From: Ryan Cavanaugh Date: Thu, 3 May 2018 13:34:10 -0700 Subject: [PATCH 24/28] PR feedback --- src/compiler/diagnosticMessages.json | 2 +- src/compiler/program.ts | 11 ++++++----- src/compiler/transformers/utilities.ts | 2 +- 3 files changed, 8 insertions(+), 7 deletions(-) diff --git a/src/compiler/diagnosticMessages.json b/src/compiler/diagnosticMessages.json index accef358cea8d..f66ddfe80baae 100644 --- a/src/compiler/diagnosticMessages.json +++ b/src/compiler/diagnosticMessages.json @@ -3557,7 +3557,7 @@ "category": "Error", "code": 6202 }, - "Projects may not disable declaration emit.": { + "Composite projects may not disable declaration emit.": { "category": "Error", "code": 6304 }, diff --git a/src/compiler/program.ts b/src/compiler/program.ts index d20ca7a76988a..5c495012a5d06 100644 --- a/src/compiler/program.ts +++ b/src/compiler/program.ts @@ -542,6 +542,7 @@ namespace ts { performance.mark("beforeProgram"); host = host || createCompilerHost(options); + const configParsingHost = parseConfigHostFromCompilerHost(host); let skipDefaultLib = options.noLib; const getDefaultLibraryFileName = memoize(() => host.getDefaultLibFileName(options)); @@ -2201,7 +2202,7 @@ namespace ts { return undefined; } - const commandLine = parseJsonSourceFileConfigFileContent(sourceFile, parseConfigHostFromCompilerHost(host), basePath, /*existingOptions*/ undefined, refPath); + const commandLine = parseJsonSourceFileConfigFileContent(sourceFile, configParsingHost, basePath, /*existingOptions*/ undefined, refPath); return { commandLine, sourceFile }; } @@ -2254,7 +2255,7 @@ namespace ts { if (options.composite) { if (options.declaration === false) { - createDiagnosticForOptionName(Diagnostics.Projects_may_not_disable_declaration_emit, "declaration"); + createDiagnosticForOptionName(Diagnostics.Composite_projects_may_not_disable_declaration_emit, "declaration"); } } @@ -2651,11 +2652,11 @@ namespace ts { function parseConfigHostFromCompilerHost(host: CompilerHost): ParseConfigFileHost { return { - fileExists: host.fileExists, + fileExists: f => host.fileExists(f), readDirectory: () => [], - readFile: host.readFile, + readFile: f => host.readFile(f), useCaseSensitiveFileNames: host.useCaseSensitiveFileNames(), - getCurrentDirectory: host.getCurrentDirectory, + getCurrentDirectory: () => host.getCurrentDirectory(), onUnRecoverableConfigFileDiagnostic: () => undefined }; } diff --git a/src/compiler/transformers/utilities.ts b/src/compiler/transformers/utilities.ts index 5265b09b082b6..7b4a35f3f2000 100644 --- a/src/compiler/transformers/utilities.ts +++ b/src/compiler/transformers/utilities.ts @@ -41,7 +41,7 @@ namespace ts { } function transformBundle(node: Bundle) { - return createBundle(node.sourceFiles.map(transformSourceFile), node.prepends); + return createBundle(map(node.sourceFiles, transformSourceFile), node.prepends); } } From 33466593d4949c2b0039652e6d65c6765ade32d4 Mon Sep 17 00:00:00 2001 From: Ryan Cavanaugh Date: Thu, 3 May 2018 13:37:31 -0700 Subject: [PATCH 25/28] Update test --- src/harness/unittests/projectReferences.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/harness/unittests/projectReferences.ts b/src/harness/unittests/projectReferences.ts index cdc4ef62b41c6..32f0381927dae 100644 --- a/src/harness/unittests/projectReferences.ts +++ b/src/harness/unittests/projectReferences.ts @@ -130,7 +130,7 @@ namespace ts { testProjectReferences(spec, "/primary/tsconfig.json", program => { const errs = program.getOptionsDiagnostics(); - assertHasError("Reports an error about the wrong decl setting", errs, Diagnostics.Projects_may_not_disable_declaration_emit); + assertHasError("Reports an error about the wrong decl setting", errs, Diagnostics.Composite_projects_may_not_disable_declaration_emit); }); }); From 5c61fb37390afbba64b2a7e500172d06d2b42f12 Mon Sep 17 00:00:00 2001 From: Ryan Cavanaugh Date: Thu, 3 May 2018 15:26:06 -0700 Subject: [PATCH 26/28] Add readDirectory as optional to CompilerHost; update to vfsys --- src/compiler/program.ts | 8 +++++--- src/compiler/types.ts | 1 + src/harness/fakes.ts | 4 ++++ src/harness/unittests/projectReferences.ts | 13 +++++++++---- tests/baselines/reference/api/tsserverlibrary.d.ts | 1 + tests/baselines/reference/api/typescript.d.ts | 1 + 6 files changed, 21 insertions(+), 7 deletions(-) diff --git a/src/compiler/program.ts b/src/compiler/program.ts index 4cae50be4985b..de1a323dfa033 100644 --- a/src/compiler/program.ts +++ b/src/compiler/program.ts @@ -188,7 +188,8 @@ namespace ts { directoryExists: directoryName => sys.directoryExists(directoryName), getEnvironmentVariable: name => sys.getEnvironmentVariable ? sys.getEnvironmentVariable(name) : "", getDirectories: (path: string) => sys.getDirectories(path), - realpath + realpath, + readDirectory: (path, extensions, include, exclude, depth) => sys.readDirectory(path, extensions, include, exclude, depth) }; } @@ -2650,10 +2651,11 @@ namespace ts { } } - function parseConfigHostFromCompilerHost(host: CompilerHost): ParseConfigFileHost { + /* @internal */ + export function parseConfigHostFromCompilerHost(host: CompilerHost): ParseConfigFileHost { return { fileExists: f => host.fileExists(f), - readDirectory: () => [], + readDirectory: (root, extensions, includes, depth?) => host.readDirectory ? host.readDirectory(root, extensions, includes, depth) : [], readFile: f => host.readFile(f), useCaseSensitiveFileNames: host.useCaseSensitiveFileNames(), getCurrentDirectory: () => host.getCurrentDirectory(), diff --git a/src/compiler/types.ts b/src/compiler/types.ts index 199ad6efe9060..ba93c808f2bb4 100644 --- a/src/compiler/types.ts +++ b/src/compiler/types.ts @@ -4703,6 +4703,7 @@ namespace ts { getCanonicalFileName(fileName: string): string; useCaseSensitiveFileNames(): boolean; getNewLine(): string; + readDirectory?(rootDir: string, extensions: ReadonlyArray, excludes: ReadonlyArray | undefined, includes: ReadonlyArray, depth?: number): string[]; /* * CompilerHost must either implement resolveModuleNames (in case if it wants to be completely in charge of diff --git a/src/harness/fakes.ts b/src/harness/fakes.ts index 669cb33c0c4c4..4ec5039f28b88 100644 --- a/src/harness/fakes.ts +++ b/src/harness/fakes.ts @@ -256,6 +256,10 @@ namespace fakes { return this.sys.getDirectories(path); } + public readDirectory(path: string, extensions?: ReadonlyArray, exclude?: ReadonlyArray, include?: ReadonlyArray, depth?: number): string[] { + return this.sys.readDirectory(path, extensions, exclude, include, depth); + } + public readFile(path: string): string | undefined { return this.sys.readFile(path); } diff --git a/src/harness/unittests/projectReferences.ts b/src/harness/unittests/projectReferences.ts index 32f0381927dae..9fb24a10f6fa4 100644 --- a/src/harness/unittests/projectReferences.ts +++ b/src/harness/unittests/projectReferences.ts @@ -45,7 +45,7 @@ namespace ts { return names.map((n, i) => `import * as mod_${i} from ${n}`).join("\r\n"); } - function testProjectReferences(spec: TestSpecification, entryPointConfigFileName: string, checkResult: (prog: Program, host: Utils.MockProjectReferenceCompilerHost) => void) { + function testProjectReferences(spec: TestSpecification, entryPointConfigFileName: string, checkResult: (prog: Program, host: fakes.CompilerHost) => void) { const files = createMap(); for (const key in spec) { const sp = spec[key]; @@ -77,13 +77,18 @@ namespace ts { } } - const host = new Utils.MockProjectReferenceCompilerHost("/", /*useCaseSensitiveFileNames*/ true, files); + const vfsys = new vfs.FileSystem(false, { files: { '/lib.d.ts': TestFSWithWatch.libFile.content! }}); + files.forEach((v, k) => { + vfsys.mkdirpSync(getDirectoryPath(k)); + vfsys.writeFileSync(k, v); + }); + const host = new fakes.CompilerHost(new fakes.System(vfsys)); const { config, error } = readConfigFile(entryPointConfigFileName, name => host.readFile(name)); // We shouldn't have any errors about invalid tsconfig files in these tests assert(config && !error, flattenDiagnosticMessageText(error && error.messageText, "\n")); - const file = parseJsonConfigFileContent(config, host.configHost, getDirectoryPath(entryPointConfigFileName), {}, entryPointConfigFileName); + const file = parseJsonConfigFileContent(config, ts.parseConfigHostFromCompilerHost(host), getDirectoryPath(entryPointConfigFileName), {}, entryPointConfigFileName); file.options.configFilePath = entryPointConfigFileName; const prog = createProgram({ rootNames: file.fileNames, @@ -278,7 +283,7 @@ namespace ts { }; testProjectReferences(spec, "/alpha/tsconfig.json", (program, host) => { program.emit(); - assert.deepEqual(host.emittedFiles.map(e => e.fileName).sort(), ["/alpha/bin/src/a.d.ts", "/alpha/bin/src/a.js"]); + assert.deepEqual(host.outputs.map(e => e.file).sort(), ["/alpha/bin/src/a.d.ts", "/alpha/bin/src/a.js"]); }); }); }); diff --git a/tests/baselines/reference/api/tsserverlibrary.d.ts b/tests/baselines/reference/api/tsserverlibrary.d.ts index 8acb9f4288324..95bc318f29ae3 100644 --- a/tests/baselines/reference/api/tsserverlibrary.d.ts +++ b/tests/baselines/reference/api/tsserverlibrary.d.ts @@ -2602,6 +2602,7 @@ declare namespace ts { getCanonicalFileName(fileName: string): string; useCaseSensitiveFileNames(): boolean; getNewLine(): string; + readDirectory?(rootDir: string, extensions: ReadonlyArray, excludes: ReadonlyArray | undefined, includes: ReadonlyArray, depth?: number): string[]; resolveModuleNames?(moduleNames: string[], containingFile: string, reusedNames?: string[]): (ResolvedModule | undefined)[]; /** * This method is a companion for 'resolveModuleNames' and is used to resolve 'types' references to actual type declaration files diff --git a/tests/baselines/reference/api/typescript.d.ts b/tests/baselines/reference/api/typescript.d.ts index 7b58ff93c25e4..90085344ec155 100644 --- a/tests/baselines/reference/api/typescript.d.ts +++ b/tests/baselines/reference/api/typescript.d.ts @@ -2602,6 +2602,7 @@ declare namespace ts { getCanonicalFileName(fileName: string): string; useCaseSensitiveFileNames(): boolean; getNewLine(): string; + readDirectory?(rootDir: string, extensions: ReadonlyArray, excludes: ReadonlyArray | undefined, includes: ReadonlyArray, depth?: number): string[]; resolveModuleNames?(moduleNames: string[], containingFile: string, reusedNames?: string[]): (ResolvedModule | undefined)[]; /** * This method is a companion for 'resolveModuleNames' and is used to resolve 'types' references to actual type declaration files From 8fa80af53d0141286f4c888f6c10ab5e612fe642 Mon Sep 17 00:00:00 2001 From: Ryan Cavanaugh Date: Thu, 3 May 2018 15:55:18 -0700 Subject: [PATCH 27/28] lint --- src/harness/unittests/projectReferences.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/harness/unittests/projectReferences.ts b/src/harness/unittests/projectReferences.ts index 9fb24a10f6fa4..76ddd5fc29ea4 100644 --- a/src/harness/unittests/projectReferences.ts +++ b/src/harness/unittests/projectReferences.ts @@ -77,18 +77,18 @@ namespace ts { } } - const vfsys = new vfs.FileSystem(false, { files: { '/lib.d.ts': TestFSWithWatch.libFile.content! }}); + const vfsys = new vfs.FileSystem(false, { files: { "/lib.d.ts": TestFSWithWatch.libFile.content! } }); files.forEach((v, k) => { vfsys.mkdirpSync(getDirectoryPath(k)); vfsys.writeFileSync(k, v); }); - const host = new fakes.CompilerHost(new fakes.System(vfsys)); + const host = new fakes.CompilerHost(new fakes.System(vfsys)); const { config, error } = readConfigFile(entryPointConfigFileName, name => host.readFile(name)); // We shouldn't have any errors about invalid tsconfig files in these tests assert(config && !error, flattenDiagnosticMessageText(error && error.messageText, "\n")); - const file = parseJsonConfigFileContent(config, ts.parseConfigHostFromCompilerHost(host), getDirectoryPath(entryPointConfigFileName), {}, entryPointConfigFileName); + const file = parseJsonConfigFileContent(config, parseConfigHostFromCompilerHost(host), getDirectoryPath(entryPointConfigFileName), {}, entryPointConfigFileName); file.options.configFilePath = entryPointConfigFileName; const prog = createProgram({ rootNames: file.fileNames, From dfbb6a27717e37343917cc43a8445b074fb9c99c Mon Sep 17 00:00:00 2001 From: Ryan Cavanaugh Date: Mon, 7 May 2018 12:22:27 -0700 Subject: [PATCH 28/28] PR comments --- src/compiler/core.ts | 12 ------- src/compiler/emitter.ts | 2 +- src/compiler/program.ts | 32 ++++++++++++++----- src/compiler/types.ts | 7 ++++ .../reference/api/tsserverlibrary.d.ts | 5 +++ tests/baselines/reference/api/typescript.d.ts | 5 +++ 6 files changed, 42 insertions(+), 21 deletions(-) diff --git a/src/compiler/core.ts b/src/compiler/core.ts index c726bbf1ca1d2..cc950c77af6da 100644 --- a/src/compiler/core.ts +++ b/src/compiler/core.ts @@ -2117,18 +2117,6 @@ namespace ts { return rootLength < 0 ? ~rootLength : rootLength; } - /** - * Normalizes a path including its leading drive letter (if any) - */ - export function normalizePathAndRoot(path: string): string { - const normalized = normalizePathAndParts(path).path; - const rootLength = getRootLength(normalized); - if (rootLength === 0) { - return normalized; - } - return path.substr(0, rootLength).toLowerCase() + path.substr(rootLength); - } - // TODO(rbuckton): replace references with `resolvePath` export function normalizePath(path: string): string { return resolvePath(path); diff --git a/src/compiler/emitter.ts b/src/compiler/emitter.ts index 8c69eefaef6ce..fc01fd6547dc4 100644 --- a/src/compiler/emitter.ts +++ b/src/compiler/emitter.ts @@ -413,7 +413,7 @@ namespace ts { for (const prepend of bundle.prepends) { print(EmitHint.Unspecified, prepend, /*sourceFile*/ undefined); - write("\n"); + writeLine(); } if (bundleInfo) { diff --git a/src/compiler/program.ts b/src/compiler/program.ts index de1a323dfa033..ebe8702887b79 100644 --- a/src/compiler/program.ts +++ b/src/compiler/program.ts @@ -601,7 +601,7 @@ namespace ts { const filesByNameIgnoreCase = host.useCaseSensitiveFileNames() ? createMap() : undefined; // A parallel array to projectReferences storing the results of reading in the referenced tsconfig files - const resolvedProjectReferences: { commandLine: ParsedCommandLine; sourceFile: SourceFile }[] = []; + const resolvedProjectReferences: (ResolvedProjectReference | undefined)[] | undefined = projectReferences ? [] : undefined; const projectReferenceRedirects: Map = createMap(); if (projectReferences) { for (const ref of projectReferences) { @@ -705,6 +705,7 @@ namespace ts { isEmittedFile, getConfigFileParsingDiagnostics, getResolvedModuleWithFailedLookupLocationsFromCache, + getProjectReferences }; verifyCompilerOptions(); @@ -939,11 +940,15 @@ namespace ts { } // Check if any referenced project tsconfig files are different - if (createProgramOptions.projectReferences) { - for (let i = 0; i < createProgramOptions.projectReferences.length; i++) { - const oldRef = resolvedProjectReferences[i]; + const oldRefs = oldProgram.getProjectReferences(); + if (projectReferences) { + if (!oldRefs) { + return oldProgram.structureIsReused = StructureIsReused.Not; + } + for (let i = 0; i < projectReferences.length; i++) { + const oldRef = oldRefs[i]; if (oldRef) { - const newRef = parseProjectReferenceConfigFile(createProgramOptions.projectReferences[i]); + const newRef = parseProjectReferenceConfigFile(projectReferences[i]); if (!newRef || newRef.sourceFile !== oldRef.sourceFile) { // Resolved project reference has gone missing or changed return oldProgram.structureIsReused = StructureIsReused.Not; @@ -951,12 +956,17 @@ namespace ts { } else { // A previously-unresolved reference may be resolved now - if (parseProjectReferenceConfigFile(createProgramOptions.projectReferences[i]) !== undefined) { + if (parseProjectReferenceConfigFile(projectReferences[i]) !== undefined) { return oldProgram.structureIsReused = StructureIsReused.Not; } } } } + else { + if (oldRefs) { + return oldProgram.structureIsReused = StructureIsReused.Not; + } + } // check if program source files has changed in the way that can affect structure of the program const newSourceFiles: SourceFile[] = []; @@ -1161,6 +1171,12 @@ namespace ts { }; } + function getProjectReferences() { + if (!resolvedProjectReferences) return; + + return resolvedProjectReferences; + } + function getPrependNodes(): InputFiles[] { if (!projectReferences) { return emptyArray; @@ -2286,8 +2302,8 @@ namespace ts { // List of collected files is complete; validate exhautiveness if this is a project with a file list if (options.composite && rootNames.length < files.length) { - const normalizedRootNames = rootNames.map(r => normalizePathAndRoot(r).toLowerCase()); - const sourceFiles = files.filter(f => !f.isDeclarationFile).map(f => normalizePathAndRoot(f.path).toLowerCase()); + const normalizedRootNames = rootNames.map(r => normalizePath(r).toLowerCase()); + const sourceFiles = files.filter(f => !f.isDeclarationFile).map(f => normalizePath(f.path).toLowerCase()); for (const file of sourceFiles) { if (normalizedRootNames.every(r => r !== file)) { programDiagnostics.add(createCompilerDiagnostic(Diagnostics.File_0_is_not_in_project_file_list_Projects_must_list_all_files_or_use_an_include_pattern, file)); diff --git a/src/compiler/types.ts b/src/compiler/types.ts index ba93c808f2bb4..c32ff97d10404 100644 --- a/src/compiler/types.ts +++ b/src/compiler/types.ts @@ -2757,6 +2757,13 @@ namespace ts { /* @internal */ isEmittedFile(file: string): boolean; /* @internal */ getResolvedModuleWithFailedLookupLocationsFromCache(moduleName: string, containingFile: string): ResolvedModuleWithFailedLookupLocations | undefined; + + getProjectReferences(): (ResolvedProjectReference | undefined)[] | undefined; + } + + export interface ResolvedProjectReference { + commandLine: ParsedCommandLine; + sourceFile: SourceFile; } /* @internal */ diff --git a/tests/baselines/reference/api/tsserverlibrary.d.ts b/tests/baselines/reference/api/tsserverlibrary.d.ts index b8e768a5791df..e14b2f52c33f5 100644 --- a/tests/baselines/reference/api/tsserverlibrary.d.ts +++ b/tests/baselines/reference/api/tsserverlibrary.d.ts @@ -1717,6 +1717,11 @@ declare namespace ts { */ getTypeChecker(): TypeChecker; isSourceFileFromExternalLibrary(file: SourceFile): boolean; + getProjectReferences(): (ResolvedProjectReference | undefined)[] | undefined; + } + interface ResolvedProjectReference { + commandLine: ParsedCommandLine; + sourceFile: SourceFile; } interface CustomTransformers { /** Custom transformers to evaluate before built-in transformations. */ diff --git a/tests/baselines/reference/api/typescript.d.ts b/tests/baselines/reference/api/typescript.d.ts index ee7b41be8bd07..f14270d75313c 100644 --- a/tests/baselines/reference/api/typescript.d.ts +++ b/tests/baselines/reference/api/typescript.d.ts @@ -1717,6 +1717,11 @@ declare namespace ts { */ getTypeChecker(): TypeChecker; isSourceFileFromExternalLibrary(file: SourceFile): boolean; + getProjectReferences(): (ResolvedProjectReference | undefined)[] | undefined; + } + interface ResolvedProjectReference { + commandLine: ParsedCommandLine; + sourceFile: SourceFile; } interface CustomTransformers { /** Custom transformers to evaluate before built-in transformations. */