Skip to content

Commit e0e3338

Browse files
authored
feat!: refactor types for better DX (#25)
1 parent 75b0209 commit e0e3338

File tree

5 files changed

+90
-52
lines changed

5 files changed

+90
-52
lines changed

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -210,7 +210,7 @@ vuetify: {
210210
vuetifyOptions: {
211211
icons: {
212212
defaultSet: 'mdi'
213-
sets: [{ name: 'mdi' }, { name: 'fa' }]
213+
sets: ['mdi', 'fa']
214214
}
215215
}
216216
}

src/module.ts

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -87,7 +87,7 @@ export default defineNuxtModule<ModuleOptions>({
8787
dateAdapter = adapter
8888
}
8989
else {
90-
if (date.find(d => d[0] === adapter) === undefined)
90+
if (date.find(d => d === adapter) === undefined)
9191
logger.warn(`Ignoring Vuetify Date configuration, date adapter "@date-io/${adapter}" not installed!`)
9292
else
9393
dateAdapter = adapter
@@ -138,17 +138,16 @@ export default defineNuxtModule<ModuleOptions>({
138138
references.push({ types: 'vuetify-nuxt-module/configuration' })
139139
})
140140

141-
const vuetifyBase = resolveVuetifyBase()
142141
nuxt.hook('components:extend', async (c) => {
142+
const vuetifyBase = resolveVuetifyBase()
143143
const { components } = JSON.parse(await readFile(resolver.resolve(vuetifyBase, 'dist/json/importMap.json'), 'utf-8'))
144144
Object.keys(components).forEach((component) => {
145145
const from = components[component].from
146146
c.push({
147147
pascalName: component,
148148
kebabName: toKebabCase(component),
149149
export: component,
150-
// pointing to the mjs file, links will not work: there is d.mts file for each component
151-
filePath: `${resolver.resolve(vuetifyBase, `lib/${from/* .replace(/\.mjs$/, '.d.mts') */}`)}`,
150+
filePath: `${resolver.resolve(vuetifyBase, `lib/${from}`)}`,
152151
shortPath: `components/${from}`,
153152
chunkName: toKebabCase(component),
154153
prefetch: false,

src/types.ts

Lines changed: 14 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,5 @@
11
import type { VuetifyOptions } from 'vuetify'
22

3-
export type BooleanOrArrayString = boolean | string[]
4-
53
export type DateAdapter = 'vuetify' | 'date-fns' | 'moment' | 'luxon' | 'dayjs' | 'js-joda' | 'date-fns-jalali' | 'jalaali' | 'hijri' | 'custom'
64

75
/**
@@ -30,20 +28,17 @@ export interface DateOptions {
3028
*
3129
* When `@nuxtjs/i18n` Nuxt module is present, this option will be ignored, locales will be extracted from the available locales.
3230
*/
33-
locale: Record<string, any>
31+
locale?: Record<string, any>
3432
}
3533

3634
export type IconSetName = 'mdi' | 'fa' | 'fa4' | 'md' | 'mdi-svg' | 'fa-svg' | 'custom'
35+
export type IconFontName = 'mdi' | 'fa' | 'fa4' | 'md'
3736

38-
export interface IconSet {
39-
name: IconSetName
40-
}
41-
42-
export interface JSSVGIconSet extends IconSet {
37+
export interface JSSVGIconSet {
4338
aliases?: Record<string, string>
4439
}
4540

46-
export interface FontAwesomeSvgIconSet extends IconSet {
41+
export interface FontAwesomeSvgIconSet {
4742
/**
4843
* The libraries to import and register with the corresponding name.
4944
*
@@ -58,7 +53,8 @@ export interface FontAwesomeSvgIconSet extends IconSet {
5853
libraries?: [defaultExport: boolean, name: string, library: string][]
5954
}
6055

61-
export interface FontIconSet extends IconSet {
56+
export interface FontIconSet {
57+
name: IconFontName
6258
/**
6359
* Use CDN?
6460
*
@@ -74,13 +70,18 @@ export interface FontIconSet extends IconSet {
7470

7571
export interface IconsOptions {
7672
defaultSet: IconSetName
77-
sets?: FontIconSet[]
73+
sets?: IconFontName | IconFontName[] | FontIconSet[]
7874
svg?: {
7975
mdi?: JSSVGIconSet
8076
fa?: FontAwesomeSvgIconSet
8177
}
8278
}
8379

80+
export type LabComponentName = keyof typeof import('vuetify/labs/components')
81+
export type LabComponents = boolean | LabComponentName | LabComponentName[]
82+
export type DirectiveName = keyof typeof import('vuetify/directives')
83+
export type Directives = boolean | DirectiveName | DirectiveName[]
84+
8485
export interface VOptions extends Partial<Omit<VuetifyOptions, 'ssr' | 'directives' | 'locale' | 'date' | 'icons'>> {
8586
/**
8687
* Include the lab components?
@@ -93,7 +94,7 @@ export interface VOptions extends Partial<Omit<VuetifyOptions, 'ssr' | 'directiv
9394
*
9495
* @default false
9596
*/
96-
labComponents?: BooleanOrArrayString
97+
labComponents?: LabComponents
9798
/**
9899
* Include the directives?
99100
*
@@ -103,7 +104,7 @@ export interface VOptions extends Partial<Omit<VuetifyOptions, 'ssr' | 'directiv
103104
*
104105
* @default false
105106
*/
106-
directives?: BooleanOrArrayString
107+
directives?: Directives
107108
/**
108109
* Date configuration.
109110
*

src/utils/icons.ts

Lines changed: 64 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { isPackageExists } from 'local-pkg'
2-
import type { IconSetName, VOptions } from '../types'
2+
import type { FontIconSet, IconFontName, IconSetName, IconsOptions, VOptions } from '../types'
33

44
export interface ResolvedIcons {
55
enabled: boolean
@@ -8,7 +8,7 @@ export interface ResolvedIcons {
88
cdn: string[]
99
local: string[]
1010
aliases: string[]
11-
imports: Record<string, string[]>
11+
imports: string[]
1212
svg: {
1313
mdi?: boolean
1414
fa?: string[]
@@ -17,38 +17,50 @@ export interface ResolvedIcons {
1717

1818
export const cssFonts: IconSetName[] = ['mdi', 'md', 'fa', 'fa4']
1919

20-
const iconsPackageNames: Record<IconSetName, { name: string; css: string }> = {
20+
const iconsPackageNames: Record<IconFontName, { name: string; css: string }> = {
2121
mdi: { name: '@mdi/font', css: '@mdi/font/css/materialdesignicons.css' },
2222
md: { name: 'material-design-icons-iconfont', css: '@mdi/font/css/materialdesignicons.css' },
2323
fa: { name: '@fortawesome/fontawesome-free', css: '@fortawesome/fontawesome-free/css/all.css' },
2424
fa4: { name: '[email protected]', css: 'font-awesome/css/font-awesome.min.css' },
2525
}
2626

27-
const iconsCDN: Record<IconSetName, string> = {
27+
const iconsCDN: Record<IconFontName, string> = {
2828
mdi: 'https://cdn.jsdelivr.net/npm/@mdi/[email protected]/css/materialdesignicons.min.css',
2929
md: 'https://fonts.googleapis.com/css?family=Material+Icons',
3030
fa: 'https://cdn.jsdelivr.net/npm/@fortawesome/fontawesome-free@latest/css/all.min.css',
3131
fa4: 'https://cdn.jsdelivr.net/npm/[email protected]/css/font-awesome.min.css',
3232
}
3333

34+
const disabledResolvedIcons: ResolvedIcons = Object.freeze({
35+
enabled: false,
36+
imports: [],
37+
aliases: [],
38+
sets: [],
39+
cdn: [],
40+
local: [],
41+
svg: {},
42+
})
43+
3444
export function prepareIcons(
3545
logger: ReturnType<typeof import('@nuxt/kit')['useLogger']>,
3646
vuetifyOptions: VOptions,
3747
): ResolvedIcons {
3848
// TODO: handle blueprint icons
3949
if (vuetifyOptions.icons === false)
40-
return { enabled: false }
50+
return disabledResolvedIcons
4151

42-
vuetifyOptions.icons = vuetifyOptions.icons ?? {}
52+
const icons: IconsOptions = (vuetifyOptions.icons as IconsOptions) || {}
4353

44-
let { defaultSet = 'mdi', sets } = vuetifyOptions.icons
54+
let { defaultSet = 'mdi', sets } = icons
4555

4656
if (!defaultSet)
47-
defaultSet = vuetifyOptions.icons.defaultSet = 'mdi'
57+
defaultSet = icons.defaultSet = 'mdi'
4858

49-
if (!sets)
59+
if (!sets && defaultSet !== 'mdi-svg' && defaultSet !== 'fa-svg' && defaultSet !== 'custom')
5060
sets = [{ name: defaultSet || 'mdi' }]
5161

62+
sets = sets ? convertFontSetsToObjectNotation(sets) : undefined
63+
5264
const resolvedIcons: ResolvedIcons = {
5365
enabled: true,
5466
defaultSet,
@@ -62,33 +74,37 @@ export function prepareIcons(
6274
},
6375
}
6476

65-
sets.filter(s => cssFonts.includes(s.name)).map(s => s.name).forEach((name) => {
66-
resolvedIcons.imports.push(`import {${name === defaultSet ? 'aliases,' : ''}${name}} from \'vuetify/iconsets/${name}\'`)
67-
resolvedIcons.sets.push(name)
68-
if (isPackageExists(iconsPackageNames[name].name))
69-
resolvedIcons.local!.push(iconsPackageNames[name].css)
70-
else
71-
resolvedIcons.cdn!.push(iconsCDN[name])
72-
})
77+
if (sets) {
78+
sets.filter(s => cssFonts.includes(s.name)).map(s => s.name).forEach((name) => {
79+
resolvedIcons.imports.push(`import {${name === defaultSet ? 'aliases,' : ''}${name}} from \'vuetify/iconsets/${name}\'`)
80+
resolvedIcons.sets.push(name)
81+
if (isPackageExists(iconsPackageNames[name].name))
82+
resolvedIcons.local!.push(iconsPackageNames[name].css)
83+
else
84+
resolvedIcons.cdn!.push(iconsCDN[name])
85+
})
86+
}
7387

74-
let faSvg = vuetifyOptions.icons.svg?.fa
88+
let faSvg = icons.svg?.fa
7589
if (defaultSet === 'fa-svg' || faSvg) {
90+
if (!faSvg)
91+
faSvg = {}
92+
7693
let faSvgExists = isPackageExists('@fortawesome/fontawesome-svg-core')
7794
if (!faSvgExists)
7895
logger.warn('Missing @fortawesome/fontawesome-svg-core dependency, install it!')
7996

8097
faSvgExists = isPackageExists('@fortawesome/vue-fontawesome')
8198
if (faSvgExists) {
82-
if (!faSvg?.libraries?.length) {
83-
faSvg = faSvg || {}
99+
if (!faSvg.libraries?.length)
84100
faSvg.libraries = [[false, 'fas', '@fortawesome/free-solid-svg-icons']]
85-
}
86101

87102
for (const p in faSvg.libraries) {
88103
const [_defaultExport, _name, library] = faSvg.libraries[p]
89-
faSvgExists = isPackageExists(library)
90-
if (!faSvgExists)
104+
if (!isPackageExists(library)) {
105+
faSvgExists = false
91106
logger.warn(`Missing library ${library} dependency, install it!`)
107+
}
92108
}
93109
}
94110
else {
@@ -101,21 +117,24 @@ export function prepareIcons(
101117
resolvedIcons.imports.push('import { FontAwesomeIcon } from \'@fortawesome/vue-fontawesome\'')
102118
resolvedIcons.imports.push('import { useNuxtApp } from \'#app\'')
103119
resolvedIcons.svg.fa = ['useNuxtApp().vueApp.component(\'font-awesome-icon\', FontAwesomeIcon)']
104-
faSvg.libraries.forEach(([defaultExport, name, library]) => {
120+
faSvg.libraries!.forEach(([defaultExport, name, library]) => {
105121
resolvedIcons.imports.push(`import ${defaultExport ? name : `{${name}}`} from \'${library}\'`)
106-
resolvedIcons.svg.fa.push(`library.add(${name})`)
122+
resolvedIcons.svg.fa!.push(`library.add(${name})`)
107123
})
108124
resolvedIcons.sets.push('fa')
109125
if (defaultSet === 'fa-svg')
110126
resolvedIcons.defaultSet = 'fa'
111127
}
112128
}
113129

114-
const mdiSvg = vuetifyOptions.icons.svg?.mdi
130+
let mdiSvg = icons.svg?.mdi
115131
if (defaultSet === 'mdi-svg' || mdiSvg) {
132+
if (!mdiSvg)
133+
mdiSvg = {}
134+
116135
const mdiSvgExists = isPackageExists('@mdi/js')
117136
if (mdiSvgExists) {
118-
resolvedIcons.svg!.mdi = true
137+
resolvedIcons.svg.mdi = true
119138
resolvedIcons.imports.push(`import {${defaultSet === 'mdi-svg' ? 'aliases,' : ''}mdi} from \'vuetify/iconsets/mdi-svg\'`)
120139
if (mdiSvg && mdiSvg.aliases) {
121140
resolvedIcons.imports.push(`import {${Object.values(mdiSvg.aliases).join(',')}} from \'@mdi/js\'`)
@@ -135,8 +154,25 @@ export function prepareIcons(
135154

136155
if (!resolvedIcons.local?.length && !resolvedIcons.cdn?.length && !resolvedIcons.svg?.mdi && !resolvedIcons.svg?.fa?.length) {
137156
logger.warn('No icons found, icons disabled!')
138-
return { enabled: false }
157+
return disabledResolvedIcons
139158
}
140159

141160
return resolvedIcons
142161
}
162+
163+
function convertFontSetsToObjectNotation(sets: IconFontName | IconFontName[] | FontIconSet[]) {
164+
const result: FontIconSet[] = []
165+
if (typeof sets === 'string') {
166+
result.push({ name: sets })
167+
}
168+
else {
169+
for (const set of sets) {
170+
if (typeof set === 'string')
171+
result.push({ name: set })
172+
else
173+
result.push(set)
174+
}
175+
}
176+
177+
return result
178+
}

src/vite/vuetify-configuration-plugin.ts

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import type { Plugin } from 'vite'
2-
import type { BooleanOrArrayString, VOptions } from '../types'
2+
import type { Directives, LabComponents, VOptions } from '../types'
33
import { RESOLVED_VIRTUAL_VUETIFY_CONFIGURATION, VIRTUAL_VUETIFY_CONFIGURATION } from './constants'
44

55
interface ImportsResult {
@@ -9,8 +9,8 @@ interface ImportsResult {
99

1010
export function vuetifyConfigurationPlugin(
1111
isDev: boolean,
12-
directives: BooleanOrArrayString,
13-
labComponents: BooleanOrArrayString,
12+
directives: Directives,
13+
labComponents: LabComponents,
1414
vuetifyAppOptions: VOptions,
1515
) {
1616
// TODO: handle blueprint
@@ -58,9 +58,10 @@ export function vuetifyConfiguration() {
5858
}
5959
}
6060
else {
61+
const useDirectives = Array.isArray(directives) ? directives : [directives]
6162
return <ImportsResult>{
62-
imports: `${directives.map(d => `import { ${d} } from 'vuetify/directives/${d}'`).join('\n')}`,
63-
expression: `options.directives = {${directives.join(',')}}`,
63+
imports: `${useDirectives.map(d => `import { ${d} } from 'vuetify/directives/${d}'`).join('\n')}`,
64+
expression: `options.directives = {${useDirectives.join(',')}}`,
6465
}
6566
}
6667
}
@@ -77,7 +78,8 @@ export function vuetifyConfiguration() {
7778
}
7879
}
7980
else {
80-
const components = [...new Set<string>(dateOptions ? ['VDatePicker', ...labComponents] : labComponents)]
81+
const useComponents = Array.isArray(labComponents) ? labComponents : [labComponents]
82+
const components = [...new Set(dateOptions ? ['VDatePicker', ...useComponents] : useComponents)]
8183
return <ImportsResult>{
8284
imports: `${components.map(d => `import { ${d} } from 'vuetify/labs/${d}'`).join('\n')}`,
8385
expression: `options.components = {${components.join(',')}}`,

0 commit comments

Comments
 (0)