@pinegrow/vite-plugin 3.0.0-beta.1 → 3.0.0-beta.100

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