Revert "use vite for web builds (#58228)" (#59132)

This reverts commit 86180de04a.
This commit is contained in:
Vova Kulikov 2023-12-20 16:23:45 -03:00 committed by GitHub
parent 3fa35fff73
commit 0f8b28b5b9
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
48 changed files with 972 additions and 845 deletions

View File

@ -22,6 +22,7 @@ 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",

View File

@ -0,0 +1,76 @@
import path from 'path'
import * as esbuild from 'esbuild'
// eslint-disable-next-line no-restricted-imports
import { type EditorFeature, featuresArr } from 'monaco-editor-webpack-plugin/out/features'
// eslint-disable-next-line no-restricted-imports
import { type EditorLanguage, languagesArr } from 'monaco-editor-webpack-plugin/out/languages'
import type { MONACO_LANGUAGES_AND_FEATURES } from '../monaco-editor'
import { ROOT_PATH } from '../paths'
const monacoModulePath = (modulePath: string): string =>
require.resolve(path.join('monaco-editor/esm', modulePath), {
paths: [path.join(ROOT_PATH, 'node_modules')],
})
/**
* An esbuild plugin that omits some unneeded features and languages from monaco-editor when
* bundling, to reduce bundle size and speed up builds. Similar to
* https://github.com/microsoft/monaco-editor-webpack-plugin.
*/
export const monacoPlugin = ({
languages,
features,
}: Required<typeof MONACO_LANGUAGES_AND_FEATURES>): esbuild.Plugin => ({
name: 'monaco',
setup: build => {
for (const feature of features) {
if (feature.startsWith('!')) {
throw new Error('negated features (starting with "!") are not supported')
}
}
// Some feature exclusions don't work because their module exports a symbol needed by
// another feature.
const ALWAYS_ENABLED_FEATURES = new Set<EditorFeature>(['snippets'])
const skipLanguageModules = languagesArr
.filter(({ label }) => !languages.includes(label as EditorLanguage))
.flatMap(({ entry }) => entry || [])
const skipFeatureModules = featuresArr
.filter(
({ label }) =>
!features.includes(label as EditorFeature) && !ALWAYS_ENABLED_FEATURES.has(label as EditorFeature)
)
.flatMap(({ entry }) => entry || [])
const skipModulePaths = [...skipLanguageModules, ...skipFeatureModules].map(monacoModulePath)
const filter = new RegExp(`^(${skipModulePaths.join('|')})$`)
// For omitted features and languages, treat their modules as empty files.
//
// TODO(sqs): This is different from how
// https://github.com/microsoft/monaco-editor-webpack-plugin does it. The
// monaco-editor-webpack-plugin approach relies on injecting a different central module
// file, rather than zeroing out each feature/language module. Our approach necessitates the
// ALWAYS_ENABLED_FEATURES hack above.
build.onLoad({ filter }, () => ({ contents: '', loader: 'js' }))
},
})
// TODO(sqs): These Monaco Web Workers could be built as part of the main build if we switch to
// using MonacoEnvironment#getWorker (from #getWorkerUrl), which would then let us use the worker
// plugin (and in Webpack the worker-loader) to load these instead of needing to hardcode them as
// build entrypoints.
export const buildMonaco = async (outdir: string): Promise<esbuild.BuildContext> =>
esbuild.context({
entryPoints: {
'scripts/editor.worker.bundle': 'monaco-editor/esm/vs/editor/editor.worker.js',
'scripts/json.worker.bundle': 'monaco-editor/esm/vs/language/json/json.worker.js',
},
format: 'iife',
target: 'es2021',
bundle: true,
outdir,
})

View File

@ -1,5 +1,6 @@
import type * as esbuild from 'esbuild'
export * from './monacoPlugin'
export * from './packageResolutionPlugin'
export * from './stylePlugin'
export * from './workerPlugin'

View File

@ -1,13 +1,14 @@
load("@aspect_rules_js//js:defs.bzl", "js_library")
load("@aspect_rules_ts//ts:defs.bzl", "ts_config")
load("@bazel_skylib//rules:build_test.bzl", "build_test")
load("@npm//:bundlesize2/package_json.bzl", bundlesize_bin = "bin")
load("@aspect_rules_js//js:defs.bzl", "js_library")
load("@npm//:defs.bzl", "npm_link_all_packages")
load("@npm//:bundlesize2/package_json.bzl", bundlesize_bin = "bin")
load("//client/shared/dev:generate_graphql_operations.bzl", "generate_graphql_operations")
load("//client/shared/dev:tools.bzl", "module_style_typings")
load("//dev:defs.bzl", "npm_package", "sass", "ts_project", "vitest_test")
load("@aspect_rules_esbuild//esbuild:defs.bzl", "esbuild")
load("//dev:esbuild.bzl", "esbuild_web_app")
load("@aspect_rules_ts//ts:defs.bzl", "ts_config")
load("//dev:eslint.bzl", "eslint_config_and_lint_root", "eslint_test_with_types")
load("//dev:vite/vite.bzl", "vite_project", "vite_web_app")
# TODO(bazel): storybook build
# gazelle:exclude **/*.story.{ts,tsx}
@ -1823,7 +1824,6 @@ ts_project(
"//:node_modules/copy-to-clipboard",
"//:node_modules/d3-time-format",
"//:node_modules/date-fns",
"//:node_modules/events",
"//:node_modules/fast-json-stable-stringify",
"//:node_modules/focus-visible",
"//:node_modules/fzf",
@ -2096,11 +2096,22 @@ vitest_test(
],
)
VITE_PROJECT_DEPS = [
# esbuild dev environment -------------------
ESBUILD_CONFIG_DEPS = [
":node_modules/@sourcegraph/build-config",
"//:node_modules/open-color",
"//:node_modules/buffer",
"//:node_modules/lodash",
"//:node_modules/path-browserify",
"//:node_modules/punycode",
"//:node_modules/util",
"//:node_modules/events",
"//:node_modules/monaco-editor",
"//:node_modules/esbuild",
"//:node_modules/react-visibility-sensor", # required for https://github.com/joshwnj/react-visibility-sensor/issues/148 workaround
"//:postcss_config_js",
"//client/web/dev",
# HACKS: bundle-time css import
"//:node_modules/open-color",
]
# Bundled data which was replaced with *.d.ts files at ts compiletime
@ -2111,40 +2122,50 @@ BUNDLE_DATA_DEPS = [
# TODO(bazel): this is already in the main ts_project(srcs). Why also here?
"src/enterprise/codeintel/configuration/schema.json",
":enterprise-yaml",
"//client/web/dev:esbuild-config-production_bundle",
]
vite_project(
esbuild(
name = "bundle",
srcs = BUNDLE_DATA_DEPS + [
":web_lib",
"//:package_json",
],
config = "//client/web/dev:vite-config",
data = BUNDLE_DATA_DEPS,
config = "//client/web/dev:esbuild-config-production_bundle",
entry_points = [
"src/enterprise/main.js",
"src/enterprise/embed/embedMain.js",
],
minify = True,
sourcemap = "linked",
splitting = True,
tags = ["exclusive"],
tsconfig = ":tsconfig",
visibility = ["//visibility:public"],
deps = VITE_PROJECT_DEPS,
visibility = ["//client/web/dist:__subpackages__"],
deps = ESBUILD_CONFIG_DEPS,
)
# Used for integration tests.
vite_web_app(
esbuild_web_app(
name = "app",
srcs = BUNDLE_DATA_DEPS + [
":web_lib",
"//:package_json",
],
config = "//client/web/dev:vite-config",
env = {
"INTEGRATION_TESTS": "true",
config = "//client/web/dev:esbuild-config-production_bundle",
define = {
"process.env.INTEGRATION_TESTS": "true",
},
tsconfig = ":tsconfig",
entry_points = [
"src/enterprise/main.js",
"src/enterprise/embed/embedMain.js",
],
sourcemap = "linked",
visibility = ["//visibility:public"],
deps = VITE_PROJECT_DEPS,
deps = ESBUILD_CONFIG_DEPS,
)
build_test(
name = "vite_test",
name = "esbuild_test",
size = "enormous",
targets = [
":bundle",

View File

@ -29,6 +29,16 @@ To use a public API that doesn't require authentication for most of the function
SOURCEGRAPH_API_URL=https://sourcegraph.com sg start web-standalone
```
### Production server
```sh
sg start web-standalone-prod
```
Web app should be available at `https://${SOURCEGRAPH_HTTPS_DOMAIN}:${SOURCEGRAPH_HTTPS_PORT}`. Build artifacts will be served from `<rootRepoPath>/client/web/dist`.
Note: If you are unable to use the above commands (e.g. you can't install Caddy), you can use `sg run web-standalone-http` instead. This will start a development server using only Node, and will be available at `http://localhost:${SOURCEGRAPH_HTTP_PORT}`.
### API proxy
In both environments, server proxies API requests to `SOURCEGRAPH_API_URL` provided as the `.env` variable.

View File

@ -1,6 +1,6 @@
const path = require('path')
const STATIC_ASSETS_PATH = path.join(process.env.WEB_BUNDLE_PATH || path.join(__dirname, 'dist'), 'assets')
const STATIC_ASSETS_PATH = process.env.WEB_BUNDLE_PATH || path.join(__dirname, 'dist')
const config = {
files: [
@ -21,7 +21,7 @@ const config = {
maxSize: '155kb',
},
{
path: path.join(STATIC_ASSETS_PATH, '*.js'),
path: path.join(STATIC_ASSETS_PATH, 'chunks/chunk-*.js'),
maxSize: '600kb', // 2 monaco chunks are very big
},
/**
@ -39,7 +39,7 @@ const config = {
maxSize: '350kb',
},
{
path: path.join(STATIC_ASSETS_PATH, '*.css'),
path: path.join(STATIC_ASSETS_PATH, 'chunks/chunk-*.css'),
maxSize: '45kb',
},
],

View File

@ -1,4 +1,5 @@
load("@aspect_rules_esbuild//esbuild:defs.bzl", "esbuild")
load("@aspect_rules_js//js:defs.bzl", "js_library")
load("@aspect_rules_ts//ts:defs.bzl", "ts_config")
load("//dev:defs.bzl", "ts_project", "vitest_test")
@ -18,67 +19,104 @@ ts_config(
ts_project(
name = "dev",
srcs = [
"server/apiProxySettings.ts",
"server/devProxyServer.ts",
"esbuild/build.ts",
"esbuild/config.ts",
"esbuild/manifest.ts",
"esbuild/manifestPlugin.ts",
"esbuild/server.ts",
"server/development.server.ts",
"server/production.server.ts",
"utils/constants.ts",
"utils/create-js-context.ts",
"utils/environment-config.ts",
"utils/get-api-proxy-settings.ts",
"utils/get-index-html.ts",
"utils/get-site-config.ts",
"utils/index.ts",
"utils/should-compress-response.ts",
"utils/success-banner.ts",
"utils/webBuildManifest.ts",
"vite/manifestPlugin.ts",
"vite/vite.config.ts",
],
tsconfig = ":tsconfig",
visibility = ["//client/web:__subpackages__"],
deps = [
"//:node_modules/@sentry/esbuild-plugin",
"//:node_modules/@types/compression",
"//:node_modules/@types/connect-history-api-fallback",
"//:node_modules/@types/express",
"//:node_modules/@types/lodash",
"//:node_modules/@types/node",
"//:node_modules/@types/signale",
"//:node_modules/@vitejs/plugin-react",
"//:node_modules/chalk",
"//:node_modules/compression",
"//:node_modules/connect-history-api-fallback",
"//:node_modules/esbuild",
"//:node_modules/express",
"//:node_modules/express-static-gzip",
"//:node_modules/http-proxy-middleware",
"//:node_modules/jsonc-parser",
"//:node_modules/lodash",
"//:node_modules/signale",
"//:node_modules/vite",
"//client/web:node_modules/@sourcegraph/build-config",
"//client/web:web_lib",
],
)
esbuild(
name = "vite-config",
srcs = [
":dev",
js_library(
name = "esbuild-config-production",
srcs = ["esbuild/bazel/esbuild.bazel.production.js"],
deps = [
"//client/build-config:build-config_lib",
"//client/web:node_modules/@sourcegraph/build-config",
"//client/web/dev",
],
entry_point = "vite/vite.config.js",
)
esbuild(
name = "esbuild-config-production_bundle",
srcs = [
":esbuild-config-production",
],
entry_point = "esbuild/bazel/esbuild.bazel.production.js",
external = [
# suppress vite require-resolve-not-external warnings
"lightningcss",
"fsevents",
"pnpapi",
"../../../../postcss.config",
# suppress esbuild require-resolve-not-external warnings
"esbuild",
"@vitejs/plugin-react",
"vite",
"path-browserify",
"monaco-yaml/lib/esm/monaco.contribution",
"monaco-yaml/lib/esm/yaml.worker",
"rxjs/_esm5/internal/OuterSubscriber",
"rxjs/_esm5/internal/util/subscribeToResult",
"rxjs/_esm5/internal/util/subscribeToArray",
"rxjs/_esm5/internal/Observable",
],
format = "cjs",
platform = "node",
sourcemap = False,
splitting = False,
target = "es2022",
visibility = ["//client/web:__subpackages__"],
visibility = ["//client:__subpackages__"],
deps = [
"//:node_modules/@vitejs/plugin-react",
"//:node_modules/esbuild",
"//:node_modules/path-browserify",
"//:node_modules/vite",
"//:postcss_config_js",
],
)
ts_project(
name = "dev_tests",
testonly = True,
srcs = ["esbuild/manifest.test.ts"],
tsconfig = ":tsconfig",
deps = [
":dev",
"//:node_modules/vitest",
],
)
vitest_test(
name = "test",
data = [
":dev_tests",
],
)

View File

@ -0,0 +1,18 @@
// This file is only used by Bazel production builds.
process.env.NODE_ENV = 'production'
const { esbuildBuildOptions } = require('../config.js')
const { ENVIRONMENT_CONFIG } = require('../../utils/environment-config.js')
module.exports = {
...esbuildBuildOptions(ENVIRONMENT_CONFIG),
// Unset configuration properties that are provided by Bazel.
entryPoints: undefined,
bundle: undefined,
outdir: undefined,
sourcemap: undefined,
splitting: undefined,
external: undefined,
}

View File

@ -0,0 +1,52 @@
import { writeFileSync } from 'fs'
import * as esbuild from 'esbuild'
import { buildMonaco } from '@sourcegraph/build-config/src/esbuild/plugins'
import { ENVIRONMENT_CONFIG } from '../utils'
import { esbuildBuildOptions } from './config'
export async function build(): Promise<void> {
const buildOptions = esbuildBuildOptions(ENVIRONMENT_CONFIG)
if (!buildOptions.outdir) {
throw new Error('no outdir')
}
const metafile = process.env.ESBUILD_METAFILE
const options: esbuild.BuildOptions = {
...buildOptions,
metafile: Boolean(metafile),
}
const result = await esbuild.build(options)
if (metafile) {
writeFileSync(metafile, JSON.stringify(result.metafile), 'utf-8')
}
if (!ENVIRONMENT_CONFIG.DEV_WEB_BUILDER_OMIT_SLOW_DEPS) {
const ctx = await buildMonaco(buildOptions.outdir)
await ctx.rebuild()
await ctx.dispose()
}
if (process.env.WATCH) {
const ctx = await esbuild.context(options)
await ctx.watch()
await new Promise(() => {}) // wait forever
}
}
if (require.main === module) {
async function main(args: string[]): Promise<void> {
if (args.length !== 0) {
throw new Error('Usage: (no options)')
}
await build()
}
// eslint-disable-next-line unicorn/prefer-top-level-await
main(process.argv.slice(2)).catch(error => {
console.error(error)
process.exit(1)
})
}

View File

@ -0,0 +1,106 @@
import path from 'path'
import { sentryEsbuildPlugin } from '@sentry/esbuild-plugin'
import type * as esbuild from 'esbuild'
import { ROOT_PATH, STATIC_ASSETS_PATH } from '@sourcegraph/build-config'
import {
stylePlugin,
packageResolutionPlugin,
monacoPlugin,
RXJS_RESOLUTIONS,
buildTimerPlugin,
} from '@sourcegraph/build-config/src/esbuild/plugins'
import { MONACO_LANGUAGES_AND_FEATURES } from '@sourcegraph/build-config/src/monaco-editor'
import type { EnvironmentConfig } from '../utils'
import { manifestPlugin } from './manifestPlugin'
/**
* Creates esbuild build options for the client/web app.
*/
export function esbuildBuildOptions(ENVIRONMENT_CONFIG: EnvironmentConfig): esbuild.BuildOptions {
return {
entryPoints: ENVIRONMENT_CONFIG.CODY_APP
? [path.join(ROOT_PATH, 'client/web/src/enterprise/app/main.tsx')]
: [
path.join(ROOT_PATH, 'client/web/src/enterprise/main.tsx'),
path.join(ROOT_PATH, 'client/web/src/enterprise/embed/embedMain.tsx'),
],
bundle: true,
minify: ENVIRONMENT_CONFIG.NODE_ENV === 'production',
treeShaking: true,
format: 'esm',
logLevel: 'error',
jsx: 'automatic',
jsxDev: ENVIRONMENT_CONFIG.NODE_ENV === 'development',
splitting: !ENVIRONMENT_CONFIG.DEV_WEB_BUILDER_NO_SPLITTING,
chunkNames: 'chunks/chunk-[name]-[hash]',
entryNames: '[name]-[hash]',
outdir: STATIC_ASSETS_PATH,
plugins: [
stylePlugin,
manifestPlugin,
packageResolutionPlugin({
path: require.resolve('path-browserify'),
...RXJS_RESOLUTIONS,
...(ENVIRONMENT_CONFIG.DEV_WEB_BUILDER_OMIT_SLOW_DEPS
? {
// Monaco
'@sourcegraph/shared/src/components/MonacoEditor':
'@sourcegraph/shared/src/components/NoMonacoEditor',
'monaco-editor': '/dev/null',
'monaco-editor/esm/vs/editor/editor.api': '/dev/null',
'monaco-yaml': '/dev/null',
// GraphiQL
'./api/ApiConsole': path.join(ROOT_PATH, 'client/web/src/api/NoApiConsole.tsx'),
'@graphiql/react': '/dev/null',
graphiql: '/dev/null',
// Misc.
recharts: '/dev/null',
// TODO(sqs): force use of same version when developing on opencodegraph because `pnpm link` breaks
'@codemirror/state': path.join(ROOT_PATH, 'node_modules/@codemirror/state'),
'@codemirror/view': path.join(ROOT_PATH, 'node_modules/@codemirror/view'),
react: path.join(ROOT_PATH, 'node_modules/react'),
'react-dom': path.join(ROOT_PATH, 'node_modules/react-dom'),
'react-dom/client': path.join(ROOT_PATH, 'node_modules/react-dom/client'),
}
: null),
}),
ENVIRONMENT_CONFIG.DEV_WEB_BUILDER_OMIT_SLOW_DEPS ? null : monacoPlugin(MONACO_LANGUAGES_AND_FEATURES),
buildTimerPlugin,
ENVIRONMENT_CONFIG.SENTRY_UPLOAD_SOURCE_MAPS
? sentryEsbuildPlugin({
org: ENVIRONMENT_CONFIG.SENTRY_ORGANIZATION,
project: ENVIRONMENT_CONFIG.SENTRY_PROJECT,
authToken: ENVIRONMENT_CONFIG.SENTRY_DOT_COM_AUTH_TOKEN,
silent: true,
release: { name: `frontend@${ENVIRONMENT_CONFIG.VERSION}` },
sourcemaps: { assets: [path.join('dist', '*.map'), path.join('dist', 'chunks', '*.map')] },
})
: null,
].filter((plugin): plugin is esbuild.Plugin => plugin !== null),
define: {
...Object.fromEntries(
Object.entries({ ...ENVIRONMENT_CONFIG, SOURCEGRAPH_API_URL: undefined }).map(([key, value]) => [
`process.env.${key}`,
JSON.stringify(value === undefined ? null : value),
])
),
global: 'window',
},
loader: {
'.yaml': 'text',
'.ttf': 'file',
'.woff2': 'file',
'.png': 'file',
},
target: 'esnext',
sourcemap: true,
}
}

View File

@ -0,0 +1,66 @@
import { expect, describe, test } from 'vitest'
import { type WebBuildManifest, createManifestFromBuildResult } from './manifest'
describe('createManifestFromBuildResult', () => {
test('non-bazel', () =>
expect(
createManifestFromBuildResult(
{ entryPoints: ['src/enterprise/main.tsx', 'src/enterprise/embed/embedMain.tsx'], outdir: 'dist' },
{
'dist/main-AAA.js': {
entryPoint: 'src/enterprise/main.tsx',
cssBundle: 'dist/main-BBB.css',
imports: [],
exports: [],
inputs: {},
bytes: 123,
},
'dist/embedMain-CCC.js': {
entryPoint: 'src/enterprise/embed/embedMain.tsx',
cssBundle: 'dist/embedMain-DDD.css',
imports: [],
exports: [],
inputs: {},
bytes: 123,
},
}
)
).toEqual({
'main.js': 'main-AAA.js',
'main.css': 'main-BBB.css',
'embed.js': 'embedMain-CCC.js',
'embed.css': 'embedMain-DDD.css',
} satisfies WebBuildManifest))
test('bazel', () =>
expect(
createManifestFromBuildResult(
{ entryPoints: ['src/enterprise/main.js', 'src/enterprise/embed/embedMain.js'], outdir: 'dist' },
{
'client/web/bundle/main-AAA.js': {
entryPoint: 'src/enterprise/main.js',
cssBundle: 'client/web/bundle/main-BBB.css',
imports: [],
exports: [],
inputs: {},
bytes: 123,
},
'client/web/bundle/embedMain-CCC.js': {
entryPoint: 'src/enterprise/embed/embedMain.js',
cssBundle: 'client/web/bundle/embedMain-DDD.css',
imports: [],
exports: [],
inputs: {},
bytes: 123,
},
},
true
)
).toEqual({
'main.js': 'main-AAA.js',
'main.css': 'main-BBB.css',
'embed.js': 'embedMain-CCC.js',
'embed.css': 'embedMain-DDD.css',
} satisfies WebBuildManifest))
})

View File

@ -0,0 +1,90 @@
import path from 'path'
import type * as esbuild from 'esbuild'
export interface WebBuildManifest {
/** Main JS bundle. */
'main.js': string
/** Main CSS bundle. */
'main.css': string
/** Embed JS bundle. */
'embed.js': string
/** Embed CSS bundle. */
'embed.css': string
}
export const assetPathPrefix = '/.assets'
export const WEB_BUILD_MANIFEST_FILENAME = 'web.manifest.json'
/**
* Create a web manifest from esbuild build results.
*
*
* @param buildOptions The esbuild options
* @param outputs The esbuild metafile outputs
* @param inBazel Whether the build is running in Bazel
*/
export function createManifestFromBuildResult(
buildOptions: { entryPoints: string[]; outdir: string },
outputs: esbuild.Metafile['outputs'],
inBazel = Boolean(process.env.BAZEL_BINDIR)
): WebBuildManifest {
const outdir = path.relative(process.cwd(), buildOptions.outdir)
const assetPath = (filePath: string): string => {
if (inBazel) {
const BAZEL_PATH_PREFIX = /^client\/web\/(app_)?bundle\//
if (!BAZEL_PATH_PREFIX.test(filePath)) {
throw new Error(`expected filePath to match ${BAZEL_PATH_PREFIX}, got ${filePath}`)
}
return filePath.replace(BAZEL_PATH_PREFIX, '')
}
return path.relative(outdir, filePath)
}
if (buildOptions.entryPoints.length !== 2) {
throw new Error('expected 2 entryPoints (main and embed)')
}
const [mainEntrypoint, embedEntrypoint] = buildOptions.entryPoints.map(filePath =>
path.relative(process.cwd(), filePath)
)
const manifest: Partial<WebBuildManifest> = {}
// Find the entrypoint in the output files
for (const [asset, output] of Object.entries(outputs)) {
if (!output.entryPoint) {
continue
}
if (output.entryPoint.endsWith(mainEntrypoint)) {
manifest['main.js'] = assetPath(asset)
if (output.cssBundle) {
manifest['main.css'] = assetPath(output.cssBundle)
}
} else if (output.entryPoint.endsWith(embedEntrypoint)) {
manifest['embed.js'] = assetPath(asset)
if (output.cssBundle) {
manifest['embed.css'] = assetPath(output.cssBundle)
}
}
}
if (!manifest['main.js']) {
throw new Error('no main.js found in outputs')
}
if (!manifest['main.css']) {
throw new Error('no main.css found in outputs')
}
if (!manifest['embed.js']) {
throw new Error('no embed.js found in outputs')
}
if (!manifest['embed.css']) {
throw new Error('no embed.css found in outputs')
}
return manifest as WebBuildManifest
}

View File

@ -0,0 +1,50 @@
import fs from 'fs'
import path from 'path'
import type * as esbuild from 'esbuild'
import { WEB_BUILD_MANIFEST_FILENAME, createManifestFromBuildResult } from './manifest'
/**
* An esbuild plugin to write a web.manifest.json file.
*/
export const manifestPlugin: esbuild.Plugin = {
name: 'manifest',
setup: build => {
const origMetafile = build.initialOptions.metafile
build.initialOptions.metafile = true
build.onEnd(async result => {
const { entryPoints } = build.initialOptions
const outputs = result?.metafile?.outputs
if (!origMetafile) {
// If we were the only consumers of the metafile, then delete it from the result to
// avoid unexpected behavior from other downstream consumers relying on the metafile
// despite not actually enabling it in the config.
delete result.metafile
}
if (!checkEntryPoints(entryPoints)) {
throw new Error('[manifestPlugin] Unexpected entryPoints format')
}
const { outdir } = build.initialOptions
if (!outdir) {
throw new Error('[manifestPlugin] No outdir found')
}
if (!outputs) {
throw new Error('[manifestPlugin] No outputs found')
}
const manifest = createManifestFromBuildResult({ entryPoints, outdir }, outputs)
const manifestPath = path.join(outdir, WEB_BUILD_MANIFEST_FILENAME)
await fs.promises.writeFile(manifestPath, JSON.stringify(manifest, null, 2))
})
},
}
function checkEntryPoints(entryPoints: esbuild.BuildOptions['entryPoints']): entryPoints is string[] {
return Array.isArray(entryPoints) && typeof entryPoints[0] === 'string'
}

View File

@ -0,0 +1,101 @@
import path from 'path'
import { context as esbuildContext } from 'esbuild'
import express from 'express'
import { createProxyMiddleware } from 'http-proxy-middleware'
import signale from 'signale'
import { STATIC_ASSETS_PATH } from '@sourcegraph/build-config'
import { buildMonaco } from '@sourcegraph/build-config/src/esbuild/monacoPlugin'
import {
DEV_SERVER_LISTEN_ADDR,
DEV_SERVER_PROXY_TARGET_ADDR,
ENVIRONMENT_CONFIG,
HTTPS_WEB_SERVER_URL,
printSuccessBanner,
} from '../utils'
import { esbuildBuildOptions } from './config'
import { assetPathPrefix } from './manifest'
export const esbuildDevelopmentServer = async (
listenAddress: { host: string; port: number },
configureProxy: (app: express.Application) => void
): Promise<void> => {
const start = performance.now()
// One-time build (these files only change when the monaco-editor npm package is changed, which
// is rare enough to ignore here).
if (!ENVIRONMENT_CONFIG.DEV_WEB_BUILDER_OMIT_SLOW_DEPS) {
const ctx = await buildMonaco(STATIC_ASSETS_PATH)
await ctx.rebuild()
await ctx.dispose()
}
const ctx = await esbuildContext(esbuildBuildOptions(ENVIRONMENT_CONFIG))
await ctx.watch()
// Start esbuild's server on a random local port.
const { host: esbuildHost, port: esbuildPort } = await ctx.serve({
host: 'localhost',
servedir: STATIC_ASSETS_PATH,
})
// Start a proxy at :3080. Asset requests (underneath /.assets/) go to esbuild; all other
// requests go to the upstream.
const proxyApp = express()
proxyApp.use(
assetPathPrefix,
createProxyMiddleware({
target: { protocol: 'http:', host: esbuildHost, port: esbuildPort },
pathRewrite: { [`^${assetPathPrefix}`]: '' },
onProxyRes: (_proxyResponse, request, response) => {
// Cache chunks because their filename includes a hash of the content.
const isCacheableChunk = path.basename(request.url).startsWith('chunk-')
if (isCacheableChunk) {
response.setHeader('Cache-Control', 'max-age=3600')
}
},
logLevel: 'error',
})
)
configureProxy(proxyApp)
const proxyServer = proxyApp.listen(listenAddress)
// eslint-disable-next-line @typescript-eslint/return-await
return await new Promise<void>((_resolve, reject) => {
proxyServer.once('listening', () => {
signale.success(`esbuild server is ready after ${Math.round(performance.now() - start)}ms`)
printSuccessBanner(['✱ Sourcegraph is really ready now!', `Click here: ${HTTPS_WEB_SERVER_URL}/search`])
})
proxyServer.once('error', error => reject(error))
})
}
if (require.main === module) {
async function main(args: string[]): Promise<void> {
if (args.length !== 0) {
throw new Error('Usage: (no options)')
}
await esbuildDevelopmentServer(DEV_SERVER_LISTEN_ADDR, app => {
app.use(
'/',
createProxyMiddleware({
target: {
protocol: 'http:',
host: DEV_SERVER_PROXY_TARGET_ADDR.host,
port: DEV_SERVER_PROXY_TARGET_ADDR.port,
},
logLevel: 'error',
})
)
})
}
// eslint-disable-next-line unicorn/prefer-top-level-await
main(process.argv.slice(2)).catch(error => {
console.error(error)
process.exit(1)
})
}

View File

@ -1,56 +0,0 @@
import express from 'express'
import { createProxyMiddleware } from 'http-proxy-middleware'
import signale from 'signale'
import { ENVIRONMENT_CONFIG, HTTPS_WEB_SERVER_URL } from '../utils/environment-config'
import { getIndexHTML, getWebBuildManifest } from '../utils/get-index-html'
import { printSuccessBanner } from '../utils/success-banner'
import { getAPIProxySettings } from './apiProxySettings'
const { SOURCEGRAPH_API_URL, SOURCEGRAPH_HTTP_PORT } = ENVIRONMENT_CONFIG
interface DevelopmentServerInit {
apiURL: string
listenAddress?: { host: string; port: number }
}
async function startDevProxyServer({
apiURL,
listenAddress = { host: '127.0.0.1', port: SOURCEGRAPH_HTTP_PORT },
}: DevelopmentServerInit): Promise<void> {
const { proxyRoutes, ...proxyMiddlewareOptions } = getAPIProxySettings({
apiURL,
getLocalIndexHTML(jsContextScript) {
return getIndexHTML({ manifest: getWebBuildManifest(), jsContextScript })
},
})
const proxyServer = express()
proxyServer.use(createProxyMiddleware(proxyRoutes, proxyMiddlewareOptions))
return new Promise<void>((_resolve, reject) => {
proxyServer
.listen(listenAddress)
.once('listening', () => {
printSuccessBanner(['✱ Sourcegraph is really ready now!', `Click here: ${HTTPS_WEB_SERVER_URL}`])
})
.once('error', error => reject(error))
})
}
if (require.main === module) {
signale.start('Starting dev server.', ENVIRONMENT_CONFIG)
if (!SOURCEGRAPH_API_URL) {
throw new Error(
'development.server.ts only supports *web-standalone* usage (must set SOURCEGRAPH_API_URL env var)'
)
}
startDevProxyServer({
apiURL: SOURCEGRAPH_API_URL,
}).catch(error => {
signale.error(error)
process.exit(1)
})
}

View File

@ -0,0 +1,41 @@
import { createProxyMiddleware } from 'http-proxy-middleware'
import signale from 'signale'
import { esbuildDevelopmentServer } from '../esbuild/server'
import { ENVIRONMENT_CONFIG, getAPIProxySettings, getIndexHTML, getWebBuildManifest } from '../utils'
const { SOURCEGRAPH_API_URL, SOURCEGRAPH_HTTP_PORT } = ENVIRONMENT_CONFIG
interface DevelopmentServerInit {
apiURL: string
}
async function startDevelopmentServer(): Promise<void> {
signale.start('Starting dev server.', ENVIRONMENT_CONFIG)
if (!SOURCEGRAPH_API_URL) {
throw new Error('development.server.ts only supports *web-standalone* usage')
}
await startEsbuildDevelopmentServer({
apiURL: SOURCEGRAPH_API_URL,
})
}
async function startEsbuildDevelopmentServer({ apiURL }: DevelopmentServerInit): Promise<void> {
const { proxyRoutes, ...proxyMiddlewareOptions } = getAPIProxySettings({
apiURL,
getLocalIndexHTML(jsContextScript) {
return getIndexHTML({ manifestFile: getWebBuildManifest(), jsContextScript })
},
})
await esbuildDevelopmentServer({ host: '0.0.0.0', port: SOURCEGRAPH_HTTP_PORT }, app => {
app.use(createProxyMiddleware(proxyRoutes, proxyMiddlewareOptions))
app.get(/.*/, (_request, response) => {
response.send(getIndexHTML({ manifestFile: getWebBuildManifest() }))
})
})
}
startDevelopmentServer().catch(error => signale.error(error))

View File

@ -0,0 +1,64 @@
import chalk from 'chalk'
import historyApiFallback from 'connect-history-api-fallback'
import express, { type RequestHandler } from 'express'
import expressStaticGzip from 'express-static-gzip'
import { createProxyMiddleware } from 'http-proxy-middleware'
import signale from 'signale'
import {
getAPIProxySettings,
ENVIRONMENT_CONFIG,
HTTP_WEB_SERVER_URL,
HTTPS_WEB_SERVER_URL,
getWebBuildManifest,
STATIC_INDEX_PATH,
getIndexHTML,
} from '../utils'
const { SOURCEGRAPH_API_URL, SOURCEGRAPH_HTTP_PORT, STATIC_ASSETS_PATH } = ENVIRONMENT_CONFIG
function startProductionServer(): void {
if (!SOURCEGRAPH_API_URL) {
throw new Error('production.server.ts only supports *web-standalone* usage')
}
signale.await('Starting production server', ENVIRONMENT_CONFIG)
const app = express()
// Serve index.html in place of any 404 responses.
app.use(historyApiFallback() as RequestHandler)
// Serve build artifacts.
app.use(
'/.assets',
expressStaticGzip(STATIC_ASSETS_PATH, {
enableBrotli: true,
orderPreference: ['br', 'gz'],
index: false,
})
)
const { proxyRoutes, ...proxyConfig } = getAPIProxySettings({
apiURL: SOURCEGRAPH_API_URL,
...(ENVIRONMENT_CONFIG.WEB_BUILDER_SERVE_INDEX && {
getLocalIndexHTML(jsContextScript) {
const manifestFile = getWebBuildManifest()
return getIndexHTML({ manifestFile, jsContextScript })
},
}),
})
// Proxy API requests to the `process.env.SOURCEGRAPH_API_URL`.
app.use(proxyRoutes, createProxyMiddleware(proxyConfig))
// Redirect remaining routes to index.html
app.get('/*', (_request, response) => response.sendFile(STATIC_INDEX_PATH))
app.listen(SOURCEGRAPH_HTTP_PORT, () => {
signale.info(`Production HTTP server is ready at ${chalk.blue.bold(HTTP_WEB_SERVER_URL)}`)
signale.success(`Production HTTPS server is ready at ${chalk.blue.bold(HTTPS_WEB_SERVER_URL)}`)
})
}
startProductionServer()

View File

@ -3,6 +3,6 @@
"compilerOptions": {
"module": "commonjs",
},
"include": ["vite", "server", "utils", "../src/jscontext.ts", "../src/graphql-operations.ts"],
"include": ["esbuild", "server", "utils", "../src/jscontext.ts", "../src/graphql-operations.ts"],
"exclude": ["vitest.config.ts"],
}

View File

@ -2,7 +2,6 @@ import path from 'path'
import { ROOT_PATH } from '@sourcegraph/build-config'
export const DEV_SERVER_LISTEN_ADDR = { host: '127.0.0.1', port: 3080 } as const
export const DEV_SERVER_PROXY_TARGET_ADDR = { host: '127.0.0.1', port: 3081 } as const
export const DEFAULT_SITE_CONFIG_PATH = path.resolve(ROOT_PATH, '../dev-private/enterprise/dev/site-config.json')
export const assetPathPrefix = '/.assets'
export const WEB_BUILD_MANIFEST_FILENAME = 'vite-manifest.json'

View File

@ -3,20 +3,19 @@ import * as zlib from 'zlib'
import { type Options, responseInterceptor } from 'http-proxy-middleware'
import { ENVIRONMENT_CONFIG, HTTPS_WEB_SERVER_URL } from '../utils/environment-config'
import { STREAMING_ENDPOINTS } from '../utils/should-compress-response'
import { ENVIRONMENT_CONFIG, HTTPS_WEB_SERVER_URL } from './environment-config'
import { STREAMING_ENDPOINTS } from './should-compress-response'
// One of the API routes: "/-/sign-in".
const PROXY_ROUTES = ['/.api', '/search/stream', '/-', '/.auth']
interface GetAPIProxySettingsOptions {
apiURL: string
/**
* Use the remote `window.context` as the basis for our local `window.context`, so that most
* parts of the remote site's config are applied here.
* If provided, the server will proxy requests to index.html
* and inject the `window.context` defined there into the local template.
*/
getLocalIndexHTML: (jsContextScript?: string) => string
getLocalIndexHTML?: (jsContextScript?: string) => string
}
interface ProxySettings extends Options {
@ -27,8 +26,8 @@ export function getAPIProxySettings(options: GetAPIProxySettingsOptions): ProxyS
const { apiURL, getLocalIndexHTML } = options
return {
// Proxy '' (for index.html).
proxyRoutes: [...PROXY_ROUTES, ''],
// Enable index.html proxy if `getLocalIndexHTML` is provided.
proxyRoutes: [...PROXY_ROUTES, ...(getLocalIndexHTML ? [''] : [])],
target: apiURL,
// Do not SSL certificate.
secure: false,
@ -53,6 +52,7 @@ export function getAPIProxySettings(options: GetAPIProxySettingsOptions): ProxyS
// Extract remote `window.context` from the HTML response and inject it into
// the index.html generated by `getLocalIndexHTML`.
if (
getLocalIndexHTML &&
// router.go is not up to date with client routes and still serves index.html with 404
(proxyRes.statusCode === 200 || proxyRes.statusCode === 404) &&
proxyRes.headers['content-type'] &&
@ -76,7 +76,7 @@ export function getAPIProxySettings(options: GetAPIProxySettingsOptions): ProxyS
// 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: 'debug',
logLevel: 'silent',
onProxyReqWs: (_proxyRequest, _request, socket) =>
socket.on('error', error => console.error('WebSocket proxy error:', error)),
}
@ -142,7 +142,6 @@ function conditionalResponseInterceptor(
res.setHeader('content-type', 'text/event-stream')
const _proxyRes = decompress(proxyRes, proxyRes.headers['content-encoding'])
// eslint-disable-next-line @typescript-eslint/no-explicit-any
_proxyRes.on('data', (chunk: any) => res.write(chunk))
_proxyRes.on('end', () => {
res.end()

View File

@ -2,11 +2,9 @@ import { readFileSync } from 'fs'
import path from 'path'
import type { SourcegraphContext } from '../../src/jscontext'
import { assetPathPrefix, WEB_BUILD_MANIFEST_FILENAME, type WebBuildManifest } from '../esbuild/manifest'
import { WEB_BUILD_MANIFEST_FILENAME, assetPathPrefix } from './constants'
import { createJsContext } from './create-js-context'
import { ENVIRONMENT_CONFIG, HTTPS_WEB_SERVER_URL } from './environment-config'
import type { WebBuildManifest } from './webBuildManifest'
import { createJsContext, ENVIRONMENT_CONFIG, HTTPS_WEB_SERVER_URL } from '.'
const { STATIC_ASSETS_PATH } = ENVIRONMENT_CONFIG
@ -17,7 +15,7 @@ export const getWebBuildManifest = (): WebBuildManifest =>
JSON.parse(readFileSync(WEB_BUILD_MANIFEST_PATH, 'utf-8')) as WebBuildManifest
interface GetHTMLPageOptions {
manifest: WebBuildManifest
manifestFile: WebBuildManifest
/**
* Used to inject dummy `window.context` in integration tests.
*/
@ -35,10 +33,11 @@ interface GetHTMLPageOptions {
* Note: This page should be kept as close as possible to `app.html` to avoid any inconsistencies
* between our development server and the actual production server.
*/
export function getIndexHTML({ manifest, jsContext, jsContextScript }: GetHTMLPageOptions): string {
if (!manifest.assets['src/enterprise/main']) {
throw new Error('entrypoint asset not found')
}
export function getIndexHTML(options: GetHTMLPageOptions): string {
const { manifestFile, jsContext, jsContextScript } = options
const { 'main.js': mainJS, 'main.css': mainCSS } = manifestFile
return `
<!DOCTYPE html>
<html lang="en">
@ -48,19 +47,14 @@ export function getIndexHTML({ manifest, jsContext, jsContextScript }: GetHTMLPa
<meta name="viewport" content="width=device-width, viewport-fit=cover" />
<meta name="referrer" content="origin-when-cross-origin"/>
<meta name="color-scheme" content="light dark"/>
<link rel="stylesheet" href="${assetPathPrefix}/${mainCSS}">
${
ENVIRONMENT_CONFIG.SOURCEGRAPHDOTCOM_MODE
? '<script src="https://js.sentry-cdn.com/ae2f74442b154faf90b5ff0f7cd1c618.min.js" crossorigin="anonymous"></script>'
: ''
}
${
manifest.assets['src/enterprise/main']?.css
? `<link rel="stylesheet" href="${assetPathPrefix}/${manifest.assets['src/enterprise/main']?.css}">`
: ''
}
</head>
<body>
${manifest.devInjectHTML ?? ''}
<div id="root"></div>
<script>
${
@ -74,7 +68,7 @@ export function getIndexHTML({ manifest, jsContext, jsContextScript }: GetHTMLPa
}
</script>
<script src="${assetPathPrefix}/${manifest.assets['src/enterprise/main'].js}" type="module"></script>
<script src="${assetPathPrefix}/${mainJS}" type="module"></script>
</body>
</html>
`

View File

@ -0,0 +1,7 @@
export * from './constants'
export * from './create-js-context'
export * from './environment-config'
export * from './get-api-proxy-settings'
export * from './get-index-html'
export * from './should-compress-response'
export * from './success-banner'

View File

@ -1,19 +0,0 @@
type Entry = 'src/enterprise/main' | 'src/enterprise/embed/embedMain' | 'src/enterprise/app/main'
export function isEntry(value: string): value is Entry {
return ['src/enterprise/main', 'src/enterprise/embed/embedMain', 'src/enterprise/app/main'].includes(value)
}
export interface WebBuildManifest {
/** Base URL for asset paths. */
url?: string
/**
* A map of entrypoint (such as "src/enterprise/main" with no extension) to its JavaScript and
* CSS assets.
*/
assets: Partial<Record<Entry, { js: string; css?: string }>>
/** Additional HTML <script> tags to inject in dev mode. */
devInjectHTML?: string
}

View File

@ -1,159 +0,0 @@
import { writeFileSync } from 'node:fs'
import path from 'node:path'
import os from 'os'
import { Plugin, normalizePath } from 'vite'
import { WebBuildManifest, isEntry } from '../utils/webBuildManifest'
export interface ManifestPluginConfig {
fileName: string
}
function createSimplifyPath(root: string, base: string): (path: string) => string {
return path => {
path = normalizePath(path)
if (root !== '/' && path.startsWith(root)) {
path = path.slice(root.length)
}
if (path.startsWith(base)) {
path = path.slice(base.length)
}
if (path[0] === '/') {
path = path.slice(1)
}
return path
}
}
/**
* A Vite plugin that writes a JSON manifest with the JavaScript and CSS output filenames for each
* entrypoint. This JSON manifest is read by our backend to inject the right scripts and styles into
* the page.
*/
export function manifestPlugin(pluginConfig: ManifestPluginConfig): Plugin {
let root: string | undefined
return {
name: 'manifest',
enforce: 'post',
configResolved(config): void {
root = config.root
},
// Run in dev mode.
configureServer({ config, httpServer }): void {
httpServer?.once('listening', () => {
// Resolve URL.
const { root: _root, base } = config
const root = normalizePath(_root)
const protocol = config.server.https ? 'https' : 'http'
const host = resolveHost(config.server.host)
const port = config.server.port
const url = `${protocol}://${host}:${port}${base}`
config.server.origin = `${protocol}://${host}:${port}`
// Resolve inputs.
const simplifyPath = createSimplifyPath(root, base)
const inputOptions = config.build.rollupOptions?.input ?? {}
const inputs =
typeof inputOptions === 'string'
? { [inputOptions]: inputOptions }
: Array.isArray(inputOptions)
? Object.fromEntries(inputOptions.map(path => [path, path]))
: inputOptions
const manifest: WebBuildManifest = {
url: url,
assets: {},
devInjectHTML: `
<script type="module">
import RefreshRuntime from "${url}@react-refresh"
RefreshRuntime.injectIntoGlobalHook(window)
window.$RefreshReg$ = () => {}
window.$RefreshSig$ = () => (type) => type
window.__vite_plugin_react_preamble_installed__ = true
</script>
<script type="module" src="${url}@vite/client"></script>`,
}
for (const [entryAlias, entryPath] of Object.entries(inputs)) {
const relativeEntryAlias = noExt(normalizePath(path.relative(root, entryAlias)))
if (isEntry(relativeEntryAlias)) {
manifest.assets[relativeEntryAlias] = {
js: simplifyPath(entryPath),
}
}
}
const outputDir = path.resolve(config.root, config.build.outDir)
writeFileSync(path.resolve(outputDir, pluginConfig.fileName), JSON.stringify(manifest, null, 2))
})
},
// Run when generating the production bundle.
generateBundle(_options, bundle): void {
if (root === undefined) {
throw new Error('no config')
}
const manifest: WebBuildManifest = { assets: {} }
for (const chunk of Object.values(bundle)) {
if (chunk.type === 'chunk' && chunk.isEntry && chunk.facadeModuleId) {
let entryAlias = noExt(normalizePath(path.relative(root, chunk.facadeModuleId)))
const css = chunk.viteMetadata ? Array.from(chunk.viteMetadata?.importedCss.values()) : []
if (css.length >= 2) {
throw new Error('multiple CSS asset files not supported')
}
if (isEntry(entryAlias)) {
manifest.assets[entryAlias] = {
js: chunk.fileName,
css: css.length === 1 ? css[0] : undefined,
}
}
}
}
this.emitFile({ fileName: pluginConfig.fileName, type: 'asset', source: JSON.stringify(manifest, null, 2) })
},
}
}
function noExt(path: string): string {
return path.replace(/\.(tsx|js)$/, '')
}
/**
* Resolve host if is passed as `true`
*
* Copied from https://github.com/vitejs/vite/blob/d4dcdd1ffaea79ecf8a9fc78cdbe311f0d801fb5/packages/vite/src/node/logger.ts#L197
*/
function resolveHost(host?: string | boolean): string {
if (!host) return 'localhost'
if (host === true) {
const nInterface = Object.values(os.networkInterfaces())
.flatMap(nInterface => nInterface ?? [])
.filter(
detail =>
detail &&
detail.address &&
// Node < v18
((typeof detail.family === 'string' && detail.family === 'IPv4') ||
// Node >= v18
(typeof detail.family === 'number' && (detail as any).family === 4))
)
.filter(detail => {
return detail.address !== '127.0.0.1'
})[0]
if (!nInterface) return 'localhost'
return nInterface.address
}
return host
}

View File

@ -1,128 +0,0 @@
import path from 'path'
import react from '@vitejs/plugin-react'
import { UserConfig, defineConfig, mergeConfig } from 'vite'
import { ENVIRONMENT_CONFIG } from '../utils/environment-config'
import { manifestPlugin } from './manifestPlugin'
/** Whether we're running in Bazel. */
const BAZEL = !!process.env.BAZEL_BINDIR
const repoRoot = BAZEL ? process.cwd() : path.join(__dirname, '../../../..')
const clientWebRoot = path.join(repoRoot, 'client/web')
export default defineConfig(() => {
let config: UserConfig = {
plugins: [react(), manifestPlugin({ fileName: 'vite-manifest.json' })],
build: {
rollupOptions: {
input: ENVIRONMENT_CONFIG.CODY_APP
? ['src/enterprise/app/main.tsx']
: ['src/enterprise/main.tsx', 'src/enterprise/embed/embedMain.tsx']
.map(BAZEL ? toJSExtension : String)
.map(p => path.join(clientWebRoot, p)),
},
sourcemap: true,
// modulepreload is supported widely enough now (https://caniuse.com/link-rel-modulepreload)
// and is only relevant for local dev.
modulePreload: { polyfill: false },
emptyOutDir: false, // client/web/dist has static assets checked in
},
base: '/.assets',
root: clientWebRoot,
publicDir: 'dist',
assetsInclude: ['**/*.yaml'],
define: {
...Object.fromEntries(
Object.entries({ ...ENVIRONMENT_CONFIG, SOURCEGRAPH_API_URL: undefined }).map(([key, value]) => [
`process.env.${key}`,
JSON.stringify(value === undefined ? null : value),
])
),
},
optimizeDeps: {
exclude: [
// Without addings this Vite throws an error
'linguist-languages',
],
},
resolve: {
alias: {
path: require.resolve('path-browserify'),
},
mainFields: ['browser', 'module', 'main'],
},
css: {
devSourcemap: true,
preprocessorOptions: {
scss: {
includePaths: [
// Our scss files and scss files in client/* often import global styles via @import 'wildcard/src/...'
// Adding '..' as load path causes scss to look for these imports in the client folder.
// (without it scss @import paths are always relative to the importing file)
path.join(clientWebRoot, '..'),
],
},
},
modules: {
localsConvention: 'camelCaseOnly',
},
},
}
if (BAZEL) {
// TODO(sqs): dedupe with client/web-sveltekit
// Merge settings necessary to make the build work with bazel
config = mergeConfig(config, {
resolve: {
alias: [
// When using Bazel, @sourcegraph/* dependencies will refer to the built packages.
// These do not contain the source *.module.scss files but still contain import statements
// that reference *.scss files. Processing them with vite throws an error unless we
// update the imports to reference the corresponding *.css files instead.
// Additionally our own source files might reference *.module.scss files, which we also want
// to rewrite.
{
find: /^(.+)\.module\.scss$/,
replacement: '$1.module.css',
customResolver(source, importer, options) {
// The this.resolve(...) part is taken from the @rollup/plugin-alias implementation. Without
// it it appears the bundler tries to resolve relative module IDs to the current working
// directory.
return source.includes('@sourcegraph') || importer?.includes('@sourcegraph/')
? this.resolve(source, importer, { skipSelf: true, ...options }).then(
resolved => resolved || { id: source }
)
: null
},
},
// Assume all other *.scss files have been built.
{
find: /^(.+)\.scss(\?.*)?$/,
replacement: '$1.css$2',
},
],
},
ssr: {
// By default vite treats dependencies that are links to other packages in the monorepo as source code
// and processes them as well.
// In a bazel sandbox however all @sourcegraph/* dependencies are built packages and thus not processed
// by vite without this additional setting.
// We have to process those files to apply certain "fixes", such as aliases defined in svelte.config.js.
noExternal: [/@sourcegraph\/.*/],
},
} satisfies UserConfig)
}
return config
})
function toJSExtension(path: string): string {
return path.replace(/\.ts$/, '.js').replace(/\.tsx$/, '.js')
}

View File

@ -17,17 +17,15 @@ import (
//
//go:embed all:*
var assetsFS embed.FS
var afs fs.FS = assetsFS
var (
afs fs.FS = assetsFS
assetsHTTPFS http.FileSystem
)
var Assets http.FileSystem
var (
webBuildManifestOnce sync.Once
assetsOnce sync.Once
webBuildManifest *assets.WebBuildManifest
webBuildManifestErr error
assetsOnce sync.Once
)
func init() {
@ -39,7 +37,7 @@ type Provider struct{}
func (p Provider) LoadWebBuildManifest() (*assets.WebBuildManifest, error) {
webBuildManifestOnce.Do(func() {
f, err := afs.Open("vite-manifest.json")
f, err := afs.Open("web.manifest.json")
if err != nil {
webBuildManifestErr = errors.Wrap(err, "read manifest file")
return
@ -66,7 +64,7 @@ func (p Provider) Assets() http.FileSystem {
// it's already containing other files known to Bazel. So instead we put those into the dist folder.
// If we do detect a dist folder when running this code, we immediately substitute the root to that dist folder.
//
// Therefore, this code works with both the traditional build approach and when built with Bazel.
// Therefore, this code works with both the traditionnal build approach and when built with Bazel.
if _, err := assetsFS.ReadDir("dist"); err == nil {
var err error
afs, err = fs.Sub(assetsFS, "dist")
@ -74,7 +72,8 @@ func (p Provider) Assets() http.FileSystem {
panic("incorrect embed")
}
}
assetsHTTPFS = http.FS(afs)
Assets = http.FS(afs)
})
return assetsHTTPFS
return Assets
}

View File

@ -16,10 +16,11 @@
"test:regression:onboarding": "pnpm task:mocha ./src/regression/onboarding.test.ts",
"test:regression:search": "pnpm task:mocha ./src/regression/search.test.ts",
"storybook": "STORIES_GLOB='client/web/src/**/*.story.tsx' pnpm --filter @sourcegraph/storybook run start",
"serve:dev": "ts-node-transpile-only --project ./dev/tsconfig.json ./dev/server/devProxyServer.ts",
"serve:dev": "ts-node-transpile-only --project ./dev/tsconfig.json ./dev/server/development.server.ts",
"serve:prod": "ts-node-transpile-only --project ./dev/tsconfig.json ./dev/server/production.server.ts",
"generate": "pnpm -w run generate",
"dev": "ts-node -T ../../node_modules/vite/bin/vite.js dev --config vite.config.ts",
"build": "pnpm run generate && ts-node -T ../../node_modules/vite/bin/vite.js build --config vite.config.ts",
"dev": "ts-node -T dev/esbuild/server.ts",
"build": "pnpm run generate && ts-node -T dev/esbuild/build.ts",
"watch": "WATCH=1 pnpm run --silent build",
"lint": "pnpm lint:js && pnpm:lint:css",
"lint:js": "NODE_OPTIONS=\"--max_old_space_size=16192\" eslint --cache '**/*.[tj]s?(x)'",

View File

@ -1,9 +1,9 @@
.dialog {
top: 3rem;
transform: translate(-50%, 0);
max-height: calc(100vh - 6rem);
width: 48rem;
max-width: 95%;
overflow: hidden;
width: auto;
display: flex;
flex-direction: column;
}

View File

@ -2,10 +2,6 @@ import React, { useCallback, useMemo, useState } from 'react'
import classNames from 'classnames'
import { getYear, parseISO } from 'date-fns'
// for polyfill
import 'events'
import {
Area,
ComposedChart,

View File

@ -37,3 +37,9 @@ window.addEventListener('DOMContentLoaded', async () => {
logger.error('Failed to initialize the app shell', error)
}
})
if (process.env.NODE_ENV === 'development') {
new EventSource('/.assets/esbuild').addEventListener('change', () => {
location.reload()
})
}

View File

@ -70,7 +70,7 @@ export const createWebIntegrationTestContext = async ({
.intercept((request, response) => {
response.type('text/html').send(
getIndexHTML({
manifest: getWebBuildManifest(),
manifestFile: getWebBuildManifest(),
jsContext: { ...jsContext, ...customContext },
})
)

View File

@ -2,13 +2,7 @@
// Don't remove the empty lines between these imports.
import './initZones'
/**
* The @opentelemetry/context-zone import enables zone.js which patches global web API
* modules. You can find a list of the patched modules here:
* https://github.com/angular/angular/blob/main/packages/zone.js/MODULE.md
*/
import { ZoneContextManager } from '@opentelemetry/context-zone'
import type { ZoneContextManager } from '@opentelemetry/context-zone'
import { OTLPTraceExporter } from '@opentelemetry/exporter-trace-otlp-http'
import { type InstrumentationOption, registerInstrumentations } from '@opentelemetry/instrumentation'
import { FetchInstrumentation } from '@opentelemetry/instrumentation-fetch'
@ -60,6 +54,17 @@ export function initOpenTelemetry(): void {
provider.addSpanProcessor(new BatchSpanProcessor(consoleExporter))
}
/**
* This import enables zone.js which patches global web API modules.
* You can find a list of the patched modules here:
* https://github.com/angular/angular/blob/main/packages/zone.js/MODULE.md
*
* It's added with the `require` statement to avoid polluting stack traces in
* the development environment when OpenTelemetry is disabled.
*/
// eslint-disable-next-line @typescript-eslint/no-require-imports, @typescript-eslint/no-var-requires
const ZoneContextManager = require('@opentelemetry/context-zone').ZoneContextManager
provider.register({
contextManager: new ZoneContextManager() as ZoneContextManager,
})

View File

@ -21,7 +21,7 @@
.logo {
display: flex;
height: 100%;
margin: 0 !important;
margin: 0;
}
.divider {

View File

@ -361,36 +361,14 @@ declare global {
}
}
/* eslint-disable @typescript-eslint/ban-ts-comment */
// Manually configure the MonacoEnvironment for the Monaco editor.
if (!window.MonacoEnvironment) {
window.MonacoEnvironment = {
// @ts-ignore
async getWorker(_, label) {
if (process.env.NODE_ENV === 'development') {
// In dev mode, we need to use a blob URL and a module worker because (1) the worker
// is loaded cross-origin and (2) the worker is not bundled and only module workers
// support `import` statements.
const workerModule =
label === 'json'
? new URL('monaco-editor/esm/vs/language/json/json.worker.js', import.meta.url)
: new URL('monaco-editor/esm/vs/editor/editor.worker.js', import.meta.url)
const source = `import ${JSON.stringify(workerModule.toString())}`
const workerBlobUrl = URL.createObjectURL(new Blob([source], { type: 'text/javascript' }))
return new Worker(workerBlobUrl, { type: 'module' })
}
let worker: any
getWorkerUrl(_moduleId: string, label: string): string {
if (label === 'json') {
// @ts-ignore
worker = await import('monaco-editor/esm/vs/language/json/json.worker?worker')
} else {
// @ts-ignore
worker = await import('monaco-editor/esm/vs/editor/editor.worker?worker')
return window.context.assetsRoot + '/scripts/json.worker.bundle.js'
}
// eslint-disable-next-line @typescript-eslint/no-unsafe-call, @typescript-eslint/no-unsafe-member-access
return new worker.default()
return window.context.assetsRoot + '/scripts/editor.worker.bundle.js'
},
}
}

View File

@ -51,7 +51,6 @@
"include": ["src", "scripts", "__mocks__", "./src/**/*.json"],
"exclude": [
"vitest.config.ts",
"vite.config.ts",
"dev",
"../../node_modules",
"./node_modules",

View File

@ -1,3 +0,0 @@
import viteConfig from './dev/vite/vite.config'
export default viteConfig

View File

@ -28,7 +28,7 @@
{{end}}
{{end}}
<title>{{.Title}}</title>
{{if .Manifest.Assets.Main.CSS}}<link rel="stylesheet" href="{{assetURL (printf "%s%s" .Manifest.URL .Manifest.Assets.Main.CSS)}}">{{end}}
{{if .Manifest.MainCSSBundlePath}}<link rel="stylesheet" href="{{assetURL .Manifest.MainCSSBundlePath}}">{{end}}
<link id='sourcegraph-chrome-webstore-item' rel="chrome-webstore-item" href="https://chrome.google.com/webstore/detail/dgjhfomjieaadpoljlnidmbgkdffpack">
<link rel="search" href="/opensearch.xml" type="application/opensearchdescription+xml" title="Sourcegraph Search">
{{if .PreloadedAssets}}
@ -86,7 +86,6 @@
{{ end }}
{{.Injected.BodyTop}}
{{.ManifestDevInjectHTML}}
<div id="root">
{{ if .Context.RedirectUnsupportedBrowser }}
<script ignore-csp>
@ -108,7 +107,7 @@
<br>
You need to enable JavaScript to run this app.
</noscript>
<script src="{{assetURL (printf "%s%s" .Manifest.URL .Manifest.Assets.Main.JS)}}" type="module"></script>
<script src="{{assetURL .Manifest.MainJSBundlePath}}" type="module"></script>
{{.Injected.BodyBottom}}
</body>

View File

@ -86,8 +86,7 @@ type Common struct {
PreloadedAssets *[]PreloadedAsset
Manifest *assets.WebBuildManifest
ManifestDevInjectHTML template.HTML
Manifest *assets.WebBuildManifest
WebBuilderDevServer bool // whether the web builder dev server is running (WEB_BUILDER_DEV_SERVER env var)
@ -183,10 +182,6 @@ func newCommon(w http.ResponseWriter, r *http.Request, db database.DB, title str
WebBuilderDevServer: webBuilderDevServer,
}
if env.InsecureDev && manifest.DevInjectHTML != "" {
common.ManifestDevInjectHTML = template.HTML(manifest.DevInjectHTML)
}
if enableHTMLInject != "true" {
common.Injected = InjectedHTML{}
}

View File

@ -9,7 +9,6 @@ import (
"io"
"net/http"
"os"
"strings"
"sync"
"github.com/sourcegraph/sourcegraph/cmd/frontend/internal/app/assetsutil"
@ -39,9 +38,6 @@ var (
// Functions that are exposed to templates.
var funcMap = template.FuncMap{
"assetURL": func(filePath string) string {
if strings.HasPrefix(filePath, "http://") {
return filePath
}
return assetsutil.URL(filePath).String()
},
"version": func(fp string) (string, error) {

View File

@ -1,11 +1,20 @@
{
http_port 3081
auto_https disable_redirects
}
# Caddy (tls :3443) -> sourcegraph-frontend (:3080)
# A bit of monstrosity, since we need to reverse proxy via the web builder dev server which then
# reverse proxies to us on HTTP.
#
# Caddy (tls :3443) -> web builder dev server (:3080) -> Caddy (:3081) -> sourcegraph-frontend (:3082)
{$SOURCEGRAPH_HTTPS_DOMAIN}:{$SOURCEGRAPH_HTTPS_PORT} {
tls internal
reverse_proxy 127.0.0.1:3080 {
lb_try_duration 60s
}
}
# Caddy (:3081) -> sourcegraph-frontend (:3082)
:3081 {
reverse_proxy 127.0.0.1:3082
}

19
dev/esbuild.bzl Normal file
View File

@ -0,0 +1,19 @@
load("@aspect_bazel_lib//lib:copy_to_directory.bzl", "copy_to_directory")
load("@aspect_rules_esbuild//esbuild:defs.bzl", "esbuild")
def esbuild_web_app(name, **kwargs):
bundle_name = "%s_bundle" % name
esbuild(
name = bundle_name,
**kwargs
)
copy_to_directory(
name = name,
# flatten static assets
# https://docs.aspect.build/rules/aspect_bazel_lib/docs/copy_to_directory/#root_paths
root_paths = ["client/web/dist", "client/web/%s" % bundle_name],
srcs = ["//client/web/dist/img:img", ":%s" % bundle_name],
visibility = ["//visibility:public"],
)

View File

@ -1,225 +0,0 @@
"Vite rule"
load("@aspect_bazel_lib//lib:copy_to_bin.bzl", "copy_file_to_bin_action", "copy_files_to_bin_actions")
load("@aspect_bazel_lib//lib:copy_to_directory.bzl", "copy_to_directory")
load("@aspect_bazel_lib//lib:directory_path.bzl", "directory_path")
load("@aspect_rules_js//js:defs.bzl", "js_binary")
load("@aspect_rules_js//js:libs.bzl", "js_lib_helpers")
load("@aspect_rules_js//js:providers.bzl", "JsInfo", "js_info")
load("@bazel_skylib//lib:paths.bzl", "paths")
_ATTRS = {
"config": attr.label(
mandatory = True,
allow_single_file = True,
doc = """Configuration file used for Vite""",
),
"data": js_lib_helpers.JS_LIBRARY_DATA_ATTR,
"deps": attr.label_list(
default = [],
doc = "A list of direct dependencies that are required to build the bundle",
providers = [JsInfo],
),
"entry_points": attr.label_list(
allow_files = True,
doc = """The bundle's entry points""",
),
"srcs": attr.label_list(
allow_files = True,
default = [],
doc = """Source files to be made available to Vite""",
),
"tsconfig": attr.label(
mandatory = True,
allow_single_file = True,
doc = """TypeScript configuration file used by Vite""",
),
"vite_js_bin": attr.label(
executable = True,
doc = "Override the default vite executable",
cfg = "exec",
),
"env": attr.string_dict(),
}
def _bin_relative_path(ctx, file):
prefix = ctx.bin_dir.path + "/"
if file.path.startswith(prefix):
return file.path[len(prefix):]
# Since file.path is relative to execroot, go up with ".." starting from
# ctx.bin_dir until we reach execroot, then join that with the file path.
up = "/".join([".." for _ in ctx.bin_dir.path.split("/")])
return up + "/" + file.path
def _output_relative_path(f):
"Give the path from bazel-out/[arch]/bin to the given File object"
if f.short_path.startswith("../"):
return "external/" + f.short_path[3:]
return f.short_path
def _filter_js(files):
return [f for f in files if f.extension == "js" or f.extension == "mjs"]
def _vite_project_impl(ctx):
input_sources = copy_files_to_bin_actions(ctx, ctx.files.srcs)
entry_points = copy_files_to_bin_actions(ctx, _filter_js(ctx.files.entry_points))
inputs = entry_points + input_sources + ctx.files.deps
args = ctx.actions.args()
output_sources = [getattr(ctx.outputs, o) for o in dir(ctx.outputs)]
output_sources.append(ctx.actions.declare_directory(ctx.label.name))
args.add_all(["--outDir", output_sources[0].basename])
config_file = copy_file_to_bin_action(ctx, ctx.file.config)
args.add_all(["--config", _output_relative_path(config_file)])
inputs.append(config_file)
env = {
"BAZEL_BINDIR": ctx.bin_dir.path,
}
for (key, value) in ctx.attr.env.items():
env[key] = value
args.add("build")
ctx.actions.run(
executable = ctx.executable.vite_js_bin,
arguments = [args],
inputs = depset(
inputs,
transitive = [js_lib_helpers.gather_files_from_js_providers(
targets = ctx.attr.srcs + ctx.attr.deps,
include_transitive_sources = True,
include_declarations = False,
include_npm_linked_packages = True,
)],
),
outputs = output_sources,
progress_message = "Building Vite project %s" % (" ".join([_bin_relative_path(ctx, entry_point) for entry_point in entry_points])),
mnemonic = "Vite",
env = env,
)
npm_linked_packages = js_lib_helpers.gather_npm_linked_packages(
srcs = ctx.attr.srcs,
deps = [],
)
npm_package_store_deps = js_lib_helpers.gather_npm_package_store_deps(
# Since we're bundling, only propagate `data` npm packages to the direct dependencies of
# downstream linked `npm_package` targets instead of the common `data` and `deps` pattern.
targets = ctx.attr.data,
)
output_sources_depset = depset(output_sources)
runfiles = js_lib_helpers.gather_runfiles(
ctx = ctx,
sources = output_sources_depset,
data = ctx.attr.data,
# Since we're bundling, we don't propagate any transitive runfiles from dependencies
deps = [],
)
return [
DefaultInfo(
files = output_sources_depset,
runfiles = runfiles,
),
js_info(
npm_linked_package_files = npm_linked_packages.direct_files,
npm_linked_packages = npm_linked_packages.direct,
npm_package_store_deps = npm_package_store_deps,
sources = output_sources_depset,
# Since we're bundling, we don't propagate linked npm packages from dependencies since
# they are bundled and the dependencies are dropped. If a subset of linked npm
# dependencies are not bundled it is up the the user to re-specify these in `data` if
# they are runtime dependencies to progagate to binary rules or `srcs` if they are to be
# propagated to downstream build targets.
transitive_npm_linked_package_files = npm_linked_packages.direct_files,
transitive_npm_linked_packages = npm_linked_packages.direct,
# Since we're bundling, we don't propagate any transitive sources from dependencies
transitive_sources = output_sources_depset,
),
]
lib = struct(
attrs = _ATTRS,
implementation = _vite_project_impl,
toolchains = [
"@rules_nodejs//nodejs:toolchain_type",
],
)
_vite_project = rule(
implementation = _vite_project_impl,
attrs = _ATTRS,
toolchains = lib.toolchains,
doc = """\
Runs Vite in Bazel
""",
)
def vite_project(name, config, data = [], deps = [], entry_points = [], srcs = [], env = {}, **kwargs):
"""Runs Vite in Bazel.
Args:
name: A unique name for this rule.
config: The Vite config file.
data: Runtime dependencies that are passed to the Vite build.
deps: Other npm dependencies that are passed to the Vite build.
entry_points: The entry points to build.
srcs: Other sources that are passed to the Vite build.
env: Environment variables to pass to the Vite build.
**kwargs: Additional arguments
"""
vite_js_entry_point = "_{}_vite_js_entry_point".format(name)
node_modules = "//:node_modules"
directory_path(
name = vite_js_entry_point,
directory = "{}/vite/dir".format(node_modules),
path = "bin/vite.js",
)
vite_js_bin = "_{}_vite_js_bin".format(name)
js_binary(
name = vite_js_bin,
data = ["%s/vite" % node_modules],
entry_point = vite_js_entry_point,
)
_vite_project(
name = name,
config = config,
data = data,
deps = deps,
entry_points = entry_points,
srcs = srcs,
vite_js_bin = vite_js_bin,
env = env,
**kwargs
)
def vite_web_app(name, **kwargs):
bundle_name = "%s_bundle" % name
vite_project(name = bundle_name, **kwargs)
copy_to_directory(
name = name,
# flatten static assets
# https://docs.aspect.build/rules/aspect_bazel_lib/docs/copy_to_directory/#root_paths
root_paths = ["client/web/dist", "client/web/%s" % bundle_name],
srcs = ["//client/web/dist/img:img", ":%s" % bundle_name],
visibility = ["//visibility:public"],
)

View File

@ -55,6 +55,7 @@ Available commandsets in `sg.config.yaml`:
* qdrant
* single-program
* web-standalone
* web-standalone-prod
* web-sveltekit-standalone
```sh
@ -141,6 +142,7 @@ Available commands in `sg.config.yaml`:
* telemetry-gateway
* web-integration-build-prod: Build production web application for integration tests
* web-integration-build: Build development web application for integration tests
* web-standalone-http-prod: Standalone web frontend (production) with API proxy to a configurable URL
* web-standalone-http: Standalone web frontend (dev) with API proxy to a configurable URL
* web-sveltekit-prod-watch: Builds the prod version of the SvelteKit web app and rebuilds on changes
* web-sveltekit-standalone: Standalone SvelteKit web frontend (dev) with API proxy to a configurable URL

View File

@ -176,7 +176,7 @@
"@types/uuid": "8.0.1",
"@types/whatwg-url": "^11.0.3",
"@types/yauzl": "^2.9.2",
"@vitejs/plugin-react": "^4.1.0",
"@vitejs/plugin-react": "^3.1.0",
"abort-controller": "^3.0.0",
"autoprefixer": "^10.2.1",
"axe-core": "^4.4.1",
@ -260,7 +260,7 @@
"typed-scss-modules": "^4.1.1",
"typescript": "^5.0.2",
"utc-version": "^2.0.2",
"vite": "^4.5.0",
"vite": "^4.1.4",
"vite-plugin-turbosnap": "^1.0.3",
"vitest": "1.0.0-beta.4",
"vitest-fetch-mock": "^0.2.2",

View File

@ -574,7 +574,7 @@ importers:
version: 7.4.6(react-dom@18.1.0)(react@18.1.0)
'@storybook/builder-vite':
specifier: ^7.4.6
version: 7.4.6(typescript@5.2.2)(vite@4.5.0)
version: 7.4.6(typescript@5.2.2)(vite@4.4.7)
'@storybook/cli':
specifier: ^7.4.6
version: 7.4.6
@ -595,7 +595,7 @@ importers:
version: 7.4.6(react-dom@18.1.0)(react@18.1.0)(typescript@5.2.2)
'@storybook/react-vite':
specifier: ^7.4.6
version: 7.4.6(react-dom@18.1.0)(react@18.1.0)(typescript@5.2.2)(vite@4.5.0)
version: 7.4.6(react-dom@18.1.0)(react@18.1.0)(typescript@5.2.2)(vite@4.4.7)
'@storybook/theming':
specifier: ^7.4.6
version: 7.4.6(react-dom@18.1.0)(react@18.1.0)
@ -798,8 +798,8 @@ importers:
specifier: ^2.9.2
version: 2.10.0
'@vitejs/plugin-react':
specifier: ^4.1.0
version: 4.1.0(vite@4.5.0)
specifier: ^3.1.0
version: 3.1.0(vite@4.4.7)
abort-controller:
specifier: ^3.0.0
version: 3.0.0
@ -1047,8 +1047,8 @@ importers:
specifier: ^2.0.2
version: 2.0.2
vite:
specifier: ^4.5.0
version: 4.5.0(@types/node@20.8.0)(sass@1.32.4)
specifier: ^4.1.4
version: 4.4.7(@types/node@20.8.0)(sass@1.32.4)
vite-plugin-turbosnap:
specifier: ^1.0.3
version: 1.0.3
@ -1486,7 +1486,7 @@ importers:
version: 7.4.6(react-dom@18.2.0)(react@18.2.0)
'@storybook/addon-svelte-csf':
specifier: ^3.0.7
version: 3.0.7(@storybook/svelte@7.2.0)(@storybook/theming@7.4.6)(@sveltejs/vite-plugin-svelte@2.4.2)(svelte@4.1.1)(vite@4.5.0)
version: 3.0.7(@storybook/svelte@7.2.0)(@storybook/theming@7.4.6)(@sveltejs/vite-plugin-svelte@2.4.2)(svelte@4.1.1)(vite@4.4.7)
'@storybook/blocks':
specifier: ^7.2.0
version: 7.4.6(@types/react-dom@18.0.2)(@types/react@18.0.8)(react-dom@18.2.0)(react@18.2.0)
@ -1495,7 +1495,7 @@ importers:
version: 7.2.0(svelte@4.1.1)
'@storybook/sveltekit':
specifier: ^7.2.0
version: 7.2.0(svelte@4.1.1)(typescript@5.2.2)(vite@4.5.0)
version: 7.2.0(svelte@4.1.1)(typescript@5.2.2)(vite@4.4.7)
'@storybook/testing-library':
specifier: 0.2.0
version: 0.2.0
@ -1507,7 +1507,7 @@ importers:
version: 2.0.3(@sveltejs/kit@1.22.3)
'@sveltejs/kit':
specifier: ^1.22.3
version: 1.22.3(svelte@4.1.1)(vite@4.5.0)
version: 1.22.3(svelte@4.1.1)(vite@4.4.7)
'@testing-library/svelte':
specifier: ^4.0.3
version: 4.0.3(svelte@4.1.1)
@ -1570,10 +1570,10 @@ importers:
version: 2.1.0
vite:
specifier: ^4.4.7
version: 4.5.0(@types/node@20.8.0)(sass@1.32.4)
version: 4.4.7(@types/node@20.8.0)(sass@1.32.4)
vite-plugin-inspect:
specifier: ^0.7.35
version: 0.7.35(vite@4.5.0)
version: 0.7.35(vite@4.4.7)
vitest:
specifier: ^0.33.0
version: 0.33.0(happy-dom@12.10.1)(jsdom@22.1.0)(sass@1.32.4)
@ -5228,7 +5228,7 @@ packages:
'@types/yargs': 17.0.23
chalk: 4.1.2
/@joshwooding/vite-plugin-react-docgen-typescript@0.2.1(typescript@5.2.2)(vite@4.5.0):
/@joshwooding/vite-plugin-react-docgen-typescript@0.2.1(typescript@5.2.2)(vite@4.4.7):
resolution: {integrity: sha512-ou4ZJSXMMWHqGS4g8uNRbC5TiTWxAgQZiVucoUrOCWuPrTbkpJbmVyIi9jU72SBry7gQtuMEDp4YR8EEXAg7VQ==}
peerDependencies:
typescript: '>= 4.3.x'
@ -5242,7 +5242,7 @@ packages:
magic-string: 0.27.0
react-docgen-typescript: 2.2.2(typescript@5.2.2)
typescript: 5.2.2
vite: 4.5.0(@types/node@20.8.0)(sass@1.32.4)
vite: 4.4.7(@types/node@20.8.0)(sass@1.32.4)
dev: true
/@jridgewell/gen-mapping@0.3.3:
@ -6253,6 +6253,7 @@ packages:
/@playwright/test@1.25.0:
resolution: {integrity: sha512-j4EZhTTQI3dBeWblE21EV//swwmBtOpIrLdOIJIRv4uqsLdHgBg1z+JtTg+AeC5o2bAXIE26kDNW5A0TimG8Bg==}
engines: {node: '>=14'}
deprecated: Please update to the latest version of Playwright to test up-to-date browsers.
hasBin: true
dependencies:
'@types/node': 20.8.0
@ -9105,7 +9106,7 @@ packages:
- '@types/react-dom'
dev: true
/@storybook/addon-svelte-csf@3.0.7(@storybook/svelte@7.2.0)(@storybook/theming@7.4.6)(@sveltejs/vite-plugin-svelte@2.4.2)(svelte@4.1.1)(vite@4.5.0):
/@storybook/addon-svelte-csf@3.0.7(@storybook/svelte@7.2.0)(@storybook/theming@7.4.6)(@sveltejs/vite-plugin-svelte@2.4.2)(svelte@4.1.1)(vite@4.4.7):
resolution: {integrity: sha512-T7KYWlhIs3G2N4r0UPawCCnHnYUWyg2rUMIfi/HLVYODsnqG7rJmK3ZAvtEgd1sFST0gRNBu13NIyH3YYUnA7A==}
peerDependencies:
'@storybook/svelte': ^7.0.0
@ -9125,12 +9126,12 @@ packages:
'@babel/runtime': 7.23.1
'@storybook/svelte': 7.2.0(svelte@4.1.1)
'@storybook/theming': 7.4.6(react-dom@18.2.0)(react@18.2.0)
'@sveltejs/vite-plugin-svelte': 2.4.2(svelte@4.1.1)(vite@4.5.0)
'@sveltejs/vite-plugin-svelte': 2.4.2(svelte@4.1.1)(vite@4.4.7)
dedent: 1.5.1
fs-extra: 11.1.1
magic-string: 0.30.5
svelte: 4.1.1
vite: 4.5.0(@types/node@20.8.0)(sass@1.32.4)
vite: 4.4.7(@types/node@20.8.0)(sass@1.32.4)
transitivePeerDependencies:
- babel-plugin-macros
dev: true
@ -9430,7 +9431,7 @@ packages:
- supports-color
dev: true
/@storybook/builder-vite@7.2.0(typescript@5.2.2)(vite@4.5.0):
/@storybook/builder-vite@7.2.0(typescript@5.2.2)(vite@4.4.7):
resolution: {integrity: sha512-YmTtyIMxWKaFOsJaLU5rGHukvt37LjVvW6QEAEK1Clbl1f3QUNSEGJwVYLRYO7xQvhymBF4JMa8N5NSc1+xSEQ==}
peerDependencies:
'@preact/preset-vite': '*'
@ -9463,15 +9464,15 @@ packages:
magic-string: 0.30.5
remark-external-links: 8.0.0
remark-slug: 6.1.0
rollup: 3.29.4
rollup: 3.26.3
typescript: 5.2.2
vite: 4.5.0(@types/node@20.8.0)(sass@1.32.4)
vite: 4.4.7(@types/node@20.8.0)(sass@1.32.4)
transitivePeerDependencies:
- encoding
- supports-color
dev: true
/@storybook/builder-vite@7.4.6(typescript@5.2.2)(vite@4.5.0):
/@storybook/builder-vite@7.4.6(typescript@5.2.2)(vite@4.4.7):
resolution: {integrity: sha512-xV9STYK+TkqWWTf2ydm6jx+7P70fjD2UPd1XTUw08uKszIjhuuxk+bG/OF5R1E25mPunAKXm6kBFh351AKejBg==}
peerDependencies:
'@preact/preset-vite': '*'
@ -9504,9 +9505,9 @@ packages:
magic-string: 0.30.5
remark-external-links: 8.0.0
remark-slug: 6.1.0
rollup: 3.29.4
rollup: 3.26.3
typescript: 5.2.2
vite: 4.5.0(@types/node@20.8.0)(sass@1.32.4)
vite: 4.4.7(@types/node@20.8.0)(sass@1.32.4)
transitivePeerDependencies:
- encoding
- supports-color
@ -10232,7 +10233,7 @@ packages:
react-dom: 18.1.0(react@18.1.0)
dev: true
/@storybook/react-vite@7.4.6(react-dom@18.1.0)(react@18.1.0)(typescript@5.2.2)(vite@4.5.0):
/@storybook/react-vite@7.4.6(react-dom@18.1.0)(react@18.1.0)(typescript@5.2.2)(vite@4.4.7):
resolution: {integrity: sha512-jkjnrf3FxzR5wcmebXRPflrsM4WIDjWyW/NVFJwxi5PeIOk7fE7/QAPrm4NFRUu2Q7DeuH3oLKsw8bigvUI9RA==}
engines: {node: '>=16'}
peerDependencies:
@ -10240,17 +10241,17 @@ packages:
react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0
vite: ^3.0.0 || ^4.0.0
dependencies:
'@joshwooding/vite-plugin-react-docgen-typescript': 0.2.1(typescript@5.2.2)(vite@4.5.0)
'@joshwooding/vite-plugin-react-docgen-typescript': 0.2.1(typescript@5.2.2)(vite@4.4.7)
'@rollup/pluginutils': 5.0.2
'@storybook/builder-vite': 7.4.6(typescript@5.2.2)(vite@4.5.0)
'@storybook/builder-vite': 7.4.6(typescript@5.2.2)(vite@4.4.7)
'@storybook/react': 7.4.6(react-dom@18.1.0)(react@18.1.0)(typescript@5.2.2)
'@vitejs/plugin-react': 3.1.0(vite@4.5.0)
'@vitejs/plugin-react': 3.1.0(vite@4.4.7)
ast-types: 0.14.2
magic-string: 0.30.5
react: 18.1.0
react-docgen: 6.0.0-alpha.3
react-dom: 18.1.0(react@18.1.0)
vite: 4.5.0(@types/node@20.8.0)(sass@1.32.4)
vite: 4.4.7(@types/node@20.8.0)(sass@1.32.4)
transitivePeerDependencies:
- '@preact/preset-vite'
- encoding
@ -10354,22 +10355,22 @@ packages:
react-dom: 18.1.0(react@18.1.0)
dev: true
/@storybook/svelte-vite@7.2.0(svelte@4.1.1)(typescript@5.2.2)(vite@4.5.0):
/@storybook/svelte-vite@7.2.0(svelte@4.1.1)(typescript@5.2.2)(vite@4.4.7):
resolution: {integrity: sha512-iGaaY64xWjMDzECpu+qw82Y0LtwZYD5FimCEmx1gjPLiv0TJDRVcpBvtGNzEf6ztevastRQs8HEDxv1H1xjCkQ==}
engines: {node: ^14.18 || >=16}
peerDependencies:
svelte: ^3.0.0 || ^4.0.0
vite: ^3.0.0 || ^4.0.0
dependencies:
'@storybook/builder-vite': 7.2.0(typescript@5.2.2)(vite@4.5.0)
'@storybook/builder-vite': 7.2.0(typescript@5.2.2)(vite@4.4.7)
'@storybook/node-logger': 7.2.0
'@storybook/svelte': 7.2.0(svelte@4.1.1)
'@sveltejs/vite-plugin-svelte': 2.4.2(svelte@4.1.1)(vite@4.5.0)
'@sveltejs/vite-plugin-svelte': 2.4.2(svelte@4.1.1)(vite@4.4.7)
magic-string: 0.30.5
svelte: 4.1.1
sveltedoc-parser: 4.2.1
ts-dedent: 2.2.0
vite: 4.5.0(@types/node@20.8.0)(sass@1.32.4)
vite: 4.4.7(@types/node@20.8.0)(sass@1.32.4)
transitivePeerDependencies:
- '@preact/preset-vite'
- encoding
@ -10399,18 +10400,18 @@ packages:
- supports-color
dev: true
/@storybook/sveltekit@7.2.0(svelte@4.1.1)(typescript@5.2.2)(vite@4.5.0):
/@storybook/sveltekit@7.2.0(svelte@4.1.1)(typescript@5.2.2)(vite@4.4.7):
resolution: {integrity: sha512-JyhRofOywpIElqgpc6NR6xMyBgop5bVSnzeDl9aJk0VTVGFvQOEco85f9FHJmlhhBpFr7g+Nal7hzsqg5vkdPg==}
engines: {node: ^14.18 || >=16}
peerDependencies:
svelte: ^3.0.0 || ^4.0.0
vite: ^4.0.0
dependencies:
'@storybook/builder-vite': 7.2.0(typescript@5.2.2)(vite@4.5.0)
'@storybook/builder-vite': 7.2.0(typescript@5.2.2)(vite@4.4.7)
'@storybook/svelte': 7.2.0(svelte@4.1.1)
'@storybook/svelte-vite': 7.2.0(svelte@4.1.1)(typescript@5.2.2)(vite@4.5.0)
'@storybook/svelte-vite': 7.2.0(svelte@4.1.1)(typescript@5.2.2)(vite@4.4.7)
svelte: 4.1.1
vite: 4.5.0(@types/node@20.8.0)(sass@1.32.4)
vite: 4.4.7(@types/node@20.8.0)(sass@1.32.4)
transitivePeerDependencies:
- '@preact/preset-vite'
- encoding
@ -10505,7 +10506,7 @@ packages:
resolution: {integrity: sha512-jwoA/TIp+U8Vz868aQT+XfoAw6qFrtn2HbZlTfwNWZsUhPFlMsGrwIVEpWqBWIoe6WITU/lNw3BuRmxul+wvAQ==}
dependencies:
'@storybook/channels': 7.2.0
'@types/babel__core': 7.20.3
'@types/babel__core': 7.1.20
'@types/express': 4.17.11
file-system-cache: 2.3.0
dev: true
@ -10514,7 +10515,7 @@ packages:
resolution: {integrity: sha512-6QLXtMVsFZFpzPkdGWsu/iuc8na9dnS67AMOBKm5qCLPwtUJOYkwhMdFRSSeJthLRpzV7JLAL8Kwvl7MFP3QSw==}
dependencies:
'@storybook/channels': 7.4.6
'@types/babel__core': 7.20.3
'@types/babel__core': 7.1.20
'@types/express': 4.17.11
file-system-cache: 2.3.0
dev: true
@ -10524,7 +10525,7 @@ packages:
peerDependencies:
'@sveltejs/kit': ^1.0.0
dependencies:
'@sveltejs/kit': 1.22.3(svelte@4.1.1)(vite@4.5.0)
'@sveltejs/kit': 1.22.3(svelte@4.1.1)(vite@4.4.7)
import-meta-resolve: 3.0.0
dev: true
@ -10533,10 +10534,10 @@ packages:
peerDependencies:
'@sveltejs/kit': ^1.5.0
dependencies:
'@sveltejs/kit': 1.22.3(svelte@4.1.1)(vite@4.5.0)
'@sveltejs/kit': 1.22.3(svelte@4.1.1)(vite@4.4.7)
dev: true
/@sveltejs/kit@1.22.3(svelte@4.1.1)(vite@4.5.0):
/@sveltejs/kit@1.22.3(svelte@4.1.1)(vite@4.4.7):
resolution: {integrity: sha512-IpHD5wvuoOIHYaHQUBJ1zERD2Iz+fB/rBXhXjl8InKw6X4VKE9BSus+ttHhE7Ke+Ie9ecfilzX8BnWE3FeQyng==}
engines: {node: ^16.14 || >=18}
hasBin: true
@ -10545,7 +10546,7 @@ packages:
svelte: ^3.54.0 || ^4.0.0-next.0
vite: ^4.0.0
dependencies:
'@sveltejs/vite-plugin-svelte': 2.4.2(svelte@4.1.1)(vite@4.5.0)
'@sveltejs/vite-plugin-svelte': 2.4.2(svelte@4.1.1)(vite@4.4.7)
'@types/cookie': 0.5.1
cookie: 0.5.0
devalue: 4.3.2
@ -10558,12 +10559,12 @@ packages:
sirv: 2.0.3
svelte: 4.1.1
undici: 5.22.1
vite: 4.5.0(@types/node@20.8.0)(sass@1.32.4)
vite: 4.4.7(@types/node@20.8.0)(sass@1.32.4)
transitivePeerDependencies:
- supports-color
dev: true
/@sveltejs/vite-plugin-svelte-inspector@1.0.3(@sveltejs/vite-plugin-svelte@2.4.2)(svelte@4.1.1)(vite@4.5.0):
/@sveltejs/vite-plugin-svelte-inspector@1.0.3(@sveltejs/vite-plugin-svelte@2.4.2)(svelte@4.1.1)(vite@4.4.7):
resolution: {integrity: sha512-Khdl5jmmPN6SUsVuqSXatKpQTMIifoQPDanaxC84m9JxIibWvSABJyHpyys0Z+1yYrxY5TTEQm+6elh0XCMaOA==}
engines: {node: ^14.18.0 || >= 16}
peerDependencies:
@ -10571,30 +10572,30 @@ packages:
svelte: ^3.54.0 || ^4.0.0
vite: ^4.0.0
dependencies:
'@sveltejs/vite-plugin-svelte': 2.4.2(svelte@4.1.1)(vite@4.5.0)
'@sveltejs/vite-plugin-svelte': 2.4.2(svelte@4.1.1)(vite@4.4.7)
debug: 4.3.4
svelte: 4.1.1
vite: 4.5.0(@types/node@20.8.0)(sass@1.32.4)
vite: 4.4.7(@types/node@20.8.0)(sass@1.32.4)
transitivePeerDependencies:
- supports-color
dev: true
/@sveltejs/vite-plugin-svelte@2.4.2(svelte@4.1.1)(vite@4.5.0):
/@sveltejs/vite-plugin-svelte@2.4.2(svelte@4.1.1)(vite@4.4.7):
resolution: {integrity: sha512-ePfcC48ftMKhkT0OFGdOyycYKnnkT6i/buzey+vHRTR/JpQvuPzzhf1PtKqCDQfJRgoPSN2vscXs6gLigx/zGw==}
engines: {node: ^14.18.0 || >= 16}
peerDependencies:
svelte: ^3.54.0 || ^4.0.0
vite: ^4.0.0
dependencies:
'@sveltejs/vite-plugin-svelte-inspector': 1.0.3(@sveltejs/vite-plugin-svelte@2.4.2)(svelte@4.1.1)(vite@4.5.0)
'@sveltejs/vite-plugin-svelte-inspector': 1.0.3(@sveltejs/vite-plugin-svelte@2.4.2)(svelte@4.1.1)(vite@4.4.7)
debug: 4.3.4
deepmerge: 4.3.1
kleur: 4.1.5
magic-string: 0.30.5
svelte: 4.1.1
svelte-hmr: 0.15.2(svelte@4.1.1)
vite: 4.5.0(@types/node@20.8.0)(sass@1.32.4)
vitefu: 0.2.4(vite@4.5.0)
vite: 4.4.7(@types/node@20.8.0)(sass@1.32.4)
vitefu: 0.2.4(vite@4.4.7)
transitivePeerDependencies:
- supports-color
dev: true
@ -10883,8 +10884,8 @@ packages:
resolution: {integrity: sha512-yzgMaql7aW1by1XuhKhovuhLyK/1A60lapFXDXXBeHmoyRGQFO2T8lkL3g8hAhHoW5PEvqPJFWPd8jvXiRnxeQ==}
dev: true
/@types/babel__core@7.20.3:
resolution: {integrity: sha512-54fjTSeSHwfan8AyHWrKbfBWiEUrNTZsUwPTDSNaaP1QDQIZbeNUg3a59E9D+375MzUw/x1vx2/0F5LBz+AeYA==}
/@types/babel__core@7.1.20:
resolution: {integrity: sha512-PVb6Bg2QuscZ30FvOU7z4guG6c926D9YRvOxEaelzndpMsvP+YM74Q/dAFASpg2l6+XLalxSGxcq/lrgYWZtyQ==}
dependencies:
'@babel/parser': 7.23.0
'@babel/types': 7.23.0
@ -12158,7 +12159,7 @@ packages:
react: 18.1.0
dev: false
/@vitejs/plugin-react@3.1.0(vite@4.5.0):
/@vitejs/plugin-react@3.1.0(vite@4.4.7):
resolution: {integrity: sha512-AfgcRL8ZBhAlc3BFdigClmTUMISmmzHn7sB2h9U1odvc5U/MjWXsAaz18b/WoppUTDBzxOJwo2VdClfUcItu9g==}
engines: {node: ^14.18.0 || >=16.0.0}
peerDependencies:
@ -12169,23 +12170,7 @@ packages:
'@babel/plugin-transform-react-jsx-source': 7.22.5(@babel/core@7.23.0)
magic-string: 0.27.0
react-refresh: 0.14.0
vite: 4.5.0(@types/node@20.8.0)(sass@1.32.4)
transitivePeerDependencies:
- supports-color
dev: true
/@vitejs/plugin-react@4.1.0(vite@4.5.0):
resolution: {integrity: sha512-rM0SqazU9iqPUraQ2JlIvReeaxOoRj6n+PzB1C0cBzIbd8qP336nC39/R9yPi3wVcah7E7j/kdU1uCUqMEU4OQ==}
engines: {node: ^14.18.0 || >=16.0.0}
peerDependencies:
vite: ^4.2.0
dependencies:
'@babel/core': 7.23.0
'@babel/plugin-transform-react-jsx-self': 7.22.5(@babel/core@7.23.0)
'@babel/plugin-transform-react-jsx-source': 7.22.5(@babel/core@7.23.0)
'@types/babel__core': 7.20.3
react-refresh: 0.14.0
vite: 4.5.0(@types/node@20.8.0)(sass@1.32.4)
vite: 4.4.7(@types/node@20.8.0)(sass@1.32.4)
transitivePeerDependencies:
- supports-color
dev: true
@ -23022,8 +23007,8 @@ packages:
dependencies:
glob: 7.2.3
/rollup@3.29.4:
resolution: {integrity: sha512-oWzmBZwvYrU0iJHtDmhsm662rC15FRXmcjCk1xD771dFDx5jJ02ufAQQTn0etB2emNk4J9EZg/yWKpsn9BWGRw==}
/rollup@3.26.3:
resolution: {integrity: sha512-7Tin0C8l86TkpcMtXvQu6saWH93nhG3dGQ1/+l5V2TDMceTxO7kDiK6GzbfLWNNxqJXm591PcEZUozZm51ogwQ==}
engines: {node: '>=14.18.0', npm: '>=8.0.0'}
hasBin: true
optionalDependencies:
@ -24724,6 +24709,7 @@ packages:
/trim@0.0.1:
resolution: {integrity: sha512-YzQV+TZg4AxpKxaTHK3c3D+kRDCGVEE7LemdlQZoQXn0iennk10RsIoY6ikzAqJTc9Xjl9C1/waHom/J86ziAQ==}
deprecated: Use String.prototype.trim() instead
dev: true
/ts-api-utils@1.0.3(typescript@5.2.2):
@ -25488,7 +25474,7 @@ packages:
mlly: 1.4.0
pathe: 1.1.1
picocolors: 1.0.0
vite: 4.5.0(@types/node@20.8.0)(sass@1.32.4)
vite: 4.4.7(@types/node@20.8.0)(sass@1.32.4)
transitivePeerDependencies:
- '@types/node'
- less
@ -25510,7 +25496,7 @@ packages:
mlly: 1.4.0
pathe: 1.1.1
picocolors: 1.0.0
vite: 4.5.0(@types/node@20.8.0)(sass@1.32.4)
vite: 4.4.7(@types/node@20.8.0)(sass@1.32.4)
transitivePeerDependencies:
- '@types/node'
- less
@ -25522,7 +25508,7 @@ packages:
- terser
dev: true
/vite-plugin-inspect@0.7.35(vite@4.5.0):
/vite-plugin-inspect@0.7.35(vite@4.4.7):
resolution: {integrity: sha512-e5w5dJAj3vDcHTxn8hHbiH+mVqYs17gaW00f3aGuMTXiqUog+T1Lsxr9Jb4WRiip84cpuhR0KFFBT1egtXboiA==}
engines: {node: '>=14'}
peerDependencies:
@ -25539,7 +25525,7 @@ packages:
open: 9.1.0
picocolors: 1.0.0
sirv: 2.0.3
vite: 4.5.0(@types/node@20.8.0)(sass@1.32.4)
vite: 4.4.7(@types/node@20.8.0)(sass@1.32.4)
transitivePeerDependencies:
- rollup
- supports-color
@ -25549,8 +25535,8 @@ packages:
resolution: {integrity: sha512-p4D8CFVhZS412SyQX125qxyzOgIFouwOcvjZWk6bQbNPR1wtaEzFT6jZxAjf1dejlGqa6fqHcuCvQea6EWUkUA==}
dev: true
/vite@4.5.0(@types/node@20.8.0)(sass@1.32.4):
resolution: {integrity: sha512-ulr8rNLA6rkyFAlVWw2q5YJ91v098AFQ2R0PRFwPzREXOUJQPtFUG0t+/ZikhaOCDqFoDhN6/v8Sq0o4araFAw==}
/vite@4.4.7(@types/node@20.8.0)(sass@1.32.4):
resolution: {integrity: sha512-6pYf9QJ1mHylfVh39HpuSfMPojPSKVxZvnclX1K1FyZ1PXDOcLBibdq5t1qxJSnL63ca8Wf4zts6mD8u8oc9Fw==}
engines: {node: ^14.18.0 || >=16.0.0}
hasBin: true
peerDependencies:
@ -25580,13 +25566,13 @@ packages:
'@types/node': 20.8.0
esbuild: 0.18.17
postcss: 8.4.31
rollup: 3.29.4
rollup: 3.26.3
sass: 1.32.4
optionalDependencies:
fsevents: 2.3.3
dev: true
/vitefu@0.2.4(vite@4.5.0):
/vitefu@0.2.4(vite@4.4.7):
resolution: {integrity: sha512-fanAXjSaf9xXtOOeno8wZXIhgia+CZury481LsDaV++lSvcU2R9Ch2bPh3PYFyoHW+w9LqAeYRISVQjUIew14g==}
peerDependencies:
vite: ^3.0.0 || ^4.0.0
@ -25594,7 +25580,7 @@ packages:
vite:
optional: true
dependencies:
vite: 4.5.0(@types/node@20.8.0)(sass@1.32.4)
vite: 4.4.7(@types/node@20.8.0)(sass@1.32.4)
dev: true
/vitest-fetch-mock@0.2.2(vitest@1.0.0-beta.4):
@ -25663,7 +25649,7 @@ packages:
strip-literal: 1.0.1
tinybench: 2.5.0
tinypool: 0.6.0
vite: 4.5.0(@types/node@20.8.0)(sass@1.32.4)
vite: 4.4.7(@types/node@20.8.0)(sass@1.32.4)
vite-node: 0.33.0(@types/node@20.8.0)(sass@1.32.4)
why-is-node-running: 2.2.2
transitivePeerDependencies:
@ -25722,7 +25708,7 @@ packages:
strip-literal: 1.0.1
tinybench: 2.5.0
tinypool: 0.8.1
vite: 4.5.0(@types/node@20.8.0)(sass@1.32.4)
vite: 4.4.7(@types/node@20.8.0)(sass@1.32.4)
vite-node: 1.0.0-beta.4(@types/node@20.8.0)(sass@1.32.4)
why-is-node-running: 2.2.2
transitivePeerDependencies:

View File

@ -29,6 +29,8 @@ env:
DEPLOY_TYPE: dev
SRC_HTTP_ADDR: ':3082'
# I don't think we even need to set these?
SEARCHER_URL: http://127.0.0.1:3181
REPO_UPDATER_URL: http://127.0.0.1:3182
@ -404,7 +406,7 @@ commands:
web:
description: Enterprise version of the web app
cmd: pnpm --filter @sourcegraph/web run dev --clearScreen=false
cmd: pnpm --filter @sourcegraph/web dev
install: |
pnpm install
pnpm run generate
@ -421,6 +423,15 @@ commands:
WEB_BUILDER_SERVE_INDEX: true
SOURCEGRAPH_API_URL: https://sourcegraph.sourcegraph.com
web-standalone-http-prod:
description: Standalone web frontend (production) with API proxy to a configurable URL
cmd: pnpm --filter @sourcegraph/web serve:prod
install: pnpm --filter @sourcegraph/web run build
env:
NODE_ENV: production
WEB_BUILDER_SERVE_INDEX: true
SOURCEGRAPH_API_URL: https://k8s.sgdev.org
web-integration-build:
description: Build development web application for integration tests
cmd: pnpm --filter @sourcegraph/web run build
@ -1505,6 +1516,11 @@ commandsets:
env:
SK_PORT: 3080
web-standalone-prod:
commands:
- web-standalone-http-prod
- caddy
# For testing our OpenTelemetry stack
otel:
checks:
@ -1632,14 +1648,6 @@ tests:
export PERCY_TOKEN=$(gcloud secrets versions access latest --secret=PERCY_TOKEN --quiet --project=sourcegraph-ci)
bazel test //client/web/src/integration:integration-tests --define=E2E_HEADLESS=false --define=E2E_SOURCEGRAPH_BASE_URL="http://localhost:7080" --define=GH_TOKEN=$GH_TOKEN --define=DISPLAY=$DISPLAY --define=PERCY_TOKEN=$PERCY_TOKEN
bazel-backend-integration:
cmd: |
export GHE_GITHUB_TOKEN=$(gcloud secrets versions access latest --secret=GHE_GITHUB_TOKEN --quiet --project=sourcegraph-ci)
export GH_TOKEN=$(gcloud secrets versions access latest --secret=GITHUB_TOKEN --quiet --project=sourcegraph-ci)
export SOURCEGRAPH_LICENSE_KEY=$(gcloud secrets versions access latest --secret=SOURCEGRAPH_LICENSE_KEY --quiet --project=sourcegraph-ci)
export SOURCEGRAPH_LICENSE_GENERATION_KEY=$(gcloud secrets versions access latest --secret=SOURCEGRAPH_LICENSE_GENERATION_KEY --quiet --project=sourcegraph-ci)
bazel test //testing:backend_integration_test --define=GHE_GITHUB_TOKEN=$GHE_GITHUB_TOKEN
backend-integration:
cmd: cd dev/gqltest && go test -long -base-url $BASE_URL -email $EMAIL -username $USERNAME -password $PASSWORD ./gqltest
env:

View File

@ -42,7 +42,7 @@ func loadWebBuildManifest() (m *WebBuildManifest, err error) {
return MockLoadWebBuildManifest()
}
manifestContent, err := os.ReadFile(filepath.Join(assetsDir, "vite-manifest.json"))
manifestContent, err := os.ReadFile(filepath.Join(assetsDir, "web.manifest.json"))
if err != nil {
return nil, errors.Wrap(err, "loading web build manifest file from disk")
}

View File

@ -1,23 +1,8 @@
package assets
// WebBuildManifest describes the web build and is produced by Vite. Keep it in sync with `interface
// WebBuildManifest`.
type WebBuildManifest struct {
// URL is the base URL for asset paths.
URL string `json:"url,omitempty"`
// Assets is a map of entrypoint (such as "src/enterprise/main.tsx") to its JavaScript and CSS assets.
Assets struct {
Main *entryAssets `json:"src/enterprise/main"`
EmbedMain *entryAssets `json:"src/enterprise/embed/embedMain"`
AppMain *entryAssets `json:"src/enterprise/app/main"`
} `json:"assets"`
// DevInjectHTML contains additional HTML <script> tags to inject in dev mode.
DevInjectHTML string `json:"devInjectHTML,omitempty"`
}
type entryAssets struct {
JS string `json:"js,omitempty"`
CSS string `json:"css,omitempty"`
MainJSBundlePath string `json:"main.js"`
MainCSSBundlePath string `json:"main.css"`
EmbedJSBundlePath string `json:"embed.js"`
EmbedCSSBundlePath string `json:"embed.css"`
}