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
3 changes: 2 additions & 1 deletion .config/cspell.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
"xcworkspace"
],
"words": [
"colorjs",
"prebuild"
]
}
}
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,7 @@
"!**/__mocks__"
],
"dependencies": {
"colorjs.io": "0.6.0-alpha.1",
"comment-json": "^4.2.5",
"debug": "^4.4.1"
},
Expand Down
13 changes: 13 additions & 0 deletions src/compiler/__tests__/compiler.test.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,18 @@
import { compile } from "../compiler";

test("reads global CSS variables", () => {
const compiled = compile(`
@layer theme {
:root, :host {
--color-red-500: oklch(63.7% 0.237 25.331);
}
}`);

expect(compiled).toStrictEqual({
vr: [["color-red-500", ["#fb2c36"]]],
});
});

test.skip("test compiler", () => {
const compiled = compile(`
.test {
Expand Down
3 changes: 3 additions & 0 deletions src/compiler/add.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
import { isStyleDescriptorArray } from "../runtime/utils/style-value";
import type {
CompilerCollection,
CompilerOptions,
EasingFunction,
StyleDeclaration,
StyleDescriptor,
Expand All @@ -17,6 +18,7 @@ export function buildAddFn(
rule: StyleRule,
collection: CompilerCollection,
mapping: StyleRuleMapping,
options: CompilerOptions,
) {
let staticDeclarations: Record<string, StyleDescriptor> | undefined;

Expand Down Expand Up @@ -100,6 +102,7 @@ export function buildAddFn(

if (property.startsWith("--")) {
if (
options.stripUnusedVariables &&
!property.startsWith("--__rn-css") &&
!collection.varUsageCount.has(property)
) {
Expand Down
106 changes: 73 additions & 33 deletions src/compiler/compiler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,15 @@ import {
transform as lightningcss,
type ContainerRule,
type MediaQuery as CSSMediaQuery,
type CustomAtRules,
type Declaration,
type DeclarationBlock,
type MediaRule,
type ParsedComponent,
type Rule,
type SelectorList,
type TokenOrValue,
type Visitor,
} from "lightningcss";

import { Specificity } from "../runtime/utils/specificity";
Expand Down Expand Up @@ -61,8 +63,9 @@ type ReactNativeAtRule = {
*/
export function compile(
code: Buffer | string,
{ logger = debug("react-native-css"), ...options }: CompilerOptions = {},
options: CompilerOptions = {},
): ReactNativeCssStyleSheet {
const { logger = debug("react-native-css") } = options;
const features = Object.assign({}, options.features);

logger(`Features ${JSON.stringify(features)}`);
Expand Down Expand Up @@ -100,35 +103,44 @@ export function compile(
}
};

const customAtRules: CustomAtRules = {
"react-native": {
body: "declaration-list",
},
};

const visitor: Visitor<typeof customAtRules> = {
Rule(rule) {
if (isReactNativeAtRule(rule)) {
extractReactNativeOptions(rule, collection);
}
},
StyleSheetExit(sheet) {
logger(`Found ${sheet.rules.length} rules to process`);

for (const rule of sheet.rules) {
// Extract the style declarations and animations from the current rule
extractRule(rule, collection, {}, options);
// We have processed this rule, so now delete it from the AST
}

logger(`Exiting lightningcss`);
},
};

if (options.stripUnusedVariables) {
visitor.Declaration = (decl) => {
if (decl.property !== "unparsed" && decl.property !== "custom") return;
decl.value.value.forEach((varObj) => onVarUsage(varObj));
return decl;
};
}

// Use the lightningcss library to traverse the CSS AST and extract style declarations and animations
lightningcss({
filename: "style.css", // This is ignored, but required
code: typeof code === "string" ? new TextEncoder().encode(code) : code,
visitor: {
Declaration(decl) {
// Track variable usage, we remove any unused variables
if (decl.property !== "unparsed" && decl.property !== "custom") return;
decl.value.value.forEach((varObj) => onVarUsage(varObj));
return decl;
},
StyleSheetExit(sheet) {
logger(`Found ${sheet.rules.length} rules to process`);

for (const rule of sheet.rules) {
// Extract the style declarations and animations from the current rule
extractRule(rule, collection);
// We have processed this rule, so now delete it from the AST
}

logger(`Exiting lightningcss`);
},
},
customAtRules: {
"react-native": {
prelude: "<custom-ident>+",
body: "declaration-list",
},
},
visitor,
});

logger(`Found ${collection.rules.size} valid rules`);
Expand Down Expand Up @@ -184,6 +196,7 @@ function extractRule(
rule: ExtractableRules,
collection: CompilerCollection,
partialStyle: Partial<StyleRule> = {},
options: CompilerOptions,
) {
// Check the rule's type to determine which extraction function to call
switch (rule.type) {
Expand All @@ -209,6 +222,7 @@ function extractRule(
rule.value.declarations,
collection,
parseReactNativeStyleAtRule(rule.value.rules),
options,
)) {
setStyleForSelectorList(
{ ...partialStyle, ...style },
Expand All @@ -220,11 +234,33 @@ function extractRule(
}
break;
}
case "custom": {
if (isReactNativeAtRule(rule)) {
extractReactNativeOptions(rule, collection);
case "layer-block":
for (const layerRule of rule.value.rules) {
extractRule(layerRule, collection, partialStyle, options);
}
}
break;
case "custom":
case "font-face":
case "font-palette-values":
case "font-feature-values":
case "namespace":
case "layer-statement":
case "property":
case "view-transition":
case "ignored":
case "unknown":
case "import":
case "page":
case "supports":
case "counter-style":
case "moz-document":
case "nesting":
case "nested-declarations":
case "viewport":
case "custom-media":
case "scope":
case "starting-style":
break;
}
}

Expand Down Expand Up @@ -377,7 +413,7 @@ function extractMedia(mediaRule: MediaRule, collection: CompilerCollection) {

// Iterate over all rules in the mediaRule and extract their styles using the updated CompilerCollection
for (const rule of mediaRule.rules) {
extractRule(rule, collection, { m });
extractRule(rule, collection, { m }, options);
}
}

Expand Down Expand Up @@ -405,7 +441,7 @@ function extractedContainer(
query.n = containerRule.name;
}

extractRule(rule, collection, { cq: [query] });
extractRule(rule, collection, { cq: [query] }, options);
}
}

Expand Down Expand Up @@ -564,6 +600,7 @@ function getExtractedStyles(
declarationBlock: DeclarationBlock<Declaration>,
collection: CompilerCollection,
mapping: StyleRuleMapping = {},
options: CompilerOptions,
): StyleRule[] {
const extractedStyles = [];

Expand All @@ -577,6 +614,7 @@ function getExtractedStyles(
collection,
specificity,
mapping,
options,
),
);
}
Expand All @@ -592,6 +630,7 @@ function getExtractedStyles(
collection,
specificity,
mapping,
options,
),
);
}
Expand All @@ -604,6 +643,7 @@ function declarationsToStyle(
collection: CompilerCollection,
specificity: SpecificityArray,
mapping: StyleRuleMapping,
options: CompilerOptions,
): StyleRule {
const extractedStyle: StyleRule = {
s: [...specificity],
Expand All @@ -617,7 +657,7 @@ function declarationsToStyle(
// TODO
};

const addFn = buildAddFn(extractedStyle, collection, mapping);
const addFn = buildAddFn(extractedStyle, collection, mapping, options);

for (const declaration of declarations) {
parseDeclaration(declaration, parseDeclarationOptions, addFn, addWarning);
Expand Down
2 changes: 2 additions & 0 deletions src/compiler/compiler.types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@ export interface CompilerOptions {
stylesheetOrder?: number;
features?: FeatureFlagRecord;
logger?: (message: string) => void;
/** Strip unused variables declarations. Defaults: false */
stripUnusedVariables?: boolean;
/** @internal */
ignorePropertyWarningRegex?: (string | RegExp)[];
}
Expand Down
Loading