|
1 | 1 | import { version } from '../package.json'
|
2 |
| -import { |
3 |
| - WalkAction, |
4 |
| - comment, |
5 |
| - decl, |
6 |
| - objectToAst, |
7 |
| - rule, |
8 |
| - toCss, |
9 |
| - walk, |
10 |
| - type AstNode, |
11 |
| - type CssInJs, |
12 |
| - type Rule, |
13 |
| -} from './ast' |
| 2 | +import { substituteAtApply } from './apply' |
| 3 | +import { WalkAction, comment, decl, rule, toCss, walk, type Rule } from './ast' |
14 | 4 | import { compileCandidates } from './compile'
|
15 | 5 | import * as CSS from './css-parser'
|
16 | 6 | import { buildDesignSystem, type DesignSystem } from './design-system'
|
| 7 | +import { buildPluginApi, type PluginAPI } from './plugin-api' |
17 | 8 | import { Theme } from './theme'
|
18 |
| -import { escape } from './utils/escape' |
19 | 9 | import { segment } from './utils/segment'
|
20 | 10 |
|
21 | 11 | const IS_VALID_UTILITY_NAME = /^[a-z][a-zA-Z0-9/%._-]*$/
|
22 | 12 |
|
23 |
| -type PluginAPI = { |
24 |
| - addVariant(name: string, variant: string | string[] | CssInJs): void |
25 |
| -} |
26 |
| - |
27 | 13 | type Plugin = (api: PluginAPI) => void
|
28 | 14 |
|
29 | 15 | type CompileOptions = {
|
@@ -311,30 +297,9 @@ export async function compile(
|
311 | 297 | customUtility(designSystem)
|
312 | 298 | }
|
313 | 299 |
|
314 |
| - let api: PluginAPI = { |
315 |
| - addVariant(name, variant) { |
316 |
| - // Single selector |
317 |
| - if (typeof variant === 'string') { |
318 |
| - designSystem.variants.static(name, (r) => { |
319 |
| - r.nodes = [rule(variant, r.nodes)] |
320 |
| - }) |
321 |
| - } |
322 |
| - |
323 |
| - // Multiple parallel selectors |
324 |
| - else if (Array.isArray(variant)) { |
325 |
| - designSystem.variants.static(name, (r) => { |
326 |
| - r.nodes = variant.map((selector) => rule(selector, r.nodes)) |
327 |
| - }) |
328 |
| - } |
329 |
| - |
330 |
| - // CSS-in-JS object |
331 |
| - else if (typeof variant === 'object') { |
332 |
| - designSystem.variants.fromAst(name, objectToAst(variant)) |
333 |
| - } |
334 |
| - }, |
335 |
| - } |
| 300 | + let pluginApi = buildPluginApi(designSystem) |
336 | 301 |
|
337 |
| - await Promise.all(pluginLoaders.map((loader) => loader.then((plugin) => plugin(api)))) |
| 302 | + await Promise.all(pluginLoaders.map((loader) => loader.then((plugin) => plugin(pluginApi)))) |
338 | 303 |
|
339 | 304 | let tailwindUtilitiesNode: Rule | null = null
|
340 | 305 |
|
@@ -420,72 +385,6 @@ export async function compile(
|
420 | 385 | }
|
421 | 386 | }
|
422 | 387 |
|
423 |
| -function substituteAtApply(ast: AstNode[], designSystem: DesignSystem) { |
424 |
| - walk(ast, (node, { replaceWith }) => { |
425 |
| - if (node.kind !== 'rule') return |
426 |
| - if (!(node.selector[0] === '@' && node.selector.startsWith('@apply '))) return |
427 |
| - |
428 |
| - let candidates = node.selector |
429 |
| - .slice(7 /* Ignore `@apply ` when parsing the selector */) |
430 |
| - .trim() |
431 |
| - .split(/\s+/g) |
432 |
| - |
433 |
| - // Replace the `@apply` rule with the actual utility classes |
434 |
| - { |
435 |
| - // Parse the candidates to an AST that we can replace the `@apply` rule |
436 |
| - // with. |
437 |
| - let candidateAst = compileCandidates(candidates, designSystem, { |
438 |
| - onInvalidCandidate: (candidate) => { |
439 |
| - throw new Error(`Cannot apply unknown utility class: ${candidate}`) |
440 |
| - }, |
441 |
| - }).astNodes |
442 |
| - |
443 |
| - // Collect the nodes to insert in place of the `@apply` rule. When a rule |
444 |
| - // was used, we want to insert its children instead of the rule because we |
445 |
| - // don't want the wrapping selector. |
446 |
| - let newNodes: AstNode[] = [] |
447 |
| - for (let candidateNode of candidateAst) { |
448 |
| - if (candidateNode.kind === 'rule' && candidateNode.selector[0] !== '@') { |
449 |
| - for (let child of candidateNode.nodes) { |
450 |
| - newNodes.push(child) |
451 |
| - } |
452 |
| - } else { |
453 |
| - newNodes.push(candidateNode) |
454 |
| - } |
455 |
| - } |
456 |
| - |
457 |
| - // Verify that we don't have any circular dependencies by verifying that |
458 |
| - // the current node does not appear in the new nodes. |
459 |
| - walk(newNodes, (child) => { |
460 |
| - if (child !== node) return |
461 |
| - |
462 |
| - // At this point we already know that we have a circular dependency. |
463 |
| - // |
464 |
| - // Figure out which candidate caused the circular dependency. This will |
465 |
| - // help to create a useful error message for the end user. |
466 |
| - for (let candidate of candidates) { |
467 |
| - let selector = `.${escape(candidate)}` |
468 |
| - |
469 |
| - for (let rule of candidateAst) { |
470 |
| - if (rule.kind !== 'rule') continue |
471 |
| - if (rule.selector !== selector) continue |
472 |
| - |
473 |
| - walk(rule.nodes, (child) => { |
474 |
| - if (child !== node) return |
475 |
| - |
476 |
| - throw new Error( |
477 |
| - `You cannot \`@apply\` the \`${candidate}\` utility here because it creates a circular dependency.`, |
478 |
| - ) |
479 |
| - }) |
480 |
| - } |
481 |
| - } |
482 |
| - }) |
483 |
| - |
484 |
| - replaceWith(newNodes) |
485 |
| - } |
486 |
| - }) |
487 |
| -} |
488 |
| - |
489 | 388 | export function __unstable__loadDesignSystem(css: string) {
|
490 | 389 | // Find all `@theme` declarations
|
491 | 390 | let theme = new Theme()
|
|
0 commit comments