@webjsdev/cli 0.8.6 → 0.9.1
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 +1 -1
- package/bin/webjs.js +4 -4
- package/lib/create.js +50 -14
- package/package.json +1 -1
- package/templates/.env.example +1 -1
- package/templates/CONVENTIONS.md +12 -1
package/README.md
CHANGED
package/bin/webjs.js
CHANGED
|
@@ -12,8 +12,8 @@ const [cmd, ...rest] = process.argv.slice(2);
|
|
|
12
12
|
const TEMPLATES = ['full-stack', 'api', 'saas'];
|
|
13
13
|
|
|
14
14
|
const USAGE = `webjs commands:
|
|
15
|
-
webjs dev [--port
|
|
16
|
-
webjs start [--port
|
|
15
|
+
webjs dev [--port 8080] Start dev server with live reload
|
|
16
|
+
webjs start [--port 8080] Start production server (serves source directly, no build step)
|
|
17
17
|
webjs test [--server|--browser] Run server + browser tests
|
|
18
18
|
webjs check Validate app against conventions
|
|
19
19
|
webjs create <name> [--template full-stack|api|saas] [--no-install] Scaffold a new webjs app
|
|
@@ -41,7 +41,7 @@ async function main() {
|
|
|
41
41
|
// If we're already inside the --watch child, start the server directly.
|
|
42
42
|
if (process.env.__WEBJS_DEV_CHILD === '1') {
|
|
43
43
|
const { startServer } = await import('@webjsdev/server');
|
|
44
|
-
const port = Number(flag(rest, '--port', process.env.PORT ||
|
|
44
|
+
const port = Number(flag(rest, '--port', process.env.PORT || 8080));
|
|
45
45
|
await startServer({ appDir: process.cwd(), port, dev: true });
|
|
46
46
|
break;
|
|
47
47
|
}
|
|
@@ -81,7 +81,7 @@ async function main() {
|
|
|
81
81
|
}
|
|
82
82
|
case 'start': {
|
|
83
83
|
const { startServer } = await import('@webjsdev/server');
|
|
84
|
-
const port = Number(flag(rest, '--port', process.env.PORT ||
|
|
84
|
+
const port = Number(flag(rest, '--port', process.env.PORT || 8080));
|
|
85
85
|
await startServer({ appDir: process.cwd(), port, dev: false });
|
|
86
86
|
break;
|
|
87
87
|
}
|
package/lib/create.js
CHANGED
|
@@ -15,6 +15,7 @@ import { mkdir, writeFile, readFile, cp } from 'node:fs/promises';
|
|
|
15
15
|
import { join, resolve, dirname } from 'node:path';
|
|
16
16
|
import { fileURLToPath } from 'node:url';
|
|
17
17
|
import { existsSync } from 'node:fs';
|
|
18
|
+
import { createRequire } from 'node:module';
|
|
18
19
|
import { spawnSync } from 'node:child_process';
|
|
19
20
|
|
|
20
21
|
/**
|
|
@@ -54,14 +55,33 @@ const TEMPLATES = resolve(__dirname, '..', 'templates');
|
|
|
54
55
|
// directly from disk at create time so the scaffolded app boots ready for
|
|
55
56
|
// `webjs ui add` without an HTTP round-trip during scaffolding.
|
|
56
57
|
//
|
|
57
|
-
//
|
|
58
|
-
// packages/
|
|
59
|
-
//
|
|
60
|
-
//
|
|
61
|
-
//
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
58
|
+
// <ui-pkg-root>/packages/registry/components/*.ts
|
|
59
|
+
// <ui-pkg-root>/packages/registry/lib/utils.ts
|
|
60
|
+
// <ui-pkg-root>/packages/registry/themes/index.css
|
|
61
|
+
//
|
|
62
|
+
// Locate <ui-pkg-root> via Node's module resolver rather than path
|
|
63
|
+
// arithmetic off __dirname. The old `__dirname/../../ui/packages/registry`
|
|
64
|
+
// form assumed @webjsdev/ui was a hoisted sibling of @webjsdev/cli under
|
|
65
|
+
// node_modules/@webjsdev/. That holds for npm/yarn's default hoisting, but
|
|
66
|
+
// breaks on nested layouts (pnpm's isolated linker, `npm install
|
|
67
|
+
// --install-strategy=nested`, some CI setups), where @webjsdev/ui lives at
|
|
68
|
+
// node_modules/@webjsdev/cli/node_modules/@webjsdev/ui and the arithmetic
|
|
69
|
+
// resolves to a path that doesn't exist, failing `webjs create`.
|
|
70
|
+
//
|
|
71
|
+
// @webjsdev/ui's package.json isn't reachable via require.resolve (its
|
|
72
|
+
// `exports` map doesn't expose `./package.json`), so resolve the package
|
|
73
|
+
// entry and walk up to the directory that owns its package.json.
|
|
74
|
+
function resolveUiRegistryRoot() {
|
|
75
|
+
const require = createRequire(import.meta.url);
|
|
76
|
+
let dir = dirname(require.resolve('@webjsdev/ui'));
|
|
77
|
+
while (!existsSync(join(dir, 'package.json'))) {
|
|
78
|
+
const parent = dirname(dir);
|
|
79
|
+
if (parent === dir) break; // reached filesystem root; give up
|
|
80
|
+
dir = parent;
|
|
81
|
+
}
|
|
82
|
+
return resolve(dir, 'packages', 'registry');
|
|
83
|
+
}
|
|
84
|
+
const UI_REGISTRY_ROOT = resolveUiRegistryRoot();
|
|
65
85
|
|
|
66
86
|
/**
|
|
67
87
|
* Read a single @webjsdev/ui registry component, rewrite its relative import
|
|
@@ -622,10 +642,21 @@ export default function RootLayout({ children }: { children: unknown }) {
|
|
|
622
642
|
<script>
|
|
623
643
|
(function(){
|
|
624
644
|
try {
|
|
625
|
-
var
|
|
626
|
-
|
|
627
|
-
|
|
645
|
+
var mq = window.matchMedia('(prefers-color-scheme: light)');
|
|
646
|
+
function apply(){
|
|
647
|
+
var t = null;
|
|
648
|
+
try { t = localStorage.getItem('webjs_theme'); } catch (_) {}
|
|
649
|
+
var el = document.documentElement;
|
|
650
|
+
if (t === 'light' || t === 'dark') el.dataset.theme = t;
|
|
651
|
+
else delete el.dataset.theme;
|
|
652
|
+
// Keep shadcn's .dark class in sync with the effective theme so the
|
|
653
|
+
// copied ui-* components (button, card, etc.) follow light/dark too.
|
|
654
|
+
// Dark is the default unless the OS prefers light or 'light' is set.
|
|
655
|
+
var dark = t === 'dark' || (t !== 'light' && !mq.matches);
|
|
656
|
+
el.classList.toggle('dark', dark);
|
|
628
657
|
}
|
|
658
|
+
apply();
|
|
659
|
+
mq.addEventListener('change', apply);
|
|
629
660
|
} catch (_) {}
|
|
630
661
|
})();
|
|
631
662
|
</script>
|
|
@@ -854,8 +885,13 @@ export class ThemeToggle extends WebComponent {
|
|
|
854
885
|
if (next === 'system') localStorage.removeItem('webjs_theme');
|
|
855
886
|
else localStorage.setItem('webjs_theme', next);
|
|
856
887
|
} catch {}
|
|
857
|
-
|
|
858
|
-
|
|
888
|
+
const el = document.documentElement;
|
|
889
|
+
if (next === 'system') delete el.dataset.theme;
|
|
890
|
+
else el.dataset.theme = next;
|
|
891
|
+
// Keep shadcn's .dark class in sync so the ui-* components follow the theme.
|
|
892
|
+
const dark = next === 'dark'
|
|
893
|
+
|| (next === 'system' && !window.matchMedia('(prefers-color-scheme: light)').matches);
|
|
894
|
+
el.classList.toggle('dark', dark);
|
|
859
895
|
}
|
|
860
896
|
|
|
861
897
|
render() {
|
|
@@ -1008,7 +1044,7 @@ For AI agents, read this before editing scaffolded files:
|
|
|
1008
1044
|
console.log(`
|
|
1009
1045
|
Next steps:
|
|
1010
1046
|
${runCommand}
|
|
1011
|
-
# → http://localhost:
|
|
1047
|
+
# → http://localhost:8080
|
|
1012
1048
|
|
|
1013
1049
|
Optional:
|
|
1014
1050
|
${uiNote}
|
package/package.json
CHANGED
package/templates/.env.example
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
# Opinionated defaults: set these and the framework auto-configures.
|
|
5
5
|
|
|
6
6
|
# ── Server ──────────────────────────────────────────────────────────
|
|
7
|
-
PORT=
|
|
7
|
+
PORT=8080
|
|
8
8
|
|
|
9
9
|
# ── Auth (required for authentication) ──────────────────────────────
|
|
10
10
|
# Generate: node -e "console.log(require('crypto').randomBytes(32).toString('hex'))"
|
package/templates/CONVENTIONS.md
CHANGED
|
@@ -213,7 +213,7 @@ variables control infrastructure (no config files needed):
|
|
|
213
213
|
| `AUTH_SECRET` | Required for auth JWT signing (32+ random chars) |
|
|
214
214
|
| `AUTH_GOOGLE_ID` | Google OAuth client ID (optional) |
|
|
215
215
|
| `AUTH_GITHUB_ID` | GitHub OAuth client ID (optional) |
|
|
216
|
-
| `PORT` | Server port (default:
|
|
216
|
+
| `PORT` | Server port (default: 8080) |
|
|
217
217
|
| `WEBJS_PUBLIC_*` | Any env var starting with this prefix is exposed to the browser as `process.env.WEBJS_PUBLIC_X`. Components can read it directly. No build step, no transform. Use for API base URLs, Stripe publishable keys, analytics IDs, anything that is intended to be visible client-side. |
|
|
218
218
|
|
|
219
219
|
**Server-only by default.** Any env var without the `WEBJS_PUBLIC_` prefix never reaches the browser. Reading `process.env.DATABASE_URL` from a component returns `undefined`, the same as a typo. The prefix is fail-closed: secrets cannot accidentally leak.
|
|
@@ -568,6 +568,17 @@ or a build-step pipeline. The framework has no hard dependency on Tailwind.
|
|
|
568
568
|
If you mix custom CSS into a light-DOM component, apply the class-prefix
|
|
569
569
|
rule (see Components section above).
|
|
570
570
|
|
|
571
|
+
**Dark mode uses two signals; a theme switch must set both.** The editorial
|
|
572
|
+
chrome tokens (`--fg`, `--bg`, `--accent`) follow a `data-theme` attribute on
|
|
573
|
+
`<html>`; the Webjs UI kit under `components/ui/` follows a `.dark` class
|
|
574
|
+
(`@custom-variant dark (&:is(.dark *))`). The scaffold's head init script and
|
|
575
|
+
`theme-toggle` set **both** (`data-theme` AND
|
|
576
|
+
`classList.toggle('dark', isDark)`). If you replace the toggle or build your
|
|
577
|
+
own theme switch, set both, or the `components/ui/*` render light tokens on a
|
|
578
|
+
dark page (white buttons and cards, invisible text) while the chrome looks
|
|
579
|
+
correct. Light mode hides this (both systems default to light), so **verify
|
|
580
|
+
dark mode in a browser, not just light.**
|
|
581
|
+
|
|
571
582
|
---
|
|
572
583
|
|
|
573
584
|
## Styling alternative: vanilla CSS end-to-end
|