Page MenuHomeSealhub

No OneTemporary

diff --git a/src/component.ts b/src/component.ts
index b5a8e49..3e8c044 100644
--- a/src/component.ts
+++ b/src/component.ts
@@ -1,156 +1,163 @@
import { FlatTemplatable } from "tempstream";
import {
ComponentArgument,
ExtractStructuredComponentArgumentsParsed,
ExtractStructuredComponentArgumentsReceived,
ExtractStructuredComponentArgumentsStorage,
JDDContext,
} from "./index.js";
export interface ComponentConstructor<
A extends Record<string, ComponentArgument<unknown>> = Record<
string,
ComponentArgument<unknown>
>
> {
new (): Component<A>;
}
export type EarlyAsset = (
| { type: "script" | "style"; url: string; integrity?: string }
| { type: "script" | "style"; content: string }
) & { identity: string }; // identity key will be used for deduplication
+export type ComponentToHTMLArgs<
+ T extends Record<string, ComponentArgument<unknown>>
+> = {
+ args: ExtractStructuredComponentArgumentsParsed<T>;
+ classes: string[];
+ jdd_context: JDDContext;
+ index: number;
+};
+
export abstract class Component<
ArgumentsT extends Record<string, ComponentArgument<unknown>> = Record<
string,
ComponentArgument<unknown>
>
> {
abstract getArguments(): ArgumentsT;
abstract toHTML(
- args: ExtractStructuredComponentArgumentsParsed<ArgumentsT>,
- classes: string[],
- context: JDDContext
+ params: ComponentToHTMLArgs<ArgumentsT>
): FlatTemplatable | Promise<FlatTemplatable>;
async getEarlyAssets(
_args: ExtractStructuredComponentArgumentsParsed<ArgumentsT>,
_context: JDDContext
): Promise<EarlyAsset[]> {
return [];
}
countWords(args: Record<string, unknown>): number {
return Object.entries(args).reduce((acc, [arg_name, value]) => {
const arg = this.getArguments()[arg_name];
if (!arg) {
console.warn(
`Arguemnt ${arg_name} was not found in the component`
);
return acc + 0;
}
return acc + arg.countWords(value);
}, 0);
}
async getExampleValues(
context: JDDContext
): Promise<ExtractStructuredComponentArgumentsParsed<ArgumentsT>> {
return Object.fromEntries(
await Promise.all(
Object.entries(this.getArguments()).map(
async ([key, value]) => [
key,
await value.getExampleValue(context),
]
)
)
) as ExtractStructuredComponentArgumentsParsed<ArgumentsT>;
}
async convertReceivedValuesToParsed(
context: JDDContext,
values: ExtractStructuredComponentArgumentsReceived<ArgumentsT>
): Promise<ExtractStructuredComponentArgumentsParsed<ArgumentsT>> {
const args = this.getArguments();
// eslint-disable-next-line @typescript-eslint/no-unsafe-return
return Object.fromEntries(
await Promise.all(
Object.entries(values).map(async ([key, value]) => {
return [
key,
await args[key].receivedToParsed(context, value),
];
})
)
);
}
async convertParsedToStorage(
context: JDDContext,
values: ExtractStructuredComponentArgumentsParsed<ArgumentsT>
): Promise<ExtractStructuredComponentArgumentsStorage<ArgumentsT>> {
const args = this.getArguments();
// eslint-disable-next-line @typescript-eslint/no-unsafe-return
return Object.fromEntries(
await Promise.all(
Object.entries(values).map(async ([key, value]) => {
return [
key,
await args[key].parsedToStorage(context, value),
];
})
)
);
}
async convertStorageToParsed(
context: JDDContext,
values: ExtractStructuredComponentArgumentsStorage<ArgumentsT>
): Promise<ExtractStructuredComponentArgumentsParsed<ArgumentsT>> {
const args = this.getArguments();
// eslint-disable-next-line @typescript-eslint/no-unsafe-return
return Object.fromEntries(
await Promise.all(
Object.entries(values).map(async ([key, value]) => {
if (!args[key]) {
return [key, null];
}
return [
key,
await args[key].storageToParsed(context, value),
];
})
)
);
}
// returns the argument, remaining path, and the values for that argument
getArgumentAtPath(
argument_path: string[],
values: ExtractStructuredComponentArgumentsParsed<ArgumentsT>
): [ComponentArgument<unknown> | null, string[], unknown] {
argument_path = [...argument_path];
if (argument_path.length == 0) {
return [null, [], null];
}
const arg_name = argument_path.shift() as string;
let argument: ComponentArgument<unknown> | null =
this.getArguments()[arg_name];
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
values = values[arg_name] as any;
if (argument_path.length == 0) {
return [argument, [], values];
}
do {
// the getSubArgument method can consume as many keys from the path as it wants
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
[argument, argument_path, values] = argument.getSubArgument(
argument_path,
values
) as any;
} while (argument_path.length && argument !== null);
return [argument, argument_path, values];
}
}
diff --git a/src/components/markdown.ts b/src/components/markdown.ts
index 13098a0..8cea8a5 100644
--- a/src/components/markdown.ts
+++ b/src/components/markdown.ts
@@ -1,29 +1,21 @@
import { FlatTemplatable, tempstream } from "tempstream";
-import { Component } from "../component.js";
+import { Component, ComponentToHTMLArgs } from "../component.js";
import { MarkdownArgument } from "../component-arguments/markdown.js";
-import {
- ExtractStructuredComponentArgumentsParsed,
- JDDContext,
-} from "../index.js";
const component_arguments = { markdown: new MarkdownArgument() } as const;
export class Markdown extends Component<typeof component_arguments> {
getArguments() {
return component_arguments;
}
- toHTML(
- {
- markdown,
- }: ExtractStructuredComponentArgumentsParsed<
- typeof component_arguments
- >,
- classes: string[],
- { render_markdown, language }: JDDContext
- ): FlatTemplatable {
+ toHTML({
+ args: { markdown },
+ classes,
+ jdd_context: { render_markdown, language },
+ }: ComponentToHTMLArgs<typeof component_arguments>): FlatTemplatable {
return tempstream`<section class="${classes.join(" ")}">
${render_markdown(language, markdown)}
</section>`;
}
}
diff --git a/src/index.ts b/src/index.ts
index f5796aa..f01c565 100644
--- a/src/index.ts
+++ b/src/index.ts
@@ -1,179 +1,180 @@
import { tempstream } from "tempstream";
import { JDDContext } from "./jdd-context.js";
import { Registry } from "./registry.js";
import { EarlyAsset } from "./component.js";
import { hasField } from "@sealcode/ts-predicates";
import {
JDDocumentContainer,
RawJDDocument,
documentContainerFromStorage,
} from "./document.js";
import { documentToParsed } from "./document.js";
export * from "./component.js";
export * from "./component-arguments/component-argument.js";
export * as ComponentArguments from "./component-arguments/component-arguments.js";
export * from "./component-arguments/component-arguments.js"; // exporting this also as root elements to make it easier to auto-import those
export * from "./jdd-context.js";
export * from "./registry.js";
export { insert_nbsp } from "./utils/insert_nbsp.js";
export * from "./document.js";
export function countWords(
registry: Registry,
document_container: JDDocumentContainer<"parsed">
): number {
return document_container.value.reduce((acc, { component_name, args }) => {
const component = registry.get(component_name);
if (!component) {
console.warn(
"Component not found in the registry: " + component_name
);
return acc + 0;
}
return acc + component.countWords(args);
}, 0);
}
export async function renderEarlyAssets(
registry: Registry,
document_container: JDDocumentContainer<"parsed">,
context: JDDContext
) {
const early_assets = (
await Promise.all(
document_container.value.map(async ({ component_name, args }) => {
const component = registry.get(component_name);
if (!component) {
console.warn(
"Component not found in the registry: " + component_name
);
return [];
}
for (const arg_name in component?.getArguments()) {
if (!Object.prototype.hasOwnProperty.call(args, arg_name)) {
args[arg_name] = component
?.getArguments()
[arg_name]?.getEmptyValue(context);
}
}
return await component.getEarlyAssets(args, context);
})
)
).flat();
const deduplicated_assets: Record<string, EarlyAsset> = {};
for (const asset of early_assets) {
deduplicated_assets[asset.identity] = asset;
}
return Object.values(deduplicated_assets)
.map((asset) => {
if (asset.type == "script") {
if (hasField("url", asset)) {
return /* HTML */ `<script
async
src="${asset.url}"
onLoad="document.dispatchEvent(new Event('loaded-${asset.identity}'))"
${(asset.integrity &&
`integrity="${asset.integrity}" crossorigin="anonymous"`) ||
""}
></script>`;
} else {
return /* HTML */ `<script><${asset.content}/script>`;
}
} else if (asset.type == "style") {
if (hasField("url", asset)) {
const integrity =
(asset.integrity &&
`integrity="${asset.integrity}" crossorigin="anonymous"`) ||
"";
// see https://web.dev/articles/defer-non-critical-css
return /* HTML */ `<link
rel="preload"
href="${asset.url}"
as="style"
onload="this.onload=null;this.rel='stylesheet'"
${integrity}
/>
<noscript
><link
rel="stylesheet"
href="${asset.url}"
${integrity}
/></noscript>`;
} else {
return /* HTML */ `<style>
${asset.content}
</style>`;
}
}
})
.join(" ");
}
export async function renderEarlyAssetsFromStorage(
registry: Registry,
document: RawJDDocument,
jdd_context: JDDContext
) {
return renderEarlyAssets(
registry,
await documentToParsed(
registry,
jdd_context,
documentContainerFromStorage(document)
),
jdd_context
);
}
export function render(
registry: Registry,
document: JDDocumentContainer<"parsed">,
context: JDDContext,
make_component_classes = (index: number) => [
"jdd-component",
`component-number-${index}`,
]
) {
return tempstream`${document.value.map(
({ component_name, args }, index) => {
const component = registry.get(component_name);
if (!component) {
console.warn(
"Component not found in the registry: " + component_name
);
return "";
}
for (const arg_name in component?.getArguments()) {
if (!Object.prototype.hasOwnProperty.call(args, arg_name)) {
args[arg_name] = component
?.getArguments()
[arg_name]?.getEmptyValue(context);
}
}
- return component.toHTML(
+ return component.toHTML({
args,
- make_component_classes(index),
- context
- );
+ classes: make_component_classes(index),
+ jdd_context: context,
+ index,
+ });
}
)}`;
}
export async function renderFromStorage(
registry: Registry,
document: RawJDDocument,
jdd_context: JDDContext
) {
return render(
registry,
await documentToParsed(
registry,
jdd_context,
documentContainerFromStorage(document)
),
jdd_context
);
}

File Metadata

Mime Type
text/x-diff
Expires
Sat, Nov 8, 04:38 (1 d, 4 h)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
1034016
Default Alt Text
(11 KB)

Event Timeline