nuxt-state 0.0.1 → 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +62 -33
- package/dist/module.json +1 -1
- package/dist/module.mjs +9 -2
- package/dist/runtime/app/composables/defineState.js +9 -1
- package/dist/runtime/app/plugins/hydration.d.ts +3 -0
- package/dist/runtime/app/plugins/hydration.js +13 -0
- package/dist/runtime/app/state-registry.d.ts +14 -0
- package/dist/runtime/app/state-registry.js +38 -0
- package/dist/runtime/app/state-snapshot.d.ts +13 -0
- package/dist/runtime/app/state-snapshot.js +70 -0
- package/package.json +33 -29
package/README.md
CHANGED
|
@@ -7,18 +7,16 @@
|
|
|
7
7
|
```ts
|
|
8
8
|
defineState(() => {
|
|
9
9
|
// standard Vue Composition API
|
|
10
|
-
return {
|
|
11
|
-
|
|
12
|
-
};
|
|
13
|
-
});
|
|
10
|
+
return {/* public state */}
|
|
11
|
+
})
|
|
14
12
|
```
|
|
15
13
|
|
|
16
14
|
A regular composable runs its factory for every invocation. A composable created by
|
|
17
15
|
`defineState` runs its factory lazily, once for the current Nuxt application instance,
|
|
18
16
|
and returns that exact result to every caller in that app.
|
|
19
17
|
|
|
20
|
-
This is a working prototype for discussion and possible future contribution to
|
|
21
|
-
|
|
18
|
+
This is a working open-source prototype for discussion and possible future contribution to
|
|
19
|
+
Nuxt. The API is intentionally narrow and the project is not yet presented as production-ready.
|
|
22
20
|
|
|
23
21
|
## Why
|
|
24
22
|
|
|
@@ -39,13 +37,10 @@ pnpm add nuxt-state
|
|
|
39
37
|
```ts
|
|
40
38
|
// nuxt.config.ts
|
|
41
39
|
export default defineNuxtConfig({
|
|
42
|
-
modules: [
|
|
43
|
-
})
|
|
40
|
+
modules: ['nuxt-state'],
|
|
41
|
+
})
|
|
44
42
|
```
|
|
45
43
|
|
|
46
|
-
The package has not been published yet; during development, use this repository as a
|
|
47
|
-
workspace dependency.
|
|
48
|
-
|
|
49
44
|
## Usage
|
|
50
45
|
|
|
51
46
|
Create a state in Nuxt 4's application source directory:
|
|
@@ -53,19 +48,19 @@ Create a state in Nuxt 4's application source directory:
|
|
|
53
48
|
```ts
|
|
54
49
|
// app/states/counter.ts
|
|
55
50
|
export const useCounter = defineState(() => {
|
|
56
|
-
const count = ref(0)
|
|
57
|
-
const double = computed(() => count.value * 2)
|
|
51
|
+
const count = ref(0)
|
|
52
|
+
const double = computed(() => count.value * 2)
|
|
58
53
|
|
|
59
54
|
function increment() {
|
|
60
|
-
count.value
|
|
55
|
+
count.value++
|
|
61
56
|
}
|
|
62
57
|
|
|
63
58
|
return {
|
|
64
59
|
count,
|
|
65
60
|
double,
|
|
66
61
|
increment,
|
|
67
|
-
}
|
|
68
|
-
})
|
|
62
|
+
}
|
|
63
|
+
})
|
|
69
64
|
```
|
|
70
65
|
|
|
71
66
|
Exports from `app/states/`, including nested directories and multiple exports per file,
|
|
@@ -74,11 +69,11 @@ as in `app/composables/`.
|
|
|
74
69
|
|
|
75
70
|
```vue
|
|
76
71
|
<script setup lang="ts">
|
|
77
|
-
const { count, double, increment } = useCounter()
|
|
72
|
+
const { count, double, increment } = useCounter()
|
|
78
73
|
|
|
79
|
-
count.value
|
|
80
|
-
increment()
|
|
81
|
-
console.log(double.value)
|
|
74
|
+
count.value++
|
|
75
|
+
increment()
|
|
76
|
+
console.log(double.value)
|
|
82
77
|
</script>
|
|
83
78
|
```
|
|
84
79
|
|
|
@@ -99,35 +94,67 @@ refs remain computed refs, reactive objects remain reactive, and functions are u
|
|
|
99
94
|
allowing old application instances to be garbage-collected.
|
|
100
95
|
- Separate `defineState()` calls have separate closure-owned caches, including calls in
|
|
101
96
|
the same file.
|
|
97
|
+
- Mutable state used during SSR is restored into the client-created refs and reactive proxies
|
|
98
|
+
before Vue hydrates the component tree.
|
|
102
99
|
|
|
103
100
|
Async factories are rejected by TypeScript and guarded at runtime for JavaScript users.
|
|
104
101
|
Expose an async function from synchronous state or use Nuxt's data-fetching APIs instead.
|
|
105
102
|
|
|
106
|
-
## SSR
|
|
103
|
+
## SSR hydration
|
|
104
|
+
|
|
105
|
+
v0.1.0 transparently hydrates mutable top-level members returned by the factory when they are
|
|
106
|
+
created with `ref()` or `reactive()`. Nuxt injects an internal call-site key at build time; the
|
|
107
|
+
developer-facing call remains exactly `defineState(factory)`.
|
|
108
|
+
|
|
109
|
+
On the server, the module captures the final values after rendering in one namespaced Nuxt
|
|
110
|
+
payload entry. On the client, it runs the factory normally and patches those values into the
|
|
111
|
+
new refs and reactive proxies before Vue hydration. The returned object is never replaced, so
|
|
112
|
+
computed refs, functions, watchers, and closures created by the client factory remain wired to
|
|
113
|
+
the hydrated state.
|
|
114
|
+
|
|
115
|
+
```ts
|
|
116
|
+
export const useAccount = defineState(() => {
|
|
117
|
+
const count = ref(0)
|
|
118
|
+
const user = reactive({ name: 'Guest', roles: [] as string[] })
|
|
119
|
+
const double = computed(() => count.value * 2)
|
|
120
|
+
const increment = () => count.value++
|
|
121
|
+
|
|
122
|
+
return { count, user, double, increment }
|
|
123
|
+
})
|
|
124
|
+
```
|
|
107
125
|
|
|
108
|
-
|
|
109
|
-
|
|
126
|
+
Nested serializable values inside supported refs and reactive objects are handled by Nuxt's
|
|
127
|
+
payload serializer. Functions, readonly computed refs, and plain runtime objects are recreated
|
|
128
|
+
by the factory rather than serialized. Concurrent SSR requests retain separate Nuxt-app
|
|
129
|
+
registries and cannot share user state.
|
|
110
130
|
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
131
|
+
`useFetch()` can remain inside a synchronous state factory. Its request caching and payload
|
|
132
|
+
hydration still belong to Nuxt; `nuxt-state` neither replaces nor triggers a second fetch
|
|
133
|
+
mechanism. If multiple sibling SSR components must all render completed data, await the returned
|
|
134
|
+
Nuxt `AsyncData` promise in a parent/page as you would with normal Nuxt data fetching.
|
|
115
135
|
|
|
116
136
|
## Current limitations
|
|
117
137
|
|
|
118
138
|
- Nuxt 4 and Vue 3 only.
|
|
119
139
|
- Synchronous factories only.
|
|
120
140
|
- No persistence or browser-storage integration.
|
|
121
|
-
-
|
|
141
|
+
- Hydration is guaranteed for standard `ref()` and `reactive()` members only. Advanced
|
|
142
|
+
primitives such as shallow/custom refs and writable-computed edge cases are not v0.1.0
|
|
143
|
+
guarantees.
|
|
144
|
+
- Hydrated values must be serializable by Nuxt's payload system; DOM nodes, sockets, functions
|
|
145
|
+
inside refs, symbols, and arbitrary native/class resources are unsupported.
|
|
122
146
|
- No Nuxt Layers support yet.
|
|
123
147
|
- State resets when its module is hot-reloaded; HMR preservation is not implemented.
|
|
124
148
|
- Compatibility with every context-sensitive Nuxt composable inside a factory is not
|
|
125
149
|
guaranteed yet.
|
|
126
150
|
- There is no reset API, keyed/multi-instance state, DevTools integration, or central
|
|
127
|
-
registry.
|
|
151
|
+
user-facing registry.
|
|
152
|
+
- The keyed transform is source-sensitive. The supported path is the module's auto-imported
|
|
153
|
+
`defineState`; a barrel re-export or unrelated manual wrapper is not guaranteed to receive an
|
|
154
|
+
internal hydration key.
|
|
128
155
|
|
|
129
|
-
See [
|
|
130
|
-
|
|
156
|
+
See [the architecture notes](./docs/architecture.md) and [roadmap](./docs/roadmap.md) for
|
|
157
|
+
implementation constraints and deferred work.
|
|
131
158
|
|
|
132
159
|
## Development
|
|
133
160
|
|
|
@@ -136,6 +163,7 @@ Requires Node.js 22+ and pnpm.
|
|
|
136
163
|
```bash
|
|
137
164
|
pnpm install
|
|
138
165
|
pnpm dev
|
|
166
|
+
pnpm fmt:check
|
|
139
167
|
pnpm lint
|
|
140
168
|
pnpm test:types
|
|
141
169
|
pnpm test
|
|
@@ -143,8 +171,9 @@ pnpm prepack
|
|
|
143
171
|
pnpm dev:build
|
|
144
172
|
```
|
|
145
173
|
|
|
146
|
-
The
|
|
147
|
-
|
|
174
|
+
The browser suite requires Chromium, installed with
|
|
175
|
+
`pnpm exec playwright-core install chromium`. The playground contains two counter components,
|
|
176
|
+
a reactive object example, a nested state, and two independent states exported from one file.
|
|
148
177
|
|
|
149
178
|
## License
|
|
150
179
|
|
package/dist/module.json
CHANGED
package/dist/module.mjs
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { resolve } from 'node:path';
|
|
2
|
-
import { defineNuxtModule, createResolver, addImports, addImportsDir } from '@nuxt/kit';
|
|
2
|
+
import { defineNuxtModule, createResolver, addImports, addPlugin, addImportsDir } from '@nuxt/kit';
|
|
3
3
|
|
|
4
4
|
const module$1 = defineNuxtModule({
|
|
5
5
|
meta: {
|
|
@@ -11,10 +11,17 @@ const module$1 = defineNuxtModule({
|
|
|
11
11
|
defaults: {},
|
|
12
12
|
setup(_options, nuxt) {
|
|
13
13
|
const resolver = createResolver(import.meta.url);
|
|
14
|
+
const defineStateSource = resolver.resolve("./runtime/app/composables/defineState");
|
|
14
15
|
addImports({
|
|
15
16
|
name: "defineState",
|
|
16
|
-
from:
|
|
17
|
+
from: defineStateSource
|
|
17
18
|
});
|
|
19
|
+
nuxt.options.optimization.keyedComposables.push({
|
|
20
|
+
name: "defineState",
|
|
21
|
+
source: defineStateSource,
|
|
22
|
+
argumentLength: 2
|
|
23
|
+
});
|
|
24
|
+
addPlugin(resolver.resolve("./runtime/app/plugins/hydration"));
|
|
18
25
|
addImportsDir(resolve(nuxt.options.srcDir, "states/**"));
|
|
19
26
|
}
|
|
20
27
|
});
|
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
import { useNuxtApp } from "#app";
|
|
2
|
+
import { registerHydratableState } from "../state-registry.js";
|
|
3
|
+
import { restoreState, snapshotState } from "../state-snapshot.js";
|
|
2
4
|
function isPromiseLike(value) {
|
|
3
5
|
return (typeof value === "object" && value !== null || typeof value === "function") && "then" in value && typeof value.then === "function";
|
|
4
6
|
}
|
|
5
|
-
export function defineState(factory) {
|
|
7
|
+
export function defineState(factory, internalKey) {
|
|
6
8
|
const instances = /* @__PURE__ */ new WeakMap();
|
|
7
9
|
return function useDefinedState() {
|
|
8
10
|
const nuxtApp = useNuxtApp();
|
|
@@ -16,6 +18,12 @@ export function defineState(factory) {
|
|
|
16
18
|
);
|
|
17
19
|
}
|
|
18
20
|
instances.set(nuxtApp, instance);
|
|
21
|
+
if (internalKey) {
|
|
22
|
+
registerHydratableState(nuxtApp, internalKey, {
|
|
23
|
+
snapshot: () => snapshotState(instance),
|
|
24
|
+
restore: (snapshot) => restoreState(instance, snapshot)
|
|
25
|
+
});
|
|
26
|
+
}
|
|
19
27
|
return instance;
|
|
20
28
|
};
|
|
21
29
|
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { defineNuxtPlugin, useHydration } from "#app";
|
|
2
|
+
import {
|
|
3
|
+
collectStateSnapshots,
|
|
4
|
+
receiveStateSnapshots
|
|
5
|
+
} from "../state-registry.js";
|
|
6
|
+
export const STATE_PAYLOAD_KEY = "__nuxt_state__";
|
|
7
|
+
export default defineNuxtPlugin((nuxtApp) => {
|
|
8
|
+
useHydration(
|
|
9
|
+
STATE_PAYLOAD_KEY,
|
|
10
|
+
() => collectStateSnapshots(nuxtApp),
|
|
11
|
+
(snapshots) => receiveStateSnapshots(nuxtApp, snapshots)
|
|
12
|
+
);
|
|
13
|
+
});
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
export type StateHydrationPayload = Record<string, unknown>;
|
|
2
|
+
export interface HydratableStateEntry {
|
|
3
|
+
snapshot: () => unknown;
|
|
4
|
+
restore: (snapshot: unknown) => void;
|
|
5
|
+
}
|
|
6
|
+
interface StateRegistry {
|
|
7
|
+
active: Map<string, HydratableStateEntry>;
|
|
8
|
+
hydration: Map<string, unknown>;
|
|
9
|
+
}
|
|
10
|
+
export declare function getStateRegistry(nuxtApp: object): StateRegistry;
|
|
11
|
+
export declare function registerHydratableState(nuxtApp: object, key: string, entry: HydratableStateEntry): void;
|
|
12
|
+
export declare function collectStateSnapshots(nuxtApp: object): StateHydrationPayload;
|
|
13
|
+
export declare function receiveStateSnapshots(nuxtApp: object, snapshots: StateHydrationPayload | undefined): void;
|
|
14
|
+
export {};
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
const registries = /* @__PURE__ */ new WeakMap();
|
|
2
|
+
export function getStateRegistry(nuxtApp) {
|
|
3
|
+
let registry = registries.get(nuxtApp);
|
|
4
|
+
if (!registry) {
|
|
5
|
+
registry = {
|
|
6
|
+
active: /* @__PURE__ */ new Map(),
|
|
7
|
+
hydration: /* @__PURE__ */ new Map()
|
|
8
|
+
};
|
|
9
|
+
registries.set(nuxtApp, registry);
|
|
10
|
+
}
|
|
11
|
+
return registry;
|
|
12
|
+
}
|
|
13
|
+
export function registerHydratableState(nuxtApp, key, entry) {
|
|
14
|
+
const registry = getStateRegistry(nuxtApp);
|
|
15
|
+
registry.active.set(key, entry);
|
|
16
|
+
if (registry.hydration.has(key)) {
|
|
17
|
+
const snapshot = registry.hydration.get(key);
|
|
18
|
+
registry.hydration.delete(key);
|
|
19
|
+
entry.restore(snapshot);
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
export function collectStateSnapshots(nuxtApp) {
|
|
23
|
+
const snapshots = {};
|
|
24
|
+
for (const [key, entry] of getStateRegistry(nuxtApp).active) {
|
|
25
|
+
snapshots[key] = entry.snapshot();
|
|
26
|
+
}
|
|
27
|
+
return snapshots;
|
|
28
|
+
}
|
|
29
|
+
export function receiveStateSnapshots(nuxtApp, snapshots) {
|
|
30
|
+
const registry = getStateRegistry(nuxtApp);
|
|
31
|
+
registry.hydration = new Map(Object.entries(snapshots ?? {}));
|
|
32
|
+
for (const [key, entry] of registry.active) {
|
|
33
|
+
if (!registry.hydration.has(key)) continue;
|
|
34
|
+
const snapshot = registry.hydration.get(key);
|
|
35
|
+
registry.hydration.delete(key);
|
|
36
|
+
entry.restore(snapshot);
|
|
37
|
+
}
|
|
38
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
interface RefSnapshot {
|
|
2
|
+
type: 'ref';
|
|
3
|
+
value: unknown;
|
|
4
|
+
}
|
|
5
|
+
interface ReactiveSnapshot {
|
|
6
|
+
type: 'reactive';
|
|
7
|
+
value: unknown;
|
|
8
|
+
}
|
|
9
|
+
type StateSnapshotEntry = RefSnapshot | ReactiveSnapshot;
|
|
10
|
+
export type StateSnapshot = Record<string, StateSnapshotEntry>;
|
|
11
|
+
export declare function snapshotState(state: unknown): StateSnapshot;
|
|
12
|
+
export declare function restoreState(state: unknown, snapshot: unknown): void;
|
|
13
|
+
export {};
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
import { isReactive, isReadonly, isRef, toRaw } from "vue";
|
|
2
|
+
function isObjectLike(value) {
|
|
3
|
+
return typeof value === "object" && value !== null || typeof value === "function";
|
|
4
|
+
}
|
|
5
|
+
function unwrapReactive(value) {
|
|
6
|
+
return isReactive(value) ? toRaw(value) : value;
|
|
7
|
+
}
|
|
8
|
+
export function snapshotState(state) {
|
|
9
|
+
const snapshot = {};
|
|
10
|
+
if (!isObjectLike(state)) return snapshot;
|
|
11
|
+
for (const [name, value] of Object.entries(state)) {
|
|
12
|
+
if (isRef(value) && !isReadonly(value)) {
|
|
13
|
+
snapshot[name] = {
|
|
14
|
+
type: "ref",
|
|
15
|
+
value: unwrapReactive(value.value)
|
|
16
|
+
};
|
|
17
|
+
} else if (isReactive(value) && !isReadonly(value)) {
|
|
18
|
+
snapshot[name] = {
|
|
19
|
+
type: "reactive",
|
|
20
|
+
value: toRaw(value)
|
|
21
|
+
};
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
return snapshot;
|
|
25
|
+
}
|
|
26
|
+
function isPlainRecord(value) {
|
|
27
|
+
return Object.prototype.toString.call(value) === "[object Object]";
|
|
28
|
+
}
|
|
29
|
+
function canPatch(target, source) {
|
|
30
|
+
return Array.isArray(target) && Array.isArray(source) || isPlainRecord(target) && isPlainRecord(source);
|
|
31
|
+
}
|
|
32
|
+
function patchValue(target, source) {
|
|
33
|
+
if (Array.isArray(target) && Array.isArray(source)) {
|
|
34
|
+
for (let index = 0; index < source.length; index++) {
|
|
35
|
+
if (canPatch(target[index], source[index])) {
|
|
36
|
+
patchValue(target[index], source[index]);
|
|
37
|
+
} else {
|
|
38
|
+
target[index] = source[index];
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
target.length = source.length;
|
|
42
|
+
return;
|
|
43
|
+
}
|
|
44
|
+
if (!isPlainRecord(target) || !isPlainRecord(source)) return;
|
|
45
|
+
for (const key of Object.keys(target)) {
|
|
46
|
+
if (!(key in source)) delete target[key];
|
|
47
|
+
}
|
|
48
|
+
for (const [key, value] of Object.entries(source)) {
|
|
49
|
+
if (canPatch(target[key], value)) {
|
|
50
|
+
patchValue(target[key], value);
|
|
51
|
+
} else {
|
|
52
|
+
target[key] = value;
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
function isStateSnapshotEntry(value) {
|
|
57
|
+
return isPlainRecord(value) && (value.type === "ref" || value.type === "reactive") && Object.hasOwn(value, "value");
|
|
58
|
+
}
|
|
59
|
+
export function restoreState(state, snapshot) {
|
|
60
|
+
if (!isObjectLike(state) || !isPlainRecord(snapshot)) return;
|
|
61
|
+
for (const [name, entry] of Object.entries(snapshot)) {
|
|
62
|
+
if (!isStateSnapshotEntry(entry)) continue;
|
|
63
|
+
const target = state[name];
|
|
64
|
+
if (entry.type === "ref" && isRef(target) && !isReadonly(target)) {
|
|
65
|
+
target.value = entry.value;
|
|
66
|
+
} else if (entry.type === "reactive" && isReactive(target) && !isReadonly(target)) {
|
|
67
|
+
patchValue(target, entry.value);
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
}
|
package/package.json
CHANGED
|
@@ -1,31 +1,31 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "nuxt-state",
|
|
3
|
-
"version": "0.0
|
|
3
|
+
"version": "0.1.0",
|
|
4
4
|
"description": "Define shared Nuxt state using the same Composition API you already use in composables.",
|
|
5
5
|
"keywords": [
|
|
6
|
+
"composable",
|
|
6
7
|
"nuxt",
|
|
7
8
|
"nuxt-module",
|
|
8
|
-
"vue",
|
|
9
9
|
"state",
|
|
10
|
-
"
|
|
10
|
+
"vue"
|
|
11
11
|
],
|
|
12
|
+
"homepage": "https://github.com/navidtm/nuxt-state#readme",
|
|
13
|
+
"bugs": {
|
|
14
|
+
"url": "https://github.com/navidtm/nuxt-state/issues"
|
|
15
|
+
},
|
|
12
16
|
"license": "MIT",
|
|
13
17
|
"author": "Navid Talebian",
|
|
14
|
-
"type": "module",
|
|
15
18
|
"repository": {
|
|
16
19
|
"type": "git",
|
|
17
20
|
"url": "git+https://github.com/navidtm/nuxt-state.git"
|
|
18
21
|
},
|
|
19
|
-
"
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
"import": "./dist/module.mjs"
|
|
27
|
-
}
|
|
28
|
-
},
|
|
22
|
+
"workspaces": [
|
|
23
|
+
"playground"
|
|
24
|
+
],
|
|
25
|
+
"files": [
|
|
26
|
+
"dist"
|
|
27
|
+
],
|
|
28
|
+
"type": "module",
|
|
29
29
|
"main": "./dist/module.mjs",
|
|
30
30
|
"typesVersions": {
|
|
31
31
|
"*": {
|
|
@@ -34,24 +34,18 @@
|
|
|
34
34
|
]
|
|
35
35
|
}
|
|
36
36
|
},
|
|
37
|
-
"
|
|
38
|
-
"
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
37
|
+
"exports": {
|
|
38
|
+
".": {
|
|
39
|
+
"types": "./dist/types.d.mts",
|
|
40
|
+
"import": "./dist/module.mjs"
|
|
41
|
+
}
|
|
42
|
+
},
|
|
43
43
|
"publishConfig": {
|
|
44
44
|
"access": "public"
|
|
45
45
|
},
|
|
46
|
-
"engines": {
|
|
47
|
-
"node": ">=22"
|
|
48
|
-
},
|
|
49
46
|
"dependencies": {
|
|
50
47
|
"@nuxt/kit": "^4.5.2"
|
|
51
48
|
},
|
|
52
|
-
"peerDependencies": {
|
|
53
|
-
"nuxt": "^4.0.0"
|
|
54
|
-
},
|
|
55
49
|
"devDependencies": {
|
|
56
50
|
"@nuxt/devtools": "^3.4.2",
|
|
57
51
|
"@nuxt/eslint-config": "^1.17.0",
|
|
@@ -60,21 +54,31 @@
|
|
|
60
54
|
"@nuxt/test-utils": "^4.1.0",
|
|
61
55
|
"@types/node": "latest",
|
|
62
56
|
"changelogen": "^0.6.2",
|
|
63
|
-
"eslint": "^10.9.0",
|
|
64
57
|
"happy-dom": "^20.8.3",
|
|
65
58
|
"nuxt": "^4.5.2",
|
|
59
|
+
"oxfmt": "^0.65.0",
|
|
60
|
+
"oxlint": "^1.80.0",
|
|
61
|
+
"playwright-core": "^1.62.1",
|
|
66
62
|
"typescript": "^6.0.3",
|
|
67
63
|
"vitest": "^4.1.11",
|
|
68
64
|
"vue": "^3.5.31",
|
|
69
65
|
"vue-tsc": "^3.3.11"
|
|
70
66
|
},
|
|
67
|
+
"engines": {
|
|
68
|
+
"node": ">=22"
|
|
69
|
+
},
|
|
71
70
|
"scripts": {
|
|
72
71
|
"dev": "pnpm dev:prepare && nuxt dev playground",
|
|
73
72
|
"dev:build": "nuxt build playground",
|
|
74
73
|
"dev:prepare": "nuxt-module-build build --stub && nuxt-module-build prepare && nuxt prepare playground",
|
|
75
|
-
"lint": "
|
|
74
|
+
"lint": "oxlint",
|
|
75
|
+
"lint:fix": "oxlint --fix",
|
|
76
|
+
"fmt": "oxfmt",
|
|
77
|
+
"fmt:check": "oxfmt --check",
|
|
76
78
|
"test": "vitest run",
|
|
79
|
+
"test:browser": "vitest run test/browser",
|
|
80
|
+
"check": "pnpm run fmt:check && pnpm run lint",
|
|
77
81
|
"test:watch": "vitest",
|
|
78
|
-
"test:types": "vue-tsc --noEmit && pnpm --dir playground exec vue-tsc --noEmit"
|
|
82
|
+
"test:types": "nuxt-module-build build && vue-tsc --noEmit && pnpm --dir playground exec vue-tsc --noEmit"
|
|
79
83
|
}
|
|
80
84
|
}
|