Page MenuHomeSealhub

No OneTemporary

diff --git a/package.json b/package.json
index e94f28f..55ffcc8 100644
--- a/package.json
+++ b/package.json
@@ -1,60 +1,60 @@
{
"name": "@sealcode/jdd",
- "version": "0.6.0-alpha",
+ "version": "0.6.0-alpha1",
"description": "JSON-Driven Documents",
"main": "lib/index.js",
"type": "module",
"scripts": {
"pretest": "npm run build",
"test": "node test.cjs",
"prebuild": "npm run lint",
"build": "tsc",
"typecheck": "tsc --noemit",
"prepare": "npm run build",
"lint": "eslint src",
"preinstrument": "npm run build && rm -fr .xunit coverage lib-instrumented",
"instrument": "npx nyc instrument --exclude \"\" lib lib-instrumented",
"pretest-reports": "npm run instrument",
"test-reports": "npx nyc --exclude \"\" ./node_modules/.bin/mocha --recursive --timeout=10000 --require source-map-support/register --reporter xunit --reporter-option output=.xunit 'lib-instrumented/**/*.test.js' && nyc report --reporter clover --exclude \"\"",
"precover-html": "rm -rf coverate/lcov-report",
"cover-html": "npm run test-reports && nyc report --reporter lcov --exclude \"\" && xdg-open coverage/lcov-report/index.html"
},
"author": "",
"license": "ISC",
"devDependencies": {
"@types/mocha": "^10.0.1",
"@types/prettier": "^2.7.0",
"@types/slug": "^5.0.9",
"@types/uuid": "^9.0.8",
"@typescript-eslint/eslint-plugin": "^5.58.0",
"@typescript-eslint/parser": "^5.58.0",
"assert": "^2.0.0",
"eslint": "^8.38.0",
"eslint-config-prettier": "^8.8.0",
"eslint-plugin-prettier": "^4.2.1",
"eslint-plugin-with-tsc-error": "^0.0.8",
"mocha": "^10.2.0",
"nyc": "^15.1.0",
"source-map-support": "^0.5.21",
"ts-node": "^10.9.1",
"typescript": "^5.0.4"
},
"types": "./@types/index.d.ts",
"dependencies": {
"@sealcode/file-manager": "^1.0.2",
"@sealcode/ts-predicates": "^0.5.3",
"escape-goat": "^4.0.0",
"hyphenopoly": "^5.3.0",
"koa-responsive-image-router": "^0.2.29",
"locreq": "^3.0.0",
"marked": "^12.0.0",
"mri": "^1.2.0",
"prettier": "^2.7.1",
"slug": "^9.1.0",
"tempstream": "^0.4.1",
"uuid": "^9.0.1"
},
"peerDependencies": {
"sealious": "^0.19.18"
}
}
diff --git a/src/component-arguments/enum.test.ts b/src/component-arguments/enum.test.ts
new file mode 100644
index 0000000..4b800be
--- /dev/null
+++ b/src/component-arguments/enum.test.ts
@@ -0,0 +1,11 @@
+import assert from "assert";
+import { Enum } from "./enum.js";
+
+describe("enum argument", () => {
+ it("uses only the example values if provided when running getExampleValues", () => {
+ const e = new Enum(["a", "b"]).setExampleValues(["a"]);
+ for (let i = 0; i <= 100; i++) {
+ assert.deepStrictEqual(e.getExampleValue(), "a");
+ }
+ });
+});
diff --git a/src/jdd.test.ts b/src/jdd.test.ts
new file mode 100644
index 0000000..30e99c9
--- /dev/null
+++ b/src/jdd.test.ts
@@ -0,0 +1,39 @@
+import assert from "assert";
+import { Markdown } from "./components/markdown.js";
+import { JDD } from "./jdd.js";
+import { Registry } from "./registry.js";
+import { simplestContext } from "./test-utils/simplest-context.js";
+
+describe("jdd", () => {
+ it("properly extracts headings from multiple components", () => {
+ const registry = new Registry();
+ registry.add("markdown", Markdown);
+ const jdd = JDD.fromParsed(registry, simplestContext(), [
+ {
+ component_name: "markdown",
+ args: {
+ markdown: `
+# heading one
+
+## heading two
+
+### heading three`,
+ },
+ },
+ {
+ component_name: "markdown",
+ args: {
+ markdown: `
+# heading four
+`,
+ },
+ },
+ ]);
+ assert.deepEqual(jdd.getHeadings(), [
+ { text: "heading one", level: 1, slug: "heading-one" },
+ { text: "heading two", level: 2, slug: "heading-two" },
+ { text: "heading three", level: 3, slug: "heading-three" },
+ { text: "heading four", level: 1, slug: "heading-four" },
+ ]);
+ });
+});
diff --git a/src/jdd.ts b/src/jdd.ts
new file mode 100644
index 0000000..43018db
--- /dev/null
+++ b/src/jdd.ts
@@ -0,0 +1,201 @@
+import { tempstream } from "tempstream";
+
+import { JDDContext } from "./jdd-context.js";
+import { Registry } from "./registry.js";
+import { EarlyAsset, JDDHeading } from "./component.js";
+import { hasField } from "@sealcode/ts-predicates";
+import {
+ JDDocumentContainer,
+ RawJDDocument,
+ documentContainerFromParsed,
+ documentContainerFromStorage,
+} from "./document.js";
+import { documentToParsed } from "./document.js";
+
+export class JDD {
+ constructor(
+ public registry: Registry,
+ public jdd_context: JDDContext,
+ public parsed: JDDocumentContainer<"parsed">
+ ) {}
+
+ static async fromStorage(
+ registry: Registry,
+ jdd_context: JDDContext,
+ document: RawJDDocument
+ ) {
+ const parsed = await documentToParsed(
+ registry,
+ jdd_context,
+ documentContainerFromStorage(document)
+ );
+ return new JDD(registry, jdd_context, parsed);
+ }
+
+ static fromParsed(
+ registry: Registry,
+ jdd_context: JDDContext,
+ parsed: RawJDDocument
+ ) {
+ return new JDD(
+ registry,
+ jdd_context,
+ documentContainerFromParsed(parsed)
+ );
+ }
+
+ render(
+ make_component_classes = (index: number) => [
+ "jdd-component",
+ `component-number-${index}`,
+ ]
+ ) {
+ return tempstream`${this.parsed.value.map(
+ ({ component_name, args }, index) => {
+ const component = this.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(this.jdd_context);
+ }
+ }
+ return component.toHTML({
+ args,
+ classes: make_component_classes(index),
+ jdd_context: this.jdd_context,
+ index,
+ });
+ }
+ )}`;
+ }
+
+ renderEarlyScript(asset: EarlyAsset): string {
+ 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>`;
+ }
+ }
+
+ renderEarlyStyle(asset: EarlyAsset): string {
+ 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>`;
+ }
+ }
+
+ async 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") {
+ return this.renderEarlyScript(asset);
+ } else if (asset.type == "style") {
+ this.renderEarlyStyle(asset);
+ }
+ })
+ .join(" ");
+ }
+
+ countWords(registry: Registry): number {
+ return this.parsed.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);
+ }
+
+ getHeadings(): JDDHeading[] {
+ return this.parsed.value
+ .map(({ component_name, args }) => {
+ const component = this.registry.get(component_name);
+ if (!component) {
+ return [];
+ }
+ return component.getHeadings(this.jdd_context, args);
+ })
+ .flat();
+ }
+
+ static async renderFromStorage(
+ registry: Registry,
+ document: RawJDDocument,
+ jdd_context: JDDContext
+ ) {
+ const jdd = await JDD.fromStorage(registry, jdd_context, document);
+ return jdd.render();
+ }
+}
diff --git a/src/test-utils/simplest-context.ts b/src/test-utils/simplest-context.ts
new file mode 100644
index 0000000..6ca7e8b
--- /dev/null
+++ b/src/test-utils/simplest-context.ts
@@ -0,0 +1,8 @@
+import { FileManager } from "@sealcode/file-manager";
+import { makeSimpleEnglishJDDContext } from "../jdd-context.js";
+
+export function simplestContext() {
+ return makeSimpleEnglishJDDContext(
+ new FileManager("/tmp", "/uploaded_files")
+ );
+}

File Metadata

Mime Type
text/x-diff
Expires
Mon, Dec 23, 14:28 (21 h, 55 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
556267
Default Alt Text
(9 KB)

Event Timeline