@weborigami/origami 0.3.3-jse.3 → 0.3.4-jse.4

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.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@weborigami/origami",
3
- "version": "0.3.3-jse.3",
3
+ "version": "0.3.4-jse.4",
4
4
  "description": "Web Origami language, CLI, framework, and server",
5
5
  "type": "module",
6
6
  "repository": {
@@ -17,10 +17,10 @@
17
17
  "typescript": "5.8.2"
18
18
  },
19
19
  "dependencies": {
20
- "@weborigami/async-tree": "0.3.3-jse.3",
20
+ "@weborigami/async-tree": "0.3.4-jse.4",
21
21
  "@weborigami/json-feed-to-rss": "1.0.0",
22
- "@weborigami/language": "0.3.3-jse.3",
23
- "@weborigami/types": "0.3.3-jse.3",
22
+ "@weborigami/language": "0.3.4-jse.4",
23
+ "@weborigami/types": "0.3.4-jse.4",
24
24
  "css-tree": "3.1.0",
25
25
  "exif-parser": "0.1.12",
26
26
  "graphviz-wasm": "3.0.2",
@@ -1,11 +1,9 @@
1
1
  import { text as treeText } from "@weborigami/async-tree";
2
2
  import { jsGlobals } from "@weborigami/language";
3
- import BuiltinsTree from "./BuiltinsTree.js";
4
3
  import * as dev from "./dev/dev.js";
5
4
  import handlerBuiltins from "./handlers/handlerBuiltins.js";
6
5
  import help from "./help/help.js";
7
6
  import * as image from "./image/image.js";
8
- import node from "./node.js";
9
7
  import * as origami from "./origami/origami.js";
10
8
  import explore from "./protocols/explore.js";
11
9
  import files from "./protocols/files.js";
@@ -14,33 +12,35 @@ import https from "./protocols/https.js";
14
12
  import httpstree from "./protocols/httpstree.js";
15
13
  import httptree from "./protocols/httptree.js";
16
14
  import inherited from "./protocols/inherited.js";
15
+ import node from "./protocols/node.js";
17
16
  import packageNamespace from "./protocols/package.js";
18
17
  import scope from "./protocols/scope.js";
19
18
  import * as site from "./site/site.js";
20
19
  import * as text from "./text/text.js";
21
20
  import * as tree from "./tree/tree.js";
22
21
 
23
- let result;
22
+ let builtins;
24
23
 
25
24
  export default function builtinsJse() {
26
- if (!result) {
27
- const Tree = new BuiltinsTree({
25
+ if (!builtins) {
26
+ const Tree = {
28
27
  ...tree,
29
28
  indent: text.indent,
30
29
  json: origami.json,
31
30
  text: treeText,
32
- });
31
+ };
33
32
 
34
- const Origami = new BuiltinsTree({
33
+ const Origami = {
35
34
  ...dev,
35
+ files,
36
36
  image,
37
37
  ...origami,
38
38
  ...site,
39
39
  ...text,
40
- });
40
+ };
41
41
 
42
42
  /** @type {any} */
43
- result = new BuiltinsTree({
43
+ builtins = {
44
44
  ...jsGlobals,
45
45
 
46
46
  "explore:": explore,
@@ -60,8 +60,8 @@ export default function builtinsJse() {
60
60
 
61
61
  // Handlers need to be exposed at top level
62
62
  ...handlerBuiltins(),
63
- });
63
+ };
64
64
  }
65
65
 
66
- return result;
66
+ return builtins;
67
67
  }
@@ -1,75 +1,44 @@
1
1
  import { jsGlobals } from "@weborigami/language";
2
- import BuiltinsTree from "./BuiltinsTree.js";
2
+ import builtinsJse from "./builtinsJse.js";
3
3
  import * as dev from "./dev/dev.js";
4
- import handlerBuiltins from "./handlers/handlerBuiltins.js";
5
- import help from "./help/help.js";
6
4
  import * as image from "./image/image.js";
7
- import node from "./node.js";
8
5
  import * as origami from "./origami/origami.js";
9
- import explore from "./protocols/explore.js";
10
- import files from "./protocols/files.js";
11
- import http from "./protocols/http.js";
12
- import https from "./protocols/https.js";
13
- import httpstree from "./protocols/httpstree.js";
14
- import httptree from "./protocols/httptree.js";
15
- import inherited from "./protocols/inherited.js";
16
6
  import instantiate from "./protocols/new.js";
17
- import packageNamespace from "./protocols/package.js";
18
7
  import scope from "./protocols/scope.js";
19
8
  import * as site from "./site/site.js";
20
9
  import * as text from "./text/text.js";
21
10
  import * as tree from "./tree/tree.js";
22
11
 
23
- let result;
12
+ let builtins;
24
13
 
25
14
  export default function builtinsShell() {
26
- if (!result) {
27
- const builtins = {
15
+ if (!builtins) {
16
+ builtins = {
17
+ // All JSE builtins
18
+ ...builtinsJse(),
19
+
20
+ // Old protocols to be deprecated
28
21
  "dev:": dev,
29
- "explore:": explore,
30
- "files:": files,
31
- "help:": help,
32
- "http:": http,
33
- "https:": https,
34
- "httpstree:": httpstree,
35
- "httptree:": httptree,
36
22
  "image:": image,
37
- "inherited:": inherited,
38
23
  "js:": jsGlobals,
39
24
  "new:": instantiate,
40
- "node:": node,
41
25
  "origami:": origami,
42
- "package:": packageNamespace,
43
26
  "scope:": scope,
44
27
  "site:": adjustReservedWords(site),
45
28
  "text:": text,
46
29
  "tree:": tree,
47
30
 
48
- // Handlers need to be exposed at top level
49
- ...handlerBuiltins(),
31
+ // For backward compat, include all methods at the top level
32
+ ...dev,
33
+ ...image,
34
+ ...origami,
35
+ ...adjustReservedWords(site),
36
+ ...text,
37
+ ...tree,
50
38
  };
51
-
52
- // For all builtins like `tree:keys`, add a shorthand `keys`.
53
- for (const [key, value] of Object.entries(builtins)) {
54
- const isNamespace = key.endsWith(":");
55
- if (isNamespace) {
56
- for (const [subKey, subValue] of Object.entries(value)) {
57
- // HACK: Skip description keys until we can make them all non-enumerable.
58
- if (subKey === "description") {
59
- continue;
60
- }
61
- if (subKey in builtins) {
62
- throw new Error(`Internal Origami error: Duplicate key: ${subKey}`);
63
- }
64
- builtins[subKey] = subValue;
65
- }
66
- }
67
- }
68
-
69
- result = new BuiltinsTree(builtins);
70
39
  }
71
40
 
72
- return result;
41
+ return builtins;
73
42
  }
74
43
 
75
44
  // Handle cases where a builtin name conflicts with a JS reserved word
@@ -21,7 +21,7 @@ export default async function documentObject(input, data) {
21
21
  }
22
22
 
23
23
  if (isPlainObject(input)) {
24
- text = input["@text"];
24
+ text = input["@text"] ?? input._body;
25
25
  inputData = input;
26
26
  } else {
27
27
  text = toString(input);
@@ -1,4 +1,4 @@
1
- import { handleExtension } from "@weborigami/language";
1
+ import { getHandlers, handleExtension } from "@weborigami/language";
2
2
 
3
3
  /**
4
4
  * Fetch the resource at the given href.
@@ -19,7 +19,8 @@ export default async function fetchAndHandleExtension(href) {
19
19
  const url = new URL(href);
20
20
  const filename = url.pathname.split("/").pop();
21
21
  if (this && filename) {
22
- buffer = await handleExtension(this, buffer, filename);
22
+ const handlers = getHandlers(this);
23
+ buffer = await handleExtension(this, buffer, filename, handlers);
23
24
  }
24
25
 
25
26
  return buffer;
@@ -94,8 +94,8 @@ export function toFunction(obj) {
94
94
  * @returns {string|null}
95
95
  */
96
96
  export function toString(object) {
97
- if (isPlainObject(object) && "@text" in object) {
98
- object = object["@text"];
97
+ if (isPlainObject(object) && ("@text" in object || "_body" in object)) {
98
+ object = object["@text"] ?? object._body;
99
99
  }
100
100
  return asyncTreeToString(object);
101
101
  }
@@ -1,6 +1,6 @@
1
1
  import { Tree, jsonKeys } from "@weborigami/async-tree";
2
2
  import { isTransformApplied, transformObject } from "../common/utilities.js";
3
- import index from "../site/index.js";
3
+ import indexPage from "../site/indexPage.js";
4
4
 
5
5
  /**
6
6
  * Wraps a tree (typically a SiteTree) to turn a standard site into an
@@ -32,7 +32,7 @@ export default function ExplorableSiteTransform(Base) {
32
32
  // The tree doesn't have the key; try the defaults.
33
33
  if (key === "index.html") {
34
34
  // This tree is both the function call target and the parameter.
35
- value = await index.call(this, this);
35
+ value = await indexPage.call(this, this);
36
36
  } else if (key === ".keys.json") {
37
37
  value = await jsonKeys.stringify(this);
38
38
  }
@@ -3,7 +3,7 @@ let frame;
3
3
 
4
4
  const modes = {
5
5
  Content: "",
6
- Index: "!index",
6
+ Index: "!indexPage",
7
7
  YAML: "!yaml",
8
8
  SVG: "!svg",
9
9
  };
@@ -15,7 +15,7 @@ function getPathFromHash() {
15
15
 
16
16
  function getModeFromLocation() {
17
17
  const href = document.location.href;
18
- const match = /[\/](?<command>\!(?:index|yaml|svg))$/.exec(href);
18
+ const match = /[\/](?<command>\!(?:indexPage|yaml|svg))$/.exec(href);
19
19
  const command = match?.groups?.command ?? "";
20
20
  const mode =
21
21
  Object.keys(modes).find((key) => modes[key] === command) ?? "Content";
@@ -4,8 +4,8 @@
4
4
  <meta charset="utf-8" />
5
5
  <meta name="viewport" content="width=device-width,initial-scale=1" />
6
6
  <title>Web Origami Explorer</title>
7
- <style>${ explore.css }</style>
8
- <script>${ explore.js.inline }</script>
7
+ <style>${ <explore.css> }</style>
8
+ <script>${ <explore.js.inline> }</script>
9
9
  </head>
10
10
  <body>
11
11
  <nav>
@@ -16,10 +16,10 @@
16
16
  <button id="buttonSVG">SVG</button>
17
17
  <button id="buttonYAML">YAML</button>
18
18
  </div>
19
- ${ map(scope, (scopeTree) => `
19
+ ${ Tree.map(scope, (scopeTree) => `
20
20
  <ul>
21
- <h2>${ scopeTree/name ?? "" }</h2>
22
- ${ map(scopeTree/keys, (key) => `
21
+ <h2>${ scopeTree.name ?? "" }</h2>
22
+ ${ Tree.map(scopeTree.keys, (key) => `
23
23
  <li>
24
24
  <a href="./!explore/${ key }" target="frame">${ key }</a>
25
25
  </li>
@@ -30,4 +30,4 @@
30
30
  <iframe id="frame" name="frame"></iframe>
31
31
  </body>
32
32
  </html>
33
- `
33
+ `
@@ -1,4 +1,3 @@
1
- import { merge } from "@weborigami/async-tree";
2
1
  import { compile } from "@weborigami/language";
3
2
  import builtinsShell from "../builtinsShell.js";
4
3
  import getConfig from "../cli/getConfig.js";
@@ -38,8 +37,11 @@ export default {
38
37
  // Compile the source code as an Origami program and evaluate it.
39
38
  const compiler = options.compiler ?? compile.program;
40
39
 
41
- const config = getConfig(parent);
42
- const globals = merge(options.globals ?? builtinsShell(), config);
40
+ const config = getConfig(parent) ?? {};
41
+ const globals = {
42
+ ...(options.globals ?? builtinsShell()),
43
+ ...config,
44
+ };
43
45
 
44
46
  const mode = options.mode ?? "shell";
45
47
  const fn = compiler(source, { globals, mode });
@@ -44,10 +44,10 @@ export default {
44
44
  globals = builtinsShell();
45
45
  }
46
46
 
47
- const mode = options.mode ?? "shell";
48
47
  const defineFn = compile.templateDocument(source, {
48
+ front: options.front,
49
49
  globals,
50
- mode,
50
+ mode: options.mode ?? "shell",
51
51
  });
52
52
 
53
53
  // Invoke the definition to get back the template function
@@ -135,17 +135,7 @@ new:
135
135
  description: Create instances of JavaScript classes
136
136
 
137
137
  node:
138
- description: Node.js classes and modules
139
- collection: true
140
- commands:
141
- Buffer:
142
- description: Node.js Buffer class
143
- path:
144
- description: Node.js path module
145
- process:
146
- description: Node.js process object
147
- url:
148
- description: Node.js URL module
138
+ description: Node.js modules
149
139
 
150
140
  origami:
151
141
  description: Perform general Origami language functions
@@ -14,5 +14,5 @@ import project from "./project.js";
14
14
  export default async function config(key) {
15
15
  const projectTree = await project.call(this);
16
16
  const projectConfig = projectTree.config;
17
- return key === undefined ? projectConfig : projectConfig.get(key);
17
+ return key === undefined ? projectConfig : projectConfig[key];
18
18
  }
@@ -1,7 +1,6 @@
1
1
  import {
2
2
  Tree,
3
3
  getRealmObjectPrototype,
4
- merge,
5
4
  toString,
6
5
  } from "@weborigami/async-tree";
7
6
  import { compile } from "@weborigami/language";
@@ -34,7 +33,12 @@ export default async function ori(
34
33
  const tree = this;
35
34
 
36
35
  const config = getConfig(tree);
37
- const globals = merge(builtinsShell(), config);
36
+ const globals = config
37
+ ? {
38
+ ...builtinsShell(),
39
+ ...config,
40
+ }
41
+ : builtinsShell();
38
42
 
39
43
  // Compile the expression. Avoid caching scope references so that, e.g.,
40
44
  // passing a function to the `watch` builtin will always look the current
@@ -1,5 +1,4 @@
1
1
  /** @typedef {import("@weborigami/types").AsyncTree} AsyncTree */
2
- import { merge } from "@weborigami/async-tree";
3
2
  import { OrigamiFiles } from "@weborigami/language";
4
3
  import assertTreeIsDefined from "../common/assertTreeIsDefined.js";
5
4
  import handlerBuiltins from "../handlers/handlerBuiltins.js";
@@ -29,6 +28,7 @@ export default async function project() {
29
28
 
30
29
  const dirname = process.cwd();
31
30
  const currentTree = new OrigamiFiles(dirname);
31
+ let config;
32
32
 
33
33
  // Search up the tree for the configuration file or package.json to determine
34
34
  // the project root.
@@ -38,10 +38,11 @@ export default async function project() {
38
38
  root = foundConfig.container;
39
39
  // Unpack Origami configuration file
40
40
  const buffer = foundConfig.value;
41
- root.config = await oriHandler.unpack(buffer, {
41
+ config = await oriHandler.unpack(buffer, {
42
42
  key: configFileName,
43
43
  parent: root,
44
44
  });
45
+ root.config = config;
45
46
  } else {
46
47
  // No Origami configuration file, look for package.json
47
48
  const foundPackageJson = await findAncestorFile(
@@ -58,7 +59,12 @@ export default async function project() {
58
59
  }
59
60
 
60
61
  // Merge config if present into handlers
61
- root.handlers = merge(handlerBuiltins(), root.config);
62
+ root.handlers = config
63
+ ? {
64
+ ...handlerBuiltins(),
65
+ ...configHandlers(config),
66
+ }
67
+ : handlerBuiltins();
62
68
 
63
69
  return root;
64
70
  }
@@ -90,3 +96,14 @@ async function findAncestorFile(start, fileName) {
90
96
  // Not found
91
97
  return null;
92
98
  }
99
+
100
+ // Return an object with all `.handler` keys from the config
101
+ function configHandlers(config) {
102
+ const handlers = {};
103
+ for (const key in config) {
104
+ if (key.endsWith(".handler")) {
105
+ handlers[key] = config[key];
106
+ }
107
+ }
108
+ return handlers;
109
+ }
@@ -1,6 +1,10 @@
1
1
  const parsers = {};
2
2
 
3
+ // TODO: Remove this deprecated function
3
4
  export default function regexMatch(text, regex) {
5
+ console.warn(
6
+ "warning: regexMatch is deprecated, use a JavaScript regular expression instead."
7
+ );
4
8
  if (!parsers[regex]) {
5
9
  const regexp = new RegExp(regex);
6
10
  parsers[regex] = (input) => input.match(regexp)?.groups;
@@ -13,12 +13,16 @@ import assertTreeIsDefined from "../common/assertTreeIsDefined.js";
13
13
  export default async function instantiate(...keys) {
14
14
  assertTreeIsDefined(this, "new:");
15
15
  let constructor;
16
+
16
17
  const scope = this ? scopeFn(this) : null;
17
18
  if (
18
19
  keys.length === 1 &&
19
20
  (typeof keys[0] === "object" || typeof keys[0] === "function")
20
21
  ) {
21
22
  constructor = keys[0];
23
+ } else if (globalThis[keys[0]]) {
24
+ // Backward compat
25
+ constructor = globalThis[keys[0]];
22
26
  } else if (scope) {
23
27
  constructor = await Tree.traverseOrThrow(scope, ...keys);
24
28
  } else {
@@ -0,0 +1,13 @@
1
+ import { trailingSlash, Tree } from "@weborigami/async-tree";
2
+
3
+ /**
4
+ * The node: protocol does a dynamic import from the `node:` namespace.
5
+ *
6
+ * @param {string[]} keys
7
+ */
8
+ export default async function node(...keys) {
9
+ const key = keys.shift();
10
+ const normalized = trailingSlash.remove(key);
11
+ const module = await import(`node:${normalized}`);
12
+ return keys.length > 0 ? Tree.traverse(module, ...keys) : module;
13
+ }
@@ -10,7 +10,7 @@ import { getDescriptor } from "../common/utilities.js";
10
10
  * @param {Treelike} [treelike]
11
11
  * @param {string} [basePath]
12
12
  */
13
- export default async function index(treelike, basePath) {
13
+ export default async function indexPage(treelike, basePath) {
14
14
  const tree = await getTreeArgument(this, arguments, treelike, "site:index");
15
15
  const keys = Array.from(await tree.keys());
16
16
 
package/src/site/site.js CHANGED
@@ -1,4 +1,4 @@
1
- export { default as index } from "./index.js";
1
+ export { default as indexPage } from "./indexPage.js";
2
2
  export { default as jsonKeys } from "./jsonKeys.js";
3
3
  export { default as redirect } from "./redirect.js";
4
4
  export { default as rss } from "./rss.js";
@@ -5,7 +5,7 @@ import { oriHandler } from "../handlers/handlers.js";
5
5
 
6
6
  const templateText = `(urls) => \`<?xml version="1.0" encoding="UTF-8"?>
7
7
  <urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
8
- \${ map(urls, (url) => \` <url>
8
+ \${ Tree.map(urls, (url) => \` <url>
9
9
  <loc>\${ url }</loc>
10
10
  </url>
11
11
  \`) }</urlset>
@@ -1,7 +1,7 @@
1
1
  import { Tree, jsonKeys } from "@weborigami/async-tree";
2
2
  import getTreeArgument from "../common/getTreeArgument.js";
3
3
  import { transformObject } from "../common/utilities.js";
4
- import index from "./index.js";
4
+ import index from "./indexPage.js";
5
5
 
6
6
  /**
7
7
  * Expose common static keys (index.html, .keys.json) for a tree.
@@ -1,12 +1,7 @@
1
- import {
2
- isUnpackable,
3
- ObjectTree,
4
- symbols,
5
- toString,
6
- } from "@weborigami/async-tree";
1
+ import { isUnpackable, symbols, toString } from "@weborigami/async-tree";
7
2
  import assertTreeIsDefined from "../common/assertTreeIsDefined.js";
8
3
  import documentObject from "../common/documentObject.js";
9
- import { oridocumentHandler } from "../handlers/handlers.js";
4
+ import { jsedocumentHandler } from "../handlers/handlers.js";
10
5
 
11
6
  /**
12
7
  * Inline any Origami expressions found inside ${...} placeholders in the input
@@ -25,8 +20,11 @@ export default async function inline(input) {
25
20
  if (isUnpackable(input)) {
26
21
  input = await input.unpack();
27
22
  }
28
- const inputIsDocument = input["@text"] !== undefined;
29
- const origami = inputIsDocument ? input["@text"] : toString(input);
23
+ const inputIsDocument =
24
+ input["@text"] !== undefined || input._body !== undefined;
25
+ const origami = inputIsDocument
26
+ ? input["@text"] ?? input._body
27
+ : toString(input);
30
28
  if (origami === null) {
31
29
  return undefined;
32
30
  }
@@ -36,15 +34,22 @@ export default async function inline(input) {
36
34
  /** @type {any} */ (input)[symbols.parent] ??
37
35
  this;
38
36
 
39
- let extendedParent = parent;
37
+ let front;
40
38
  if (inputIsDocument) {
41
- extendedParent = new ObjectTree(input);
42
- extendedParent.parent = parent;
39
+ // Collect all document properties except the body
40
+ front = Object.fromEntries(
41
+ Object.entries(input).filter(
42
+ ([key]) => key !== "@text" && key !== "_body"
43
+ )
44
+ );
45
+ } else {
46
+ front = null;
43
47
  }
44
48
 
45
49
  // @ts-ignore
46
- let result = await oridocumentHandler.unpack(input, {
47
- parent: extendedParent,
50
+ let result = await jsedocumentHandler.unpack(origami, {
51
+ front,
52
+ parent,
48
53
  });
49
54
 
50
55
  if (result instanceof Function) {
@@ -47,7 +47,8 @@ export default async function mdHtml(input) {
47
47
  if (isUnpackable(input)) {
48
48
  input = await input.unpack();
49
49
  }
50
- const inputIsDocument = input["@text"] !== undefined;
50
+ const inputIsDocument =
51
+ input["@text"] !== undefined || input._body !== undefined;
51
52
  const markdown = toString(input);
52
53
  if (markdown === null) {
53
54
  throw new Error("mdHtml: The provided input couldn't be treated as text.");
package/src/node.js DELETED
@@ -1,22 +0,0 @@
1
- import path from "node:path";
2
- import process from "node:process";
3
- import url from "node:url";
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
- enumerable: true,
11
- get: function () {
12
- return Object.assign({}, process.env);
13
- },
14
- },
15
- });
16
-
17
- export default {
18
- Buffer,
19
- path,
20
- process: patchedProcess,
21
- url,
22
- };