diff --git a/extensions/typescript-language-features/.vscodeignore b/extensions/typescript-language-features/.vscodeignore index 5c4f06d4cd3..c9b6c88a79c 100644 --- a/extensions/typescript-language-features/.vscodeignore +++ b/extensions/typescript-language-features/.vscodeignore @@ -4,8 +4,7 @@ web/** test/** test-workspace/** out/** -tsconfig.json -extension.webpack.config.js -extension-browser.webpack.config.js +tsconfig*.json +esbuild*.mts cgmanifest.json package-lock.json diff --git a/extensions/typescript-language-features/esbuild.browser.mts b/extensions/typescript-language-features/esbuild.browser.mts new file mode 100644 index 00000000000..b24d4781cb8 --- /dev/null +++ b/extensions/typescript-language-features/esbuild.browser.mts @@ -0,0 +1,141 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +import * as fs from 'node:fs'; +import * as path from 'node:path'; +import type esbuild from 'esbuild'; +import { run } from '../esbuild-extension-common.mts'; + +const srcDir = path.join(import.meta.dirname, 'src'); +const outDir = path.join(import.meta.dirname, 'dist', 'browser'); + +// https://esbuild.github.io/plugins/#webassembly-plugin +const wasmPlugin: esbuild.Plugin = { + name: 'wasm', + setup(build) { + build.onResolve({ filter: /\.wasm$/ }, args => { + if (args.namespace === 'wasm-stub') { + return { + path: args.path, + namespace: 'wasm-binary', + }; + } + + if (args.resolveDir === '') { + return; + } + return { + path: path.isAbsolute(args.path) + ? args.path + : path.join(args.resolveDir, args.path), + namespace: 'wasm-stub', + }; + }); + + build.onLoad({ filter: /.*/, namespace: 'wasm-stub' }, async (args) => ({ + contents: `import wasm from ${JSON.stringify(args.path)} + export default (imports) => + WebAssembly.instantiate(wasm, imports).then( + result => result.instance.exports)`, + })); + + build.onLoad({ filter: /.*/, namespace: 'wasm-binary' }, async (args) => ({ + contents: await fs.promises.readFile(args.path), + loader: 'binary', + })); + }, +}; + +const languages = [ + 'zh-tw', + 'cs', + 'de', + 'es', + 'fr', + 'it', + 'ja', + 'ko', + 'pl', + 'pt-br', + 'ru', + 'tr', + 'zh-cn', +]; + +/** + * Copy TypeScript lib files (.d.ts, typesMap.json, and language packs) to the output directory. + */ +async function copyTypescriptLibFiles(outDir: string): Promise { + try { + const typescriptLibDir = path.join(import.meta.dirname, '..', 'node_modules', 'typescript', 'lib'); + const destDir = path.join(outDir, 'typescript'); + + await fs.promises.mkdir(destDir, { recursive: true }); + + // Copy .d.ts files + const libFiles = await fs.promises.readdir(typescriptLibDir); + for (const file of libFiles) { + if (file.endsWith('.d.ts')) { + await fs.promises.copyFile(path.join(typescriptLibDir, file), path.join(destDir, file)); + } + } + + // Copy typesMap.json + await fs.promises.copyFile(path.join(typescriptLibDir, 'typesMap.json'), path.join(destDir, 'typesMap.json')); + + // Copy language packs + for (const lang of languages) { + const langSrcDir = path.join(typescriptLibDir, lang); + const langDestDir = path.join(destDir, lang); + try { + await fs.promises.mkdir(langDestDir, { recursive: true }); + const langFiles = await fs.promises.readdir(langSrcDir); + for (const file of langFiles) { + const srcPath = path.join(langSrcDir, file); + const destPath = path.join(langDestDir, file); + const stat = await fs.promises.stat(srcPath); + if (stat.isFile()) { + await fs.promises.copyFile(srcPath, destPath); + } + } + } catch { + // Skip if language directory doesn't exist + } + } + } catch (error) { + console.error('Error copying TypeScript lib files:', error); + throw error; + } +} + + +await Promise.all([ + // Build the browser extension entry point + run({ + platform: 'browser', + entryPoints: { + 'extension': path.join(srcDir, 'extension.browser.ts'), + }, + srcDir, + outdir: outDir, + additionalOptions: { + plugins: [wasmPlugin], + }, + }, process.argv, copyTypescriptLibFiles), + + // Build the web tsserver worker + run({ + platform: 'browser', + entryPoints: { + 'typescript/tsserver.web': path.join(import.meta.dirname, 'web', 'src', 'webServer.ts'), + }, + srcDir: path.join(import.meta.dirname, 'web', 'src'), + outdir: outDir, + additionalOptions: { + tsconfig: path.join(import.meta.dirname, 'web', 'tsconfig.json'), + external: ['perf_hooks'], + plugins: [wasmPlugin], + }, + }, process.argv), +]); diff --git a/extensions/typescript-language-features/extension.webpack.config.js b/extensions/typescript-language-features/esbuild.mts similarity index 51% rename from extensions/typescript-language-features/extension.webpack.config.js rename to extensions/typescript-language-features/esbuild.mts index 4928186ae55..2b75ca703da 100644 --- a/extensions/typescript-language-features/extension.webpack.config.js +++ b/extensions/typescript-language-features/esbuild.mts @@ -2,15 +2,17 @@ * Copyright (c) Microsoft Corporation. All rights reserved. * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -// @ts-check -import withDefaults from '../shared.webpack.config.mjs'; +import * as path from 'node:path'; +import { run } from '../esbuild-extension-common.mts'; -export default withDefaults({ - context: import.meta.dirname, - resolve: { - mainFields: ['module', 'main'] +const srcDir = path.join(import.meta.dirname, 'src'); +const outDir = path.join(import.meta.dirname, 'dist'); + +run({ + platform: 'node', + entryPoints: { + 'extension': path.join(srcDir, 'extension.ts'), }, - entry: { - extension: './src/extension.ts', - } -}); + srcDir, + outdir: outDir, +}, process.argv); diff --git a/extensions/typescript-language-features/extension-browser.webpack.config.js b/extensions/typescript-language-features/extension-browser.webpack.config.js deleted file mode 100644 index 86733dfd1ee..00000000000 --- a/extensions/typescript-language-features/extension-browser.webpack.config.js +++ /dev/null @@ -1,78 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ -// @ts-check -import CopyPlugin from 'copy-webpack-plugin'; -import path from 'path'; -import defaultConfig, { browser as withBrowserDefaults, browserPlugins } from '../shared.webpack.config.mjs'; - -const languages = [ - 'zh-tw', - 'cs', - 'de', - 'es', - 'fr', - 'it', - 'ja', - 'ko', - 'pl', - 'pt-br', - 'ru', - 'tr', - 'zh-cn', -]; -export default [withBrowserDefaults({ - context: import.meta.dirname, - entry: { - extension: './src/extension.browser.ts', - }, - plugins: [ - ...browserPlugins(import.meta.dirname), // add plugins, don't replace inherited - - // @ts-ignore - new CopyPlugin({ - patterns: [ - { - from: '../node_modules/typescript/lib/*.d.ts', - to: 'typescript/[name][ext]', - }, - { - from: '../node_modules/typescript/lib/typesMap.json', - to: 'typescript/' - }, - ...languages.map(lang => ({ - from: `../node_modules/typescript/lib/${lang}/**/*`, - to: (pathData) => { - const normalizedFileName = pathData.absoluteFilename.replace(/[\\/]/g, '/'); - const match = normalizedFileName.match(/typescript\/lib\/(.*)/); - if (match) { - return `typescript/${match[1]}`; - } - console.log(`Did not find typescript/lib in ${normalizedFileName}`); - return 'typescript/'; - } - })) - ], - }), - ], -}), withBrowserDefaults({ - context: import.meta.dirname, - entry: { - 'typescript/tsserver.web': './web/src/webServer.ts' - }, - module: { - exprContextCritical: false, - }, - ignoreWarnings: [/Critical dependency: the request of a dependency is an expression/], - output: { - // all output goes into `dist`. - // packaging depends on that and this must always be like it - filename: '[name].js', - path: path.join(import.meta.dirname, 'dist', 'browser'), - libraryTarget: undefined, - }, - externals: { - 'perf_hooks': 'commonjs perf_hooks', - } -})]; diff --git a/extensions/typescript-language-features/package.json b/extensions/typescript-language-features/package.json index 66a1c65819d..28565c0e751 100644 --- a/extensions/typescript-language-features/package.json +++ b/extensions/typescript-language-features/package.json @@ -54,8 +54,12 @@ }, "scripts": { "vscode:prepublish": "node ../../node_modules/gulp/bin/gulp.js --gulpfile ../../build/gulpfile.extensions.mjs compile-extension:typescript-language-features", - "compile-web": "npx webpack-cli --config extension-browser.webpack.config --mode none", - "watch-web": "npx webpack-cli --config extension-browser.webpack.config --mode none --watch" + "compile-web": "npm-run-all2 -lp bundle-web typecheck-web", + "bundle-web": "node ./esbuild.browser.mts", + "typecheck-web": "tsgo --project ./tsconfig.browser.json --noEmit", + "watch-web": "npm-run-all2 -lp watch-bundle-web watch-typecheck-web", + "watch-bundle-web": "node ./esbuild.browser.mts --watch", + "watch-typecheck-web": "tsgo --project ./tsconfig.browser.json --noEmit --watch" }, "activationEvents": [ "onLanguage:javascript", diff --git a/extensions/typescript-language-features/tsconfig.browser.json b/extensions/typescript-language-features/tsconfig.browser.json new file mode 100644 index 00000000000..790349e7fec --- /dev/null +++ b/extensions/typescript-language-features/tsconfig.browser.json @@ -0,0 +1,10 @@ +{ + "extends": "./tsconfig.json", + "compilerOptions": {}, + "exclude": [ + "./src/test/**" + ], + "files": [ + "./src/extension.browser.ts" + ] +}