Skip to content

Commit

Permalink
feat: introduce clientBundle.includeCustomCollections option, #34 (#…
Browse files Browse the repository at this point in the history
  • Loading branch information
antfu authored Jul 29, 2024
1 parent 313a5f2 commit 5b373e4
Show file tree
Hide file tree
Showing 11 changed files with 297 additions and 250 deletions.
4 changes: 2 additions & 2 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -42,8 +42,8 @@ jobs:
- name: Lint
run: npm run lint

- name: Test types
run: npm run test:types
- name: Typecheck
run: npm run typecheck

- name: Prepack
run: npm run prepack
8 changes: 7 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -283,16 +283,22 @@ export default defineNuxtConfig({
],
icon: {
clientBundle: {
// list of icons to include in the client bundle
icons: [
'uil:github',
'logos:vitejs'
],

// include all custom collections in the client bundle
includeCustomCollections: true,
},
},
})
```

Currently, this is a manual config process, we are working on making it more automatic and easier to use.
`includeCustomCollections` will include all the custom collections you have defined in `icon.customCollections` in the client bundle. It's disabled by default but will automatically enable when `ssr: false` is set.

Currently, `icons` list a manual config process, we are working on making it more automatic and easier to use.

### Render Function

Expand Down
2 changes: 1 addition & 1 deletion eslint.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,6 @@ export default createConfigForNuxt({
})
.prepend({
ignores: [
'src/collections.ts',
'src/collection-names.ts',
],
})
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@
"dev:build": "nuxi build playground",
"dev:prepare": "nuxt-module-build build --stub && nuxt-module-build prepare && nuxi prepare playground",
"lint": "eslint .",
"test:types": "vue-tsc --noEmit",
"typecheck": "vue-tsc --noEmit",
"prepublishOnly": "pnpm lint",
"release": "release-it"
},
Expand Down
2 changes: 1 addition & 1 deletion scripts/collections.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import fs from 'node:fs/promises'
import collections from '@iconify/collections/collections.json' with { type: 'json' }

await fs.writeFile(
'./src/collections.ts',
'./src/collection-names.ts',
`// GENERATED BY scripts/collections.ts\nexport default ${JSON.stringify(Object.keys(collections).sort(), null, 2)}`,
'utf-8',
)
26 changes: 24 additions & 2 deletions src/bundle-client.ts
Original file line number Diff line number Diff line change
@@ -1,16 +1,34 @@
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'

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

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

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

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

console.log({ icons })

if (!icons.length)
return 'export function init() {}'
Expand Down Expand Up @@ -42,6 +60,10 @@ export function registerClientBundle(options: ModuleOptions) {
}
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)})`
})),
' _initialized = true',
'}',
)
Expand Down
69 changes: 4 additions & 65 deletions src/bundle-server.ts
Original file line number Diff line number Diff line change
@@ -1,15 +1,8 @@
import { basename, join, isAbsolute } from 'node:path'
import fs from 'node:fs/promises'
import { addTemplate, logger } from '@nuxt/kit'
import type { Nuxt } from '@nuxt/schema'
import fg from 'fast-glob'
import type { IconifyJSON } from '@iconify/types'
import { isPackageExists } from 'local-pkg'
import { parseSVGContent, convertParsedSVG } from '@iconify/utils/lib/svg/parse'
import collectionNames from './collections'
import type { ModuleOptions, ResolvedServerBundleOptions, CustomCollection, ServerBundleOptions, NuxtIconRuntimeOptions, RemoteCollection } from './types'

const isFullCollectionExists = isPackageExists('@iconify/json')
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,
Expand All @@ -32,7 +25,7 @@ async function resolveServerBundle(
if (!resolved.collections)
resolved.collections = resolved.remote
? collectionNames
: await discoverLocalCollections()
: await discoverInstalledCollections()

return {
disabled: false,
Expand All @@ -48,49 +41,6 @@ async function resolveServerBundle(
}
}

async function resolveCollection(
nuxt: Nuxt,
collection: string | IconifyJSON | CustomCollection | RemoteCollection,
): Promise<string | IconifyJSON | RemoteCollection> {
if (typeof collection === 'string')
return collection
// Custom collection
if ('dir' in collection) {
const dir = isAbsolute(collection.dir) ? collection.dir : join(nuxt.options.rootDir, collection.dir)
const files = (await fg('*.svg', { cwd: dir, onlyFiles: true }))
.sort()

const parsedIcons = await Promise.all(files.map(async (file) => {
const name = basename(file, '.svg')
let svg = await fs.readFile(join(dir, file), 'utf-8')
const cleanupIdx = svg.indexOf('<svg')
if (cleanupIdx > 0)
svg = svg.slice(cleanupIdx)
const data = convertParsedSVG(parseSVGContent(svg)!)
if (!data) {
logger.error(`Nuxt Icon could not parse the SVG content for icon \`${name}\``)
return [name, {}]
}
if (data.top === 0)
delete data.top
if (data.left === 0)
delete data.left
return [name, data]
}))

const successfulIcons = parsedIcons.filter(([_, data]) => Object.keys(data).length > 0)
// @ts-expect-error remove extra properties
delete collection.dir

logger.success(`Nuxt Icon loaded local collection \`${collection.prefix}\` with ${successfulIcons.length} icons`)
return {
...collection,
icons: Object.fromEntries(successfulIcons),
}
}
return collection
}

export function registerServerBundle(
options: ModuleOptions,
nuxt: Nuxt,
Expand Down Expand Up @@ -196,14 +146,3 @@ export function registerServerBundle(
nuxt.options.nitro.alias ||= {}
nuxt.options.nitro.alias['#nuxt-icon-server-bundle'] = templateServer.dst
}

async function discoverLocalCollections(): Promise<ServerBundleOptions['collections']> {
const collections = isFullCollectionExists
? collectionNames
: collectionNames.filter(collection => isPackageExists('@iconify-json/' + collection))
if (isFullCollectionExists)
logger.success(`Nuxt Icon discovered local-installed ${collections.length} collections (@iconify/json)`)
else if (collections.length)
logger.success(`Nuxt Icon discovered local-installed ${collections.length} collections:`, collections.join(', '))
return collections
}
179 changes: 179 additions & 0 deletions src/collection-names.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,179 @@
// GENERATED BY scripts/collections.ts
export default [
"academicons",
"akar-icons",
"ant-design",
"arcticons",
"basil",
"bi",
"bitcoin-icons",
"bpmn",
"brandico",
"bx",
"bxl",
"bxs",
"bytesize",
"carbon",
"catppuccin",
"cbi",
"charm",
"ci",
"cib",
"cif",
"cil",
"circle-flags",
"circum",
"clarity",
"codicon",
"covid",
"cryptocurrency",
"cryptocurrency-color",
"dashicons",
"devicon",
"devicon-plain",
"ei",
"el",
"emojione",
"emojione-monotone",
"emojione-v1",
"entypo",
"entypo-social",
"eos-icons",
"ep",
"et",
"eva",
"f7",
"fa",
"fa-brands",
"fa-regular",
"fa-solid",
"fa6-brands",
"fa6-regular",
"fa6-solid",
"fad",
"fe",
"feather",
"file-icons",
"flag",
"flagpack",
"flat-color-icons",
"flat-ui",
"flowbite",
"fluent",
"fluent-emoji",
"fluent-emoji-flat",
"fluent-emoji-high-contrast",
"fluent-mdl2",
"fontelico",
"fontisto",
"formkit",
"foundation",
"fxemoji",
"gala",
"game-icons",
"geo",
"gg",
"gis",
"gravity-ui",
"gridicons",
"grommet-icons",
"guidance",
"healthicons",
"heroicons",
"heroicons-outline",
"heroicons-solid",
"hugeicons",
"humbleicons",
"ic",
"icomoon-free",
"icon-park",
"icon-park-outline",
"icon-park-solid",
"icon-park-twotone",
"iconamoon",
"iconoir",
"icons8",
"il",
"ion",
"iwwa",
"jam",
"la",
"lets-icons",
"line-md",
"logos",
"ls",
"lucide",
"mage",
"majesticons",
"maki",
"map",
"marketeq",
"material-symbols",
"material-symbols-light",
"mdi",
"mdi-light",
"medical-icon",
"memory",
"meteocons",
"mi",
"mingcute",
"mono-icons",
"mynaui",
"nimbus",
"nonicons",
"noto",
"noto-v1",
"octicon",
"oi",
"ooui",
"openmoji",
"oui",
"pajamas",
"pepicons",
"pepicons-pencil",
"pepicons-pop",
"pepicons-print",
"ph",
"pixelarticons",
"prime",
"ps",
"quill",
"radix-icons",
"raphael",
"ri",
"rivet-icons",
"si-glyph",
"simple-icons",
"simple-line-icons",
"skill-icons",
"solar",
"streamline",
"streamline-emojis",
"subway",
"svg-spinners",
"system-uicons",
"tabler",
"tdesign",
"teenyicons",
"token",
"token-branded",
"topcoat",
"twemoji",
"typcn",
"uil",
"uim",
"uis",
"uit",
"uiw",
"unjs",
"vaadin",
"vs",
"vscode-icons",
"websymbol",
"weui",
"whh",
"wi",
"wpf",
"zmdi",
"zondicons"
]
Loading

0 comments on commit 5b373e4

Please sign in to comment.