redweb 0.11.0 → 0.12.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/CHANGELOG.md CHANGED
@@ -1,6 +1,12 @@
1
- # Changelog
2
-
3
- ## 0.11.0
1
+ # Changelog
2
+
3
+ ## 0.12.0
4
+
5
+ - Added `npx redweb init [directory]` to create a minimal TypeScript + TSX application without overwriting existing files.
6
+ - Added the reusable `redweb/tsconfig.json` preset so TypeScript builds and editors resolve Redweb's JSX runtime consistently.
7
+ - Updated the Live HTML examples to inherit the shared preset and added real-filesystem CLI, generated-project compilation, and packed-package verification.
8
+
9
+ ## 0.11.0
4
10
 
5
11
  - Added dependency-free server-side TSX rendering through `redweb/jsx-runtime` and `redweb/jsx-dev-runtime`, with fragments, function components, automatic text and attribute escaping, safe URL validation, boolean attributes, and direct interoperability with existing `HtmlFragment` values.
6
12
  - Added a compiled TSX Live HTML example plus real HTTP, WebSocket, type-checking, and packed-consumer verification.
package/README.md CHANGED
@@ -7,8 +7,18 @@ Version 0.9 adds production-minded multiplayer building blocks while preserving
7
7
  ## Install
8
8
 
9
9
  ```bash
10
- npm install redweb
11
- ```
10
+ npm install redweb
11
+ ```
12
+
13
+ Start a TypeScript + TSX project with Redweb's compiler preset and a small server-rendered page:
14
+
15
+ ```bash
16
+ npx redweb init
17
+ npm install
18
+ npm run dev
19
+ ```
20
+
21
+ Pass a directory to create a new project there: `npx redweb init my-app`. Existing files are never overwritten, so rerunning the command is safe.
12
22
 
13
23
  ## Exports
14
24
 
@@ -45,14 +55,18 @@ const {
45
55
 
46
56
  TSX is the concise default for new pages. It renders straight to Redweb's existing `HtmlFragment`; there is no React dependency, virtual DOM, hydration pass, or client component runtime:
47
57
 
48
- ```json
49
- {
50
- "compilerOptions": {
51
- "jsx": "react-jsx",
52
- "jsxImportSource": "redweb"
53
- }
54
- }
55
- ```
58
+ Extend Redweb's TypeScript preset so builds and editors use the dependency-free JSX runtime consistently:
59
+
60
+ ```json
61
+ {
62
+ "extends": "redweb/tsconfig.json",
63
+ "compilerOptions": {
64
+ "rootDir": "src",
65
+ "outDir": "dist"
66
+ },
67
+ "include": ["src/**/*.ts", "src/**/*.tsx"]
68
+ }
69
+ ```
56
70
 
57
71
  ```tsx
58
72
  import { LivePage, action, component, page, start, state } from 'redweb';
package/bin/redweb.js ADDED
@@ -0,0 +1,20 @@
1
+ #!/usr/bin/env node
2
+ 'use strict';
3
+
4
+ const path = require('path');
5
+ const ProjectInitializer = require('../src/cli/ProjectInitializer');
6
+ const { version } = require('../package.json');
7
+
8
+ const [command, target = '.', ...extra] = process.argv.slice(2);
9
+
10
+ if (command === 'init' && extra.length === 0) {
11
+ const result = new ProjectInitializer(version).initialize(path.resolve(process.cwd(), target));
12
+ console.log(`Redweb project ready in ${result.root}`);
13
+ if (result.created.length) console.log(`Created: ${result.created.join(', ')}`);
14
+ if (result.skipped.length) console.log(`Kept existing: ${result.skipped.join(', ')}`);
15
+ console.log('Next: npm install && npm run dev');
16
+ } else {
17
+ const stream = command === undefined || command === '--help' || command === '-h' ? process.stdout : process.stderr;
18
+ stream.write('Usage: redweb init [directory]\n');
19
+ if (stream === process.stderr) process.exitCode = 1;
20
+ }
@@ -0,0 +1,14 @@
1
+ {
2
+ "$schema": "https://json.schemastore.org/tsconfig",
3
+ "compilerOptions": {
4
+ "target": "ES2022",
5
+ "module": "NodeNext",
6
+ "moduleResolution": "NodeNext",
7
+ "jsx": "react-jsx",
8
+ "jsxImportSource": "redweb",
9
+ "strict": true,
10
+ "experimentalDecorators": false,
11
+ "useDefineForClassFields": true,
12
+ "skipLibCheck": false
13
+ }
14
+ }
package/docs/LIVE_HTML.md CHANGED
@@ -4,7 +4,20 @@ Live HTML is Redweb's decorator-first server-rendering layer. It uses the existi
4
4
 
5
5
  ## TSX rendering
6
6
 
7
- New pages can return TSX directly. Configure TypeScript with `"jsx": "react-jsx"` and `"jsxImportSource": "redweb"`; Redweb supplies its own dependency-free JSX runtimes and renders immediately to `HtmlFragment` values:
7
+ New pages can return TSX directly. Run `npx redweb init` for a starter project, or extend `redweb/tsconfig.json` from an existing project's root `tsconfig.json`. The preset makes builds and editors use Redweb's dependency-free JSX runtime consistently:
8
+
9
+ ```json
10
+ {
11
+ "extends": "redweb/tsconfig.json",
12
+ "compilerOptions": {
13
+ "rootDir": "src",
14
+ "outDir": "dist"
15
+ },
16
+ "include": ["src/**/*.ts", "src/**/*.tsx"]
17
+ }
18
+ ```
19
+
20
+ Redweb renders TSX immediately to `HtmlFragment` values:
8
21
 
9
22
  ```tsx
10
23
  import { LivePage, action, component, page, state } from 'redweb';
@@ -1,15 +1,9 @@
1
- {
2
- "compilerOptions": {
3
- "target": "ES2022",
4
- "module": "CommonJS",
5
- "moduleResolution": "Node",
6
- "jsx": "react-jsx",
7
- "jsxImportSource": "redweb",
8
- "strict": true,
9
- "experimentalDecorators": false,
10
- "useDefineForClassFields": true,
11
- "skipLibCheck": false,
12
- "baseUrl": "../..",
1
+ {
2
+ "extends": "../../config/tsconfig.json",
3
+ "compilerOptions": {
4
+ "module": "CommonJS",
5
+ "moduleResolution": "Node",
6
+ "baseUrl": "../..",
13
7
  "paths": {
14
8
  "redweb": ["."],
15
9
  "redweb/*": ["*"]
package/package.json CHANGED
@@ -1,9 +1,12 @@
1
1
  {
2
2
  "name": "redweb",
3
- "version": "0.11.0",
4
- "description": "A way to quickly set up an express server",
3
+ "version": "0.12.0",
4
+ "description": "A small Node.js foundation for HTTP, WebSockets, multiplayer services, and server-rendered HTML",
5
5
  "main": "index.js",
6
6
  "types": "index.d.ts",
7
+ "bin": {
8
+ "redweb": "bin/redweb.js"
9
+ },
7
10
  "exports": {
8
11
  ".": {
9
12
  "types": "./index.d.ts",
@@ -29,6 +32,7 @@
29
32
  "require": "./jsx-dev-runtime.js",
30
33
  "default": "./jsx-dev-runtime.js"
31
34
  },
35
+ "./tsconfig.json": "./config/tsconfig.json",
32
36
  "./package.json": "./package.json"
33
37
  },
34
38
  "scripts": {
@@ -53,6 +57,8 @@
53
57
  "test": "npx jest"
54
58
  },
55
59
  "files": [
60
+ "bin",
61
+ "config",
56
62
  "src/*",
57
63
  "client.js",
58
64
  "client.d.ts",
@@ -0,0 +1,30 @@
1
+ 'use strict';
2
+
3
+ const fs = require('fs');
4
+ const path = require('path');
5
+ const { projectFiles } = require('./templates');
6
+
7
+ class ProjectInitializer {
8
+ constructor(version) {
9
+ this.files = projectFiles(version);
10
+ }
11
+
12
+ initialize(target) {
13
+ const root = path.resolve(target);
14
+ const created = [];
15
+ const skipped = [];
16
+ for (const file of this.files) {
17
+ const destination = path.join(root, file.path);
18
+ fs.mkdirSync(path.dirname(destination), { recursive: true });
19
+ if (fs.existsSync(destination)) {
20
+ skipped.push(file.path);
21
+ continue;
22
+ }
23
+ fs.writeFileSync(destination, file.content, { encoding: 'utf8', flag: 'wx' });
24
+ created.push(file.path);
25
+ }
26
+ return Object.freeze({ root, created: Object.freeze(created), skipped: Object.freeze(skipped) });
27
+ }
28
+ }
29
+
30
+ module.exports = ProjectInitializer;
@@ -0,0 +1,87 @@
1
+ 'use strict';
2
+
3
+ const TYPESCRIPT_CONFIG = `${JSON.stringify({
4
+ extends: 'redweb/tsconfig.json',
5
+ compilerOptions: { rootDir: 'src', outDir: 'dist' },
6
+ include: ['src/**/*.ts', 'src/**/*.tsx'],
7
+ }, null, 2)}\n`;
8
+
9
+ const APP_SOURCE = `import path from 'node:path';
10
+ import { page, start } from 'redweb';
11
+
12
+ @page('/', { css: 'app.css', live: false })
13
+ class HomePage {
14
+ render() {
15
+ return (
16
+ <main class="home">
17
+ <span class="eyebrow">Redweb</span>
18
+ <h1>Your server-rendered app is ready.</h1>
19
+ <p>Edit <code>src/app.tsx</code>, then build again.</p>
20
+ </main>
21
+ );
22
+ }
23
+ }
24
+
25
+ start(HomePage, {
26
+ port: Number(process.env.PORT ?? 8181),
27
+ templateRoot: path.resolve('src'),
28
+ });
29
+ `;
30
+
31
+ const APP_STYLES = `:root {
32
+ color-scheme: dark;
33
+ font-family: Inter, ui-sans-serif, system-ui, sans-serif;
34
+ background: #08090d;
35
+ color: #fff;
36
+ }
37
+
38
+ body { margin: 0; }
39
+
40
+ .home {
41
+ width: min(42rem, calc(100% - 2rem));
42
+ margin: 18vh auto 0;
43
+ }
44
+
45
+ .eyebrow {
46
+ color: #ff5064;
47
+ font-size: 0.75rem;
48
+ font-weight: 800;
49
+ letter-spacing: 0.18em;
50
+ text-transform: uppercase;
51
+ }
52
+
53
+ h1 {
54
+ margin: 0.75rem 0;
55
+ font-size: clamp(2.5rem, 8vw, 5rem);
56
+ line-height: 0.98;
57
+ }
58
+
59
+ p { color: rgb(255 255 255 / 65%); }
60
+ code { color: #ff8795; }
61
+ `;
62
+
63
+ function projectManifest(version) {
64
+ return `${JSON.stringify({
65
+ name: 'redweb-app',
66
+ private: true,
67
+ version: '0.0.0',
68
+ scripts: {
69
+ build: 'tsc',
70
+ start: 'node dist/app.js',
71
+ dev: 'npm run build && npm start',
72
+ },
73
+ dependencies: { redweb: `^${version}` },
74
+ devDependencies: { typescript: '^5.9.3' },
75
+ }, null, 2)}\n`;
76
+ }
77
+
78
+ function projectFiles(version) {
79
+ return Object.freeze([
80
+ Object.freeze({ path: 'package.json', content: projectManifest(version) }),
81
+ Object.freeze({ path: 'tsconfig.json', content: TYPESCRIPT_CONFIG }),
82
+ Object.freeze({ path: 'src/app.tsx', content: APP_SOURCE }),
83
+ Object.freeze({ path: 'src/app.css', content: APP_STYLES }),
84
+ ]);
85
+ }
86
+
87
+ module.exports = { projectFiles };