|
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