Attempt #3: upgrade to Webpack v5 (#22580)

- web: upgrade `Webpack` to v5 for the web app
- web: upgrade `Webpack` to v5 for Storybook
- web: upgrade `Webpack` to v5 for the browser extension
- web: enable filesystem cache only in the development environment
- web: disable node.js polyfills
- web: disable `webpack.IgnorePlugin` for the web app
- web: switch to `react-refresh-webpack-plugin` from `react-hot-loader`
- web: use `cache` only in development mode
- web: remove redundant polyfill dependencies
- web: do not use `Webpack` cache on CI
- web: use runtime bundle in development mode by `frontend` server
This commit is contained in:
Valery Bugakov 2021-07-19 16:08:50 +08:00 committed by GitHub
parent edab7134e7
commit e31288f052
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
27 changed files with 1564 additions and 1692 deletions

View File

@ -1,17 +1,21 @@
import * as path from 'path'
import path from 'path'
import CssMinimizerWebpackPlugin from 'css-minimizer-webpack-plugin'
import MiniCssExtractPlugin from 'mini-css-extract-plugin'
import TerserPlugin from 'terser-webpack-plugin'
import * as webpack from 'webpack'
import webpack, { optimize } from 'webpack'
const buildEntry = (...files: string[]): string[] => files.map(file => path.join(__dirname, file))
import { subtypeOf } from '../../../shared/src/util/types'
const contentEntry = '../../src/config/content.entry.js'
const backgroundEntry = '../../src/config/background.entry.js'
const optionsEntry = '../../src/config/options.entry.js'
const pageEntry = '../../src/config/page.entry.js'
const extensionEntry = '../../src/config/extension.entry.js'
export const rootPath = path.resolve(__dirname, '../../../../')
export const browserWorkspacePath = path.resolve(rootPath, 'client/browser')
const browserSourcePath = path.resolve(browserWorkspacePath, 'src')
const contentEntry = path.resolve(browserSourcePath, 'config/content.entry.js')
const backgroundEntry = path.resolve(browserSourcePath, 'config/background.entry.js')
const optionsEntry = path.resolve(browserSourcePath, 'config/options.entry.js')
const pageEntry = path.resolve(browserSourcePath, 'config/page.entry.js')
const extensionEntry = path.resolve(browserSourcePath, 'config/extension.entry.js')
const babelLoader = {
loader: 'babel-loader',
@ -22,7 +26,6 @@ const babelLoader = {
}
const extensionHostWorker = /main\.worker\.ts$/
const rootPath = path.resolve(__dirname, '../../../../')
const getCSSLoaders = (...loaders: webpack.RuleSetUseItem[]): webpack.RuleSetUse => [
MiniCssExtractPlugin.loader,
@ -40,29 +43,38 @@ const getCSSLoaders = (...loaders: webpack.RuleSetUseItem[]): webpack.RuleSetUse
},
]
export const config: webpack.Configuration = {
export const config = subtypeOf<webpack.Configuration>()({
target: 'browserslist',
entry: {
// Browser extension
background: buildEntry(
background: [
extensionEntry,
backgroundEntry,
'../../src/browser-extension/scripts/backgroundPage.main.ts'
),
inject: buildEntry(extensionEntry, contentEntry, '../../src/browser-extension/scripts/contentPage.main.ts'),
options: buildEntry(extensionEntry, optionsEntry, '../../src/browser-extension/scripts/optionsPage.main.tsx'),
'after-install': path.resolve(__dirname, '../../src/browser-extension/scripts/afterInstallPage.main.tsx'),
path.resolve(browserSourcePath, 'browser-extension/scripts/backgroundPage.main.ts'),
],
inject: [
extensionEntry,
contentEntry,
path.resolve(browserSourcePath, 'browser-extension/scripts/contentPage.main.ts'),
],
options: [
extensionEntry,
optionsEntry,
path.resolve(browserSourcePath, 'browser-extension/scripts/optionsPage.main.tsx'),
],
'after-install': path.resolve(browserSourcePath, 'browser-extension/scripts/afterInstallPage.main.tsx'),
// Common native integration entry point (Gitlab, Bitbucket)
integration: buildEntry(pageEntry, '../../src/native-integration/integration.main.ts'),
integration: [pageEntry, path.resolve(browserSourcePath, 'native-integration/integration.main.ts')],
// Phabricator-only native integration entry point
phabricator: buildEntry(pageEntry, '../../src/native-integration/phabricator/integration.main.ts'),
phabricator: [pageEntry, path.resolve(browserSourcePath, 'native-integration/phabricator/integration.main.ts')],
// Styles
style: path.join(__dirname, '../../src/app.scss'),
'branded-style': path.join(__dirname, '../../src/branded.scss'),
style: path.join(browserSourcePath, 'app.scss'),
'branded-style': path.join(browserSourcePath, 'branded.scss'),
},
output: {
path: path.join(__dirname, '../../build/dist/js'),
path: path.join(browserWorkspacePath, 'build/dist/js'),
filename: '[name].bundle.js',
chunkFilename: '[id].chunk.js',
},
@ -70,15 +82,14 @@ export const config: webpack.Configuration = {
optimization: {
minimizer: [
new TerserPlugin({
sourceMap: true,
terserOptions: {
compress: {
// // Don't inline functions, which causes name collisions with uglify-es:
// Don't inline functions, which causes name collisions with uglify-es:
// https://github.com/mishoo/UglifyJS2/issues/2842
inline: 1,
},
},
}),
}) as webpack.WebpackPluginInstance,
new CssMinimizerWebpackPlugin(),
],
},
@ -86,7 +97,7 @@ export const config: webpack.Configuration = {
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 webpack.optimize.LimitChunkCountPlugin({ maxChunks: 1 }),
new optimize.LimitChunkCountPlugin({ maxChunks: 1 }),
],
resolve: {
extensions: ['.ts', '.tsx', '.js'],
@ -131,4 +142,4 @@ export const config: webpack.Configuration = {
},
],
},
}
})

View File

@ -2,12 +2,10 @@ import * as path from 'path'
import * as webpack from 'webpack'
import { config as baseConfig } from './base.config'
import { config as baseConfig, browserWorkspacePath, rootPath } from './base.config'
import { generateBundleUID } from './utils'
const { plugins, entry, ...base } = baseConfig
const entries = entry as webpack.Entry
const { plugins, entry: entries, ...base } = baseConfig
const entriesWithAutoReload = {
...entries,
@ -18,6 +16,19 @@ export const config: webpack.Configuration = {
...base,
entry: process.env.AUTO_RELOAD === 'false' ? entries : entriesWithAutoReload,
mode: 'development',
// Use cache only in `development` mode to speed up production build.
cache: {
type: 'filesystem',
buildDependencies: {
// Invalidate cache on config change.
config: [
__filename,
path.resolve(browserWorkspacePath, 'babel.config.js'),
path.resolve(rootPath, 'babel.config.js'),
path.resolve(rootPath, 'postcss.config.js'),
],
},
},
plugins: (plugins || []).concat(
...[
new webpack.DefinePlugin({

View File

@ -13,7 +13,6 @@ export const config: webpack.Configuration = {
minimize: true,
minimizer: [
new TerserPlugin({
sourceMap: true,
terserOptions: {
output: {
// Without this, Uglify will change \u0000 to \0 (NULL byte),
@ -22,7 +21,7 @@ export const config: webpack.Configuration = {
beautify: false,
},
},
}),
}) as webpack.WebpackPluginInstance,
],
},
plugins: (plugins || []).concat(

View File

@ -17,9 +17,9 @@ const compiler = webpack(config)
signale.await('Webpack compilation')
compiler.run((error, stats) => {
console.log(stats.toString(tasks.WEBPACK_STATS_OPTIONS))
console.log(stats?.toString(tasks.WEBPACK_STATS_OPTIONS))
if (stats.hasErrors()) {
if (stats?.hasErrors()) {
signale.error('Webpack compilation error')
process.exit(1)
}

View File

@ -27,9 +27,9 @@ compiler.watch(
aggregateTimeout: 300,
},
(error, stats) => {
signale.complete(stats.toString(tasks.WEBPACK_STATS_OPTIONS))
signale.complete(stats?.toString(tasks.WEBPACK_STATS_OPTIONS))
if (error || stats.hasErrors()) {
if (error || stats?.hasErrors()) {
signale.error('Webpack compilation error')
return
}

View File

@ -6,7 +6,7 @@ import { omit } from 'lodash'
import shelljs from 'shelljs'
import signale from 'signale'
import utcVersion from 'utc-version'
import { Stats } from 'webpack'
import { Configuration } from 'webpack'
import extensionInfo from '../src/browser-extension/manifest.spec.json'
import schema from '../src/browser-extension/schema.json'
@ -37,7 +37,7 @@ const BUILDS_DIR = 'build'
*/
const useUtcVersion = true
export const WEBPACK_STATS_OPTIONS: Stats.ToStringOptions = {
export const WEBPACK_STATS_OPTIONS: Configuration['stats'] = {
all: false,
timings: true,
errors: true,

View File

@ -11,7 +11,7 @@ import { isErrorLike } from '../util/errors'
import { sanitizeClass } from '../util/strings'
import { toNativeEvent } from './helpers'
import { HoverContext, HoverOverlayBaseProps, GetAlertClassName } from './HoverOverlay.types'
import type { HoverContext, HoverOverlayBaseProps, GetAlertClassName } from './HoverOverlay.types'
import { HoverOverlayAlerts, HoverOverlayAlertsProps } from './HoverOverlayAlerts'
import { HoverOverlayContents } from './HoverOverlayContents'
import { useLogTelemetryEvent } from './useLogTelemetryEvent'
@ -21,7 +21,7 @@ const LOADING = 'loading' as const
const transformMouseEvent = (handler: (event: MouseEvent) => void) => (event: React.MouseEvent<HTMLElement>) =>
handler(toNativeEvent(event))
export { HoverContext }
export type { HoverContext }
export interface HoverOverlayClassProps {
/** An optional class name to apply to the outermost element of the HoverOverlay */

View File

@ -176,7 +176,8 @@ export class Driver {
!message
.text()
.includes('Warning: componentWillReceiveProps has been renamed') &&
!message.text().includes('React-Hot-Loader') &&
!message.text().includes('Download the Apollo DevTools') &&
!message.text().includes('debug') &&
// These requests are expected to fail, we use them to check if the browser extension is installed.
message.location().url !== 'chrome-extension://invalid/'
),

View File

@ -1,4 +1,4 @@
import { Stats } from 'webpack'
import { StatsCompilation } from 'webpack'
import { nodeModulesPath } from '../webpack.config.common'
@ -6,7 +6,7 @@ import { nodeModulesPath } from '../webpack.config.common'
const SKIP_VENDOR_MODULES = ['webpack']
// Get a list of files to include into a DLL bundle based on Webpack stats provided.
export function getVendorModules(webpackStats: Stats.ToJsonOutput): Set<string> {
export function getVendorModules(webpackStats: StatsCompilation): Set<string> {
const vendorsChunk = webpackStats.chunks?.find(
chunk => typeof chunk.id === 'string' && chunk.id.includes('vendors')
)
@ -16,9 +16,13 @@ export function getVendorModules(webpackStats: Stats.ToJsonOutput): Set<string>
}
const vendorModules = vendorsChunk.modules
.map(({ identifier }) => {
.map(module => {
if (!module.identifier) {
return ''
}
// `identifier` contains loaders prefix, so `path.relative()` doesn't work for all cases.
const [relativePathToModule] = identifier.split(`${nodeModulesPath}/`).slice(-1)
const [relativePathToModule] = module.identifier.split(`${nodeModulesPath}/`).slice(-1)
// Remove suffix generated for some Storybook modules.
return relativePathToModule.replace('-generated-other-entry.js', '')

View File

@ -3,7 +3,7 @@ import fs from 'fs'
import path from 'path'
import signale from 'signale'
import { Stats } from 'webpack'
import { StatsCompilation } from 'webpack'
import { readJsonFile, storybookWorkspacePath, rootPath } from '../webpack.config.common'
@ -27,8 +27,8 @@ export const ensureWebpackStatsAreReady = (): void => {
}
// Read Webpack stats JSON file. If it's not available use `yarn build:webpack-stats` command to create it.
export function getWebpackStats(): Stats.ToJsonOutput {
export function getWebpackStats(): StatsCompilation {
ensureWebpackStatsAreReady()
return readJsonFile(webpackStatsPath) as Stats.ToJsonOutput
return readJsonFile(webpackStatsPath) as StatsCompilation
}

View File

@ -6,7 +6,15 @@ import { remove } from 'lodash'
import signale from 'signale'
import SpeedMeasurePlugin from 'speed-measure-webpack-plugin'
import TerserPlugin from 'terser-webpack-plugin'
import { DllReferencePlugin, Configuration, DefinePlugin, ProgressPlugin, RuleSetUseItem, RuleSetUse } from 'webpack'
import webpack, {
DllReferencePlugin,
Configuration,
DefinePlugin,
ProgressPlugin,
RuleSetUseItem,
RuleSetUse,
RuleSetRule,
} from 'webpack'
import { BundleAnalyzerPlugin } from 'webpack-bundle-analyzer'
import { ensureDllBundleIsReady } from './dllPlugin'
@ -22,6 +30,7 @@ import {
nodeModulesPath,
getBasicCSSLoader,
readJsonFile,
storybookWorkspacePath,
} from './webpack.config.common'
const getStoriesGlob = (): string[] => {
@ -79,6 +88,10 @@ const config = {
'@storybook/addon-toolbars',
],
core: {
builder: 'webpack5',
},
features: {
// Explicitly disable the deprecated, not used postCSS support,
// so no warning is rendered on each start of storybook.
@ -101,7 +114,7 @@ const config = {
config.mode = environment.shouldMinify ? 'production' : 'development'
// Check the default config is in an expected shape.
if (!config.module || !config.plugins) {
if (!config.module?.rules || !config.plugins) {
throw new Error(
'The format of the default storybook webpack config changed, please check if the config in ./src/main.ts is still valid'
)
@ -111,6 +124,11 @@ const config = {
new DefinePlugin({
NODE_ENV: JSON.stringify(config.mode),
'process.env.NODE_ENV': JSON.stringify(config.mode),
}),
new webpack.ProvidePlugin({
process: 'process/browser',
// Based on the issue: https://github.com/webpack/changelog-v5/issues/10
Buffer: ['buffer', 'Buffer'],
})
)
@ -118,12 +136,10 @@ const config = {
if (!config.optimization) {
throw new Error('The structure of the config changed, expected config.optimization to be not-null')
}
config.optimization.namedModules = false
config.optimization.minimize = true
config.optimization.minimizer = [
new TerserPlugin({
terserOptions: {
sourceMap: true,
compress: {
// Don't inline functions, which causes name collisions with uglify-es:
// https://github.com/mishoo/UglifyJS2/issues/2842
@ -132,6 +148,21 @@ const config = {
},
}),
]
} else {
// Use cache only in `development` mode to speed up production build.
config.cache = {
type: 'filesystem',
buildDependencies: {
// Invalidate cache on config change.
config: [
__filename,
path.resolve(storybookWorkspacePath, 'babel.config.js'),
path.resolve(rootPath, 'babel.config.js'),
path.resolve(rootPath, 'postcss.config.js'),
path.resolve(__dirname, './webpack.config.dll.ts'),
],
},
}
}
// We don't use Storybook's default Babel config for our repo, it doesn't include everything we need.
@ -174,7 +205,9 @@ const config = {
})
// Make sure Storybook style loaders are only evaluated for Storybook styles.
const cssRule = config.module.rules.find(rule => rule.test?.toString() === /\.css$/.toString())
const cssRule = config.module.rules.find(
(rule): rule is RuleSetRule => typeof rule !== 'string' && rule.test?.toString() === /\.css$/.toString()
)
if (!cssRule) {
throw new Error('Cannot find original CSS rule')
}
@ -190,7 +223,7 @@ const config = {
config.module.rules.push({
test: /\.ya?ml$/,
use: ['raw-loader'],
type: 'asset/source',
})
// Disable `CaseSensitivePathsPlugin` by default to speed up development build.

View File

@ -26,7 +26,7 @@ export const getMonacoCSSRule = (): RuleSetRule => ({
export const getMonacoTTFRule = (): RuleSetRule => ({
test: /\.ttf$/,
include: [monacoEditorPath],
use: ['file-loader'],
type: 'asset/resource',
})
export const getBasicCSSLoader = (): RuleSetUseItem => ({

View File

@ -26,6 +26,8 @@ const config: Configuration = {
filename: '[name].bundle.[contenthash].js',
path: dllPluginConfig.context,
library: dllPluginConfig.name,
// Required to fix the `WebpackManifestPlugin` output: https://github.com/shellscape/webpack-manifest-plugin/issues/229
publicPath: '',
},
module: {
rules: [

View File

@ -1,4 +1,6 @@
import chalk from 'chalk'
import { Application } from 'express'
import { once } from 'lodash'
import signale from 'signale'
import createWebpackCompiler, { Configuration } from 'webpack'
import WebpackDevServer, { ProxyConfigArrayItem } from 'webpack-dev-server'
@ -11,7 +13,6 @@ import {
getCSRFTokenAndCookie,
STATIC_ASSETS_PATH,
STATIC_ASSETS_URL,
WEBPACK_STATS_OPTIONS,
WEB_SERVER_URL,
} from '../utils'
@ -23,9 +24,9 @@ const { SOURCEGRAPH_API_URL, SOURCEGRAPH_HTTPS_PORT, IS_HOT_RELOAD_ENABLED } = e
export async function startDevelopmentServer(): Promise<void> {
// Get CSRF token value from the `SOURCEGRAPH_API_URL`.
const { csrfContextValue, csrfCookieValue } = await getCSRFTokenAndCookie(SOURCEGRAPH_API_URL)
signale.await('Development server', { ...environmentConfig, csrfContextValue, csrfCookieValue })
signale.start('Starting webpack-dev-server with environment config:\n', environmentConfig)
const proxyConfig = {
const proxyConfig: ProxyConfigArrayItem = {
context: PROXY_ROUTES,
...getAPIProxySettings({
csrfContextValue,
@ -33,7 +34,11 @@ export async function startDevelopmentServer(): Promise<void> {
}),
}
const options: WebpackDevServer.Configuration = {
// It's not possible to use `WebpackDevServer.Configuration` here yet, because
// type definitions for the `webpack-dev-server` are not updated to match v4.
const developmentServerConfig = {
// react-refresh plugin triggers page reload if needed.
liveReload: false,
hot: IS_HOT_RELOAD_ENABLED,
// TODO: resolve https://github.com/webpack/webpack-dev-server/issues/2313 and enable HTTPS.
https: false,
@ -41,24 +46,33 @@ export async function startDevelopmentServer(): Promise<void> {
disableDotRule: true,
},
port: SOURCEGRAPH_HTTPS_PORT,
publicPath: STATIC_ASSETS_URL,
contentBase: STATIC_ASSETS_PATH,
contentBasePublicPath: [STATIC_ASSETS_URL, '/'],
stats: WEBPACK_STATS_OPTIONS,
noInfo: false,
disableHostCheck: true,
proxy: [proxyConfig as ProxyConfigArrayItem],
before(app) {
client: {
overlay: false,
},
static: {
directory: STATIC_ASSETS_PATH,
publicPath: [STATIC_ASSETS_URL, '/'],
},
firewall: false,
proxy: [proxyConfig],
onBeforeSetupMiddleware(app: Application) {
app.use(getCSRFTokenCookieMiddleware(csrfCookieValue))
},
}
WebpackDevServer.addDevServerEntrypoints(webpackConfig, options)
const compiler = createWebpackCompiler(webpackConfig)
const server = new WebpackDevServer(compiler, developmentServerConfig as WebpackDevServer.Configuration)
const server = new WebpackDevServer(createWebpackCompiler(webpackConfig), options)
compiler.hooks.afterEmit.tap(
'development-server-logger',
once(() => {
signale.success('Webpack build is ready!')
})
)
server.listen(SOURCEGRAPH_HTTPS_PORT, '0.0.0.0', () => {
signale.success(`Development server is ready at ${chalk.blue.bold(WEB_SERVER_URL)}`)
signale.await('Waiting for Webpack to compile assets')
})
}

View File

@ -2,21 +2,25 @@ import path from 'path'
import HtmlWebpackHarddiskPlugin from 'html-webpack-harddisk-plugin'
import HtmlWebpackPlugin, { TemplateParameter, Options } from 'html-webpack-plugin'
import { Plugin } from 'webpack'
import { WebpackPluginInstance } from 'webpack'
import { createJsContext, environmentConfig, STATIC_ASSETS_PATH } from '../utils'
const { SOURCEGRAPH_HTTPS_PORT, NODE_ENV } = environmentConfig
export const getHTMLWebpackPlugins = (): Plugin[] => {
export const getHTMLWebpackPlugins = (): WebpackPluginInstance[] => {
const jsContext = createJsContext({ sourcegraphBaseUrl: `http://localhost:${SOURCEGRAPH_HTTPS_PORT}` })
// TODO: use `cmd/frontend/internal/app/ui/app.html` template to be consistent with the default production setup.
/**
* To match `cmd/frontend/internal/app/ui/app.html` template used by our `frontend` server
* script tags are injected after the <body> tag.
*/
const templateContent = ({ htmlWebpackPlugin }: TemplateParameter): string => `
<!DOCTYPE html>
<html lang="en">
<head>
<title>${htmlWebpackPlugin.options.title || 'Sourcegraph'}</title>
<title>Sourcegraph</title>
${htmlWebpackPlugin.tags.headTags.filter(tag => tag.tagName !== 'script').toString()}
</head>
<body>
<div id="root"></div>
@ -27,6 +31,7 @@ export const getHTMLWebpackPlugins = (): Plugin[] => {
// Required mock of the JS context object.
window.context = ${JSON.stringify(jsContext)}
</script>
${htmlWebpackPlugin.tags.headTags.filter(tag => tag.tagName === 'script').toString()}
</body>
</html>
`
@ -42,6 +47,7 @@ export const getHTMLWebpackPlugins = (): Plugin[] => {
},
filename: path.resolve(STATIC_ASSETS_PATH, 'index.html'),
alwaysWriteToDisk: true,
inject: false,
})
// Write index.html to the disk so it can be served by dev/prod servers.

View File

@ -1,4 +1,3 @@
// @ts-check
const path = require('path')
require('ts-node').register({
@ -9,8 +8,12 @@ require('ts-node').register({
const log = require('fancy-log')
const gulp = require('gulp')
const signale = require('signale')
const createWebpackCompiler = require('webpack')
const WebpackDevServer = require('webpack-dev-server')
// The `DevServerPlugin` should be exposed after the `webpack-dev-server@4` goes out of the beta stage.
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
const DevServerPlugin = require('webpack-dev-server/lib/utils/DevServerPlugin')
const {
graphQlSchema,
@ -72,37 +75,52 @@ async function webpackDevelopmentServer() {
const sockHost = process.env.SOURCEGRAPH_HTTPS_DOMAIN || 'sourcegraph.test'
const sockPort = Number(process.env.SOURCEGRAPH_HTTPS_PORT || 3443)
/** @type {import('webpack-dev-server').Configuration & { liveReload?: boolean }} */
/** @type {import('webpack-dev-server').ProxyConfigMap } */
const proxyConfig = {
'/': {
target: 'http://localhost:3081',
// Avoid crashing on "read ECONNRESET".
onError: () => undefined,
// Don't log proxy errors, these usually just contain
// ECONNRESET errors caused by the browser cancelling
// requests. This should not be needed to actually debug something.
logLevel: 'silent',
onProxyReqWs: (_proxyRequest, _request, socket) =>
socket.on('error', error => console.error('WebSocket proxy error:', error)),
},
}
const options = {
// react-refresh plugin triggers page reload if needed.
liveReload: false,
hot: !process.env.NO_HOT,
inline: !process.env.NO_HOT,
allowedHosts: ['.host.docker.internal'],
firewall: false,
host: 'localhost',
port: 3080,
publicPath: '/.assets/',
contentBase: './ui/assets',
stats: WEBPACK_STATS_OPTIONS,
noInfo: false,
disableHostCheck: true,
proxy: {
'/': {
target: 'http://localhost:3081',
// Avoid crashing on "read ECONNRESET".
onError: () => undefined,
// Don't log proxy errors, these usually just contain
// ECONNRESET errors caused by the browser cancelling
// requests. This should not be needed to actually debug something.
logLevel: 'silent',
onProxyReqWs: (_proxyRequest, _request, socket) =>
socket.on('error', error => console.error('WebSocket proxy error:', error)),
},
client: {
host: sockHost,
port: sockPort,
overlay: false,
},
static: {
directory: './ui/assets',
publicPath: '/.assets/',
},
proxy: proxyConfig,
transportMode: {
client: 'ws',
},
sockHost,
sockPort,
}
WebpackDevServer.addDevServerEntrypoints(webpackConfig, options)
// Based on the update: https://github.com/webpack/webpack-dev-server/pull/2844
if (!webpackConfig.plugins.find(plugin => plugin.constructor === DevServerPlugin)) {
// eslint-disable-next-line @typescript-eslint/no-unsafe-call
webpackConfig.plugins.push(new DevServerPlugin(options))
}
const server = new WebpackDevServer(createWebpackCompiler(webpackConfig), options)
await new Promise((resolve, reject) => {
signale.await('Waiting for Webpack to compile assets')
server.listen(3080, '0.0.0.0', error => (error ? reject(error) : resolve()))
})
}

View File

@ -0,0 +1,51 @@
import React from 'react'
import { extensionAreaHeaderNavItems } from './extensions/extension/extensionAreaHeaderNavItems'
import { extensionAreaRoutes } from './extensions/extension/routes'
import { extensionsAreaHeaderActionButtons } from './extensions/extensionsAreaHeaderActionButtons'
import { extensionsAreaRoutes } from './extensions/routes'
import './SourcegraphWebApp.scss'
import { KEYBOARD_SHORTCUTS } from './keyboardShortcuts/keyboardShortcuts'
import { orgAreaHeaderNavItems } from './org/area/navitems'
import { orgAreaRoutes } from './org/area/routes'
import { repoHeaderActionButtons } from './repo/repoHeaderActionButtons'
import { repoContainerRoutes, repoRevisionContainerRoutes } from './repo/routes'
import { repoSettingsAreaRoutes } from './repo/settings/routes'
import { repoSettingsSideBarGroups } from './repo/settings/sidebaritems'
import { routes } from './routes'
import { siteAdminOverviewComponents } from './site-admin/overview/overviewComponents'
import { siteAdminAreaRoutes } from './site-admin/routes'
import { siteAdminSidebarGroups } from './site-admin/sidebaritems'
import { SourcegraphWebApp } from './SourcegraphWebApp'
import { userAreaHeaderNavItems } from './user/area/navitems'
import { userAreaRoutes } from './user/area/routes'
import { userSettingsAreaRoutes } from './user/settings/routes'
import { userSettingsSideBarItems } from './user/settings/sidebaritems'
// Entry point for the app without enterprise functionality.
// For more info see: https://docs.sourcegraph.com/admin/subscriptions#paid-subscriptions-for-sourcegraph-enterprise
export const OpenSourceWebApp: React.FunctionComponent = () => (
<SourcegraphWebApp
extensionAreaRoutes={extensionAreaRoutes}
extensionAreaHeaderNavItems={extensionAreaHeaderNavItems}
extensionsAreaRoutes={extensionsAreaRoutes}
extensionsAreaHeaderActionButtons={extensionsAreaHeaderActionButtons}
siteAdminAreaRoutes={siteAdminAreaRoutes}
siteAdminSideBarGroups={siteAdminSidebarGroups}
siteAdminOverviewComponents={siteAdminOverviewComponents}
userAreaRoutes={userAreaRoutes}
userAreaHeaderNavItems={userAreaHeaderNavItems}
userSettingsSideBarItems={userSettingsSideBarItems}
userSettingsAreaRoutes={userSettingsAreaRoutes}
orgAreaRoutes={orgAreaRoutes}
orgAreaHeaderNavItems={orgAreaHeaderNavItems}
repoContainerRoutes={repoContainerRoutes}
repoRevisionContainerRoutes={repoRevisionContainerRoutes}
repoHeaderActionButtons={repoHeaderActionButtons}
repoSettingsAreaRoutes={repoSettingsAreaRoutes}
repoSettingsSidebarGroups={repoSettingsSideBarGroups}
routes={routes}
keyboardShortcuts={KEYBOARD_SHORTCUTS}
showBatchChanges={false}
/>
)

View File

@ -4,7 +4,6 @@ import { ApolloProvider } from '@apollo/client'
import { ShortcutProvider } from '@slimsag/react-shortcuts'
import ServerIcon from 'mdi-react/ServerIcon'
import * as React from 'react'
import { hot } from 'react-hot-loader/root'
import { Route } from 'react-router'
import { BrowserRouter } from 'react-router-dom'
import { combineLatest, from, Subscription, fromEvent, of, Subject } from 'rxjs'
@ -259,11 +258,8 @@ const LayoutWithActivation = window.context.sourcegraphDotComMode ? Layout : wit
/**
* The root component.
*
* This is the non-hot-reload component. It is wrapped in `hot(...)` below to make it
* hot-reloadable in development.
*/
class ColdSourcegraphWebApp extends React.Component<SourcegraphWebAppProps, SourcegraphWebAppState> {
export class SourcegraphWebApp extends React.Component<SourcegraphWebAppProps, SourcegraphWebAppState> {
private readonly subscriptions = new Subscription()
private readonly userRepositoriesUpdates = new Subject<void>()
private readonly darkThemeMediaList = window.matchMedia('(prefers-color-scheme: dark)')
@ -671,5 +667,3 @@ class ColdSourcegraphWebApp extends React.Component<SourcegraphWebAppProps, Sour
await extensionHostAPI.setSearchContext(spec)
}
}
export const SourcegraphWebApp = hot(ColdSourcegraphWebApp)

View File

@ -11,14 +11,17 @@ export const withAuthenticatedUser = <P extends object & { authenticatedUser: Au
Component: React.ComponentType<P>
): React.ComponentType<
Pick<P, Exclude<keyof P, 'authenticatedUser'>> & { authenticatedUser: AuthenticatedUser | null }
> => ({ authenticatedUser, ...props }) => {
// If not logged in, redirect to sign in.
if (!authenticatedUser) {
const newUrl = new URL(window.location.href)
newUrl.pathname = '/sign-in'
// Return to the current page after sign up/in.
newUrl.searchParams.set('returnTo', window.location.href)
return <Redirect to={newUrl.pathname + newUrl.search} />
> =>
// It's important to add names to all components to avoid full reload on hot-update.
// https://github.com/pmmmwh/react-refresh-webpack-plugin/blob/main/docs/TROUBLESHOOTING.md#edits-always-lead-to-full-reload
function WithAuthenticatedUser({ authenticatedUser, ...props }) {
// If not logged in, redirect to sign in.
if (!authenticatedUser) {
const newUrl = new URL(window.location.href)
newUrl.pathname = '/sign-in'
// Return to the current page after sign up/in.
newUrl.searchParams.set('returnTo', window.location.href)
return <Redirect to={newUrl.pathname + newUrl.search} />
}
return <Component {...({ ...props, authenticatedUser } as P)} />
}
return <Component {...({ ...props, authenticatedUser } as P)} />
}

View File

@ -0,0 +1,51 @@
import React from 'react'
import '../SourcegraphWebApp.scss'
import '../enterprise.scss'
import { KEYBOARD_SHORTCUTS } from '../keyboardShortcuts/keyboardShortcuts'
import { SourcegraphWebApp } from '../SourcegraphWebApp'
import { enterpriseExtensionAreaHeaderNavItems } from './extensions/extension/extensionAreaHeaderNavItems'
import { enterpriseExtensionAreaRoutes } from './extensions/extension/routes'
import { enterpriseExtensionsAreaHeaderActionButtons } from './extensions/extensionsAreaHeaderActionButtons'
import { enterpriseExtensionsAreaRoutes } from './extensions/routes'
import { enterpriseOrgAreaHeaderNavItems } from './organizations/navitems'
import { enterpriseOrganizationAreaRoutes } from './organizations/routes'
import { enterpriseRepoHeaderActionButtons } from './repo/repoHeaderActionButtons'
import { enterpriseRepoContainerRoutes, enterpriseRepoRevisionContainerRoutes } from './repo/routes'
import { enterpriseRepoSettingsAreaRoutes } from './repo/settings/routes'
import { enterpriseRepoSettingsSidebarGroups } from './repo/settings/sidebaritems'
import { enterpriseRoutes } from './routes'
import { enterpriseSiteAdminOverviewComponents } from './site-admin/overview/overviewComponents'
import { enterpriseSiteAdminAreaRoutes } from './site-admin/routes'
import { enterpriseSiteAdminSidebarGroups } from './site-admin/sidebaritems'
import { enterpriseUserAreaHeaderNavItems } from './user/navitems'
import { enterpriseUserAreaRoutes } from './user/routes'
import { enterpriseUserSettingsAreaRoutes } from './user/settings/routes'
import { enterpriseUserSettingsSideBarItems } from './user/settings/sidebaritems'
export const EnterpriseWebApp: React.FunctionComponent = () => (
<SourcegraphWebApp
extensionAreaRoutes={enterpriseExtensionAreaRoutes}
extensionAreaHeaderNavItems={enterpriseExtensionAreaHeaderNavItems}
extensionsAreaRoutes={enterpriseExtensionsAreaRoutes}
extensionsAreaHeaderActionButtons={enterpriseExtensionsAreaHeaderActionButtons}
siteAdminAreaRoutes={enterpriseSiteAdminAreaRoutes}
siteAdminSideBarGroups={enterpriseSiteAdminSidebarGroups}
siteAdminOverviewComponents={enterpriseSiteAdminOverviewComponents}
userAreaHeaderNavItems={enterpriseUserAreaHeaderNavItems}
userAreaRoutes={enterpriseUserAreaRoutes}
userSettingsSideBarItems={enterpriseUserSettingsSideBarItems}
userSettingsAreaRoutes={enterpriseUserSettingsAreaRoutes}
orgAreaRoutes={enterpriseOrganizationAreaRoutes}
orgAreaHeaderNavItems={enterpriseOrgAreaHeaderNavItems}
repoContainerRoutes={enterpriseRepoContainerRoutes}
repoRevisionContainerRoutes={enterpriseRepoRevisionContainerRoutes}
repoHeaderActionButtons={enterpriseRepoHeaderActionButtons}
repoSettingsAreaRoutes={enterpriseRepoSettingsAreaRoutes}
repoSettingsSidebarGroups={enterpriseRepoSettingsSidebarGroups}
routes={enterpriseRoutes}
keyboardShortcuts={KEYBOARD_SHORTCUTS}
showBatchChanges={window.context.batchChangesEnabled}
/>
)

View File

@ -10,55 +10,10 @@ import '../sentry'
import React from 'react'
import { render } from 'react-dom'
import '../SourcegraphWebApp.scss'
import '../enterprise.scss'
import { KEYBOARD_SHORTCUTS } from '../keyboardShortcuts/keyboardShortcuts'
import { SourcegraphWebApp } from '../SourcegraphWebApp'
import { enterpriseExtensionAreaHeaderNavItems } from './extensions/extension/extensionAreaHeaderNavItems'
import { enterpriseExtensionAreaRoutes } from './extensions/extension/routes'
import { enterpriseExtensionsAreaHeaderActionButtons } from './extensions/extensionsAreaHeaderActionButtons'
import { enterpriseExtensionsAreaRoutes } from './extensions/routes'
import { enterpriseOrgAreaHeaderNavItems } from './organizations/navitems'
import { enterpriseOrganizationAreaRoutes } from './organizations/routes'
import { enterpriseRepoHeaderActionButtons } from './repo/repoHeaderActionButtons'
import { enterpriseRepoContainerRoutes, enterpriseRepoRevisionContainerRoutes } from './repo/routes'
import { enterpriseRepoSettingsAreaRoutes } from './repo/settings/routes'
import { enterpriseRepoSettingsSidebarGroups } from './repo/settings/sidebaritems'
import { enterpriseRoutes } from './routes'
import { enterpriseSiteAdminOverviewComponents } from './site-admin/overview/overviewComponents'
import { enterpriseSiteAdminAreaRoutes } from './site-admin/routes'
import { enterpriseSiteAdminSidebarGroups } from './site-admin/sidebaritems'
import { enterpriseUserAreaHeaderNavItems } from './user/navitems'
import { enterpriseUserAreaRoutes } from './user/routes'
import { enterpriseUserSettingsAreaRoutes } from './user/settings/routes'
import { enterpriseUserSettingsSideBarItems } from './user/settings/sidebaritems'
import { EnterpriseWebApp } from './EnterpriseWebApp'
// It's important to have a root component in a separate file to create a react-refresh boundary and avoid page reload.
// https://github.com/pmmmwh/react-refresh-webpack-plugin/blob/main/docs/TROUBLESHOOTING.md#edits-always-lead-to-full-reload
window.addEventListener('DOMContentLoaded', () => {
render(
<SourcegraphWebApp
extensionAreaRoutes={enterpriseExtensionAreaRoutes}
extensionAreaHeaderNavItems={enterpriseExtensionAreaHeaderNavItems}
extensionsAreaRoutes={enterpriseExtensionsAreaRoutes}
extensionsAreaHeaderActionButtons={enterpriseExtensionsAreaHeaderActionButtons}
siteAdminAreaRoutes={enterpriseSiteAdminAreaRoutes}
siteAdminSideBarGroups={enterpriseSiteAdminSidebarGroups}
siteAdminOverviewComponents={enterpriseSiteAdminOverviewComponents}
userAreaHeaderNavItems={enterpriseUserAreaHeaderNavItems}
userAreaRoutes={enterpriseUserAreaRoutes}
userSettingsSideBarItems={enterpriseUserSettingsSideBarItems}
userSettingsAreaRoutes={enterpriseUserSettingsAreaRoutes}
orgAreaRoutes={enterpriseOrganizationAreaRoutes}
orgAreaHeaderNavItems={enterpriseOrgAreaHeaderNavItems}
repoContainerRoutes={enterpriseRepoContainerRoutes}
repoRevisionContainerRoutes={enterpriseRepoRevisionContainerRoutes}
repoHeaderActionButtons={enterpriseRepoHeaderActionButtons}
repoSettingsAreaRoutes={enterpriseRepoSettingsAreaRoutes}
repoSettingsSidebarGroups={enterpriseRepoSettingsSidebarGroups}
routes={enterpriseRoutes}
keyboardShortcuts={KEYBOARD_SHORTCUTS}
showBatchChanges={window.context.batchChangesEnabled}
/>,
document.querySelector('#root')
)
render(<EnterpriseWebApp />, document.querySelector('#root'))
})

View File

@ -10,53 +10,10 @@ import './sentry'
import React from 'react'
import { render } from 'react-dom'
import { extensionAreaHeaderNavItems } from './extensions/extension/extensionAreaHeaderNavItems'
import { extensionAreaRoutes } from './extensions/extension/routes'
import { extensionsAreaHeaderActionButtons } from './extensions/extensionsAreaHeaderActionButtons'
import { extensionsAreaRoutes } from './extensions/routes'
import './SourcegraphWebApp.scss'
import { KEYBOARD_SHORTCUTS } from './keyboardShortcuts/keyboardShortcuts'
import { orgAreaHeaderNavItems } from './org/area/navitems'
import { orgAreaRoutes } from './org/area/routes'
import { repoHeaderActionButtons } from './repo/repoHeaderActionButtons'
import { repoContainerRoutes, repoRevisionContainerRoutes } from './repo/routes'
import { repoSettingsAreaRoutes } from './repo/settings/routes'
import { repoSettingsSideBarGroups } from './repo/settings/sidebaritems'
import { routes } from './routes'
import { siteAdminOverviewComponents } from './site-admin/overview/overviewComponents'
import { siteAdminAreaRoutes } from './site-admin/routes'
import { siteAdminSidebarGroups } from './site-admin/sidebaritems'
import { SourcegraphWebApp } from './SourcegraphWebApp'
import { userAreaHeaderNavItems } from './user/area/navitems'
import { userAreaRoutes } from './user/area/routes'
import { userSettingsAreaRoutes } from './user/settings/routes'
import { userSettingsSideBarItems } from './user/settings/sidebaritems'
import { OpenSourceWebApp } from './OpenSourceWebApp'
// It's important to have a root component in a separate file to create a react-refresh boundary and avoid page reload.
// https://github.com/pmmmwh/react-refresh-webpack-plugin/blob/main/docs/TROUBLESHOOTING.md#edits-always-lead-to-full-reload
window.addEventListener('DOMContentLoaded', () => {
render(
<SourcegraphWebApp
extensionAreaRoutes={extensionAreaRoutes}
extensionAreaHeaderNavItems={extensionAreaHeaderNavItems}
extensionsAreaRoutes={extensionsAreaRoutes}
extensionsAreaHeaderActionButtons={extensionsAreaHeaderActionButtons}
siteAdminAreaRoutes={siteAdminAreaRoutes}
siteAdminSideBarGroups={siteAdminSidebarGroups}
siteAdminOverviewComponents={siteAdminOverviewComponents}
userAreaRoutes={userAreaRoutes}
userAreaHeaderNavItems={userAreaHeaderNavItems}
userSettingsSideBarItems={userSettingsSideBarItems}
userSettingsAreaRoutes={userSettingsAreaRoutes}
orgAreaRoutes={orgAreaRoutes}
orgAreaHeaderNavItems={orgAreaHeaderNavItems}
repoContainerRoutes={repoContainerRoutes}
repoRevisionContainerRoutes={repoRevisionContainerRoutes}
repoHeaderActionButtons={repoHeaderActionButtons}
repoSettingsAreaRoutes={repoSettingsAreaRoutes}
repoSettingsSidebarGroups={repoSettingsSideBarGroups}
routes={routes}
keyboardShortcuts={KEYBOARD_SHORTCUTS}
showBatchChanges={false}
/>,
document.querySelector('#root')
)
render(<OpenSourceWebApp />, document.querySelector('#root'))
})

View File

@ -2,6 +2,7 @@
const path = require('path')
const ReactRefreshWebpackPlugin = require('@pmmmwh/react-refresh-webpack-plugin')
const CssMinimizerWebpackPlugin = require('css-minimizer-webpack-plugin')
const logger = require('gulplog')
const MiniCssExtractPlugin = require('mini-css-extract-plugin')
@ -19,7 +20,11 @@ logger.info('Using mode', mode)
const isDevelopment = mode === 'development'
const isProduction = mode === 'production'
const devtool = isProduction ? 'source-map' : 'cheap-module-eval-source-map'
const isCI = process.env.CI === 'true'
const isCacheEnabled = isDevelopment && !isCI
const isHotReloadEnabled = isDevelopment && !isCI
const devtool = isProduction ? 'source-map' : 'eval-cheap-module-source-map'
const shouldServeIndexHTML = process.env.WEBPACK_SERVE_INDEX === 'true'
if (shouldServeIndexHTML) {
@ -36,6 +41,9 @@ if (shouldAnalyze) {
}
const rootPath = path.resolve(__dirname, '..', '..')
const hotLoadablePaths = ['branded', 'shared', 'web', 'wildcard'].map(workspace =>
path.resolve(rootPath, 'client', workspace, 'src')
)
const nodeModulesPath = path.resolve(rootPath, 'node_modules')
const monacoEditorPaths = [path.resolve(nodeModulesPath, 'monaco-editor')]
@ -56,11 +64,35 @@ const extensionHostWorker = /main\.worker\.ts$/
const config = {
context: __dirname, // needed when running `gulp webpackDevServer` from the root dir
mode,
stats: {
// Minimize logging in case if Webpack is used along with multiple other services.
// Use `normal` output preset in case of running standalone web server.
preset: shouldServeIndexHTML || isProduction ? 'normal' : 'errors-warnings',
errorDetails: true,
timings: true,
},
infrastructureLogging: {
// Controls webpack-dev-server logging level.
level: 'warn',
},
target: 'browserslist',
// Use cache only in `development` mode to speed up production build.
cache: isCacheEnabled && {
type: 'filesystem',
buildDependencies: {
// Invalidate cache on config change.
config: [
__filename,
path.resolve(__dirname, 'babel.config.js'),
path.resolve(rootPath, 'babel.config.js'),
path.resolve(rootPath, 'postcss.config.js'),
],
},
},
optimization: {
minimize: isProduction,
minimizer: [
new TerserPlugin({
sourceMap: true,
terserOptions: {
compress: {
// Don't inline functions, which causes name collisions with uglify-es:
@ -71,24 +103,19 @@ const config = {
}),
new CssMinimizerWebpackPlugin(),
],
namedModules: false,
...(isDevelopment
? {
removeAvailableModules: false,
removeEmptyChunks: false,
splitChunks: false,
}
: {}),
...(isDevelopment && {
// Running multiple entries on a single page that do not share a runtime chunk from the same compilation is not supported.
// https://github.com/webpack/webpack-dev-server/issues/2792#issuecomment-808328432
runtimeChunk: isHotReloadEnabled ? 'single' : false,
removeAvailableModules: false,
removeEmptyChunks: false,
splitChunks: false,
}),
},
entry: {
// Enterprise vs. OSS builds use different entrypoints. The enterprise entrypoint imports a
// strict superset of the OSS entrypoint.
app: [
'react-hot-loader/patch',
isEnterpriseBuild ? path.join(enterpriseDirectory, 'main.tsx') : path.join(__dirname, 'src', 'main.tsx'),
],
app: isEnterpriseBuild ? path.join(enterpriseDirectory, 'main.tsx') : path.join(__dirname, 'src', 'main.tsx'),
'editor.worker': 'monaco-editor/esm/vs/editor/editor.worker.js',
'json.worker': 'monaco-editor/esm/vs/language/json/json.worker',
},
@ -110,6 +137,11 @@ const config = {
...(shouldServeIndexHTML && webServerEnvironmentVariables),
},
}),
new webpack.ProvidePlugin({
process: 'process/browser',
// Based on the issue: https://github.com/webpack/changelog-v5/issues/10
Buffer: ['buffer', 'Buffer'],
}),
new MiniCssExtractPlugin({
// Do not [hash] for development -- see https://github.com/webpack/webpack-dev-server/issues/377#issuecomment-241258405
filename: mode === 'production' ? 'styles/[name].[contenthash].bundle.css' : 'styles/[name].bundle.css',
@ -130,16 +162,18 @@ const config = {
'suggest',
],
}),
new webpack.IgnorePlugin(/\.flow$/, /.*/),
new WebpackManifestPlugin({
writeToFileEmit: true,
fileName: 'webpack.manifest.json',
// Only output files that are required to run the application
filter: ({ isInitial }) => isInitial,
}),
!shouldServeIndexHTML &&
new WebpackManifestPlugin({
writeToFileEmit: true,
fileName: 'webpack.manifest.json',
// Only output files that are required to run the application
filter: ({ isInitial }) => isInitial,
}),
...(shouldServeIndexHTML ? getHTMLWebpackPlugins() : []),
...(shouldAnalyze ? [new BundleAnalyzerPlugin()] : []),
],
shouldAnalyze && new BundleAnalyzerPlugin(),
isHotReloadEnabled && new webpack.HotModuleReplacementPlugin(),
isHotReloadEnabled && new ReactRefreshWebpackPlugin({ overlay: false }),
].filter(Boolean),
resolve: {
extensions: ['.mjs', '.ts', '.tsx', '.js', '.json'],
mainFields: ['es2015', 'module', 'browser', 'main'],
@ -147,7 +181,6 @@ const config = {
// react-visibility-sensor's main field points to a UMD bundle instead of ESM
// https://github.com/joshwnj/react-visibility-sensor/issues/148
'react-visibility-sensor': path.resolve(rootPath, 'node_modules/react-visibility-sensor/visibility-sensor.js'),
'react-dom': '@hot-loader/react-dom',
},
},
module: {
@ -156,7 +189,7 @@ const config = {
// slow to run on all JavaScript code).
{
test: /\.[jt]sx?$/,
include: path.join(__dirname, 'src'),
include: hotLoadablePaths,
exclude: extensionHostWorker,
use: [
...(isProduction ? ['thread-loader'] : []),
@ -164,23 +197,14 @@ const config = {
loader: 'babel-loader',
options: {
cacheDirectory: true,
plugins: [
'react-hot-loader/babel',
[
'@sourcegraph/babel-plugin-transform-react-hot-loader-wrapper',
{
modulePattern: 'web/src/.*\\.tsx$',
componentNamePattern: '(Page|Area)$',
},
],
],
...(isHotReloadEnabled && { plugins: ['react-refresh/babel'] }),
},
},
],
},
{
test: /\.[jt]sx?$/,
exclude: [path.join(__dirname, 'src'), extensionHostWorker],
exclude: [...hotLoadablePaths, extensionHostWorker],
use: [...(isProduction ? ['thread-loader'] : []), babelLoader],
},
{
@ -214,13 +238,13 @@ const config = {
// TTF rule for monaco-editor
test: /\.ttf$/,
include: monacoEditorPaths,
use: ['file-loader'],
type: 'asset/resource',
},
{
test: extensionHostWorker,
use: [{ loader: 'worker-loader', options: { inline: 'no-fallback' } }, babelLoader],
},
{ test: /\.ya?ml$/, use: ['raw-loader'] },
{ test: /\.ya?ml$/, type: 'asset/source' },
],
},
}

View File

@ -63,6 +63,7 @@
<br>
You need to enable JavaScript to run this app.
</noscript>
{{if .WebpackDevServer}}<script src="{{.Manifest.AppJSRuntimeBundlePath}}"></script>{{end}}
<script src="{{.Manifest.AppJSBundlePath}}"></script>
{{.Injected.BodyBottom}}
</body>

View File

@ -105,12 +105,11 @@
"@octokit/rest": "^16.36.0",
"@peculiar/webcrypto": "^1.1.7",
"@percy/puppeteer": "^1.1.0",
"@pmmmwh/react-refresh-webpack-plugin": "^0.4.3",
"@pmmmwh/react-refresh-webpack-plugin": "0.5.0-rc.1",
"@pollyjs/adapter": "^5.0.0",
"@pollyjs/core": "^5.1.0",
"@pollyjs/persister-fs": "^5.0.0",
"@slack/web-api": "^5.10.0",
"@sourcegraph/babel-plugin-transform-react-hot-loader-wrapper": "^1.1.0",
"@sourcegraph/eslint-config": "^0.25.1",
"@sourcegraph/eslint-plugin-sourcegraph": "1.0.0",
"@sourcegraph/prettierrc": "^3.0.3",
@ -126,10 +125,12 @@
"@storybook/addon-toolbars": "^6.3.0",
"@storybook/addons": "^6.3.0",
"@storybook/api": "^6.3.0",
"@storybook/builder-webpack5": "^6.3.0",
"@storybook/client-api": "^6.3.0",
"@storybook/components": "^6.3.0",
"@storybook/core": "^6.3.0",
"@storybook/core-events": "^6.3.0",
"@storybook/manager-webpack5": "^6.3.2",
"@storybook/react": "^6.3.0",
"@storybook/theming": "^6.3.0",
"@terminus-term/to-string-loader": "^1.1.7-beta.1",
@ -145,7 +146,7 @@
"@types/classnames": "2.2.10",
"@types/command-exists": "1.2.0",
"@types/connect-history-api-fallback": "^1.3.4",
"@types/css-minimizer-webpack-plugin": "^1.1.3",
"@types/css-minimizer-webpack-plugin": "^3.0.1",
"@types/d3-axis": "1.0.12",
"@types/d3-format": "^2.0.0",
"@types/d3-scale": "2.2.0",
@ -167,7 +168,7 @@
"@types/lodash": "4.14.167",
"@types/marked": "2.0.2",
"@types/mime-types": "2.1.0",
"@types/mini-css-extract-plugin": "1.2.2",
"@types/mini-css-extract-plugin": "^2.0.1",
"@types/mocha": "8.2.0",
"@types/mockdate": "2.0.0",
"@types/mz": "2.7.3",
@ -180,7 +181,6 @@
"@types/react-circular-progressbar": "1.0.2",
"@types/react-dom": "16.9.8",
"@types/react-grid-layout": "0.17.2",
"@types/react-hot-loader": "4.1.0",
"@types/react-router": "5.1.10",
"@types/react-router-dom": "5.1.7",
"@types/react-stripe-elements": "6.0.4",
@ -196,15 +196,14 @@
"@types/sinon": "9.0.4",
"@types/socket.io": "2.1.10",
"@types/socket.io-client": "1.4.33",
"@types/speed-measure-webpack-plugin": "^1.3.3",
"@types/terser-webpack-plugin": "^4.2.0",
"@types/speed-measure-webpack-plugin": "^1.3.4",
"@types/terser-webpack-plugin": "^5.0.4",
"@types/testing-library__jest-dom": "^5.9.5",
"@types/textarea-caret": "3.0.0",
"@types/uuid": "8.0.1",
"@types/webpack": "^4.41.25",
"@types/webpack-bundle-analyzer": "^2.9.0",
"@types/webpack-bundle-analyzer": "^4.4.1",
"@types/webpack-dev-server": "^3.11.5",
"@types/webpack-manifest-plugin": "^3.0.4",
"@types/webpack-manifest-plugin": "^3.0.5",
"abort-controller": "^3.0.0",
"autoprefixer": "^10.2.1",
"babel-jest": "^25.5.1",
@ -221,8 +220,8 @@
"command-exists": "^1.2.9",
"connect-history-api-fallback": "^1.6.0",
"cross-env": "^7.0.2",
"css-loader": "^5.2.4",
"css-minimizer-webpack-plugin": "^1.3.0",
"css-loader": "^5.2.6",
"css-minimizer-webpack-plugin": "^3.0.2",
"enzyme": "^3.11.0",
"enzyme-adapter-react-16": "^1.15.4",
"enzyme-to-json": "^3.5.0",
@ -240,7 +239,7 @@
"graphql-schema-linter": "^2.0.1",
"gulp": "^4.0.2",
"html-webpack-harddisk-plugin": "^2.0.0",
"html-webpack-plugin": "^4.5.2",
"html-webpack-plugin": "^5.3.2",
"http-proxy-middleware": "^1.1.2",
"identity-obj-proxy": "^3.0.0",
"jest": "^25.5.4",
@ -252,7 +251,7 @@
"license-checker": "^25.0.1",
"message-port-polyfill": "^0.2.0",
"mime-types": "^2.1.28",
"mini-css-extract-plugin": "^1.3.3",
"mini-css-extract-plugin": "^2.1.0",
"mocha": "^8.3.2",
"mockdate": "^3.0.2",
"monaco-editor-webpack-plugin": "^3.1.0",
@ -262,21 +261,20 @@
"open": "^7.0.4",
"p-retry": "^4.2.0",
"p-timeout": "^4.1.0",
"postcss": "^8.2.4",
"postcss": "^8.3.5",
"postcss-custom-media": "^8.0.0",
"postcss-focus-visible": "^5.0.0",
"postcss-loader": "^4.1.0",
"postcss-loader": "^6.1.1",
"prettier": "^2.2.1",
"process": "^0.11.10",
"puppeteer": "5.5.0",
"puppeteer-firefox": "^0.5.1",
"raw-loader": "^4.0.2",
"react-docgen-typescript-webpack-plugin": "^1.1.0",
"react-hot-loader": "^4.13.0",
"react-refresh": "^0.10.0",
"react-spring": "^9.0.0",
"react-test-renderer": "^16.14.0",
"sass": "^1.32.4",
"sass-loader": "^10.1.0",
"sass-loader": "^12.1.0",
"shelljs": "^0.8.4",
"signale": "^1.4.0",
"simmerjs": "^0.5.6",
@ -284,31 +282,30 @@
"socket.io": "^2.3.0",
"socket.io-client": "^2.3.0",
"speed-measure-webpack-plugin": "^1.5.0",
"storybook-addon-designs": "^5.4.5",
"storybook-addon-designs": "^6.0.0",
"storybook-dark-mode": "^1.0.4",
"string-width": "^4.2.0",
"style-loader": "^2.0.0",
"style-loader": "^3.1.0",
"stylelint": "^13.8.0",
"term-size": "^2.2.0",
"terser-webpack-plugin": "^4.2.3",
"thread-loader": "^3.0.1",
"terser-webpack-plugin": "^5.1.4",
"thread-loader": "^3.0.4",
"ts-morph": "^8.1.0",
"ts-node": "^9.1.1",
"typed-scss-modules": "^4.1.1",
"typescript": "^4.1.3",
"utc-version": "^2.0.2",
"web-ext": "^4.2.0",
"webpack": "^4.44.2",
"webpack-bundle-analyzer": "^4.0.0",
"webpack-cli": "^4.6.0",
"webpack-dev-server": "^3.11.1",
"webpack": "^5.45.1",
"webpack-bundle-analyzer": "^4.4.2",
"webpack-cli": "^4.7.2",
"webpack-dev-server": "4.0.0-beta.3",
"webpack-manifest-plugin": "^3.1.0",
"worker-loader": "^3.0.8",
"yarn-deduplicate": "^3.1.0"
},
"dependencies": {
"@apollo/client": "^3.3.20",
"@hot-loader/react-dom": "^16.14.0",
"@reach/accordion": "^0.10.2",
"@reach/combobox": "^0.15.2",
"@reach/dialog": "^0.11.2",
@ -401,6 +398,7 @@
},
"resolutions": {
"history": "4.5.1",
"cssnano": "4.1.10"
"cssnano": "4.1.10",
"webpack": "5"
}
}

View File

@ -5,6 +5,10 @@ type WebpackManifest struct {
// Webpack bundle that serves as the entrypoint
// for the webapp code.
AppJSBundlePath string `json:"app.js"`
// AppJSRuntimeBundlePath contains the file name of the
// JS runtime bundle which is served only in development environment
// to kick off the main application code.
AppJSRuntimeBundlePath *string `json:"runtime.js"`
// Main CSS bundle, only present in production.
AppCSSBundlePath *string `json:"app.css"`
}

2561
yarn.lock

File diff suppressed because it is too large Load Diff