@constela/start 0.4.1 → 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.d.ts +77 -9
- package/dist/index.js +365 -24
- package/package.json +12 -11
package/dist/index.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { CompiledProgram } from '@constela/compiler';
|
|
1
|
+
import { CompiledProgram, CompiledNode } from '@constela/compiler';
|
|
2
2
|
import { LayoutProgram, Program, StaticPathsDefinition, DataSource } from '@constela/core';
|
|
3
3
|
|
|
4
4
|
/**
|
|
@@ -357,6 +357,47 @@ declare class LayoutResolver {
|
|
|
357
357
|
getAll(): ScannedLayout[];
|
|
358
358
|
}
|
|
359
359
|
|
|
360
|
+
/**
|
|
361
|
+
* MDX to Constela AST Pipeline
|
|
362
|
+
*
|
|
363
|
+
* Transforms MDX content into CompiledProgram for Constela runtime.
|
|
364
|
+
* Uses unified/remark for parsing and gray-matter for frontmatter.
|
|
365
|
+
*/
|
|
366
|
+
|
|
367
|
+
/**
|
|
368
|
+
* Component definition for custom MDX components
|
|
369
|
+
*/
|
|
370
|
+
interface ComponentDef$1 {
|
|
371
|
+
params?: Record<string, {
|
|
372
|
+
type: string;
|
|
373
|
+
required?: boolean;
|
|
374
|
+
}>;
|
|
375
|
+
view: CompiledNode;
|
|
376
|
+
}
|
|
377
|
+
/**
|
|
378
|
+
* Options for MDX to Constela transformation
|
|
379
|
+
*/
|
|
380
|
+
interface MDXToConstelaOptions {
|
|
381
|
+
components?: Record<string, ComponentDef$1>;
|
|
382
|
+
}
|
|
383
|
+
/**
|
|
384
|
+
* Transform MDX source to CompiledProgram
|
|
385
|
+
*
|
|
386
|
+
* @param source - MDX source string
|
|
387
|
+
* @param options - Transformation options
|
|
388
|
+
* @returns CompiledProgram
|
|
389
|
+
*/
|
|
390
|
+
declare function mdxToConstela(source: string, options?: MDXToConstelaOptions): Promise<CompiledProgram>;
|
|
391
|
+
/**
|
|
392
|
+
* Transform MDX content (without frontmatter) to CompiledNode
|
|
393
|
+
* For use when frontmatter has already been extracted
|
|
394
|
+
*
|
|
395
|
+
* @param content - MDX content string (frontmatter already removed)
|
|
396
|
+
* @param options - Transformation options
|
|
397
|
+
* @returns CompiledNode
|
|
398
|
+
*/
|
|
399
|
+
declare function mdxContentToNode$1(content: string, options?: MDXToConstelaOptions): Promise<CompiledNode>;
|
|
400
|
+
|
|
360
401
|
/**
|
|
361
402
|
* Data Loader for Build-time Content Loading
|
|
362
403
|
*
|
|
@@ -374,17 +415,35 @@ interface GlobResult {
|
|
|
374
415
|
frontmatter?: Record<string, unknown>;
|
|
375
416
|
content?: string;
|
|
376
417
|
}
|
|
418
|
+
interface MdxGlobResult {
|
|
419
|
+
file: string;
|
|
420
|
+
raw: string;
|
|
421
|
+
frontmatter: Record<string, unknown>;
|
|
422
|
+
content: CompiledNode;
|
|
423
|
+
slug: string;
|
|
424
|
+
}
|
|
425
|
+
interface ComponentDef {
|
|
426
|
+
params?: Record<string, {
|
|
427
|
+
type: string;
|
|
428
|
+
required?: boolean;
|
|
429
|
+
}>;
|
|
430
|
+
view: CompiledNode;
|
|
431
|
+
}
|
|
432
|
+
declare const mdxContentToNode: typeof mdxContentToNode$1;
|
|
377
433
|
interface StaticPath {
|
|
378
434
|
params: Record<string, string>;
|
|
379
435
|
data?: unknown;
|
|
380
436
|
}
|
|
381
437
|
/**
|
|
382
|
-
*
|
|
438
|
+
* Load component definitions from a JSON file
|
|
383
439
|
*/
|
|
384
|
-
declare function
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
440
|
+
declare function loadComponentDefinitions(baseDir: string, componentsPath: string): Record<string, ComponentDef>;
|
|
441
|
+
/**
|
|
442
|
+
* Transform MDX content - parse frontmatter and compile content to CompiledNode
|
|
443
|
+
*/
|
|
444
|
+
declare function transformMdx(content: string, file: string, options?: {
|
|
445
|
+
components?: Record<string, ComponentDef>;
|
|
446
|
+
}): Promise<MdxGlobResult>;
|
|
388
447
|
/**
|
|
389
448
|
* Transform YAML content
|
|
390
449
|
*/
|
|
@@ -396,7 +455,9 @@ declare function transformCsv(content: string): Record<string, string>[];
|
|
|
396
455
|
/**
|
|
397
456
|
* Load files matching a glob pattern
|
|
398
457
|
*/
|
|
399
|
-
declare function loadGlob(baseDir: string, pattern: string, transform?: string
|
|
458
|
+
declare function loadGlob(baseDir: string, pattern: string, transform?: string, options?: {
|
|
459
|
+
components?: Record<string, ComponentDef>;
|
|
460
|
+
}): Promise<GlobResult[] | MdxGlobResult[]>;
|
|
400
461
|
/**
|
|
401
462
|
* Load a single file
|
|
402
463
|
*/
|
|
@@ -414,12 +475,19 @@ declare function generateStaticPaths(data: unknown[], staticPathsDef: StaticPath
|
|
|
414
475
|
*/
|
|
415
476
|
declare class DataLoader {
|
|
416
477
|
private cache;
|
|
478
|
+
private componentCache;
|
|
417
479
|
private projectRoot;
|
|
418
480
|
constructor(projectRoot: string);
|
|
481
|
+
/**
|
|
482
|
+
* Resolve components from string path or import reference
|
|
483
|
+
*/
|
|
484
|
+
private resolveComponents;
|
|
419
485
|
/**
|
|
420
486
|
* Load a single data source
|
|
421
487
|
*/
|
|
422
|
-
loadDataSource(name: string, dataSource: DataSource
|
|
488
|
+
loadDataSource(name: string, dataSource: DataSource, context?: {
|
|
489
|
+
imports?: Record<string, unknown>;
|
|
490
|
+
}): Promise<unknown>;
|
|
423
491
|
/**
|
|
424
492
|
* Load all data sources
|
|
425
493
|
*/
|
|
@@ -438,4 +506,4 @@ declare class DataLoader {
|
|
|
438
506
|
getCacheSize(): number;
|
|
439
507
|
}
|
|
440
508
|
|
|
441
|
-
export { type APIContext, type APIModule, type BuildOptions, type ConstelaConfig, DataLoader, type DevServerOptions, type GenerateStaticPagesOptions, type GlobResult, type LayoutInfo, LayoutResolver, type Middleware, type MiddlewareContext, type MiddlewareNext, type PageExportFunction, type PageModule, type ScannedLayout, type ScannedRoute, type StaticFileResult, type StaticPath, type StaticPathsProvider, type StaticPathsResult, build, createAPIHandler, createAdapter, createDevServer, createMiddlewareChain, filePathToPattern, generateStaticPages, generateStaticPaths, getMimeType, isPageExportFunction, isPathSafe, loadApi, loadFile, loadGlob, loadLayout, resolveLayout, resolvePageExport, resolveStaticFile, scanLayouts, scanRoutes, transformCsv, transformMdx, transformYaml };
|
|
509
|
+
export { type APIContext, type APIModule, type BuildOptions, type ComponentDef$1 as ComponentDef, type ConstelaConfig, DataLoader, type DevServerOptions, type GenerateStaticPagesOptions, type GlobResult, type LayoutInfo, LayoutResolver, type MDXToConstelaOptions, type MdxGlobResult, type Middleware, type MiddlewareContext, type MiddlewareNext, type PageExportFunction, type PageModule, type ScannedLayout, type ScannedRoute, type StaticFileResult, type StaticPath, type StaticPathsProvider, type StaticPathsResult, build, createAPIHandler, createAdapter, createDevServer, createMiddlewareChain, filePathToPattern, generateStaticPages, generateStaticPaths, getMimeType, isPageExportFunction, isPathSafe, loadApi, loadComponentDefinitions, loadFile, loadGlob, loadLayout, mdxContentToNode, mdxToConstela, resolveLayout, resolvePageExport, resolveStaticFile, scanLayouts, scanRoutes, transformCsv, transformMdx, transformYaml };
|
package/dist/index.js
CHANGED
|
@@ -415,8 +415,276 @@ var LayoutResolver = class {
|
|
|
415
415
|
|
|
416
416
|
// src/data/loader.ts
|
|
417
417
|
import { existsSync as existsSync2, readFileSync } from "fs";
|
|
418
|
-
import { join as join3 } from "path";
|
|
418
|
+
import { basename as basename2, extname, join as join3 } from "path";
|
|
419
419
|
import fg2 from "fast-glob";
|
|
420
|
+
|
|
421
|
+
// src/build/mdx.ts
|
|
422
|
+
import { unified } from "unified";
|
|
423
|
+
import remarkParse from "remark-parse";
|
|
424
|
+
import remarkMdx from "remark-mdx";
|
|
425
|
+
import remarkGfm from "remark-gfm";
|
|
426
|
+
import matter from "gray-matter";
|
|
427
|
+
function lit(value) {
|
|
428
|
+
return { expr: "lit", value };
|
|
429
|
+
}
|
|
430
|
+
function textNode(value) {
|
|
431
|
+
return { kind: "text", value: lit(value) };
|
|
432
|
+
}
|
|
433
|
+
function elementNode(tag, props, children) {
|
|
434
|
+
const node = { kind: "element", tag };
|
|
435
|
+
if (props && Object.keys(props).length > 0) {
|
|
436
|
+
node.props = props;
|
|
437
|
+
}
|
|
438
|
+
if (children && children.length > 0) {
|
|
439
|
+
node.children = children;
|
|
440
|
+
}
|
|
441
|
+
return node;
|
|
442
|
+
}
|
|
443
|
+
function codeNode(language, content) {
|
|
444
|
+
return {
|
|
445
|
+
kind: "code",
|
|
446
|
+
language: lit(language),
|
|
447
|
+
content: lit(content)
|
|
448
|
+
};
|
|
449
|
+
}
|
|
450
|
+
function wrapNodes(nodes) {
|
|
451
|
+
if (nodes.length === 0) {
|
|
452
|
+
return elementNode("div");
|
|
453
|
+
}
|
|
454
|
+
if (nodes.length === 1 && nodes[0]) {
|
|
455
|
+
return nodes[0];
|
|
456
|
+
}
|
|
457
|
+
return elementNode("div", void 0, nodes);
|
|
458
|
+
}
|
|
459
|
+
function isCustomComponent(name) {
|
|
460
|
+
if (!name) return false;
|
|
461
|
+
return /^[A-Z]/.test(name);
|
|
462
|
+
}
|
|
463
|
+
function parseAttributeValue(attr) {
|
|
464
|
+
if (attr.value === null) {
|
|
465
|
+
return lit(true);
|
|
466
|
+
}
|
|
467
|
+
if (typeof attr.value === "string") {
|
|
468
|
+
return lit(attr.value);
|
|
469
|
+
}
|
|
470
|
+
if (attr.value.type === "mdxJsxAttributeValueExpression") {
|
|
471
|
+
const exprValue = attr.value.value.trim();
|
|
472
|
+
if (exprValue === "true") return lit(true);
|
|
473
|
+
if (exprValue === "false") return lit(false);
|
|
474
|
+
if (exprValue === "null") return lit(null);
|
|
475
|
+
const num = Number(exprValue);
|
|
476
|
+
if (!Number.isNaN(num)) return lit(num);
|
|
477
|
+
return lit(exprValue);
|
|
478
|
+
}
|
|
479
|
+
return lit(null);
|
|
480
|
+
}
|
|
481
|
+
function transformNode(node, ctx) {
|
|
482
|
+
switch (node.type) {
|
|
483
|
+
case "heading":
|
|
484
|
+
return elementNode(
|
|
485
|
+
`h${node.depth}`,
|
|
486
|
+
void 0,
|
|
487
|
+
transformChildren(node.children, ctx)
|
|
488
|
+
);
|
|
489
|
+
case "paragraph":
|
|
490
|
+
return elementNode("p", void 0, transformChildren(node.children, ctx));
|
|
491
|
+
case "text":
|
|
492
|
+
return textNode(node.value);
|
|
493
|
+
case "emphasis":
|
|
494
|
+
return elementNode("em", void 0, transformChildren(node.children, ctx));
|
|
495
|
+
case "strong":
|
|
496
|
+
return elementNode("strong", void 0, transformChildren(node.children, ctx));
|
|
497
|
+
case "link": {
|
|
498
|
+
const props = {
|
|
499
|
+
href: lit(node.url)
|
|
500
|
+
};
|
|
501
|
+
if (node["title"]) {
|
|
502
|
+
props["title"] = lit(node["title"]);
|
|
503
|
+
}
|
|
504
|
+
return elementNode("a", props, transformChildren(node.children, ctx));
|
|
505
|
+
}
|
|
506
|
+
case "inlineCode":
|
|
507
|
+
return elementNode("code", void 0, [textNode(node.value)]);
|
|
508
|
+
case "code": {
|
|
509
|
+
const lang = node.lang || "text";
|
|
510
|
+
return codeNode(lang, node.value);
|
|
511
|
+
}
|
|
512
|
+
case "blockquote":
|
|
513
|
+
return elementNode("blockquote", void 0, transformChildren(node.children, ctx));
|
|
514
|
+
case "list": {
|
|
515
|
+
const tag = node.ordered ? "ol" : "ul";
|
|
516
|
+
return elementNode(tag, void 0, transformChildren(node.children, ctx));
|
|
517
|
+
}
|
|
518
|
+
case "listItem": {
|
|
519
|
+
const children = [];
|
|
520
|
+
for (const child of node.children) {
|
|
521
|
+
if (child.type === "paragraph") {
|
|
522
|
+
children.push(...transformChildren(child.children, ctx));
|
|
523
|
+
} else {
|
|
524
|
+
const transformed = transformNode(child, ctx);
|
|
525
|
+
if (transformed) {
|
|
526
|
+
if (Array.isArray(transformed)) {
|
|
527
|
+
children.push(...transformed);
|
|
528
|
+
} else {
|
|
529
|
+
children.push(transformed);
|
|
530
|
+
}
|
|
531
|
+
}
|
|
532
|
+
}
|
|
533
|
+
}
|
|
534
|
+
return elementNode("li", void 0, children);
|
|
535
|
+
}
|
|
536
|
+
case "thematicBreak":
|
|
537
|
+
return elementNode("hr");
|
|
538
|
+
case "break":
|
|
539
|
+
return elementNode("br");
|
|
540
|
+
case "image": {
|
|
541
|
+
const props = {
|
|
542
|
+
src: lit(node.url)
|
|
543
|
+
};
|
|
544
|
+
if (node["alt"]) {
|
|
545
|
+
props["alt"] = lit(node["alt"]);
|
|
546
|
+
}
|
|
547
|
+
if (node["title"]) {
|
|
548
|
+
props["title"] = lit(node["title"]);
|
|
549
|
+
}
|
|
550
|
+
return elementNode("img", props);
|
|
551
|
+
}
|
|
552
|
+
case "html":
|
|
553
|
+
return textNode(node.value);
|
|
554
|
+
// MDX JSX elements
|
|
555
|
+
case "mdxJsxFlowElement":
|
|
556
|
+
case "mdxJsxTextElement":
|
|
557
|
+
return transformJsxElement(node, ctx);
|
|
558
|
+
// MDX expressions
|
|
559
|
+
case "mdxFlowExpression":
|
|
560
|
+
case "mdxTextExpression": {
|
|
561
|
+
const exprNode = node;
|
|
562
|
+
const value = exprNode.value.trim();
|
|
563
|
+
if (value === "") return null;
|
|
564
|
+
if (value === "true") return textNode("true");
|
|
565
|
+
if (value === "false") return textNode("false");
|
|
566
|
+
if (value === "null") return textNode("null");
|
|
567
|
+
const num = Number(value);
|
|
568
|
+
if (!Number.isNaN(num)) return textNode(String(num));
|
|
569
|
+
return textNode(value);
|
|
570
|
+
}
|
|
571
|
+
// GFM extensions
|
|
572
|
+
case "table":
|
|
573
|
+
return elementNode("table", void 0, transformChildren(node.children, ctx));
|
|
574
|
+
case "tableRow":
|
|
575
|
+
return elementNode("tr", void 0, transformChildren(node.children, ctx));
|
|
576
|
+
case "tableCell":
|
|
577
|
+
return elementNode("td", void 0, transformChildren(node.children, ctx));
|
|
578
|
+
case "delete":
|
|
579
|
+
return elementNode("del", void 0, transformChildren(node.children, ctx));
|
|
580
|
+
default:
|
|
581
|
+
return null;
|
|
582
|
+
}
|
|
583
|
+
}
|
|
584
|
+
function transformJsxElement(node, ctx) {
|
|
585
|
+
const name = node.name;
|
|
586
|
+
if (!name) {
|
|
587
|
+
const children2 = transformChildren(node.children, ctx);
|
|
588
|
+
return wrapNodes(children2);
|
|
589
|
+
}
|
|
590
|
+
if (isCustomComponent(name)) {
|
|
591
|
+
const def = ctx.components[name];
|
|
592
|
+
if (!def) {
|
|
593
|
+
throw new Error(`Undefined component: ${name}`);
|
|
594
|
+
}
|
|
595
|
+
const props2 = {};
|
|
596
|
+
for (const attr of node.attributes) {
|
|
597
|
+
if (attr.type === "mdxJsxAttribute") {
|
|
598
|
+
props2[attr.name] = parseAttributeValue(attr);
|
|
599
|
+
}
|
|
600
|
+
}
|
|
601
|
+
const children2 = transformChildren(node.children, ctx);
|
|
602
|
+
return applyComponentView(def.view, props2, children2);
|
|
603
|
+
}
|
|
604
|
+
const props = {};
|
|
605
|
+
for (const attr of node.attributes) {
|
|
606
|
+
if (attr.type === "mdxJsxAttribute") {
|
|
607
|
+
props[attr.name] = parseAttributeValue(attr);
|
|
608
|
+
}
|
|
609
|
+
}
|
|
610
|
+
const children = transformChildren(node.children, ctx);
|
|
611
|
+
return elementNode(name, props, children);
|
|
612
|
+
}
|
|
613
|
+
function applyComponentView(view, props, children) {
|
|
614
|
+
return substituteInNode(view, props, children);
|
|
615
|
+
}
|
|
616
|
+
function substituteInNode(node, props, children) {
|
|
617
|
+
if (node.kind === "element") {
|
|
618
|
+
const elem = node;
|
|
619
|
+
const newProps = elem.props ? { ...elem.props } : {};
|
|
620
|
+
for (const [key, value] of Object.entries(props)) {
|
|
621
|
+
if (!(key in newProps)) {
|
|
622
|
+
newProps[key] = value;
|
|
623
|
+
}
|
|
624
|
+
}
|
|
625
|
+
let newChildren;
|
|
626
|
+
if (elem.children) {
|
|
627
|
+
newChildren = [];
|
|
628
|
+
for (const child of elem.children) {
|
|
629
|
+
if (child.kind === "slot") {
|
|
630
|
+
newChildren.push(...children);
|
|
631
|
+
} else {
|
|
632
|
+
newChildren.push(substituteInNode(child, props, children));
|
|
633
|
+
}
|
|
634
|
+
}
|
|
635
|
+
}
|
|
636
|
+
return elementNode(
|
|
637
|
+
elem.tag,
|
|
638
|
+
Object.keys(newProps).length > 0 ? newProps : void 0,
|
|
639
|
+
newChildren && newChildren.length > 0 ? newChildren : void 0
|
|
640
|
+
);
|
|
641
|
+
}
|
|
642
|
+
return node;
|
|
643
|
+
}
|
|
644
|
+
function transformChildren(children, ctx) {
|
|
645
|
+
const result = [];
|
|
646
|
+
for (const child of children) {
|
|
647
|
+
const transformed = transformNode(child, ctx);
|
|
648
|
+
if (transformed) {
|
|
649
|
+
if (Array.isArray(transformed)) {
|
|
650
|
+
result.push(...transformed);
|
|
651
|
+
} else {
|
|
652
|
+
result.push(transformed);
|
|
653
|
+
}
|
|
654
|
+
}
|
|
655
|
+
}
|
|
656
|
+
return result;
|
|
657
|
+
}
|
|
658
|
+
function transformRoot(root, ctx) {
|
|
659
|
+
const nodes = transformChildren(root.children, ctx);
|
|
660
|
+
return wrapNodes(nodes);
|
|
661
|
+
}
|
|
662
|
+
async function mdxToConstela(source, options) {
|
|
663
|
+
const { content, data: _frontmatter } = matter(source);
|
|
664
|
+
const processor = unified().use(remarkParse).use(remarkGfm).use(remarkMdx);
|
|
665
|
+
const tree = processor.parse(content);
|
|
666
|
+
const ctx = {
|
|
667
|
+
components: options?.components ?? {}
|
|
668
|
+
};
|
|
669
|
+
const view = transformRoot(tree, ctx);
|
|
670
|
+
return {
|
|
671
|
+
version: "1.0",
|
|
672
|
+
state: {},
|
|
673
|
+
actions: {},
|
|
674
|
+
view
|
|
675
|
+
};
|
|
676
|
+
}
|
|
677
|
+
async function mdxContentToNode(content, options) {
|
|
678
|
+
const processor = unified().use(remarkParse).use(remarkGfm).use(remarkMdx);
|
|
679
|
+
const tree = processor.parse(content);
|
|
680
|
+
const ctx = {
|
|
681
|
+
components: options?.components ?? {}
|
|
682
|
+
};
|
|
683
|
+
return transformRoot(tree, ctx);
|
|
684
|
+
}
|
|
685
|
+
|
|
686
|
+
// src/data/loader.ts
|
|
687
|
+
var mdxContentToNode2 = mdxContentToNode;
|
|
420
688
|
function parseYaml(content) {
|
|
421
689
|
const result = {};
|
|
422
690
|
const lines = content.split("\n");
|
|
@@ -494,13 +762,46 @@ function parseValue(value) {
|
|
|
494
762
|
}
|
|
495
763
|
return trimmed;
|
|
496
764
|
}
|
|
497
|
-
function
|
|
765
|
+
function loadComponentDefinitions(baseDir, componentsPath) {
|
|
766
|
+
const fullPath = join3(baseDir, componentsPath);
|
|
767
|
+
const resolvedBase = join3(baseDir, "");
|
|
768
|
+
const resolvedPath = join3(fullPath, "");
|
|
769
|
+
if (!resolvedPath.startsWith(resolvedBase)) {
|
|
770
|
+
throw new Error(`Invalid component path: path traversal detected`);
|
|
771
|
+
}
|
|
772
|
+
if (!existsSync2(fullPath)) {
|
|
773
|
+
throw new Error(`MDX components file not found: ${fullPath}`);
|
|
774
|
+
}
|
|
775
|
+
const content = readFileSync(fullPath, "utf-8");
|
|
776
|
+
try {
|
|
777
|
+
return JSON.parse(content);
|
|
778
|
+
} catch {
|
|
779
|
+
throw new Error(`Invalid JSON in MDX components file: ${fullPath}`);
|
|
780
|
+
}
|
|
781
|
+
}
|
|
782
|
+
async function transformMdx(content, file, options) {
|
|
498
783
|
const match = content.match(/^---\n([\s\S]*?)\n---\n([\s\S]*)$/);
|
|
499
|
-
|
|
500
|
-
|
|
784
|
+
let frontmatter = {};
|
|
785
|
+
let mdxContent;
|
|
786
|
+
if (match) {
|
|
787
|
+
frontmatter = parseYaml(match[1]);
|
|
788
|
+
mdxContent = match[2].trim();
|
|
789
|
+
} else {
|
|
790
|
+
mdxContent = content.trim();
|
|
501
791
|
}
|
|
502
|
-
const
|
|
503
|
-
|
|
792
|
+
const compiledContent = await mdxContentToNode(
|
|
793
|
+
mdxContent,
|
|
794
|
+
options?.components ? { components: options.components } : void 0
|
|
795
|
+
);
|
|
796
|
+
const fmSlug = frontmatter["slug"];
|
|
797
|
+
const slug = typeof fmSlug === "string" ? fmSlug : basename2(file, extname(file));
|
|
798
|
+
return {
|
|
799
|
+
file,
|
|
800
|
+
raw: content,
|
|
801
|
+
frontmatter,
|
|
802
|
+
content: compiledContent,
|
|
803
|
+
slug
|
|
804
|
+
};
|
|
504
805
|
}
|
|
505
806
|
function transformYaml(content) {
|
|
506
807
|
return parseYaml(content);
|
|
@@ -550,7 +851,7 @@ function applyTransform(content, transform, filename) {
|
|
|
550
851
|
}
|
|
551
852
|
switch (transform) {
|
|
552
853
|
case "mdx":
|
|
553
|
-
|
|
854
|
+
throw new Error("MDX transform for single files is not supported via loadFile. Use loadGlob instead.");
|
|
554
855
|
case "yaml":
|
|
555
856
|
return transformYaml(content);
|
|
556
857
|
case "csv":
|
|
@@ -559,26 +860,26 @@ function applyTransform(content, transform, filename) {
|
|
|
559
860
|
return content;
|
|
560
861
|
}
|
|
561
862
|
}
|
|
562
|
-
async function loadGlob(baseDir, pattern, transform) {
|
|
863
|
+
async function loadGlob(baseDir, pattern, transform, options) {
|
|
563
864
|
const files = await fg2(pattern, { cwd: baseDir });
|
|
865
|
+
if (transform === "mdx") {
|
|
866
|
+
const results2 = [];
|
|
867
|
+
for (const file of files) {
|
|
868
|
+
const fullPath = join3(baseDir, file);
|
|
869
|
+
const content = readFileSync(fullPath, "utf-8");
|
|
870
|
+
const transformed = await transformMdx(content, file, options);
|
|
871
|
+
results2.push(transformed);
|
|
872
|
+
}
|
|
873
|
+
return results2;
|
|
874
|
+
}
|
|
564
875
|
const results = [];
|
|
565
876
|
for (const file of files) {
|
|
566
877
|
const fullPath = join3(baseDir, file);
|
|
567
878
|
const content = readFileSync(fullPath, "utf-8");
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
raw: content,
|
|
573
|
-
frontmatter: transformed.frontmatter,
|
|
574
|
-
content: transformed.content
|
|
575
|
-
});
|
|
576
|
-
} else {
|
|
577
|
-
results.push({
|
|
578
|
-
file,
|
|
579
|
-
raw: content
|
|
580
|
-
});
|
|
581
|
-
}
|
|
879
|
+
results.push({
|
|
880
|
+
file,
|
|
881
|
+
raw: content
|
|
882
|
+
});
|
|
582
883
|
}
|
|
583
884
|
return results;
|
|
584
885
|
}
|
|
@@ -652,24 +953,61 @@ async function generateStaticPaths(data, staticPathsDef) {
|
|
|
652
953
|
}
|
|
653
954
|
var DataLoader = class {
|
|
654
955
|
cache = /* @__PURE__ */ new Map();
|
|
956
|
+
componentCache = /* @__PURE__ */ new Map();
|
|
655
957
|
projectRoot;
|
|
656
958
|
constructor(projectRoot) {
|
|
657
959
|
this.projectRoot = projectRoot;
|
|
658
960
|
}
|
|
961
|
+
/**
|
|
962
|
+
* Resolve components from string path or import reference
|
|
963
|
+
*/
|
|
964
|
+
resolveComponents(ref, imports) {
|
|
965
|
+
if (typeof ref === "string") {
|
|
966
|
+
if (this.componentCache.has(ref)) {
|
|
967
|
+
return this.componentCache.get(ref);
|
|
968
|
+
}
|
|
969
|
+
const defs = loadComponentDefinitions(this.projectRoot, ref);
|
|
970
|
+
this.componentCache.set(ref, defs);
|
|
971
|
+
return defs;
|
|
972
|
+
}
|
|
973
|
+
if (ref.expr === "import") {
|
|
974
|
+
if (!imports) {
|
|
975
|
+
throw new Error(`Import context required for component reference "${ref.name}"`);
|
|
976
|
+
}
|
|
977
|
+
const imported = imports[ref.name];
|
|
978
|
+
if (!imported || typeof imported !== "object") {
|
|
979
|
+
throw new Error(`Component import "${ref.name}" not found or invalid`);
|
|
980
|
+
}
|
|
981
|
+
return imported;
|
|
982
|
+
}
|
|
983
|
+
return {};
|
|
984
|
+
}
|
|
659
985
|
/**
|
|
660
986
|
* Load a single data source
|
|
661
987
|
*/
|
|
662
|
-
async loadDataSource(name, dataSource) {
|
|
988
|
+
async loadDataSource(name, dataSource, context) {
|
|
663
989
|
if (this.cache.has(name)) {
|
|
664
990
|
return this.cache.get(name);
|
|
665
991
|
}
|
|
992
|
+
let componentDefs;
|
|
993
|
+
if (dataSource.transform === "mdx" && dataSource.components) {
|
|
994
|
+
componentDefs = this.resolveComponents(
|
|
995
|
+
dataSource.components,
|
|
996
|
+
context?.imports
|
|
997
|
+
);
|
|
998
|
+
}
|
|
666
999
|
let data;
|
|
667
1000
|
switch (dataSource.type) {
|
|
668
1001
|
case "glob":
|
|
669
1002
|
if (!dataSource.pattern) {
|
|
670
1003
|
throw new Error(`Glob data source '${name}' requires pattern`);
|
|
671
1004
|
}
|
|
672
|
-
data = await loadGlob(
|
|
1005
|
+
data = await loadGlob(
|
|
1006
|
+
this.projectRoot,
|
|
1007
|
+
dataSource.pattern,
|
|
1008
|
+
dataSource.transform,
|
|
1009
|
+
componentDefs ? { components: componentDefs } : void 0
|
|
1010
|
+
);
|
|
673
1011
|
break;
|
|
674
1012
|
case "file":
|
|
675
1013
|
if (!dataSource.path) {
|
|
@@ -737,9 +1075,12 @@ export {
|
|
|
737
1075
|
isPageExportFunction,
|
|
738
1076
|
isPathSafe,
|
|
739
1077
|
loadApi,
|
|
1078
|
+
loadComponentDefinitions,
|
|
740
1079
|
loadFile,
|
|
741
1080
|
loadGlob,
|
|
742
1081
|
loadLayout,
|
|
1082
|
+
mdxContentToNode2 as mdxContentToNode,
|
|
1083
|
+
mdxToConstela,
|
|
743
1084
|
resolveLayout,
|
|
744
1085
|
resolvePageExport,
|
|
745
1086
|
resolveStaticFile,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@constela/start",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "1.0.0",
|
|
4
4
|
"description": "Meta-framework for Constela applications",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|
|
@@ -30,24 +30,25 @@
|
|
|
30
30
|
"node": ">=20.0.0"
|
|
31
31
|
},
|
|
32
32
|
"dependencies": {
|
|
33
|
-
"hono": "^4.0.0",
|
|
34
|
-
"vite": "^6.0.0",
|
|
35
33
|
"commander": "^12.0.0",
|
|
36
34
|
"fast-glob": "^3.3.0",
|
|
37
|
-
"unified": "^11.0.0",
|
|
38
|
-
"remark-parse": "^11.0.0",
|
|
39
|
-
"remark-mdx": "^3.0.0",
|
|
40
|
-
"remark-gfm": "^4.0.0",
|
|
41
35
|
"gray-matter": "^4.0.0",
|
|
42
|
-
"
|
|
36
|
+
"hono": "^4.0.0",
|
|
37
|
+
"remark-gfm": "^4.0.0",
|
|
38
|
+
"remark-mdx": "^3.0.0",
|
|
39
|
+
"remark-parse": "^11.0.0",
|
|
40
|
+
"unified": "^11.0.0",
|
|
41
|
+
"vite": "^6.0.0",
|
|
42
|
+
"@constela/compiler": "0.6.1",
|
|
43
|
+
"@constela/core": "0.7.0",
|
|
43
44
|
"@constela/router": "6.0.0",
|
|
44
45
|
"@constela/server": "2.0.0",
|
|
45
|
-
"@constela/runtime": "0.9.
|
|
46
|
-
"@constela/compiler": "0.6.0"
|
|
46
|
+
"@constela/runtime": "0.9.1"
|
|
47
47
|
},
|
|
48
48
|
"devDependencies": {
|
|
49
|
-
"
|
|
49
|
+
"@types/mdast": "^4.0.4",
|
|
50
50
|
"tsup": "^8.0.0",
|
|
51
|
+
"typescript": "^5.3.0",
|
|
51
52
|
"vitest": "^2.0.0"
|
|
52
53
|
},
|
|
53
54
|
"license": "MIT",
|