From f3c5029def53487af1ac0f53a717f4bc6e75fc03 Mon Sep 17 00:00:00 2001 From: Ryan Cavanaugh Date: Mon, 3 Apr 2017 23:03:14 -0700 Subject: [PATCH 01/12] Add tests and fix bugs --- .../unittests/tsserverProjectSystem.ts | 61 ++++++++++-- src/server/editorServices.ts | 93 ++++++++++++++++++- src/server/protocol.ts | 2 + src/server/session.ts | 6 ++ 4 files changed, 150 insertions(+), 12 deletions(-) diff --git a/src/harness/unittests/tsserverProjectSystem.ts b/src/harness/unittests/tsserverProjectSystem.ts index b78fa094571..e5b2d325411 100644 --- a/src/harness/unittests/tsserverProjectSystem.ts +++ b/src/harness/unittests/tsserverProjectSystem.ts @@ -17,6 +17,30 @@ namespace ts.projectSystem { }) }; + const typeMapList = { + path: "/typeMapList.json", + content: JSON.stringify({ + "jquery": { + // jquery files can have names like "jquery-1.10.2.min.js" (or "jquery.intellisense.js") + "match": "/jquery(-(\\.\\d+)+)?(\\.intellisense)?(\\.min)?\\.js$", + "types": ["jquery"] + }, + "WinJS": { + "match": "^(.*/winjs)/base\\.js$", // If the winjs/base.js file is found.. + "exclude": [["^", 1, "/.*"]], // ..then exclude all files under the winjs folder + "types": ["winjs"] // And fetch the @types package for WinJS + }, + "Office Nuget": { + "match": "^(.*/1/office)/excel\\.debug\\.js$", // Office NuGet package is installed under a "1/office" folder + "exclude": [["^", 1, "/.*"]], // Exclude that whole folder if the file indicated above is found in it + "types": ["office"] // @types package to fetch instead + }, + "Minified files": { + "match": "^.*\\.min\\.js$" // Catch-all for minified files. Default exclude is the matched file. + } + }) + }; + export interface PostExecAction { readonly success: boolean; readonly callback: TI.RequestCompletedAction; @@ -1445,6 +1469,25 @@ namespace ts.projectSystem { checkProjectActualFiles(projectService.inferredProjects[1], [file3.path]); }); + it("ignores files excluded by the safe type list", () => { + const file1 = { + path: "/a/b/f1.ts", + content: "export let x = 5" + }; + const office = { + path: "lib/1/office/excel.debug.js", + content: "whoa do @@ not parse me ok thanks!!!" + }; + const host = createServerHost([typeMapList, file1, office]); + const projectService = createProjectService(host); + projectService.loadSafeList(typeMapList.path); + + projectService.openExternalProject({ projectFileName: "project", options: {}, rootFiles: toExternalFiles([file1.path, office.path]) }); + const proj = projectService.externalProjects[0]; + assert.deepEqual(proj.getFileNames(true), [file1.path]); + assert.deepEqual(proj.getTypeAcquisition().include, ["office"]); + }); + it("open file become a part of configured project if it is referenced from root file", () => { const file1 = { path: "/a/b/f1.ts", @@ -1695,7 +1738,7 @@ namespace ts.projectSystem { checkProjectActualFiles(projectService.inferredProjects[1], [file2.path]); }); - it ("loading files with correct priority", () => { + it("loading files with correct priority", () => { const f1 = { path: "/a/main.ts", content: "let x = 1" @@ -1720,14 +1763,14 @@ namespace ts.projectSystem { }); projectService.openClientFile(f1.path); projectService.checkNumberOfProjects({ configuredProjects: 1 }); - checkProjectActualFiles(projectService.configuredProjects[0], [ f1.path ]); + checkProjectActualFiles(projectService.configuredProjects[0], [f1.path]); projectService.closeClientFile(f1.path); projectService.openClientFile(f2.path); projectService.checkNumberOfProjects({ configuredProjects: 1, inferredProjects: 1 }); - checkProjectActualFiles(projectService.configuredProjects[0], [ f1.path ]); - checkProjectActualFiles(projectService.inferredProjects[0], [ f2.path ]); + checkProjectActualFiles(projectService.configuredProjects[0], [f1.path]); + checkProjectActualFiles(projectService.inferredProjects[0], [f2.path]); }); it("tsconfig script block support", () => { @@ -1845,7 +1888,7 @@ namespace ts.projectSystem { // #3. Ensure no errors when compiler options aren't specified const config3 = { path: "/a/b/tsconfig.json", - content: JSON.stringify({ }) + content: JSON.stringify({}) }; host = createServerHost([file1, file2, config3, libFile], { executingFilePath: combinePaths(getDirectoryPath(libFile.path), "tsc.js") }); @@ -3381,13 +3424,13 @@ namespace ts.projectSystem { assert.equal((response)[0].projectUsesOutFile, expectedUsesOutFile, "usesOutFile"); } - it ("projectUsesOutFile should not be returned if not set", () => { + it("projectUsesOutFile should not be returned if not set", () => { test({}, /*expectedUsesOutFile*/ false); }); - it ("projectUsesOutFile should be true if outFile is set", () => { + it("projectUsesOutFile should be true if outFile is set", () => { test({ outFile: "/a/out.js" }, /*expectedUsesOutFile*/ true); }); - it ("projectUsesOutFile should be true if out is set", () => { + it("projectUsesOutFile should be true if out is set", () => { test({ out: "/a/out.js" }, /*expectedUsesOutFile*/ true); }); }); @@ -3468,7 +3511,7 @@ namespace ts.projectSystem { const cancellationToken = new TestServerCancellationToken(); const host = createServerHost([f1, config]); - const session = createSession(host, /*typingsInstaller*/ undefined, () => {}, cancellationToken); + const session = createSession(host, /*typingsInstaller*/ undefined, () => { }, cancellationToken); { session.executeCommandSeq({ command: "open", diff --git a/src/server/editorServices.ts b/src/server/editorServices.ts index 30c7a87dc74..78108debef7 100644 --- a/src/server/editorServices.ts +++ b/src/server/editorServices.ts @@ -35,6 +35,10 @@ namespace ts.server { (event: ProjectServiceEvent): void; } + export interface SafeList { + [name: string]: { match: RegExp, exclude?: Array>, types?: string[] }; + } + function prepareConvertersForEnumLikeCompilerOptions(commandLineOptions: CommandLineOption[]): Map> { const map: Map> = createMap>(); for (const option of commandLineOptions) { @@ -259,6 +263,7 @@ namespace ts.server { private readonly throttledOperations: ThrottledOperations; private readonly hostConfiguration: HostConfiguration; + private static safelist: SafeList = {}; private changedFiles: ScriptInfo[]; @@ -284,8 +289,6 @@ namespace ts.server { this.typingsCache = new TypingsCache(this.typingsInstaller); - // ts.disableIncrementalParsing = true; - this.hostConfiguration = { formatCodeOptions: getDefaultFormatCodeSettings(this.host), hostInfo: "Unknown host", @@ -831,7 +834,7 @@ namespace ts.server { getDirectoryPath(configFilename), /*existingOptions*/ {}, configFilename, - /*resolutionStack*/ [], + /*resolutionStack*/[], this.hostConfiguration.extraFileExtensions); if (parsedCommandLine.errors.length) { @@ -1399,6 +1402,87 @@ namespace ts.server { this.refreshInferredProjects(); } + /** Makes a filename safe to insert in a RegExp */ + private static filenameEscapeRegexp = /[-\/\\^$*+?.()|[\]{}]/g; + private static escapeFilenameForRegex(filename: string) { + return filename.replace(this.filenameEscapeRegexp, "\\$&"); + } + + loadSafeList(fileName: string): void { + const raw: SafeList = JSON.parse(this.host.readFile(fileName, "utf-8")); + // Parse the regexps + for (const k of Object.keys(raw)) { + raw[k].match = new RegExp(raw[k].match as {} as string, "gi"); + } + // raw is now fixed and ready + ProjectService.safelist = raw; + } + + applySafeList(proj: protocol.ExternalProject): void { + const { rootFiles, typeAcquisition } = proj; + const types = (typeAcquisition && typeAcquisition.include) || []; + + const excludeRules: string[] = []; + + for (const name of Object.keys(ProjectService.safelist)) { + const rule = ProjectService.safelist[name]; + for (const root of rootFiles) { + if (rule.match.test(root.fileName)) { + this.logger.info(`Excluding files based on rule ${name}`); + + // If the file matches, collect its types packages and exclude rules + if (rule.types) { + for (const type of rule.types) { + if (types.indexOf(type) < 0) { + types.push(type); + } + } + } + + if (rule.exclude) { + for (const exclude of rule.exclude) { + const processedRule = root.fileName.replace(rule.match, (...groups: Array) => { + return exclude.map(groupNumberOrString => { + // RegExp group numbers are 1-based, but the first element in groups + // is actually the original string, so it all works out in the end. + if (typeof groupNumberOrString === "number") { + if (typeof groups[groupNumberOrString] !== "string") { + // Specification was wrong - exclude nothing! + this.logger.info(`Incorrect RegExp specification in safelist rule ${name} - not enough groups`); + // * can't appear in a filename; escape it because it's feeding into a RegExp + return "\\*"; + } + return ProjectService.escapeFilenameForRegex(groups[groupNumberOrString]); + } + return groupNumberOrString; + }).join(""); + }); + + if (excludeRules.indexOf(processedRule) == -1) { + excludeRules.push(processedRule); + } + } + } + else { + // If not rules listed, add the default rule to exclude the matched file + if (excludeRules.indexOf(root.fileName) < 0) { + excludeRules.push(root.fileName); + } + } + } + } + + // Copy back this field into the project if needed + if (types.length > 0) { + proj.typeAcquisition = proj.typeAcquisition || { }; + proj.typeAcquisition.include = types; + } + } + + const excludeRegexes = excludeRules.map(e => new RegExp(e, "i")); + proj.rootFiles = proj.rootFiles.filter(file => !excludeRegexes.some(re => re.test(file.fileName))); + } + openExternalProject(proj: protocol.ExternalProject, suppressRefreshOfInferredProjects = false): void { // typingOptions has been deprecated and is only supported for backward compatibility // purposes. It should be removed in future releases - use typeAcquisition instead. @@ -1406,6 +1490,9 @@ namespace ts.server { const typeAcquisition = convertEnableAutoDiscoveryToEnable(proj.typingOptions); proj.typeAcquisition = typeAcquisition; } + + this.applySafeList(proj); + let tsConfigFiles: NormalizedPath[]; const rootFiles: protocol.ExternalFile[] = []; for (const file of proj.rootFiles) { diff --git a/src/server/protocol.ts b/src/server/protocol.ts index 0bf13df7f4b..bc96099f5e1 100644 --- a/src/server/protocol.ts +++ b/src/server/protocol.ts @@ -91,8 +91,10 @@ namespace ts.server.protocol { /* @internal */ export type BreakpointStatement = "breakpointStatement"; export type CompilerOptionsForInferredProjects = "compilerOptionsForInferredProjects"; + export type LoadTypesMap = "loadTypesMap"; export type GetCodeFixes = "getCodeFixes"; /* @internal */ + /* @internal */ export type GetCodeFixesFull = "getCodeFixes-full"; export type GetSupportedCodeFixes = "getSupportedCodeFixes"; } diff --git a/src/server/session.ts b/src/server/session.ts index 4cdddc831a1..97c221a8add 100644 --- a/src/server/session.ts +++ b/src/server/session.ts @@ -186,6 +186,7 @@ namespace ts.server { /* @internal */ export const BreakpointStatement: protocol.CommandTypes.BreakpointStatement = "breakpointStatement"; export const CompilerOptionsForInferredProjects: protocol.CommandTypes.CompilerOptionsForInferredProjects = "compilerOptionsForInferredProjects"; + export const LoadTypesMap: protocol.CommandTypes.LoadTypesMap = "loadTypesMap"; export const GetCodeFixes: protocol.CommandTypes.GetCodeFixes = "getCodeFixes"; /* @internal */ export const GetCodeFixesFull: protocol.CommandTypes.GetCodeFixesFull = "getCodeFixes-full"; @@ -1765,6 +1766,11 @@ namespace ts.server { this.setCompilerOptionsForInferredProjects(request.arguments); return this.requiredResponse(true); }, + [CommandNames.LoadTypesMap]: (request: protocol.FileRequest) => { + const loadArgs = request.arguments; + this.projectService.loadSafeList(loadArgs.file); + return this.notRequired(); + }, [CommandNames.ProjectInfo]: (request: protocol.ProjectInfoRequest) => { return this.requiredResponse(this.getProjectInfo(request.arguments)); }, From 54b1c34a4435d0f9c6581f241111b9d82914654d Mon Sep 17 00:00:00 2001 From: Ryan Cavanaugh Date: Tue, 4 Apr 2017 10:17:17 -0700 Subject: [PATCH 02/12] Fix failing test --- src/harness/unittests/typingsInstaller.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/harness/unittests/typingsInstaller.ts b/src/harness/unittests/typingsInstaller.ts index d6f8a2d01fc..af95874a32c 100644 --- a/src/harness/unittests/typingsInstaller.ts +++ b/src/harness/unittests/typingsInstaller.ts @@ -381,12 +381,12 @@ namespace ts.projectSystem { const p = projectService.externalProjects[0]; projectService.checkNumberOfProjects({ externalProjects: 1 }); - checkProjectActualFiles(p, [file1.path, file2.path]); + checkProjectActualFiles(p, [file2.path]); installer.checkPendingCommands(/*expectedCount*/ 0); checkNumberOfProjects(projectService, { externalProjects: 1 }); - checkProjectActualFiles(p, [file1.path, file2.path]); + checkProjectActualFiles(p, [file2.path]); }); it("external project - with type acquisition, with only js, d.ts files", () => { From a4c2f78b75e29486f66f4270968b24790d07215f Mon Sep 17 00:00:00 2001 From: Ryan Cavanaugh Date: Wed, 5 Apr 2017 09:24:35 -0700 Subject: [PATCH 03/12] Update tsserverProjectSystem.ts --- src/harness/unittests/tsserverProjectSystem.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/harness/unittests/tsserverProjectSystem.ts b/src/harness/unittests/tsserverProjectSystem.ts index e5b2d325411..fb91aa9a431 100644 --- a/src/harness/unittests/tsserverProjectSystem.ts +++ b/src/harness/unittests/tsserverProjectSystem.ts @@ -22,7 +22,7 @@ namespace ts.projectSystem { content: JSON.stringify({ "jquery": { // jquery files can have names like "jquery-1.10.2.min.js" (or "jquery.intellisense.js") - "match": "/jquery(-(\\.\\d+)+)?(\\.intellisense)?(\\.min)?\\.js$", + "match": "/jquery(-(\\.?\\d+)+)?(\\.intellisense)?(\\.min)?\\.js$", "types": ["jquery"] }, "WinJS": { @@ -3793,4 +3793,4 @@ namespace ts.projectSystem { assert.isUndefined(project.getCompilerOptions().maxNodeModuleJsDepth); }); }); -} \ No newline at end of file +} From 376cea648ef31136ef0666b3e4ffdedf7881ef44 Mon Sep 17 00:00:00 2001 From: Ryan Cavanaugh Date: Wed, 5 Apr 2017 12:56:14 -0700 Subject: [PATCH 04/12] Allow resetting so tests pass --- .../unittests/tsserverProjectSystem.ts | 41 +++++++------------ src/server/editorServices.ts | 24 ++++++++++- 2 files changed, 38 insertions(+), 27 deletions(-) diff --git a/src/harness/unittests/tsserverProjectSystem.ts b/src/harness/unittests/tsserverProjectSystem.ts index fb91aa9a431..370c1a4cf22 100644 --- a/src/harness/unittests/tsserverProjectSystem.ts +++ b/src/harness/unittests/tsserverProjectSystem.ts @@ -17,27 +17,13 @@ namespace ts.projectSystem { }) }; - const typeMapList = { + const customSafeList = { path: "/typeMapList.json", content: JSON.stringify({ - "jquery": { - // jquery files can have names like "jquery-1.10.2.min.js" (or "jquery.intellisense.js") - "match": "/jquery(-(\\.?\\d+)+)?(\\.intellisense)?(\\.min)?\\.js$", - "types": ["jquery"] + "quack": { + "match": "/duckquack-(\\d+)\\.min\\.js", + "types": ["duck-types"] }, - "WinJS": { - "match": "^(.*/winjs)/base\\.js$", // If the winjs/base.js file is found.. - "exclude": [["^", 1, "/.*"]], // ..then exclude all files under the winjs folder - "types": ["winjs"] // And fetch the @types package for WinJS - }, - "Office Nuget": { - "match": "^(.*/1/office)/excel\\.debug\\.js$", // Office NuGet package is installed under a "1/office" folder - "exclude": [["^", 1, "/.*"]], // Exclude that whole folder if the file indicated above is found in it - "types": ["office"] // @types package to fetch instead - }, - "Minified files": { - "match": "^.*\\.min\\.js$" // Catch-all for minified files. Default exclude is the matched file. - } }) }; @@ -1475,17 +1461,20 @@ namespace ts.projectSystem { content: "export let x = 5" }; const office = { - path: "lib/1/office/excel.debug.js", + path: "/lib/duckquack-3.min.js", content: "whoa do @@ not parse me ok thanks!!!" }; - const host = createServerHost([typeMapList, file1, office]); + const host = createServerHost([customSafeList, file1, office]); const projectService = createProjectService(host); - projectService.loadSafeList(typeMapList.path); - - projectService.openExternalProject({ projectFileName: "project", options: {}, rootFiles: toExternalFiles([file1.path, office.path]) }); - const proj = projectService.externalProjects[0]; - assert.deepEqual(proj.getFileNames(true), [file1.path]); - assert.deepEqual(proj.getTypeAcquisition().include, ["office"]); + projectService.loadSafeList(customSafeList.path); + try { + projectService.openExternalProject({ projectFileName: "project", options: {}, rootFiles: toExternalFiles([file1.path, office.path]) }); + const proj = projectService.externalProjects[0]; + assert.deepEqual(proj.getFileNames(true), [file1.path]); + assert.deepEqual(proj.getTypeAcquisition().include, ["duck-types"]); + } finally { + projectService.resetSafeList(); + } }); it("open file become a part of configured project if it is referenced from root file", () => { diff --git a/src/server/editorServices.ts b/src/server/editorServices.ts index 78108debef7..3d1406f3e43 100644 --- a/src/server/editorServices.ts +++ b/src/server/editorServices.ts @@ -61,6 +61,24 @@ namespace ts.server { "smart": IndentStyle.Smart }); + const defaultTypeSafeList: SafeList = { + "jquery": { + // jquery files can have names like "jquery-1.10.2.min.js" (or "jquery.intellisense.js") + "match": /jquery(-(\.?\d+)+)?(\.intellisense)?(\.min)?\.js$/gi, + "types": ["jquery"] + }, + "WinJS": { + "match": /^(.*\/winjs)\/base\.js$/gi, // If the winjs/base.js file is found.. + "exclude": [["^", 1, "/.*"]], // ..then exclude all files under the winjs folder + "types": ["winjs"] // And fetch the @types package for WinJS + }, + "Office Nuget": { + "match": /^(.*\/1\/office)\/excel\.debug\.js$/gi, // Office NuGet package is installed under a "1/office" folder + "exclude": [["^", 1, "/.*"]], // Exclude that whole folder if the file indicated above is found in it + "types": ["office"] // @types package to fetch instead + } + }; + export function convertFormatOptions(protocolOptions: protocol.FormatCodeSettings): FormatCodeSettings { if (typeof protocolOptions.indentStyle === "string") { protocolOptions.indentStyle = indentStyle.get(protocolOptions.indentStyle.toLowerCase()); @@ -263,7 +281,7 @@ namespace ts.server { private readonly throttledOperations: ThrottledOperations; private readonly hostConfiguration: HostConfiguration; - private static safelist: SafeList = {}; + private static safelist: SafeList = defaultTypeSafeList; private changedFiles: ScriptInfo[]; @@ -1408,6 +1426,10 @@ namespace ts.server { return filename.replace(this.filenameEscapeRegexp, "\\$&"); } + resetSafeList(): void { + ProjectService.safelist = defaultTypeSafeList; + } + loadSafeList(fileName: string): void { const raw: SafeList = JSON.parse(this.host.readFile(fileName, "utf-8")); // Parse the regexps From 125c012c8d3e1ecdee9b264abe6854e1fa9bb100 Mon Sep 17 00:00:00 2001 From: Ryan Cavanaugh Date: Wed, 5 Apr 2017 13:13:23 -0700 Subject: [PATCH 05/12] Lint --- 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 370c1a4cf22..ccd46004540 100644 --- a/src/harness/unittests/tsserverProjectSystem.ts +++ b/src/harness/unittests/tsserverProjectSystem.ts @@ -1470,7 +1470,7 @@ namespace ts.projectSystem { try { projectService.openExternalProject({ projectFileName: "project", options: {}, rootFiles: toExternalFiles([file1.path, office.path]) }); const proj = projectService.externalProjects[0]; - assert.deepEqual(proj.getFileNames(true), [file1.path]); + assert.deepEqual(proj.getFileNames(/*excludeFilesFromExternalLibraries*/ true), [file1.path]); assert.deepEqual(proj.getTypeAcquisition().include, ["duck-types"]); } finally { projectService.resetSafeList(); From c164730a01719c78268767704e121ca383551aa6 Mon Sep 17 00:00:00 2001 From: Ryan Cavanaugh Date: Thu, 6 Apr 2017 12:39:25 -0700 Subject: [PATCH 06/12] Add Kendo; fix Office --- src/server/editorServices.ts | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/src/server/editorServices.ts b/src/server/editorServices.ts index 3d1406f3e43..21330fb319c 100644 --- a/src/server/editorServices.ts +++ b/src/server/editorServices.ts @@ -72,7 +72,15 @@ namespace ts.server { "exclude": [["^", 1, "/.*"]], // ..then exclude all files under the winjs folder "types": ["winjs"] // And fetch the @types package for WinJS }, - "Office Nuget": { + "Kendo": { + "match": /^(.*\/kendo\/.+\.js$/gi, + "exclude": [["^", 1, "/.*"]] + }, + "Office Loc Files": { + "match": /^(.*\/office\/1\/\w\w-\w\w\/).+\.js$/gi, // Office NuGet package is installed under a "office/1" folder + "exclude": [["^", 1, "/.*"]] // Exclude that whole folder if the file indicated above is found in it + }, + "Office Nuget": { // TODO this one "match": /^(.*\/1\/office)\/excel\.debug\.js$/gi, // Office NuGet package is installed under a "1/office" folder "exclude": [["^", 1, "/.*"]], // Exclude that whole folder if the file indicated above is found in it "types": ["office"] // @types package to fetch instead From 857b762edde9c72ad8fcc22acae5f6e34f0f1cd2 Mon Sep 17 00:00:00 2001 From: Ryan Cavanaugh Date: Thu, 6 Apr 2017 12:50:22 -0700 Subject: [PATCH 07/12] Fixes --- src/server/editorServices.ts | 14 +++++--------- 1 file changed, 5 insertions(+), 9 deletions(-) diff --git a/src/server/editorServices.ts b/src/server/editorServices.ts index 21330fb319c..12b3f84e050 100644 --- a/src/server/editorServices.ts +++ b/src/server/editorServices.ts @@ -64,24 +64,20 @@ namespace ts.server { const defaultTypeSafeList: SafeList = { "jquery": { // jquery files can have names like "jquery-1.10.2.min.js" (or "jquery.intellisense.js") - "match": /jquery(-(\.?\d+)+)?(\.intellisense)?(\.min)?\.js$/gi, + "match": /jquery(-(\.?\d+)+)?(\.intellisense)?(\.min)?\.js$/i, "types": ["jquery"] }, "WinJS": { - "match": /^(.*\/winjs)\/base\.js$/gi, // If the winjs/base.js file is found.. + "match": /^(.*\/winjs)\/base\.js$/i, // If the winjs/base.js file is found.. "exclude": [["^", 1, "/.*"]], // ..then exclude all files under the winjs folder "types": ["winjs"] // And fetch the @types package for WinJS }, "Kendo": { - "match": /^(.*\/kendo\/.+\.js$/gi, + "match": /^(.*\/kendo\/.+\.js$/i, "exclude": [["^", 1, "/.*"]] }, - "Office Loc Files": { - "match": /^(.*\/office\/1\/\w\w-\w\w\/).+\.js$/gi, // Office NuGet package is installed under a "office/1" folder - "exclude": [["^", 1, "/.*"]] // Exclude that whole folder if the file indicated above is found in it - }, - "Office Nuget": { // TODO this one - "match": /^(.*\/1\/office)\/excel\.debug\.js$/gi, // Office NuGet package is installed under a "1/office" folder + "Office Nuget": { + "match": /^(.*\/1\/office)\/excel\.debug\.js$/i, // Office NuGet package is installed under a "1/office" folder "exclude": [["^", 1, "/.*"]], // Exclude that whole folder if the file indicated above is found in it "types": ["office"] // @types package to fetch instead } From 439f1c77bd5a152f4453dca2a13a8b8e0c7e23e5 Mon Sep 17 00:00:00 2001 From: Ryan Cavanaugh Date: Thu, 6 Apr 2017 13:19:22 -0700 Subject: [PATCH 08/12] Fix regex for real this time --- src/server/editorServices.ts | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/src/server/editorServices.ts b/src/server/editorServices.ts index 12b3f84e050..1dbf52b56a9 100644 --- a/src/server/editorServices.ts +++ b/src/server/editorServices.ts @@ -68,16 +68,20 @@ namespace ts.server { "types": ["jquery"] }, "WinJS": { - "match": /^(.*\/winjs)\/base\.js$/i, // If the winjs/base.js file is found.. + // e.g. c:/temp/UWApp1/lib/winjs-4.0.1/js/base.js + "match": /^(.*\/winjs-[.\d]+)\/js\/base\.js$/i, // If the winjs/base.js file is found.. "exclude": [["^", 1, "/.*"]], // ..then exclude all files under the winjs folder "types": ["winjs"] // And fetch the @types package for WinJS }, "Kendo": { - "match": /^(.*\/kendo\/.+\.js$/i, - "exclude": [["^", 1, "/.*"]] + // e.g. /Kendo3/wwwroot/lib/kendo/kendo.all.min.js + "match": /^(.*\/kendo)\/kendo\.all\.min\.js$/i, + "exclude": [["^", 1, "/.*"]], + "types": ["kendo-ui"] }, "Office Nuget": { - "match": /^(.*\/1\/office)\/excel\.debug\.js$/i, // Office NuGet package is installed under a "1/office" folder + // e.g. /scripts/Office/1/excel-15.debug.js + "match": /^(.*\/office\/1)\/excel-\d+\.debug\.js$/i, // Office NuGet package is installed under a "1/office" folder "exclude": [["^", 1, "/.*"]], // Exclude that whole folder if the file indicated above is found in it "types": ["office"] // @types package to fetch instead } From 47cf7caf6e08852690058472d480cf10a4945f96 Mon Sep 17 00:00:00 2001 From: Ryan Cavanaugh Date: Fri, 7 Apr 2017 12:49:46 -0700 Subject: [PATCH 09/12] Fix path escaping and slash normalization --- src/server/editorServices.ts | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/src/server/editorServices.ts b/src/server/editorServices.ts index 1dbf52b56a9..7dc17a385d3 100644 --- a/src/server/editorServices.ts +++ b/src/server/editorServices.ts @@ -1454,10 +1454,12 @@ namespace ts.server { const excludeRules: string[] = []; + const normalizedNames = rootFiles.map(f => normalizeSlashes(f.fileName)); + for (const name of Object.keys(ProjectService.safelist)) { const rule = ProjectService.safelist[name]; - for (const root of rootFiles) { - if (rule.match.test(root.fileName)) { + for (const root of normalizedNames) { + if (rule.match.test(root)) { this.logger.info(`Excluding files based on rule ${name}`); // If the file matches, collect its types packages and exclude rules @@ -1471,7 +1473,7 @@ namespace ts.server { if (rule.exclude) { for (const exclude of rule.exclude) { - const processedRule = root.fileName.replace(rule.match, (...groups: Array) => { + const processedRule = root.replace(rule.match, (...groups: Array) => { return exclude.map(groupNumberOrString => { // RegExp group numbers are 1-based, but the first element in groups // is actually the original string, so it all works out in the end. @@ -1488,15 +1490,16 @@ namespace ts.server { }).join(""); }); - if (excludeRules.indexOf(processedRule) == -1) { + if (excludeRules.indexOf(processedRule) === -1) { excludeRules.push(processedRule); } } } else { // If not rules listed, add the default rule to exclude the matched file - if (excludeRules.indexOf(root.fileName) < 0) { - excludeRules.push(root.fileName); + const escaped = ProjectService.escapeFilenameForRegex(root); + if (excludeRules.indexOf(escaped) < 0) { + excludeRules.push(escaped); } } } @@ -1510,7 +1513,7 @@ namespace ts.server { } const excludeRegexes = excludeRules.map(e => new RegExp(e, "i")); - proj.rootFiles = proj.rootFiles.filter(file => !excludeRegexes.some(re => re.test(file.fileName))); + proj.rootFiles = proj.rootFiles.filter((_file, index) => !excludeRegexes.some(re => re.test(normalizedNames[index]))); } openExternalProject(proj: protocol.ExternalProject, suppressRefreshOfInferredProjects = false): void { From fc988a16ee3300b8fa61cdaa758f0f0ed73e0ab0 Mon Sep 17 00:00:00 2001 From: Ryan Cavanaugh Date: Fri, 7 Apr 2017 16:58:13 -0700 Subject: [PATCH 10/12] Pass correct regex flag --- src/server/editorServices.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/server/editorServices.ts b/src/server/editorServices.ts index 7dc17a385d3..6996f0c4df9 100644 --- a/src/server/editorServices.ts +++ b/src/server/editorServices.ts @@ -1442,7 +1442,7 @@ namespace ts.server { const raw: SafeList = JSON.parse(this.host.readFile(fileName, "utf-8")); // Parse the regexps for (const k of Object.keys(raw)) { - raw[k].match = new RegExp(raw[k].match as {} as string, "gi"); + raw[k].match = new RegExp(raw[k].match as {} as string, "i"); } // raw is now fixed and ready ProjectService.safelist = raw; From 2b09e54baae89270445b5927f5bcfd27d410ff16 Mon Sep 17 00:00:00 2001 From: Ryan Cavanaugh Date: Fri, 7 Apr 2017 16:58:21 -0700 Subject: [PATCH 11/12] Remove bad merge + unused cmd name --- src/server/protocol.ts | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/server/protocol.ts b/src/server/protocol.ts index bc96099f5e1..0bf13df7f4b 100644 --- a/src/server/protocol.ts +++ b/src/server/protocol.ts @@ -91,10 +91,8 @@ namespace ts.server.protocol { /* @internal */ export type BreakpointStatement = "breakpointStatement"; export type CompilerOptionsForInferredProjects = "compilerOptionsForInferredProjects"; - export type LoadTypesMap = "loadTypesMap"; export type GetCodeFixes = "getCodeFixes"; /* @internal */ - /* @internal */ export type GetCodeFixesFull = "getCodeFixes-full"; export type GetSupportedCodeFixes = "getSupportedCodeFixes"; } From a874567d46c3a6440f1fea5c75ec174026ea553d Mon Sep 17 00:00:00 2001 From: Ryan Cavanaugh Date: Fri, 7 Apr 2017 16:58:32 -0700 Subject: [PATCH 12/12] Remove tsserver command for loading different map --- src/server/session.ts | 6 ------ 1 file changed, 6 deletions(-) diff --git a/src/server/session.ts b/src/server/session.ts index 97c221a8add..4cdddc831a1 100644 --- a/src/server/session.ts +++ b/src/server/session.ts @@ -186,7 +186,6 @@ namespace ts.server { /* @internal */ export const BreakpointStatement: protocol.CommandTypes.BreakpointStatement = "breakpointStatement"; export const CompilerOptionsForInferredProjects: protocol.CommandTypes.CompilerOptionsForInferredProjects = "compilerOptionsForInferredProjects"; - export const LoadTypesMap: protocol.CommandTypes.LoadTypesMap = "loadTypesMap"; export const GetCodeFixes: protocol.CommandTypes.GetCodeFixes = "getCodeFixes"; /* @internal */ export const GetCodeFixesFull: protocol.CommandTypes.GetCodeFixesFull = "getCodeFixes-full"; @@ -1766,11 +1765,6 @@ namespace ts.server { this.setCompilerOptionsForInferredProjects(request.arguments); return this.requiredResponse(true); }, - [CommandNames.LoadTypesMap]: (request: protocol.FileRequest) => { - const loadArgs = request.arguments; - this.projectService.loadSafeList(loadArgs.file); - return this.notRequired(); - }, [CommandNames.ProjectInfo]: (request: protocol.ProjectInfoRequest) => { return this.requiredResponse(this.getProjectInfo(request.arguments)); },