mirror of
https://github.com/tailwindlabs/tailwindcss.git
synced 2025-12-08 21:36:08 +00:00
Use resolveJsId when resolving tailwindcss/package.json (#15041)
This PR uses the `enhanced-resolve` instead of `createRequire(…).resolve` which improves the usability when running the upgrade tool locally using Bun. While testing, we also noticed that it is not possible to use a `cjs`-only plugin inside of an `esm` project. It was also not possible to use an `esm`-only plugin inside of a `cjs` project. # Test plan We added integration tests in both the CLI (the CLI is an mjs project) and in the PostCSS (where we can configure a `cjs` and `esm` PostCSS config) integration tests where we created an `esm` and `cjs` based project with 4 plugins (`cjs`-only, `esm`-only, and TypeScript based plugins: `cts`-only and `mts`-only).
This commit is contained in:
parent
e4bfa8c9b7
commit
fe9fc9abba
@ -23,11 +23,13 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
||||
### Fixed
|
||||
|
||||
- Ensure `flex` is suggested ([#15014](https://github.com/tailwindlabs/tailwindcss/pull/15014))
|
||||
- Improve module resolution for `cjs`-only and `esm`-only plugins ([#15041](https://github.com/tailwindlabs/tailwindcss/pull/15041))
|
||||
- _Upgrade (experimental)_: Resolve imports when specifying a CSS entry point on the command-line ([#15010](https://github.com/tailwindlabs/tailwindcss/pull/15010))
|
||||
- _Upgrade (experimental)_: Resolve nearest Tailwind config file when CSS file does not contain `@config` ([#15001](https://github.com/tailwindlabs/tailwindcss/pull/15001))
|
||||
- _Upgrade (experimental)_: Improve output when CSS imports can not be found ([#15038](https://github.com/tailwindlabs/tailwindcss/pull/15038))
|
||||
- _Upgrade (experimental)_: Ignore analyzing imports with external URLs (e.g.: `@import "https://fonts.google.com"`) ([#15040](https://github.com/tailwindlabs/tailwindcss/pull/15040))
|
||||
- _Upgrade (experimental)_: Ignore analyzing imports with `url(…)` (e.g.: `@import url("https://fonts.google.com")`) ([#15040](https://github.com/tailwindlabs/tailwindcss/pull/15040))
|
||||
- _Upgrade (experimental)_: Use `resolveJsId` when resolving `tailwindcss/package.json` ([#15041](https://github.com/tailwindlabs/tailwindcss/pull/15041))
|
||||
|
||||
### Changed
|
||||
|
||||
|
||||
@ -2,7 +2,7 @@ import dedent from 'dedent'
|
||||
import os from 'node:os'
|
||||
import path from 'node:path'
|
||||
import { describe, expect } from 'vitest'
|
||||
import { candidate, css, html, js, json, test, yaml } from '../utils'
|
||||
import { candidate, css, html, js, json, test, ts, yaml } from '../utils'
|
||||
|
||||
const STANDALONE_BINARY = (() => {
|
||||
switch (os.platform()) {
|
||||
@ -257,6 +257,161 @@ describe.each([
|
||||
await fs.expectFileToContain('dist/out.css', [candidate`underline`])
|
||||
},
|
||||
)
|
||||
|
||||
test(
|
||||
'module resolution using CJS, ESM, CTS, and MTS',
|
||||
{
|
||||
fs: {
|
||||
'package.json': json`{}`,
|
||||
'pnpm-workspace.yaml': yaml`
|
||||
#
|
||||
packages:
|
||||
- project-cjs
|
||||
- project-esm
|
||||
- plugin-cjs
|
||||
- plugin-esm
|
||||
- plugin-cts
|
||||
- plugin-mts
|
||||
`,
|
||||
'project-cjs/package.json': json`
|
||||
{
|
||||
"type": "commonjs",
|
||||
"dependencies": {
|
||||
"tailwindcss": "workspace:^",
|
||||
"@tailwindcss/cli": "workspace:^",
|
||||
"plugin-cjs": "workspace:*",
|
||||
"plugin-esm": "workspace:*",
|
||||
"plugin-cts": "workspace:*",
|
||||
"plugin-mts": "workspace:*"
|
||||
}
|
||||
}
|
||||
`,
|
||||
'project-cjs/index.html': html`
|
||||
<div class="cjs esm cts mts"></div>
|
||||
`,
|
||||
'project-cjs/src/index.css': css`
|
||||
@import 'tailwindcss/utilities';
|
||||
@plugin 'plugin-cjs';
|
||||
@plugin 'plugin-esm';
|
||||
@plugin 'plugin-cts';
|
||||
@plugin 'plugin-mts';
|
||||
`,
|
||||
|
||||
'project-esm/package.json': json`
|
||||
{
|
||||
"type": "module",
|
||||
"dependencies": {
|
||||
"tailwindcss": "workspace:^",
|
||||
"@tailwindcss/cli": "workspace:^",
|
||||
"plugin-cjs": "workspace:*",
|
||||
"plugin-esm": "workspace:*",
|
||||
"plugin-cts": "workspace:*",
|
||||
"plugin-mts": "workspace:*"
|
||||
}
|
||||
}
|
||||
`,
|
||||
'project-esm/index.html': html`
|
||||
<div class="cjs esm cts mts"></div>
|
||||
`,
|
||||
'project-esm/src/index.css': css`
|
||||
@import 'tailwindcss/utilities';
|
||||
@plugin 'plugin-cjs';
|
||||
@plugin 'plugin-esm';
|
||||
@plugin 'plugin-cts';
|
||||
@plugin 'plugin-mts';
|
||||
`,
|
||||
|
||||
'plugin-cjs/package.json': json`
|
||||
{
|
||||
"name": "plugin-cjs",
|
||||
"type": "commonjs",
|
||||
"exports": {
|
||||
".": {
|
||||
"require": "./index.cjs"
|
||||
}
|
||||
}
|
||||
}
|
||||
`,
|
||||
'plugin-cjs/index.cjs': js`
|
||||
module.exports = function ({ addUtilities }) {
|
||||
addUtilities({ '.cjs': { content: '"cjs"' } })
|
||||
}
|
||||
`,
|
||||
|
||||
'plugin-esm/package.json': json`
|
||||
{
|
||||
"name": "plugin-esm",
|
||||
"type": "module",
|
||||
"exports": {
|
||||
".": {
|
||||
"import": "./index.mjs"
|
||||
}
|
||||
}
|
||||
}
|
||||
`,
|
||||
'plugin-esm/index.mjs': js`
|
||||
export default function ({ addUtilities }) {
|
||||
addUtilities({ '.esm': { content: '"esm"' } })
|
||||
}
|
||||
`,
|
||||
|
||||
'plugin-cts/package.json': json`
|
||||
{
|
||||
"name": "plugin-cts",
|
||||
"type": "commonjs",
|
||||
"exports": {
|
||||
".": {
|
||||
"require": "./index.cts"
|
||||
}
|
||||
}
|
||||
}
|
||||
`,
|
||||
'plugin-cts/index.cts': ts`
|
||||
export default function ({ addUtilities }) {
|
||||
addUtilities({ '.cts': { content: '"cts"' as const } })
|
||||
}
|
||||
`,
|
||||
|
||||
'plugin-mts/package.json': json`
|
||||
{
|
||||
"name": "plugin-mts",
|
||||
"type": "module",
|
||||
"exports": {
|
||||
".": {
|
||||
"import": "./index.mts"
|
||||
}
|
||||
}
|
||||
}
|
||||
`,
|
||||
'plugin-mts/index.mts': ts`
|
||||
export default function ({ addUtilities }) {
|
||||
addUtilities({ '.mts': { content: '"mts"' as const } })
|
||||
}
|
||||
`,
|
||||
},
|
||||
},
|
||||
async ({ root, fs, exec }) => {
|
||||
await exec(`${command} --input src/index.css --output dist/out.css`, {
|
||||
cwd: path.join(root, 'project-cjs'),
|
||||
})
|
||||
await exec(`${command} --input src/index.css --output dist/out.css`, {
|
||||
cwd: path.join(root, 'project-esm'),
|
||||
})
|
||||
|
||||
await fs.expectFileToContain('./project-cjs/dist/out.css', [
|
||||
candidate`cjs`,
|
||||
candidate`esm`,
|
||||
candidate`cts`,
|
||||
candidate`mts`,
|
||||
])
|
||||
await fs.expectFileToContain('./project-esm/dist/out.css', [
|
||||
candidate`cjs`,
|
||||
candidate`esm`,
|
||||
candidate`cts`,
|
||||
candidate`mts`,
|
||||
])
|
||||
},
|
||||
)
|
||||
})
|
||||
|
||||
test(
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
import dedent from 'dedent'
|
||||
import path from 'node:path'
|
||||
import { expect } from 'vitest'
|
||||
import { candidate, css, html, js, json, test, yaml } from '../utils'
|
||||
import { candidate, css, html, js, json, test, ts, yaml } from '../utils'
|
||||
|
||||
test(
|
||||
'production build (string)',
|
||||
@ -315,6 +315,177 @@ test(
|
||||
},
|
||||
)
|
||||
|
||||
test(
|
||||
'module resolution using CJS, ESM, CTS, and MTS',
|
||||
{
|
||||
fs: {
|
||||
'package.json': json`{}`,
|
||||
'pnpm-workspace.yaml': yaml`
|
||||
#
|
||||
packages:
|
||||
- project-cjs
|
||||
- project-esm
|
||||
- plugin-cjs
|
||||
- plugin-esm
|
||||
- plugin-cts
|
||||
- plugin-mts
|
||||
`,
|
||||
'project-cjs/package.json': json`
|
||||
{
|
||||
"type": "commonjs",
|
||||
"dependencies": {
|
||||
"@tailwindcss/postcss": "workspace:^",
|
||||
"plugin-cjs": "workspace:*",
|
||||
"plugin-cts": "workspace:*",
|
||||
"plugin-esm": "workspace:*",
|
||||
"plugin-mts": "workspace:*",
|
||||
"postcss": "^8",
|
||||
"postcss-cli": "^10",
|
||||
"tailwindcss": "workspace:^"
|
||||
}
|
||||
}
|
||||
`,
|
||||
'project-cjs/postcss.config.cjs': js`
|
||||
let tailwindcss = require('@tailwindcss/postcss')
|
||||
module.exports = {
|
||||
plugins: [tailwindcss()],
|
||||
}
|
||||
`,
|
||||
'project-cjs/index.html': html`
|
||||
<div class="cjs esm cts mts"></div>
|
||||
`,
|
||||
'project-cjs/src/index.css': css`
|
||||
@import 'tailwindcss/utilities';
|
||||
@plugin 'plugin-cjs';
|
||||
@plugin 'plugin-esm';
|
||||
@plugin 'plugin-cts';
|
||||
@plugin 'plugin-mts';
|
||||
`,
|
||||
|
||||
'project-esm/package.json': json`
|
||||
{
|
||||
"type": "module",
|
||||
"dependencies": {
|
||||
"@tailwindcss/postcss": "workspace:^",
|
||||
"plugin-cjs": "workspace:*",
|
||||
"plugin-cts": "workspace:*",
|
||||
"plugin-esm": "workspace:*",
|
||||
"plugin-mts": "workspace:*",
|
||||
"postcss": "^8",
|
||||
"postcss-cli": "^10",
|
||||
"tailwindcss": "workspace:^"
|
||||
}
|
||||
}
|
||||
`,
|
||||
'project-esm/postcss.config.mjs': js`
|
||||
import tailwindcss from '@tailwindcss/postcss'
|
||||
export default {
|
||||
plugins: [tailwindcss()],
|
||||
}
|
||||
`,
|
||||
'project-esm/index.html': html`
|
||||
<div class="cjs esm cts mts"></div>
|
||||
`,
|
||||
'project-esm/src/index.css': css`
|
||||
@import 'tailwindcss/utilities';
|
||||
@plugin 'plugin-cjs';
|
||||
@plugin 'plugin-esm';
|
||||
@plugin 'plugin-cts';
|
||||
@plugin 'plugin-mts';
|
||||
`,
|
||||
|
||||
'plugin-cjs/package.json': json`
|
||||
{
|
||||
"name": "plugin-cjs",
|
||||
"type": "commonjs",
|
||||
"exports": {
|
||||
".": {
|
||||
"require": "./index.cjs"
|
||||
}
|
||||
}
|
||||
}
|
||||
`,
|
||||
'plugin-cjs/index.cjs': js`
|
||||
module.exports = function ({ addUtilities }) {
|
||||
addUtilities({ '.cjs': { content: '"cjs"' } })
|
||||
}
|
||||
`,
|
||||
|
||||
'plugin-esm/package.json': json`
|
||||
{
|
||||
"name": "plugin-esm",
|
||||
"type": "module",
|
||||
"exports": {
|
||||
".": {
|
||||
"import": "./index.mjs"
|
||||
}
|
||||
}
|
||||
}
|
||||
`,
|
||||
'plugin-esm/index.mjs': js`
|
||||
export default function ({ addUtilities }) {
|
||||
addUtilities({ '.esm': { content: '"esm"' } })
|
||||
}
|
||||
`,
|
||||
|
||||
'plugin-cts/package.json': json`
|
||||
{
|
||||
"name": "plugin-cts",
|
||||
"type": "commonjs",
|
||||
"exports": {
|
||||
".": {
|
||||
"require": "./index.cts"
|
||||
}
|
||||
}
|
||||
}
|
||||
`,
|
||||
'plugin-cts/index.cts': ts`
|
||||
export default function ({ addUtilities }) {
|
||||
addUtilities({ '.cts': { content: '"cts"' as const } })
|
||||
}
|
||||
`,
|
||||
|
||||
'plugin-mts/package.json': json`
|
||||
{
|
||||
"name": "plugin-mts",
|
||||
"type": "module",
|
||||
"exports": {
|
||||
".": {
|
||||
"import": "./index.mts"
|
||||
}
|
||||
}
|
||||
}
|
||||
`,
|
||||
'plugin-mts/index.mts': ts`
|
||||
export default function ({ addUtilities }) {
|
||||
addUtilities({ '.mts': { content: '"mts"' as const } })
|
||||
}
|
||||
`,
|
||||
},
|
||||
},
|
||||
async ({ root, fs, exec }) => {
|
||||
await exec(`pnpm postcss src/index.css --output dist/out.css`, {
|
||||
cwd: path.join(root, 'project-cjs'),
|
||||
})
|
||||
await exec(`pnpm postcss src/index.css --output dist/out.css`, {
|
||||
cwd: path.join(root, 'project-esm'),
|
||||
})
|
||||
|
||||
await fs.expectFileToContain('./project-cjs/dist/out.css', [
|
||||
candidate`cjs`,
|
||||
candidate`esm`,
|
||||
candidate`cts`,
|
||||
candidate`mts`,
|
||||
])
|
||||
await fs.expectFileToContain('./project-esm/dist/out.css', [
|
||||
candidate`cjs`,
|
||||
candidate`esm`,
|
||||
candidate`cts`,
|
||||
candidate`mts`,
|
||||
])
|
||||
},
|
||||
)
|
||||
|
||||
test(
|
||||
'watch mode',
|
||||
{
|
||||
|
||||
@ -174,11 +174,18 @@ async function resolveCssId(id: string, base: string): Promise<string | false |
|
||||
return runResolver(cssResolver, id, base)
|
||||
}
|
||||
|
||||
const jsResolver = EnhancedResolve.ResolverFactory.createResolver({
|
||||
const esmResolver = EnhancedResolve.ResolverFactory.createResolver({
|
||||
fileSystem: new EnhancedResolve.CachedInputFileSystem(fs, 4000),
|
||||
useSyncFileSystemCalls: true,
|
||||
extensions: ['.js', '.json', '.node', '.ts'],
|
||||
conditionNames: import.meta.url ? ['node', 'import'] : ['node', 'require'],
|
||||
conditionNames: ['node', 'import'],
|
||||
})
|
||||
|
||||
const cjsResolver = EnhancedResolve.ResolverFactory.createResolver({
|
||||
fileSystem: new EnhancedResolve.CachedInputFileSystem(fs, 4000),
|
||||
useSyncFileSystemCalls: true,
|
||||
extensions: ['.js', '.json', '.node', '.ts'],
|
||||
conditionNames: ['node', 'require'],
|
||||
})
|
||||
|
||||
function resolveJsId(id: string, base: string): Promise<string | false | undefined> {
|
||||
@ -188,7 +195,7 @@ function resolveJsId(id: string, base: string): Promise<string | false | undefin
|
||||
return Promise.resolve(resolved)
|
||||
}
|
||||
}
|
||||
return runResolver(jsResolver, id, base)
|
||||
return runResolver(esmResolver, id, base).catch(() => runResolver(cjsResolver, id, base))
|
||||
}
|
||||
|
||||
function runResolver(
|
||||
|
||||
@ -1,7 +1,5 @@
|
||||
import fs from 'node:fs/promises'
|
||||
import { createRequire } from 'node:module'
|
||||
|
||||
const localResolve = createRequire(import.meta.url).resolve
|
||||
import { resolveJsId } from './resolve'
|
||||
|
||||
/**
|
||||
* Resolves the version string of an npm dependency installed in the based
|
||||
@ -9,7 +7,8 @@ const localResolve = createRequire(import.meta.url).resolve
|
||||
*/
|
||||
export async function getPackageVersion(pkg: string, base: string): Promise<string | null> {
|
||||
try {
|
||||
let packageJson = localResolve(`${pkg}/package.json`, { paths: [base] })
|
||||
let packageJson = resolveJsId(`${pkg}/package.json`, base)
|
||||
if (!packageJson) return null
|
||||
let { version } = JSON.parse(await fs.readFile(packageJson, 'utf8'))
|
||||
return version
|
||||
} catch {
|
||||
|
||||
@ -13,6 +13,28 @@ export function resolve(id: string) {
|
||||
return localResolve(id)
|
||||
}
|
||||
|
||||
const esmResolver = EnhancedResolve.ResolverFactory.createResolver({
|
||||
fileSystem: new EnhancedResolve.CachedInputFileSystem(fs, 4000),
|
||||
useSyncFileSystemCalls: true,
|
||||
extensions: ['.js', '.json', '.node', '.ts'],
|
||||
conditionNames: ['node', 'import'],
|
||||
})
|
||||
|
||||
const cjsResolver = EnhancedResolve.ResolverFactory.createResolver({
|
||||
fileSystem: new EnhancedResolve.CachedInputFileSystem(fs, 4000),
|
||||
useSyncFileSystemCalls: true,
|
||||
extensions: ['.js', '.json', '.node', '.ts'],
|
||||
conditionNames: ['node', 'require'],
|
||||
})
|
||||
|
||||
export function resolveJsId(id: string, base: string) {
|
||||
try {
|
||||
return esmResolver.resolveSync({}, base, id)
|
||||
} catch {
|
||||
return cjsResolver.resolveSync({}, base, id)
|
||||
}
|
||||
}
|
||||
|
||||
const resolver = EnhancedResolve.ResolverFactory.createResolver({
|
||||
fileSystem: new EnhancedResolve.CachedInputFileSystem(fs, 4000),
|
||||
useSyncFileSystemCalls: true,
|
||||
@ -21,12 +43,5 @@ const resolver = EnhancedResolve.ResolverFactory.createResolver({
|
||||
conditionNames: ['style'],
|
||||
})
|
||||
export function resolveCssId(id: string, base: string) {
|
||||
if (typeof globalThis.__tw_resolve === 'function') {
|
||||
let resolved = globalThis.__tw_resolve(id, base)
|
||||
if (resolved) {
|
||||
return resolved
|
||||
}
|
||||
}
|
||||
|
||||
return resolver.resolveSync({}, base, id)
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user