Skip to content

Commit 039de9b

Browse files
committed
major: refactored test, added timeouts on file generation
1 parent afc9655 commit 039de9b

File tree

16 files changed

+243
-178
lines changed

16 files changed

+243
-178
lines changed

README.md

Lines changed: 31 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
11
# React Zero‑UI (Beta)
22

3-
**Instant UI state updates. ZERO React re‑renders. <1 KB runtime.**
3+
**Instant UI state updates. ZERO React re‑renders. <1 KB runtime.**
4+
5+
Pre‑render your UI once, flip a `data-*` attribute to update — that's it.
46

5-
Pre‑render your UI once, flip a `data-*` attribute to update — that's it.
6-
77
<a href="https://www.npmjs.com/package/@austinserb/react-zero-ui" target="_blank" rel="noopener noreferrer"><img src="https://badgen.net/bundlephobia/min/@austinserb/[email protected]" alt="npm version" />
88
</a>
99

@@ -13,19 +13,19 @@
1313

1414
## 🚀 Live Demo
1515

16-
| Example | Link | What it shows | Link to Code |
17-
| --------------------------------------- | ---------------------------------------------------------------------------------------- | ------------------------------------------------------------- |------------------------------------------------------------------------------ |
18-
| Interactive menu with render tracker | <a href="https://react-zero-ui.vercel.app/" target="_blank" rel="noopener noreferrer"><strong>Main Demo↗</strong></a> | Compare Zero‑UI vs. React side‑by‑side while toggling a menu. | <a href="https://github.com/Austin1serb/React-Zero-UI/tree/main/examples/demo" target="_blank" rel="noopener noreferrer">Github</a> |
19-
| React benchmark (10 000 nested nodes) | <a href="https://react-zero-ui.vercel.app/react" target="_blank" rel="noopener noreferrer"><strong>React 10k↗</strong></a> | How long the traditional React render path takes. | <a href="https://github.com/Austin1serb/React-Zero-UI/tree/main/examples/demo/src/app/react" target="_blank" rel="noopener noreferrer">Github</a> |
20-
| Zero‑UI benchmark (10 000 nested nodes) | <a href="https://react-zero-ui.vercel.app/zero-ui" target="_blank" rel="noopener noreferrer"><strong style="text-align: nowrap;">Zero‑UI 10k↗</strong></a> | Identical DOM, but powered by Zero‑UI's `data-*` switch. | <a href="https://github.com/Austin1serb/React-Zero-UI/tree/main/examples/demo/src/app/zero-ui" target="_blank" rel="noopener noreferrer">Github</a> |
16+
| Example | Link | What it shows | Link to Code |
17+
| --------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------- |
18+
| Interactive menu with render tracker | <a href="https://react-zero-ui.vercel.app/" target="_blank" rel="noopener noreferrer"><strong>Main Demo↗</strong></a> | Compare Zero‑UI vs. React side‑by‑side while toggling a menu. | <a href="https://github.com/Austin1serb/React-Zero-UI/tree/main/examples/demo" target="_blank" rel="noopener noreferrer">Github</a> |
19+
| React benchmark (10 000 nested nodes) | <a href="https://react-zero-ui.vercel.app/react" target="_blank" rel="noopener noreferrer"><strong>React 10k↗</strong></a> | How long the traditional React render path takes. | <a href="https://github.com/Austin1serb/React-Zero-UI/tree/main/examples/demo/src/app/react" target="_blank" rel="noopener noreferrer">Github</a> |
20+
| Zero‑UI benchmark (10 000 nested nodes) | <a href="https://react-zero-ui.vercel.app/zero-ui" target="_blank" rel="noopener noreferrer"><strong style="text-align: nowrap;">Zero‑UI 10k↗</strong></a> | Identical DOM, but powered by Zero‑UI's `data-*` switch. | <a href="https://github.com/Austin1serb/React-Zero-UI/tree/main/examples/demo/src/app/zero-ui" target="_blank" rel="noopener noreferrer">Github</a> |
2121

2222
---
2323

2424
## 🧐 Why Zero‑UI?
2525

26-
Every `setState` in React triggers the full VDOM → Diff → Reconciliation → Paint pipeline. For *pure UI state* (themes, menus, toggles) that work is wasted.
26+
Every `setState` in React triggers the full VDOM → Diff → Reconciliation → Paint pipeline. For _pure UI state_ (themes, menus, toggles) that work is wasted.
2727

28-
**Zero‑UI introduces "*PRE‑rendering*":**
28+
**Zero‑UI introduces "_PRE‑rendering_":**
2929

3030
1. Tailwind variants for every state are **generated at build‑time**.
3131
2. The app **pre‑renders once**.
@@ -75,8 +75,8 @@ Then follow **Setup →** for your bundler.
7575
import { zeroUIPlugin } from '@austinserb/react-zero-ui/vite';
7676

7777
export default {
78-
// ❗️Remove the default `tailwindcss()` plugin — Zero‑UI extends it internally
79-
plugins: [zeroUIPlugin()],
78+
// ❗️Remove the default `tailwindcss()` plugin — Zero‑UI extends it internally
79+
plugins: [zeroUIPlugin()],
8080
};
8181
```
8282

@@ -90,24 +90,19 @@ export default {
9090
// or: import { bodyAttributes } from '../.zero-ui/attributes';
9191

9292
export default function RootLayout({ children }) {
93-
return (
94-
<html lang="en">
95-
<body {...bodyAttributes}>{children}</body>
96-
</html>
97-
);
93+
return (
94+
<html lang="en">
95+
<body {...bodyAttributes}>{children}</body>
96+
</html>
97+
);
9898
}
9999
```
100100

101-
2. **Add the PostCSS plugin (must come *before* Tailwind).**
101+
2. **Add the PostCSS plugin (must come _before_ Tailwind).**
102102

103103
```js
104104
// postcss.config.js
105-
module.exports = {
106-
plugins: {
107-
'@austinserb/react-zero-ui/postcss': {},
108-
tailwindcss: {},
109-
},
110-
};
105+
module.exports = { plugins: { '@austinserb/react-zero-ui/postcss': {}, tailwindcss: {} } };
111106
```
112107

113108
---
@@ -120,11 +115,9 @@ export default {
120115
import { useUI } from '@austinserb/react-zero-ui';
121116

122117
export function ThemeToggle() {
123-
const [, setTheme] = useUI<'light' | 'dark'>('theme', 'light');
118+
const [, setTheme] = useUI<'light' | 'dark'>('theme', 'light');
124119

125-
return (
126-
<button onClick={() => setTheme('dark')}>Switch to dark</button>
127-
);
120+
return <button onClick={() => setTheme('dark')}>Switch to dark</button>;
128121
}
129122
```
130123

@@ -141,15 +134,12 @@ Consume the state anywhere with Tailwind variants:
141134
### `useUI(key, defaultValue)`
142135

143136
```ts
144-
const [staleValue, setValue] = useUI<'open' | 'closed'>(
145-
'sidebar',
146-
'closed',
147-
);
137+
const [staleValue, setValue] = useUI<'open' | 'closed'>('sidebar', 'closed');
148138
```
149139

150-
* `key` → becomes `data-{key}` on `<body>`.
151-
* `defaultValue` → optional, prevents FOUC.
152-
* **Note:** the returned `staleValue` does **not** update (`useUI` is write‑only).
140+
- `key` → becomes `data-{key}` on `<body>`.
141+
- `defaultValue` → optional, prevents FOUC.
142+
- **Note:** the returned `staleValue` does **not** update (`useUI` is write‑only).
153143

154144
### Tailwind variants
155145

@@ -172,12 +162,12 @@ Any `data-{key}="{value}"` pair becomes a variant: `{key}-{value}:`.
172162

173163
## ✅ Features
174164

175-
* **Zero React re‑renders** for UI‑only state.
176-
* **Global setters** — call from any component or util.
177-
* **Tiny**: < 1 KB gzipped runtime.
178-
* **TypeScript‑first**.
179-
* **SSR‑friendly** (Next.js & Vite SSR).
180-
* **Framework‑agnostic CSS** — generated classes work in plain HTML / Vue / Svelte as well.
165+
- **Zero React re‑renders** for UI‑only state.
166+
- **Global setters** — call from any component or util.
167+
- **Tiny**: < 1 KB gzipped runtime.
168+
- **TypeScript‑first**.
169+
- **SSR‑friendly** (Next.js & Vite SSR).
170+
- **Framework‑agnostic CSS** — generated classes work in plain HTML / Vue / Svelte as well.
181171

182172
---
183173

packages/core/__tests__/config/playwright.next.config.js

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -13,13 +13,21 @@ export default defineConfig({
1313
workers: 2,
1414
timeout: 30_000,
1515
expect: { timeout: 15_000 },
16-
reporter: 'html',
16+
reporter: [
17+
['list'], // Shows test results in terminal
18+
],
19+
20+
use: {
21+
headless: true,
22+
baseURL: BASE_URL,
23+
// Show console logs from page/browser
24+
trace: 'retain-on-failure',
25+
},
1726

18-
use: { headless: true, baseURL: BASE_URL },
27+
globalSetup: '../e2e/nextSetup.js',
1928

2029
projects: [
21-
{ name: 'setup', testMatch: /nextSetup\.js/ },
22-
{ name: 'next-cli-e2e', dependencies: ['setup'], testMatch: /cli-next\.spec\.js/ },
30+
{ name: 'next-cli-e2e', testMatch: /cli-next\.spec\.js/ },
2331
{ name: 'next-e2e', dependencies: ['next-cli-e2e'], testMatch: /next\.spec\.js/ },
2432
],
2533
webServer: {

packages/core/__tests__/config/playwright.vite.config.js

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -13,16 +13,24 @@ export default defineConfig({
1313
workers: 2,
1414
timeout: 30_000,
1515
expect: { timeout: 15_000 },
16-
reporter: 'html',
16+
reporter: [
17+
['list'], // Shows test results in terminal
18+
['html'] // Also keep HTML report
19+
],
20+
21+
use: {
22+
headless: true,
23+
baseURL: BASE_URL,
24+
// Show console logs from page/browser
25+
trace: 'retain-on-failure',
26+
},
1727

18-
use: { headless: true, baseURL: BASE_URL },
28+
globalSetup: '../e2e/viteSetup.js',
1929

2030
// One project = one fixture app (Next, Vite, etc.)
2131
projects: [
22-
{ name: 'setup', testMatch: /viteSetup\.js/ },
2332
{
2433
name: 'vite-cli-e2e',
25-
dependencies: ['setup'],
2634
testMatch: /cli-vite\.spec\.js/, // Matches both cli-vite.spec.js and vite.spec.js
2735
},
2836
{

packages/core/__tests__/e2e/nextSetup.js

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,5 +15,10 @@ export default async function globalSetup() {
1515
const zeroUiCli = await loadCliFromFixture(projectDir);
1616
await zeroUiCli([]);
1717

18+
// Wait for 5 seconds to make sure the file system is stable
19+
console.log('[Global Setup] ⏳ Waiting 5 seconds for file system to stabilize...');
20+
await new Promise(resolve => setTimeout(resolve, 5000));
21+
1822
console.log('[Global Setup] ✅ Next.js fixture setup complete!✅');
23+
1924
}

packages/core/__tests__/e2e/viteSetup.js

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,4 +16,10 @@ export default async function globalSetup() {
1616
await zeroUiCli([]);
1717

1818
console.log('[Global Setup] ✅ Vite fixture setup complete!✅');
19+
20+
// Wait for 10 seconds to make sure the file system is stable
21+
console.log('[Global Setup] ⏳ Waiting 5 seconds for file system to stabilize...');
22+
await new Promise(resolve => setTimeout(resolve, 5000));
23+
return;
24+
1925
}
Lines changed: 10 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,11 @@
1+
import { bodyAttributes } from "@zero-ui/attributes";
12
import './globals.css';
2-
3-
export default function RootLayout({ children }) {
4-
return (
5-
<html lang="en">
6-
<body>{children}</body>
7-
</html>
8-
);
9-
}
3+
export default function RootLayout({
4+
children
5+
}) {
6+
return <html lang="en">
7+
<body {...bodyAttributes} className="bg-red test-ww this is to test the body tag" id="88">
8+
{children}
9+
</body>
10+
</html>;
11+
}
Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
11
// postcss.config.mjs
2-
const config = { plugins: ['@austinserb/react-zero-ui/postcss', '@tailwindcss/postcss'] };
3-
export default config;
2+
const config = {
3+
plugins: ["@austinserb/react-zero-ui/postcss", '@tailwindcss/postcss']
4+
};
5+
export default config;
Lines changed: 41 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,43 @@
11
{
2-
"compilerOptions": {
3-
"lib": ["dom", "dom.iterable", "esnext"],
4-
"allowJs": true,
5-
"skipLibCheck": true,
6-
"strict": false,
7-
"noEmit": true,
8-
"incremental": true,
9-
"module": "ESNext",
10-
"esModuleInterop": true,
11-
"moduleResolution": "node",
12-
"resolveJsonModule": true,
13-
"isolatedModules": true,
14-
"jsx": "preserve",
15-
"plugins": [{ "name": "next" }],
16-
"target": "ES2017",
17-
"baseUrl": ".",
18-
"paths": { "@zero-ui/attributes": ["./.zero-ui/attributes.js"] }
19-
},
20-
"include": ["next-env.d.ts", ".next/types/**/*.ts", "**/*.ts", "**/*.tsx", ".zero-ui/**/*.d.ts", ".next/**/*.d.ts"],
21-
"exclude": ["node_modules"]
2+
"compilerOptions": {
3+
"lib": [
4+
"dom",
5+
"dom.iterable",
6+
"esnext"
7+
],
8+
"allowJs": true,
9+
"skipLibCheck": true,
10+
"strict": false,
11+
"noEmit": true,
12+
"incremental": true,
13+
"module": "ESNext",
14+
"esModuleInterop": true,
15+
"moduleResolution": "node",
16+
"resolveJsonModule": true,
17+
"isolatedModules": true,
18+
"jsx": "preserve",
19+
"plugins": [
20+
{
21+
"name": "next"
22+
}
23+
],
24+
"target": "ES2017",
25+
"baseUrl": ".",
26+
"paths": {
27+
"@zero-ui/attributes": [
28+
"./.zero-ui/attributes.js"
29+
]
30+
}
31+
},
32+
"include": [
33+
"next-env.d.ts",
34+
".next/types/**/*.ts",
35+
"**/*.ts",
36+
"**/*.tsx",
37+
".next/**/*.d.ts",
38+
".zero-ui/**/*.d.ts"
39+
],
40+
"exclude": [
41+
"node_modules"
42+
]
2243
}
Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,8 @@
1-
import zeroUI from '@austinserb/react-zero-ui/vite';
1+
import zeroUI from "@austinserb/react-zero-ui/vite";
22
import { defineConfig } from 'vite';
33
import react from '@vitejs/plugin-react';
4+
45
// https://vite.dev/config/
5-
export default defineConfig({ plugins: [react(), zeroUI()] });
6+
export default defineConfig({
7+
plugins: [react(), zeroUI()]
8+
});
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
import fs from 'fs';
2+
3+
/**
4+
* Overwrites a given file with the provided content.
5+
*
6+
* @param {string} filePath - Absolute path to the file (e.g., projectDir/layout.tsx)
7+
* @param {string} content - New content to write
8+
*/
9+
export async function overwriteFile(filePath, content) {
10+
if (!fs.existsSync(filePath)) {
11+
console.warn(`[Reset] ⚠️ File not found: ${filePath} — skipping overwrite.`);
12+
return;
13+
}
14+
15+
fs.writeFileSync(filePath, content);
16+
console.log(`[Reset] ✅ Overwrote: ${filePath}`);
17+
18+
await new Promise(resolve => setTimeout(resolve, 1000));
19+
console.log(`[Reset] ✅ Wait complete, continuing...`);
20+
}

0 commit comments

Comments
 (0)