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
4 changes: 2 additions & 2 deletions .config/jest.config.cjs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/* eslint-disable no-undef */
/* eslint-disable @typescript-eslint/no-require-imports */
/* eslint-disable no-undef, @typescript-eslint/no-require-imports */

/* eslint-disable @typescript-eslint/no-unsafe-assignment */
const jestExpo = require("jest-expo/jest-preset");

Expand Down
11 changes: 11 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,17 @@
"./types": {
"types": "./types.d.ts"
},
"./style-collection": {
"source": "./src/runtime/native/style-collection/index.ts",
"import": {
"types": "./dist/typescript/module/src/style-collection/index.d.ts",
"default": "./dist/module/style-collection/index.js"
},
"require": {
"types": "./dist/typescript/commonjs/src/style-collection/index.d.ts",
"default": "./dist/commonjs/style-collection/index.js"
}
},
"./metro": {
"source": "./src/metro/index.ts",
"import": {
Expand Down
3 changes: 2 additions & 1 deletion src/compiler/compiler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -105,9 +105,10 @@ export function compile(code: Buffer | string, options: CompilerOptions = {}) {

// 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,
filename: options.filename ?? "style.css",
projectRoot: options.projectRoot ?? process.cwd(),
});

return {
Expand Down
3 changes: 2 additions & 1 deletion src/compiler/compiler.types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,9 @@ import type { MediaFeatureNameFor_MediaFeatureId } from "lightningcss";
import { VAR_SYMBOL } from "../runtime/native/reactivity";

export interface CompilerOptions {
filename?: string;
projectRoot?: string;
inlineRem?: number | false;
grouping?: (string | RegExp)[];
selectorPrefix?: string;
stylesheetOrder?: number;
features?: FeatureFlagRecord;
Expand Down
3 changes: 2 additions & 1 deletion src/jest/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,9 @@ import { Appearance, Dimensions } from "react-native";

import { inspect } from "node:util";

import { StyleCollection } from "react-native-css/style-collection";

import { compile, type CompilerOptions } from "../compiler";
import { StyleCollection } from "../runtime/native/injection";
import { colorScheme, dimensions, rem } from "../runtime/native/reactivity";

declare global {
Expand Down
10 changes: 6 additions & 4 deletions src/metro/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -53,16 +53,18 @@ export function withReactNativeCSS<
const originalMiddleware = config.server?.enhanceMiddleware;
const originalResolver = config.resolver?.resolveRequest;

const poisonPillPath = "./poison.pill";

return {
...config,
transformerPath: require.resolve("./metro-transformer"),
transformer: {
...config.transformer,
reactNativeCSS: options,
},
resolver: {
...config.resolver,
sourceExts: [...(config?.resolver?.sourceExts || []), "css"],
resolveRequest: (context, moduleName, platform) => {
if (moduleName === poisonPillPath) {
if (moduleName.includes("poison.pill")) {
return { type: "empty" };
}

Expand Down Expand Up @@ -161,7 +163,7 @@ export function withReactNativeCSS<
// Let the transformer know that we will handle compilation
customTransformOptions: {
...transformOptions.customTransformOptions,
reactNativeCSSCompile: false,
reactNativeCSS: options,
},
},
fileBuffer,
Expand Down
16 changes: 6 additions & 10 deletions src/metro/injection-code.ts
Original file line number Diff line number Diff line change
@@ -1,17 +1,13 @@
/**
* This is a hack around Expo's handling of CSS files.
* This is a hack around Metro's handling of bundles.
* When a component is inside a lazy() barrier, it is inside a different JS bundle.
* So when it updates, it only updates its local bundle, not the global one which contains the CSS files.
*
* To fix this, we force our code to always import the CSS files.
* Now the CSS files are in every bundle.
* This means that the CSS file will not be re-evaluated when a component in a different bundle updates,
* breaking tools like Tailwind CSS
*
* We achieve this by collecting all CSS files and injecting them into a special file
* which is included inside react-native-css's runtime.
*
* This is why both of these function add imports for the CSS files.
* To fix this, we force our code to always import the CSS files, so now the CSS files are in every bundle.
*/

export function getWebInjectionCode(filePaths: string[]) {
const importStatements = filePaths
.map((filePath) => `import "${filePath}";`)
Expand All @@ -27,12 +23,12 @@ export function getNativeInjectionCode(
const importStatements = cssFilePaths
.map((filePath) => `import "${filePath}";`)
.join("\n");
const importPath = `import { StyleCollection } from "./api";`;

const contents = values
.map((value) => `StyleCollection.inject(${JSON.stringify(value)});`)
.join("\n");

return Buffer.from(
`${importStatements}\n${importPath}\n${contents};export {};`,
`import { StyleCollection } from "react-native-css/style-collection";\n${importStatements}\n${contents};export {};`,
);
}
34 changes: 25 additions & 9 deletions src/metro/metro-transformer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,28 +5,44 @@ import type {
TransformResponse,
} from "metro-transform-worker";

import { compile, type CompilerOptions } from "../compiler";
import { getNativeInjectionCode } from "./injection-code";

const worker =
// eslint-disable-next-line @typescript-eslint/no-require-imports, @typescript-eslint/no-unsafe-argument
require(unstable_transformerPath) as typeof import("metro-transform-worker");

export function transform(
export async function transform(
config: JsTransformerConfig,
projectRoot: string,
filePath: string,
data: Buffer,
options: JsTransformOptions,
options: JsTransformOptions & {
reactNativeCSS?: CompilerOptions | undefined;
},
): Promise<TransformResponse> {
const isCss = options.type !== "asset" && /\.(s?css|sass)$/.test(filePath);
const skipCompile =
options.customTransformOptions &&
"reactNativeCSSCompile" in options.customTransformOptions &&
options.customTransformOptions.reactNativeCSSCompile === false;

if (!isCss || skipCompile) {
if (options.platform === "web" || !isCss) {
return worker.transform(config, projectRoot, filePath, data, options);
}

// TODO - compile the CSS file inline
const cssFile = (await worker.transform(config, projectRoot, filePath, data, {
...options,
platform: "web",
})) as TransformResponse & {
output: [{ data: { css: { code: Buffer } } }];
};

const css = cssFile.output[0].data.css.code.toString();

const productionJS = compile(css, {
...options.reactNativeCSS,
filename: filePath,
projectRoot: projectRoot,
}).stylesheet();

data = Buffer.from(getNativeInjectionCode([], [productionJS]));

return worker.transform(config, projectRoot, filePath, data, options);
return worker.transform(config, projectRoot, `${filePath}.js`, data, options);
}
20 changes: 10 additions & 10 deletions src/poison.pill.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,9 @@ const canWarn = process.env.NODE_ENV !== "test";
if (canWarn) {
throw new Error(`react-native-css has encountered a setup error.

┌─────-─┐
| Metro |
└─────-─┘
┌──────┐
Metro
└──────┘

Either your metro.config.js is missing the 'withReactNativeCSS' wrapper OR
the resolver.resolveRequest function of your config is being overridden, and not calling the parent resolver.
Expand All @@ -19,15 +19,15 @@ module.exports = with3rdPartyPlugin(
)
\`\`\`

┌─────------─┐
| NativeWind |
└─────------─┘
┌────────────┐
NativeWind
└────────────┘

If you are using NativeWind with the 'withNativeWind' function, follow the Metro instructions above, but use 'withNativeWind' instead of 'withReactNativeCSS'.
If you are using NativeWind follow the Metro instructions above but use 'withNativeWind' instead of 'withReactNativeCSS'.

┌─────----------─┐
| Other bundlers |
└─────----------─┘
┌────────────────┐
Other bundlers
└────────────────┘

If you are using another bundler (Vite, Webpack, etc), or non-Metro framework (Next.js, Remix, etc), please ensure you have included 'react-native-css/babel' as a babel preset.
`);
Expand Down
2 changes: 1 addition & 1 deletion src/runtime/native/api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ import {
} from "./reactivity";
import { resolveValue } from "./styles/resolve";

export { StyleCollection } from "./injection";
export { StyleCollection } from "react-native-css/style-collection";

/**
* Generates a new Higher-Order component the wraps the base component and applies the styles.
Expand Down
132 changes: 0 additions & 132 deletions src/runtime/native/injection.ts

This file was deleted.

3 changes: 2 additions & 1 deletion src/runtime/native/react/rules.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
/* eslint-disable */
import { StyleCollection } from "react-native-css/style-collection";

import type { InlineVariable, StyleRule } from "../../../compiler";
import { getDeepPath } from "../../utils";
import { testRule } from "../conditions";
import { DEFAULT_CONTAINER_NAME } from "../conditions/container-query";
import type { RenderGuard } from "../conditions/guards";
import { StyleCollection } from "../injection";
import {
activeFamily,
containerLayoutFamily,
Expand Down
3 changes: 2 additions & 1 deletion src/runtime/native/styles/shorthands/animation.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
/* eslint-disable */
import { StyleCollection } from "react-native-css/style-collection";

import { applyShorthand } from "../../../utils";
import { StyleCollection } from "../../injection";
import type { StyleFunctionResolver } from "../resolve";
import { shorthandHandler } from "./_handler";

Expand Down
6 changes: 5 additions & 1 deletion src/runtime/native/styles/variables.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,11 @@
import {
rootVariables,
universalVariables,
} from "react-native-css/style-collection";

import type { StyleDescriptor, StyleFunction } from "../../../compiler";
import { isStyleDescriptorArray } from "../../utils";
import { VAR_SYMBOL, type Getter } from "../reactivity";
import { rootVariables, universalVariables } from "../root";
import type { ResolveValueOptions, SimpleResolveValue } from "./resolve";

export function varResolver(
Expand Down
Loading
Loading