Files
notesnook/apps/web/vite.config.ts
Abdullah Atta c23f31f284 web: use service worker for cross-tab communication only as a fallback
This commit basically reverts 1b63b2dccf.
Using service worker for this by default is a bad idea because:

1. It takes some time for the service worker to be installed and ready
which slows down first launch significantly
2. Service worker is not as reliable when updating from version to
version

We can, however, use it as a fallback i.e for devices that don't yet
support shared worker.
2025-04-10 10:29:26 +05:00

279 lines
8.0 KiB
TypeScript

/*
This file is part of the Notesnook project (https://notesnook.com/)
Copyright (C) 2023 Streetwriters (Private) Limited
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
import { Plugin, PluginOption, ResolvedConfig, defineConfig } from "vite";
import react from "@vitejs/plugin-react-swc";
import svgrPlugin from "vite-plugin-svgr";
import envCompatible from "vite-plugin-env-compatible";
import { VitePWA } from "vite-plugin-pwa";
import autoprefixer from "autoprefixer";
import { WEB_MANIFEST } from "./web-manifest";
import { execSync } from "child_process";
import { version } from "./package.json";
import { visualizer } from "rollup-plugin-visualizer";
import { OutputPlugin } from "rollup";
import path from "path";
const gitHash = (() => {
try {
return execSync("git rev-parse --short HEAD").toString().trim();
} catch (e) {
return process.env.GIT_HASH || "gitless";
}
})();
// const appVersion = version.replaceAll(".", "").replace("-beta", "");
const isBeta = version.includes("-beta");
const isTesting =
process.env.TEST === "true" || process.env.NODE_ENV === "development";
const isDesktop = process.env.PLATFORM === "desktop";
const isThemeBuilder = process.env.THEME_BUILDER === "true";
const isAnalyzing = process.env.ANALYZING === "true";
export default defineConfig({
envPrefix: "NN_",
root: "src/",
publicDir: isThemeBuilder ? path.join(__dirname, "public") : "../public",
build: {
target: isDesktop ? "esnext" : "modules",
outDir: "../build",
minify: "esbuild",
cssMinify: true,
emptyOutDir: true,
sourcemap: !isDesktop,
rollupOptions: {
output: {
plugins: [emitEditorStyles()],
assetFileNames: "assets/[name]-[hash:12][extname]",
chunkFileNames: "assets/[name]-[hash:12].js",
manualChunks: (id: string) => {
if (
(id.includes("/editor/languages/") ||
id.includes("/html/languages/") ||
id.includes("/refractor/lang/")) &&
path.basename(id) !== "index.js"
)
return `code-lang-${path.basename(id, "js")}`;
return null;
}
}
}
},
define: {
APP_TITLE: `"${isThemeBuilder ? "Notesnook Theme Builder" : "Notesnook"}"`,
GIT_HASH: `"${gitHash}"`,
APP_VERSION: `"${version}"`,
PUBLIC_URL: `"${process.env.PUBLIC_URL || ""}"`,
IS_DESKTOP_APP: isDesktop,
PLATFORM: `"${process.env.PLATFORM}"`,
IS_TESTING: process.env.TEST === "true",
IS_BETA: isBeta,
IS_THEME_BUILDER: isThemeBuilder
},
logLevel: process.env.NODE_ENV === "production" ? "warn" : "info",
resolve: {
dedupe: [
"react",
"react-dom",
"@mdi/js",
"@mdi/react",
"@emotion/react",
"katex",
"react-modal",
"dayjs",
"@streetwriters/kysely"
],
alias: [
{
find: /\/desktop-bridge$/gm,
replacement: isDesktop
? "/desktop-bridge/index.desktop"
: "/desktop-bridge/index"
},
{
find: /\/sqlite$/gm,
replacement: isDesktop ? "/sqlite/index.desktop" : "/sqlite/index"
}
]
},
server: {
port: 3000
},
worker: {
format: "es",
rollupOptions: {
output: {
assetFileNames: "assets/[name]-[hash:12][extname]",
chunkFileNames: "assets/[name]-[hash:12].js",
inlineDynamicImports: true
}
}
},
css: {
postcss: {
plugins: [autoprefixer()]
}
},
plugins: [
...(isAnalyzing
? [
visualizer({
gzipSize: true,
brotliSize: true,
open: true
}) as PluginOption
]
: []),
...((isThemeBuilder || isDesktop) && process.env.NODE_ENV === "production"
? []
: [
VitePWA({
strategies: "injectManifest",
minify: true,
manifest: WEB_MANIFEST,
injectRegister: null,
srcDir: "",
filename: "service-worker.ts",
mode: "production",
workbox: { mode: "production" },
injectManifest: {
globPatterns: ["**/*.{js,css,html,wasm}", "**/Inter-*.woff2"],
globIgnores: [
"**/node_modules/**/*",
"**/code-lang-*.js",
"pdf.worker.min.js"
]
}
})
]),
react({
plugins: isTesting
? undefined
: [
[
"@swc/plugin-react-remove-properties",
{
properties: ["^data-test-id$"]
}
]
]
}),
envCompatible({
prefix: "NN_",
mountedPath: "process.env"
}),
svgrPlugin({
svgrOptions: {
icon: true,
namedExport: "ReactComponent"
// ...svgr options (https://react-svgr.com/docs/options/)
}
}),
...(isDesktop
? []
: [
prefetchPlugin({
excludeFn: (assetName) =>
assetName.includes("wa-sqlite-async") ||
!assetName.includes("wa-sqlite")
})
])
]
});
function emitEditorStyles(): OutputPlugin {
return {
name: "rollup-plugin-emit-editor-styles",
generateBundle(options, bundle) {
for (const file in bundle) {
const chunk = bundle[file];
if (
chunk.type === "asset" &&
chunk.fileName.endsWith(".css") &&
typeof chunk.source === "string" &&
(chunk.source.includes("KaTeX_Fraktur-Bold-") ||
chunk.source.includes("Hack typeface"))
) {
this.emitFile({
type: "asset",
fileName: "assets/editor-styles.css",
name: "editor-styles.css",
source: chunk.source
});
}
}
}
};
}
function prefetchPlugin(options?: {
excludeFn?: (assetName: string) => boolean;
}): Plugin {
let config: ResolvedConfig;
return {
name: "vite-plugin-bundle-prefetch",
apply: "build",
configResolved(resolvedConfig: ResolvedConfig) {
// store the resolved config
config = resolvedConfig;
},
transformIndexHtml(
html: string,
ctx: {
path: string;
filename: string;
bundle?: import("rollup").OutputBundle;
chunk?: import("rollup").OutputChunk;
}
) {
const bundles = Object.keys(ctx.bundle ?? {});
const isLegacy = bundles.some((bundle) => bundle.includes("legacy"));
if (isLegacy) {
//legacy build won't add prefetch
return html;
}
// remove map files
let modernBundles = bundles.filter(
(bundle) => bundle.endsWith(".map") === false
);
const excludeFn = options?.excludeFn;
if (excludeFn) {
modernBundles = modernBundles.filter((bundle) => !excludeFn(bundle));
}
// Remove existing files and concatenate them into link tags
const prefechBundlesString = modernBundles
.filter((bundle) => html.includes(bundle) === false)
.map((bundle) => `<link rel="prefetch" href="${config.base}${bundle}">`)
.join("\n");
// Use regular expression to get the content within <head> </head>
const headContent = html.match(/<head>([\s\S]*)<\/head>/)?.[1] ?? "";
// Insert the content of prefetch into the head
const newHeadContent = `${headContent}${prefechBundlesString}`;
// Replace the original head
html = html.replace(
/<head>([\s\S]*)<\/head>/,
`<head>${newHeadContent}</head>`
);
return html;
}
};
}