blob: 52defa45c683e2dafa604d6cc6cf5974d4178ea0 [file] [log] [blame]
// Copyright 2023 The LUCI Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
import * as path from 'node:path';
import replace from '@rollup/plugin-replace';
import react from '@vitejs/plugin-react';
import { defineConfig, loadEnv } from 'vite';
import { VitePWA } from 'vite-plugin-pwa';
import tsconfigPaths from 'vite-tsconfig-paths';
import { devServer } from './dev_utils/dev_server';
import { localAuthPlugin } from './dev_utils/local_auth_plugin';
import { preloadModulesPlugin } from './dev_utils/preload_modules_plugin';
import { previewServer } from './dev_utils/preview_server';
import {
getVirtualSettingsJsPlugin,
overrideMiloHostPlugin,
stableSettingsJsLinkPlugin,
} from './dev_utils/settings_js_utils';
import {
getVirtualUiVersionJsPlugin,
stableUiVersionJsLinkPlugin,
} from './dev_utils/ui_version_js_utils';
import { getBoolEnv } from './dev_utils/utils';
import { routes } from './src/core/routes';
import { regexpsForRoutes } from './src/generic_libs/tools/react_router_utils';
export default defineConfig(({ mode }) => {
const env = {
...Object.fromEntries(
Object.entries(process.env).filter(([k]) => k.startsWith('VITE_')),
),
...loadEnv(
mode,
process.cwd(),
// Our production UI build should not depend on any env vars.
// Still include `VITE_LOCAL_` env vars. They are used when previewing a
// production build locally.
mode === 'production' ? 'VITE_LOCAL_' : 'VITE_',
),
};
const virtualSettingsJs = getVirtualSettingsJsPlugin(mode, env);
const virtualUiVersionJs = getVirtualUiVersionJsPlugin(mode, env);
const overrideMiloHost = overrideMiloHostPlugin(env);
const defineRoutesRegex = replace({
preventAssignment: true,
include: ['./src/sw/ui_sw.ts'],
values: {
// The build pipeline gets confused when the service worker
// depends on a lazy-loaded assets (which are used in `routes`).
// Computes the defined routes at build time to avoid polluting
// the service worker with unwanted dependencies.
DEFINED_ROUTES_REGEXP: JSON.stringify(
regexpsForRoutes([{ path: 'ui', children: routes }]).source,
),
},
});
return {
base: '/ui',
build: {
// Vite doesn't like index.html to be placed anywhere other than at the
// root level of the input/output directory.
// Vite doesn't allow us to output any asset to a place outside of the
// defined output directory.
// We want to place root_sw.js in the parent directory of Vite's output
// directory because that closely matches how those files are served.
// e.g. `root_sw.js` is served at `/root_sw.js`, while other assets are
// served at `/ui/` or `/ui/immutable/`.
// As a result, we need to output Vite generated assets to `dist/ui` and
// split the `root_sw.js` generation to a separate built step.
outDir: 'dist/ui',
assetsDir: 'immutable',
sourcemap: true,
rollupOptions: {
// Silence source map generattion warning. This is a workaround for
// https://github.com/vitejs/vite/issues/15012.
onwarn: (warning, defaultHandler) =>
warning.code !== 'SOURCEMAP_ERROR' && defaultHandler(warning),
},
// Set to 8 MB to silence warnings. The files are cached by service worker
// anyway. Large chunks won't hurt much.
chunkSizeWarningLimit: Math.pow(2, 13),
},
assetsInclude: ['RELEASE_NOTES.md'],
plugins: [
replace({
preventAssignment: true,
// We have different building pipeline for dev/test/production builds.
// Limits the impact of the pipeline-specific features to only the
// entry files for better consistently across different builds.
include: ['./src/main.tsx'],
values: {
ENABLE_UI_SW: JSON.stringify(
getBoolEnv(env, 'VITE_ENABLE_UI_SW') ?? true,
),
},
}),
stableUiVersionJsLinkPlugin(),
virtualUiVersionJs,
stableSettingsJsLinkPlugin(),
virtualSettingsJs,
overrideMiloHost,
defineRoutesRegex,
preloadModulesPlugin(),
localAuthPlugin(),
devServer(env),
previewServer(path.join(__dirname, 'dist'), env),
react({
babel: {
configFile: true,
},
}),
// Support custom path mapping declared in tsconfig.json.
tsconfigPaths(),
// Needed to add workbox powered service workers, which enables us to
// implement some cache strategy that's not possible with regular HTTP
// cache due to dynamic URLs.
VitePWA({
injectRegister: null,
// We cannot use the simpler 'generateSW' mode because
// 1. we need to inject custom scripts that import other files, and
// 2. in 'generateSW' mode, custom scripts can only be injected via
// `workbox.importScripts`, which doesn't support ES modules, and
// 3. in 'generateSW' mode, we need to build the custom script to be
// imported by the service worker as an entry file, which means the
// resulting bundle may contain ES import statements due to
// [vite/rollup not supporting disabling code splitting][1].
//
// [1]: https://github.com/rollup/rollup/issues/2756
strategies: 'injectManifest',
srcDir: 'src/sw',
filename: 'ui_sw.ts',
devOptions: {
enabled: true,
// During development, the service worker script can only be a JS
// module, because it runs through the same pipeline as the rest of
// the scripts, which produces ES modules.
type: 'module',
navigateFallback: 'index.html',
},
manifest: {
theme_color: '#1976d2',
},
injectManifest: {
globPatterns: ['**/*.{js,css,html,svg,png}'],
buildPlugins: {
vite: [
defineRoutesRegex,
virtualUiVersionJs,
virtualSettingsJs,
overrideMiloHost,
tsconfigPaths(),
],
},
// Set to 8 MB. Some files might be larger than the default.
maximumFileSizeToCacheInBytes: Math.pow(2, 23),
},
}),
],
server: {
port: 8080,
strictPort: true,
// Proxy the queries to `self.location.host` to the configured milo server
// (typically https://luci-milo-dev.appspot.com) since we don't run the
// milo go server on the same host.
proxy: {
'^(?!/ui/.*$)': {
target: env['VITE_LOCAL_PROXY_URL'],
changeOrigin: true,
secure: false,
},
},
},
preview: {
port: 8000,
},
};
});