Fix native ESM config loading in v3 (#18938)

Unfortunately for backwards compatibility purposes (with `loadConfig` at
least) we can't switch things to use `import(…)` because there's baked
in knowledge that the config is loaded synchronously for v3.

This PR does two things:
- Defers to `require(…)` which allows newer versions that support
`require(esm)` to work natively. This works around the need to switch to
`import(…)` for those versions.
- Allows newer versions of `postcss-load-config` enabling better
ESM+TypeScript support for PostCSS configs in the CLI.

We support v4, v5, and v6 of `postcss-load-config` simultaneously so any
of those versions should work. I've verified that newer node versions
seem to install v6 while earlier ones like Node v14 install v4 of
`postcss-load-config`. So this should be a backwards compatible change.

- [x] needs tests for `import.meta.resolve(…)`
- [x] needs tests for ESM postcss configs

Fixes #14152
Fixes #14423
This commit is contained in:
Jordan Pittman 2025-09-16 14:03:09 -04:00 • committed by GitHub
parent ff52f8caf2
commit ba55a445cd
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
9 changed files with 164 additions and 45 deletions

View file

@ -207,6 +207,52 @@ describe('Build command', () => {
)
})
test('configs support import.meta', async () => {
// Skip this test in Node 18 as this only works with
// `require(esm)` in Node 20.19+
if (process.versions.node.startsWith('18.')) {
expect(true).toBe(true)
return
}
await writeInputFile('index.html', html`<div class="font-bold"></div>`)
let customConfig = `
console.log(import.meta.url)
console.log(import.meta.resolve('./tailwind.config.mjs'))
export default ${JSON.stringify(
{
content: ['./src/index.html'],
theme: {
extend: {
fontWeight: {
bold: 'BOLD',
},
},
},
corePlugins: {
preflight: false,
},
plugins: [],
},
null,
2
)}
`
await writeInputFile('../tailwind.config.mjs', customConfig)
await $(`${EXECUTABLE} --output ./dist/main.css --config ./tailwind.config.mjs`)
expect(await readOutputFile('main.css')).toIncludeCss(
css`
.font-bold {
font-weight: BOLD;
}
`
)
})
test('--content', async () => {
await writeInputFile('other.html', html`<div class="font-bold"></div>`)
@ -391,6 +437,79 @@ describe('Build command', () => {
expect(contents).toContain(`/*# sourceMappingURL`)
})
test('--postcss supports ESM configs', async () => {
await writeInputFile('index.html', html`<div class="font-bold"></div>`)
let customConfig = javascript`
import * as path from 'path'
import { createRequire } from 'module'
const require = createRequire(import.meta.url)
export default {
map: { inline: true },
plugins: [
function tailwindcss() {
return require(path.resolve('..', '..'))
},
],
}
`
await removeFile('./postcss.config.js')
await writeInputFile('../postcss.config.mjs', customConfig)
await $(`${EXECUTABLE} --output ./dist/main.css --postcss`)
let contents = await readOutputFile('main.css')
expect(contents).toIncludeCss(
css`
.font-bold {
font-weight: 700;
}
`
)
expect(contents).toContain(`/*# sourceMappingURL`)
})
test('--postcss supports TS configs', async () => {
await writeInputFile('index.html', html`<div class="font-bold"></div>`)
let customConfig = javascript`
import * as path from 'path'
import { createRequire } from 'module'
import type { AcceptedPlugin } from 'postcss'
const require = createRequire(import.meta.url)
export default {
map: { inline: true },
plugins: [
function tailwindcss() {
return require(path.resolve('..', '..'))
} as AcceptedPlugin,
],
}
`
await removeFile('./postcss.config.js')
await writeInputFile('../postcss.config.ts', customConfig)
await $(`${EXECUTABLE} --output ./dist/main.css --postcss`)
let contents = await readOutputFile('main.css')
expect(contents).toIncludeCss(
css`
.font-bold {
font-weight: 700;
}
`
)
expect(contents).toContain(`/*# sourceMappingURL`)
})
test('postcss-import is supported by default', async () => {
cleanupFile('src/test.css')