tosijs-ui 1.6.20 → 1.6.21
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/dist/code-editor-cm.d.ts +18 -0
- package/dist/code-editor-cm.js +94 -7
- package/dist/code-editor.d.ts +7 -0
- package/dist/code-editor.js +22 -0
- package/dist/doc-browser.js +13 -5
- package/dist/doc-system/site/dev-server.js +4 -4
- package/dist/doc-system/site/index.d.ts +1 -1
- package/dist/doc-system/site/make-llms-txt.js +16 -0
- package/dist/doc-system/site/orchestrator.js +41 -5
- package/dist/doc-system/site/site-config.d.ts +28 -0
- package/dist/iife.js +136 -120
- package/dist/iife.js.map +17 -12
- package/dist/live-example/code-transform.js +1 -1
- package/dist/live-example/component.js +185 -2
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/llms.txt +8 -1
- package/package.json +3 -3
- package/tls/create-dev-certs.sh +25 -9
package/dist/code-editor-cm.d.ts
CHANGED
|
@@ -2,11 +2,29 @@ import { EditorView } from '@codemirror/view';
|
|
|
2
2
|
import { Extension } from '@codemirror/state';
|
|
3
3
|
/** Map a `<tosi-code mode>` value to a CodeMirror language extension. */
|
|
4
4
|
export declare function languageForMode(mode: string): Extension;
|
|
5
|
+
/** Runtime-introspection hooks for tjs autocomplete (all optional). */
|
|
6
|
+
export interface TjsAutocompleteConfig {
|
|
7
|
+
getMetadata?: () => Record<string, unknown> | undefined;
|
|
8
|
+
getImports?: () => Record<string, Record<string, unknown>> | undefined;
|
|
9
|
+
getLiveBindings?: () => Record<string, unknown> | undefined;
|
|
10
|
+
getMembers?: (path: string) => Promise<unknown[] | undefined>;
|
|
11
|
+
}
|
|
12
|
+
/**
|
|
13
|
+
* Lazy-load tjs-lang's CodeMirror language + completion bundle. Returns a single
|
|
14
|
+
* CodeMirror `Extension` (tjs language, forbidden-keyword highlighting, theme, and
|
|
15
|
+
* `tjsCompletionSource`-driven autocomplete) suitable for `setLanguageExtension()`,
|
|
16
|
+
* or `null` when tjs-lang (an optional peer) isn't installed or its editor build
|
|
17
|
+
* lacks `tjsEditorExtension`. The dynamic import keeps it out of the bundle until a
|
|
18
|
+
* tjs editor actually mounts.
|
|
19
|
+
*/
|
|
20
|
+
export declare function loadTjsExtension(autocomplete?: TjsAutocompleteConfig): Promise<Extension | null>;
|
|
5
21
|
export interface CmHandle {
|
|
6
22
|
view: EditorView;
|
|
7
23
|
getValue(): string;
|
|
8
24
|
setValue(text: string): void;
|
|
9
25
|
setMode(mode: string): void;
|
|
26
|
+
/** Swap the language compartment wholesale (e.g. an async-loaded tjs bundle). */
|
|
27
|
+
setLanguageExtension(ext: Extension): void;
|
|
10
28
|
setReadOnly(readOnly: boolean): void;
|
|
11
29
|
undo(): void;
|
|
12
30
|
redo(): void;
|
package/dist/code-editor-cm.js
CHANGED
|
@@ -21,6 +21,18 @@ import { javascript } from '@codemirror/lang-javascript';
|
|
|
21
21
|
import { css } from '@codemirror/lang-css';
|
|
22
22
|
import { html } from '@codemirror/lang-html';
|
|
23
23
|
import { markdown } from '@codemirror/lang-markdown';
|
|
24
|
+
import { oneDarkHighlightStyle } from '@codemirror/theme-one-dark';
|
|
25
|
+
// The doc-system flips dark mode with a `darkmode` class on <body> (which inverts
|
|
26
|
+
// the luminance of --code-bg / --text-color, so the editor SURFACE adapts for
|
|
27
|
+
// free). Only CM's syntax-token colors need swapping: the light defaultHighlightStyle
|
|
28
|
+
// washes out on a dark surface, so use oneDark's brighter palette in dark mode.
|
|
29
|
+
const isDarkMode = () => typeof document !== 'undefined' &&
|
|
30
|
+
!!document.body &&
|
|
31
|
+
document.body.classList.contains('darkmode');
|
|
32
|
+
const highlightFor = (dark) => dark
|
|
33
|
+
? syntaxHighlighting(oneDarkHighlightStyle)
|
|
34
|
+
: syntaxHighlighting(defaultHighlightStyle, { fallback: true });
|
|
35
|
+
const isTjsMode = (mode) => mode === 'tjs' || mode === 'ajs';
|
|
24
36
|
/** Map a `<tosi-code mode>` value to a CodeMirror language extension. */
|
|
25
37
|
export function languageForMode(mode) {
|
|
26
38
|
switch (mode) {
|
|
@@ -29,8 +41,10 @@ export function languageForMode(mode) {
|
|
|
29
41
|
return javascript();
|
|
30
42
|
case 'ts':
|
|
31
43
|
case 'typescript':
|
|
32
|
-
// tjs highlights
|
|
33
|
-
//
|
|
44
|
+
// tjs highlights as TypeScript here (the sync default). First-class tjs — the
|
|
45
|
+
// tjs-lang language + autocomplete — is layered on asynchronously via
|
|
46
|
+
// `loadTjsExtension()` + `setLanguageExtension()`, since it lazy-loads the
|
|
47
|
+
// (optional) tjs-lang editor build; this keeps `mode="tjs"` readable meanwhile.
|
|
34
48
|
case 'tjs':
|
|
35
49
|
case 'ajs':
|
|
36
50
|
return javascript({ typescript: true });
|
|
@@ -45,10 +59,61 @@ export function languageForMode(mode) {
|
|
|
45
59
|
return javascript();
|
|
46
60
|
}
|
|
47
61
|
}
|
|
62
|
+
// The language compartment carries the language AND its completion source, so
|
|
63
|
+
// exactly ONE `autocompletion()` is ever active: standard modes use CM's built-in
|
|
64
|
+
// (language-provided) completions here, while the tjs bundle brings its own
|
|
65
|
+
// `autocompletion({ override: [tjsCompletionSource] })`. (This is why the base
|
|
66
|
+
// extension set below does NOT include `autocompletion()`.)
|
|
67
|
+
function standardLanguageBundle(mode) {
|
|
68
|
+
return [languageForMode(mode), autocompletion()];
|
|
69
|
+
}
|
|
70
|
+
/**
|
|
71
|
+
* Lazy-load tjs-lang's CodeMirror language + completion bundle. Returns a single
|
|
72
|
+
* CodeMirror `Extension` (tjs language, forbidden-keyword highlighting, theme, and
|
|
73
|
+
* `tjsCompletionSource`-driven autocomplete) suitable for `setLanguageExtension()`,
|
|
74
|
+
* or `null` when tjs-lang (an optional peer) isn't installed or its editor build
|
|
75
|
+
* lacks `tjsEditorExtension`. The dynamic import keeps it out of the bundle until a
|
|
76
|
+
* tjs editor actually mounts.
|
|
77
|
+
*/
|
|
78
|
+
export async function loadTjsExtension(autocomplete = {}) {
|
|
79
|
+
try {
|
|
80
|
+
const mod = (await import('tjs-lang/editors/codemirror'));
|
|
81
|
+
if (typeof mod.tjsEditorExtension === 'function') {
|
|
82
|
+
return mod.tjsEditorExtension({ typescript: true, autocomplete });
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
catch {
|
|
86
|
+
// tjs-lang absent or editor build unavailable — caller keeps TS highlighting.
|
|
87
|
+
}
|
|
88
|
+
return null;
|
|
89
|
+
}
|
|
90
|
+
// A minimal theme so the editor has a real surface instead of showing whatever is
|
|
91
|
+
// behind it. It's applied as a CodeMirror theme (not shadow-DOM CSS) so it beats
|
|
92
|
+
// CM's injected base styles. Colors come from the doc-system theme vars
|
|
93
|
+
// (`--code-bg` / `--text-color`, which pierce the shadow root and adapt to dark
|
|
94
|
+
// mode) with sensible standalone fallbacks.
|
|
95
|
+
const editorTheme = EditorView.theme({
|
|
96
|
+
'&': {
|
|
97
|
+
height: '100%',
|
|
98
|
+
backgroundColor: 'var(--code-bg, var(--input-bg, #fdfdfd))',
|
|
99
|
+
color: 'var(--text-color, #222)',
|
|
100
|
+
},
|
|
101
|
+
'.cm-scroller': {
|
|
102
|
+
fontFamily: "Menlo, Monaco, Consolas, 'Courier New', monospace",
|
|
103
|
+
},
|
|
104
|
+
'.cm-gutters': {
|
|
105
|
+
backgroundColor: 'var(--code-bg, var(--input-bg, #fdfdfd))',
|
|
106
|
+
color: 'var(--text-color, #888)',
|
|
107
|
+
border: 'none',
|
|
108
|
+
},
|
|
109
|
+
'.cm-activeLine': { backgroundColor: 'transparent' },
|
|
110
|
+
'.cm-activeLineGutter': { backgroundColor: 'transparent' },
|
|
111
|
+
});
|
|
48
112
|
// A basicSetup-equivalent without a hard-coded color theme, so the host page's CSS
|
|
49
113
|
// (and dark mode) controls the palette. Line-wrapping mirrors the old ACE `wrap:true`.
|
|
50
114
|
function baseExtensions() {
|
|
51
115
|
return [
|
|
116
|
+
editorTheme,
|
|
52
117
|
lineNumbers(),
|
|
53
118
|
highlightActiveLineGutter(),
|
|
54
119
|
highlightSpecialChars(),
|
|
@@ -60,10 +125,11 @@ function baseExtensions() {
|
|
|
60
125
|
indentOnInput(),
|
|
61
126
|
indentUnit.of(' '),
|
|
62
127
|
EditorState.tabSize.of(2),
|
|
63
|
-
|
|
128
|
+
// syntax highlighting is applied per-instance via a compartment (dark-mode aware)
|
|
64
129
|
bracketMatching(),
|
|
65
130
|
closeBrackets(),
|
|
66
|
-
autocompletion()
|
|
131
|
+
// NB: autocompletion() lives in the language compartment (see
|
|
132
|
+
// standardLanguageBundle) so tjs's own completion source never doubles up.
|
|
67
133
|
rectangularSelection(),
|
|
68
134
|
crosshairCursor(),
|
|
69
135
|
highlightActiveLine(),
|
|
@@ -85,7 +151,8 @@ function baseExtensions() {
|
|
|
85
151
|
export function createCmEditor(parent, opts = {}) {
|
|
86
152
|
const language = new Compartment();
|
|
87
153
|
const readonly = new Compartment();
|
|
88
|
-
const
|
|
154
|
+
const highlight = new Compartment();
|
|
155
|
+
const langExt = opts.languageExtension ?? standardLanguageBundle(opts.mode ?? 'javascript');
|
|
89
156
|
const view = new EditorView({
|
|
90
157
|
parent,
|
|
91
158
|
root: opts.root,
|
|
@@ -93,6 +160,7 @@ export function createCmEditor(parent, opts = {}) {
|
|
|
93
160
|
doc: opts.value ?? '',
|
|
94
161
|
extensions: [
|
|
95
162
|
baseExtensions(),
|
|
163
|
+
highlight.of(highlightFor(isDarkMode())),
|
|
96
164
|
language.of(langExt),
|
|
97
165
|
readonly.of(EditorState.readOnly.of(!!opts.readOnly)),
|
|
98
166
|
EditorView.updateListener.of((u) => {
|
|
@@ -102,6 +170,19 @@ export function createCmEditor(parent, opts = {}) {
|
|
|
102
170
|
],
|
|
103
171
|
}),
|
|
104
172
|
});
|
|
173
|
+
// Re-apply the right highlight palette when the doc-system toggles dark mode
|
|
174
|
+
// (it flips the `darkmode` class on <body>).
|
|
175
|
+
let dark = isDarkMode();
|
|
176
|
+
const darkObserver = typeof MutationObserver !== 'undefined' && typeof document !== 'undefined'
|
|
177
|
+
? new MutationObserver(() => {
|
|
178
|
+
const nowDark = isDarkMode();
|
|
179
|
+
if (nowDark !== dark) {
|
|
180
|
+
dark = nowDark;
|
|
181
|
+
view.dispatch({ effects: highlight.reconfigure(highlightFor(dark)) });
|
|
182
|
+
}
|
|
183
|
+
})
|
|
184
|
+
: null;
|
|
185
|
+
darkObserver?.observe(document.body, { attributes: true, attributeFilter: ['class'] });
|
|
105
186
|
const handle = {
|
|
106
187
|
view,
|
|
107
188
|
getValue: () => view.state.doc.toString(),
|
|
@@ -113,7 +194,10 @@ export function createCmEditor(parent, opts = {}) {
|
|
|
113
194
|
});
|
|
114
195
|
},
|
|
115
196
|
setMode(mode) {
|
|
116
|
-
view.dispatch({ effects: language.reconfigure(
|
|
197
|
+
view.dispatch({ effects: language.reconfigure(standardLanguageBundle(mode)) });
|
|
198
|
+
},
|
|
199
|
+
setLanguageExtension(ext) {
|
|
200
|
+
view.dispatch({ effects: language.reconfigure(ext) });
|
|
117
201
|
},
|
|
118
202
|
setReadOnly(ro) {
|
|
119
203
|
view.dispatch({
|
|
@@ -126,7 +210,10 @@ export function createCmEditor(parent, opts = {}) {
|
|
|
126
210
|
canRedo: () => redoDepth(view.state) > 0,
|
|
127
211
|
focus: () => view.focus(),
|
|
128
212
|
refresh: () => view.requestMeasure(),
|
|
129
|
-
destroy: () =>
|
|
213
|
+
destroy: () => {
|
|
214
|
+
darkObserver?.disconnect();
|
|
215
|
+
view.destroy();
|
|
216
|
+
},
|
|
130
217
|
};
|
|
131
218
|
return handle;
|
|
132
219
|
}
|
package/dist/code-editor.d.ts
CHANGED
|
@@ -50,6 +50,13 @@ export declare class CodeEditor extends WebComponent<CodeEditorParts> {
|
|
|
50
50
|
};
|
|
51
51
|
onResize(): void;
|
|
52
52
|
connectedCallback(): void;
|
|
53
|
+
private isTjsMode;
|
|
54
|
+
/**
|
|
55
|
+
* When in tjs mode, lazily upgrade the editor to tjs-lang's CodeMirror language +
|
|
56
|
+
* autocomplete. No-op (keeps TS highlighting) if not tjs, if tjs-lang isn't
|
|
57
|
+
* installed, or if the mode/handle changed before the async load resolved.
|
|
58
|
+
*/
|
|
59
|
+
private applyTjsExtension;
|
|
53
60
|
render(): void;
|
|
54
61
|
}
|
|
55
62
|
export declare const codeEditor: ElementCreator<CodeEditor>;
|
package/dist/code-editor.js
CHANGED
|
@@ -141,10 +141,31 @@ export class CodeEditor extends WebComponent {
|
|
|
141
141
|
this._handle = handle;
|
|
142
142
|
this._appliedMode = this.mode;
|
|
143
143
|
this._appliedDisabled = this.disabled;
|
|
144
|
+
this.applyTjsExtension();
|
|
144
145
|
return handle;
|
|
145
146
|
});
|
|
146
147
|
}
|
|
147
148
|
}
|
|
149
|
+
isTjsMode() {
|
|
150
|
+
return this.mode === 'tjs' || this.mode === 'ajs';
|
|
151
|
+
}
|
|
152
|
+
/**
|
|
153
|
+
* When in tjs mode, lazily upgrade the editor to tjs-lang's CodeMirror language +
|
|
154
|
+
* autocomplete. No-op (keeps TS highlighting) if not tjs, if tjs-lang isn't
|
|
155
|
+
* installed, or if the mode/handle changed before the async load resolved.
|
|
156
|
+
*/
|
|
157
|
+
applyTjsExtension() {
|
|
158
|
+
const handle = this._handle;
|
|
159
|
+
if (!handle || !this.isTjsMode())
|
|
160
|
+
return;
|
|
161
|
+
import('./code-editor-cm')
|
|
162
|
+
.then(({ loadTjsExtension }) => loadTjsExtension())
|
|
163
|
+
.then((ext) => {
|
|
164
|
+
if (ext && this._handle === handle && this.isTjsMode()) {
|
|
165
|
+
handle.setLanguageExtension(ext);
|
|
166
|
+
}
|
|
167
|
+
});
|
|
168
|
+
}
|
|
148
169
|
render() {
|
|
149
170
|
super.render();
|
|
150
171
|
if (this._handle) {
|
|
@@ -155,6 +176,7 @@ export class CodeEditor extends WebComponent {
|
|
|
155
176
|
if (this.mode !== this._appliedMode) {
|
|
156
177
|
this._handle.setMode(this.mode);
|
|
157
178
|
this._appliedMode = this.mode;
|
|
179
|
+
this.applyTjsExtension();
|
|
158
180
|
}
|
|
159
181
|
}
|
|
160
182
|
}
|
package/dist/doc-browser.js
CHANGED
|
@@ -472,6 +472,11 @@ export function createDocBrowser(options) {
|
|
|
472
472
|
}
|
|
473
473
|
});
|
|
474
474
|
}
|
|
475
|
+
// This instance's own sidenav. Captured from its own change event (below), so the
|
|
476
|
+
// header nav toggle drives THIS doc-browser — not `document.querySelector('tosi-
|
|
477
|
+
// sidenav')`, which returns the FIRST sidenav in the document (an OUTER instance
|
|
478
|
+
// when this one is an embedded, memory-routed browser).
|
|
479
|
+
let sidenav = null;
|
|
475
480
|
const headerContent = [
|
|
476
481
|
button({
|
|
477
482
|
class: 'iconic',
|
|
@@ -489,8 +494,8 @@ export function createDocBrowser(options) {
|
|
|
489
494
|
},
|
|
490
495
|
},
|
|
491
496
|
onClick() {
|
|
492
|
-
|
|
493
|
-
|
|
497
|
+
if (sidenav)
|
|
498
|
+
sidenav.contentVisible = !sidenav.contentVisible;
|
|
494
499
|
},
|
|
495
500
|
}, icons.menu()),
|
|
496
501
|
span({ style: { flex: '0 0 10px' } }),
|
|
@@ -1027,9 +1032,12 @@ export function createDocBrowser(options) {
|
|
|
1027
1032
|
flex: '1 1 auto',
|
|
1028
1033
|
overflow: 'hidden',
|
|
1029
1034
|
},
|
|
1030
|
-
onChange() {
|
|
1031
|
-
|
|
1032
|
-
|
|
1035
|
+
onChange(event) {
|
|
1036
|
+
// Scope to the sidenav that fired — and capture it so the header toggle
|
|
1037
|
+
// targets THIS instance, not the first sidenav in the document.
|
|
1038
|
+
sidenav = event.target.closest(TosiSidenav.tagName);
|
|
1039
|
+
if (sidenav)
|
|
1040
|
+
app.compact = sidenav.compact;
|
|
1033
1041
|
},
|
|
1034
1042
|
}, searchField, navContent, div({
|
|
1035
1043
|
style: {
|
|
@@ -48,10 +48,10 @@ async function ensureHaltijaChannel(port) {
|
|
|
48
48
|
// HTTPS dev page has no mixed-content), while the `hj` CLI drives over its
|
|
49
49
|
// default HTTP 8700 — one server, both transports, shared state. HTTPS cert
|
|
50
50
|
// is mkcert-trusted. Output quiet so it doesn't drown the dev log.
|
|
51
|
-
//
|
|
52
|
-
//
|
|
53
|
-
//
|
|
54
|
-
spawn(['bunx', 'haltija@
|
|
51
|
+
// `@latest` (1.3.2+): the widget-mode WebRTC screen capture (which makes the
|
|
52
|
+
// Electron app CI-only) is on the stable channel now, along with the fix for
|
|
53
|
+
// an accidental Electron launch. (Was pinned to `@beta` before 1.3.2.)
|
|
54
|
+
spawn(['bunx', 'haltija@latest', '--server', '--both'], {
|
|
55
55
|
stdout: 'ignore',
|
|
56
56
|
stderr: 'ignore',
|
|
57
57
|
});
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
export { defineSiteConfig } from './site-config';
|
|
2
|
-
export type { SiteConfig, SiteHost } from './site-config';
|
|
2
|
+
export type { SiteConfig, SiteHost, LibraryBuildContext } from './site-config';
|
|
3
3
|
export { buildSite } from './orchestrator';
|
|
4
4
|
export { devServer } from './dev-server';
|
|
5
5
|
export { extractDocs, saveDocsJSON } from './docs';
|
|
@@ -102,6 +102,21 @@ export function generateLlmsTxt(outputPath, meta = {}, corpus) {
|
|
|
102
102
|
console.warn('llms.txt: no readable package.json — using config values only');
|
|
103
103
|
}
|
|
104
104
|
const entries = corpus ? entriesFromCorpus(corpus, meta) : entriesFromSrcScan();
|
|
105
|
+
// Only orient agents about the live-example execution model when the corpus
|
|
106
|
+
// actually has live examples (a book / pure-docs site has none).
|
|
107
|
+
const exampleFence = /```(js|tjs|ts|html|css)\b/;
|
|
108
|
+
const hasLiveExamples = !!corpus && corpus.some((d) => exampleFence.test(d.text ?? ''));
|
|
109
|
+
const liveExampleNote = hasLiveExamples
|
|
110
|
+
? [
|
|
111
|
+
'Live examples run inline on the shared page by default (not sandboxed):',
|
|
112
|
+
'each has its own `preview` element, but they share one tosijs module, so',
|
|
113
|
+
'`tosi()` state singletons are shared across every example on a page. This is',
|
|
114
|
+
'deliberate (it shows tosi isolation and lets you drive demos via the global',
|
|
115
|
+
'singleton). Namespace example state with unique top-level keys so examples',
|
|
116
|
+
"don't clobber each other; the `iframe` attribute isolates DOM/CSS, not state.",
|
|
117
|
+
'',
|
|
118
|
+
]
|
|
119
|
+
: [];
|
|
105
120
|
const name = meta.name ?? pkg.name ?? '';
|
|
106
121
|
const description = meta.description ?? pkg.description ?? '';
|
|
107
122
|
const links = [];
|
|
@@ -125,6 +140,7 @@ export function generateLlmsTxt(outputPath, meta = {}, corpus) {
|
|
|
125
140
|
'',
|
|
126
141
|
'Full documentation, with live code examples, is at the links below.',
|
|
127
142
|
'',
|
|
143
|
+
...liveExampleNote,
|
|
128
144
|
'## Pages',
|
|
129
145
|
'',
|
|
130
146
|
];
|
|
@@ -25,6 +25,18 @@ import { buildEpub } from './epub';
|
|
|
25
25
|
// Module specifiers contain regex metacharacters (`/`, `.`, `@`, …), so escape
|
|
26
26
|
// before interpolating into the require-shim detector below.
|
|
27
27
|
const escapeRegExp = (s) => s.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
|
28
|
+
// Externalize the tjs CodeMirror editor extension ONLY when tjs-lang isn't
|
|
29
|
+
// installed — otherwise it's bundled in so it shares the editor's CodeMirror
|
|
30
|
+
// instance (see the `external` comment in buildSite). Returns [] when present.
|
|
31
|
+
function tjsEditorExternal(root) {
|
|
32
|
+
try {
|
|
33
|
+
Bun.resolveSync('tjs-lang/editors/codemirror', root);
|
|
34
|
+
return [];
|
|
35
|
+
}
|
|
36
|
+
catch {
|
|
37
|
+
return ['tjs-lang/editors/codemirror'];
|
|
38
|
+
}
|
|
39
|
+
}
|
|
28
40
|
export async function buildSite(config) {
|
|
29
41
|
const PROJECT_ROOT = './';
|
|
30
42
|
const PUBLIC = path.resolve(PROJECT_ROOT, config.outputDir ?? 'docs');
|
|
@@ -97,7 +109,17 @@ export async function buildSite(config) {
|
|
|
97
109
|
console.time('build');
|
|
98
110
|
// Optionally also build the library (ESM + type declarations) — for repos
|
|
99
111
|
// whose single build publishes both an npm package and its doc site.
|
|
100
|
-
if (config.
|
|
112
|
+
if (config.libraryBuild) {
|
|
113
|
+
// Full override — the consumer owns emitting dist/*.js + *.d.ts for ALL
|
|
114
|
+
// sources (e.g. tsc for `.ts` + `tjs convert`/`generateDTS` for native `.tjs`
|
|
115
|
+
// that tsc can't compile). See BUILD-TJS-HOOK.md.
|
|
116
|
+
await config.libraryBuild({
|
|
117
|
+
dist: DIST,
|
|
118
|
+
root: path.resolve(PROJECT_ROOT),
|
|
119
|
+
tsconfig: config.libraryTsconfig,
|
|
120
|
+
});
|
|
121
|
+
}
|
|
122
|
+
else if (config.libraryTsconfig) {
|
|
101
123
|
// Consumer-controlled library build (handles root noEmit, removeComments,
|
|
102
124
|
// outDir, etc.).
|
|
103
125
|
try {
|
|
@@ -127,12 +149,20 @@ export async function buildSite(config) {
|
|
|
127
149
|
format: 'iife',
|
|
128
150
|
minify: true,
|
|
129
151
|
naming: scriptName,
|
|
130
|
-
// tjs-lang
|
|
131
|
-
// runtime (
|
|
152
|
+
// tjs-lang's TRANSPILER (browser bundles) is dynamically import()'d at
|
|
153
|
+
// runtime (same-origin `/tjs/` copy, else CDN), so keep it out of the bundle.
|
|
154
|
+
//
|
|
155
|
+
// `tjs-lang/editors/codemirror` (the CodeMirror language + autocomplete) is
|
|
156
|
+
// different: it MUST share the editor's CodeMirror instance, so it has to be
|
|
157
|
+
// bundled IN — a separately loaded copy carries its own `@codemirror/state`
|
|
158
|
+
// and silently no-ops. So we do NOT externalize it *when tjs-lang is
|
|
159
|
+
// installed*. But tjs-lang is an OPTIONAL peer; if it's absent, bundling would
|
|
160
|
+
// fail to resolve, so we externalize it in that case (the runtime import then
|
|
161
|
+
// no-ops to plain TS highlighting — graceful degradation).
|
|
132
162
|
external: [
|
|
133
|
-
'tjs-lang',
|
|
134
163
|
'tjs-lang/browser',
|
|
135
164
|
'tjs-lang/browser/from-ts',
|
|
165
|
+
...tjsEditorExternal(PROJECT_ROOT),
|
|
136
166
|
...(config.bundleExternals ?? []),
|
|
137
167
|
],
|
|
138
168
|
});
|
|
@@ -258,7 +288,13 @@ export async function buildSite(config) {
|
|
|
258
288
|
const genCss = existsSync(genCssTs)
|
|
259
289
|
? genCssTs
|
|
260
290
|
: `${import.meta.dir}/generate-css.js`;
|
|
261
|
-
|
|
291
|
+
// generate-css imports the consumer's library to burn the theme; when that graph
|
|
292
|
+
// reaches non-`.ts` sources (e.g. `.tjs`), `--preload` a module that registers the
|
|
293
|
+
// Bun loader plugin so those modules evaluate. (See BUILD-TJS-HOOK.md.)
|
|
294
|
+
const themeArg = JSON.stringify(config.theme || {});
|
|
295
|
+
await (config.generateCssPreload
|
|
296
|
+
? $ `bun --preload ${config.generateCssPreload} ${genCss} ${PUBLIC}/doc-system.css ${themeArg}`
|
|
297
|
+
: $ `bun ${genCss} ${PUBLIC}/doc-system.css ${themeArg}`).text();
|
|
262
298
|
console.log(`generated ${pageCount} static pages`);
|
|
263
299
|
// ── host preset files ──
|
|
264
300
|
// Idempotent, and an explicit static file (copied from staticDirs) always wins.
|
|
@@ -3,6 +3,15 @@ import type { DocSystemTheme } from '../doc-system-styles';
|
|
|
3
3
|
import type { Doc } from './docs';
|
|
4
4
|
import type { BookManifest } from '../book-manifest';
|
|
5
5
|
export type SiteHost = 'github-pages' | 'firebase' | 'static';
|
|
6
|
+
/** Resolved paths handed to a `libraryBuild` override (see SiteConfig). */
|
|
7
|
+
export interface LibraryBuildContext {
|
|
8
|
+
/** absolute path to the `dist` output dir the artifacts must land in */
|
|
9
|
+
dist: string;
|
|
10
|
+
/** project root the build runs from */
|
|
11
|
+
root: string;
|
|
12
|
+
/** the configured `libraryTsconfig`, if any (so the override can still run tsc) */
|
|
13
|
+
tsconfig?: string;
|
|
14
|
+
}
|
|
6
15
|
export interface SiteConfig {
|
|
7
16
|
/** project / brand name — header, <title> suffix, og:site_name */
|
|
8
17
|
name: string;
|
|
@@ -103,6 +112,25 @@ export interface SiteConfig {
|
|
|
103
112
|
* yourself (e.g. keep doc comments in the published JS for AI readers).
|
|
104
113
|
*/
|
|
105
114
|
libraryTsconfig?: string;
|
|
115
|
+
/**
|
|
116
|
+
* Fully override the library build (the `tsc` step that `libraryTsconfig` /
|
|
117
|
+
* `emitLibrary` run). Receives the resolved paths and is responsible for emitting
|
|
118
|
+
* `dist/*.js` + `*.d.ts` for ALL sources. Use this when some sources aren't `.ts`
|
|
119
|
+
* — e.g. native tjs-lang `.tjs` modules, which `tsc` can't compile: run `tsc` for
|
|
120
|
+
* the `.ts` graph and `tjs convert` + `generateDTS` for the `.tjs` files, into the
|
|
121
|
+
* same `dist`. Takes precedence over `libraryTsconfig` and `emitLibrary`. Pairs
|
|
122
|
+
* with `generateCssPreload` (below) for the CSS-extraction eval. See
|
|
123
|
+
* BUILD-TJS-HOOK.md.
|
|
124
|
+
*/
|
|
125
|
+
libraryBuild?: (ctx: LibraryBuildContext) => void | Promise<void>;
|
|
126
|
+
/**
|
|
127
|
+
* A module to `--preload` into the CSS-extraction subprocess (`generate-css`),
|
|
128
|
+
* which imports your library to burn the theme stylesheet. Needed when that import
|
|
129
|
+
* graph reaches non-`.ts` sources (e.g. `.tjs`) that require a Bun loader plugin
|
|
130
|
+
* to evaluate — point this at a module that registers it (via `Bun.plugin({...})`
|
|
131
|
+
* at import time). Without it the subprocess throws `Cannot find module './x.tjs'`.
|
|
132
|
+
*/
|
|
133
|
+
generateCssPreload?: string;
|
|
106
134
|
/**
|
|
107
135
|
* Emit llms.txt agent-discoverability index. Default true (uses `name` /
|
|
108
136
|
* `description` / `baseUrl` / `projectLinks`). Set false to skip, or pass a
|