diff --git a/CHANGELOG.md b/CHANGELOG.md index 6e8b14ef..d8d08b97 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -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 diff --git a/scripts/install-fixture-deps.js b/scripts/install-fixture-deps.js index c79939d7..9103ba89 100644 --- a/scripts/install-fixture-deps.js +++ b/scripts/install-fixture-deps.js @@ -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) }) }), ) diff --git a/src/config.ts b/src/config.ts index ee3294d9..d2195e06 100644 --- a/src/config.ts +++ b/src/config.ts @@ -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) @@ -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({ + 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, @@ -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 { diff --git a/src/resolve.ts b/src/resolve.ts index 80a095ff..00f5c08e 100644 --- a/src/resolve.ts +++ b/src/resolve.ts @@ -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) @@ -45,3 +46,5 @@ function freshMaybeResolve(name: string) { return null } } + +export { resolveFrom } diff --git a/tests/fixtures.test.ts b/tests/fixtures.test.ts index 0cecbbd6..e6d65826 100644 --- a/tests/fixtures.test.ts +++ b/tests/fixtures.test.ts @@ -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 = [ diff --git a/tests/fixtures/v4/basic/app.css b/tests/fixtures/v4/basic/app.css new file mode 100644 index 00000000..ac52a795 --- /dev/null +++ b/tests/fixtures/v4/basic/app.css @@ -0,0 +1,5 @@ +@import 'tailwindcss'; + +@theme { + --color-tomato: tomato; +} diff --git a/tests/fixtures/v4/basic/index.html b/tests/fixtures/v4/basic/index.html new file mode 100644 index 00000000..f7a76090 --- /dev/null +++ b/tests/fixtures/v4/basic/index.html @@ -0,0 +1 @@ +
diff --git a/tests/fixtures/v4/basic/output.html b/tests/fixtures/v4/basic/output.html new file mode 100644 index 00000000..8409de1a --- /dev/null +++ b/tests/fixtures/v4/basic/output.html @@ -0,0 +1 @@ +
diff --git a/tests/fixtures/v4/basic/package-lock.json b/tests/fixtures/v4/basic/package-lock.json new file mode 100644 index 00000000..04abdf83 --- /dev/null +++ b/tests/fixtures/v4/basic/package-lock.json @@ -0,0 +1,17 @@ +{ + "name": "basic", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "dependencies": { + "tailwindcss": "^4.0.0-alpha.23" + } + }, + "node_modules/tailwindcss": { + "version": "4.0.0-alpha.23", + "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-4.0.0-alpha.23.tgz", + "integrity": "sha512-5dy4L1icQUYkG2Fa427QG3fKGkNqMi6V4bJE0DoxBZkR4e550w7uxYJ7vBXINWrRmDd4LHmQInkgsHza3lwONg==" + } + } +} diff --git a/tests/fixtures/v4/basic/package.json b/tests/fixtures/v4/basic/package.json new file mode 100644 index 00000000..7f5a61c1 --- /dev/null +++ b/tests/fixtures/v4/basic/package.json @@ -0,0 +1,8 @@ +{ + "dependencies": { + "tailwindcss": "^4.0.0-alpha.23" + }, + "prettier": { + "tailwindEntryPoint": "./app.css" + } +} diff --git a/tests/fixtures/v4/configs/app.css b/tests/fixtures/v4/configs/app.css new file mode 100644 index 00000000..bd6458d4 --- /dev/null +++ b/tests/fixtures/v4/configs/app.css @@ -0,0 +1,8 @@ +@import "tailwindcss"; + +@config "./tailwind.config.mjs"; +@plugin "./my-plugin.mjs"; + +@theme { + --color-tomato: tomato; +} diff --git a/tests/fixtures/v4/configs/index.html b/tests/fixtures/v4/configs/index.html new file mode 100644 index 00000000..296b7d7f --- /dev/null +++ b/tests/fixtures/v4/configs/index.html @@ -0,0 +1,3 @@ +
diff --git a/tests/fixtures/v4/configs/my-plugin.mjs b/tests/fixtures/v4/configs/my-plugin.mjs new file mode 100644 index 00000000..bbbd63db --- /dev/null +++ b/tests/fixtures/v4/configs/my-plugin.mjs @@ -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', + }, + }) +}) diff --git a/tests/fixtures/v4/configs/output.html b/tests/fixtures/v4/configs/output.html new file mode 100644 index 00000000..6b4aae01 --- /dev/null +++ b/tests/fixtures/v4/configs/output.html @@ -0,0 +1,3 @@ +
diff --git a/tests/fixtures/v4/configs/package-lock.json b/tests/fixtures/v4/configs/package-lock.json new file mode 100644 index 00000000..fbba9f01 --- /dev/null +++ b/tests/fixtures/v4/configs/package-lock.json @@ -0,0 +1,17 @@ +{ + "name": "configs", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "dependencies": { + "tailwindcss": "^4.0.0-alpha.23" + } + }, + "node_modules/tailwindcss": { + "version": "4.0.0-alpha.23", + "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-4.0.0-alpha.23.tgz", + "integrity": "sha512-5dy4L1icQUYkG2Fa427QG3fKGkNqMi6V4bJE0DoxBZkR4e550w7uxYJ7vBXINWrRmDd4LHmQInkgsHza3lwONg==" + } + } +} diff --git a/tests/fixtures/v4/configs/package.json b/tests/fixtures/v4/configs/package.json new file mode 100644 index 00000000..7f5a61c1 --- /dev/null +++ b/tests/fixtures/v4/configs/package.json @@ -0,0 +1,8 @@ +{ + "dependencies": { + "tailwindcss": "^4.0.0-alpha.23" + }, + "prettier": { + "tailwindEntryPoint": "./app.css" + } +} diff --git a/tests/fixtures/v4/configs/tailwind.config.mjs b/tests/fixtures/v4/configs/tailwind.config.mjs new file mode 100644 index 00000000..9c3cfa75 --- /dev/null +++ b/tests/fixtures/v4/configs/tailwind.config.mjs @@ -0,0 +1,9 @@ +export default { + theme: { + extend: { + colors: { + "from-config": "#3490dc", + }, + }, + }, +};