Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add PostCSS plugin to fix relative @content and @plugin paths in @imported files #14063

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
20 commits
Select commit Hold shift + click to select a range
4cbf657
Add PostCSS plugin to fix relative `@content` and `@plugin` paths in …
philipp-spiess Jul 26, 2024
a04e2ba
Add invalid path examples
philipp-spiess Jul 26, 2024
74cee52
Run CI on Windows workers
philipp-spiess Jul 26, 2024
4a86bb5
Fix linter issue and append missing ;
philipp-spiess Jul 26, 2024
9721170
Disable fail-fast so that windows tests run through even when mac tes…
philipp-spiess Jul 26, 2024
81ee9c7
Only lint on linux
philipp-spiess Jul 26, 2024
9cdeed9
Revert changes that makes CI run on Windows (and mvoe it to #14065)
philipp-spiess Jul 26, 2024
68c5b16
Move plugin into a separate, private, package
philipp-spiess Jul 26, 2024
13f562e
Run new plugin for CLI and postcss clients
philipp-spiess Jul 26, 2024
e297bfe
Use over
philipp-spiess Jul 26, 2024
1162822
Fix relative paths that would have removed
philipp-spiess Jul 26, 2024
e5b330d
Fix vite integration by avoiding infinite loops and handling string c…
philipp-spiess Jul 29, 2024
70a96e6
Rename postcss plugin to internal-postcss-fix-relative-paths
philipp-spiess Jul 29, 2024
663181a
Clean up vite config callback
philipp-spiess Jul 29, 2024
e60d0b5
Add (failing) test for postcss client
philipp-spiess Jul 29, 2024
64175c5
Fix pnpm dependencies
philipp-spiess Jul 29, 2024
73111a5
Fix linter issues
philipp-spiess Jul 29, 2024
e474510
Fix issue with postcss client by converting to a visitor API and avoi…
philipp-spiess Jul 29, 2024
11bf660
Get rid of a few more const
philipp-spiess Jul 29, 2024
2c34553
Apply PR feedback
philipp-spiess Jul 29, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 3 additions & 2 deletions packages/@tailwindcss-cli/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
"homepage": "https://tailwindcss.com",
"scripts": {
"lint": "tsc --noEmit",
"build": "tsup-node ./src/index.ts --format esm --minify --clean",
"build": "tsup-node",
"dev": "pnpm run build -- --watch"
},
"bin": {
Expand All @@ -36,7 +36,8 @@
"picocolors": "^1.0.1",
"postcss": "8.4.24",
"postcss-import": "^16.1.0",
"tailwindcss": "workspace:^"
"tailwindcss": "workspace:^",
"internal-postcss-fix-relative-paths": "workspace:^"
},
"devDependencies": {
"@types/postcss-import": "^14.0.3"
Expand Down
2 changes: 2 additions & 0 deletions packages/@tailwindcss-cli/src/commands/build/index.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import watcher from '@parcel/watcher'
import { IO, Parsing, scanDir, scanFiles, type ChangedContent } from '@tailwindcss/oxide'
import fixRelativePathsPlugin from 'internal-postcss-fix-relative-paths'
import { Features, transform } from 'lightningcss'
import { existsSync } from 'node:fs'
import fs from 'node:fs/promises'
Expand Down Expand Up @@ -259,6 +260,7 @@ function handleImports(

return postcss()
.use(atImport())
.use(fixRelativePathsPlugin())
.process(input, { from: file })
.then((result) => [
result.css,
Expand Down
9 changes: 9 additions & 0 deletions packages/@tailwindcss-cli/tsup.config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import { defineConfig } from 'tsup'

export default defineConfig({
format: ['esm'],
clean: true,
minify: true,
entry: ['src/index.ts'],
noExternal: ['internal-postcss-fix-relative-paths'],
})
5 changes: 3 additions & 2 deletions packages/@tailwindcss-postcss/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
"homepage": "https://tailwindcss.com",
"scripts": {
"lint": "tsc --noEmit",
"build": "tsup-node ./src/index.ts --format cjs,esm --dts --cjsInterop --splitting --minify --clean",
"build": "tsup-node",
"dev": "pnpm run build -- --watch"
},
"files": [
Expand All @@ -33,7 +33,8 @@
"@tailwindcss/oxide": "workspace:^",
"lightningcss": "^1.25.1",
"postcss-import": "^16.1.0",
"tailwindcss": "workspace:^"
"tailwindcss": "workspace:^",
"internal-postcss-fix-relative-paths": "workspace:^"
},
"devDependencies": {
"@types/node": "^20.12.12",
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
@plugin '../plugin.js';
32 changes: 31 additions & 1 deletion packages/@tailwindcss-postcss/src/index.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -144,7 +144,7 @@ describe('plugins', () => {
let result = await processor.process(
css`
@import 'tailwindcss/utilities';
@plugin 'tailwindcss-test-utils';
@plugin './plugin.js';
`,
{ from: INPUT_CSS_PATH },
)
Expand All @@ -166,6 +166,36 @@ describe('plugins', () => {
`)
})

test('local CJS plugin from `@import`-ed file', async () => {
let processor = postcss([
tailwindcss({ base: `${__dirname}/fixtures/example-project`, optimize: { minify: false } }),
])

let result = await processor.process(
css`
@import 'tailwindcss/utilities';
@import '../example-project/src/relative-import.css';
`,
{ from: `${__dirname}/fixtures/another-project/input.css` },
)

expect(result.css.trim()).toMatchInlineSnapshot(`
".underline {
text-decoration-line: underline;
}

@media (inverted-colors: inverted) {
.inverted\\:flex {
display: flex;
}
}

.hocus\\:underline:focus, .hocus\\:underline:hover {
text-decoration-line: underline;
}"
`)
})

test('published CJS plugin', async () => {
let processor = postcss([
tailwindcss({ base: `${__dirname}/fixtures/example-project`, optimize: { minify: false } }),
Expand Down
193 changes: 99 additions & 94 deletions packages/@tailwindcss-postcss/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,10 @@ import { scanDir } from '@tailwindcss/oxide'
import fs from 'fs'
import { Features, transform } from 'lightningcss'
import path from 'path'
import postcss, { type AcceptedPlugin, type PluginCreator } from 'postcss'
import postcss, { AtRule, type AcceptedPlugin, type PluginCreator } from 'postcss'
import postcssImport from 'postcss-import'
import { compile } from 'tailwindcss'
import fixRelativePathsPlugin from '../../internal-postcss-fix-relative-paths/src'

/**
* A Map that can generate default values for keys that don't exist.
Expand Down Expand Up @@ -48,114 +49,118 @@ function tailwindcss(opts: PluginOptions = {}): AcceptedPlugin {
}
})

let hasApply: boolean, hasTailwind: boolean

return {
postcssPlugin: '@tailwindcss/postcss',
plugins: [
// We need to run `postcss-import` first to handle `@import` rules.
postcssImport(),
fixRelativePathsPlugin(),

{
postcssPlugin: 'tailwindcss',
Once() {
// Reset some state between builds
hasApply = false
hasTailwind = false
},
AtRule(rule: AtRule) {
if (rule.name === 'apply') {
hasApply = true
} else if (rule.name === 'tailwind') {
hasApply = true
hasTailwind = true
}
},
OnceExit(root, { result }) {
let inputFile = result.opts.from ?? ''
let context = cache.get(inputFile)

let rebuildStrategy: 'full' | 'incremental' = 'incremental'

// Track file modification times to CSS files
{
let files = result.messages.flatMap((message) => {
if (message.type !== 'dependency') return []
return message.file
})
files.push(inputFile)
for (let file of files) {
let changedTime = fs.statSync(file, { throwIfNoEntry: false })?.mtimeMs ?? null
if (changedTime === null) {
if (file === inputFile) {
rebuildStrategy = 'full'
}
continue
}

(root, result) => {
let inputFile = result.opts.from ?? ''
let context = cache.get(inputFile)

let rebuildStrategy: 'full' | 'incremental' = 'incremental'
let prevTime = context.mtimes.get(file)
if (prevTime === changedTime) continue

// Track file modification times to CSS files
{
let files = result.messages.flatMap((message) => {
if (message.type !== 'dependency') return []
return message.file
})
files.push(inputFile)
for (let file of files) {
let changedTime = fs.statSync(file, { throwIfNoEntry: false })?.mtimeMs ?? null
if (changedTime === null) {
if (file === inputFile) {
rebuildStrategy = 'full'
}
continue
rebuildStrategy = 'full'
context.mtimes.set(file, changedTime)
}
}

let prevTime = context.mtimes.get(file)
if (prevTime === changedTime) continue
// Do nothing if neither `@tailwind` nor `@apply` is used
if (!hasTailwind && !hasApply) return

rebuildStrategy = 'full'
context.mtimes.set(file, changedTime)
let css = ''

// Look for candidates used to generate the CSS
let { candidates, files, globs } = scanDir({ base, globs: true })

// Add all found files as direct dependencies
for (let file of files) {
result.messages.push({
type: 'dependency',
plugin: '@tailwindcss/postcss',
file,
parent: result.opts.from,
})
}
}

let hasApply = false
let hasTailwind = false
// Register dependencies so changes in `base` cause a rebuild while
// giving tools like Vite or Parcel a glob that can be used to limit
// the files that cause a rebuild to only those that match it.
for (let { base, glob } of globs) {
result.messages.push({
type: 'dir-dependency',
plugin: '@tailwindcss/postcss',
dir: base,
glob,
parent: result.opts.from,
})
}

root.walkAtRules((rule) => {
if (rule.name === 'apply') {
hasApply = true
} else if (rule.name === 'tailwind') {
hasApply = true
hasTailwind = true
// If we've found `@tailwind` then we already
// know we have to run a "full" build
return false
if (rebuildStrategy === 'full') {
let basePath = path.dirname(path.resolve(inputFile))
let { build } = compile(root.toString(), {
loadPlugin: (pluginPath) => {
if (pluginPath[0] === '.') {
return require(path.resolve(basePath, pluginPath))
}

return require(pluginPath)
},
})
context.build = build
css = build(hasTailwind ? candidates : [])
} else if (rebuildStrategy === 'incremental') {
css = context.build!(candidates)
}
})

// Do nothing if neither `@tailwind` nor `@apply` is used
if (!hasTailwind && !hasApply) return

let css = ''

// Look for candidates used to generate the CSS
let { candidates, files, globs } = scanDir({ base, globs: true })

// Add all found files as direct dependencies
for (let file of files) {
result.messages.push({
type: 'dependency',
plugin: '@tailwindcss/postcss',
file,
parent: result.opts.from,
})
}

// Register dependencies so changes in `base` cause a rebuild while
// giving tools like Vite or Parcel a glob that can be used to limit
// the files that cause a rebuild to only those that match it.
for (let { base, glob } of globs) {
result.messages.push({
type: 'dir-dependency',
plugin: '@tailwindcss/postcss',
dir: base,
glob,
parent: result.opts.from,
})
}

if (rebuildStrategy === 'full') {
let basePath = path.dirname(path.resolve(inputFile))
let { build } = compile(root.toString(), {
loadPlugin: (pluginPath) => {
if (pluginPath[0] === '.') {
return require(path.resolve(basePath, pluginPath))
}

return require(pluginPath)
},
})
context.build = build
css = build(hasTailwind ? candidates : [])
} else if (rebuildStrategy === 'incremental') {
css = context.build!(candidates)
}

// Replace CSS
if (css !== context.css && optimize) {
context.optimizedCss = optimizeCss(css, {
minify: typeof optimize === 'object' ? optimize.minify : true,
})
}
context.css = css
root.removeAll()
root.append(postcss.parse(optimize ? context.optimizedCss : context.css, result.opts))
// Replace CSS
if (css !== context.css && optimize) {
context.optimizedCss = optimizeCss(css, {
minify: typeof optimize === 'object' ? optimize.minify : true,
})
}
context.css = css
root.removeAll()
root.append(postcss.parse(optimize ? context.optimizedCss : context.css, result.opts))
},
},
],
}
Expand Down
12 changes: 12 additions & 0 deletions packages/@tailwindcss-postcss/tsup.config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import { defineConfig } from 'tsup'

export default defineConfig({
format: ['esm', 'cjs'],
clean: true,
minify: true,
splitting: true,
cjsInterop: true,
dts: true,
entry: ['src/index.ts'],
noExternal: ['internal-postcss-fix-relative-paths'],
})
4 changes: 3 additions & 1 deletion packages/@tailwindcss-vite/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
"bugs": "https://github.com/tailwindlabs/tailwindcss/issues",
"homepage": "https://tailwindcss.com",
"scripts": {
"build": "tsup-node ./src/index.ts --format esm --dts --minify --clean",
"build": "tsup-node",
"dev": "pnpm run build -- --watch"
},
"files": [
Expand All @@ -30,10 +30,12 @@
"dependencies": {
"@tailwindcss/oxide": "workspace:^",
"lightningcss": "^1.25.1",
"postcss-load-config": "^6.0.1",
"tailwindcss": "workspace:^"
},
"devDependencies": {
"@types/node": "^20.12.12",
"internal-postcss-fix-relative-paths": "workspace:^",
"vite": "^5.2.11"
},
"peerDependencies": {
Expand Down
Loading