diff --git a/Gulpfile.ts b/Gulpfile.ts index 757cd4fed08..645443370eb 100644 --- a/Gulpfile.ts +++ b/Gulpfile.ts @@ -463,10 +463,11 @@ gulp.task(serverFile, /*help*/ false, [servicesFile, typingsInstallerJs, cancell .pipe(gulp.dest("src/server")); }); +const typesMapJson = path.join(builtLocalDirectory, "typesMap.json"); const tsserverLibraryFile = path.join(builtLocalDirectory, "tsserverlibrary.js"); const tsserverLibraryDefinitionFile = path.join(builtLocalDirectory, "tsserverlibrary.d.ts"); -gulp.task(tsserverLibraryFile, /*help*/ false, [servicesFile], (done) => { +gulp.task(tsserverLibraryFile, /*help*/ false, [servicesFile, typesMapJson], (done) => { const serverLibraryProject = tsc.createProject("src/server/tsconfig.library.json", getCompilerSettings({}, /*useBuiltCompiler*/ true)); const {js, dts}: { js: NodeJS.ReadableStream, dts: NodeJS.ReadableStream } = serverLibraryProject.src() .pipe(sourcemaps.init()) @@ -485,6 +486,15 @@ gulp.task(tsserverLibraryFile, /*help*/ false, [servicesFile], (done) => { ]); }); +gulp.task(typesMapJson, /*help*/ false, [], () => { + return gulp.src("src/server/typesMap.json") + .pipe(insert.transform((contents, file) => { + JSON.parse(contents); + return contents; + })) + .pipe(gulp.dest(builtLocalDirectory)); +}); + gulp.task("lssl", "Builds language service server library", [tsserverLibraryFile]); gulp.task("local", "Builds the full compiler and services", [builtLocalCompiler, servicesFile, serverFile, builtGeneratedDiagnosticMessagesJSON, tsserverLibraryFile]); gulp.task("tsc", "Builds only the compiler", [builtLocalCompiler]); diff --git a/Jakefile.js b/Jakefile.js index 15fe40141f6..546727db4e0 100644 --- a/Jakefile.js +++ b/Jakefile.js @@ -88,6 +88,8 @@ var watchGuardSources = filesFromConfig(path.join(serverDirectory, "watchGuard/t var serverSources = filesFromConfig(path.join(serverDirectory, "tsconfig.json")) var languageServiceLibrarySources = filesFromConfig(path.join(serverDirectory, "tsconfig.library.json")); +var typesMapOutputPath = path.join(builtLocalDirectory, 'typesMap.json'); + var harnessCoreSources = [ "harness.ts", "virtualFileSystem.ts", @@ -423,6 +425,7 @@ var buildProtocolTs = path.join(scriptsDirectory, "buildProtocol.ts"); var buildProtocolJs = path.join(scriptsDirectory, "buildProtocol.js"); var buildProtocolDts = path.join(builtLocalDirectory, "protocol.d.ts"); var typescriptServicesDts = path.join(builtLocalDirectory, "typescriptServices.d.ts"); +var typesMapJson = path.join(builtLocalDirectory, "typesMap.json"); file(buildProtocolTs); @@ -587,6 +590,16 @@ var serverFile = path.join(builtLocalDirectory, "tsserver.js"); compileFile(serverFile, serverSources, [builtLocalDirectory, copyright, cancellationTokenFile, typingsInstallerFile, watchGuardFile].concat(serverSources).concat(servicesSources), /*prefixes*/ [copyright], /*useBuiltCompiler*/ true, { types: ["node"], preserveConstEnums: true, lib: "es6" }); var tsserverLibraryFile = path.join(builtLocalDirectory, "tsserverlibrary.js"); var tsserverLibraryDefinitionFile = path.join(builtLocalDirectory, "tsserverlibrary.d.ts"); +file(typesMapOutputPath, function() { + var content = fs.readFileSync(path.join(serverDirectory, 'typesMap.json')); + // Validate that it's valid JSON + try { + JSON.parse(content); + } catch (e) { + console.log("Parse error in typesMap.json: " + e); + } + fs.writeFileSync(typesMapOutputPath, content); +}); compileFile( tsserverLibraryFile, languageServiceLibrarySources, @@ -609,7 +622,7 @@ compileFile( // Local target to build the language service server library desc("Builds language service server library"); -task("lssl", [tsserverLibraryFile, tsserverLibraryDefinitionFile]); +task("lssl", [tsserverLibraryFile, tsserverLibraryDefinitionFile, typesMapOutputPath]); desc("Emit the start of the build fold"); task("build-fold-start", [], function () { @@ -638,7 +651,6 @@ task("release", function () { // Set the default task to "local" task("default", ["local"]); - // Cleans the built directory desc("Cleans the compiler output, declare files, and tests"); task("clean", function () { diff --git a/package.json b/package.json index 68cfe5d0a19..8d28b6051c6 100644 --- a/package.json +++ b/package.json @@ -93,5 +93,8 @@ "fs": false, "os": false, "path": false + }, + "dependencies": { + "browser-resolve": "^1.11.2" } } diff --git a/src/harness/unittests/tsserverProjectSystem.ts b/src/harness/unittests/tsserverProjectSystem.ts index 6e548231c91..fa0eef12919 100644 --- a/src/harness/unittests/tsserverProjectSystem.ts +++ b/src/harness/unittests/tsserverProjectSystem.ts @@ -18,14 +18,28 @@ namespace ts.projectSystem { }) }; - const customSafeList = { - path: "/typeMapList.json", - content: JSON.stringify({ - "quack": { - "match": "/duckquack-(\\d+)\\.min\\.js", - "types": ["duck-types"] + export const customTypesMap = { + path: "/typesMap.json", + content: `{ + "typesMap": { + "jquery": { + "match": "jquery(-(\\\\.?\\\\d+)+)?(\\\\.intellisense)?(\\\\.min)?\\\\.js$", + "types": ["jquery"] + }, + "quack": { + "match": "/duckquack-(\\\\d+)\\\\.min\\\\.js", + "types": ["duck-types"] + } }, - }) + "simpleMap": { + "Bacon": "baconjs", + "bliss": "blissfuljs", + "commander": "commander", + "cordova": "cordova", + "react": "react", + "lodash": "lodash" + } + }` }; export interface PostExecAction { @@ -59,7 +73,7 @@ namespace ts.projectSystem { installTypingHost: server.ServerHost, readonly typesRegistry = createMap(), log?: TI.Log) { - super(installTypingHost, globalTypingsCacheLocation, safeList.path, throttleLimit, log); + super(installTypingHost, globalTypingsCacheLocation, safeList.path, customTypesMap.path, throttleLimit, log); } protected postExecActions: PostExecAction[] = []; @@ -229,6 +243,7 @@ namespace ts.projectSystem { useSingleInferredProject, useInferredProjectPerProjectRoot: false, typingsInstaller, + typesMapLocation: customTypesMap.path, eventHandler, ...opts }); @@ -1491,9 +1506,8 @@ namespace ts.projectSystem { path: "/lib/duckquack-3.min.js", content: "whoa do @@ not parse me ok thanks!!!" }; - const host = createServerHost([customSafeList, file1, office]); + const host = createServerHost([file1, office, customTypesMap]); const projectService = createProjectService(host); - projectService.loadSafeList(customSafeList.path); try { projectService.openExternalProject({ projectFileName: "project", options: {}, rootFiles: toExternalFiles([file1.path, office.path]) }); const proj = projectService.externalProjects[0]; diff --git a/src/harness/unittests/typingsInstaller.ts b/src/harness/unittests/typingsInstaller.ts index 6a6978254c1..de8ceb7f984 100644 --- a/src/harness/unittests/typingsInstaller.ts +++ b/src/harness/unittests/typingsInstaller.ts @@ -322,7 +322,7 @@ namespace ts.projectSystem { content: "declare const lodash: { x: number }" }; - const host = createServerHost([file1, file2, file3]); + const host = createServerHost([file1, file2, file3, customTypesMap]); const installer = new (class extends Installer { constructor() { super(host, { typesRegistry: createTypesRegistry("lodash", "react") }); @@ -445,7 +445,7 @@ namespace ts.projectSystem { content: "declare const moment: { x: number }" }; - const host = createServerHost([file1, file2, file3, packageJson]); + const host = createServerHost([file1, file2, file3, packageJson, customTypesMap]); const installer = new (class extends Installer { constructor() { super(host, { typesRegistry: createTypesRegistry("jquery", "commander", "moment", "express") }); @@ -521,7 +521,7 @@ namespace ts.projectSystem { }; const typingFiles = [commander, express, jquery, moment, lodash]; - const host = createServerHost([lodashJs, commanderJs, file3, packageJson]); + const host = createServerHost([lodashJs, commanderJs, file3, packageJson, customTypesMap]); const installer = new (class extends Installer { constructor() { super(host, { throttleLimit: 3, typesRegistry: createTypesRegistry("commander", "express", "jquery", "moment", "lodash") }); @@ -600,7 +600,7 @@ namespace ts.projectSystem { typings: typingsName("gulp") }; - const host = createServerHost([lodashJs, commanderJs, file3]); + const host = createServerHost([lodashJs, commanderJs, file3, customTypesMap]); const installer = new (class extends Installer { constructor() { super(host, { throttleLimit: 1, typesRegistry: createTypesRegistry("commander", "jquery", "lodash", "cordova", "gulp", "grunt") }); diff --git a/src/server/editorServices.ts b/src/server/editorServices.ts index cb497ff774f..7cef21b1194 100644 --- a/src/server/editorServices.ts +++ b/src/server/editorServices.ts @@ -109,6 +109,11 @@ namespace ts.server { "smart": IndentStyle.Smart }); + export interface TypesMapFile { + typesMap: SafeList; + simpleMap: string[]; + } + /** * How to understand this block: * * The 'match' property is a regexp that matches a filename. @@ -330,6 +335,7 @@ namespace ts.server { globalPlugins?: ReadonlyArray; pluginProbeLocations?: ReadonlyArray; allowLocalPluginLoads?: boolean; + typesMapLocation?: string; } export class ProjectService { @@ -391,6 +397,7 @@ namespace ts.server { public readonly globalPlugins: ReadonlyArray; public readonly pluginProbeLocations: ReadonlyArray; public readonly allowLocalPluginLoads: boolean; + public readonly typesMapLocation: string | undefined; /** Tracks projects that we have already sent telemetry for. */ private readonly seenProjects = createMap(); @@ -407,6 +414,7 @@ namespace ts.server { this.globalPlugins = opts.globalPlugins || emptyArray; this.pluginProbeLocations = opts.pluginProbeLocations || emptyArray; this.allowLocalPluginLoads = !!opts.allowLocalPluginLoads; + this.typesMapLocation = (opts.typesMapLocation === undefined) ? combinePaths(this.host.getExecutingFilePath(), "../typesMap.json") : opts.typesMapLocation; Debug.assert(!!this.host.createHash, "'ServerHost.createHash' is required for ProjectService"); @@ -414,6 +422,10 @@ namespace ts.server { this.directoryWatchers = new DirectoryWatchers(this); this.throttledOperations = new ThrottledOperations(this.host); + if (opts.typesMapLocation) { + this.loadTypesMap(); + } + this.typingsInstaller.attach(this); this.typingsCache = new TypingsCache(this.typingsInstaller); @@ -451,6 +463,27 @@ namespace ts.server { this.eventHandler(event); } + private loadTypesMap() { + try { + const fileContent = this.host.readFile(this.typesMapLocation); + if (fileContent === undefined) { + this.logger.info(`Provided types map file "${this.typesMapLocation}" doesn't exist`); + return; + } + const raw: TypesMapFile = JSON.parse(fileContent); + // Parse the regexps + for (const k of Object.keys(raw.typesMap)) { + raw.typesMap[k].match = new RegExp(raw.typesMap[k].match as {} as string, "i"); + } + // raw is now fixed and ready + this.safelist = raw.typesMap; + } + catch (e) { + this.logger.info(`Error loading types map: ${e}`); + this.safelist = defaultTypeSafeList; + } + } + updateTypingsForProject(response: SetTypings | InvalidateCachedTypings): void { const project = this.findProject(response.projectName); if (!project) { @@ -1712,23 +1745,14 @@ namespace ts.server { this.safelist = defaultTypeSafeList; } - 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, "i"); - } - // raw is now fixed and ready - this.safelist = raw; - } - - applySafeList(proj: protocol.ExternalProject): void { + applySafeList(proj: protocol.ExternalProject): NormalizedPath[] { const { rootFiles, typeAcquisition } = proj; const types = (typeAcquisition && typeAcquisition.include) || []; const excludeRules: string[] = []; - const normalizedNames = rootFiles.map(f => normalizeSlashes(f.fileName)); + const normalizedNames = rootFiles.map(f => normalizeSlashes(f.fileName)) as NormalizedPath[]; + const excludedFiles: NormalizedPath[] = []; for (const name of Object.keys(this.safelist)) { const rule = this.safelist[name]; @@ -1787,7 +1811,17 @@ namespace ts.server { } const excludeRegexes = excludeRules.map(e => new RegExp(e, "i")); - proj.rootFiles = proj.rootFiles.filter((_file, index) => !excludeRegexes.some(re => re.test(normalizedNames[index]))); + const filesToKeep: ts.server.protocol.ExternalFile[] = []; + for (let i = 0; i < proj.rootFiles.length; i++) { + if (excludeRegexes.some(re => re.test(normalizedNames[i]))) { + excludedFiles.push(normalizedNames[i]); + } + else { + filesToKeep.push(proj.rootFiles[i]); + } + } + proj.rootFiles = filesToKeep; + return excludedFiles; } openExternalProject(proj: protocol.ExternalProject, suppressRefreshOfInferredProjects = false): void { @@ -1798,7 +1832,7 @@ namespace ts.server { proj.typeAcquisition = typeAcquisition; } - this.applySafeList(proj); + const excludedFiles = this.applySafeList(proj); let tsConfigFiles: NormalizedPath[]; const rootFiles: protocol.ExternalFile[] = []; @@ -1822,6 +1856,7 @@ namespace ts.server { const externalProject = this.findExternalProjectByProjectName(proj.projectFileName); let exisingConfigFiles: string[]; if (externalProject) { + externalProject.excludedFiles = excludedFiles; if (!tsConfigFiles) { const compilerOptions = convertCompilerOptions(proj.options); if (this.exceededTotalSizeLimitForNonTsFiles(proj.projectFileName, compilerOptions, proj.rootFiles, externalFilePropertyReader)) { @@ -1891,7 +1926,8 @@ namespace ts.server { else { // no config files - remove the item from the collection this.externalProjectToConfiguredProjectMap.delete(proj.projectFileName); - this.createAndAddExternalProject(proj.projectFileName, rootFiles, proj.options, proj.typeAcquisition); + const newProj = this.createAndAddExternalProject(proj.projectFileName, rootFiles, proj.options, proj.typeAcquisition); + newProj.excludedFiles = excludedFiles; } if (!suppressRefreshOfInferredProjects) { this.refreshInferredProjects(); diff --git a/src/server/project.ts b/src/server/project.ts index c7cd503953b..623c43e8d3a 100644 --- a/src/server/project.ts +++ b/src/server/project.ts @@ -376,6 +376,10 @@ namespace ts.server { return this.getLanguageService().getEmitOutput(info.fileName, emitOnlyDtsFiles); } + getExcludedFiles(): ReadonlyArray { + return emptyArray; + } + getFileNames(excludeFilesFromExternalLibraries?: boolean, excludeConfigFiles?: boolean) { if (!this.program) { return []; @@ -1166,6 +1170,7 @@ namespace ts.server { * These are created only if a host explicitly calls `openExternalProject`. */ export class ExternalProject extends Project { + excludedFiles: ReadonlyArray = []; private typeAcquisition: TypeAcquisition; constructor(public externalProjectName: string, projectService: ProjectService, @@ -1175,6 +1180,11 @@ namespace ts.server { public compileOnSaveEnabled: boolean, private readonly projectFilePath?: string) { super(externalProjectName, ProjectKind.External, projectService, documentRegistry, /*hasExplicitListOfFiles*/ true, languageServiceEnabled, compilerOptions, compileOnSaveEnabled); + + } + + getExcludedFiles() { + return this.excludedFiles; } getProjectRootPath() { diff --git a/src/server/server.ts b/src/server/server.ts index 0fe37dd8ba0..f70e2d0faf7 100644 --- a/src/server/server.ts +++ b/src/server/server.ts @@ -14,6 +14,7 @@ namespace ts.server { globalTypingsCacheLocation: string; logger: Logger; typingSafeListLocation: string; + typesMapLocation: string | undefined; npmLocation: string | undefined; telemetryEnabled: boolean; globalPlugins: ReadonlyArray; @@ -250,6 +251,7 @@ namespace ts.server { eventPort: number, readonly globalTypingsCacheLocation: string, readonly typingSafeListLocation: string, + readonly typesMapLocation: string, private readonly npmLocation: string | undefined, private newLine: string) { this.throttledOperations = new ThrottledOperations(host); @@ -295,6 +297,9 @@ namespace ts.server { if (this.typingSafeListLocation) { args.push(Arguments.TypingSafeListLocation, this.typingSafeListLocation); } + if (this.typesMapLocation) { + args.push(Arguments.TypesMapLocation, this.typesMapLocation); + } if (this.npmLocation) { args.push(Arguments.NpmLocation, this.npmLocation); } @@ -408,10 +413,10 @@ namespace ts.server { class IOSession extends Session { constructor(options: IOSessionOptions) { - const { host, installerEventPort, globalTypingsCacheLocation, typingSafeListLocation, npmLocation, canUseEvents } = options; + const { host, installerEventPort, globalTypingsCacheLocation, typingSafeListLocation, typesMapLocation, npmLocation, canUseEvents } = options; const typingsInstaller = disableAutomaticTypingAcquisition ? undefined - : new NodeTypingsInstaller(telemetryEnabled, logger, host, installerEventPort, globalTypingsCacheLocation, typingSafeListLocation, npmLocation, host.newLine); + : new NodeTypingsInstaller(telemetryEnabled, logger, host, installerEventPort, globalTypingsCacheLocation, typingSafeListLocation, typesMapLocation, npmLocation, host.newLine); super({ host, @@ -768,6 +773,7 @@ namespace ts.server { setStackTraceLimit(); const typingSafeListLocation = findArgument(Arguments.TypingSafeListLocation); + const typesMapLocation = findArgument(Arguments.TypesMapLocation) || combinePaths(sys.getExecutingFilePath(), "../typesMap.json"); const npmLocation = findArgument(Arguments.NpmLocation); function parseStringArray(argName: string): ReadonlyArray { @@ -797,6 +803,7 @@ namespace ts.server { disableAutomaticTypingAcquisition, globalTypingsCacheLocation: getGlobalTypingsCacheLocation(), typingSafeListLocation, + typesMapLocation, npmLocation, telemetryEnabled, logger, diff --git a/src/server/shared.ts b/src/server/shared.ts index 1285eba06e1..66f739a5b97 100644 --- a/src/server/shared.ts +++ b/src/server/shared.ts @@ -12,6 +12,7 @@ namespace ts.server { export const LogFile = "--logFile"; export const EnableTelemetry = "--enableTelemetry"; export const TypingSafeListLocation = "--typingSafeListLocation"; + export const TypesMapLocation = "--typesMapLocation"; /** * This argument specifies the location of the NPM executable. * typingsInstaller will run the command with `${npmLocation} install ...`. diff --git a/src/server/typesMap.json b/src/server/typesMap.json new file mode 100644 index 00000000000..5e7df046dbe --- /dev/null +++ b/src/server/typesMap.json @@ -0,0 +1,487 @@ +{ + "typesMap": { + "jquery": { + "match": "jquery(-(\\.?\\d+)+)?(\\.intellisense)?(\\.min)?\\.js$", + "types": ["jquery"] + }, + "WinJS": { + "match": "^(.*\\/winjs-[.\\d]+)\\/js\\/base\\.js$", + "exclude": [["^", 1, "/.*"]], + "types": ["winjs"] + }, + "Kendo": { + "match": "^(.*\\/kendo)\\/kendo\\.all\\.min\\.js$", + "exclude": [["^", 1, "/.*"]], + "types": ["kendo-ui"] + }, + "Office Nuget": { + "match": "^(.*\\/office\\/1)\\/excel-\\d+\\.debug\\.js$", + "exclude": [["^", 1, "/.*"]], + "types": ["office"] + }, + "Minified files": { + "match": "^(.+\\.min\\.js)$", + "exclude": [["^", 1, "$"]] + } + }, + "simpleMap": { + "accounting": "accounting", + "ace.js": "ace", + "ag-grid": "ag-grid", + "alertify": "alertify", + "alt": "alt", + "amcharts.js": "amcharts", + "amplify": "amplifyjs", + "angular": "angular", + "angular-bootstrap-lightbox": "angular-bootstrap-lightbox", + "angular-cookie": "angular-cookie", + "angular-file-upload": "angular-file-upload", + "angularfire": "angularfire", + "angular-gettext": "angular-gettext", + "angular-google-analytics": "angular-google-analytics", + "angular-local-storage": "angular-local-storage", + "angularLocalStorage": "angularLocalStorage", + "angular-scroll": "angular-scroll", + "angular-spinner": "angular-spinner", + "angular-strap": "angular-strap", + "angulartics": "angulartics", + "angular-toastr": "angular-toastr", + "angular-translate": "angular-translate", + "angular-ui-router": "angular-ui-router", + "angular-ui-tree": "angular-ui-tree", + "angular-wizard": "angular-wizard", + "async": "async", + "atmosphere": "atmosphere", + "aws-sdk": "aws-sdk", + "aws-sdk-js": "aws-sdk", + "axios": "axios", + "backbone": "backbone", + "backbone.layoutmanager": "backbone.layoutmanager", + "backbone.paginator": "backbone.paginator", + "backbone.radio": "backbone.radio", + "backbone-associations": "backbone-associations", + "backbone-relational": "backbone-relational", + "backgrid": "backgrid", + "Bacon": "baconjs", + "benchmark": "benchmark", + "blazy": "blazy", + "bliss": "blissfuljs", + "bluebird": "bluebird", + "body-parser": "body-parser", + "bootbox": "bootbox", + "bootstrap": "bootstrap", + "bootstrap-editable": "x-editable", + "bootstrap-maxlength": "bootstrap-maxlength", + "bootstrap-notify": "bootstrap-notify", + "bootstrap-slider": "bootstrap-slider", + "bootstrap-switch": "bootstrap-switch", + "bowser": "bowser", + "breeze": "breeze", + "browserify": "browserify", + "bson": "bson", + "c3": "c3", + "canvasjs": "canvasjs", + "chai": "chai", + "chalk": "chalk", + "chance": "chance", + "chartist": "chartist", + "cheerio": "cheerio", + "chokidar": "chokidar", + "chosen.jquery": "chosen", + "chroma": "chroma-js", + "ckeditor.js": "ckeditor", + "cli-color": "cli-color", + "clipboard": "clipboard", + "codemirror": "codemirror", + "colors": "colors", + "commander": "commander", + "commonmark": "commonmark", + "compression": "compression", + "confidence": "confidence", + "connect": "connect", + "Control.FullScreen": "leaflet.fullscreen", + "cookie": "cookie", + "cookie-parser": "cookie-parser", + "cookies": "cookies", + "core": "core-js", + "core-js": "core-js", + "crossfilter": "crossfilter", + "crossroads": "crossroads", + "css": "css", + "ct-ui-router-extras": "ui-router-extras", + "d3": "d3", + "dagre-d3": "dagre-d3", + "dat.gui": "dat-gui", + "debug": "debug", + "deep-diff": "deep-diff", + "Dexie": "dexie", + "dialogs": "angular-dialog-service", + "dojo.js": "dojo", + "doT": "dot", + "dragula": "dragula", + "drop": "drop", + "dropbox": "dropboxjs", + "dropzone": "dropzone", + "Dts Name": "Dts Name", + "dust-core": "dustjs-linkedin", + "easeljs": "easeljs", + "ejs": "ejs", + "ember": "ember", + "envify": "envify", + "epiceditor": "epiceditor", + "es6-promise": "es6-promise", + "ES6-Promise": "es6-promise", + "es6-shim": "es6-shim", + "expect": "expect", + "express": "express", + "express-session": "express-session", + "ext-all.js": "extjs", + "extend": "extend", + "fabric": "fabricjs", + "faker": "faker", + "fastclick": "fastclick", + "favico": "favico.js", + "featherlight": "featherlight", + "FileSaver": "FileSaver", + "fingerprint": "fingerprintjs", + "fixed-data-table": "fixed-data-table", + "flickity.pkgd": "flickity", + "flight": "flight", + "flow": "flowjs", + "Flux": "flux", + "formly": "angular-formly", + "foundation": "foundation", + "fpsmeter": "fpsmeter", + "fuse": "fuse", + "generator": "yeoman-generator", + "gl-matrix": "gl-matrix", + "globalize": "globalize", + "graceful-fs": "graceful-fs", + "gridstack": "gridstack", + "gulp": "gulp", + "gulp-rename": "gulp-rename", + "gulp-uglify": "gulp-uglify", + "gulp-util": "gulp-util", + "hammer": "hammerjs", + "handlebars": "handlebars", + "hasher": "hasher", + "he": "he", + "hello.all": "hellojs", + "highcharts.js": "highcharts", + "highlight": "highlightjs", + "history": "history", + "History": "history", + "hopscotch": "hopscotch", + "hotkeys": "angular-hotkeys", + "html2canvas": "html2canvas", + "humane": "humane", + "i18next": "i18next", + "icheck": "icheck", + "impress": "impress", + "incremental-dom": "incremental-dom", + "Inquirer": "inquirer", + "insight": "insight", + "interact": "interactjs", + "intercom": "intercomjs", + "intro": "intro.js", + "ion.rangeSlider": "ion.rangeSlider", + "ionic": "ionic", + "is": "is_js", + "iscroll": "iscroll", + "jade": "jade", + "jasmine": "jasmine", + "joint": "jointjs", + "jquery": "jquery", + "jquery.address": "jquery.address", + "jquery.are-you-sure": "jquery.are-you-sure", + "jquery.blockUI": "jquery.blockUI", + "jquery.bootstrap.wizard": "jquery.bootstrap.wizard", + "jquery.bootstrap-touchspin": "bootstrap-touchspin", + "jquery.color": "jquery.color", + "jquery.colorbox": "jquery.colorbox", + "jquery.contextMenu": "jquery.contextMenu", + "jquery.cookie": "jquery.cookie", + "jquery.customSelect": "jquery.customSelect", + "jquery.cycle.all": "jquery.cycle", + "jquery.cycle2": "jquery.cycle2", + "jquery.dataTables": "jquery.dataTables", + "jquery.dropotron": "jquery.dropotron", + "jquery.fancybox.pack.js": "fancybox", + "jquery.fancytree-all": "jquery.fancytree", + "jquery.fileupload": "jquery.fileupload", + "jquery.flot": "flot", + "jquery.form": "jquery.form", + "jquery.gridster": "jquery.gridster", + "jquery.handsontable.full": "jquery-handsontable", + "jquery.joyride": "jquery.joyride", + "jquery.jqGrid": "jqgrid", + "jquery.mmenu": "jquery.mmenu", + "jquery.mockjax": "jquery-mockjax", + "jquery.noty": "jquery.noty", + "jquery.payment": "jquery.payment", + "jquery.pjax": "jquery.pjax", + "jquery.placeholder": "jquery.placeholder", + "jquery.qrcode": "jquery.qrcode", + "jquery.qtip": "qtip2", + "jquery.raty": "raty", + "jquery.scrollTo": "jquery.scrollTo", + "jquery.signalR": "signalr", + "jquery.simplemodal": "jquery.simplemodal", + "jquery.timeago": "jquery.timeago", + "jquery.tinyscrollbar": "jquery.tinyscrollbar", + "jquery.tipsy": "jquery.tipsy", + "jquery.tooltipster": "tooltipster", + "jquery.transit": "jquery.transit", + "jquery.uniform": "jquery.uniform", + "jquery.watch": "watch", + "jquery-sortable": "jquery-sortable", + "jquery-ui": "jqueryui", + "js.cookie": "js-cookie", + "js-data": "js-data", + "js-data-angular": "js-data-angular", + "js-data-http": "js-data-http", + "jsdom": "jsdom", + "jsnlog": "jsnlog", + "json5": "json5", + "jspdf": "jspdf", + "jsrender": "jsrender", + "js-signals": "js-signals", + "jstorage": "jstorage", + "jstree": "jstree", + "js-yaml": "js-yaml", + "jszip": "jszip", + "katex": "katex", + "kefir": "kefir", + "keymaster": "keymaster", + "keypress": "keypress", + "kinetic": "kineticjs", + "knockback": "knockback", + "knockout": "knockout", + "knockout.mapping": "knockout.mapping", + "knockout.validation": "knockout.validation", + "knockout-paging": "knockout-paging", + "knockout-pre-rendered": "knockout-pre-rendered", + "ladda": "ladda", + "later": "later", + "lazy": "lazy.js", + "Leaflet.Editable": "leaflet-editable", + "leaflet.js": "leaflet", + "less": "less", + "linq": "linq", + "loading-bar": "angular-loading-bar", + "lodash": "lodash", + "log4javascript": "log4javascript", + "loglevel": "loglevel", + "lokijs": "lokijs", + "lovefield": "lovefield", + "lunr": "lunr", + "lz-string": "lz-string", + "mailcheck": "mailcheck", + "maquette": "maquette", + "marked": "marked", + "math": "mathjs", + "MathJax.js": "mathjax", + "matter": "matter-js", + "md5": "blueimp-md5", + "md5.js": "crypto-js", + "messenger": "messenger", + "method-override": "method-override", + "minimatch": "minimatch", + "minimist": "minimist", + "mithril": "mithril", + "mobile-detect": "mobile-detect", + "mocha": "mocha", + "mock-ajax": "jasmine-ajax", + "modernizr": "modernizr", + "Modernizr": "Modernizr", + "moment": "moment", + "moment-range": "moment-range", + "moment-timezone": "moment-timezone", + "mongoose": "mongoose", + "morgan": "morgan", + "mousetrap": "mousetrap", + "ms": "ms", + "mustache": "mustache", + "native.history": "history", + "nconf": "nconf", + "ncp": "ncp", + "nedb": "nedb", + "ng-cordova": "ng-cordova", + "ngDialog": "ng-dialog", + "ng-flow-standalone": "ng-flow", + "ng-grid": "ng-grid", + "ng-i18next": "ng-i18next", + "ng-table": "ng-table", + "node_redis": "redis", + "node-clone": "clone", + "node-fs-extra": "fs-extra", + "node-glob": "glob", + "Nodemailer": "nodemailer", + "node-mime": "mime", + "node-mkdirp": "mkdirp", + "node-mongodb-native": "mongodb", + "node-mysql": "mysql", + "node-open": "open", + "node-optimist": "optimist", + "node-progress": "progress", + "node-semver": "semver", + "node-tar": "tar", + "node-uuid": "node-uuid", + "node-xml2js": "xml2js", + "nopt": "nopt", + "notify": "notify", + "nouislider": "nouislider", + "npm": "npm", + "nprogress": "nprogress", + "numbro": "numbro", + "numeral": "numeraljs", + "nunjucks": "nunjucks", + "nv.d3": "nvd3", + "object-assign": "object-assign", + "oboe-browser": "oboe", + "office": "office-js", + "offline": "offline-js", + "onsenui": "onsenui", + "OpenLayers.js": "openlayers", + "openpgp": "openpgp", + "p2": "p2", + "packery.pkgd": "packery", + "page": "page", + "pako": "pako", + "papaparse": "papaparse", + "passport": "passport", + "passport-local": "passport-local", + "path": "pathjs", + "peer": "peerjs", + "peg": "pegjs", + "photoswipe": "photoswipe", + "picker.js": "pickadate", + "pikaday": "pikaday", + "pixi": "pixi.js", + "platform": "platform", + "Please": "pleasejs", + "plottable": "plottable", + "polymer": "polymer", + "postal": "postal", + "preloadjs": "preloadjs", + "progress": "progress", + "purify": "dompurify", + "purl": "purl", + "q": "q", + "qs": "qs", + "qunit": "qunit", + "ractive": "ractive", + "rangy-core": "rangy", + "raphael": "raphael", + "raven": "ravenjs", + "react": "react", + "react-bootstrap": "react-bootstrap", + "react-intl": "react-intl", + "react-redux": "react-redux", + "ReactRouter": "react-router", + "ready": "domready", + "redux": "redux", + "request": "request", + "require": "require", + "restangular": "restangular", + "reveal": "reveal", + "rickshaw": "rickshaw", + "rimraf": "rimraf", + "rivets": "rivets", + "rx": "rx", + "rx.angular": "rx-angular", + "sammy": "sammyjs", + "SAT": "sat", + "sax-js": "sax", + "screenfull": "screenfull", + "seedrandom": "seedrandom", + "select2": "select2", + "selectize": "selectize", + "serve-favicon": "serve-favicon", + "serve-static": "serve-static", + "shelljs": "shelljs", + "should": "should", + "showdown": "showdown", + "sigma": "sigmajs", + "signature_pad": "signature_pad", + "sinon": "sinon", + "sjcl": "sjcl", + "slick": "slick-carousel", + "smoothie": "smoothie", + "socket.io": "socket.io", + "socket.io-client": "socket.io-client", + "sockjs": "sockjs-client", + "sortable": "angular-ui-sortable", + "soundjs": "soundjs", + "source-map": "source-map", + "spectrum": "spectrum", + "spin": "spin", + "sprintf": "sprintf", + "stampit": "stampit", + "state-machine": "state-machine", + "Stats": "stats", + "store": "storejs", + "string": "string", + "string_score": "string_score", + "strophe": "strophe", + "stylus": "stylus", + "sugar": "sugar", + "superagent": "superagent", + "svg": "svgjs", + "svg-injector": "svg-injector", + "swfobject": "swfobject", + "swig": "swig", + "swipe": "swipe", + "swiper": "swiper", + "system.js": "systemjs", + "tether": "tether", + "three": "threejs", + "through": "through", + "through2": "through2", + "timeline": "timelinejs", + "tinycolor": "tinycolor", + "tmhDynamicLocale": "angular-dynamic-locale", + "toaster": "angularjs-toaster", + "toastr": "toastr", + "tracking": "tracking", + "trunk8": "trunk8", + "turf": "turf", + "tweenjs": "tweenjs", + "TweenMax": "gsap", + "twig": "twig", + "twix": "twix", + "typeahead.bundle": "typeahead", + "typescript": "typescript", + "ui": "winjs", + "ui-bootstrap-tpls": "angular-ui-bootstrap", + "ui-grid": "ui-grid", + "uikit": "uikit", + "underscore": "underscore", + "underscore.string": "underscore.string", + "update-notifier": "update-notifier", + "url": "jsurl", + "UUID": "uuid", + "validator": "validator", + "vega": "vega", + "vex": "vex-js", + "video": "videojs", + "vue": "vue", + "vue-router": "vue-router", + "webtorrent": "webtorrent", + "when": "when", + "winston": "winston", + "wrench-js": "wrench", + "ws": "ws", + "xlsx": "xlsx", + "xml2json": "x2js", + "xmlbuilder-js": "xmlbuilder", + "xregexp": "xregexp", + "yargs": "yargs", + "yosay": "yosay", + "yui": "yui", + "yui3": "yui", + "zepto": "zepto", + "ZeroClipboard": "zeroclipboard", + "ZSchema-browser": "z-schema" + } +} \ No newline at end of file diff --git a/src/server/typingsInstaller/nodeTypingsInstaller.ts b/src/server/typingsInstaller/nodeTypingsInstaller.ts index f80b7a70df6..6a31a114d23 100644 --- a/src/server/typingsInstaller/nodeTypingsInstaller.ts +++ b/src/server/typingsInstaller/nodeTypingsInstaller.ts @@ -17,10 +17,10 @@ namespace ts.server.typingsInstaller { constructor(private readonly logFile?: string) { } - isEnabled() { + isEnabled = () => { return this.logEnabled && this.logFile !== undefined; } - writeLine(text: string) { + writeLine = (text: string) => { try { fs.appendFileSync(this.logFile, text + sys.newLine); } @@ -77,11 +77,12 @@ namespace ts.server.typingsInstaller { private delayedInitializationError: InitializationFailedResponse; - constructor(globalTypingsCacheLocation: string, typingSafeListLocation: string, npmLocation: string | undefined, throttleLimit: number, log: Log) { + constructor(globalTypingsCacheLocation: string, typingSafeListLocation: string, typesMapLocation: string, npmLocation: string | undefined, throttleLimit: number, log: Log) { super( sys, globalTypingsCacheLocation, typingSafeListLocation ? toPath(typingSafeListLocation, "", createGetCanonicalFileName(sys.useCaseSensitiveFileNames)) : toPath("typingSafeList.json", __dirname, createGetCanonicalFileName(sys.useCaseSensitiveFileNames)), + typesMapLocation ? toPath(typesMapLocation, "", createGetCanonicalFileName(sys.useCaseSensitiveFileNames)) : toPath("typesMap.json", __dirname, createGetCanonicalFileName(sys.useCaseSensitiveFileNames)), throttleLimit, log); this.npmPath = npmLocation !== undefined ? npmLocation : getDefaultNPMLocation(process.argv[0]); @@ -175,6 +176,7 @@ namespace ts.server.typingsInstaller { const logFilePath = findArgument(server.Arguments.LogFile); const globalTypingsCacheLocation = findArgument(server.Arguments.GlobalCacheLocation); const typingSafeListLocation = findArgument(server.Arguments.TypingSafeListLocation); + const typesMapLocation = findArgument(server.Arguments.TypesMapLocation); const npmLocation = findArgument(server.Arguments.NpmLocation); const log = new FileLog(logFilePath); @@ -189,6 +191,6 @@ namespace ts.server.typingsInstaller { } process.exit(0); }); - const installer = new NodeTypingsInstaller(globalTypingsCacheLocation, typingSafeListLocation, npmLocation, /*throttleLimit*/5, log); + const installer = new NodeTypingsInstaller(globalTypingsCacheLocation, typingSafeListLocation, typesMapLocation, npmLocation, /*throttleLimit*/5, log); installer.listen(); } diff --git a/src/server/typingsInstaller/typingsInstaller.ts b/src/server/typingsInstaller/typingsInstaller.ts index df2b6916e4b..2b941c42141 100644 --- a/src/server/typingsInstaller/typingsInstaller.ts +++ b/src/server/typingsInstaller/typingsInstaller.ts @@ -97,10 +97,11 @@ namespace ts.server.typingsInstaller { protected readonly installTypingHost: InstallTypingHost, private readonly globalCachePath: string, private readonly safeListPath: Path, + private readonly typesMapLocation: Path, private readonly throttleLimit: number, protected readonly log = nullLog) { if (this.log.isEnabled()) { - this.log.writeLine(`Global cache location '${globalCachePath}', safe file path '${safeListPath}'`); + this.log.writeLine(`Global cache location '${globalCachePath}', safe file path '${safeListPath}', types map path ${typesMapLocation}`); } this.processCacheLocation(this.globalCachePath); } @@ -145,7 +146,7 @@ namespace ts.server.typingsInstaller { } if (this.safeList === undefined) { - this.safeList = JsTyping.loadSafeList(this.installTypingHost, this.safeListPath); + this.initializeSafeList(); } const discoverTypingsResult = JsTyping.discoverTypings( this.installTypingHost, @@ -178,6 +179,20 @@ namespace ts.server.typingsInstaller { } } + private initializeSafeList() { + // Prefer the safe list from the types map if it exists + if (this.typesMapLocation) { + const safeListFromMap = JsTyping.loadTypesMap(this.installTypingHost, this.typesMapLocation); + if (safeListFromMap) { + this.log.writeLine(`Loaded safelist from types map file '${this.typesMapLocation}'`); + this.safeList = safeListFromMap; + return; + } + this.log.writeLine(`Failed to load safelist from types map file '${this.typesMapLocation}'`); + } + this.safeList = JsTyping.loadSafeList(this.installTypingHost, this.safeListPath); + } + private processCacheLocation(cacheLocation: string) { if (this.log.isEnabled()) { this.log.writeLine(`Processing cache location '${cacheLocation}'`); diff --git a/src/server/utilities.ts b/src/server/utilities.ts index e2e2a67eaf1..66ebfb24dc6 100644 --- a/src/server/utilities.ts +++ b/src/server/utilities.ts @@ -49,7 +49,7 @@ namespace ts.server { export function createInstallTypingsRequest(project: Project, typeAcquisition: TypeAcquisition, unresolvedImports: SortedReadonlyArray, cachePath?: string): DiscoverTypings { return { projectName: project.getProjectName(), - fileNames: project.getFileNames(/*excludeFilesFromExternalLibraries*/ true, /*excludeConfigFiles*/ true), + fileNames: project.getFileNames(/*excludeFilesFromExternalLibraries*/ true, /*excludeConfigFiles*/ true).concat(project.getExcludedFiles() as NormalizedPath[]), compilerOptions: project.getCompilerOptions(), typeAcquisition, unresolvedImports, diff --git a/src/services/jsTyping.ts b/src/services/jsTyping.ts index 5e7b7d424f8..c775a995fd6 100644 --- a/src/services/jsTyping.ts +++ b/src/services/jsTyping.ts @@ -47,6 +47,14 @@ namespace ts.JsTyping { return createMapFromTemplate(result.config); } + export function loadTypesMap(host: TypingResolutionHost, typesMapPath: Path): SafeList | undefined { + const result = readConfigFile(typesMapPath, path => host.readFile(path)); + if (result.config) { + return createMapFromTemplate(result.config.simpleMap); + } + return undefined; + } + /** * @param host is the object providing I/O related operations. * @param fileNames are the file names that belong to the same project