Skip to content

Commit 5272ec6

Browse files
committed
Types Map WIP
1 parent 70e5c6b commit 5272ec6

File tree

12 files changed

+642
-40
lines changed

12 files changed

+642
-40
lines changed

Gulpfile.ts

Lines changed: 19 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -464,6 +464,7 @@ gulp.task(serverFile, /*help*/ false, [servicesFile, typingsInstallerJs, cancell
464464
.pipe(gulp.dest("src/server"));
465465
});
466466

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

@@ -473,7 +474,7 @@ gulp.task(tsserverLibraryFile, /*help*/ false, [servicesFile], (done) => {
473474
.pipe(sourcemaps.init())
474475
.pipe(newer(tsserverLibraryFile))
475476
.pipe(serverLibraryProject());
476-
477+
477478
return merge2([
478479
js.pipe(prependCopyright())
479480
.pipe(sourcemaps.write("."))
@@ -486,7 +487,23 @@ gulp.task(tsserverLibraryFile, /*help*/ false, [servicesFile], (done) => {
486487
]);
487488
});
488489

489-
gulp.task("lssl", "Builds language service server library", [tsserverLibraryFile]);
490+
gulp.task(typesMapJson, /*help*/ false, [], (done) => {
491+
fs.readFile('src/server/typesMaps.json', 'utf-8', (err, data) => {
492+
if (err) {
493+
return done(err);
494+
}
495+
try {
496+
JSON.parse(data);
497+
} catch (e) {
498+
done(e);
499+
}
500+
fs.writeFile(typesMapJson, data, err => {
501+
done(err);
502+
});
503+
});
504+
});
505+
506+
gulp.task("lssl", "Builds language service server library", [tsserverLibraryFile, typesMapJson]);
490507
gulp.task("local", "Builds the full compiler and services", [builtLocalCompiler, servicesFile, serverFile, builtGeneratedDiagnosticMessagesJSON, tsserverLibraryFile]);
491508
gulp.task("tsc", "Builds only the compiler", [builtLocalCompiler]);
492509

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",
@@ -422,6 +424,7 @@ var buildProtocolTs = path.join(scriptsDirectory, "buildProtocol.ts");
422424
var buildProtocolJs = path.join(scriptsDirectory, "buildProtocol.js");
423425
var buildProtocolDts = path.join(builtLocalDirectory, "protocol.d.ts");
424426
var typescriptServicesDts = path.join(builtLocalDirectory, "typescriptServices.d.ts");
427+
var typesMapJson = path.join(builtLocalDirectory, "typesMap.json");
425428

426429
file(buildProtocolTs);
427430

@@ -603,6 +606,16 @@ var serverFile = path.join(builtLocalDirectory, "tsserver.js");
603606
compileFile(serverFile, serverSources, [builtLocalDirectory, copyright, cancellationTokenFile, typingsInstallerFile, watchGuardFile].concat(serverSources).concat(servicesSources), /*prefixes*/ [copyright], /*useBuiltCompiler*/ true, { types: ["node"], preserveConstEnums: true, lib: "es6" });
604607
var tsserverLibraryFile = path.join(builtLocalDirectory, "tsserverlibrary.js");
605608
var tsserverLibraryDefinitionFile = path.join(builtLocalDirectory, "tsserverlibrary.d.ts");
609+
file(typesMapOutputPath, function() {
610+
var content = fs.readFileSync(path.join(serverDirectory, 'typesMap.json'));
611+
// Validate that it's valid JSON
612+
try {
613+
JSON.parse(content);
614+
} catch (e) {
615+
console.log("Parse error in typesMap.json: " + e);
616+
}
617+
fs.writeFileSync(typesMapOutputPath, content);
618+
});
606619
compileFile(
607620
tsserverLibraryFile,
608621
languageServiceLibrarySources,
@@ -625,7 +638,7 @@ compileFile(
625638

626639
// Local target to build the language service server library
627640
desc("Builds language service server library");
628-
task("lssl", [tsserverLibraryFile, tsserverLibraryDefinitionFile]);
641+
task("lssl", [tsserverLibraryFile, tsserverLibraryDefinitionFile, typesMapOutputPath]);
629642

630643
desc("Emit the start of the build fold");
631644
task("build-fold-start", [], function () {
@@ -654,7 +667,6 @@ task("release", function () {
654667
// Set the default task to "local"
655668
task("default", ["local"]);
656669

657-
658670
// Cleans the built directory
659671
desc("Cleans the compiler output, declare files, and tests");
660672
task("clean", function () {

src/harness/unittests/tsserverProjectSystem.ts

Lines changed: 21 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -18,14 +18,24 @@ 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+
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+
}
38+
}`
2939
};
3040

3141
export interface PostExecAction {
@@ -59,7 +69,7 @@ namespace ts.projectSystem {
5969
installTypingHost: server.ServerHost,
6070
readonly typesRegistry = createMap<void>(),
6171
log?: TI.Log) {
62-
super(installTypingHost, globalTypingsCacheLocation, safeList.path, throttleLimit, log);
72+
super(installTypingHost, globalTypingsCacheLocation, safeList.path, customTypesMap.path, throttleLimit, log);
6373
}
6474

6575
protected postExecActions: PostExecAction[] = [];
@@ -218,7 +228,7 @@ namespace ts.projectSystem {
218228
constructor(host: server.ServerHost, logger: server.Logger, cancellationToken: HostCancellationToken, useSingleInferredProject: boolean,
219229
typingsInstaller: server.ITypingsInstaller, eventHandler: server.ProjectServiceEventHandler) {
220230
super({
221-
host, logger, cancellationToken, useSingleInferredProject, typingsInstaller, eventHandler
231+
host, logger, cancellationToken, useSingleInferredProject, typingsInstaller, eventHandler, typesMapLocation: customTypesMap.path
222232
});
223233
}
224234

@@ -1479,9 +1489,8 @@ namespace ts.projectSystem {
14791489
path: "/lib/duckquack-3.min.js",
14801490
content: "whoa do @@ not parse me ok thanks!!!"
14811491
};
1482-
const host = createServerHost([customSafeList, file1, office]);
1492+
const host = createServerHost([file1, office, customTypesMap]);
14831493
const projectService = createProjectService(host);
1484-
projectService.loadSafeList(customSafeList.path);
14851494
try {
14861495
projectService.openExternalProject({ projectFileName: "project", options: {}, rootFiles: toExternalFiles([file1.path, office.path]) });
14871496
const proj = projectService.externalProjects[0];
@@ -1491,7 +1500,7 @@ namespace ts.projectSystem {
14911500
projectService.resetSafeList();
14921501
}
14931502
});
1494-
1503+
14951504
it("ignores files excluded by the default type list", () => {
14961505
const file1 = {
14971506
path: "/a/b/f1.ts",

src/server/editorServices.ts

Lines changed: 49 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.
@@ -329,6 +334,7 @@ namespace ts.server {
329334
globalPlugins?: string[];
330335
pluginProbeLocations?: string[];
331336
allowLocalPluginLoads?: boolean;
337+
typesMapLocation?: string;
332338
}
333339

334340
export class ProjectService {
@@ -389,6 +395,7 @@ namespace ts.server {
389395
public readonly globalPlugins: ReadonlyArray<string>;
390396
public readonly pluginProbeLocations: ReadonlyArray<string>;
391397
public readonly allowLocalPluginLoads: boolean;
398+
public readonly typesMapLocation: string | undefined;
392399

393400
/** Tracks projects that we have already sent telemetry for. */
394401
private readonly seenProjects = createMap<true>();
@@ -404,13 +411,18 @@ namespace ts.server {
404411
this.globalPlugins = opts.globalPlugins || emptyArray;
405412
this.pluginProbeLocations = opts.pluginProbeLocations || emptyArray;
406413
this.allowLocalPluginLoads = !!opts.allowLocalPluginLoads;
414+
this.typesMapLocation = (opts.typesMapLocation === undefined) ? combinePaths(this.host.getExecutingFilePath(), '../typesMap.json') : opts.typesMapLocation;
407415

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

410418
this.toCanonicalFileName = createGetCanonicalFileName(this.host.useCaseSensitiveFileNames);
411419
this.directoryWatchers = new DirectoryWatchers(this);
412420
this.throttledOperations = new ThrottledOperations(this.host);
413421

422+
if (opts.typesMapLocation) {
423+
this.loadTypesMap();
424+
}
425+
414426
this.typingsInstaller.attach(this);
415427

416428
this.typingsCache = new TypingsCache(this.typingsInstaller);
@@ -447,6 +459,25 @@ namespace ts.server {
447459
});
448460
}
449461

462+
private loadTypesMap() {
463+
if (!this.host.fileExists(this.typesMapLocation)) {
464+
this.logger.info(`Provided types map file "${this.typesMapLocation}" doesn't exist`);
465+
return;
466+
}
467+
try {
468+
const raw: TypesMapFile = JSON.parse(this.host.readFile(this.typesMapLocation));
469+
// Parse the regexps
470+
for (const k of Object.keys(raw.typesMap)) {
471+
raw.typesMap[k].match = new RegExp(raw.typesMap[k].match as {} as string, "i");
472+
}
473+
// raw is now fixed and ready
474+
this.safelist = raw.typesMap;
475+
} catch(e) {
476+
this.logger.info(`Error loading types map: ${e}`);
477+
this.safelist = defaultTypeSafeList;
478+
}
479+
}
480+
450481
updateTypingsForProject(response: SetTypings | InvalidateCachedTypings): void {
451482
const project = this.findProject(response.projectName);
452483
if (!project) {
@@ -1623,23 +1654,14 @@ namespace ts.server {
16231654
this.safelist = defaultTypeSafeList;
16241655
}
16251656

1626-
loadSafeList(fileName: string): void {
1627-
const raw: SafeList = JSON.parse(this.host.readFile(fileName, "utf-8"));
1628-
// Parse the regexps
1629-
for (const k of Object.keys(raw)) {
1630-
raw[k].match = new RegExp(raw[k].match as {} as string, "i");
1631-
}
1632-
// raw is now fixed and ready
1633-
this.safelist = raw;
1634-
}
1635-
1636-
applySafeList(proj: protocol.ExternalProject): void {
1657+
applySafeList(proj: protocol.ExternalProject): NormalizedPath[] {
16371658
const { rootFiles, typeAcquisition } = proj;
16381659
const types = (typeAcquisition && typeAcquisition.include) || [];
16391660

16401661
const excludeRules: string[] = [];
16411662

1642-
const normalizedNames = rootFiles.map(f => normalizeSlashes(f.fileName));
1663+
const normalizedNames = rootFiles.map(f => normalizeSlashes(f.fileName)) as NormalizedPath[];
1664+
const excludedFiles: NormalizedPath[] = [];
16431665

16441666
for (const name of Object.keys(this.safelist)) {
16451667
const rule = this.safelist[name];
@@ -1698,7 +1720,17 @@ namespace ts.server {
16981720
}
16991721

17001722
const excludeRegexes = excludeRules.map(e => new RegExp(e, "i"));
1701-
proj.rootFiles = proj.rootFiles.filter((_file, index) => !excludeRegexes.some(re => re.test(normalizedNames[index])));
1723+
const filesToKeep: ts.server.protocol.ExternalFile[] = [];
1724+
for(let i = 0; i < proj.rootFiles.length; i++) {
1725+
if (excludeRegexes.some(re => re.test(normalizedNames[i]))) {
1726+
excludedFiles.push(normalizedNames[i]);
1727+
}
1728+
else {
1729+
filesToKeep.push(proj.rootFiles[i]);
1730+
}
1731+
}
1732+
proj.rootFiles = filesToKeep;
1733+
return excludedFiles;
17021734
}
17031735

17041736
openExternalProject(proj: protocol.ExternalProject, suppressRefreshOfInferredProjects = false): void {
@@ -1709,7 +1741,7 @@ namespace ts.server {
17091741
proj.typeAcquisition = typeAcquisition;
17101742
}
17111743

1712-
this.applySafeList(proj);
1744+
const excludedFiles = this.applySafeList(proj);
17131745

17141746
let tsConfigFiles: NormalizedPath[];
17151747
const rootFiles: protocol.ExternalFile[] = [];
@@ -1733,6 +1765,7 @@ namespace ts.server {
17331765
const externalProject = this.findExternalProjectByProjectName(proj.projectFileName);
17341766
let exisingConfigFiles: string[];
17351767
if (externalProject) {
1768+
externalProject.excludedFiles = excludedFiles;
17361769
if (!tsConfigFiles) {
17371770
const compilerOptions = convertCompilerOptions(proj.options);
17381771
if (this.exceededTotalSizeLimitForNonTsFiles(proj.projectFileName, compilerOptions, proj.rootFiles, externalFilePropertyReader)) {
@@ -1802,7 +1835,8 @@ namespace ts.server {
18021835
else {
18031836
// no config files - remove the item from the collection
18041837
this.externalProjectToConfiguredProjectMap.delete(proj.projectFileName);
1805-
this.createAndAddExternalProject(proj.projectFileName, rootFiles, proj.options, proj.typeAcquisition);
1838+
const newProj = this.createAndAddExternalProject(proj.projectFileName, rootFiles, proj.options, proj.typeAcquisition);
1839+
newProj.excludedFiles = excludedFiles;
18061840
}
18071841
if (!suppressRefreshOfInferredProjects) {
18081842
this.refreshInferredProjects();

src/server/project.ts

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

378+
getExcludedFiles(): ReadonlyArray<NormalizedPath> {
379+
return emptyArray;
380+
}
381+
378382
getFileNames(excludeFilesFromExternalLibraries?: boolean, excludeConfigFiles?: boolean) {
379383
if (!this.program) {
380384
return [];
@@ -1161,6 +1165,7 @@ namespace ts.server {
11611165
* These are created only if a host explicitly calls `openExternalProject`.
11621166
*/
11631167
export class ExternalProject extends Project {
1168+
excludedFiles: ReadonlyArray<NormalizedPath> = [];
11641169
private typeAcquisition: TypeAcquisition;
11651170
constructor(public externalProjectName: string,
11661171
projectService: ProjectService,
@@ -1170,6 +1175,11 @@ namespace ts.server {
11701175
public compileOnSaveEnabled: boolean,
11711176
private readonly projectFilePath?: string) {
11721177
super(externalProjectName, ProjectKind.External, projectService, documentRegistry, /*hasExplicitListOfFiles*/ true, languageServiceEnabled, compilerOptions, compileOnSaveEnabled);
1178+
1179+
}
1180+
1181+
getExcludedFiles() {
1182+
return this.excludedFiles;
11731183
}
11741184

11751185
getProjectRootPath() {

src/server/server.ts

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ namespace ts.server {
1313
globalTypingsCacheLocation: string;
1414
logger: Logger;
1515
typingSafeListLocation: string;
16+
typesMapLocation: string | undefined;
1617
npmLocation: string | undefined;
1718
telemetryEnabled: boolean;
1819
globalPlugins: string[];
@@ -243,6 +244,7 @@ namespace ts.server {
243244
eventPort: number,
244245
readonly globalTypingsCacheLocation: string,
245246
readonly typingSafeListLocation: string,
247+
readonly typesMapLocation: string,
246248
private readonly npmLocation: string | undefined,
247249
private newLine: string) {
248250
this.throttledOperations = new ThrottledOperations(host);
@@ -288,6 +290,9 @@ namespace ts.server {
288290
if (this.typingSafeListLocation) {
289291
args.push(Arguments.TypingSafeListLocation, this.typingSafeListLocation);
290292
}
293+
if (this.typesMapLocation) {
294+
args.push(Arguments.TypesMapLocation, this.typesMapLocation);
295+
}
291296
if (this.npmLocation) {
292297
args.push(Arguments.NpmLocation, this.npmLocation);
293298
}
@@ -401,10 +406,10 @@ namespace ts.server {
401406

402407
class IOSession extends Session {
403408
constructor(options: IOSessionOptions) {
404-
const { host, installerEventPort, globalTypingsCacheLocation, typingSafeListLocation, npmLocation, canUseEvents } = options;
409+
const { host, installerEventPort, globalTypingsCacheLocation, typingSafeListLocation, typesMapLocation, npmLocation, canUseEvents } = options;
405410
const typingsInstaller = disableAutomaticTypingAcquisition
406411
? undefined
407-
: new NodeTypingsInstaller(telemetryEnabled, logger, host, installerEventPort, globalTypingsCacheLocation, typingSafeListLocation, npmLocation, host.newLine);
412+
: new NodeTypingsInstaller(telemetryEnabled, logger, host, installerEventPort, globalTypingsCacheLocation, typingSafeListLocation, typesMapLocation, npmLocation, host.newLine);
408413

409414
super({
410415
host,
@@ -758,6 +763,7 @@ namespace ts.server {
758763
}
759764

760765
const typingSafeListLocation = findArgument(Arguments.TypingSafeListLocation);
766+
const typesMapLocation = findArgument(Arguments.TypesMapLocation) || combinePaths(sys.getExecutingFilePath(), '../typesMap.json');
761767
const npmLocation = findArgument(Arguments.NpmLocation);
762768

763769
const globalPlugins = (findArgument("--globalPlugins") || "").split(",");
@@ -777,6 +783,7 @@ namespace ts.server {
777783
disableAutomaticTypingAcquisition,
778784
globalTypingsCacheLocation: getGlobalTypingsCacheLocation(),
779785
typingSafeListLocation,
786+
typesMapLocation,
780787
npmLocation,
781788
telemetryEnabled,
782789
logger,

0 commit comments

Comments
 (0)