Skip to content

Commit 298bfb6

Browse files
authored
fix: production bundling (#110)
1 parent 47eaaae commit 298bfb6

File tree

16 files changed

+223
-176
lines changed

16 files changed

+223
-176
lines changed

.config/jest.config.cjs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
1-
/* eslint-disable no-undef */
2-
/* eslint-disable @typescript-eslint/no-require-imports */
1+
/* eslint-disable no-undef, @typescript-eslint/no-require-imports */
2+
33
/* eslint-disable @typescript-eslint/no-unsafe-assignment */
44
const jestExpo = require("jest-expo/jest-preset");
55

package.json

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,17 @@
1616
"./types": {
1717
"types": "./types.d.ts"
1818
},
19+
"./style-collection": {
20+
"source": "./src/runtime/native/style-collection/index.ts",
21+
"import": {
22+
"types": "./dist/typescript/module/src/style-collection/index.d.ts",
23+
"default": "./dist/module/style-collection/index.js"
24+
},
25+
"require": {
26+
"types": "./dist/typescript/commonjs/src/style-collection/index.d.ts",
27+
"default": "./dist/commonjs/style-collection/index.js"
28+
}
29+
},
1930
"./metro": {
2031
"source": "./src/metro/index.ts",
2132
"import": {

src/compiler/compiler.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -105,9 +105,10 @@ export function compile(code: Buffer | string, options: CompilerOptions = {}) {
105105

106106
// Use the lightningcss library to traverse the CSS AST and extract style declarations and animations
107107
lightningcss({
108-
filename: "style.css", // This is ignored, but required
109108
code: typeof code === "string" ? new TextEncoder().encode(code) : code,
110109
visitor,
110+
filename: options.filename ?? "style.css",
111+
projectRoot: options.projectRoot ?? process.cwd(),
111112
});
112113

113114
return {

src/compiler/compiler.types.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,9 @@ import type { MediaFeatureNameFor_MediaFeatureId } from "lightningcss";
55
import { VAR_SYMBOL } from "../runtime/native/reactivity";
66

77
export interface CompilerOptions {
8+
filename?: string;
9+
projectRoot?: string;
810
inlineRem?: number | false;
9-
grouping?: (string | RegExp)[];
1011
selectorPrefix?: string;
1112
stylesheetOrder?: number;
1213
features?: FeatureFlagRecord;

src/jest/index.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,9 @@ import { Appearance, Dimensions } from "react-native";
22

33
import { inspect } from "node:util";
44

5+
import { StyleCollection } from "react-native-css/style-collection";
6+
57
import { compile, type CompilerOptions } from "../compiler";
6-
import { StyleCollection } from "../runtime/native/injection";
78
import { colorScheme, dimensions, rem } from "../runtime/native/reactivity";
89

910
declare global {

src/metro/index.ts

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -53,16 +53,18 @@ export function withReactNativeCSS<
5353
const originalMiddleware = config.server?.enhanceMiddleware;
5454
const originalResolver = config.resolver?.resolveRequest;
5555

56-
const poisonPillPath = "./poison.pill";
57-
5856
return {
5957
...config,
6058
transformerPath: require.resolve("./metro-transformer"),
59+
transformer: {
60+
...config.transformer,
61+
reactNativeCSS: options,
62+
},
6163
resolver: {
6264
...config.resolver,
6365
sourceExts: [...(config?.resolver?.sourceExts || []), "css"],
6466
resolveRequest: (context, moduleName, platform) => {
65-
if (moduleName === poisonPillPath) {
67+
if (moduleName.includes("poison.pill")) {
6668
return { type: "empty" };
6769
}
6870

@@ -161,7 +163,7 @@ export function withReactNativeCSS<
161163
// Let the transformer know that we will handle compilation
162164
customTransformOptions: {
163165
...transformOptions.customTransformOptions,
164-
reactNativeCSSCompile: false,
166+
reactNativeCSS: options,
165167
},
166168
},
167169
fileBuffer,

src/metro/injection-code.ts

Lines changed: 6 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,13 @@
11
/**
2-
* This is a hack around Expo's handling of CSS files.
2+
* This is a hack around Metro's handling of bundles.
33
* When a component is inside a lazy() barrier, it is inside a different JS bundle.
44
* So when it updates, it only updates its local bundle, not the global one which contains the CSS files.
55
*
6-
* To fix this, we force our code to always import the CSS files.
7-
* Now the CSS files are in every bundle.
6+
* This means that the CSS file will not be re-evaluated when a component in a different bundle updates,
7+
* breaking tools like Tailwind CSS
88
*
9-
* We achieve this by collecting all CSS files and injecting them into a special file
10-
* which is included inside react-native-css's runtime.
11-
*
12-
* This is why both of these function add imports for the CSS files.
9+
* To fix this, we force our code to always import the CSS files, so now the CSS files are in every bundle.
1310
*/
14-
1511
export function getWebInjectionCode(filePaths: string[]) {
1612
const importStatements = filePaths
1713
.map((filePath) => `import "${filePath}";`)
@@ -27,12 +23,12 @@ export function getNativeInjectionCode(
2723
const importStatements = cssFilePaths
2824
.map((filePath) => `import "${filePath}";`)
2925
.join("\n");
30-
const importPath = `import { StyleCollection } from "./api";`;
26+
3127
const contents = values
3228
.map((value) => `StyleCollection.inject(${JSON.stringify(value)});`)
3329
.join("\n");
3430

3531
return Buffer.from(
36-
`${importStatements}\n${importPath}\n${contents};export {};`,
32+
`import { StyleCollection } from "react-native-css/style-collection";\n${importStatements}\n${contents};export {};`,
3733
);
3834
}

src/metro/metro-transformer.ts

Lines changed: 25 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -5,28 +5,44 @@ import type {
55
TransformResponse,
66
} from "metro-transform-worker";
77

8+
import { compile, type CompilerOptions } from "../compiler";
9+
import { getNativeInjectionCode } from "./injection-code";
10+
811
const worker =
912
// eslint-disable-next-line @typescript-eslint/no-require-imports, @typescript-eslint/no-unsafe-argument
1013
require(unstable_transformerPath) as typeof import("metro-transform-worker");
1114

12-
export function transform(
15+
export async function transform(
1316
config: JsTransformerConfig,
1417
projectRoot: string,
1518
filePath: string,
1619
data: Buffer,
17-
options: JsTransformOptions,
20+
options: JsTransformOptions & {
21+
reactNativeCSS?: CompilerOptions | undefined;
22+
},
1823
): Promise<TransformResponse> {
1924
const isCss = options.type !== "asset" && /\.(s?css|sass)$/.test(filePath);
20-
const skipCompile =
21-
options.customTransformOptions &&
22-
"reactNativeCSSCompile" in options.customTransformOptions &&
23-
options.customTransformOptions.reactNativeCSSCompile === false;
2425

25-
if (!isCss || skipCompile) {
26+
if (options.platform === "web" || !isCss) {
2627
return worker.transform(config, projectRoot, filePath, data, options);
2728
}
2829

29-
// TODO - compile the CSS file inline
30+
const cssFile = (await worker.transform(config, projectRoot, filePath, data, {
31+
...options,
32+
platform: "web",
33+
})) as TransformResponse & {
34+
output: [{ data: { css: { code: Buffer } } }];
35+
};
36+
37+
const css = cssFile.output[0].data.css.code.toString();
38+
39+
const productionJS = compile(css, {
40+
...options.reactNativeCSS,
41+
filename: filePath,
42+
projectRoot: projectRoot,
43+
}).stylesheet();
44+
45+
data = Buffer.from(getNativeInjectionCode([], [productionJS]));
3046

31-
return worker.transform(config, projectRoot, filePath, data, options);
47+
return worker.transform(config, projectRoot, `${filePath}.js`, data, options);
3248
}

src/poison.pill.ts

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,9 @@ const canWarn = process.env.NODE_ENV !== "test";
33
if (canWarn) {
44
throw new Error(`react-native-css has encountered a setup error.
55
6-
┌─────-─┐
7-
| Metro |
8-
└─────-─┘
6+
┌──────┐
7+
Metro
8+
└──────┘
99
1010
Either your metro.config.js is missing the 'withReactNativeCSS' wrapper OR
1111
the resolver.resolveRequest function of your config is being overridden, and not calling the parent resolver.
@@ -19,15 +19,15 @@ module.exports = with3rdPartyPlugin(
1919
)
2020
\`\`\`
2121
22-
┌─────------─┐
23-
| NativeWind |
24-
└─────------─┘
22+
┌────────────┐
23+
NativeWind
24+
└────────────┘
2525
26-
If you are using NativeWind with the 'withNativeWind' function, follow the Metro instructions above, but use 'withNativeWind' instead of 'withReactNativeCSS'.
26+
If you are using NativeWind follow the Metro instructions above but use 'withNativeWind' instead of 'withReactNativeCSS'.
2727
28-
┌─────----------─┐
29-
| Other bundlers |
30-
└─────----------─┘
28+
┌────────────────┐
29+
Other bundlers
30+
└────────────────┘
3131
3232
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.
3333
`);

src/runtime/native/api.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ import {
2121
} from "./reactivity";
2222
import { resolveValue } from "./styles/resolve";
2323

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

2626
/**
2727
* Generates a new Higher-Order component the wraps the base component and applies the styles.

0 commit comments

Comments
 (0)