diff --git a/docs/nuxt.config.ts b/docs/nuxt.config.ts index ee54ece4..cf62e6d7 100644 --- a/docs/nuxt.config.ts +++ b/docs/nuxt.config.ts @@ -1,7 +1,7 @@ import { createResolver } from '@nuxt/kit' import colors from 'tailwindcss/colors' import module from '../src/module' -import { excludeColors } from '../src/colors' +import { excludeColors } from '../src/runtime/utils/colors' import pkg from '../package.json' const { resolve } = createResolver(import.meta.url) diff --git a/src/module.ts b/src/module.ts index 097b8399..1d17c62c 100644 --- a/src/module.ts +++ b/src/module.ts @@ -1,14 +1,11 @@ import { defineNuxtModule, installModule, addComponentsDir, addImportsDir, createResolver, addPlugin } from '@nuxt/kit' import defaultColors from 'tailwindcss/colors.js' -import { defaultExtractor as createDefaultExtractor } from 'tailwindcss/lib/lib/defaultExtractor.js' -import { iconsPlugin, getIconCollections, type CollectionNames, type IconsPluginOptions } from '@egoist/tailwindcss-icons' +import type { CollectionNames, IconsPluginOptions } from '@egoist/tailwindcss-icons' import { name, version } from '../package.json' -import { generateSafelist, excludeColors, customSafelistExtractor } from './colors' import createTemplates from './templates' import * as config from './runtime/ui.config' import type { DeepPartial, Strategy } from './runtime/types/utils' - -const defaultExtractor = createDefaultExtractor({ tailwindConfig: { separator: ':' } }) +import installTailwind from './tailwind' // @ts-ignore delete defaultColors.lightBlue @@ -91,109 +88,13 @@ export default defineNuxtModule({ nuxt.options.css.push(resolve(runtimeDir, 'ui.css')) } - // @ts-ignore - nuxt.hook('tailwindcss:config', function (tailwindConfig) { - tailwindConfig.theme = tailwindConfig.theme || {} - tailwindConfig.theme.extend = tailwindConfig.theme.extend || {} - tailwindConfig.theme.extend.colors = tailwindConfig.theme.extend.colors || {} - - const globalColors: any = { - ...(tailwindConfig.theme.colors || defaultColors), - ...tailwindConfig.theme.extend?.colors - } - - // @ts-ignore - 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) / )', - DEFAULT: 'rgb(var(--color-primary-DEFAULT) / )' - } - - if (globalColors.gray) { - // @ts-ignore - globalColors.cool = tailwindConfig.theme.extend.colors.cool = defaultColors.gray - } - - // @ts-ignore - 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 colors = excludeColors(globalColors) - - // @ts-ignore - nuxt.options.appConfig.ui = { - primary: 'green', - gray: 'cool', - colors, - strategy: 'merge' - } - - tailwindConfig.safelist = tailwindConfig.safelist || [] - tailwindConfig.safelist.push(...generateSafelist(options.safelistColors || [], colors)) - - tailwindConfig.plugins = tailwindConfig.plugins || [] - tailwindConfig.plugins.push(iconsPlugin(Array.isArray(options.icons) || options.icons === 'all' ? { collections: getIconCollections(options.icons) } : typeof options.icons === 'object' ? options.icons as IconsPluginOptions : {})) - }) - createTemplates(nuxt) // Modules await installModule('nuxt-icon') await installModule('@nuxtjs/color-mode', { classSuffix: '' }) - await installModule('@nuxtjs/tailwindcss', { - exposeConfig: true, - config: { - darkMode: 'class', - plugins: [ - require('@tailwindcss/forms')({ strategy: 'class' }), - require('@tailwindcss/aspect-ratio'), - require('@tailwindcss/typography'), - require('@tailwindcss/container-queries'), - require('@headlessui/tailwindcss') - ], - content: { - files: [ - resolve(runtimeDir, 'components/**/*.{vue,mjs,ts}'), - resolve(runtimeDir, 'ui.config/**/*.{mjs,js,ts}') - ], - transform: { - vue: (content) => { - return content.replaceAll(/(?:\r\n|\r|\n)/g, ' ') - } - }, - extract: { - vue: (content) => { - return [ - ...defaultExtractor(content), - // @ts-ignore - ...customSafelistExtractor(options.prefix, content, nuxt.options.appConfig.ui.colors, options.safelistColors) - ] - } - } - } - } - }) + await installTailwind(options, nuxt) // Plugins diff --git a/src/colors.ts b/src/runtime/utils/colors.ts similarity index 99% rename from src/colors.ts rename to src/runtime/utils/colors.ts index a0455fc9..e69f3b47 100644 --- a/src/colors.ts +++ b/src/runtime/utils/colors.ts @@ -1,4 +1,4 @@ -import { omit } from './runtime/utils/lodash' +import { omit } from './lodash' import { kebabCase, camelCase, upperFirst } from 'scule' const colorsToExclude = [ diff --git a/src/tailwind.ts b/src/tailwind.ts new file mode 100644 index 00000000..18867c4d --- /dev/null +++ b/src/tailwind.ts @@ -0,0 +1,133 @@ +import { join } from 'pathe' +import { addTemplate, createResolver, installModule, useNuxt } from '@nuxt/kit' +import defaultColors from 'tailwindcss/colors.js' + +import { excludeColors, generateSafelist } from './runtime/utils/colors' +import type { ModuleOptions } from './module' + +export default async function installTailwind ( + moduleOptions: ModuleOptions, + nuxt = useNuxt() +) { + const { resolve } = createResolver(import.meta.url) + const runtimeDir = resolve('./runtime') + + // 1. register hook + // @ts-ignore + nuxt.hook('tailwindcss:config', function (tailwindConfig) { + tailwindConfig.theme = tailwindConfig.theme || {} + tailwindConfig.theme.extend = tailwindConfig.theme.extend || {} + tailwindConfig.theme.extend.colors = + tailwindConfig.theme.extend.colors || {} + + const globalColors: any = { + ...(tailwindConfig.theme.colors || defaultColors), + ...tailwindConfig.theme.extend?.colors + } + + // @ts-ignore + 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) / )', + DEFAULT: 'rgb(var(--color-primary-DEFAULT) / )' + } + + if (globalColors.gray) { + // @ts-ignore + globalColors.cool = tailwindConfig.theme.extend.colors.cool = + defaultColors.gray + } + + // @ts-ignore + 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 colors = excludeColors(globalColors) + + // @ts-ignore + nuxt.options.appConfig.ui = { + primary: 'green', + gray: 'cool', + colors, + strategy: 'merge' + } + + tailwindConfig.safelist = tailwindConfig.safelist || [] + tailwindConfig.safelist.push( + ...generateSafelist(moduleOptions.safelistColors || [], colors) + ) + }) + + // 2. add config template + const configTemplate = addTemplate({ + filename: 'nuxtui-tailwind.config.cjs', + write: true, + getContents: () => ` + const { defaultExtractor: createDefaultExtractor } = require('tailwindcss/lib/lib/defaultExtractor.js') + const { customSafelistExtractor } = require(${JSON.stringify(resolve(runtimeDir, 'utils', 'colors'))}) + const { iconsPlugin, getIconCollections } = require('@egoist/tailwindcss-icons') + + const defaultExtractor = createDefaultExtractor({ tailwindConfig: { separator: ':' } }) + + module.exports = { + darkMode: 'class', + plugins: [ + require('@tailwindcss/forms')({ strategy: 'class' }), + require('@tailwindcss/aspect-ratio'), + require('@tailwindcss/typography'), + require('@tailwindcss/container-queries'), + require('@headlessui/tailwindcss'), + iconsPlugin(${Array.isArray(moduleOptions.icons) || moduleOptions.icons === 'all' ? `{ collections: getIconCollections(${JSON.stringify(moduleOptions.icons)}) }` : typeof moduleOptions.icons === 'object' ? JSON.stringify(moduleOptions.icons) : {}}) + ], + content: { + files: [ + ${JSON.stringify(resolve(runtimeDir, 'components/**/*.{vue,mjs,ts}'))}, + ${JSON.stringify(resolve(runtimeDir, 'ui.config/**/*.{mjs,js,ts}'))} + ], + transform: { + vue: (content) => { + return content.replaceAll(/(?:\\r\\n|\\r|\\n)/g, ' ') + } + }, + extract: { + vue: (content) => { + return [ + ...defaultExtractor(content), + ...customSafelistExtractor(${JSON.stringify(moduleOptions.prefix)}, content, ${JSON.stringify(nuxt.options.appConfig.ui.colors)}, ${JSON.stringify(moduleOptions.safelistColors)}) + ] + } + } + } + } + ` + }) + + // 3. install module + await installModule('@nuxtjs/tailwindcss', { + exposeConfig: true, + configPath: [ + configTemplate.dst, + join(nuxt.options.rootDir, 'tailwind.config') + ] + }) +}