Skip to content

Commit

Permalink
refactor: extract context
Browse files Browse the repository at this point in the history
  • Loading branch information
antfu committed Jul 31, 2024
1 parent 177c9f2 commit 27f92ad
Show file tree
Hide file tree
Showing 4 changed files with 145 additions and 97 deletions.
38 changes: 22 additions & 16 deletions src/bundle-client.ts
Original file line number Diff line number Diff line change
@@ -1,31 +1,26 @@
import { addTemplate, logger } from '@nuxt/kit'
import type { IconifyIcon, IconifyJSON } from '@iconify/types'
import type { Nuxt } from 'nuxt/schema'
import type { ModuleOptions } from './types'
import { loadCustomCollection } from './collections'
import type { NuxtIconModuleContext } from './context'

export function registerClientBundle(
options: ModuleOptions,
nuxt: Nuxt,
ctx: NuxtIconModuleContext,
): void {
const iconifyCollectionMap = new Map<string, Promise<IconifyJSON | undefined>>()

const {
includeCustomCollections = options.provider !== 'server',
} = options.clientBundle || {}
includeCustomCollections = ctx.options.provider !== 'server',
} = ctx.options.clientBundle || {}

// Client bundle
addTemplate({
filename: 'nuxt-icon-client-bundle.mjs',
write: true,
async getContents() {
const icons = [...options.clientBundle?.icons || []]
const icons = [...ctx.options.clientBundle?.icons || []]

let customCollections: IconifyJSON[] = []
if (includeCustomCollections && options.customCollections?.length) {
customCollections = await Promise.all(
options.customCollections.map(collection => loadCustomCollection(collection, nuxt)),
)
if (includeCustomCollections && ctx.options.customCollections?.length) {
customCollections = await ctx.loadCustomCollection()
}

if (!icons.length && !customCollections.length)
Expand All @@ -42,6 +37,9 @@ export function registerClientBundle(
'export function init() {',
' if (_initialized)',
' return',
)

lines.push(
...await Promise.all(icons.map(async (icon) => {
const [prefix, name] = icon.split(':')
if (!iconifyCollectionMap.has(prefix))
Expand All @@ -58,10 +56,18 @@ export function registerClientBundle(
}
return ` addIcon('${icon}', ${JSON.stringify(data)})`
})),
customCollections.length ? ' // ===== Custom collections =====' : '',
...customCollections.flatMap(collection => Object.entries(collection.icons).map(([name, data]) => {
return ` addIcon('${collection.prefix}:${name}', ${JSON.stringify(data)})`
})),
)

if (customCollections.length) {
lines.push(
' // ===== Custom collections =====',
...customCollections.flatMap(collection => Object.entries(collection.icons).map(([name, data]) => {
return ` addIcon('${collection.prefix}:${name}', ${JSON.stringify(data)})`
})),
)
}

lines.push(
' _initialized = true',
'}',
)
Expand Down
59 changes: 9 additions & 50 deletions src/bundle-server.ts
Original file line number Diff line number Diff line change
@@ -1,58 +1,17 @@
import { addTemplate } from '@nuxt/kit'
import type { Nuxt } from '@nuxt/schema'
import collectionNames from './collection-names'
import type { ModuleOptions, ResolvedServerBundleOptions, CustomCollection, ServerBundleOptions, NuxtIconRuntimeOptions } from './types'
import { discoverInstalledCollections, isFullCollectionExists, resolveCollection } from './collections'

async function resolveServerBundle(
nuxt: Nuxt,
options: ServerBundleOptions | Promise<ServerBundleOptions>,
customCollections: CustomCollection[] = [],
): Promise<ResolvedServerBundleOptions> {
const resolved = await options

if (resolved.disabled) {
return {
disabled: true,
remote: false,
collections: [],
}
}

if (!resolved.collections)
resolved.collections = resolved.remote
? collectionNames
: await discoverInstalledCollections()

return {
disabled: false,
remote: resolved.remote === true
? 'jsdelivr' // Default remote source
: resolved.remote || false,

collections: await Promise.all(([
...(resolved.collections || []),
...customCollections,
])
.map(c => resolveCollection(nuxt, c))),
}
}
import type { NuxtIconRuntimeOptions } from './types'
import { isFullCollectionExists } from './collections'
import type { NuxtIconModuleContext } from './context'

export function registerServerBundle(
options: ModuleOptions,
nuxt: Nuxt,
serverBundle: ModuleOptions['serverBundle'],
ctx: NuxtIconModuleContext,
): void {
// Bundle icons for server
const bundle = resolveServerBundle(
const {
nuxt,
(!serverBundle || options.provider !== 'server')
? { disabled: true }
: typeof serverBundle === 'string'
? { remote: serverBundle === 'remote' }
: serverBundle,
options.customCollections,
)
} = ctx

// Bundle icons for server
const bundle = ctx.resolveServerBundle()

const templateServer = addTemplate({
filename: 'nuxt-icon-server-bundle.mjs',
Expand Down
104 changes: 104 additions & 0 deletions src/context.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
import type { Nuxt } from 'nuxt/schema'
import type { IconifyJSON } from '@iconify/types'
import collectionNames from './collection-names'
import type { ModuleOptions, NuxtIconRuntimeOptions, ResolvedServerBundleOptions } from './types'
import { discoverInstalledCollections, loadCustomCollection, resolveCollection } from './collections'

const KEYWORDS_EDGE_TARGETS: string[] = [
'edge',
'cloudflare',
'worker',
]

export class NuxtIconModuleContext {
public serverBundle: Exclude<ModuleOptions['serverBundle'], 'auto'>

constructor(
public readonly nuxt: Nuxt,
public readonly options: ModuleOptions,
) {
if (options.serverBundle === 'auto') {
this.serverBundle = nuxt.options.dev
? 'local'
: KEYWORDS_EDGE_TARGETS.some(word =>
(typeof nuxt.options.nitro.preset === 'string' && nuxt.options.nitro.preset.includes(word))
|| process.env.NITRO_PRESET?.includes(word)
|| process.env.SERVER_PRESET?.includes(word),
)
? 'remote'
: 'local'
}
else {
this.serverBundle = options.serverBundle
}
}

getRuntimeCollections(runtimeOptions: NuxtIconRuntimeOptions): string[] {
return runtimeOptions.fallbackToApi
? collectionNames
: typeof this.serverBundle === 'string'
? collectionNames
: this.serverBundle
? this.serverBundle.collections
?.map(c => typeof c === 'string' ? c : c.prefix) || []
: []
}

async resolveServerBundle(): Promise<ResolvedServerBundleOptions> {
const resolved = (!this.serverBundle || this.options.provider !== 'server')
? { disabled: true }
: typeof this.serverBundle === 'string'
? { remote: this.serverBundle === 'remote' }
: this.serverBundle

if (resolved.disabled) {
return {
disabled: true,
remote: false,
collections: [],
}
}

if (!resolved.collections)
resolved.collections = resolved.remote
? collectionNames
: await discoverInstalledCollections()

const collections = await Promise.all(
(resolved.collections || [])
.map(c => resolveCollection(this.nuxt, c)),
)

return {
disabled: false,
remote: resolved.remote === true
? 'jsdelivr' // Default remote source
: resolved.remote || false,

collections: [
...collections,
...await this.loadCustomCollection(),
],
}
}

private _customCollections: IconifyJSON[] | Promise<IconifyJSON[]> | undefined

async loadCustomCollection(force = false): Promise<IconifyJSON[]> {
if (force) {
this._customCollections = undefined
}
if (!this._customCollections) {
this._customCollections = Promise.all(
(this.options.customCollections || [])
.map(collection => loadCustomCollection(collection, this.nuxt)),
)
.then((collections) => {
if (this._customCollections)
this._customCollections = collections
return collections
})
}
return this._customCollections
}
}
41 changes: 10 additions & 31 deletions src/module.ts
Original file line number Diff line number Diff line change
@@ -1,20 +1,15 @@
import { defineNuxtModule, addPlugin, addServerHandler, hasNuxtModule, createResolver, addComponent, logger } from '@nuxt/kit'
import { addCustomTab } from '@nuxt/devtools-kit'
import collectionNames from './collection-names'
import { resolvePath } from 'mlly'
import { schema } from './schema'
import type { ModuleOptions } from './types'
import type { ModuleOptions, NuxtIconRuntimeOptions } from './types'
import { unocssIntegration } from './integrations/unocss'
import { registerServerBundle } from './bundle-server'
import { registerClientBundle } from './bundle-client'
import { NuxtIconModuleContext } from './context'

export type { ModuleOptions }

const KEYWORDS_EDGE_TARGETS: string[] = [
'edge',
'cloudflare',
'worker',
]

export default defineNuxtModule<ModuleOptions>({
meta: {
name: '@nuxt/icon',
Expand Down Expand Up @@ -58,19 +53,9 @@ export default defineNuxtModule<ModuleOptions>({
: 'server'
}

let serverBundle = options.serverBundle
if (serverBundle === 'auto') {
serverBundle = nuxt.options.dev
? 'local'
: KEYWORDS_EDGE_TARGETS.some(word =>
(typeof nuxt.options.nitro.preset === 'string' && nuxt.options.nitro.preset.includes(word))
|| process.env.NITRO_PRESET?.includes(word)
|| process.env.SERVER_PRESET?.includes(word),
)
? 'remote'
: 'local'
logger.info(`Nuxt Icon server bundle mode is set to \`${serverBundle}\``)
}
const ctx = new NuxtIconModuleContext(nuxt, options)
if (options.serverBundle === 'auto')
logger.info(`Nuxt Icon server bundle mode is set to \`${ctx.serverBundle}\``)

addPlugin(
resolver.resolve('./runtime/plugin'),
Expand All @@ -89,15 +74,9 @@ export default defineNuxtModule<ModuleOptions>({
const runtimeOptions = Object.fromEntries(
Object.entries(options)
.filter(([key]) => key in schema),
)
) as NuxtIconRuntimeOptions
if (!runtimeOptions.collections) {
runtimeOptions.collections = runtimeOptions.fallbackToApi
? collectionNames
: typeof serverBundle === 'string'
? collectionNames
: serverBundle
? serverBundle.collections
: []
runtimeOptions.collections = ctx.getRuntimeCollections(runtimeOptions)
}
nuxt.options.appConfig.icon = Object.assign(
nuxt.options.appConfig.icon || {},
Expand All @@ -113,8 +92,8 @@ export default defineNuxtModule<ModuleOptions>({
})
})

registerServerBundle(options, nuxt, serverBundle)
registerClientBundle(options, nuxt)
registerServerBundle(ctx)
registerClientBundle(ctx)

// Devtools
addCustomTab({
Expand Down

0 comments on commit 27f92ad

Please sign in to comment.