@zvndev/darkmown 0.8.1 → 0.9.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/AGENTS.md +1 -1
- package/CHANGELOG.md +24 -2
- package/README.md +15 -5
- package/docs/cli.md +2 -0
- package/docs/spec-alignment.md +8 -4
- package/package.json +17 -3
- package/site/_/nav.skin +7 -0
- package/site/pages/docs/index.wd +1 -1
- package/site/pages/index.skin +165 -6
- package/site/pages/index.wd +95 -6
- package/src/builder.js +60 -1
- package/src/cli.js +14 -1
- package/src/compiler.js +530 -21
- package/src/config.js +19 -0
- package/src/dev.js +9 -0
- package/src/router.js +26 -0
- package/src/runtime.js +129 -29
- package/src/scaffold.js +12 -0
- package/src/skin.js +31 -3
- package/src/statics.js +20 -0
- package/types/builder.d.ts +39 -0
- package/types/compiler.d.ts +184 -0
- package/types/config.d.ts +45 -0
- package/types/dev.d.ts +13 -0
- package/types/index.d.ts +33 -0
- package/types/router.d.ts +39 -0
- package/types/scaffold.d.ts +8 -0
- package/types/skin.d.ts +21 -0
- package/types/statics.d.ts +23 -0
- package/docs/okf-integration.md +0 -120
package/src/builder.js
CHANGED
|
@@ -6,16 +6,36 @@ import { createPaths } from "./config.js";
|
|
|
6
6
|
import { discoverRoutes, outputPathForRoute } from "./router.js";
|
|
7
7
|
import { compileSkin } from "./skin.js";
|
|
8
8
|
|
|
9
|
+
/**
|
|
10
|
+
* @typedef {import("./config.js").Paths} Paths
|
|
11
|
+
* @typedef {import("./compiler.js").Assets} Assets
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* A built route entry written to `dist/routes.json`.
|
|
16
|
+
* @typedef {object} RouteManifestEntry
|
|
17
|
+
* @property {string} route Public route path.
|
|
18
|
+
* @property {string} file Source file path, relative to cwd, POSIX-separated.
|
|
19
|
+
* @property {{ skins: string[], scripts: string[], runtime: boolean }} assets
|
|
20
|
+
*/
|
|
21
|
+
|
|
9
22
|
const moduleDir = path.dirname(fileURLToPath(import.meta.url));
|
|
10
23
|
|
|
24
|
+
/**
|
|
25
|
+
* Compile the whole site under `cwd` into `dist`, returning the route manifest.
|
|
26
|
+
* @param {string} [cwd] Project working directory (defaults to `process.cwd()`).
|
|
27
|
+
* @returns {{ routes: RouteManifestEntry[], distRoot: string }}
|
|
28
|
+
*/
|
|
11
29
|
export function buildSite(cwd = process.cwd()) {
|
|
12
30
|
const paths = createPaths(cwd);
|
|
13
31
|
fs.rmSync(paths.distRoot, { recursive: true, force: true });
|
|
14
32
|
fs.mkdirSync(paths.distRoot, { recursive: true });
|
|
15
33
|
emitShelfAssets(paths);
|
|
16
34
|
const routes = discoverRoutes(paths.routesRoot);
|
|
35
|
+
/** @type {RouteManifestEntry[]} */
|
|
17
36
|
const manifest = [];
|
|
18
37
|
|
|
38
|
+
/** @type {Set<string>} */
|
|
19
39
|
const warned = new Set();
|
|
20
40
|
|
|
21
41
|
for (const route of routes) {
|
|
@@ -45,12 +65,41 @@ export function buildSite(cwd = process.cwd()) {
|
|
|
45
65
|
return { routes: manifest, distRoot: paths.distRoot };
|
|
46
66
|
}
|
|
47
67
|
|
|
68
|
+
/**
|
|
69
|
+
* Strip JSDoc `/** ... *\/` blocks from the runtime before it ships.
|
|
70
|
+
* The source keeps full type annotations (for `checkJs` + `.d.ts`), but the
|
|
71
|
+
* browser never needs to download them — this keeps the shipped runtime lean
|
|
72
|
+
* and the gzip budget honest (measured against what users actually receive).
|
|
73
|
+
* Only `/** ... *\/` blocks are removed; code and string literals are untouched.
|
|
74
|
+
* @param {string} source
|
|
75
|
+
* @returns {string}
|
|
76
|
+
*/
|
|
77
|
+
export function stripRuntimeComments(source) {
|
|
78
|
+
return source
|
|
79
|
+
// Remove the JSDoc block text only — never the surrounding newlines, so an
|
|
80
|
+
// inline `/** @type {x} */ code` keeps its line and never glues onto a
|
|
81
|
+
// preceding `//` comment.
|
|
82
|
+
.replace(/\/\*\*[\s\S]*?\*\//g, "")
|
|
83
|
+
// Drop the now blank/whitespace-only lines the removed blocks leave behind.
|
|
84
|
+
.replace(/^[ \t]*\n/gm, "");
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
/**
|
|
88
|
+
* @param {Paths} paths
|
|
89
|
+
* @returns {void}
|
|
90
|
+
*/
|
|
48
91
|
function emitRuntime(paths) {
|
|
49
92
|
const out = path.join(paths.distRoot, "__wd/runtime.js");
|
|
50
93
|
fs.mkdirSync(path.dirname(out), { recursive: true });
|
|
51
|
-
fs.
|
|
94
|
+
const source = fs.readFileSync(path.join(moduleDir, "runtime.js"), "utf8");
|
|
95
|
+
fs.writeFileSync(out, stripRuntimeComments(source));
|
|
52
96
|
}
|
|
53
97
|
|
|
98
|
+
/**
|
|
99
|
+
* @param {Assets} assets
|
|
100
|
+
* @param {Paths} paths
|
|
101
|
+
* @returns {void}
|
|
102
|
+
*/
|
|
54
103
|
function emitAssets(assets, paths) {
|
|
55
104
|
for (const [source, href] of assets.files) {
|
|
56
105
|
const out = path.join(paths.distRoot, href);
|
|
@@ -63,6 +112,11 @@ function emitAssets(assets, paths) {
|
|
|
63
112
|
}
|
|
64
113
|
}
|
|
65
114
|
|
|
115
|
+
/**
|
|
116
|
+
* Copy non-page shelf assets (JSON data, media) into `dist/__wd`.
|
|
117
|
+
* @param {Paths} paths
|
|
118
|
+
* @returns {void}
|
|
119
|
+
*/
|
|
66
120
|
function emitShelfAssets(paths) {
|
|
67
121
|
if (!fs.existsSync(paths.shelfRoot)) return;
|
|
68
122
|
for (const file of walk(paths.shelfRoot)) {
|
|
@@ -76,7 +130,12 @@ function emitShelfAssets(paths) {
|
|
|
76
130
|
}
|
|
77
131
|
}
|
|
78
132
|
|
|
133
|
+
/**
|
|
134
|
+
* @param {string} dir
|
|
135
|
+
* @returns {string[]}
|
|
136
|
+
*/
|
|
79
137
|
function walk(dir) {
|
|
138
|
+
/** @type {string[]} */
|
|
80
139
|
const files = [];
|
|
81
140
|
for (const entry of fs.readdirSync(dir, { withFileTypes: true })) {
|
|
82
141
|
const abs = path.join(dir, entry.name);
|
package/src/cli.js
CHANGED
|
@@ -29,7 +29,9 @@ if (command === "help" || command === "--help" || command === "-h") {
|
|
|
29
29
|
const port = Number(process.env.PORT || 5173);
|
|
30
30
|
const distRoot = path.join(process.cwd(), "dist");
|
|
31
31
|
buildSite();
|
|
32
|
+
/** @type {Set<import("node:http").ServerResponse>} */
|
|
32
33
|
const clients = new Set();
|
|
34
|
+
/** @type {ReturnType<typeof setTimeout> | undefined} */
|
|
33
35
|
let timer;
|
|
34
36
|
|
|
35
37
|
// Rebuild in a child process so changes to Darkmown's own src/ always load
|
|
@@ -94,7 +96,7 @@ if (command === "help" || command === "--help" || command === "-h") {
|
|
|
94
96
|
serveDev(distRoot, url, res);
|
|
95
97
|
} catch (error) {
|
|
96
98
|
res.writeHead(500, { "content-type": "text/plain" });
|
|
97
|
-
res.end(error.stack || String(error));
|
|
99
|
+
res.end((error instanceof Error && error.stack) || String(error));
|
|
98
100
|
}
|
|
99
101
|
});
|
|
100
102
|
server.listen(port, () => {
|
|
@@ -117,6 +119,12 @@ if (command === "help" || command === "--help" || command === "-h") {
|
|
|
117
119
|
process.exit(1);
|
|
118
120
|
}
|
|
119
121
|
|
|
122
|
+
/**
|
|
123
|
+
* @param {string} distRoot
|
|
124
|
+
* @param {string} url
|
|
125
|
+
* @param {import("node:http").ServerResponse} res
|
|
126
|
+
* @returns {void}
|
|
127
|
+
*/
|
|
120
128
|
function serveDev(distRoot, url, res) {
|
|
121
129
|
const file = resolvePublicFile(distRoot, url);
|
|
122
130
|
if (!file || !fs.existsSync(file)) {
|
|
@@ -132,6 +140,11 @@ function serveDev(distRoot, url, res) {
|
|
|
132
140
|
fs.createReadStream(file).pipe(res);
|
|
133
141
|
}
|
|
134
142
|
|
|
143
|
+
/**
|
|
144
|
+
* @param {Set<import("node:http").ServerResponse>} clients
|
|
145
|
+
* @param {string} message
|
|
146
|
+
* @returns {void}
|
|
147
|
+
*/
|
|
135
148
|
function broadcast(clients, message) {
|
|
136
149
|
for (const client of clients) client.write(message);
|
|
137
150
|
}
|