use esbuild for browser extension builds (#57231)

* move bext-only extension host worker to client/browser

* use esbuild for browser extension build

esbuild is much faster and simpler than Webpack.
This commit is contained in:
Quinn Slack 2023-10-07 22:18:52 -07:00 committed by GitHub
parent 500718ca59
commit fb3c9c6015
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
22 changed files with 213 additions and 495 deletions

View File

@ -2,7 +2,7 @@ load("@aspect_rules_ts//ts:defs.bzl", "ts_config")
load("@aspect_rules_js//js:defs.bzl", "js_library")
load("@npm//:defs.bzl", "npm_link_all_packages")
load("//dev:defs.bzl", "jest_test", "sass", "ts_project")
load("//dev:webpack.bzl", "webpack_bundle")
load("@aspect_rules_esbuild//esbuild:defs.bzl", "esbuild")
load("//client/shared/dev:generate_graphql_operations.bzl", "generate_graphql_operations")
load("//client/shared/dev:build_code_intel_extensions.bzl", "build_code_intel_extensions")
load("//client/shared/dev:tools.bzl", "module_style_typings")
@ -216,6 +216,7 @@ ts_project(
"src/shared/components/TrackAnchorClick.tsx",
"src/shared/components/WildcardThemeProvider.tsx",
"src/shared/context.ts",
"src/shared/extensionHostWorker.ts",
"src/shared/platform/context.ts",
"src/shared/platform/extensionHost.ts",
"src/shared/platform/inlineExtensionsService.ts",
@ -341,20 +342,19 @@ jest_test(
)
filegroup(
name = "entry-conigs",
name = "entry-configs",
srcs = glob(["src/config/*.js"]),
)
webpack_bundle(
esbuild(
name = "bundle",
srcs = [
# JSON imports
"src/browser-extension/manifest.spec.json",
"code-intel-extensions.json",
# SRCS
":browser",
":entry-conigs",
":entry-configs",
":module_styles",
":package_styles",
@ -363,24 +363,21 @@ webpack_bundle(
"//:browserslist",
"//:package_json",
# STATIC ASSSETS
# STATIC ASSETS
"//client/browser/assets",
],
entry_points = {
"src/browser-extension/scripts/backgroundPage.main.js": "backgroundPage.main",
"src/browser-extension/scripts/contentPage.main.js": "contentPage.main",
"src/browser-extension/scripts/optionsPage.main.js": "optionsPage.main",
"src/browser-extension/scripts/afterInstallPage.main.js": "afterInstallPage.main",
"src/native-integration/nativeIntegration.main.js": "nativeIntegration.main",
"src/native-integration/phabricator/phabricatorNativeIntegration.main.js": "phabricatorNativeIntegration.main",
"src/app.css": "app",
"src/branded.css": "branded",
},
env = {
"NODE_ENV": "production",
},
output_dir = True,
webpack_config = "//client/browser/config:webpack-config",
config = "//client/browser/config:esbuild-config",
entry_points = [
"src/browser-extension/scripts/backgroundPage.main.js",
"src/browser-extension/scripts/contentPage.main.js",
"src/browser-extension/scripts/optionsPage.main.js",
"src/browser-extension/scripts/afterInstallPage.main.js",
"src/native-integration/nativeIntegration.main.js",
"src/native-integration/phabricator/phabricatorNativeIntegration.main.js",
"src/app.css",
"src/branded.css",
"src/shared/extensionHostWorker.js",
],
deps = ["//client/browser/config"],
)

View File

@ -51,7 +51,7 @@ It works as follows:
- `shared/`
Code shared between multiple code hosts.
- `config/`
Configuration code that is bundled via webpack. The configuration code adds properties to `window` that make it easier to tell what environment the script is running in. This is useful because the code can be run in the content script, background, options page, or in the actual page when injected by Phabricator and each environment will have different ways to do different things.
Configuration code that adds properties to `window` that make it easier to tell what environment the script is running in. This is useful because the code can be run in the content script, background, options page, or in the actual page when injected by Phabricator and each environment will have different ways to do different things.
- `end-to-end/`
E2E test suite.
- `scripts/`
@ -59,7 +59,7 @@ It works as follows:
- `config/`
Build configs.
- `build/`
Generated directory containing the output from webpack and the generated bundles for each browser.
Generated directory containing the build output and the generated bundles for each browser.
## Requirements

View File

@ -9,12 +9,8 @@ load("@aspect_rules_js//js:defs.bzl", "js_library")
ts_project(
name = "config",
srcs = [
"webpack/base.config.bazel.ts",
"webpack/base.config.ts",
"webpack/development.config.ts",
"webpack/production.config.bazel.ts",
"webpack/production.config.ts",
"webpack/utils.ts",
"esbuild.ts",
"utils.ts",
],
module = "commonjs",
tsconfig = "//client/browser:tsconfig",
@ -22,22 +18,21 @@ ts_project(
deps = [
"//:node_modules/@babel/runtime", #keep
"//:node_modules/@types/node",
"//:node_modules/css-loader", #keep
"//:node_modules/css-minimizer-webpack-plugin",
"//:node_modules/mini-css-extract-plugin",
"//:node_modules/esbuild",
# HACKS: bundle-time css import
"//:node_modules/open-color", #keep
"//:node_modules/path-browserify", #keep
"//:node_modules/style-loader", #keep
"//:node_modules/terser-webpack-plugin",
"//:node_modules/webpack",
"//:node_modules/worker-loader", #keep
"//client/browser:node_modules/@sourcegraph/build-config",
],
)
js_library(
name = "webpack-config",
srcs = ["webpack/production.config.bazel.js"],
name = "esbuild-config",
srcs = ["esbuild.bazel.js"],
visibility = ["//client:__subpackages__"],
deps = [
"//client/browser:node_modules/@sourcegraph/build-config",
"//client/browser/config",
"//client/build-config:build-config_lib",
],
)

View File

@ -0,0 +1,13 @@
// This file is only used by Bazel builds.
const { esbuildBuildOptions } = require('./esbuild.js')
module.exports = {
...esbuildBuildOptions(process.env.NODE_ENV === 'development' ? 'dev' : 'prod'),
// Unset configuration properties that are provided by Bazel.
entryPoints: undefined,
bundle: undefined,
outdir: undefined,
sourcemap: undefined,
}

View File

@ -0,0 +1,57 @@
import path from 'path'
import type * as esbuild from 'esbuild'
import { ROOT_PATH, stylePlugin } from '@sourcegraph/build-config'
import { generateBundleUID } from './utils'
const browserWorkspacePath = path.resolve(ROOT_PATH, 'client/browser')
const browserSourcePath = path.resolve(browserWorkspacePath, 'src')
/**
* Returns the esbuild build options for the browser extension build.
*/
export function esbuildBuildOptions(mode: 'dev' | 'prod', extraPlugins: esbuild.Plugin[] = []): esbuild.BuildOptions {
return {
entryPoints: [
// Browser extension
path.resolve(browserSourcePath, 'browser-extension/scripts/backgroundPage.main.ts'),
path.resolve(browserSourcePath, 'browser-extension/scripts/contentPage.main.ts'),
path.resolve(browserSourcePath, 'browser-extension/scripts/optionsPage.main.tsx'),
path.resolve(browserSourcePath, 'browser-extension/scripts/afterInstallPage.main.tsx'),
// Common native integration entry point (Gitlab, Bitbucket)
path.resolve(browserSourcePath, 'native-integration/nativeIntegration.main.ts'),
// Phabricator-only native integration entry point
path.resolve(browserSourcePath, 'native-integration/phabricator/phabricatorNativeIntegration.main.ts'),
// Styles
path.join(browserSourcePath, 'app.scss'),
path.join(browserSourcePath, 'branded.scss'),
// Worker
path.resolve(browserSourcePath, 'shared/extensionHostWorker.ts'),
],
format: 'cjs',
platform: 'browser',
plugins: [stylePlugin, ...extraPlugins],
define: {
'process.env.NODE_ENV': JSON.stringify(mode === 'dev' ? 'development' : 'production'),
'process.env.BUNDLE_UID': JSON.stringify(generateBundleUID()),
},
bundle: true,
minify: false,
logLevel: 'error',
jsx: 'automatic',
outdir: path.join(browserWorkspacePath, 'build/dist'),
chunkNames: '[ext]/[hash].chunk',
entryNames: '[ext]/[name].bundle',
target: 'esnext',
sourcemap: true,
alias: { path: 'path-browserify' },
loader: {
'.svg': 'text',
},
}
}

View File

@ -1,97 +0,0 @@
import path from 'path'
import CssMinimizerWebpackPlugin from 'css-minimizer-webpack-plugin'
import MiniCssExtractPlugin from 'mini-css-extract-plugin'
import webpack, { optimize } from 'webpack'
import {
getBazelCSSLoaders as getCSSLoaders,
getProvidePlugin,
getTerserPlugin,
getBasicCSSLoader,
getCSSModulesLoader,
} from '@sourcegraph/build-config'
const browserWorkspacePath = path.resolve(process.cwd(), 'client/browser')
const JS_OUTPUT_FOLDER = 'scripts'
const CSS_OUTPUT_FOLDER = 'css'
const extensionHostWorker = /main\.worker\.js$/
export const config = {
stats: {
children: true,
},
target: 'browserslist',
output: {
path: path.join(browserWorkspacePath, 'build/dist/js'),
filename: `${JS_OUTPUT_FOLDER}/[name].bundle.js`,
chunkFilename: '[id].chunk.js',
},
devtool: 'inline-cheap-module-source-map',
optimization: {
minimizer: [getTerserPlugin() as webpack.WebpackPluginInstance, new CssMinimizerWebpackPlugin()],
},
plugins: [
// Change scss imports to the pre-compiled css files
new webpack.NormalModuleReplacementPlugin(/.*\.scss$/, resource => {
resource.request = resource.request.replace(/\.scss$/, '.css')
}),
new MiniCssExtractPlugin({ filename: `${CSS_OUTPUT_FOLDER}/[name].bundle.css` }),
// Code splitting doesn't make sense/work in the browser extension, but we still want to use dynamic import()
new optimize.LimitChunkCountPlugin({ maxChunks: 1 }),
getProvidePlugin(),
],
resolve: {
extensions: ['.ts', '.tsx', '.js'],
alias: {
path: require.resolve('path-browserify'),
},
},
module: {
rules: [
{
test: /\.m?js$/,
exclude: extensionHostWorker,
type: 'javascript/auto',
resolve: {
// Allow importing without file extensions
// https://webpack.js.org/configuration/module/#resolvefullyspecified
fullySpecified: false,
},
},
{
// SCSS rule for our own and third-party styles
test: /\.css$/,
exclude: /\.module\.css$/,
use: getCSSLoaders(MiniCssExtractPlugin.loader, getBasicCSSLoader()),
},
{
test: /\.css$/,
// CSS Modules loaders are only applied when the file is explicitly named as CSS module stylesheet using the extension `.module.scss`.
include: /\.module\.css$/,
use: getCSSLoaders(MiniCssExtractPlugin.loader, getCSSModulesLoader({ sourceMap: false, url: false })),
},
{
test: /\.svg$/i,
type: 'asset/inline',
},
{
test: extensionHostWorker,
use: [
{
loader: 'worker-loader',
options: { filename: `${JS_OUTPUT_FOLDER}/extensionHostWorker.bundle.js` },
},
],
resolve: {
// Allow importing without file extensions
// https://webpack.js.org/configuration/module/#resolvefullyspecified
fullySpecified: false,
},
},
],
},
} satisfies webpack.Configuration

View File

@ -1,97 +0,0 @@
import path from 'path'
import CssMinimizerWebpackPlugin from 'css-minimizer-webpack-plugin'
import MiniCssExtractPlugin from 'mini-css-extract-plugin'
import type webpack from 'webpack'
import { optimize } from 'webpack'
import {
ROOT_PATH,
getBabelLoader,
getCSSLoaders,
getProvidePlugin,
getTerserPlugin,
getCSSModulesLoader,
getBasicCSSLoader,
} from '@sourcegraph/build-config'
export const browserWorkspacePath = path.resolve(ROOT_PATH, 'client/browser')
const browserSourcePath = path.resolve(browserWorkspacePath, 'src')
const extensionHostWorker = /main\.worker\.ts$/
export const config = {
target: 'browserslist',
entry: [
// Browser extension
path.resolve(browserSourcePath, 'browser-extension/scripts/backgroundPage.main.ts'),
path.resolve(browserSourcePath, 'browser-extension/scripts/contentPage.main.ts'),
path.resolve(browserSourcePath, 'browser-extension/scripts/optionsPage.main.tsx'),
path.resolve(browserSourcePath, 'browser-extension/scripts/afterInstallPage.main.tsx'),
// Common native integration entry point (Gitlab, Bitbucket)
path.resolve(browserSourcePath, 'native-integration/nativeIntegration.main.ts'),
// Phabricator-only native integration entry point
path.resolve(browserSourcePath, 'native-integration/phabricator/phabricatorNativeIntegration.main.ts'),
// Styles
path.join(browserSourcePath, 'app.scss'),
path.join(browserSourcePath, 'branded.scss'),
],
output: {
path: path.join(browserWorkspacePath, 'build/dist/js'),
filename: '[name].bundle.js',
chunkFilename: '[id].chunk.js',
},
devtool: 'inline-cheap-module-source-map',
optimization: {
minimizer: [getTerserPlugin() as webpack.WebpackPluginInstance, new CssMinimizerWebpackPlugin()],
},
plugins: [
new MiniCssExtractPlugin({ filename: '../css/[name].bundle.css' }),
// Code splitting doesn't make sense/work in the browser extension, but we still want to use dynamic import()
new optimize.LimitChunkCountPlugin({ maxChunks: 1 }),
getProvidePlugin(),
],
resolve: {
extensions: ['.ts', '.tsx', '.js'],
alias: {
path: require.resolve('path-browserify'),
},
},
module: {
rules: [
{
test: /\.[jt]sx?$/,
exclude: extensionHostWorker,
use: [getBabelLoader()],
},
{
// SCSS rule for our own and third-party styles
test: /\.(css|sass|scss)$/,
exclude: /\.module\.(sass|scss)$/,
use: getCSSLoaders(MiniCssExtractPlugin.loader, getBasicCSSLoader()),
},
{
test: /\.(css|sass|scss)$/,
include: /\.module\.(sass|scss)$/,
use: getCSSLoaders(MiniCssExtractPlugin.loader, getCSSModulesLoader({ sourceMap: false, url: false })),
},
{
test: /\.svg$/i,
type: 'asset/inline',
},
{
test: extensionHostWorker,
use: [
{
loader: 'worker-loader',
options: { filename: 'extensionHostWorker.bundle.js' },
},
getBabelLoader(),
],
},
],
},
} satisfies webpack.Configuration

View File

@ -1,27 +0,0 @@
import * as path from 'path'
import * as webpack from 'webpack'
import { getCacheConfig } from '@sourcegraph/build-config'
import { config as baseConfig, browserWorkspacePath } from './base.config'
import { generateBundleUID } from './utils'
const { plugins, ...base } = baseConfig
export const config: webpack.Configuration = {
...base,
mode: 'development',
// Use cache only in `development` mode to speed up production build.
cache: getCacheConfig({ invalidateCacheFiles: [path.resolve(browserWorkspacePath, 'babel.config.js')] }),
plugins: (plugins || []).concat(
...[
new webpack.DefinePlugin({
'process.env': {
NODE_ENV: JSON.stringify('development'),
BUNDLE_UID: JSON.stringify(generateBundleUID()),
},
}),
]
),
}

View File

@ -1,47 +0,0 @@
import TerserPlugin from 'terser-webpack-plugin'
import * as webpack from 'webpack'
import { config as baseConfig } from './base.config.bazel'
import { generateBundleUID } from './utils'
const { plugins, ...base } = baseConfig
export const config: webpack.Configuration = {
...base,
mode: 'production',
optimization: {
minimize: true,
minimizer: [
new TerserPlugin({
extractComments: false,
terserOptions: {
output: {
// Without this, Uglify will change \u0000 to \0 (NULL byte),
// which causes Chrome to complain that the bundle is not UTF8
ascii_only: true,
beautify: false,
comments: false,
},
},
}) as webpack.WebpackPluginInstance,
],
},
plugins: (plugins || []).concat(
...[
new webpack.DefinePlugin({
'process.env': {
NODE_ENV: JSON.stringify('production'),
BUNDLE_UID: JSON.stringify(generateBundleUID()),
},
}),
new webpack.ProvidePlugin({
$: 'jquery',
jQuery: 'jquery',
'$.fn.pjax': 'jquery-pjax',
}),
]
),
}
// eslint-disable-next-line import/no-default-export
export default config

View File

@ -1,44 +0,0 @@
import TerserPlugin from 'terser-webpack-plugin'
import * as webpack from 'webpack'
import { config as baseConfig } from './base.config'
import { generateBundleUID } from './utils'
const { plugins, ...base } = baseConfig
export const config: webpack.Configuration = {
...base,
mode: 'production',
optimization: {
minimize: true,
minimizer: [
new TerserPlugin({
extractComments: false,
terserOptions: {
output: {
// Without this, Uglify will change \u0000 to \0 (NULL byte),
// which causes Chrome to complain that the bundle is not UTF8
ascii_only: true,
beautify: false,
comments: false,
},
},
}) as webpack.WebpackPluginInstance,
],
},
plugins: (plugins || []).concat(
...[
new webpack.DefinePlugin({
'process.env': {
NODE_ENV: JSON.stringify('production'),
BUNDLE_UID: JSON.stringify(generateBundleUID()),
},
}),
new webpack.ProvidePlugin({
$: 'jquery',
jQuery: 'jquery',
'$.fn.pjax': 'jquery-pjax',
}),
]
),
}

View File

@ -1,45 +1,16 @@
import shelljs from 'shelljs'
import signale from 'signale'
import webpack from 'webpack'
import * as esbuild from 'esbuild'
import { config } from '../config/webpack/production.config'
import { esbuildBuildOptions } from '../config/esbuild'
import { buildNpm } from './build-npm'
import * as tasks from './tasks'
import { browserBuildStepsPlugin, copyAssetsPlugin } from './esbuildPlugins'
const buildChrome = tasks.buildChrome('prod')
const buildFirefox = tasks.buildFirefox('prod')
const buildSafari = tasks.buildSafari('prod')
const buildEdge = tasks.buildEdge('prod')
tasks.copyAssets()
const compiler = webpack(config)
signale.await('Webpack compilation')
compiler.run(async (error, stats) => {
console.log(stats?.toString(tasks.WEBPACK_STATS_OPTIONS))
if (stats?.hasErrors()) {
signale.error('Webpack compilation error')
process.exit(1)
}
signale.success('Webpack compilation done')
buildChrome()
buildEdge()
buildFirefox()
if (isXcodeAvailable()) {
buildSafari()
} else {
signale.debug('Skipping Safari build because Xcode tools were not found (xcrun, xcodebuild)')
}
tasks.copyIntegrationAssets()
await buildNpm()
signale.success('Build done')
})
function isXcodeAvailable(): boolean {
return !!shelljs.which('xcrun') && !!shelljs.which('xcodebuild')
async function build(): Promise<void> {
await esbuild.build(esbuildBuildOptions('prod', [copyAssetsPlugin, browserBuildStepsPlugin('prod')]))
}
if (require.main === module) {
build().catch(error => {
console.error('Error:', error)
process.exit(1)
})
}

View File

@ -1,40 +1,20 @@
import * as esbuild from 'esbuild'
import signale from 'signale'
import webpack from 'webpack'
import { config } from '../config/webpack/development.config'
import { esbuildBuildOptions } from '../config/esbuild'
import * as tasks from './tasks'
import { browserBuildStepsPlugin, copyAssetsPlugin } from './esbuildPlugins'
signale.config({ displayTimestamp: true })
async function watch(): Promise<void> {
const ctx = await esbuild.context(esbuildBuildOptions('dev', [copyAssetsPlugin, browserBuildStepsPlugin('dev')]))
await ctx.watch()
signale.info('Watching...')
await new Promise(() => {}) // wait forever
}
const buildChrome = tasks.buildChrome('dev')
const buildFirefox = tasks.buildFirefox('dev')
const buildEdge = tasks.buildEdge('dev')
tasks.copyAssets()
const compiler = webpack(config)
signale.info('Running webpack')
compiler.hooks.watchRun.tap('Notify', () => signale.await('Compiling...'))
compiler.watch(
{
aggregateTimeout: 300,
},
(error, stats) => {
signale.complete(stats?.toString(tasks.WEBPACK_STATS_OPTIONS))
if (error || stats?.hasErrors()) {
signale.error('Webpack compilation error')
return
}
signale.success('Webpack compilation done')
buildChrome()
buildEdge()
buildFirefox()
tasks.copyIntegrationAssets()
}
)
if (require.main === module) {
watch().catch(error => {
console.error('Error:', error)
process.exit(1)
})
}

View File

@ -0,0 +1,46 @@
import type * as esbuild from 'esbuild'
import shelljs from 'shelljs'
import signale from 'signale'
import { buildNpm } from './build-npm'
import * as tasks from './tasks'
export const copyAssetsPlugin: esbuild.Plugin = {
name: 'copyAssets',
setup: (): void => {
tasks.copyAssets()
},
}
export function browserBuildStepsPlugin(mode: 'dev' | 'prod'): esbuild.Plugin {
return {
name: 'browserBuildSteps',
setup: (build: esbuild.PluginBuild): void => {
build.onEnd(async result => {
if (result.errors.length === 0) {
signale.info('Running browser build steps...')
tasks.buildChrome(mode)()
tasks.buildFirefox(mode)()
tasks.buildEdge(mode)()
if (mode === 'prod') {
if (isXcodeAvailable()) {
tasks.buildSafari(mode)()
} else {
signale.debug(
'Skipping Safari build because Xcode tools were not found (xcrun, xcodebuild)'
)
}
}
tasks.copyIntegrationAssets()
if (mode === 'prod') {
await buildNpm()
}
}
})
},
}
}
function isXcodeAvailable(): boolean {
return !!shelljs.which('xcrun') && !!shelljs.which('xcodebuild')
}

View File

@ -6,7 +6,6 @@ import { omit, cloneDeep, curry } from 'lodash'
import shelljs from 'shelljs'
import signale from 'signale'
import utcVersion from 'utc-version'
import type { Configuration } from 'webpack'
import manifestSpec from '../src/browser-extension/manifest.spec.json'
import schema from '../src/browser-extension/schema.json'
@ -20,7 +19,7 @@ const EXTENSION_PERMISSIONS_ALL_URLS = Boolean(
process.env.EXTENSION_PERMISSIONS_ALL_URLS && JSON.parse(process.env.EXTENSION_PERMISSIONS_ALL_URLS)
)
export type BuildEnvironment = 'dev' | 'prod'
type BuildEnvironment = 'dev' | 'prod'
type Browser = 'firefox' | 'chrome' | 'safari' | 'edge'
@ -37,14 +36,6 @@ const BUILDS_DIR = 'build'
*/
const useUtcVersion = true
export const WEBPACK_STATS_OPTIONS: Configuration['stats'] = {
all: false,
timings: true,
errors: true,
warnings: true,
colors: true,
}
function ensurePaths(browser?: Browser): void {
shelljs.mkdir('-p', 'build/dist')
shelljs.mkdir('-p', 'build/bundles')

View File

@ -1,13 +1,11 @@
import '../../polyfills'
import '@sourcegraph/shared/src/polyfills'
import { fromEvent } from 'rxjs'
import { take } from 'rxjs/operators'
import { hasProperty, logger } from '@sourcegraph/common'
import { isEndpointPair } from '../../platform/context'
import { startExtensionHost } from './extensionHost'
import { startExtensionHost } from '@sourcegraph/shared/src/api/extension/extensionHost'
import { isEndpointPair } from '@sourcegraph/shared/src/platform/context'
interface InitMessage {
endpoints: {

View File

@ -3,8 +3,8 @@ load("@npm//:defs.bzl", "npm_link_all_packages")
load("//dev:defs.bzl", "npm_package", "ts_project")
load("//dev:eslint.bzl", "eslint_config_and_lint_root")
# TODO(bazel): currently handled with #keep
# gazelle:js_resolve **/esbuild/* //client/build-config/src/esbuild
# gazelle:js_ignore_imports ../../../../postcss.config
# gazelle:js_ignore_imports ../../../../../../../../postcss.config
npm_link_all_packages(name = "node_modules")
@ -22,6 +22,11 @@ ts_config(
ts_project(
name = "build-config_lib",
srcs = [
"src/esbuild/monacoPlugin.ts",
"src/esbuild/packageResolutionPlugin.ts",
"src/esbuild/plugins.ts",
"src/esbuild/stylePlugin.ts",
"src/esbuild/workerPlugin.ts",
"src/index.ts",
"src/paths.ts",
"src/utils/environment-config.ts",
@ -35,10 +40,16 @@ ts_project(
tsconfig = ":tsconfig",
deps = [
":node_modules/@statoscope/webpack-plugin",
":node_modules/@types/sass",
":node_modules/buffer", #keep
":node_modules/css-loader", #keep
":node_modules/enhanced-resolve",
":node_modules/esbuild",
":node_modules/monaco-editor-webpack-plugin",
":node_modules/postcss",
":node_modules/postcss-modules",
":node_modules/process", #keep
":node_modules/sass",
":node_modules/style-loader", #keep
":node_modules/terser-webpack-plugin",
":node_modules/webpack",
@ -48,7 +59,6 @@ ts_project(
"//:node_modules/@types/node",
"//:node_modules/monaco-editor", #keep
"//:node_modules/monaco-yaml", #keep
"//client/build-config/src/esbuild",
],
)

View File

@ -1,24 +0,0 @@
# TODO(bazel): esbuild build disabled, mocked for now
load("@aspect_rules_js//js:defs.bzl", "js_library")
genrule(
name = "esbuild-mocked",
outs = ["plugins.js"],
cmd = "echo 'exports.stylePlugin = {}' > $@",
)
genrule(
name = "esbuild-mocked-types",
outs = ["plugins.d.ts"],
cmd = "echo 'export const stylePlugin: any' > $@",
)
js_library(
name = "esbuild",
srcs = [
":esbuild-mocked",
":esbuild-mocked-types",
],
visibility = ["//client/build-config:__pkg__"],
)

View File

@ -7,17 +7,24 @@ import postcss from 'postcss'
import postcssModules from 'postcss-modules'
import sass from 'sass'
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
import postcssConfig from '../../../../postcss.config'
import { NODE_MODULES_PATH, ROOT_PATH, WORKSPACE_NODE_MODULES_PATHS } from '../paths'
/* eslint-disable import/extensions */
const postcssConfig = process.env.BAZEL_BINDIR
? // eslint-disable-next-line @typescript-eslint/no-require-imports
require('../../../../../../../../postcss.config')
: // eslint-disable-next-line @typescript-eslint/no-require-imports
require('../../../../postcss.config')
/**
* An esbuild plugin that builds .css and .scss stylesheets (including support for CSS modules).
*/
export const stylePlugin: esbuild.Plugin = {
name: 'style',
setup: build => {
const isBazel = process.env.BAZEL_BINDIR
const modulesMap = new Map<string, string>()
const modulesPlugin = postcssModules({
generateScopedName: '[name]__[local]', // omit hash for local dev
@ -105,13 +112,19 @@ export const stylePlugin: esbuild.Plugin = {
unsafeCache: true,
})
build.onResolve({ filter: /\.s?css$/, namespace: 'file' }, async args => {
build.onResolve({ filter: /\.s?css$/, namespace: 'file' }, async ({ path: argsPath, ...args }) => {
// If running in Bazel, assume that the SASS compiler has already been run and just
// import the `.css` file.
if (isBazel) {
argsPath = argsPath.replace(/\.scss$/, '.css')
}
const inputPath = await new Promise<string>((resolve, reject) => {
resolver.resolve({}, args.resolveDir, args.path, {}, (error, filepath) => {
resolver.resolve({}, args.resolveDir, argsPath, {}, (error, filepath) => {
if (filepath) {
resolve(filepath)
} else {
reject(error ?? new Error(`Could not resolve file path for ${args.path}`))
reject(error ?? new Error(`Could not resolve file path for ${argsPath}`))
}
})
})

View File

@ -154,7 +154,6 @@ ts_project(
"src/api/extension/extensionHost.ts",
"src/api/extension/extensionHostApi.ts",
"src/api/extension/extensionHostState.ts",
"src/api/extension/main.worker.ts",
"src/api/extension/test/test-helpers.ts",
"src/api/extension/util.ts",
"src/api/extension/utils/ReferenceCounter.ts",

View File

@ -1,24 +1,12 @@
// eslint-disable-next-line import/extensions
import { Subscription } from 'rxjs'
import type { EndpointPair, ClosableEndpointPair } from '../../platform/context'
/* eslint-disable import/extensions, @typescript-eslint/ban-ts-comment */
// @ts-ignore
import ExtensionHostWorker from './main.worker'
/* eslint-enable import/extensions, @typescript-eslint/ban-ts-comment */
import type { EndpointPair } from '../../platform/context'
/**
* Creates a web worker with the extension host and sets up a bidirectional MessageChannel-based communication channel.
*
* If a `workerBundleURL` is provided, it is used to create a new Worker(), instead of using the ExtensionHostWorker
* returned by worker-loader. This is useful to load the worker bundle from a different path.
*/
export function createExtensionHostWorker(workerBundleURL?: string): { worker: Worker; clientEndpoints: EndpointPair } {
export function createExtensionHostWorker(workerBundleURL: string): { worker: Worker; clientEndpoints: EndpointPair } {
const clientAPIChannel = new MessageChannel()
const extensionHostAPIChannel = new MessageChannel()
const worker = workerBundleURL ? new Worker(workerBundleURL) : new ExtensionHostWorker()
const worker = new Worker(workerBundleURL)
const workerEndpoints: EndpointPair = {
proxy: clientAPIChannel.port2,
expose: extensionHostAPIChannel.port2,
@ -30,8 +18,3 @@ export function createExtensionHostWorker(workerBundleURL?: string): { worker: W
}
return { worker, clientEndpoints }
}
export function createExtensionHost(workerBundleURL?: string): ClosableEndpointPair {
const { clientEndpoints, worker } = createExtensionHostWorker(workerBundleURL)
return { endpoints: clientEndpoints, subscription: new Subscription(() => worker.terminate()) }
}

View File

@ -2100,6 +2100,7 @@ WEBPACK_CONFIG_DEPS = [
"//:node_modules/react-refresh",
"//client/web/dev",
"//:node_modules/react-visibility-sensor", # required for https://github.com/joshwnj/react-visibility-sensor/issues/148 workaround
"//:postcss_config_js",
# HACKS: bundle-time css import
"//:node_modules/open-color",