@pinegrow/vite-plugin 3.0.0-beta.9 → 3.0.0-beta.90

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.
@@ -0,0 +1,355 @@
1
+ import { onBeforeMount, onMounted, onBeforeUnmount, getCurrentInstance, ref, reactive, computed } from 'vue'
2
+
3
+ export function usePinegrow() {
4
+ const initCache = () => {
5
+ // conditional
6
+ const winObj = window
7
+
8
+ if (!(winObj?.process?.client && winObj.process.client !== true)) {
9
+ if (!winObj.pinegrow) {
10
+ // conditional
11
+ // console.log('Cache initialized by Pinegrow Vue Plugin!')
12
+ const elCache = reactive(new Map())
13
+
14
+ const enrichWithComponentName = (elCacheObj, localFile) => {
15
+ return {
16
+ name: elCacheObj.instance.type.__name || '',
17
+ localFile,
18
+ ...elCacheObj,
19
+ }
20
+ }
21
+
22
+ const pgIdToElComputed = computed(() => {
23
+ const pgIds = {}
24
+ for (let [el, elCacheNodes] of elCache.entries()) {
25
+ if (el.isConnected) {
26
+ elCacheNodes.forEach(elCacheObj => {
27
+ if (
28
+ elCacheObj.instance.isMounted &&
29
+ !elCacheObj.instance.isUnmounted &&
30
+ elCacheObj.pgId &&
31
+ (!elCacheObj.rootEl || elCacheObj.isRootFragment)
32
+ ) {
33
+ if (!pgIds[elCacheObj.pgId]) {
34
+ pgIds[elCacheObj.pgId] = []
35
+ }
36
+ if (elCacheObj.key === undefined || elCacheObj.key === null) {
37
+ pgIds[elCacheObj.pgId].push(elCacheObj)
38
+ } else {
39
+ pgIds[elCacheObj.pgId].push([elCacheObj.key, elCacheObj])
40
+ }
41
+ }
42
+ })
43
+ }
44
+ }
45
+ return pgIds
46
+ })
47
+
48
+ const localComponentToElComputed = computed(() => {
49
+ const localComponents = {}
50
+ for (let [el, elCacheNodes] of elCache.entries()) {
51
+ if (el.isConnected) {
52
+ elCacheNodes.forEach(elCacheObj => {
53
+ if (
54
+ elCacheObj.instance.isMounted &&
55
+ !elCacheObj.instance.isUnmounted &&
56
+ elCacheObj.localFile
57
+ // && (!elCacheObj.rootEl || elCacheObj.isRootFragment)
58
+ ) {
59
+ if (!localComponents[elCacheObj.localFile]) {
60
+ localComponents[elCacheObj.localFile] = []
61
+ }
62
+ localComponents[elCacheObj.localFile].push(
63
+ enrichWithComponentName(elCacheObj, elCacheObj.localFile)
64
+ )
65
+ }
66
+ })
67
+ }
68
+ }
69
+ return localComponents
70
+ })
71
+
72
+ const appTree = computed(() => {
73
+ const appTreeNodes = Object.values(localComponentToElComputed.value).reduce(
74
+ (acc, localComponent) => [...acc, ...localComponent],
75
+ []
76
+ )
77
+ const appTreeNodesSorted = appTreeNodes.sort((a, b) =>
78
+ a.el.compareDocumentPosition(b.el) & Node.DOCUMENT_POSITION_FOLLOWING ? -1 : 1
79
+ )
80
+ return appTreeNodesSorted
81
+ })
82
+
83
+ winObj.pinegrow = {
84
+ elCache,
85
+ // // Uncomment these two to test locally
86
+ // elCacheErrHandlerFn,
87
+ // elUpdateHanderFn,
88
+ // rootFragmentToPgIdComputed,
89
+ pgIdToElComputed,
90
+ localComponentToElComputed,
91
+ appTree,
92
+ }
93
+ }
94
+ }
95
+ }
96
+
97
+ // pgId & key can be optional
98
+ const pgUpdateElCache = (hook, pgId, rootEl, key, localFile) => async vnode => {
99
+ if (!vnode) return
100
+ if (window?.process?.client && process?.client !== true) return
101
+ if (!window.pinegrow) initCache()
102
+
103
+ let el = vnode.el
104
+ if (!el) return
105
+
106
+ const instance = vnode.component || vnode.ctx || el.__vueParentComponent
107
+
108
+ try {
109
+ if ((key !== null && key !== undefined) || (vnode.key !== null && vnode.key !== undefined)) {
110
+ key = vnode.key || key
111
+ }
112
+
113
+ localFile = localFile || vnode.type.__file
114
+
115
+ let isRootFragment = false
116
+ let isFragment = el.nodeType !== 1
117
+ let firstEl,
118
+ lastEl,
119
+ isIsland = false,
120
+ childVNodes = []
121
+
122
+ // May be an iles Island that wraps components with client directives
123
+ if (localFile) {
124
+ if (localFile.includes('node_modules/iles') && localFile.includes('Island.vue')) {
125
+ // Retain localFiles of iles island to apply to child elements
126
+ isIsland = true
127
+ }
128
+
129
+ if (localFile.includes('node_modules')) {
130
+ // Ignore SFCs from node_modules
131
+ localFile = null
132
+ }
133
+ }
134
+
135
+ const localFileFromInstance = instance.type.__file
136
+ if (
137
+ localFileFromInstance &&
138
+ localFileFromInstance.includes('node_modules/iles') &&
139
+ localFileFromInstance.includes('Island.vue')
140
+ ) {
141
+ // Retain localFiles of iles island to apply to child elements
142
+ isIsland = true
143
+
144
+ localFile = instance.props.component?.__file || instance.props.importFrom
145
+ }
146
+
147
+ // // Computed props anyway filters out unmounted ones, and we use the local components unmounted hooks to cleanup unmounted nodes. Using the unmounted hook seems to be out of sync when el is reused but instance is remounted.
148
+ // if (hook === 'unmounted') {
149
+ // if (pinegrow.elCache.has(el)) {
150
+ // pinegrow.elCache.delete(el)
151
+ // }
152
+ // for (let [key, value] of pinegrow.elCache.entries()) {
153
+ // if (value.rootEl === rootEl) {
154
+ // pinegrow.elCache.delete(key)
155
+ // }
156
+ // }
157
+ // return
158
+ // }
159
+
160
+ // Text/comment node
161
+ if (isFragment) {
162
+ if (!rootEl) {
163
+ // root Fragment
164
+ isRootFragment = true
165
+ }
166
+
167
+ function isFragmentEl(instance) {
168
+ // return appRecord.options.types.Fragment === instance.subTree?.type
169
+ return instance.subTree.el.nodeType !== 1
170
+ }
171
+
172
+ function getRootVNodesFromComponentInstance(instance) {
173
+ if (isFragmentEl(instance)) {
174
+ return getFragmentRootVNodes(instance.subTree)
175
+ }
176
+ if (!instance.subTree) return []
177
+ return [instance.subTree]
178
+ }
179
+
180
+ function getFragmentRootVNodes(vnode) {
181
+ if (!vnode.children) return []
182
+ // children is v-if when the vnode has a condition
183
+ if (!Array.isArray(vnode.children)) return []
184
+
185
+ const list = []
186
+
187
+ for (let i = 0, l = vnode.children.length; i < l; i++) {
188
+ const childVnode = vnode.children[i]
189
+ if (childVnode.component) {
190
+ list.push(...getRootVNodesFromComponentInstance(childVnode.component))
191
+ } else if (childVnode) {
192
+ list.push(childVnode)
193
+ }
194
+ }
195
+
196
+ return list
197
+ }
198
+
199
+ childVNodes = getRootVNodesFromComponentInstance(instance)
200
+
201
+ if (!childVNodes.length) {
202
+ // For NuxtLayout, no childVNodes are returned, the subTree.children is in fact an object {ctx: {}, default: f}
203
+ if (el.nextElementSibling) {
204
+ const childEl = el.nextElementSibling
205
+ firstEl = lastEl = childEl
206
+ const childInstance = childEl.$ || childEl.__vueParentComponent
207
+ const childVNode = childInstance?.vnode // or subTree?
208
+ if (childVNode) {
209
+ childVNodes.push(childVNode)
210
+ }
211
+ }
212
+ }
213
+
214
+ if (childVNodes.length) {
215
+ // Filter out recursive vnodes text1 -> div, text1 (happens when text1 is the closest to a fragment with div & slot)
216
+ childVNodes = childVNodes.filter(childVNode => childVNode.el !== el)
217
+
218
+ if (childVNodes.length) {
219
+ if (childVNodes.length === 1) {
220
+ firstEl = lastEl = childVNodes[0].el
221
+ } else {
222
+ firstEl = childVNodes[0].el
223
+ lastEl = childVNodes[childVNodes.length - 1].el
224
+ }
225
+
226
+ if (firstEl && firstEl.nodeType !== 1) {
227
+ firstEl = firstEl.nextElementSibling
228
+ }
229
+ if (lastEl && lastEl.nodeType !== 1) {
230
+ lastEl = lastEl.nextElementSibling
231
+ }
232
+ }
233
+ }
234
+ }
235
+
236
+ if (isIsland && !isRootFragment) {
237
+ return
238
+ }
239
+
240
+ let elCacheObj = {
241
+ el,
242
+ isRootFragment,
243
+ rootEl,
244
+ vnode,
245
+ instance,
246
+ isFragment,
247
+ firstEl,
248
+ lastEl,
249
+ pgId,
250
+ key,
251
+ localFile,
252
+ isIsland,
253
+ }
254
+
255
+ let prevElCacheNodes = pinegrow.elCache.get(el)
256
+
257
+ if (prevElCacheNodes) {
258
+ let index = prevElCacheNodes.findIndex(elCacheObj => elCacheObj.instance.uid === instance.uid)
259
+
260
+ if (index > -1) {
261
+ const prevElCacheObj = prevElCacheNodes[index]
262
+ elCacheObj.localFile = elCacheObj.localFile || prevElCacheObj.localFile
263
+ elCacheObj.pgId = elCacheObj.pgId || prevElCacheObj.pgId
264
+ prevElCacheNodes[index] = elCacheObj
265
+ } else {
266
+ prevElCacheNodes.push(elCacheObj)
267
+ }
268
+ pinegrow.elCache.set(el, prevElCacheNodes)
269
+ } else {
270
+ pinegrow.elCache.set(el, [elCacheObj])
271
+ childVNodes.forEach(childVNode => {
272
+ pgUpdateElCache(hook, pgId, isRootFragment ? el : rootEl, key)(childVNode)
273
+ })
274
+ }
275
+
276
+ if (pinegrow.elUpdateHanderFn) {
277
+ pinegrow.elUpdateHanderFn(el)
278
+ }
279
+ } catch (err) {
280
+ console.log(err)
281
+ if (pinegrow.elCacheErrHandlerFn) {
282
+ pinegrow.elCacheErrHandlerFn(vnode, hook, rootEl, pgId, key, el, instance, err.message)
283
+ }
284
+ }
285
+ }
286
+
287
+ const elUpdateHanderFn = el => {
288
+ // if (pinegrow.elCache.has(el)) {
289
+ // const { pgId } = pinegrow.elCache.get(el)
290
+ // if (pgId) {
291
+ // // console.log(`Reselect ${pgId}`)
292
+ // }
293
+ // }
294
+ }
295
+
296
+ const elCacheErrHandlerFn = () => {
297
+ if (message) {
298
+ // console.log(message)
299
+ }
300
+ }
301
+
302
+ const cleanupCache = () => {
303
+ // for (let [key, value] of pinegrow.elCache.entries()) {
304
+ // if (value.isFragment && !value.firstEl) {
305
+ // console.log(value)
306
+ // }
307
+ // }
308
+ for (let [el, elCacheNodes] of pinegrow.elCache.entries()) {
309
+ // if (!el.isConnected) {
310
+ // pinegrow.elCache.delete(el)
311
+ // continue
312
+ // }
313
+ const cleanedUpElCacheNodes = elCacheNodes.filter(
314
+ elCacheObj => !elCacheObj.instance.isUnmounted // || elCacheObj.localFile
315
+ )
316
+ if (cleanedUpElCacheNodes.length) {
317
+ if (cleanedUpElCacheNodes.length !== elCacheNodes.length) {
318
+ pinegrow.elCache.set(el, cleanedUpElCacheNodes)
319
+ }
320
+ } else {
321
+ pinegrow.elCache.delete(el)
322
+ }
323
+ }
324
+ }
325
+
326
+ // Local Component
327
+ const rootVNode = ref(null)
328
+
329
+ const mountLocalComponent = () => {
330
+ const instance = getCurrentInstance()
331
+ const vnode = instance?.vnode
332
+ const el = vnode?.el
333
+ const localFile = instance.type.__file && !instance.type.__file.includes('node_modules') && instance.type.__file
334
+ if (instance && vnode && el) {
335
+ rootVNode.value = vnode
336
+ pgUpdateElCache('mounted', null, null, null, localFile)(vnode)
337
+ }
338
+ }
339
+
340
+ const unmountLocalComponent = () => {
341
+ if (rootVNode.value) {
342
+ pgUpdateElCache('unmounted')(rootVNode.value)
343
+ }
344
+ cleanupCache()
345
+ }
346
+
347
+ onBeforeMount(() => initCache())
348
+ onMounted(() => {
349
+ mountLocalComponent()
350
+ })
351
+ onBeforeUnmount(() => unmountLocalComponent())
352
+
353
+ return { pgUpdateElCache }
354
+ }
355
+
package/package.json CHANGED
@@ -1,13 +1,15 @@
1
1
  {
2
2
  "name": "@pinegrow/vite-plugin",
3
- "version": "3.0.0-beta.9",
3
+ "version": "3.0.0-beta.90",
4
4
  "description": "Pinegrow Vite Plugin",
5
5
  "type": "module",
6
6
  "files": [
7
- "dist"
7
+ "dist",
8
+ "./types.d.ts"
8
9
  ],
9
10
  "main": "./dist/index.cjs",
10
11
  "module": "./dist/index.cjs",
12
+ "types": "./types.d.ts",
11
13
  "exports": {
12
14
  ".": {
13
15
  "import": "./dist/index.cjs",
@@ -16,6 +18,12 @@
16
18
  "./dev": {
17
19
  "import": "./src/index.js",
18
20
  "require": "./src/index.js"
21
+ },
22
+ "./vue": {
23
+ "import": "./dist/vue/index.js"
24
+ },
25
+ "./pgia": {
26
+ "import": "./dist/pgia/index.js"
19
27
  }
20
28
  },
21
29
  "keywords": [
@@ -33,6 +41,8 @@
33
41
  "increment-beta-version": "npm version prerelease --preid=beta"
34
42
  },
35
43
  "dependencies": {
44
+ "@vue/compiler-sfc": "^3.2.45",
45
+ "magic-string": "^0.27.0",
36
46
  "node-html-parser": "^5.2.0"
37
47
  }
38
48
  }
package/types.d.ts ADDED
@@ -0,0 +1,13 @@
1
+ declare module '@pinegrow/vite-plugin'
2
+
3
+ // export interface liveDesigner {
4
+ // repoRoot?: string;
5
+ // configPath?: string;
6
+ // plugins?: string[];
7
+ // devtoolsKey?: string;
8
+ // vscodeTunnelUrl?: string
9
+ // customTypes?: {
10
+ // default?: {};
11
+ // };
12
+ // };
13
+