// ***************************************************************************** // Copyright (C) 2017 TypeFox and others. // // This program and the accompanying materials are made available under the // terms of the Eclipse Public License v. 2.0 which is available at // http://www.eclipse.org/legal/epl-2.0. // // This Source Code may also be made available under the following Secondary // Licenses when the conditions for such availability set forth in the Eclipse // Public License v. 2.0 are satisfied: GNU General Public License, version 2 // with the GNU Classpath Exception which is available at // https://www.gnu.org/software/classpath/license.html. // // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0 // ***************************************************************************** import * as paths from 'path'; import * as fs from 'fs-extra'; import { AbstractGenerator } from './abstract-generator'; export class BundlerGenerator extends AbstractGenerator { async generate(): Promise { if (await this.preferESBuild()) { await this.write(this.genESBuildBrowserPath, this.compileESBuildBrowserConfig()); if (!this.pck.isBrowserOnly()) { await this.write(this.genESBuildNodePath, this.compileESBuildNodeConfig()); if (this.pck.isElectron()) { await this.write(this.genESBuildElectronPath, this.compileESBuildElectronConfig()); } } if (await this.shouldGenerateUserESBuildConfig()) { await this.write(this.esbuildPath, this.compileESBuildUserConfig()); } } else { await this.write(this.genConfigPath, this.compileWebpackConfig()); if (!this.pck.isBrowserOnly()) { await this.write(this.genNodeConfigPath, this.compileNodeWebpackConfig()); } if (await this.shouldGenerateUserWebpackConfig()) { await this.write(this.configPath, this.compileUserWebpackConfig()); } } } async preferESBuild(): Promise { // If an esbuild file already exists, prefer esbuild if (await fs.pathExists(this.esbuildPath)) { return true; } // If a webpack file already exists, prefer webpack if (await fs.pathExists(this.configPath)) { return false; } // Otherwise, prefer ESBuild (for performance) return true; } protected async shouldGenerateUserWebpackConfig(): Promise { if (!(await fs.pathExists(this.configPath))) { return true; } const content = await fs.readFile(this.configPath, 'utf8'); return !content.includes('gen-webpack'); } protected async shouldGenerateUserESBuildConfig(): Promise { if (!(await fs.pathExists(this.esbuildPath))) { return true; } const content = await fs.readFile(this.esbuildPath, 'utf8'); return !content.includes('gen-esbuild'); } get configPath(): string { return this.pck.path('webpack.config.js'); } get genConfigPath(): string { return this.pck.path('gen-webpack.config.js'); } get genNodeConfigPath(): string { return this.pck.path('gen-webpack.node.config.js'); } get esbuildPath(): string { return this.pck.path('esbuild.mjs'); } get genESBuildBrowserPath(): string { return this.pck.path('gen-esbuild.browser.mjs'); } get genESBuildNodePath(): string { return this.pck.path('gen-esbuild.node.mjs'); } get genESBuildElectronPath(): string { return this.pck.path('gen-esbuild.electron.mjs'); } protected compileWebpackConfig(): string { return `/** * Don't touch this file. It will be regenerated by theia build. * To customize webpack configuration change ${this.configPath} */ // @ts-check const path = require('path'); const webpack = require('webpack'); const yargs = require('yargs'); const resolvePackagePath = require('resolve-package-path'); const CopyWebpackPlugin = require('copy-webpack-plugin'); const CompressionPlugin = require('compression-webpack-plugin'); const MiniCssExtractPlugin = require('mini-css-extract-plugin'); const { MonacoWebpackPlugin } = require('@theia/bundle-plugin'); const outputPath = path.resolve(__dirname, 'lib', 'frontend'); const { mode, staticCompression } = yargs.option('mode', { description: "Mode to use", choices: ["development", "production"], default: "production" }).option('static-compression', { description: 'Controls whether to enable compression of static artifacts.', type: 'boolean', default: true }).argv; const development = mode === 'development'; const plugins = [ new CopyWebpackPlugin({ patterns: [ { // copy secondary window html file to lib folder from: path.resolve(__dirname, 'src-gen/frontend/secondary-window.html') }${this.ifPackage('@theia/plugin-ext', `, { // copy webview files to lib folder from: path.join(resolvePackagePath('@theia/plugin-ext', __dirname), '..', 'src', 'main', 'browser', 'webview', 'pre'), to: path.resolve(__dirname, 'lib', 'webview', 'pre') }`)} ${this.ifPackage('@theia/plugin-ext-vscode', `, { // copy frontend plugin host files from: path.join(resolvePackagePath('@theia/plugin-ext-vscode', __dirname), '..', 'lib', 'node', 'context', 'plugin-vscode-init-fe.js'), to: path.resolve(__dirname, 'lib', 'frontend', 'context', 'plugin-vscode-init-fe.js') }`)} ${this.ifPackage('@theia/terminal', `, { // copy shell integration scripts from: path.join(resolvePackagePath('@theia/terminal', __dirname), '..', 'src', 'node', 'shell-integrations'), to: path.resolve(__dirname, 'lib', 'backend', 'shell-integrations') }`)} ] }), new webpack.ProvidePlugin({ // the Buffer class doesn't exist in the browser but some dependencies rely on it Buffer: ['buffer', 'Buffer'] }), new MonacoWebpackPlugin() ]; // it should go after copy-plugin in order to compress monaco as well if (staticCompression) { plugins.push(new CompressionPlugin({})); } module.exports = [{ mode, plugins, devtool: 'source-map', entry: { bundle: path.resolve(__dirname, 'src-gen/frontend/index.js'), ${this.ifPackage('@theia/plugin-ext', "'plugin-worker': '@theia/plugin-ext/lib/hosted/browser/worker/worker-main.js',")} }, output: { filename: '[name].js', path: outputPath, devtoolModuleFilenameTemplate: 'webpack:///[resource-path]?[loaders]', globalObject: 'self' }, target: 'web', cache: staticCompression, module: { rules: [ { test: /\\.css$/, use: ['style-loader', 'css-loader'] }, { test: /\\.(ttf|eot|svg)(\\?v=\\d+\\.\\d+\\.\\d+)?$/, type: 'asset', parser: { dataUrlCondition: { maxSize: 10000, } }, generator: { dataUrl: { mimetype: 'image/svg+xml' } } }, { test: /\\.(jpg|png|gif)$/, type: 'asset/resource', generator: { filename: '[hash].[ext]' } }, { // see https://github.com/eclipse-theia/theia/issues/556 test: /source-map-support/, loader: 'ignore-loader' }, { test: /\\.d\\.ts$/, loader: 'ignore-loader' }, { test: /\\.js$/, enforce: 'pre', loader: 'source-map-loader', exclude: /jsonc-parser|fast-plist|onigasm/ }, { test: /\\.woff(2)?(\\?v=[0-9]\\.[0-9]\\.[0-9])?$/, type: 'asset', parser: { dataUrlCondition: { maxSize: 10000, } }, generator: { dataUrl: { mimetype: 'image/svg+xml' } } }, { test: /node_modules[\\\\|\/](vscode-languageserver-types|vscode-uri|jsonc-parser|vscode-languageserver-protocol)/, loader: 'umd-compat-loader' }, { test: /\\.wasm$/, type: 'asset/resource' }, { test: /\\.plist$/, type: 'asset/resource' } ] }, resolve: { fallback: { 'child_process': false, 'crypto': false, 'net': false, 'path': require.resolve('path-browserify'), 'process': false, 'os': false, 'timers': false }, ${this.ifMonaco(() => `alias: { // Replace Monaco's nls module with Theia's localization-aware version. // ESM exports are immutable so we cannot override localize/localize2 at runtime. // Using the resolved absolute path ensures that both external imports // (e.g. '@theia/monaco-editor-core/esm/vs/nls') and internal relative // imports within Monaco (e.g. '../nls.js') are redirected. [path.join(resolvePackagePath('@theia/monaco-editor-core', __dirname), '..', 'esm', 'vs', 'nls.js')]: path.join(resolvePackagePath('@theia/monaco', __dirname), '..', 'lib', 'browser', 'monaco-nls.js') },`)} extensions: ['.js'] }, stats: { warnings: true, children: true }, ignoreWarnings: [ // Some packages do not have source maps, that's ok /Failed to parse source map/, { // Monaco uses 'require' in a non-standard way module: /@theia\\/monaco-editor-core/, message: /require function is used in a way in which dependencies cannot be statically extracted/ } ] }, ${this.ifMonaco(() => `{ // The Monaco editor worker must be built separately without the NLS alias. // The NLS alias redirects to monaco-nls.ts which imports from @theia/core, // and those modules are not available in the web worker context. mode, devtool: 'source-map', entry: { 'editor.worker': '@theia/monaco-editor-core/esm/vs/editor/common/services/editorWebWorkerMain.js' }, output: { filename: '[name].js', path: outputPath, devtoolModuleFilenameTemplate: 'webpack:///[resource-path]?[loaders]', globalObject: 'self' }, target: 'webworker', cache: staticCompression, resolve: { extensions: ['.js'] }, ignoreWarnings: [ { module: /@theia\\/monaco-editor-core/, message: /require function is used in a way in which dependencies cannot be statically extracted/ } ] },`)} { mode, plugins: [ new MiniCssExtractPlugin({ // Options similar to the same options in webpackOptions.output // both options are optional filename: "[name].css", chunkFilename: "[id].css", }), new MonacoWebpackPlugin(), ], devtool: 'source-map', entry: { "secondary-window": path.resolve(__dirname, 'src-gen/frontend/secondary-index.js'), }, output: { filename: '[name].js', path: outputPath, devtoolModuleFilenameTemplate: 'webpack:///[resource-path]?[loaders]', globalObject: 'self' }, target: 'web', cache: staticCompression, module: { rules: [ { test: /\.css$/i, use: [MiniCssExtractPlugin.loader, "css-loader"] }, { test: /\.wasm$/, type: 'asset/resource' } ] }, resolve: { fallback: { 'child_process': false, 'crypto': false, 'net': false, 'path': require.resolve('path-browserify'), 'process': false, 'os': false, 'timers': false }, ${this.ifMonaco(() => `alias: { // Replace Monaco's nls module with Theia's localization-aware version. // ESM exports are immutable so we cannot override localize/localize2 at runtime. // Using the resolved absolute path ensures that both external imports // (e.g. '@theia/monaco-editor-core/esm/vs/nls') and internal relative // imports within Monaco (e.g. '../nls.js') are redirected. [path.join(resolvePackagePath('@theia/monaco-editor-core', __dirname), '..', 'esm', 'vs', 'nls.js')]: path.join(resolvePackagePath('@theia/monaco', __dirname), '..', 'lib', 'browser', 'monaco-nls.js') },`)} extensions: ['.js'] }, stats: { warnings: true, children: true }, ignoreWarnings: [ { // Monaco uses 'require' in a non-standard way module: /@theia\\/monaco-editor-core/, message: /require function is used in a way in which dependencies cannot be statically extracted/ } ] }${this.ifElectron(`, { mode, devtool: 'source-map', entry: { "preload": path.resolve(__dirname, 'src-gen/frontend/preload.js'), }, output: { filename: '[name].js', path: outputPath, devtoolModuleFilenameTemplate: 'webpack:///[resource-path]?[loaders]', globalObject: 'self' }, target: 'electron-preload', cache: staticCompression, stats: { warnings: true, children: true } }`)}];`; } protected compileUserWebpackConfig(): string { return `/** * This file can be edited to customize webpack configuration. * To reset delete this file and rerun theia build again. */ // @ts-check const configs = require('./${paths.basename(this.genConfigPath)}'); ${this.ifBrowserOnly('', `const nodeConfig = require('./${paths.basename(this.genNodeConfigPath)}');`)} /** * Expose bundled modules on window.theia.moduleName namespace, e.g. * window['theia']['@theia/core/lib/common/uri']. * Such syntax can be used by external code, for instance, for testing. configs[0].module.rules.push({ test: /\\.js$/, loader: require.resolve('@theia/application-manager/lib/expose-loader') }); */ ${this.ifBrowserOnly('module.exports = configs;', `module.exports = [ ...configs, nodeConfig.config ];`)} `; } protected compileNodeWebpackConfig(): string { return `/** * Don't touch this file. It will be regenerated by theia build. * To customize webpack configuration change ${this.configPath} */ // @ts-check const path = require('path'); const yargs = require('yargs'); const webpack = require('webpack'); const TerserPlugin = require('terser-webpack-plugin'); const { NativeWebpackPlugin, MonacoWebpackPlugin } = require('@theia/bundle-plugin'); const { mode } = yargs.option('mode', { description: "Mode to use", choices: ["development", "production"], default: "production" }).argv; const production = mode === 'production'; /** @type {import('webpack').EntryObject} */ const commonJsLibraries = {}; for (const [entryPointName, entryPointPath] of Object.entries({ ${this.ifPackage('@theia/plugin-ext', "'backend-init-theia': '@theia/plugin-ext/lib/hosted/node/scanners/backend-init-theia',")} ${this.ifPackage('@theia/filesystem', "'parcel-watcher': '@theia/filesystem/lib/node/parcel-watcher',")} ${this.ifPackage('@theia/plugin-ext-vscode', "'plugin-vscode-init': '@theia/plugin-ext-vscode/lib/node/plugin-vscode-init',")} ${this.ifPackage('@theia/api-provider-sample', "'gotd-api-init': '@theia/api-provider-sample/lib/plugin/gotd-api-init',")} })) { commonJsLibraries[entryPointName] = { import: require.resolve(entryPointPath), library: { type: 'commonjs2', }, }; } const ignoredResources = new Set(); if (process.platform !== 'win32') { ignoredResources.add('@vscode/windows-ca-certs'); ignoredResources.add('@vscode/windows-ca-certs/build/Release/crypt32.node'); } const nativePlugin = new NativeWebpackPlugin({ out: 'native', trash: ${this.ifPackage('@theia/filesystem', 'true', 'false')}, ripgrep: ${this.ifPackage(['@theia/search-in-workspace', '@theia/file-search'], 'true', 'false')}, pty: ${this.ifPackage('@theia/process', 'true', 'false')}, nativeBindings: { drivelist: 'drivelist/build/Release/drivelist.node' } }); ${this.ifPackage('@theia/process', () => `// Ensure that node-pty is correctly hoisted try { require.resolve('node-pty'); } catch { console.error('"node-pty" dependency is not installed correctly. Ensure that it is available in the root node_modules directory.'); console.error('Exiting webpack build process.'); process.exit(1); }`)} /** @type {import('webpack').Configuration} */ const config = { mode, devtool: mode === 'development' ? 'source-map' : false, target: 'node', node: { global: false, __filename: false, __dirname: false }, resolve: { extensions: ['.js', '.json', '.wasm', '.node'], }, output: { filename: '[name].js', path: path.resolve(__dirname, 'lib', 'backend'), devtoolModuleFilenameTemplate: 'webpack:///[absolute-resource-path]?[loaders]', },${this.ifElectron(` externals: { electron: 'require("electron")' },`)} entry: { // Main entry point of the Theia application backend: 'main': require.resolve('./src-gen/backend/main'), // Theia's IPC mechanism: 'ipc-bootstrap': require.resolve('@theia/core/lib/node/messaging/ipc-bootstrap'), ${this.ifPackage('@theia/plugin-ext', () => `// VS Code extension support: 'plugin-host': require.resolve('@theia/plugin-ext/lib/hosted/node/plugin-host'),`)} ${this.ifPackage('@theia/plugin-ext-headless', () => `// Theia Headless Plugin support: 'plugin-host-headless': require.resolve('@theia/plugin-ext-headless/lib/hosted/node/plugin-host-headless'),`)} ${this.ifPackage('@theia/process', () => `// Make sure the node-pty thread workers can be executed: 'worker/conoutSocketWorker': require.resolve('node-pty/lib/worker/conoutSocketWorker'), 'conpty_console_list_agent': require.resolve('node-pty/lib/conpty_console_list_agent'),`)} ${this.ifElectron("'electron-main': require.resolve('./src-gen/backend/electron-main'),")} ${this.ifPackage('@theia/dev-container', () => `// VS Code Dev-Container communication: 'dev-container-server': require.resolve('@theia/dev-container/lib/dev-container-server/dev-container-server'),`)} ...commonJsLibraries }, module: { rules: [ // Make sure we can still find and load our native addons. { test: /\\.node$/, loader: 'node-loader', options: { name: 'native/[name].[ext]' } }, { test: /\\.d\\.ts$/, loader: 'ignore-loader' }, { test: /\\.js$/, enforce: 'pre', loader: 'source-map-loader' }, // node-pty uses a dynamic require which needs to be rewritten to work with webpack. { test: /node_modules[/\\\\]node-pty[/\\\\]lib[/\\\\]utils\.js$/, loader: 'string-replace-loader', options: { search: /require\\(/, replace: '__non_webpack_require__(', flags: 'g' } }, // jsonc-parser exposes its UMD implementation by default, which // confuses Webpack leading to missing js in the bundles. { test: /node_modules[\\/](jsonc-parser)/, loader: 'umd-compat-loader' } ] }, plugins: [ // Some native dependencies need special handling nativePlugin, // Optional node dependencies can be safely ignored new webpack.IgnorePlugin({ checkResource: resource => ignoredResources.has(resource) }), new MonacoWebpackPlugin() ], optimization: { // Split and reuse code across the various entry points splitChunks: { chunks: 'all' }, // Only minimize if we run webpack in production mode minimize: production, minimizer: [ new TerserPlugin({ exclude: /^(lib|builtins)\\//${this.ifPackage(['@theia/scanoss', '@theia/ai-anthropic', '@theia/ai-openai'], () => `, terserOptions: { keep_classnames: /AbortSignal/ }`)} }) ] }, ignoreWarnings: [ // Some packages do not have source maps, that's ok /Failed to parse source map/, // require with expressions are not supported /the request of a dependency is an expression/, // Some packages use dynamic requires, we can safely ignore them (they are handled by the native webpack plugin) /require function is used in a way in which dependencies cannot be statically extracted/, { module: /yargs/ }, { module: /node-pty/ }, { module: /require-main-filename/ }, { module: /ws/ }, { module: /express/ }, { module: /cross-spawn/ }, { module: /@parcel\\/watcher/ } ] }; module.exports = { config, nativePlugin, ignoredResources }; `; } protected compileESBuildBrowserConfig(): string { return `/** * Don't touch this file. It will be regenerated by theia build. * To customize the build process, change ./esbuild.mjs */ import { nodeModulesPolyfillPlugin } from 'esbuild-plugins-node-modules-polyfill'; import { copy } from 'esbuild-plugin-copy'; import { monacoNlsPlugin, problemMatcherPlugin, sourceMapPathsPlugin } from '@theia/bundle-plugin'; import yargs from 'yargs'; import { hideBin } from 'yargs/helpers'; import resolvePackagePath from 'resolve-package-path'; import path from 'node:path'; import { fileURLToPath } from 'node:url'; export const __dirname = path.dirname(fileURLToPath(import.meta.url)); export function join(...parts) { return path.join(...parts).replace(/\\\\/g, '/'); } const { mode, watch } = yargs(hideBin(process.argv)).option('mode', { description: "Mode to use", choices: ["development", "production"], default: "production" }).option('watch', { description: 'Controls whether to enable watch mode', type: 'boolean', default: false }).argv; const production = mode === 'production'; const sourcemap = production ? false : 'linked'; const minify = production; export { mode, watch, sourcemap, minify }; /** * @type {Record} */ export const loader = { '.css': 'css', '.ttf': 'dataurl', '.eot': 'dataurl', '.svg': 'dataurl', '.woff': 'dataurl', '.woff2': 'dataurl', '.jpg': 'dataurl', '.png': 'dataurl', '.gif': 'dataurl', '.wasm': 'dataurl', '.plist': 'dataurl', '.node': 'file' }; /** * @type {import('esbuild').BuildOptions} */ export const browserOptions = { entryPoints: { 'bundle': './src-gen/frontend/index.js', 'secondary-window': './src-gen/frontend/secondary-index.js', ${this.ifMonaco(() => "'editor.worker': '@theia/monaco-editor-core/esm/vs/editor/common/services/editorWebWorkerMain.js',")} ${this.ifPackage('@theia/plugin-ext', "'plugin-worker': '@theia/plugin-ext/lib/hosted/browser/worker/worker-main.js',")} }, assetNames: '[name]', bundle: true, outdir: 'lib/frontend', platform: 'browser', // Support UMD libraries // but ensure that we also prioritize browser exports where possible mainFields: ['browser', 'module', 'main'], loader, minify, sourcemap, plugins: [ problemMatcherPlugin(watch, 'browser'), ${this.ifMonaco(() => 'monacoNlsPlugin(),')} nodeModulesPolyfillPlugin({ globals: { Buffer: true, process: false } }), copy({ assets: [ { // copy secondary window html file to lib folder from: join(__dirname, 'src-gen/frontend/secondary-window.html'), to: join(__dirname, 'lib', 'frontend') }${this.ifPackage('@theia/plugin-ext', `, { // copy webview files to lib folder from: join(resolvePackagePath('@theia/plugin-ext', __dirname), '..', 'src', 'main', 'browser', 'webview', 'pre', '*'), to: join(__dirname, 'lib', 'webview', 'pre') }`)}${this.ifPackage('@theia/plugin-ext-vscode', `, { // copy frontend plugin host files from: join(resolvePackagePath('@theia/plugin-ext-vscode', __dirname), '..', 'lib', 'node', 'context', 'plugin-vscode-init-fe.js'), to: join(__dirname, 'lib', 'frontend', 'context') }`)} ] }), sourceMapPathsPlugin() ] }; `; } protected compileESBuildNodeConfig(): string { return `/** * Don't touch this file. It will be regenerated by theia build. * To customize the build process, change ./esbuild.mjs */ import { nativeDependenciesPlugin, problemMatcherPlugin, sourceMapPathsPlugin } from '@theia/bundle-plugin'; import { watch, loader, minify, sourcemap, join, __dirname } from './gen-esbuild.browser.mjs'; import { copy } from 'esbuild-plugin-copy'; import resolvePackagePath from 'resolve-package-path'; /** * @type {Record} */ export const nativeBindings = { drivelist: 'drivelist/build/Release/drivelist.node' }; /** * @type {import('esbuild').BuildOptions} */ export const nodeOptions = { entryPoints: { 'main': './src-gen/backend/main', 'ipc-bootstrap': '@theia/core/lib/node/messaging/ipc-bootstrap', ${this.ifElectron("'electron-main': './src-gen/backend/electron-main',")} ${this.ifPackage('@theia/plugin-ext', () => `// VS Code extension support: 'plugin-host': '@theia/plugin-ext/lib/hosted/node/plugin-host',`)} ${this.ifPackage('@theia/plugin-ext-headless', () => `// Theia Headless Plugin support: 'plugin-host-headless': '@theia/plugin-ext-headless/lib/hosted/node/plugin-host-headless',`)} ${this.ifPackage('@theia/process', () => `// Make sure the node-pty thread workers can be executed: 'worker/conoutSocketWorker': 'node-pty/lib/worker/conoutSocketWorker', 'conpty_console_list_agent': 'node-pty/lib/conpty_console_list_agent',`)} ${this.ifPackage('@theia/dev-container', () => `// VS Code Dev-Container communication: 'dev-container-server': '@theia/dev-container/lib/dev-container-server/dev-container-server',`)} ${this.ifPackage('@theia/plugin-ext', "'backend-init-theia': '@theia/plugin-ext/lib/hosted/node/scanners/backend-init-theia',")} ${this.ifPackage('@theia/filesystem', "'parcel-watcher': '@theia/filesystem/lib/node/parcel-watcher',")} ${this.ifPackage('@theia/plugin-ext-vscode', "'plugin-vscode-init': '@theia/plugin-ext-vscode/lib/node/plugin-vscode-init',")} ${this.ifPackage('@theia/api-provider-sample', "'gotd-api-init': '@theia/api-provider-sample/lib/plugin/gotd-api-init',")} ${this.ifPackage('@theia/git', "'git-locator-host': '@theia/git/lib/node/git-locator/git-locator-host',")} }, assetNames: 'native/[name]', bundle: true, outdir: 'lib/backend', platform: 'node', mainFields: ['node', 'module', 'main'], external: ['electron'], loader, minify, sourcemap, plugins: [ problemMatcherPlugin(watch, 'node'), nativeDependenciesPlugin({ pty: ${this.ifPackage('@theia/process', 'true', 'false')}, ripgrep: ${this.ifPackage(['@theia/search-in-workspace', '@theia/file-search'], 'true', 'false')}, trash: ${this.ifPackage('@theia/filesystem', 'true', 'false')}, nativeBindings }), copy({ assets: [${this.ifPackage('@theia/terminal', ` { // copy shell integration scripts from: join(resolvePackagePath('@theia/terminal', __dirname), '..', 'src', 'node', 'shell-integrations', '**', '*'), to: join(__dirname, 'lib', 'backend', 'shell-integrations') },`)} ] }), sourceMapPathsPlugin() ] }; `; } protected compileESBuildUserConfig(): string { return `/** * This file can be edited to adjust the ESBuild build process. * To reset, delete this file and rerun theia build again. */ import { browserOptions, watch } from './gen-esbuild.browser.mjs'; ${this.ifBrowserOnly(`import esbuild from 'esbuild'; const browserContext = await esbuild.context(browserOptions); if (watch) { await browserContext.watch(); } else { try { await browserContext.rebuild(); await browserContext.dispose(); } catch { process.exit(1); } }`, `import { nodeOptions } from './gen-esbuild.node.mjs'; ${this.ifElectron("import { electronOptions } from './gen-esbuild.electron.mjs';")} import esbuild from 'esbuild'; const browserContext = await esbuild.context(browserOptions); const nodeContext = await esbuild.context(nodeOptions); ${this.ifElectron('const electronContext = await esbuild.context(electronOptions);')} if (watch) { await Promise.all([ browserContext.watch(), nodeContext.watch(),${this.ifElectron(` electronContext.watch(),`)} ]); } else { try { await browserContext.rebuild(); await browserContext.dispose(); await nodeContext.rebuild(); await nodeContext.dispose();${this.ifElectron(` await electronContext.rebuild(); await electronContext.dispose();`)} } catch { process.exit(1); } }`)} `; } protected compileESBuildElectronConfig(): string { return `/** * Don't touch this file. It will be regenerated by theia build. * To customize the build process, change ./esbuild.mjs */ import { problemMatcherPlugin, sourceMapPathsPlugin } from '@theia/bundle-plugin'; import { watch, loader, minify, sourcemap } from './gen-esbuild.browser.mjs'; /** * @type {import('esbuild').BuildOptions} */ export const electronOptions = { entryPoints: { 'preload': './src-gen/frontend/preload' }, bundle: true, outdir: 'lib/frontend', platform: 'node', mainFields: ['node', 'module', 'main'], external: ['electron'], loader, minify, sourcemap, plugins: [ problemMatcherPlugin(watch, 'electron'), sourceMapPathsPlugin() ] }; `; } }