diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index a34f073df3ec8..6751a122a2c70 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -2220,7 +2220,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 9710b0c12621e..f648bddefe9a7 100644 --- a/src/compiler/commandLineParser.ts +++ b/src/compiler/commandLineParser.ts @@ -235,6 +235,13 @@ 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", + isTSConfigOnly: true, + category: Diagnostics.Basic_Options, + description: Diagnostics.Enable_project_compilation, + }, { name: "removeComments", type: "boolean", @@ -814,12 +821,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 }; @@ -933,6 +942,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 @@ -1008,6 +1060,14 @@ namespace ts { name: "extends", type: "string" }, + { + name: "references", + type: "list", + element: { + name: "references", + type: "object" + } + }, { name: "files", type: "list", @@ -1415,7 +1475,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}},`); @@ -1499,12 +1559,13 @@ 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(); + const { fileNames, wildcardDirectories, spec, projectReferences } = getFileNames(); return { options, fileNames, + projectReferences, typeAcquisition: parsedConfig.typeAcquisition || getDefaultTypeAcquisition(), raw, errors, @@ -1558,10 +1619,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) { + 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, + circular: ref.circular + }); + } + } + result.projectReferences = references; + } + else { + createCompilerDiagnosticOnlyIfJson(Diagnostics.Compiler_option_0_requires_a_value_of_type_1, "references", "Array"); + } + } + return result; } @@ -1850,6 +1934,9 @@ namespace ts { const options = getDefaultCompilerOptions(configFileName); convertOptionsFromJson(optionDeclarations, jsonOptions, basePath, options, Diagnostics.Unknown_compiler_option_0, errors); + if (configFileName) { + options.configFilePath = normalizeSlashes(configFileName); + } return options; } @@ -2048,7 +2135,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); } @@ -2119,8 +2206,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/core.ts b/src/compiler/core.ts index c5e9a75b4ee07..20c3562271c7b 100644 --- a/src/compiler/core.ts +++ b/src/compiler/core.ts @@ -1963,6 +1963,10 @@ namespace ts { : moduleKind === ModuleKind.System; } + export function getEmitDeclarations(compilerOptions: CompilerOptions): boolean { + return !!(compilerOptions.declaration || compilerOptions.composite); + } + export type StrictOptionName = "noImplicitAny" | "noImplicitThis" | "strictNullChecks" | "strictFunctionTypes" | "strictPropertyInitialization" | "alwaysStrict"; export function getStrictOptionValue(compilerOptions: CompilerOptions, flag: StrictOptionName): boolean { diff --git a/src/compiler/diagnosticMessages.json b/src/compiler/diagnosticMessages.json index c12e0a9931b9b..1e101ce6e766b 100644 --- a/src/compiler/diagnosticMessages.json +++ b/src/compiler/diagnosticMessages.json @@ -3552,6 +3552,43 @@ "code": 6197 }, + "Projects to reference": { + "category": "Message", + "code": 6300 + }, + "Enable project compilation": { + "category": "Message", + "code": 6302 + }, + "Project references may not form a circular graph. Cycle detected: {0}": { + "category": "Error", + "code": 6202 + }, + "Composite 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 + }, + "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", "code": 7005 diff --git a/src/compiler/emitter.ts b/src/compiler/emitter.ts index 0786e849495e2..20bc916faf4b1 100644 --- a/src/compiler/emitter.ts +++ b/src/compiler/emitter.ts @@ -1,4 +1,5 @@ namespace ts { + const infoExtension = ".tsbundleinfo"; const brackets = createBracketsMap(); /*@internal*/ @@ -15,12 +16,11 @@ namespace ts { 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 bundle = createBundle(sourceFiles, host.getPrependNodes()); const result = action(getOutputPathsFor(bundle, host, emitOnlyDtsFiles), bundle); if (result) { return result; @@ -45,7 +45,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) + infoExtension); + return { jsFilePath, sourceMapFilePath, declarationFilePath, declarationMapPath, bundleInfoPath }; } else { const jsFilePath = getOwnEmitOutputFilePath(sourceFile, host, getOutputExtension(sourceFile, options)); @@ -54,7 +55,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 }; } } @@ -62,6 +63,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 @@ -87,7 +95,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; @@ -103,6 +111,7 @@ namespace ts { // Explicitly do not passthru either `inline` option }); + let bundleInfo: BundleInfo = createDefaultBundleInfo(); let emitSkipped = false; // Emit each output file @@ -118,8 +127,8 @@ namespace ts { sourceMaps: sourceMapDataList }; - 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); if (!emitSkipped && emittedFilesList) { @@ -132,11 +141,13 @@ namespace ts { if (declarationFilePath) { emittedFilesList.push(declarationFilePath); } + if (bundleInfoPath) { + emittedFilesList.push(bundleInfoPath); + } } } - function emitJsFileOrBundle(sourceFileOrBundle: SourceFile | Bundle, jsFilePath: string, sourceMapFilePath: string) { - const sourceFiles = isSourceFile(sourceFileOrBundle) ? [sourceFileOrBundle] : sourceFileOrBundle.sourceFiles; + function emitJsFileOrBundle(sourceFileOrBundle: SourceFile | Bundle, jsFilePath: string, sourceMapFilePath: string, bundleInfoPath: string | undefined) { // 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; @@ -146,7 +157,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, { @@ -166,7 +177,8 @@ namespace ts { onSetSourceFile: setSourceFile, }); - printSourceFileOrBundle(jsFilePath, sourceMapFilePath, isSourceFile(sourceFileOrBundle) ? transform.transformed[0] : createBundle(transform.transformed), 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(); @@ -179,7 +191,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) { @@ -203,19 +215,20 @@ 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); + 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(); } - function printSourceFileOrBundle(jsFilePath: string, sourceMapFilePath: string | undefined, sourceFileOrBundle: SourceFile | Bundle, printer: Printer, mapRecorder: SourceMapWriter) { + 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]; mapRecorder.initialize(jsFilePath, sourceMapFilePath || "", sourceFileOrBundle, sourceMapDataList); if (bundle) { - printer.writeBundle(bundle, writer); + printer.writeBundle(bundle, writer, bundleInfo); } else { printer.writeFile(sourceFile, writer); @@ -236,9 +249,17 @@ 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(); + + bundleInfo = createDefaultBundleInfo(); } function setSourceFile(node: SourceFile) { @@ -335,6 +356,7 @@ namespace ts { switch (node.kind) { case SyntaxKind.SourceFile: return printFile(node); case SyntaxKind.Bundle: return printBundle(node); + case SyntaxKind.UnparsedSource: return printUnparsedSource(node); } writeNode(hint, node, sourceFile, beginPrint()); return endPrint(); @@ -355,6 +377,11 @@ namespace ts { return endPrint(); } + function printUnparsedSource(unparsed: UnparsedSource): string { + writeUnparsedSource(unparsed, beginPrint()); + return endPrint(); + } + /** * If `sourceFile` is `undefined`, `node` must be a synthesized `TypeNode`. */ @@ -379,7 +406,7 @@ namespace ts { writer = previousWriter; } - function writeBundle(bundle: Bundle, output: EmitTextWriter) { + function writeBundle(bundle: Bundle, output: EmitTextWriter, bundleInfo?: BundleInfo) { isOwnFileEmit = false; const previousWriter = writer; setWriter(output); @@ -387,6 +414,16 @@ namespace ts { emitPrologueDirectivesIfNeeded(bundle); emitHelpers(bundle); emitSyntheticTripleSlashReferencesIfNeeded(bundle); + + for (const prepend of bundle.prepends) { + print(EmitHint.Unspecified, prepend, /*sourceFile*/ undefined); + writeLine(); + } + + if (bundleInfo) { + bundleInfo.originalOffset = writer.getTextPos(); + } + for (const sourceFile of bundle.sourceFiles) { print(EmitHint.SourceFile, sourceFile, sourceFile); } @@ -394,6 +431,14 @@ namespace ts { writer = previousWriter; } + function writeUnparsedSource(unparsed: UnparsedSource, output: EmitTextWriter) { + const previousWriter = writer; + setWriter(output); + print(EmitHint.Unspecified, unparsed, /*sourceFile*/ undefined); + reset(); + writer = previousWriter; + } + function writeFile(sourceFile: SourceFile, output: EmitTextWriter) { isOwnFileEmit = true; const previousWriter = writer; @@ -534,6 +579,9 @@ namespace ts { case SyntaxKind.TemplateTail: return emitLiteral(node); + case SyntaxKind.UnparsedSource: + return emitUnparsedSource(node); + // Identifiers case SyntaxKind.Identifier: return emitIdentifier(node); @@ -978,6 +1026,11 @@ namespace ts { } } + // SyntaxKind.UnparsedSource + function emitUnparsedSource(unparsed: UnparsedSource) { + write(unparsed.text); + } + // // Identifiers // diff --git a/src/compiler/factory.ts b/src/compiler/factory.ts index 735de930ae9c0..e8916ac62e51e 100644 --- a/src/compiler/factory.ts +++ b/src/compiler/factory.ts @@ -2562,15 +2562,29 @@ 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) { - if (node.sourceFiles !== sourceFiles) { - return createBundle(sourceFiles); + 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; + } + + 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/parser.ts b/src/compiler/parser.ts index b84c8ca004f04..9db66792a7ad8 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 4f052581c2028..3da1da2b01896 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) }; } @@ -328,14 +329,14 @@ namespace ts { output += formatColorAndReset(relativeFileName, ForegroundColorEscapeSequences.Cyan); output += ":"; - output += formatColorAndReset(`${ firstLine + 1 }`, ForegroundColorEscapeSequences.Yellow); + output += formatColorAndReset(`${firstLine + 1}`, ForegroundColorEscapeSequences.Yellow); output += ":"; - output += formatColorAndReset(`${ firstLineChar + 1 }`, ForegroundColorEscapeSequences.Yellow); + output += formatColorAndReset(`${firstLineChar + 1}`, ForegroundColorEscapeSequences.Yellow); output += " - "; } output += formatColorAndReset(diagnosticCategoryName(diagnostic), getCategoryFormat(diagnostic.category)); - output += formatColorAndReset(` TS${ diagnostic.code }: `, ForegroundColorEscapeSequences.Grey); + output += formatColorAndReset(` TS${diagnostic.code}: `, ForegroundColorEscapeSequences.Grey); output += flattenDiagnosticMessageText(diagnostic.messageText, host.getNewLine()); if (diagnostic.file) { @@ -477,6 +478,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. @@ -491,7 +502,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; @@ -526,6 +543,7 @@ namespace ts { performance.mark("beforeProgram"); host = host || createCompilerHost(options); + const configParsingHost = parseConfigHostFromCompilerHost(host); let skipDefaultLib = options.noLib; const getDefaultLibraryFileName = memoize(() => host.getDefaultLibFileName(options)); @@ -537,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[]; @@ -581,6 +600,23 @@ namespace ts { // used to track cases when two file names differ only in casing const filesByNameIgnoreCase = host.useCaseSensitiveFileNames() ? createMap() : undefined; + // A parallel array to projectReferences storing the results of reading in the referenced tsconfig files + const resolvedProjectReferences: (ResolvedProjectReference | undefined)[] | undefined = projectReferences ? [] : undefined; + const projectReferenceRedirects: Map = createMap(); + if (projectReferences) { + for (const ref of projectReferences) { + const parsedRef = parseProjectReferenceConfigFile(ref); + resolvedProjectReferences.push(parsedRef); + if (parsedRef) { + if (parsedRef.commandLine.options.outFile) { + const dtsOutfile = changeExtension(parsedRef.commandLine.options.outFile, ".d.ts"); + processSourceFile(dtsOutfile, /*isDefaultLib*/ false, /*packageId*/ undefined); + } + addProjectReferenceRedirects(parsedRef.commandLine, projectReferenceRedirects); + } + } + } + const shouldCreateNewSourceFile = shouldProgramCreateNewSourceFiles(oldProgram, options); const structuralIsReused = tryReuseStructureFromOldProgram(); if (structuralIsReused !== StructureIsReused.Completely) { @@ -669,6 +705,7 @@ namespace ts { isEmittedFile, getConfigFileParsingDiagnostics, getResolvedModuleWithFailedLookupLocationsFromCache, + getProjectReferences }; verifyCompilerOptions(); @@ -689,12 +726,18 @@ namespace ts { if (commonSourceDirectory === undefined) { const emittedFiles = filter(files, file => sourceFileMayBeEmitted(file, options, isSourceFileFromExternalLibrary)); if (options.rootDir && checkSourceFilesBelongToPath(emittedFiles, options.rootDir)) { - // If a rootDir is specified and is valid use it as the commonSourceDirectory + // 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)); + 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 @@ -896,6 +939,35 @@ namespace ts { return oldProgram.structureIsReused = StructureIsReused.Not; } + // Check if any referenced project tsconfig files are different + 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(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(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[] = []; const filePaths: Path[] = []; @@ -1083,6 +1155,7 @@ namespace ts { function getEmitHost(writeFileCallback?: WriteFileCallback): EmitHost { return { + getPrependNodes, getCanonicalFileName, getCommonSourceDirectory: program.getCommonSourceDirectory, getCompilerOptions: program.getCompilerOptions, @@ -1098,6 +1171,35 @@ namespace ts { }; } + function getProjectReferences() { + if (!resolvedProjectReferences) return; + + return resolvedProjectReferences; + } + + function getPrependNodes(): InputFiles[] { + if (!projectReferences) { + return emptyArray; + } + + const nodes: InputFiles[] = []; + for (let i = 0; i < projectReferences.length; i++) { + 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`; + const node = createInputFiles(js, dts); + nodes.push(node); + } + } + return nodes; + } + function isSourceFileFromExternalLibrary(file: SourceFile): boolean { return sourceFilesFoundSearchingNodeModules.get(file.path); } @@ -1723,7 +1825,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); @@ -1819,6 +1927,21 @@ 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); + } + } + // 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) { @@ -1851,6 +1974,10 @@ namespace ts { } filesByName.set(path, file); + if (redirectedPath) { + filesByName.set(redirectedPath, file); + } + if (file) { sourceFilesFoundSearchingNodeModules.set(path, currentNodeModulesDepth > 0); file.path = path; @@ -1888,6 +2015,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); @@ -1898,6 +2042,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++) { @@ -2061,6 +2209,31 @@ namespace ts { return allFilesBelongToPath; } + function parseProjectReferenceConfigFile(ref: ProjectReference): { commandLine: ParsedCommandLine, sourceFile: SourceFile } | undefined { + // The actual filename (i.e. add "/tsconfig.json" if necessary) + 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) as JsonSourceFile; + if (sourceFile === undefined) { + return undefined; + } + + const commandLine = parseJsonSourceFileConfigFileContent(sourceFile, configParsingHost, basePath, /*existingOptions*/ undefined, refPath); + return { commandLine, sourceFile }; + } + + function addProjectReferenceRedirects(referencedProject: ParsedCommandLine, target: Map) { + const rootDir = normalizePath(referencedProject.options.rootDir || getDirectoryPath(referencedProject.options.configFilePath)); + target.set(rootDir, getDeclarationOutputDirectory(referencedProject)); + } + + function getDeclarationOutputDirectory(proj: ParsedCommandLine) { + return proj.options.declarationDir || + proj.options.outDir || + getDirectoryPath(proj.options.configFilePath); + } + function verifyCompilerOptions() { if (options.strictPropertyInitialization && !options.strictNullChecks) { createDiagnosticForOptionName(Diagnostics.Option_0_cannot_be_specified_without_specifying_option_1, "strictPropertyInitialization", "strictNullChecks"); @@ -2097,6 +2270,47 @@ namespace ts { createDiagnosticForOptionName(Diagnostics.Option_paths_cannot_be_used_without_specifying_baseUrl_option, "paths"); } + if (options.composite) { + if (options.declaration === false) { + createDiagnosticForOptionName(Diagnostics.Composite_projects_may_not_disable_declaration_emit, "declaration"); + } + } + + if (projectReferences) { + for (let i = 0; i < projectReferences.length; i++) { + const ref = projectReferences[i]; + const resolvedRefOpts = resolvedProjectReferences[i] && resolvedProjectReferences[i].commandLine.options; + if (resolvedRefOpts === undefined) { + createDiagnosticForReference(i, Diagnostics.File_0_does_not_exist, ref.path); + continue; + } + if (!resolvedRefOpts.composite) { + createDiagnosticForReference(i, Diagnostics.Referenced_project_0_must_have_setting_composite_Colon_true, ref.path); + } + if (ref.prepend) { + 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 { + createDiagnosticForReference(i, Diagnostics.Cannot_prepend_project_0_because_it_does_not_have_outFile_set, ref.path); + } + } + } + } + + // 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 => 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)); + } + } + } + if (options.paths) { for (const key in options.paths) { if (!hasProperty(options.paths, key)) { @@ -2332,12 +2546,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) { @@ -2348,6 +2566,16 @@ namespace ts { createDiagnosticForOption(/*onKey*/ false, option1, /*option2*/ undefined, message, arg0); } + function createDiagnosticForReference(index: number, message: DiagnosticMessage, arg0?: string | number, arg1?: string | number) { + const referencesSyntax = getProjectReferencesSyntax(); + if (referencesSyntax) { + if (createOptionDiagnosticInArrayLiteralSyntax(referencesSyntax, index, message, arg0, arg1)) { + return; + } + } + 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) { const compilerOptionsObjectLiteralSyntax = getCompilerOptionsObjectLiteralSyntax(); const needCompilerDiagnostic = !compilerOptionsObjectLiteralSyntax || @@ -2358,6 +2586,22 @@ namespace ts { } } + function getProjectReferencesSyntax(): ArrayLiteralExpression | null { + if (_referencesArrayLiteralSyntax === undefined) { + _referencesArrayLiteralSyntax = null; // tslint:disable-line:no-null-keyword + if (options.configFile) { + const jsonObjectLiteral = getTsConfigObjectLiteralExpression(options.configFile); + for (const prop of getPropertyAssignment(jsonObjectLiteral, "references")) { + if (isArrayLiteralExpression(prop.initializer)) { + _referencesArrayLiteralSyntax = prop.initializer; + break; + } + } + } + } + return _referencesArrayLiteralSyntax; + } + function getCompilerOptionsObjectLiteralSyntax() { if (_compilerOptionsObjectLiteralSyntax === undefined) { _compilerOptionsObjectLiteralSyntax = null; // tslint:disable-line:no-null-keyword @@ -2382,6 +2626,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); @@ -2423,6 +2675,28 @@ namespace ts { } } + /* @internal */ + export function parseConfigHostFromCompilerHost(host: CompilerHost): ParseConfigFileHost { + return { + fileExists: f => host.fileExists(f), + readDirectory: (root, extensions, includes, depth?) => host.readDirectory ? host.readDirectory(root, extensions, includes, depth) : [], + readFile: f => host.readFile(f), + useCaseSensitiveFileNames: host.useCaseSensitiveFileNames(), + getCurrentDirectory: () => host.getCurrentDirectory(), + onUnRecoverableConfigFileDiagnostic: () => undefined + }; + } + + /** + * 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 ref.path; + } + /* @internal */ /** * Returns a DiagnosticMessage if we won't include a resolved module due to its extension. 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..0a7cf397dddef 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); } - )); + ), 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 d87712d451d4e..503d662ad5891 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 7c561e7d6960d..862b79dbf2416 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 13d2dff895a80..f8a6a6a2795d9 100644 --- a/src/compiler/transformers/utilities.ts +++ b/src/compiler/transformers/utilities.ts @@ -25,6 +25,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(map(node.sourceFiles, transformSourceFile), node.prepends); + } + } + export function getImportNeedsImportStarHelper(node: ImportDeclaration) { if (!!getNamespaceDeclarationNode(node)) { return true; diff --git a/src/compiler/tsc.ts b/src/compiler/tsc.ts index e45a95f7989df..7617fc62cb6f2 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 4f48fe96a8727..593e13a131f87 100644 --- a/src/compiler/types.ts +++ b/src/compiler/types.ts @@ -397,6 +397,8 @@ namespace ts { // Top-level nodes SourceFile, Bundle, + UnparsedSource, + InputFiles, // JSDoc nodes JSDocTypeExpression, @@ -2601,6 +2603,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[]; @@ -2614,12 +2622,24 @@ namespace ts { export interface Bundle extends Node { kind: SyntaxKind.Bundle; + prepends: ReadonlyArray; sourceFiles: ReadonlyArray; /* @internal */ syntheticFileReferences?: ReadonlyArray; /* @internal */ syntheticTypeReferences?: ReadonlyArray; /* @internal */ hasNoDefaultLib?: boolean; } + 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 { statements: NodeArray; } @@ -2751,6 +2771,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 */ @@ -4175,7 +4202,18 @@ namespace ts { name: string; } - export type CompilerOptionsValue = string | number | boolean | (string | number)[] | string[] | MapLike | PluginImport[] | null | undefined; + 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; export interface CompilerOptions { /*@internal*/ all?: boolean; @@ -4247,6 +4285,7 @@ namespace ts { /* @internal */ pretty?: boolean; reactNamespace?: string; jsxFactory?: string; + composite?: boolean; removeComments?: boolean; rootDir?: string; rootDirs?: string[]; @@ -4340,12 +4379,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 */ @@ -4353,6 +4393,7 @@ namespace ts { options: CompilerOptions; typeAcquisition?: TypeAcquisition; fileNames: string[]; + projectReferences?: ReadonlyArray; raw?: any; errors: Diagnostic[]; wildcardDirectories?: MapLike; @@ -4368,6 +4409,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 */ @@ -4383,10 +4425,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; @@ -4673,6 +4725,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 @@ -4911,6 +4964,8 @@ namespace ts { isEmitBlocked(emitFileName: string): boolean; + getPrependNodes(): ReadonlyArray; + writeFile: WriteFileCallback; } @@ -5062,7 +5117,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 22eb3d2c97c2b..7afc862bba483 100644 --- a/src/compiler/utilities.ts +++ b/src/compiler/utilities.ts @@ -2915,6 +2915,7 @@ namespace ts { sourceMapFilePath: string | undefined; declarationFilePath: string | undefined; declarationMapPath: string | undefined; + bundleInfoPath: string | undefined; } /** diff --git a/src/compiler/watch.ts b/src/compiler/watch.ts index 6b3adfcf3a4ca..3c5646cc48bff 100644 --- a/src/compiler/watch.ts +++ b/src/compiler/watch.ts @@ -86,13 +86,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; @@ -102,30 +95,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 */ @@ -297,7 +266,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; @@ -382,16 +350,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/fakes.ts b/src/harness/fakes.ts index c6d955e821590..3e450f90d5756 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/convertCompilerOptionsFromJson.ts b/src/harness/unittests/convertCompilerOptionsFromJson.ts index 84225a364cb4e..c120bfae8f9a6 100644 --- a/src/harness/unittests/convertCompilerOptionsFromJson.ts +++ b/src/harness/unittests/convertCompilerOptionsFromJson.ts @@ -14,7 +14,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..76ddd5fc29ea4 --- /dev/null +++ b/src/harness/unittests/projectReferences.ts @@ -0,0 +1,291 @@ +/// +/// + +namespace ts { + interface TestProjectSpecification { + configFileName?: string; + references?: ReadonlyArray; + 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: fakes.CompilerHost) => void) { + const files = createMap(); + for (const key in spec) { + const sp = spec[key]; + const configFileName = combineAllPaths("/", key, sp.configFileName || "tsconfig.json"); + const options = { + compilerOptions: { + composite: true, + outDir: "bin", + ...sp.options + }, + references: (sp.references || []).map(r => { + if (typeof r === "string") { + return { path: r }; + } + return r; + }), + ...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 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, parseConfigHostFromCompilerHost(host), getDirectoryPath(entryPointConfigFileName), {}, entryPointConfigFileName); + file.options.configFilePath = entryPointConfigFileName; + const prog = createProgram({ + rootNames: file.fileNames, + options: file.options, + host, + projectReferences: file.projectReferences + }); + 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()); + }); + }); + }); + + /** + * 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.Composite_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); + }); + }); + + 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); + }); + }); + }); + + /** + * 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: { + declaration: true, + outDir: "bin" + }, + references: [] + } + }; + testProjectReferences(spec, "/alpha/tsconfig.json", (program, host) => { + program.emit(); + assert.deepEqual(host.outputs.map(e => e.file).sort(), ["/alpha/bin/src/a.d.ts", "/alpha/bin/src/a.js"]); + }); + }); + }); + +} diff --git a/src/server/client.ts b/src/server/client.ts index 39231ae878d5a..a07e793dcba55 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/server/editorServices.ts b/src/server/editorServices.ts index 085ac1d8d7347..2bc61c6721017 100644 --- a/src/server/editorServices.ts +++ b/src/server/editorServices.ts @@ -1331,7 +1331,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 }; @@ -1464,7 +1465,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 @@ -1589,6 +1591,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 a97fb9a9bf0f2..328d41649ca27 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; @@ -1288,7 +1292,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, @@ -1330,6 +1335,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/protocol.ts b/src/server/protocol.ts index d78e3f6bb0fa1..3eaa42bb103dd 100644 --- a/src/server/protocol.ts +++ b/src/server/protocol.ts @@ -2734,6 +2734,7 @@ namespace ts.server.protocol { project?: string; reactNamespace?: string; removeComments?: boolean; + references?: ProjectReference[]; rootDir?: string; rootDirs?: string[]; skipLibCheck?: boolean; diff --git a/src/server/session.ts b/src/server/session.ts index bb635988755d5..8d6defa1562bf 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/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/getEditsForFileRename.ts b/src/services/getEditsForFileRename.ts index a5f1c5ed4f447..d5fc94c6d62ff 100644 --- a/src/services/getEditsForFileRename.ts +++ b/src/services/getEditsForFileRename.ts @@ -45,7 +45,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 }); } } diff --git a/src/services/services.ts b/src/services/services.ts index f17b7698a877c..700b07085a06e 100644 --- a/src/services/services.ts +++ b/src/services/services.ts @@ -878,6 +878,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); @@ -912,9 +916,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 { @@ -1241,7 +1254,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 @@ -1501,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+\/=]+)$)?/; @@ -2265,6 +2291,7 @@ namespace ts { getProgram, getApplicableRefactors, getEditsForRefactor, + toLineColumnOffset }; } diff --git a/src/services/types.ts b/src/services/types.ts index cd4a497f69484..d71b315f29a82 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; @@ -320,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; diff --git a/tests/baselines/reference/api/tsserverlibrary.d.ts b/tests/baselines/reference/api/tsserverlibrary.d.ts index b8cb75677cb72..1e2d3bbc60571 100644 --- a/tests/baselines/reference/api/tsserverlibrary.d.ts +++ b/tests/baselines/reference/api/tsserverlibrary.d.ts @@ -337,32 +337,34 @@ declare namespace ts { EnumMember = 272, SourceFile = 273, Bundle = 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, + UnparsedSource = 275, + 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, @@ -388,10 +390,10 @@ declare namespace ts { FirstBinaryOperator = 27, LastBinaryOperator = 70, FirstNode = 145, - FirstJSDocNode = 275, - LastJSDocNode = 293, - FirstJSDocTagNode = 285, - LastJSDocTagNode = 293 + FirstJSDocNode = 277, + LastJSDocNode = 295, + FirstJSDocTagNode = 287, + LastJSDocTagNode = 295 } enum NodeFlags { None = 0, @@ -1645,8 +1647,18 @@ declare namespace ts { } interface Bundle extends Node { kind: SyntaxKind.Bundle; + prepends: ReadonlyArray; sourceFiles: ReadonlyArray; } + interface InputFiles extends Node { + kind: SyntaxKind.InputFiles; + javascriptText: string; + declarationText: string; + } + interface UnparsedSource extends Node { + kind: SyntaxKind.UnparsedSource; + text: string; + } interface JsonSourceFile extends SourceFile { statements: NodeArray; } @@ -1716,6 +1728,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. */ @@ -2335,7 +2352,17 @@ declare namespace ts { interface PluginImport { name: string; } - type CompilerOptionsValue = string | number | boolean | (string | number)[] | string[] | MapLike | PluginImport[] | null | undefined; + 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 { allowJs?: boolean; allowSyntheticDefaultImports?: boolean; @@ -2391,6 +2418,7 @@ declare namespace ts { project?: string; reactNamespace?: string; jsxFactory?: string; + composite?: boolean; removeComments?: boolean; rootDir?: string; rootDirs?: string[]; @@ -2466,6 +2494,7 @@ declare namespace ts { ES2017 = 4, ES2018 = 5, ESNext = 6, + JSON = 100, Latest = 6 } enum LanguageVariant { @@ -2477,6 +2506,7 @@ declare namespace ts { options: CompilerOptions; typeAcquisition?: TypeAcquisition; fileNames: string[]; + projectReferences?: ReadonlyArray; raw?: any; errors: Diagnostic[]; wildcardDirectories?: MapLike; @@ -2488,8 +2518,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; @@ -2580,6 +2619,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 @@ -3746,8 +3786,10 @@ 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 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; function createImmediatelyInvokedArrowFunction(statements: ReadonlyArray): CallExpression; @@ -3954,6 +3996,7 @@ 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; } declare namespace ts { @@ -4093,7 +4136,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; @@ -4156,15 +4198,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 */ @@ -4211,6 +4244,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 @@ -4383,6 +4436,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; @@ -4460,6 +4514,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; @@ -5383,6 +5438,7 @@ declare namespace ts.server { configHasFilesProperty: boolean; configHasIncludeProperty: boolean; configHasExcludeProperty: boolean; + projectReferences: ReadonlyArray | undefined; /** * these fields can be present in the project file */ @@ -7562,6 +7618,7 @@ declare namespace ts.server.protocol { project?: string; reactNamespace?: string; removeComments?: boolean; + references?: ProjectReference[]; rootDir?: string; rootDirs?: string[]; skipLibCheck?: boolean; @@ -7758,6 +7815,7 @@ declare namespace ts.server { getCompilerOptions(): CompilerOptions; getNewLine(): string; getProjectVersion(): string; + getProjectReferences(): ReadonlyArray | undefined; getScriptFileNames(): string[]; private getOrCreateScriptInfoAndAttachToProject; getScriptKind(fileName: string): ScriptKind; @@ -7859,6 +7917,7 @@ declare namespace ts.server { */ class ConfiguredProject extends Project { compileOnSaveEnabled: boolean; + private projectReferences; private typeAcquisition; private directoriesWatchedForWildcards; readonly canonicalConfigFilePath: NormalizedPath; @@ -7871,6 +7930,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 ff0b1f65134fe..b6dbb1d54ddc8 100644 --- a/tests/baselines/reference/api/typescript.d.ts +++ b/tests/baselines/reference/api/typescript.d.ts @@ -337,32 +337,34 @@ declare namespace ts { EnumMember = 272, SourceFile = 273, Bundle = 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, + UnparsedSource = 275, + 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, @@ -388,10 +390,10 @@ declare namespace ts { FirstBinaryOperator = 27, LastBinaryOperator = 70, FirstNode = 145, - FirstJSDocNode = 275, - LastJSDocNode = 293, - FirstJSDocTagNode = 285, - LastJSDocTagNode = 293 + FirstJSDocNode = 277, + LastJSDocNode = 295, + FirstJSDocTagNode = 287, + LastJSDocTagNode = 295 } enum NodeFlags { None = 0, @@ -1645,8 +1647,18 @@ declare namespace ts { } interface Bundle extends Node { kind: SyntaxKind.Bundle; + prepends: ReadonlyArray; sourceFiles: ReadonlyArray; } + interface InputFiles extends Node { + kind: SyntaxKind.InputFiles; + javascriptText: string; + declarationText: string; + } + interface UnparsedSource extends Node { + kind: SyntaxKind.UnparsedSource; + text: string; + } interface JsonSourceFile extends SourceFile { statements: NodeArray; } @@ -1716,6 +1728,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. */ @@ -2335,7 +2352,17 @@ declare namespace ts { interface PluginImport { name: string; } - type CompilerOptionsValue = string | number | boolean | (string | number)[] | string[] | MapLike | PluginImport[] | null | undefined; + 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 { allowJs?: boolean; allowSyntheticDefaultImports?: boolean; @@ -2391,6 +2418,7 @@ declare namespace ts { project?: string; reactNamespace?: string; jsxFactory?: string; + composite?: boolean; removeComments?: boolean; rootDir?: string; rootDirs?: string[]; @@ -2466,6 +2494,7 @@ declare namespace ts { ES2017 = 4, ES2018 = 5, ESNext = 6, + JSON = 100, Latest = 6 } enum LanguageVariant { @@ -2477,6 +2506,7 @@ declare namespace ts { options: CompilerOptions; typeAcquisition?: TypeAcquisition; fileNames: string[]; + projectReferences?: ReadonlyArray; raw?: any; errors: Diagnostic[]; wildcardDirectories?: MapLike; @@ -2488,8 +2518,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; @@ -2580,6 +2619,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 @@ -3746,8 +3786,10 @@ 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 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; function createImmediatelyInvokedArrowFunction(statements: ReadonlyArray): CallExpression; @@ -3954,6 +3996,7 @@ 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; } declare namespace ts { @@ -4093,7 +4136,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; @@ -4156,15 +4198,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 */ @@ -4211,6 +4244,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 @@ -4383,6 +4436,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; @@ -4460,6 +4514,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->^^^^^^^^ 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/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/tsConfig/Default initialized TSConfig/tsconfig.json b/tests/baselines/reference/tsConfig/Default initialized TSConfig/tsconfig.json index 28ccc78540c7a..0b72c3fd8a779 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'. */ 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'. */ 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..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 @@ -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'. */ 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..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 @@ -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'. */ 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..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 @@ -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'. */ 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..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 @@ -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'. */ 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..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 @@ -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'. */ 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..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 @@ -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'. */ 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..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 @@ -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'. */