mirror of
https://github.com/grimsi/gameyfin.git
synced 2026-02-06 11:27:07 +00:00
* Switched from TomCat to Jetty * Hibernate migrations * Removed dependency on Spring-Boot-Content-FS * Migrate to Jackson 3 * Migrate LegacyExtensionFinder -> IndexedExtensionFinder * Fix code inspection issues * Exclude Config classes from Sonar coverage calcualtion * Add FileStorageServiceTest * Add tests for (De-)serializers * Exclude H2 package from Sonar coverage reporting * Add Sonar scan * Update JVM in CI * Update dependency versions
675 lines
26 KiB
TypeScript
675 lines
26 KiB
TypeScript
/**
|
|
* NOTICE: this is an auto-generated file
|
|
*
|
|
* This file has been generated by the `flow:prepare-frontend` maven goal.
|
|
* This file will be overwritten on every run. Any custom changes should be made to vite.config.ts
|
|
*/
|
|
import path from 'path';
|
|
import { existsSync, mkdirSync, readdirSync, readFileSync, writeFileSync, Stats } from 'fs';
|
|
import { createHash } from 'crypto';
|
|
import * as net from 'net';
|
|
|
|
import { processThemeResources } from './build/plugins/application-theme-plugin/theme-handle.js';
|
|
import { rewriteCssUrls } from './build/plugins/theme-loader/theme-loader-utils.js';
|
|
import { addFunctionComponentSourceLocationBabel } from './build/plugins/react-function-location-plugin/react-function-location-plugin.js';
|
|
import settings from './build/vaadin-dev-server-settings.json';
|
|
import {
|
|
AssetInfo,
|
|
ChunkInfo,
|
|
defineConfig,
|
|
mergeConfig,
|
|
OutputOptions,
|
|
PluginOption,
|
|
UserConfigFn
|
|
} from 'vite';
|
|
|
|
import * as rollup from 'rollup';
|
|
import brotli from 'rollup-plugin-brotli';
|
|
import checker from 'vite-plugin-checker';
|
|
import postcssLit from './build/plugins/rollup-plugin-postcss-lit-custom/rollup-plugin-postcss-lit.js';
|
|
import vaadinI18n from './build/plugins/rollup-plugin-vaadin-i18n/rollup-plugin-vaadin-i18n.js';
|
|
import serviceWorkerPlugin from './build/plugins/vite-plugin-service-worker';
|
|
import vaadinBundlesPlugin from './build/plugins/vite-plugin-vaadin-bundles';
|
|
|
|
import { visualizer } from 'rollup-plugin-visualizer';
|
|
import reactPlugin from '@vitejs/plugin-react';
|
|
|
|
|
|
import vitePluginFileSystemRouter from '@vaadin/hilla-file-router/vite-plugin.js';
|
|
|
|
const frontendFolder = path.resolve(__dirname, settings.frontendFolder);
|
|
const themeFolder = path.resolve(frontendFolder, settings.themeFolder);
|
|
const frontendBundleFolder = path.resolve(__dirname, settings.frontendBundleOutput);
|
|
const devBundleFolder = path.resolve(__dirname, settings.devBundleOutput);
|
|
const devBundle = !!process.env.devBundle;
|
|
const jarResourcesFolder = path.resolve(__dirname, settings.jarResourcesFolder);
|
|
const themeResourceFolder = path.resolve(__dirname, settings.themeResourceFolder);
|
|
const projectPackageJsonFile = path.resolve(__dirname, 'package.json');
|
|
|
|
const buildOutputFolder = devBundle ? devBundleFolder : frontendBundleFolder;
|
|
const statsFolder = path.resolve(__dirname, devBundle ? settings.devBundleStatsOutput : settings.statsOutput);
|
|
const statsFile = path.resolve(statsFolder, 'stats.json');
|
|
const bundleSizeFile = path.resolve(statsFolder, 'bundle-size.html');
|
|
const i18nFolder = path.resolve(__dirname, settings.i18nOutput);
|
|
const nodeModulesFolder = path.resolve(__dirname, 'node_modules');
|
|
const webComponentTags = '';
|
|
|
|
const projectIndexHtml = path.resolve(frontendFolder, 'index.html');
|
|
|
|
const projectStaticAssetsFolders = [
|
|
path.resolve(__dirname, 'src', 'main', 'resources', 'META-INF', 'resources'),
|
|
path.resolve(__dirname, 'src', 'main', 'resources', 'static'),
|
|
frontendFolder
|
|
];
|
|
|
|
// Folders in the project which can contain application themes
|
|
const themeProjectFolders = projectStaticAssetsFolders.map((folder) => path.resolve(folder, settings.themeFolder));
|
|
|
|
const themeOptions = {
|
|
devMode: false,
|
|
useDevBundle: devBundle,
|
|
// The following matches folder 'frontend/generated/themes/'
|
|
// (not 'frontend/themes') for theme in JAR that is copied there
|
|
themeResourceFolder: path.resolve(themeResourceFolder, settings.themeFolder),
|
|
themeProjectFolders: themeProjectFolders,
|
|
projectStaticAssetsOutputFolder: devBundle
|
|
? path.resolve(devBundleFolder, '../assets')
|
|
: path.resolve(__dirname, settings.staticOutput),
|
|
frontendGeneratedFolder: path.resolve(frontendFolder, settings.generatedFolder),
|
|
projectStaticOutput: path.resolve(__dirname, settings.staticOutput),
|
|
javaResourceFolder: settings.javaResourceFolder ? path.resolve(__dirname, settings.javaResourceFolder) : ''
|
|
};
|
|
|
|
const hasExportedWebComponents = existsSync(path.resolve(frontendFolder, 'web-component.html'));
|
|
const commercialBannerComponent = path.resolve(frontendFolder, settings.generatedFolder, 'commercial-banner.js');
|
|
const hasCommercialBanner = existsSync(commercialBannerComponent);
|
|
|
|
const target = ['es2023'];
|
|
|
|
// Block debug and trace logs.
|
|
console.trace = () => {};
|
|
console.debug = () => {};
|
|
|
|
function statsExtracterPlugin(): PluginOption {
|
|
function collectThemeJsonsInFrontend(themeJsonContents: Record<string, string>, themeName: string) {
|
|
const themeJson = path.resolve(frontendFolder, settings.themeFolder, themeName, 'theme.json');
|
|
if (existsSync(themeJson)) {
|
|
const themeJsonContent = readFileSync(themeJson, { encoding: 'utf-8' }).replace(/\r\n/g, '\n');
|
|
themeJsonContents[themeName] = themeJsonContent;
|
|
const themeJsonObject = JSON.parse(themeJsonContent);
|
|
if (themeJsonObject.parent) {
|
|
collectThemeJsonsInFrontend(themeJsonContents, themeJsonObject.parent);
|
|
}
|
|
}
|
|
}
|
|
|
|
return {
|
|
name: 'vaadin:stats',
|
|
enforce: 'post',
|
|
async writeBundle(options: OutputOptions, bundle: { [fileName: string]: AssetInfo | ChunkInfo }) {
|
|
const modules = Object.values(bundle).flatMap((b) => (b.modules ? Object.keys(b.modules) : []));
|
|
const nodeModulesFolders = modules
|
|
.map((id) => id.replace(/\\/g, '/'))
|
|
.filter((id) => id.startsWith(nodeModulesFolder.replace(/\\/g, '/')))
|
|
.map((id) => id.substring(nodeModulesFolder.length + 1));
|
|
const npmModules = nodeModulesFolders
|
|
.map((id) => id.replace(/\\/g, '/'))
|
|
.map((id) => {
|
|
const parts = id.split('/');
|
|
if (id.startsWith('@')) {
|
|
return parts[0] + '/' + parts[1];
|
|
} else {
|
|
return parts[0];
|
|
}
|
|
})
|
|
.sort()
|
|
.filter((value, index, self) => self.indexOf(value) === index);
|
|
const npmModuleAndVersion = Object.fromEntries(npmModules.map((module) => [module, getVersion(module)]));
|
|
const cvdls = Object.fromEntries(
|
|
npmModules
|
|
.filter((module) => getCvdlName(module) != null)
|
|
.map((module) => [module, { name: getCvdlName(module), version: getVersion(module) }])
|
|
);
|
|
|
|
mkdirSync(path.dirname(statsFile), { recursive: true });
|
|
const projectPackageJson = JSON.parse(readFileSync(projectPackageJsonFile, { encoding: 'utf-8' }));
|
|
|
|
const entryScripts = Object.values(bundle)
|
|
.filter((bundle) => bundle.isEntry)
|
|
.map((bundle) => bundle.fileName);
|
|
|
|
const generatedIndexHtml = path.resolve(buildOutputFolder, 'index.html');
|
|
const customIndexData: string = readFileSync(projectIndexHtml, { encoding: 'utf-8' });
|
|
const generatedIndexData: string = readFileSync(generatedIndexHtml, {
|
|
encoding: 'utf-8'
|
|
});
|
|
|
|
const customIndexRows = new Set(customIndexData.split(/[\r\n]/).filter((row) => row.trim() !== ''));
|
|
const generatedIndexRows = generatedIndexData.split(/[\r\n]/).filter((row) => row.trim() !== '');
|
|
|
|
const rowsGenerated: string[] = [];
|
|
generatedIndexRows.forEach((row) => {
|
|
if (!customIndexRows.has(row)) {
|
|
rowsGenerated.push(row);
|
|
}
|
|
});
|
|
|
|
//After dev-bundle build add used Flow frontend imports JsModule/JavaScript/CssImport
|
|
|
|
const parseImports = (filename: string, result: Set<string>): void => {
|
|
const content: string = readFileSync(filename, { encoding: 'utf-8' });
|
|
const lines = content.split('\n');
|
|
const staticImports = lines
|
|
.filter((line) => line.startsWith('import '))
|
|
.map((line) => line.substring(line.indexOf("'") + 1, line.lastIndexOf("'")))
|
|
.map((line) => (line.includes('?') ? line.substring(0, line.lastIndexOf('?')) : line));
|
|
const dynamicImports = lines
|
|
.filter((line) => line.includes('import('))
|
|
.map((line) => line.replace(/.*import\(/, ''))
|
|
.map((line) => line.split(/'/)[1])
|
|
.map((line) => (line.includes('?') ? line.substring(0, line.lastIndexOf('?')) : line));
|
|
|
|
staticImports.forEach((staticImport) => result.add(staticImport));
|
|
|
|
dynamicImports.map((dynamicImport) => {
|
|
const importedFile = path.resolve(path.dirname(filename), dynamicImport);
|
|
parseImports(importedFile, result);
|
|
});
|
|
};
|
|
|
|
const generatedImportsSet = new Set<string>();
|
|
parseImports(
|
|
path.resolve(themeOptions.frontendGeneratedFolder, 'flow', 'generated-flow-imports.js'),
|
|
generatedImportsSet
|
|
);
|
|
parseImports(
|
|
path.resolve(themeOptions.frontendGeneratedFolder, 'app-shell-imports.js'),
|
|
generatedImportsSet
|
|
);
|
|
const generatedImports = Array.from(generatedImportsSet).sort();
|
|
|
|
const frontendFiles: Record<string, string> = {};
|
|
frontendFiles['index.html'] = createHash('sha256').update(customIndexData.replace(/\r\n/g, '\n'), 'utf8').digest('hex');
|
|
|
|
const projectFileExtensions = ['.js', '.js.map', '.ts', '.ts.map', '.tsx', '.tsx.map', '.css', '.css.map'];
|
|
|
|
const isThemeComponentsResource = (id: string) =>
|
|
id.startsWith(themeOptions.frontendGeneratedFolder.replace(/\\/g, '/'))
|
|
&& id.match(/.*\/jar-resources\/themes\/[^\/]+\/components\//);
|
|
|
|
const isGeneratedWebComponentResource = (id: string) =>
|
|
id.startsWith(themeOptions.frontendGeneratedFolder.replace(/\\/g, '/'))
|
|
&& id.match(/.*\/flow\/web-components\//);
|
|
|
|
const isFrontendResourceCollected = (id: string) =>
|
|
!id.startsWith(themeOptions.frontendGeneratedFolder.replace(/\\/g, '/'))
|
|
|| isThemeComponentsResource(id)
|
|
|| isGeneratedWebComponentResource(id);
|
|
|
|
// collects project's frontend resources in frontend folder, excluding
|
|
// 'generated' sub-folder, except for legacy shadow DOM stylesheets
|
|
// packaged in `theme/components/` folder
|
|
// and generated web component resources in `flow/web-components` folder.
|
|
modules
|
|
.map((id) => id.replace(/\\/g, '/'))
|
|
.filter((id) => id.startsWith(frontendFolder.replace(/\\/g, '/')))
|
|
.filter(isFrontendResourceCollected)
|
|
.map((id) => id.substring(frontendFolder.length + 1))
|
|
.map((line: string) => (line.includes('?') ? line.substring(0, line.lastIndexOf('?')) : line))
|
|
.forEach((line: string) => {
|
|
// \r\n from windows made files may be used so change to \n
|
|
const filePath = path.resolve(frontendFolder, line);
|
|
if (projectFileExtensions.includes(path.extname(filePath))) {
|
|
const fileBuffer = readFileSync(filePath, { encoding: 'utf-8' }).replace(/\r\n/g, '\n');
|
|
frontendFiles[line] = createHash('sha256').update(fileBuffer, 'utf8').digest('hex');
|
|
}
|
|
});
|
|
|
|
// collects frontend resources from the JARs
|
|
generatedImports
|
|
.filter((line: string) => line.includes('generated/jar-resources'))
|
|
.forEach((line: string) => {
|
|
let filename = line.substring(line.indexOf('generated'));
|
|
// \r\n from windows made files may be used ro remove to be only \n
|
|
const fileBuffer = readFileSync(path.resolve(frontendFolder, filename), { encoding: 'utf-8' }).replace(
|
|
/\r\n/g,
|
|
'\n'
|
|
);
|
|
const hash = createHash('sha256').update(fileBuffer, 'utf8').digest('hex');
|
|
|
|
const fileKey = line.substring(line.indexOf('jar-resources/') + 14);
|
|
frontendFiles[fileKey] = hash;
|
|
});
|
|
// collects and hash rest of the Frontend resources excluding files in /generated/ and /themes/
|
|
// and files already in frontendFiles.
|
|
let frontendFolderAlias = "Frontend";
|
|
generatedImports
|
|
.filter((line: string) => line.startsWith(frontendFolderAlias + '/'))
|
|
.filter((line: string) => !line.startsWith(frontendFolderAlias + '/generated/'))
|
|
.filter((line: string) => !line.startsWith(frontendFolderAlias + '/themes/'))
|
|
.map((line) => line.substring(frontendFolderAlias.length + 1))
|
|
.filter((line: string) => !frontendFiles[line])
|
|
.forEach((line: string) => {
|
|
const filePath = path.resolve(frontendFolder, line);
|
|
if (projectFileExtensions.includes(path.extname(filePath)) && existsSync(filePath)) {
|
|
const fileBuffer = readFileSync(filePath, { encoding: 'utf-8' }).replace(/\r\n/g, '\n');
|
|
frontendFiles[line] = createHash('sha256').update(fileBuffer, 'utf8').digest('hex');
|
|
}
|
|
});
|
|
// If a index.ts exists hash it to be able to see if it changes.
|
|
if (existsSync(path.resolve(frontendFolder, 'index.ts'))) {
|
|
const fileBuffer = readFileSync(path.resolve(frontendFolder, 'index.ts'), { encoding: 'utf-8' }).replace(
|
|
/\r\n/g,
|
|
'\n'
|
|
);
|
|
frontendFiles[`index.ts`] = createHash('sha256').update(fileBuffer, 'utf8').digest('hex');
|
|
}
|
|
if (hasCommercialBanner) {
|
|
const fileBuffer = readFileSync(commercialBannerComponent, { encoding: 'utf-8' }).replace(/\r\n/g, '\n');
|
|
frontendFiles[settings.generatedFolder + '/commercial-banner.js'] = createHash('sha256').update(fileBuffer, 'utf8').digest('hex');
|
|
}
|
|
|
|
const themeJsonContents: Record<string, string> = {};
|
|
const themesFolder = path.resolve(jarResourcesFolder, 'themes');
|
|
if (existsSync(themesFolder)) {
|
|
readdirSync(themesFolder).forEach((themeFolder) => {
|
|
const themeJson = path.resolve(themesFolder, themeFolder, 'theme.json');
|
|
if (existsSync(themeJson)) {
|
|
themeJsonContents[path.basename(themeFolder)] = readFileSync(themeJson, { encoding: 'utf-8' }).replace(
|
|
/\r\n/g,
|
|
'\n'
|
|
);
|
|
}
|
|
});
|
|
}
|
|
|
|
collectThemeJsonsInFrontend(themeJsonContents, settings.themeName);
|
|
|
|
let webComponents: string[] = [];
|
|
if (webComponentTags) {
|
|
webComponents = webComponentTags.split(';');
|
|
}
|
|
|
|
const stats = {
|
|
packageJsonDependencies: projectPackageJson.dependencies,
|
|
npmModules: npmModuleAndVersion,
|
|
bundleImports: generatedImports,
|
|
frontendHashes: frontendFiles,
|
|
themeJsonContents: themeJsonContents,
|
|
entryScripts,
|
|
webComponents,
|
|
cvdlModules: cvdls,
|
|
packageJsonHash: projectPackageJson?.vaadin?.hash,
|
|
indexHtmlGenerated: rowsGenerated
|
|
};
|
|
writeFileSync(statsFile, JSON.stringify(stats, null, 1));
|
|
}
|
|
};
|
|
}
|
|
|
|
function themePlugin(opts: { devMode: boolean }): PluginOption {
|
|
const fullThemeOptions = { ...themeOptions, devMode: opts.devMode };
|
|
return {
|
|
name: 'vaadin:theme',
|
|
config() {
|
|
processThemeResources(fullThemeOptions, console);
|
|
},
|
|
configureServer(server) {
|
|
function handleThemeFileCreateDelete(themeFile: string, stats?: Stats) {
|
|
if (themeFile.startsWith(themeFolder)) {
|
|
const changed = path.relative(themeFolder, themeFile);
|
|
console.debug('Theme file ' + (!!stats ? 'created' : 'deleted'), changed);
|
|
processThemeResources(fullThemeOptions, console);
|
|
}
|
|
}
|
|
server.watcher.on('add', handleThemeFileCreateDelete);
|
|
server.watcher.on('unlink', handleThemeFileCreateDelete);
|
|
},
|
|
handleHotUpdate(context) {
|
|
const contextPath = path.resolve(context.file);
|
|
const themePath = path.resolve(themeFolder);
|
|
if (contextPath.startsWith(themePath)) {
|
|
const changed = path.relative(themePath, contextPath);
|
|
|
|
console.debug('Theme file changed', changed);
|
|
|
|
if (changed.startsWith(settings.themeName)) {
|
|
processThemeResources(fullThemeOptions, console);
|
|
}
|
|
}
|
|
},
|
|
async resolveId(id, importer) {
|
|
// force theme generation if generated theme sources does not yet exist
|
|
// this may happen for example during Java hot reload when updating
|
|
// @Theme annotation value
|
|
if (
|
|
path.resolve(themeOptions.frontendGeneratedFolder, 'theme.js') === importer &&
|
|
!existsSync(path.resolve(themeOptions.frontendGeneratedFolder, id))
|
|
) {
|
|
console.debug('Generate theme file ' + id + ' not existing. Processing theme resource');
|
|
processThemeResources(fullThemeOptions, console);
|
|
return;
|
|
}
|
|
if (!id.startsWith(settings.themeFolder)) {
|
|
return;
|
|
}
|
|
for (const location of [themeResourceFolder, frontendFolder]) {
|
|
const result = await this.resolve(path.resolve(location, id));
|
|
if (result) {
|
|
return result;
|
|
}
|
|
}
|
|
},
|
|
async transform(raw, id, options) {
|
|
// rewrite urls for the application theme css files
|
|
const [bareId, query] = id.split('?');
|
|
if (
|
|
(!bareId?.startsWith(themeFolder) && !bareId?.startsWith(themeOptions.themeResourceFolder)) ||
|
|
!bareId?.endsWith('.css')
|
|
) {
|
|
return;
|
|
}
|
|
const resourceThemeFolder = bareId.startsWith(themeFolder) ? themeFolder : themeOptions.themeResourceFolder;
|
|
const [themeName] = bareId.substring(resourceThemeFolder.length + 1).split('/');
|
|
return rewriteCssUrls(raw, path.dirname(bareId), path.resolve(resourceThemeFolder, themeName), console, opts);
|
|
}
|
|
};
|
|
}
|
|
|
|
function runWatchDog(watchDogPort: number, watchDogHost: string | undefined) {
|
|
const client = new net.Socket();
|
|
client.setEncoding('utf8');
|
|
client.on('error', function (err) {
|
|
console.log('Watchdog connection error. Terminating vite process...', err);
|
|
client.destroy();
|
|
process.exit(0);
|
|
});
|
|
client.on('close', function () {
|
|
client.destroy();
|
|
runWatchDog(watchDogPort, watchDogHost);
|
|
});
|
|
|
|
client.connect(watchDogPort, watchDogHost || 'localhost');
|
|
}
|
|
|
|
const allowedFrontendFolders = [frontendFolder, nodeModulesFolder];
|
|
|
|
function showRecompileReason(): PluginOption {
|
|
return {
|
|
name: 'vaadin:why-you-compile',
|
|
handleHotUpdate(context) {
|
|
console.log('Recompiling because', context.file, 'changed');
|
|
}
|
|
};
|
|
}
|
|
|
|
const DEV_MODE_START_REGEXP = /\/\*[\*!]\s+vaadin-dev-mode:start/;
|
|
const DEV_MODE_CODE_REGEXP = /\/\*[\*!]\s+vaadin-dev-mode:start([\s\S]*)vaadin-dev-mode:end\s+\*\*\//i;
|
|
|
|
function preserveUsageStats() {
|
|
return {
|
|
name: 'vaadin:preserve-usage-stats',
|
|
|
|
transform(src: string, id: string) {
|
|
if (id.includes('vaadin-usage-statistics')) {
|
|
if (src.includes('vaadin-dev-mode:start')) {
|
|
const expectedComment = '/*! vaadin-dev-mode:start';
|
|
const newSrc = src.replace(DEV_MODE_START_REGEXP, expectedComment);
|
|
if (newSrc === src) {
|
|
if (!src.includes(expectedComment)) {
|
|
console.error('vaadin-dev-mode:start tag not found');
|
|
}
|
|
} else if (!newSrc.match(DEV_MODE_CODE_REGEXP)) {
|
|
console.error('New comment fails to match original regexp');
|
|
} else {
|
|
return { code: newSrc };
|
|
}
|
|
}
|
|
}
|
|
|
|
return { code: src };
|
|
}
|
|
};
|
|
}
|
|
|
|
export const vaadinConfig: UserConfigFn = (env) => {
|
|
const devMode = env.mode === 'development';
|
|
const productionMode = !devMode && !devBundle
|
|
const commercialBanner = productionMode && hasCommercialBanner;
|
|
|
|
if (devMode && process.env.watchDogPort) {
|
|
// Open a connection with the Java dev-mode handler in order to finish
|
|
// vite when it exits or crashes.
|
|
runWatchDog(parseInt(process.env.watchDogPort), process.env.watchDogHost);
|
|
}
|
|
|
|
return {
|
|
root: frontendFolder,
|
|
base: '',
|
|
publicDir: false,
|
|
resolve: {
|
|
alias: {
|
|
'@vaadin/flow-frontend': jarResourcesFolder,
|
|
Frontend: frontendFolder
|
|
},
|
|
preserveSymlinks: true
|
|
},
|
|
define: {
|
|
OFFLINE_PATH: settings.offlinePath,
|
|
VITE_ENABLED: 'true'
|
|
},
|
|
server: {
|
|
host: '127.0.0.1',
|
|
strictPort: true,
|
|
fs: {
|
|
allow: allowedFrontendFolders
|
|
}
|
|
},
|
|
esbuild: {
|
|
legalComments: 'inline',
|
|
},
|
|
build: {
|
|
minify: productionMode,
|
|
outDir: buildOutputFolder,
|
|
emptyOutDir: devBundle,
|
|
assetsDir: 'VAADIN/build',
|
|
target,
|
|
rollupOptions: {
|
|
input: {
|
|
indexhtml: projectIndexHtml,
|
|
|
|
...(hasExportedWebComponents ? { webcomponenthtml: path.resolve(frontendFolder, 'web-component.html') } : {})
|
|
},
|
|
output: {
|
|
// Workaround to enable dynamic imports with top-level await for
|
|
// commonjs modules, such as "atmosphere.js" in Hilla. Extracting
|
|
// Rollup's commonjs helpers into separate manual chunk avoids
|
|
// circular dependencies in this case. Caused
|
|
// - https://github.com/vitejs/vite/issues/10995
|
|
// - https://github.com/rollup/rollup/issues/5884
|
|
// - https://github.com/vitejs/vite/issues/19695
|
|
// - https://github.com/vitejs/vite/issues/12209
|
|
manualChunks: (id: string) => id.startsWith('\0commonjsHelpers.js') ? 'commonjsHelpers' : null
|
|
},
|
|
onwarn: (warning: rollup.RollupLog, defaultHandler: rollup.LoggingFunction) => {
|
|
const ignoreEvalWarning = [
|
|
'generated/jar-resources/FlowClient.js',
|
|
'generated/jar-resources/vaadin-spreadsheet/spreadsheet-export.js',
|
|
'@vaadin/charts/src/helpers.js'
|
|
];
|
|
if (warning.code === 'EVAL' && warning.id && !!ignoreEvalWarning.find((id) => warning.id?.endsWith(id))) {
|
|
return;
|
|
}
|
|
defaultHandler(warning);
|
|
}
|
|
}
|
|
},
|
|
optimizeDeps: {
|
|
esbuildOptions: {
|
|
target,
|
|
},
|
|
entries: [
|
|
// Pre-scan entrypoints in Vite to avoid reloading on first open
|
|
'generated/vaadin.ts'
|
|
],
|
|
exclude: [
|
|
'@vaadin/router',
|
|
'@vaadin/vaadin-license-checker',
|
|
'@vaadin/vaadin-usage-statistics',
|
|
'workbox-core',
|
|
'workbox-precaching',
|
|
'workbox-routing',
|
|
'workbox-strategies'
|
|
]
|
|
},
|
|
plugins: [
|
|
productionMode && brotli(),
|
|
devMode && vaadinBundlesPlugin({
|
|
nodeModulesFolder
|
|
}),
|
|
devMode && showRecompileReason(),
|
|
settings.offlineEnabled && serviceWorkerPlugin({
|
|
srcPath: settings.clientServiceWorkerSource,
|
|
}),
|
|
!devMode && statsExtracterPlugin(),
|
|
!productionMode && preserveUsageStats(),
|
|
themePlugin({ devMode }),
|
|
postcssLit({
|
|
include: ['**/*.css', /.*\/.*\.css\?.*/],
|
|
exclude: [
|
|
`${themeFolder}/**/*.css`,
|
|
new RegExp(`${themeFolder}/.*/.*\\.css\\?.*`),
|
|
`${themeResourceFolder}/**/*.css`,
|
|
new RegExp(`${themeResourceFolder}/.*/.*\\.css\\?.*`),
|
|
new RegExp('.*/.*\\?html-proxy.*')
|
|
]
|
|
}),
|
|
// The React plugin provides fast refresh and debug source info
|
|
reactPlugin({
|
|
include: '**/*.tsx',
|
|
babel: {
|
|
// We need to use babel to provide the source information for it to be correct
|
|
// (otherwise Babel will slightly rewrite the source file and esbuild generate source info for the modified file)
|
|
presets: [
|
|
[
|
|
'@babel/preset-react',
|
|
{
|
|
runtime: 'automatic',
|
|
importSource: productionMode ? 'react' : 'Frontend/generated/jsx-dev-transform',
|
|
development: !productionMode
|
|
}
|
|
]
|
|
],
|
|
// React writes the source location for where components are used, this writes for where they are defined
|
|
plugins: [
|
|
!productionMode && addFunctionComponentSourceLocationBabel(),
|
|
[
|
|
'module:@preact/signals-react-transform',
|
|
{
|
|
mode: 'all' // Needed to include translations which do not use something.value
|
|
}
|
|
]
|
|
].filter(Boolean)
|
|
}
|
|
}),
|
|
|
|
productionMode && vaadinI18n({
|
|
cwd: __dirname,
|
|
meta: {
|
|
output: {
|
|
dir: i18nFolder,
|
|
},
|
|
},
|
|
}),
|
|
{
|
|
name: 'vaadin:force-remove-html-middleware',
|
|
configureServer(server) {
|
|
return () => {
|
|
server.middlewares.stack = server.middlewares.stack.filter((mw) => {
|
|
const handleName = `${mw.handle}`;
|
|
return !handleName.includes('viteHtmlFallbackMiddleware');
|
|
});
|
|
};
|
|
},
|
|
},
|
|
hasExportedWebComponents && {
|
|
name: 'vaadin:inject-entrypoints-to-web-component-html',
|
|
transformIndexHtml: {
|
|
order: 'pre',
|
|
handler(_html, { path, server }) {
|
|
if (path !== '/web-component.html') {
|
|
return;
|
|
}
|
|
const scripts = [
|
|
{
|
|
tag: 'script',
|
|
attrs: { type: 'module', src: `/generated/vaadin-web-component.ts` },
|
|
injectTo: 'head'
|
|
}
|
|
];
|
|
if (commercialBanner) {
|
|
scripts.push({
|
|
tag: 'script',
|
|
attrs: { type: 'module', src: '/generated/commercial-banner.js' },
|
|
injectTo: 'head'
|
|
});
|
|
}
|
|
return scripts;
|
|
}
|
|
}
|
|
},
|
|
{
|
|
name: 'vaadin:inject-entrypoints-to-index-html',
|
|
transformIndexHtml: {
|
|
order: 'pre',
|
|
handler(_html, { path, server }) {
|
|
if (path !== '/index.html') {
|
|
return;
|
|
}
|
|
|
|
const scripts = [];
|
|
|
|
if (devMode) {
|
|
scripts.push({
|
|
tag: 'script',
|
|
attrs: { type: 'module', src: `/generated/vite-devmode.ts`, onerror: "document.location.reload()" },
|
|
injectTo: 'head'
|
|
});
|
|
}
|
|
scripts.push({
|
|
tag: 'script',
|
|
attrs: { type: 'module', src: '/generated/vaadin.ts' },
|
|
injectTo: 'head'
|
|
});
|
|
if (commercialBanner) {
|
|
scripts.push({
|
|
tag: 'script',
|
|
attrs: { type: 'module', src: '/generated/commercial-banner.js' },
|
|
injectTo: 'head'
|
|
});
|
|
}
|
|
return scripts;
|
|
}
|
|
}
|
|
},
|
|
vitePluginFileSystemRouter({isDevMode: devMode}),
|
|
checker({
|
|
typescript: true
|
|
}),
|
|
productionMode && visualizer({ brotliSize: true, filename: bundleSizeFile })
|
|
]
|
|
};
|
|
};
|
|
|
|
export const overrideVaadinConfig = (customConfig: UserConfigFn) => {
|
|
return defineConfig((env) => mergeConfig(vaadinConfig(env), customConfig(env)));
|
|
};
|
|
function getVersion(module: string): string {
|
|
const packageJson = path.resolve(nodeModulesFolder, module, 'package.json');
|
|
return JSON.parse(readFileSync(packageJson, { encoding: 'utf-8' })).version;
|
|
}
|
|
function getCvdlName(module: string): string {
|
|
const packageJson = path.resolve(nodeModulesFolder, module, 'package.json');
|
|
return JSON.parse(readFileSync(packageJson, { encoding: 'utf-8' })).cvdlName;
|
|
}
|