@c4a/context-cli 0.5.38 → 0.5.39

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.
@@ -0,0 +1,776 @@
1
+ /* Generated by packages/context-cli/scripts/build-aspect-runtime.ts. Do not edit directly. */
2
+ // src/aspect-runtime/index.ts
3
+ import path3 from "node:path";
4
+ import { fileURLToPath } from "node:url";
5
+
6
+ // src/aspect-runtime/context.ts
7
+ import { readFile, readdir, realpath, stat } from "node:fs/promises";
8
+ import path2 from "node:path";
9
+ import { pathToFileURL } from "node:url";
10
+
11
+ // src/aspect-runtime/helpers.ts
12
+ import path from "node:path";
13
+ import * as ts from "typescript";
14
+ function toPosix(value) {
15
+ return value.split(path.sep).join("/");
16
+ }
17
+ function repoRelative(repoRoot, absPath) {
18
+ const rel = toPosix(path.relative(repoRoot, absPath));
19
+ validateSourcePath(rel);
20
+ return rel;
21
+ }
22
+ function validateSourcePath(sourcePath) {
23
+ if (sourcePath.length === 0 || sourcePath.includes("\x00") || sourcePath.includes("\\")) {
24
+ throw new Error(`invalid source_path: ${sourcePath}`);
25
+ }
26
+ if (path.posix.isAbsolute(sourcePath)) {
27
+ throw new Error(`source_path must be relative: ${sourcePath}`);
28
+ }
29
+ const parts = sourcePath.split("/");
30
+ if (parts.some((part) => part.length === 0 || part === "." || part === "..")) {
31
+ throw new Error(`source_path must be normalized and stay inside repo_root: ${sourcePath}`);
32
+ }
33
+ }
34
+ function sourceFileRef(sourcePath) {
35
+ validateSourcePath(sourcePath);
36
+ return {
37
+ path: sourcePath,
38
+ name: path.posix.basename(sourcePath),
39
+ extension: path.posix.extname(sourcePath).toLowerCase()
40
+ };
41
+ }
42
+ function encodeSourcePath(sourcePath) {
43
+ validateSourcePath(sourcePath);
44
+ return sourcePath.split("/").map((segment) => encodeURIComponent(segment)).join("/");
45
+ }
46
+ function encodeFragment(fragment) {
47
+ return encodeURIComponent(fragment);
48
+ }
49
+ function fileSourceRef(sourcePath, artifact, hash) {
50
+ return `file:${encodeSourcePath(sourcePath)}#${encodeFragment(artifact)}@${hash}`;
51
+ }
52
+ function slugSegment(raw) {
53
+ const segment = raw.trim().replace(/\.[^.]+$/u, "").replace(/\+$/u, "").replace(/([a-z0-9])([A-Z])/gu, "$1-$2").replace(/[^A-Za-z0-9]+/gu, "-").replace(/^-+|-+$/gu, "").toLowerCase();
54
+ return segment.length > 0 ? segment : "untitled";
55
+ }
56
+ function markdownFence(language, body) {
57
+ const fence = body.includes("```") ? "~~~~" : "```";
58
+ return `${fence}${language}
59
+ ${body.trimEnd()}
60
+ ${fence}`;
61
+ }
62
+ function normalizeSymbolSlug(raw) {
63
+ const cleaned = raw.replace(/\.[^.]+$/u, "").replace(/\+$/u, "").replace(/[^A-Za-z0-9]+/gu, "").toLowerCase();
64
+ return cleaned.length === 0 ? "unknown" : cleaned;
65
+ }
66
+ function normalizeComponentSlug(raw) {
67
+ return normalizeSymbolSlug(raw);
68
+ }
69
+ function inferComponentName(source, fallback) {
70
+ const patterns = [
71
+ /\bimport\s*\{[^}]*\b([A-Z][A-Za-z0-9]+)\b[^}]*\}/u,
72
+ /<\s*([A-Z][A-Za-z0-9]+)\b/u,
73
+ /\b([A-Z][A-Za-z0-9]+)\s*\(/u
74
+ ];
75
+ for (const pattern of patterns) {
76
+ const match = source.match(pattern);
77
+ if (match?.[1])
78
+ return match[1];
79
+ }
80
+ return fallback;
81
+ }
82
+ function artifactFromRelPath(sourcePath) {
83
+ return sourcePath.replace(/\.[^.]+$/u, "");
84
+ }
85
+ function firstMarkdownHeading(markdown, fallback) {
86
+ const heading = markdown.match(/^#{1,6}\s+(.+)$/mu)?.[1]?.trim();
87
+ return heading && heading.length > 0 ? heading : fallback;
88
+ }
89
+ function sourceFileFor(fileName, source) {
90
+ const ext = path.extname(fileName).toLowerCase();
91
+ const scriptKind = ext === ".tsx" ? ts.ScriptKind.TSX : ext === ".jsx" ? ts.ScriptKind.JSX : ext === ".js" || ext === ".cjs" || ext === ".mjs" ? ts.ScriptKind.JS : ts.ScriptKind.TS;
92
+ return ts.createSourceFile(fileName, source, ts.ScriptTarget.Latest, true, scriptKind);
93
+ }
94
+ function propertyNameText(name) {
95
+ if (ts.isIdentifier(name) || ts.isStringLiteral(name) || ts.isNumericLiteral(name))
96
+ return name.text;
97
+ if (ts.isComputedPropertyName(name) && ts.isStringLiteralLike(name.expression)) {
98
+ return name.expression.text;
99
+ }
100
+ return null;
101
+ }
102
+ function jsxTagNameText(name) {
103
+ if (ts.isIdentifier(name))
104
+ return name.text;
105
+ if (ts.isPropertyAccessExpression(name))
106
+ return name.name.text;
107
+ return null;
108
+ }
109
+ function pushUnique(out, seen, value) {
110
+ if (!value || seen.has(value))
111
+ return;
112
+ seen.add(value);
113
+ out.push(value);
114
+ }
115
+ function parseTsxComponentNames(source, fileName) {
116
+ const sf = sourceFileFor(fileName, source);
117
+ const names = [];
118
+ const seen = new Set;
119
+ const isComponentName = (value) => /^[A-Z][A-Za-z0-9]+$/u.test(value);
120
+ function visit(node) {
121
+ if (ts.isImportDeclaration(node) && node.importClause?.namedBindings && ts.isNamedImports(node.importClause.namedBindings)) {
122
+ for (const specifier of node.importClause.namedBindings.elements) {
123
+ const imported = specifier.propertyName?.text ?? specifier.name.text;
124
+ if (isComponentName(imported))
125
+ pushUnique(names, seen, imported);
126
+ }
127
+ }
128
+ if (ts.isJsxOpeningElement(node) || ts.isJsxSelfClosingElement(node)) {
129
+ const tag = jsxTagNameText(node.tagName);
130
+ if (tag && isComponentName(tag))
131
+ pushUnique(names, seen, tag);
132
+ }
133
+ if (ts.isCallExpression(node) && ts.isIdentifier(node.expression) && isComponentName(node.expression.text)) {
134
+ pushUnique(names, seen, node.expression.text);
135
+ }
136
+ ts.forEachChild(node, visit);
137
+ }
138
+ visit(sf);
139
+ return names;
140
+ }
141
+ function parseJsLikeTokenNames(source, fileName, category) {
142
+ const sf = sourceFileFor(fileName, source);
143
+ const names = [];
144
+ const seen = new Set;
145
+ function maybePush(value) {
146
+ if (!value)
147
+ return;
148
+ if (!/^[A-Za-z_$-][A-Za-z0-9_$-]*$/u.test(value))
149
+ return;
150
+ pushUnique(names, seen, value);
151
+ }
152
+ function visit(node) {
153
+ if (ts.isPropertyAssignment(node)) {
154
+ const name = propertyNameText(node.name);
155
+ if (name === "cssVarName" && ts.isStringLiteralLike(node.initializer)) {
156
+ maybePush(node.initializer.text);
157
+ } else {
158
+ maybePush(name);
159
+ }
160
+ } else if (ts.isShorthandPropertyAssignment(node)) {
161
+ maybePush(node.name.text);
162
+ } else if (ts.isStringLiteralLike(node) && (/^--[A-Za-z0-9_-]+$/u.test(node.text) || /^\$[A-Za-z0-9_-]+$/u.test(node.text))) {
163
+ maybePush(node.text);
164
+ }
165
+ ts.forEachChild(node, visit);
166
+ }
167
+ visit(sf);
168
+ return names.sort((a, b) => a.localeCompare(b));
169
+ }
170
+ function parseScssTokenNames(source) {
171
+ const names = new Set;
172
+ for (const line of source.split(/\r?\n/u)) {
173
+ const trimmed = line.trim();
174
+ if (trimmed.startsWith("//") || trimmed.startsWith("@import") || trimmed.startsWith("@use"))
175
+ continue;
176
+ const variable = trimmed.match(/^(\$[A-Za-z0-9_-]+)\s*:/u)?.[1];
177
+ if (variable)
178
+ names.add(variable);
179
+ const cssVar = trimmed.match(/(--[A-Za-z0-9_-]+)\s*:/u)?.[1];
180
+ if (cssVar)
181
+ names.add(cssVar);
182
+ const property = trimmed.match(/^([A-Za-z-]+)\s*:/u)?.[1];
183
+ if (property && !["if", "for", "each", "include"].includes(property))
184
+ names.add(property);
185
+ }
186
+ return [...names].sort((a, b) => a.localeCompare(b));
187
+ }
188
+ function codePackages(input) {
189
+ return "code" in input ? input.code.packages : input.code_index?.packages ?? [];
190
+ }
191
+ function codeSymbols(input) {
192
+ return "code" in input ? input.code.symbols : input.code_index?.symbols ?? [];
193
+ }
194
+ function resolvePackageNodeSlug(input, packageSlug) {
195
+ return codePackages(input).find((pkg) => pkg.package_slug === packageSlug)?.node_slug ?? packageSlug;
196
+ }
197
+ function resolveSymbolNodeSlug(input, packageSlug, componentName) {
198
+ const componentSlug = normalizeSymbolSlug(componentName);
199
+ const normalizedExport = componentName.toLowerCase();
200
+ const match = codeSymbols(input).find((symbol) => {
201
+ if (symbol.package_slug !== undefined && symbol.package_slug !== packageSlug)
202
+ return false;
203
+ const nodeLeaf = symbol.node_slug.split("/").at(-1);
204
+ const names = [symbol.export_name, symbol.symbol_name, ...symbol.aliases ?? [], nodeLeaf].filter((name) => typeof name === "string" && name.length > 0);
205
+ return names.some((rawName) => normalizeSymbolSlug(rawName) === componentSlug || rawName.toLowerCase() === normalizedExport);
206
+ });
207
+ return match?.node_slug ?? `${resolvePackageNodeSlug(input, packageSlug)}/symbol/${componentSlug}`;
208
+ }
209
+
210
+ // src/aspect-runtime/context.ts
211
+ function defineAspect(plugin) {
212
+ return plugin;
213
+ }
214
+ async function readHostInput() {
215
+ const chunks = [];
216
+ for await (const chunk of process.stdin) {
217
+ chunks.push(Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk));
218
+ }
219
+ const input = JSON.parse(Buffer.concat(chunks).toString("utf8"));
220
+ for (const key of ["workspace_root", "repo_root", "aspect", "source_slug", "source_id", "snapshot_id"]) {
221
+ if (typeof input[key] !== "string" || input[key].length === 0) {
222
+ throw new Error(`aspect host input missing ${key}`);
223
+ }
224
+ }
225
+ return input;
226
+ }
227
+ async function pathExists(absPath) {
228
+ try {
229
+ await stat(absPath);
230
+ return true;
231
+ } catch (err) {
232
+ const code = err.code;
233
+ if (code === "ENOENT" || code === "ENOTDIR")
234
+ return false;
235
+ throw err;
236
+ }
237
+ }
238
+ function sourceRefFromInput(source) {
239
+ return typeof source === "string" ? source : source.path;
240
+ }
241
+ function normalizeSourceRoot(root) {
242
+ if (root === undefined || root === "" || root === ".")
243
+ return "";
244
+ const normalized = toPosix(path2.posix.normalize(toPosix(root)));
245
+ validateSourcePath(normalized);
246
+ return normalized;
247
+ }
248
+ async function resolveRepoPath(repoRoot, sourcePath) {
249
+ validateSourcePath(sourcePath);
250
+ const realRepoRoot = await realpath(repoRoot);
251
+ const realSource = await realpath(path2.resolve(realRepoRoot, sourcePath));
252
+ const rel = path2.relative(realRepoRoot, realSource);
253
+ if (rel.startsWith("..") || path2.isAbsolute(rel)) {
254
+ throw new Error(`source_path escapes repo_root: ${sourcePath}`);
255
+ }
256
+ return realSource;
257
+ }
258
+ async function readRepoText(repoRoot, source) {
259
+ return readFile(await resolveRepoPath(repoRoot, sourceRefFromInput(source)), "utf8");
260
+ }
261
+ async function collectRepoFiles(repoRoot, rootRel, extensions, recursive) {
262
+ const normalizedRoot = normalizeSourceRoot(rootRel);
263
+ const realRepoRoot = await realpath(repoRoot);
264
+ let root;
265
+ try {
266
+ root = normalizedRoot.length > 0 ? await resolveRepoPath(realRepoRoot, normalizedRoot) : realRepoRoot;
267
+ } catch (err) {
268
+ const code = err.code;
269
+ if (code === "ENOENT" || code === "ENOTDIR")
270
+ return [];
271
+ throw err;
272
+ }
273
+ if (!await pathExists(root))
274
+ return [];
275
+ const normalizedExtensions = extensions.map((ext) => ext.toLowerCase());
276
+ const out = [];
277
+ async function walk(dir) {
278
+ const entries = await readdir(dir, { withFileTypes: true });
279
+ for (const entry of entries) {
280
+ if (entry.name.startsWith("."))
281
+ continue;
282
+ const absPath = path2.join(dir, entry.name);
283
+ if (entry.isDirectory()) {
284
+ if (recursive)
285
+ await walk(absPath);
286
+ continue;
287
+ }
288
+ if (!entry.isFile())
289
+ continue;
290
+ const ext = path2.extname(entry.name).toLowerCase();
291
+ if (normalizedExtensions.length > 0 && !normalizedExtensions.includes(ext))
292
+ continue;
293
+ out.push(sourceFileRef(repoRelative(realRepoRoot, absPath)));
294
+ }
295
+ }
296
+ await walk(root);
297
+ return out.sort((a, b) => a.path.localeCompare(b.path));
298
+ }
299
+ function normalizeSection(row) {
300
+ const sourcePath = row.source_path ?? (row.source !== undefined ? sourceRefFromInput(row.source) : undefined);
301
+ if (sourcePath === undefined) {
302
+ throw new Error("ctx.emit.section requires source or source_path");
303
+ }
304
+ validateSourcePath(sourcePath);
305
+ if (row.artifact.trim().length === 0) {
306
+ throw new Error("ctx.emit.section requires a non-empty artifact");
307
+ }
308
+ return {
309
+ node_slug: row.node_slug,
310
+ kind: row.kind,
311
+ ...row.summary !== undefined && row.summary.length > 0 ? { summary: row.summary } : {},
312
+ content: row.content,
313
+ source_path: sourcePath,
314
+ artifact: row.artifact
315
+ };
316
+ }
317
+ function normalizeNode(row) {
318
+ if (row.node_slug.trim().length === 0) {
319
+ throw new Error("ctx.emit.node requires a non-empty node_slug");
320
+ }
321
+ if (row.title.trim().length === 0) {
322
+ throw new Error("ctx.emit.node requires a non-empty title");
323
+ }
324
+ return {
325
+ node_slug: row.node_slug,
326
+ title: row.title,
327
+ type: row.type ?? "entity",
328
+ tags: [...row.tags ?? ["module"]],
329
+ ...row.summary !== undefined && row.summary.length > 0 ? { summary: row.summary } : {},
330
+ ...row.parent_slug !== undefined && row.parent_slug.length > 0 ? { parent_slug: row.parent_slug } : {},
331
+ ...row.code_package !== undefined && row.code_package.length > 0 ? { code_package: row.code_package } : {},
332
+ ...row.language !== undefined && row.language.length > 0 ? { language: row.language } : {}
333
+ };
334
+ }
335
+ function findPackage(packages, packageSlug) {
336
+ return packages.find((pkg) => pkg.package_slug === packageSlug);
337
+ }
338
+ function findSymbol(symbols, packageSlug, symbolName) {
339
+ const target = normalizeComponentSlug(symbolName);
340
+ return symbols.find((symbol) => {
341
+ if (symbol.package_slug !== undefined && symbol.package_slug !== packageSlug)
342
+ return false;
343
+ const nodeLeaf = symbol.node_slug.split("/").at(-1);
344
+ const names = [symbol.export_name, symbol.symbol_name, ...symbol.aliases ?? [], nodeLeaf].filter((name) => typeof name === "string" && name.length > 0);
345
+ return names.some((name) => normalizeComponentSlug(name) === target || name.toLowerCase() === symbolName.toLowerCase());
346
+ });
347
+ }
348
+ function buildContext(input, nodes, sections, warnings) {
349
+ const packages = input.code_index?.packages ?? [];
350
+ const symbols = input.code_index?.symbols ?? [];
351
+ return {
352
+ aspect: {
353
+ name: input.aspect,
354
+ source_slug: input.source_slug,
355
+ source_id: input.source_id,
356
+ snapshot_id: input.snapshot_id
357
+ },
358
+ source: {
359
+ glob: (options = {}) => collectRepoFiles(input.repo_root, options.root ?? "", options.extensions ?? [], options.recursive ?? true),
360
+ immediateFiles: (root, extensions) => collectRepoFiles(input.repo_root, root, extensions, false),
361
+ exists: async (source) => {
362
+ try {
363
+ await resolveRepoPath(input.repo_root, sourceRefFromInput(source));
364
+ return true;
365
+ } catch (err) {
366
+ const code = err.code;
367
+ if (code === "ENOENT" || code === "ENOTDIR")
368
+ return false;
369
+ return false;
370
+ }
371
+ },
372
+ readText: (source) => readRepoText(input.repo_root, source)
373
+ },
374
+ code: {
375
+ packages,
376
+ symbols,
377
+ findPackage: (packageSlug) => findPackage(packages, packageSlug),
378
+ findSymbol: (packageSlug, symbolName) => findSymbol(symbols, packageSlug, symbolName)
379
+ },
380
+ emit: {
381
+ node: (row) => {
382
+ nodes.push(normalizeNode(row));
383
+ },
384
+ section: (row) => {
385
+ sections.push(normalizeSection(row));
386
+ },
387
+ warning: (message, detail) => {
388
+ warnings.push({
389
+ message,
390
+ ...detail !== undefined ? { detail } : {}
391
+ });
392
+ }
393
+ }
394
+ };
395
+ }
396
+ async function runAspectPluginForHost(pluginModulePath, input) {
397
+ const nodes = [];
398
+ const sections = [];
399
+ const warnings = [];
400
+ const pluginModule = await import(pathToFileURL(pluginModulePath).href);
401
+ const plugin = pluginModule.default;
402
+ if (plugin === undefined || typeof plugin.capture !== "function") {
403
+ throw new Error(`${pluginModulePath} must export default defineAspect({ capture(ctx) { ... } })`);
404
+ }
405
+ const ctx = buildContext(input, nodes, sections, warnings);
406
+ await plugin.setup?.(ctx);
407
+ try {
408
+ await plugin.capture(ctx);
409
+ } finally {
410
+ await plugin.teardown?.(ctx);
411
+ }
412
+ return {
413
+ type: "aspect-plugin-result.v1",
414
+ nodes,
415
+ sections,
416
+ warnings
417
+ };
418
+ }
419
+ // src/aspect-runtime/mdx.ts
420
+ import { unified } from "unified";
421
+ import remarkGfm from "remark-gfm";
422
+ import remarkMdx from "remark-mdx";
423
+ import remarkParse from "remark-parse";
424
+ import remarkStringify from "remark-stringify";
425
+ function compactWhitespace(value) {
426
+ return value.replace(/[ \t]+/gu, " ").replace(/\n{3,}/gu, `
427
+
428
+ `).trim();
429
+ }
430
+ function normalizeMarkdown(value) {
431
+ return value.replace(/\r\n?/gu, `
432
+ `).replace(/\n{3,}/gu, `
433
+
434
+ `).trim();
435
+ }
436
+ function cloneNode(node, children) {
437
+ const cloned = { ...node };
438
+ delete cloned.attributes;
439
+ delete cloned.name;
440
+ if (children !== undefined)
441
+ cloned.children = children;
442
+ return cloned;
443
+ }
444
+ function cloneMarkdownNode(node) {
445
+ return {
446
+ ...node,
447
+ ...node.children !== undefined ? { children: node.children.map(cloneMarkdownNode) } : {}
448
+ };
449
+ }
450
+ function mdxAttrValueText(value) {
451
+ if (value === null || value === undefined)
452
+ return;
453
+ if (typeof value === "string")
454
+ return value;
455
+ const expression = value.value?.trim();
456
+ if (!expression)
457
+ return;
458
+ return expressionLiteralText(expression) ?? expression;
459
+ }
460
+ function mdxAttributes(attrs) {
461
+ const out = {};
462
+ for (const attr of attrs ?? []) {
463
+ if (attr.type !== "mdxJsxAttribute" || !attr.name)
464
+ continue;
465
+ const value = mdxAttrValueText(attr.value);
466
+ if (value !== undefined)
467
+ out[attr.name] = value;
468
+ }
469
+ return out;
470
+ }
471
+ function expressionLiteralText(expression) {
472
+ const trimmed = expression.trim();
473
+ const quote = trimmed.charAt(0);
474
+ if ((quote === '"' || quote === "'" || quote === "`") && trimmed.charAt(trimmed.length - 1) === quote) {
475
+ const body = trimmed.slice(1, -1);
476
+ if (quote === "`" && body.includes("${"))
477
+ return;
478
+ return body;
479
+ }
480
+ return;
481
+ }
482
+ function markdownText(value) {
483
+ return { type: "text", value };
484
+ }
485
+ function markdownLink(label, href) {
486
+ return {
487
+ type: "link",
488
+ url: href,
489
+ title: null,
490
+ children: [markdownText(label)]
491
+ };
492
+ }
493
+ function markdownCodeBlock(code) {
494
+ return {
495
+ type: "code",
496
+ lang: "tsx",
497
+ value: code.trim()
498
+ };
499
+ }
500
+ function expressionNodes(node, context) {
501
+ const value = expressionLiteralText(node.value ?? "");
502
+ if (value === undefined || value.length === 0)
503
+ return [];
504
+ if (context === "text" && (node.value ?? "").trim().startsWith("`")) {
505
+ return [{ type: "inlineCode", value }];
506
+ }
507
+ return nodesForContext([markdownText(value)], context);
508
+ }
509
+ function isPhrasing(node) {
510
+ return [
511
+ "break",
512
+ "delete",
513
+ "emphasis",
514
+ "html",
515
+ "image",
516
+ "imageReference",
517
+ "inlineCode",
518
+ "link",
519
+ "linkReference",
520
+ "strong",
521
+ "text"
522
+ ].includes(node.type);
523
+ }
524
+ function asFlowNodes(nodes) {
525
+ if (nodes.length === 0)
526
+ return [];
527
+ return nodes.every(isPhrasing) ? [{ type: "paragraph", children: nodes }] : nodes;
528
+ }
529
+ function nodesForContext(nodes, context) {
530
+ return context === "flow" ? asFlowNodes(nodes) : nodes.filter(isPhrasing);
531
+ }
532
+ function mdxElementNodes(node, context) {
533
+ const attrs = mdxAttributes(node.attributes);
534
+ if (attrs.code) {
535
+ return context === "text" ? [{ type: "inlineCode", value: attrs.code.trim() }] : [markdownCodeBlock(attrs.code)];
536
+ }
537
+ if (attrs.text) {
538
+ return nodesForContext([attrs.href ? markdownLink(attrs.text, attrs.href) : markdownText(attrs.text)], context);
539
+ }
540
+ if (node.name === "br")
541
+ return nodesForContext([{ type: "break" }], context);
542
+ if (node.name === "img" || node.name === "Image") {
543
+ const alt = attrs.alt ?? attrs.title;
544
+ return alt ? nodesForContext([markdownText(alt)], context) : [];
545
+ }
546
+ const childContext = context === "text" ? "text" : "flow";
547
+ const children = transformChildren(node.children ?? [], childContext);
548
+ return nodesForContext(children, context);
549
+ }
550
+ function transformChildren(children, context) {
551
+ return children.flatMap((child) => transformNode(child, context));
552
+ }
553
+ function transformNode(node, context) {
554
+ if (node.type === "mdxjsEsm")
555
+ return [];
556
+ if (node.type === "mdxFlowExpression" || node.type === "mdxTextExpression")
557
+ return expressionNodes(node, context);
558
+ if (node.type === "mdxJsxFlowElement" || node.type === "mdxJsxTextElement")
559
+ return mdxElementNodes(node, context);
560
+ if (node.children) {
561
+ const childContext = node.type === "paragraph" || node.type === "heading" ? "text" : "flow";
562
+ return [cloneNode(node, transformChildren(node.children, childContext))];
563
+ }
564
+ return [cloneNode(node, undefined)];
565
+ }
566
+ function normalizeMdxTree() {
567
+ return (tree) => {
568
+ tree.children = transformChildren(tree.children ?? [], "flow");
569
+ };
570
+ }
571
+ var mdxProcessor = unified().use(remarkParse).use(remarkMdx).use(remarkGfm).use(normalizeMdxTree).use(remarkStringify, {
572
+ bullet: "-",
573
+ fences: true,
574
+ listItemIndent: "one",
575
+ rule: "-",
576
+ setext: false
577
+ });
578
+ function markdownBlocks(markdown) {
579
+ const blocks = [];
580
+ let paragraph = [];
581
+ let code = [];
582
+ let codeLanguage = "";
583
+ let readingCode = false;
584
+ function flushParagraph() {
585
+ const text = compactWhitespace(paragraph.join(`
586
+ `));
587
+ if (text.length > 0)
588
+ blocks.push({ type: "paragraph", text });
589
+ paragraph = [];
590
+ }
591
+ for (const line of markdown.split(/\r?\n/u)) {
592
+ const fence = line.trim().match(/^(```|~~~)(.*)$/u);
593
+ if (fence) {
594
+ if (readingCode) {
595
+ blocks.push({ type: "code", language: codeLanguage, text: code.join(`
596
+ `) });
597
+ code = [];
598
+ codeLanguage = "";
599
+ readingCode = false;
600
+ } else {
601
+ flushParagraph();
602
+ codeLanguage = (fence[2] ?? "").trim();
603
+ readingCode = true;
604
+ }
605
+ continue;
606
+ }
607
+ if (readingCode) {
608
+ code.push(line);
609
+ continue;
610
+ }
611
+ const heading = line.match(/^(#{1,6})\s+(.+)$/u);
612
+ if (heading) {
613
+ flushParagraph();
614
+ blocks.push({ type: "heading", depth: (heading[1] ?? "").length, text: (heading[2] ?? "").trim() });
615
+ continue;
616
+ }
617
+ if (line.trim().length === 0) {
618
+ flushParagraph();
619
+ continue;
620
+ }
621
+ paragraph.push(line);
622
+ }
623
+ flushParagraph();
624
+ return blocks;
625
+ }
626
+ function plainText(node) {
627
+ if (typeof node.value === "string")
628
+ return node.value;
629
+ return (node.children ?? []).map(plainText).join("");
630
+ }
631
+ function normalizedHeadingLabel(value) {
632
+ return compactWhitespace(value).toLowerCase();
633
+ }
634
+ function headingMatchesTitle(node, title) {
635
+ if (title === undefined || node.type !== "heading")
636
+ return false;
637
+ return normalizedHeadingLabel(plainText(node)) === normalizedHeadingLabel(title);
638
+ }
639
+ function firstHeadingText(nodes) {
640
+ for (const node of nodes) {
641
+ if (node.type === "heading") {
642
+ const text = compactWhitespace(plainText(node));
643
+ if (text.length > 0)
644
+ return text;
645
+ }
646
+ const childHeading = firstHeadingText(node.children ?? []);
647
+ if (childHeading)
648
+ return childHeading;
649
+ }
650
+ return;
651
+ }
652
+ function stripYamlFrontmatter(raw) {
653
+ const normalized = raw.replace(/\r\n?/gu, `
654
+ `);
655
+ if (!normalized.startsWith(`---
656
+ `))
657
+ return raw;
658
+ const lines = normalized.split(`
659
+ `);
660
+ for (let index = 1;index < lines.length; index += 1) {
661
+ if (lines[index]?.trim() === "---") {
662
+ return lines.slice(index + 1).join(`
663
+ `);
664
+ }
665
+ }
666
+ return raw;
667
+ }
668
+ function clampHeadingLevel(value) {
669
+ return Math.min(6, Math.max(1, Math.trunc(value)));
670
+ }
671
+ function minHeadingDepth(nodes) {
672
+ let out;
673
+ function visit(node) {
674
+ if (node.type === "heading" && typeof node.depth === "number") {
675
+ out = out === undefined ? node.depth : Math.min(out, node.depth);
676
+ }
677
+ for (const child of node.children ?? [])
678
+ visit(child);
679
+ }
680
+ for (const node of nodes)
681
+ visit(node);
682
+ return out;
683
+ }
684
+ function shiftHeadingDepths(nodes, offset) {
685
+ return nodes.map((node) => {
686
+ const next = cloneMarkdownNode(node);
687
+ if (next.type === "heading" && typeof next.depth === "number") {
688
+ next.depth = clampHeadingLevel(next.depth + offset);
689
+ }
690
+ if (next.children !== undefined) {
691
+ next.children = shiftHeadingDepths(next.children, offset);
692
+ }
693
+ return next;
694
+ });
695
+ }
696
+ function sectionBodyMarkdownFromTree(tree, options) {
697
+ const minHeadingLevel = clampHeadingLevel(options.minHeadingLevel ?? 3);
698
+ const removeTitleHeading = options.removeTitleHeading ?? true;
699
+ const children = (tree.children ?? []).map(cloneMarkdownNode);
700
+ const withoutTitle = removeTitleHeading && children[0] !== undefined && headingMatchesTitle(children[0], options.title) ? children.slice(1) : children;
701
+ const minDepth = minHeadingDepth(withoutTitle);
702
+ const headingOffset = minDepth === undefined ? 0 : Math.max(0, minHeadingLevel - minDepth);
703
+ return normalizeMarkdown(mdxProcessor.stringify({
704
+ ...tree,
705
+ children: shiftHeadingDepths(withoutTitle, headingOffset)
706
+ }));
707
+ }
708
+ function normalizeSectionMarkdown(markdown, options = {}) {
709
+ const source = stripYamlFrontmatter(markdown);
710
+ const tree = mdxProcessor.parse(source);
711
+ const normalized = mdxProcessor.runSync(tree);
712
+ const title = options.title ?? firstHeadingText(normalized.children ?? []);
713
+ return sectionBodyMarkdownFromTree(normalized, {
714
+ ...options,
715
+ ...title !== undefined ? { title } : {}
716
+ });
717
+ }
718
+ function parseMdxDocument(raw, fallbackTitle) {
719
+ const source = stripYamlFrontmatter(raw);
720
+ const tree = mdxProcessor.parse(source);
721
+ const normalized = mdxProcessor.runSync(tree);
722
+ const markdown = normalizeMarkdown(mdxProcessor.stringify(normalized));
723
+ const blocks = markdownBlocks(markdown);
724
+ const title = firstHeadingText(normalized.children ?? []) ?? fallbackTitle;
725
+ const bodyMarkdown = sectionBodyMarkdownFromTree(normalized, { title });
726
+ return { title, markdown, bodyMarkdown, blocks };
727
+ }
728
+
729
+ // src/aspect-runtime/index.ts
730
+ async function runPlugin(pluginModulePath) {
731
+ return runAspectPluginForHost(pluginModulePath, await readHostInput());
732
+ }
733
+ async function main() {
734
+ const pluginModulePath = process.argv[2];
735
+ if (pluginModulePath === undefined || pluginModulePath.length === 0) {
736
+ throw new Error("usage: aspectRunnerSdk <aspect-plugin-module>");
737
+ }
738
+ const result = await runPlugin(path3.resolve(pluginModulePath));
739
+ process.stdout.write(`${JSON.stringify(result)}
740
+ `);
741
+ }
742
+ if (process.argv[1] !== undefined && path3.resolve(process.argv[1]) === fileURLToPath(import.meta.url)) {
743
+ main().catch((err) => {
744
+ const message = err instanceof Error ? err.stack ?? err.message : String(err);
745
+ process.stderr.write(`${message}
746
+ `);
747
+ process.exitCode = 1;
748
+ });
749
+ }
750
+ export {
751
+ validateSourcePath,
752
+ toPosix,
753
+ sourceFileRef,
754
+ slugSegment,
755
+ runAspectPluginForHost,
756
+ resolveSymbolNodeSlug,
757
+ resolvePackageNodeSlug,
758
+ repoRelative,
759
+ readHostInput,
760
+ parseTsxComponentNames,
761
+ parseScssTokenNames,
762
+ parseMdxDocument,
763
+ parseJsLikeTokenNames,
764
+ normalizeSymbolSlug,
765
+ normalizeSectionMarkdown,
766
+ normalizeComponentSlug,
767
+ markdownFence,
768
+ inferComponentName,
769
+ firstMarkdownHeading,
770
+ fileSourceRef,
771
+ encodeSourcePath,
772
+ encodeFragment,
773
+ defineAspect,
774
+ compactWhitespace,
775
+ artifactFromRelPath
776
+ };