@pinegrow/vite-plugin 3.0.0-beta.14 → 3.0.0-beta.141

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,412 @@
1
+ import {
2
+ onBeforeMount,
3
+ onMounted,
4
+ onBeforeUnmount,
5
+ getCurrentInstance,
6
+ ref,
7
+ reactive,
8
+ onUpdated,
9
+ computed,
10
+ watch,
11
+ } from 'vue'
12
+
13
+ export function usePinegrow() {
14
+ const initCache = () => {
15
+ // conditional
16
+ const winObj = window
17
+
18
+ if (!(winObj?.process?.client && winObj.process.client !== true)) {
19
+ if (!winObj.pinegrow) {
20
+ // console.log('Cache initialized by Vue Plugin!')
21
+ const elCache = reactive(new Map())
22
+
23
+ const enrichWithComponentName = (elCacheObj, localFile) => {
24
+ return {
25
+ name: elCacheObj.instance.type.__name || '',
26
+ localFile,
27
+ ...elCacheObj,
28
+ }
29
+ }
30
+
31
+ const pgIdToElComputed = computed(() => {
32
+ const pgIds = {}
33
+ for (let [el, elCacheNodes] of elCache.entries()) {
34
+ if (el.isConnected) {
35
+ elCacheNodes.forEach(elCacheObj => {
36
+ if (
37
+ // elCacheObj.vnode.el.isConnected &&
38
+ elCacheObj.instance.isMounted &&
39
+ !elCacheObj.instance.isUnmounted &&
40
+ elCacheObj.pgId &&
41
+ (!elCacheObj.rootEl ||
42
+ elCacheObj.isRootFragment ||
43
+ (elCacheObj.rootEl && elCacheObj.rootEl === elCacheObj.el.parentNode))
44
+ ) {
45
+ if (!pgIds[elCacheObj.pgId]) {
46
+ pgIds[elCacheObj.pgId] = []
47
+ }
48
+ if (elCacheObj.key === undefined || elCacheObj.key === null) {
49
+ pgIds[elCacheObj.pgId].push(elCacheObj)
50
+ } else {
51
+ pgIds[elCacheObj.pgId].push([elCacheObj.key, elCacheObj])
52
+ }
53
+ }
54
+ })
55
+ }
56
+ }
57
+ return pgIds
58
+ })
59
+
60
+ const localComponentToElComputed = computed(() => {
61
+ const localComponents = {}
62
+ for (let [el, elCacheNodes] of elCache.entries()) {
63
+ if (el.isConnected) {
64
+ elCacheNodes.forEach(elCacheObj => {
65
+ if (
66
+ // elCacheObj.vnode.el.isConnected &&
67
+ elCacheObj.instance.isMounted &&
68
+ !elCacheObj.instance.isUnmounted &&
69
+ elCacheObj.localFile
70
+ // && !elCacheObj.isIsland
71
+ // && (!elCacheObj.rootEl ||
72
+ // elCacheObj.isRootFragment ||
73
+ // (elCacheObj.rootEl && elCacheObj.rootEl === elCacheObj.el.parentNode))
74
+ ) {
75
+ if (!localComponents[elCacheObj.localFile]) {
76
+ localComponents[elCacheObj.localFile] = []
77
+ }
78
+ // 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
79
+ const possibleIslandRootIndex = localComponents[elCacheObj.localFile].findIndex(
80
+ prevElCacheObj =>
81
+ prevElCacheObj.isIsland &&
82
+ prevElCacheObj.firstEl?.firstElementChild === elCacheObj.el
83
+ )
84
+
85
+ if (possibleIslandRootIndex > -1) {
86
+ const pgId = localComponents[elCacheObj.localFile][possibleIslandRootIndex].pgId
87
+ localComponents[elCacheObj.localFile][possibleIslandRootIndex] =
88
+ enrichWithComponentName({ ...elCacheObj, pgId }, elCacheObj.localFile)
89
+ } else {
90
+ localComponents[elCacheObj.localFile].push(
91
+ enrichWithComponentName(elCacheObj, elCacheObj.localFile)
92
+ )
93
+ }
94
+ }
95
+ })
96
+ }
97
+ }
98
+ return localComponents
99
+ })
100
+
101
+ const appTree = computed(() => {
102
+ const appTreeNodes = Object.values(localComponentToElComputed.value).reduce((acc, elCacheNodes) => {
103
+ const filteredElCacheNodes = []
104
+ elCacheNodes.forEach(elCacheObj => {
105
+ // If Island and already hydrated, then remove it off
106
+ if (elCacheObj.isIsland) {
107
+ const childEl = elCacheObj.firstEl.firstElementChild
108
+ if (childEl) {
109
+ if (elCache.has(childEl)) {
110
+ // Hydrated, don't include
111
+ return
112
+ }
113
+ // const childElCacheObj = elCache.get(childEl)
114
+ // if (childElCacheObj) {
115
+ // childElCacheObj.pgId = elCacheObj.pgId
116
+ // // Hydrated, don't include
117
+ // return
118
+ // }
119
+ }
120
+ }
121
+ filteredElCacheNodes.push(elCacheObj)
122
+ })
123
+ return [...acc, ...filteredElCacheNodes]
124
+ }, [])
125
+ const appTreeNodesSorted = appTreeNodes.sort((a, b) =>
126
+ a.el.compareDocumentPosition(b.el) & Node.DOCUMENT_POSITION_FOLLOWING ? -1 : 1
127
+ )
128
+ return appTreeNodesSorted
129
+ })
130
+
131
+ winObj.pinegrow = {
132
+ elCache,
133
+ pgIdToElComputed,
134
+ localComponentToElComputed,
135
+ appTree,
136
+ refFromContext: ref,
137
+ computedFromContext: computed,
138
+ watchFromContext: watch,
139
+ // // Uncomment these two to test locally
140
+ // elCacheErrHandlerFn,
141
+ // elUpdateHanderFn,
142
+ }
143
+ }
144
+ }
145
+ }
146
+
147
+ // pgId & key can be optional
148
+ const pgUpdateElCache = (hook, pgId, rootEl, key, localFile) => async vnode => {
149
+ if (!vnode) return
150
+ if (window?.process?.client && process?.client !== true) return
151
+ if (!window.pinegrow) initCache()
152
+
153
+ let el = vnode.el
154
+
155
+ const instance = vnode.component || vnode.ctx || el.__vueParentComponent
156
+
157
+ if (!el || !instance) return
158
+
159
+ try {
160
+ if ((key !== null && key !== undefined) || (vnode.key !== null && vnode.key !== undefined)) {
161
+ key = vnode.key || key
162
+ }
163
+
164
+ localFile = localFile || vnode.type.__file
165
+
166
+ let isRootFragment = false
167
+ let isFragment = el.nodeType !== 1
168
+ let firstEl,
169
+ lastEl,
170
+ isIsland = false,
171
+ childVNodes = []
172
+
173
+ // May be an iles Island that wraps components with client directives
174
+ if (localFile) {
175
+ if (localFile.includes('node_modules/iles') && localFile.includes('Island.vue')) {
176
+ // Retain localFiles of iles island to apply to child elements
177
+ isIsland = true
178
+ }
179
+
180
+ if (localFile.includes('node_modules')) {
181
+ // Ignore SFCs from node_modules
182
+ localFile = null
183
+ }
184
+ }
185
+
186
+ const localFileFromInstance = instance.type.__file
187
+ if (
188
+ localFileFromInstance &&
189
+ localFileFromInstance.includes('node_modules/iles') &&
190
+ localFileFromInstance.includes('Island.vue')
191
+ ) {
192
+ // Retain localFiles of iles island to apply to child elements
193
+ isIsland = true
194
+
195
+ localFile = instance.props.component?.__file || instance.props.importFrom
196
+ }
197
+
198
+ // // 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.
199
+ // if (hook === 'unmounted') {
200
+ // if (pinegrow.elCache.has(el)) {
201
+ // pinegrow.elCache.delete(el)
202
+ // }
203
+ // for (let [key, value] of pinegrow.elCache.entries()) {
204
+ // if (value.rootEl === rootEl) {
205
+ // pinegrow.elCache.delete(key)
206
+ // }
207
+ // }
208
+ // return
209
+ // }
210
+
211
+ // Text/comment node
212
+ if (isFragment) {
213
+ if (!rootEl) {
214
+ // root Fragment
215
+ isRootFragment = true
216
+ }
217
+
218
+ function isFragmentEl(instance) {
219
+ // return appRecord.options.types.Fragment === instance.subTree?.type
220
+ return instance.subTree.el.nodeType !== 1
221
+ }
222
+
223
+ function getRootVNodesFromComponentInstance(instance) {
224
+ if (isFragmentEl(instance)) {
225
+ return getFragmentRootVNodes(instance.subTree)
226
+ }
227
+ if (!instance.subTree) return []
228
+ return [instance.subTree]
229
+ }
230
+
231
+ function getFragmentRootVNodes(vnode) {
232
+ if (!vnode.children) return []
233
+ // children is v-if when the vnode has a condition
234
+ if (!Array.isArray(vnode.children)) return []
235
+
236
+ const list = []
237
+
238
+ for (let i = 0, l = vnode.children.length; i < l; i++) {
239
+ const childVnode = vnode.children[i]
240
+ if (childVnode.component) {
241
+ list.push(...getRootVNodesFromComponentInstance(childVnode.component))
242
+ } else if (childVnode) {
243
+ list.push(childVnode)
244
+ }
245
+ }
246
+
247
+ return list
248
+ }
249
+
250
+ childVNodes = getRootVNodesFromComponentInstance(instance)
251
+
252
+ if (!childVNodes.length) {
253
+ // For NuxtLayout, no childVNodes are returned, the subTree.children is in fact an object {ctx: {}, default: f}
254
+ if (el.nextElementSibling) {
255
+ const childEl = el.nextElementSibling
256
+ firstEl = lastEl = childEl
257
+ const childInstance = childEl.$ || childEl.__vueParentComponent
258
+ const childVNode = childInstance?.vnode // or subTree?
259
+ if (childVNode) {
260
+ childVNodes.push(childVNode)
261
+ }
262
+ }
263
+ }
264
+
265
+ if (childVNodes.length) {
266
+ // Filter out recursive vnodes text1 -> div, text1 (happens when text1 is the closest to a fragment with div & slot)
267
+ childVNodes = childVNodes.filter(childVNode => childVNode.el !== el)
268
+
269
+ if (childVNodes.length) {
270
+ if (childVNodes.length === 1) {
271
+ firstEl = lastEl = childVNodes[0].el
272
+ } else {
273
+ firstEl = childVNodes[0].el
274
+ lastEl = childVNodes[childVNodes.length - 1].el
275
+ }
276
+
277
+ if (firstEl && firstEl.nodeType !== 1) {
278
+ firstEl = firstEl.nextElementSibling
279
+ }
280
+ if (lastEl && lastEl.nodeType !== 1) {
281
+ lastEl = lastEl.nextElementSibling
282
+ }
283
+ }
284
+ }
285
+ }
286
+
287
+ if (isIsland && !isRootFragment) {
288
+ return
289
+ }
290
+
291
+ let elCacheObj = {
292
+ el,
293
+ isRootFragment,
294
+ rootEl,
295
+ vnode,
296
+ instance,
297
+ isFragment,
298
+ firstEl,
299
+ lastEl,
300
+ pgId,
301
+ key,
302
+ localFile,
303
+ isIsland,
304
+ }
305
+
306
+ let prevElCacheNodes = pinegrow.elCache.get(el)
307
+
308
+ if (prevElCacheNodes) {
309
+ let index = prevElCacheNodes.findIndex(elCacheObj => elCacheObj.instance.uid === instance.uid)
310
+
311
+ if (index > -1) {
312
+ const prevElCacheObj = prevElCacheNodes[index]
313
+ elCacheObj.localFile = elCacheObj.localFile || prevElCacheObj.localFile
314
+ elCacheObj.pgId = elCacheObj.pgId || prevElCacheObj.pgId
315
+ prevElCacheNodes[index] = elCacheObj
316
+ } else {
317
+ prevElCacheNodes.push(elCacheObj)
318
+ }
319
+ pinegrow.elCache.set(el, prevElCacheNodes)
320
+ } else {
321
+ pinegrow.elCache.set(el, [elCacheObj])
322
+ childVNodes.forEach(childVNode => {
323
+ pgUpdateElCache(hook, pgId, isRootFragment ? el : rootEl, key)(childVNode)
324
+ })
325
+ }
326
+
327
+ if (pinegrow.elUpdateHanderFn) {
328
+ pinegrow.elUpdateHanderFn(el)
329
+ }
330
+ } catch (err) {
331
+ console.log(err)
332
+ if (pinegrow.elCacheErrHandlerFn) {
333
+ pinegrow.elCacheErrHandlerFn(vnode, hook, rootEl, pgId, key, el, instance, err.message)
334
+ }
335
+ }
336
+ }
337
+
338
+ const elUpdateHanderFn = el => {
339
+ // if (pinegrow.elCache.has(el)) {
340
+ // const { pgId } = pinegrow.elCache.get(el)
341
+ // if (pgId) {
342
+ // // console.log(`Reselect ${pgId}`)
343
+ // }
344
+ // }
345
+ }
346
+
347
+ const elCacheErrHandlerFn = () => {
348
+ if (message) {
349
+ // console.log(message)
350
+ }
351
+ }
352
+
353
+ const cleanupCache = () => {
354
+ // for (let [key, value] of pinegrow.elCache.entries()) {
355
+ // if (value.isFragment && !value.firstEl) {
356
+ // console.log(value)
357
+ // }
358
+ // }
359
+ for (let [el, elCacheNodes] of pinegrow.elCache.entries()) {
360
+ // if (!el.isConnected) {
361
+ // pinegrow.elCache.delete(el)
362
+ // continue
363
+ // }
364
+ const cleanedUpElCacheNodes = elCacheNodes.filter(
365
+ elCacheObj => !elCacheObj.instance.isUnmounted // || elCacheObj.localFile
366
+ )
367
+ if (cleanedUpElCacheNodes.length) {
368
+ if (cleanedUpElCacheNodes.length !== elCacheNodes.length) {
369
+ pinegrow.elCache.set(el, cleanedUpElCacheNodes)
370
+ }
371
+ } else {
372
+ pinegrow.elCache.delete(el)
373
+ }
374
+ if (pinegrow.elUpdateHanderFn) {
375
+ pinegrow.elUpdateHanderFn(el)
376
+ }
377
+ }
378
+ }
379
+
380
+ // Local Component
381
+ const rootVNode = ref(null)
382
+
383
+ const mountLocalComponent = () => {
384
+ const instance = getCurrentInstance()
385
+ const vnode = instance?.vnode
386
+ const el = vnode?.el
387
+ const localFile = instance.type.__file && !instance.type.__file.includes('node_modules') && instance.type.__file
388
+ if (instance && vnode && el) {
389
+ rootVNode.value = vnode
390
+ pgUpdateElCache('mounted', null, null, null, localFile)(vnode)
391
+ }
392
+ }
393
+
394
+ const unmountLocalComponent = () => {
395
+ if (rootVNode.value) {
396
+ pgUpdateElCache('unmounted')(rootVNode.value)
397
+ }
398
+ cleanupCache()
399
+ }
400
+
401
+ onBeforeMount(() => initCache())
402
+ onMounted(() => {
403
+ mountLocalComponent()
404
+ })
405
+ onBeforeUnmount(() => unmountLocalComponent())
406
+ onUpdated(() => {
407
+ mountLocalComponent()
408
+ })
409
+
410
+ return { pgUpdateElCache }
411
+ }
412
+
package/package.json CHANGED
@@ -1,13 +1,15 @@
1
1
  {
2
2
  "name": "@pinegrow/vite-plugin",
3
- "version": "3.0.0-beta.14",
3
+ "version": "3.0.0-beta.141",
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,12 +18,18 @@
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": [
22
30
  "pinegrow",
23
- "pinegrow-plugin",
24
- "vite plugin",
31
+ "vue-designer",
32
+ "vite-plugin",
25
33
  "@pinegrow/vite-plugin"
26
34
  ],
27
35
  "author": "Pinegrow (http://pinegrow.com/)",
@@ -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,100 @@
1
+ import type { PinegrowTailwindCSSModuleOptions } from '@pinegrow/tailwindcss-plugin'
2
+ import type { PluginOption as VitePluginOption } from 'vite'
3
+
4
+ interface PinegrowExternalPlugin {
5
+ /**
6
+ * Display name in Props Panel and Library Panels
7
+ * Eg, "My Awesome Lib 3.3.1", defaults to key if not provided
8
+ */
9
+ name?: string
10
+ /**
11
+ * Unique key that doesn't conflict with other pinegrow frameworks
12
+ * Run `Object.values(pinegrow.getFrameworks()).map(fx => fx.key)` in Vue Designer's devtools console for existing keys
13
+ *
14
+ * Allowed characters - alphanumeric with hyphens or period, no spaces
15
+ * Eg, 'my-awesome-lib'
16
+ */
17
+ key: string
18
+ /**
19
+ * Web-types of component library (or) pinegrow commonjs plugin
20
+ * Must return an absolute path
21
+ *
22
+ * Use path functions according to the project module type (commonjs/module)
23
+ * For eg, path.resolve(__dirname, '../relative-path/my-lib.cjs')
24
+ * (or)
25
+ * import { fileURLToPath, URL } from 'node:url'
26
+ * fileURLToPath(new URL('./relative-path/web-types.json', import.meta.url)) // web-types of component library
27
+ * fileURLToPath(new URL('./relative-path/my-lib.cjs', import.meta.url)) // pinegrow commonjs plugin
28
+ */
29
+ pluginPath: string
30
+ }
31
+
32
+ export interface LiveDesignerOptions {
33
+ /**
34
+ * Absolute path to the root of a monorepo.
35
+ * Relative path from the project root to the root of a monorepo.
36
+ */
37
+ repoRoot?: string
38
+
39
+ /**
40
+ * Absolute path to a custom config file (for eg, vite-dev.config.js).
41
+ * Relative path from the project root to a custom config file (for eg, vite-dev.config.js).
42
+ */
43
+ configPath?: string
44
+
45
+ /**
46
+ * Key of the devtools exposed on the window object.
47
+ * For instructions, check Config Panel in Vue Designer
48
+ */
49
+ devtoolsKey?: string
50
+
51
+ /**
52
+ * Preferred format for icons in icon-picker - https://github.com/antfu/icones/blob/main/src/utils/case.ts
53
+ * To see the different formats, go to https://icones.js.org/collection/mdi, turn off name copying mode if active, select an icon. In the bottom drawer, next to the icon-name & copy icon, there is dropup icon. Click the drop-up to see the different formats.
54
+ * * @default 'unocss' // For eg, i-material-symbols-home
55
+ *
56
+ */
57
+ iconPreferredCase?:
58
+ | 'unocss'
59
+ | 'bare'
60
+ | 'barePascal'
61
+ | 'iconify'
62
+ | 'dash'
63
+ | 'camel'
64
+ | 'pascal'
65
+ | 'component'
66
+ | 'componentKebab'
67
+
68
+ /**
69
+ * Router history modes - 'html5', 'hash'
70
+ * @default 'html5'
71
+ */
72
+
73
+ routerHistoryMode?: string //
74
+
75
+ /**
76
+ * Array of Pinegrow plugins (npm package names)
77
+ * To use file-based Pinegrow Plugins, provide the absolute path of the plugin file, for eg, [path.resolve(__dirname, './pg-plugin/index.js')] as pluginPath, along with a unique key
78
+ */
79
+ plugins?: PinegrowExternalPlugin[] | string | string[]
80
+
81
+ /**
82
+ * Pinegrow TailwindCSS plugin options
83
+ */
84
+ tailwindcss?: PinegrowTailwindCSSModuleOptions
85
+ }
86
+
87
+ interface PinegrowVitePlugin<VitePluginOption> {
88
+ /**
89
+ * Pinegrow Live Designer options
90
+ * For details, check https://pinegrow.com/vue-designer
91
+ */
92
+ liveDesigner?: {
93
+ [key in string]?: any
94
+ } & LiveDesignerOptions
95
+ }
96
+
97
+ declare function export_default(liveDesigner: LiveDesignerOptions): PinegrowVitePlugin
98
+
99
+ export { PinegrowVitePlugin, LiveDesignerOptions, export_default as default, export_default as liveDesigner }
100
+