Skip to content

Commit

Permalink
feat: first class hmr (#818)
Browse files Browse the repository at this point in the history
  • Loading branch information
ineshbose authored Apr 12, 2024
1 parent 4ecc5ad commit 81595fe
Show file tree
Hide file tree
Showing 24 changed files with 3,307 additions and 1,398 deletions.
4 changes: 2 additions & 2 deletions docs/content/1.getting-started/2.configuration.md
Original file line number Diff line number Diff line change
Expand Up @@ -167,13 +167,13 @@ You can edit the endpoint by `viewer.endpoint` and if you'd like to export the v

- Default: `false`

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).
You can take advantage of some DX utilities this modules provide to you as you develop your Nuxt application with Tailwind. Read more in [Editor Support](/tailwind/editor-support).

```ts [nuxt.config.ts]
export default defineNuxtConfig({
tailwindcss: {
editorSupport: true
// editorSupport: { autocompleteUtil: { as: 'tailwindClasses' }, generateConfig: true }
// editorSupport: { autocompleteUtil: { as: 'tailwindClasses' } }
}
})
```
33 changes: 19 additions & 14 deletions docs/content/2.tailwind/3.editor-support.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ 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.

## Autocomplete
## String Classes 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:

Expand All @@ -44,9 +44,20 @@ const variantClasses = {
</script>
```

## Load Config File
## Configuration IntelliSense

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:
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 have a complex Nuxt project with multiple Tailwind configurations that are within layers, passed from hooks or inline `nuxt.config` and want to use a merged configuration, the module generates it in `.nuxt/tailwind.config.cjs` that you can use by adding the following VSCode setting:

```diff [.vscode/settings.json]
// ...
+ "tailwindCSS.experimental.configFile": ".nuxt/tailwind.config.cjs",
"files.associations": {
"*.css": "tailwindcss"
},
// ...
```

If you require more customisation to what configuration the IntelliSense extension reads, you can take advantage of hooks, especially the `tailwindcss:resolvedConfig` hook that runs the configuration through [`tailwindcss/resolveConfig`](https://github.com/tailwindlabs/tailwindcss/blob/master/resolveConfig.js) to provide the complete config object.

```ts [modules/tw-cjs-config.ts]
import { defineNuxtModule, addTemplate } from '@nuxt/kit'
Expand All @@ -55,8 +66,11 @@ export default defineNuxtModule({
setup (options, nuxt) {
nuxt.hook('tailwindcss:resolvedConfig', (config) => {
addTemplate({
filename: 'tailwind.config.cjs', // gets prepended by .nuxt/
getContents: () => `module.exports = ${JSON.stringify(config)}`,
filename: 'intellisense-tw.cjs', // gets prepended by .nuxt/
getContents: () => `
/* my-comment */
module.exports = ${JSON.stringify(config)}
`,
write: true
})
})
Expand All @@ -65,12 +79,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 [.vscode/settings.json]
// ...
+ "tailwindCSS.experimental.configFile": ".nuxt/tailwind.config.cjs",
"files.associations": {
"*.css": "tailwindcss"
},
// ...
```
37 changes: 17 additions & 20 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -43,38 +43,35 @@
"test:types": "pnpm dev:prepare && tsc --noEmit && nuxi typecheck playground"
},
"dependencies": {
"@nuxt/kit": "^3.9.3",
"autoprefixer": "^10.4.17",
"chokidar": "^3.5.3",
"clear-module": "^4.1.2",
"@nuxt/kit": "^3.11.1",
"autoprefixer": "^10.4.19",
"consola": "^3.2.3",
"defu": "^6.1.4",
"h3": "^1.10.0",
"micromatch": "^4.0.5",
"h3": "^1.11.1",
"pathe": "^1.1.2",
"postcss": "^8.4.33",
"postcss-custom-properties": "^13.3.4",
"postcss-nesting": "^12.0.2",
"postcss-custom-properties": "^13.3.6",
"postcss-nesting": "^12.1.0",
"tailwind-config-viewer": "^1.7.3",
"tailwindcss": "~3.4.1",
"ufo": "^1.3.2"
"ufo": "^1.5.3",
"unctx": "^2.3.1"
},
"devDependencies": {
"@fontsource/inter": "^5.0.16",
"@nuxt/content": "^2.10.0",
"@nuxt/devtools": "^1.0.8",
"@fontsource/inter": "^5.0.17",
"@nuxt/content": "^2.12.1",
"@nuxt/devtools": "^1.1.5",
"@nuxt/eslint-config": "latest",
"@nuxt/module-builder": "^0.5.5",
"@nuxt/test-utils": "^3.10.0",
"@tailwindcss/typography": "^0.5.10",
"@types/micromatch": "^4.0.6",
"@nuxt/test-utils": "^3.12.0",
"@tailwindcss/typography": "^0.5.12",
"changelogen": "^0.5.5",
"destr": "^2.0.2",
"destr": "^2.0.3",
"eslint": "latest",
"happy-dom": "^13.1.4",
"nuxt": "^3.9.3",
"typescript": "^5.3.3",
"vitest": "1.1.0"
"nuxt": "^3.11.1",
"typescript": "^5.4.3",
"vitest": "1.4.0"
},
"packageManager": "pnpm@8.14.1",
"resolutions": {
Expand All @@ -83,4 +80,4 @@
"stackblitz": {
"startCommand": "pnpm dev:prepare && pnpm dev"
}
}
}
2 changes: 1 addition & 1 deletion playground/app.vue
Original file line number Diff line number Diff line change
Expand Up @@ -5,4 +5,4 @@
</template>

<script setup>
</script>
</script>
34 changes: 34 additions & 0 deletions playground/modules/resolved.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
import { defineNuxtModule, addTemplate, updateTemplates } from '@nuxt/kit'
import { resolve } from 'pathe'
import loadConfig from 'tailwindcss/loadConfig.js'
import resolveConfig from 'tailwindcss/resolveConfig.js'

export default defineNuxtModule({
setup (_options, nuxt) {
// logger.info('Creating test-tailwind.config.cjs...')
let counter = 1

nuxt.hook('tailwindcss:resolvedConfig', (config, oldConfig) =>
oldConfig
? setTimeout(() => updateTemplates({ filter: t => t.filename === 'resolved-config.cjs' }), 100)
: addTemplate({
filename: 'resolved-config.cjs',
getContents: () => `module.exports = /* ${counter++}, ${Boolean(oldConfig)} */ ${JSON.stringify(config, null, 2)}`,
write: true
})
)

// const template = addTemplate({
// filename: 'resolved-config.config.cjs', // gets prepended by .nuxt/
// getContents: ({ nuxt }) => {
// const config = loadConfig(resolve(nuxt.options.buildDir, 'tailwind.config.cjs'))
// return `module.exports = ${JSON.stringify(resolveConfig(config), null, 2)}`
// },
// write: true
// })

// nuxt.hook('app:templatesGenerated', (_app, templates) => {
// templates.map(t => t.dst).includes(resolve(nuxt.options.buildDir, 'tailwind.config.cjs')) && updateTemplates({ filter: t => t.dst === template.dst })
// })
}
})
27 changes: 27 additions & 0 deletions playground/modules/template.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import { defineNuxtModule, addTemplate } from '@nuxt/kit'

export default defineNuxtModule((_, nuxt) => {
const template = addTemplate({
filename: 'my-tw-config.cjs',
write: true,
getContents: () => `
const colors = require('tailwindcss/colors')
module.exports = {
theme: {
extend: {
colors: {
accent: colors.slate['500']
}
}
}
}
`
})

nuxt.options.tailwindcss = nuxt.options.tailwindcss ?? {}
if (!Array.isArray(nuxt.options.tailwindcss.configPath)) {
nuxt.options.tailwindcss.configPath = nuxt.options.tailwindcss.configPath ? [nuxt.options.tailwindcss.configPath] : ['tailwind.config']
}
nuxt.options.tailwindcss.configPath.push(template.dst)
})
45 changes: 42 additions & 3 deletions playground/nuxt.config.ts
Original file line number Diff line number Diff line change
@@ -1,19 +1,58 @@
import { defineNuxtConfig } from 'nuxt/config'
import { existsSync } from 'node:fs'
import { resolve } from 'pathe'
import { consola } from 'consola'
import typography from '@tailwindcss/typography'

import type { ModuleHooks, ModuleOptions } from '../src/types'

const logger = consola.withTag('nuxt:tailwindcss:playground')

export default defineNuxtConfig({
extends: ['./theme'],
// builder: 'webpack',
modules: [
'@nuxt/content',
existsSync(resolve(__dirname, '../dist/module.mjs')) ? '@nuxtjs/tailwindcss' : '../src/module',
'@nuxt/devtools'
'@nuxt/content',
],
// @ts-ignore
tailwindcss: {
// viewer: false,
config: { plugins: [typography()] },
exposeConfig: true,
cssPath: '~/assets/css/tailwind.css',
editorSupport: true
},
} satisfies Partial<ModuleOptions>,
hooks: {
'tailwindcss:loadConfig': (config, configPath, idx) => {
logger.info('Running `tailwindcss:loadConfig` hook...', Object.keys(config || {}), { configPath, idx })

if (idx === 0 && config) {
config.theme = config.theme ?? {}
config.theme.extend = config.theme.extend ?? {}
config.theme.extend.screens = { md2: '100px' }
config.theme.extend.colors = config.theme.extend.colors ?? {}
// @ts-ignore
config.theme.extend.colors.zeroLayer = '#0fe325'
} else if (idx === 1 && config) {
config.content = config.content ?? []
Array.isArray(config.content) ? config.content.push('my-content') : config.content.files.push('my-file-content')
}
},
'tailwindcss:config': (config) => {
logger.info('Running `tailwindcss:config` hook...')

config.theme = config.theme ?? {}
config.theme.extend = config.theme.extend ?? {}
config.theme.extend.colors = config.theme.extend.colors ?? {}
// @ts-ignore
config.theme.extend.colors.twConfig = '#f0ff0f'
},
'tailwindcss:resolvedConfig': (config, oldConfig) => {
// @ts-ignore
logger.info('Running `tailwindcss:resolvedConfig` hook...', { typography: Boolean(config.theme.typography), hasOld: Boolean(oldConfig) })
}
} satisfies Partial<ModuleHooks>,
content: {
documentDriven: true
},
Expand Down
4 changes: 1 addition & 3 deletions playground/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,5 @@
"dev": "nuxi dev",
"build": "nuxi build",
"generate": "nuxi generate"
},
"dependencies": {},
"devDependencies": {}
}
}
13 changes: 13 additions & 0 deletions playground/pages/index.vue
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,20 @@
<span class="text-blue-500">meow!</span>
</div>
<p class="text-brand">
This color comes from the `./tailwind.config`
</p>
<p class="text-secondary">
This color comes from the `./theme` layer
</p>
<p class="text-accent">
This color comes from the `./modules/template` module
</p>
<p class="text-zeroLayer">
This color comes from the `tailwindcss:loadConfig` hook
</p>
<p class="text-twConfig">
This color comes from the `tailwindcss:config` hook
</p>
<div>
<NuxtLink
to="/content"
Expand All @@ -35,5 +47,6 @@
import { tw } from '#imports'
import tailwindConfig from '#tailwind-config'
// const tw = <T>(s: T) => s
const mainDivClass = tw`max-w-screen-lg p-4 mx-auto space-y-4`
</script>
3 changes: 2 additions & 1 deletion playground/theme/tailwind.config.cjs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,8 @@ module.exports = {
theme: {
extend: {
colors: {
brand: colors.fuchsia['500']
brand: colors.fuchsia['500'],
secondary: colors.fuchsia['500']
}
}
}
Expand Down
Loading

0 comments on commit 81595fe

Please sign in to comment.