uniwind-plugin-next 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md ADDED
@@ -0,0 +1,80 @@
1
+ # uniwind-plugin-next
2
+
3
+ > This is an unofficial plugin, and is not affiliated with Uniwind or Next.js in any way.
4
+
5
+ Next.js plugin for [Uniwind](https://uniwind.dev/) support. Note that only Webpack-based projects are supported, there are no plans to support Turbopack-based projects.
6
+
7
+ The implementation of this plugin is based on the official [Uniwind Vite plugin](https://docs.uniwind.dev/quickstart#vite), and aims to match its functionality as closely as possible. All Uniwind features should work as expected - see the limitations section below for known differences.
8
+
9
+ ## Compatibility
10
+ See the table below for tested versions of `uniwind-plugin-next` and corresponding versions of `uniwind`. Other versions of `uniwind` may work, but are not guaranteed to.
11
+
12
+ Tested on `next` version `16.1`, but other versions will likely work fine.
13
+
14
+ | uniwind-plugin-next | Uniwind |
15
+ |---------------------|---------|
16
+ | `1.0.0` | `1.2.2` |
17
+
18
+ ## Installation & setup
19
+ This setup guide assumes you already have a next.js project setup with Tailwind v4
20
+
21
+ 1. Install uniwind and this plugin:
22
+
23
+ ```shell
24
+ pnpm install uniwind uniwind-plugin-next
25
+ ```
26
+
27
+ 2. Wrap next.js config with `withUniwind()`
28
+ ```ts
29
+ // next.config.ts
30
+ import type { NextConfig } from "next";
31
+ import { withUniwind } from 'uniwind-plugin-next'
32
+
33
+ const nextConfig: NextConfig = {
34
+ transpilePackages: ['react-native', 'react-native-web'],
35
+ };
36
+
37
+ // Wrap your config with `withUniwind()`
38
+ export default withUniwind(nextConfig, {
39
+ cssEntryFile: './app/globals.css',
40
+ // Takes the same options as the vite & metro plugins.
41
+ // See https://docs.uniwind.dev/api/metro-config#configuration-options
42
+ });
43
+ ```
44
+ > Note that you do not need `@expo/next-adapter` if you are using `uniwind-plugin-next`.
45
+
46
+ 3. Add the postcss plugin
47
+ ```js
48
+ const config = {
49
+ plugins: {
50
+ "@tailwindcss/postcss": {},
51
+ 'uniwind-plugin-next/postcss': {}, // Add this line
52
+ },
53
+ };
54
+
55
+ ```
56
+
57
+ 4. Add `@import 'uniwind';` to the global CSS file (or wherever you `@import 'tailwindcss'`)
58
+ ```css
59
+ /* src/app/globals.css */
60
+ @import 'tailwindcss';
61
+ @import 'uniwind';
62
+ ```
63
+
64
+ 5. Add `suppressHydrationWarning` to root `<html>` tag (in `app/layout.tsx` by default)
65
+ ```tsx
66
+ // src/app/layout.tsx
67
+ ...
68
+
69
+ return (
70
+ <html lang="en" suppressHydrationWarning>
71
+ ...
72
+ </html>
73
+ );
74
+ ```
75
+
76
+ 6. Start the dev server to generate `uniwind-types.d.ts`. Make sure that it's included in your `tsconfig.json`'s `include` array.
77
+
78
+ ## Known limitations
79
+
80
+ - This plugin uses a much more primitive regex-based postcss plugin for transforming Uniwind CSS functions (`pixelRatio()`, `fontScale()`, `hairlineWidth()`) compared to the official Vite plugin (which uses a full AST parser). As a result, some edge cases may not be handled correctly. If you do not use these functions in your CSS, this will not impact you. If you do run into any issues, please open an issue.
@@ -0,0 +1,8 @@
1
+ import { createRequire } from "node:module";
2
+
3
+ //#region rolldown:runtime
4
+ var __commonJSMin = (cb, mod) => () => (mod || cb((mod = { exports: {} }).exports, mod), mod.exports);
5
+ var __require = /* @__PURE__ */ createRequire(import.meta.url);
6
+
7
+ //#endregion
8
+ export { __require as n, __commonJSMin as t };
@@ -0,0 +1,6 @@
1
+ // Injects uniwind configuration into the bundle.
2
+ export default function uniwindConfigInjectionLoader(source) {
3
+ const { stringifiedThemes } = this.query
4
+
5
+ return `${source}\nUniwind.__reinit(() => ({}), ${stringifiedThemes});`
6
+ }
package/dist/index.cjs ADDED
@@ -0,0 +1,375 @@
1
+ //#region rolldown:runtime
2
+ var __create = Object.create;
3
+ var __defProp = Object.defineProperty;
4
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
5
+ var __getOwnPropNames = Object.getOwnPropertyNames;
6
+ var __getProtoOf = Object.getPrototypeOf;
7
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
8
+ var __copyProps = (to, from, except, desc) => {
9
+ if (from && typeof from === "object" || typeof from === "function") {
10
+ for (var keys = __getOwnPropNames(from), i = 0, n = keys.length, key; i < n; i++) {
11
+ key = keys[i];
12
+ if (!__hasOwnProp.call(to, key) && key !== except) {
13
+ __defProp(to, key, {
14
+ get: ((k) => from[k]).bind(null, key),
15
+ enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable
16
+ });
17
+ }
18
+ }
19
+ }
20
+ return to;
21
+ };
22
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", {
23
+ value: mod,
24
+ enumerable: true
25
+ }) : target, mod));
26
+
27
+ //#endregion
28
+ let webpack = require("webpack");
29
+ let path = require("path");
30
+ path = __toESM(path);
31
+ let fs = require("fs");
32
+ fs = __toESM(fs);
33
+ let _tailwindcss_node = require("@tailwindcss/node");
34
+ let lightningcss = require("lightningcss");
35
+
36
+ //#region src/webpack/uniwind/src/utils/common.ts
37
+ const uniq = (arr) => Array.from(new Set(arr));
38
+
39
+ //#endregion
40
+ //#region src/webpack/patchFsForWritingUniwindCSSFile.ts
41
+ const uniwindDir = path.default.dirname(require.resolve("uniwind/package.json"));
42
+ /**
43
+ * The contents of the `css` directory is copied direct from the uniwind source code. This makes it easier to keep it in
44
+ * sync with the upstream uniwind package, so we purposefully avoid modifying any code in `css`.
45
+ *
46
+ * However, we do need to patch `buildCSS()` to output the generated `uniwind.css` file into the `uniwind` package directory
47
+ * within node_modules. To do this we temporarily patch `fs.writeFileSync` to redirect all writes of `uniwind.css` to the
48
+ * correct location.
49
+ *
50
+ * Usage:
51
+ * ```ts
52
+ * const unpatchFs = patchFs();
53
+ * await buildCSS(themes, cssEntryFile);
54
+ * unpatchFs();
55
+ */
56
+ function patchFsForWritingUniwindCSSFile() {
57
+ const originalWriteFileSync = fs.default.writeFileSync;
58
+ fs.default.writeFileSync = function(_path, data, options) {
59
+ let newPath = _path;
60
+ if (typeof newPath === "string" && newPath.endsWith("uniwind.css")) newPath = path.default.join(uniwindDir, "uniwind.css");
61
+ return originalWriteFileSync.call(fs.default, newPath, data, options);
62
+ };
63
+ return function unpatchFs() {
64
+ fs.default.writeFileSync = originalWriteFileSync;
65
+ };
66
+ }
67
+
68
+ //#endregion
69
+ //#region src/webpack/uniwind/src/css/insets.ts
70
+ const types = ["m", "p"];
71
+ const sides = [
72
+ "",
73
+ "x",
74
+ "y",
75
+ "t",
76
+ "b",
77
+ "l",
78
+ "r"
79
+ ];
80
+ const spacing = "--spacing(--value(integer))";
81
+ const length = "--value([length])";
82
+ const generateCSSForInsets = () => {
83
+ let css = `@utility h-screen-safe {
84
+ height: calc(100vh - (env(safe-area-inset-top) + env(safe-area-inset-bottom)));
85
+ }\n`;
86
+ const getInsetsForSide = (side) => {
87
+ switch (side) {
88
+ case "t": return ["top"];
89
+ case "b": return ["bottom"];
90
+ case "l": return ["left"];
91
+ case "r": return ["right"];
92
+ case "x": return ["left", "right"];
93
+ case "y": return ["top", "bottom"];
94
+ default: return [
95
+ "top",
96
+ "bottom",
97
+ "left",
98
+ "right"
99
+ ];
100
+ }
101
+ };
102
+ types.forEach((type) => {
103
+ sides.forEach((side) => {
104
+ const styleName = type === "m" ? "margin" : "padding";
105
+ const styles = getInsetsForSide(side).map((inset) => `${styleName}-${inset}: env(safe-area-inset-${inset});`);
106
+ const safeStyles = styles.flatMap((style) => {
107
+ const styleWithoutSemicolon = style.replace(";", "");
108
+ return [styleWithoutSemicolon.replace(/: (env.*)/, (_, env) => `: max(${env}, ${spacing});`), styleWithoutSemicolon.replace(/: (env.*)/, (_, env) => `: max(${env}, ${length});`)];
109
+ });
110
+ const safeOffsetStyles = styles.flatMap((style) => {
111
+ const styleWithoutSemicolon = style.replace(";", "");
112
+ return [styleWithoutSemicolon.replace(/: (env.*)/, (_, env) => `: calc(${env} + ${spacing});`), styleWithoutSemicolon.replace(/: (env.*)/, (_, env) => `: calc(${env} + ${length});`)];
113
+ });
114
+ css += `
115
+ @utility ${type}${side}-safe {
116
+ ${styles.join("\n ")}
117
+ }
118
+
119
+ @utility ${type}${side}-safe-or-* {
120
+ ${safeStyles.join("\n ")}
121
+ }
122
+
123
+ @utility ${type}${side}-safe-offset-* {
124
+ ${safeOffsetStyles.join("\n ")}
125
+ }
126
+ `;
127
+ });
128
+ });
129
+ return css.replaceAll(" ", "").trim() + "\n";
130
+ };
131
+
132
+ //#endregion
133
+ //#region src/webpack/uniwind/src/metro/logger.ts
134
+ const red = "\x1B[91m";
135
+ const yellow = "\x1B[33m";
136
+ const blue = "\x1B[36m";
137
+ const reset = "\x1B[0m";
138
+ var Logger = class Logger {
139
+ static {
140
+ this.debug = false;
141
+ }
142
+ constructor(name$1) {
143
+ this.name = name$1;
144
+ }
145
+ static log(message, meta = "") {
146
+ if (!Logger.debug) return;
147
+ console.log(`${blue}Uniwind ${meta}- ${message}${reset}`);
148
+ }
149
+ static error(message, meta = "") {
150
+ console.log(`${red}Uniwind Error ${meta}- ${message}${reset}`);
151
+ }
152
+ static warn(message, meta = "") {
153
+ if (!Logger.debug) return;
154
+ console.log(`${yellow}Uniwind Warning ${meta}- ${message}${reset}`);
155
+ }
156
+ log(message) {
157
+ Logger.log(message, `[${this.name} Processor] `);
158
+ }
159
+ error(message) {
160
+ Logger.error(message, `[${this.name} Processor] `);
161
+ }
162
+ warn(message) {
163
+ Logger.warn(message, `[${this.name} Processor] `);
164
+ }
165
+ };
166
+
167
+ //#endregion
168
+ //#region src/webpack/uniwind/src/css/themes.ts
169
+ const readFileSafe = (filePath) => {
170
+ try {
171
+ return fs.default.readFileSync(filePath, "utf-8");
172
+ } catch {
173
+ return null;
174
+ }
175
+ };
176
+ const isExcludedDependency = (url) => [
177
+ url.includes("node_modules/tailwindcss"),
178
+ url.includes("node_modules/@tailwindcss"),
179
+ url.includes("node_modules/uniwind")
180
+ ].some(Boolean);
181
+ const generateCSSForThemes = async (themes, input) => {
182
+ const themesVariables = Object.fromEntries(themes.map((theme) => [theme, /* @__PURE__ */ new Set()]));
183
+ const findVariantsRec = async (cssPath) => {
184
+ const css = readFileSafe(cssPath);
185
+ if (css === null) return;
186
+ const { dependencies } = (0, lightningcss.transform)({
187
+ code: Buffer.from(css),
188
+ filename: "uniwind.css",
189
+ analyzeDependencies: true,
190
+ visitor: { Rule: (rule) => {
191
+ if (rule.type === "unknown" && rule.value.name === "variant") {
192
+ const [firstPrelude] = rule.value.prelude;
193
+ if (firstPrelude?.type !== "token" || firstPrelude.value.type !== "ident" || !themes.includes(firstPrelude.value.value)) return;
194
+ const theme = firstPrelude.value.value;
195
+ rule.value.block?.forEach((block) => {
196
+ if (block.type === "dashed-ident") themesVariables[theme]?.add(block.value);
197
+ });
198
+ }
199
+ } }
200
+ });
201
+ if (!Array.isArray(dependencies)) return;
202
+ const importUrls = /* @__PURE__ */ new Set();
203
+ await (0, _tailwindcss_node.compile)(dependencies.filter((dependency) => {
204
+ if (dependency.url.startsWith(".")) {
205
+ importUrls.add(path.default.resolve(path.default.dirname(cssPath), dependency.url));
206
+ return false;
207
+ }
208
+ return !isExcludedDependency(dependency.url);
209
+ }).map((dependency) => `@import "${dependency.url}";`).join("\n"), {
210
+ base: path.default.resolve(path.default.dirname(cssPath)),
211
+ onDependency: (dependency) => {
212
+ if (isExcludedDependency(dependency)) return;
213
+ importUrls.add(dependency);
214
+ }
215
+ });
216
+ for (const filePath of importUrls) await findVariantsRec(filePath);
217
+ };
218
+ await findVariantsRec(input);
219
+ let hasErrors = false;
220
+ const hasVariables = Object.values(themesVariables).some((variables) => variables.size > 0);
221
+ Object.values(themesVariables).forEach((variables) => {
222
+ Object.entries(themesVariables).forEach(([checkedTheme, checkedVariables]) => {
223
+ variables.forEach((variable) => {
224
+ if (!checkedVariables.has(variable)) {
225
+ Logger.error(`Theme ${checkedTheme} is missing variable ${variable}`);
226
+ hasErrors = true;
227
+ }
228
+ });
229
+ });
230
+ });
231
+ if (hasErrors) Logger.error("All themes must have the same variables");
232
+ const variablesCSS = hasVariables ? [
233
+ "",
234
+ "@theme {",
235
+ ...Array.from(Object.values(themesVariables).at(0) ?? []).map((variable) => ` ${variable}: unset;`),
236
+ "}"
237
+ ] : [];
238
+ return [...themes.map((theme) => `@custom-variant ${theme} (&:where(.${theme}, .${theme} *));`), ...variablesCSS].join("\n");
239
+ };
240
+
241
+ //#endregion
242
+ //#region src/webpack/uniwind/src/css/variants.ts
243
+ const variants = [
244
+ "ios",
245
+ "android",
246
+ "web",
247
+ "native"
248
+ ];
249
+ const generateCSSForVariants = () => {
250
+ let css = "";
251
+ variants.forEach((variant) => {
252
+ css += `@custom-variant ${variant} (${variant === "web" ? "html &" : `@media ${variant}`});\n`;
253
+ });
254
+ return css;
255
+ };
256
+
257
+ //#endregion
258
+ //#region src/webpack/uniwind/src/css/index.ts
259
+ const dirname = typeof __dirname !== "undefined" ? __dirname : __dirname;
260
+ const buildCSS = async (themes, input) => {
261
+ const variants$1 = generateCSSForVariants();
262
+ const insets = generateCSSForInsets();
263
+ const themesCSS = await generateCSSForThemes(themes, input);
264
+ const cssFile = path.default.join(dirname, "../../uniwind.css");
265
+ if ((fs.default.existsSync(cssFile) ? fs.default.readFileSync(cssFile, "utf-8") : "") === cssFile) return;
266
+ fs.default.writeFileSync(cssFile, [
267
+ variants$1,
268
+ insets,
269
+ themesCSS
270
+ ].join("\n"));
271
+ };
272
+
273
+ //#endregion
274
+ //#region src/webpack/uniwind/package.json
275
+ var name = "uniwind";
276
+
277
+ //#endregion
278
+ //#region src/webpack/uniwind/src/utils/buildDtsFile.ts
279
+ const buildDtsFile = (dtsPath, stringifiedThemes) => {
280
+ const oldDtsContent = fs.default.existsSync(dtsPath) ? fs.default.readFileSync(dtsPath, "utf-8") : "";
281
+ const dtsContent = [
282
+ `// NOTE: This file is generated by ${name} and it should not be edited manually.`,
283
+ `/// <reference types="${name}/types" />`,
284
+ "",
285
+ `declare module '${name}' {`,
286
+ ` export interface UniwindConfig {`,
287
+ ` themes: readonly ${stringifiedThemes}`,
288
+ ` }`,
289
+ `}`,
290
+ "",
291
+ `export {}`,
292
+ ""
293
+ ].join("\n");
294
+ if (oldDtsContent === dtsContent) return;
295
+ fs.default.writeFileSync(dtsPath, dtsContent);
296
+ };
297
+
298
+ //#endregion
299
+ //#region src/webpack/uniwind/src/utils/stringifyThemes.ts
300
+ const stringifyThemes = (themes = []) => `[${themes.map((theme) => `'${theme}'`).join(", ")}]`;
301
+
302
+ //#endregion
303
+ //#region src/webpack/UniwindWebpackPlugin.ts
304
+ var UniwindWebpackPlugin = class {
305
+ constructor({ cssEntryFile, extraThemes = [], dtsFile = "uniwind-types.d.ts" }) {
306
+ this.hasRun = false;
307
+ this.themes = uniq([
308
+ "light",
309
+ "dark",
310
+ ...extraThemes ?? []
311
+ ]);
312
+ this.dtsFile = dtsFile;
313
+ this.cssEntryFile = cssEntryFile;
314
+ }
315
+ apply(compiler) {
316
+ compiler.hooks.beforeCompile.tapPromise("UniwindWebpackPlugin", async () => {
317
+ if (this.hasRun) return;
318
+ this.hasRun = true;
319
+ const removePatch = patchFsForWritingUniwindCSSFile();
320
+ await buildCSS(this.themes, this.cssEntryFile);
321
+ removePatch();
322
+ buildDtsFile(this.dtsFile, stringifyThemes(this.themes));
323
+ });
324
+ compiler.options.module = compiler.options.module || { rules: [] };
325
+ compiler.options.module.rules.push({
326
+ test: /config\.c?js$/,
327
+ include: /uniwind[\/\\]dist/,
328
+ use: [{
329
+ loader: path.default.resolve(__dirname, "configInjectionLoader.js"),
330
+ options: { stringifiedThemes: stringifyThemes(this.themes) }
331
+ }]
332
+ });
333
+ }
334
+ };
335
+
336
+ //#endregion
337
+ //#region src/webpack/withUniwind.ts
338
+ function withUniwind(nextConfig = {}, uniwindConfig) {
339
+ return {
340
+ ...nextConfig,
341
+ transpilePackages: uniq([
342
+ ...nextConfig.transpilePackages || [],
343
+ "uniwind",
344
+ "react-native",
345
+ "react-native-web"
346
+ ]),
347
+ webpack(config, options) {
348
+ if (!config.resolve) config.resolve = {};
349
+ if (!config.plugins) config.plugins = [];
350
+ config.resolve.alias = {
351
+ ...config.resolve.alias || {},
352
+ "react-native/Libraries/EventEmitter/RCTDeviceEventEmitter$": "react-native-web/dist/vendor/react-native/NativeEventEmitter/RCTDeviceEventEmitter",
353
+ "react-native/Libraries/vendor/emitter/EventEmitter$": "react-native-web/dist/vendor/react-native/emitter/EventEmitter",
354
+ "react-native/Libraries/EventEmitter/NativeEventEmitter$": "react-native-web/dist/vendor/react-native/NativeEventEmitter"
355
+ };
356
+ config.resolve.extensions = [
357
+ ".web.js",
358
+ ".web.jsx",
359
+ ".web.ts",
360
+ ...config.resolve?.extensions ?? []
361
+ ];
362
+ config.plugins.push(new webpack.DefinePlugin({ __DEV__: JSON.stringify(process.env.NODE_ENV !== "production") }));
363
+ config.plugins.push(new webpack.NormalModuleReplacementPlugin(/^react-native$/, (resource) => {
364
+ if ((resource.context || "").includes(`${path.default.sep}uniwind${path.default.sep}dist${path.default.sep}module${path.default.sep}components${path.default.sep}web`)) resource.request = "react-native-web";
365
+ else resource.request = "uniwind/components/index";
366
+ }));
367
+ config.plugins.push(new UniwindWebpackPlugin(uniwindConfig));
368
+ if (typeof nextConfig.webpack === "function") return nextConfig.webpack(config, options);
369
+ return config;
370
+ }
371
+ };
372
+ }
373
+
374
+ //#endregion
375
+ exports.withUniwind = withUniwind;
@@ -0,0 +1,11 @@
1
+ //#region src/webpack/types.d.ts
2
+ type UniwindConfig = {
3
+ cssEntryFile: string;
4
+ extraThemes?: Array<string>;
5
+ dtsFile?: string;
6
+ };
7
+ //#endregion
8
+ //#region src/webpack/withUniwind.d.ts
9
+ declare function withUniwind(nextConfig: any | undefined, uniwindConfig: UniwindConfig): any;
10
+ //#endregion
11
+ export { withUniwind };
@@ -0,0 +1,11 @@
1
+ //#region src/webpack/types.d.ts
2
+ type UniwindConfig = {
3
+ cssEntryFile: string;
4
+ extraThemes?: Array<string>;
5
+ dtsFile?: string;
6
+ };
7
+ //#endregion
8
+ //#region src/webpack/withUniwind.d.ts
9
+ declare function withUniwind(nextConfig: any | undefined, uniwindConfig: UniwindConfig): any;
10
+ //#endregion
11
+ export { withUniwind };
package/dist/index.mjs ADDED
@@ -0,0 +1,347 @@
1
+ import { n as __require } from "./chunk-3qlvafw2.mjs";
2
+ import { DefinePlugin, NormalModuleReplacementPlugin } from "webpack";
3
+ import path from "path";
4
+ import fs from "fs";
5
+ import { compile } from "@tailwindcss/node";
6
+ import { transform } from "lightningcss";
7
+
8
+ //#region src/webpack/uniwind/src/utils/common.ts
9
+ const uniq = (arr) => Array.from(new Set(arr));
10
+
11
+ //#endregion
12
+ //#region src/webpack/patchFsForWritingUniwindCSSFile.ts
13
+ const uniwindDir = path.dirname(__require.resolve("uniwind/package.json"));
14
+ /**
15
+ * The contents of the `css` directory is copied direct from the uniwind source code. This makes it easier to keep it in
16
+ * sync with the upstream uniwind package, so we purposefully avoid modifying any code in `css`.
17
+ *
18
+ * However, we do need to patch `buildCSS()` to output the generated `uniwind.css` file into the `uniwind` package directory
19
+ * within node_modules. To do this we temporarily patch `fs.writeFileSync` to redirect all writes of `uniwind.css` to the
20
+ * correct location.
21
+ *
22
+ * Usage:
23
+ * ```ts
24
+ * const unpatchFs = patchFs();
25
+ * await buildCSS(themes, cssEntryFile);
26
+ * unpatchFs();
27
+ */
28
+ function patchFsForWritingUniwindCSSFile() {
29
+ const originalWriteFileSync = fs.writeFileSync;
30
+ fs.writeFileSync = function(_path, data, options) {
31
+ let newPath = _path;
32
+ if (typeof newPath === "string" && newPath.endsWith("uniwind.css")) newPath = path.join(uniwindDir, "uniwind.css");
33
+ return originalWriteFileSync.call(fs, newPath, data, options);
34
+ };
35
+ return function unpatchFs() {
36
+ fs.writeFileSync = originalWriteFileSync;
37
+ };
38
+ }
39
+
40
+ //#endregion
41
+ //#region src/webpack/uniwind/src/css/insets.ts
42
+ const types = ["m", "p"];
43
+ const sides = [
44
+ "",
45
+ "x",
46
+ "y",
47
+ "t",
48
+ "b",
49
+ "l",
50
+ "r"
51
+ ];
52
+ const spacing = "--spacing(--value(integer))";
53
+ const length = "--value([length])";
54
+ const generateCSSForInsets = () => {
55
+ let css = `@utility h-screen-safe {
56
+ height: calc(100vh - (env(safe-area-inset-top) + env(safe-area-inset-bottom)));
57
+ }\n`;
58
+ const getInsetsForSide = (side) => {
59
+ switch (side) {
60
+ case "t": return ["top"];
61
+ case "b": return ["bottom"];
62
+ case "l": return ["left"];
63
+ case "r": return ["right"];
64
+ case "x": return ["left", "right"];
65
+ case "y": return ["top", "bottom"];
66
+ default: return [
67
+ "top",
68
+ "bottom",
69
+ "left",
70
+ "right"
71
+ ];
72
+ }
73
+ };
74
+ types.forEach((type) => {
75
+ sides.forEach((side) => {
76
+ const styleName = type === "m" ? "margin" : "padding";
77
+ const styles = getInsetsForSide(side).map((inset) => `${styleName}-${inset}: env(safe-area-inset-${inset});`);
78
+ const safeStyles = styles.flatMap((style) => {
79
+ const styleWithoutSemicolon = style.replace(";", "");
80
+ return [styleWithoutSemicolon.replace(/: (env.*)/, (_, env) => `: max(${env}, ${spacing});`), styleWithoutSemicolon.replace(/: (env.*)/, (_, env) => `: max(${env}, ${length});`)];
81
+ });
82
+ const safeOffsetStyles = styles.flatMap((style) => {
83
+ const styleWithoutSemicolon = style.replace(";", "");
84
+ return [styleWithoutSemicolon.replace(/: (env.*)/, (_, env) => `: calc(${env} + ${spacing});`), styleWithoutSemicolon.replace(/: (env.*)/, (_, env) => `: calc(${env} + ${length});`)];
85
+ });
86
+ css += `
87
+ @utility ${type}${side}-safe {
88
+ ${styles.join("\n ")}
89
+ }
90
+
91
+ @utility ${type}${side}-safe-or-* {
92
+ ${safeStyles.join("\n ")}
93
+ }
94
+
95
+ @utility ${type}${side}-safe-offset-* {
96
+ ${safeOffsetStyles.join("\n ")}
97
+ }
98
+ `;
99
+ });
100
+ });
101
+ return css.replaceAll(" ", "").trim() + "\n";
102
+ };
103
+
104
+ //#endregion
105
+ //#region src/webpack/uniwind/src/metro/logger.ts
106
+ const red = "\x1B[91m";
107
+ const yellow = "\x1B[33m";
108
+ const blue = "\x1B[36m";
109
+ const reset = "\x1B[0m";
110
+ var Logger = class Logger {
111
+ static {
112
+ this.debug = false;
113
+ }
114
+ constructor(name$1) {
115
+ this.name = name$1;
116
+ }
117
+ static log(message, meta = "") {
118
+ if (!Logger.debug) return;
119
+ console.log(`${blue}Uniwind ${meta}- ${message}${reset}`);
120
+ }
121
+ static error(message, meta = "") {
122
+ console.log(`${red}Uniwind Error ${meta}- ${message}${reset}`);
123
+ }
124
+ static warn(message, meta = "") {
125
+ if (!Logger.debug) return;
126
+ console.log(`${yellow}Uniwind Warning ${meta}- ${message}${reset}`);
127
+ }
128
+ log(message) {
129
+ Logger.log(message, `[${this.name} Processor] `);
130
+ }
131
+ error(message) {
132
+ Logger.error(message, `[${this.name} Processor] `);
133
+ }
134
+ warn(message) {
135
+ Logger.warn(message, `[${this.name} Processor] `);
136
+ }
137
+ };
138
+
139
+ //#endregion
140
+ //#region src/webpack/uniwind/src/css/themes.ts
141
+ const readFileSafe = (filePath) => {
142
+ try {
143
+ return fs.readFileSync(filePath, "utf-8");
144
+ } catch {
145
+ return null;
146
+ }
147
+ };
148
+ const isExcludedDependency = (url) => [
149
+ url.includes("node_modules/tailwindcss"),
150
+ url.includes("node_modules/@tailwindcss"),
151
+ url.includes("node_modules/uniwind")
152
+ ].some(Boolean);
153
+ const generateCSSForThemes = async (themes, input) => {
154
+ const themesVariables = Object.fromEntries(themes.map((theme) => [theme, /* @__PURE__ */ new Set()]));
155
+ const findVariantsRec = async (cssPath) => {
156
+ const css = readFileSafe(cssPath);
157
+ if (css === null) return;
158
+ const { dependencies } = transform({
159
+ code: Buffer.from(css),
160
+ filename: "uniwind.css",
161
+ analyzeDependencies: true,
162
+ visitor: { Rule: (rule) => {
163
+ if (rule.type === "unknown" && rule.value.name === "variant") {
164
+ const [firstPrelude] = rule.value.prelude;
165
+ if (firstPrelude?.type !== "token" || firstPrelude.value.type !== "ident" || !themes.includes(firstPrelude.value.value)) return;
166
+ const theme = firstPrelude.value.value;
167
+ rule.value.block?.forEach((block) => {
168
+ if (block.type === "dashed-ident") themesVariables[theme]?.add(block.value);
169
+ });
170
+ }
171
+ } }
172
+ });
173
+ if (!Array.isArray(dependencies)) return;
174
+ const importUrls = /* @__PURE__ */ new Set();
175
+ await compile(dependencies.filter((dependency) => {
176
+ if (dependency.url.startsWith(".")) {
177
+ importUrls.add(path.resolve(path.dirname(cssPath), dependency.url));
178
+ return false;
179
+ }
180
+ return !isExcludedDependency(dependency.url);
181
+ }).map((dependency) => `@import "${dependency.url}";`).join("\n"), {
182
+ base: path.resolve(path.dirname(cssPath)),
183
+ onDependency: (dependency) => {
184
+ if (isExcludedDependency(dependency)) return;
185
+ importUrls.add(dependency);
186
+ }
187
+ });
188
+ for (const filePath of importUrls) await findVariantsRec(filePath);
189
+ };
190
+ await findVariantsRec(input);
191
+ let hasErrors = false;
192
+ const hasVariables = Object.values(themesVariables).some((variables) => variables.size > 0);
193
+ Object.values(themesVariables).forEach((variables) => {
194
+ Object.entries(themesVariables).forEach(([checkedTheme, checkedVariables]) => {
195
+ variables.forEach((variable) => {
196
+ if (!checkedVariables.has(variable)) {
197
+ Logger.error(`Theme ${checkedTheme} is missing variable ${variable}`);
198
+ hasErrors = true;
199
+ }
200
+ });
201
+ });
202
+ });
203
+ if (hasErrors) Logger.error("All themes must have the same variables");
204
+ const variablesCSS = hasVariables ? [
205
+ "",
206
+ "@theme {",
207
+ ...Array.from(Object.values(themesVariables).at(0) ?? []).map((variable) => ` ${variable}: unset;`),
208
+ "}"
209
+ ] : [];
210
+ return [...themes.map((theme) => `@custom-variant ${theme} (&:where(.${theme}, .${theme} *));`), ...variablesCSS].join("\n");
211
+ };
212
+
213
+ //#endregion
214
+ //#region src/webpack/uniwind/src/css/variants.ts
215
+ const variants = [
216
+ "ios",
217
+ "android",
218
+ "web",
219
+ "native"
220
+ ];
221
+ const generateCSSForVariants = () => {
222
+ let css = "";
223
+ variants.forEach((variant) => {
224
+ css += `@custom-variant ${variant} (${variant === "web" ? "html &" : `@media ${variant}`});\n`;
225
+ });
226
+ return css;
227
+ };
228
+
229
+ //#endregion
230
+ //#region src/webpack/uniwind/src/css/index.ts
231
+ const dirname = typeof __dirname !== "undefined" ? __dirname : import.meta.dirname;
232
+ const buildCSS = async (themes, input) => {
233
+ const variants$1 = generateCSSForVariants();
234
+ const insets = generateCSSForInsets();
235
+ const themesCSS = await generateCSSForThemes(themes, input);
236
+ const cssFile = path.join(dirname, "../../uniwind.css");
237
+ if ((fs.existsSync(cssFile) ? fs.readFileSync(cssFile, "utf-8") : "") === cssFile) return;
238
+ fs.writeFileSync(cssFile, [
239
+ variants$1,
240
+ insets,
241
+ themesCSS
242
+ ].join("\n"));
243
+ };
244
+
245
+ //#endregion
246
+ //#region src/webpack/uniwind/package.json
247
+ var name = "uniwind";
248
+
249
+ //#endregion
250
+ //#region src/webpack/uniwind/src/utils/buildDtsFile.ts
251
+ const buildDtsFile = (dtsPath, stringifiedThemes) => {
252
+ const oldDtsContent = fs.existsSync(dtsPath) ? fs.readFileSync(dtsPath, "utf-8") : "";
253
+ const dtsContent = [
254
+ `// NOTE: This file is generated by ${name} and it should not be edited manually.`,
255
+ `/// <reference types="${name}/types" />`,
256
+ "",
257
+ `declare module '${name}' {`,
258
+ ` export interface UniwindConfig {`,
259
+ ` themes: readonly ${stringifiedThemes}`,
260
+ ` }`,
261
+ `}`,
262
+ "",
263
+ `export {}`,
264
+ ""
265
+ ].join("\n");
266
+ if (oldDtsContent === dtsContent) return;
267
+ fs.writeFileSync(dtsPath, dtsContent);
268
+ };
269
+
270
+ //#endregion
271
+ //#region src/webpack/uniwind/src/utils/stringifyThemes.ts
272
+ const stringifyThemes = (themes = []) => `[${themes.map((theme) => `'${theme}'`).join(", ")}]`;
273
+
274
+ //#endregion
275
+ //#region src/webpack/UniwindWebpackPlugin.ts
276
+ var UniwindWebpackPlugin = class {
277
+ constructor({ cssEntryFile, extraThemes = [], dtsFile = "uniwind-types.d.ts" }) {
278
+ this.hasRun = false;
279
+ this.themes = uniq([
280
+ "light",
281
+ "dark",
282
+ ...extraThemes ?? []
283
+ ]);
284
+ this.dtsFile = dtsFile;
285
+ this.cssEntryFile = cssEntryFile;
286
+ }
287
+ apply(compiler) {
288
+ compiler.hooks.beforeCompile.tapPromise("UniwindWebpackPlugin", async () => {
289
+ if (this.hasRun) return;
290
+ this.hasRun = true;
291
+ const removePatch = patchFsForWritingUniwindCSSFile();
292
+ await buildCSS(this.themes, this.cssEntryFile);
293
+ removePatch();
294
+ buildDtsFile(this.dtsFile, stringifyThemes(this.themes));
295
+ });
296
+ compiler.options.module = compiler.options.module || { rules: [] };
297
+ compiler.options.module.rules.push({
298
+ test: /config\.c?js$/,
299
+ include: /uniwind[\/\\]dist/,
300
+ use: [{
301
+ loader: path.resolve(__dirname, "configInjectionLoader.js"),
302
+ options: { stringifiedThemes: stringifyThemes(this.themes) }
303
+ }]
304
+ });
305
+ }
306
+ };
307
+
308
+ //#endregion
309
+ //#region src/webpack/withUniwind.ts
310
+ function withUniwind(nextConfig = {}, uniwindConfig) {
311
+ return {
312
+ ...nextConfig,
313
+ transpilePackages: uniq([
314
+ ...nextConfig.transpilePackages || [],
315
+ "uniwind",
316
+ "react-native",
317
+ "react-native-web"
318
+ ]),
319
+ webpack(config, options) {
320
+ if (!config.resolve) config.resolve = {};
321
+ if (!config.plugins) config.plugins = [];
322
+ config.resolve.alias = {
323
+ ...config.resolve.alias || {},
324
+ "react-native/Libraries/EventEmitter/RCTDeviceEventEmitter$": "react-native-web/dist/vendor/react-native/NativeEventEmitter/RCTDeviceEventEmitter",
325
+ "react-native/Libraries/vendor/emitter/EventEmitter$": "react-native-web/dist/vendor/react-native/emitter/EventEmitter",
326
+ "react-native/Libraries/EventEmitter/NativeEventEmitter$": "react-native-web/dist/vendor/react-native/NativeEventEmitter"
327
+ };
328
+ config.resolve.extensions = [
329
+ ".web.js",
330
+ ".web.jsx",
331
+ ".web.ts",
332
+ ...config.resolve?.extensions ?? []
333
+ ];
334
+ config.plugins.push(new DefinePlugin({ __DEV__: JSON.stringify(process.env.NODE_ENV !== "production") }));
335
+ config.plugins.push(new NormalModuleReplacementPlugin(/^react-native$/, (resource) => {
336
+ if ((resource.context || "").includes(`${path.sep}uniwind${path.sep}dist${path.sep}module${path.sep}components${path.sep}web`)) resource.request = "react-native-web";
337
+ else resource.request = "uniwind/components/index";
338
+ }));
339
+ config.plugins.push(new UniwindWebpackPlugin(uniwindConfig));
340
+ if (typeof nextConfig.webpack === "function") return nextConfig.webpack(config, options);
341
+ return config;
342
+ }
343
+ };
344
+ }
345
+
346
+ //#endregion
347
+ export { withUniwind };
@@ -0,0 +1,17 @@
1
+
2
+ //#region src/postcss/index.ts
3
+ module.exports = () => {
4
+ return {
5
+ postcssPlugin: "uniwind-plugin-next",
6
+ Once(root) {
7
+ root.walkDecls((decl) => {
8
+ decl.value = decl.value.replace(/pixelRatio\(([^)]+)\)/g, "calc($1 * 1px)");
9
+ decl.value = decl.value.replace(/fontScale\(([^)]+)\)/g, "calc($1 * 1rem)");
10
+ decl.value = decl.value.replace(/hairlineWidth\(\)/g, "1px");
11
+ });
12
+ }
13
+ };
14
+ };
15
+ module.exports.postcss = true;
16
+
17
+ //#endregion
@@ -0,0 +1 @@
1
+ export { };
@@ -0,0 +1 @@
1
+ export { };
@@ -0,0 +1,23 @@
1
+ import { t as __commonJSMin } from "../chunk-3qlvafw2.mjs";
2
+
3
+ //#region src/postcss/index.ts
4
+ var require_postcss = /* @__PURE__ */ __commonJSMin(((exports, module) => {
5
+ module.exports = () => {
6
+ return {
7
+ postcssPlugin: "uniwind-plugin-next",
8
+ Once(root) {
9
+ root.walkDecls((decl) => {
10
+ decl.value = decl.value.replace(/pixelRatio\(([^)]+)\)/g, "calc($1 * 1px)");
11
+ decl.value = decl.value.replace(/fontScale\(([^)]+)\)/g, "calc($1 * 1rem)");
12
+ decl.value = decl.value.replace(/hairlineWidth\(\)/g, "1px");
13
+ });
14
+ }
15
+ };
16
+ };
17
+ module.exports.postcss = true;
18
+ }));
19
+
20
+ //#endregion
21
+ export default require_postcss();
22
+
23
+ export { };
package/package.json ADDED
@@ -0,0 +1,33 @@
1
+ {
2
+ "name": "uniwind-plugin-next",
3
+ "version": "1.0.0",
4
+ "description": "Compatibility plugin for using Uniwind with Next.js",
5
+ "devDependencies": {
6
+ "@types/node": "^25.0.3",
7
+ "tsdown": "^0.18.2",
8
+ "typescript": "^5.9.3"
9
+ },
10
+ "peerDependencies": {
11
+ "@tailwindcss/node": "^4",
12
+ "lightningcss": ">=1.30.2",
13
+ "uniwind": "~1.2.2",
14
+ "webpack": "^5"
15
+ },
16
+ "exports": {
17
+ ".": {
18
+ "require": "./dist/index.cjs",
19
+ "import": "./dist/index.mjs"
20
+ },
21
+ "./postcss": {
22
+ "require": "./dist/postcss/index.cjs",
23
+ "import": "./dist/postcss/index.mjs"
24
+ },
25
+ "./package.json": "./package.json"
26
+ },
27
+ "main": "./dist/index.cjs",
28
+ "module": "./dist/index.mjs",
29
+ "types": "./dist/index.d.cts",
30
+ "scripts": {
31
+ "build": "tsdown"
32
+ }
33
+ }