tosijs-ui 1.6.21 → 1.6.22
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 +36 -0
- package/dist/code-editor.d.ts +9 -35
- package/dist/code-editor.js +62 -90
- package/dist/doc-system/site/dev-server.js +56 -4
- package/dist/doc-system/site/epub-cli.d.ts +1 -0
- package/dist/doc-system/site/epub-cli.js +22 -0
- package/dist/doc-system/site/epub.js +6 -0
- package/dist/doc-system/site/make-llms-txt.js +0 -16
- package/dist/doc-system/site/orchestrator.js +102 -47
- package/dist/doc-system/site/site-config.d.ts +10 -0
- package/dist/iife.js +109 -153
- package/dist/iife.js.map +6 -34
- package/dist/live-example/component.js +14 -192
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/llms.txt +2 -9
- package/package.json +1 -16
- package/tls/create-dev-certs.sh +9 -25
- package/dist/code-editor-cm.d.ts +0 -48
- package/dist/code-editor-cm.js +0 -219
|
@@ -12,31 +12,80 @@ the icon system here would put src/icon-data.ts into `bun --watch`'s graph and
|
|
|
12
12
|
cause an endless rebuild loop.
|
|
13
13
|
*/
|
|
14
14
|
import * as path from 'path';
|
|
15
|
-
import { existsSync, mkdirSync } from 'fs';
|
|
15
|
+
import { existsSync, mkdirSync, unlinkSync } from 'fs';
|
|
16
|
+
import { tmpdir } from 'os';
|
|
16
17
|
import { gzipSync } from 'zlib';
|
|
17
|
-
import {
|
|
18
|
+
import { $, spawn } from 'bun';
|
|
18
19
|
import { extractDocs } from './docs';
|
|
19
20
|
import { checkExamples, formatExampleProblems } from './check-examples';
|
|
20
21
|
import { ensureSections } from './sections';
|
|
21
22
|
import { generateLlmsTxt } from './make-llms-txt';
|
|
22
23
|
import { generateSite } from './generate-site';
|
|
23
24
|
import { findOutputDirOverlap } from './output-guard';
|
|
24
|
-
import { buildEpub } from './epub';
|
|
25
25
|
// Module specifiers contain regex metacharacters (`/`, `.`, `@`, …), so escape
|
|
26
26
|
// before interpolating into the require-shim detector below.
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
27
|
+
/** Give up on a hung ePub child rather than wedge the dev server's rebuild. */
|
|
28
|
+
const EPUB_TIMEOUT_MS = 120_000;
|
|
29
|
+
/**
|
|
30
|
+
* Run buildEpub() in a child process.
|
|
31
|
+
*
|
|
32
|
+
* buildEpub drives happy-dom (HTML→XHTML for every chapter) and @resvg/resvg-js
|
|
33
|
+
* (cover raster) — both native, both retaining — and it runs on EVERY dev rebuild,
|
|
34
|
+
* so in-process it strands memory in a watch process that lives for days. The child
|
|
35
|
+
* hands it all back on exit. If it hangs we kill it, and if it fails we warn: the
|
|
36
|
+
* ePub is a side artifact, so neither should block the page you're trying to look at.
|
|
37
|
+
*
|
|
38
|
+
* Only the data buildEpub reads is forwarded — SiteConfig also carries functions
|
|
39
|
+
* (llmsTxt, libraryBuild, prebuild) that don't survive JSON.
|
|
40
|
+
*/
|
|
41
|
+
async function buildEpubInChild(config, opts) {
|
|
42
|
+
// Resolve the sibling relative to THIS module so it works both in-repo (.ts)
|
|
43
|
+
// and when shipped (compiled .js) — same trick as generate-css below.
|
|
44
|
+
const cliTs = `${import.meta.dir}/epub-cli.ts`;
|
|
45
|
+
const cli = existsSync(cliTs) ? cliTs : `${import.meta.dir}/epub-cli.js`;
|
|
46
|
+
const payload = {
|
|
47
|
+
config: {
|
|
48
|
+
basePath: config.basePath,
|
|
49
|
+
baseUrl: config.baseUrl,
|
|
50
|
+
book: config.book,
|
|
51
|
+
docsJson: config.docsJson,
|
|
52
|
+
favicon: config.favicon,
|
|
53
|
+
lang: config.lang,
|
|
54
|
+
name: config.name,
|
|
55
|
+
outputDir: config.outputDir,
|
|
56
|
+
},
|
|
57
|
+
opts,
|
|
58
|
+
};
|
|
59
|
+
const payloadPath = path.join(tmpdir(), `tosijs-epub-${process.pid}.json`);
|
|
60
|
+
await Bun.write(payloadPath, JSON.stringify(payload));
|
|
61
|
+
const child = spawn(['bun', cli, payloadPath], {
|
|
62
|
+
stdout: 'inherit',
|
|
63
|
+
stderr: 'inherit',
|
|
64
|
+
});
|
|
65
|
+
const killer = setTimeout(() => {
|
|
66
|
+
console.warn(`⚠️ epub build exceeded ${EPUB_TIMEOUT_MS / 1000}s — killing it. The site is\n` +
|
|
67
|
+
` fine; the .epub may be stale.`);
|
|
68
|
+
child.kill();
|
|
69
|
+
}, EPUB_TIMEOUT_MS);
|
|
32
70
|
try {
|
|
33
|
-
|
|
34
|
-
|
|
71
|
+
const code = await child.exited;
|
|
72
|
+
if (code !== 0) {
|
|
73
|
+
console.warn(`⚠️ epub build failed (exit ${code}). The site is fine; the .epub may be stale.`);
|
|
74
|
+
}
|
|
35
75
|
}
|
|
36
|
-
|
|
37
|
-
|
|
76
|
+
finally {
|
|
77
|
+
clearTimeout(killer);
|
|
78
|
+
try {
|
|
79
|
+
unlinkSync(payloadPath);
|
|
80
|
+
}
|
|
81
|
+
catch {
|
|
82
|
+
// already gone — fine
|
|
83
|
+
}
|
|
38
84
|
}
|
|
39
85
|
}
|
|
86
|
+
// Module specifiers contain regex metacharacters (`/`, `.`, `@`, …), so escape
|
|
87
|
+
// before interpolating into the require-shim detector below.
|
|
88
|
+
const escapeRegExp = (s) => s.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
|
40
89
|
export async function buildSite(config) {
|
|
41
90
|
const PROJECT_ROOT = './';
|
|
42
91
|
const PUBLIC = path.resolve(PROJECT_ROOT, config.outputDir ?? 'docs');
|
|
@@ -142,36 +191,47 @@ export async function buildSite(config) {
|
|
|
142
191
|
// consumer supplies (e.g. via staticDirs or an absolute URL).
|
|
143
192
|
const scriptName = (config.scriptUrl ?? '/iife.js').replace(/^\//, '');
|
|
144
193
|
if (config.bundleEntry) {
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
194
|
+
// tjs-lang transpiles live examples; it's dynamically import()'d at
|
|
195
|
+
// runtime (falling back to CDN), so keep it out of the bundle.
|
|
196
|
+
const externals = [
|
|
197
|
+
'tjs-lang',
|
|
198
|
+
'tjs-lang/browser',
|
|
199
|
+
'tjs-lang/browser/from-ts',
|
|
200
|
+
...(config.bundleExternals ?? []),
|
|
201
|
+
];
|
|
202
|
+
// Bundle in a CHILD PROCESS, not via Bun.build().
|
|
203
|
+
//
|
|
204
|
+
// Bun.build() never gives back the bundler's native arena: measured at ~9MB
|
|
205
|
+
// of RSS per call and rising, monotonic, with no plateau (40 sequential
|
|
206
|
+
// builds of one real entry = +367MB, still climbing ~5MB/build at the end),
|
|
207
|
+
// while the JS heap stays flat — so it is invisible to Bun.gc() and to any
|
|
208
|
+
// heap profiler. devServer() calls this once per rebuild in a process that
|
|
209
|
+
// lives for DAYS, so it compounds: a ~2-day watch session reached 136GB RSS
|
|
210
|
+
// and took the machine down with it. Filed as oven-sh/bun#34053.
|
|
211
|
+
//
|
|
212
|
+
// The CLI does identical work in a child whose memory the OS reclaims on
|
|
213
|
+
// exit: the same 15 bundles leave the parent +0.5MB instead of +192MB.
|
|
214
|
+
// Keep these flags in sync with the Bun.build() options they replace.
|
|
215
|
+
const bundle = spawn([
|
|
216
|
+
'bun',
|
|
217
|
+
'build',
|
|
218
|
+
config.bundleEntry,
|
|
219
|
+
'--outdir',
|
|
220
|
+
DIST,
|
|
221
|
+
'--sourcemap=linked',
|
|
222
|
+
'--format=iife',
|
|
223
|
+
'--minify',
|
|
224
|
+
'--entry-naming',
|
|
225
|
+
scriptName,
|
|
226
|
+
...externals.flatMap((ext) => ['--external', ext]),
|
|
227
|
+
], { stdout: 'inherit', stderr: 'inherit' });
|
|
228
|
+
if ((await bundle.exited) !== 0) {
|
|
170
229
|
console.error('bundle build failed');
|
|
171
|
-
for (const message of result.logs)
|
|
172
|
-
console.error(message);
|
|
173
230
|
return false;
|
|
174
231
|
}
|
|
232
|
+
await $ `cp ${DIST}/${scriptName} ${PUBLIC}`.text();
|
|
233
|
+
const bundleFile = await Bun.file(`${DIST}/${scriptName}`).arrayBuffer();
|
|
234
|
+
const bundleJs = Buffer.from(bundleFile).toString('utf8');
|
|
175
235
|
// Warn only when an external actually compiled to a synchronous require()
|
|
176
236
|
// shim, which throws at module-eval ("Dynamic require of … is not
|
|
177
237
|
// supported"). That only happens for a *static* `import x from 'ext'`. A
|
|
@@ -180,20 +240,15 @@ export async function buildSite(config) {
|
|
|
180
240
|
// stay silent. The config alone can't tell the two apart; the emitted
|
|
181
241
|
// bundle can, so we inspect the actual output.
|
|
182
242
|
if (config.bundleExternals && config.bundleExternals.length > 0) {
|
|
183
|
-
const
|
|
184
|
-
.filter((o) => o.kind === 'entry-point' || o.path.endsWith('.js'))
|
|
185
|
-
.map((o) => o.text()))).join('\n');
|
|
186
|
-
const broken = config.bundleExternals.filter((ext) => new RegExp(`(?:__require|\\brequire)\\(\\s*["'\`]${escapeRegExp(ext)}["'\`]`).test(js));
|
|
243
|
+
const broken = config.bundleExternals.filter((ext) => new RegExp(`(?:__require|\\brequire)\\(\\s*["'\`]${escapeRegExp(ext)}["'\`]`).test(bundleJs));
|
|
187
244
|
if (broken.length > 0) {
|
|
188
245
|
console.warn(`⚠️ bundleExternals compiled to a synchronous require() shim that throws at runtime (${broken.join(', ')}). Reference these via a dynamic import() (kept async by the bundler)\n` +
|
|
189
246
|
` or an importmap, not a static import.`);
|
|
190
247
|
}
|
|
191
248
|
}
|
|
192
|
-
await $ `cp ${DIST}/${scriptName} ${PUBLIC}`.text();
|
|
193
|
-
const bundleFile = await Bun.file(`${DIST}/${scriptName}`).arrayBuffer();
|
|
194
249
|
// import.meta is illegal in a classic <script> — if it survived bundling
|
|
195
250
|
// (a branch the bundler couldn't eliminate) the IIFE will SyntaxError.
|
|
196
|
-
if (
|
|
251
|
+
if (bundleJs.includes('import.meta')) {
|
|
197
252
|
console.warn(`⚠️ ${scriptName} contains \`import.meta\`, which is a SyntaxError in a classic <script>.\n` +
|
|
198
253
|
` A dependency referenced it in a branch the bundler couldn't drop — mark that dep external\n` +
|
|
199
254
|
` (+ importmap) or choose a browser-only entry point.`);
|
|
@@ -319,7 +374,7 @@ export async function buildSite(config) {
|
|
|
319
374
|
// the corpus and survives the `rm -rf <outputDir>` at the top of the NEXT build
|
|
320
375
|
// (otherwise a dev rebuild would silently drop it). Cheap (~0.4s).
|
|
321
376
|
if (config.epub) {
|
|
322
|
-
await
|
|
377
|
+
await buildEpubInChild(config, typeof config.epub === 'object' ? config.epub : {});
|
|
323
378
|
}
|
|
324
379
|
console.timeEnd('build');
|
|
325
380
|
return true;
|
|
@@ -186,6 +186,16 @@ export interface SiteConfig {
|
|
|
186
186
|
port?: number;
|
|
187
187
|
/** extra dev-server watch paths (added to docPaths + bundleEntry dir). */
|
|
188
188
|
watchPaths?: string[];
|
|
189
|
+
/**
|
|
190
|
+
* RSS ceiling (MB) for the dev server, default 4096. A watch process lives for
|
|
191
|
+
* days across thousands of rebuilds, so anything the build strands per rebuild
|
|
192
|
+
* compounds until the machine swaps itself to death. Past this, the server
|
|
193
|
+
* prints the growth-per-rebuild and exits rather than take the machine with it.
|
|
194
|
+
* Overridden by the DEV_MEMORY_LIMIT_MB env var. Raise it if a genuinely large
|
|
195
|
+
* build needs the headroom — but sustained growth per rebuild is a leak, not a
|
|
196
|
+
* ceiling that's too low.
|
|
197
|
+
*/
|
|
198
|
+
memoryLimitMb?: number;
|
|
189
199
|
/**
|
|
190
200
|
* Enable the dev-server source read/write endpoints (`/__docstore/source`) that
|
|
191
201
|
* back in-browser "edit page source". Local dev only — the dev server runs on
|