Skip to content

Commit

Permalink
forward thrown errors from parseImportParams(…)
Browse files Browse the repository at this point in the history
  • Loading branch information
RobinMalfait committed Nov 22, 2024
1 parent 9f97137 commit 7456de2
Showing 1 changed file with 50 additions and 54 deletions.
104 changes: 50 additions & 54 deletions packages/tailwindcss/src/at-import.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { atRule, context, toCss, walk, WalkAction, type AstNode } from './ast'
import { atRule, context, walk, WalkAction, type AstNode } from './ast'
import * as CSS from './css-parser'
import * as ValueParser from './value-parser'

Expand All @@ -14,57 +14,47 @@ export async function substituteAtImports(

walk(ast, (node, { replaceWith }) => {
if (node.kind === 'at-rule' && node.name === '@import') {
try {
let { uri, layer, media, supports } = parseImportParams(ValueParser.parse(node.params))

// Skip importing data or remote URIs
if (uri.startsWith('data:')) return
if (uri.startsWith('http://') || uri.startsWith('https://')) return

let contextNode = context({}, [])

promises.push(
(async () => {
// Since we do not have fully resolved paths in core, we can't reliably detect circular
// imports. Instead, we try to limit the recursion depth to a number that is too large
// to be reached in practice.
if (recurseCount > 100) {
throw new Error(
`Exceeded maximum recursion depth while resolving \`${uri}\` in \`${base}\`)`,
)
}

const loaded = await loadStylesheet(uri, base)
let ast = CSS.parse(loaded.content)
await substituteAtImports(ast, loaded.base, loadStylesheet, recurseCount + 1)

contextNode.nodes = buildImportNodes(
[
context(
{
base: loaded.base,
get source() {
return toCss([node])
},
},
ast,
),
],
layer,
media,
supports,
let parsed = parseImportParams(ValueParser.parse(node.params))
if (parsed === null) return

let { uri, layer, media, supports } = parsed

// Skip importing data or remote URIs
if (uri.startsWith('data:')) return
if (uri.startsWith('http://') || uri.startsWith('https://')) return

let contextNode = context({}, [])

promises.push(
(async () => {
// Since we do not have fully resolved paths in core, we can't
// reliably detect circular imports. Instead, we try to limit the
// recursion depth to a number that is too large to be reached in
// practice.
if (recurseCount > 100) {
throw new Error(
`Exceeded maximum recursion depth while resolving \`${uri}\` in \`${base}\`)`,
)
})(),
)

replaceWith(contextNode)
// The resolved Stylesheets already have their transitive @imports
// resolved, so we can skip walking them.
return WalkAction.Skip
} catch (e: any) {
// When an error occurs while parsing the `@import` statement, we skip
// the import.
}
}

let loaded = await loadStylesheet(uri, base)
let ast = CSS.parse(loaded.content)
await substituteAtImports(ast, loaded.base, loadStylesheet, recurseCount + 1)

contextNode.nodes = buildImportNodes(
[context({ base: loaded.base }, ast)],
layer,
media,
supports,
)
})(),
)

replaceWith(contextNode)

// The resolved Stylesheets already have their transitive @imports
// resolved, so we can skip walking them.
return WalkAction.Skip
}
})

Expand All @@ -82,7 +72,7 @@ export function parseImportParams(params: ValueParser.ValueAstNode[]) {
let supports: string | null = null

for (let i = 0; i < params.length; i++) {
const node = params[i]
let node = params[i]

if (node.kind === 'separator') continue

Expand All @@ -95,7 +85,10 @@ export function parseImportParams(params: ValueParser.ValueAstNode[]) {
}

if (node.kind === 'function' && node.value.toLowerCase() === 'url') {
throw new Error('`url(…)` functions are not supported')
// `@import` with `url(…)` functions are not inlined but skipped and kept
// in the final CSS instead.
// E.g.: `@import url("https://fonts.google.com")`
return null
}

if (!uri) throw new Error('Unable to find uri')
Expand All @@ -105,7 +98,10 @@ export function parseImportParams(params: ValueParser.ValueAstNode[]) {
node.value.toLowerCase() === 'layer'
) {
if (layer) throw new Error('Multiple layers')
if (supports) throw new Error('`layer(…)` must be defined before `supports(…)` conditions')
if (supports)
throw new Error(
'`layer(…)` in an `@import` should come before any other functions or conditions',
)

if ('nodes' in node) {
layer = ValueParser.toCss(node.nodes)
Expand Down

0 comments on commit 7456de2

Please sign in to comment.