@weborigami/origami 0.0.43 → 0.0.45

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.
@@ -38,18 +38,21 @@ export { default as js } from "../src/builtins/@js.js";
38
38
  export { default as json } from "../src/builtins/@json.js";
39
39
  export { default as keys } from "../src/builtins/@keys.js";
40
40
  export { default as keysJson } from "../src/builtins/@keysJson.js";
41
- export { default as loadersCss } from "../src/builtins/@loaders/css.js";
42
- export { default as loadersHtm } from "../src/builtins/@loaders/htm.js";
43
- export { default as loadersHtml } from "../src/builtins/@loaders/html.js";
41
+ export * from "../src/builtins/@loaders/css.js";
42
+ export * from "../src/builtins/@loaders/htm.js";
43
+ export * from "../src/builtins/@loaders/html.js";
44
+ export { default as loadersJpeg } from "../src/builtins/@loaders/jpeg.js";
45
+ export * from "../src/builtins/@loaders/jpg.js";
44
46
  export { default as loadersJs } from "../src/builtins/@loaders/js.js";
45
47
  export { default as loadersJson } from "../src/builtins/@loaders/json.js";
46
- export { default as loadersMd } from "../src/builtins/@loaders/md.js";
47
- export { default as loadersMjs } from "../src/builtins/@loaders/mjs.js";
48
+ export * from "../src/builtins/@loaders/md.js";
49
+ export * from "../src/builtins/@loaders/mjs.js";
48
50
  export { default as loadersOri } from "../src/builtins/@loaders/ori.js";
49
51
  export { default as loadersTxt } from "../src/builtins/@loaders/txt.js";
50
- export { default as loadersXhtml } from "../src/builtins/@loaders/xhtml.js";
52
+ export { default as loadersWasm } from "../src/builtins/@loaders/wasm.js";
53
+ export * from "../src/builtins/@loaders/xhtml.js";
51
54
  export { default as loadersYaml } from "../src/builtins/@loaders/yaml.js";
52
- export { default as loadersYml } from "../src/builtins/@loaders/yml.js";
55
+ export * from "../src/builtins/@loaders/yml.js";
53
56
  export { default as map } from "../src/builtins/@map.js";
54
57
  export { default as mapDeep } from "../src/builtins/@mapDeep.js";
55
58
  export { default as match } from "../src/builtins/@match.js";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@weborigami/origami",
3
- "version": "0.0.43",
3
+ "version": "0.0.45",
4
4
  "description": "Web Origami language, CLI, framework, and server",
5
5
  "type": "module",
6
6
  "repository": {
@@ -19,9 +19,10 @@
19
19
  "typescript": "5.3.3"
20
20
  },
21
21
  "dependencies": {
22
- "@weborigami/async-tree": "0.0.43",
23
- "@weborigami/language": "0.0.43",
24
- "@weborigami/types": "0.0.43",
22
+ "@weborigami/async-tree": "0.0.45",
23
+ "@weborigami/language": "0.0.45",
24
+ "@weborigami/types": "0.0.45",
25
+ "exif-parser": "0.1.12",
25
26
  "graphviz-wasm": "3.0.1",
26
27
  "highlight.js": "11.9.0",
27
28
  "marked": "11.1.1",
@@ -1,8 +1,8 @@
1
1
  import { Scope, functionResultsMap } from "@weborigami/language";
2
- import builtins from "../../src/builtins/@builtins.js";
3
2
  import arrowFunctionsMap from "../common/arrowFunctionsMap.js";
4
3
  import { keySymbol } from "../common/utilities.js";
5
4
  import getTreeArgument from "../misc/getTreeArgument.js";
5
+ import builtins from "./@builtins.js";
6
6
 
7
7
  /**
8
8
  * Interpret arrow keys in the tree as function calls.
@@ -10,7 +10,7 @@ import getTreeArgument from "../misc/getTreeArgument.js";
10
10
  */
11
11
  export default async function count(treelike) {
12
12
  const tree = await getTreeArgument(this, arguments, treelike, "@count");
13
- const keys = [...(await tree.keys())];
13
+ const keys = Array.from(await tree.keys());
14
14
  return keys.length;
15
15
  }
16
16
 
@@ -1,3 +1,4 @@
1
+ import { Tree, isPlainObject } from "@weborigami/async-tree";
1
2
  import { compile } from "@weborigami/language";
2
3
  import unpackText from "../builtins/@loaders/txt.js";
3
4
  import assertScopeIsDefined from "../misc/assertScopeIsDefined.js";
@@ -28,7 +29,17 @@ export default async function inline(input) {
28
29
  inputDocument = await unpackText(input);
29
30
  }
30
31
 
32
+ // If the input document is a plain object or AsyncTree, we'll have it
33
+ // included in scope for the evaluated expression. We ignore other kinds of
34
+ // treelike inputs for this test: in particular, a Buffer will be interpreted
35
+ // as a tree, but we don't want to put a Buffer in scope.
36
+ const attachedData =
37
+ isPlainObject(inputDocument) || Tree.isAsyncTree(inputDocument)
38
+ ? inputDocument
39
+ : null;
40
+
31
41
  const templateFn = await unpackOrigamiExpression(inputDocument, {
42
+ attachedData,
32
43
  compiler: compile.templateDocument,
33
44
  });
34
45
  const templateResult = await templateFn(inputDocument);
@@ -1,4 +1,2 @@
1
- import unpackText from "./txt.js";
2
-
3
- // Load this file type as text with possible front matter.
4
- export default unpackText;
1
+ // .css files use the .txt loader
2
+ export { default } from "./txt.js";
@@ -1,4 +1,2 @@
1
- import unpackText from "./txt.js";
2
-
3
- // Load this file type as text with possible front matter.
4
- export default unpackText;
1
+ // .htm files use the .txt loader
2
+ export { default } from "./txt.js";
@@ -1,4 +1,2 @@
1
- import unpackText from "./txt.js";
2
-
3
- // Load this file type as text with possible front matter.
4
- export default unpackText;
1
+ // .html files use the .txt loader
2
+ export { default } from "./txt.js";
@@ -0,0 +1,55 @@
1
+ import exifParser from "exif-parser";
2
+
3
+ const exifDateTags = [
4
+ "ModifyDate",
5
+ "MDPrepDate",
6
+ "DateTimeOriginal",
7
+ "CreateDate",
8
+ "PreviewDateTime",
9
+ "GPSDateStamp",
10
+ ];
11
+
12
+ /**
13
+ * Load Exif data from a JPEG file.
14
+ *
15
+ * @type {import("@weborigami/language").FileUnpackFunction}
16
+ */
17
+ export default async function unpackJpeg(buffer, options) {
18
+ const parser = exifParser.create(buffer);
19
+ parser.enableTagNames(true);
20
+ parser.enableSimpleValues(true);
21
+ const parsed = await parser.parse();
22
+
23
+ // The exif-parser `enableSimpleValues` option should convert dates to
24
+ // JavaScript Date objects, but that doesn't seem to work. Ensure dates are
25
+ // Date objects.
26
+ const exif = parsed.tags;
27
+ for (const tag of exifDateTags) {
28
+ if (typeof exif[tag] === "number") {
29
+ exif[tag] = new Date(exif[tag] * 1000);
30
+ }
31
+ }
32
+
33
+ const result = {
34
+ height: parsed.imageSize.height,
35
+ width: parsed.imageSize.width,
36
+ exif,
37
+ };
38
+
39
+ // Promote some Exif properties to the top level.
40
+ const tagsToPromote = {
41
+ ImageDescription: "caption",
42
+ ModifyDate: "modified",
43
+ Orientation: "orientation",
44
+ };
45
+ for (const [tag, key] of Object.entries(tagsToPromote)) {
46
+ if (exif[tag] !== undefined) {
47
+ result[key] = exif[tag];
48
+ }
49
+ }
50
+
51
+ // Add aspect ratio for use with `aspect-ratio` CSS.
52
+ result.aspectRatio = result.width / result.height;
53
+
54
+ return result;
55
+ }
@@ -0,0 +1,2 @@
1
+ // .jpg extension is a synonym for .jpeg
2
+ export { default } from "./jpeg.js";
@@ -1,8 +1,14 @@
1
+ import * as utilities from "../../common/utilities.js";
2
+
1
3
  /**
2
4
  * Load a file as JSON.
3
5
  *
4
6
  * @type {import("@weborigami/language").FileUnpackFunction}
5
7
  */
6
8
  export default function unpackJson(input) {
7
- return JSON.parse(String(input));
9
+ const json = utilities.toString(input);
10
+ if (!json) {
11
+ throw new Error("Tried to parse something as JSON but it wasn't text.");
12
+ }
13
+ return JSON.parse(json);
8
14
  }
@@ -1,4 +1,2 @@
1
- import unpackText from "./txt.js";
2
-
3
- // Load this file type as text with possible front matter.
4
- export default unpackText;
1
+ // .md files use the .txt loader
2
+ export { default } from "./txt.js";
@@ -1,4 +1,2 @@
1
- import unpackModule from "./js.js";
2
-
3
- // .mjs modules use the same loader as .js modules.
4
- export default unpackModule;
1
+ // .mjs files use the .js loader
2
+ export { default } from "./js.js";
@@ -1,5 +1,4 @@
1
- import { Scope } from "@weborigami/language";
2
- import * as compile from "../../../../language/src/compiler/compile.js";
1
+ import { Scope, compile, symbols } from "@weborigami/language";
3
2
  import processUnpackedContent from "../../common/processUnpackedContent.js";
4
3
  import * as utilities from "../../common/utilities.js";
5
4
  import builtins from "../@builtins.js";
@@ -13,10 +12,11 @@ export default async function unpackOrigamiExpression(
13
12
  inputDocument,
14
13
  options = {}
15
14
  ) {
15
+ const attachedData = options.attachedData;
16
16
  const parent =
17
17
  options.parent ??
18
18
  /** @type {any} */ (inputDocument).parent ??
19
- /** @type {any} */ (inputDocument)[utilities.parentSymbol];
19
+ /** @type {any} */ (inputDocument)[symbols.parent];
20
20
 
21
21
  // Construct an object to represent the source code.
22
22
  const sourceName = options.key;
@@ -41,5 +41,5 @@ export default async function unpackOrigamiExpression(
41
41
  const parentScope = parent ? Scope.getScope(parent) : builtins;
42
42
  let content = await fn.call(parentScope);
43
43
 
44
- return processUnpackedContent(content, parent, inputDocument);
44
+ return processUnpackedContent(content, parent, attachedData);
45
45
  }
@@ -1,5 +1,6 @@
1
1
  import TextDocument from "../../common/TextDocument.js";
2
2
  import { evaluateYaml } from "../../common/serialize.js";
3
+ import * as utilities from "../../common/utilities.js";
3
4
 
4
5
  /**
5
6
  * Load a file as text document with possible front matter.
@@ -16,7 +17,10 @@ import { evaluateYaml } from "../../common/serialize.js";
16
17
  */
17
18
  export default async function unpackText(input, options = {}) {
18
19
  const parent = options.parent ?? null;
19
- const text = String(input);
20
+ const text = utilities.toString(input);
21
+ if (!text) {
22
+ throw new Error("Tried to treat something as text but it wasn't text.");
23
+ }
20
24
  const regex =
21
25
  /^(---\r?\n(?<frontText>[\s\S]*?\r?\n)---\r?\n)(?<body>[\s\S]*$)/;
22
26
  const match = regex.exec(text);
@@ -0,0 +1,11 @@
1
+ import processUnpackedContent from "../../common/processUnpackedContent.js";
2
+
3
+ /**
4
+ * Load a .js file as module's default export or exports.
5
+ *
6
+ * @type {import("@weborigami/language").FileUnpackFunction}
7
+ */
8
+ export default async function unpackWasm(buffer, options = {}) {
9
+ const wasmModule = await WebAssembly.instantiate(buffer);
10
+ return processUnpackedContent(wasmModule.instance.exports, options.parent);
11
+ }
@@ -1,4 +1,2 @@
1
- import unpackText from "./txt.js";
2
-
3
- // Load this file type as text with possible front matter.
4
- export default unpackText;
1
+ // .xhtml files use the .txt loader
2
+ export { default } from "./txt.js";
@@ -1,6 +1,7 @@
1
1
  import * as YAMLModule from "yaml";
2
2
  import processUnpackedContent from "../../common/processUnpackedContent.js";
3
3
  import { evaluateYaml } from "../../common/serialize.js";
4
+ import * as utilities from "../../common/utilities.js";
4
5
 
5
6
  // See notes at serialize.js
6
7
  // @ts-ignore
@@ -13,6 +14,10 @@ const YAML = YAMLModule.default ?? YAMLModule.YAML;
13
14
  */
14
15
  export default async function unpackYaml(input, options = {}) {
15
16
  const parent = options.parent ?? null;
16
- const data = evaluateYaml(String(input), options.parent);
17
+ const yaml = utilities.toString(input);
18
+ if (!yaml) {
19
+ throw new Error("Tried to parse something as YAML but it wasn't text.");
20
+ }
21
+ const data = await evaluateYaml(yaml, options.parent);
17
22
  return processUnpackedContent(data, parent);
18
23
  }
@@ -1,4 +1,2 @@
1
- import yaml from "./yaml.js";
2
-
3
- // .yml files uses the YAML loader
4
- export default yaml;
1
+ // .yml extension is a synonym for .yaml
2
+ export { default } from "./yaml.js";
@@ -2,10 +2,21 @@ import path from "node:path";
2
2
  import process from "node:process";
3
3
  import url from "node:url";
4
4
 
5
+ // Patch process.env to be a plain object. Among other things, this lets us dump
6
+ // the complete environment to the terminal with `ori @node/process/env`.
7
+ const patchedProcess = Object.create(null, {
8
+ ...Object.getOwnPropertyDescriptors(process),
9
+ env: {
10
+ value: function () {
11
+ return Object.assign({}, process.env);
12
+ },
13
+ },
14
+ });
15
+
5
16
  const node = {
6
17
  Buffer,
7
18
  path,
8
- process,
19
+ process: patchedProcess,
9
20
  url,
10
21
  };
11
22
 
@@ -1,5 +1,5 @@
1
1
  import { Tree, getRealmObjectPrototype } from "@weborigami/async-tree";
2
- import * as compile from "../../../language/src/compiler/compile.js";
2
+ import { compile } from "@weborigami/language";
3
3
  import builtins from "../builtins/@builtins.js";
4
4
  import { toYaml } from "../common/serialize.js";
5
5
  import assertScopeIsDefined from "../misc/assertScopeIsDefined.js";
@@ -73,13 +73,13 @@ async function formatResult(result) {
73
73
  text = result;
74
74
  }
75
75
 
76
- // If the result is a tree, attach the tree to the text output.
76
+ // If the result is treelike, attach it to the text output.
77
77
  if (Tree.isTreelike(result)) {
78
78
  if (typeof text === "string") {
79
79
  // @ts-ignore
80
80
  text = new String(text);
81
81
  }
82
- /** @type {any} */ (text).unpack = () => Tree.from(result);
82
+ /** @type {any} */ (text).unpack = () => result;
83
83
  }
84
84
 
85
85
  return text;
@@ -18,7 +18,7 @@ export default async function rss(jsonFeedTree) {
18
18
  parts.push("rss.xml");
19
19
  const rssUrl = parts.join("/");
20
20
 
21
- const itemsRss = items?.map((story) => itemRss(story)).join("\n") ?? [];
21
+ const itemsRss = items?.map((story) => itemRss(story)).join("") ?? [];
22
22
 
23
23
  return `<?xml version="1.0" ?>
24
24
  <rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
@@ -27,7 +27,7 @@ export default async function rss(jsonFeedTree) {
27
27
  <title>${title}</title>
28
28
  <link>${home_page_url}</link>
29
29
  <description>${description}</description>
30
- ${itemsRss}</channel>
30
+ ${itemsRss}</channel>
31
31
  </rss>`;
32
32
  }
33
33
 
@@ -36,12 +36,14 @@ function itemRss(jsonFeedItem) {
36
36
  // RSS wants dates in RFC-822.
37
37
  const date = date_published?.toUTCString() ?? null;
38
38
  const dateElement = date ? ` <pubDate>${date}</pubDate>\n` : "";
39
+ const guidElement = id ? ` <guid>${id}</guid>\n` : "";
40
+ const contentElement = content_html
41
+ ? ` <description><![CDATA[${content_html}]]></description>\n`
42
+ : "";
39
43
  return ` <item>
40
- ${dateElement}<title>${title}</title>
44
+ ${dateElement} <title>${title}</title>
41
45
  <link>${url}</link>
42
- <guid>${id}</guid>
43
- <description><![CDATA[${content_html}]]></description>
44
- </item>
46
+ ${guidElement}${contentElement} </item>
45
47
  `;
46
48
  }
47
49
 
@@ -27,7 +27,7 @@ export default function CommandsModulesTransform(Base) {
27
27
  }
28
28
 
29
29
  async keys() {
30
- const keys = [...(await super.keys())];
30
+ const keys = Array.from(await super.keys());
31
31
  // If we find a key like "foo.js", then return "foo" as the key.
32
32
  return keys.map((key) =>
33
33
  key.endsWith(".js") ? path.basename(key, ".js") : key
@@ -1,4 +1,5 @@
1
1
  import { isStringLike } from "@weborigami/async-tree";
2
+ import { symbols } from "@weborigami/language";
2
3
  import { toYaml } from "./serialize.js";
3
4
  import * as utilities from "./utilities.js";
4
5
 
@@ -22,7 +23,7 @@ export default class TextDocument {
22
23
  constructor(data, parent) {
23
24
  Object.assign(this, data);
24
25
  if (parent) {
25
- this[utilities.parentSymbol] = parent;
26
+ this[symbols.parent] = parent;
26
27
  }
27
28
  }
28
29
 
@@ -10,18 +10,20 @@ import builtins from "../builtins/@builtins.js";
10
10
  *
11
11
  * @param {any} content
12
12
  * @param {AsyncTree|null} parent
13
- * @param {any} [inputDocument]
13
+ * @param {any} [attachedData]
14
14
  * @returns
15
15
  */
16
- export default function processUnpackedContent(content, parent, inputDocument) {
16
+ export default function processUnpackedContent(content, parent, attachedData) {
17
17
  if (typeof content === "function") {
18
18
  // Wrap the function such to add ambients to the scope.
19
19
  const fn = content;
20
20
 
21
21
  // Use the parent's scope, adding any attached data.
22
22
  const parentScope = parent ? Scope.getScope(parent) : builtins;
23
- const extendedScope = Tree.isTreelike(inputDocument)
24
- ? new Scope(inputDocument, parentScope)
23
+
24
+ // If there's attached data, include it in the scope.
25
+ const extendedScope = attachedData
26
+ ? new Scope(attachedData, parentScope)
25
27
  : parentScope;
26
28
 
27
29
  const boundFn = fn.bind(extendedScope);
@@ -1,6 +1,5 @@
1
1
 
2
2
  export const keySymbol: unique symbol;
3
- export const parentSymbol: unique symbol;
4
3
  export function hasNonPrintableCharacters(text: string): boolean;
5
4
  export function isTransformApplied(Transform: Function, object: any): boolean;
6
5
  export function replaceExtension(key: string, sourceExtension: string, resultExtension: string): string;
@@ -30,8 +30,6 @@ export function isTransformApplied(Transform, obj) {
30
30
 
31
31
  export const keySymbol = Symbol("key");
32
32
 
33
- export const parentSymbol = Symbol("parent");
34
-
35
33
  /**
36
34
  * If the given key ends in the source extension (which will generally include a
37
35
  * period), replace that extension with the result extension (which again should
@@ -1,5 +1,5 @@
1
- import { expressionFunction } from "@weborigami/language";
2
- import * as compile from "../../../language/src/compiler/compile.js";
1
+ import { compile, expressionFunction } from "@weborigami/language";
2
+
3
3
  /**
4
4
  * A YAML tag for an Origami expression.
5
5
  */