diff --git a/build/gulpfile.editor.js b/build/gulpfile.editor.js index 39f83320051..1136430dd99 100644 --- a/build/gulpfile.editor.js +++ b/build/gulpfile.editor.js @@ -41,22 +41,23 @@ const extractEditorSrcTask = task.define('extract-editor-src', () => { standalone.extractEditor({ sourcesRoot: path.join(root, 'src'), entryPoints: [ - 'vs/editor/editor.main', - 'vs/editor/editor.worker.start', - 'vs/editor/common/services/editorWebWorkerMain', + 'vs/editor/editor.main.ts', + 'vs/editor/editor.worker.start.ts', + 'vs/editor/common/services/editorWebWorkerMain.ts', ], inlineEntryPoints: [ apiusages, extrausages ], typings: [], + additionalFilesToCopyOut: [ + 'vs/base/browser/dompurify/dompurify.js', + 'vs/base/common/marked/marked.js', + ], shakeLevel: 2, // 0-Files, 1-InnerFile, 2-ClassMembers importIgnorePattern: /\.css$/, destRoot: path.join(root, 'out-editor-src'), tsOutDir: '../out-monaco-editor-core/esm/vs', - redirects: { - '@vscode/tree-sitter-wasm': '../node_modules/@vscode/tree-sitter-wasm/wasm/web-tree-sitter', - } }); }); diff --git a/build/lib/monaco-api.js b/build/lib/monaco-api.js index 06e3d63029f..3d9a4c7ca10 100644 --- a/build/lib/monaco-api.js +++ b/build/lib/monaco-api.js @@ -336,7 +336,7 @@ function generateDeclarationFile(ts, recipe, sourceFileGetter) { usage.push(`var b: any;`); const generateUsageImport = (moduleId) => { const importName = 'm' + (++usageCounter); - usageImports.push(`import * as ${importName} from './${moduleId.replace(/\.d\.ts$/, '')}';`); + usageImports.push(`import * as ${importName} from './${moduleId}';`); return importName; }; const enums = []; @@ -538,6 +538,9 @@ class DeclarationResolver { if (/\.d\.ts$/.test(moduleId)) { return path_1.default.join(SRC, moduleId); } + if (/\.js$/.test(moduleId)) { + return path_1.default.join(SRC, moduleId.replace(/\.js$/, '.ts')); + } return path_1.default.join(SRC, `${moduleId}.ts`); } _getDeclarationSourceFile(moduleId) { @@ -555,7 +558,7 @@ class DeclarationResolver { const fileMap = new Map([ ['file.ts', fileContents] ]); - const service = this.ts.createLanguageService(new typeScriptLanguageServiceHost_1.TypeScriptLanguageServiceHost(this.ts, new Map(), fileMap, {}, 'defaultLib:es5')); + const service = this.ts.createLanguageService(new typeScriptLanguageServiceHost_1.TypeScriptLanguageServiceHost(this.ts, fileMap, {})); const text = service.getEmitOutput('file.ts', true, true).outputFiles[0].text; return new CacheEntry(this.ts.createSourceFile(fileName, text, this.ts.ScriptTarget.ES5), mtime); } diff --git a/build/lib/monaco-api.ts b/build/lib/monaco-api.ts index f3bdfa3a693..e0622bcd336 100644 --- a/build/lib/monaco-api.ts +++ b/build/lib/monaco-api.ts @@ -406,7 +406,7 @@ function generateDeclarationFile(ts: Typescript, recipe: string, sourceFileGette const generateUsageImport = (moduleId: string) => { const importName = 'm' + (++usageCounter); - usageImports.push(`import * as ${importName} from './${moduleId.replace(/\.d\.ts$/, '')}';`); + usageImports.push(`import * as ${importName} from './${moduleId}';`); return importName; }; @@ -641,6 +641,9 @@ export class DeclarationResolver { if (/\.d\.ts$/.test(moduleId)) { return path.join(SRC, moduleId); } + if (/\.js$/.test(moduleId)) { + return path.join(SRC, moduleId.replace(/\.js$/, '.ts')); + } return path.join(SRC, `${moduleId}.ts`); } @@ -662,7 +665,7 @@ export class DeclarationResolver { const fileMap: IFileMap = new Map([ ['file.ts', fileContents] ]); - const service = this.ts.createLanguageService(new TypeScriptLanguageServiceHost(this.ts, new Map(), fileMap, {}, 'defaultLib:es5')); + const service = this.ts.createLanguageService(new TypeScriptLanguageServiceHost(this.ts, fileMap, {})); const text = service.getEmitOutput('file.ts', true, true).outputFiles[0].text; return new CacheEntry( this.ts.createSourceFile(fileName, text, this.ts.ScriptTarget.ES5), diff --git a/build/lib/standalone.js b/build/lib/standalone.js index a56cbf3deff..b153e70348b 100644 --- a/build/lib/standalone.js +++ b/build/lib/standalone.js @@ -44,8 +44,6 @@ exports.extractEditor = extractEditor; const fs_1 = __importDefault(require("fs")); const path_1 = __importDefault(require("path")); const tss = __importStar(require("./treeshaking")); -const REPO_ROOT = path_1.default.join(__dirname, '../../'); -const SRC_DIR = path_1.default.join(REPO_ROOT, 'src'); const dirCache = {}; function writeFile(filePath, contents) { function ensureDirs(dirPath) { @@ -84,35 +82,33 @@ function extractEditor(options) { console.log(`Running tree shaker with shakeLevel ${tss.toStringShakeLevel(options.shakeLevel)}`); // Take the extra included .d.ts files from `tsconfig.monaco.json` options.typings = tsConfig.include.filter(includedFile => /\.d\.ts$/.test(includedFile)); - // Add extra .d.ts files from `node_modules/@types/` - if (Array.isArray(options.compilerOptions?.types)) { - options.compilerOptions.types.forEach((type) => { - if (type === '@webgpu/types') { - options.typings.push(`../node_modules/${type}/dist/index.d.ts`); - } - else { - options.typings.push(`../node_modules/@types/${type}/index.d.ts`); - } - }); - } const result = tss.shake(options); for (const fileName in result) { if (result.hasOwnProperty(fileName)) { - writeFile(path_1.default.join(options.destRoot, fileName), result[fileName]); + const relativePath = path_1.default.relative(options.sourcesRoot, fileName); + writeFile(path_1.default.join(options.destRoot, relativePath), result[fileName]); } } const copied = {}; - const copyFile = (fileName) => { + const copyFile = (fileName, toFileName) => { if (copied[fileName]) { return; } copied[fileName] = true; - const srcPath = path_1.default.join(options.sourcesRoot, fileName); - const dstPath = path_1.default.join(options.destRoot, fileName); - writeFile(dstPath, fs_1.default.readFileSync(srcPath)); + if (path_1.default.isAbsolute(fileName)) { + const relativePath = path_1.default.relative(options.sourcesRoot, fileName); + const dstPath = path_1.default.join(options.destRoot, toFileName ?? relativePath); + writeFile(dstPath, fs_1.default.readFileSync(fileName)); + } + else { + const srcPath = path_1.default.join(options.sourcesRoot, fileName); + const dstPath = path_1.default.join(options.destRoot, toFileName ?? fileName); + writeFile(dstPath, fs_1.default.readFileSync(srcPath)); + } }; const writeOutputFile = (fileName, contents) => { - writeFile(path_1.default.join(options.destRoot, fileName), contents); + const relativePath = path_1.default.isAbsolute(fileName) ? path_1.default.relative(options.sourcesRoot, fileName) : fileName; + writeFile(path_1.default.join(options.destRoot, relativePath), contents); }; for (const fileName in result) { if (result.hasOwnProperty(fileName)) { @@ -138,17 +134,18 @@ function extractEditor(options) { } delete tsConfig.compilerOptions.moduleResolution; writeOutputFile('tsconfig.json', JSON.stringify(tsConfig, null, '\t')); - [ - 'vs/loader.js', - 'typings/css.d.ts' - ].forEach(copyFile); + options.additionalFilesToCopyOut?.forEach((file) => { + copyFile(file); + }); + copyFile('vs/loader.js'); + copyFile('typings/css.d.ts'); + copyFile('../node_modules/@vscode/tree-sitter-wasm/wasm/web-tree-sitter.d.ts', '@vscode/tree-sitter-wasm.d.ts'); } function transportCSS(module, enqueue, write) { if (!/\.css/.test(module)) { return false; } - const filename = path_1.default.join(SRC_DIR, module); - const fileContents = fs_1.default.readFileSync(filename).toString(); + const fileContents = fs_1.default.readFileSync(module).toString(); const inlineResources = 'base64'; // see https://github.com/microsoft/monaco-editor/issues/148 const newContents = _rewriteOrInlineUrls(fileContents, inlineResources === 'base64'); write(module, newContents); @@ -163,7 +160,7 @@ function transportCSS(module, enqueue, write) { return relativeFontPath; } const imagePath = path_1.default.join(path_1.default.dirname(module), url); - const fileContents = fs_1.default.readFileSync(path_1.default.join(SRC_DIR, imagePath)); + const fileContents = fs_1.default.readFileSync(imagePath); const MIME = /\.svg$/.test(url) ? 'image/svg+xml' : 'image/png'; let DATA = ';base64,' + fileContents.toString('base64'); if (!forceBase64 && /\.svg$/.test(url)) { diff --git a/build/lib/standalone.ts b/build/lib/standalone.ts index 9e44d52908d..5f2104cb4c6 100644 --- a/build/lib/standalone.ts +++ b/build/lib/standalone.ts @@ -7,9 +7,6 @@ import fs from 'fs'; import path from 'path'; import * as tss from './treeshaking'; -const REPO_ROOT = path.join(__dirname, '../../'); -const SRC_DIR = path.join(REPO_ROOT, 'src'); - const dirCache: { [dir: string]: boolean } = {}; function writeFile(filePath: string, contents: Buffer | string): void { @@ -29,7 +26,7 @@ function writeFile(filePath: string, contents: Buffer | string): void { fs.writeFileSync(filePath, contents); } -export function extractEditor(options: tss.ITreeShakingOptions & { destRoot: string; tsOutDir: string }): void { +export function extractEditor(options: tss.ITreeShakingOptions & { destRoot: string; tsOutDir: string; additionalFilesToCopyOut?: string[] }): void { const ts = require('typescript') as typeof import('typescript'); const tsConfig = JSON.parse(fs.readFileSync(path.join(options.sourcesRoot, 'tsconfig.monaco.json')).toString()); @@ -57,35 +54,33 @@ export function extractEditor(options: tss.ITreeShakingOptions & { destRoot: str // Take the extra included .d.ts files from `tsconfig.monaco.json` options.typings = (tsConfig.include).filter(includedFile => /\.d\.ts$/.test(includedFile)); - // Add extra .d.ts files from `node_modules/@types/` - if (Array.isArray(options.compilerOptions?.types)) { - options.compilerOptions.types.forEach((type: string) => { - if (type === '@webgpu/types') { - options.typings.push(`../node_modules/${type}/dist/index.d.ts`); - } else { - options.typings.push(`../node_modules/@types/${type}/index.d.ts`); - } - }); - } - const result = tss.shake(options); for (const fileName in result) { if (result.hasOwnProperty(fileName)) { - writeFile(path.join(options.destRoot, fileName), result[fileName]); + const relativePath = path.relative(options.sourcesRoot, fileName); + writeFile(path.join(options.destRoot, relativePath), result[fileName]); } } const copied: { [fileName: string]: boolean } = {}; - const copyFile = (fileName: string) => { + const copyFile = (fileName: string, toFileName?: string) => { if (copied[fileName]) { return; } copied[fileName] = true; - const srcPath = path.join(options.sourcesRoot, fileName); - const dstPath = path.join(options.destRoot, fileName); - writeFile(dstPath, fs.readFileSync(srcPath)); + + if (path.isAbsolute(fileName)) { + const relativePath = path.relative(options.sourcesRoot, fileName); + const dstPath = path.join(options.destRoot, toFileName ?? relativePath); + writeFile(dstPath, fs.readFileSync(fileName)); + } else { + const srcPath = path.join(options.sourcesRoot, fileName); + const dstPath = path.join(options.destRoot, toFileName ?? fileName); + writeFile(dstPath, fs.readFileSync(srcPath)); + } }; const writeOutputFile = (fileName: string, contents: string | Buffer) => { - writeFile(path.join(options.destRoot, fileName), contents); + const relativePath = path.isAbsolute(fileName) ? path.relative(options.sourcesRoot, fileName) : fileName; + writeFile(path.join(options.destRoot, relativePath), contents); }; for (const fileName in result) { if (result.hasOwnProperty(fileName)) { @@ -115,10 +110,13 @@ export function extractEditor(options: tss.ITreeShakingOptions & { destRoot: str delete tsConfig.compilerOptions.moduleResolution; writeOutputFile('tsconfig.json', JSON.stringify(tsConfig, null, '\t')); - [ - 'vs/loader.js', - 'typings/css.d.ts' - ].forEach(copyFile); + options.additionalFilesToCopyOut?.forEach((file) => { + copyFile(file); + }); + + copyFile('vs/loader.js'); + copyFile('typings/css.d.ts'); + copyFile('../node_modules/@vscode/tree-sitter-wasm/wasm/web-tree-sitter.d.ts', '@vscode/tree-sitter-wasm.d.ts'); } function transportCSS(module: string, enqueue: (module: string) => void, write: (path: string, contents: string | Buffer) => void): boolean { @@ -127,8 +125,7 @@ function transportCSS(module: string, enqueue: (module: string) => void, write: return false; } - const filename = path.join(SRC_DIR, module); - const fileContents = fs.readFileSync(filename).toString(); + const fileContents = fs.readFileSync(module).toString(); const inlineResources = 'base64'; // see https://github.com/microsoft/monaco-editor/issues/148 const newContents = _rewriteOrInlineUrls(fileContents, inlineResources === 'base64'); @@ -146,7 +143,7 @@ function transportCSS(module: string, enqueue: (module: string) => void, write: } const imagePath = path.join(path.dirname(module), url); - const fileContents = fs.readFileSync(path.join(SRC_DIR, imagePath)); + const fileContents = fs.readFileSync(imagePath); const MIME = /\.svg$/.test(url) ? 'image/svg+xml' : 'image/png'; let DATA = ';base64,' + fileContents.toString('base64'); diff --git a/build/lib/treeshaking.js b/build/lib/treeshaking.js index 8085f5ea02d..a679446f60b 100644 --- a/build/lib/treeshaking.js +++ b/build/lib/treeshaking.js @@ -12,7 +12,6 @@ exports.shake = shake; const fs_1 = __importDefault(require("fs")); const path_1 = __importDefault(require("path")); const typeScriptLanguageServiceHost_1 = require("./typeScriptLanguageServiceHost"); -const TYPESCRIPT_LIB_FOLDER = path_1.default.dirname(require.resolve('typescript/lib/lib.d.ts')); var ShakeLevel; (function (ShakeLevel) { ShakeLevel[ShakeLevel["Files"] = 0] = "Files"; @@ -68,101 +67,26 @@ function shake(options) { //#region Discovery, LanguageService & Setup function createTypeScriptLanguageService(ts, options) { // Discover referenced files - const FILES = discoverAndReadFiles(ts, options); + const FILES = new Map(); + // Add entrypoints + options.entryPoints.forEach(entryPoint => { + const filePath = path_1.default.join(options.sourcesRoot, entryPoint); + FILES.set(filePath, fs_1.default.readFileSync(filePath).toString()); + }); // Add fake usage files options.inlineEntryPoints.forEach((inlineEntryPoint, index) => { - FILES.set(`inlineEntryPoint.${index}.ts`, inlineEntryPoint); + FILES.set(path_1.default.join(options.sourcesRoot, `inlineEntryPoint.${index}.ts`), inlineEntryPoint); }); // Add additional typings options.typings.forEach((typing) => { const filePath = path_1.default.join(options.sourcesRoot, typing); - FILES.set(typing, fs_1.default.readFileSync(filePath).toString()); + FILES.set(filePath, fs_1.default.readFileSync(filePath).toString()); }); - // Resolve libs - const RESOLVED_LIBS = processLibFiles(ts, options); - const compilerOptions = ts.convertCompilerOptionsFromJson(options.compilerOptions, options.sourcesRoot).options; - const host = new typeScriptLanguageServiceHost_1.TypeScriptLanguageServiceHost(ts, RESOLVED_LIBS, FILES, compilerOptions, 'defaultLib:lib.d.ts'); + const basePath = path_1.default.join(options.sourcesRoot, '..'); + const compilerOptions = ts.convertCompilerOptionsFromJson(options.compilerOptions, basePath).options; + const host = new typeScriptLanguageServiceHost_1.TypeScriptLanguageServiceHost(ts, FILES, compilerOptions); return ts.createLanguageService(host); } -/** - * Read imports and follow them until all files have been handled - */ -function discoverAndReadFiles(ts, options) { - const FILES = new Map(); - const in_queue = Object.create(null); - const queue = []; - const enqueue = (moduleId) => { - // To make the treeshaker work on windows... - moduleId = moduleId.replace(/\\/g, '/'); - if (in_queue[moduleId]) { - return; - } - in_queue[moduleId] = true; - queue.push(moduleId); - }; - options.entryPoints.forEach((entryPoint) => enqueue(entryPoint)); - while (queue.length > 0) { - const moduleId = queue.shift(); - let redirectedModuleId = moduleId; - if (options.redirects[moduleId]) { - redirectedModuleId = options.redirects[moduleId]; - } - const dts_filename = path_1.default.join(options.sourcesRoot, redirectedModuleId + '.d.ts'); - if (fs_1.default.existsSync(dts_filename)) { - const dts_filecontents = fs_1.default.readFileSync(dts_filename).toString(); - FILES.set(`${moduleId}.d.ts`, dts_filecontents); - continue; - } - const js_filename = path_1.default.join(options.sourcesRoot, redirectedModuleId + '.js'); - if (fs_1.default.existsSync(js_filename)) { - // This is an import for a .js file, so ignore it... - continue; - } - const ts_filename = path_1.default.join(options.sourcesRoot, redirectedModuleId + '.ts'); - const ts_filecontents = fs_1.default.readFileSync(ts_filename).toString(); - const info = ts.preProcessFile(ts_filecontents); - for (let i = info.importedFiles.length - 1; i >= 0; i--) { - const importedFileName = info.importedFiles[i].fileName; - if (options.importIgnorePattern.test(importedFileName)) { - // Ignore *.css imports - continue; - } - let importedModuleId = importedFileName; - if (/(^\.\/)|(^\.\.\/)/.test(importedModuleId)) { - importedModuleId = path_1.default.join(path_1.default.dirname(moduleId), importedModuleId); - if (importedModuleId.endsWith('.js')) { // ESM: code imports require to be relative and have a '.js' file extension - importedModuleId = importedModuleId.substr(0, importedModuleId.length - 3); - } - } - enqueue(importedModuleId); - } - FILES.set(`${moduleId}.ts`, ts_filecontents); - } - return FILES; -} -/** - * Read lib files and follow lib references - */ -function processLibFiles(ts, options) { - const stack = [...options.compilerOptions.lib]; - const result = new Map(); - while (stack.length > 0) { - const filename = `lib.${stack.shift().toLowerCase()}.d.ts`; - const key = `defaultLib:${filename}`; - if (!result.has(key)) { - // add this file - const filepath = path_1.default.join(TYPESCRIPT_LIB_FOLDER, filename); - const sourceText = fs_1.default.readFileSync(filepath).toString(); - result.set(key, sourceText); - // precess dependencies and "recurse" - const info = ts.preProcessFile(sourceText); - for (const ref of info.libReferenceDirectives) { - stack.push(ref.fileName); - } - } - } - return result; -} //#endregion //#region Tree Shaking var NodeColor; @@ -403,16 +327,22 @@ function markNodes(ts, languageService, options) { if (importText.endsWith('.js')) { // ESM: code imports require to be relative and to have a '.js' file extension importText = importText.substr(0, importText.length - 3); } - fullPath = path_1.default.join(path_1.default.dirname(nodeSourceFile.fileName), importText) + '.ts'; + fullPath = path_1.default.join(path_1.default.dirname(nodeSourceFile.fileName), importText); } else { - fullPath = importText + '.ts'; + fullPath = importText; + } + if (fs_1.default.existsSync(fullPath + '.ts')) { + fullPath = fullPath + '.ts'; + } + else { + fullPath = fullPath + '.js'; } enqueueFile(fullPath); } - options.entryPoints.forEach(moduleId => enqueueFile(moduleId + '.ts')); + options.entryPoints.forEach(moduleId => enqueueFile(path_1.default.join(options.sourcesRoot, moduleId))); // Add fake usage files - options.inlineEntryPoints.forEach((_, index) => enqueueFile(`inlineEntryPoint.${index}.ts`)); + options.inlineEntryPoints.forEach((_, index) => enqueueFile(path_1.default.join(options.sourcesRoot, `inlineEntryPoint.${index}.ts`))); let step = 0; const checker = program.getTypeChecker(); while (black_queue.length > 0 || gray_queue.length > 0) { diff --git a/build/lib/treeshaking.ts b/build/lib/treeshaking.ts index a3424d366e6..9c2fcc11925 100644 --- a/build/lib/treeshaking.ts +++ b/build/lib/treeshaking.ts @@ -6,9 +6,7 @@ import fs from 'fs'; import path from 'path'; import type * as ts from 'typescript'; -import { IFileMap, ILibMap, TypeScriptLanguageServiceHost } from './typeScriptLanguageServiceHost'; - -const TYPESCRIPT_LIB_FOLDER = path.dirname(require.resolve('typescript/lib/lib.d.ts')); +import { IFileMap, TypeScriptLanguageServiceHost } from './typeScriptLanguageServiceHost'; enum ShakeLevel { Files = 0, @@ -57,8 +55,6 @@ export interface ITreeShakingOptions { * regex pattern to ignore certain imports e.g. `.css` imports */ importIgnorePattern: RegExp; - - redirects: { [module: string]: string }; } export interface ITreeShakingResult { @@ -111,126 +107,31 @@ export function shake(options: ITreeShakingOptions): ITreeShakingResult { //#region Discovery, LanguageService & Setup function createTypeScriptLanguageService(ts: typeof import('typescript'), options: ITreeShakingOptions): ts.LanguageService { // Discover referenced files - const FILES = discoverAndReadFiles(ts, options); + const FILES: IFileMap = new Map(); + + // Add entrypoints + options.entryPoints.forEach(entryPoint => { + const filePath = path.join(options.sourcesRoot, entryPoint); + FILES.set(filePath, fs.readFileSync(filePath).toString()); + }); // Add fake usage files options.inlineEntryPoints.forEach((inlineEntryPoint, index) => { - FILES.set(`inlineEntryPoint.${index}.ts`, inlineEntryPoint); + FILES.set(path.join(options.sourcesRoot, `inlineEntryPoint.${index}.ts`), inlineEntryPoint); }); // Add additional typings options.typings.forEach((typing) => { const filePath = path.join(options.sourcesRoot, typing); - FILES.set(typing, fs.readFileSync(filePath).toString()); + FILES.set(filePath, fs.readFileSync(filePath).toString()); }); - // Resolve libs - const RESOLVED_LIBS = processLibFiles(ts, options); - - const compilerOptions = ts.convertCompilerOptionsFromJson(options.compilerOptions, options.sourcesRoot).options; - - const host = new TypeScriptLanguageServiceHost(ts, RESOLVED_LIBS, FILES, compilerOptions, 'defaultLib:lib.d.ts'); + const basePath = path.join(options.sourcesRoot, '..'); + const compilerOptions = ts.convertCompilerOptionsFromJson(options.compilerOptions, basePath).options; + const host = new TypeScriptLanguageServiceHost(ts, FILES, compilerOptions); return ts.createLanguageService(host); } -/** - * Read imports and follow them until all files have been handled - */ -function discoverAndReadFiles(ts: typeof import('typescript'), options: ITreeShakingOptions): IFileMap { - const FILES: IFileMap = new Map(); - - const in_queue: { [module: string]: boolean } = Object.create(null); - const queue: string[] = []; - - const enqueue = (moduleId: string) => { - // To make the treeshaker work on windows... - moduleId = moduleId.replace(/\\/g, '/'); - if (in_queue[moduleId]) { - return; - } - in_queue[moduleId] = true; - queue.push(moduleId); - }; - - options.entryPoints.forEach((entryPoint) => enqueue(entryPoint)); - - while (queue.length > 0) { - const moduleId = queue.shift()!; - let redirectedModuleId: string = moduleId; - if (options.redirects[moduleId]) { - redirectedModuleId = options.redirects[moduleId]; - } - - const dts_filename = path.join(options.sourcesRoot, redirectedModuleId + '.d.ts'); - if (fs.existsSync(dts_filename)) { - const dts_filecontents = fs.readFileSync(dts_filename).toString(); - FILES.set(`${moduleId}.d.ts`, dts_filecontents); - continue; - } - - - const js_filename = path.join(options.sourcesRoot, redirectedModuleId + '.js'); - if (fs.existsSync(js_filename)) { - // This is an import for a .js file, so ignore it... - continue; - } - - const ts_filename = path.join(options.sourcesRoot, redirectedModuleId + '.ts'); - - const ts_filecontents = fs.readFileSync(ts_filename).toString(); - const info = ts.preProcessFile(ts_filecontents); - for (let i = info.importedFiles.length - 1; i >= 0; i--) { - const importedFileName = info.importedFiles[i].fileName; - - if (options.importIgnorePattern.test(importedFileName)) { - // Ignore *.css imports - continue; - } - - let importedModuleId = importedFileName; - if (/(^\.\/)|(^\.\.\/)/.test(importedModuleId)) { - importedModuleId = path.join(path.dirname(moduleId), importedModuleId); - if (importedModuleId.endsWith('.js')) { // ESM: code imports require to be relative and have a '.js' file extension - importedModuleId = importedModuleId.substr(0, importedModuleId.length - 3); - } - } - enqueue(importedModuleId); - } - - FILES.set(`${moduleId}.ts`, ts_filecontents); - } - - return FILES; -} - -/** - * Read lib files and follow lib references - */ -function processLibFiles(ts: typeof import('typescript'), options: ITreeShakingOptions): ILibMap { - - const stack: string[] = [...options.compilerOptions.lib]; - const result: ILibMap = new Map(); - - while (stack.length > 0) { - const filename = `lib.${stack.shift()!.toLowerCase()}.d.ts`; - const key = `defaultLib:${filename}`; - if (!result.has(key)) { - // add this file - const filepath = path.join(TYPESCRIPT_LIB_FOLDER, filename); - const sourceText = fs.readFileSync(filepath).toString(); - result.set(key, sourceText); - - // precess dependencies and "recurse" - const info = ts.preProcessFile(sourceText); - for (const ref of info.libReferenceDirectives) { - stack.push(ref.fileName); - } - } - } - - return result; -} - //#endregion //#region Tree Shaking @@ -522,16 +423,23 @@ function markNodes(ts: typeof import('typescript'), languageService: ts.Language if (importText.endsWith('.js')) { // ESM: code imports require to be relative and to have a '.js' file extension importText = importText.substr(0, importText.length - 3); } - fullPath = path.join(path.dirname(nodeSourceFile.fileName), importText) + '.ts'; + fullPath = path.join(path.dirname(nodeSourceFile.fileName), importText); } else { - fullPath = importText + '.ts'; + fullPath = importText; } + + if (fs.existsSync(fullPath + '.ts')) { + fullPath = fullPath + '.ts'; + } else { + fullPath = fullPath + '.js'; + } + enqueueFile(fullPath); } - options.entryPoints.forEach(moduleId => enqueueFile(moduleId + '.ts')); + options.entryPoints.forEach(moduleId => enqueueFile(path.join(options.sourcesRoot, moduleId))); // Add fake usage files - options.inlineEntryPoints.forEach((_, index) => enqueueFile(`inlineEntryPoint.${index}.ts`)); + options.inlineEntryPoints.forEach((_, index) => enqueueFile(path.join(options.sourcesRoot, `inlineEntryPoint.${index}.ts`))); let step = 0; diff --git a/build/lib/typeScriptLanguageServiceHost.js b/build/lib/typeScriptLanguageServiceHost.js index f7e198e0a4b..d90ad86f9be 100644 --- a/build/lib/typeScriptLanguageServiceHost.js +++ b/build/lib/typeScriptLanguageServiceHost.js @@ -1,21 +1,26 @@ "use strict"; +var __importDefault = (this && this.__importDefault) || function (mod) { + return (mod && mod.__esModule) ? mod : { "default": mod }; +}; Object.defineProperty(exports, "__esModule", { value: true }); exports.TypeScriptLanguageServiceHost = void 0; +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +const typescript_1 = __importDefault(require("typescript")); +const node_fs_1 = __importDefault(require("node:fs")); /** * A TypeScript language service host */ class TypeScriptLanguageServiceHost { ts; - libs; - files; + topLevelFiles; compilerOptions; - defaultLibName; - constructor(ts, libs, files, compilerOptions, defaultLibName) { + constructor(ts, topLevelFiles, compilerOptions) { this.ts = ts; - this.libs = libs; - this.files = files; + this.topLevelFiles = topLevelFiles; this.compilerOptions = compilerOptions; - this.defaultLibName = defaultLibName; } // --- language service host --------------- getCompilationSettings() { @@ -23,8 +28,8 @@ class TypeScriptLanguageServiceHost { } getScriptFileNames() { return [ - ...this.libs.keys(), - ...this.files.keys(), + ...this.topLevelFiles.keys(), + this.ts.getDefaultLibFilePath(this.compilerOptions) ]; } getScriptVersion(_fileName) { @@ -34,14 +39,11 @@ class TypeScriptLanguageServiceHost { return '1'; } getScriptSnapshot(fileName) { - if (this.files.has(fileName)) { - return this.ts.ScriptSnapshot.fromString(this.files.get(fileName)); - } - else if (this.libs.has(fileName)) { - return this.ts.ScriptSnapshot.fromString(this.libs.get(fileName)); + if (this.topLevelFiles.has(fileName)) { + return this.ts.ScriptSnapshot.fromString(this.topLevelFiles.get(fileName)); } else { - return this.ts.ScriptSnapshot.fromString(''); + return typescript_1.default.ScriptSnapshot.fromString(node_fs_1.default.readFileSync(fileName).toString()); } } getScriptKind(_fileName) { @@ -50,17 +52,20 @@ class TypeScriptLanguageServiceHost { getCurrentDirectory() { return ''; } - getDefaultLibFileName(_options) { - return this.defaultLibName; + getDefaultLibFileName(options) { + return this.ts.getDefaultLibFilePath(options); } - isDefaultLibFileName(fileName) { - return fileName === this.getDefaultLibFileName(this.compilerOptions); - } - readFile(path, _encoding) { - return this.files.get(path) || this.libs.get(path); + readFile(path, encoding) { + if (this.topLevelFiles.get(path)) { + return this.topLevelFiles.get(path); + } + return typescript_1.default.sys.readFile(path, encoding); } fileExists(path) { - return this.files.has(path) || this.libs.has(path); + if (this.topLevelFiles.has(path)) { + return true; + } + return typescript_1.default.sys.fileExists(path); } } exports.TypeScriptLanguageServiceHost = TypeScriptLanguageServiceHost; diff --git a/build/lib/typeScriptLanguageServiceHost.ts b/build/lib/typeScriptLanguageServiceHost.ts index c6d20f0f32b..faa11d44da3 100644 --- a/build/lib/typeScriptLanguageServiceHost.ts +++ b/build/lib/typeScriptLanguageServiceHost.ts @@ -4,8 +4,8 @@ *--------------------------------------------------------------------------------------------*/ import ts from 'typescript'; +import fs from 'node:fs'; -export type ILibMap = Map; export type IFileMap = Map; /** @@ -15,10 +15,8 @@ export class TypeScriptLanguageServiceHost implements ts.LanguageServiceHost { constructor( private readonly ts: typeof import('typescript'), - private readonly libs: ILibMap, - private readonly files: IFileMap, + private readonly topLevelFiles: IFileMap, private readonly compilerOptions: ts.CompilerOptions, - private readonly defaultLibName: string, ) { } // --- language service host --------------- @@ -27,8 +25,8 @@ export class TypeScriptLanguageServiceHost implements ts.LanguageServiceHost { } getScriptFileNames(): string[] { return [ - ...this.libs.keys(), - ...this.files.keys(), + ...this.topLevelFiles.keys(), + this.ts.getDefaultLibFilePath(this.compilerOptions) ]; } getScriptVersion(_fileName: string): string { @@ -38,12 +36,10 @@ export class TypeScriptLanguageServiceHost implements ts.LanguageServiceHost { return '1'; } getScriptSnapshot(fileName: string): ts.IScriptSnapshot { - if (this.files.has(fileName)) { - return this.ts.ScriptSnapshot.fromString(this.files.get(fileName)!); - } else if (this.libs.has(fileName)) { - return this.ts.ScriptSnapshot.fromString(this.libs.get(fileName)!); + if (this.topLevelFiles.has(fileName)) { + return this.ts.ScriptSnapshot.fromString(this.topLevelFiles.get(fileName)!); } else { - return this.ts.ScriptSnapshot.fromString(''); + return ts.ScriptSnapshot.fromString(fs.readFileSync(fileName).toString()); } } getScriptKind(_fileName: string): ts.ScriptKind { @@ -52,16 +48,19 @@ export class TypeScriptLanguageServiceHost implements ts.LanguageServiceHost { getCurrentDirectory(): string { return ''; } - getDefaultLibFileName(_options: ts.CompilerOptions): string { - return this.defaultLibName; + getDefaultLibFileName(options: ts.CompilerOptions): string { + return this.ts.getDefaultLibFilePath(options); } - isDefaultLibFileName(fileName: string): boolean { - return fileName === this.getDefaultLibFileName(this.compilerOptions); - } - readFile(path: string, _encoding?: string): string | undefined { - return this.files.get(path) || this.libs.get(path); + readFile(path: string, encoding?: string): string | undefined { + if (this.topLevelFiles.get(path)) { + return this.topLevelFiles.get(path); + } + return ts.sys.readFile(path, encoding); } fileExists(path: string): boolean { - return this.files.has(path) || this.libs.has(path); + if (this.topLevelFiles.has(path)) { + return true; + } + return ts.sys.fileExists(path); } } diff --git a/build/monaco/monaco.d.ts.recipe b/build/monaco/monaco.d.ts.recipe index 43b91bbb80e..b1f676796af 100644 --- a/build/monaco/monaco.d.ts.recipe +++ b/build/monaco/monaco.d.ts.recipe @@ -70,27 +70,27 @@ declare namespace monaco { dispose(): void; } -#include(vs/platform/markers/common/markers): MarkerTag, MarkerSeverity -#include(vs/base/common/cancellation): CancellationTokenSource, CancellationToken -#include(vs/base/common/uri): URI, UriComponents -#include(vs/base/common/keyCodes): KeyCode -#include(vs/editor/common/services/editorBaseApi): KeyMod -#include(vs/base/common/htmlContent): IMarkdownString, MarkdownStringTrustedOptions -#include(vs/base/browser/keyboardEvent): IKeyboardEvent -#include(vs/base/browser/mouseEvent): IMouseEvent -#include(vs/editor/common/editorCommon): IScrollEvent -#include(vs/editor/common/core/position): IPosition, Position -#include(vs/editor/common/core/range): IRange, Range -#include(vs/editor/common/core/selection): ISelection, Selection, SelectionDirection -#include(vs/editor/common/languages): Token +#include(vs/platform/markers/common/markers.js): MarkerTag, MarkerSeverity +#include(vs/base/common/cancellation.js): CancellationTokenSource, CancellationToken +#include(vs/base/common/uri.js): URI, UriComponents +#include(vs/base/common/keyCodes.js): KeyCode +#include(vs/editor/common/services/editorBaseApi.js): KeyMod +#include(vs/base/common/htmlContent.js): IMarkdownString, MarkdownStringTrustedOptions +#include(vs/base/browser/keyboardEvent.js): IKeyboardEvent +#include(vs/base/browser/mouseEvent.js): IMouseEvent +#include(vs/editor/common/editorCommon.js): IScrollEvent +#include(vs/editor/common/core/position.js): IPosition, Position +#include(vs/editor/common/core/range.js): IRange, Range +#include(vs/editor/common/core/selection.js): ISelection, Selection, SelectionDirection +#include(vs/editor/common/languages.js): Token } declare namespace monaco.editor { -#includeAll(vs/editor/standalone/browser/standaloneEditor;languages.Token=>Token): -#include(vs/editor/standalone/common/standaloneTheme): BuiltinTheme, IStandaloneThemeData, IColors -#include(vs/editor/common/languages/supports/tokenization): ITokenThemeRule -#include(vs/editor/standalone/browser/standaloneWebWorker): MonacoWebWorker, IInternalWebWorkerOptions -#include(vs/editor/standalone/browser/standaloneCodeEditor): IActionDescriptor, IGlobalEditorOptions, IStandaloneEditorConstructionOptions, IStandaloneDiffEditorConstructionOptions, IStandaloneCodeEditor, IStandaloneDiffEditor +#includeAll(vs/editor/standalone/browser/standaloneEditor.js;languages.Token=>Token): +#include(vs/editor/standalone/common/standaloneTheme.js): BuiltinTheme, IStandaloneThemeData, IColors +#include(vs/editor/common/languages/supports/tokenization.js): ITokenThemeRule +#include(vs/editor/standalone/browser/standaloneWebWorker.js): MonacoWebWorker, IInternalWebWorkerOptions +#include(vs/editor/standalone/browser/standaloneCodeEditor.js): IActionDescriptor, IGlobalEditorOptions, IStandaloneEditorConstructionOptions, IStandaloneDiffEditorConstructionOptions, IStandaloneCodeEditor, IStandaloneDiffEditor export interface ICommandHandler { (...args: any[]): void; } @@ -101,27 +101,27 @@ export interface ILocalizedString { export interface ICommandMetadata { readonly description: ILocalizedString | string; } -#include(vs/platform/contextkey/common/contextkey): IContextKey, ContextKeyValue -#include(vs/editor/standalone/browser/standaloneServices): IEditorOverrideServices -#include(vs/platform/markers/common/markers): IMarker, IMarkerData, IRelatedInformation -#include(vs/editor/standalone/browser/colorizer): IColorizerOptions, IColorizerElementOptions -#include(vs/base/common/scrollable): ScrollbarVisibility -#include(vs/base/common/themables): ThemeColor, ThemeIcon -#include(vs/editor/common/core/editOperation): ISingleEditOperation -#include(vs/editor/common/core/wordHelper): IWordAtPosition -#includeAll(vs/editor/common/model): IScrollEvent -#include(vs/editor/common/diff/legacyLinesDiffComputer): IChange, ICharChange, ILineChange -#include(vs/editor/common/core/2d/dimension): IDimension -#includeAll(vs/editor/common/editorCommon): IScrollEvent -#includeAll(vs/editor/common/textModelEvents): -#include(vs/editor/common/model/mirrorTextModel): IModelContentChange -#includeAll(vs/editor/common/cursorEvents): -#include(vs/platform/accessibility/common/accessibility): AccessibilitySupport -#includeAll(vs/editor/common/config/editorOptions): -#include(vs/editor/browser/config/editorConfiguration): IEditorConstructionOptions -#includeAll(vs/editor/browser/editorBrowser;editorCommon.=>): -#include(vs/editor/common/config/fontInfo): FontInfo, BareFontInfo -#include(vs/editor/common/config/editorZoom): EditorZoom, IEditorZoom +#include(vs/platform/contextkey/common/contextkey.js): IContextKey, ContextKeyValue +#include(vs/editor/standalone/browser/standaloneServices.js): IEditorOverrideServices +#include(vs/platform/markers/common/markers.js): IMarker, IMarkerData, IRelatedInformation +#include(vs/editor/standalone/browser/colorizer.js): IColorizerOptions, IColorizerElementOptions +#include(vs/base/common/scrollable.js): ScrollbarVisibility +#include(vs/base/common/themables.js): ThemeColor, ThemeIcon +#include(vs/editor/common/core/editOperation.js): ISingleEditOperation +#include(vs/editor/common/core/wordHelper.js): IWordAtPosition +#includeAll(vs/editor/common/model.js): IScrollEvent +#include(vs/editor/common/diff/legacyLinesDiffComputer.js): IChange, ICharChange, ILineChange +#include(vs/editor/common/core/2d/dimension.js): IDimension +#includeAll(vs/editor/common/editorCommon.js): IScrollEvent +#includeAll(vs/editor/common/textModelEvents.js): +#include(vs/editor/common/model/mirrorTextModel.js): IModelContentChange +#includeAll(vs/editor/common/cursorEvents.js): +#include(vs/platform/accessibility/common/accessibility.js): AccessibilitySupport +#includeAll(vs/editor/common/config/editorOptions.js): +#include(vs/editor/browser/config/editorConfiguration.js): IEditorConstructionOptions +#includeAll(vs/editor/browser/editorBrowser.js;editorCommon.=>): +#include(vs/editor/common/config/fontInfo.js): FontInfo, BareFontInfo +#include(vs/editor/common/config/editorZoom.js): EditorZoom, IEditorZoom //compatibility: export type IReadOnlyModel = ITextModel; @@ -130,21 +130,21 @@ export type IModel = ITextModel; declare namespace monaco.languages { -#include(vs/editor/common/textModelEditSource): EditDeltaInfo -#include(vs/base/common/glob): IRelativePattern -#include(vs/editor/common/languageSelector): LanguageSelector, LanguageFilter -#includeAll(vs/editor/standalone/browser/standaloneLanguages;languages.=>;editorCommon.=>editor.;model.=>editor.;IMarkerData=>editor.IMarkerData): -#includeAll(vs/editor/common/languages/languageConfiguration): -#includeAll(vs/editor/common/languages;IMarkerData=>editor.IMarkerData;ISingleEditOperation=>editor.ISingleEditOperation;model.=>editor.;ThemeIcon=>editor.ThemeIcon): Token -#include(vs/editor/common/languages/language): ILanguageExtensionPoint -#includeAll(vs/editor/standalone/common/monarch/monarchTypes): +#include(vs/editor/common/textModelEditSource.js): EditDeltaInfo +#include(vs/base/common/glob.js): IRelativePattern +#include(vs/editor/common/languageSelector.js): LanguageSelector, LanguageFilter +#includeAll(vs/editor/standalone/browser/standaloneLanguages.js;languages.=>;editorCommon.=>editor.;model.=>editor.;IMarkerData=>editor.IMarkerData): +#includeAll(vs/editor/common/languages/languageConfiguration.js): +#includeAll(vs/editor/common/languages.js;IMarkerData=>editor.IMarkerData;ISingleEditOperation=>editor.ISingleEditOperation;model.=>editor.;ThemeIcon=>editor.ThemeIcon): Token +#include(vs/editor/common/languages/language.js): ILanguageExtensionPoint +#includeAll(vs/editor/standalone/common/monarch/monarchTypes.js): } declare namespace monaco.worker { -#include(vs/editor/common/model/mirrorTextModel): IMirrorTextModel -#includeAll(vs/editor/common/services/editorWebWorker;): +#include(vs/editor/common/model/mirrorTextModel.js): IMirrorTextModel +#includeAll(vs/editor/common/services/editorWebWorker.js;): } diff --git a/build/monaco/monaco.usage.recipe b/build/monaco/monaco.usage.recipe index 9e96a68568a..b0526a4506e 100644 --- a/build/monaco/monaco.usage.recipe +++ b/build/monaco/monaco.usage.recipe @@ -1,12 +1,12 @@ // This file is adding references to various symbols which should not be removed via tree shaking -import { IObservable } from './vs/base/common/observable'; +import { IObservable } from './vs/base/common/observable.js'; -import { ServiceIdentifier } from './vs/platform/instantiation/common/instantiation'; -import { start } from './vs/editor/editor.worker.start'; -import { SyncDescriptor0 } from './vs/platform/instantiation/common/descriptors'; -import * as editorAPI from './vs/editor/editor.api'; +import { ServiceIdentifier } from './vs/platform/instantiation/common/instantiation.js'; +import { start } from './vs/editor/editor.worker.start.js'; +import { SyncDescriptor0 } from './vs/platform/instantiation/common/descriptors.js'; +import * as editorAPI from './vs/editor/editor.api.js'; (function () { var a: any; diff --git a/extensions/debug-auto-launch/tsconfig.json b/extensions/debug-auto-launch/tsconfig.json index 69c1f9755bf..22c47de77db 100644 --- a/extensions/debug-auto-launch/tsconfig.json +++ b/extensions/debug-auto-launch/tsconfig.json @@ -2,7 +2,6 @@ "extends": "../tsconfig.base.json", "compilerOptions": { "outDir": "./out", - "downlevelIteration": true, "types": [ "node" ], diff --git a/extensions/debug-server-ready/tsconfig.json b/extensions/debug-server-ready/tsconfig.json index 66064e1e9f2..21e3648ffed 100644 --- a/extensions/debug-server-ready/tsconfig.json +++ b/extensions/debug-server-ready/tsconfig.json @@ -2,7 +2,6 @@ "extends": "../tsconfig.base.json", "compilerOptions": { "outDir": "./out", - "downlevelIteration": true, "types": [ "node" ], diff --git a/extensions/tunnel-forwarding/tsconfig.json b/extensions/tunnel-forwarding/tsconfig.json index f6631c8c88d..a65d85d716d 100644 --- a/extensions/tunnel-forwarding/tsconfig.json +++ b/extensions/tunnel-forwarding/tsconfig.json @@ -5,7 +5,6 @@ "typeRoots": [ "./node_modules/@types" ], - "downlevelIteration": true, "types": [ "node" ] diff --git a/package-lock.json b/package-lock.json index 4b4836edeb1..0bda4675df5 100644 --- a/package-lock.json +++ b/package-lock.json @@ -74,7 +74,7 @@ "@types/trusted-types": "^1.0.6", "@types/vscode-notebook-renderer": "^1.72.0", "@types/webpack": "^5.28.5", - "@types/wicg-file-system-access": "^2020.9.6", + "@types/wicg-file-system-access": "^2023.10.7", "@types/windows-foreground-love": "^0.3.0", "@types/winreg": "^1.2.30", "@types/yauzl": "^2.10.0", @@ -2197,10 +2197,11 @@ } }, "node_modules/@types/wicg-file-system-access": { - "version": "2020.9.6", - "resolved": "https://registry.npmjs.org/@types/wicg-file-system-access/-/wicg-file-system-access-2020.9.6.tgz", - "integrity": "sha512-6hogE75Hl2Ov/jgp8ZhDaGmIF/q3J07GtXf8nCJCwKTHq7971po5+DId7grft09zG7plBwpF6ZU0yx9Du4/e1A==", - "dev": true + "version": "2023.10.7", + "resolved": "https://registry.npmjs.org/@types/wicg-file-system-access/-/wicg-file-system-access-2023.10.7.tgz", + "integrity": "sha512-g49ijasEJvCd7ifmAY2D0wdEtt1xRjBbA33PJTiv8mKBr7DoMsPeISoJ8oQOTopSRi+FBWPpPW5ouDj2QPKtGA==", + "dev": true, + "license": "MIT" }, "node_modules/@types/windows-foreground-love": { "version": "0.3.0", diff --git a/package.json b/package.json index 9c97d57a761..1ce8f67d781 100644 --- a/package.json +++ b/package.json @@ -135,7 +135,7 @@ "@types/trusted-types": "^1.0.6", "@types/vscode-notebook-renderer": "^1.72.0", "@types/webpack": "^5.28.5", - "@types/wicg-file-system-access": "^2020.9.6", + "@types/wicg-file-system-access": "^2023.10.7", "@types/windows-foreground-love": "^0.3.0", "@types/winreg": "^1.2.30", "@types/yauzl": "^2.10.0", @@ -239,4 +239,4 @@ "optionalDependencies": { "windows-foreground-love": "0.5.0" } -} \ No newline at end of file +} diff --git a/src/tsconfig.monaco.json b/src/tsconfig.monaco.json index d1c93b6a672..cd3d0d860b9 100644 --- a/src/tsconfig.monaco.json +++ b/src/tsconfig.monaco.json @@ -7,9 +7,8 @@ "trusted-types", "wicg-file-system-access" ], - "paths": {}, - "module": "esnext", - "moduleResolution": "classic", + "module": "nodenext", + "moduleResolution": "nodenext", "removeComments": false, "preserveConstEnums": true, "target": "ES2022", diff --git a/src/vs/base/browser/ui/countBadge/countBadge.css b/src/vs/base/browser/ui/countBadge/countBadge.css index eb0c0837ee9..9fb4d0bee48 100644 --- a/src/vs/base/browser/ui/countBadge/countBadge.css +++ b/src/vs/base/browser/ui/countBadge/countBadge.css @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ .monaco-count-badge { - padding: 3px 6px; + padding: 3px 5px; border-radius: 11px; font-size: 11px; min-width: 18px; diff --git a/src/vs/editor/browser/widget/diffEditor/features/hideUnchangedRegionsFeature.ts b/src/vs/editor/browser/widget/diffEditor/features/hideUnchangedRegionsFeature.ts index 0f829bfdd9e..a55e05ee97e 100644 --- a/src/vs/editor/browser/widget/diffEditor/features/hideUnchangedRegionsFeature.ts +++ b/src/vs/editor/browser/widget/diffEditor/features/hideUnchangedRegionsFeature.ts @@ -32,7 +32,7 @@ import { IObservableViewZone, PlaceholderViewZone, ViewZoneOverlayWidget, applyO */ export class HideUnchangedRegionsFeature extends Disposable { private static readonly _breadcrumbsSourceFactory = observableValue<((textModel: ITextModel, instantiationService: IInstantiationService) => IDiffEditorBreadcrumbsSource)>( - HideUnchangedRegionsFeature, () => ({ + this, () => ({ dispose() { }, getBreadcrumbItems(startRange, reader) { diff --git a/src/vs/editor/contrib/inlineCompletions/browser/controller/inlineCompletionsController.ts b/src/vs/editor/contrib/inlineCompletions/browser/controller/inlineCompletionsController.ts index 5508ab07ba8..ce4ffa430a8 100644 --- a/src/vs/editor/contrib/inlineCompletions/browser/controller/inlineCompletionsController.ts +++ b/src/vs/editor/contrib/inlineCompletions/browser/controller/inlineCompletionsController.ts @@ -42,7 +42,7 @@ import { InlineCompletionContextKeys } from './inlineCompletionContextKeys.js'; export class InlineCompletionsController extends Disposable { private static readonly _instances = new Set(); - public static hot = createHotClass(InlineCompletionsController); + public static hot = createHotClass(this); public static ID = 'editor.contrib.inlineCompletionsController'; /** diff --git a/src/vs/editor/contrib/inlineCompletions/browser/hintsWidget/inlineCompletionsHintsWidget.ts b/src/vs/editor/contrib/inlineCompletions/browser/hintsWidget/inlineCompletionsHintsWidget.ts index adcfd26eb70..52b393c1949 100644 --- a/src/vs/editor/contrib/inlineCompletions/browser/hintsWidget/inlineCompletionsHintsWidget.ts +++ b/src/vs/editor/contrib/inlineCompletions/browser/hintsWidget/inlineCompletionsHintsWidget.ts @@ -118,7 +118,7 @@ const inlineSuggestionHintsNextIcon = registerIcon('inline-suggestion-hints-next const inlineSuggestionHintsPreviousIcon = registerIcon('inline-suggestion-hints-previous', Codicon.chevronLeft, localize('parameterHintsPreviousIcon', 'Icon for show previous parameter hint.')); export class InlineSuggestionHintsContentWidget extends Disposable implements IContentWidget { - public static readonly hot = createHotClass(InlineSuggestionHintsContentWidget); + public static readonly hot = createHotClass(this); private static _dropDownVisible = false; public static get dropDownVisible() { return this._dropDownVisible; } diff --git a/src/vs/editor/contrib/inlineCompletions/browser/view/ghostText/ghostTextView.ts b/src/vs/editor/contrib/inlineCompletions/browser/view/ghostText/ghostTextView.ts index 88ce4ca138f..21235c7f9e0 100644 --- a/src/vs/editor/contrib/inlineCompletions/browser/view/ghostText/ghostTextView.ts +++ b/src/vs/editor/contrib/inlineCompletions/browser/view/ghostText/ghostTextView.ts @@ -52,7 +52,7 @@ const GHOST_TEXT_CLASS_NAME = 'ghost-text'; export class GhostTextView extends Disposable { private readonly _isDisposed; private readonly _editorObs; - public static hot = createHotClass(GhostTextView); + public static hot = createHotClass(this); private _warningState; diff --git a/src/vs/editor/contrib/inlineCompletions/browser/view/inlineEdits/inlineEditsViewProducer.ts b/src/vs/editor/contrib/inlineCompletions/browser/view/inlineEdits/inlineEditsViewProducer.ts index 8cb79a3f028..caa4419eeea 100644 --- a/src/vs/editor/contrib/inlineCompletions/browser/view/inlineEdits/inlineEditsViewProducer.ts +++ b/src/vs/editor/contrib/inlineCompletions/browser/view/inlineEdits/inlineEditsViewProducer.ts @@ -21,7 +21,7 @@ import { InlineEditsView } from './inlineEditsView.js'; import { InlineEditTabAction } from './inlineEditsViewInterface.js'; export class InlineEditsViewAndDiffProducer extends Disposable { // TODO: This class is no longer a diff producer. Rename it or get rid of it - public static readonly hot = createHotClass(InlineEditsViewAndDiffProducer); + public static readonly hot = createHotClass(this); private readonly _editorObs: ObservableCodeEditor; diff --git a/src/vs/monaco.d.ts b/src/vs/monaco.d.ts index 6ecf342cb0b..cad4767ed96 100644 --- a/src/vs/monaco.d.ts +++ b/src/vs/monaco.d.ts @@ -5356,7 +5356,7 @@ declare namespace monaco.editor { renderWhitespace: IEditorOption; revealHorizontalRightPadding: IEditorOption; roundedSelection: IEditorOption; - rulers: IEditorOption; + rulers: IEditorOption; scrollbar: IEditorOption; scrollBeyondLastColumn: IEditorOption; scrollBeyondLastLine: IEditorOption; @@ -5385,12 +5385,12 @@ declare namespace monaco.editor { tabCompletion: IEditorOption; tabIndex: IEditorOption; trimWhitespaceOnDelete: IEditorOption; - unicodeHighlight: IEditorOption; + unicodeHighlight: IEditorOption>>; unusualLineTerminators: IEditorOption; useShadowDOM: IEditorOption; useTabStops: IEditorOption; wordBreak: IEditorOption; - wordSegmenterLocales: IEditorOption; + wordSegmenterLocales: IEditorOption; wordSeparators: IEditorOption; wordWrap: IEditorOption; wordWrapBreakAfterCharacters: IEditorOption; diff --git a/src/vs/platform/terminal/common/capabilities/commandDetection/promptInputModel.ts b/src/vs/platform/terminal/common/capabilities/commandDetection/promptInputModel.ts index 15da82b2144..328f5f941ef 100644 --- a/src/vs/platform/terminal/common/capabilities/commandDetection/promptInputModel.ts +++ b/src/vs/platform/terminal/common/capabilities/commandDetection/promptInputModel.ts @@ -546,9 +546,9 @@ export class PromptInputModel extends Disposable implements IPromptInputModel { // Retrieve the positions of all cells with the same style as `lastNonWhitespaceCell` const positionsWithGhostStyle = styleMap.get(this._getCellStyleAsString(lastNonWhitespaceCell)); if (positionsWithGhostStyle) { - // Ghost text must start at the cursor or one char after (e.g. a space), - // preventing right-prompt styles from being misdetected as ghost text. - if (positionsWithGhostStyle[0] > buffer.cursorX + 1) { + // Ghost text must start at the cursor or one char after (e.g. a space) + // To account for cursor movement, we also ensure there are not 5+ spaces preceding the ghost text position + if (positionsWithGhostStyle[0] > buffer.cursorX + 1 && this._isPositionRightPrompt(line, positionsWithGhostStyle[0])) { return -1; } // Ensure these positions are contiguous @@ -583,6 +583,29 @@ export class PromptInputModel extends Disposable implements IPromptInputModel { return ghostTextIndex >= cursorIndex ? ghostTextIndex : -1; } + /** + * 5+ spaces preceding the position, following the command start, + * indicates that we're likely in a right prompt at the current position + */ + private _isPositionRightPrompt(line: IBufferLine, position: number): boolean { + let count = 0; + for (let i = position - 1; i >= this._commandStartX; i--) { + const cell = line.getCell(i); + // treat missing cell or whitespace-only cell as empty; reset count on first non-empty + if (!cell || cell.getChars().trim().length === 0) { + count++; + // If we've already found 5 consecutive empties we can early-return + if (count >= 5) { + return true; + } + } else { + // consecutive sequence broken + count = 0; + } + } + return false; + } + private _getCellStyleAsString(cell: IBufferCell): string { return `${cell.getFgColor()}${cell.getBgColor()}${cell.isBold()}${cell.isItalic()}${cell.isDim()}${cell.isUnderline()}${cell.isBlink()}${cell.isInverse()}${cell.isInvisible()}${cell.isStrikethrough()}${cell.isOverline()}${cell.getFgColorMode()}${cell.getBgColorMode()}`; } diff --git a/src/vs/platform/terminal/test/common/capabilities/commandDetection/promptInputModel.test.ts b/src/vs/platform/terminal/test/common/capabilities/commandDetection/promptInputModel.test.ts index 2d0c801066e..f87ab1f0ce5 100644 --- a/src/vs/platform/terminal/test/common/capabilities/commandDetection/promptInputModel.test.ts +++ b/src/vs/platform/terminal/test/common/capabilities/commandDetection/promptInputModel.test.ts @@ -215,6 +215,9 @@ suite('PromptInputModel', () => { await writePromise('foo\x1b[38;2;255;0;0m bar\x1b[0m\x1b[4D'); await assertPromptInput('foo|[ bar]'); + + await writePromise('\x1b[2D'); + await assertPromptInput('f|oo[ bar]'); }); test('no ghost text when foreground color matches earlier text', async () => { await writePromise('$ '); @@ -425,6 +428,13 @@ suite('PromptInputModel', () => { await assertPromptInput(`find . -name test|`); }); }); + test('Does not detect right prompt as ghost text', async () => { + await writePromise('$ '); + fireCommandStart(); + await assertPromptInput('|'); + await writePromise('cmd' + ' '.repeat(6) + '\x1b[38;2;255;0;0mRP\x1b[0m\x1b[8D'); + await assertPromptInput('cmd|' + ' '.repeat(6) + 'RP'); + }); }); test('wide input (Korean)', async () => { diff --git a/src/vs/workbench/contrib/chat/browser/actions/chatContextActions.ts b/src/vs/workbench/contrib/chat/browser/actions/chatContextActions.ts index b7bfac015d0..40d8d46b6e9 100644 --- a/src/vs/workbench/contrib/chat/browser/actions/chatContextActions.ts +++ b/src/vs/workbench/contrib/chat/browser/actions/chatContextActions.ts @@ -51,6 +51,9 @@ import { resizeImage } from '../imageUtils.js'; import { registerPromptActions } from '../promptSyntax/promptFileActions.js'; import { CHAT_CATEGORY } from './chatActions.js'; +// Schemes that should not be available for chat context attach +const UNSUPPORTED_CONTEXT_SCHEMES = new Set(['webview-panel', 'walkThrough', 'vscode-settings']); + export function registerChatContextActions() { registerAction2(AttachContextAction); registerAction2(AttachFileToChatAction); @@ -455,6 +458,12 @@ export class AttachContextAction extends Action2 { const commandService = accessor.get(ICommandService); const providerOptions: AnythingQuickAccessProviderRunOptions = { + filter: (pick) => { + if (isIQuickPickItemWithResource(pick) && pick.resource) { + return !UNSUPPORTED_CONTEXT_SCHEMES.has(pick.resource.scheme); + } + return true; + }, additionPicks, handleAccept: async (item: IQuickPickServicePickItem | IContextPickItemItem, isBackgroundAccept: boolean) => { diff --git a/src/vs/workbench/contrib/chat/browser/chatEditorInput.ts b/src/vs/workbench/contrib/chat/browser/chatEditorInput.ts index df47ec3a0f7..bfeb8de21b7 100644 --- a/src/vs/workbench/contrib/chat/browser/chatEditorInput.ts +++ b/src/vs/workbench/contrib/chat/browser/chatEditorInput.ts @@ -194,9 +194,9 @@ export class ChatEditorInput extends EditorInput implements IEditorCloseHandler this.model = await this.chatService.loadSessionForResource(this.resource, ChatAgentLocation.Chat, CancellationToken.None); } else if (typeof this.sessionId === 'string') { this.model = await this.chatService.getOrRestoreSession(this.sessionId) - ?? this.chatService.startSession(ChatAgentLocation.Chat, CancellationToken.None, undefined, inputType); + ?? this.chatService.startSession(ChatAgentLocation.Chat, CancellationToken.None, undefined, { canUseTools: false, inputType: inputType }); } else if (!this.options.target) { - this.model = this.chatService.startSession(ChatAgentLocation.Chat, CancellationToken.None, undefined, inputType); + this.model = this.chatService.startSession(ChatAgentLocation.Chat, CancellationToken.None, undefined, { canUseTools: !inputType, inputType: inputType }); } else if ('data' in this.options.target) { this.model = this.chatService.loadSessionFromContent(this.options.target.data); } diff --git a/src/vs/workbench/contrib/chat/common/chatModel.ts b/src/vs/workbench/contrib/chat/common/chatModel.ts index 369e5a82350..5c7970f9685 100644 --- a/src/vs/workbench/contrib/chat/common/chatModel.ts +++ b/src/vs/workbench/contrib/chat/common/chatModel.ts @@ -1441,9 +1441,14 @@ export class ChatModel extends Disposable implements IChatModel { return this._initialLocation; } + private readonly _canUseTools: boolean = true; + get canUseTools(): boolean { + return this._canUseTools; + } + constructor( initialData: ISerializableChatData | IExportableChatData | undefined, - initialModelProps: { initialLocation: ChatAgentLocation; inputType?: string }, + initialModelProps: { initialLocation: ChatAgentLocation; canUseTools: boolean; inputType?: string }, @ILogService private readonly logService: ILogService, @IChatAgentService private readonly chatAgentService: IChatAgentService, @IChatEditingService private readonly chatEditingService: IChatEditingService, @@ -1469,6 +1474,7 @@ export class ChatModel extends Disposable implements IChatModel { this._inputType = initialData?.inputType ?? initialModelProps.inputType; this._initialLocation = initialData?.initialLocation ?? initialModelProps.initialLocation; + this._canUseTools = initialModelProps.canUseTools; const lastResponse = observableFromEvent(this, this.onDidChange, () => this._requests.at(-1)?.response); diff --git a/src/vs/workbench/contrib/chat/common/chatService.ts b/src/vs/workbench/contrib/chat/common/chatService.ts index 828b59cfcb2..e60fe7a8ad5 100644 --- a/src/vs/workbench/contrib/chat/common/chatService.ts +++ b/src/vs/workbench/contrib/chat/common/chatService.ts @@ -729,7 +729,7 @@ export interface IChatService { isEnabled(location: ChatAgentLocation): boolean; hasSessions(): boolean; - startSession(location: ChatAgentLocation, token: CancellationToken, isGlobalEditingSession?: boolean, inputType?: string): ChatModel; + startSession(location: ChatAgentLocation, token: CancellationToken, isGlobalEditingSession?: boolean, options?: { canUseTools?: boolean; inputType?: string }): ChatModel; getSession(sessionId: string): IChatModel | undefined; getOrRestoreSession(sessionId: string): Promise; getPersistedSessionTitle(sessionId: string): string | undefined; diff --git a/src/vs/workbench/contrib/chat/common/chatServiceImpl.ts b/src/vs/workbench/contrib/chat/common/chatServiceImpl.ts index 024df8d11b8..1c5d664f520 100644 --- a/src/vs/workbench/contrib/chat/common/chatServiceImpl.ts +++ b/src/vs/workbench/contrib/chat/common/chatServiceImpl.ts @@ -328,13 +328,13 @@ export class ChatService extends Disposable implements IChatService { await this._chatSessionStore.clearAllSessions(); } - startSession(location: ChatAgentLocation, token: CancellationToken, isGlobalEditingSession: boolean = true, inputType?: string): ChatModel { + startSession(location: ChatAgentLocation, token: CancellationToken, isGlobalEditingSession: boolean = true, options?: { canUseTools?: boolean; inputType?: string }): ChatModel { this.trace('startSession'); - return this._startSession(undefined, location, isGlobalEditingSession, token, inputType); + return this._startSession(undefined, location, isGlobalEditingSession, token, options); } - private _startSession(someSessionHistory: IExportableChatData | ISerializableChatData | undefined, location: ChatAgentLocation, isGlobalEditingSession: boolean, token: CancellationToken, inputType?: string): ChatModel { - const model = this.instantiationService.createInstance(ChatModel, someSessionHistory, { initialLocation: location, inputType }); + private _startSession(someSessionHistory: IExportableChatData | ISerializableChatData | undefined, location: ChatAgentLocation, isGlobalEditingSession: boolean, token: CancellationToken, options?: { canUseTools?: boolean; inputType?: string }): ChatModel { + const model = this.instantiationService.createInstance(ChatModel, someSessionHistory, { initialLocation: location, canUseTools: options?.canUseTools ?? true, inputType: options?.inputType }); if (location === ChatAgentLocation.Chat) { model.startEditingSession(isGlobalEditingSession); } @@ -400,7 +400,7 @@ export class ChatService extends Disposable implements IChatService { return undefined; } - const session = this._startSession(sessionData, sessionData.initialLocation ?? ChatAgentLocation.Chat, true, CancellationToken.None); + const session = this._startSession(sessionData, sessionData.initialLocation ?? ChatAgentLocation.Chat, true, CancellationToken.None, { canUseTools: true }); const isTransferred = this.transferredSessionData?.sessionId === sessionId; if (isTransferred) { @@ -470,7 +470,8 @@ export class ChatService extends Disposable implements IChatService { const chatSessionType = parsed.chatSessionType; const content = await this.chatSessionService.provideChatSessionContent(chatSessionType, parsed.sessionId, CancellationToken.None); - const model = this._startSession(undefined, location, true, CancellationToken.None, chatSessionType); + // Contributed sessions do not use UI tools + const model = this._startSession(undefined, location, true, CancellationToken.None, { canUseTools: false, inputType: chatSessionType }); // Record mapping from internal model session id to external contributed chat session identity this._modelToExternalSession.set(model.sessionId, { chatSessionType, chatSessionId: parsed.sessionId }); if (!this._contentProviderSessionModels.has(chatSessionType)) { @@ -865,11 +866,13 @@ export class ChatService extends Disposable implements IChatService { } completeResponseCreated(); - // Check if there are MCP servers requiring interaction and show message if not shown yet - const autostartResult = new ChatMcpServersStarting(this.mcpService.autostart(token)); - if (!autostartResult.isEmpty) { - progressCallback([autostartResult]); - await autostartResult.wait(); + // MCP autostart: only run for native VS Code sessions (sidebar, new editors) but not for extension contributed sessions that have inputType set. + if (model.canUseTools) { + const autostartResult = new ChatMcpServersStarting(this.mcpService.autostart(token)); + if (!autostartResult.isEmpty) { + progressCallback([autostartResult]); + await autostartResult.wait(); + } } const agentResult = await this.chatAgentService.invokeAgent(agent.id, requestProps, progressCallback, history, token); diff --git a/src/vs/workbench/contrib/chat/test/common/chatModel.test.ts b/src/vs/workbench/contrib/chat/test/common/chatModel.test.ts index 0c8ff8ebde0..5f5d94305fa 100644 --- a/src/vs/workbench/contrib/chat/test/common/chatModel.test.ts +++ b/src/vs/workbench/contrib/chat/test/common/chatModel.test.ts @@ -40,7 +40,7 @@ suite('ChatModel', () => { }); test('removeRequest', async () => { - const model = testDisposables.add(instantiationService.createInstance(ChatModel, undefined, { initialLocation: ChatAgentLocation.Chat })); + const model = testDisposables.add(instantiationService.createInstance(ChatModel, undefined, { initialLocation: ChatAgentLocation.Chat, canUseTools: true })); const text = 'hello'; model.addRequest({ text, parts: [new ChatRequestTextPart(new OffsetRange(0, text.length), new Range(1, text.length, 1, text.length), text)] }, { variables: [] }, 0); @@ -52,8 +52,8 @@ suite('ChatModel', () => { }); test('adoptRequest', async function () { - const model1 = testDisposables.add(instantiationService.createInstance(ChatModel, undefined, { initialLocation: ChatAgentLocation.EditorInline })); - const model2 = testDisposables.add(instantiationService.createInstance(ChatModel, undefined, { initialLocation: ChatAgentLocation.Chat })); + const model1 = testDisposables.add(instantiationService.createInstance(ChatModel, undefined, { initialLocation: ChatAgentLocation.EditorInline, canUseTools: true })); + const model2 = testDisposables.add(instantiationService.createInstance(ChatModel, undefined, { initialLocation: ChatAgentLocation.Chat, canUseTools: true })); const text = 'hello'; const request1 = model1.addRequest({ text, parts: [new ChatRequestTextPart(new OffsetRange(0, text.length), new Range(1, text.length, 1, text.length), text)] }, { variables: [] }, 0); @@ -76,7 +76,7 @@ suite('ChatModel', () => { }); test('addCompleteRequest', async function () { - const model1 = testDisposables.add(instantiationService.createInstance(ChatModel, undefined, { initialLocation: ChatAgentLocation.Chat })); + const model1 = testDisposables.add(instantiationService.createInstance(ChatModel, undefined, { initialLocation: ChatAgentLocation.Chat, canUseTools: true })); const text = 'hello'; const request1 = model1.addRequest({ text, parts: [new ChatRequestTextPart(new OffsetRange(0, text.length), new Range(1, text.length, 1, text.length), text)] }, { variables: [] }, 0, undefined, undefined, undefined, undefined, undefined, undefined, true); diff --git a/src/vs/workbench/contrib/editTelemetry/browser/editStats/aiStatsStatusBar.ts b/src/vs/workbench/contrib/editTelemetry/browser/editStats/aiStatsStatusBar.ts index 12f1948bd42..16248f06f26 100644 --- a/src/vs/workbench/contrib/editTelemetry/browser/editStats/aiStatsStatusBar.ts +++ b/src/vs/workbench/contrib/editTelemetry/browser/editStats/aiStatsStatusBar.ts @@ -21,7 +21,7 @@ import type { AiStatsFeature } from './aiStatsFeature.js'; import './media.css'; export class AiStatsStatusBar extends Disposable { - public static readonly hot = createHotClass(AiStatsStatusBar); + public static readonly hot = createHotClass(this); constructor( private readonly _aiStatsFeature: AiStatsFeature, diff --git a/src/vs/workbench/contrib/inlineCompletions/browser/inlineCompletionLanguageStatusBarContribution.ts b/src/vs/workbench/contrib/inlineCompletions/browser/inlineCompletionLanguageStatusBarContribution.ts index 8177c0b0e52..45af71b0fb4 100644 --- a/src/vs/workbench/contrib/inlineCompletions/browser/inlineCompletionLanguageStatusBarContribution.ts +++ b/src/vs/workbench/contrib/inlineCompletions/browser/inlineCompletionLanguageStatusBarContribution.ts @@ -15,7 +15,7 @@ import { IEditorService } from '../../../services/editor/common/editorService.js import { ILanguageStatusService } from '../../../services/languageStatus/common/languageStatusService.js'; export class InlineCompletionLanguageStatusBarContribution extends Disposable implements IWorkbenchContribution { - public static readonly hot = createHotClass(InlineCompletionLanguageStatusBarContribution); + public static readonly hot = createHotClass(this); public static Id = 'vs.contrib.inlineCompletionLanguageStatusBarContribution'; public static readonly languageStatusBarDisposables = new Set(); diff --git a/src/vs/workbench/contrib/terminal/test/browser/xterm/recordings/rich/windows11_pwsh7_echo_3_times.ts b/src/vs/workbench/contrib/terminal/test/browser/xterm/recordings/rich/windows11_pwsh7_echo_3_times.ts index 3e524eda4c3..5483ff1b48b 100644 --- a/src/vs/workbench/contrib/terminal/test/browser/xterm/recordings/rich/windows11_pwsh7_echo_3_times.ts +++ b/src/vs/workbench/contrib/terminal/test/browser/xterm/recordings/rich/windows11_pwsh7_echo_3_times.ts @@ -324,7 +324,7 @@ export const events = [ }, { "type": "promptInputChange", - "data": "echo b|" + "data": "echo b|[]" }, { "type": "output", @@ -491,7 +491,7 @@ export const events = [ }, { "type": "promptInputChange", - "data": "echo c|" + "data": "echo c|[]" }, { "type": "output", diff --git a/src/vs/workbench/services/dialogs/browser/fileDialogService.ts b/src/vs/workbench/services/dialogs/browser/fileDialogService.ts index 70d9dc6f979..f75fa27784e 100644 --- a/src/vs/workbench/services/dialogs/browser/fileDialogService.ts +++ b/src/vs/workbench/services/dialogs/browser/fileDialogService.ts @@ -142,10 +142,10 @@ export class FileDialogService extends AbstractFileDialogService implements IFil private getFilePickerTypes(filters?: FileFilter[]): FilePickerAcceptType[] | undefined { return filters?.filter(filter => { return !((filter.extensions.length === 1) && ((filter.extensions[0] === '*') || filter.extensions[0] === '')); - }).map(filter => { - const accept: Record = {}; + }).map((filter): FilePickerAcceptType => { + const accept: Record = {}; const extensions = filter.extensions.filter(ext => (ext.indexOf('-') < 0) && (ext.indexOf('*') < 0) && (ext.indexOf('_') < 0)); - accept[getMediaOrTextMime(`fileName.${filter.extensions[0]}`) ?? 'text/plain'] = extensions.map(ext => ext.startsWith('.') ? ext : `.${ext}`); + accept[(getMediaOrTextMime(`fileName.${filter.extensions[0]}`) ?? 'text/plain') as MIMEType] = extensions.map(ext => ext.startsWith('.') ? ext : `.${ext}`) as FileExtension[]; return { description: filter.name, accept