Compare commits

...
Author SHA1 Message Date
Tunglies 334cf3ef37 fix(build): regenerate polyfills after release version rewrites 2026-11-11 06:44:17 +08:00
Tunglies cf20bcd76d perf(build): guard generated modern polyfill inventory 2026-11-11 06:31:29 +08:00
Tunglies 84783abb73 perf(build): filter SVGR loads before the JavaScript bridge 2026-11-11 06:17:02 +08:00
❤是纱雾酱哟~andrenovate[bot] <29139614+renovate[bot]@users.noreply.github.com> f86db4b344 chore(deps): update cargo dependencies (#8619)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-11-10 17:29:51 +00:00
renovate[bot]renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>❤是纱雾酱哟~
389dddf512 chore(deps): update github/gh-aw-actions action to v1.2.2 (#8617)
* chore(deps): update github/gh-aw-actions action to v1.2.2

* ci(workflows): recompile workflow lockfiles with gh-aw v1.2.2

Signed-off-by: ❤是纱雾酱哟~ <49941141+Dragon1573@users.noreply.github.com>

* ci(workflows): set permissions to suppress tips

Agentic workflows of this repository are using available credits by some
collaborators' personal API Key. Configuring permissions of these
workflows to suppress warnings by GitHub Agentic Workflow extension
during lockfile compilation.

Signed-off-by: ❤是纱雾酱哟~ <49941141+Dragon1573@users.noreply.github.com>

---------

Signed-off-by: ❤是纱雾酱哟~ <49941141+Dragon1573@users.noreply.github.com>
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: ❤是纱雾酱哟~ <49941141+Dragon1573@users.noreply.github.com>
2026-11-10 14:01:34 +00:00
renovate[bot]andrenovate[bot] <29139614+renovate[bot]@users.noreply.github.com> 304a65fc36 chore(deps): update npm dependencies (#8615)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-11-10 05:17:19 +00:00
20 changed files with 1217 additions and 356 deletions
+3 -3
View File
@@ -5,10 +5,10 @@
"version": "v9.0.0",
"sha": "3a2844b7e9c422d3c10d287c895573f7108da1b3"
},
"github/gh-aw-actions/setup@v1.0.1": {
"github/gh-aw-actions/setup@v1.2.2": {
"repo": "github/gh-aw-actions/setup",
"version": "v1.0.1",
"sha": "221ba4f4319de6b8e809d1a15528b3ae6a818aa5"
"version": "v1.2.2",
"sha": "53d0749e19424a918acd82b4d140264ce48f2f14"
}
},
"containers": {
+9 -3
View File
@@ -191,7 +191,9 @@ jobs:
pnpm run prebuild ${{ matrix.target }}
- name: Release ${{ env.TAG_CHANNEL }} Version
run: pnpm release-version autobuild-latest
run: |
pnpm release-version autobuild-latest
pnpm polyfills:generate
- name: Tauri build for Windows-macOS-Linux
uses: tauri-apps/tauri-action@v1
@@ -271,7 +273,9 @@ jobs:
pnpm run prebuild ${{ matrix.target }}
- name: Release ${{ env.TAG_CHANNEL }} Version
run: pnpm release-version autobuild-latest
run: |
pnpm release-version autobuild-latest
pnpm polyfills:generate
- name: 'Setup for linux'
run: |-
@@ -404,7 +408,9 @@ jobs:
pnpm run prebuild ${{ matrix.target }}
- name: Release ${{ env.TAG_CHANNEL }} Version
run: pnpm release-version autobuild-latest
run: |
pnpm release-version autobuild-latest
pnpm polyfills:generate
- name: Download WebView2 Runtime
run: |
+1 -1
View File
@@ -31,7 +31,7 @@ jobs:
with:
persist-credentials: false
- name: Install gh-aw extension
uses: github/gh-aw-actions/setup-cli@406c7719e31d7fe354224d4bb8fe3d4aeb47da1c # v1.2.1
uses: github/gh-aw-actions/setup-cli@53d0749e19424a918acd82b4d140264ce48f2f14 # v1.2.2
with:
version: v1.0.1
- name: Install Rust Stable
+3 -1
View File
@@ -115,7 +115,9 @@ jobs:
- name: Release ${{ env.TAG_CHANNEL }} Version
if: github.event.inputs[matrix.input] == 'true'
run: pnpm release-version ${{ env.TAG_NAME }}
run: |
pnpm release-version ${{ env.TAG_NAME }}
pnpm polyfills:generate
- name: Add Rust Target
if: github.event.inputs[matrix.input] == 'true'
+3
View File
@@ -58,6 +58,9 @@ jobs:
- name: Run TypeScript typecheck
run: pnpm typecheck
- name: Verify modern polyfill inventory against automatic discovery
run: pnpm polyfills:check
- name: Run tests
run: pnpm test
File diff suppressed because one or more lines are too long
+1
View File
@@ -33,6 +33,7 @@ max-daily-ai-credits: 5000
permissions:
issues: read
pull-requests: read
copilot-requests: none
pre-agent-steps:
- name: Make Copilot quota exhaustion non-blocking
+288 -107
View File
File diff suppressed because one or more lines are too long
+1
View File
@@ -30,6 +30,7 @@ permissions:
contents: read
issues: read
pull-requests: read
copilot-requests: none
steps:
- name: Load review rules from the base revision
uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
File diff suppressed because one or more lines are too long
@@ -28,6 +28,7 @@ permissions:
contents: read
issues: read
pull-requests: read
copilot-requests: none
max-ai-credits: 60
max-turns: 12
max-daily-ai-credits: 500
+11
View File
@@ -101,6 +101,17 @@ For frontend-only development, use `pnpm web:dev`. It does not start the Rust ba
### Build the Project
`pnpm web:build` uses a generated modern-polyfill inventory and rejects changed
emitted module inputs, output, targets, or locked dependencies before writing
output. Inputs include dependency code and code later removed by minification.
When it reports a stale inventory, run `pnpm polyfills:generate` and commit
`scripts/modern-polyfills.json`. Generation preserves automatic discovery's import
order and requires byte-identical automatic/explicit outputs for the macOS, Linux,
and Windows production defines. CI repeats this comparison with
`pnpm polyfills:check`; browser targets and worker configuration stay unchanged.
Release build jobs regenerate after rewriting versions because version data is
also emitted in frontend modules.
Standard build:
```bash
Generated
+4 -4
View File
@@ -159,9 +159,9 @@ dependencies = [
[[package]]
name = "arc-swap"
version = "1.9.4"
version = "1.9.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "db4c94f57a570fce5d381be3289b3f79aef6332d72948fe82ab5772e423ccf92"
checksum = "84a22b5d51209f6de83d37a0b33d320acb8da88823e42a94c075140708f032a5"
dependencies = [
"rustversion",
]
@@ -10126,9 +10126,9 @@ dependencies = [
[[package]]
name = "zip"
version = "9.4.2"
version = "9.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "df3845f734baabdd02de353a20580e30ceccd03897def44a4eb8e9654992e294"
checksum = "0700e6c427aff64acdbf02e85fbfd946c2ee12319d1af013092f3a2178914ff8"
dependencies = [
"aes",
"bzip2",
+4 -2
View File
@@ -15,6 +15,8 @@
"tauri": "tauri",
"web:dev": "vite",
"web:build": "tsrs -p . --noEmit && vite build",
"polyfills:generate": "node scripts/generate-modern-polyfills.mjs --write",
"polyfills:check": "node scripts/generate-modern-polyfills.mjs --check",
"web:serve": "vite preview",
"prebuild": "node scripts/prebuild.mjs",
"updater": "node scripts/updater.mjs",
@@ -67,7 +69,7 @@
"dayjs": "1.11.23",
"foxact": "^0.3.12",
"foxts": "^5.9.1",
"i18next": "^26.4.2",
"i18next": "^27.0.0",
"js-yaml": "^5.4.2",
"lodash-es": "^4.18.1",
"meta-json-schema": "^1.19.31",
@@ -79,7 +81,7 @@
"react-dom": "19.3.1",
"react-error-boundary": "6.1.6",
"react-hook-form": "^7.88.0",
"react-i18next": "17.1.13",
"react-i18next": "18.0.1",
"react-markdown": "10.1.0",
"react-router": "^8.4.0",
"rehype-raw": "^7.0.0",
+18 -21
View File
@@ -239,8 +239,8 @@ importers:
specifier: ^5.9.1
version: 5.9.1
i18next:
specifier: ^26.4.2
version: 26.4.2(typescript@6.0.3)
specifier: ^27.0.0
version: 27.0.1(typescript@6.0.3)
js-yaml:
specifier: ^5.4.2
version: 5.4.2
@@ -275,8 +275,8 @@ importers:
specifier: ^7.88.0
version: 7.88.0(react@19.3.1)
react-i18next:
specifier: 17.1.13
version: 17.1.13(i18next@26.4.2(typescript@6.0.3))(react-dom@19.3.1(react@19.3.1))(react@19.3.1)(typescript@6.0.3)
specifier: 18.0.1
version: 18.0.1(@types/react@19.3.0)(i18next@27.0.1(typescript@6.0.3))(react@19.3.1)(typescript@6.0.3)
react-markdown:
specifier: 10.1.0
version: 10.1.0(@types/react@19.3.0)(react@19.3.1)(supports-color@7.2.0)
@@ -3689,8 +3689,9 @@ packages:
engines: {node: '>=18'}
hasBin: true
i18next@26.4.2:
resolution: {integrity: sha512-RX+R0VLg13IbvRuJSxnqykUFS9vQZTl8wYpWPCIUDWVrSGjsQywB5Y+pjzrkboxGAuYfJZVH1InFTdgBdxq6ug==}
i18next@27.0.1:
resolution: {integrity: sha512-KaE5ozP8UUeVpOJIwMVbbnpRKpyYQuxd9ZbseJoDzezE3nvLnZZQn20pkzyIJwPI2Uet/Qs3WzPTG812f6F/Eg==}
engines: {node: '>=22'}
peerDependencies:
typescript: ^5 || ^6 || ^7
peerDependenciesMeta:
@@ -4396,18 +4397,16 @@ packages:
peerDependencies:
react: ^16.8.0 || ^17 || ^18 || ^19
react-i18next@17.1.13:
resolution: {integrity: sha512-ZxhOSlyKSKPtbUNPLwYMmE3w3bhoc0jaZJZ6bc7wGOT6cLbrbs//6m01HxCLtFdSjHvFpwFwoPhxxzIlHSs9cw==}
react-i18next@18.0.1:
resolution: {integrity: sha512-kPWXI0E/lrPj3+/umcKNzYZVnWmEedLiamMzDIHfbs+5ANf6yzvlZsjKou8f7kaGkfMMDLIZBejV+4aSN1CyBw==}
engines: {node: '>=22'}
peerDependencies:
i18next: '>= 26.2.0'
react: '>= 16.8.0'
react-dom: '*'
react-native: '*'
'@types/react': '*'
i18next: '>= 27.0.1'
react: '>= 18.0.0'
typescript: ^5 || ^6 || ^7
peerDependenciesMeta:
react-dom:
optional: true
react-native:
'@types/react':
optional: true
typescript:
optional: true
@@ -8533,7 +8532,7 @@ snapshots:
husky@9.1.7: {}
i18next@26.4.2(typescript@6.0.3):
i18next@27.0.1(typescript@6.0.3):
optionalDependencies:
typescript: 6.0.3
@@ -9357,15 +9356,13 @@ snapshots:
dependencies:
react: 19.3.1
react-i18next@17.1.13(i18next@26.4.2(typescript@6.0.3))(react-dom@19.3.1(react@19.3.1))(react@19.3.1)(typescript@6.0.3):
react-i18next@18.0.1(@types/react@19.3.0)(i18next@27.0.1(typescript@6.0.3))(react@19.3.1)(typescript@6.0.3):
dependencies:
'@babel/runtime': 7.29.7
html-parse-stringify: 4.0.1
i18next: 26.4.2(typescript@6.0.3)
i18next: 27.0.1(typescript@6.0.3)
react: 19.3.1
use-sync-external-store: 1.7.0(react@19.3.1)
optionalDependencies:
react-dom: 19.3.1(react@19.3.1)
'@types/react': 19.3.0
typescript: 6.0.3
react-is@16.13.1: {}
+80
View File
@@ -0,0 +1,80 @@
import assert from 'node:assert/strict'
import { createHash } from 'node:crypto'
import { mkdtempSync, readFileSync, rmSync, writeFileSync } from 'node:fs'
import os from 'node:os'
import path from 'node:path'
import { build } from 'vite'
const [command, ...extra] = process.argv.slice(2)
if (!['--write', '--check'].includes(command) || extra.length) {
throw new Error(
'Usage: node scripts/generate-modern-polyfills.mjs --write|--check',
)
}
const inventoryPath = 'scripts/modern-polyfills.json'
const staleMessage =
'Modern polyfill inventory is stale; run pnpm polyfills:generate and commit scripts/modern-polyfills.json'
function outputHashes(result) {
assert(
!Array.isArray(result) && 'output' in result,
'Expected one app output',
)
return result.output
.map((file) => [
file.fileName,
createHash('sha256')
.update(file.type === 'chunk' ? file.code : file.source)
.digest('hex'),
])
.sort(([a], [b]) => a.localeCompare(b))
}
const temporary = mkdtempSync(path.join(os.tmpdir(), 'modern-polyfills-'))
const capture = path.join(temporary, 'capture.json')
const candidate = path.join(temporary, 'inventory.json')
const snapshots = []
const fingerprints = []
let imports
try {
process.env.MODERN_POLYFILLS_CAPTURE = capture
// OS_PLATFORM changes emitted code; certify each production define without
// launching a native app or normalizing away code the detector actually sees.
for (const platform of ['darwin', 'linux', 'win32']) {
const config = {
logLevel: 'error',
define: { OS_PLATFORM: JSON.stringify(platform) },
build: { write: false },
}
const automatic = await build(config)
const detected = JSON.parse(readFileSync(capture, 'utf8'))
if (imports) assert.deepEqual(detected.imports, imports)
imports = detected.imports
fingerprints.push(detected.fingerprint)
snapshots.push({ config, hashes: outputHashes(automatic) })
}
const content = `${JSON.stringify({ imports, fingerprints: [...new Set(fingerprints)].sort() }, null, 2)}\n`
writeFileSync(candidate, content)
delete process.env.MODERN_POLYFILLS_CAPTURE
process.env.MODERN_POLYFILLS_INVENTORY = candidate
for (const { config, hashes } of snapshots) {
assert.deepEqual(
outputHashes(await build(config)),
hashes,
'Explicit polyfills must be byte-identical to automatic discovery',
)
}
if (command === '--check') {
assert.equal(readFileSync(inventoryPath, 'utf8'), content, staleMessage)
} else {
writeFileSync(inventoryPath, content)
}
console.log(
`Modern polyfills ${command === '--check' ? 'verified' : 'generated'}: ${imports.length} imports; all three OS outputs byte-identical to automatic discovery`,
)
} finally {
delete process.env.MODERN_POLYFILLS_CAPTURE
delete process.env.MODERN_POLYFILLS_INVENTORY
rmSync(temporary, { recursive: true })
}
+72
View File
@@ -0,0 +1,72 @@
{
"imports": [
"core-js/modules/es.error.cause.js",
"core-js/modules/es.array.at.js",
"core-js/modules/es.array.includes.js",
"core-js/modules/es.array.push.js",
"core-js/modules/es.array.unshift.js",
"core-js/modules/es.array-buffer.detached.js",
"core-js/modules/es.array-buffer.transfer.js",
"core-js/modules/es.array-buffer.transfer-to-fixed-length.js",
"core-js/modules/es.iterator.constructor.js",
"core-js/modules/es.iterator.every.js",
"core-js/modules/es.iterator.filter.js",
"core-js/modules/es.iterator.find.js",
"core-js/modules/es.iterator.flat-map.js",
"core-js/modules/es.iterator.for-each.js",
"core-js/modules/es.iterator.map.js",
"core-js/modules/es.iterator.reduce.js",
"core-js/modules/es.iterator.some.js",
"core-js/modules/es.iterator.to-array.js",
"core-js/modules/es.json.parse.js",
"core-js/modules/es.json.stringify.js",
"core-js/modules/es.map.get-or-insert.js",
"core-js/modules/es.map.get-or-insert-computed.js",
"core-js/modules/es.regexp.flags.js",
"core-js/modules/es.set.difference.v2.js",
"core-js/modules/es.set.intersection.v2.js",
"core-js/modules/es.set.is-disjoint-from.v2.js",
"core-js/modules/es.set.is-subset-of.v2.js",
"core-js/modules/es.set.is-superset-of.v2.js",
"core-js/modules/es.set.symmetric-difference.v2.js",
"core-js/modules/es.set.union.v2.js",
"core-js/modules/es.string.at-alternative.js",
"core-js/modules/es.typed-array.at.js",
"core-js/modules/es.typed-array.fill.js",
"core-js/modules/es.typed-array.find-last.js",
"core-js/modules/es.typed-array.find-last-index.js",
"core-js/modules/es.typed-array.set.js",
"core-js/modules/es.typed-array.sort.js",
"core-js/modules/es.typed-array.to-reversed.js",
"core-js/modules/es.typed-array.to-sorted.js",
"core-js/modules/es.typed-array.with.js",
"core-js/modules/es.uint8-array.set-from-base64.js",
"core-js/modules/es.uint8-array.set-from-hex.js",
"core-js/modules/es.uint8-array.to-base64.js",
"core-js/modules/es.uint8-array.to-hex.js",
"core-js/modules/es.weak-map.get-or-insert.js",
"core-js/modules/es.weak-map.get-or-insert-computed.js",
"core-js/modules/esnext.array.group.js",
"core-js/modules/web.dom-exception.stack.js",
"core-js/modules/web.immediate.js",
"core-js/modules/web.url-search-params.delete.js",
"core-js/modules/web.url-search-params.has.js",
"core-js/modules/web.url-search-params.size.js",
"core-js/modules/es.object.has-own.js",
"core-js/modules/es.array.with.js",
"core-js/modules/es.symbol.async-dispose.js",
"core-js/modules/es.async-iterator.async-dispose.js",
"core-js/modules/es.symbol.dispose.js",
"core-js/modules/es.iterator.dispose.js",
"core-js/modules/es.aggregate-error.cause.js",
"core-js/modules/es.array.find-last.js",
"core-js/modules/es.iterator.drop.js",
"core-js/modules/es.iterator.take.js",
"core-js/modules/web.structured-clone.js"
],
"fingerprints": [
"30dd6a7f16e7065fa9803260e11aa2f2db56cabb69664257c940ab05bae8505b",
"5630fe8a486687073b6d9894dd39735b77d5d8177432a4ada81947a429ff6991",
"ddbb980f9ad3fe92ee7c30248d46ed4cdeb421c714266e627b825b59042c30bc"
]
}
+139
View File
@@ -0,0 +1,139 @@
import { createHash } from 'node:crypto'
import { existsSync, readFileSync, writeFileSync } from 'node:fs'
import path from 'node:path'
import legacy, { detectPolyfills } from '@vitejs/plugin-legacy'
const digest = (value) => createHash('sha256').update(value).digest('hex')
const inventoryPath = 'scripts/modern-polyfills.json'
const staleMessage =
'Modern polyfill inventory is stale; run pnpm polyfills:generate and commit scripts/modern-polyfills.json'
// Bind complete emitted module inputs as well as output: minification can hide APIs.
// Generation double-checks the exported detector against legacy's automatic build.
export function guardedLegacy(options) {
const capturePath = process.env.MODERN_POLYFILLS_CAPTURE
const inventory = capturePath
? null
: JSON.parse(
readFileSync(
process.env.MODERN_POLYFILLS_INVENTORY || inventoryPath,
'utf8',
),
)
const plugins = legacy({
...options,
modernPolyfills: Boolean(capturePath),
additionalModernPolyfills: [
...options.additionalModernPolyfills,
...(capturePath ? [] : inventory.imports),
],
})
const postProcess = plugins.find(
(plugin) => plugin.name === 'vite:legacy-post-process',
)
if (!postProcess?.renderChunk) {
throw new Error('plugin-legacy must provide its post-process render hook')
}
const original = postProcess.renderChunk
const render = typeof original === 'function' ? original : original.handler
const discovered = new Map()
let chunkNames
let context
if (capturePath) {
postProcess.renderChunk = async function (raw, chunk, output, meta) {
chunkNames ??= Object.keys(meta.chunks)
const imports = new Set()
await detectPolyfills(
raw,
options.modernTargets,
options.assumptions || {},
imports,
)
discovered.set(chunk.fileName, imports)
return render.call(this, raw, chunk, output, meta)
}
}
return [
...plugins,
{
name: 'guard-modern-polyfills',
enforce: 'post',
apply: 'build',
configResolved(config) {
if (config.build.ssr || options.renderLegacyChunks !== false) {
throw new Error(
'Modern polyfill guard requires a modern-only app build',
)
}
// Custom import paths must not bind the inventory to a checkout location.
context = [
{
...options,
additionalModernPolyfills: options.additionalModernPolyfills.map(
(file) =>
path.relative(process.cwd(), file).split(path.sep).join('/'),
),
},
config.build.target,
config.mode,
...[
'package.json',
'pnpm-lock.yaml',
'vite.config.mts',
'scripts/modern-polyfills.mjs',
'scripts/generate-modern-polyfills.mjs',
].map((file) => digest(readFileSync(file))),
...[
'.browserslistrc',
'browserslist',
'src/.browserslistrc',
'src/browserslist',
].map((file) =>
existsSync(file) ? digest(readFileSync(file)) : null,
),
digest(
readFileSync(new URL(import.meta.resolve('@vitejs/plugin-legacy'))),
),
]
},
generateBundle(_output, bundle) {
const imports = capturePath
? [
...new Set(
chunkNames.flatMap((name) => [...discovered.get(name)]),
),
]
: inventory.imports
const chunks = Object.values(bundle).filter(
(file) => file.type === 'chunk',
)
const moduleIds = new Set(
chunks.flatMap((chunk) => Object.keys(chunk.modules)),
)
const inputs = [...moduleIds].map((id) => {
// Rolldown's runtime is generated, not a transformed input module.
if (id === '\0rolldown/runtime.js') return null
// Vite's HTML entry code embeds an absolute import to the main module.
if (id.endsWith('.html')) return digest(readFileSync(id))
const code = this.getModuleInfo(id)?.code
if (code == null) this.error(`Missing emitted module input: ${id}`)
return digest(code)
})
const fingerprint = digest(
JSON.stringify([
context,
inputs.sort(),
chunks.map((chunk) => digest(chunk.code)).sort(),
imports,
]),
)
if (capturePath) {
writeFileSync(capturePath, JSON.stringify({ fingerprint, imports }))
} else if (!inventory.fingerprints.includes(fingerprint)) {
this.error(staleMessage)
}
},
},
]
}
+2 -2
View File
@@ -77,7 +77,7 @@ tauri-plugin-dialog = "2.8.4"
tauri-plugin-fs = "2.7.1"
tauri-plugin-deep-link = "2.6.3"
tauri-plugin-window-state = "2.5.0"
zip = "9.4.2"
zip = "9.5.0"
flate2 = "1.1.11"
reqwest_dav = "0.3.3"
aes-gcm = { version = "0.11.1" }
@@ -97,7 +97,7 @@ async-trait = "0.1.92"
clash_verge_service_ipc = { git = "https://github.com/clash-verge-rev/clash-verge-service-ipc.git", tag = "v2.7.7", version = "2.7.7", features = [
"client",
] }
arc-swap = "1.9.4"
arc-swap = "1.9.5"
tokio-rustls = "0.26.6"
rustls = { version = "0.23.46", features = ["ring"] }
webpki-roots = "1.0.11"
+13 -6
View File
@@ -1,22 +1,29 @@
import path from 'node:path'
import legacy from '@vitejs/plugin-legacy'
import react from '@vitejs/plugin-react'
import { defineConfig } from 'vite'
import svgr from 'vite-plugin-svgr'
import { guardedLegacy } from './scripts/modern-polyfills.mjs'
const svgPlugin = svgr()
const svgLoad = svgPlugin.load
if (!svgLoad) throw new Error('vite-plugin-svgr must provide a load hook')
// SVGR's internal include filter still crosses the JS bridge for every module.
svgPlugin.load = {
filter: { id: /\.svg\?react$/ },
handler: typeof svgLoad === 'function' ? svgLoad : svgLoad.handler,
}
export default defineConfig({
root: 'src',
server: { port: 3000 },
plugins: [
svgr(),
svgPlugin,
react(),
legacy({
guardedLegacy({
modernTargets: ['edge>=109', 'safari>=14'],
renderLegacyChunks: false,
// Auto-detected from the emitted bundle, dependencies included,
// so post-Safari-14 APIs never depend on extending a manual list.
modernPolyfills: true,
additionalModernPolyfills: [
path.resolve('./src/polyfills/matchMedia.js'),
path.resolve('./src/polyfills/WeakRef.js'),