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 +9 -3
- package/README.md +24 -10
- package/bin/redweb.js +20 -0
- package/config/tsconfig.json +14 -0
- package/docs/LIVE_HTML.md +14 -1
- package/examples/live-html/tsconfig.json +6 -12
- package/package.json +8 -2
- package/src/cli/ProjectInitializer.js +30 -0
- package/src/cli/templates.js +87 -0
package/CHANGELOG.md
CHANGED
|
@@ -1,6 +1,12 @@
|
|
|
1
|
-
# Changelog
|
|
2
|
-
|
|
3
|
-
## 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
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
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.
|
|
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
|
-
"
|
|
3
|
-
|
|
4
|
-
"module": "CommonJS",
|
|
5
|
-
"moduleResolution": "Node",
|
|
6
|
-
"
|
|
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.
|
|
4
|
-
"description": "A
|
|
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 };
|