@md-plugins/quasar-app-extension-q-press 0.1.0-beta.10 → 0.1.0-beta.12

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.
Files changed (62) hide show
  1. package/README.md +4 -2
  2. package/dist/templates/init/src/_q-press/components/MarkdownApi.vue +128 -83
  3. package/dist/templates/init/src/_q-press/components/MarkdownCodepen.vue +115 -20
  4. package/dist/templates/init/src/_q-press/components/MarkdownExample.vue +1 -1
  5. package/dist/templates/init/src/_q-press/components/MarkdownLink.vue +1 -1
  6. package/dist/templates/init/src/_q-press/components/MarkdownTree.vue +1 -1
  7. package/dist/templates/init/src/components/LandingPage/LandingPage.vue +1 -1
  8. package/dist/templates/init/src/markdown/guides/faq.md +1 -1
  9. package/dist/templates/init/src/markdown/guides/upgrade-guide.md +3 -3
  10. package/dist/templates/init/src/markdown/md-plugins/blockquote/overview.md +3 -1
  11. package/dist/templates/init/src/markdown/md-plugins/codeblocks/overview.md +7 -1
  12. package/dist/templates/init/src/markdown/md-plugins/containers/overview.md +3 -1
  13. package/dist/templates/init/src/markdown/md-plugins/frontmatter/overview.md +3 -1
  14. package/dist/templates/init/src/markdown/md-plugins/headers/overview.md +3 -1
  15. package/dist/templates/init/src/markdown/md-plugins/image/overview.md +3 -1
  16. package/dist/templates/init/src/markdown/md-plugins/imports/overview.md +3 -1
  17. package/dist/templates/init/src/markdown/md-plugins/inline-code/overview.md +3 -1
  18. package/dist/templates/init/src/markdown/md-plugins/link/overview.md +3 -1
  19. package/dist/templates/init/src/markdown/md-plugins/table/overview.md +3 -1
  20. package/dist/templates/init/src/markdown/md-plugins/title/overview.md +3 -1
  21. package/dist/templates/init/src/markdown/quasar-app-extensions/qpress/advanced.md +3 -1
  22. package/dist/templates/init/src/markdown/quasar-app-extensions/qpress/overview.md +5 -1
  23. package/dist/templates/init/src/markdown/quasar-app-extensions/vite-md-plugin-app-ext/overview.md +1 -1
  24. package/dist/templates/init/src/markdown/vite-plugins/vite-examples-plugin/overview.md +3 -1
  25. package/dist/templates/init/src/markdown/vite-plugins/vite-md-plugin/overview.md +3 -1
  26. package/dist/templates/init/src/siteConfig/index.ts +6 -1
  27. package/dist/templates/update/src/_q-press/components/MarkdownApi.vue +128 -83
  28. package/dist/templates/update/src/_q-press/components/MarkdownCodepen.vue +115 -20
  29. package/dist/templates/update/src/_q-press/components/MarkdownExample.vue +1 -1
  30. package/dist/templates/update/src/_q-press/components/MarkdownLink.vue +1 -1
  31. package/dist/templates/update/src/_q-press/components/MarkdownTree.vue +1 -1
  32. package/package.json +16 -16
  33. package/src/templates/init/src/_q-press/components/MarkdownApi.vue +128 -83
  34. package/src/templates/init/src/_q-press/components/MarkdownCodepen.vue +115 -20
  35. package/src/templates/init/src/_q-press/components/MarkdownExample.vue +1 -1
  36. package/src/templates/init/src/_q-press/components/MarkdownLink.vue +1 -1
  37. package/src/templates/init/src/_q-press/components/MarkdownTree.vue +1 -1
  38. package/src/templates/init/src/components/LandingPage/LandingPage.vue +1 -1
  39. package/src/templates/init/src/markdown/guides/faq.md +1 -1
  40. package/src/templates/init/src/markdown/guides/upgrade-guide.md +3 -3
  41. package/src/templates/init/src/markdown/md-plugins/blockquote/overview.md +3 -1
  42. package/src/templates/init/src/markdown/md-plugins/codeblocks/overview.md +7 -1
  43. package/src/templates/init/src/markdown/md-plugins/containers/overview.md +3 -1
  44. package/src/templates/init/src/markdown/md-plugins/frontmatter/overview.md +3 -1
  45. package/src/templates/init/src/markdown/md-plugins/headers/overview.md +3 -1
  46. package/src/templates/init/src/markdown/md-plugins/image/overview.md +3 -1
  47. package/src/templates/init/src/markdown/md-plugins/imports/overview.md +3 -1
  48. package/src/templates/init/src/markdown/md-plugins/inline-code/overview.md +3 -1
  49. package/src/templates/init/src/markdown/md-plugins/link/overview.md +3 -1
  50. package/src/templates/init/src/markdown/md-plugins/table/overview.md +3 -1
  51. package/src/templates/init/src/markdown/md-plugins/title/overview.md +3 -1
  52. package/src/templates/init/src/markdown/quasar-app-extensions/qpress/advanced.md +3 -1
  53. package/src/templates/init/src/markdown/quasar-app-extensions/qpress/overview.md +5 -1
  54. package/src/templates/init/src/markdown/quasar-app-extensions/vite-md-plugin-app-ext/overview.md +1 -1
  55. package/src/templates/init/src/markdown/vite-plugins/vite-examples-plugin/overview.md +3 -1
  56. package/src/templates/init/src/markdown/vite-plugins/vite-md-plugin/overview.md +3 -1
  57. package/src/templates/init/src/siteConfig/index.ts +6 -1
  58. package/src/templates/update/src/_q-press/components/MarkdownApi.vue +128 -83
  59. package/src/templates/update/src/_q-press/components/MarkdownCodepen.vue +115 -20
  60. package/src/templates/update/src/_q-press/components/MarkdownExample.vue +1 -1
  61. package/src/templates/update/src/_q-press/components/MarkdownLink.vue +1 -1
  62. package/src/templates/update/src/_q-press/components/MarkdownTree.vue +1 -1
package/README.md CHANGED
@@ -4,9 +4,9 @@ The Ultimate Markdown Solution for the Quasar Framework.
4
4
 
5
5
  See the [documentation](https://md-plugins.netlify.app/quasar-app-extensions/qpress/overview) for more information.
6
6
 
7
- > Current beta release: `0.1.0-beta.10`.
7
+ > Current beta release: `0.1.0-beta.12`.
8
8
  >
9
- > Q-Press currently targets Quasar Vite projects using `@quasar/app-vite` `>=3.0.0-beta.29`. TypeScript processing is required.
9
+ > Q-Press currently targets Quasar Vite projects using `@quasar/app-vite` `>=3.0.0-beta.30`. TypeScript processing is required.
10
10
 
11
11
  ## Features
12
12
 
@@ -38,12 +38,14 @@ See the [documentation](https://md-plugins.netlify.app/quasar-app-extensions/qpr
38
38
  - `npm i -D markdown-it @types/markdown-it`
39
39
  - `yarn add -D markdown-it @types/markdown-it`
40
40
  - `pnpm i -D markdown-it @types/markdown-it`
41
+ - `bun add -d markdown-it @types/markdown-it`
41
42
 
42
43
  3. Add `prismjs` to your project dependencies
43
44
 
44
45
  - `npm i prismjs`
45
46
  - `yarn add prismjs`
46
47
  - `pnpm add prismjs`
48
+ - `bun add prismjs`
47
49
 
48
50
  ## Modifications
49
51
 
@@ -55,7 +55,10 @@
55
55
  <q-tab v-for="tab in tabsList" :key="`api-tab-${tab}`" :name="tab" class="header-btn">
56
56
  <div class="row no-wrap items-center">
57
57
  <span class="q-mr-xs text-capitalize">{{ tab }}</span>
58
- <q-badge v-if="filteredApiCount[tab].overall" :label="filteredApiCount[tab].overall" />
58
+ <q-badge
59
+ v-if="filteredApiCount[tab]?.overall"
60
+ :label="filteredApiCount[tab]?.overall"
61
+ />
59
62
  </div>
60
63
  </q-tab>
61
64
  </q-tabs>
@@ -64,7 +67,10 @@
64
67
 
65
68
  <q-tab-panels v-model="currentTab" animated>
66
69
  <q-tab-panel v-for="tab in tabsList" :key="tab" class="q-pa-none" :name="tab">
67
- <div v-if="innerTabsList[tab].length !== 1" class="markdown-api__container row no-wrap">
70
+ <div
71
+ v-if="innerTabsList[tab]?.length !== 1"
72
+ class="markdown-api__container row no-wrap"
73
+ >
68
74
  <div class="col-auto">
69
75
  <q-tabs
70
76
  v-model="currentInnerTab"
@@ -86,8 +92,8 @@
86
92
  <span class="q-mr-xs text-capitalize">{{ innerTab }}</span>
87
93
  <div class="col" />
88
94
  <q-badge
89
- v-if="filteredApiCount[tab].category[innerTab]"
90
- :label="filteredApiCount[tab].category[innerTab]"
95
+ v-if="filteredApiCount[tab]?.category[innerTab]"
96
+ :label="filteredApiCount[tab]?.category[innerTab]"
91
97
  />
92
98
  </div>
93
99
  </q-tab>
@@ -109,12 +115,12 @@
109
115
  class="q-pa-none"
110
116
  :name="innerTab"
111
117
  >
112
- <MarkdownApiEntry :type="tab" :definition="filteredApi[tab][innerTab]" />
118
+ <MarkdownApiEntry :type="tab" :definition="filteredApi[tab]?.[innerTab]" />
113
119
  </q-tab-panel>
114
120
  </q-tab-panels>
115
121
  </div>
116
122
  <div v-else class="markdown-api__container">
117
- <MarkdownApiEntry :type="tab" :definition="filteredApi[tab][defaultInnerTabName]" />
123
+ <MarkdownApiEntry :type="tab" :definition="filteredApi[tab]?.[defaultInnerTabName]" />
118
124
  </div>
119
125
  </q-tab-panel>
120
126
  </q-tab-panels>
@@ -131,20 +137,57 @@ import MarkdownApiEntry from './MarkdownApiEntry'
131
137
 
132
138
  const defaultInnerTabName = '__default'
133
139
 
140
+ type ApiEntry = Record<string, any> & {
141
+ category?: string
142
+ definition?: Record<string, ApiEntry>
143
+ desc?: string
144
+ propName?: string
145
+ }
146
+
147
+ type ApiDefinition = Record<string, ApiEntry>
148
+ type ParsedApi = Record<string, Record<string, any>>
149
+ type InnerTabsMap = Record<string, string[]>
150
+ type ApiCount = Record<string, { overall: number; category: Record<string, number> }>
151
+
152
+ type ApiFile = Record<string, any> & {
153
+ addedIn?: string
154
+ behavior?: unknown
155
+ internal?: unknown
156
+ meta?: {
157
+ docsUrl?: string
158
+ }
159
+ type?: string
160
+ }
161
+
162
+ type MarkdownApiProps = {
163
+ api?: ApiFile | null
164
+ file?: string
165
+ name?: string
166
+ pageLink?: boolean
167
+ }
168
+
169
+ type QPressEnv = {
170
+ QCLI_FS_QUASAR_FOLDER?: string
171
+ QUASAR_CLIENT?: boolean
172
+ QUASAR_DEV?: boolean
173
+ }
174
+
175
+ const qPressEnv = (import.meta as ImportMeta & { env: QPressEnv }).env
176
+
134
177
  /**
135
178
  * Extracts and categorizes properties based on their categories.
136
179
  *
137
180
  * @param {Object} props - The properties object where each key is a property name and each value is an object containing a `category` string.
138
181
  * @returns {Array<string>} - An array of unique category names sorted alphabetically. If there is only one unique category, returns an array with a default inner tab name.
139
182
  */
140
- function getPropsCategories(props) {
141
- const acc = new Set()
183
+ function getPropsCategories(props: ApiDefinition | undefined): string[] {
184
+ const acc = new Set<string>()
142
185
 
143
- for (const key in props) {
186
+ for (const key in props ?? {}) {
144
187
  if (props[key] !== void 0) {
145
188
  const value = props[key]
146
189
 
147
- value.category.split('|').forEach((groupKey) => {
190
+ ;(value.category ?? defaultInnerTabName).split('|').forEach((groupKey: string) => {
148
191
  acc.add(groupKey)
149
192
  })
150
193
  }
@@ -161,10 +204,10 @@ function getPropsCategories(props) {
161
204
  * @param {string} apiType - The type of the API.
162
205
  * @returns {Array} - The array of inner tabs.
163
206
  */
164
- function getInnerTabs(api, tabs, apiType) {
165
- const acc = {}
207
+ function getInnerTabs(api: ApiFile, tabs: string[], apiType?: string): InnerTabsMap {
208
+ const acc: InnerTabsMap = {}
166
209
 
167
- tabs.forEach((tab) => {
210
+ tabs.forEach((tab: string) => {
168
211
  acc[tab] =
169
212
  apiType === 'component' && tab === 'props'
170
213
  ? getPropsCategories(api[tab])
@@ -181,16 +224,17 @@ function getInnerTabs(api, tabs, apiType) {
181
224
  * @param {Array} tabs - The array to store the main tabs.
182
225
  * @param {Array} innerTabs - The array to store the inner tabs.
183
226
  */
184
- function parseApi(api, tabs, innerTabs) {
185
- const acc = {}
227
+ function parseApi(api: ApiFile, tabs: string[], innerTabs: InnerTabsMap): ParsedApi {
228
+ const acc: ParsedApi = {}
186
229
 
187
- tabs.forEach((tab) => {
230
+ tabs.forEach((tab: string) => {
188
231
  const apiValue = api[tab]
232
+ const tabInnerTabs = innerTabs[tab] ?? [defaultInnerTabName]
189
233
 
190
- if (innerTabs[tab].length > 1) {
191
- const inner = {}
234
+ if (tabInnerTabs.length > 1) {
235
+ const inner: Record<string, ApiDefinition> = {}
192
236
 
193
- innerTabs[tab].forEach((subTab) => {
237
+ tabInnerTabs.forEach((subTab: string) => {
194
238
  inner[subTab] = {}
195
239
  })
196
240
 
@@ -198,8 +242,10 @@ function parseApi(api, tabs, innerTabs) {
198
242
  if (apiValue[key] !== void 0) {
199
243
  const value = apiValue[key]
200
244
 
201
- value.category.split('|').forEach((groupKey) => {
202
- inner[groupKey][key] = value
245
+ ;(value.category ?? defaultInnerTabName).split('|').forEach((groupKey: string) => {
246
+ if (inner[groupKey] !== undefined) {
247
+ inner[groupKey][key] = value
248
+ }
203
249
  })
204
250
  }
205
251
  }
@@ -222,7 +268,7 @@ function parseApi(api, tabs, innerTabs) {
222
268
  * @param {string} desc - The description of the item to check.
223
269
  * @returns {boolean} - Returns true if the item passes the filter, otherwise false.
224
270
  */
225
- function passesFilter(filter, name, desc) {
271
+ function passesFilter(filter: string, name: string, desc?: string): boolean {
226
272
  return (
227
273
  name.toLowerCase().indexOf(filter) > -1 ||
228
274
  (desc !== void 0 && desc.toLowerCase().indexOf(filter) > -1)
@@ -238,23 +284,29 @@ function passesFilter(filter, name, desc) {
238
284
  * @param {Array} innerTabs - The list of inner tabs to consider while filtering.
239
285
  * @returns {Object} - The filtered API data.
240
286
  */
241
- function getFilteredApi(parsedApi, filter, tabs, innerTabs) {
287
+ function getFilteredApi(
288
+ parsedApi: ParsedApi,
289
+ filter: string,
290
+ tabs: string[],
291
+ innerTabs: InnerTabsMap,
292
+ ): ParsedApi {
242
293
  if (filter === '') {
243
294
  return parsedApi
244
295
  }
245
296
 
246
- const acc = {}
297
+ const acc: ParsedApi = {}
247
298
 
248
- tabs.forEach((tab) => {
299
+ tabs.forEach((tab: string) => {
249
300
  if (tab === 'injection') {
250
- const name = parsedApi[tab][defaultInnerTabName]
301
+ const name = parsedApi[tab]?.[defaultInnerTabName]
251
302
  acc[tab] = {}
252
- acc[tab][defaultInnerTabName] = passesFilter(filter, name, '') === true ? name : {}
303
+ acc[tab][defaultInnerTabName] =
304
+ typeof name === 'string' && passesFilter(filter, name, '') === true ? name : {}
253
305
  return
254
306
  }
255
307
 
256
308
  if (tab === 'quasarConfOptions') {
257
- const api = parsedApi[tab][defaultInnerTabName]
309
+ const api = (parsedApi[tab]?.[defaultInnerTabName] ?? {}) as ApiEntry
258
310
  acc[tab] = {}
259
311
  acc[tab][defaultInnerTabName] = {
260
312
  ...api,
@@ -262,8 +314,8 @@ function getFilteredApi(parsedApi, filter, tabs, innerTabs) {
262
314
  }
263
315
  const result = acc[tab][defaultInnerTabName]
264
316
 
265
- for (const name in api.definition || {}) {
266
- const entry = api.definition[name]
317
+ for (const name in api.definition ?? {}) {
318
+ const entry = api.definition[name]!
267
319
  if (passesFilter(filter, name, entry.desc) === true) {
268
320
  result.definition[name] = entry
269
321
  }
@@ -279,17 +331,17 @@ function getFilteredApi(parsedApi, filter, tabs, innerTabs) {
279
331
  return
280
332
  }
281
333
 
282
- const tabApi = parsedApi[tab]
283
- const tabCategories = innerTabs[tab]
334
+ const tabApi = parsedApi[tab] ?? {}
335
+ const tabCategories = innerTabs[tab] ?? [defaultInnerTabName]
284
336
 
285
337
  acc[tab] = {}
286
- tabCategories.forEach((categ) => {
287
- const subTabs = {}
288
- const categoryEntries = tabApi[categ]
338
+ tabCategories.forEach((categ: string) => {
339
+ const subTabs: ApiDefinition = {}
340
+ const categoryEntries = (tabApi[categ] ?? {}) as ApiDefinition
289
341
 
290
342
  for (const name in categoryEntries) {
291
343
  const entry = categoryEntries[name]
292
- if (passesFilter(filter, name, entry.desc) === true) {
344
+ if (entry !== undefined && passesFilter(filter, name, entry.desc) === true) {
293
345
  subTabs[name] = entry
294
346
  }
295
347
  }
@@ -309,22 +361,24 @@ function getFilteredApi(parsedApi, filter, tabs, innerTabs) {
309
361
  * @param {Array} innerTabs - The array of inner tab configurations.
310
362
  * @returns {number} - The count of API entries.
311
363
  */
312
- function getApiCount(parsedApi, tabs, innerTabs) {
313
- const acc = {}
364
+ function getApiCount(parsedApi: ParsedApi, tabs: string[], innerTabs: InnerTabsMap): ApiCount {
365
+ const acc: ApiCount = {}
314
366
 
315
- tabs.forEach((tab) => {
316
- const tabApi = parsedApi[tab]
317
- const tabCategories = innerTabs[tab]
367
+ tabs.forEach((tab: string) => {
368
+ const tabApi = parsedApi[tab] ?? {}
369
+ const tabCategories = innerTabs[tab] ?? [defaultInnerTabName]
370
+ const firstCategory = tabCategories[0] ?? defaultInnerTabName
318
371
 
319
372
  if (['value', 'arg', 'injection'].includes(tab)) {
320
373
  acc[tab] = {
321
- overall: Object.keys(tabApi[tabCategories[0]]).length === 0 ? 0 : 1,
374
+ overall: Object.keys(tabApi[firstCategory] ?? {}).length === 0 ? 0 : 1,
375
+ category: {},
322
376
  }
323
377
  return
324
378
  }
325
379
 
326
380
  if (tab === 'quasarConfOptions') {
327
- const api = tabApi[tabCategories[0]]
381
+ const api = (tabApi[firstCategory] ?? {}) as ApiEntry
328
382
  acc[tab] = {
329
383
  overall:
330
384
  Object.keys(api).length === 0
@@ -332,25 +386,24 @@ function getApiCount(parsedApi, tabs, innerTabs) {
332
386
  : api.definition === void 0
333
387
  ? 1
334
388
  : Object.keys(api.definition).length,
389
+ category: {},
335
390
  }
336
391
  return
337
392
  }
338
393
 
339
- acc[tab] = { overall: 0 }
394
+ acc[tab] = { overall: 0, category: {} }
340
395
 
341
396
  if (tabCategories.length === 1) {
342
- const categ = tabCategories[0]
343
- const count = Object.keys(tabApi[categ]).length
397
+ const categ = tabCategories[0] ?? defaultInnerTabName
398
+ const count = Object.keys(tabApi[categ] ?? {}).length
344
399
 
345
400
  acc[tab] = {
346
401
  overall: count,
347
402
  category: { [categ]: count },
348
403
  }
349
404
  } else {
350
- acc[tab].category = {}
351
-
352
- tabCategories.forEach((categ) => {
353
- const count = Object.keys(tabApi[categ]).length
405
+ tabCategories.forEach((categ: string) => {
406
+ const count = Object.keys(tabApi[categ] ?? {}).length
354
407
  acc[tab].category[categ] = count
355
408
  acc[tab].overall += count
356
409
  })
@@ -361,29 +414,18 @@ function getApiCount(parsedApi, tabs, innerTabs) {
361
414
  }
362
415
 
363
416
  const getJsonUrl =
364
- import.meta.env.QUASAR_DEV === true
365
- ? (file) => `/@fs/${import.meta.env.QCLI_FS_QUASAR_FOLDER}/dist/api/${file}.json`
366
- : (file) => `/quasar-api/${file}.json`
367
-
368
- const props = defineProps({
369
- file: {
370
- type: String,
371
- required: false,
372
- default: '',
373
- },
374
- api: {
375
- type: Object,
376
- required: false,
377
- default: null,
378
- },
379
- name: {
380
- type: String,
381
- default: 'API Documentation',
382
- },
383
- pageLink: Boolean,
417
+ qPressEnv.QUASAR_DEV === true
418
+ ? (file: string) => `/@fs/${qPressEnv.QCLI_FS_QUASAR_FOLDER ?? ''}/dist/api/${file}.json`
419
+ : (file: string) => `/quasar-api/${file}.json`
420
+
421
+ const props = withDefaults(defineProps<MarkdownApiProps>(), {
422
+ api: null,
423
+ file: '',
424
+ name: 'API Documentation',
425
+ pageLink: false,
384
426
  })
385
427
 
386
- const inputRef = ref(null)
428
+ const inputRef = ref<HTMLInputElement | null>(null)
387
429
 
388
430
  const loading = ref(true)
389
431
  const nameBanner = ref(`Loading ${props.file || props.name} API...`)
@@ -392,16 +434,16 @@ const nothingToShow = ref(false)
392
434
  const apiPath = ref('')
393
435
 
394
436
  const filter = ref('')
395
- const apiDef = ref({})
437
+ const apiDef = ref<ParsedApi>({})
396
438
 
397
- const tabsList = ref([])
398
- const innerTabsList = ref({})
439
+ const tabsList = ref<string[]>([])
440
+ const innerTabsList = ref<InnerTabsMap>({})
399
441
 
400
- const currentTab = ref(null)
401
- const currentInnerTab = ref(null)
442
+ const currentTab = ref('')
443
+ const currentInnerTab = ref('')
402
444
 
403
445
  watch(currentTab, (val) => {
404
- currentInnerTab.value = innerTabsList.value[val][0]
446
+ currentInnerTab.value = innerTabsList.value[val]?.[0] ?? defaultInnerTabName
405
447
  })
406
448
 
407
449
  const inputIcon = computed(() => (filter.value !== '' ? mdiClose : mdiMagnify))
@@ -436,11 +478,14 @@ const filteredApiCount = computed(() =>
436
478
  * @param {Object} api - Additional API properties.
437
479
  * @returns {Object} The parsed API information.
438
480
  */
439
- function parseApiFile(name, { type, behavior, meta, addedIn, ...api }) {
481
+ function parseApiFile(
482
+ name: string,
483
+ { type, behavior: _behavior, meta, addedIn: _addedIn, ...api }: ApiFile,
484
+ ) {
440
485
  nameBanner.value = `${name} API`
441
- apiPath.value = meta.docsUrl
486
+ apiPath.value = meta?.docsUrl ?? ''
442
487
 
443
- const { internal: _, ...apiSections } = api
488
+ const { internal: _internal, ...apiSections } = api
444
489
  const tabs = Object.keys(apiSections)
445
490
 
446
491
  if (tabs.length === 0) {
@@ -457,7 +502,7 @@ function parseApiFile(name, { type, behavior, meta, addedIn, ...api }) {
457
502
  }
458
503
 
459
504
  function onSearchFieldClick() {
460
- inputRef.value.focus()
505
+ inputRef.value?.focus()
461
506
  }
462
507
 
463
508
  function onFilterClick() {
@@ -466,12 +511,12 @@ function onFilterClick() {
466
511
  }
467
512
  }
468
513
 
469
- if (import.meta.env.QUASAR_CLIENT) {
514
+ if (qPressEnv.QUASAR_CLIENT === true) {
470
515
  onMounted(() => {
471
516
  if (props.file) {
472
517
  fetch(getJsonUrl(props.file))
473
518
  .then((response) => response.json())
474
- .then((json) => {
519
+ .then((json: ApiFile) => {
475
520
  parseApiFile(props.file, json)
476
521
  loading.value = false
477
522
  })
@@ -26,6 +26,11 @@ type CodepenParts = {
26
26
  [key: string]: string | undefined
27
27
  }
28
28
 
29
+ type CodepenGlobalPackage = {
30
+ packageName: string
31
+ globalName: string
32
+ }
33
+
29
34
  const defaultCssResources = [
30
35
  'https://fonts.googleapis.com/css?family=Roboto:100,300,400,500,700,900|Material+Icons',
31
36
  `https://cdn.jsdelivr.net/npm/quasar@${Quasar.version}/dist/quasar.min.css`,
@@ -44,22 +49,59 @@ function indent(code: string, spaces = 2) {
44
49
  .join('\n')
45
50
  }
46
51
 
47
- function getImportNames(content: string, packageName: string) {
48
- const names = new Set<string>()
49
- const importRe = new RegExp(`import\\s+{([^}'"\\n]+)}\\s+from\\s+['"]${packageName}['"];?`, 'g')
52
+ function getImportParts(content: string, packageName: string) {
53
+ const parts: { importName: string; bindingName: string }[] = []
54
+ const escapedPackageName = packageName.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')
55
+ const importRe = new RegExp(
56
+ `import\\s+(?!type\\b){([^}]*)}\\s+from\\s+['"]${escapedPackageName}['"];?`,
57
+ 'g',
58
+ )
50
59
  let match: RegExpExecArray | null
51
60
 
52
61
  while ((match = importRe.exec(content)) !== null) {
53
- for (const part of match[1].split(',')) {
54
- const name = part.trim().replace(/\s+as\s+/g, ': ')
62
+ for (const part of (match[1] ?? '').split(',')) {
63
+ const rawName = part.trim()
55
64
 
56
- if (name.length > 0) {
57
- names.add(name)
65
+ if (rawName.length === 0 || rawName.startsWith('type ')) {
66
+ continue
67
+ }
68
+
69
+ const [importName, bindingName = importName] = rawName.split(/\s+as\s+/)
70
+
71
+ if (importName !== undefined && importName.length > 0) {
72
+ parts.push({
73
+ importName,
74
+ bindingName,
75
+ })
58
76
  }
59
77
  }
60
78
  }
61
79
 
62
- return [...names]
80
+ return parts
81
+ }
82
+
83
+ function getImportNames(content: string, packageName: string) {
84
+ return [
85
+ ...new Set(
86
+ getImportParts(content, packageName).map(({ importName, bindingName }) =>
87
+ importName === bindingName ? importName : `${importName}: ${bindingName}`,
88
+ ),
89
+ ),
90
+ ]
91
+ }
92
+
93
+ function getImportBindingNames(content: string, packageName: string) {
94
+ return [...new Set(getImportParts(content, packageName).map(({ bindingName }) => bindingName))]
95
+ }
96
+
97
+ function getGlobalPackageImportLines(content: string) {
98
+ return (siteConfig.codepen?.globalPackages ?? [])
99
+ .map(({ packageName, globalName }: CodepenGlobalPackage) => {
100
+ const imports = getImportNames(content, packageName)
101
+
102
+ return imports.length > 0 ? `const { ${imports.join(', ')} } = ${globalName}` : ''
103
+ })
104
+ .filter((line) => line.length > 0)
63
105
  }
64
106
 
65
107
  function getGlobalImportLines(content: string) {
@@ -69,9 +111,20 @@ function getGlobalImportLines(content: string) {
69
111
  return [
70
112
  vueImports.length > 0 ? `const { ${vueImports.join(', ')} } = Vue` : '',
71
113
  quasarImports.length > 0 ? `const { ${quasarImports.join(', ')} } = Quasar` : '',
114
+ ...getGlobalPackageImportLines(content),
72
115
  ].filter((line) => line.length > 0)
73
116
  }
74
117
 
118
+ function getGlobalImportBindingNames(content: string) {
119
+ return [
120
+ ...getImportBindingNames(content, 'vue'),
121
+ ...getImportBindingNames(content, 'quasar'),
122
+ ...(siteConfig.codepen?.globalPackages ?? []).flatMap(({ packageName }: CodepenGlobalPackage) =>
123
+ getImportBindingNames(content, packageName),
124
+ ),
125
+ ]
126
+ }
127
+
75
128
  function stripImports(content: string) {
76
129
  return content
77
130
  .replace(/^\s*import\s+type\s+[\s\S]*?\s+from\s+['"][^'"]+['"];?\s*$/gm, '')
@@ -101,21 +154,53 @@ function getScriptBlock(script: string, setup: boolean) {
101
154
 
102
155
  function getSetupReturnNames(content: string) {
103
156
  const names = new Set<string>()
104
- const declarationRe = /(?:^|\n)\s*(?:const|let|var)\s+([A-Za-z_$][\w$]*)/g
157
+ const topLevelContent = getTopLevelContent(content)
158
+ const declarationRe =
159
+ /(?:^|\n)\s*(?:const|let|var)\s+([\s\S]*?)(?=\n\s*(?:const|let|var|function|interface|type|class)\s+|\s*$)/g
160
+ const variableNameRe = /(?:^|\n)\s*([A-Za-z_$][\w$]*)\s*(?:[:=,]|$)/g
105
161
  const functionRe = /(?:^|\n)\s*function\s+([A-Za-z_$][\w$]*)/g
106
162
  let match: RegExpExecArray | null
107
163
 
108
- while ((match = declarationRe.exec(content)) !== null) {
109
- names.add(match[1])
164
+ while ((match = declarationRe.exec(topLevelContent)) !== null) {
165
+ let variableMatch: RegExpExecArray | null
166
+
167
+ while ((variableMatch = variableNameRe.exec(match[1] ?? '')) !== null) {
168
+ if (variableMatch[1] !== undefined) {
169
+ names.add(variableMatch[1])
170
+ }
171
+ }
110
172
  }
111
173
 
112
- while ((match = functionRe.exec(content)) !== null) {
113
- names.add(match[1])
174
+ while ((match = functionRe.exec(topLevelContent)) !== null) {
175
+ if (match[1] !== undefined) {
176
+ names.add(match[1])
177
+ }
114
178
  }
115
179
 
116
180
  return [...names]
117
181
  }
118
182
 
183
+ function getTopLevelContent(content: string) {
184
+ let depth = 0
185
+ let output = ''
186
+
187
+ for (const char of content) {
188
+ if (depth === 0 || char === '\n') {
189
+ output += char
190
+ } else {
191
+ output += ' '
192
+ }
193
+
194
+ if (char === '{') {
195
+ depth++
196
+ } else if (char === '}') {
197
+ depth = Math.max(0, depth - 1)
198
+ }
199
+ }
200
+
201
+ return output
202
+ }
203
+
119
204
  function getAppSetup() {
120
205
  return ['app.use(Quasar, { config: {} })', siteConfig.codepen?.jsSetup ?? '']
121
206
  .map((line) => line.trim())
@@ -127,7 +212,9 @@ function createSetupScript(script: string) {
127
212
  const { content } = getScriptBlock(script, true)
128
213
  const globalImports = getGlobalImportLines(content)
129
214
  const setupContent = stripCompilerMacros(stripImports(content))
130
- const returnNames = getSetupReturnNames(setupContent)
215
+ const returnNames = [
216
+ ...new Set([...getGlobalImportBindingNames(content), ...getSetupReturnNames(setupContent)]),
217
+ ]
131
218
  const setupBody = [
132
219
  setupContent.length > 0 ? indent(setupContent, 4) : '',
133
220
  returnNames.length > 0 ? ` return { ${returnNames.join(', ')} }` : '',
@@ -172,7 +259,7 @@ function createOptionsScript(script: string) {
172
259
  const props = defineProps({ title: { type: String, required: true } })
173
260
 
174
261
  const active = ref(false)
175
- const formRef = ref(null)
262
+ const formRef = ref<HTMLFormElement | null>(null)
176
263
  const def = reactive<{ parts: CodepenParts }>({ parts: {} })
177
264
 
178
265
  const cssResources = computed(() => {
@@ -216,7 +303,10 @@ const html = computed(() => {
216
303
  .replace(/([\w]+=")([^"]*?)(")/g, function (match, p1, p2, p3) {
217
304
  return p1 + p2.replace(/>/g, '___TEMP_REPLACEMENT___') + p3
218
305
  })
219
- .replace(/<(q-[\w-]+|div)([^>]*?)\s*?([\n\r][\t ]+)?\/>/gs, '<$1$2$3></$1>')
306
+ .replace(
307
+ /<([A-Z][\w-]*|[a-z][\w]*-[\w-]+|div)([^>]*?)\s*?([\n\r][\t ]+)?\/>/gs,
308
+ '<$1$2$3></$1>',
309
+ )
220
310
  .replace(
221
311
  /(<template[^>]*>)(\s*?(?:[\n\r][\t ]+)?)<(thead|tbody|tfoot)/gs,
222
312
  '$1$2<___PREVENT_TEMPLATE___$3',
@@ -244,15 +334,20 @@ const html = computed(() => {
244
334
  })
245
335
 
246
336
  const editors = computed(() => {
247
- const flag = (html.value && 0b100) | (css.value && 0b010) | (js.value && 0b001)
337
+ const flag =
338
+ (html.value.length > 0 ? 0b100 : 0) |
339
+ (css.value.length > 0 ? 0b010 : 0) |
340
+ (js.value.length > 0 ? 0b001 : 0)
248
341
  return flag.toString(2)
249
342
  })
250
343
 
251
344
  const computedTitle = computed(() => {
345
+ const titleSuffix = siteConfig.codepen?.titleSuffix ?? `Quasar v${Quasar.version}`
346
+
252
347
  return (
253
348
  (typeof document !== 'undefined' ? document.title.split(' | ')[0] + ': ' : '') +
254
349
  (props.title ? props.title + ' - ' : '') +
255
- `Quasar v${Quasar.version}`
350
+ titleSuffix
256
351
  )
257
352
  })
258
353
 
@@ -298,14 +393,14 @@ function open(whichParts: CodepenParts) {
298
393
  def.parts = whichParts
299
394
 
300
395
  if (active.value) {
301
- formRef.value.submit()
396
+ formRef.value?.submit()
302
397
  return
303
398
  }
304
399
 
305
400
  active.value = true
306
401
 
307
402
  nextTick(() => {
308
- formRef.value.submit()
403
+ formRef.value?.submit()
309
404
  })
310
405
  }
311
406
 
@@ -96,7 +96,7 @@
96
96
  import { computed, inject, markRaw, ref, reactive, onBeforeUnmount, onMounted } from 'vue'
97
97
  import { openURL } from 'quasar'
98
98
 
99
- import { fabGithub, fabCodepen } from '@quasar/extras/fontawesome-v6'
99
+ import { fabGithub, fabCodepen } from '@quasar/extras/fontawesome-v7'
100
100
  // import { mdiCompare } from '@quasar/extras/mdi-v7'
101
101
 
102
102
  import MarkdownCode from './MarkdownCode.vue'
@@ -10,7 +10,7 @@
10
10
 
11
11
  <script setup lang="ts">
12
12
  import { computed } from 'vue'
13
- import { mdiLaunch } from '@quasar/extras/mdi-v6'
13
+ import { mdiLaunch } from '@quasar/extras/mdi-v7'
14
14
 
15
15
  const props = defineProps({ to: { type: String, required: true } })
16
16
  const internal = computed(