mirror of
https://github.com/clash-verge-rev/clash-verge-rev.git
synced 2026-11-12 05:32:09 +08:00
Compare commits
3
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
334cf3ef37 | ||
|
|
cf20bcd76d | ||
|
|
84783abb73 |
@@ -4,7 +4,7 @@
|
||||
|
||||
"features": {
|
||||
"ghcr.io/devcontainers/features/node:2": {
|
||||
"version": "26.13.1",
|
||||
"version": "26.13.0",
|
||||
"pnpmVersion": "12.18.0"
|
||||
},
|
||||
"ghcr.io/devcontainers/features/rust:1": {
|
||||
|
||||
@@ -51,7 +51,7 @@ jobs:
|
||||
- name: Install Node
|
||||
uses: actions/setup-node@v7
|
||||
with:
|
||||
node-version: '26.13.1'
|
||||
node-version: '26.13.0'
|
||||
|
||||
- name: Install dependencies
|
||||
run: pnpm install --frozen-lockfile
|
||||
@@ -182,7 +182,7 @@ jobs:
|
||||
- name: Install Node
|
||||
uses: actions/setup-node@v7
|
||||
with:
|
||||
node-version: '26.13.1'
|
||||
node-version: '26.13.0'
|
||||
cache: 'pnpm'
|
||||
|
||||
- name: Pnpm install and check
|
||||
@@ -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
|
||||
@@ -262,7 +264,7 @@ jobs:
|
||||
- name: Install Node
|
||||
uses: actions/setup-node@v7
|
||||
with:
|
||||
node-version: '26.13.1'
|
||||
node-version: '26.13.0'
|
||||
cache: 'pnpm'
|
||||
|
||||
- name: Pnpm install and check
|
||||
@@ -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: |-
|
||||
@@ -395,7 +399,7 @@ jobs:
|
||||
- name: Install Node
|
||||
uses: actions/setup-node@v7
|
||||
with:
|
||||
node-version: '26.13.1'
|
||||
node-version: '26.13.0'
|
||||
cache: 'pnpm'
|
||||
|
||||
- name: Pnpm install and check
|
||||
@@ -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: |
|
||||
@@ -479,7 +485,7 @@ jobs:
|
||||
- name: Install Node
|
||||
uses: actions/setup-node@v7
|
||||
with:
|
||||
node-version: '26.13.1'
|
||||
node-version: '26.13.0'
|
||||
|
||||
- uses: pnpm/action-setup@v6.1.1
|
||||
name: Install pnpm
|
||||
|
||||
@@ -63,7 +63,7 @@ jobs:
|
||||
|
||||
- uses: actions/setup-node@v7
|
||||
with:
|
||||
node-version: '26.13.1'
|
||||
node-version: '26.13.0'
|
||||
cache: 'pnpm'
|
||||
|
||||
- run: pnpm install --frozen-lockfile
|
||||
|
||||
@@ -33,7 +33,7 @@ jobs:
|
||||
- name: Install gh-aw extension
|
||||
uses: github/gh-aw-actions/setup-cli@53d0749e19424a918acd82b4d140264ce48f2f14 # v1.2.2
|
||||
with:
|
||||
version: v1.2.2
|
||||
version: v1.0.1
|
||||
- name: Install Rust Stable
|
||||
uses: dtolnay/rust-toolchain@stable
|
||||
with:
|
||||
@@ -49,7 +49,7 @@ jobs:
|
||||
- name: Install Node.js
|
||||
uses: actions/setup-node@v7
|
||||
with:
|
||||
node-version: '26.13.1'
|
||||
node-version: '26.13.0'
|
||||
cache: 'pnpm'
|
||||
- name: Install frontend dependencies
|
||||
env:
|
||||
|
||||
@@ -43,7 +43,7 @@ jobs:
|
||||
- name: Install Node
|
||||
uses: actions/setup-node@v7
|
||||
with:
|
||||
node-version: '26.13.1'
|
||||
node-version: '26.13.0'
|
||||
|
||||
- uses: pnpm/action-setup@v6
|
||||
name: Install pnpm
|
||||
|
||||
@@ -104,7 +104,7 @@ jobs:
|
||||
if: github.event.inputs[matrix.input] == 'true'
|
||||
uses: actions/setup-node@v7
|
||||
with:
|
||||
node-version: '26.13.1'
|
||||
node-version: '26.13.0'
|
||||
cache: 'pnpm'
|
||||
|
||||
- name: Pnpm install and check
|
||||
@@ -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'
|
||||
|
||||
@@ -36,7 +36,7 @@ jobs:
|
||||
|
||||
- uses: actions/setup-node@v7
|
||||
with:
|
||||
node-version: '26.13.1'
|
||||
node-version: '26.13.0'
|
||||
cache: 'pnpm'
|
||||
|
||||
- run: pnpm install --frozen-lockfile
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -61,7 +61,7 @@ jobs:
|
||||
|
||||
- uses: actions/setup-node@v7
|
||||
with:
|
||||
node-version: '26.13.1'
|
||||
node-version: '26.13.0'
|
||||
|
||||
- uses: pnpm/action-setup@v6
|
||||
if: github.event.action != 'closed'
|
||||
|
||||
@@ -46,7 +46,7 @@ jobs:
|
||||
fetch-depth: 0
|
||||
- uses: actions/setup-node@v7
|
||||
with:
|
||||
node-version: '26.13.1'
|
||||
node-version: '26.13.0'
|
||||
- name: Validate source and publish the approved snapshot
|
||||
id: source
|
||||
env:
|
||||
@@ -231,7 +231,7 @@ jobs:
|
||||
- name: Install Node
|
||||
uses: actions/setup-node@v7
|
||||
with:
|
||||
node-version: '26.13.1'
|
||||
node-version: '26.13.0'
|
||||
cache: 'pnpm'
|
||||
|
||||
- name: Pnpm install and check
|
||||
@@ -330,7 +330,7 @@ jobs:
|
||||
- name: Install Node
|
||||
uses: actions/setup-node@v7
|
||||
with:
|
||||
node-version: '26.13.1'
|
||||
node-version: '26.13.0'
|
||||
cache: 'pnpm'
|
||||
|
||||
- name: Pnpm install and check
|
||||
@@ -461,7 +461,7 @@ jobs:
|
||||
- name: Install Node
|
||||
uses: actions/setup-node@v7
|
||||
with:
|
||||
node-version: '26.13.1'
|
||||
node-version: '26.13.0'
|
||||
cache: 'pnpm'
|
||||
|
||||
- name: Pnpm install and check
|
||||
@@ -542,7 +542,7 @@ jobs:
|
||||
fetch-depth: 0
|
||||
- uses: actions/setup-node@v7
|
||||
with:
|
||||
node-version: '26.13.1'
|
||||
node-version: '26.13.0'
|
||||
- uses: pnpm/action-setup@v6
|
||||
with:
|
||||
run_install: false
|
||||
@@ -565,7 +565,7 @@ jobs:
|
||||
- name: Install Node
|
||||
uses: actions/setup-node@v7
|
||||
with:
|
||||
node-version: '26.13.1'
|
||||
node-version: '26.13.0'
|
||||
|
||||
- uses: pnpm/action-setup@v6
|
||||
name: Install pnpm
|
||||
@@ -593,7 +593,7 @@ jobs:
|
||||
- name: Install Node
|
||||
uses: actions/setup-node@v7
|
||||
with:
|
||||
node-version: '26.13.1'
|
||||
node-version: '26.13.0'
|
||||
|
||||
- uses: pnpm/action-setup@v6
|
||||
name: Install pnpm
|
||||
@@ -661,7 +661,7 @@ jobs:
|
||||
- name: Install Node
|
||||
uses: actions/setup-node@v7
|
||||
with:
|
||||
node-version: '26.13.1'
|
||||
node-version: '26.13.0'
|
||||
|
||||
- uses: pnpm/action-setup@v6
|
||||
name: Install pnpm
|
||||
|
||||
@@ -28,7 +28,7 @@ jobs:
|
||||
- name: Install Node
|
||||
uses: actions/setup-node@v7
|
||||
with:
|
||||
node-version: '26.13.1'
|
||||
node-version: '26.13.0'
|
||||
|
||||
- name: Install pnpm
|
||||
uses: pnpm/action-setup@v6
|
||||
|
||||
@@ -34,7 +34,7 @@ jobs:
|
||||
- name: Install Node
|
||||
uses: actions/setup-node@v7
|
||||
with:
|
||||
node-version: '26.13.1'
|
||||
node-version: '26.13.0'
|
||||
|
||||
- uses: pnpm/action-setup@v6
|
||||
name: Install pnpm
|
||||
|
||||
@@ -15,7 +15,7 @@ jobs:
|
||||
- name: Install Node
|
||||
uses: actions/setup-node@v7
|
||||
with:
|
||||
node-version: '26.13.1'
|
||||
node-version: '26.13.0'
|
||||
|
||||
- uses: pnpm/action-setup@v6
|
||||
name: Install pnpm
|
||||
@@ -39,7 +39,7 @@ jobs:
|
||||
- name: Install Node
|
||||
uses: actions/setup-node@v7
|
||||
with:
|
||||
node-version: '26.13.1'
|
||||
node-version: '26.13.0'
|
||||
|
||||
- uses: pnpm/action-setup@v6
|
||||
name: Install pnpm
|
||||
|
||||
+1
-1
@@ -1,3 +1,3 @@
|
||||
nodejs 26.13.1
|
||||
nodejs 26.13.0
|
||||
pnpm 12.18.0
|
||||
rust 1.99.1
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -24,8 +24,6 @@
|
||||
<details>
|
||||
<summary><strong> 🐞 修复问题 </strong></summary>
|
||||
|
||||
- 修复应用退出时无条件重写 `clash.yaml`、`verge.yaml` 和 `profiles.yaml` 导致文件创建时间丢失的问题
|
||||
|
||||
- 修复检测更新后又发布新版时,应用内更新失败
|
||||
- 修复订阅响应过大导致内存占用过高,限制单次下载为 4 MiB
|
||||
- 修复全局模式默认停在 `DIRECT` 或 `REJECT` 导致流量未经代理,现自动切换到第一个策略组
|
||||
@@ -42,7 +40,6 @@
|
||||
<details>
|
||||
<summary><strong> ✨ 新增功能 </strong></summary>
|
||||
|
||||
- 新增连接页面按网络协议(TCP/UDP)筛选连接
|
||||
|
||||
</details>
|
||||
|
||||
|
||||
@@ -74,16 +74,12 @@ impl<T: Clone> Draft<T> {
|
||||
result
|
||||
}
|
||||
|
||||
/// Commits and clears the draft, reporting whether one was staged.
|
||||
/// Commits and clears the draft.
|
||||
#[inline]
|
||||
pub fn apply(&self) -> bool {
|
||||
pub fn apply(&self) {
|
||||
let mut guard = self.inner.data.lock();
|
||||
match guard.1.take() {
|
||||
Some(d) => {
|
||||
guard.0 = d;
|
||||
true
|
||||
}
|
||||
None => false,
|
||||
if let Some(d) = guard.1.take() {
|
||||
guard.0 = d;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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 })
|
||||
}
|
||||
@@ -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"
|
||||
]
|
||||
}
|
||||
@@ -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)
|
||||
}
|
||||
},
|
||||
},
|
||||
]
|
||||
}
|
||||
@@ -280,29 +280,25 @@ impl Config {
|
||||
}
|
||||
|
||||
/// Commits drafts during exit/restart/shutdown so user changes are not lost.
|
||||
/// A config with nothing staged keeps its file, so its timestamps stay put.
|
||||
pub async fn apply_all_and_save_file() {
|
||||
logging!(debug, Type::Config, "save all draft data");
|
||||
let save_clash_task = AsyncHandler::spawn(|| async {
|
||||
let clash = Self::clash().await;
|
||||
if clash.apply() {
|
||||
logging_error!(Type::Config, clash.data_arc().save_config().await);
|
||||
}
|
||||
clash.apply();
|
||||
logging_error!(Type::Config, clash.data_arc().save_config().await);
|
||||
});
|
||||
|
||||
let save_verge_task = AsyncHandler::spawn(|| async {
|
||||
let verge = Self::verge().await;
|
||||
if verge.apply() {
|
||||
logging_error!(Type::Config, verge.data_arc().save_file().await);
|
||||
}
|
||||
verge.apply();
|
||||
logging_error!(Type::Config, verge.data_arc().save_file().await);
|
||||
});
|
||||
|
||||
let save_profiles_task = AsyncHandler::spawn(|| async {
|
||||
let _profile_write = super::profiles::PROFILE_WRITE_LOCK.lock().await;
|
||||
let profiles = Self::profiles().await;
|
||||
if profiles.apply() {
|
||||
logging_error!(Type::Config, profiles.data_arc().save_file().await);
|
||||
}
|
||||
profiles.apply();
|
||||
logging_error!(Type::Config, profiles.data_arc().save_file().await);
|
||||
});
|
||||
|
||||
let _ = tokio::join!(save_clash_task, save_verge_task, save_profiles_task);
|
||||
|
||||
@@ -71,10 +71,6 @@ const ORDER_OPTIONS = [
|
||||
|
||||
type OrderKey = (typeof ORDER_OPTIONS)[number]['id']
|
||||
|
||||
type NetworkFilter = 'all' | 'tcp' | 'udp'
|
||||
|
||||
let savedNetworkFilter: NetworkFilter = 'all'
|
||||
|
||||
const orderFunctionMap = ORDER_OPTIONS.reduce<Record<OrderKey, OrderFunc>>(
|
||||
(acc, option) => {
|
||||
acc[option.id] = option.fn
|
||||
@@ -90,9 +86,6 @@ const ConnectionsPage = () => {
|
||||
const { searchState, match, onSearch } = usePageSearch('connections')
|
||||
const hasSearch = searchState.text.length > 0
|
||||
const [curOrderOpt, setCurOrderOpt] = useState<OrderKey>('default')
|
||||
const [networkFilter, setNetworkFilter] =
|
||||
useState<NetworkFilter>(savedNetworkFilter)
|
||||
const hasFilter = networkFilter !== 'all'
|
||||
const [connectionsType, setConnectionsType] = useState<'active' | 'closed'>(
|
||||
'active',
|
||||
)
|
||||
@@ -119,30 +112,18 @@ const ConnectionsPage = () => {
|
||||
const filterConn = useMemo(() => {
|
||||
const orderFunc = orderFunctionMap[curOrderOpt]
|
||||
|
||||
if (isTableLayout && !hasSearch && !hasFilter) return selectedConnections
|
||||
if (!hasSearch && !hasFilter) return orderFunc([...selectedConnections])
|
||||
if (isTableLayout && !hasSearch) return selectedConnections
|
||||
if (!hasSearch) return orderFunc([...selectedConnections])
|
||||
|
||||
const matchConns = selectedConnections.filter((conn) => {
|
||||
const { host, destinationIP, process, network } = conn.metadata
|
||||
if (networkFilter !== 'all' && network !== networkFilter) return false
|
||||
const { host, destinationIP, process } = conn.metadata
|
||||
return (
|
||||
!hasSearch ||
|
||||
match(host || '') ||
|
||||
match(destinationIP || '') ||
|
||||
match(process || '')
|
||||
match(host || '') || match(destinationIP || '') || match(process || '')
|
||||
)
|
||||
})
|
||||
|
||||
return orderFunc ? orderFunc(matchConns) : matchConns
|
||||
}, [
|
||||
selectedConnections,
|
||||
isTableLayout,
|
||||
hasSearch,
|
||||
hasFilter,
|
||||
networkFilter,
|
||||
match,
|
||||
curOrderOpt,
|
||||
])
|
||||
}, [selectedConnections, isTableLayout, hasSearch, match, curOrderOpt])
|
||||
|
||||
const displayRows = useConnectionRowViews(
|
||||
isTableLayout ? EMPTY_CONNECTIONS : filterConn,
|
||||
@@ -274,24 +255,6 @@ const ConnectionsPage = () => {
|
||||
))}
|
||||
</BaseStyledSelect>
|
||||
)}
|
||||
<BaseStyledSelect
|
||||
style={{ width: 80 }}
|
||||
value={networkFilter}
|
||||
onChange={(e) => {
|
||||
savedNetworkFilter = e.target.value as NetworkFilter
|
||||
setNetworkFilter(savedNetworkFilter)
|
||||
}}
|
||||
>
|
||||
<MenuItem value="all">
|
||||
<span style={{ fontSize: 14 }}>All</span>
|
||||
</MenuItem>
|
||||
<MenuItem value="tcp">
|
||||
<span style={{ fontSize: 14 }}>TCP</span>
|
||||
</MenuItem>
|
||||
<MenuItem value="udp">
|
||||
<span style={{ fontSize: 14 }}>UDP</span>
|
||||
</MenuItem>
|
||||
</BaseStyledSelect>
|
||||
<Box
|
||||
sx={{
|
||||
flex: 1,
|
||||
|
||||
+13
-6
@@ -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'),
|
||||
|
||||
Reference in New Issue
Block a user