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

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 (44) hide show
  1. package/README.md +3 -1
  2. package/dist/templates/init/src/_q-press/components/MarkdownApi.vue +128 -83
  3. package/dist/templates/init/src/_q-press/components/MarkdownCodepen.vue +73 -12
  4. package/dist/templates/init/src/markdown/guides/faq.md +1 -1
  5. package/dist/templates/init/src/markdown/md-plugins/blockquote/overview.md +3 -1
  6. package/dist/templates/init/src/markdown/md-plugins/codeblocks/overview.md +7 -1
  7. package/dist/templates/init/src/markdown/md-plugins/containers/overview.md +3 -1
  8. package/dist/templates/init/src/markdown/md-plugins/frontmatter/overview.md +3 -1
  9. package/dist/templates/init/src/markdown/md-plugins/headers/overview.md +3 -1
  10. package/dist/templates/init/src/markdown/md-plugins/image/overview.md +3 -1
  11. package/dist/templates/init/src/markdown/md-plugins/imports/overview.md +3 -1
  12. package/dist/templates/init/src/markdown/md-plugins/inline-code/overview.md +3 -1
  13. package/dist/templates/init/src/markdown/md-plugins/link/overview.md +3 -1
  14. package/dist/templates/init/src/markdown/md-plugins/table/overview.md +3 -1
  15. package/dist/templates/init/src/markdown/md-plugins/title/overview.md +3 -1
  16. package/dist/templates/init/src/markdown/quasar-app-extensions/qpress/advanced.md +3 -1
  17. package/dist/templates/init/src/markdown/quasar-app-extensions/qpress/overview.md +4 -0
  18. package/dist/templates/init/src/markdown/vite-plugins/vite-examples-plugin/overview.md +3 -1
  19. package/dist/templates/init/src/markdown/vite-plugins/vite-md-plugin/overview.md +3 -1
  20. package/dist/templates/init/src/siteConfig/index.ts +4 -0
  21. package/dist/templates/update/src/_q-press/components/MarkdownApi.vue +128 -83
  22. package/dist/templates/update/src/_q-press/components/MarkdownCodepen.vue +73 -12
  23. package/package.json +14 -14
  24. package/src/templates/init/src/_q-press/components/MarkdownApi.vue +128 -83
  25. package/src/templates/init/src/_q-press/components/MarkdownCodepen.vue +73 -12
  26. package/src/templates/init/src/markdown/guides/faq.md +1 -1
  27. package/src/templates/init/src/markdown/md-plugins/blockquote/overview.md +3 -1
  28. package/src/templates/init/src/markdown/md-plugins/codeblocks/overview.md +7 -1
  29. package/src/templates/init/src/markdown/md-plugins/containers/overview.md +3 -1
  30. package/src/templates/init/src/markdown/md-plugins/frontmatter/overview.md +3 -1
  31. package/src/templates/init/src/markdown/md-plugins/headers/overview.md +3 -1
  32. package/src/templates/init/src/markdown/md-plugins/image/overview.md +3 -1
  33. package/src/templates/init/src/markdown/md-plugins/imports/overview.md +3 -1
  34. package/src/templates/init/src/markdown/md-plugins/inline-code/overview.md +3 -1
  35. package/src/templates/init/src/markdown/md-plugins/link/overview.md +3 -1
  36. package/src/templates/init/src/markdown/md-plugins/table/overview.md +3 -1
  37. package/src/templates/init/src/markdown/md-plugins/title/overview.md +3 -1
  38. package/src/templates/init/src/markdown/quasar-app-extensions/qpress/advanced.md +3 -1
  39. package/src/templates/init/src/markdown/quasar-app-extensions/qpress/overview.md +4 -0
  40. package/src/templates/init/src/markdown/vite-plugins/vite-examples-plugin/overview.md +3 -1
  41. package/src/templates/init/src/markdown/vite-plugins/vite-md-plugin/overview.md +3 -1
  42. package/src/templates/init/src/siteConfig/index.ts +4 -0
  43. package/src/templates/update/src/_q-press/components/MarkdownApi.vue +128 -83
  44. package/src/templates/update/src/_q-press/components/MarkdownCodepen.vue +73 -12
package/README.md CHANGED
@@ -4,7 +4,7 @@ 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.11`.
8
8
  >
9
9
  > Q-Press currently targets Quasar Vite projects using `@quasar/app-vite` `>=3.0.0-beta.29`. TypeScript processing is required.
10
10
 
@@ -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`,
@@ -46,12 +51,22 @@ function indent(code: string, spaces = 2) {
46
51
 
47
52
  function getImportNames(content: string, packageName: string) {
48
53
  const names = new Set<string>()
49
- const importRe = new RegExp(`import\\s+{([^}'"\\n]+)}\\s+from\\s+['"]${packageName}['"];?`, 'g')
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()
64
+
65
+ if (rawName.length === 0 || rawName.startsWith('type ')) {
66
+ continue
67
+ }
68
+
69
+ const name = rawName.replace(/\s+as\s+/g, ': ')
55
70
 
56
71
  if (name.length > 0) {
57
72
  names.add(name)
@@ -62,6 +77,16 @@ function getImportNames(content: string, packageName: string) {
62
77
  return [...names]
63
78
  }
64
79
 
80
+ function getGlobalPackageImportLines(content: string) {
81
+ return (siteConfig.codepen?.globalPackages ?? [])
82
+ .map(({ packageName, globalName }: CodepenGlobalPackage) => {
83
+ const imports = getImportNames(content, packageName)
84
+
85
+ return imports.length > 0 ? `const { ${imports.join(', ')} } = ${globalName}` : ''
86
+ })
87
+ .filter((line) => line.length > 0)
88
+ }
89
+
65
90
  function getGlobalImportLines(content: string) {
66
91
  const vueImports = getImportNames(content, 'vue')
67
92
  const quasarImports = getImportNames(content, 'quasar')
@@ -69,6 +94,7 @@ function getGlobalImportLines(content: string) {
69
94
  return [
70
95
  vueImports.length > 0 ? `const { ${vueImports.join(', ')} } = Vue` : '',
71
96
  quasarImports.length > 0 ? `const { ${quasarImports.join(', ')} } = Quasar` : '',
97
+ ...getGlobalPackageImportLines(content),
72
98
  ].filter((line) => line.length > 0)
73
99
  }
74
100
 
@@ -101,21 +127,53 @@ function getScriptBlock(script: string, setup: boolean) {
101
127
 
102
128
  function getSetupReturnNames(content: string) {
103
129
  const names = new Set<string>()
104
- const declarationRe = /(?:^|\n)\s*(?:const|let|var)\s+([A-Za-z_$][\w$]*)/g
130
+ const topLevelContent = getTopLevelContent(content)
131
+ const declarationRe =
132
+ /(?:^|\n)\s*(?:const|let|var)\s+([\s\S]*?)(?=\n\s*(?:const|let|var|function|interface|type|class)\s+|\s*$)/g
133
+ const variableNameRe = /(?:^|\n)\s*([A-Za-z_$][\w$]*)\s*(?:[:=,]|$)/g
105
134
  const functionRe = /(?:^|\n)\s*function\s+([A-Za-z_$][\w$]*)/g
106
135
  let match: RegExpExecArray | null
107
136
 
108
- while ((match = declarationRe.exec(content)) !== null) {
109
- names.add(match[1])
137
+ while ((match = declarationRe.exec(topLevelContent)) !== null) {
138
+ let variableMatch: RegExpExecArray | null
139
+
140
+ while ((variableMatch = variableNameRe.exec(match[1] ?? '')) !== null) {
141
+ if (variableMatch[1] !== undefined) {
142
+ names.add(variableMatch[1])
143
+ }
144
+ }
110
145
  }
111
146
 
112
- while ((match = functionRe.exec(content)) !== null) {
113
- names.add(match[1])
147
+ while ((match = functionRe.exec(topLevelContent)) !== null) {
148
+ if (match[1] !== undefined) {
149
+ names.add(match[1])
150
+ }
114
151
  }
115
152
 
116
153
  return [...names]
117
154
  }
118
155
 
156
+ function getTopLevelContent(content: string) {
157
+ let depth = 0
158
+ let output = ''
159
+
160
+ for (const char of content) {
161
+ if (depth === 0 || char === '\n') {
162
+ output += char
163
+ } else {
164
+ output += ' '
165
+ }
166
+
167
+ if (char === '{') {
168
+ depth++
169
+ } else if (char === '}') {
170
+ depth = Math.max(0, depth - 1)
171
+ }
172
+ }
173
+
174
+ return output
175
+ }
176
+
119
177
  function getAppSetup() {
120
178
  return ['app.use(Quasar, { config: {} })', siteConfig.codepen?.jsSetup ?? '']
121
179
  .map((line) => line.trim())
@@ -172,7 +230,7 @@ function createOptionsScript(script: string) {
172
230
  const props = defineProps({ title: { type: String, required: true } })
173
231
 
174
232
  const active = ref(false)
175
- const formRef = ref(null)
233
+ const formRef = ref<HTMLFormElement | null>(null)
176
234
  const def = reactive<{ parts: CodepenParts }>({ parts: {} })
177
235
 
178
236
  const cssResources = computed(() => {
@@ -244,7 +302,10 @@ const html = computed(() => {
244
302
  })
245
303
 
246
304
  const editors = computed(() => {
247
- const flag = (html.value && 0b100) | (css.value && 0b010) | (js.value && 0b001)
305
+ const flag =
306
+ (html.value.length > 0 ? 0b100 : 0) |
307
+ (css.value.length > 0 ? 0b010 : 0) |
308
+ (js.value.length > 0 ? 0b001 : 0)
248
309
  return flag.toString(2)
249
310
  })
250
311
 
@@ -298,14 +359,14 @@ function open(whichParts: CodepenParts) {
298
359
  def.parts = whichParts
299
360
 
300
361
  if (active.value) {
301
- formRef.value.submit()
362
+ formRef.value?.submit()
302
363
  return
303
364
  }
304
365
 
305
366
  active.value = true
306
367
 
307
368
  nextTick(() => {
308
- formRef.value.submit()
369
+ formRef.value?.submit()
309
370
  })
310
371
  }
311
372
 
@@ -11,7 +11,7 @@ Markdown Plugins are tools that extend the functionality of Markdown, allowing y
11
11
 
12
12
  ### How do I install Markdown Plugins?
13
13
 
14
- You can install Markdown Plugins using npm, yarn, or pnpm. Here is an example using npm:
14
+ You can install Markdown Plugins using npm, yarn, pnpm, or bun. Here is an example using npm:
15
15
 
16
16
  ```bash
17
17
  npm install @md-plugins/vite-md-plugin
@@ -115,11 +115,13 @@ The official NPM name is `@md-plugins/md-plugin-blockquote`.
115
115
 
116
116
  ## Installation
117
117
 
118
- You can install the Blockquote plugin using `npm`, `yarn`, or `pnpm`. Choose your preferred method below:
118
+ You can install the Blockquote plugin using `npm`, `yarn`, `pnpm`, or `bun`. Choose your preferred method below:
119
119
 
120
120
  ```tabs
121
121
  <<| bash pnpm |>>
122
122
  pnpm add @md-plugins/md-plugin-blockquote
123
+ <<| bash bun |>>
124
+ bun add @md-plugins/md-plugin-blockquote
123
125
  <<| bash yarn |>>
124
126
  yarn add @md-plugins/md-plugin-blockquote
125
127
  <<| bash npm |>>
@@ -482,6 +482,8 @@ Look for the `+` and `-` on individual lines at the far-left.
482
482
  ```tabs
483
483
  <<| bash pnpm |>>
484
484
  pnpm add @md-plugins/md-plugin-codeblocks
485
+ <<| bash bun |>>
486
+ bun add @md-plugins/md-plugin-codeblocks
485
487
  <<| bash yarn |>>
486
488
  yarn add @md-plugins/md-plugin-codeblocks
487
489
  <<| bash npm |>>
@@ -492,6 +494,8 @@ npm install @md-plugins/md-plugin-codeblocks
492
494
  ```tabs
493
495
  <<| bash pnpm |>>
494
496
  pnpm add @md-plugins/md-plugin-codeblocks
497
+ <<| bash bun |>>
498
+ bun add @md-plugins/md-plugin-codeblocks
495
499
  <<| bash yarn |>>
496
500
  yarn add @md-plugins/md-plugin-codeblocks
497
501
  <<| bash npm |>>
@@ -597,11 +601,13 @@ The official NPM name is `@md-plugins/md-plugin-codeblocks`.
597
601
 
598
602
  ## Installation
599
603
 
600
- You can install the Codeblocks plugin using npm, yarn, or pnpm. Choose your preferred method below:
604
+ You can install the Codeblocks plugin using npm, yarn, pnpm, or bun. Choose your preferred method below:
601
605
 
602
606
  ```tabs
603
607
  <<| bash pnpm |>>
604
608
  pnpm add @md-plugins/md-plugin-codeblocks
609
+ <<| bash bun |>>
610
+ bun add @md-plugins/md-plugin-codeblocks
605
611
  <<| bash yarn |>>
606
612
  yarn add @md-plugins/md-plugin-codeblocks
607
613
  <<| bash npm |>>
@@ -89,11 +89,13 @@ The official NPM name is `@md-plugins/md-plugin-containers`.
89
89
 
90
90
  ## Installation
91
91
 
92
- You can install the Containers plugin using npm, yarn, or pnpm. Choose your preferred method below:
92
+ You can install the Containers plugin using npm, yarn, pnpm, or bun. Choose your preferred method below:
93
93
 
94
94
  ```tabs
95
95
  <<| bash pnpm |>>
96
96
  pnpm add @md-plugins/md-plugin-containers
97
+ <<| bash bun |>>
98
+ bun add @md-plugins/md-plugin-containers
97
99
  <<| bash yarn |>>
98
100
  yarn add @md-plugins/md-plugin-containers
99
101
  <<| bash npm |>>
@@ -54,11 +54,13 @@ The official NPM name is `@md-plugins/md-plugin-frontmatter`.
54
54
 
55
55
  ## Installation
56
56
 
57
- You can install the Frontmatter plugin using npm, yarn, or pnpm. Choose your preferred method below:
57
+ You can install the Frontmatter plugin using npm, yarn, pnpm, or bun. Choose your preferred method below:
58
58
 
59
59
  ```tabs
60
60
  <<| bash pnpm |>>
61
61
  pnpm add @md-plugins/md-plugin-frontmatter
62
+ <<| bash bun |>>
63
+ bun add @md-plugins/md-plugin-frontmatter
62
64
  <<| bash yarn |>>
63
65
  yarn add @md-plugins/md-plugin-frontmatter
64
66
  <<| bash npm |>>