diff --git a/extensions/typescript-language-features/package.json b/extensions/typescript-language-features/package.json index ed85772ade5..412986886aa 100644 --- a/extensions/typescript-language-features/package.json +++ b/extensions/typescript-language-features/package.json @@ -2556,6 +2556,16 @@ "TypeScript" ] }, + "js/ts.tsserver.diagnosticDir": { + "type": "string", + "markdownDescription": "%configuration.tsserver.diagnosticDir%", + "scope": "window", + "keywords": [ + "TypeScript", + "diagnostic", + "memory" + ] + }, "typescript.tsserver.maxTsServerMemory": { "type": "number", "default": 3072, @@ -2563,6 +2573,48 @@ "markdownDeprecationMessage": "%configuration.tsserver.maxTsServerMemory.unifiedDeprecationMessage%", "scope": "window" }, + "js/ts.tsserver.heapSnapshot": { + "type": "number", + "default": 0, + "minimum": 0, + "markdownDescription": "%configuration.tsserver.heapSnapshot%", + "scope": "window", + "keywords": [ + "TypeScript", + "memory", + "diagnostics" + ] + }, + "js/ts.tsserver.heapProfile": { + "type": "object", + "default": { + "enabled": false + }, + "markdownDescription": "%configuration.tsserver.heapProfile%", + "scope": "window", + "properties": { + "enabled": { + "type": "boolean", + "default": false, + "description": "%configuration.tsserver.heapProfile.enabled%" + }, + "dir": { + "type": "string", + "description": "%configuration.tsserver.heapProfile.dir%" + }, + "interval": { + "type": "number", + "minimum": 1, + "description": "%configuration.tsserver.heapProfile.interval%" + } + }, + "keywords": [ + "TypeScript", + "memory", + "heap", + "profile" + ] + }, "js/ts.tsserver.watchOptions": { "description": "%configuration.tsserver.watchOptions%", "scope": "window", diff --git a/extensions/typescript-language-features/package.nls.json b/extensions/typescript-language-features/package.nls.json index 8c28dd87ccd..40c4081de54 100644 --- a/extensions/typescript-language-features/package.nls.json +++ b/extensions/typescript-language-features/package.nls.json @@ -126,6 +126,12 @@ "configuration.tsserver.maxTsServerMemory": "The maximum amount of memory (in MB) to allocate to the TypeScript server process. To use a memory limit greater than 4 GB, use `#js/ts.tsserver.node.path#` to run TS Server with a custom Node installation.", "configuration.tsserver.maxTsServerMemory.unifiedDeprecationMessage": "This setting is deprecated. Use `#js/ts.tsserver.maxMemory#` instead.", "configuration.tsserver.maxMemory": "The maximum amount of memory (in MB) to allocate to the TypeScript server process. To use a memory limit greater than 4 GB, use `#js/ts.tsserver.node.path#` to run TS Server with a custom Node installation.", + "configuration.tsserver.diagnosticDir": "Directory where TypeScript server writes Node diagnostic output by passing `--diagnostic-dir`.", + "configuration.tsserver.heapSnapshot": "Controls how many near-heap-limit snapshots TypeScript server writes by passing `--heapsnapshot-near-heap-limit`. Set to `0` to disable.", + "configuration.tsserver.heapProfile": "Configures heap profiling for TypeScript server.", + "configuration.tsserver.heapProfile.enabled": "Enable heap profiling for TypeScript server by passing `--heap-prof`.", + "configuration.tsserver.heapProfile.dir": "Directory where TypeScript server writes heap profiles by passing `--heap-prof-dir`.", + "configuration.tsserver.heapProfile.interval": "Sampling interval in bytes for TypeScript server heap profiling by passing `--heap-prof-interval`.", "configuration.tsserver.experimental.enableProjectDiagnostics": "Enables project wide error reporting.", "configuration.tsserver.experimental.enableProjectDiagnostics.unifiedDeprecationMessage": "This setting is deprecated. Use `#js/ts.tsserver.experimental.enableProjectDiagnostics#` instead.", "typescript.locale": "Sets the locale used to report JavaScript and TypeScript errors. Defaults to use VS Code's locale.", diff --git a/extensions/typescript-language-features/src/configuration/configuration.ts b/extensions/typescript-language-features/src/configuration/configuration.ts index a557f08c024..ae43fda659e 100644 --- a/extensions/typescript-language-features/src/configuration/configuration.ts +++ b/extensions/typescript-language-features/src/configuration/configuration.ts @@ -110,6 +110,12 @@ export class ImplicitProjectConfiguration { } } +export interface TsServerHeapProfileConfiguration { + readonly enabled: boolean; + readonly dir: string | undefined; + readonly interval: number | undefined; +} + export interface TypeScriptServiceConfiguration { readonly locale: string | null; readonly globalTsdk: string | null; @@ -126,6 +132,9 @@ export interface TypeScriptServiceConfiguration { readonly enableDiagnosticsTelemetry: boolean; readonly enableProjectDiagnostics: boolean; readonly maxTsServerMemory: number; + readonly diagnosticDir: string | undefined; + readonly heapSnapshot: number; + readonly heapProfile: TsServerHeapProfileConfiguration; readonly enablePromptUseWorkspaceTsdk: boolean; readonly useVsCodeWatcher: boolean; readonly watchOptions: Proto.WatchOptions | undefined; @@ -168,6 +177,9 @@ export abstract class BaseServiceConfigurationProvider implements ServiceConfigu enableDiagnosticsTelemetry: this.readEnableDiagnosticsTelemetry(), enableProjectDiagnostics: this.readEnableProjectDiagnostics(), maxTsServerMemory: this.readMaxTsServerMemory(), + diagnosticDir: this.readDiagnosticDir(), + heapSnapshot: this.readHeapSnapshot(), + heapProfile: this.readHeapProfileConfiguration(), enablePromptUseWorkspaceTsdk: this.readEnablePromptUseWorkspaceTsdk(), useVsCodeWatcher: this.readUseVsCodeWatcher(configuration), watchOptions: this.readWatchOptions(), @@ -288,6 +300,42 @@ export abstract class BaseServiceConfigurationProvider implements ServiceConfigu return Math.max(memoryInMB, minimumMaxMemory); } + protected readDiagnosticDir(): string | undefined { + const diagnosticDir = readUnifiedConfig('tsserver.diagnosticDir', undefined, { fallbackSection: 'typescript' }); + return typeof diagnosticDir === 'string' && diagnosticDir.length > 0 ? diagnosticDir : undefined; + } + + protected readHeapSnapshot(): number { + const defaultNearHeapLimitSnapshotCount = 0; + const nearHeapLimitSnapshotCount = readUnifiedConfig('tsserver.heapSnapshot', defaultNearHeapLimitSnapshotCount, { fallbackSection: 'typescript' }); + if (!Number.isSafeInteger(nearHeapLimitSnapshotCount)) { + return defaultNearHeapLimitSnapshotCount; + } + return Math.max(nearHeapLimitSnapshotCount, 0); + } + + private readHeapProfileConfiguration(): TsServerHeapProfileConfiguration { + const defaultHeapProfileConfiguration: TsServerHeapProfileConfiguration = { + enabled: false, + dir: undefined, + interval: undefined, + }; + + const rawConfig = readUnifiedConfig<{ enabled?: unknown; dir?: unknown; interval?: unknown }>('tsserver.heapProfile', defaultHeapProfileConfiguration, { fallbackSection: 'typescript' }); + + const enabled = typeof rawConfig.enabled === 'boolean' ? rawConfig.enabled : false; + const dir = typeof rawConfig.dir === 'string' && rawConfig.dir.length > 0 ? rawConfig.dir : undefined; + const interval = typeof rawConfig.interval === 'number' && Number.isSafeInteger(rawConfig.interval) && rawConfig.interval > 0 + ? rawConfig.interval + : undefined; + + return { + enabled, + dir, + interval, + }; + } + protected readEnablePromptUseWorkspaceTsdk(): boolean { return readUnifiedConfig('tsdk.promptToUseWorkspaceVersion', false, { fallbackSection: 'typescript', fallbackSubSectionNameOverride: 'enablePromptUseWorkspaceTsdk' }); } diff --git a/extensions/typescript-language-features/src/tsServer/serverProcess.electron.ts b/extensions/typescript-language-features/src/tsServer/serverProcess.electron.ts index 7dbde90f792..992cae925df 100644 --- a/extensions/typescript-language-features/src/tsServer/serverProcess.electron.ts +++ b/extensions/typescript-language-features/src/tsServer/serverProcess.electron.ts @@ -162,6 +162,24 @@ function getExecArgv(kind: TsServerProcessKind, configuration: TypeScriptService args.push(`--max-old-space-size=${configuration.maxTsServerMemory}`); } + if (configuration.diagnosticDir) { + args.push(`--diagnostic-dir=${configuration.diagnosticDir}`); + } + + if (configuration.heapSnapshot > 0) { + args.push(`--heapsnapshot-near-heap-limit=${configuration.heapSnapshot}`); + } + + if (configuration.heapProfile.enabled) { + args.push('--heap-prof'); + if (configuration.heapProfile.dir) { + args.push(`--heap-prof-dir=${configuration.heapProfile.dir}`); + } + if (configuration.heapProfile.interval) { + args.push(`--heap-prof-interval=${configuration.heapProfile.interval}`); + } + } + return args; }