Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

- _Upgrade (experimental)_: Migrate `plugins` with options to CSS ([#14700](https://github.com/tailwindlabs/tailwindcss/pull/14700))
- _Upgrade (experimental)_: Allow JS configuration files with `corePlugins` options to be migrated to CSS ([#14742](https://github.com/tailwindlabs/tailwindcss/pull/14742))
- _Upgrade (experimental)_: Migrate `@import` statements for relative CSS files to use relative path syntax (e.g. `./file.css`) ([#14755](https://github.com/tailwindlabs/tailwindcss/pull/14755))
- _Upgrade (experimental)_: Migrate `max-w-screen-*` utilities to `max-w-[var(…)]`([#14754](https://github.com/tailwindlabs/tailwindcss/pull/14754))
- _Upgrade (experimental)_: Migrate `@variants` and `@responsive` directives ([#14748](https://github.com/tailwindlabs/tailwindcss/pull/14748))
- _Upgrade (experimental)_: Migrate `@screen` directive ([#14749](https://github.com/tailwindlabs/tailwindcss/pull/14749))
Expand All @@ -36,6 +37,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

### Changed

- Require a relative path prefix for importing relative CSS files (e.g. `@import './styles.css'` instead of `@import 'styles.css'`) ([#14755](https://github.com/tailwindlabs/tailwindcss/pull/14755))
- _Upgrade (experimental)_: Don't create `@source` rules for `content` paths that are already covered by automatic source detection ([#14714](https://github.com/tailwindlabs/tailwindcss/pull/14714))

## [4.0.0-alpha.28] - 2024-10-17
Expand Down
17 changes: 1 addition & 16 deletions packages/@tailwindcss-node/src/compile.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import EnhancedResolve from 'enhanced-resolve'
import { createJiti, type Jiti } from 'jiti'
import fs from 'node:fs'
import fsPromises from 'node:fs/promises'
import path, { dirname, extname } from 'node:path'
import path, { dirname } from 'node:path'
import { pathToFileURL } from 'node:url'
import {
__unstable__loadDesignSystem as ___unstable__loadDesignSystem,
Expand Down Expand Up @@ -120,21 +120,6 @@ async function resolveCssId(id: string, base: string): Promise<string | false |
}
}

// CSS imports that do not have a dir prefix are considered relative. Since
// the resolver does not account for this, we need to do a first pass with an
// assumed relative import by prefixing `./${path}`. We don't have to do this
// when the path starts with a `.` or when the path has no extension (at which
// case it's likely an npm package and not a relative stylesheet).
let skipRelativeCheck = extname(id) === '' || id.startsWith('.')

if (!skipRelativeCheck) {
try {
let dotResolved = await runResolver(cssResolver, `./${id}`, base)
if (!dotResolved) throw new Error()
return dotResolved
} catch {}
}

return runResolver(cssResolver, id, base)
}

Expand Down
3 changes: 3 additions & 0 deletions packages/@tailwindcss-upgrade/src/codemods/fixtures/test.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
.foo {
color: red;
}
102 changes: 102 additions & 0 deletions packages/@tailwindcss-upgrade/src/codemods/migrate-import.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
import { __unstable__loadDesignSystem } from '@tailwindcss/node'
import dedent from 'dedent'
import postcss from 'postcss'
import { expect, it } from 'vitest'
import type { UserConfig } from '../../../tailwindcss/src/compat/config/types'
import { migrateImport } from './migrate-import'

const css = dedent

async function migrate(input: string, userConfig: UserConfig = {}) {
return postcss()
.use(
migrateImport({
designSystem: await __unstable__loadDesignSystem(`@import 'tailwindcss';`, {
base: __dirname,
}),
userConfig,
}),
)
.process(input, { from: expect.getState().testPath })
.then((result) => result.css)
}

it('prints relative file imports as relative paths', async () => {
expect(
await migrate(css`
@import 'fixtures/test';
@import 'fixtures/test.css';
@import './fixtures/test.css';
@import './fixtures/test';

@import 'fixtures/test' screen;
@import 'fixtures/test.css' screen;
@import './fixtures/test.css' screen;
@import './fixtures/test' screen;

@import 'fixtures/test' supports(display: grid);
@import 'fixtures/test.css' supports(display: grid);
@import './fixtures/test.css' supports(display: grid);
@import './fixtures/test' supports(display: grid);

@import 'fixtures/test' layer(utilities);
@import 'fixtures/test.css' layer(utilities);
@import './fixtures/test.css' layer(utilities);
@import './fixtures/test' layer(utilities);

@import 'fixtures/test' theme(inline);
@import 'fixtures/test.css' theme(inline);
@import './fixtures/test.css' theme(inline);
@import './fixtures/test' theme(inline);

@import 'fixtures/test' layer(utilities) supports(display: grid) screen and (min-width: 600px);
@import 'fixtures/test.css' layer(utilities) supports(display: grid) screen and
(min-width: 600px);
@import './fixtures/test.css' layer(utilities) supports(display: grid) screen and
(min-width: 600px);
@import './fixtures/test' layer(utilities) supports(display: grid) screen and
(min-width: 600px);

@import 'tailwindcss';
@import 'tailwindcss/theme.css';
@import 'tailwindcss/theme';
`),
).toMatchInlineSnapshot(`
"@import './fixtures/test.css';
@import './fixtures/test.css';
@import './fixtures/test.css';
@import './fixtures/test.css';

@import './fixtures/test.css' screen;
@import './fixtures/test.css' screen;
@import './fixtures/test.css' screen;
@import './fixtures/test.css' screen;

@import './fixtures/test.css' supports(display: grid);
@import './fixtures/test.css' supports(display: grid);
@import './fixtures/test.css' supports(display: grid);
@import './fixtures/test.css' supports(display: grid);

@import './fixtures/test.css' layer(utilities);
@import './fixtures/test.css' layer(utilities);
@import './fixtures/test.css' layer(utilities);
@import './fixtures/test.css' layer(utilities);

@import './fixtures/test.css' theme(inline);
@import './fixtures/test.css' theme(inline);
@import './fixtures/test.css' theme(inline);
@import './fixtures/test.css' theme(inline);

@import './fixtures/test.css' layer(utilities) supports(display: grid) screen and (min-width: 600px);
@import './fixtures/test.css' layer(utilities) supports(display: grid) screen and
(min-width: 600px);
@import './fixtures/test.css' layer(utilities) supports(display: grid) screen and
(min-width: 600px);
@import './fixtures/test.css' layer(utilities) supports(display: grid) screen and
(min-width: 600px);

@import 'tailwindcss';
@import 'tailwindcss/theme.css';
@import 'tailwindcss/theme';"
`)
})
45 changes: 45 additions & 0 deletions packages/@tailwindcss-upgrade/src/codemods/migrate-import.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
import fs from 'node:fs/promises'
import { dirname, resolve } from 'node:path'
import { type Plugin, type Root } from 'postcss'
import { parseImportParams } from '../../../tailwindcss/src/at-import'
import { segment } from '../../../tailwindcss/src/utils/segment'
import * as ValueParser from '../../../tailwindcss/src/value-parser'

export function migrateImport(): Plugin {
async function migrate(root: Root) {
let file = root.source?.input.file
if (!file) return

let promises: Promise<void>[] = []
root.walkAtRules('import', (rule) => {
let [firstParam, ...rest] = segment(rule.params, ' ')

let params = parseImportParams(ValueParser.parse(firstParam))

let isRelative = params.uri[0] === '.'
let hasCssExtension = params.uri.endsWith('.css')

if (isRelative && hasCssExtension) {
return
}

let fullPath = resolve(dirname(file), params.uri)
if (!hasCssExtension) fullPath += '.css'

promises.push(
fs.stat(fullPath).then(() => {
let ext = hasCssExtension ? '' : '.css'
let path = isRelative ? params.uri : `./${params.uri}`
rule.params = [`'${path}${ext}'`, ...rest].join(' ')
}),
)
})

await Promise.allSettled(promises)
}

return {
postcssPlugin: '@tailwindcss/upgrade/migrate-import',
OnceExit: migrate,
}
}
2 changes: 1 addition & 1 deletion packages/tailwindcss/src/at-import.ts
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@ export async function substituteAtImports(
// `postcss-import` <https://github.com/postcss/postcss-import>
// Copyright (c) 2014 Maxime Thirouin, Jason Campbell & Kevin Mårtensson
// Released under the MIT License.
function parseImportParams(params: ValueParser.ValueAstNode[]) {
export function parseImportParams(params: ValueParser.ValueAstNode[]) {
let uri
let layer: string | null = null
let media: string | null = null
Expand Down