From 50eb5125664d47ac7cb59a2889236f200a4f4c7d Mon Sep 17 00:00:00 2001 From: Armando Aguirre Date: Thu, 5 Apr 2018 17:30:04 -0700 Subject: [PATCH] Added deferred ScriptKind and renamed JsFileExtensionInfo to FileExtensionInfo --- src/compiler/commandLineParser.ts | 10 +++---- src/compiler/core.ts | 28 +++++++++++-------- src/compiler/program.ts | 4 +-- src/compiler/types.ts | 9 ++++-- src/server/editorServices.ts | 16 +++++++++-- src/server/project.ts | 2 +- src/server/protocol.ts | 2 +- .../reference/api/tsserverlibrary.d.ts | 18 ++++++++---- tests/baselines/reference/api/typescript.d.ts | 13 ++++++--- 9 files changed, 67 insertions(+), 35 deletions(-) diff --git a/src/compiler/commandLineParser.ts b/src/compiler/commandLineParser.ts index f386c701dd7..9fa0c20c00b 100644 --- a/src/compiler/commandLineParser.ts +++ b/src/compiler/commandLineParser.ts @@ -1421,7 +1421,7 @@ namespace ts { * @param basePath A root directory to resolve relative path entries in the config * file to. e.g. outDir */ - export function parseJsonConfigFileContent(json: any, host: ParseConfigHost, basePath: string, existingOptions?: CompilerOptions, configFileName?: string, resolutionStack?: Path[], extraFileExtensions?: ReadonlyArray): ParsedCommandLine { + export function parseJsonConfigFileContent(json: any, host: ParseConfigHost, basePath: string, existingOptions?: CompilerOptions, configFileName?: string, resolutionStack?: Path[], extraFileExtensions?: ReadonlyArray): ParsedCommandLine { return parseJsonConfigFileContentWorker(json, /*sourceFile*/ undefined, host, basePath, existingOptions, configFileName, resolutionStack, extraFileExtensions); } @@ -1432,7 +1432,7 @@ namespace ts { * @param basePath A root directory to resolve relative path entries in the config * file to. e.g. outDir */ - export function parseJsonSourceFileConfigFileContent(sourceFile: JsonSourceFile, host: ParseConfigHost, basePath: string, existingOptions?: CompilerOptions, configFileName?: string, resolutionStack?: Path[], extraFileExtensions?: ReadonlyArray): ParsedCommandLine { + export function parseJsonSourceFileConfigFileContent(sourceFile: JsonSourceFile, host: ParseConfigHost, basePath: string, existingOptions?: CompilerOptions, configFileName?: string, resolutionStack?: Path[], extraFileExtensions?: ReadonlyArray): ParsedCommandLine { return parseJsonConfigFileContentWorker(/*json*/ undefined, sourceFile, host, basePath, existingOptions, configFileName, resolutionStack, extraFileExtensions); } @@ -1471,7 +1471,7 @@ namespace ts { existingOptions: CompilerOptions = {}, configFileName?: string, resolutionStack: Path[] = [], - extraFileExtensions: ReadonlyArray = [], + extraFileExtensions: ReadonlyArray = [], ): ParsedCommandLine { Debug.assert((json === undefined && sourceFile !== undefined) || (json !== undefined && sourceFile === undefined)); const errors: Diagnostic[] = []; @@ -2004,7 +2004,7 @@ namespace ts { options: CompilerOptions, host: ParseConfigHost, errors: Push, - extraFileExtensions: ReadonlyArray, + extraFileExtensions: ReadonlyArray, jsonSourceFile: JsonSourceFile ): ExpandResult { basePath = normalizePath(basePath); @@ -2042,7 +2042,7 @@ namespace ts { * @param extraFileExtensions optionaly file extra file extension information from host */ /* @internal */ - export function getFileNamesFromConfigSpecs(spec: ConfigFileSpecs, basePath: string, options: CompilerOptions, host: ParseConfigHost, extraFileExtensions: ReadonlyArray = []): ExpandResult { + export function getFileNamesFromConfigSpecs(spec: ConfigFileSpecs, basePath: string, options: CompilerOptions, host: ParseConfigHost, extraFileExtensions: ReadonlyArray = []): ExpandResult { basePath = normalizePath(basePath); const keyMapper = host.useCaseSensitiveFileNames ? identity : toLowerCase; diff --git a/src/compiler/core.ts b/src/compiler/core.ts index a75cc8cba0e..78e308761c8 100644 --- a/src/compiler/core.ts +++ b/src/compiler/core.ts @@ -2322,7 +2322,7 @@ namespace ts { } export function fileExtensionIs(path: string, extension: string): boolean { - return path.length > extension.length && endsWith(path, extension); + return path.length >= extension.length && endsWith(path, extension); } export function fileExtensionIsOneOf(path: string, extensions: ReadonlyArray): boolean { @@ -2666,16 +2666,22 @@ namespace ts { export const supportedJavascriptExtensions: ReadonlyArray = [Extension.Js, Extension.Jsx]; const allSupportedExtensions: ReadonlyArray = [...supportedTypeScriptExtensions, ...supportedJavascriptExtensions]; - export function getSupportedExtensions(options?: CompilerOptions, extraFileExtensions?: ReadonlyArray): ReadonlyArray { - const needAllExtensions = options && options.allowJs; - if (!extraFileExtensions || extraFileExtensions.length === 0 || !needAllExtensions) { - return needAllExtensions ? allSupportedExtensions : supportedTypeScriptExtensions; + export function getSupportedExtensions(options?: CompilerOptions, extraFileExtensions?: ReadonlyArray): ReadonlyArray { + const needJsExtensions = options && options.allowJs; + let extensions: string[] = needJsExtensions ? [...allSupportedExtensions] : [...supportedTypeScriptExtensions]; + + if (extraFileExtensions) { + extensions = [ + ...extensions, + ...extraFileExtensions.filter(x => x.scriptKind === ScriptKind.Deferred || needJsExtensions && isJavaScriptLike(x.scriptKind)).map(x => x.extension), + ]; } - return deduplicate( - [...allSupportedExtensions, ...extraFileExtensions.map(e => e.extension)], - equateStringsCaseSensitive, - compareStringsCaseSensitive - ); + + return deduplicate(extensions, equateStringsCaseSensitive, compareStringsCaseSensitive); + } + + function isJavaScriptLike(scriptKind: ScriptKind): boolean { + return scriptKind === ScriptKind.JS || scriptKind === ScriptKind.JSX; } export function hasJavaScriptFileExtension(fileName: string) { @@ -2686,7 +2692,7 @@ namespace ts { return forEach(supportedTypeScriptExtensions, extension => fileExtensionIs(fileName, extension)); } - export function isSupportedSourceFileName(fileName: string, compilerOptions?: CompilerOptions, extraFileExtensions?: ReadonlyArray) { + export function isSupportedSourceFileName(fileName: string, compilerOptions?: CompilerOptions, extraFileExtensions?: ReadonlyArray) { if (!fileName) { return false; } for (const extension of getSupportedExtensions(compilerOptions, extraFileExtensions)) { diff --git a/src/compiler/program.ts b/src/compiler/program.ts index 31220c68d3c..21ae663ba59 100755 --- a/src/compiler/program.ts +++ b/src/compiler/program.ts @@ -1299,9 +1299,9 @@ namespace ts { Debug.assert(!!sourceFile.bindDiagnostics); const isCheckJs = isCheckJsEnabledForFile(sourceFile, options); - // By default, only type-check .ts, .tsx, and 'External' files (external files are added by plugins) + // By default, only type-check .ts, .tsx, 'Deferred' and 'External' files (external files are added by plugins) const includeBindAndCheckDiagnostics = sourceFile.scriptKind === ScriptKind.TS || sourceFile.scriptKind === ScriptKind.TSX || - sourceFile.scriptKind === ScriptKind.External || isCheckJs; + sourceFile.scriptKind === ScriptKind.External || isCheckJs || sourceFile.scriptKind === ScriptKind.Deferred; const bindDiagnostics = includeBindAndCheckDiagnostics ? sourceFile.bindDiagnostics : emptyArray; const checkDiagnostics = includeBindAndCheckDiagnostics ? typeChecker.getDiagnostics(sourceFile, cancellationToken) : emptyArray; const fileProcessingDiagnosticsInFile = fileProcessingDiagnostics.getDiagnostics(sourceFile.fileName); diff --git a/src/compiler/types.ts b/src/compiler/types.ts index f820201063c..5f9475e7bb9 100644 --- a/src/compiler/types.ts +++ b/src/compiler/types.ts @@ -4058,7 +4058,7 @@ namespace ts { Prototype, } - export interface JsFileExtensionInfo { + export interface FileExtensionInfo { extension: string; isMixedContent: boolean; scriptKind?: ScriptKind; @@ -4265,7 +4265,12 @@ namespace ts { TS = 3, TSX = 4, External = 5, - JSON = 6 + JSON = 6, + /** + * Used on extensions that doesn't define the ScriptKind but the content defines it. + * Deferred extensions are going to be included in all project contexts. + */ + Deferred = 7 } export const enum ScriptTarget { diff --git a/src/server/editorServices.ts b/src/server/editorServices.ts index 09c5e784ba8..8907dd284ac 100644 --- a/src/server/editorServices.ts +++ b/src/server/editorServices.ts @@ -202,7 +202,7 @@ namespace ts.server { formatCodeOptions: FormatCodeSettings; preferences: UserPreferences; hostInfo: string; - extraFileExtensions?: JsFileExtensionInfo[]; + extraFileExtensions?: FileExtensionInfo[]; } export interface OpenConfiguredProjectResult { @@ -212,8 +212,8 @@ namespace ts.server { interface FilePropertyReader { getFileName(f: T): string; - getScriptKind(f: T, extraFileExtensions?: JsFileExtensionInfo[]): ScriptKind; - hasMixedContent(f: T, extraFileExtensions: JsFileExtensionInfo[]): boolean; + getScriptKind(f: T, extraFileExtensions?: FileExtensionInfo[]): ScriptKind; + hasMixedContent(f: T, extraFileExtensions: FileExtensionInfo[]): boolean; } const fileNamePropertyReader: FilePropertyReader = { @@ -2405,5 +2405,15 @@ namespace ts.server { this.createExternalProject(proj.projectFileName, rootFiles, proj.options, proj.typeAcquisition, excludedFiles); } } + + hasDeferredExtension() { + for (const extension of this.hostConfiguration.extraFileExtensions) { + if (extension.scriptKind === ScriptKind.Deferred) { + return true; + } + } + + return false; + } } } diff --git a/src/server/project.ts b/src/server/project.ts index 442c7e3f5ad..74ac7d32717 100644 --- a/src/server/project.ts +++ b/src/server/project.ts @@ -235,7 +235,7 @@ namespace ts.server { this.compilerOptions.allowNonTsExtensions = true; this.compilerOptions.allowJs = true; } - else if (hasExplicitListOfFiles || this.compilerOptions.allowJs) { + else if (hasExplicitListOfFiles || this.compilerOptions.allowJs || this.projectService.hasDeferredExtension()) { // If files are listed explicitly or allowJs is specified, allow all extensions this.compilerOptions.allowNonTsExtensions = true; } diff --git a/src/server/protocol.ts b/src/server/protocol.ts index 74b6865a3c3..3bbb6da05fa 100644 --- a/src/server/protocol.ts +++ b/src/server/protocol.ts @@ -1274,7 +1274,7 @@ namespace ts.server.protocol { /** * The host's additional supported .js file extensions */ - extraFileExtensions?: JsFileExtensionInfo[]; + extraFileExtensions?: FileExtensionInfo[]; } /** diff --git a/tests/baselines/reference/api/tsserverlibrary.d.ts b/tests/baselines/reference/api/tsserverlibrary.d.ts index 89c1b54eec5..a2e84b8bca0 100644 --- a/tests/baselines/reference/api/tsserverlibrary.d.ts +++ b/tests/baselines/reference/api/tsserverlibrary.d.ts @@ -2261,7 +2261,7 @@ declare namespace ts { AlwaysStrict = 64, PriorityImpliesCombination = 28 } - interface JsFileExtensionInfo { + interface FileExtensionInfo { extension: string; isMixedContent: boolean; scriptKind?: ScriptKind; @@ -2423,7 +2423,12 @@ declare namespace ts { TS = 3, TSX = 4, External = 5, - JSON = 6 + JSON = 6, + /** + * Used on extensions that doesn't define the ScriptKind but the content defines it. + * Deferred extensions are going to be included in all project contexts. + */ + Deferred = 7 } enum ScriptTarget { ES3 = 0, @@ -3384,7 +3389,7 @@ declare namespace ts { * @param basePath A root directory to resolve relative path entries in the config * file to. e.g. outDir */ - function parseJsonConfigFileContent(json: any, host: ParseConfigHost, basePath: string, existingOptions?: CompilerOptions, configFileName?: string, resolutionStack?: Path[], extraFileExtensions?: ReadonlyArray): ParsedCommandLine; + function parseJsonConfigFileContent(json: any, host: ParseConfigHost, basePath: string, existingOptions?: CompilerOptions, configFileName?: string, resolutionStack?: Path[], extraFileExtensions?: ReadonlyArray): ParsedCommandLine; /** * Parse the contents of a config file (tsconfig.json). * @param jsonNode The contents of the config file to parse @@ -3392,7 +3397,7 @@ declare namespace ts { * @param basePath A root directory to resolve relative path entries in the config * file to. e.g. outDir */ - function parseJsonSourceFileConfigFileContent(sourceFile: JsonSourceFile, host: ParseConfigHost, basePath: string, existingOptions?: CompilerOptions, configFileName?: string, resolutionStack?: Path[], extraFileExtensions?: ReadonlyArray): ParsedCommandLine; + function parseJsonSourceFileConfigFileContent(sourceFile: JsonSourceFile, host: ParseConfigHost, basePath: string, existingOptions?: CompilerOptions, configFileName?: string, resolutionStack?: Path[], extraFileExtensions?: ReadonlyArray): ParsedCommandLine; function convertCompilerOptionsFromJson(jsonOptions: any, basePath: string, configFileName?: string): { options: CompilerOptions; errors: Diagnostic[]; @@ -6016,7 +6021,7 @@ declare namespace ts.server.protocol { /** * The host's additional supported .js file extensions */ - extraFileExtensions?: JsFileExtensionInfo[]; + extraFileExtensions?: FileExtensionInfo[]; } /** * Configure request; value of command field is "configure". Specifies @@ -7850,7 +7855,7 @@ declare namespace ts.server { formatCodeOptions: FormatCodeSettings; preferences: UserPreferences; hostInfo: string; - extraFileExtensions?: JsFileExtensionInfo[]; + extraFileExtensions?: FileExtensionInfo[]; } interface OpenConfiguredProjectResult { configFileName?: NormalizedPath; @@ -8099,6 +8104,7 @@ declare namespace ts.server { resetSafeList(): void; applySafeList(proj: protocol.ExternalProject): NormalizedPath[]; openExternalProject(proj: protocol.ExternalProject): void; + hasDeferredExtension(): boolean; } } diff --git a/tests/baselines/reference/api/typescript.d.ts b/tests/baselines/reference/api/typescript.d.ts index dbb4d6cc7bf..90111f939da 100644 --- a/tests/baselines/reference/api/typescript.d.ts +++ b/tests/baselines/reference/api/typescript.d.ts @@ -2261,7 +2261,7 @@ declare namespace ts { AlwaysStrict = 64, PriorityImpliesCombination = 28 } - interface JsFileExtensionInfo { + interface FileExtensionInfo { extension: string; isMixedContent: boolean; scriptKind?: ScriptKind; @@ -2423,7 +2423,12 @@ declare namespace ts { TS = 3, TSX = 4, External = 5, - JSON = 6 + JSON = 6, + /** + * Used on extensions that doesn't define the ScriptKind but the content defines it. + * Deferred extensions are going to be included in all project contexts. + */ + Deferred = 7 } enum ScriptTarget { ES3 = 0, @@ -4201,7 +4206,7 @@ declare namespace ts { * @param basePath A root directory to resolve relative path entries in the config * file to. e.g. outDir */ - function parseJsonConfigFileContent(json: any, host: ParseConfigHost, basePath: string, existingOptions?: CompilerOptions, configFileName?: string, resolutionStack?: Path[], extraFileExtensions?: ReadonlyArray): ParsedCommandLine; + function parseJsonConfigFileContent(json: any, host: ParseConfigHost, basePath: string, existingOptions?: CompilerOptions, configFileName?: string, resolutionStack?: Path[], extraFileExtensions?: ReadonlyArray): ParsedCommandLine; /** * Parse the contents of a config file (tsconfig.json). * @param jsonNode The contents of the config file to parse @@ -4209,7 +4214,7 @@ declare namespace ts { * @param basePath A root directory to resolve relative path entries in the config * file to. e.g. outDir */ - function parseJsonSourceFileConfigFileContent(sourceFile: JsonSourceFile, host: ParseConfigHost, basePath: string, existingOptions?: CompilerOptions, configFileName?: string, resolutionStack?: Path[], extraFileExtensions?: ReadonlyArray): ParsedCommandLine; + function parseJsonSourceFileConfigFileContent(sourceFile: JsonSourceFile, host: ParseConfigHost, basePath: string, existingOptions?: CompilerOptions, configFileName?: string, resolutionStack?: Path[], extraFileExtensions?: ReadonlyArray): ParsedCommandLine; function convertCompilerOptionsFromJson(jsonOptions: any, basePath: string, configFileName?: string): { options: CompilerOptions; errors: Diagnostic[];