From 9e22efeb080376cafd1fc5d899a474158f67008e Mon Sep 17 00:00:00 2001 From: Ron Buckton Date: Thu, 11 May 2017 13:17:26 -0700 Subject: [PATCH 1/9] Add support for 'lib' reference directive --- src/compiler/checker.ts | 41 +------ src/compiler/commandLineParser.ts | 64 +++++----- src/compiler/comments.ts | 4 +- src/compiler/core.ts | 4 + src/compiler/diagnosticMessages.json | 8 ++ src/compiler/factory.ts | 1 + src/compiler/parser.ts | 5 + src/compiler/program.ts | 71 ++++++++--- src/compiler/types.ts | 11 ++ src/compiler/utilities.ts | 115 +++++++++++++++--- src/services/services.ts | 1 + .../reference/libReferenceDirective.js | 8 ++ .../reference/libReferenceDirective.symbols | 6 + .../reference/libReferenceDirective.types | 7 ++ .../libReferenceDirectiveErrors.errors.txt | 22 ++++ .../reference/libReferenceDirectiveErrors.js | 19 +++ tests/cases/compiler/libReferenceDirective.ts | 3 + .../compiler/libReferenceDirectiveErrors.ts | 11 ++ 18 files changed, 292 insertions(+), 109 deletions(-) create mode 100644 tests/baselines/reference/libReferenceDirective.js create mode 100644 tests/baselines/reference/libReferenceDirective.symbols create mode 100644 tests/baselines/reference/libReferenceDirective.types create mode 100644 tests/baselines/reference/libReferenceDirectiveErrors.errors.txt create mode 100644 tests/baselines/reference/libReferenceDirectiveErrors.js create mode 100644 tests/cases/compiler/libReferenceDirective.ts create mode 100644 tests/cases/compiler/libReferenceDirectiveErrors.ts diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index dfccfca1b8fb1..679b11ce53223 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -14278,46 +14278,7 @@ namespace ts { * Names longer than 30 characters don't get suggestions because Levenshtein distance is an n**2 algorithm. */ function getSpellingSuggestionForName(name: string, symbols: Symbol[], meaning: SymbolFlags): Symbol | undefined { - const worstDistance = name.length * 0.4; - const maximumLengthDifference = Math.min(3, name.length * 0.34); - let bestDistance = Number.MAX_VALUE; - let bestCandidate = undefined; - if (name.length > 30) { - return undefined; - } - name = name.toLowerCase(); - for (const candidate of symbols) { - if (candidate.flags & meaning && - candidate.name && - Math.abs(candidate.name.length - name.length) < maximumLengthDifference) { - const candidateName = candidate.name.toLowerCase(); - if (candidateName === name) { - return candidate; - } - if (candidateName.length < 3 || - name.length < 3 || - candidateName === "eval" || - candidateName === "intl" || - candidateName === "undefined" || - candidateName === "map" || - candidateName === "nan" || - candidateName === "set") { - continue; - } - const distance = levenshtein(name, candidateName); - if (distance > worstDistance) { - continue; - } - if (distance < 3) { - return candidate; - } - else if (distance < bestDistance) { - bestDistance = distance; - bestCandidate = candidate; - } - } - } - return bestCandidate; + return getSpellingSuggestion(name, symbols, ["eval", "intl", "undefined", "map", "set"], candidate => candidate.flags & meaning ? candidate.name : undefined); } function markPropertyAsReferenced(prop: Symbol) { diff --git a/src/compiler/commandLineParser.ts b/src/compiler/commandLineParser.ts index 9494098c072f3..c3b052a748852 100644 --- a/src/compiler/commandLineParser.ts +++ b/src/compiler/commandLineParser.ts @@ -7,6 +7,39 @@ namespace ts { /* @internal */ export const compileOnSaveCommandLineOption: CommandLineOption = { name: "compileOnSave", type: "boolean" }; + + /* @internal */ + export const libMap = createMapFromTemplate({ + // JavaScript only + "es5": "lib.es5.d.ts", + "es6": "lib.es2015.d.ts", + "es2015": "lib.es2015.d.ts", + "es7": "lib.es2016.d.ts", + "es2016": "lib.es2016.d.ts", + "es2017": "lib.es2017.d.ts", + "esnext": "lib.esnext.d.ts", + // Host only + "dom": "lib.dom.d.ts", + "dom.iterable": "lib.dom.iterable.d.ts", + "webworker": "lib.webworker.d.ts", + "scripthost": "lib.scripthost.d.ts", + // ES2015 Or ESNext By-feature options + "es2015.core": "lib.es2015.core.d.ts", + "es2015.collection": "lib.es2015.collection.d.ts", + "es2015.generator": "lib.es2015.generator.d.ts", + "es2015.iterable": "lib.es2015.iterable.d.ts", + "es2015.promise": "lib.es2015.promise.d.ts", + "es2015.proxy": "lib.es2015.proxy.d.ts", + "es2015.reflect": "lib.es2015.reflect.d.ts", + "es2015.symbol": "lib.es2015.symbol.d.ts", + "es2015.symbol.wellknown": "lib.es2015.symbol.wellknown.d.ts", + "es2016.array.include": "lib.es2016.array.include.d.ts", + "es2017.object": "lib.es2017.object.d.ts", + "es2017.sharedmemory": "lib.es2017.sharedmemory.d.ts", + "es2017.string": "lib.es2017.string.d.ts", + "esnext.asynciterable": "lib.esnext.asynciterable.d.ts", + }); + /* @internal */ export const optionDeclarations: CommandLineOption[] = [ // CommandLine only options @@ -111,36 +144,7 @@ namespace ts { type: "list", element: { name: "lib", - type: createMapFromTemplate({ - // JavaScript only - "es5": "lib.es5.d.ts", - "es6": "lib.es2015.d.ts", - "es2015": "lib.es2015.d.ts", - "es7": "lib.es2016.d.ts", - "es2016": "lib.es2016.d.ts", - "es2017": "lib.es2017.d.ts", - "esnext": "lib.esnext.d.ts", - // Host only - "dom": "lib.dom.d.ts", - "dom.iterable": "lib.dom.iterable.d.ts", - "webworker": "lib.webworker.d.ts", - "scripthost": "lib.scripthost.d.ts", - // ES2015 Or ESNext By-feature options - "es2015.core": "lib.es2015.core.d.ts", - "es2015.collection": "lib.es2015.collection.d.ts", - "es2015.generator": "lib.es2015.generator.d.ts", - "es2015.iterable": "lib.es2015.iterable.d.ts", - "es2015.promise": "lib.es2015.promise.d.ts", - "es2015.proxy": "lib.es2015.proxy.d.ts", - "es2015.reflect": "lib.es2015.reflect.d.ts", - "es2015.symbol": "lib.es2015.symbol.d.ts", - "es2015.symbol.wellknown": "lib.es2015.symbol.wellknown.d.ts", - "es2016.array.include": "lib.es2016.array.include.d.ts", - "es2017.object": "lib.es2017.object.d.ts", - "es2017.sharedmemory": "lib.es2017.sharedmemory.d.ts", - "es2017.string": "lib.es2017.string.d.ts", - "esnext.asynciterable": "lib.esnext.asynciterable.d.ts", - }), + type: libMap, }, showInSimplifiedHelpView: true, category: Diagnostics.Basic_Options, diff --git a/src/compiler/comments.ts b/src/compiler/comments.ts index c50cacc2ccb23..47b13cbd40c7d 100644 --- a/src/compiler/comments.ts +++ b/src/compiler/comments.ts @@ -419,9 +419,7 @@ namespace ts { commentPos + 2 < commentEnd && currentText.charCodeAt(commentPos + 2) === CharacterCodes.slash) { const textSubStr = currentText.substring(commentPos, commentEnd); - return textSubStr.match(fullTripleSlashReferencePathRegEx) || - textSubStr.match(fullTripleSlashAMDReferencePathRegEx) ? - true : false; + return isTripleSlashPathReferenceOrAmdDependency(textSubStr); } return false; } diff --git a/src/compiler/core.ts b/src/compiler/core.ts index 321cc06f7651d..1308b9b44c6f4 100644 --- a/src/compiler/core.ts +++ b/src/compiler/core.ts @@ -1097,6 +1097,10 @@ namespace ts { /** Does nothing. */ export function noop(): void {} + export function identity(value: T): T { + return value; + } + /** Throws an error because a function is not implemented. */ export function notImplemented(): never { throw new Error("Not implemented"); diff --git a/src/compiler/diagnosticMessages.json b/src/compiler/diagnosticMessages.json index c520290acbd84..f720e47d2efc9 100644 --- a/src/compiler/diagnosticMessages.json +++ b/src/compiler/diagnosticMessages.json @@ -2131,6 +2131,14 @@ "category": "Error", "code": 2710 }, + "Cannot find lib definition for '{0}'.": { + "category": "Error", + "code": 2711 + }, + "Cannot find lib definition for '{0}'. Did you mean '{1}'?": { + "category": "Error", + "code": 2712 + }, "Import declaration '{0}' is using private name '{1}'.": { "category": "Error", diff --git a/src/compiler/factory.ts b/src/compiler/factory.ts index 2b781b6dbb3ce..dcfa33a6d5bef 100644 --- a/src/compiler/factory.ts +++ b/src/compiler/factory.ts @@ -1978,6 +1978,7 @@ namespace ts { if (node.moduleName !== undefined) updated.moduleName = node.moduleName; if (node.referencedFiles !== undefined) updated.referencedFiles = node.referencedFiles; if (node.typeReferenceDirectives !== undefined) updated.typeReferenceDirectives = node.typeReferenceDirectives; + if (node.libReferenceDirectives !== undefined) updated.libReferenceDirectives = node.libReferenceDirectives; if (node.languageVariant !== undefined) updated.languageVariant = node.languageVariant; if (node.isDeclarationFile !== undefined) updated.isDeclarationFile = node.isDeclarationFile; if (node.renamedDependencies !== undefined) updated.renamedDependencies = node.renamedDependencies; diff --git a/src/compiler/parser.ts b/src/compiler/parser.ts index cc3fb24bdb86d..b611f30766cfe 100644 --- a/src/compiler/parser.ts +++ b/src/compiler/parser.ts @@ -5828,6 +5828,7 @@ namespace ts { const triviaScanner = createScanner(sourceFile.languageVersion, /*skipTrivia*/ false, LanguageVariant.Standard, sourceText); const referencedFiles: FileReference[] = []; const typeReferenceDirectives: FileReference[] = []; + const libReferenceDirectives: FileReference[] = []; const amdDependencies: { path: string; name: string }[] = []; let amdModuleName: string; let checkJsDirective: CheckJsDirective = undefined; @@ -5862,6 +5863,9 @@ namespace ts { if (referencePathMatchResult.isTypeReferenceDirective) { typeReferenceDirectives.push(fileReference); } + else if (referencePathMatchResult.isLibReferenceDirective) { + libReferenceDirectives.push(fileReference); + } else { referencedFiles.push(fileReference); } @@ -5907,6 +5911,7 @@ namespace ts { sourceFile.referencedFiles = referencedFiles; sourceFile.typeReferenceDirectives = typeReferenceDirectives; + sourceFile.libReferenceDirectives = libReferenceDirectives; sourceFile.amdDependencies = amdDependencies; sourceFile.moduleName = amdModuleName; sourceFile.checkJsDirective = checkJsDirective; diff --git a/src/compiler/program.ts b/src/compiler/program.ts index 7893fee651e2d..9b4b56fbfa5c8 100644 --- a/src/compiler/program.ts +++ b/src/compiler/program.ts @@ -295,6 +295,7 @@ namespace ts { let program: Program; let files: SourceFile[] = []; let commonSourceDirectory: string; + let libDirectory: string; let diagnosticsProducingTypeChecker: TypeChecker; let noDiagnosticsTypeChecker: TypeChecker; let classifiableNames: Map; @@ -370,7 +371,7 @@ namespace ts { const structuralIsReused = tryReuseStructureFromOldProgram(); if (structuralIsReused !== StructureIsReused.Completely) { - forEach(rootNames, name => processRootFile(name, /*isDefaultLib*/ false)); + forEach(rootNames, name => processRootFile(name, /*isDefaultLib*/ false, /*ignoreNoDefaultLib*/ false)); // load type declarations specified via 'types' argument or implicitly from types/ and node_modules/@types folders const typeReferences: string[] = getAutomaticTypeDirectiveNames(options, host); @@ -393,12 +394,12 @@ namespace ts { // If '--lib' is not specified, include default library file according to '--target' // otherwise, using options specified in '--lib' instead of '--target' default library file if (!options.lib) { - processRootFile(host.getDefaultLibFileName(options), /*isDefaultLib*/ true); + processRootFile(host.getDefaultLibFileName(options), /*isDefaultLib*/ true, /*ignoreNoDefaultLib*/ false); } else { - const libDirectory = host.getDefaultLibLocation ? host.getDefaultLibLocation() : getDirectoryPath(host.getDefaultLibFileName(options)); + const libDirectory = getLibDirectory(); forEach(options.lib, libFileName => { - processRootFile(combinePaths(libDirectory, libFileName), /*isDefaultLib*/ true); + processRootFile(combinePaths(libDirectory, libFileName), /*isDefaultLib*/ true, /*ignoreNoDefaultLib*/ false); }); } } @@ -463,6 +464,13 @@ namespace ts { return commonSourceDirectory; } + function getLibDirectory() { + if (libDirectory === undefined) { + libDirectory = host.getDefaultLibLocation ? host.getDefaultLibLocation() : getDirectoryPath(host.getDefaultLibFileName(options)); + } + return libDirectory; + } + function getClassifiableNames() { if (!classifiableNames) { // Initialize a checker so that all our files are bound. @@ -671,6 +679,11 @@ namespace ts { if (oldSourceFile !== newSourceFile) { // The `newSourceFile` object was created for the new program. + if (!arrayIsEqualTo(oldSourceFile.libReferenceDirectives, newSourceFile.libReferenceDirectives, fileReferenceIsEqualTo)) { + // 'lib' references has changed, matches behavior in changesAffectModuleResolution + return oldProgram.structureIsReused = StructureIsReused.Not; + } + if (oldSourceFile.hasNoDefaultLib !== newSourceFile.hasNoDefaultLib) { // value of no-default-lib has changed // this will affect if default library is injected into the list of files @@ -1230,8 +1243,8 @@ namespace ts { return sortAndDeduplicateDiagnostics(allDiagnostics); } - function processRootFile(fileName: string, isDefaultLib: boolean) { - processSourceFile(normalizePath(fileName), isDefaultLib); + function processRootFile(fileName: string, isDefaultLib: boolean, ignoreNoDefaultLib: boolean) { + processSourceFile(normalizePath(fileName), isDefaultLib, ignoreNoDefaultLib); } function fileReferenceIsEqualTo(a: FileReference, b: FileReference): boolean { @@ -1348,7 +1361,7 @@ namespace ts { } } - function processSourceFile(fileName: string, isDefaultLib: boolean, refFile?: SourceFile, refPos?: number, refEnd?: number) { + function processSourceFile(fileName: string, isDefaultLib: boolean, ignoreNoDefaultLib: boolean, refFile?: SourceFile, refPos?: number, refEnd?: number) { let diagnosticArgument: string[]; let diagnostic: DiagnosticMessage; if (hasExtension(fileName)) { @@ -1356,7 +1369,7 @@ namespace ts { diagnostic = Diagnostics.File_0_has_unsupported_extension_The_only_supported_extensions_are_1; diagnosticArgument = [fileName, "'" + supportedExtensions.join("', '") + "'"]; } - else if (!findSourceFile(fileName, toPath(fileName, currentDirectory, getCanonicalFileName), isDefaultLib, refFile, refPos, refEnd)) { + else if (!findSourceFile(fileName, toPath(fileName, currentDirectory, getCanonicalFileName), isDefaultLib, ignoreNoDefaultLib, refFile, refPos, refEnd)) { diagnostic = Diagnostics.File_0_not_found; diagnosticArgument = [fileName]; } @@ -1366,13 +1379,13 @@ namespace ts { } } else { - const nonTsFile: SourceFile = options.allowNonTsExtensions && findSourceFile(fileName, toPath(fileName, currentDirectory, getCanonicalFileName), isDefaultLib, refFile, refPos, refEnd); + const nonTsFile: SourceFile = options.allowNonTsExtensions && findSourceFile(fileName, toPath(fileName, currentDirectory, getCanonicalFileName), isDefaultLib, ignoreNoDefaultLib, refFile, refPos, refEnd); if (!nonTsFile) { if (options.allowNonTsExtensions) { diagnostic = Diagnostics.File_0_not_found; diagnosticArgument = [fileName]; } - else if (!forEach(supportedExtensions, extension => findSourceFile(fileName + extension, toPath(fileName + extension, currentDirectory, getCanonicalFileName), isDefaultLib, refFile, refPos, refEnd))) { + else if (!forEach(supportedExtensions, extension => findSourceFile(fileName + extension, toPath(fileName + extension, currentDirectory, getCanonicalFileName), isDefaultLib, ignoreNoDefaultLib, refFile, refPos, refEnd))) { diagnostic = Diagnostics.File_0_not_found; fileName += ".ts"; diagnosticArgument = [fileName]; @@ -1401,7 +1414,7 @@ namespace ts { } // Get source file from normalized fileName - function findSourceFile(fileName: string, path: Path, isDefaultLib: boolean, refFile?: SourceFile, refPos?: number, refEnd?: number): SourceFile { + function findSourceFile(fileName: string, path: Path, isDefaultLib: boolean, ignoreNoDefaultLib: boolean, refFile?: SourceFile, refPos?: number, refEnd?: number): SourceFile { if (filesByName.contains(path)) { const file = filesByName.get(path); // try to check if we've already seen this file but with a different casing in path @@ -1416,6 +1429,7 @@ namespace ts { sourceFilesFoundSearchingNodeModules.set(file.path, false); if (!options.noResolve) { processReferencedFiles(file, isDefaultLib); + processLibReferenceDirectives(file); processTypeReferenceDirectives(file); } @@ -1460,10 +1474,11 @@ namespace ts { } } - skipDefaultLib = skipDefaultLib || file.hasNoDefaultLib; + skipDefaultLib = skipDefaultLib || (file.hasNoDefaultLib && !ignoreNoDefaultLib); if (!options.noResolve) { processReferencedFiles(file, isDefaultLib); + processLibReferenceDirectives(file); processTypeReferenceDirectives(file); } @@ -1484,7 +1499,7 @@ namespace ts { function processReferencedFiles(file: SourceFile, isDefaultLib: boolean) { forEach(file.referencedFiles, ref => { const referencedFileName = resolveTripleslashReference(ref.fileName, file.fileName); - processSourceFile(referencedFileName, isDefaultLib, file, ref.pos, ref.end); + processSourceFile(referencedFileName, isDefaultLib, /*ignoreNoDefaultLib*/ false, file, ref.pos, ref.end); }); } @@ -1515,7 +1530,7 @@ namespace ts { if (resolvedTypeReferenceDirective) { if (resolvedTypeReferenceDirective.primary) { // resolved from the primary path - processSourceFile(resolvedTypeReferenceDirective.resolvedFileName, /*isDefaultLib*/ false, refFile, refPos, refEnd); + processSourceFile(resolvedTypeReferenceDirective.resolvedFileName, /*isDefaultLib*/ false, /*ignoreNoDefaultLib*/ false, refFile, refPos, refEnd); } else { // If we already resolved to this file, it must have been a secondary reference. Check file contents @@ -1538,7 +1553,7 @@ namespace ts { } else { // First resolution of this library - processSourceFile(resolvedTypeReferenceDirective.resolvedFileName, /*isDefaultLib*/ false, refFile, refPos, refEnd); + processSourceFile(resolvedTypeReferenceDirective.resolvedFileName, /*isDefaultLib*/ false, /*ignoreNoDefaultLib*/ false, refFile, refPos, refEnd); } } } @@ -1551,6 +1566,30 @@ namespace ts { } } + function processLibReferenceDirectives(file: SourceFile) { + const libDirectory = getLibDirectory(); + forEach(file.libReferenceDirectives, libReference => { + const libName = libReference.fileName.toLocaleLowerCase(); + const libFileName = libMap.get(libName); + if (libFileName) { + // we ignore any 'no-default-lib' reference set on this import. + processRootFile(combinePaths(libDirectory, libFileName), /*isDefaultLib*/ true, /*ignoreNoDefaultLib*/ false); + } + else { + let unqualifiedLibName = libName; + if (startsWith(unqualifiedLibName, "lib.")) { + unqualifiedLibName = unqualifiedLibName.substr(4); + } + if (endsWith(unqualifiedLibName, ".d.ts")) { + unqualifiedLibName = removeExtension(unqualifiedLibName, ".d.ts"); + } + const suggestion = getSpellingSuggestion(unqualifiedLibName, arrayFrom(libMap.keys())); + const message = suggestion ? Diagnostics.Cannot_find_lib_definition_for_0_Did_you_mean_1 : Diagnostics.Cannot_find_lib_definition_for_0; + fileProcessingDiagnostics.add(createDiagnostic(file, libReference.pos, libReference.end, message, libName, suggestion)); + } + }); + } + function createDiagnostic(refFile: SourceFile, refPos: number, refEnd: number, message: DiagnosticMessage, ...args: any[]): Diagnostic { if (refFile === undefined || refPos === undefined || refEnd === undefined) { return createCompilerDiagnostic(message, ...args); @@ -1605,7 +1644,7 @@ namespace ts { else if (shouldAddFile) { const path = toPath(resolvedFileName, currentDirectory, getCanonicalFileName); const pos = skipTrivia(file.text, file.imports[i].pos); - findSourceFile(resolvedFileName, path, /*isDefaultLib*/ false, file, pos, file.imports[i].end); + findSourceFile(resolvedFileName, path, /*isDefaultLib*/ false, /*ignoreNoDefaultLib*/ false, file, pos, file.imports[i].end); } if (isFromNodeModulesSearch) { diff --git a/src/compiler/types.ts b/src/compiler/types.ts index 74670352c34cd..b02edd07b3120 100644 --- a/src/compiler/types.ts +++ b/src/compiler/types.ts @@ -10,13 +10,23 @@ namespace ts { /** ES6 Map interface. */ export interface Map { + readonly size: number; get(key: string): T | undefined; has(key: string): boolean; set(key: string, value: T): this; delete(key: string): boolean; clear(): void; forEach(action: (value: T, key: string) => void): void; + keys(): Iterator; + values(): Iterator; + entries(): Iterator<[string, T]>; + } + + export interface ReadonlyMap { readonly size: number; + get(key: string): T | undefined; + has(key: string): boolean; + forEach(action: (value: T, key: string) => void): void; keys(): Iterator; values(): Iterator; entries(): Iterator<[string, T]>; @@ -2262,6 +2272,7 @@ namespace ts { moduleName: string; referencedFiles: FileReference[]; typeReferenceDirectives: FileReference[]; + libReferenceDirectives: FileReference[]; languageVariant: LanguageVariant; isDeclarationFile: boolean; diff --git a/src/compiler/utilities.ts b/src/compiler/utilities.ts index 460a9860f1e0a..300c42cae3687 100644 --- a/src/compiler/utilities.ts +++ b/src/compiler/utilities.ts @@ -9,6 +9,7 @@ namespace ts { diagnosticMessage?: DiagnosticMessage; isNoDefaultLib?: boolean; isTypeReferenceDirective?: boolean; + isLibReferenceDirective?: boolean; } export function getDeclarationOfKind(symbol: Symbol, kind: SyntaxKind): Declaration { @@ -637,10 +638,6 @@ namespace ts { text.charCodeAt(comment.pos + 3) !== CharacterCodes.slash); } - export let fullTripleSlashReferencePathRegEx = /^(\/\/\/\s*/; - export let fullTripleSlashReferenceTypeReferenceDirectiveRegEx = /^(\/\/\/\s*/; - export let fullTripleSlashAMDReferencePathRegEx = /^(\/\/\/\s*/; - export function isPartOfTypeNode(node: Node): boolean { if (SyntaxKind.FirstTypeNode <= node.kind && node.kind <= SyntaxKind.LastTypeNode) { return true; @@ -1542,7 +1539,7 @@ namespace ts { return node && firstOrUndefined(getJSDocTags(node, kind)); } - export function getJSDocs(node: Node): (JSDoc | JSDocTag)[] { + export function getJSDocs(node: Node): (JSDoc | JSDocTag)[] { let cache: (JSDoc | JSDocTag)[] = node.jsDocCache; if (!cache) { getJSDocsWorker(node); @@ -1913,39 +1910,58 @@ namespace ts { return undefined; } + const simpleReferenceRegEx = /^\/\/\/\s*/i; + + const fullTripleSlashReferencePathOrAmdDependencyPathRegEx = /^\/\/\/\s*<(reference|amd-dependency)\s+path\s*=\s*('|")(.+?)\2.*?\/>/; + + export function isTripleSlashPathReferenceOrAmdDependency(comment: string) { + return fullTripleSlashReferencePathOrAmdDependencyPathRegEx.test(comment); + } + export function getFileReferenceFromReferencePath(comment: string, commentRange: CommentRange): ReferencePathMatchResult { - const simpleReferenceRegEx = /^\/\/\/\s*/gim; if (simpleReferenceRegEx.test(comment)) { - if (isNoDefaultLibRegEx.test(comment)) { - return { - isNoDefaultLib: true - }; - } - else { - const refMatchResult = fullTripleSlashReferencePathRegEx.exec(comment); - const refLibResult = !refMatchResult && fullTripleSlashReferenceTypeReferenceDirectiveRegEx.exec(comment); - if (refMatchResult || refLibResult) { + const match = fullTripleSlashReferenceRegEx.exec(comment); + if (match) { + const name = match[1]; + if (name === "no-default-lib") { + return { + isNoDefaultLib: true + }; + } + else if (name === "path" || name === "types" || name === "lib") { + const value = match[3]; const start = commentRange.pos; const end = commentRange.end; return { fileReference: { pos: start, end: end, - fileName: (refMatchResult || refLibResult)[3] + fileName: value }, isNoDefaultLib: false, - isTypeReferenceDirective: !!refLibResult + isTypeReferenceDirective: name === "types", + isLibReferenceDirective: name === "lib" }; } - + } + else { return { diagnosticMessage: Diagnostics.Invalid_reference_directive_syntax, isNoDefaultLib: false }; } } - return undefined; } @@ -4242,6 +4258,65 @@ namespace ts { // Firefox has Object.prototype.watch return options.watch && options.hasOwnProperty("watch"); } + + /** + * Given a name and a list of names are *not* equal to the name, return a spelling suggestion if there is one that is close enough. + * Names less than length 3 only check for case-insensitive equality, not Levenshtein distance. + */ + export function getSpellingSuggestion(name: string, choices: string[]): string | undefined; + + /** + * Given a name and a list of names are *not* equal to the name, return a spelling suggestion if there is one that is close enough. + * Names less than length 3 only check for case-insensitive equality, not Levenshtein distance. + */ + export function getSpellingSuggestion(name: string, choices: T[], exclusions: string[] | undefined, getName: (candidate: T) => string | undefined): T | undefined; + export function getSpellingSuggestion(name: string, choices: T[], exclusions?: string[], getName: (candidate: T) => string | undefined = identity): T | undefined { + // If there is a candidate that's the same except for case, return that. + // If there is a candidate that's within one edit of the name, return that. + // Otherwise, return the candidate with the smallest Levenshtein distance, + // except for candidates: + // * With no name + // * Whose length differs from the target name by more than 0.3 of the length of the name. + // * Whose levenshtein distance is more than 0.4 of the length of the name + // (0.4 allows 1 substitution/transposition for every 5 characters, + // and 1 insertion/deletion at 3 characters) + // Names longer than 30 characters don't get suggestions because Levenshtein distance is an n**2 algorithm. + const worstDistance = name.length * 0.4; + const maximumLengthDifference = Math.min(3, name.length * 0.34); + let bestDistance = Number.MAX_VALUE; + let bestCandidate = undefined; + if (name.length > 30) { + return undefined; + } + name = name.toLowerCase(); + for (const candidate of choices) { + let candidateName = getName(candidate); + if (candidateName && + Math.abs(candidateName.length - name.length) < maximumLengthDifference) { + candidateName = candidateName.toLowerCase(); + if (candidateName === name) { + return candidate; + } + if (candidateName.length < 3 || + name.length < 3 || + contains(exclusions, candidateName)) { + continue; + } + const distance = levenshtein(name, candidateName); + if (distance > worstDistance) { + continue; + } + if (distance < 3) { + return candidate; + } + else if (distance < bestDistance) { + bestDistance = distance; + bestCandidate = candidate; + } + } + } + return bestCandidate; + } } namespace ts { diff --git a/src/services/services.ts b/src/services/services.ts index 1b4678b5dbc7a..c39f9e37bba4e 100644 --- a/src/services/services.ts +++ b/src/services/services.ts @@ -482,6 +482,7 @@ namespace ts { public moduleName: string; public referencedFiles: FileReference[]; public typeReferenceDirectives: FileReference[]; + public libReferenceDirectives: FileReference[]; public syntacticDiagnostics: Diagnostic[]; public referenceDiagnostics: Diagnostic[]; diff --git a/tests/baselines/reference/libReferenceDirective.js b/tests/baselines/reference/libReferenceDirective.js new file mode 100644 index 0000000000000..8be9a68a8a906 --- /dev/null +++ b/tests/baselines/reference/libReferenceDirective.js @@ -0,0 +1,8 @@ +//// [libReferenceDirective.ts] +/// +const m = new Map(); + + +//// [libReferenceDirective.js] +/// +var m = new Map(); diff --git a/tests/baselines/reference/libReferenceDirective.symbols b/tests/baselines/reference/libReferenceDirective.symbols new file mode 100644 index 0000000000000..564506ee0c24b --- /dev/null +++ b/tests/baselines/reference/libReferenceDirective.symbols @@ -0,0 +1,6 @@ +=== tests/cases/compiler/libReferenceDirective.ts === +/// +const m = new Map(); +>m : Symbol(m, Decl(libReferenceDirective.ts, 1, 5)) +>Map : Symbol(Map, Decl(lib.es2015.symbol.wellknown.d.ts, --, --), Decl(lib.es2015.iterable.d.ts, --, --), Decl(lib.es2015.collection.d.ts, --, --), Decl(lib.es2015.collection.d.ts, --, --)) + diff --git a/tests/baselines/reference/libReferenceDirective.types b/tests/baselines/reference/libReferenceDirective.types new file mode 100644 index 0000000000000..5eef516b30946 --- /dev/null +++ b/tests/baselines/reference/libReferenceDirective.types @@ -0,0 +1,7 @@ +=== tests/cases/compiler/libReferenceDirective.ts === +/// +const m = new Map(); +>m : Map +>new Map() : Map +>Map : MapConstructor + diff --git a/tests/baselines/reference/libReferenceDirectiveErrors.errors.txt b/tests/baselines/reference/libReferenceDirectiveErrors.errors.txt new file mode 100644 index 0000000000000..1d2327480e0fa --- /dev/null +++ b/tests/baselines/reference/libReferenceDirectiveErrors.errors.txt @@ -0,0 +1,22 @@ +tests/cases/compiler/a.ts(1,1): error TS1084: Invalid 'reference' directive syntax. +tests/cases/compiler/b.ts(1,1): error TS1084: Invalid 'reference' directive syntax. +tests/cases/compiler/c.ts(1,1): error TS2711: Cannot find lib definition for 'es2015.foo'. +tests/cases/compiler/d.ts(1,1): error TS2712: Cannot find lib definition for 'es2015.collections'. Did you mean 'es2015.collection'? + + +==== tests/cases/compiler/a.ts (1 errors) ==== + /// + ~~~~~~~~~~~~~~~~~ +!!! error TS1084: Invalid 'reference' directive syntax. +==== tests/cases/compiler/b.ts (1 errors) ==== + /// + ~~~~~~~~~~~~~~~~~~~~~~~~ +!!! error TS1084: Invalid 'reference' directive syntax. +==== tests/cases/compiler/c.ts (1 errors) ==== + /// + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +!!! error TS2711: Cannot find lib definition for 'es2015.foo'. +==== tests/cases/compiler/d.ts (1 errors) ==== + /// + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +!!! error TS2712: Cannot find lib definition for 'es2015.collections'. Did you mean 'es2015.collection'? \ No newline at end of file diff --git a/tests/baselines/reference/libReferenceDirectiveErrors.js b/tests/baselines/reference/libReferenceDirectiveErrors.js new file mode 100644 index 0000000000000..fb25be159c167 --- /dev/null +++ b/tests/baselines/reference/libReferenceDirectiveErrors.js @@ -0,0 +1,19 @@ +//// [tests/cases/compiler/libReferenceDirectiveErrors.ts] //// + +//// [a.ts] +/// +//// [b.ts] +/// +//// [c.ts] +/// +//// [d.ts] +/// + +//// [a.js] +/// +//// [b.js] +/// +//// [c.js] +/// +//// [d.js] +/// diff --git a/tests/cases/compiler/libReferenceDirective.ts b/tests/cases/compiler/libReferenceDirective.ts new file mode 100644 index 0000000000000..be1250873129d --- /dev/null +++ b/tests/cases/compiler/libReferenceDirective.ts @@ -0,0 +1,3 @@ +// @target: es5 +/// +const m = new Map(); diff --git a/tests/cases/compiler/libReferenceDirectiveErrors.ts b/tests/cases/compiler/libReferenceDirectiveErrors.ts new file mode 100644 index 0000000000000..a40739b5c08c7 --- /dev/null +++ b/tests/cases/compiler/libReferenceDirectiveErrors.ts @@ -0,0 +1,11 @@ +// @target: es5 +// @filename: a.ts +/// +// @filename: b.ts +/// +// @filename: c.ts +/// +// @filename: d.ts +/// +// @filename: e.ts +/// \ No newline at end of file From f61beb1911580b8a23ed12f710caf45e5275c239 Mon Sep 17 00:00:00 2001 From: Ron Buckton Date: Thu, 11 May 2017 15:24:28 -0700 Subject: [PATCH 2/9] Update baseline, PR feedback --- src/compiler/commandLineParser.ts | 3 + src/compiler/parser.ts | 32 ++++---- src/compiler/program.ts | 2 +- src/compiler/types.ts | 10 --- src/compiler/utilities.ts | 78 +++++++++---------- src/services/preProcess.ts | 20 +++-- .../libReferenceDirectiveErrors.errors.txt | 7 +- .../reference/libReferenceDirectiveErrors.js | 4 + 8 files changed, 81 insertions(+), 75 deletions(-) diff --git a/src/compiler/commandLineParser.ts b/src/compiler/commandLineParser.ts index c3b052a748852..241e38b733f94 100644 --- a/src/compiler/commandLineParser.ts +++ b/src/compiler/commandLineParser.ts @@ -40,6 +40,9 @@ namespace ts { "esnext.asynciterable": "lib.esnext.asynciterable.d.ts", }); + /* @internal */ + export const libs = arrayFrom(libMap.keys()); + /* @internal */ export const optionDeclarations: CommandLineOption[] = [ // CommandLine only options diff --git a/src/compiler/parser.ts b/src/compiler/parser.ts index b611f30766cfe..51091d3e3f16e 100644 --- a/src/compiler/parser.ts +++ b/src/compiler/parser.ts @@ -5856,22 +5856,22 @@ namespace ts { const comment = sourceText.substring(range.pos, range.end); const referencePathMatchResult = getFileReferenceFromReferencePath(comment, range); if (referencePathMatchResult) { - const fileReference = referencePathMatchResult.fileReference; - sourceFile.hasNoDefaultLib = referencePathMatchResult.isNoDefaultLib; - const diagnosticMessage = referencePathMatchResult.diagnosticMessage; - if (fileReference) { - if (referencePathMatchResult.isTypeReferenceDirective) { - typeReferenceDirectives.push(fileReference); - } - else if (referencePathMatchResult.isLibReferenceDirective) { - libReferenceDirectives.push(fileReference); - } - else { - referencedFiles.push(fileReference); - } - } - if (diagnosticMessage) { - parseDiagnostics.push(createFileDiagnostic(sourceFile, range.pos, range.end - range.pos, diagnosticMessage)); + switch (referencePathMatchResult.kind) { + case "error": + parseDiagnostics.push(createFileDiagnostic(sourceFile, range.pos, range.end - range.pos, referencePathMatchResult.diagnosticMessage)); + break; + case "no-default-lib": + sourceFile.hasNoDefaultLib = true; + break; + case "types": + typeReferenceDirectives.push(referencePathMatchResult.fileReference); + break; + case "lib": + libReferenceDirectives.push(referencePathMatchResult.fileReference); + break; + case "path": + referencedFiles.push(referencePathMatchResult.fileReference); + break; } } else { diff --git a/src/compiler/program.ts b/src/compiler/program.ts index 9b4b56fbfa5c8..521bbee1ff276 100644 --- a/src/compiler/program.ts +++ b/src/compiler/program.ts @@ -1583,7 +1583,7 @@ namespace ts { if (endsWith(unqualifiedLibName, ".d.ts")) { unqualifiedLibName = removeExtension(unqualifiedLibName, ".d.ts"); } - const suggestion = getSpellingSuggestion(unqualifiedLibName, arrayFrom(libMap.keys())); + const suggestion = getSpellingSuggestion(unqualifiedLibName, libs); const message = suggestion ? Diagnostics.Cannot_find_lib_definition_for_0_Did_you_mean_1 : Diagnostics.Cannot_find_lib_definition_for_0; fileProcessingDiagnostics.add(createDiagnostic(file, libReference.pos, libReference.end, message, libName, suggestion)); } diff --git a/src/compiler/types.ts b/src/compiler/types.ts index b02edd07b3120..eed2da4c39c09 100644 --- a/src/compiler/types.ts +++ b/src/compiler/types.ts @@ -22,16 +22,6 @@ namespace ts { entries(): Iterator<[string, T]>; } - export interface ReadonlyMap { - readonly size: number; - get(key: string): T | undefined; - has(key: string): boolean; - forEach(action: (value: T, key: string) => void): void; - keys(): Iterator; - values(): Iterator; - entries(): Iterator<[string, T]>; - } - /** ES6 Iterator type. */ export interface Iterator { next(): { value: T, done: false } | { value: never, done: true }; diff --git a/src/compiler/utilities.ts b/src/compiler/utilities.ts index 300c42cae3687..393cf2d0e95d8 100644 --- a/src/compiler/utilities.ts +++ b/src/compiler/utilities.ts @@ -4,14 +4,22 @@ namespace ts { export const externalHelpersModuleNameText = "tslib"; - export interface ReferencePathMatchResult { - fileReference?: FileReference; - diagnosticMessage?: DiagnosticMessage; - isNoDefaultLib?: boolean; - isTypeReferenceDirective?: boolean; - isLibReferenceDirective?: boolean; + export interface NoDefaultLibDirective { + kind: "no-default-lib"; } + export interface ReferenceDirective { + kind: "path" | "types" | "lib"; + fileReference: FileReference; + } + + export interface ReferenceDirectiveError { + kind: "error"; + diagnosticMessage: DiagnosticMessage; + } + + export type ReferencePathMatchResult = NoDefaultLibDirective | ReferenceDirective | ReferenceDirectiveError; + export function getDeclarationOfKind(symbol: Symbol, kind: SyntaxKind): Declaration { const declarations = symbol.declarations; if (declarations) { @@ -1929,40 +1937,32 @@ namespace ts { return fullTripleSlashReferencePathOrAmdDependencyPathRegEx.test(comment); } - export function getFileReferenceFromReferencePath(comment: string, commentRange: CommentRange): ReferencePathMatchResult { - if (simpleReferenceRegEx.test(comment)) { - const match = fullTripleSlashReferenceRegEx.exec(comment); - if (match) { - const name = match[1]; - if (name === "no-default-lib") { - return { - isNoDefaultLib: true - }; - } - else if (name === "path" || name === "types" || name === "lib") { - const value = match[3]; - const start = commentRange.pos; - const end = commentRange.end; - return { - fileReference: { - pos: start, - end: end, - fileName: value - }, - isNoDefaultLib: false, - isTypeReferenceDirective: name === "types", - isLibReferenceDirective: name === "lib" - }; - } - } - else { - return { - diagnosticMessage: Diagnostics.Invalid_reference_directive_syntax, - isNoDefaultLib: false - }; - } + export function getFileReferenceFromReferencePath(comment: string, commentRange: CommentRange): ReferencePathMatchResult | undefined { + if (!simpleReferenceRegEx.test(comment)) { + return undefined; } - return undefined; + const match = fullTripleSlashReferenceRegEx.exec(comment); + if (!match) { + return { + diagnosticMessage: Diagnostics.Invalid_reference_directive_syntax, + kind: "error" + }; + } + const kind = match[1] as "no-default-lib" | "path" | "types" | "lib"; + if (kind === "no-default-lib") { + return { kind }; + } + const value = match[3]; + const start = commentRange.pos; + const end = commentRange.end; + return { + fileReference: { + pos: start, + end: end, + fileName: value + }, + kind + }; } export function isKeyword(token: SyntaxKind): boolean { diff --git a/src/services/preProcess.ts b/src/services/preProcess.ts index 0f0702066e114..191ae25657fad 100644 --- a/src/services/preProcess.ts +++ b/src/services/preProcess.ts @@ -27,14 +27,18 @@ namespace ts { const comment = sourceText.substring(commentRange.pos, commentRange.end); const referencePathMatchResult = getFileReferenceFromReferencePath(comment, commentRange); if (referencePathMatchResult) { - isNoDefaultLib = referencePathMatchResult.isNoDefaultLib; - const fileReference = referencePathMatchResult.fileReference; - if (fileReference) { - const collection = referencePathMatchResult.isTypeReferenceDirective - ? typeReferenceDirectives - : referencedFiles; - - collection.push(fileReference); + switch (referencePathMatchResult.kind) { + case "no-default-lib": + isNoDefaultLib = true; + break; + case "types": + typeReferenceDirectives.push(referencePathMatchResult.fileReference); + break; + case "path": + referencedFiles.push(referencePathMatchResult.fileReference); + break; + // TODO(rbuckton): pass lib references to core services + // case "lib": } } }); diff --git a/tests/baselines/reference/libReferenceDirectiveErrors.errors.txt b/tests/baselines/reference/libReferenceDirectiveErrors.errors.txt index 1d2327480e0fa..91b2d9e47f82a 100644 --- a/tests/baselines/reference/libReferenceDirectiveErrors.errors.txt +++ b/tests/baselines/reference/libReferenceDirectiveErrors.errors.txt @@ -2,6 +2,7 @@ tests/cases/compiler/a.ts(1,1): error TS1084: Invalid 'reference' directive synt tests/cases/compiler/b.ts(1,1): error TS1084: Invalid 'reference' directive syntax. tests/cases/compiler/c.ts(1,1): error TS2711: Cannot find lib definition for 'es2015.foo'. tests/cases/compiler/d.ts(1,1): error TS2712: Cannot find lib definition for 'es2015.collections'. Did you mean 'es2015.collection'? +tests/cases/compiler/e.ts(1,1): error TS2712: Cannot find lib definition for 'lib.es2015.d.ts'. Did you mean 'es2015'? ==== tests/cases/compiler/a.ts (1 errors) ==== @@ -19,4 +20,8 @@ tests/cases/compiler/d.ts(1,1): error TS2712: Cannot find lib definition for 'es ==== tests/cases/compiler/d.ts (1 errors) ==== /// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -!!! error TS2712: Cannot find lib definition for 'es2015.collections'. Did you mean 'es2015.collection'? \ No newline at end of file +!!! error TS2712: Cannot find lib definition for 'es2015.collections'. Did you mean 'es2015.collection'? +==== tests/cases/compiler/e.ts (1 errors) ==== + /// + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +!!! error TS2712: Cannot find lib definition for 'lib.es2015.d.ts'. Did you mean 'es2015'? \ No newline at end of file diff --git a/tests/baselines/reference/libReferenceDirectiveErrors.js b/tests/baselines/reference/libReferenceDirectiveErrors.js index fb25be159c167..ca141299f507d 100644 --- a/tests/baselines/reference/libReferenceDirectiveErrors.js +++ b/tests/baselines/reference/libReferenceDirectiveErrors.js @@ -8,6 +8,8 @@ /// //// [d.ts] /// +//// [e.ts] +/// //// [a.js] /// @@ -17,3 +19,5 @@ /// //// [d.js] /// +//// [e.js] +/// From 2f8ae00677aa796a7d18b97cf3b1f47da81109a1 Mon Sep 17 00:00:00 2001 From: Ron Buckton Date: Fri, 12 May 2017 17:12:30 -0700 Subject: [PATCH 3/9] Address PR feedback --- src/compiler/program.ts | 6 ++---- src/compiler/utilities.ts | 32 ++++++++++++++++++++++---------- 2 files changed, 24 insertions(+), 14 deletions(-) diff --git a/src/compiler/program.ts b/src/compiler/program.ts index 521bbee1ff276..c45c2ec432c31 100644 --- a/src/compiler/program.ts +++ b/src/compiler/program.ts @@ -397,9 +397,8 @@ namespace ts { processRootFile(host.getDefaultLibFileName(options), /*isDefaultLib*/ true, /*ignoreNoDefaultLib*/ false); } else { - const libDirectory = getLibDirectory(); forEach(options.lib, libFileName => { - processRootFile(combinePaths(libDirectory, libFileName), /*isDefaultLib*/ true, /*ignoreNoDefaultLib*/ false); + processRootFile(combinePaths(getLibDirectory(), libFileName), /*isDefaultLib*/ true, /*ignoreNoDefaultLib*/ false); }); } } @@ -1567,13 +1566,12 @@ namespace ts { } function processLibReferenceDirectives(file: SourceFile) { - const libDirectory = getLibDirectory(); forEach(file.libReferenceDirectives, libReference => { const libName = libReference.fileName.toLocaleLowerCase(); const libFileName = libMap.get(libName); if (libFileName) { // we ignore any 'no-default-lib' reference set on this import. - processRootFile(combinePaths(libDirectory, libFileName), /*isDefaultLib*/ true, /*ignoreNoDefaultLib*/ false); + processRootFile(combinePaths(getLibDirectory(), libFileName), /*isDefaultLib*/ true, /*ignoreNoDefaultLib*/ true); } else { let unqualifiedLibName = libName; diff --git a/src/compiler/utilities.ts b/src/compiler/utilities.ts index a6cd67417b90b..2cebec989c3ef 100644 --- a/src/compiler/utilities.ts +++ b/src/compiler/utilities.ts @@ -4254,25 +4254,37 @@ namespace ts { /** * Given a name and a list of names are *not* equal to the name, return a spelling suggestion if there is one that is close enough. * Names less than length 3 only check for case-insensitive equality, not Levenshtein distance. + * + * If there is a candidate that's the same except for case, return that. + * If there is a candidate that's within one edit of the name, return that. + * Otherwise, return the candidate with the smallest Levenshtein distance, + * except for candidates: + * * With no name + * * Whose length differs from the target name by more than 0.3 of the length of the name. + * * Whose levenshtein distance is more than 0.4 of the length of the name + * (0.4 allows 1 substitution/transposition for every 5 characters, + * and 1 insertion/deletion at 3 characters) + * Names longer than 30 characters don't get suggestions because Levenshtein distance is an n**2 algorithm. */ export function getSpellingSuggestion(name: string, choices: string[]): string | undefined; /** * Given a name and a list of names are *not* equal to the name, return a spelling suggestion if there is one that is close enough. * Names less than length 3 only check for case-insensitive equality, not Levenshtein distance. + * + * If there is a candidate that's the same except for case, return that. + * If there is a candidate that's within one edit of the name, return that. + * Otherwise, return the candidate with the smallest Levenshtein distance, + * except for candidates: + * * With no name + * * Whose length differs from the target name by more than 0.3 of the length of the name. + * * Whose levenshtein distance is more than 0.4 of the length of the name + * (0.4 allows 1 substitution/transposition for every 5 characters, + * and 1 insertion/deletion at 3 characters) + * Names longer than 30 characters don't get suggestions because Levenshtein distance is an n**2 algorithm. */ export function getSpellingSuggestion(name: string, choices: T[], exclusions: string[] | undefined, getName: (candidate: T) => string | undefined): T | undefined; export function getSpellingSuggestion(name: string, choices: T[], exclusions?: string[], getName: (candidate: T) => string | undefined = identity): T | undefined { - // If there is a candidate that's the same except for case, return that. - // If there is a candidate that's within one edit of the name, return that. - // Otherwise, return the candidate with the smallest Levenshtein distance, - // except for candidates: - // * With no name - // * Whose length differs from the target name by more than 0.3 of the length of the name. - // * Whose levenshtein distance is more than 0.4 of the length of the name - // (0.4 allows 1 substitution/transposition for every 5 characters, - // and 1 insertion/deletion at 3 characters) - // Names longer than 30 characters don't get suggestions because Levenshtein distance is an n**2 algorithm. const worstDistance = name.length * 0.4; const maximumLengthDifference = Math.min(3, name.length * 0.34); let bestDistance = Number.MAX_VALUE; From 295f3c15571e54ca17d2c4f1f6341b2622199c1a Mon Sep 17 00:00:00 2001 From: Ron Buckton Date: Mon, 5 Jun 2017 15:01:49 -0700 Subject: [PATCH 4/9] Swap reference paths to lib --- src/lib/dom.iterable.d.ts | 2 +- src/lib/es2015.d.ts | 20 ++++++++++---------- src/lib/es2015.iterable.d.ts | 2 +- src/lib/es2015.symbol.wellknown.d.ts | 2 +- src/lib/es2016.d.ts | 4 ++-- src/lib/es2017.d.ts | 10 +++++----- src/lib/es2017.sharedmemory.d.ts | 4 ++-- src/lib/esnext.asynciterable.d.ts | 4 ++-- src/lib/esnext.d.ts | 4 ++-- src/lib/importes5.d.ts | 2 +- 10 files changed, 27 insertions(+), 27 deletions(-) diff --git a/src/lib/dom.iterable.d.ts b/src/lib/dom.iterable.d.ts index e89a22f2c1418..6fed7ce9224d2 100644 --- a/src/lib/dom.iterable.d.ts +++ b/src/lib/dom.iterable.d.ts @@ -1,4 +1,4 @@ -/// +/// interface DOMTokenList { [Symbol.iterator](): IterableIterator; diff --git a/src/lib/es2015.d.ts b/src/lib/es2015.d.ts index ba30ea74669c4..df32ef4cf9a61 100644 --- a/src/lib/es2015.d.ts +++ b/src/lib/es2015.d.ts @@ -1,10 +1,10 @@ -/// -/// -/// -/// -/// -/// -/// -/// -/// -/// \ No newline at end of file +/// +/// +/// +/// +/// +/// +/// +/// +/// +/// \ No newline at end of file diff --git a/src/lib/es2015.iterable.d.ts b/src/lib/es2015.iterable.d.ts index 049b1898b9b6f..9985792d8fdf1 100644 --- a/src/lib/es2015.iterable.d.ts +++ b/src/lib/es2015.iterable.d.ts @@ -1,4 +1,4 @@ -/// +/// interface SymbolConstructor { /** diff --git a/src/lib/es2015.symbol.wellknown.d.ts b/src/lib/es2015.symbol.wellknown.d.ts index 578cf0acbc2f2..98ab4f9987350 100644 --- a/src/lib/es2015.symbol.wellknown.d.ts +++ b/src/lib/es2015.symbol.wellknown.d.ts @@ -1,4 +1,4 @@ -/// +/// interface SymbolConstructor { /** diff --git a/src/lib/es2016.d.ts b/src/lib/es2016.d.ts index 34f833d621b41..fc1aab7798cac 100644 --- a/src/lib/es2016.d.ts +++ b/src/lib/es2016.d.ts @@ -1,2 +1,2 @@ -/// -/// \ No newline at end of file +/// +/// \ No newline at end of file diff --git a/src/lib/es2017.d.ts b/src/lib/es2017.d.ts index 80282355a45b3..dace30f5de0a5 100644 --- a/src/lib/es2017.d.ts +++ b/src/lib/es2017.d.ts @@ -1,5 +1,5 @@ -/// -/// -/// -/// -/// +/// +/// +/// +/// +/// diff --git a/src/lib/es2017.sharedmemory.d.ts b/src/lib/es2017.sharedmemory.d.ts index b9a9b0f7e10d7..018a2f162a585 100644 --- a/src/lib/es2017.sharedmemory.d.ts +++ b/src/lib/es2017.sharedmemory.d.ts @@ -1,5 +1,5 @@ -/// -/// +/// +/// interface SharedArrayBuffer { /** diff --git a/src/lib/esnext.asynciterable.d.ts b/src/lib/esnext.asynciterable.d.ts index 8379ba5ba6cd0..11093af623dee 100644 --- a/src/lib/esnext.asynciterable.d.ts +++ b/src/lib/esnext.asynciterable.d.ts @@ -1,5 +1,5 @@ -/// -/// +/// +/// interface SymbolConstructor { /** diff --git a/src/lib/esnext.d.ts b/src/lib/esnext.d.ts index 71fab82a866bc..1ff3fad8cda3d 100644 --- a/src/lib/esnext.d.ts +++ b/src/lib/esnext.d.ts @@ -1,2 +1,2 @@ -/// -/// +/// +/// diff --git a/src/lib/importes5.d.ts b/src/lib/importes5.d.ts index 872bf0a1bedc2..7c4389bf15aa0 100644 --- a/src/lib/importes5.d.ts +++ b/src/lib/importes5.d.ts @@ -1 +1 @@ -/// +/// From 32dd033ae8f2860af7bb8e8f84563a21a1654a6c Mon Sep 17 00:00:00 2001 From: Ron Buckton Date: Mon, 5 Jun 2017 16:42:12 -0700 Subject: [PATCH 5/9] Switch built-in libs to use lib refs --- Gulpfile.ts | 105 +++++++----------- Jakefile.js | 105 +++++++----------- src/compiler/commandLineParser.ts | 13 ++- src/harness/harness.ts | 5 + .../unittests/tsserverProjectSystem.ts | 25 +++-- src/lib/default.es2015.d.ts | 14 +++ src/lib/default.es2016.d.ts | 5 + src/lib/default.es2017.d.ts | 5 + src/lib/default.es5.d.ts | 4 + src/lib/default.esnext.d.ts | 5 + 10 files changed, 147 insertions(+), 139 deletions(-) create mode 100644 src/lib/default.es2015.d.ts create mode 100644 src/lib/default.es2016.d.ts create mode 100644 src/lib/default.es2017.d.ts create mode 100644 src/lib/default.es5.d.ts create mode 100644 src/lib/default.esnext.d.ts diff --git a/Gulpfile.ts b/Gulpfile.ts index d856254296eb7..ed548ceb058a2 100644 --- a/Gulpfile.ts +++ b/Gulpfile.ts @@ -105,70 +105,49 @@ const nodeModulesPathPrefix = path.resolve("./node_modules/.bin/"); const isWin = /^win/.test(process.platform); const mocha = path.join(nodeModulesPathPrefix, "mocha") + (isWin ? ".cmd" : ""); -const es2015LibrarySources = [ - "es2015.core.d.ts", - "es2015.collection.d.ts", - "es2015.generator.d.ts", - "es2015.iterable.d.ts", - "es2015.promise.d.ts", - "es2015.proxy.d.ts", - "es2015.reflect.d.ts", - "es2015.symbol.d.ts", - "es2015.symbol.wellknown.d.ts" -]; - -const es2015LibrarySourceMap = es2015LibrarySources.map(function(source) { - return { target: "lib." + source, sources: ["header.d.ts", source] }; -}); - -const es2016LibrarySource = ["es2016.array.include.d.ts"]; - -const es2016LibrarySourceMap = es2016LibrarySource.map(function(source) { - return { target: "lib." + source, sources: ["header.d.ts", source] }; -}); - -const es2017LibrarySource = [ - "es2017.object.d.ts", - "es2017.sharedmemory.d.ts", - "es2017.string.d.ts", - "es2017.intl.d.ts", -]; - -const es2017LibrarySourceMap = es2017LibrarySource.map(function(source) { - return { target: "lib." + source, sources: ["header.d.ts", source] }; -}); - -const esnextLibrarySource = [ - "esnext.asynciterable.d.ts" -]; - -const esnextLibrarySourceMap = esnextLibrarySource.map(function (source) { - return { target: "lib." + source, sources: ["header.d.ts", source] }; -}); - -const hostsLibrarySources = ["dom.generated.d.ts", "webworker.importscripts.d.ts", "scripthost.d.ts"]; - const librarySourceMap = [ - // Host library - { target: "lib.dom.d.ts", sources: ["header.d.ts", "dom.generated.d.ts"] }, - { target: "lib.dom.iterable.d.ts", sources: ["header.d.ts", "dom.iterable.d.ts"] }, - { target: "lib.webworker.d.ts", sources: ["header.d.ts", "webworker.generated.d.ts"] }, - { target: "lib.scripthost.d.ts", sources: ["header.d.ts", "scripthost.d.ts"] }, - - // JavaScript library - { target: "lib.es5.d.ts", sources: ["header.d.ts", "es5.d.ts"] }, - { target: "lib.es2015.d.ts", sources: ["header.d.ts", "es2015.d.ts"] }, - { target: "lib.es2016.d.ts", sources: ["header.d.ts", "es2016.d.ts"] }, - { target: "lib.es2017.d.ts", sources: ["header.d.ts", "es2017.d.ts"] }, - { target: "lib.esnext.d.ts", sources: ["header.d.ts", "esnext.d.ts"] }, - - // JavaScript + all host library - { target: "lib.d.ts", sources: ["header.d.ts", "es5.d.ts"].concat(hostsLibrarySources) }, - { target: "lib.es6.d.ts", sources: ["header.d.ts", "es5.d.ts"].concat(es2015LibrarySources, hostsLibrarySources, "dom.iterable.d.ts") }, - { target: "lib.es2016.full.d.ts", sources: ["header.d.ts", "es2016.d.ts"].concat(es2015LibrarySources, hostsLibrarySources, "dom.iterable.d.ts") }, - { target: "lib.es2017.full.d.ts", sources: ["header.d.ts", "es2017.d.ts"].concat(es2015LibrarySources, hostsLibrarySources, "dom.iterable.d.ts") }, - { target: "lib.esnext.full.d.ts", sources: ["header.d.ts", "esnext.d.ts"].concat(es2015LibrarySources, hostsLibrarySources, "dom.iterable.d.ts") }, -].concat(es2015LibrarySourceMap, es2016LibrarySourceMap, es2017LibrarySourceMap, esnextLibrarySourceMap); + // Host libraries + "dom.generated=lib.dom.d.ts", + "dom.iterable", + "webworker.generated=lib.webworker.d.ts", + "webworker.importscripts", + "scripthost", + + // Javascript libraries + "es5", + "es2015", + "es2015.core", + "es2015.collection", + "es2015.generator", + "es2015.iterable", + "es2015.promise", + "es2015.proxy", + "es2015.reflect", + "es2015.symbol", + "es2015.symbol.wellknown", + "es2016", + "es2016.array.include", + "es2017", + "es2017.object", + "es2017.sharedmemory", + "es2017.string", + "es2017.intl", + "esnext", + "esnext.asynciterable", + + // Default libraries + "default.es5=lib.d.ts", + "default.es2015=lib.es6.d.ts", + "default.es2016=lib.es2016.full.d.ts", + "default.es2017=lib.es2017.full.d.ts", + "default.esnext=lib.esnext.full.d.ts", +].map(function (lib) { + var parts = lib.split("=", 2); + return { + sources: ["header.d.ts", parts[0] + ".d.ts"], + target: parts[1] || ("lib." + parts[0] + ".d.ts") + }; +}); const libraryTargets = librarySourceMap.map(function(f) { return path.join(builtLocalDirectory, f.target); diff --git a/Jakefile.js b/Jakefile.js index 439fced0720ff..fb2501260e5db 100644 --- a/Jakefile.js +++ b/Jakefile.js @@ -148,70 +148,49 @@ var harnessSources = harnessCoreSources.concat([ return path.join(serverDirectory, f); })); -var es2015LibrarySources = [ - "es2015.core.d.ts", - "es2015.collection.d.ts", - "es2015.generator.d.ts", - "es2015.iterable.d.ts", - "es2015.promise.d.ts", - "es2015.proxy.d.ts", - "es2015.reflect.d.ts", - "es2015.symbol.d.ts", - "es2015.symbol.wellknown.d.ts" -]; - -var es2015LibrarySourceMap = es2015LibrarySources.map(function (source) { - return { target: "lib." + source, sources: ["header.d.ts", source] }; -}); - -var es2016LibrarySource = ["es2016.array.include.d.ts"]; - -var es2016LibrarySourceMap = es2016LibrarySource.map(function (source) { - return { target: "lib." + source, sources: ["header.d.ts", source] }; -}); - -var es2017LibrarySource = [ - "es2017.object.d.ts", - "es2017.sharedmemory.d.ts", - "es2017.string.d.ts", - "es2017.intl.d.ts" -]; - -var es2017LibrarySourceMap = es2017LibrarySource.map(function (source) { - return { target: "lib." + source, sources: ["header.d.ts", source] }; -}); - -var esnextLibrarySource = [ - "esnext.asynciterable.d.ts" -]; - -var esnextLibrarySourceMap = esnextLibrarySource.map(function (source) { - return { target: "lib." + source, sources: ["header.d.ts", source] }; -}); - -var hostsLibrarySources = ["dom.generated.d.ts", "webworker.importscripts.d.ts", "scripthost.d.ts"]; - var librarySourceMap = [ - // Host library - { target: "lib.dom.d.ts", sources: ["header.d.ts", "dom.generated.d.ts"] }, - { target: "lib.dom.iterable.d.ts", sources: ["header.d.ts", "dom.iterable.d.ts"] }, - { target: "lib.webworker.d.ts", sources: ["header.d.ts", "webworker.generated.d.ts"] }, - { target: "lib.scripthost.d.ts", sources: ["header.d.ts", "scripthost.d.ts"] }, - - // JavaScript library - { target: "lib.es5.d.ts", sources: ["header.d.ts", "es5.d.ts"] }, - { target: "lib.es2015.d.ts", sources: ["header.d.ts", "es2015.d.ts"] }, - { target: "lib.es2016.d.ts", sources: ["header.d.ts", "es2016.d.ts"] }, - { target: "lib.es2017.d.ts", sources: ["header.d.ts", "es2017.d.ts"] }, - { target: "lib.esnext.d.ts", sources: ["header.d.ts", "esnext.d.ts"] }, - - // JavaScript + all host library - { target: "lib.d.ts", sources: ["header.d.ts", "es5.d.ts"].concat(hostsLibrarySources) }, - { target: "lib.es6.d.ts", sources: ["header.d.ts", "es5.d.ts"].concat(es2015LibrarySources, hostsLibrarySources, "dom.iterable.d.ts") }, - { target: "lib.es2016.full.d.ts", sources: ["header.d.ts", "es2016.d.ts"].concat(hostsLibrarySources, "dom.iterable.d.ts") }, - { target: "lib.es2017.full.d.ts", sources: ["header.d.ts", "es2017.d.ts"].concat(hostsLibrarySources, "dom.iterable.d.ts") }, - { target: "lib.esnext.full.d.ts", sources: ["header.d.ts", "esnext.d.ts"].concat(hostsLibrarySources, "dom.iterable.d.ts") }, -].concat(es2015LibrarySourceMap, es2016LibrarySourceMap, es2017LibrarySourceMap, esnextLibrarySourceMap); + // Host libraries + "dom.generated=lib.dom.d.ts", + "dom.iterable", + "webworker.generated=lib.webworker.d.ts", + "webworker.importscripts", + "scripthost", + + // Javascript libraries + "es5", + "es2015", + "es2015.core", + "es2015.collection", + "es2015.generator", + "es2015.iterable", + "es2015.promise", + "es2015.proxy", + "es2015.reflect", + "es2015.symbol", + "es2015.symbol.wellknown", + "es2016", + "es2016.array.include", + "es2017", + "es2017.object", + "es2017.sharedmemory", + "es2017.string", + "es2017.intl", + "esnext", + "esnext.asynciterable", + + // Default libraries + "default.es5=lib.d.ts", + "default.es2015=lib.es6.d.ts", + "default.es2016=lib.es2016.full.d.ts", + "default.es2017=lib.es2017.full.d.ts", + "default.esnext=lib.esnext.full.d.ts", +].map(function (lib) { + var parts = lib.split("=", 2); + return { + sources: ["header.d.ts", parts[0] + ".d.ts"], + target: parts[1] || ("lib." + parts[0] + ".d.ts") + }; +}); var libraryTargets = librarySourceMap.map(function (f) { return path.join(builtLocalDirectory, f.target); diff --git a/src/compiler/commandLineParser.ts b/src/compiler/commandLineParser.ts index 8f7598e5e2df7..fcd9610f7ae58 100644 --- a/src/compiler/commandLineParser.ts +++ b/src/compiler/commandLineParser.ts @@ -8,8 +8,7 @@ namespace ts { /* @internal */ export const compileOnSaveCommandLineOption: CommandLineOption = { name: "compileOnSave", type: "boolean" }; - /* @internal */ - export const libMap = createMapFromTemplate({ + const commandLineLibMap = createMapFromTemplate({ // JavaScript only "es5": "lib.es5.d.ts", "es6": "lib.es2015.d.ts", @@ -42,7 +41,13 @@ namespace ts { }); /* @internal */ - export const libs = arrayFrom(libMap.keys()); + // We only support "webworker.importscripts" when used as part of a default lib. It's not available + // on the command line. + export const libMap = cloneMap(commandLineLibMap) + .set("webworker.importscripts", "lib.webworker.importscripts.d.ts"); + + /* @internal */ + export const libs = arrayFrom(commandLineLibMap.keys()); /* @internal */ export const optionDeclarations: CommandLineOption[] = [ @@ -148,7 +153,7 @@ namespace ts { type: "list", element: { name: "lib", - type: libMap, + type: commandLineLibMap, }, showInSimplifiedHelpView: true, category: Diagnostics.Basic_Options, diff --git a/src/harness/harness.ts b/src/harness/harness.ts index 49dbdbf2102e2..e554427874548 100644 --- a/src/harness/harness.ts +++ b/src/harness/harness.ts @@ -1983,5 +1983,10 @@ namespace Harness { return { unitName: libFile, content: io.readFile(libFile) }; } + export function getNamedDefaultLibraryFile(io: Harness.IO, libName: string): Harness.Compiler.TestFile { + const libFile = Harness.userSpecifiedRoot + Harness.libFolder + "lib." + libName + ".d.ts"; + return { unitName: libFile, content: io.readFile(libFile) }; + } + if (Error) (Error).stackTraceLimit = 100; } diff --git a/src/harness/unittests/tsserverProjectSystem.ts b/src/harness/unittests/tsserverProjectSystem.ts index 6f898e7f4a66c..0723641dfd56a 100644 --- a/src/harness/unittests/tsserverProjectSystem.ts +++ b/src/harness/unittests/tsserverProjectSystem.ts @@ -49,6 +49,13 @@ namespace ts.projectSystem { path: "/a/lib/lib.d.ts", content: libFileContent }; + export const libFiles: FileOrFolder[] = [ + { path: "/a/lib/lib.d.ts", content: Harness.getDefaultLibraryFile(Harness.IO).content }, + { path: "/a/lib/lib.es5.d.ts", content: Harness.getNamedDefaultLibraryFile(Harness.IO, "es5").content }, + { path: "/a/lib/lib.dom.d.ts", content: Harness.getNamedDefaultLibraryFile(Harness.IO, "dom").content }, + { path: "/a/lib/lib.webworker.importscripts.d.ts", content: Harness.getNamedDefaultLibraryFile(Harness.IO, "webworker.importscripts").content }, + { path: "/a/lib/lib.scripthost.d.ts", content: Harness.getNamedDefaultLibraryFile(Harness.IO, "scripthost").content } + ]; export class TestTypingsInstaller extends TI.TypingsInstaller implements server.ITypingsInstaller { protected projectService: server.ProjectService; @@ -1285,7 +1292,7 @@ namespace ts.projectSystem { content: "let x: number;" }; - const host = createServerHost([f1, f2, libFile]); + const host = createServerHost([f1, f2, ...libFiles]); const service = createProjectService(host); service.openExternalProject({ projectFileName: "/a/b/project", rootFiles: toExternalFiles([f1.path, f2.path]), options: {} }); @@ -1293,7 +1300,7 @@ namespace ts.projectSystem { service.openClientFile(f2.path, "let x: string"); service.checkNumberOfProjects({ externalProjects: 1 }); - checkProjectActualFiles(service.externalProjects[0], [f1.path, f2.path, libFile.path]); + checkProjectActualFiles(service.externalProjects[0], [f1.path, f2.path, ...libFiles.map(f => f.path)]); const completions1 = service.externalProjects[0].getLanguageService().getCompletionsAtPosition(f1.path, 2); // should contain completions for string @@ -1895,7 +1902,7 @@ namespace ts.projectSystem { content: JSON.stringify({ compilerOptions: { allowJs: true } }) }; - let host = createServerHost([file1, file2, config1, libFile], { executingFilePath: combinePaths(getDirectoryPath(libFile.path), "tsc.js") }); + let host = createServerHost([file1, file2, config1, ...libFiles], { executingFilePath: combinePaths(getDirectoryPath(libFile.path), "tsc.js") }); let session = createSession(host); // Specify .html extension as mixed content in a configure host request @@ -1917,7 +1924,7 @@ namespace ts.projectSystem { content: JSON.stringify({ compilerOptions: { allowJs: false } }) }; - host = createServerHost([file1, file2, config2, libFile], { executingFilePath: combinePaths(getDirectoryPath(libFile.path), "tsc.js") }); + host = createServerHost([file1, file2, config2, ...libFiles], { executingFilePath: combinePaths(getDirectoryPath(libFile.path), "tsc.js") }); session = createSession(host); session.executeCommand(configureHostRequest).response; @@ -1936,7 +1943,7 @@ namespace ts.projectSystem { content: JSON.stringify({}) }; - host = createServerHost([file1, file2, config3, libFile], { executingFilePath: combinePaths(getDirectoryPath(libFile.path), "tsc.js") }); + host = createServerHost([file1, file2, config3, ...libFiles], { executingFilePath: combinePaths(getDirectoryPath(libFile.path), "tsc.js") }); session = createSession(host); session.executeCommand(configureHostRequest).response; @@ -1955,7 +1962,7 @@ namespace ts.projectSystem { content: JSON.stringify({ compilerOptions: { allowJs: true }, files: [file1.path, file2.path] }) }; - host = createServerHost([file1, file2, config4, libFile], { executingFilePath: combinePaths(getDirectoryPath(libFile.path), "tsc.js") }); + host = createServerHost([file1, file2, config4, ...libFiles], { executingFilePath: combinePaths(getDirectoryPath(libFile.path), "tsc.js") }); session = createSession(host); session.executeCommand(configureHostRequest).response; @@ -1974,7 +1981,7 @@ namespace ts.projectSystem { content: JSON.stringify({ compilerOptions: { allowJs: true }, exclude: [file2.path] }) }; - host = createServerHost([file1, file2, config5, libFile], { executingFilePath: combinePaths(getDirectoryPath(libFile.path), "tsc.js") }); + host = createServerHost([file1, file2, config5, ...libFiles], { executingFilePath: combinePaths(getDirectoryPath(libFile.path), "tsc.js") }); session = createSession(host); session.executeCommand(configureHostRequest).response; @@ -3471,7 +3478,7 @@ namespace ts.projectSystem { path: "/a/b/f1.js", content: "function test1() { }" }; - const host = createServerHost([f1, libFile]); + const host = createServerHost([f1, ...libFiles]); const session = createSession(host); openFilesForSession([f1], session); @@ -3507,7 +3514,7 @@ namespace ts.projectSystem { path: "/a/b/f1.js", content: "function test1() { }" }; - const host = createServerHost([f1, libFile]); + const host = createServerHost([f1, ...libFiles]); const session = createSession(host); const projectService = session.getProjectService(); const projectFileName = "/a/b/project.csproj"; diff --git a/src/lib/default.es2015.d.ts b/src/lib/default.es2015.d.ts new file mode 100644 index 0000000000000..14cff0fccc524 --- /dev/null +++ b/src/lib/default.es2015.d.ts @@ -0,0 +1,14 @@ +/// +/// +/// +/// +/// +/// +/// +/// +/// +/// +/// +/// +/// +/// \ No newline at end of file diff --git a/src/lib/default.es2016.d.ts b/src/lib/default.es2016.d.ts new file mode 100644 index 0000000000000..5dda8cc35e785 --- /dev/null +++ b/src/lib/default.es2016.d.ts @@ -0,0 +1,5 @@ +/// +/// +/// +/// +/// diff --git a/src/lib/default.es2017.d.ts b/src/lib/default.es2017.d.ts new file mode 100644 index 0000000000000..ab03ea04d4844 --- /dev/null +++ b/src/lib/default.es2017.d.ts @@ -0,0 +1,5 @@ +/// +/// +/// +/// +/// diff --git a/src/lib/default.es5.d.ts b/src/lib/default.es5.d.ts new file mode 100644 index 0000000000000..d681253ad7ff7 --- /dev/null +++ b/src/lib/default.es5.d.ts @@ -0,0 +1,4 @@ +/// +/// +/// +/// \ No newline at end of file diff --git a/src/lib/default.esnext.d.ts b/src/lib/default.esnext.d.ts new file mode 100644 index 0000000000000..70fc982b908bc --- /dev/null +++ b/src/lib/default.esnext.d.ts @@ -0,0 +1,5 @@ +/// +/// +/// +/// +/// From e9818b40ee90bc3d5befb3eaa6a06a22eb9e82e2 Mon Sep 17 00:00:00 2001 From: Ron Buckton Date: Fri, 9 Jun 2017 00:08:00 -0700 Subject: [PATCH 6/9] single source for libs --- Gulpfile.ts | 54 ++++++++------------------ Jakefile.js | 64 ++++++++----------------------- src/compiler/commandLineParser.ts | 8 ++-- src/lib/libs.json | 36 +++++++++++++++++ 4 files changed, 74 insertions(+), 88 deletions(-) create mode 100644 src/lib/libs.json diff --git a/Gulpfile.ts b/Gulpfile.ts index dba0f9ffb768d..2664b96df07ae 100644 --- a/Gulpfile.ts +++ b/Gulpfile.ts @@ -31,6 +31,7 @@ import merge2 = require("merge2"); import intoStream = require("into-stream"); import * as os from "os"; import fold = require("travis-fold"); +import ts = require("./lib/typescript"); const gulp = helpMaker(originalGulp); const mochaParallel = require("./scripts/mocha-parallel.js"); const {runTestsInParallel} = mochaParallel; @@ -67,6 +68,21 @@ const cmdLineOptions = minimist(process.argv.slice(2), { } }); +function readJson(jsonPath: string): any { + const jsonText = fs.readFileSync(jsonPath).toString(); + const result = ts.parseConfigFileTextToJson(jsonPath, jsonText, /*stripComments*/ true); + if (result.error) { + throw new Error(diagnosticsToString([result.error])); + } + + return result.config; + + function diagnosticsToString(s: ts.Diagnostic[]) { + return s.map(e => ts.flattenDiagnosticMessageText(e.messageText, ts.sys.newLine)).join(ts.sys.newLine); + } +} + + function exec(cmd: string, args: string[], complete: () => void = (() => { }), error: (e: any, status: number) => void = (() => { })) { console.log(`${cmd} ${args.join(" ")}`); // TODO (weswig): Update child_process types to add windowsVerbatimArguments to the type definition @@ -105,43 +121,7 @@ const nodeModulesPathPrefix = path.resolve("./node_modules/.bin/"); const isWin = /^win/.test(process.platform); const mocha = path.join(nodeModulesPathPrefix, "mocha") + (isWin ? ".cmd" : ""); -const librarySourceMap = [ - // Host libraries - "dom.generated=lib.dom.d.ts", - "dom.iterable", - "webworker.generated=lib.webworker.d.ts", - "webworker.importscripts", - "scripthost", - - // Javascript libraries - "es5", - "es2015", - "es2015.core", - "es2015.collection", - "es2015.generator", - "es2015.iterable", - "es2015.promise", - "es2015.proxy", - "es2015.reflect", - "es2015.symbol", - "es2015.symbol.wellknown", - "es2016", - "es2016.array.include", - "es2017", - "es2017.object", - "es2017.sharedmemory", - "es2017.string", - "es2017.intl", - "esnext", - "esnext.asynciterable", - - // Default libraries - "default.es5=lib.d.ts", - "default.es2015=lib.es6.d.ts", - "default.es2016=lib.es2016.full.d.ts", - "default.es2017=lib.es2017.full.d.ts", - "default.esnext=lib.esnext.full.d.ts", -].map(function (lib) { +const librarySourceMap = (readJson(path.resolve("./src/lib/libs.json")) as string[]).map(function (lib) { const parts = lib.split("=", 2); return { sources: ["header.d.ts", parts[0] + ".d.ts"], diff --git a/Jakefile.js b/Jakefile.js index fb2501260e5db..5641d4b63eb34 100644 --- a/Jakefile.js +++ b/Jakefile.js @@ -34,24 +34,30 @@ else if (process.env.PATH !== undefined) { process.env.PATH = nodeModulesPathPrefix + process.env.PATH; } -function filesFromConfig(configPath) { - var configText = fs.readFileSync(configPath).toString(); - var config = ts.parseConfigFileTextToJson(configPath, configText, /*stripComments*/ true); - if (config.error) { - throw new Error(diagnosticsToString([config.error])); - } - const configFileContent = ts.parseJsonConfigFileContent(config.config, ts.sys, path.dirname(configPath)); - if (configFileContent.errors && configFileContent.errors.length) { - throw new Error(diagnosticsToString(configFileContent.errors)); +function readJson(jsonPath) { + var jsonText = fs.readFileSync(jsonPath).toString(); + var result = ts.parseConfigFileTextToJson(jsonPath, jsonText, /*stripComments*/ true); + if (result.error) { + throw new Error(diagnosticsToString([result.error])); } - return configFileContent.fileNames; + return result.config; function diagnosticsToString(s) { return s.map(function(e) { return ts.flattenDiagnosticMessageText(e.messageText, ts.sys.newLine); }).join(ts.sys.newLine); } } +function filesFromConfig(configPath) { + var config = readJson(configPath); + var configFileContent = ts.parseJsonConfigFileContent(config, ts.sys, path.dirname(configPath)); + if (configFileContent.errors && configFileContent.errors.length) { + throw new Error(diagnosticsToString(configFileContent.errors)); + } + + return configFileContent.fileNames; +} + function toNs(diff) { return diff[0] * 1e9 + diff[1]; } @@ -148,43 +154,7 @@ var harnessSources = harnessCoreSources.concat([ return path.join(serverDirectory, f); })); -var librarySourceMap = [ - // Host libraries - "dom.generated=lib.dom.d.ts", - "dom.iterable", - "webworker.generated=lib.webworker.d.ts", - "webworker.importscripts", - "scripthost", - - // Javascript libraries - "es5", - "es2015", - "es2015.core", - "es2015.collection", - "es2015.generator", - "es2015.iterable", - "es2015.promise", - "es2015.proxy", - "es2015.reflect", - "es2015.symbol", - "es2015.symbol.wellknown", - "es2016", - "es2016.array.include", - "es2017", - "es2017.object", - "es2017.sharedmemory", - "es2017.string", - "es2017.intl", - "esnext", - "esnext.asynciterable", - - // Default libraries - "default.es5=lib.d.ts", - "default.es2015=lib.es6.d.ts", - "default.es2016=lib.es2016.full.d.ts", - "default.es2017=lib.es2017.full.d.ts", - "default.esnext=lib.esnext.full.d.ts", -].map(function (lib) { +var librarySourceMap = readJson(path.resolve("./src/lib/libs.json")).map(function (lib) { var parts = lib.split("=", 2); return { sources: ["header.d.ts", parts[0] + ".d.ts"], diff --git a/src/compiler/commandLineParser.ts b/src/compiler/commandLineParser.ts index 02364dd6c0c78..9e5c2d3630ec5 100644 --- a/src/compiler/commandLineParser.ts +++ b/src/compiler/commandLineParser.ts @@ -22,7 +22,7 @@ namespace ts { "dom.iterable": "lib.dom.iterable.d.ts", "webworker": "lib.webworker.d.ts", "scripthost": "lib.scripthost.d.ts", - // ES2015 Or ESNext By-feature options + // By-feature options "es2015.core": "lib.es2015.core.d.ts", "es2015.collection": "lib.es2015.collection.d.ts", "es2015.generator": "lib.es2015.generator.d.ts", @@ -41,10 +41,10 @@ namespace ts { }); /* @internal */ - // We only support "webworker.importscripts" when used as part of a default lib. It's not available - // on the command line. + // Internally we add some additional lib references that we only support when used as part of a + // "lib" reference directive. They are not available on the command line or in tsconfig.json. export const libMap = cloneMap(commandLineLibMap) - .set("webworker.importscripts", "lib.webworker.importscripts.d.ts"); + .set("webworker.importscripts", "lib.webworker.importscripts.d.ts"); /* @internal */ export const libs = arrayFrom(commandLineLibMap.keys()); diff --git a/src/lib/libs.json b/src/lib/libs.json new file mode 100644 index 0000000000000..8fa8e97556f3a --- /dev/null +++ b/src/lib/libs.json @@ -0,0 +1,36 @@ +[ + // JavaScript only + "es5", + "es2015", + "es2016", + "es2017", + "esnext", + // Host only + "dom.generated=lib.dom.d.ts", + "dom.iterable", + "webworker.generated=lib.webworker.d.ts", + "webworker.importscripts", + "scripthost", + // By-feature options + "es2015.core", + "es2015.collection", + "es2015.generator", + "es2015.iterable", + "es2015.promise", + "es2015.proxy", + "es2015.reflect", + "es2015.symbol", + "es2015.symbol.wellknown", + "es2016.array.include", + "es2017.object", + "es2017.sharedmemory", + "es2017.string", + "es2017.intl", + "esnext.asynciterable", + // Default libraries + "default.es5=lib.d.ts", + "default.es2015=lib.es6.d.ts", + "default.es2016=lib.es2016.full.d.ts", + "default.es2017=lib.es2017.full.d.ts", + "default.esnext=lib.esnext.full.d.ts" +] \ No newline at end of file From 2587d05ef85a006e57cd2f857327bd84881cf03c Mon Sep 17 00:00:00 2001 From: Ron Buckton Date: Wed, 8 Nov 2017 12:50:39 -0800 Subject: [PATCH 7/9] Simplify libs.json --- Gulpfile.ts | 34 +++++++----------- Jakefile.js | 36 +++++++------------ .../{default.es2015.d.ts => es2015.full.d.ts} | 2 +- .../{default.es2016.d.ts => es2016.full.d.ts} | 0 .../{default.es2017.d.ts => es2017.full.d.ts} | 0 src/lib/{default.es5.d.ts => es5.full.d.ts} | 0 .../{default.esnext.d.ts => esnext.full.d.ts} | 0 src/lib/libs.json | 22 +++++++----- 8 files changed, 39 insertions(+), 55 deletions(-) rename src/lib/{default.es2015.d.ts => es2015.full.d.ts} (97%) rename src/lib/{default.es2016.d.ts => es2016.full.d.ts} (100%) rename src/lib/{default.es2017.d.ts => es2017.full.d.ts} (100%) rename src/lib/{default.es5.d.ts => es5.full.d.ts} (100%) rename src/lib/{default.esnext.d.ts => esnext.full.d.ts} (100%) diff --git a/Gulpfile.ts b/Gulpfile.ts index 93b34fdb310de..12225b8a4720e 100644 --- a/Gulpfile.ts +++ b/Gulpfile.ts @@ -130,17 +130,6 @@ const nodeModulesPathPrefix = path.resolve("./node_modules/.bin/"); const isWin = /^win/.test(process.platform); const mocha = path.join(nodeModulesPathPrefix, "mocha") + (isWin ? ".cmd" : ""); -const librarySourceMap = (readJson(path.resolve("./src/lib/libs.json")) as { libs: string[] }).libs.map(lib => { - const parts = lib.split("=", 2); - return { - sources: ["header.d.ts", parts[0] + ".d.ts"], - target: parts[1] || ("lib." + parts[0] + ".d.ts") - }; -}); - -const libraryTargets = librarySourceMap.map(f => - path.join(builtLocalDirectory, f.target)); - /** * .lcg file is what localization team uses to know what messages to localize. * The file is always generated in 'enu\diagnosticMessages.generated.json.lcg' @@ -158,17 +147,6 @@ const localizationTargets = ["cs", "de", "es", "fr", "it", "ja", "ko", "pl", "pt .map(f => path.join(builtLocalDirectory, f, "diagnosticMessages.generated.json")) .concat(generatedLCGFile); -for (const i in libraryTargets) { - const entry = librarySourceMap[i]; - const target = libraryTargets[i]; - const sources = [copyright].concat(entry.sources.map(s => path.join(libraryDirectory, s))); - gulp.task(target, /*help*/ false, [], () => - gulp.src(sources) - .pipe(newer(target)) - .pipe(concat(target, { newLine: "\n\n" })) - .pipe(gulp.dest("."))); -} - const configureNightlyJs = path.join(scriptsDirectory, "configureNightly.js"); const configureNightlyTs = path.join(scriptsDirectory, "configureNightly.ts"); const packageJson = "package.json"; @@ -303,6 +281,18 @@ gulp.task("importDefinitelyTypedTests", "Runs scripts/importDefinitelyTypedTests exec(host, [importDefinitelyTypedTestsJs, "./", "../DefinitelyTyped"], done, done); }); +const libraries: { libs: string[], paths: Record } = readJson(path.resolve("./src/lib/libs.json")); +const libraryTargets = libraries.libs.map(function (lib) { + const sources = [copyright].concat(["header.d.ts", lib + ".d.ts"].map(s =>path.join(libraryDirectory, s))); + const target = path.join(builtLocalDirectory, libraries.paths[lib] || ("lib." + lib + ".d.ts")); + gulp.task(target, /*help*/ false, [], () => + gulp.src(sources) + .pipe(newer(target)) + .pipe(concat(target, { newLine: "\n\n" })) + .pipe(gulp.dest("."))); + return target; +}); + gulp.task("lib", "Builds the library targets", libraryTargets); diff --git a/Jakefile.js b/Jakefile.js index 93784f2d9e4b7..02b200a37bbe6 100644 --- a/Jakefile.js +++ b/Jakefile.js @@ -201,18 +201,6 @@ var harnessSources = harnessCoreSources.concat([ return path.join(serverDirectory, f); })); -var librarySourceMap = readJson(path.resolve("./src/lib/libs.json")).libs.map(function (lib) { - var parts = lib.split("=", 2); - return { - sources: ["header.d.ts", parts[0] + ".d.ts"], - target: parts[1] || ("lib." + parts[0] + ".d.ts") - }; -}); - -var libraryTargets = librarySourceMap.map(function (f) { - return path.join(builtLocalDirectory, f.target); -}); - /** * .lcg file is what localization team uses to know what messages to localize. * The file is always generated in 'enu\diagnosticMessages.generated.json.lcg' @@ -387,18 +375,18 @@ function compileFile(outFile, sources, prereqs, prefixes, useBuiltCompiler, opts // Prerequisite task for built directory and library typings directory(builtLocalDirectory); -for (var i in libraryTargets) { - (function (i) { - var entry = librarySourceMap[i]; - var target = libraryTargets[i]; - var sources = [copyright].concat(entry.sources.map(function (s) { - return path.join(libraryDirectory, s); - })); - file(target, [builtLocalDirectory].concat(sources), function () { - concatenateFiles(target, sources); - }); - })(i); -} +/** @type {{ libs:string[], paths: Record }} */ +var libraries = readJson(path.resolve("./src/lib/libs.json")); +var libraryTargets = libraries.libs.map(function (lib) { + var sources = [copyright].concat(["header.d.ts", lib + ".d.ts"].map(function (s) { + return path.join(libraryDirectory, s); + })); + var target = path.join(builtLocalDirectory, libraries.paths[lib] || ("lib." + lib + ".d.ts")); + file(target, [builtLocalDirectory].concat(sources), function () { + concatenateFiles(target, sources); + }); + return target; +}); // Lib target to build the library files desc("Builds the library targets"); diff --git a/src/lib/default.es2015.d.ts b/src/lib/es2015.full.d.ts similarity index 97% rename from src/lib/default.es2015.d.ts rename to src/lib/es2015.full.d.ts index 14cff0fccc524..4652bbb84c557 100644 --- a/src/lib/default.es2015.d.ts +++ b/src/lib/es2015.full.d.ts @@ -2,8 +2,8 @@ /// /// /// -/// /// +/// /// /// /// diff --git a/src/lib/default.es2016.d.ts b/src/lib/es2016.full.d.ts similarity index 100% rename from src/lib/default.es2016.d.ts rename to src/lib/es2016.full.d.ts diff --git a/src/lib/default.es2017.d.ts b/src/lib/es2017.full.d.ts similarity index 100% rename from src/lib/default.es2017.d.ts rename to src/lib/es2017.full.d.ts diff --git a/src/lib/default.es5.d.ts b/src/lib/es5.full.d.ts similarity index 100% rename from src/lib/default.es5.d.ts rename to src/lib/es5.full.d.ts diff --git a/src/lib/default.esnext.d.ts b/src/lib/esnext.full.d.ts similarity index 100% rename from src/lib/default.esnext.d.ts rename to src/lib/esnext.full.d.ts diff --git a/src/lib/libs.json b/src/lib/libs.json index 9dd678aaebc8d..f7a1f2052145d 100644 --- a/src/lib/libs.json +++ b/src/lib/libs.json @@ -7,9 +7,9 @@ "es2017", "esnext", // Host only - "dom.generated=lib.dom.d.ts", + "dom.generated", "dom.iterable", - "webworker.generated=lib.webworker.d.ts", + "webworker.generated", "webworker.importscripts", "scripthost", // By-feature options @@ -30,10 +30,16 @@ "es2017.typedarrays", "esnext.asynciterable", // Default libraries - "default.es5=lib.d.ts", - "default.es2015=lib.es6.d.ts", - "default.es2016=lib.es2016.full.d.ts", - "default.es2017=lib.es2017.full.d.ts", - "default.esnext=lib.esnext.full.d.ts" - ] + "es5.full", + "es2015.full", + "es2016.full", + "es2017.full", + "esnext.full" + ], + "paths": { + "dom.generated": "lib.dom.d.ts", + "webworker.generated": "lib.webworker.d.ts", + "es5.full": "lib.d.ts", + "es2015.full": "lib.es6.d.ts" + } } \ No newline at end of file From a6508c65911687be8c85e71a513c566be29853d0 Mon Sep 17 00:00:00 2001 From: Ron Buckton Date: Wed, 8 Nov 2017 14:49:40 -0800 Subject: [PATCH 8/9] Fix merge conflict --- src/harness/unittests/tsserverProjectSystem.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/harness/unittests/tsserverProjectSystem.ts b/src/harness/unittests/tsserverProjectSystem.ts index 1a27b97feb730..c4618c38f235b 100644 --- a/src/harness/unittests/tsserverProjectSystem.ts +++ b/src/harness/unittests/tsserverProjectSystem.ts @@ -57,7 +57,7 @@ namespace ts.projectSystem { getLogFileName: (): string => undefined }; export const libFiles: FileOrFolder[] = [ - { path: "/a/lib/lib.d.ts", content: Harness.getDefaultLibraryFile(Harness.IO).content }, + { path: "/a/lib/lib.d.ts", content: Harness.getDefaultLibraryFile("lib.d.ts", Harness.IO).content }, { path: "/a/lib/lib.es5.d.ts", content: Harness.getNamedDefaultLibraryFile(Harness.IO, "es5").content }, { path: "/a/lib/lib.dom.d.ts", content: Harness.getNamedDefaultLibraryFile(Harness.IO, "dom").content }, { path: "/a/lib/lib.webworker.importscripts.d.ts", content: Harness.getNamedDefaultLibraryFile(Harness.IO, "webworker.importscripts").content }, From e1a9b74dce875898987e7869e31c5b874dafa906 Mon Sep 17 00:00:00 2001 From: Ron Buckton Date: Wed, 8 Nov 2017 16:59:09 -0800 Subject: [PATCH 9/9] Fix isRecognizedTripleSlashComment --- Gulpfile.ts | 4 +- src/compiler/parser.ts | 46 +++++++------------ src/compiler/utilities.ts | 44 ++++++++++++++---- tests/baselines/reference/1.0lib-noErrors.js | 1 + .../reference/typeReferenceDirectives1.js | 1 + .../reference/typeReferenceDirectives3.js | 1 + 6 files changed, 57 insertions(+), 40 deletions(-) diff --git a/Gulpfile.ts b/Gulpfile.ts index 12225b8a4720e..c691afcf333fa 100644 --- a/Gulpfile.ts +++ b/Gulpfile.ts @@ -282,8 +282,8 @@ gulp.task("importDefinitelyTypedTests", "Runs scripts/importDefinitelyTypedTests }); const libraries: { libs: string[], paths: Record } = readJson(path.resolve("./src/lib/libs.json")); -const libraryTargets = libraries.libs.map(function (lib) { - const sources = [copyright].concat(["header.d.ts", lib + ".d.ts"].map(s =>path.join(libraryDirectory, s))); +const libraryTargets = libraries.libs.map(lib => { + const sources = [copyright].concat(["header.d.ts", lib + ".d.ts"].map(s => path.join(libraryDirectory, s))); const target = path.join(builtLocalDirectory, libraries.paths[lib] || ("lib." + lib + ".d.ts")); gulp.task(target, /*help*/ false, [], () => gulp.src(sources) diff --git a/src/compiler/parser.ts b/src/compiler/parser.ts index 970a775646c83..15117ab546e5c 100644 --- a/src/compiler/parser.ts +++ b/src/compiler/parser.ts @@ -6122,39 +6122,27 @@ namespace ts { referencedFiles.push(referencePathMatchResult.fileReference); break; } + continue; } - else { - const amdModuleNameRegEx = /^\/\/\/\s*/i; - - const fullTripleSlashReferencePathOrAmdDependencyPathRegEx = /^\/\/\/\s*<(reference|amd-dependency)\s+path\s*=\s*('|")(.+?)\2.*?\/>/; - - export function isTripleSlashPathReferenceOrAmdDependency(comment: string) { - return fullTripleSlashReferencePathOrAmdDependencyPathRegEx.test(comment); - } + const fullTripleSlashReferenceRegEx = /^(\/\/\/\s*/i; export function getFileReferenceFromReferencePath(comment: string, commentRange: CommentRange): ReferencePathMatchResult | undefined { if (!simpleReferenceRegEx.test(comment)) { @@ -1908,6 +1903,37 @@ namespace ts { return { fileReference: { pos, end, fileName }, kind }; } + const fullTripleSlashAmdModuleNameRegEx = /^\/\/\/\s*/i; + + export function getAmdDependencyFromComment(comment: string): AmdDependency | undefined { + const match = fullTripleSlashAmdDependencyRegEx.exec(comment); + if (match) { + const path = match[2] || match[8]; + const name = match[4] || match[6]; + return { path, name }; + } + } + + const checkJsDirectiveRegEx = /^\/\/\/?\s*(@ts-check|@ts-nocheck)\s*$/i; + + export function getCheckJsDirectiveFromComment(comment: string, commentRange: CommentRange): CheckJsDirective | undefined { + const checkJsDirectiveMatchResult = checkJsDirectiveRegEx.exec(comment); + if (checkJsDirectiveMatchResult) { + return { + enabled: equateStringsCaseInsensitive(checkJsDirectiveMatchResult[1], "@ts-check"), + end: commentRange.end, + pos: commentRange.pos + }; + } + } + export function isKeyword(token: SyntaxKind): boolean { return SyntaxKind.FirstKeyword <= token && token <= SyntaxKind.LastKeyword; } diff --git a/tests/baselines/reference/1.0lib-noErrors.js b/tests/baselines/reference/1.0lib-noErrors.js index 1dcfa9743b2b8..ade0f4bf903b9 100644 --- a/tests/baselines/reference/1.0lib-noErrors.js +++ b/tests/baselines/reference/1.0lib-noErrors.js @@ -1158,3 +1158,4 @@ MERCHANTABLITY OR NON-INFRINGEMENT. See the Apache Version 2.0 License for specific language governing permissions and limitations under the License. ***************************************************************************** */ +/// diff --git a/tests/baselines/reference/typeReferenceDirectives1.js b/tests/baselines/reference/typeReferenceDirectives1.js index a0865e544a890..9ff2e66ff634e 100644 --- a/tests/baselines/reference/typeReferenceDirectives1.js +++ b/tests/baselines/reference/typeReferenceDirectives1.js @@ -10,6 +10,7 @@ interface A { } //// [app.js] +/// //// [app.d.ts] diff --git a/tests/baselines/reference/typeReferenceDirectives3.js b/tests/baselines/reference/typeReferenceDirectives3.js index b320e602237a5..28d57f93fe512 100644 --- a/tests/baselines/reference/typeReferenceDirectives3.js +++ b/tests/baselines/reference/typeReferenceDirectives3.js @@ -16,6 +16,7 @@ interface A { } //// [app.js] +/// ///