@weborigami/origami 0.0.60 → 0.0.62

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.
@@ -1,6 +1,5 @@
1
1
  // This file is generated by running buildExports.js -- do not edit by hand.
2
2
  export { default as addNextPrevious } from "../src/builtins/@addNextPrevious.js";
3
- export { default as arrowsMap } from "../src/builtins/@arrowsMap.js";
4
3
  export { default as basename } from "../src/builtins/@basename.js";
5
4
  export { default as breakpoint } from "../src/builtins/@breakpoint.js";
6
5
  export { default as builtins } from "../src/builtins/@builtins.js";
@@ -99,6 +98,7 @@ export { default as treeHttp } from "../src/builtins/@treeHttp.js";
99
98
  export { default as treeHttps } from "../src/builtins/@treeHttps.js";
100
99
  export { default as unpack } from "../src/builtins/@unpack.js";
101
100
  export { default as values } from "../src/builtins/@values.js";
101
+ export { default as version } from "../src/builtins/@version.js";
102
102
  export { default as watch } from "../src/builtins/@watch.js";
103
103
  export { default as yaml } from "../src/builtins/@yaml.js";
104
104
  export { default as yamlParse } from "../src/builtins/@yamlParse.js";
@@ -111,6 +111,7 @@ export { default as jsHandler } from "../src/builtins/js_handler.js";
111
111
  export { default as jsonHandler } from "../src/builtins/json_handler.js";
112
112
  export { default as mdHandler } from "../src/builtins/md_handler.js";
113
113
  export { default as mjsHandler } from "../src/builtins/mjs_handler.js";
114
+ export { default as oriDocumentHandler } from "../src/builtins/ori_document_handler.js";
114
115
  export { default as oriHandler } from "../src/builtins/ori_handler.js";
115
116
  export { default as txtHandler } from "../src/builtins/txt_handler.js";
116
117
  export { default as wasmHandler } from "../src/builtins/wasm_handler.js";
@@ -133,6 +134,7 @@ export * from "../src/common/utilities.js";
133
134
  export { default as assertTreeIsDefined } from "../src/misc/assertTreeIsDefined.js";
134
135
  export { default as getTreeArgument } from "../src/misc/getTreeArgument.js";
135
136
  export { default as OriCommandTransform } from "../src/misc/OriCommandTransform.js";
137
+ export { default as origamiHighlightDefinition } from "../src/misc/origamiHighlightDefinition.js";
136
138
  export { default as treeDot } from "../src/misc/treeDot.js";
137
139
  export { default as constructResponse } from "../src/server/constructResponse.js";
138
140
  export * from "../src/server/mediaTypes.js";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@weborigami/origami",
3
- "version": "0.0.60",
3
+ "version": "0.0.62",
4
4
  "description": "Web Origami language, CLI, framework, and server",
5
5
  "type": "module",
6
6
  "repository": {
@@ -17,9 +17,9 @@
17
17
  "typescript": "5.5.3"
18
18
  },
19
19
  "dependencies": {
20
- "@weborigami/async-tree": "0.0.60",
21
- "@weborigami/language": "0.0.60",
22
- "@weborigami/types": "0.0.60",
20
+ "@weborigami/async-tree": "0.0.62",
21
+ "@weborigami/language": "0.0.62",
22
+ "@weborigami/types": "0.0.62",
23
23
  "exif-parser": "0.1.12",
24
24
  "graphviz-wasm": "3.0.2",
25
25
  "highlight.js": "11.9.0",
@@ -3,8 +3,8 @@ import { Tree } from "@weborigami/async-tree";
3
3
  // Given an old tree and a new tree, return a tree of changes indicated
4
4
  // by the values: "added", "changed", or "deleted".
5
5
  export default async function changes(oldTreelike, newTreelike) {
6
- const oldTree = Tree.from(oldTreelike);
7
- const newTree = Tree.from(newTreelike);
6
+ const oldTree = Tree.from(oldTreelike, { deep: true });
7
+ const newTree = Tree.from(newTreelike, { deep: true });
8
8
 
9
9
  const oldKeys = Array.from(await oldTree.keys());
10
10
  const newKeys = Array.from(await newTree.keys());
@@ -11,7 +11,13 @@ import getTreeArgument from "../misc/getTreeArgument.js";
11
11
  * @param {Treelike} [treelike]
12
12
  */
13
13
  export default async function deepValuesBuiltin(treelike) {
14
- const tree = await getTreeArgument(this, arguments, treelike, "@valuesDeep");
14
+ const tree = await getTreeArgument(
15
+ this,
16
+ arguments,
17
+ treelike,
18
+ "@valuesDeep",
19
+ true
20
+ );
15
21
  return deepValues(tree);
16
22
  }
17
23
 
@@ -6,6 +6,9 @@ import { markedHighlight } from "marked-highlight";
6
6
  import { markedSmartypants } from "marked-smartypants";
7
7
  import documentObject from "../common/documentObject.js";
8
8
  import { replaceExtension, toString } from "../common/utilities.js";
9
+ import origamiHighlightDefinition from "../misc/origamiHighlightDefinition.js";
10
+
11
+ highlight.registerLanguage("ori", origamiHighlightDefinition);
9
12
 
10
13
  marked.use(
11
14
  markedGfmHeadingId(),
@@ -1,4 +1,8 @@
1
- import { Tree, getRealmObjectPrototype } from "@weborigami/async-tree";
1
+ import {
2
+ Tree,
3
+ getRealmObjectPrototype,
4
+ toString,
5
+ } from "@weborigami/async-tree";
2
6
  import { compile } from "@weborigami/language";
3
7
  import builtins from "../builtins/@builtins.js";
4
8
  import { toYaml } from "../common/serialize.js";
@@ -22,7 +26,7 @@ export default async function ori(
22
26
  assertTreeIsDefined(this, "ori");
23
27
 
24
28
  // In case expression has come from a file, cast it to a string.
25
- expression = String(expression);
29
+ expression = toString(expression);
26
30
 
27
31
  // Run in the context of `this` if defined, otherwise use the builtins.
28
32
  const tree = this ?? builtins;
@@ -1,55 +1,119 @@
1
1
  import { Tree } from "@weborigami/async-tree";
2
2
  import assertTreeIsDefined from "../misc/assertTreeIsDefined.js";
3
3
 
4
+ const days = ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"];
5
+ const months = [
6
+ "Jan",
7
+ "Feb",
8
+ "Mar",
9
+ "Apr",
10
+ "May",
11
+ "Jun",
12
+ "Jul",
13
+ "Aug",
14
+ "Sep",
15
+ "Oct",
16
+ "Nov",
17
+ "Dec",
18
+ ];
19
+
4
20
  /**
5
21
  * @typedef {import("@weborigami/types").AsyncTree} AsyncTree
6
22
  * @typedef {import("@weborigami/async-tree").Treelike} Treelike
7
23
  * @this {AsyncTree|null}
8
24
  * @param {Treelike} jsonFeedTree
25
+ * @param {any} options
9
26
  */
10
- export default async function rss(jsonFeedTree) {
27
+ export default async function rss(jsonFeedTree, options = {}) {
11
28
  assertTreeIsDefined(this, "rss");
12
29
  const jsonFeed = await Tree.plain(jsonFeedTree);
13
- const { description, home_page_url, items, feed_url, title } = jsonFeed;
30
+ const { description, home_page_url, items, title } = jsonFeed;
14
31
 
15
- // Presume that the RSS feed lives in same location as feed_url.
16
- const parts = feed_url.split("/");
17
- parts.pop();
18
- parts.push("rss.xml");
19
- const rssUrl = parts.join("/");
32
+ let { feed_url, language } = options;
33
+ if (!feed_url && jsonFeed.feed_url) {
34
+ // Presume that the RSS feed lives in same location as feed_url
35
+ // but with a .xml extension.
36
+ feed_url = jsonFeed.feed_url;
37
+ if (feed_url.endsWith(".json")) {
38
+ feed_url = feed_url.replace(".json", ".xml");
39
+ }
40
+ }
20
41
 
21
42
  const itemsRss = items?.map((story) => itemRss(story)).join("") ?? [];
22
43
 
44
+ const titleElement = title ? ` <title>${escapeXml(title)}</title>\n` : "";
45
+ const descriptionElement = description
46
+ ? ` <description>${escapeXml(description)}</description>\n`
47
+ : "";
48
+ const linkElement = home_page_url
49
+ ? ` <link>${home_page_url}</link>\n`
50
+ : "";
51
+ const languageElement = language
52
+ ? ` <language>${language}</language>\n`
53
+ : "";
54
+ const feedLinkElement = ` <atom:link href="${feed_url}" rel="self" type="application/rss+xml"/>\n`;
55
+ // const feedLinkElement = ` <link rel="self" type="application/atom+xml" href="${feed_url}"/>\n`;
56
+
23
57
  return `<?xml version="1.0" ?>
24
- <rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
58
+ <rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:content="http://purl.org/rss/1.0/modules/content/">
25
59
  <channel>
26
- <atom:link href="${rssUrl}" rel="self" type="application/rss+xml"/>
27
- <title><![CDATA[${title}]]></title>
28
- <link>${home_page_url}</link>
29
- <description>${description}</description>
30
- ${itemsRss}</channel>
60
+ ${titleElement}${descriptionElement}${linkElement}${languageElement}${feedLinkElement}${itemsRss} </channel>
31
61
  </rss>`;
32
62
  }
33
63
 
34
64
  function itemRss(jsonFeedItem) {
35
- const { content_html, date_published, id, title, url } = jsonFeedItem;
36
- // RSS wants dates in RFC-822, essentially the same as ISO 8601.
65
+ const { content_html, id, summary, title, url } = jsonFeedItem;
66
+ let { date_published } = jsonFeedItem;
67
+ if (typeof date_published === "string") {
68
+ // Parse as ISO 8601 date.
69
+ date_published = new Date(date_published);
70
+ }
37
71
  const date =
38
72
  date_published instanceof Date
39
- ? date_published.toUTCString()
73
+ ? toRFC822Date(date_published)
40
74
  : date_published;
75
+
41
76
  const dateElement = date ? ` <pubDate>${date}</pubDate>\n` : "";
42
- const guidElement = id ? ` <guid>${id}</guid>\n` : "";
77
+ const isPermaLink =
78
+ id !== undefined && !URL.canParse(id) ? ` isPermaLink="false"` : "";
79
+ const guidElement = id ? ` <guid${isPermaLink}>${id}</guid>\n` : "";
80
+ const descriptionElement = summary
81
+ ? ` <description>${escapeXml(summary)}</description>\n`
82
+ : "";
43
83
  const contentElement = content_html
44
- ? ` <description><![CDATA[${content_html}]]></description>\n`
84
+ ? ` <content:encoded><![CDATA[${content_html}]]></content:encoded>\n`
45
85
  : "";
46
- const titleElement = `<![CDATA[${title}]]>`;
86
+ const titleElement = title
87
+ ? ` <title>${escapeXml(title)}</title>\n`
88
+ : "";
89
+ const linkElement = url ? ` <link>${url}</link>\n` : "";
90
+
47
91
  return ` <item>
48
- ${dateElement} <title>${titleElement}</title>
49
- <link>${url}</link>
50
- ${guidElement}${contentElement} </item>
92
+ ${dateElement}${titleElement}${linkElement}${guidElement}${descriptionElement}${contentElement} </item>
51
93
  `;
52
94
  }
53
95
 
96
+ // Escape XML entities for in the text.
97
+ function escapeXml(text) {
98
+ return text
99
+ .replace(/&/g, "&amp;")
100
+ .replace(/</g, "&lt;")
101
+ .replace(/>/g, "&gt;")
102
+ .replace(/"/g, "&quot;")
103
+ .replace(/'/g, "&apos;");
104
+ }
105
+
106
+ // RSS wants dates in RFC-822.
107
+ function toRFC822Date(date) {
108
+ const day = days[date.getUTCDay()];
109
+ const dayOfMonth = date.getUTCDate().toString().padStart(2, "0");
110
+ const month = months[date.getUTCMonth()];
111
+ const year = date.getUTCFullYear();
112
+ const hours = date.getUTCHours().toString().padStart(2, "0");
113
+ const minutes = date.getUTCMinutes().toString().padStart(2, "0");
114
+ const seconds = date.getUTCSeconds().toString().padStart(2, "0");
115
+ return `${day}, ${dayOfMonth} ${month} ${year} ${hours}:${minutes}:${seconds} GMT`;
116
+ }
117
+
54
118
  rss.usage = `@rss <feed>\tTransforms a JSON Feed tree to RSS XML`;
55
119
  rss.documentation = "https://weborigami.org/language/@rss.html";
@@ -0,0 +1,13 @@
1
+ // When this is no longer experimental in Node:
2
+ // import packageJson from "../../package.json" with { type: "json" };
3
+
4
+ import fs from "node:fs/promises";
5
+ import path from "node:path";
6
+ import { fileURLToPath } from "node:url";
7
+
8
+ const dirname = path.dirname(fileURLToPath(import.meta.url));
9
+ const packageJsonPath = path.resolve(dirname, "../../package.json");
10
+ const buffer = await fs.readFile(packageJsonPath);
11
+ const data = JSON.parse(String(buffer));
12
+
13
+ export default data.version;
@@ -0,0 +1,43 @@
1
+ import { symbols } from "@weborigami/async-tree";
2
+ import { compile } from "@weborigami/language";
3
+ import processUnpackedContent from "../common/processUnpackedContent.js";
4
+ import * as utilities from "../common/utilities.js";
5
+
6
+ /**
7
+ * An Origami template document: a plain text file that contains Origami
8
+ * expressions.
9
+ */
10
+ export default {
11
+ mediaType: "text/plain",
12
+
13
+ /** @type {import("@weborigami/language").UnpackFunction} */
14
+ async unpack(packed, options = {}) {
15
+ const parent =
16
+ options.parent ??
17
+ /** @type {any} */ (packed).parent ??
18
+ /** @type {any} */ (packed)[symbols.parent];
19
+
20
+ // Construct an object to represent the source code.
21
+ const sourceName = options.key;
22
+ let url;
23
+ if (sourceName && parent?.url) {
24
+ let parentHref = parent.url.href;
25
+ if (!parentHref.endsWith("/")) {
26
+ parentHref += "/";
27
+ }
28
+ url = new URL(sourceName, parentHref);
29
+ }
30
+
31
+ const source = {
32
+ text: utilities.toString(packed),
33
+ name: options.key,
34
+ url,
35
+ };
36
+
37
+ // Compile the text as an Origami template document.
38
+ const templateDefineFn = compile.templateDocument(source);
39
+ const templateFn = await templateDefineFn.call(parent);
40
+
41
+ return processUnpackedContent(templateFn, parent);
42
+ },
43
+ };
@@ -1,4 +1,4 @@
1
- import { Tree } from "@weborigami/async-tree";
1
+ import { symbols, Tree } from "@weborigami/async-tree";
2
2
  import builtins from "../builtins/@builtins.js";
3
3
 
4
4
  /**
@@ -29,6 +29,14 @@ export default function processUnpackedContent(content, parent, attachedData) {
29
29
  const result = Object.create(content);
30
30
  result.parent = parent;
31
31
  return result;
32
+ } else if (Object.isExtensible(content) && !content[symbols.parent]) {
33
+ Object.defineProperty(content, symbols.parent, {
34
+ configurable: true,
35
+ enumerable: false,
36
+ value: parent,
37
+ writable: true,
38
+ });
39
+ return content;
32
40
  } else {
33
41
  return content;
34
42
  }
@@ -91,10 +91,9 @@ export function toFunction(obj) {
91
91
  */
92
92
  export function toString(object) {
93
93
  if (isPlainObject(object) && "@text" in object) {
94
- return object["@text"];
95
- } else {
96
- return asyncTreeToString(object);
94
+ object = object["@text"];
97
95
  }
96
+ return asyncTreeToString(object);
98
97
  }
99
98
 
100
99
  /**
@@ -18,13 +18,15 @@ import assertTreeIsDefined from "./assertTreeIsDefined.js";
18
18
  * @param {IArguments} args
19
19
  * @param {Treelike|undefined} treelike
20
20
  * @param {string} methodName
21
+ * @param {boolean} [deep]
21
22
  * @returns {Promise<AsyncTree>}
22
23
  */
23
24
  export default async function getTreeArgument(
24
25
  parent,
25
26
  args,
26
27
  treelike,
27
- methodName
28
+ methodName,
29
+ deep = false
28
30
  ) {
29
31
  assertTreeIsDefined(parent, methodName);
30
32
 
@@ -33,7 +35,7 @@ export default async function getTreeArgument(
33
35
  treelike = await treelike.unpack();
34
36
  }
35
37
  if (isTreelike(treelike)) {
36
- let tree = Tree.from(treelike);
38
+ let tree = Tree.from(treelike, { deep });
37
39
  // If the tree was created from a treelike object and does not yet have a
38
40
  // parent, make the current tree its parent.
39
41
  if (!tree.parent) {
@@ -0,0 +1,36 @@
1
+ /**
2
+ * Origami language definition for highlight.js
3
+ */
4
+ export default function origamiHighlightDefinition(hljs) {
5
+ return {
6
+ name: "Origami",
7
+ contains: [
8
+ hljs.C_LINE_COMMENT_MODE,
9
+ hljs.C_BLOCK_COMMENT_MODE,
10
+ hljs.C_NUMBER_MODE,
11
+ hljs.APOS_STRING_MODE,
12
+ hljs.QUOTE_STRING_MODE,
13
+ hljs.BACKSLASH_ESCAPE,
14
+ {
15
+ // Backtick template strings
16
+ className: "string",
17
+ begin: "`",
18
+ end: "`",
19
+ contains: [
20
+ hljs.BACKSLASH_ESCAPE,
21
+ {
22
+ className: "subst",
23
+ begin: "\\$\\{",
24
+ end: "\\}",
25
+ contains: [hljs.C_NUMBER_MODE, hljs.QUOTE_STRING_MODE],
26
+ },
27
+ ],
28
+ },
29
+ {
30
+ // Treat all `@` builtins as keywords.
31
+ className: "keyword",
32
+ begin: /@\w+\b/,
33
+ },
34
+ ],
35
+ };
36
+ }
@@ -1,24 +0,0 @@
1
- import { functionResultsMap } from "@weborigami/language";
2
- import arrowsMapFn from "../common/arrowsMapFn.js";
3
- import { keySymbol } from "../common/utilities.js";
4
- import getTreeArgument from "../misc/getTreeArgument.js";
5
- import builtins from "./@builtins.js";
6
-
7
- /**
8
- * Interpret arrow keys in the tree as function calls.
9
- *
10
- * @typedef {import("@weborigami/types").AsyncTree} AsyncTree
11
- * @typedef {import("@weborigami/async-tree").Treelike} Treelike
12
- * @this {AsyncTree|null}
13
- * @param {Treelike} [treelike]
14
- */
15
- export default async function arrowsMap(treelike) {
16
- const tree = await getTreeArgument(this, arguments, treelike, "arrowsMap");
17
- const mapped = functionResultsMap(arrowsMapFn()(tree));
18
- mapped.parent = this ?? builtins;
19
- mapped[keySymbol] = "@arrowsMap";
20
- return mapped;
21
- }
22
-
23
- arrowsMap.usage = `@arrowsMap <obj>\tInterpret arrow keys in the tree as function calls`;
24
- arrowsMap.documentation = "https://weborigami.org/language/@arrowsMap.html";