Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
1 change: 0 additions & 1 deletion c/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -285,7 +285,6 @@ pub extern "C" fn lightningcss_stylesheet_parse(
error_recovery: options.error_recovery,
source_index: 0,
warnings: Some(warnings.clone()),
at_rule_parser: None,
};

let stylesheet = unwrap!(StyleSheet::parse(code, opts), error, std::ptr::null_mut());
Expand Down
20 changes: 10 additions & 10 deletions examples/custom_at_rule.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ use lightningcss::{
selector::{Component, Selector},
stylesheet::{ParserOptions, PrinterOptions, StyleSheet},
targets::Browsers,
traits::ToCss,
traits::{AtRuleParser, ToCss},
values::{color::CssColor, length::LengthValue},
vendor_prefix::VendorPrefix,
visit_types,
Expand All @@ -21,17 +21,11 @@ fn main() {
let args: Vec<String> = std::env::args().collect();
let source = std::fs::read_to_string(&args[1]).unwrap();
let opts = ParserOptions {
at_rule_parser: Some(TailwindAtRuleParser),
filename: args[1].clone(),
nesting: true,
custom_media: false,
css_modules: None,
error_recovery: false,
warnings: None,
source_index: 0,
..Default::default()
};

let mut stylesheet = StyleSheet::parse(&source, opts).unwrap();
let mut stylesheet = StyleSheet::parse_with(&source, opts, &mut TailwindAtRuleParser).unwrap();

println!("{:?}", stylesheet);

Expand Down Expand Up @@ -103,6 +97,7 @@ impl<'i> AtRuleParser<'i> for TailwindAtRuleParser {
&mut self,
name: CowRcStr<'i>,
input: &mut Parser<'i, 't>,
_options: &ParserOptions<'_, 'i>,
) -> Result<Self::Prelude, ParseError<'i, Self::Error>> {
match_ignore_ascii_case! {&*name,
"tailwind" => {
Expand Down Expand Up @@ -135,7 +130,12 @@ impl<'i> AtRuleParser<'i> for TailwindAtRuleParser {
}
}

fn rule_without_block(&mut self, prelude: Self::Prelude, start: &ParserState) -> Result<Self::AtRule, ()> {
fn rule_without_block(
&mut self,
prelude: Self::Prelude,
start: &ParserState,
_options: &ParserOptions<'_, 'i>,
) -> Result<Self::AtRule, ()> {
let loc = start.source_location();
match prelude {
Prelude::Tailwind(directive) => Ok(AtRule::Tailwind(TailwindRule { directive, loc })),
Expand Down
14 changes: 4 additions & 10 deletions node/ast.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6039,22 +6039,16 @@ export type SelectorComponent =
| (
| {
type: "namespace";
value: "none";
kind: "none";
}
| {
type: "namespace";
value: "any";
}
| {
type: "namespace";
url: string;
value: "default";
kind: "any";
}
| {
type: "namespace";
kind: "named";
prefix: string;
url: string;
value: "some";
}
)
| {
Expand Down Expand Up @@ -7042,7 +7036,7 @@ export interface StyleRule<D = Declaration> {
/**
* The declarations within the style rule.
*/
declarations: DeclarationBlock<D>;
declarations?: DeclarationBlock<D>;
/**
* The location of the rule in the source file.
*/
Expand Down
107 changes: 90 additions & 17 deletions node/index.d.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
import type { Angle, CssColor, Rule, CustomProperty, EnvironmentVariable, Function, Image, LengthValue, MediaQuery, Declaration, Ratio, Resolution, Selector, SupportsCondition, Time, Token, TokenOrValue, UnknownAtRule, Url, Variable, StyleRule, DeclarationBlock } from './ast';
import type { Angle, CssColor, Rule, CustomProperty, EnvironmentVariable, Function, Image, LengthValue, MediaQuery, Declaration, Ratio, Resolution, Selector, SupportsCondition, Time, Token, TokenOrValue, UnknownAtRule, Url, Variable, StyleRule, DeclarationBlock, ParsedComponent, Multiplier } from './ast';
import type { Targets } from './targets';

export * from './ast';

export interface TransformOptions {
export interface TransformOptions<C extends CustomAtRules> {
/** The filename being transformed. Used for error messages and source maps. */
filename: string,
/** The source code to transform. */
Expand Down Expand Up @@ -55,7 +55,14 @@ export interface TransformOptions {
* For optimal performance, visitors should be as specific as possible about what types of values
* they care about so that JavaScript has to be called as little as possible.
*/
visitor?: Visitor
visitor?: Visitor<C>,
/**
* Defines how to parse custom CSS at-rules. Each at-rule can have a prelude, defined using a CSS
* [syntax string](https://drafts.css-houdini.org/css-properties-values-api/#syntax-strings), and
* a block body. The body can be a declaration list, rule list, or style block as defined in the
* [css spec](https://drafts.csswg.org/css-syntax/#declaration-rule-list).
*/
customAtRules?: C
}

// This is a hack to make TS still provide autocomplete for `property` vs. just making it `string`.
Expand All @@ -79,12 +86,54 @@ type MappedRuleVisitors = {
[Name in Exclude<Rule['type'], 'unknown' | 'custom'>]?: RuleVisitor<RequiredValue<FindByType<Rule, Name>>>;
}

type UnknownVisitors = {
[name: string]: RuleVisitor<UnknownAtRule>
type UnknownVisitors<T> = {
[name: string]: RuleVisitor<T>
}

type RuleVisitors = MappedRuleVisitors & {
unknown?: UnknownVisitors | RuleVisitor<UnknownAtRule>
type CustomVisitors<T extends CustomAtRules> = {
[Name in keyof T]?: RuleVisitor<CustomAtRule<Name, T[Name]>>
};

type AnyCustomAtRule<C extends CustomAtRules> = {
[Key in keyof C]: CustomAtRule<Key, C[Key]>
}[keyof C];

type RuleVisitors<C extends CustomAtRules> = MappedRuleVisitors & {
unknown?: UnknownVisitors<UnknownAtRule> | Omit<RuleVisitor<UnknownAtRule>, keyof CallableFunction>,
custom?: CustomVisitors<C> | Omit<RuleVisitor<AnyCustomAtRule<C>>, keyof CallableFunction>
};

type PreludeTypes = Exclude<ParsedComponent['type'], 'literal' | 'repeated' | 'token'>;
type SyntaxString = `<${PreludeTypes}>` | `<${PreludeTypes}>+` | `<${PreludeTypes}>#` | (string & {});
type ComponentTypes = {
[Key in PreludeTypes as `<${Key}>`]: FindByType<ParsedComponent, Key>
};

type Repetitions = {
[Key in PreludeTypes as `<${Key}>+` | `<${Key}>#`]: {
type: "repeated",
value: {
components: FindByType<ParsedComponent, Key>[],
multiplier: Multiplier
}
}
};

type MappedPrelude = ComponentTypes & Repetitions;
type MappedBody<P extends CustomAtRuleDefinition['body']> = P extends 'style-block' ? 'rule-list' : P;
interface CustomAtRule<N, R extends CustomAtRuleDefinition> {
name: N,
prelude: R['prelude'] extends keyof MappedPrelude ? MappedPrelude[R['prelude']] : ParsedComponent,
body: FindByType<CustomAtRuleBody, MappedBody<R['body']>>,
loc: Location
}

type CustomAtRuleBody = {
type: 'declaration-list',
value: Required<DeclarationBlock>
} | {
type: 'rule-list',
value: RequiredValue<Rule>[]
};

type FindProperty<Union, Name> = Union extends { property: Name } ? Union : never;
Expand Down Expand Up @@ -113,9 +162,9 @@ type EnvironmentVariableVisitors = {
[name: string]: EnvironmentVariableVisitor
};

export interface Visitor {
Rule?: RuleVisitor | RuleVisitors;
RuleExit?: RuleVisitor | RuleVisitors;
export interface Visitor<C extends CustomAtRules> {
Rule?: RuleVisitor | RuleVisitors<C>;
RuleExit?: RuleVisitor | RuleVisitors<C>;
Declaration?: DeclarationVisitor | DeclarationVisitors;
DeclarationExit?: DeclarationVisitor | DeclarationVisitors;
Url?(url: Url): Url | void;
Expand Down Expand Up @@ -143,14 +192,38 @@ export interface Visitor {
EnvironmentVariableExit?: EnvironmentVariableVisitor | EnvironmentVariableVisitors;
}

export interface CustomAtRules {
[name: string]: CustomAtRuleDefinition
}

export interface CustomAtRuleDefinition {
/**
* Defines the syntax for a custom at-rule prelude. The value should be a
* CSS [syntax string](https://drafts.css-houdini.org/css-properties-values-api/#syntax-strings)
* representing the types of values that are accepted. This property may be omitted or
* set to null to indicate that no prelude is accepted.
*/
prelude?: SyntaxString | null,
/**
* Defines the type of body contained within the at-rule block.
* - declaration-list: A CSS declaration list, as in a style rule.
* - rule-list: A list of CSS rules, as supported within a non-nested
* at-rule such as `@media` or `@supports`.
* - style-block: Both a declaration list and rule list, as accepted within
* a nested at-rule within a style rule (e.g. `@media` inside a style rule
* with directly nested declarations).
*/
body?: 'declaration-list' | 'rule-list' | 'style-block' | null
}

export interface DependencyOptions {
/** Whether to preserve `@import` rules rather than removing them. */
preserveImports?: boolean
}

export type BundleOptions = Omit<TransformOptions, 'code'>;
export type BundleOptions<C extends CustomAtRules> = Omit<TransformOptions<C>, 'code'>;

export interface BundleAsyncOptions extends BundleOptions {
export interface BundleAsyncOptions<C extends CustomAtRules> extends BundleOptions<C> {
resolver?: Resolver;
}

Expand Down Expand Up @@ -299,7 +372,7 @@ export interface ErrorLocation extends Location {
* Compiles a CSS file, including optionally minifying and lowering syntax to the given
* targets. A source map may also be generated, but this is not enabled by default.
*/
export declare function transform(options: TransformOptions): TransformResult;
export declare function transform<C extends CustomAtRules>(options: TransformOptions<C>): TransformResult;

export interface TransformAttributeOptions {
/** The filename in which the style attribute appeared. Used for error messages and dependencies. */
Expand Down Expand Up @@ -329,7 +402,7 @@ export interface TransformAttributeOptions {
* For optimal performance, visitors should be as specific as possible about what types of values
* they care about so that JavaScript has to be called as little as possible.
*/
visitor?: Visitor
visitor?: Visitor<never>
}

export interface TransformAttributeResult {
Expand All @@ -355,14 +428,14 @@ export declare function browserslistToTargets(browserslist: string[]): Targets;
/**
* Bundles a CSS file and its dependencies, inlining @import rules.
*/
export declare function bundle(options: BundleOptions): TransformResult;
export declare function bundle<C extends CustomAtRules>(options: BundleOptions<C>): TransformResult;

/**
* Bundles a CSS file and its dependencies asynchronously, inlining @import rules.
*/
export declare function bundleAsync(options: BundleAsyncOptions): Promise<TransformResult>;
export declare function bundleAsync<C extends CustomAtRules>(options: BundleAsyncOptions<C>): Promise<TransformResult>;

/**
* Composes multiple visitor objects into a single one.
*/
export declare function composeVisitors(visitors: Visitor[]): Visitor;
export declare function composeVisitors<C extends CustomAtRules>(visitors: Visitor<C>[]): Visitor<C>;
Loading