diff --git a/docs/content/1.getting-started/1.setup.md b/docs/content/1.getting-started/1.installation.md similarity index 97% rename from docs/content/1.getting-started/1.setup.md rename to docs/content/1.getting-started/1.installation.md index 463d9378..70fa20cd 100644 --- a/docs/content/1.getting-started/1.setup.md +++ b/docs/content/1.getting-started/1.installation.md @@ -1,5 +1,5 @@ --- -title: Setup +title: Installation description: Using Tailwind CSS in your Nuxt project is only one command away. --- @@ -106,7 +106,7 @@ npx tailwindcss init ``` ::callout{color="blue" icon="i-ph-info-duotone"} -You can configure the paths in the [module options](/getting-started/options). +You can configure the paths in the [module options](/getting-started/configuration). :: If you're going to create your own Tailwind CSS file, make sure to add the `@tailwind` directives for each of Tailwind’s layer types (base, components, and utilities). @@ -131,4 +131,4 @@ export default defineNuxtConfig({ }) ``` -See the [module options](/getting-started/options). +See the [module options](/getting-started/configuration). diff --git a/docs/content/1.getting-started/2.options.md b/docs/content/1.getting-started/2.configuration.md similarity index 88% rename from docs/content/1.getting-started/2.options.md rename to docs/content/1.getting-started/2.configuration.md index dd066b6f..53dd673e 100644 --- a/docs/content/1.getting-started/2.options.md +++ b/docs/content/1.getting-started/2.configuration.md @@ -1,5 +1,5 @@ --- -title: Options +title: Configuration description: Configure Nuxt Tailwind with the `tailwindcss` property. --- @@ -163,25 +163,17 @@ export default defineNuxtConfig({ You can edit the endpoint by `viewer.endpoint` and if you'd like to export the viewer as a static asset during build, you can set `viewer.exportViewer` to `true` (it will internally run [`npx tailwind-config-viewer export`](https://github.com/rogden/tailwind-config-viewer/blob/master/cli/export.js)). -## `addTwUtil ` +## `editorSupport` - Default: `false` -This option adds the utility function `tw` that will provide IntelliSense suggestions in JS/TS strings if the [Tailwind CSS IntelliSense](https://marketplace.visualstudio.com/items?itemName=bradlc.vscode-tailwindcss) extension is installed. For this to work, you will have to add the following VSCode setting: +You can take advantage of some DX utilities this modules provide to you as you develop your Nuxt applicatio with Tailwind. Read more in [Editor Support](/tailwind/editor-support). -```json [settings.json] -{ - "tailwindCSS.experimental.classRegex": ["tw`([^`]*)", "tw\\('([^'\\)]*)"], -} -``` - -Once added, the new utility function can be used as follows, providing IntelliSense suggestions when writing Tailwind classes: - -```vue [index.vue] - +```ts [nuxt.config.ts] +export default defineNuxtConfig({ + tailwindcss: { + editorSupport: true + // editorSupport: { autocompleteUtil: { as: 'tailwindClasses' }, generateConfig: true } + } +}) ``` diff --git a/docs/content/2.tailwind/1.config.md b/docs/content/2.tailwind/1.config.md index 35e16a2f..4e98850e 100644 --- a/docs/content/2.tailwind/1.config.md +++ b/docs/content/2.tailwind/1.config.md @@ -126,7 +126,7 @@ This is an advanced usage section and intended primarily for Nuxt modules author #### `tailwindcss:loadConfig` -Passes any Tailwind configuration read by the module for each (extended) [layer](https://nuxt.com/docs/getting-started/layers)/[path](/getting-started/options#configpath) before merging all of them. +Passes any Tailwind configuration read by the module for each (extended) [layer](https://nuxt.com/docs/getting-started/layers)/[path](/getting-started/configuration#configpath) before merging all of them. #### `tailwindcss:config` @@ -141,26 +141,17 @@ Passes the _complete_ resolved configuration with all defaults from [the full Ta You can use a [Nuxt hook](https://nuxtjs.org/guides/directory-structure/modules#run-tasks-on-specific-hooks) to manipulate the Tailwind configuration. ```ts -// ~/modules/nuxt-tailwind-typo/index.ts +// ~/modules/nuxt-tailwind-mod/index.ts import { defineNuxtModule, addTemplate } from '@nuxt/kit' -import tailwindTypography from '@tailwindcss/typography' export default defineNuxtModule({ - meta: { - name: 'nuxt-tailwind-typo' - }, setup (options, nuxt) { nuxt.hook('tailwindcss:config', function (tailwindConfig) { - tailwindConfig.plugins.push(tailwindTypography) + tailwindConfig.theme.colors.blue = '#fff' }) nuxt.hook('tailwindcss:resolvedConfig', function (resolvedConfig) { - // read: https://tailwindcss.nuxtjs.org/tailwind/editor-support - addTemplate({ - filename: 'tailwind.config.cjs', - getContents: () => `module.exports = ${JSON.stringify(resolvedConfig)}`, - write: true - }) + console.log('This is the resulting config', JSON.stringify(resolvedConfig)) }) } }) @@ -259,7 +250,7 @@ module.exports = { It can often be useful to reference Tailwind configuration values at runtime, e.g. to access some of your theme values when dynamically applying inline styles in a component. -If you need resolved Tailwind config at runtime, you can enable the [exposeConfig](/getting-started/options#exposeconfig) option: +If you need resolved Tailwind config at runtime, you can enable the [exposeConfig](/getting-started/configuration#exposeconfig) option: ```js{}[nuxt.config] export default { diff --git a/docs/content/2.tailwind/2.viewer.md b/docs/content/2.tailwind/2.viewer.md index fbfa2793..8138a918 100644 --- a/docs/content/2.tailwind/2.viewer.md +++ b/docs/content/2.tailwind/2.viewer.md @@ -17,4 +17,4 @@ When enabled, you will see the Tailwind viewer url in your terminal: -Check out the [viewer option](/getting-started/options#viewer) to disable the viewer in development. +Check out the [viewer option](/getting-started/configuration#viewer) to disable the viewer in development. diff --git a/docs/content/2.tailwind/3.editor-support.md b/docs/content/2.tailwind/3.editor-support.md index b9309a3d..e7e79bbb 100644 --- a/docs/content/2.tailwind/3.editor-support.md +++ b/docs/content/2.tailwind/3.editor-support.md @@ -20,7 +20,33 @@ Add the following configuration to your `.vscode/settings.json` file, so that Ta If you use pnpm, ensure that tailwindcss is installed in your top-level node_modules folder. -Since Tailwind CSS v3.3, [ESM/TS configuration has been supported](https://tailwindcss.com/blog/tailwindcss-v3-3#esm-and-type-script-support) so your editor should automatically configure autocomplete based on your `tailwind.config`. If you happen to use a lower version and/or require the configuration in CommonJS, you can use the `tailwindcss:resolvedConfig` hook and a custom Nuxt module: +## Autocomplete + +When using strings of Tailwind classes, you can enable IntelliSense suggestions using the [`editorSupport.autocompleteUtil`](/getting-started/configuration#editorsupport) option. You will have to add the following VSCode setting: + +```diff [.vscode/settings.json] +// ... ++ "tailwindCSS.experimental.classRegex": ["tw`(.*?)`", "tw\\('(.*?)'\\)"], +"files.associations": { + "*.css": "tailwindcss" +}, +// ... +``` + +Once added, the new utility function can be used as follows, providing IntelliSense suggestions when writing Tailwind classes: + +```vue [index.vue] + +``` + +## Load Config File + +Since Tailwind CSS v3.3, [ESM/TS configuration has been supported](https://tailwindcss.com/blog/tailwindcss-v3-3#esm-and-type-script-support) so your editor should automatically configure autocomplete based on your `tailwind.config`. If you happen to use a lower version and/or require to generate a flat configuration, you can do so using [`editorSupport.generateConfig`](/getting-started/configuration#editorsupport) option, or you can use the `tailwindcss:resolvedConfig` hook and a custom Nuxt module: ```ts [modules/tw-cjs-config.ts] import { defineNuxtModule, addTemplate } from '@nuxt/kit' @@ -38,6 +64,8 @@ export default defineNuxtModule({ }) ``` +This hook allows you to customize your generated template in different ways (e.g., different filename, contents, etc.) through a module. Please be aware that using `JSON.stringify` will remove plugins from your configuration. + ```diff [.vscode/settings.json] // ... + "tailwindCSS.experimental.configFile": ".nuxt/tailwind.config.cjs", @@ -46,5 +74,3 @@ export default defineNuxtModule({ }, // ... ``` - -This hook allows you to customize your generated template in different ways (e.g., different filename, contents, etc.) through a module. Please be aware that using `JSON.stringify` will remove plugins from your configuration. diff --git a/playground/nuxt.config.ts b/playground/nuxt.config.ts index ff805a9b..fda428bf 100644 --- a/playground/nuxt.config.ts +++ b/playground/nuxt.config.ts @@ -8,7 +8,8 @@ export default defineNuxtConfig({ tailwindcss: { // viewer: false, exposeConfig: true, - injectPosition: 'last' + injectPosition: 'last', + editorSupport: true }, content: { documentDriven: true diff --git a/playground/pages/index.vue b/playground/pages/index.vue index c7d44909..5e85022d 100644 --- a/playground/pages/index.vue +++ b/playground/pages/index.vue @@ -3,7 +3,7 @@
-
+
This is a HMR test, try changing the color: meow! @@ -32,5 +32,8 @@ diff --git a/src/module.ts b/src/module.ts index b096e725..f2b87e86 100644 --- a/src/module.ts +++ b/src/module.ts @@ -11,7 +11,8 @@ import { useNuxt, addTemplate, addImportsDir, - createResolver + createResolver, + addImports } from '@nuxt/kit' // @ts-expect-error @@ -25,7 +26,8 @@ import { resolveCSSPath, resolveInjectPosition, resolveExposeConfig, - resolveViewerConfig + resolveViewerConfig, + resolveEditorSupportConfig } from './resolvers' import logger, { LogLevels } from './logger' import createTemplates from './templates' @@ -48,6 +50,7 @@ const defaults = (nuxt = useNuxt()): ModuleOptions => ({ disableHmrHotfix: false, quiet: nuxt.options.logLevel === 'silent', addTwUtil: false, + editorSupport: false, }) export default defineNuxtModule({ @@ -90,7 +93,6 @@ export default defineNuxtModule({ if (moduleOptions.exposeConfig) { const exposeConfig = resolveExposeConfig({ level: moduleOptions.exposeLevel, ...(typeof moduleOptions.exposeConfig === 'object' ? moduleOptions.exposeConfig : {})}) createTemplates(resolvedConfig, exposeConfig, nuxt) - isNuxt2() && addTemplate({ filename: 'tailwind.config.cjs', getContents: () => `module.exports = ${JSON.stringify(resolvedConfig, null, 2)}` }) } // Compute tailwindConfig hash @@ -139,8 +141,25 @@ export default defineNuxtModule({ }) } - if (moduleOptions.addTwUtil) { - addImportsDir(resolve('./runtime/utils')) + if (moduleOptions.editorSupport || moduleOptions.addTwUtil || isNuxt2()) { + const editorSupportConfig = resolveEditorSupportConfig(moduleOptions.editorSupport) + + if (editorSupportConfig.autocompleteUtil || moduleOptions.addTwUtil) { + addImports({ + name: 'autocompleteUtil', + from: resolve('./runtime/utils'), + as: 'tw', + ...(typeof editorSupportConfig.autocompleteUtil === 'object' ? editorSupportConfig.autocompleteUtil : {}) + }) + } + + if (editorSupportConfig.generateConfig || isNuxt2()) { + addTemplate({ + filename: 'tailwind.config.cjs', + getContents: () => `module.exports = ${JSON.stringify(resolvedConfig, null, 2)}`, + ...(typeof editorSupportConfig.generateConfig === 'object' ? editorSupportConfig.generateConfig : {}) + }) + } } // enabled only in development diff --git a/src/resolvers.ts b/src/resolvers.ts index e6807876..522ce9a1 100644 --- a/src/resolvers.ts +++ b/src/resolvers.ts @@ -2,7 +2,7 @@ import { existsSync } from 'fs' import { defu } from 'defu' import { join, relative, resolve } from 'pathe' import { addTemplate, createResolver, findPath, useNuxt, tryResolveModule, resolveAlias } from '@nuxt/kit' -import type { Arrayable, ExposeConfig, InjectPosition, ModuleOptions, ViewerConfig } from './types' +import type { Arrayable, EditorSupportConfig, ExposeConfig, InjectPosition, ModuleOptions, ViewerConfig } from './types' /** * Resolves all configPath values for an application @@ -124,6 +124,7 @@ export async function resolveCSSPath (cssPath: ModuleOptions['cssPath'], nuxt = const resolveBoolObj = >(config: T, fb: U): U => defu(typeof config === 'object' ? config : {}, fb) export const resolveViewerConfig = (config: ModuleOptions['viewer']): ViewerConfig => resolveBoolObj(config, { endpoint: '/_tailwind', exportViewer: false }) export const resolveExposeConfig = (config: ModuleOptions['exposeConfig']): ExposeConfig => resolveBoolObj(config, { alias: '#tailwind-config', level: 2 }) +export const resolveEditorSupportConfig = (config: ModuleOptions['editorSupport']): EditorSupportConfig => resolveBoolObj(config, { autocompleteUtil: true, generateConfig: false }) /** * Resolve human-readable inject position specification into absolute index in the array diff --git a/src/runtime/utils.ts b/src/runtime/utils.ts new file mode 100644 index 00000000..55cdbc67 --- /dev/null +++ b/src/runtime/utils.ts @@ -0,0 +1 @@ +export const autocompleteUtil = (tailwindClasses: T) => tailwindClasses diff --git a/src/runtime/utils/tw.ts b/src/runtime/utils/tw.ts deleted file mode 100644 index 361fa9c8..00000000 --- a/src/runtime/utils/tw.ts +++ /dev/null @@ -1,3 +0,0 @@ -export const tw = function (tailwindClasses: TemplateStringsArray | string) { - return tailwindClasses -} diff --git a/src/types.ts b/src/types.ts index fb37646e..afcf5c1d 100644 --- a/src/types.ts +++ b/src/types.ts @@ -1,13 +1,15 @@ +type Import = Exclude[0], any[]> + export type TWConfig = import('tailwindcss').Config; -export type ResolvedTwConfig = ReturnType +export type ResolvedTwConfig = ReturnType; export type Arrayable = T | Array; export type InjectPosition = 'first' | 'last' | number | { after: string }; interface ExtendTailwindConfig { content?: - | TWConfig['content'] - | ((contentDefaults: Array) => TWConfig['content']); -} + | TWConfig['content'] + | ((contentDefaults: Array) => TWConfig['content']); +}; type BoolObj> = boolean | Partial; @@ -17,7 +19,7 @@ export type ViewerConfig = { * * @default '/_tailwind' */ - endpoint: `/${string}` + endpoint: `/${string}`; /** * Export the viewer during build * @@ -43,6 +45,41 @@ export type ExposeConfig = { level: number }; +export type EditorSupportConfig = { + /** + * Enable utility to write Tailwind CSS classes inside strings. + * + * You will need to update `.vscode/settings.json` based on this value. + * + * ```json + * { + * "tailwindCSS.experimental.classRegex": ["tw`(.*?)`", "tw\\('(.*?)'\\)"] + * } + * ``` + * + * Read https://tailwindcss.nuxtjs.org/tailwind/editor-support#autocomplete. + * + * @default false // if true, { as: 'tw' } + */ + autocompleteUtil: BoolObj>; + /** + * Create a flat configuration template for Intellisense plugin. + * + * You will need to update `.vscode/settings.json` based on this value. + * + * ```json + * { + * "tailwindCSS.experimental.configFile": ".nuxt/tailwind.config.cjs" + * } + * ``` + * + * Read https://tailwindcss.nuxtjs.org/tailwind/editor-support#load-config-file. + * + * @default false // if true, { filename: 'tailwind.config.cjs', write: true } + */ + generateConfig: BoolObj>; +}; + export interface ModuleOptions { /** * The path of the Tailwind configuration file. The extension can be omitted, in which case it will try to find a `.js`, `.cjs`, `.mjs`, or `.ts` file. @@ -101,9 +138,18 @@ export interface ModuleOptions { * Add util to write Tailwind CSS classes inside strings with `` tw`{classes}` `` * * @default false + * @deprecated use `editorSupport.autocompleteUtil` as object */ addTwUtil: boolean; -} + /** + * Enable some utilities for better editor support and DX. + * + * Read https://tailwindcss.nuxtjs.org/tailwind/editor-support. + * + * @default false // if true, { autocompleteUtil: true } + */ + editorSupport: BoolObj; +}; export interface ModuleHooks { /**