@weborigami/language 0.5.5 → 0.5.7
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/index.ts +16 -6
- package/main.js +9 -4
- package/package.json +4 -3
- package/src/compiler/compile.js +10 -4
- package/src/compiler/optimize.js +115 -97
- package/src/compiler/origami.pegjs +1 -4
- package/src/compiler/parse.js +568 -588
- package/src/compiler/parserHelpers.js +2 -2
- package/src/handlers/css_handler.js +7 -0
- package/src/handlers/csv_handler.js +129 -0
- package/src/handlers/handlers.js +33 -0
- package/src/handlers/htm_handler.js +2 -0
- package/src/handlers/html_handler.js +7 -0
- package/src/handlers/jpeg_handler.js +62 -0
- package/src/handlers/jpg_handler.js +2 -0
- package/src/handlers/js_handler.js +51 -0
- package/src/handlers/json_handler.js +26 -0
- package/src/handlers/md_handler.js +7 -0
- package/src/handlers/mjs_handler.js +2 -0
- package/src/handlers/ori_handler.js +52 -0
- package/src/handlers/oridocument_handler.js +77 -0
- package/src/handlers/parseFrontMatter.js +16 -0
- package/src/handlers/ts_handler.js +1 -0
- package/src/handlers/txt_handler.js +108 -0
- package/src/handlers/wasm_handler.js +15 -0
- package/src/handlers/xhtml_handler.js +2 -0
- package/src/handlers/yaml_handler.js +33 -0
- package/src/handlers/yml_handler.js +2 -0
- package/src/project/builtins.js +5 -0
- package/src/project/coreGlobals.js +17 -0
- package/src/{runtime → project}/jsGlobals.js +3 -1
- package/src/project/projectConfig.js +36 -0
- package/src/project/projectGlobals.js +19 -0
- package/src/project/projectRoot.js +59 -0
- package/src/protocols/constructHref.js +20 -0
- package/src/protocols/constructSiteTree.js +26 -0
- package/src/protocols/explore.js +14 -0
- package/src/protocols/fetchAndHandleExtension.js +25 -0
- package/src/protocols/files.js +26 -0
- package/src/protocols/http.js +15 -0
- package/src/protocols/https.js +15 -0
- package/src/protocols/httpstree.js +14 -0
- package/src/protocols/httptree.js +14 -0
- package/src/protocols/node.js +13 -0
- package/src/protocols/package.js +67 -0
- package/src/protocols/protocolGlobals.js +12 -0
- package/src/protocols/protocols.js +8 -0
- package/src/runtime/EventTargetMixin.js +1 -1
- package/src/runtime/HandleExtensionsTransform.js +3 -12
- package/src/runtime/ImportModulesMixin.js +4 -10
- package/src/runtime/InvokeFunctionsTransform.js +1 -1
- package/src/runtime/evaluate.js +15 -8
- package/src/runtime/expressionFunction.js +5 -7
- package/src/runtime/expressionObject.js +10 -20
- package/src/runtime/functionResultsMap.js +1 -3
- package/src/runtime/{handlers.js → handleExtension.js} +13 -11
- package/src/runtime/mergeTrees.js +1 -8
- package/src/runtime/ops.js +83 -90
- package/test/compiler/compile.test.js +20 -19
- package/test/compiler/optimize.test.js +60 -25
- package/test/compiler/parse.test.js +4 -4
- package/test/generator/oriEval.js +4 -5
- package/test/handlers/csv.handler.test.js +36 -0
- package/test/handlers/fixtures/add.wasm +0 -0
- package/test/handlers/fixtures/exif.jpeg +0 -0
- package/test/handlers/fixtures/frontMatter.md +5 -0
- package/test/handlers/fixtures/list.js +4 -0
- package/test/handlers/fixtures/multiple.js +4 -0
- package/test/handlers/fixtures/obj.js +3 -0
- package/test/handlers/fixtures/site.ori +5 -0
- package/test/handlers/fixtures/string.js +1 -0
- package/test/handlers/fixtures/tag.yaml +5 -0
- package/test/handlers/fixtures/test.ori +9 -0
- package/test/handlers/jpeg.handler.test.js +18 -0
- package/test/handlers/js.handler.test.js +46 -0
- package/test/handlers/json.handler.test.js +14 -0
- package/test/handlers/ori.handler.test.js +87 -0
- package/test/handlers/oridocument.handler.test.js +68 -0
- package/test/handlers/txt.handler.test.js +41 -0
- package/test/handlers/wasm.handler.test.js +20 -0
- package/test/handlers/yaml.handler.test.js +17 -0
- package/test/project/fixtures/withConfig/config.ori +4 -0
- package/test/project/fixtures/withConfig/subfolder/greet.js +1 -0
- package/test/project/fixtures/withPackageJson/package.json +0 -0
- package/test/project/jsGlobals.test.js +21 -0
- package/test/project/projectConfig.test.js +28 -0
- package/test/project/projectRoot.test.js +40 -0
- package/test/protocols/package.test.js +11 -0
- package/test/runtime/evaluate.test.js +26 -42
- package/test/runtime/expressionObject.test.js +16 -20
- package/test/runtime/{handlers.test.js → handleExtension.test.js} +4 -20
- package/test/runtime/jsGlobals.test.js +4 -6
- package/test/runtime/mergeTrees.test.js +2 -4
- package/test/runtime/ops.test.js +66 -68
- package/src/runtime/getHandlers.js +0 -10
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { Tree } from "@weborigami/async-tree";
|
|
2
|
+
import protocolGlobals from "../protocols/protocolGlobals.js";
|
|
3
|
+
import * as protocols from "../protocols/protocols.js";
|
|
4
|
+
import builtins from "./builtins.js";
|
|
5
|
+
import jsGlobals from "./jsGlobals.js";
|
|
6
|
+
|
|
7
|
+
export default async function coreGlobals() {
|
|
8
|
+
const handlerGlobals = await import("../handlers/handlers.js");
|
|
9
|
+
return {
|
|
10
|
+
...jsGlobals,
|
|
11
|
+
Tree,
|
|
12
|
+
Protocol: protocols,
|
|
13
|
+
...protocolGlobals,
|
|
14
|
+
...handlerGlobals,
|
|
15
|
+
...builtins,
|
|
16
|
+
};
|
|
17
|
+
}
|
|
@@ -184,6 +184,7 @@ async function importWrapper(modulePath) {
|
|
|
184
184
|
const filePath = path.resolve(current.path, modulePath);
|
|
185
185
|
return import(filePath);
|
|
186
186
|
}
|
|
187
|
+
importWrapper.containerAsTarget = true;
|
|
187
188
|
|
|
188
189
|
/**
|
|
189
190
|
* Some JavaScript globals like Promise have static methods like Promise.all
|
|
@@ -219,10 +220,11 @@ function bindStaticMethods(obj) {
|
|
|
219
220
|
}
|
|
220
221
|
|
|
221
222
|
let extended;
|
|
222
|
-
// A regular object or an oddball like Proxy with no prototype
|
|
223
223
|
if (typeof obj === "object" || !obj.prototype) {
|
|
224
|
+
// A regular object or an oddball like Proxy with no prototype
|
|
224
225
|
extended = Object.create(obj);
|
|
225
226
|
} else {
|
|
227
|
+
// A function, possibly a constructor called with or without `new`
|
|
226
228
|
/** @this {any} */
|
|
227
229
|
extended = function (...args) {
|
|
228
230
|
const calledWithNew = this instanceof extended;
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { FileTree, toString } from "@weborigami/async-tree";
|
|
2
|
+
import ori_handler from "../handlers/ori_handler.js";
|
|
3
|
+
import coreGlobals from "./coreGlobals.js";
|
|
4
|
+
import projectRoot from "./projectRoot.js";
|
|
5
|
+
|
|
6
|
+
const mapPathToConfig = new Map();
|
|
7
|
+
|
|
8
|
+
export default async function config(dir = process.cwd()) {
|
|
9
|
+
const root = await projectRoot(dir);
|
|
10
|
+
|
|
11
|
+
const rootPath = root.path;
|
|
12
|
+
const cached = mapPathToConfig.get(rootPath);
|
|
13
|
+
if (cached) {
|
|
14
|
+
return cached;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
// Use a plain FileTree to avoid loading extension handlers
|
|
18
|
+
const rootFileTree = new FileTree(rootPath);
|
|
19
|
+
const configBuffer = await rootFileTree.get("config.ori");
|
|
20
|
+
let configObject = {};
|
|
21
|
+
if (configBuffer) {
|
|
22
|
+
const configText = toString(configBuffer);
|
|
23
|
+
if (configText) {
|
|
24
|
+
// Config uses only core globals (we're defining the config)
|
|
25
|
+
const globals = await coreGlobals();
|
|
26
|
+
// Evaluate the config file to obtain the configuration object
|
|
27
|
+
configObject = await ori_handler.unpack(configText, {
|
|
28
|
+
globals,
|
|
29
|
+
parent: root,
|
|
30
|
+
});
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
mapPathToConfig.set(rootPath, configObject);
|
|
35
|
+
return configObject;
|
|
36
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import coreGlobals from "./coreGlobals.js";
|
|
2
|
+
import projectConfig from "./projectConfig.js";
|
|
3
|
+
|
|
4
|
+
let globals;
|
|
5
|
+
|
|
6
|
+
// Core globals plus project config
|
|
7
|
+
export default async function projectGlobals() {
|
|
8
|
+
if (!globals) {
|
|
9
|
+
// Start with core globals
|
|
10
|
+
globals = await coreGlobals();
|
|
11
|
+
// Now get config. The config.ori file may require access to globals,
|
|
12
|
+
// which will obtain the core globals set above. Once we've got the
|
|
13
|
+
// config, we add it to the globals.
|
|
14
|
+
const config = await projectConfig();
|
|
15
|
+
Object.assign(globals, config);
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
return globals;
|
|
19
|
+
}
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import { FileTree } from "@weborigami/async-tree";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
import OrigamiFiles from "../runtime/OrigamiFiles.js";
|
|
4
|
+
|
|
5
|
+
const configFileName = "config.ori";
|
|
6
|
+
const packageFileName = "package.json";
|
|
7
|
+
|
|
8
|
+
const mapPathToRoot = new Map();
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Return an OrigamiFiles object for the current project.
|
|
12
|
+
*
|
|
13
|
+
* This searches the current directory and its ancestors for an Origami file
|
|
14
|
+
* called `config.ori`. If an Origami configuration file is found, the
|
|
15
|
+
* containing folder is considered to be the project root.
|
|
16
|
+
*
|
|
17
|
+
* Otherwise, this looks for a package.json file to determine the project root.
|
|
18
|
+
* If no package.json is found, the current folder is used as the project root.
|
|
19
|
+
*
|
|
20
|
+
* @typedef {import("@weborigami/types").AsyncTree} AsyncTree
|
|
21
|
+
*
|
|
22
|
+
* @param {string} [dirname]
|
|
23
|
+
*/
|
|
24
|
+
export default async function projectRoot(dirname = process.cwd()) {
|
|
25
|
+
const cached = mapPathToRoot.get(dirname);
|
|
26
|
+
if (cached) {
|
|
27
|
+
return cached;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
let root;
|
|
31
|
+
let value;
|
|
32
|
+
// Use a plain FileTree to avoid loading extension handlers
|
|
33
|
+
const currentTree = new FileTree(dirname);
|
|
34
|
+
// Try looking for config file
|
|
35
|
+
value = await currentTree.get(configFileName);
|
|
36
|
+
if (value) {
|
|
37
|
+
// Found config file
|
|
38
|
+
root = new OrigamiFiles(currentTree.path);
|
|
39
|
+
} else {
|
|
40
|
+
// Try looking for package.json
|
|
41
|
+
value = await currentTree.get(packageFileName);
|
|
42
|
+
if (value) {
|
|
43
|
+
// Found package.json
|
|
44
|
+
root = new OrigamiFiles(currentTree.path);
|
|
45
|
+
} else {
|
|
46
|
+
// Move up a folder and try again
|
|
47
|
+
const parentPath = path.dirname(dirname);
|
|
48
|
+
if (parentPath !== dirname) {
|
|
49
|
+
root = await projectRoot(parentPath);
|
|
50
|
+
} else {
|
|
51
|
+
// At filesystem root, use current working directory
|
|
52
|
+
root = new OrigamiFiles(process.cwd());
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
mapPathToRoot.set(dirname, root);
|
|
58
|
+
return root;
|
|
59
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { pathFromKeys } from "@weborigami/async-tree";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Given a protocol, a host, and a list of keys, construct an href.
|
|
5
|
+
*
|
|
6
|
+
* @param {string} protocol
|
|
7
|
+
* @param {string} host
|
|
8
|
+
* @param {string[]} keys
|
|
9
|
+
*/
|
|
10
|
+
export default function constructHref(protocol, host, ...keys) {
|
|
11
|
+
const path = pathFromKeys(keys);
|
|
12
|
+
let href = [host, path].join("/");
|
|
13
|
+
if (!href.startsWith(protocol)) {
|
|
14
|
+
if (!href.startsWith("//")) {
|
|
15
|
+
href = `//${href}`;
|
|
16
|
+
}
|
|
17
|
+
href = `${protocol}${href}`;
|
|
18
|
+
}
|
|
19
|
+
return href;
|
|
20
|
+
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { trailingSlash } from "@weborigami/async-tree";
|
|
2
|
+
import HandleExtensionsTransform from "../runtime/HandleExtensionsTransform.js";
|
|
3
|
+
import constructHref from "./constructHref.js";
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Given a protocol, a host, and a list of keys, construct an href.
|
|
7
|
+
*
|
|
8
|
+
* @typedef {import("@weborigami/types").AsyncTree} AsyncTree
|
|
9
|
+
*
|
|
10
|
+
* @param {string} protocol
|
|
11
|
+
* @param {import("../../index.ts").Constructor<AsyncTree>} treeClass
|
|
12
|
+
* @param {string} host
|
|
13
|
+
* @param {string[]} keys
|
|
14
|
+
*/
|
|
15
|
+
export default function constructSiteTree(protocol, treeClass, host, ...keys) {
|
|
16
|
+
// If the last key doesn't end in a slash, remove it for now.
|
|
17
|
+
let lastKey;
|
|
18
|
+
if (keys.length > 0 && keys.at(-1) && !trailingSlash.has(keys.at(-1))) {
|
|
19
|
+
lastKey = keys.pop();
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
const href = constructHref(protocol, host, ...keys);
|
|
23
|
+
let result = new (HandleExtensionsTransform(treeClass))(href);
|
|
24
|
+
|
|
25
|
+
return lastKey ? result.get(lastKey) : result;
|
|
26
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { ExplorableSiteTree } from "@weborigami/async-tree";
|
|
2
|
+
import constructSiteTree from "./constructSiteTree.js";
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* A site tree with JSON Keys via HTTPS.
|
|
6
|
+
*
|
|
7
|
+
* @typedef {import("@weborigami/types").AsyncTree} AsyncTree
|
|
8
|
+
*
|
|
9
|
+
* @param {string} host
|
|
10
|
+
* @param {...string} keys
|
|
11
|
+
*/
|
|
12
|
+
export default function explore(host, ...keys) {
|
|
13
|
+
return constructSiteTree("https:", ExplorableSiteTree, host, ...keys);
|
|
14
|
+
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import handleExtension from "../runtime/handleExtension.js";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Fetch the resource at the given href.
|
|
5
|
+
*
|
|
6
|
+
* @typedef {import("@weborigami/types").AsyncTree} AsyncTree
|
|
7
|
+
*
|
|
8
|
+
* @param {string} href
|
|
9
|
+
*/
|
|
10
|
+
export default async function fetchAndHandleExtension(href) {
|
|
11
|
+
const response = await fetch(href);
|
|
12
|
+
if (!response.ok) {
|
|
13
|
+
return undefined;
|
|
14
|
+
}
|
|
15
|
+
let buffer = await response.arrayBuffer();
|
|
16
|
+
|
|
17
|
+
// Attach any loader defined for the file type.
|
|
18
|
+
const url = new URL(href);
|
|
19
|
+
const filename = url.pathname.split("/").pop();
|
|
20
|
+
if (filename) {
|
|
21
|
+
buffer = await handleExtension(buffer, filename);
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
return buffer;
|
|
25
|
+
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import os from "node:os";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
import process from "node:process";
|
|
4
|
+
import OrigamiFiles from "../runtime/OrigamiFiles.js";
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* @typedef {import("@weborigami/types").AsyncTree} AsyncTree
|
|
8
|
+
*
|
|
9
|
+
* @param {string[]} keys
|
|
10
|
+
*/
|
|
11
|
+
export default async function files(...keys) {
|
|
12
|
+
// If path begins with `~`, treat it relative to the home directory.
|
|
13
|
+
// Otherwise, treat it relative to the current working directory.
|
|
14
|
+
let relativePath = keys.join(path.sep);
|
|
15
|
+
let basePath;
|
|
16
|
+
if (relativePath.startsWith("~")) {
|
|
17
|
+
basePath = os.homedir();
|
|
18
|
+
relativePath = relativePath.slice(2);
|
|
19
|
+
} else {
|
|
20
|
+
basePath = process.cwd();
|
|
21
|
+
}
|
|
22
|
+
const resolved = path.resolve(basePath, relativePath);
|
|
23
|
+
|
|
24
|
+
const result = new OrigamiFiles(resolved);
|
|
25
|
+
return result;
|
|
26
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import constructHref from "./constructHref.js";
|
|
2
|
+
import fetchAndHandleExtension from "./fetchAndHandleExtension.js";
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Retrieve the indicated web resource via HTTP.
|
|
6
|
+
*
|
|
7
|
+
* @typedef {import("@weborigami/types").AsyncTree} AsyncTree
|
|
8
|
+
*
|
|
9
|
+
* @param {string} host
|
|
10
|
+
* @param {...string} keys
|
|
11
|
+
*/
|
|
12
|
+
export default async function http(host, ...keys) {
|
|
13
|
+
const href = constructHref("http:", host, ...keys);
|
|
14
|
+
return fetchAndHandleExtension(href);
|
|
15
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import constructHref from "./constructHref.js";
|
|
2
|
+
import fetchAndHandleExtension from "./fetchAndHandleExtension.js";
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Retrieve the indicated web resource via HTTPS.
|
|
6
|
+
*
|
|
7
|
+
* @typedef {import("@weborigami/types").AsyncTree} AsyncTree
|
|
8
|
+
*
|
|
9
|
+
* @param {string} host
|
|
10
|
+
* @param {...string} keys
|
|
11
|
+
*/
|
|
12
|
+
export default async function https(host, ...keys) {
|
|
13
|
+
const href = constructHref("https:", host, ...keys);
|
|
14
|
+
return fetchAndHandleExtension(href);
|
|
15
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { SiteTree } from "@weborigami/async-tree";
|
|
2
|
+
import constructSiteTree from "./constructSiteTree.js";
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Return a website tree via HTTPS.
|
|
6
|
+
*
|
|
7
|
+
* @typedef {import("@weborigami/types").AsyncTree} AsyncTree
|
|
8
|
+
*
|
|
9
|
+
* @param {string} host
|
|
10
|
+
* @param {...string} keys
|
|
11
|
+
*/
|
|
12
|
+
export default function httpstree(host, ...keys) {
|
|
13
|
+
return constructSiteTree("https:", SiteTree, host, ...keys);
|
|
14
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { SiteTree } from "@weborigami/async-tree";
|
|
2
|
+
import constructSiteTree from "./constructSiteTree.js";
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Return a website tree via HTTP.
|
|
6
|
+
*
|
|
7
|
+
* @typedef {import("@weborigami/types").AsyncTree} AsyncTree
|
|
8
|
+
*
|
|
9
|
+
* @param {string} host
|
|
10
|
+
* @param {...string} keys
|
|
11
|
+
*/
|
|
12
|
+
export default function httptree(host, ...keys) {
|
|
13
|
+
return constructSiteTree("http:", SiteTree, host, ...keys);
|
|
14
|
+
}
|
|
@@ -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
|
+
}
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import { Tree, keysFromPath } from "@weborigami/async-tree";
|
|
2
|
+
import projectRoot from "../project/projectRoot.js";
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* @param {string[]} keys
|
|
6
|
+
*/
|
|
7
|
+
export default async function packageNamespace(...keys) {
|
|
8
|
+
const parent = await projectRoot();
|
|
9
|
+
|
|
10
|
+
let name = keys.shift();
|
|
11
|
+
let organization;
|
|
12
|
+
if (name?.startsWith("@")) {
|
|
13
|
+
// First key is an npm organization
|
|
14
|
+
organization = name;
|
|
15
|
+
if (keys.length === 0) {
|
|
16
|
+
// Return a function that will process the next key
|
|
17
|
+
return async (name, ...keys) =>
|
|
18
|
+
getPackage(parent, organization, name, keys);
|
|
19
|
+
}
|
|
20
|
+
name = keys.shift();
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
return getPackage(parent, organization, name, keys);
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
async function getPackage(parent, organization, name, keys) {
|
|
27
|
+
const packagePath = ["node_modules"];
|
|
28
|
+
if (organization) {
|
|
29
|
+
packagePath.push(organization);
|
|
30
|
+
}
|
|
31
|
+
packagePath.push(name);
|
|
32
|
+
|
|
33
|
+
const parentScope = await Tree.scope(parent);
|
|
34
|
+
const packageRoot = await Tree.traverse(
|
|
35
|
+
// @ts-ignore
|
|
36
|
+
parentScope,
|
|
37
|
+
...packagePath
|
|
38
|
+
);
|
|
39
|
+
|
|
40
|
+
if (!packageRoot) {
|
|
41
|
+
throw new Error(`Can't find ${packagePath.join("/")}`);
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
const mainPath = await Tree.traverse(packageRoot, "package.json", "main");
|
|
45
|
+
if (!mainPath) {
|
|
46
|
+
throw new Error(
|
|
47
|
+
`node_modules/${keys.join(
|
|
48
|
+
"/"
|
|
49
|
+
)} doesn't contain a package.json with a "main" entry.`
|
|
50
|
+
);
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
const mainKeys = keysFromPath(mainPath);
|
|
54
|
+
const mainContainerKeys = mainKeys.slice(0, -1);
|
|
55
|
+
const mainFileName = mainKeys[mainKeys.length - 1];
|
|
56
|
+
const mainContainer = await Tree.traverse(packageRoot, ...mainContainerKeys);
|
|
57
|
+
const packageExports = await mainContainer.import(mainFileName);
|
|
58
|
+
|
|
59
|
+
let result =
|
|
60
|
+
"default" in packageExports ? packageExports.default : packageExports;
|
|
61
|
+
|
|
62
|
+
if (keys.length > 0) {
|
|
63
|
+
result = await Tree.traverse(result, ...keys);
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
return result;
|
|
67
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import * as protocols from "./protocols.js";
|
|
2
|
+
|
|
3
|
+
export default {
|
|
4
|
+
"explore:": protocols.explore,
|
|
5
|
+
"files:": protocols.files,
|
|
6
|
+
"http:": protocols.http,
|
|
7
|
+
"https:": protocols.https,
|
|
8
|
+
"httpstree:": protocols.httpstree,
|
|
9
|
+
"httptree:": protocols.httptree,
|
|
10
|
+
"node:": protocols.node,
|
|
11
|
+
"package:": protocols.package,
|
|
12
|
+
};
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
export { default as explore } from "./explore.js";
|
|
2
|
+
export { default as files } from "./files.js";
|
|
3
|
+
export { default as http } from "./http.js";
|
|
4
|
+
export { default as https } from "./https.js";
|
|
5
|
+
export { default as httpstree } from "./httpstree.js";
|
|
6
|
+
export { default as httptree } from "./httptree.js";
|
|
7
|
+
export { default as node } from "./node.js";
|
|
8
|
+
export { default as package } from "./package.js";
|
|
@@ -1,5 +1,4 @@
|
|
|
1
|
-
import
|
|
2
|
-
import { handleExtension } from "./handlers.js";
|
|
1
|
+
import handleExtension from "./handleExtension.js";
|
|
3
2
|
|
|
4
3
|
/**
|
|
5
4
|
* @typedef {import("@weborigami/types").AsyncTree} AsyncTree
|
|
@@ -9,18 +8,10 @@ import { handleExtension } from "./handlers.js";
|
|
|
9
8
|
* @param {AsyncTreeConstructor} Base
|
|
10
9
|
*/
|
|
11
10
|
export default function HandleExtensionsTransform(Base) {
|
|
12
|
-
return class
|
|
13
|
-
constructor(...args) {
|
|
14
|
-
super(...args);
|
|
15
|
-
|
|
16
|
-
// Callers should set this to the set of supported extension handlers
|
|
17
|
-
this.handlers = null;
|
|
18
|
-
}
|
|
19
|
-
|
|
11
|
+
return class HandleExtensions extends Base {
|
|
20
12
|
async get(key) {
|
|
21
13
|
const value = await super.get(key);
|
|
22
|
-
|
|
23
|
-
return handleExtension(this, value, key, handlers);
|
|
14
|
+
return handleExtension(value, key, this);
|
|
24
15
|
}
|
|
25
16
|
};
|
|
26
17
|
}
|
|
@@ -20,9 +20,9 @@ export default function ImportModulesMixin(Base) {
|
|
|
20
20
|
fileUrl.href + `?cacheBust=${moduleCache.getTimestamp()}`;
|
|
21
21
|
|
|
22
22
|
// Try to load the module.
|
|
23
|
-
let
|
|
23
|
+
let object;
|
|
24
24
|
try {
|
|
25
|
-
|
|
25
|
+
object = await import(modulePath);
|
|
26
26
|
} catch (/** @type {any} */ error) {
|
|
27
27
|
if (error.code !== "ERR_MODULE_NOT_FOUND") {
|
|
28
28
|
throw error;
|
|
@@ -48,14 +48,8 @@ export default function ImportModulesMixin(Base) {
|
|
|
48
48
|
throw new SyntaxError(message);
|
|
49
49
|
}
|
|
50
50
|
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
return obj.default;
|
|
54
|
-
} else {
|
|
55
|
-
// Module with multiple named exports. Cast from a module namespace
|
|
56
|
-
// object to a plain object.
|
|
57
|
-
return { ...obj };
|
|
58
|
-
}
|
|
51
|
+
// Cast from an exotic module namespace object to a plain object.
|
|
52
|
+
return { ...object };
|
|
59
53
|
}
|
|
60
54
|
};
|
|
61
55
|
}
|
|
@@ -13,7 +13,7 @@ export default function InvokeFunctionsTransform(Base) {
|
|
|
13
13
|
async get(key) {
|
|
14
14
|
let value = await super.get(key);
|
|
15
15
|
if (typeof value === "function") {
|
|
16
|
-
value = await value
|
|
16
|
+
value = await value();
|
|
17
17
|
|
|
18
18
|
if (Tree.isAsyncTree(value) && !value.parent) {
|
|
19
19
|
value.parent = this;
|
package/src/runtime/evaluate.js
CHANGED
|
@@ -1,18 +1,17 @@
|
|
|
1
1
|
import { Tree, isUnpackable } from "@weborigami/async-tree";
|
|
2
|
-
import { displayWarning, symbols } from "@weborigami/language";
|
|
3
2
|
import codeFragment from "./codeFragment.js";
|
|
3
|
+
import { displayWarning } from "./errors.js";
|
|
4
|
+
import * as symbols from "./symbols.js";
|
|
4
5
|
|
|
5
6
|
/**
|
|
6
7
|
* Evaluate the given code and return the result.
|
|
7
8
|
*
|
|
8
9
|
* `this` should be the tree used as the context for the evaluation.
|
|
9
10
|
*
|
|
10
|
-
* @this {import("@weborigami/types").AsyncTree|null}
|
|
11
11
|
* @param {import("../../index.ts").AnnotatedCode} code
|
|
12
|
+
* @param {import("../../index.ts").RuntimeState} [state]
|
|
12
13
|
*/
|
|
13
|
-
export default async function evaluate(code) {
|
|
14
|
-
const tree = this;
|
|
15
|
-
|
|
14
|
+
export default async function evaluate(code, state = {}) {
|
|
16
15
|
if (!(code instanceof Array)) {
|
|
17
16
|
// Simple scalar; return as is.
|
|
18
17
|
return code;
|
|
@@ -25,7 +24,7 @@ export default async function evaluate(code) {
|
|
|
25
24
|
} else {
|
|
26
25
|
// Evaluate each instruction in the code.
|
|
27
26
|
evaluated = await Promise.all(
|
|
28
|
-
code.map((instruction) => evaluate
|
|
27
|
+
code.map((instruction) => evaluate(instruction, state))
|
|
29
28
|
);
|
|
30
29
|
}
|
|
31
30
|
|
|
@@ -46,13 +45,21 @@ export default async function evaluate(code) {
|
|
|
46
45
|
fn = await fn.unpack();
|
|
47
46
|
}
|
|
48
47
|
|
|
48
|
+
if (fn.needsState) {
|
|
49
|
+
// The function is an op that wants the runtime state
|
|
50
|
+
args.push(state);
|
|
51
|
+
} else if (fn.containerAsTarget && state.container) {
|
|
52
|
+
// The function wants the code's container as the `this` target
|
|
53
|
+
fn = fn.bind(state.container);
|
|
54
|
+
}
|
|
55
|
+
|
|
49
56
|
// Execute the function or traverse the tree.
|
|
50
57
|
let result;
|
|
51
58
|
try {
|
|
52
59
|
result =
|
|
53
60
|
fn instanceof Function
|
|
54
|
-
? await fn
|
|
55
|
-
: await Tree.traverseOrThrow
|
|
61
|
+
? await fn(...args) // Invoke the function
|
|
62
|
+
: await Tree.traverseOrThrow(fn, ...args); // Traverse the tree.
|
|
56
63
|
} catch (/** @type {any} */ error) {
|
|
57
64
|
if (!error.location) {
|
|
58
65
|
// Attach the location of the code we tried to evaluate.
|
|
@@ -6,15 +6,13 @@ import { evaluate } from "./internal.js";
|
|
|
6
6
|
* Given parsed Origami code, return a function that executes that code.
|
|
7
7
|
*
|
|
8
8
|
* @param {import("../../index.js").AnnotatedCode} code - parsed Origami expression
|
|
9
|
-
* @param {
|
|
9
|
+
* @param {AsyncTree} parent - the parent tree in which the code is running
|
|
10
10
|
*/
|
|
11
|
-
export function createExpressionFunction(code,
|
|
12
|
-
/** @this {AsyncTree|null} */
|
|
11
|
+
export function createExpressionFunction(code, parent) {
|
|
13
12
|
async function fn() {
|
|
14
|
-
return evaluate
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
Object.defineProperty(fn, "name", { value: name });
|
|
13
|
+
return evaluate(code, {
|
|
14
|
+
container: parent,
|
|
15
|
+
});
|
|
18
16
|
}
|
|
19
17
|
fn.code = code;
|
|
20
18
|
fn.toString = () => code.location.source.text;
|
|
@@ -6,8 +6,7 @@ import {
|
|
|
6
6
|
trailingSlash,
|
|
7
7
|
Tree,
|
|
8
8
|
} from "@weborigami/async-tree";
|
|
9
|
-
import
|
|
10
|
-
import { handleExtension } from "./handlers.js";
|
|
9
|
+
import handleExtension from "./handleExtension.js";
|
|
11
10
|
import { evaluate, ops } from "./internal.js";
|
|
12
11
|
|
|
13
12
|
/**
|
|
@@ -25,11 +24,12 @@ import { evaluate, ops } from "./internal.js";
|
|
|
25
24
|
* property getter on the object.
|
|
26
25
|
*
|
|
27
26
|
* @param {*} entries
|
|
28
|
-
* @param {import("
|
|
27
|
+
* @param {import("../../index.ts").RuntimeState} [state]
|
|
29
28
|
*/
|
|
30
|
-
export default async function expressionObject(entries,
|
|
29
|
+
export default async function expressionObject(entries, state = {}) {
|
|
31
30
|
// Create the object and set its parent
|
|
32
31
|
const object = {};
|
|
32
|
+
const parent = state?.object ?? null;
|
|
33
33
|
if (parent !== null && !Tree.isAsyncTree(parent)) {
|
|
34
34
|
throw new TypeError(`Parent must be an AsyncTree or null`);
|
|
35
35
|
}
|
|
@@ -86,22 +86,12 @@ export default async function expressionObject(entries, parent) {
|
|
|
86
86
|
code = value;
|
|
87
87
|
}
|
|
88
88
|
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
const handlers = getHandlers(tree);
|
|
96
|
-
return handleExtension(tree, result, key, handlers);
|
|
97
|
-
};
|
|
98
|
-
} else {
|
|
99
|
-
// No extension, so getter just invokes code.
|
|
100
|
-
get = async () => {
|
|
101
|
-
tree ??= new ObjectTree(object);
|
|
102
|
-
return evaluate.call(tree, code);
|
|
103
|
-
};
|
|
104
|
-
}
|
|
89
|
+
const get = async () => {
|
|
90
|
+
tree ??= new ObjectTree(object);
|
|
91
|
+
const newState = Object.assign({}, state, { object: tree });
|
|
92
|
+
const result = await evaluate(code, newState);
|
|
93
|
+
return extname ? handleExtension(result, key, tree) : result;
|
|
94
|
+
};
|
|
105
95
|
|
|
106
96
|
Object.defineProperty(object, key, {
|
|
107
97
|
configurable: true,
|
|
@@ -10,9 +10,7 @@ export default async function functionResultsMap(treelike) {
|
|
|
10
10
|
|
|
11
11
|
value: async (sourceValue, sourceKey, tree) => {
|
|
12
12
|
const resultValue =
|
|
13
|
-
typeof sourceValue === "function"
|
|
14
|
-
? await sourceValue.call(tree)
|
|
15
|
-
: sourceValue;
|
|
13
|
+
typeof sourceValue === "function" ? await sourceValue() : sourceValue;
|
|
16
14
|
return resultValue;
|
|
17
15
|
},
|
|
18
16
|
});
|