Skip to content

Commit 15e15ab

Browse files
Merge pull request microsoft#17468 from RyanCavanaugh/newTypesMap
Types Map
2 parents a6015e9 + 43b8ce6 commit 15e15ab

File tree

14 files changed

+646
-41
lines changed

14 files changed

+646
-41
lines changed

Gulpfile.ts

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -463,10 +463,11 @@ gulp.task(serverFile, /*help*/ false, [servicesFile, typingsInstallerJs, cancell
463463
.pipe(gulp.dest("src/server"));
464464
});
465465

466+
const typesMapJson = path.join(builtLocalDirectory, "typesMap.json");
466467
const tsserverLibraryFile = path.join(builtLocalDirectory, "tsserverlibrary.js");
467468
const tsserverLibraryDefinitionFile = path.join(builtLocalDirectory, "tsserverlibrary.d.ts");
468469

469-
gulp.task(tsserverLibraryFile, /*help*/ false, [servicesFile], (done) => {
470+
gulp.task(tsserverLibraryFile, /*help*/ false, [servicesFile, typesMapJson], (done) => {
470471
const serverLibraryProject = tsc.createProject("src/server/tsconfig.library.json", getCompilerSettings({}, /*useBuiltCompiler*/ true));
471472
const {js, dts}: { js: NodeJS.ReadableStream, dts: NodeJS.ReadableStream } = serverLibraryProject.src()
472473
.pipe(sourcemaps.init())
@@ -485,6 +486,15 @@ gulp.task(tsserverLibraryFile, /*help*/ false, [servicesFile], (done) => {
485486
]);
486487
});
487488

489+
gulp.task(typesMapJson, /*help*/ false, [], () => {
490+
return gulp.src("src/server/typesMap.json")
491+
.pipe(insert.transform((contents, file) => {
492+
JSON.parse(contents);
493+
return contents;
494+
}))
495+
.pipe(gulp.dest(builtLocalDirectory));
496+
});
497+
488498
gulp.task("lssl", "Builds language service server library", [tsserverLibraryFile]);
489499
gulp.task("local", "Builds the full compiler and services", [builtLocalCompiler, servicesFile, serverFile, builtGeneratedDiagnosticMessagesJSON, tsserverLibraryFile]);
490500
gulp.task("tsc", "Builds only the compiler", [builtLocalCompiler]);

Jakefile.js

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -88,6 +88,8 @@ var watchGuardSources = filesFromConfig(path.join(serverDirectory, "watchGuard/t
8888
var serverSources = filesFromConfig(path.join(serverDirectory, "tsconfig.json"))
8989
var languageServiceLibrarySources = filesFromConfig(path.join(serverDirectory, "tsconfig.library.json"));
9090

91+
var typesMapOutputPath = path.join(builtLocalDirectory, 'typesMap.json');
92+
9193
var harnessCoreSources = [
9294
"harness.ts",
9395
"virtualFileSystem.ts",
@@ -423,6 +425,7 @@ var buildProtocolTs = path.join(scriptsDirectory, "buildProtocol.ts");
423425
var buildProtocolJs = path.join(scriptsDirectory, "buildProtocol.js");
424426
var buildProtocolDts = path.join(builtLocalDirectory, "protocol.d.ts");
425427
var typescriptServicesDts = path.join(builtLocalDirectory, "typescriptServices.d.ts");
428+
var typesMapJson = path.join(builtLocalDirectory, "typesMap.json");
426429

427430
file(buildProtocolTs);
428431

@@ -587,6 +590,16 @@ var serverFile = path.join(builtLocalDirectory, "tsserver.js");
587590
compileFile(serverFile, serverSources, [builtLocalDirectory, copyright, cancellationTokenFile, typingsInstallerFile, watchGuardFile].concat(serverSources).concat(servicesSources), /*prefixes*/ [copyright], /*useBuiltCompiler*/ true, { types: ["node"], preserveConstEnums: true, lib: "es6" });
588591
var tsserverLibraryFile = path.join(builtLocalDirectory, "tsserverlibrary.js");
589592
var tsserverLibraryDefinitionFile = path.join(builtLocalDirectory, "tsserverlibrary.d.ts");
593+
file(typesMapOutputPath, function() {
594+
var content = fs.readFileSync(path.join(serverDirectory, 'typesMap.json'));
595+
// Validate that it's valid JSON
596+
try {
597+
JSON.parse(content);
598+
} catch (e) {
599+
console.log("Parse error in typesMap.json: " + e);
600+
}
601+
fs.writeFileSync(typesMapOutputPath, content);
602+
});
590603
compileFile(
591604
tsserverLibraryFile,
592605
languageServiceLibrarySources,
@@ -609,7 +622,7 @@ compileFile(
609622

610623
// Local target to build the language service server library
611624
desc("Builds language service server library");
612-
task("lssl", [tsserverLibraryFile, tsserverLibraryDefinitionFile]);
625+
task("lssl", [tsserverLibraryFile, tsserverLibraryDefinitionFile, typesMapOutputPath]);
613626

614627
desc("Emit the start of the build fold");
615628
task("build-fold-start", [], function () {
@@ -638,7 +651,6 @@ task("release", function () {
638651
// Set the default task to "local"
639652
task("default", ["local"]);
640653

641-
642654
// Cleans the built directory
643655
desc("Cleans the compiler output, declare files, and tests");
644656
task("clean", function () {

package.json

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -93,5 +93,8 @@
9393
"fs": false,
9494
"os": false,
9595
"path": false
96+
},
97+
"dependencies": {
98+
"browser-resolve": "^1.11.2"
9699
}
97100
}

src/harness/unittests/tsserverProjectSystem.ts

Lines changed: 24 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -18,14 +18,28 @@ namespace ts.projectSystem {
1818
})
1919
};
2020

21-
const customSafeList = {
22-
path: <Path>"/typeMapList.json",
23-
content: JSON.stringify({
24-
"quack": {
25-
"match": "/duckquack-(\\d+)\\.min\\.js",
26-
"types": ["duck-types"]
21+
export const customTypesMap = {
22+
path: <Path>"/typesMap.json",
23+
content: `{
24+
"typesMap": {
25+
"jquery": {
26+
"match": "jquery(-(\\\\.?\\\\d+)+)?(\\\\.intellisense)?(\\\\.min)?\\\\.js$",
27+
"types": ["jquery"]
28+
},
29+
"quack": {
30+
"match": "/duckquack-(\\\\d+)\\\\.min\\\\.js",
31+
"types": ["duck-types"]
32+
}
2733
},
28-
})
34+
"simpleMap": {
35+
"Bacon": "baconjs",
36+
"bliss": "blissfuljs",
37+
"commander": "commander",
38+
"cordova": "cordova",
39+
"react": "react",
40+
"lodash": "lodash"
41+
}
42+
}`
2943
};
3044

3145
export interface PostExecAction {
@@ -59,7 +73,7 @@ namespace ts.projectSystem {
5973
installTypingHost: server.ServerHost,
6074
readonly typesRegistry = createMap<void>(),
6175
log?: TI.Log) {
62-
super(installTypingHost, globalTypingsCacheLocation, safeList.path, throttleLimit, log);
76+
super(installTypingHost, globalTypingsCacheLocation, safeList.path, customTypesMap.path, throttleLimit, log);
6377
}
6478

6579
protected postExecActions: PostExecAction[] = [];
@@ -229,6 +243,7 @@ namespace ts.projectSystem {
229243
useSingleInferredProject,
230244
useInferredProjectPerProjectRoot: false,
231245
typingsInstaller,
246+
typesMapLocation: customTypesMap.path,
232247
eventHandler,
233248
...opts
234249
});
@@ -1491,9 +1506,8 @@ namespace ts.projectSystem {
14911506
path: "/lib/duckquack-3.min.js",
14921507
content: "whoa do @@ not parse me ok thanks!!!"
14931508
};
1494-
const host = createServerHost([customSafeList, file1, office]);
1509+
const host = createServerHost([file1, office, customTypesMap]);
14951510
const projectService = createProjectService(host);
1496-
projectService.loadSafeList(customSafeList.path);
14971511
try {
14981512
projectService.openExternalProject({ projectFileName: "project", options: {}, rootFiles: toExternalFiles([file1.path, office.path]) });
14991513
const proj = projectService.externalProjects[0];

src/harness/unittests/typingsInstaller.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -322,7 +322,7 @@ namespace ts.projectSystem {
322322
content: "declare const lodash: { x: number }"
323323
};
324324

325-
const host = createServerHost([file1, file2, file3]);
325+
const host = createServerHost([file1, file2, file3, customTypesMap]);
326326
const installer = new (class extends Installer {
327327
constructor() {
328328
super(host, { typesRegistry: createTypesRegistry("lodash", "react") });
@@ -445,7 +445,7 @@ namespace ts.projectSystem {
445445
content: "declare const moment: { x: number }"
446446
};
447447

448-
const host = createServerHost([file1, file2, file3, packageJson]);
448+
const host = createServerHost([file1, file2, file3, packageJson, customTypesMap]);
449449
const installer = new (class extends Installer {
450450
constructor() {
451451
super(host, { typesRegistry: createTypesRegistry("jquery", "commander", "moment", "express") });
@@ -521,7 +521,7 @@ namespace ts.projectSystem {
521521
};
522522

523523
const typingFiles = [commander, express, jquery, moment, lodash];
524-
const host = createServerHost([lodashJs, commanderJs, file3, packageJson]);
524+
const host = createServerHost([lodashJs, commanderJs, file3, packageJson, customTypesMap]);
525525
const installer = new (class extends Installer {
526526
constructor() {
527527
super(host, { throttleLimit: 3, typesRegistry: createTypesRegistry("commander", "express", "jquery", "moment", "lodash") });
@@ -600,7 +600,7 @@ namespace ts.projectSystem {
600600
typings: typingsName("gulp")
601601
};
602602

603-
const host = createServerHost([lodashJs, commanderJs, file3]);
603+
const host = createServerHost([lodashJs, commanderJs, file3, customTypesMap]);
604604
const installer = new (class extends Installer {
605605
constructor() {
606606
super(host, { throttleLimit: 1, typesRegistry: createTypesRegistry("commander", "jquery", "lodash", "cordova", "gulp", "grunt") });

src/server/editorServices.ts

Lines changed: 51 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -109,6 +109,11 @@ namespace ts.server {
109109
"smart": IndentStyle.Smart
110110
});
111111

112+
export interface TypesMapFile {
113+
typesMap: SafeList;
114+
simpleMap: string[];
115+
}
116+
112117
/**
113118
* How to understand this block:
114119
* * The 'match' property is a regexp that matches a filename.
@@ -330,6 +335,7 @@ namespace ts.server {
330335
globalPlugins?: ReadonlyArray<string>;
331336
pluginProbeLocations?: ReadonlyArray<string>;
332337
allowLocalPluginLoads?: boolean;
338+
typesMapLocation?: string;
333339
}
334340

335341
export class ProjectService {
@@ -391,6 +397,7 @@ namespace ts.server {
391397
public readonly globalPlugins: ReadonlyArray<string>;
392398
public readonly pluginProbeLocations: ReadonlyArray<string>;
393399
public readonly allowLocalPluginLoads: boolean;
400+
public readonly typesMapLocation: string | undefined;
394401

395402
/** Tracks projects that we have already sent telemetry for. */
396403
private readonly seenProjects = createMap<true>();
@@ -407,13 +414,18 @@ namespace ts.server {
407414
this.globalPlugins = opts.globalPlugins || emptyArray;
408415
this.pluginProbeLocations = opts.pluginProbeLocations || emptyArray;
409416
this.allowLocalPluginLoads = !!opts.allowLocalPluginLoads;
417+
this.typesMapLocation = (opts.typesMapLocation === undefined) ? combinePaths(this.host.getExecutingFilePath(), "../typesMap.json") : opts.typesMapLocation;
410418

411419
Debug.assert(!!this.host.createHash, "'ServerHost.createHash' is required for ProjectService");
412420

413421
this.toCanonicalFileName = createGetCanonicalFileName(this.host.useCaseSensitiveFileNames);
414422
this.directoryWatchers = new DirectoryWatchers(this);
415423
this.throttledOperations = new ThrottledOperations(this.host);
416424

425+
if (opts.typesMapLocation) {
426+
this.loadTypesMap();
427+
}
428+
417429
this.typingsInstaller.attach(this);
418430

419431
this.typingsCache = new TypingsCache(this.typingsInstaller);
@@ -451,6 +463,27 @@ namespace ts.server {
451463
this.eventHandler(event);
452464
}
453465

466+
private loadTypesMap() {
467+
try {
468+
const fileContent = this.host.readFile(this.typesMapLocation);
469+
if (fileContent === undefined) {
470+
this.logger.info(`Provided types map file "${this.typesMapLocation}" doesn't exist`);
471+
return;
472+
}
473+
const raw: TypesMapFile = JSON.parse(fileContent);
474+
// Parse the regexps
475+
for (const k of Object.keys(raw.typesMap)) {
476+
raw.typesMap[k].match = new RegExp(raw.typesMap[k].match as {} as string, "i");
477+
}
478+
// raw is now fixed and ready
479+
this.safelist = raw.typesMap;
480+
}
481+
catch (e) {
482+
this.logger.info(`Error loading types map: ${e}`);
483+
this.safelist = defaultTypeSafeList;
484+
}
485+
}
486+
454487
updateTypingsForProject(response: SetTypings | InvalidateCachedTypings): void {
455488
const project = this.findProject(response.projectName);
456489
if (!project) {
@@ -1712,23 +1745,14 @@ namespace ts.server {
17121745
this.safelist = defaultTypeSafeList;
17131746
}
17141747

1715-
loadSafeList(fileName: string): void {
1716-
const raw: SafeList = JSON.parse(this.host.readFile(fileName, "utf-8"));
1717-
// Parse the regexps
1718-
for (const k of Object.keys(raw)) {
1719-
raw[k].match = new RegExp(raw[k].match as {} as string, "i");
1720-
}
1721-
// raw is now fixed and ready
1722-
this.safelist = raw;
1723-
}
1724-
1725-
applySafeList(proj: protocol.ExternalProject): void {
1748+
applySafeList(proj: protocol.ExternalProject): NormalizedPath[] {
17261749
const { rootFiles, typeAcquisition } = proj;
17271750
const types = (typeAcquisition && typeAcquisition.include) || [];
17281751

17291752
const excludeRules: string[] = [];
17301753

1731-
const normalizedNames = rootFiles.map(f => normalizeSlashes(f.fileName));
1754+
const normalizedNames = rootFiles.map(f => normalizeSlashes(f.fileName)) as NormalizedPath[];
1755+
const excludedFiles: NormalizedPath[] = [];
17321756

17331757
for (const name of Object.keys(this.safelist)) {
17341758
const rule = this.safelist[name];
@@ -1787,7 +1811,17 @@ namespace ts.server {
17871811
}
17881812

17891813
const excludeRegexes = excludeRules.map(e => new RegExp(e, "i"));
1790-
proj.rootFiles = proj.rootFiles.filter((_file, index) => !excludeRegexes.some(re => re.test(normalizedNames[index])));
1814+
const filesToKeep: ts.server.protocol.ExternalFile[] = [];
1815+
for (let i = 0; i < proj.rootFiles.length; i++) {
1816+
if (excludeRegexes.some(re => re.test(normalizedNames[i]))) {
1817+
excludedFiles.push(normalizedNames[i]);
1818+
}
1819+
else {
1820+
filesToKeep.push(proj.rootFiles[i]);
1821+
}
1822+
}
1823+
proj.rootFiles = filesToKeep;
1824+
return excludedFiles;
17911825
}
17921826

17931827
openExternalProject(proj: protocol.ExternalProject, suppressRefreshOfInferredProjects = false): void {
@@ -1798,7 +1832,7 @@ namespace ts.server {
17981832
proj.typeAcquisition = typeAcquisition;
17991833
}
18001834

1801-
this.applySafeList(proj);
1835+
const excludedFiles = this.applySafeList(proj);
18021836

18031837
let tsConfigFiles: NormalizedPath[];
18041838
const rootFiles: protocol.ExternalFile[] = [];
@@ -1822,6 +1856,7 @@ namespace ts.server {
18221856
const externalProject = this.findExternalProjectByProjectName(proj.projectFileName);
18231857
let exisingConfigFiles: string[];
18241858
if (externalProject) {
1859+
externalProject.excludedFiles = excludedFiles;
18251860
if (!tsConfigFiles) {
18261861
const compilerOptions = convertCompilerOptions(proj.options);
18271862
if (this.exceededTotalSizeLimitForNonTsFiles(proj.projectFileName, compilerOptions, proj.rootFiles, externalFilePropertyReader)) {
@@ -1891,7 +1926,8 @@ namespace ts.server {
18911926
else {
18921927
// no config files - remove the item from the collection
18931928
this.externalProjectToConfiguredProjectMap.delete(proj.projectFileName);
1894-
this.createAndAddExternalProject(proj.projectFileName, rootFiles, proj.options, proj.typeAcquisition);
1929+
const newProj = this.createAndAddExternalProject(proj.projectFileName, rootFiles, proj.options, proj.typeAcquisition);
1930+
newProj.excludedFiles = excludedFiles;
18951931
}
18961932
if (!suppressRefreshOfInferredProjects) {
18971933
this.refreshInferredProjects();

src/server/project.ts

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -376,6 +376,10 @@ namespace ts.server {
376376
return this.getLanguageService().getEmitOutput(info.fileName, emitOnlyDtsFiles);
377377
}
378378

379+
getExcludedFiles(): ReadonlyArray<NormalizedPath> {
380+
return emptyArray;
381+
}
382+
379383
getFileNames(excludeFilesFromExternalLibraries?: boolean, excludeConfigFiles?: boolean) {
380384
if (!this.program) {
381385
return [];
@@ -1166,6 +1170,7 @@ namespace ts.server {
11661170
* These are created only if a host explicitly calls `openExternalProject`.
11671171
*/
11681172
export class ExternalProject extends Project {
1173+
excludedFiles: ReadonlyArray<NormalizedPath> = [];
11691174
private typeAcquisition: TypeAcquisition;
11701175
constructor(public externalProjectName: string,
11711176
projectService: ProjectService,
@@ -1175,6 +1180,11 @@ namespace ts.server {
11751180
public compileOnSaveEnabled: boolean,
11761181
private readonly projectFilePath?: string) {
11771182
super(externalProjectName, ProjectKind.External, projectService, documentRegistry, /*hasExplicitListOfFiles*/ true, languageServiceEnabled, compilerOptions, compileOnSaveEnabled);
1183+
1184+
}
1185+
1186+
getExcludedFiles() {
1187+
return this.excludedFiles;
11781188
}
11791189

11801190
getProjectRootPath() {

0 commit comments

Comments
 (0)