mirror of
https://github.com/actualbudget/actual.git
synced 2026-10-11 22:49:59 +00:00
[AI] lage - move browser build to using lage (#7602)
* Simplify desktop client browser build * [AI] Move browser build orchestration into vite config and lage Moves loot-core worker build, public/ staging (migrations, default-db, sql-wasm, data-file-index), and build-stats wiring from the deleted packages/desktop-client/bin/build-browser shell script into a lootCoreBackend vite plugin in packages/desktop-client/vite.config.mts. Adds a build:browser target to lage.config.js so bin/package-browser runs as a single `lage build:browser --to=@actual-app/web` call, with crdt + loot-core built via lage's ^build dependency before the desktop-client build. Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]> * Refactor e2e-test workflow and update desktop-client configurations * [AI] Move plugins-service staging into desktop-client vite config Declares plugins-service as a workspace devDependency of @actual-app/web so lage's ^build edge picks it up automatically in the build:browser pipeline, and moves the cross-package file staging (production copy + dev serving) into vite.config.mts, mirroring the lootCoreBackend pattern. Drops the plugins-service shell wrapper script and simplifies its package.json scripts to invoke vite build directly. Updates root start:browser to run plugins-service watch in parallel with the dev server instead of pre-building once. Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]> * [AI] Sync tsconfig project references for plugins-service edge Follow-up to the plugins-service workspace edge: adds the ../plugins-service project reference in packages/desktop-client/tsconfig.json via yarn sync:tsconfig-references. Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]> * Release notes * [AI] Ignore .venv/ so lage's git hasher skips Electron CI's Python venv Electron CI provisions a Python virtualenv at the repo root for setuptools. With browser builds now routed through lage, lage's git hash-object pass walks untracked-not-ignored files and fails on the venv's broken lib64 symlink ("fatal: Unable to hash .../.venv/lib64"). Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]> * [AI] Bake Weblate translations back into VRT/e2e bundle build-web set download-translations: false and relied on bin/package-browser's ad-hoc git clone + git pull. That path is fragile inside the playwright container, so vite's import.meta.glob('/locale/*.json') frequently produced an empty languages map and the bundle shipped with no en.json. VRTs then rendered source-code English and diffed against snapshots authored from Weblate strings. Route translation provisioning back through actions/checkout (download-translations: true) in build-web and vrt-update-generate, and add --skip-translations to bin/package-browser (mirroring bin/package-electron) so the in-script git pull is bypassed when CI has already staged the locale dir. Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]> * [AI] Skip translation cloning in build-web bundle for VRT determinism bin/package-browser used to unconditionally clone actualbudget/translations before vite ran, baking Weblate en.json into the build artifact. With the e2e-test pipeline now serving that artifact via serve-build.mjs, VRT screenshots ended up rendering Weblate strings — drifting from the snapshots, which were authored against source-code English (master VRTs ran on vite dev without a locale dir). Pass --skip-translations to bin/package-browser from build-web so the bundle ships with no locale chunks. download-translations stays 'false' across the e2e-test and vrt-update-generate workflows, matching the prior behavior. Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]> --------- Co-authored-by: Claude Opus 4.7 (1M context) <[email protected]>
This commit is contained in:
co-authored by
Claude Opus 4.7
parent
11ce29e7fd
commit
ff0f5bdb35
@@ -46,13 +46,12 @@ jobs:
|
|||||||
# via ConfigurationPage.createTestFile()) is still rendered in a
|
# via ConfigurationPage.createTestFile()) is still rendered in a
|
||||||
# production build. Without it, e2e tests would time out waiting for
|
# production build. Without it, e2e tests would time out waiting for
|
||||||
# a button that was tree-shaken out.
|
# a button that was tree-shaken out.
|
||||||
|
# --skip-translations keeps VRT screenshots deterministic by rendering
|
||||||
|
# source-code English instead of upstream Weblate en.json (which can
|
||||||
|
# drift between snapshot capture and test runs).
|
||||||
env:
|
env:
|
||||||
REACT_APP_NETLIFY: 'true'
|
REACT_APP_NETLIFY: 'true'
|
||||||
run: |
|
run: yarn build:browser --skip-translations
|
||||||
yarn workspace plugins-service build
|
|
||||||
yarn workspace @actual-app/crdt build
|
|
||||||
yarn workspace @actual-app/core build:browser
|
|
||||||
yarn workspace @actual-app/web build:browser
|
|
||||||
- name: Upload build artifact
|
- name: Upload build artifact
|
||||||
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
|
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
|
||||||
with:
|
with:
|
||||||
|
|||||||
@@ -42,6 +42,9 @@ bundle.desktop.js.map
|
|||||||
bundle.mobile.js
|
bundle.mobile.js
|
||||||
bundle.mobile.js.map
|
bundle.mobile.js.map
|
||||||
|
|
||||||
|
# Python virtualenv (Electron CI provisions one at the repo root for setuptools)
|
||||||
|
.venv/
|
||||||
|
|
||||||
# Yarn
|
# Yarn
|
||||||
.pnp.*
|
.pnp.*
|
||||||
.yarn/*
|
.yarn/*
|
||||||
|
|||||||
+25
-16
@@ -4,21 +4,30 @@ ROOT=`dirname $0`
|
|||||||
|
|
||||||
cd "$ROOT/.."
|
cd "$ROOT/.."
|
||||||
|
|
||||||
echo "Updating translations..."
|
SKIP_TRANSLATIONS=false
|
||||||
if ! [ -d packages/desktop-client/locale ]; then
|
while [[ $# -gt 0 ]]; do
|
||||||
git clone https://github.com/actualbudget/translations packages/desktop-client/locale
|
case "$1" in
|
||||||
|
--skip-translations)
|
||||||
|
SKIP_TRANSLATIONS=true
|
||||||
|
shift
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
echo "Unknown argument: $1" >&2
|
||||||
|
exit 1
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
done
|
||||||
|
|
||||||
|
if [ "$SKIP_TRANSLATIONS" = false ]; then
|
||||||
|
echo "Updating translations..."
|
||||||
|
if ! [ -d packages/desktop-client/locale ]; then
|
||||||
|
git clone https://github.com/actualbudget/translations packages/desktop-client/locale
|
||||||
|
fi
|
||||||
|
pushd packages/desktop-client/locale > /dev/null
|
||||||
|
git checkout .
|
||||||
|
git pull
|
||||||
|
popd > /dev/null
|
||||||
|
packages/desktop-client/bin/remove-untranslated-languages
|
||||||
fi
|
fi
|
||||||
pushd packages/desktop-client/locale > /dev/null
|
|
||||||
git checkout .
|
|
||||||
git pull
|
|
||||||
popd > /dev/null
|
|
||||||
packages/desktop-client/bin/remove-untranslated-languages
|
|
||||||
|
|
||||||
export NODE_OPTIONS="--max-old-space-size=4096"
|
lage build:browser --to=@actual-app/web
|
||||||
|
|
||||||
yarn workspace @actual-app/crdt build
|
|
||||||
yarn workspace plugins-service build
|
|
||||||
yarn workspace @actual-app/core build:browser
|
|
||||||
yarn workspace @actual-app/web build:browser
|
|
||||||
|
|
||||||
echo "packages/desktop-client/build"
|
|
||||||
|
|||||||
@@ -57,8 +57,7 @@ yarn workspace @actual-app/core build:node
|
|||||||
yarn workspace @actual-app/web build --mode=desktop # electron specific build
|
yarn workspace @actual-app/web build --mode=desktop # electron specific build
|
||||||
|
|
||||||
# required for running the sync-server server
|
# required for running the sync-server server
|
||||||
yarn workspace @actual-app/core build:browser
|
yarn build:browser
|
||||||
yarn workspace @actual-app/web build:browser
|
|
||||||
yarn workspace @actual-app/sync-server build
|
yarn workspace @actual-app/sync-server build
|
||||||
|
|
||||||
# Emit @actual-app/core declarations so desktop-electron (which includes typings/window.ts) can build
|
# Emit @actual-app/core declarations so desktop-electron (which includes typings/window.ts) can build
|
||||||
|
|||||||
@@ -25,6 +25,14 @@ module.exports = {
|
|||||||
outputGlob: BUILD_OUTPUT_GLOBS,
|
outputGlob: BUILD_OUTPUT_GLOBS,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
// Not cached: the script stages files into public/ and build-stats/ that
|
||||||
|
// fall outside BUILD_OUTPUT_GLOBS, so a cache hit would skip the side
|
||||||
|
// effects.
|
||||||
|
'build:browser': {
|
||||||
|
type: 'npmScript',
|
||||||
|
dependsOn: ['^build'],
|
||||||
|
cache: false,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
cacheOptions: {
|
cacheOptions: {
|
||||||
cacheStorageConfig: {
|
cacheStorageConfig: {
|
||||||
|
|||||||
+2
-4
@@ -24,18 +24,16 @@
|
|||||||
"start:server-dev": "NODE_ENV=development BROWSER_OPEN=localhost:5006 yarn npm-run-all --parallel 'start:server-monitor' 'start'",
|
"start:server-dev": "NODE_ENV=development BROWSER_OPEN=localhost:5006 yarn npm-run-all --parallel 'start:server-monitor' 'start'",
|
||||||
"start:desktop": "yarn desktop-dependencies && npm-run-all --parallel 'start:desktop-*'",
|
"start:desktop": "yarn desktop-dependencies && npm-run-all --parallel 'start:desktop-*'",
|
||||||
"start:docs": "yarn workspace docs start",
|
"start:docs": "yarn workspace docs start",
|
||||||
"desktop-dependencies": "npm-run-all --parallel rebuild-electron build:browser-backend build:plugins-service",
|
"desktop-dependencies": "npm-run-all --parallel rebuild-electron build:plugins-service",
|
||||||
"start:desktop-node": "yarn workspace @actual-app/core watch:node",
|
"start:desktop-node": "yarn workspace @actual-app/core watch:node",
|
||||||
"start:desktop-client": "yarn workspace @actual-app/web watch",
|
"start:desktop-client": "yarn workspace @actual-app/web watch",
|
||||||
"start:desktop-server-client": "yarn workspace @actual-app/web build:browser",
|
"start:desktop-server-client": "yarn workspace @actual-app/web build:browser",
|
||||||
"start:desktop-electron": "yarn workspace desktop-electron watch",
|
"start:desktop-electron": "yarn workspace desktop-electron watch",
|
||||||
"start:browser": "yarn workspace plugins-service build-dev && npm-run-all --parallel 'start:browser-*'",
|
"start:browser": "npm-run-all --parallel 'start:browser-*' 'start:service-plugins'",
|
||||||
"start:service-plugins": "yarn workspace plugins-service watch",
|
"start:service-plugins": "yarn workspace plugins-service watch",
|
||||||
"start:browser-backend": "yarn workspace @actual-app/core watch:browser",
|
|
||||||
"start:browser-frontend": "yarn workspace @actual-app/web start:browser",
|
"start:browser-frontend": "yarn workspace @actual-app/web start:browser",
|
||||||
"start:storybook": "yarn workspace @actual-app/components start:storybook",
|
"start:storybook": "yarn workspace @actual-app/components start:storybook",
|
||||||
"build": "lage build",
|
"build": "lage build",
|
||||||
"build:browser-backend": "yarn workspace @actual-app/core build:browser",
|
|
||||||
"build:server": "yarn build:browser && yarn workspace @actual-app/sync-server build",
|
"build:server": "yarn build:browser && yarn workspace @actual-app/sync-server build",
|
||||||
"build:browser": "./bin/package-browser",
|
"build:browser": "./bin/package-browser",
|
||||||
"build:desktop": "./bin/package-electron",
|
"build:desktop": "./bin/package-electron",
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ coverage
|
|||||||
test-results
|
test-results
|
||||||
playwright-report
|
playwright-report
|
||||||
blob-report
|
blob-report
|
||||||
|
.playwright-cli
|
||||||
|
|
||||||
# production
|
# production
|
||||||
build
|
build
|
||||||
|
|||||||
@@ -1,17 +0,0 @@
|
|||||||
#!/bin/sh -ex
|
|
||||||
|
|
||||||
ROOT=`dirname $0`
|
|
||||||
cd "$ROOT/.."
|
|
||||||
|
|
||||||
echo "Building the browser..."
|
|
||||||
|
|
||||||
rm -fr build
|
|
||||||
|
|
||||||
export REACT_APP_BACKEND_WORKER_HASH=`ls "$ROOT"/../public/kcab/kcab.worker.*.js | sed 's/.*kcab\.worker\.\(.*\)\.js/\1/'`
|
|
||||||
|
|
||||||
yarn build --mode=browser
|
|
||||||
|
|
||||||
rm -fr build-stats
|
|
||||||
mkdir build-stats
|
|
||||||
mv build/kcab/stats.json build-stats/loot-core-stats.json
|
|
||||||
mv ./stats.json build-stats/web-stats.json
|
|
||||||
@@ -104,7 +104,7 @@
|
|||||||
"start:browser": "cross-env ./bin/watch-browser",
|
"start:browser": "cross-env ./bin/watch-browser",
|
||||||
"watch": "cross-env BROWSER=none yarn start",
|
"watch": "cross-env BROWSER=none yarn start",
|
||||||
"build": "vite build",
|
"build": "vite build",
|
||||||
"build:browser": "cross-env ./bin/build-browser",
|
"build:browser": "vite build --mode=browser",
|
||||||
"generate:i18n": "i18next",
|
"generate:i18n": "i18next",
|
||||||
"test": "vitest --run",
|
"test": "vitest --run",
|
||||||
"validate:theme-catalog": "node --experimental-strip-types bin/validate-theme-catalog.mts",
|
"validate:theme-catalog": "node --experimental-strip-types bin/validate-theme-catalog.mts",
|
||||||
@@ -164,6 +164,7 @@
|
|||||||
"mdast-util-newline-to-break": "^2.0.0",
|
"mdast-util-newline-to-break": "^2.0.0",
|
||||||
"memoize-one": "^6.0.0",
|
"memoize-one": "^6.0.0",
|
||||||
"pikaday": "1.8.2",
|
"pikaday": "1.8.2",
|
||||||
|
"plugins-service": "workspace:*",
|
||||||
"promise-retry": "^2.0.1",
|
"promise-retry": "^2.0.1",
|
||||||
"re-resizable": "^6.11.2",
|
"re-resizable": "^6.11.2",
|
||||||
"react": "19.2.4",
|
"react": "19.2.4",
|
||||||
|
|||||||
@@ -24,6 +24,9 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"path": "../loot-core"
|
"path": "../loot-core"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "../plugins-service"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"include": ["**/*.ts", "src/**/*.tsx", "src/**/*.js"],
|
"include": ["**/*.ts", "src/**/*.tsx", "src/**/*.js"],
|
||||||
|
|||||||
@@ -1,3 +1,7 @@
|
|||||||
|
import { spawn } from 'node:child_process';
|
||||||
|
import type { ChildProcess } from 'node:child_process';
|
||||||
|
import { createReadStream } from 'node:fs';
|
||||||
|
import { cp, mkdir, readdir, rename, rm, writeFile } from 'node:fs/promises';
|
||||||
import * as path from 'path';
|
import * as path from 'path';
|
||||||
import { fileURLToPath } from 'url';
|
import { fileURLToPath } from 'url';
|
||||||
|
|
||||||
@@ -8,7 +12,7 @@ import react, { reactCompilerPreset } from '@vitejs/plugin-react';
|
|||||||
import type { PreRenderedAsset } from 'rolldown';
|
import type { PreRenderedAsset } from 'rolldown';
|
||||||
import { visualizer } from 'rollup-plugin-visualizer';
|
import { visualizer } from 'rollup-plugin-visualizer';
|
||||||
/// <reference types="vitest" />
|
/// <reference types="vitest" />
|
||||||
import { defineConfig, loadEnv } from 'vite';
|
import { build, defineConfig, loadEnv } from 'vite';
|
||||||
import type { Plugin } from 'vite';
|
import type { Plugin } from 'vite';
|
||||||
import { VitePWA } from 'vite-plugin-pwa';
|
import { VitePWA } from 'vite-plugin-pwa';
|
||||||
|
|
||||||
@@ -103,7 +107,163 @@ const injectShims = (): Plugin[] => {
|
|||||||
|
|
||||||
// https://vitejs.dev/config/
|
// https://vitejs.dev/config/
|
||||||
|
|
||||||
export default defineConfig(async ({ mode }) => {
|
const lootCoreRoot = path.resolve(__dirname, '../loot-core');
|
||||||
|
const lootCoreOutDir = path.resolve(lootCoreRoot, 'lib-dist/browser');
|
||||||
|
const lootCoreConfig = path.resolve(lootCoreRoot, 'vite.config.mts');
|
||||||
|
const sqlWasmSrc = path.resolve(
|
||||||
|
__dirname,
|
||||||
|
'../../node_modules/@jlongster/sql.js/dist/sql-wasm.wasm',
|
||||||
|
);
|
||||||
|
const publicDir = path.resolve(__dirname, 'public');
|
||||||
|
const publicDataDir = path.resolve(publicDir, 'data');
|
||||||
|
const publicKcabDir = path.resolve(publicDir, 'kcab');
|
||||||
|
const buildStatsDir = path.resolve(__dirname, 'build-stats');
|
||||||
|
const pluginsServiceDistDir = path.resolve(
|
||||||
|
__dirname,
|
||||||
|
'../plugins-service/dist',
|
||||||
|
);
|
||||||
|
const serviceWorkerDir = path.resolve(__dirname, 'service-worker');
|
||||||
|
|
||||||
|
const WORKER_FILENAME_RE = /^kcab\.worker\.(.+)\.js$/;
|
||||||
|
|
||||||
|
async function extractWorkerHash(): Promise<string> {
|
||||||
|
const files = await readdir(lootCoreOutDir);
|
||||||
|
for (const f of files) {
|
||||||
|
const match = f.match(WORKER_FILENAME_RE);
|
||||||
|
if (match) return match[1];
|
||||||
|
}
|
||||||
|
throw new Error(
|
||||||
|
`loot-core worker build produced no hashed output at ${lootCoreOutDir}`,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Serve loot-core worker assets with correct content types so the browser can
|
||||||
|
// stream-compile the sql.js wasm module.
|
||||||
|
const CONTENT_TYPES: Record<string, string> = {
|
||||||
|
'.js': 'application/javascript',
|
||||||
|
'.mjs': 'application/javascript',
|
||||||
|
'.map': 'application/json',
|
||||||
|
'.wasm': 'application/wasm',
|
||||||
|
};
|
||||||
|
|
||||||
|
async function stagePluginsService(): Promise<void> {
|
||||||
|
await rm(serviceWorkerDir, { recursive: true, force: true });
|
||||||
|
await cp(pluginsServiceDistDir, serviceWorkerDir, { recursive: true });
|
||||||
|
}
|
||||||
|
|
||||||
|
async function stagePublicData(): Promise<void> {
|
||||||
|
const migrationsDest = path.resolve(publicDataDir, 'migrations');
|
||||||
|
await mkdir(publicDataDir, { recursive: true });
|
||||||
|
await rm(migrationsDest, { recursive: true, force: true });
|
||||||
|
await Promise.all([
|
||||||
|
cp(path.resolve(lootCoreRoot, 'migrations'), migrationsDest, {
|
||||||
|
recursive: true,
|
||||||
|
}),
|
||||||
|
cp(
|
||||||
|
path.resolve(lootCoreRoot, 'default-db.sqlite'),
|
||||||
|
path.resolve(publicDataDir, 'default-db.sqlite'),
|
||||||
|
),
|
||||||
|
cp(sqlWasmSrc, path.resolve(publicDir, 'sql-wasm.wasm')),
|
||||||
|
]);
|
||||||
|
|
||||||
|
const entries = await readdir(publicDataDir, {
|
||||||
|
recursive: true,
|
||||||
|
withFileTypes: true,
|
||||||
|
});
|
||||||
|
const files = entries
|
||||||
|
.filter(e => e.isFile())
|
||||||
|
.map(e =>
|
||||||
|
path
|
||||||
|
.relative(publicDataDir, path.join(e.parentPath, e.name))
|
||||||
|
.replaceAll(path.sep, '/'),
|
||||||
|
)
|
||||||
|
.sort();
|
||||||
|
await writeFile(
|
||||||
|
path.resolve(publicDir, 'data-file-index.txt'),
|
||||||
|
files.join('\n') + '\n',
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const lootCoreBackend = (): Plugin => ({
|
||||||
|
name: 'loot-core-backend',
|
||||||
|
configureServer(server) {
|
||||||
|
const child: ChildProcess = spawn(
|
||||||
|
'yarn',
|
||||||
|
[
|
||||||
|
'vite',
|
||||||
|
'build',
|
||||||
|
'--config',
|
||||||
|
lootCoreConfig,
|
||||||
|
'--mode',
|
||||||
|
'development',
|
||||||
|
'--watch',
|
||||||
|
],
|
||||||
|
{ cwd: lootCoreRoot, stdio: 'inherit' },
|
||||||
|
);
|
||||||
|
child.on('error', err => {
|
||||||
|
server.config.logger.error(
|
||||||
|
`loot-core backend failed to spawn: ${err.message}`,
|
||||||
|
);
|
||||||
|
});
|
||||||
|
const cleanup = () => {
|
||||||
|
if (!child.killed) child.kill('SIGTERM');
|
||||||
|
};
|
||||||
|
server.httpServer?.once('close', cleanup);
|
||||||
|
process.once('SIGINT', cleanup);
|
||||||
|
process.once('SIGTERM', cleanup);
|
||||||
|
process.once('exit', cleanup);
|
||||||
|
|
||||||
|
server.middlewares.use('/kcab', (req, res, next) => {
|
||||||
|
const url = new URL(req.url ?? '/', 'http://localhost');
|
||||||
|
const filePath = path.join(lootCoreOutDir, url.pathname);
|
||||||
|
if (!filePath.startsWith(lootCoreOutDir + path.sep)) return next();
|
||||||
|
const stream = createReadStream(filePath);
|
||||||
|
stream
|
||||||
|
.on('open', () => {
|
||||||
|
res.setHeader(
|
||||||
|
'Content-Type',
|
||||||
|
CONTENT_TYPES[path.extname(filePath)] ?? 'application/octet-stream',
|
||||||
|
);
|
||||||
|
stream.pipe(res);
|
||||||
|
})
|
||||||
|
.on('error', () => next());
|
||||||
|
});
|
||||||
|
},
|
||||||
|
async closeBundle() {
|
||||||
|
await mkdir(buildStatsDir, { recursive: true });
|
||||||
|
try {
|
||||||
|
await rename(
|
||||||
|
path.resolve(__dirname, 'build/kcab/stats.json'),
|
||||||
|
path.resolve(buildStatsDir, 'loot-core-stats.json'),
|
||||||
|
);
|
||||||
|
} catch (err) {
|
||||||
|
if ((err as NodeJS.ErrnoException).code !== 'ENOENT') throw err;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const pluginsServiceAssets = (): Plugin => ({
|
||||||
|
name: 'plugins-service-assets',
|
||||||
|
configureServer(server) {
|
||||||
|
server.middlewares.use('/service-worker', (req, res, next) => {
|
||||||
|
const url = new URL(req.url ?? '/', 'http://localhost');
|
||||||
|
const filePath = path.join(pluginsServiceDistDir, url.pathname);
|
||||||
|
if (!filePath.startsWith(pluginsServiceDistDir + path.sep)) return next();
|
||||||
|
const stream = createReadStream(filePath);
|
||||||
|
stream
|
||||||
|
.on('open', () => {
|
||||||
|
res.setHeader(
|
||||||
|
'Content-Type',
|
||||||
|
CONTENT_TYPES[path.extname(filePath)] ?? 'application/octet-stream',
|
||||||
|
);
|
||||||
|
stream.pipe(res);
|
||||||
|
})
|
||||||
|
.on('error', () => next());
|
||||||
|
});
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
export default defineConfig(async ({ mode, command }) => {
|
||||||
const env = loadEnv(mode, process.cwd(), '');
|
const env = loadEnv(mode, process.cwd(), '');
|
||||||
const devHeaders = {
|
const devHeaders = {
|
||||||
'Cross-Origin-Opener-Policy': 'same-origin',
|
'Cross-Origin-Opener-Policy': 'same-origin',
|
||||||
@@ -116,6 +276,32 @@ export default defineConfig(async ({ mode }) => {
|
|||||||
process.env.REACT_APP_BRANCH = process.env.BRANCH;
|
process.env.REACT_APP_BRANCH = process.env.BRANCH;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Electron packaging (--mode=desktop) bundles loot-core directly, so skip
|
||||||
|
// all browser-only staging there.
|
||||||
|
if (mode !== 'desktop') {
|
||||||
|
if (command === 'build') {
|
||||||
|
const stageKcab = build({
|
||||||
|
configFile: lootCoreConfig,
|
||||||
|
mode: 'production',
|
||||||
|
root: lootCoreRoot,
|
||||||
|
}).then(async () => {
|
||||||
|
const hash = await extractWorkerHash();
|
||||||
|
await rm(publicKcabDir, { recursive: true, force: true });
|
||||||
|
await cp(lootCoreOutDir, publicKcabDir, { recursive: true });
|
||||||
|
return hash;
|
||||||
|
});
|
||||||
|
const [, , hash] = await Promise.all([
|
||||||
|
stagePublicData(),
|
||||||
|
stagePluginsService(),
|
||||||
|
stageKcab,
|
||||||
|
]);
|
||||||
|
process.env.REACT_APP_BACKEND_WORKER_HASH = hash;
|
||||||
|
} else {
|
||||||
|
await stagePublicData();
|
||||||
|
process.env.REACT_APP_BACKEND_WORKER_HASH = 'dev';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const browserOpen = env.BROWSER_OPEN ? `//${env.BROWSER_OPEN}` : true;
|
const browserOpen = env.BROWSER_OPEN ? `//${env.BROWSER_OPEN}` : true;
|
||||||
|
|
||||||
return {
|
return {
|
||||||
@@ -213,13 +399,18 @@ export default defineConfig(async ({ mode }) => {
|
|||||||
}),
|
}),
|
||||||
injectShims(),
|
injectShims(),
|
||||||
addWatchers(),
|
addWatchers(),
|
||||||
|
mode === 'desktop' ? undefined : lootCoreBackend(),
|
||||||
|
mode === 'desktop' ? undefined : pluginsServiceAssets(),
|
||||||
react(),
|
react(),
|
||||||
babel({
|
babel({
|
||||||
include: [reactCompilerInclude],
|
include: [reactCompilerInclude],
|
||||||
// n.b. Must be a string to ensure plugin resolution order. See https://github.com/actualbudget/actual/pull/5853
|
// n.b. Must be a string to ensure plugin resolution order. See https://github.com/actualbudget/actual/pull/5853
|
||||||
presets: [reactCompilerPreset()],
|
presets: [reactCompilerPreset()],
|
||||||
}),
|
}),
|
||||||
visualizer({ template: 'raw-data' }),
|
visualizer({
|
||||||
|
template: 'raw-data',
|
||||||
|
filename: 'build-stats/web-stats.json',
|
||||||
|
}),
|
||||||
!!env.HTTPS && basicSsl(),
|
!!env.HTTPS && basicSsl(),
|
||||||
],
|
],
|
||||||
test: {
|
test: {
|
||||||
|
|||||||
@@ -1,40 +0,0 @@
|
|||||||
#!/bin/bash -e
|
|
||||||
|
|
||||||
cd `dirname "$0"`
|
|
||||||
ROOT=`pwd -P`
|
|
||||||
VITE_ARGS=""
|
|
||||||
|
|
||||||
PUBLIC_DIR="$ROOT"/../../desktop-client/public
|
|
||||||
DATA_DIR="$PUBLIC_DIR"/data
|
|
||||||
mkdir -p "$DATA_DIR"
|
|
||||||
"$ROOT"/copy-migrations "$DATA_DIR"
|
|
||||||
|
|
||||||
cd "$DATA_DIR"
|
|
||||||
find * -type f | sort > ../data-file-index.txt
|
|
||||||
cd "$ROOT"
|
|
||||||
|
|
||||||
# Clean out previous build files
|
|
||||||
rm -f ../lib-dist/browser/*
|
|
||||||
rm -rf ../../desktop-client/public/kcab
|
|
||||||
|
|
||||||
if [ $NODE_ENV == 'development' ]; then
|
|
||||||
# In dev mode, always enable watch mode and symlink the build files.
|
|
||||||
# Make sure to do this before starting the build since watch mode
|
|
||||||
# will block
|
|
||||||
VITE_ARGS="$VITE_ARGS --watch"
|
|
||||||
if [ "$OSTYPE" == "msys" ]; then
|
|
||||||
# Ensure symlinks are created as native Windows symlinks.
|
|
||||||
export MSYS=winsymlinks:nativestrict
|
|
||||||
fi
|
|
||||||
ln -snf "$ROOT"/../lib-dist/browser ../../desktop-client/public/kcab
|
|
||||||
fi
|
|
||||||
|
|
||||||
cp ../../../node_modules/@jlongster/sql.js/dist/sql-wasm.wasm "$PUBLIC_DIR"/sql-wasm.wasm
|
|
||||||
|
|
||||||
yarn vite build --config ../vite.config.mts --mode $NODE_ENV $VITE_ARGS
|
|
||||||
|
|
||||||
if [ $NODE_ENV == 'production' ]; then
|
|
||||||
# In production, just copy the built files
|
|
||||||
mkdir ../../desktop-client/public/kcab
|
|
||||||
cp -r ../lib-dist/browser/* ../../desktop-client/public/kcab
|
|
||||||
fi
|
|
||||||
@@ -1,9 +0,0 @@
|
|||||||
#!/bin/bash -e
|
|
||||||
|
|
||||||
ROOT=`dirname $(dirname "$0")`
|
|
||||||
DEST="$1"
|
|
||||||
|
|
||||||
rm -rf "$DEST"/migrations
|
|
||||||
mkdir -p "$DEST"/migrations
|
|
||||||
cp "$ROOT"/migrations/* "$DEST"/migrations/
|
|
||||||
cp "$ROOT"/default-db.sqlite "$DEST"
|
|
||||||
@@ -158,8 +158,6 @@
|
|||||||
"scripts": {
|
"scripts": {
|
||||||
"build:node": "cross-env NODE_ENV=production vite build --config ./vite.desktop.config.mts",
|
"build:node": "cross-env NODE_ENV=production vite build --config ./vite.desktop.config.mts",
|
||||||
"watch:node": "cross-env NODE_ENV=development vite build --config ./vite.desktop.config.mts --watch",
|
"watch:node": "cross-env NODE_ENV=development vite build --config ./vite.desktop.config.mts --watch",
|
||||||
"build:browser": "cross-env NODE_ENV=production ./bin/build-browser",
|
|
||||||
"watch:browser": "cross-env NODE_ENV=development ./bin/build-browser",
|
|
||||||
"generate:i18n": "i18next",
|
"generate:i18n": "i18next",
|
||||||
"test": "npm-run-all -cp 'test:*'",
|
"test": "npm-run-all -cp 'test:*'",
|
||||||
"test:node": "ENV=node vitest --run",
|
"test:node": "ENV=node vitest --run",
|
||||||
|
|||||||
@@ -1,28 +0,0 @@
|
|||||||
#!/bin/bash -e
|
|
||||||
|
|
||||||
cd `dirname "$0"`
|
|
||||||
ROOT=`pwd -P`
|
|
||||||
VITE_ARGS=""
|
|
||||||
|
|
||||||
DESKTOP_DIR="$ROOT"/../../desktop-client
|
|
||||||
SERVICE_WORKER_DIR="$DESKTOP_DIR"/service-worker
|
|
||||||
|
|
||||||
# Clean out previous build files
|
|
||||||
rm -f ../dist/*
|
|
||||||
rm -rf "$DESKTOP_DIR"/service-worker
|
|
||||||
|
|
||||||
if [ $NODE_ENV == 'development' ]; then
|
|
||||||
if [ "$OSTYPE" == "msys" ]; then
|
|
||||||
# Ensure symlinks are created as native Windows symlinks.
|
|
||||||
export MSYS=winsymlinks:nativestrict
|
|
||||||
fi
|
|
||||||
ln -snf "$ROOT"/../dist/ "$DESKTOP_DIR"/service-worker
|
|
||||||
fi
|
|
||||||
|
|
||||||
yarn vite build --config ../vite.config.mts --mode $NODE_ENV $VITE_ARGS
|
|
||||||
|
|
||||||
if [ $NODE_ENV == 'production' ]; then
|
|
||||||
# In production, just copy the built files
|
|
||||||
mkdir -p "$SERVICE_WORKER_DIR"
|
|
||||||
cp -r ../dist/* "$DESKTOP_DIR"/service-worker
|
|
||||||
fi
|
|
||||||
@@ -6,9 +6,9 @@
|
|||||||
"author": "",
|
"author": "",
|
||||||
"main": "plugin-sw.js",
|
"main": "plugin-sw.js",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"build": "cross-env NODE_ENV=production ./bin/build-service-worker",
|
"build": "cross-env NODE_ENV=production vite build",
|
||||||
"build-dev": "cross-env NODE_ENV=development ./bin/build-service-worker",
|
"build-dev": "cross-env NODE_ENV=development vite build",
|
||||||
"watch": "cross-env NODE_ENV=development ./bin/build-service-worker --watch",
|
"watch": "cross-env NODE_ENV=development vite build --watch",
|
||||||
"typecheck": "tsgo -b && tsc-strict"
|
"typecheck": "tsgo -b && tsc-strict"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
|||||||
@@ -0,0 +1,6 @@
|
|||||||
|
---
|
||||||
|
category: Maintenance
|
||||||
|
authors: [MatissJanis]
|
||||||
|
---
|
||||||
|
|
||||||
|
Build scripts: port browser build version to using lage
|
||||||
@@ -264,6 +264,7 @@ __metadata:
|
|||||||
mdast-util-newline-to-break: "npm:^2.0.0"
|
mdast-util-newline-to-break: "npm:^2.0.0"
|
||||||
memoize-one: "npm:^6.0.0"
|
memoize-one: "npm:^6.0.0"
|
||||||
pikaday: "npm:1.8.2"
|
pikaday: "npm:1.8.2"
|
||||||
|
plugins-service: "workspace:*"
|
||||||
promise-retry: "npm:^2.0.1"
|
promise-retry: "npm:^2.0.1"
|
||||||
re-resizable: "npm:^6.11.2"
|
re-resizable: "npm:^6.11.2"
|
||||||
react: "npm:19.2.4"
|
react: "npm:19.2.4"
|
||||||
@@ -22776,7 +22777,7 @@ __metadata:
|
|||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
"plugins-service@workspace:packages/plugins-service":
|
"plugins-service@workspace:*, plugins-service@workspace:packages/plugins-service":
|
||||||
version: 0.0.0-use.local
|
version: 0.0.0-use.local
|
||||||
resolution: "plugins-service@workspace:packages/plugins-service"
|
resolution: "plugins-service@workspace:packages/plugins-service"
|
||||||
dependencies:
|
dependencies:
|
||||||
|
|||||||
Reference in New Issue
Block a user