Skip to content

Commit de7fbc0

Browse files
Merge pull request microsoft#19542 from RyanCavanaugh/fix19533
Exclude legacy safelist files in external projects
2 parents 93f50d0 + 0d5dec9 commit de7fbc0

File tree

6 files changed

+89
-17
lines changed

6 files changed

+89
-17
lines changed

src/compiler/core.ts

+16-5
Original file line numberDiff line numberDiff line change
@@ -68,13 +68,13 @@ namespace ts {
6868
}
6969

7070
// The global Map object. This may not be available, so we must test for it.
71-
declare const Map: { new<T>(): Map<T> } | undefined;
71+
declare const Map: { new <T>(): Map<T> } | undefined;
7272
// Internet Explorer's Map doesn't support iteration, so don't use it.
7373
// tslint:disable-next-line no-in-operator variable-name
7474
const MapCtr = typeof Map !== "undefined" && "entries" in Map.prototype ? Map : shimMap();
7575

7676
// Keep the class inside a function so it doesn't get compiled if it's not used.
77-
function shimMap(): { new<T>(): Map<T> } {
77+
function shimMap(): { new <T>(): Map<T> } {
7878

7979
class MapIterator<T, U extends (string | T | [string, T])> {
8080
private data: MapLike<T>;
@@ -97,7 +97,7 @@ namespace ts {
9797
}
9898
}
9999

100-
return class<T> implements Map<T> {
100+
return class <T> implements Map<T> {
101101
private data = createDictionaryObject<T>();
102102
public size = 0;
103103

@@ -2635,6 +2635,17 @@ namespace ts {
26352635
return <T>(removeFileExtension(path) + newExtension);
26362636
}
26372637

2638+
/**
2639+
* Takes a string like "jquery-min.4.2.3" and returns "jquery"
2640+
*/
2641+
export function removeMinAndVersionNumbers(fileName: string) {
2642+
// Match a "." or "-" followed by a version number or 'min' at the end of the name
2643+
const trailingMinOrVersion = /[.-]((min)|(\d+(\.\d+)*))$/;
2644+
2645+
// The "min" or version may both be present, in either order, so try applying the above twice.
2646+
return fileName.replace(trailingMinOrVersion, "").replace(trailingMinOrVersion, "");
2647+
}
2648+
26382649
export interface ObjectAllocator {
26392650
getNodeConstructor(): new (kind: SyntaxKind, pos?: number, end?: number) => Node;
26402651
getTokenConstructor(): new <TKind extends SyntaxKind>(kind: TKind, pos?: number, end?: number) => Token<TKind>;
@@ -2835,7 +2846,7 @@ namespace ts {
28352846
return findBestPatternMatch(patterns, _ => _, candidate);
28362847
}
28372848

2838-
export function patternText({prefix, suffix}: Pattern): string {
2849+
export function patternText({ prefix, suffix }: Pattern): string {
28392850
return `${prefix}*${suffix}`;
28402851
}
28412852

@@ -2865,7 +2876,7 @@ namespace ts {
28652876
return matchedValue;
28662877
}
28672878

2868-
function isPatternMatch({prefix, suffix}: Pattern, candidate: string) {
2879+
function isPatternMatch({ prefix, suffix }: Pattern, candidate: string) {
28692880
return candidate.length >= prefix.length + suffix.length &&
28702881
startsWith(candidate, prefix) &&
28712882
endsWith(candidate, suffix);

src/harness/unittests/tsserverProjectSystem.ts

+36
Original file line numberDiff line numberDiff line change
@@ -1539,6 +1539,42 @@ namespace ts.projectSystem {
15391539
}
15401540
});
15411541

1542+
it("removes version numbers correctly", () => {
1543+
const testData: [string, string][] = [
1544+
["jquery-max", "jquery-max"],
1545+
["jquery.min", "jquery"],
1546+
["jquery-min.4.2.3", "jquery"],
1547+
["jquery.min.4.2.1", "jquery"],
1548+
["minimum", "minimum"],
1549+
["min", "min"],
1550+
["min.3.2", "min"],
1551+
["jquery", "jquery"]
1552+
];
1553+
for (const t of testData) {
1554+
assert.equal(removeMinAndVersionNumbers(t[0]), t[1], t[0]);
1555+
}
1556+
});
1557+
1558+
it("ignores files excluded by a legacy safe type list", () => {
1559+
const file1 = {
1560+
path: "/a/b/bliss.js",
1561+
content: "let x = 5"
1562+
};
1563+
const file2 = {
1564+
path: "/a/b/foo.js",
1565+
content: ""
1566+
};
1567+
const host = createServerHost([file1, file2, customTypesMap]);
1568+
const projectService = createProjectService(host);
1569+
try {
1570+
projectService.openExternalProject({ projectFileName: "project", options: {}, rootFiles: toExternalFiles([file1.path, file2.path]), typeAcquisition: { enable: true } });
1571+
const proj = projectService.externalProjects[0];
1572+
assert.deepEqual(proj.getFileNames(), [file2.path]);
1573+
} finally {
1574+
projectService.resetSafeList();
1575+
}
1576+
});
1577+
15421578
it("open file become a part of configured project if it is referenced from root file", () => {
15431579
const file1 = {
15441580
path: "/a/b/f1.ts",

src/harness/unittests/typingsInstaller.ts

+3-2
Original file line numberDiff line numberDiff line change
@@ -1057,11 +1057,12 @@ namespace ts.projectSystem {
10571057
const host = createServerHost([app, jquery, chroma]);
10581058
const logger = trackingLogger();
10591059
const result = JsTyping.discoverTypings(host, logger.log, [app.path, jquery.path, chroma.path], getDirectoryPath(<Path>app.path), safeList, emptyMap, { enable: true }, emptyArray);
1060-
assert.deepEqual(logger.finish(), [
1060+
const finish = logger.finish();
1061+
assert.deepEqual(finish, [
10611062
'Inferred typings from file names: ["jquery","chroma-js"]',
10621063
"Inferred typings from unresolved imports: []",
10631064
'Result: {"cachedTypingPaths":[],"newTypingNames":["jquery","chroma-js"],"filesToWatch":["/a/b/bower_components","/a/b/node_modules"]}',
1064-
]);
1065+
], finish.join("\r\n"));
10651066
assert.deepEqual(result.newTypingNames, ["jquery", "chroma-js"]);
10661067
});
10671068

src/server/editorServices.ts

+28-7
Original file line numberDiff line numberDiff line change
@@ -110,7 +110,7 @@ namespace ts.server {
110110

111111
export interface TypesMapFile {
112112
typesMap: SafeList;
113-
simpleMap: string[];
113+
simpleMap: { [libName: string]: string };
114114
}
115115

116116
/**
@@ -380,6 +380,7 @@ namespace ts.server {
380380

381381
private readonly hostConfiguration: HostConfiguration;
382382
private safelist: SafeList = defaultTypeSafeList;
383+
private legacySafelist: { [key: string]: string } = {};
383384

384385
private changedFiles: ScriptInfo[];
385386
private pendingProjectUpdates = createMap<Project>();
@@ -432,9 +433,12 @@ namespace ts.server {
432433
this.toCanonicalFileName = createGetCanonicalFileName(this.host.useCaseSensitiveFileNames);
433434
this.throttledOperations = new ThrottledOperations(this.host, this.logger);
434435

435-
if (opts.typesMapLocation) {
436+
if (this.typesMapLocation) {
436437
this.loadTypesMap();
437438
}
439+
else {
440+
this.logger.info("No types map provided; using the default");
441+
}
438442

439443
this.typingsInstaller.attach(this);
440444

@@ -524,10 +528,12 @@ namespace ts.server {
524528
}
525529
// raw is now fixed and ready
526530
this.safelist = raw.typesMap;
531+
this.legacySafelist = raw.simpleMap;
527532
}
528533
catch (e) {
529534
this.logger.info(`Error loading types map: ${e}`);
530535
this.safelist = defaultTypeSafeList;
536+
this.legacySafelist = {};
531537
}
532538
}
533539

@@ -1418,7 +1424,7 @@ namespace ts.server {
14181424
}
14191425
}
14201426

1421-
private createExternalProject(projectFileName: string, files: protocol.ExternalFile[], options: protocol.ExternalProjectCompilerOptions, typeAcquisition: TypeAcquisition) {
1427+
private createExternalProject(projectFileName: string, files: protocol.ExternalFile[], options: protocol.ExternalProjectCompilerOptions, typeAcquisition: TypeAcquisition, excludedFiles: NormalizedPath[]) {
14221428
const compilerOptions = convertCompilerOptions(options);
14231429
const project = new ExternalProject(
14241430
projectFileName,
@@ -1427,6 +1433,7 @@ namespace ts.server {
14271433
compilerOptions,
14281434
/*languageServiceEnabled*/ !this.exceededTotalSizeLimitForNonTsFiles(projectFileName, compilerOptions, files, externalFilePropertyReader),
14291435
options.compileOnSave === undefined ? true : options.compileOnSave);
1436+
project.excludedFiles = excludedFiles;
14301437

14311438
this.addFilesToNonInferredProjectAndUpdateGraph(project, files, externalFilePropertyReader, typeAcquisition);
14321439
this.externalProjects.push(project);
@@ -2197,7 +2204,7 @@ namespace ts.server {
21972204
const rule = this.safelist[name];
21982205
for (const root of normalizedNames) {
21992206
if (rule.match.test(root)) {
2200-
this.logger.info(`Excluding files based on rule ${name}`);
2207+
this.logger.info(`Excluding files based on rule ${name} matching file '${root}'`);
22012208

22022209
// If the file matches, collect its types packages and exclude rules
22032210
if (rule.types) {
@@ -2256,7 +2263,22 @@ namespace ts.server {
22562263
excludedFiles.push(normalizedNames[i]);
22572264
}
22582265
else {
2259-
filesToKeep.push(proj.rootFiles[i]);
2266+
let exclude = false;
2267+
if (typeAcquisition && (typeAcquisition.enable || typeAcquisition.enableAutoDiscovery)) {
2268+
const baseName = getBaseFileName(normalizedNames[i].toLowerCase());
2269+
if (fileExtensionIs(baseName, "js")) {
2270+
const inferredTypingName = removeFileExtension(baseName);
2271+
const cleanedTypingName = removeMinAndVersionNumbers(inferredTypingName);
2272+
if (this.legacySafelist[cleanedTypingName]) {
2273+
this.logger.info(`Excluded '${normalizedNames[i]}' because it matched ${cleanedTypingName} from the legacy safelist`);
2274+
excludedFiles.push(normalizedNames[i]);
2275+
exclude = true;
2276+
}
2277+
}
2278+
}
2279+
if (!exclude) {
2280+
filesToKeep.push(proj.rootFiles[i]);
2281+
}
22602282
}
22612283
}
22622284
proj.rootFiles = filesToKeep;
@@ -2364,8 +2386,7 @@ namespace ts.server {
23642386
else {
23652387
// no config files - remove the item from the collection
23662388
this.externalProjectToConfiguredProjectMap.delete(proj.projectFileName);
2367-
const newProj = this.createExternalProject(proj.projectFileName, rootFiles, proj.options, proj.typeAcquisition);
2368-
newProj.excludedFiles = excludedFiles;
2389+
this.createExternalProject(proj.projectFileName, rootFiles, proj.options, proj.typeAcquisition, excludedFiles);
23692390
}
23702391
if (!suppressRefreshOfInferredProjects) {
23712392
this.ensureProjectStructuresUptoDate(/*refreshInferredProjects*/ true);

src/services/jsTyping.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -183,7 +183,7 @@ namespace ts.JsTyping {
183183
if (!hasJavaScriptFileExtension(j)) return undefined;
184184

185185
const inferredTypingName = removeFileExtension(getBaseFileName(j.toLowerCase()));
186-
const cleanedTypingName = inferredTypingName.replace(/((?:\.|-)min(?=\.|$))|((?:-|\.)\d+)/g, "");
186+
const cleanedTypingName = removeMinAndVersionNumbers(inferredTypingName);
187187
return safeList.get(cleanedTypingName);
188188
});
189189
if (fromFileNames.length) {

tests/baselines/reference/api/tsserverlibrary.d.ts

+5-2
Original file line numberDiff line numberDiff line change
@@ -7433,7 +7433,9 @@ declare namespace ts.server {
74337433
}
74347434
interface TypesMapFile {
74357435
typesMap: SafeList;
7436-
simpleMap: string[];
7436+
simpleMap: {
7437+
[libName: string]: string;
7438+
};
74377439
}
74387440
function convertFormatOptions(protocolOptions: protocol.FormatCodeSettings): FormatCodeSettings;
74397441
function convertCompilerOptions(protocolOptions: protocol.ExternalProjectCompilerOptions): CompilerOptions & protocol.CompileOnSaveMixin;
@@ -7514,6 +7516,7 @@ declare namespace ts.server {
75147516
private readonly throttledOperations;
75157517
private readonly hostConfiguration;
75167518
private safelist;
7519+
private legacySafelist;
75177520
private changedFiles;
75187521
private pendingProjectUpdates;
75197522
private pendingInferredProjectUpdate;
@@ -7622,7 +7625,7 @@ declare namespace ts.server {
76227625
private findExternalProjectByProjectName(projectFileName);
76237626
private convertConfigFileContentToProjectOptions(configFilename, cachedDirectoryStructureHost);
76247627
private exceededTotalSizeLimitForNonTsFiles<T>(name, options, fileNames, propertyReader);
7625-
private createExternalProject(projectFileName, files, options, typeAcquisition);
7628+
private createExternalProject(projectFileName, files, options, typeAcquisition, excludedFiles);
76267629
private sendProjectTelemetry(projectKey, project, projectOptions?);
76277630
private addFilesToNonInferredProjectAndUpdateGraph<T>(project, files, propertyReader, typeAcquisition);
76287631
private createConfiguredProject(configFileName);

0 commit comments

Comments
 (0)