Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
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))

### Fixed

Expand All @@ -26,6 +27,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

### Changed

- No longer resolve filenames without a prefix as relative 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