Skip to content

Commit

Permalink
Add support for @plugin and @config in v4 (#316)
Browse files Browse the repository at this point in the history
* Support `@plugin` and `@config` in v4

* Rework fixture installation script

* Add basic v4 test

* Add v4 configs test

* Update changelog
  • Loading branch information
thecrypticace committed Sep 10, 2024
1 parent 9844623 commit 15e8009
Show file tree
Hide file tree
Showing 17 changed files with 167 additions and 17 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
## [Unreleased]

- Improved performance with large Svelte, Liquid, and Angular files ([#312](https://github.com/tailwindlabs/prettier-plugin-tailwindcss/pull/312))
- Add support for @plugin and @config in v4 ([#316](https://github.com/tailwindlabs/prettier-plugin-tailwindcss/pull/316))

## [0.6.6] - 2024-08-09

Expand Down
23 changes: 10 additions & 13 deletions scripts/install-fixture-deps.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,26 +3,23 @@ import * as fs from 'node:fs/promises'
import * as path from 'node:path'
import { fileURLToPath } from 'node:url'
import { promisify } from 'node:util'

const execAsync = promisify(exec)
import glob from 'fast-glob'

const __dirname = path.dirname(fileURLToPath(import.meta.url))

let fixturesDir = path.resolve(__dirname, '../tests/fixtures')
let fixtureDirs = await fs.readdir(fixturesDir)
let fixtures = fixtureDirs.map((name) => path.join(fixturesDir, name))
const fixtures = glob.sync(
['tests/fixtures/*/package.json', 'tests/fixtures/v4/*/package.json'],
{
cwd: path.resolve(__dirname, '..'),
},
)

const execAsync = promisify(exec)

await Promise.all(
fixtures.map(async (fixture) => {
let exists = await fs.access(path.join(fixture, 'package.json')).then(
() => true,
() => false,
)

if (!exists) return

console.log(`Installing dependencies for ${fixture}`)

await execAsync('npm install', { cwd: fixture })
await execAsync('npm install', { cwd: path.dirname(fixture) })
}),
)
53 changes: 49 additions & 4 deletions src/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ import loadConfigFallback from 'tailwindcss/loadConfig'
import resolveConfigFallback from 'tailwindcss/resolveConfig'
import type { RequiredConfig } from 'tailwindcss/types/config.js'
import { expiringMap } from './expiring-map.js'
import { resolveIn } from './resolve'
import { resolveFrom, resolveIn } from './resolve'
import type { ContextContainer } from './types'

let localRequire = createRequire(import.meta.url)
Expand Down Expand Up @@ -147,6 +147,37 @@ async function loadTailwindConfig(
}
}

/**
* Create a loader function that can load plugins and config files relative to
* the CSS file that uses them. However, we don't want missing files to prevent
* everything from working so we'll let the error handler decide how to proceed.
*
* @param {object} param0
* @returns
*/
function createLoader<T>({
filepath,
onError,
}: {
filepath: string
onError: (id: string, error: unknown) => T
}) {
let baseDir = path.dirname(filepath)
let cacheKey = `${+Date.now()}`

return async function loadFile(id: string) {
try {
let resolved = resolveFrom(baseDir, id)
let url = pathToFileURL(resolved)
url.searchParams.append('t', cacheKey)

return await import(url.href).then((m) => m.default ?? m)
} catch (err) {
return onError(id, err)
}
}
}

async function loadV4(
baseDir: string,
pkgDir: string,
Expand All @@ -172,9 +203,23 @@ async function loadV4(
// Load the design system and set up a compatible context object that is
// usable by the rest of the plugin
let design = await tw.__unstable__loadDesignSystem(result.css, {
loadPlugin() {
return () => {}
},
loadPlugin: createLoader({
filepath: entryPoint,
onError(id, err) {
console.error(`Unable to load plugin: ${id}`, err)

return () => {}
},
}),

loadConfig: createLoader({
filepath: entryPoint,
onError(id, err) {
console.error(`Unable to load config: ${id}`, err)

return {}
},
}),
})

return {
Expand Down
3 changes: 3 additions & 0 deletions src/resolve.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { createRequire as req } from 'node:module'
import resolveFrom from 'resolve-from'
import { expiringMap } from './expiring-map'

const localRequire = req(import.meta.url)
Expand Down Expand Up @@ -45,3 +46,5 @@ function freshMaybeResolve(name: string) {
return null
}
}

export { resolveFrom }
11 changes: 11 additions & 0 deletions tests/fixtures.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,17 @@ let fixtures = [
dir: 'custom-jsx',
ext: 'jsx',
},

{
name: 'v4: basic formatting',
dir: 'v4/basic',
ext: 'html',
},
{
name: 'v4: configs and plugins',
dir: 'v4/configs',
ext: 'html',
},
]

let configs = [
Expand Down
5 changes: 5 additions & 0 deletions tests/fixtures/v4/basic/app.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
@import 'tailwindcss';

@theme {
--color-tomato: tomato;
}
1 change: 1 addition & 0 deletions tests/fixtures/v4/basic/index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
<div class="sm:bg-tomato bg-red-500"></div>
1 change: 1 addition & 0 deletions tests/fixtures/v4/basic/output.html
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
<div class="bg-red-500 sm:bg-tomato"></div>
17 changes: 17 additions & 0 deletions tests/fixtures/v4/basic/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

8 changes: 8 additions & 0 deletions tests/fixtures/v4/basic/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
{
"dependencies": {
"tailwindcss": "^4.0.0-alpha.23"
},
"prettier": {
"tailwindEntryPoint": "./app.css"
}
}
8 changes: 8 additions & 0 deletions tests/fixtures/v4/configs/app.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
@import "tailwindcss";

@config "./tailwind.config.mjs";
@plugin "./my-plugin.mjs";

@theme {
--color-tomato: tomato;
}
3 changes: 3 additions & 0 deletions tests/fixtures/v4/configs/index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
<div
class="sm:bg-tomato bg-red-500 from-plugin-1 from-plugin-2 bg-from-config sm:from-plugin-1"
></div>
13 changes: 13 additions & 0 deletions tests/fixtures/v4/configs/my-plugin.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import plugin from 'tailwindcss/plugin'

export default plugin(function ({ addUtilities }) {
addUtilities({
'.from-plugin-1': {
width: '100%',
},
'.from-plugin-2': {
color: 'red',
margin: '2rem',
},
})
})
3 changes: 3 additions & 0 deletions tests/fixtures/v4/configs/output.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
<div
class="from-plugin-2 from-plugin-1 bg-from-config bg-red-500 sm:from-plugin-1 sm:bg-tomato"
></div>
17 changes: 17 additions & 0 deletions tests/fixtures/v4/configs/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

8 changes: 8 additions & 0 deletions tests/fixtures/v4/configs/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
{
"dependencies": {
"tailwindcss": "^4.0.0-alpha.23"
},
"prettier": {
"tailwindEntryPoint": "./app.css"
}
}
9 changes: 9 additions & 0 deletions tests/fixtures/v4/configs/tailwind.config.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
export default {
theme: {
extend: {
colors: {
"from-config": "#3490dc",
},
},
},
};

0 comments on commit 15e8009

Please sign in to comment.