@poveste/plugin-vue 0.2.1 → 0.3.1
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/dist/bundled/client/app/MountStory.js +45 -54
- package/dist/bundled/client/app/RenderStory.js +66 -73
- package/dist/bundled/client/app/RouterLinkStub.js +38 -39
- package/dist/bundled/client/app/Story.js +98 -111
- package/dist/bundled/client/app/Variant.js +89 -103
- package/dist/bundled/client/app/auto-props.js +46 -62
- package/dist/bundled/client/app/global-components.js +63 -79
- package/dist/bundled/client/app/host.js +72 -76
- package/dist/bundled/client/app/index.js +3 -6
- package/dist/bundled/client/app/render-context.js +9 -10
- package/dist/bundled/client/app/util.js +68 -65
- package/dist/bundled/client/client.js +4 -9
- package/dist/bundled/client/codegen.js +143 -190
- package/dist/bundled/client/server/Story.js +74 -78
- package/dist/bundled/client/server/Variant.js +45 -45
- package/dist/bundled/client/server/run.js +41 -42
- package/dist/bundled/client/server/stub.js +9 -7
- package/dist/bundled/client/server.js +1 -3
- package/dist/client/app/Story.d.ts +1 -1
- package/dist/client/app/host.d.ts +1 -1
- package/dist/client/server/Story.d.ts +2 -2
- package/dist/client/server/Variant.d.ts +1 -1
- package/package.json +11 -8
- package/src/client/app/Variant.ts +15 -5
- package/src/client/app/__tests__/state-sync.spec.ts +154 -0
- package/src/client/app/host.ts +2 -1
- package/src/client/app/util.ts +24 -10
- package/src/client/codegen.ts +1 -1
- package/src/client/server/Variant.ts +2 -1
- package/tsconfig.json +1 -1
- package/vitest.config.ts +13 -0
|
@@ -1,79 +1,75 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
return t || console.error(e), null;
|
|
74
|
-
}
|
|
75
|
-
}
|
|
1
|
+
import { autoStubComponents as e } from "./stub.js";
|
|
2
|
+
import { defineComponent as t, inject as n, onMounted as r, provide as i } from "vue";
|
|
3
|
+
//#region src/client/server/Story.ts
|
|
4
|
+
var a = t({
|
|
5
|
+
name: "PovesteStory",
|
|
6
|
+
inheritAttrs: !1,
|
|
7
|
+
props: {
|
|
8
|
+
title: {
|
|
9
|
+
type: String,
|
|
10
|
+
default: void 0
|
|
11
|
+
},
|
|
12
|
+
id: {
|
|
13
|
+
type: String,
|
|
14
|
+
default: void 0
|
|
15
|
+
},
|
|
16
|
+
group: {
|
|
17
|
+
type: String,
|
|
18
|
+
default: void 0
|
|
19
|
+
},
|
|
20
|
+
layout: {
|
|
21
|
+
type: Object,
|
|
22
|
+
default: void 0
|
|
23
|
+
},
|
|
24
|
+
icon: {
|
|
25
|
+
type: String,
|
|
26
|
+
default: void 0
|
|
27
|
+
},
|
|
28
|
+
iconColor: {
|
|
29
|
+
type: String,
|
|
30
|
+
default: void 0
|
|
31
|
+
},
|
|
32
|
+
docsOnly: {
|
|
33
|
+
type: Boolean,
|
|
34
|
+
default: !1
|
|
35
|
+
},
|
|
36
|
+
meta: {
|
|
37
|
+
type: Object,
|
|
38
|
+
default: void 0
|
|
39
|
+
}
|
|
40
|
+
},
|
|
41
|
+
setup(e) {
|
|
42
|
+
let t = n("hstStoryFile"), a = {
|
|
43
|
+
id: e.id ?? t?.id,
|
|
44
|
+
title: e.title ?? t?.fileName,
|
|
45
|
+
group: e.group,
|
|
46
|
+
layout: e.layout,
|
|
47
|
+
icon: e.icon,
|
|
48
|
+
iconColor: e.iconColor,
|
|
49
|
+
docsOnly: e.docsOnly,
|
|
50
|
+
meta: e.meta,
|
|
51
|
+
variants: []
|
|
52
|
+
};
|
|
53
|
+
return n("addStory", null)?.(a), i("story", a), i("addVariant", (e) => {
|
|
54
|
+
a.variants.push(e);
|
|
55
|
+
}), r(() => {
|
|
56
|
+
a.variants.length || a.variants.push({
|
|
57
|
+
id: "_default",
|
|
58
|
+
title: "default"
|
|
59
|
+
});
|
|
60
|
+
}), { story: a };
|
|
61
|
+
},
|
|
62
|
+
render() {
|
|
63
|
+
let t = !1;
|
|
64
|
+
try {
|
|
65
|
+
let n = this.$slots.default?.({ get state() {
|
|
66
|
+
return t = !0, {};
|
|
67
|
+
} });
|
|
68
|
+
return Array.isArray(n) && e(n), n;
|
|
69
|
+
} catch (e) {
|
|
70
|
+
return t || console.error(e), null;
|
|
71
|
+
}
|
|
72
|
+
}
|
|
76
73
|
});
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
};
|
|
74
|
+
//#endregion
|
|
75
|
+
export { a as default };
|
|
@@ -1,46 +1,46 @@
|
|
|
1
|
-
import { defineComponent as
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
1
|
+
import { defineComponent as e, inject as t } from "vue";
|
|
2
|
+
//#region src/client/server/Variant.ts
|
|
3
|
+
var n = e({
|
|
4
|
+
name: "PovesteVariant",
|
|
5
|
+
props: {
|
|
6
|
+
title: {
|
|
7
|
+
type: String,
|
|
8
|
+
default: "untitled"
|
|
9
|
+
},
|
|
10
|
+
id: {
|
|
11
|
+
type: String,
|
|
12
|
+
default: void 0
|
|
13
|
+
},
|
|
14
|
+
icon: {
|
|
15
|
+
type: String,
|
|
16
|
+
default: void 0
|
|
17
|
+
},
|
|
18
|
+
iconColor: {
|
|
19
|
+
type: String,
|
|
20
|
+
default: void 0
|
|
21
|
+
},
|
|
22
|
+
meta: {
|
|
23
|
+
type: Object,
|
|
24
|
+
default: void 0
|
|
25
|
+
}
|
|
26
|
+
},
|
|
27
|
+
setup(e) {
|
|
28
|
+
let n = t("story");
|
|
29
|
+
function r() {
|
|
30
|
+
return `${n.id}-${n.variants.length}`;
|
|
31
|
+
}
|
|
32
|
+
let i = {
|
|
33
|
+
id: e.id ?? r(),
|
|
34
|
+
title: e.title,
|
|
35
|
+
icon: e.icon,
|
|
36
|
+
iconColor: e.iconColor,
|
|
37
|
+
meta: e.meta
|
|
38
|
+
};
|
|
39
|
+
t("addVariant")(i);
|
|
40
|
+
},
|
|
41
|
+
render() {
|
|
42
|
+
return null;
|
|
43
|
+
}
|
|
43
44
|
});
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
};
|
|
45
|
+
//#endregion
|
|
46
|
+
export { n as default };
|
|
@@ -1,44 +1,43 @@
|
|
|
1
|
+
import e from "./Story.js";
|
|
2
|
+
import t from "./Variant.js";
|
|
3
|
+
import { createApp as n, h as r } from "vue";
|
|
1
4
|
import { getSetupHook as i } from "@poveste/shared";
|
|
2
|
-
import * as
|
|
3
|
-
import * as
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
import
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
});
|
|
39
|
-
}
|
|
40
|
-
t.unmount();
|
|
5
|
+
import * as a from "virtual:$poveste-generated-global-setup";
|
|
6
|
+
import * as o from "virtual:$poveste-setup";
|
|
7
|
+
//#region src/client/server/run.ts
|
|
8
|
+
async function s({ file: s, storyData: c, el: l }) {
|
|
9
|
+
let { default: u } = await import(/* @vite-ignore */
|
|
10
|
+
/* @vite-ignore */
|
|
11
|
+
s.moduleId
|
|
12
|
+
), d = n({
|
|
13
|
+
provide: {
|
|
14
|
+
addStory(e) {
|
|
15
|
+
c.push(e);
|
|
16
|
+
},
|
|
17
|
+
hstStoryFile: s
|
|
18
|
+
},
|
|
19
|
+
render() {
|
|
20
|
+
return r(u);
|
|
21
|
+
}
|
|
22
|
+
});
|
|
23
|
+
d.component("Story", e), d.component("Variant", t);
|
|
24
|
+
let f = {
|
|
25
|
+
app: d,
|
|
26
|
+
story: null,
|
|
27
|
+
variant: null,
|
|
28
|
+
addWrapper: () => {}
|
|
29
|
+
}, p = i(a, "setupVue3");
|
|
30
|
+
p && await p(f);
|
|
31
|
+
let m = i(o, "setupVue3");
|
|
32
|
+
if (m && await m(f), d.mount(l), u.doc) {
|
|
33
|
+
let e = document.createElement("div");
|
|
34
|
+
e.innerHTML = u.doc;
|
|
35
|
+
let t = e.textContent;
|
|
36
|
+
c.forEach((e) => {
|
|
37
|
+
e.docsText = t;
|
|
38
|
+
});
|
|
39
|
+
}
|
|
40
|
+
d.unmount();
|
|
41
41
|
}
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
};
|
|
42
|
+
//#endregion
|
|
43
|
+
export { s as run };
|
|
@@ -1,8 +1,10 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
}
|
|
6
|
-
export {
|
|
7
|
-
o as autoStubComponents
|
|
1
|
+
//#region src/client/server/stub.ts
|
|
2
|
+
var e = {
|
|
3
|
+
name: "StubbedComponent",
|
|
4
|
+
render: () => null
|
|
8
5
|
};
|
|
6
|
+
function t(n) {
|
|
7
|
+
for (let r of n) typeof r.type == "object" && r.type.name !== "PovesteVariant" && (r.type = e), Array.isArray(r.children) && t(r.children);
|
|
8
|
+
}
|
|
9
|
+
//#endregion
|
|
10
|
+
export { t as autoStubComponents };
|
|
@@ -27,7 +27,7 @@ declare const _default: import("vue").DefineComponent<import("vue").ExtractPropT
|
|
|
27
27
|
default: any;
|
|
28
28
|
};
|
|
29
29
|
}>> & Readonly<{}>, {
|
|
30
|
-
meta: import("
|
|
30
|
+
meta: import("poveste").StoryMeta;
|
|
31
31
|
initState: () => any | Promise<any>;
|
|
32
32
|
}, {}, {}, {}, string, import("vue").ComponentProvideOptions, true, {}, any>;
|
|
33
33
|
export default _default;
|
|
@@ -69,11 +69,11 @@ declare const _default: import("vue").DefineComponent<import("vue").ExtractPropT
|
|
|
69
69
|
default: any;
|
|
70
70
|
};
|
|
71
71
|
}>> & Readonly<{}>, {
|
|
72
|
-
meta: import("
|
|
72
|
+
meta: import("poveste").StoryMeta;
|
|
73
73
|
title: string;
|
|
74
74
|
id: string;
|
|
75
75
|
group: string;
|
|
76
|
-
layout: import("
|
|
76
|
+
layout: import("poveste").StoryLayout;
|
|
77
77
|
icon: string;
|
|
78
78
|
iconColor: string;
|
|
79
79
|
docsOnly: boolean;
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@poveste/plugin-vue",
|
|
3
3
|
"type": "module",
|
|
4
|
-
"version": "0.
|
|
4
|
+
"version": "0.3.1",
|
|
5
5
|
"description": "Poveste plugin for Vue.js support",
|
|
6
6
|
"author": {
|
|
7
7
|
"name": "Sorin Gitlan"
|
|
@@ -44,28 +44,31 @@
|
|
|
44
44
|
"types": "dist/index.node.d.ts",
|
|
45
45
|
"peerDependencies": {
|
|
46
46
|
"vue": "^3.5.26",
|
|
47
|
-
"poveste": "^0.
|
|
47
|
+
"poveste": "^0.3.1"
|
|
48
48
|
},
|
|
49
49
|
"dependencies": {
|
|
50
50
|
"change-case": "^5.4.4",
|
|
51
51
|
"globby": "^14.0.2",
|
|
52
52
|
"launch-editor": "^2.9.1",
|
|
53
53
|
"pathe": "^1.1.2",
|
|
54
|
-
"@poveste/controls": "^0.
|
|
55
|
-
"@poveste/
|
|
56
|
-
"@poveste/
|
|
54
|
+
"@poveste/controls": "^0.3.1",
|
|
55
|
+
"@poveste/shared": "^0.3.1",
|
|
56
|
+
"@poveste/vendors": "^0.3.1"
|
|
57
57
|
},
|
|
58
58
|
"devDependencies": {
|
|
59
59
|
"@types/node": "^22.10.1",
|
|
60
60
|
"concurrently": "^9.1.0",
|
|
61
61
|
"typescript": "5.6.3",
|
|
62
|
-
"vite": "^
|
|
62
|
+
"vite": "^8.2.0",
|
|
63
|
+
"vitest": "^4.1.10",
|
|
63
64
|
"vue": "^3.5.26",
|
|
64
|
-
"poveste": "0.
|
|
65
|
+
"poveste": "0.3.1"
|
|
65
66
|
},
|
|
66
67
|
"scripts": {
|
|
67
68
|
"build": "rimraf dist && vite build && tsc -d -P tsconfig.build.json && pnpm run build:types",
|
|
68
69
|
"build:types": "tsc --declaration --emitDeclarationOnly",
|
|
69
|
-
"watch": "concurrently \"vite build --watch\" \"tsc -d -P tsconfig.build.json --watch\" \"pnpm run build:types --watch\""
|
|
70
|
+
"watch": "concurrently \"vite build --watch\" \"tsc -d -P tsconfig.build.json --watch\" \"pnpm run build:types --watch\"",
|
|
71
|
+
"test": "vitest run",
|
|
72
|
+
"test:dev": "vitest"
|
|
70
73
|
}
|
|
71
74
|
}
|
|
@@ -61,6 +61,20 @@ export default defineComponent({
|
|
|
61
61
|
let lastPropsTypesSnapshot: string
|
|
62
62
|
let renderVariant: Variant
|
|
63
63
|
let renderStateSync: ReturnType<typeof syncStateBundledAndExternal>
|
|
64
|
+
let mountStateSync: ReturnType<typeof syncStateBundledAndExternal>
|
|
65
|
+
|
|
66
|
+
// Registered before the first `await` below, and deliberately so. This is
|
|
67
|
+
// an async setup(), and Vue drops any lifecycle hook registered after an
|
|
68
|
+
// await has actually suspended — it warns, then the hook is bound to no
|
|
69
|
+
// instance and never runs. That is how both state syncs below used to
|
|
70
|
+
// survive unmount: a story with `initState` suspends here, so the teardown
|
|
71
|
+
// that used to sit at the bottom of setup() was silently discarded, and
|
|
72
|
+
// every leaked pair kept deep-watching the variant state it was created
|
|
73
|
+
// for. See #77.
|
|
74
|
+
onBeforeUnmount(() => {
|
|
75
|
+
renderStateSync?.stop()
|
|
76
|
+
mountStateSync?.stop()
|
|
77
|
+
})
|
|
64
78
|
|
|
65
79
|
const mountVariant = computed(() => attrs.variant)
|
|
66
80
|
|
|
@@ -70,7 +84,7 @@ export default defineComponent({
|
|
|
70
84
|
}
|
|
71
85
|
|
|
72
86
|
if (renderContext?.mode !== 'render' && mountVariant.value && implicitState) {
|
|
73
|
-
syncStateBundledAndExternal(mountVariant.value.state, implicitState())
|
|
87
|
+
mountStateSync = syncStateBundledAndExternal(mountVariant.value.state, implicitState())
|
|
74
88
|
}
|
|
75
89
|
|
|
76
90
|
function updateVariant(variant: Variant) {
|
|
@@ -160,10 +174,6 @@ export default defineComponent({
|
|
|
160
174
|
updateVariant(mountVariant.value)
|
|
161
175
|
}
|
|
162
176
|
|
|
163
|
-
onBeforeUnmount(() => {
|
|
164
|
-
renderStateSync?.stop()
|
|
165
|
-
})
|
|
166
|
-
|
|
167
177
|
return {
|
|
168
178
|
renderContext,
|
|
169
179
|
renderVariantSlot,
|
|
@@ -0,0 +1,154 @@
|
|
|
1
|
+
import { nextTick as bundledNextTick, reactive as bundledReactive } from '@poveste/vendors/vue'
|
|
2
|
+
import { describe, expect, it } from 'vitest'
|
|
3
|
+
import { nextTick, reactive } from 'vue'
|
|
4
|
+
import { syncStateBundledAndExternal } from '../util.js'
|
|
5
|
+
|
|
6
|
+
// These run against both Vue copies for real: `@poveste/vendors/vue` is the one
|
|
7
|
+
// poveste bundles, `vue` is the one a user's story would run on. That is the
|
|
8
|
+
// whole point — the sync exists because the two have separate reactivity graphs
|
|
9
|
+
// and separate schedulers, and nothing about it can be tested with one of them.
|
|
10
|
+
|
|
11
|
+
// Each side flushes on its own scheduler and a write can bounce between them a
|
|
12
|
+
// few times before settling, so drain both alternately rather than once.
|
|
13
|
+
async function settle() {
|
|
14
|
+
for (let i = 0; i < 6; i++) {
|
|
15
|
+
await nextTick()
|
|
16
|
+
await bundledNextTick()
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
function setup(initial: Record<string, any>) {
|
|
21
|
+
const bundled = bundledReactive(structuredClone(initial))
|
|
22
|
+
const external = reactive(structuredClone(initial))
|
|
23
|
+
const sync = syncStateBundledAndExternal(bundled, external)
|
|
24
|
+
return { bundled, external, sync }
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
describe('syncStateBundledAndExternal', () => {
|
|
28
|
+
it('mirrors an edit in each direction', async () => {
|
|
29
|
+
const { bundled, external, sync } = setup({ count: 0 })
|
|
30
|
+
await settle()
|
|
31
|
+
|
|
32
|
+
external.count = 1
|
|
33
|
+
await settle()
|
|
34
|
+
expect(bundled.count).toBe(1)
|
|
35
|
+
|
|
36
|
+
bundled.count = 2
|
|
37
|
+
await settle()
|
|
38
|
+
expect(external.count).toBe(2)
|
|
39
|
+
|
|
40
|
+
sync.stop()
|
|
41
|
+
})
|
|
42
|
+
|
|
43
|
+
it('stops mirroring once stopped', async () => {
|
|
44
|
+
const { bundled, external, sync } = setup({ count: 0 })
|
|
45
|
+
await settle()
|
|
46
|
+
sync.stop()
|
|
47
|
+
|
|
48
|
+
external.count = 1
|
|
49
|
+
await settle()
|
|
50
|
+
expect(bundled.count).toBe(0)
|
|
51
|
+
})
|
|
52
|
+
|
|
53
|
+
// The #95 regression. `applyState` iterates the incoming keys, so it has no
|
|
54
|
+
// way to express "this key is gone" — the write it performs for a removal
|
|
55
|
+
// changes nothing on the far side, and the far side's watcher never fires.
|
|
56
|
+
//
|
|
57
|
+
// That matters because the two watchers coordinate through a `syncing` boolean
|
|
58
|
+
// meaning "ignore the next firing, it is my own echo", and the counterpart
|
|
59
|
+
// firing is what clears it. No counterpart, no clear: the flag stayed set, and
|
|
60
|
+
// the next edit was mistaken for an echo and dropped on the floor.
|
|
61
|
+
//
|
|
62
|
+
// Worth being exact about how bad this was in the shipped app, because it is
|
|
63
|
+
// the reason the issue had no repro: it never bit here. Vue stories always
|
|
64
|
+
// carry `_hPropState` and `_hPropDefs`, and `applyState` sent every
|
|
65
|
+
// `_h`-prefixed key down the plain-assignment branch, so those two were
|
|
66
|
+
// re-identified on every apply and a counterpart firing was guaranteed. The
|
|
67
|
+
// invariant held by luck. These tests use a state without that bookkeeping,
|
|
68
|
+
// which is what the function actually promises to handle — and which is the
|
|
69
|
+
// shape the sandbox bridge and `plugin-svelte` were relying on luck for too.
|
|
70
|
+
it('does not drop the edit after a removal it cannot mirror', async () => {
|
|
71
|
+
const { bundled, external, sync } = setup({ count: 0, gone: 1 })
|
|
72
|
+
await settle()
|
|
73
|
+
|
|
74
|
+
delete external.gone
|
|
75
|
+
await settle()
|
|
76
|
+
|
|
77
|
+
external.count = 1
|
|
78
|
+
await settle()
|
|
79
|
+
expect(bundled.count).toBe(1)
|
|
80
|
+
|
|
81
|
+
sync.stop()
|
|
82
|
+
})
|
|
83
|
+
|
|
84
|
+
it('does not drop the edit after a nested removal it cannot mirror', async () => {
|
|
85
|
+
// The same shape one level down, and the more plausible one: a story that
|
|
86
|
+
// drops an entry out of a map in state, then touches a control.
|
|
87
|
+
const { bundled, external, sync } = setup({ count: 0, items: { a: 1, b: 2 } })
|
|
88
|
+
await settle()
|
|
89
|
+
|
|
90
|
+
delete external.items.b
|
|
91
|
+
await settle()
|
|
92
|
+
|
|
93
|
+
external.count = 1
|
|
94
|
+
await settle()
|
|
95
|
+
expect(bundled.count).toBe(1)
|
|
96
|
+
|
|
97
|
+
sync.stop()
|
|
98
|
+
})
|
|
99
|
+
|
|
100
|
+
it('does not drop the edit after a change that nets out to nothing', async () => {
|
|
101
|
+
const { bundled, external, sync } = setup({ count: 0, list: [1, 2] })
|
|
102
|
+
await settle()
|
|
103
|
+
|
|
104
|
+
external.list.push(9)
|
|
105
|
+
external.list.pop()
|
|
106
|
+
await settle()
|
|
107
|
+
|
|
108
|
+
external.count = 1
|
|
109
|
+
await settle()
|
|
110
|
+
expect(bundled.count).toBe(1)
|
|
111
|
+
|
|
112
|
+
sync.stop()
|
|
113
|
+
})
|
|
114
|
+
|
|
115
|
+
it('leaves object identity alone on both sides when a sync changes nothing', async () => {
|
|
116
|
+
// The other half of the fix, and the reason the flag can now be trusted.
|
|
117
|
+
// `toRawDeep` rebuilds every object it passes, so mirroring `count` used to
|
|
118
|
+
// drop a fresh `nested` and `list` onto the receiving side as well, and each
|
|
119
|
+
// of those re-triggered its watchers. Every sync was a burst of firings that
|
|
120
|
+
// happened to keep the count even; none of them meant anything.
|
|
121
|
+
const { bundled, external, sync } = setup({ count: 0, nested: { a: 1 }, list: [1, 2] })
|
|
122
|
+
await settle()
|
|
123
|
+
|
|
124
|
+
const externalNested = external.nested
|
|
125
|
+
const externalList = external.list
|
|
126
|
+
const bundledNested = bundled.nested
|
|
127
|
+
const bundledList = bundled.list
|
|
128
|
+
|
|
129
|
+
external.count = 1
|
|
130
|
+
await settle()
|
|
131
|
+
|
|
132
|
+
expect(bundled.count).toBe(1)
|
|
133
|
+
expect(external.nested).toBe(externalNested)
|
|
134
|
+
expect(external.list).toBe(externalList)
|
|
135
|
+
// The receiving side is the one that used to get churned.
|
|
136
|
+
expect(bundled.nested).toBe(bundledNested)
|
|
137
|
+
expect(bundled.list).toBe(bundledList)
|
|
138
|
+
|
|
139
|
+
sync.stop()
|
|
140
|
+
})
|
|
141
|
+
|
|
142
|
+
it('mirrors many edits in a row without falling behind', async () => {
|
|
143
|
+
const { bundled, external, sync } = setup({ count: 0 })
|
|
144
|
+
await settle()
|
|
145
|
+
|
|
146
|
+
for (let i = 1; i <= 10; i++) {
|
|
147
|
+
external.count = i
|
|
148
|
+
await settle()
|
|
149
|
+
expect(bundled.count).toBe(i)
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
sync.stop()
|
|
153
|
+
})
|
|
154
|
+
})
|
package/src/client/app/host.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import type { Story, Variant } from '@poveste/shared'
|
|
2
2
|
import type { App, Component, PropType, VNode } from 'vue'
|
|
3
3
|
import type { Vue3StorySetupApi, Vue3StorySetupHandler } from '../../helpers.js'
|
|
4
|
+
import type { PreviewRenderContext } from './render-context.js'
|
|
4
5
|
import { getSetupHook } from '@poveste/shared'
|
|
5
6
|
// @ts-expect-error virtual module id
|
|
6
7
|
import * as generatedSetup from 'virtual:$poveste-generated-global-setup'
|
|
@@ -13,7 +14,7 @@ import {
|
|
|
13
14
|
Suspense,
|
|
14
15
|
} from 'vue'
|
|
15
16
|
import { registerGlobalComponents } from './global-components.js'
|
|
16
|
-
import {
|
|
17
|
+
import { provideRenderContext } from './render-context.js'
|
|
17
18
|
import { RouterLinkStub } from './RouterLinkStub'
|
|
18
19
|
|
|
19
20
|
interface PreviewHostOptions {
|