import { defineNuxtModule, installModule, addComponentsDir, addImportsDir, createResolver, addPlugin, resolvePath } from '@nuxt/kit' import colors from 'tailwindcss/colors.js' import { iconsPlugin, getIconCollections } from '@egoist/tailwindcss-icons' import { name, version } from '../package.json' import { colorsAsRegex, excludeColors } from './runtime/utils/colors' import appConfig from './runtime/app.config' type DeepPartial = Partial<{ [P in keyof T]: DeepPartial | { [key: string]: string } }> // @ts-ignore delete colors.lightBlue // @ts-ignore delete colors.warmGray // @ts-ignore delete colors.trueGray // @ts-ignore delete colors.coolGray // @ts-ignore delete colors.blueGray declare module 'nuxt/schema' { interface AppConfigInput { ui?: { primary?: string gray?: string colors?: string[] } & DeepPartial } } export interface ModuleOptions { /** * @default 'u' */ prefix?: string /** * @default false */ global?: boolean icons: string[] | string } export default defineNuxtModule({ meta: { name, version, configKey: 'ui', compatibility: { nuxt: '^3.0.0-rc.8' } }, defaults: { prefix: 'u', icons: ['heroicons'] }, async setup (options, nuxt) { const { resolve } = createResolver(import.meta.url) // Transpile runtime const runtimeDir = resolve('./runtime') nuxt.options.build.transpile.push(runtimeDir) nuxt.options.build.transpile.push('@popperjs/core', '@headlessui/vue') nuxt.options.css.push(resolve(runtimeDir, 'ui.css')) const appConfigFile = await resolvePath(resolve(runtimeDir, 'app.config')) nuxt.hook('app:resolve', (app) => { app.configs.push(appConfigFile) }) // @ts-ignore nuxt.hook('tailwindcss:config', function (tailwindConfig: TailwindConfig) { const globalColors = { ...(tailwindConfig.theme.colors || colors), ...tailwindConfig.theme.extend?.colors } tailwindConfig.theme.extend.colors = tailwindConfig.theme.extend.colors || {} globalColors.primary = tailwindConfig.theme.extend.colors.primary = { 50: 'rgb(var(--color-primary-50) / )', 100: 'rgb(var(--color-primary-100) / )', 200: 'rgb(var(--color-primary-200) / )', 300: 'rgb(var(--color-primary-300) / )', 400: 'rgb(var(--color-primary-400) / )', 500: 'rgb(var(--color-primary-500) / )', 600: 'rgb(var(--color-primary-600) / )', 700: 'rgb(var(--color-primary-700) / )', 800: 'rgb(var(--color-primary-800) / )', 900: 'rgb(var(--color-primary-900) / )', 950: 'rgb(var(--color-primary-950) / )' } if (globalColors.gray) { globalColors.cool = tailwindConfig.theme.extend.colors.cool = colors.gray } globalColors.gray = tailwindConfig.theme.extend.colors.gray = { 50: 'rgb(var(--color-gray-50) / )', 100: 'rgb(var(--color-gray-100) / )', 200: 'rgb(var(--color-gray-200) / )', 300: 'rgb(var(--color-gray-300) / )', 400: 'rgb(var(--color-gray-400) / )', 500: 'rgb(var(--color-gray-500) / )', 600: 'rgb(var(--color-gray-600) / )', 700: 'rgb(var(--color-gray-700) / )', 800: 'rgb(var(--color-gray-800) / )', 900: 'rgb(var(--color-gray-900) / )', 950: 'rgb(var(--color-gray-950) / )' } const variantColors = excludeColors(globalColors) const safeColorsAsRegex = colorsAsRegex(variantColors) nuxt.options.appConfig.ui = { ...nuxt.options.appConfig.ui, primary: 'green', gray: 'cool', colors: variantColors } tailwindConfig.safelist = tailwindConfig.safelist || [] tailwindConfig.safelist.push(...[ 'bg-gray-500', 'dark:bg-gray-400', { pattern: new RegExp(`bg-(${safeColorsAsRegex})-(50|400|500)`) }, { pattern: new RegExp(`bg-(${safeColorsAsRegex})-500`), variants: ['disabled'] }, { pattern: new RegExp(`bg-(${safeColorsAsRegex})-(400|950)`), variants: ['dark'] }, { pattern: new RegExp(`bg-(${safeColorsAsRegex})-(500|900|950)`), variants: ['dark:hover'] }, { pattern: new RegExp(`bg-(${safeColorsAsRegex})-400`), variants: ['dark:disabled'] }, { pattern: new RegExp(`bg-(${safeColorsAsRegex})-(50|100|600)`), variants: ['hover'] }, { pattern: new RegExp(`outline-(${safeColorsAsRegex})-500`), variants: ['focus-visible'] }, { pattern: new RegExp(`outline-(${safeColorsAsRegex})-400`), variants: ['dark:focus-visible'] }, { pattern: new RegExp(`ring-(${safeColorsAsRegex})-500`), variants: ['focus', 'focus-visible'] }, { pattern: new RegExp(`ring-(${safeColorsAsRegex})-400`), variants: ['dark', 'dark:focus', 'dark:focus-visible'] }, { pattern: new RegExp(`text-(${safeColorsAsRegex})-400`), variants: ['dark'] }, { pattern: new RegExp(`text-(${safeColorsAsRegex})-500`), variants: ['dark:hover'] }, { pattern: new RegExp(`text-(${safeColorsAsRegex})-600`), variants: ['hover'] } ]) tailwindConfig.plugins = tailwindConfig.plugins || [] tailwindConfig.plugins.push(iconsPlugin({ collections: getIconCollections(options.icons as any[]) })) }) await installModule('@nuxtjs/color-mode', { classSuffix: '' }) await installModule('@nuxtjs/tailwindcss', { viewer: false, exposeConfig: true, config: { darkMode: 'class', plugins: [ require('@tailwindcss/forms'), require('@tailwindcss/aspect-ratio'), require('@tailwindcss/typography') ], content: [ resolve(runtimeDir, 'components/**/*.{vue,mjs,ts}'), resolve(runtimeDir, '*.{mjs,js,ts}') ] } }) addPlugin({ src: resolve(runtimeDir, 'plugins', 'colors') }) addComponentsDir({ path: resolve(runtimeDir, 'components', 'elements'), prefix: options.prefix, global: options.global, watch: false }) addComponentsDir({ path: resolve(runtimeDir, 'components', 'forms'), prefix: options.prefix, global: options.global, watch: false }) addComponentsDir({ path: resolve(runtimeDir, 'components', 'data'), prefix: options.prefix, global: options.global, watch: false }) addComponentsDir({ path: resolve(runtimeDir, 'components', 'layout'), prefix: options.prefix, global: options.global, watch: false }) addComponentsDir({ path: resolve(runtimeDir, 'components', 'navigation'), prefix: options.prefix, global: options.global, watch: false }) addComponentsDir({ path: resolve(runtimeDir, 'components', 'overlays'), prefix: options.prefix, global: options.global, watch: false }) addImportsDir(resolve(runtimeDir, 'composables')) } })