Skip to content

Commit 85407e1

Browse files
Upgrade: Rewrite imports of relative files to use relative file paths
1 parent 557ed8c commit 85407e1

File tree

4 files changed

+158
-1
lines changed

4 files changed

+158
-1
lines changed
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
.foo {
2+
color: red;
3+
}
Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
import { __unstable__loadDesignSystem } from '@tailwindcss/node'
2+
import dedent from 'dedent'
3+
import postcss from 'postcss'
4+
import { expect, it } from 'vitest'
5+
import type { UserConfig } from '../../../tailwindcss/src/compat/config/types'
6+
import { migrateImport } from './migrate-import'
7+
8+
const css = dedent
9+
10+
async function migrate(input: string, userConfig: UserConfig = {}) {
11+
return postcss()
12+
.use(
13+
migrateImport({
14+
designSystem: await __unstable__loadDesignSystem(`@import 'tailwindcss';`, {
15+
base: __dirname,
16+
}),
17+
userConfig,
18+
}),
19+
)
20+
.process(input, { from: expect.getState().testPath })
21+
.then((result) => result.css)
22+
}
23+
24+
it('prints relative file imports as relative paths', async () => {
25+
expect(
26+
await migrate(css`
27+
@import 'fixtures/test';
28+
@import 'fixtures/test.css';
29+
@import './fixtures/test.css';
30+
31+
@import 'fixtures/test' screen;
32+
@import 'fixtures/test.css' screen;
33+
@import './fixtures/test.css' screen;
34+
35+
@import 'fixtures/test' supports (display: grid);
36+
@import 'fixtures/test.css' supports (display: grid);
37+
@import './fixtures/test.css' supports (display: grid);
38+
39+
@import 'fixtures/test' layer(utilities);
40+
@import 'fixtures/test.css' layer(utilities);
41+
@import './fixtures/test.css' layer(utilities);
42+
43+
@import 'fixtures/test' theme(inline);
44+
@import 'fixtures/test.css' theme(inline);
45+
@import './fixtures/test.css' theme(inline);
46+
47+
@import 'fixtures/test' layer(utilities) supports (display: grid) screen and
48+
(min-width: 600px);
49+
@import 'fixtures/test.css' layer(utilities) supports (display: grid) screen and
50+
(min-width: 600px);
51+
@import './fixtures/test.css' layer(utilities) supports (display: grid) screen and
52+
(min-width: 600px);
53+
54+
@import 'tailwindcss';
55+
@import 'tailwindcss/theme.css';
56+
`),
57+
).toMatchInlineSnapshot(`
58+
"@import './fixtures/test.css';
59+
@import './fixtures/test.css';
60+
@import './fixtures/test.css';
61+
62+
@import './fixtures/test.css' screen;
63+
@import './fixtures/test.css' screen;
64+
@import './fixtures/test.css' screen;
65+
66+
@import './fixtures/test.css' supports (display: grid);
67+
@import './fixtures/test.css' supports (display: grid);
68+
@import './fixtures/test.css' supports (display: grid);
69+
70+
@import './fixtures/test.css' layer(utilities);
71+
@import './fixtures/test.css' layer(utilities);
72+
@import './fixtures/test.css' layer(utilities);
73+
74+
@import './fixtures/test.css' theme(inline);
75+
@import './fixtures/test.css' theme(inline);
76+
@import './fixtures/test.css' theme(inline);
77+
78+
@import './fixtures/test.css' layer(utilities) supports (display: grid) screen and
79+
(min-width: 600px);
80+
@import './fixtures/test.css' layer(utilities) supports (display: grid) screen and
81+
(min-width: 600px);
82+
@import './fixtures/test.css' layer(utilities) supports (display: grid) screen and
83+
(min-width: 600px);
84+
85+
@import 'tailwindcss';
86+
@import 'tailwindcss/theme.css';"
87+
`)
88+
})
Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
import fs from 'node:fs/promises'
2+
import { dirname, extname, resolve } from 'node:path'
3+
import { type Plugin, type Root } from 'postcss'
4+
import type { Config } from 'tailwindcss'
5+
import { parseImportParams } from '../../../tailwindcss/src/at-import'
6+
import { resolveConfig } from '../../../tailwindcss/src/compat/config/resolve-config'
7+
import { buildMediaQuery } from '../../../tailwindcss/src/compat/screens-config'
8+
import type { DesignSystem } from '../../../tailwindcss/src/design-system'
9+
import { DefaultMap } from '../../../tailwindcss/src/utils/default-map'
10+
import { segment } from '../../../tailwindcss/src/utils/segment'
11+
import * as ValueParser from '../../../tailwindcss/src/value-parser'
12+
13+
export function migrateImport({
14+
designSystem,
15+
userConfig,
16+
}: {
17+
designSystem?: DesignSystem
18+
userConfig?: Config
19+
} = {}): Plugin {
20+
async function migrate(root: Root) {
21+
if (!designSystem || !userConfig) return
22+
let file = root.source?.input.file
23+
if (!file) return
24+
25+
let { resolvedConfig } = resolveConfig(designSystem, [{ base: '', config: userConfig }])
26+
let screens = resolvedConfig?.theme?.screens || {}
27+
28+
let mediaQueries = new DefaultMap<string, string | null>((name) => {
29+
let value = designSystem?.resolveThemeValue(`--breakpoint-${name}`) ?? screens?.[name]
30+
if (typeof value === 'string') return `(width >= theme(--breakpoint-${name}))`
31+
return value ? buildMediaQuery(value) : null
32+
})
33+
34+
let mutations: Promise<void>[] = []
35+
36+
root.walkAtRules('import', (rule) => {
37+
let [firstParam, ...rest] = segment(rule.params, ' ')
38+
39+
let params = parseImportParams(ValueParser.parse(firstParam))
40+
41+
// check if the file exists
42+
let fullPath = resolve(dirname(file), params.uri)
43+
if (extname(fullPath) === '') fullPath += '.css'
44+
45+
if (params.uri[0] === '.') {
46+
return
47+
}
48+
49+
mutations.push(
50+
(async () => {
51+
if (await fs.stat(fullPath)) {
52+
let ext = extname(params.uri) === '' ? '.css' : ''
53+
rule.params = [`'./${params.uri}${ext}'`, ...rest].join(' ')
54+
}
55+
})(),
56+
)
57+
})
58+
59+
await Promise.allSettled(mutations)
60+
}
61+
62+
return {
63+
postcssPlugin: '@tailwindcss/upgrade/migrate-import',
64+
OnceExit: migrate,
65+
}
66+
}

packages/tailwindcss/src/at-import.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -67,7 +67,7 @@ export async function substituteAtImports(
6767
// `postcss-import` <https://github.com/postcss/postcss-import>
6868
// Copyright (c) 2014 Maxime Thirouin, Jason Campbell & Kevin Mårtensson
6969
// Released under the MIT License.
70-
function parseImportParams(params: ValueParser.ValueAstNode[]) {
70+
export function parseImportParams(params: ValueParser.ValueAstNode[]) {
7171
let uri
7272
let layer: string | null = null
7373
let media: string | null = null

0 commit comments

Comments
 (0)