Page Menu
Home
Sealhub
Search
Configure Global Search
Log In
Files
F10360113
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Award Token
Flag For Later
Size
11 KB
Referenced Files
None
Subscribers
None
View Options
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
Details
Attached
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)
Attached To
Mode
rJDD jdd
Attached
Detach File
Event Timeline
Log In to Comment