@vertesia/build-tools 1.2.0 → 1.4.0-dev.20260614.160504Z
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/README.md +3 -3
- package/lib/bin/build.d.ts +35 -0
- package/lib/bin/build.d.ts.map +1 -0
- package/lib/bin/build.js +79 -0
- package/lib/bin/build.js.map +1 -0
- package/lib/bin/config.d.ts +24 -0
- package/lib/bin/config.d.ts.map +1 -0
- package/lib/bin/config.js +91 -0
- package/lib/bin/config.js.map +1 -0
- package/lib/core/compilers/widget.d.ts +27 -0
- package/lib/core/compilers/widget.d.ts.map +1 -0
- package/lib/core/compilers/widget.js +37 -0
- package/lib/core/compilers/widget.js.map +1 -0
- package/lib/{types → core}/parsers/frontmatter.d.ts +1 -1
- package/lib/core/parsers/frontmatter.d.ts.map +1 -0
- package/lib/{esm → core}/parsers/frontmatter.js +1 -1
- package/lib/core/parsers/frontmatter.js.map +1 -0
- package/lib/core/transformers/index.d.ts +10 -0
- package/lib/core/transformers/index.d.ts.map +1 -0
- package/lib/core/transformers/index.js +10 -0
- package/lib/core/transformers/index.js.map +1 -0
- package/lib/{types/presets → core/transformers}/prompt.d.ts +7 -21
- package/lib/core/transformers/prompt.d.ts.map +1 -0
- package/lib/{esm/presets → core/transformers}/prompt.js +17 -14
- package/lib/core/transformers/prompt.js.map +1 -0
- package/lib/core/transformers/raw.d.ts.map +1 -0
- package/lib/{esm/presets → core/transformers}/raw.js +2 -2
- package/lib/core/transformers/raw.js.map +1 -0
- package/lib/core/transformers/skill-collection.d.ts.map +1 -0
- package/lib/{esm/presets → core/transformers}/skill-collection.js +5 -12
- package/lib/core/transformers/skill-collection.js.map +1 -0
- package/lib/core/transformers/skill.d.ts +111 -0
- package/lib/core/transformers/skill.d.ts.map +1 -0
- package/lib/{esm/presets → core/transformers}/skill.js +70 -51
- package/lib/core/transformers/skill.js.map +1 -0
- package/lib/core/transformers/template-collection.d.ts.map +1 -0
- package/lib/{esm/presets → core/transformers}/template-collection.js +5 -12
- package/lib/core/transformers/template-collection.js.map +1 -0
- package/lib/{types/presets → core/transformers}/template.d.ts +7 -22
- package/lib/core/transformers/template.d.ts.map +1 -0
- package/lib/{esm/presets → core/transformers}/template.js +16 -11
- package/lib/core/transformers/template.js.map +1 -0
- package/lib/{types → core}/types.d.ts +5 -61
- package/lib/core/types.d.ts.map +1 -0
- package/lib/core/types.js +6 -0
- package/lib/{cjs → core}/types.js.map +1 -1
- package/lib/core/utils/asset-copy.d.ts.map +1 -0
- package/lib/core/utils/asset-copy.js.map +1 -0
- package/lib/core/utils/asset-discovery.d.ts.map +1 -0
- package/lib/{esm → core}/utils/asset-discovery.js +4 -4
- package/lib/core/utils/asset-discovery.js.map +1 -0
- package/lib/core/utils/template-asset-discovery.d.ts.map +1 -0
- package/lib/{esm → core}/utils/template-asset-discovery.js +3 -7
- package/lib/core/utils/template-asset-discovery.js.map +1 -0
- package/lib/import-transform/builtins.d.ts +21 -0
- package/lib/import-transform/builtins.d.ts.map +1 -0
- package/lib/import-transform/builtins.js +50 -0
- package/lib/import-transform/builtins.js.map +1 -0
- package/lib/import-transform/chunk-emitter.d.ts +24 -0
- package/lib/import-transform/chunk-emitter.d.ts.map +1 -0
- package/lib/import-transform/chunk-emitter.js +35 -0
- package/lib/import-transform/chunk-emitter.js.map +1 -0
- package/lib/import-transform/detector.d.ts +24 -0
- package/lib/import-transform/detector.d.ts.map +1 -0
- package/lib/import-transform/detector.js +34 -0
- package/lib/import-transform/detector.js.map +1 -0
- package/lib/import-transform/index.d.ts +47 -0
- package/lib/import-transform/index.d.ts.map +1 -0
- package/lib/import-transform/index.js +115 -0
- package/lib/import-transform/index.js.map +1 -0
- package/lib/import-transform/patterns.d.ts +25 -0
- package/lib/import-transform/patterns.d.ts.map +1 -0
- package/lib/import-transform/patterns.js +27 -0
- package/lib/import-transform/patterns.js.map +1 -0
- package/lib/import-transform/resolver.d.ts +23 -0
- package/lib/import-transform/resolver.d.ts.map +1 -0
- package/lib/import-transform/resolver.js +30 -0
- package/lib/import-transform/resolver.js.map +1 -0
- package/lib/import-transform/rewriter.d.ts +21 -0
- package/lib/import-transform/rewriter.d.ts.map +1 -0
- package/lib/import-transform/rewriter.js +30 -0
- package/lib/import-transform/rewriter.js.map +1 -0
- package/lib/import-transform/scanner.d.ts +17 -0
- package/lib/import-transform/scanner.d.ts.map +1 -0
- package/lib/import-transform/scanner.js +46 -0
- package/lib/import-transform/scanner.js.map +1 -0
- package/lib/index.d.ts +39 -0
- package/lib/index.d.ts.map +1 -0
- package/lib/index.js +43 -0
- package/lib/index.js.map +1 -0
- package/lib/vite/api-server.d.ts +54 -0
- package/lib/vite/api-server.d.ts.map +1 -0
- package/lib/vite/api-server.js +94 -0
- package/lib/vite/api-server.js.map +1 -0
- package/lib/vite/dev-server.d.ts +42 -0
- package/lib/vite/dev-server.d.ts.map +1 -0
- package/lib/vite/dev-server.js +111 -0
- package/lib/vite/dev-server.js.map +1 -0
- package/lib/vite/index.d.ts +15 -0
- package/lib/vite/index.d.ts.map +1 -0
- package/lib/vite/index.js +15 -0
- package/lib/vite/index.js.map +1 -0
- package/package.json +38 -28
- package/src/bin/build.ts +83 -0
- package/src/bin/config.ts +113 -0
- package/src/core/compilers/widget.ts +69 -0
- package/src/{parsers → core/parsers}/frontmatter.ts +2 -2
- package/src/core/transformers/index.ts +27 -0
- package/src/{presets → core/transformers}/prompt.ts +35 -32
- package/src/{presets → core/transformers}/raw.ts +2 -2
- package/src/{presets → core/transformers}/skill-collection.ts +5 -12
- package/src/{presets → core/transformers}/skill.ts +119 -99
- package/src/{presets → core/transformers}/template-collection.ts +76 -83
- package/src/{presets → core/transformers}/template.ts +121 -116
- package/src/core/types.ts +71 -0
- package/src/{utils → core/utils}/asset-copy.ts +3 -2
- package/src/{utils → core/utils}/asset-discovery.ts +5 -5
- package/src/{utils → core/utils}/template-asset-discovery.ts +70 -77
- package/src/import-transform/builtins.ts +56 -0
- package/src/import-transform/chunk-emitter.ts +60 -0
- package/src/import-transform/detector.ts +56 -0
- package/src/import-transform/index.ts +171 -0
- package/src/import-transform/patterns.ts +34 -0
- package/src/import-transform/resolver.ts +57 -0
- package/src/import-transform/rewriter.ts +48 -0
- package/src/import-transform/scanner.ts +55 -0
- package/src/index.ts +67 -42
- package/src/vite/api-server.ts +142 -0
- package/src/vite/dev-server.ts +128 -0
- package/src/vite/index.ts +15 -0
- package/lib/build-tools.js +0 -2031
- package/lib/build-tools.js.map +0 -1
- package/lib/cjs/index.js +0 -43
- package/lib/cjs/index.js.map +0 -1
- package/lib/cjs/package.json +0 -3
- package/lib/cjs/parsers/frontmatter.js +0 -25
- package/lib/cjs/parsers/frontmatter.js.map +0 -1
- package/lib/cjs/plugin.js +0 -150
- package/lib/cjs/plugin.js.map +0 -1
- package/lib/cjs/presets/index.js +0 -25
- package/lib/cjs/presets/index.js.map +0 -1
- package/lib/cjs/presets/prompt.js +0 -185
- package/lib/cjs/presets/prompt.js.map +0 -1
- package/lib/cjs/presets/raw.js +0 -25
- package/lib/cjs/presets/raw.js.map +0 -1
- package/lib/cjs/presets/skill-collection.js +0 -83
- package/lib/cjs/presets/skill-collection.js.map +0 -1
- package/lib/cjs/presets/skill.js +0 -272
- package/lib/cjs/presets/skill.js.map +0 -1
- package/lib/cjs/presets/template-collection.js +0 -80
- package/lib/cjs/presets/template-collection.js.map +0 -1
- package/lib/cjs/presets/template.js +0 -105
- package/lib/cjs/presets/template.js.map +0 -1
- package/lib/cjs/types.js +0 -6
- package/lib/cjs/utils/asset-copy.js +0 -61
- package/lib/cjs/utils/asset-copy.js.map +0 -1
- package/lib/cjs/utils/asset-discovery.js +0 -100
- package/lib/cjs/utils/asset-discovery.js.map +0 -1
- package/lib/cjs/utils/template-asset-discovery.js +0 -63
- package/lib/cjs/utils/template-asset-discovery.js.map +0 -1
- package/lib/cjs/utils/widget-compiler.js +0 -115
- package/lib/cjs/utils/widget-compiler.js.map +0 -1
- package/lib/cjs/vite.js +0 -45
- package/lib/cjs/vite.js.map +0 -1
- package/lib/esm/index.js +0 -26
- package/lib/esm/index.js.map +0 -1
- package/lib/esm/parsers/frontmatter.js.map +0 -1
- package/lib/esm/plugin.js +0 -144
- package/lib/esm/plugin.js.map +0 -1
- package/lib/esm/presets/index.js +0 -10
- package/lib/esm/presets/index.js.map +0 -1
- package/lib/esm/presets/prompt.js.map +0 -1
- package/lib/esm/presets/raw.js.map +0 -1
- package/lib/esm/presets/skill-collection.js.map +0 -1
- package/lib/esm/presets/skill.js.map +0 -1
- package/lib/esm/presets/template-collection.js.map +0 -1
- package/lib/esm/presets/template.js.map +0 -1
- package/lib/esm/types.js +0 -5
- package/lib/esm/types.js.map +0 -1
- package/lib/esm/utils/asset-copy.js.map +0 -1
- package/lib/esm/utils/asset-discovery.js.map +0 -1
- package/lib/esm/utils/template-asset-discovery.js.map +0 -1
- package/lib/esm/utils/widget-compiler.js +0 -76
- package/lib/esm/utils/widget-compiler.js.map +0 -1
- package/lib/esm/vite.js +0 -42
- package/lib/esm/vite.js.map +0 -1
- package/lib/types/index.d.ts +0 -24
- package/lib/types/index.d.ts.map +0 -1
- package/lib/types/parsers/frontmatter.d.ts.map +0 -1
- package/lib/types/plugin.d.ts +0 -10
- package/lib/types/plugin.d.ts.map +0 -1
- package/lib/types/presets/index.d.ts +0 -10
- package/lib/types/presets/index.d.ts.map +0 -1
- package/lib/types/presets/prompt.d.ts.map +0 -1
- package/lib/types/presets/raw.d.ts.map +0 -1
- package/lib/types/presets/skill-collection.d.ts.map +0 -1
- package/lib/types/presets/skill.d.ts +0 -361
- package/lib/types/presets/skill.d.ts.map +0 -1
- package/lib/types/presets/template-collection.d.ts.map +0 -1
- package/lib/types/presets/template.d.ts.map +0 -1
- package/lib/types/types.d.ts.map +0 -1
- package/lib/types/utils/asset-copy.d.ts.map +0 -1
- package/lib/types/utils/asset-discovery.d.ts.map +0 -1
- package/lib/types/utils/template-asset-discovery.d.ts.map +0 -1
- package/lib/types/utils/widget-compiler.d.ts +0 -15
- package/lib/types/utils/widget-compiler.d.ts.map +0 -1
- package/lib/types/vite.d.ts +0 -32
- package/lib/types/vite.d.ts.map +0 -1
- package/src/plugin.ts +0 -166
- package/src/presets/index.ts +0 -10
- package/src/types.ts +0 -140
- package/src/utils/widget-compiler.ts +0 -98
- package/src/vite.ts +0 -45
- /package/lib/{types/presets → core/transformers}/raw.d.ts +0 -0
- /package/lib/{types/presets → core/transformers}/skill-collection.d.ts +0 -0
- /package/lib/{types/presets → core/transformers}/template-collection.d.ts +0 -0
- /package/lib/{types → core}/utils/asset-copy.d.ts +0 -0
- /package/lib/{esm → core}/utils/asset-copy.js +0 -0
- /package/lib/{types → core}/utils/asset-discovery.d.ts +0 -0
- /package/lib/{types → core}/utils/template-asset-discovery.d.ts +0 -0
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Finds Vertesia query-style import specifiers inside a JavaScript source
|
|
3
|
+
* file and maps each one to the transformer that should handle it.
|
|
4
|
+
*
|
|
5
|
+
* The detector is regex-based and works on `tsc` output where import
|
|
6
|
+
* specifiers are preserved literally. It identifies any string literal that
|
|
7
|
+
* contains a known marker (`?skill`, `?raw`, …, or `/SKILL.md`) and then
|
|
8
|
+
* matches the specifier against the configured transformer patterns.
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
import type { TransformerRule } from '../core/types.js';
|
|
12
|
+
import { QUERY_STRING_LITERAL } from './patterns.js';
|
|
13
|
+
|
|
14
|
+
export interface ImportOccurrence {
|
|
15
|
+
/** The transformer whose pattern matched the specifier. */
|
|
16
|
+
transformer: TransformerRule;
|
|
17
|
+
|
|
18
|
+
/** The original specifier text (the value between the quotes). */
|
|
19
|
+
specifier: string;
|
|
20
|
+
|
|
21
|
+
/** Offset in the source where the opening quote begins. */
|
|
22
|
+
quoteStart: number;
|
|
23
|
+
|
|
24
|
+
/** Offset in the source where the closing quote ends (exclusive). */
|
|
25
|
+
quoteEnd: number;
|
|
26
|
+
|
|
27
|
+
/** The quote character used in the source. */
|
|
28
|
+
quote: "'" | '"' | '`';
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
export function detectQueryImports(content: string, transformers: TransformerRule[]): ImportOccurrence[] {
|
|
32
|
+
const occurrences: ImportOccurrence[] = [];
|
|
33
|
+
QUERY_STRING_LITERAL.lastIndex = 0;
|
|
34
|
+
|
|
35
|
+
let match: RegExpExecArray | null;
|
|
36
|
+
// biome-ignore lint/suspicious/noAssignInExpressions: standard regex.exec loop
|
|
37
|
+
while ((match = QUERY_STRING_LITERAL.exec(content)) !== null) {
|
|
38
|
+
const quote = match[1] as "'" | '"' | '`';
|
|
39
|
+
const specifier = match[2];
|
|
40
|
+
|
|
41
|
+
for (const transformer of transformers) {
|
|
42
|
+
if (transformer.pattern.test(specifier)) {
|
|
43
|
+
occurrences.push({
|
|
44
|
+
transformer,
|
|
45
|
+
specifier,
|
|
46
|
+
quoteStart: match.index,
|
|
47
|
+
quoteEnd: match.index + match[0].length,
|
|
48
|
+
quote,
|
|
49
|
+
});
|
|
50
|
+
break;
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
return occurrences;
|
|
56
|
+
}
|
|
@@ -0,0 +1,171 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Standalone import transformer for Vertesia query-style imports.
|
|
3
|
+
*
|
|
4
|
+
* Operates directly on the JavaScript output of `tsc`:
|
|
5
|
+
*
|
|
6
|
+
* 1. Walk `libDir` for `.js` files containing query imports.
|
|
7
|
+
* 2. For each occurrence, resolve back to the corresponding source asset
|
|
8
|
+
* under `srcDir`, run the transformer, and write the generated module
|
|
9
|
+
* as a sibling chunk in `libDir`.
|
|
10
|
+
* 3. Rewrite the importing file in place so the original `?skill` /
|
|
11
|
+
* `SKILL.md` specifier points at the new chunk.
|
|
12
|
+
* 4. Copy any transformer-declared assets (scripts, handlebars, …) and
|
|
13
|
+
* bundle any discovered widget `.tsx` siblings via esbuild.
|
|
14
|
+
*
|
|
15
|
+
* Emitted chunks are re-scanned for nested query imports (e.g. a `?skills`
|
|
16
|
+
* collection chunk that imports `SKILL.md` siblings), so the pipeline
|
|
17
|
+
* matches rollup's recursive resolveId/load behavior.
|
|
18
|
+
*/
|
|
19
|
+
|
|
20
|
+
import path from 'node:path';
|
|
21
|
+
import { compileWidgets, type WidgetCompilerConfig } from '../core/compilers/widget.js';
|
|
22
|
+
import type { AssetFile, TransformerRule } from '../core/types.js';
|
|
23
|
+
import { copyAssets } from '../core/utils/asset-copy.js';
|
|
24
|
+
import type { WidgetMetadata } from '../core/utils/asset-discovery.js';
|
|
25
|
+
import { emitChunk } from './chunk-emitter.js';
|
|
26
|
+
import { detectQueryImports } from './detector.js';
|
|
27
|
+
import { SNIFF_PATTERN } from './patterns.js';
|
|
28
|
+
import { resolveImport } from './resolver.js';
|
|
29
|
+
import { type ImportReplacement, writeRewrittenFile } from './rewriter.js';
|
|
30
|
+
import { scanLibForQueryImports } from './scanner.js';
|
|
31
|
+
|
|
32
|
+
export interface TransformImportsOptions {
|
|
33
|
+
/** Root of the compiled JavaScript output (e.g. `./lib`). */
|
|
34
|
+
libDir: string;
|
|
35
|
+
|
|
36
|
+
/** Root of the original sources, mirroring `libDir` (e.g. `./src`). */
|
|
37
|
+
srcDir: string;
|
|
38
|
+
|
|
39
|
+
/** Transformers to apply. */
|
|
40
|
+
transformers: TransformerRule[];
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* Root directory where script assets are copied and widgets are written.
|
|
44
|
+
* - String: assets and widgets are emitted under this directory.
|
|
45
|
+
* - `false`: asset copying and widget compilation are skipped.
|
|
46
|
+
* - Default: `libDir` (so widgets land in `lib/<widgetsDir>/`).
|
|
47
|
+
*/
|
|
48
|
+
assetsDir?: string | false;
|
|
49
|
+
|
|
50
|
+
/** Sub-directory under `assetsDir` for widget bundles. Default: 'widgets'. */
|
|
51
|
+
widgetsDir?: string;
|
|
52
|
+
|
|
53
|
+
/** Configuration forwarded to the esbuild widget bundler. */
|
|
54
|
+
widgetConfig?: WidgetCompilerConfig;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
export interface TransformImportsResult {
|
|
58
|
+
filesProcessed: number;
|
|
59
|
+
chunksEmitted: number;
|
|
60
|
+
assetsCopied: number;
|
|
61
|
+
widgetsCompiled: number;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
interface WorkItem {
|
|
65
|
+
path: string;
|
|
66
|
+
content: string;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
export async function transformImports(opts: TransformImportsOptions): Promise<TransformImportsResult> {
|
|
70
|
+
const { libDir, srcDir, transformers, widgetsDir = 'widgets', widgetConfig } = opts;
|
|
71
|
+
const assetsDir = opts.assetsDir === undefined ? libDir : opts.assetsDir;
|
|
72
|
+
const shouldCopyAssets = assetsDir !== false;
|
|
73
|
+
const shouldCompileWidgets = assetsDir !== false;
|
|
74
|
+
|
|
75
|
+
if (!transformers || transformers.length === 0) {
|
|
76
|
+
throw new Error('transformImports: At least one transformer must be configured');
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
const seenFiles = new Set<string>();
|
|
80
|
+
const workQueue: WorkItem[] = [];
|
|
81
|
+
|
|
82
|
+
for (const file of scanLibForQueryImports(libDir)) {
|
|
83
|
+
if (!seenFiles.has(file.path)) {
|
|
84
|
+
seenFiles.add(file.path);
|
|
85
|
+
workQueue.push(file);
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
const emittedChunks = new Set<string>();
|
|
90
|
+
const seenWidgets = new Map<string, WidgetMetadata>();
|
|
91
|
+
const assetsToCopy: AssetFile[] = [];
|
|
92
|
+
let chunksEmitted = 0;
|
|
93
|
+
let filesProcessed = 0;
|
|
94
|
+
|
|
95
|
+
while (workQueue.length > 0) {
|
|
96
|
+
const file = workQueue.shift() as WorkItem;
|
|
97
|
+
const occurrences = detectQueryImports(file.content, transformers);
|
|
98
|
+
if (occurrences.length === 0) {
|
|
99
|
+
continue;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
const replacements: ImportReplacement[] = [];
|
|
103
|
+
|
|
104
|
+
for (const occ of occurrences) {
|
|
105
|
+
const resolved = resolveImport(file.path, occ.specifier, libDir, srcDir);
|
|
106
|
+
|
|
107
|
+
if (!emittedChunks.has(resolved.chunkLibPath)) {
|
|
108
|
+
const emitted = await emitChunk(resolved.resolvedSrcPath, resolved.chunkLibPath, occ.transformer);
|
|
109
|
+
emittedChunks.add(resolved.chunkLibPath);
|
|
110
|
+
chunksEmitted++;
|
|
111
|
+
|
|
112
|
+
if (emitted.assets.length > 0 && shouldCopyAssets) {
|
|
113
|
+
assetsToCopy.push(...emitted.assets);
|
|
114
|
+
}
|
|
115
|
+
if (emitted.widgets.length > 0 && shouldCompileWidgets) {
|
|
116
|
+
for (const widget of emitted.widgets) {
|
|
117
|
+
if (!seenWidgets.has(widget.path)) {
|
|
118
|
+
seenWidgets.set(widget.path, widget);
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
// Chunks may themselves contain query imports (e.g. ?skills
|
|
124
|
+
// collection chunks emit `import './foo/SKILL.md'` lines).
|
|
125
|
+
if (SNIFF_PATTERN.test(emitted.content) && !seenFiles.has(resolved.chunkLibPath)) {
|
|
126
|
+
seenFiles.add(resolved.chunkLibPath);
|
|
127
|
+
workQueue.push({ path: resolved.chunkLibPath, content: emitted.content });
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
replacements.push({
|
|
132
|
+
quoteStart: occ.quoteStart,
|
|
133
|
+
quoteEnd: occ.quoteEnd,
|
|
134
|
+
quote: occ.quote,
|
|
135
|
+
newSpecifier: resolved.chunkSpecifier,
|
|
136
|
+
});
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
if (writeRewrittenFile(file.path, file.content, replacements)) {
|
|
140
|
+
filesProcessed++;
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
let assetsCopied = 0;
|
|
145
|
+
if (shouldCopyAssets && assetsToCopy.length > 0) {
|
|
146
|
+
assetsCopied = copyAssets(assetsToCopy, assetsDir as string);
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
let widgetsCompiled = 0;
|
|
150
|
+
if (shouldCompileWidgets && seenWidgets.size > 0) {
|
|
151
|
+
const widgetInputs = Array.from(seenWidgets.values()).map((widget) => ({
|
|
152
|
+
name: widget.name,
|
|
153
|
+
entry: mapSrcWidgetToLib(widget.path, srcDir, libDir),
|
|
154
|
+
}));
|
|
155
|
+
const widgetOutput = path.join(assetsDir as string, widgetsDir);
|
|
156
|
+
widgetsCompiled = await compileWidgets(widgetInputs, widgetOutput, widgetConfig);
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
return { filesProcessed, chunksEmitted, assetsCopied, widgetsCompiled };
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
/**
|
|
163
|
+
* Map a widget `.tsx` source path to its tsc-compiled `.js` counterpart in lib/.
|
|
164
|
+
*/
|
|
165
|
+
function mapSrcWidgetToLib(srcTsxPath: string, srcDir: string, libDir: string): string {
|
|
166
|
+
const srcRoot = path.resolve(srcDir);
|
|
167
|
+
const libRoot = path.resolve(libDir);
|
|
168
|
+
const rel = path.relative(srcRoot, srcTsxPath);
|
|
169
|
+
const libRel = rel.replace(/\.tsx$/, '.js');
|
|
170
|
+
return path.join(libRoot, libRel);
|
|
171
|
+
}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared regex fragments used by the scanner, detector, and orchestrator to
|
|
3
|
+
* identify Vertesia query-style imports. Keep these in sync with the
|
|
4
|
+
* `pattern` field of each transformer in `core/transformers/`.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Suffix tokens for `?xxx` query imports.
|
|
9
|
+
*/
|
|
10
|
+
export const QUERY_SUFFIXES = ['skill', 'raw', 'prompt', 'template', 'skills', 'templates'] as const;
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Bare filename tokens that mark an import as a transform target without a
|
|
14
|
+
* `?query` suffix (e.g. `import skill from './my-skill/SKILL.md'`).
|
|
15
|
+
*/
|
|
16
|
+
export const BARE_FILENAMES = ['SKILL.md', 'TEMPLATE.md'] as const;
|
|
17
|
+
|
|
18
|
+
const QUERY_GROUP = QUERY_SUFFIXES.join('|');
|
|
19
|
+
const BARE_GROUP = BARE_FILENAMES.map((name) => `\\/${name.replace('.', '\\.')}`).join('|');
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Coarse sniff for whether a file body could contain any query-style imports.
|
|
23
|
+
* Cheap regex check before invoking the more expensive detector.
|
|
24
|
+
*/
|
|
25
|
+
export const SNIFF_PATTERN = new RegExp(`\\?(?:${QUERY_GROUP})\\b|(?:${BARE_GROUP})\\b`);
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Matches a quoted string literal whose contents end with a query marker.
|
|
29
|
+
* Capture group 1 is the quote character; capture group 2 is the specifier.
|
|
30
|
+
*/
|
|
31
|
+
export const QUERY_STRING_LITERAL = new RegExp(
|
|
32
|
+
`(['"\`])([^'"\`]*?(?:\\?(?:${QUERY_GROUP})|${BARE_GROUP})[^'"\`]*)\\1`,
|
|
33
|
+
'g',
|
|
34
|
+
);
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Resolves a query-style import specifier to:
|
|
3
|
+
* 1. the source file path in `src/` (where the original asset lives), and
|
|
4
|
+
* 2. the chunk path in `lib/` (where the generated module will be written),
|
|
5
|
+
* and the new relative specifier to write back into the importing file.
|
|
6
|
+
*
|
|
7
|
+
* Path mapping rule: lib/ and src/ are mirror trees, so a file at
|
|
8
|
+
* `<libDir>/<rel>` corresponds to `<srcDir>/<rel>`.
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
import path from 'node:path';
|
|
12
|
+
|
|
13
|
+
export interface ResolvedImport {
|
|
14
|
+
/** Specifier with any `?query` suffix stripped. */
|
|
15
|
+
cleanSpecifier: string;
|
|
16
|
+
|
|
17
|
+
/** Absolute lib/ path of the resolved (extension-preserved) target. */
|
|
18
|
+
resolvedLibPath: string;
|
|
19
|
+
|
|
20
|
+
/** Absolute src/ path of the same target. */
|
|
21
|
+
resolvedSrcPath: string;
|
|
22
|
+
|
|
23
|
+
/** Absolute lib/ path where the generated chunk will be written. */
|
|
24
|
+
chunkLibPath: string;
|
|
25
|
+
|
|
26
|
+
/** Relative specifier (importer-relative) that should replace the original. */
|
|
27
|
+
chunkSpecifier: string;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
export function resolveImport(
|
|
31
|
+
importerLibPath: string,
|
|
32
|
+
specifier: string,
|
|
33
|
+
libDir: string,
|
|
34
|
+
srcDir: string,
|
|
35
|
+
): ResolvedImport {
|
|
36
|
+
const queryIndex = specifier.indexOf('?');
|
|
37
|
+
const cleanSpecifier = queryIndex >= 0 ? specifier.substring(0, queryIndex) : specifier;
|
|
38
|
+
|
|
39
|
+
const importerDir = path.dirname(importerLibPath);
|
|
40
|
+
const resolvedLibPath = path.resolve(importerDir, cleanSpecifier);
|
|
41
|
+
|
|
42
|
+
const libRoot = path.resolve(libDir);
|
|
43
|
+
const srcRoot = path.resolve(srcDir);
|
|
44
|
+
const relFromLib = path.relative(libRoot, resolvedLibPath);
|
|
45
|
+
const resolvedSrcPath = path.join(srcRoot, relFromLib);
|
|
46
|
+
|
|
47
|
+
const chunkLibPath = `${resolvedLibPath}.js`;
|
|
48
|
+
const chunkSpecifier = `${cleanSpecifier}.js`;
|
|
49
|
+
|
|
50
|
+
return {
|
|
51
|
+
cleanSpecifier,
|
|
52
|
+
resolvedLibPath,
|
|
53
|
+
resolvedSrcPath,
|
|
54
|
+
chunkLibPath,
|
|
55
|
+
chunkSpecifier,
|
|
56
|
+
};
|
|
57
|
+
}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Rewrites query-style import specifiers inside a JavaScript source file to
|
|
3
|
+
* point at the chunks emitted by the chunk-emitter.
|
|
4
|
+
*
|
|
5
|
+
* All replacements are intra-line (only the quoted string changes), which
|
|
6
|
+
* preserves line numbers — so existing source maps remain valid at the line
|
|
7
|
+
* level without re-emitting them.
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
import { writeFileSync } from 'node:fs';
|
|
11
|
+
|
|
12
|
+
export interface ImportReplacement {
|
|
13
|
+
/** Offset where the opening quote begins in the original content. */
|
|
14
|
+
quoteStart: number;
|
|
15
|
+
|
|
16
|
+
/** Offset where the closing quote ends (exclusive). */
|
|
17
|
+
quoteEnd: number;
|
|
18
|
+
|
|
19
|
+
/** Quote character to wrap the new specifier with. */
|
|
20
|
+
quote: "'" | '"' | '`';
|
|
21
|
+
|
|
22
|
+
/** Replacement specifier (without quotes). */
|
|
23
|
+
newSpecifier: string;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export function rewriteImports(content: string, replacements: ImportReplacement[]): string {
|
|
27
|
+
if (replacements.length === 0) {
|
|
28
|
+
return content;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
const sorted = [...replacements].sort((a, b) => b.quoteStart - a.quoteStart);
|
|
32
|
+
|
|
33
|
+
let out = content;
|
|
34
|
+
for (const r of sorted) {
|
|
35
|
+
const literal = `${r.quote}${r.newSpecifier}${r.quote}`;
|
|
36
|
+
out = out.slice(0, r.quoteStart) + literal + out.slice(r.quoteEnd);
|
|
37
|
+
}
|
|
38
|
+
return out;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
export function writeRewrittenFile(filePath: string, content: string, replacements: ImportReplacement[]): boolean {
|
|
42
|
+
if (replacements.length === 0) {
|
|
43
|
+
return false;
|
|
44
|
+
}
|
|
45
|
+
const next = rewriteImports(content, replacements);
|
|
46
|
+
writeFileSync(filePath, next, 'utf-8');
|
|
47
|
+
return true;
|
|
48
|
+
}
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Walks a compiled library directory to find candidate JavaScript files
|
|
3
|
+
* that may contain Vertesia query-style imports (`?skill`, `?raw`, …) or
|
|
4
|
+
* `SKILL.md` imports.
|
|
5
|
+
*
|
|
6
|
+
* Returns absolute paths to `.js` files whose content matches a quick
|
|
7
|
+
* content sniff for any of the trigger tokens, avoiding a re-read in the
|
|
8
|
+
* detector.
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
import { readdirSync, readFileSync, statSync } from 'node:fs';
|
|
12
|
+
import path from 'node:path';
|
|
13
|
+
import { SNIFF_PATTERN } from './patterns.js';
|
|
14
|
+
|
|
15
|
+
export interface ScannedFile {
|
|
16
|
+
/** Absolute path to the file. */
|
|
17
|
+
path: string;
|
|
18
|
+
|
|
19
|
+
/** File content, captured during scanning. */
|
|
20
|
+
content: string;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export function scanLibForQueryImports(libDir: string): ScannedFile[] {
|
|
24
|
+
const results: ScannedFile[] = [];
|
|
25
|
+
walk(libDir, results);
|
|
26
|
+
return results;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
function walk(dir: string, out: ScannedFile[]): void {
|
|
30
|
+
let entries: string[];
|
|
31
|
+
try {
|
|
32
|
+
entries = readdirSync(dir);
|
|
33
|
+
} catch {
|
|
34
|
+
return;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
for (const entry of entries) {
|
|
38
|
+
const full = path.join(dir, entry);
|
|
39
|
+
let stats: ReturnType<typeof statSync>;
|
|
40
|
+
try {
|
|
41
|
+
stats = statSync(full);
|
|
42
|
+
} catch {
|
|
43
|
+
continue;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
if (stats.isDirectory()) {
|
|
47
|
+
walk(full, out);
|
|
48
|
+
} else if (stats.isFile() && full.endsWith('.js')) {
|
|
49
|
+
const content = readFileSync(full, 'utf-8');
|
|
50
|
+
if (SNIFF_PATTERN.test(content)) {
|
|
51
|
+
out.push({ path: full, content });
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
}
|
package/src/index.ts
CHANGED
|
@@ -1,57 +1,82 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Vertesia
|
|
2
|
+
* Vertesia Build Tools
|
|
3
3
|
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
4
|
+
* Transformers for custom import syntaxes (`?skill`, `?raw`, `?prompt`,
|
|
5
|
+
* `?template`, `?skills`, `?templates`, and bare `SKILL.md` / `TEMPLATE.md`),
|
|
6
|
+
* a standalone CLI (`vertesia-build`) that runs them as a post-`tsc` step,
|
|
7
|
+
* and Vite plugins for dev-mode integration.
|
|
8
|
+
*
|
|
9
|
+
* Two consumer entry points:
|
|
10
|
+
*
|
|
11
|
+
* - **Build-time:** invoke the `vertesia-build` CLI from your package
|
|
12
|
+
* scripts (after `tsc`). Config lives under `vertesia-build` in your
|
|
13
|
+
* `package.json`. The CLI calls `transformImports()` internally — you
|
|
14
|
+
* can also call it directly if you need finer control.
|
|
15
|
+
*
|
|
16
|
+
* - **Dev-time (Vite):** import `vertesiaDevServerPlugin` (or
|
|
17
|
+
* `apiServerPlugin` for full Hono tool-server wiring) from
|
|
18
|
+
* `@vertesia/build-tools/vite`. Same transformers, same behavior,
|
|
19
|
+
* applied to source files at request time.
|
|
6
20
|
*
|
|
7
21
|
* @example
|
|
8
22
|
* ```typescript
|
|
9
|
-
* import {
|
|
23
|
+
* import { transformImports } from '@vertesia/build-tools';
|
|
10
24
|
*
|
|
11
|
-
*
|
|
12
|
-
*
|
|
13
|
-
*
|
|
14
|
-
*
|
|
15
|
-
*
|
|
16
|
-
*
|
|
17
|
-
* };
|
|
25
|
+
* await transformImports({
|
|
26
|
+
* libDir: './lib',
|
|
27
|
+
* srcDir: './src',
|
|
28
|
+
* transformers: ['skill', 'raw'],
|
|
29
|
+
* assetsDir: './dist',
|
|
30
|
+
* });
|
|
18
31
|
* ```
|
|
19
32
|
*/
|
|
20
33
|
|
|
21
|
-
//
|
|
22
|
-
export {
|
|
23
|
-
|
|
34
|
+
// esbuild-based widget bundler
|
|
35
|
+
export {
|
|
36
|
+
compileWidget,
|
|
37
|
+
compileWidgets,
|
|
38
|
+
type WidgetCompilerConfig,
|
|
39
|
+
type WidgetInput,
|
|
40
|
+
} from './core/compilers/widget.js';
|
|
41
|
+
// Parsers
|
|
42
|
+
export { type FrontmatterResult, parseFrontmatter } from './core/parsers/frontmatter.js';
|
|
43
|
+
// Transformers (the pure transformation functions)
|
|
44
|
+
export {
|
|
45
|
+
type PromptContentType,
|
|
46
|
+
type PromptDefinition,
|
|
47
|
+
PromptDefinitionSchema,
|
|
48
|
+
PromptRole,
|
|
49
|
+
promptTransformer,
|
|
50
|
+
type RenderingTemplateDefinition,
|
|
51
|
+
RenderingTemplateDefinitionSchema,
|
|
52
|
+
rawTransformer,
|
|
53
|
+
type SkillContentType,
|
|
54
|
+
type SkillDefinition,
|
|
55
|
+
SkillDefinitionSchema,
|
|
56
|
+
SkillPropertiesSchema,
|
|
57
|
+
skillCollectionTransformer,
|
|
58
|
+
skillTransformer,
|
|
59
|
+
TemplateType,
|
|
60
|
+
templateCollectionTransformer,
|
|
61
|
+
templateTransformer,
|
|
62
|
+
} from './core/transformers/index.js';
|
|
24
63
|
// Types
|
|
25
64
|
export type {
|
|
26
|
-
|
|
27
|
-
TransformerRule,
|
|
65
|
+
AssetFile,
|
|
28
66
|
TransformerPreset,
|
|
67
|
+
TransformerRule,
|
|
29
68
|
TransformFunction,
|
|
30
69
|
TransformResult,
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
} from './types.js';
|
|
34
|
-
|
|
35
|
-
// Presets
|
|
70
|
+
} from './core/types.js';
|
|
71
|
+
// CLI-friendly transformer name registry
|
|
36
72
|
export {
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
type SkillDefinition,
|
|
48
|
-
type SkillContentType,
|
|
49
|
-
type RenderingTemplateDefinition,
|
|
50
|
-
type PromptDefinition,
|
|
51
|
-
type PromptContentType,
|
|
52
|
-
PromptRole,
|
|
53
|
-
TemplateType
|
|
54
|
-
} from './presets/index.js';
|
|
55
|
-
|
|
56
|
-
// Utilities
|
|
57
|
-
export { parseFrontmatter, type FrontmatterResult } from './parsers/frontmatter.js';
|
|
73
|
+
BUILTIN_TRANSFORMER_NAMES,
|
|
74
|
+
BUILTIN_TRANSFORMERS,
|
|
75
|
+
resolveTransformerNames,
|
|
76
|
+
} from './import-transform/builtins.js';
|
|
77
|
+
// Standalone import transformer (tsc → vertesia-build pipeline)
|
|
78
|
+
export {
|
|
79
|
+
type TransformImportsOptions,
|
|
80
|
+
type TransformImportsResult,
|
|
81
|
+
transformImports,
|
|
82
|
+
} from './import-transform/index.js';
|
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Vite plugin set that mounts a Vertesia Hono tool server as middleware on
|
|
3
|
+
* the Vite dev server. Includes the Vertesia import transformers needed by
|
|
4
|
+
* `ssrLoadModule` to handle `?skill` / `?template` / `?prompt` / `?raw`
|
|
5
|
+
* imports in the tool server source.
|
|
6
|
+
*
|
|
7
|
+
* - `vite dev` → loads the server entry via `ssrLoadModule` (source,
|
|
8
|
+
* with HMR invalidation)
|
|
9
|
+
* - `vite preview` → loads the compiled server from `lib/` (validates
|
|
10
|
+
* production output)
|
|
11
|
+
*
|
|
12
|
+
* The Hono app is served on the same port as Vite under `/api`.
|
|
13
|
+
*
|
|
14
|
+
* @example
|
|
15
|
+
* ```typescript
|
|
16
|
+
* // vite.config.ts
|
|
17
|
+
* import { defineConfig } from 'vite';
|
|
18
|
+
* import { apiServerPlugin } from '@vertesia/build-tools/vite';
|
|
19
|
+
*
|
|
20
|
+
* export default defineConfig({
|
|
21
|
+
* plugins: [
|
|
22
|
+
* apiServerPlugin({ entry: './src/tool-server/server.ts' }),
|
|
23
|
+
* ],
|
|
24
|
+
* });
|
|
25
|
+
* ```
|
|
26
|
+
*/
|
|
27
|
+
|
|
28
|
+
import type { IncomingMessage, ServerResponse } from 'node:http';
|
|
29
|
+
import path from 'node:path';
|
|
30
|
+
import { getRequestListener } from '@hono/node-server';
|
|
31
|
+
import type { Plugin, ViteDevServer } from 'vite';
|
|
32
|
+
import { vertesiaDevServerPlugin } from './dev-server.js';
|
|
33
|
+
|
|
34
|
+
interface HonoApp {
|
|
35
|
+
fetch: (request: Request, env?: unknown, executionCtx?: unknown) => Response | Promise<Response>;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
type NextHandleFunction = (req: IncomingMessage, res: ServerResponse, next: (err?: unknown) => void) => void;
|
|
39
|
+
|
|
40
|
+
export interface ApiServerPluginOptions {
|
|
41
|
+
/**
|
|
42
|
+
* Tool server entry point (TypeScript source). Resolved by Vite from the
|
|
43
|
+
* project root in dev mode.
|
|
44
|
+
* @default './src/tool-server/server.ts'
|
|
45
|
+
*/
|
|
46
|
+
entry?: string;
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* Compiled server module path used in preview mode. Resolved relative to
|
|
50
|
+
* `process.cwd()` (the consuming project's root when running `vite preview`).
|
|
51
|
+
* @default './lib/server.js'
|
|
52
|
+
*/
|
|
53
|
+
compiledEntry?: string;
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* Path prefix that routes requests to the Hono server. Requests whose URL
|
|
57
|
+
* starts with this prefix are forwarded; others fall through to Vite.
|
|
58
|
+
* @default '/api'
|
|
59
|
+
*/
|
|
60
|
+
apiPrefix?: string;
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* Names of transformers to enable in dev mode. Defaults to all built-ins
|
|
64
|
+
* (`skill`, `skills`, `template`, `templates`, `prompt`, `raw`).
|
|
65
|
+
*/
|
|
66
|
+
transformers?: readonly string[];
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
export function apiServerPlugin(options: ApiServerPluginOptions = {}): Plugin[] {
|
|
70
|
+
const {
|
|
71
|
+
entry = './src/tool-server/server.ts',
|
|
72
|
+
compiledEntry = './lib/server.js',
|
|
73
|
+
apiPrefix = '/api',
|
|
74
|
+
transformers,
|
|
75
|
+
} = options;
|
|
76
|
+
|
|
77
|
+
// Resolve compiled entry relative to the consumer's cwd. Vite is run from
|
|
78
|
+
// the project root by convention, so this lands at the right file.
|
|
79
|
+
const absoluteCompiledEntry = path.resolve(process.cwd(), compiledEntry);
|
|
80
|
+
|
|
81
|
+
return [
|
|
82
|
+
// Vertesia query-import transformer (skill / raw / prompt / template etc.).
|
|
83
|
+
vertesiaDevServerPlugin(transformers ? { transformers } : undefined),
|
|
84
|
+
|
|
85
|
+
// Hono middleware bridge for dev + preview.
|
|
86
|
+
{
|
|
87
|
+
name: 'vertesia-api-server',
|
|
88
|
+
|
|
89
|
+
configureServer(server: ViteDevServer) {
|
|
90
|
+
server.middlewares.use(createDevListener(server, entry, apiPrefix));
|
|
91
|
+
},
|
|
92
|
+
|
|
93
|
+
configurePreviewServer(server) {
|
|
94
|
+
server.middlewares.use(createPreviewListener(absoluteCompiledEntry, apiPrefix));
|
|
95
|
+
},
|
|
96
|
+
},
|
|
97
|
+
];
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
/**
|
|
101
|
+
* Connect-compatible middleware that loads the Hono app from source via
|
|
102
|
+
* Vite's `ssrLoadModule` (preserving HMR invalidation on changes).
|
|
103
|
+
*/
|
|
104
|
+
function createDevListener(server: ViteDevServer, entry: string, apiPrefix: string): NextHandleFunction {
|
|
105
|
+
return async (req, res, next) => {
|
|
106
|
+
if (!req.url?.startsWith(apiPrefix)) {
|
|
107
|
+
return next();
|
|
108
|
+
}
|
|
109
|
+
try {
|
|
110
|
+
const mod = await server.ssrLoadModule(entry);
|
|
111
|
+
const app = mod.default as HonoApp;
|
|
112
|
+
const requestListener = getRequestListener(app.fetch);
|
|
113
|
+
void requestListener(req, res);
|
|
114
|
+
} catch (e) {
|
|
115
|
+
next(e);
|
|
116
|
+
}
|
|
117
|
+
};
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
/**
|
|
121
|
+
* Connect-compatible middleware that loads the compiled Hono app from `lib/`
|
|
122
|
+
* for production validation. The compiled module is cached after first load
|
|
123
|
+
* (no HMR in preview mode).
|
|
124
|
+
*/
|
|
125
|
+
function createPreviewListener(compiledEntry: string, apiPrefix: string): NextHandleFunction {
|
|
126
|
+
let appPromise: Promise<HonoApp> | null = null;
|
|
127
|
+
return async (req, res, next) => {
|
|
128
|
+
if (!req.url?.startsWith(apiPrefix)) {
|
|
129
|
+
return next();
|
|
130
|
+
}
|
|
131
|
+
try {
|
|
132
|
+
if (!appPromise) {
|
|
133
|
+
appPromise = import(compiledEntry).then((mod) => mod.default);
|
|
134
|
+
}
|
|
135
|
+
const app = await appPromise;
|
|
136
|
+
const requestListener = getRequestListener(app.fetch);
|
|
137
|
+
void requestListener(req, res);
|
|
138
|
+
} catch (e) {
|
|
139
|
+
next(e);
|
|
140
|
+
}
|
|
141
|
+
};
|
|
142
|
+
}
|