Skip to content

Commit a96d7a4

Browse files
committed
feat: persist user settings
1 parent 9c6ffea commit a96d7a4

File tree

10 files changed

+43
-21
lines changed

10 files changed

+43
-21
lines changed

packages/core/package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,7 @@
5858
"birpc": "catalog:deps",
5959
"birpc-x": "catalog:deps",
6060
"cac": "catalog:deps",
61+
"h3": "catalog:deps",
6162
"immer": "catalog:deps",
6263
"launch-editor": "catalog:deps",
6364
"mlly": "catalog:deps",

packages/core/src/client/webcomponents/state/context.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -86,7 +86,7 @@ export async function createDocksContext(
8686
const getSettingsStore = async () => {
8787
if (!_settingsStorePromise) {
8888
_settingsStorePromise = rpc.sharedState.get(
89-
'vite:internal:docks:settings',
89+
'vite:internal:user-settings',
9090
{ initialValue: DEFAULT_STATE_DOCKS_SETTINGS() },
9191
)
9292
}

packages/core/src/client/webcomponents/state/dock-settings.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,7 @@ export function docksGroupByCategories(
6161
const customOrderA = customOrder[a.id] ?? 0
6262
const customOrderB = customOrder[b.id] ?? 0
6363
if (customOrderA !== customOrderB)
64-
return customOrderB - customOrderA
64+
return customOrderA - customOrderB
6565

6666
// Finally by default order
6767
const ia = a.defaultOrder ?? 0

packages/core/src/node/context.ts

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -39,13 +39,13 @@ export async function createDevToolsContext(
3939
context.views = viewsHost
4040
context.terminals = terminalsHost
4141

42-
await docksHost.init()
43-
4442
// Build-in function to list all RPC functions
4543
for (const fn of builtinRpcDeclarations) {
4644
rpcHost.register(fn)
4745
}
4846

47+
await docksHost.init()
48+
4949
const docksSharedState = await rpcHost.sharedState.get('vite:internal:docks', { initialValue: [] })
5050

5151
// Register hosts side effects
@@ -69,7 +69,6 @@ export async function createDevToolsContext(
6969

7070
// Register plugins
7171
const plugins = viteConfig.plugins.filter(plugin => 'devtools' in plugin)
72-
7372
for (const plugin of plugins) {
7473
if (!plugin.devtools?.setup)
7574
continue

packages/core/src/node/host-docks.ts

Lines changed: 12 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
import type { DevToolsDockEntry, DevToolsDockHost as DevToolsDockHostType, DevToolsDocksUserSettings, DevToolsDockUserEntry, DevToolsNodeContext, DevToolsViewBuiltin } from '@vitejs/devtools-kit'
22
import type { SharedState } from '@vitejs/devtools-kit/utils/shared-state'
33
import { createEventEmitter } from '@vitejs/devtools-kit/utils/events'
4+
import { join } from 'pathe'
5+
import { createStorage } from './storage'
46

57
export class DevToolsDockHost implements DevToolsDockHostType {
68
public readonly views: DevToolsDockHostType['views'] = new Map()
@@ -14,13 +16,16 @@ export class DevToolsDockHost implements DevToolsDockHostType {
1416
}
1517

1618
async init() {
17-
this.userSettings = await this.context.rpc.sharedState.get('vite:internal:docks:settings', {
18-
initialValue: {
19-
hiddenDocks: [],
20-
hiddenCategories: [],
21-
pinnedDocks: [],
22-
customOrder: {},
23-
},
19+
this.userSettings = await this.context.rpc.sharedState.get('vite:internal:user-settings', {
20+
sharedState: createStorage({
21+
filepath: join(this.context.workspaceRoot, 'node_modules/.vite/devtools/settings.json'),
22+
initialValue: {
23+
hiddenDocks: [],
24+
hiddenCategories: [],
25+
pinnedDocks: [],
26+
customOrder: {},
27+
},
28+
}),
2429
})
2530
}
2631

packages/core/src/node/rpc-shared-state.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -46,12 +46,12 @@ export function createRpcSharedStateServerHost(
4646
if (sharedState.has(key)) {
4747
return sharedState.get(key)!
4848
}
49-
if (options?.initialValue === undefined) {
49+
if (options?.initialValue === undefined && options?.sharedState === undefined) {
5050
throw new Error(`Shared state of "${key}" is not found, please provide an initial value for the first time`)
5151
}
5252
debug('new-state', key)
53-
const state = createSharedState<T>({
54-
initialValue: options.initialValue,
53+
const state = options.sharedState ?? createSharedState<T>({
54+
initialValue: options.initialValue as T,
5555
enablePatches: false,
5656
})
5757
registerSharedState(key, state)

packages/core/src/node/rpc/index.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,7 @@ declare module '@vitejs/devtools-kit' {
6464

6565
// @keep-sorted
6666
export interface DevToolsRpcSharedStates {
67-
'vite:internal:docks:settings': DevToolsDocksUserSettings
6867
'vite:internal:docks': DevToolsDockEntry[]
68+
'vite:internal:user-settings': DevToolsDocksUserSettings
6969
}
7070
}

packages/core/src/node/storage.ts

Lines changed: 18 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,25 @@
11
import fs from 'node:fs'
22
import { createSharedState } from '@vitejs/devtools-kit/utils/shared-state'
33
import { dirname } from 'pathe'
4+
import { debounce } from 'perfect-debounce'
45

56
export interface CreateStorageOptions<T extends object> {
67
filepath: string
78
initialValue: T
9+
mergeInitialValue?: false | ((initialValue: T, savedValue: T) => T)
10+
debounce?: number
811
}
912

1013
export function createStorage<T extends object>(options: CreateStorageOptions<T>) {
14+
const {
15+
mergeInitialValue = (initialValue, savedValue) => ({ ...initialValue, ...savedValue }),
16+
debounce: debounceTime = 100,
17+
} = options
18+
1119
let initialValue: T
1220
if (fs.existsSync(options.filepath)) {
13-
initialValue = JSON.parse(fs.readFileSync(options.filepath, 'utf-8')) as T
21+
const savedValue = JSON.parse(fs.readFileSync(options.filepath, 'utf-8')) as T
22+
initialValue = mergeInitialValue ? mergeInitialValue(options.initialValue, savedValue) : savedValue
1423
}
1524
else {
1625
initialValue = options.initialValue
@@ -21,10 +30,14 @@ export function createStorage<T extends object>(options: CreateStorageOptions<T>
2130
enablePatches: false,
2231
})
2332

24-
state.on('updated', (newState) => {
25-
fs.mkdirSync(dirname(options.filepath), { recursive: true })
26-
fs.writeFileSync(options.filepath, `${JSON.stringify(newState, null, 2)}\n`)
27-
})
33+
// throttle the write to the file
34+
state.on(
35+
'updated',
36+
debounce((newState) => {
37+
fs.mkdirSync(dirname(options.filepath), { recursive: true })
38+
fs.writeFileSync(options.filepath, `${JSON.stringify(newState, null, 2)}\n`)
39+
}, debounceTime),
40+
)
2841

2942
return state
3043
}

packages/kit/src/types/rpc.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@ export type RpcFunctionsHost = RpcFunctionsCollectorBase<DevToolsRpcServerFuncti
4646

4747
export interface RpcSharedStateGetOptions<T> {
4848
initialValue?: T
49+
sharedState?: SharedState<T>
4950
}
5051

5152
export interface RpcSharedStateHost {

pnpm-lock.yaml

Lines changed: 3 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)