Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
51 commits
Select commit Hold shift + click to select a range
d0ade6f
Add `@import` resolver
philipp-spiess Sep 10, 2024
e5768a1
Add bench
philipp-spiess Sep 11, 2024
fa14f54
Parse @import params
philipp-spiess Sep 11, 2024
911f2f0
wip
philipp-spiess Sep 17, 2024
e3b067d
Make tests pass again
philipp-spiess Sep 18, 2024
89f9db6
Add context node to AST
philipp-spiess Sep 18, 2024
3f065c1
Make resolvers return a new base
philipp-spiess Sep 18, 2024
ed5070e
Handle relative paths in imported files
philipp-spiess Sep 18, 2024
75e888b
Reanem API to loadModule/loadStylesheet
philipp-spiess Sep 19, 2024
9853d47
Fix config resolver to keep track of proper bases
philipp-spiess Sep 19, 2024
fa4b781
Fix css function tests
philipp-spiess Sep 19, 2024
bd605bb
Fix tailwindcss/src/index.test.ts tests
philipp-spiess Sep 19, 2024
0d0c0e6
Fix compat/config.test.ts
philipp-spiess Sep 19, 2024
51f7c55
Fix screens-config.test.ts
philipp-spiess Sep 19, 2024
45e7db3
Work around plugin-api.test.ts changes for now
philipp-spiess Sep 19, 2024
0c4de34
Fix node API by using require.resolve over path.resolve, whoops/
philipp-spiess Sep 19, 2024
615cc99
Fix @tailwindcss/postcss
philipp-spiess Sep 19, 2024
58657bf
Update @tailwindcss/vite
philipp-spiess Sep 19, 2024
87c0bed
Update @tailwindcss/cli
philipp-spiess Sep 19, 2024
3d563c6
Fix linter issues
philipp-spiess Sep 19, 2024
d57dcf3
Remove accidentially commited file
philipp-spiess Sep 19, 2024
d8f5b0a
Use async resolver APIs to avoid blocking core
philipp-spiess Sep 19, 2024
71df1a7
Remove @tailwindcss/internal-postcss-fix-relative-paths
philipp-spiess Sep 19, 2024
cff95a7
Fix type error in plugin-api tests
philipp-spiess Sep 19, 2024
edb1d47
Fix TypeScript issues
philipp-spiess Sep 19, 2024
a11cd80
Cleanup @import resolver
philipp-spiess Sep 19, 2024
4d60a61
Fix context node issue
philipp-spiess Sep 19, 2024
8cc9143
Avoid second walk when resolving `@import`
philipp-spiess Sep 20, 2024
2529aa9
Don't check for `@import` before doing the `@import` walk
philipp-spiess Sep 20, 2024
224ea66
Remove leftover export
philipp-spiess Sep 20, 2024
8124823
Make context nodes transparent in walks
philipp-spiess Sep 20, 2024
fba81c2
Remove leftover context node in buildImportNodes
philipp-spiess Sep 20, 2024
a7d98ad
Add \`resourceHint\` to loadModule callback
philipp-spiess Sep 20, 2024
31ff3eb
Revert screwup
philipp-spiess Sep 20, 2024
8ffad46
Handle recursion
philipp-spiess Sep 20, 2024
b8fbe56
Improve spec compliance
philipp-spiess Sep 20, 2024
026c30c
Add a test case for imports starting with `/`.
philipp-spiess Sep 20, 2024
19ab40d
Move noramlizePath into node lib
philipp-spiess Sep 20, 2024
c733587
revert change in postcss fixture
philipp-spiess Sep 20, 2024
03a3bbc
Move `base` into compiler options
philipp-spiess Sep 20, 2024
2d541e9
Update all plugin-api tests to use `loadModule`
thecrypticace Sep 20, 2024
b59ccc9
Remove .only
philipp-spiess Sep 20, 2024
0dd3a38
Add changelog entry
philipp-spiess Sep 20, 2024
b06d194
Add comment regarding recursion
philipp-spiess Sep 23, 2024
69f4ffc
Gate out http/https resources
philipp-spiess Sep 23, 2024
ecdf002
Make `@import url(...)` pass-through
philipp-spiess Sep 23, 2024
0763a25
Clean up tests
philipp-spiess Sep 23, 2024
e6ee1dc
Change context type
philipp-spiess Sep 23, 2024
2174a5a
Remove `//`
philipp-spiess Sep 23, 2024
8afbcdc
Avoid two-pass resolution for some cases
philipp-spiess Sep 23, 2024
9dd29d0
Don't use regex for case-insensitive string comparisons
philipp-spiess Sep 23, 2024
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
Next Next commit
Add @import resolver
  • Loading branch information
philipp-spiess committed Sep 23, 2024
commit d0ade6fca8f1f8ff119de564dc6f11ae38341361
79 changes: 79 additions & 0 deletions packages/tailwindcss/src/at-import.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
import { expect, it } from 'vitest'
import { compile } from './index'

let css = String.raw

async function run(
css: string,
resolveImport: (id: string, basedir: string) => Promise<{ content: string; basedir: string }>,
) {
let compiler = await compile(css, '/root', { resolveImport })
return compiler.build([])
}

it('can resolve relative @imports', async () => {
let resolver = async (id: string, basedir: string) => {
expect(basedir).toBe('/root')
expect(id).toBe('./foo/bar.css')
return {
content: css`
.foo {
color: red;
}
`,
basedir: '/root/foo',
}
}

await expect(
run(
css`
@import './foo/bar.css';
`,
resolver,
),
).resolves.toMatchInlineSnapshot(`
".foo {
color: red;
}
"
`)
})

it('can recursively resolve relative @imports', async () => {
let resolver = async (id: string, basedir: string) => {
if (basedir === '/root' && id === './foo/bar.css') {
return {
content: css`
@import './bar/baz.css';
`,
basedir: '/root/foo',
}
} else if (basedir === '/root/foo' && id === './bar/baz.css') {
return {
content: css`
.baz {
color: blue;
}
`,
basedir: '/root/foo/bar',
}
}

throw new Error(`Unexpected import: ${id}`)
}

await expect(
run(
css`
@import './foo/bar.css';
`,
resolver,
),
).resolves.toMatchInlineSnapshot(`
".baz {
color: blue;
}
"
`)
})
52 changes: 52 additions & 0 deletions packages/tailwindcss/src/at-import.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
import { walk, WalkAction, type AstNode } from './ast'
import * as CSS from './css-parser'

type ResolveImport = (id: string, basedir: string) => Promise<{ content: string; basedir: string }>

export async function substituteAtImports(
ast: AstNode[],
basedir: string,
resolveImport: ResolveImport,
) {
let promises: Map<string, Promise<AstNode[]>> = new Map()

walk(ast, (node) => {
// Find @import rules and start resolving them
if (node.kind === 'rule' && node.selector[0] === '@' && node.selector.startsWith('@import ')) {
let id = node.selector.slice('@import "'.length, -1)
promises.set(key(id, basedir), resolveAtImport(id, basedir, resolveImport))
}
})

let entries = [...promises.entries()]
let resolvers = entries.map(async ([id, promise]) => [id, await promise] as const)
let resolved = await Promise.all(resolvers)
let unwrapped = new Map(resolved)

walk(ast, (node, { replaceWith }) => {
// Replace all @import rules
if (node.kind === 'rule' && node.selector[0] === '@' && node.selector.startsWith('@import ')) {
let id = node.selector.slice('@import "'.length, -1)
let result = unwrapped.get(key(id, basedir))
if (result) {
replaceWith(result)
return WalkAction.Skip
}
}
})
}

async function resolveAtImport(
id: string,
basedir: string,
resolveImport: ResolveImport,
): Promise<AstNode[]> {
const { content, basedir: nestedBaseDir } = await resolveImport(id, basedir)
let ast = CSS.parse(content)
await substituteAtImports(ast, nestedBaseDir, resolveImport)
return ast
}

function key(id: string, basedir: string): string {
return `${id}:${basedir}`
}
20 changes: 17 additions & 3 deletions packages/tailwindcss/src/index.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { version } from '../package.json'
import { substituteAtApply } from './apply'
import { comment, decl, rule, toCss, walk, WalkAction, type Rule } from './ast'
import { substituteAtImports } from './at-import'
import { applyCompatibilityHooks } from './compat/apply-compat-hooks'
import type { UserConfig } from './compat/config/types'
import { type Plugin } from './compat/plugin-api'
Expand All @@ -17,6 +18,7 @@ const IS_VALID_UTILITY_NAME = /^[a-z][a-zA-Z0-9/%._-]*$/
type CompileOptions = {
loadPlugin?: (path: string) => Promise<Plugin>
loadConfig?: (path: string) => Promise<UserConfig>
resolveImport?: (id: string, basedir: string) => Promise<{ content: string; basedir: string }>
}

function throwOnPlugin(): never {
Expand All @@ -27,6 +29,10 @@ function throwOnConfig(): never {
throw new Error('No `loadConfig` function provided to `compile`')
}

function throwOnResolveImport(): never {
throw new Error('No `resolveImport` function provided to `compile`')
}

function parseThemeOptions(selector: string) {
let options = ThemeOptions.NONE

Expand All @@ -45,10 +51,17 @@ function parseThemeOptions(selector: string) {

async function parseCss(
css: string,
{ loadPlugin = throwOnPlugin, loadConfig = throwOnConfig }: CompileOptions = {},
basedir: string,
{
loadPlugin = throwOnPlugin,
loadConfig = throwOnConfig,
resolveImport = throwOnResolveImport,
}: CompileOptions = {},
) {
let ast = CSS.parse(css)

await substituteAtImports(ast, basedir, resolveImport)

// Find all `@theme` declarations
let theme = new Theme()
let customVariants: ((designSystem: DesignSystem) => void)[] = []
Expand Down Expand Up @@ -314,12 +327,13 @@ async function parseCss(

export async function compile(
css: string,
basedir: string,
opts: CompileOptions = {},
): Promise<{
globs: { origin?: string; pattern: string }[]
build(candidates: string[]): string
}> {
let { designSystem, ast, globs } = await parseCss(css, opts)
let { designSystem, ast, globs } = await parseCss(css, basedir, opts)

let tailwindUtilitiesNode: Rule | null = null

Expand Down Expand Up @@ -397,7 +411,7 @@ export async function compile(
}

export async function __unstable__loadDesignSystem(css: string, opts: CompileOptions = {}) {
let result = await parseCss(css, opts)
let result = await parseCss(css, '', opts)
return result.designSystem
}

Expand Down
Loading