@weborigami/origami 0.0.55 → 0.0.57

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.0.55",
3
+ "version": "0.0.57",
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.4.5"
18
18
  },
19
19
  "dependencies": {
20
- "@weborigami/async-tree": "0.0.55",
21
- "@weborigami/language": "0.0.55",
22
- "@weborigami/types": "0.0.55",
20
+ "@weborigami/async-tree": "0.0.57",
21
+ "@weborigami/language": "0.0.57",
22
+ "@weborigami/types": "0.0.57",
23
23
  "exif-parser": "0.1.12",
24
24
  "graphviz-wasm": "3.0.2",
25
25
  "highlight.js": "11.9.0",
@@ -8,7 +8,7 @@ import getTreeArgument from "../misc/getTreeArgument.js";
8
8
  * @param {import("@weborigami/async-tree").Treelike} treelike
9
9
  */
10
10
  export default async function clean(treelike) {
11
- const tree = await getTreeArgument(this, arguments, treelike, "@sequence");
11
+ const tree = await getTreeArgument(this, arguments, treelike, "@clean");
12
12
  if (!Tree.isAsyncMutableTree(tree)) {
13
13
  throw new TypeError("@clean: the given tree is read-only.");
14
14
  }
@@ -129,17 +129,22 @@ export default function mapFnBuiltin(operation) {
129
129
  function parseExtensions(specifier) {
130
130
  const lowercase = specifier?.toLowerCase() ?? "";
131
131
  const extensionRegex =
132
- /^\.?(?<sourceExtension>\S*)(?:\s*(→|->)\s*)\.?(?<extension>\S+)$/;
133
- let resultExtension;
134
- let sourceExtension;
132
+ /^(\.?(?<sourceExtension>\S*)\s*(→|->)\s*\.?(?<resultExtension>\S*))|(\.?(?<extension>\S*))$/;
135
133
  const match = lowercase.match(extensionRegex);
136
- if (match?.groups) {
134
+ if (!match) {
135
+ // Shouldn't happen because the regex is exhaustive.
136
+ throw new Error(`@mapFn: Invalid extension specifier "${specifier}".`);
137
+ }
138
+ // @ts-ignore
139
+ const { extension, resultExtension, sourceExtension } = match.groups;
140
+ if (sourceExtension || resultExtension) {
137
141
  // foo→bar
138
- ({ extension: resultExtension, sourceExtension } = match.groups);
142
+ return { resultExtension, sourceExtension };
139
143
  } else {
140
144
  // foo
141
- resultExtension = lowercase;
142
- sourceExtension = lowercase;
145
+ return {
146
+ resultExtension: specifier,
147
+ sourceExtension: specifier,
148
+ };
143
149
  }
144
- return { resultExtension, sourceExtension };
145
150
  }
@@ -26,40 +26,49 @@ export default async function project(key) {
26
26
 
27
27
  const dirname = process.cwd();
28
28
  const currentTree = new OrigamiFiles(dirname);
29
- let containerTree = await findConfigContainer(currentTree);
30
29
 
31
- let config;
32
- if (containerTree) {
30
+ // Search up the tree for the configuration file or package.json to determine
31
+ // the project root.
32
+ let rootTree =
33
+ (await findAncestorFile(currentTree, configFileName)) ??
34
+ (await findAncestorFile(currentTree, "package.json"));
35
+
36
+ let config = null;
37
+ if (rootTree) {
33
38
  // Load the configuration.
34
- const configParent = Scope.treeWithScope(containerTree, builtins);
39
+ const configParent = Scope.treeWithScope(rootTree, builtins);
35
40
  const buffer = await configParent.get(configFileName);
36
- config = await fileTypeOrigami.unpack(buffer, {
37
- key: configFileName,
38
- parent: configParent,
39
- });
40
- if (!config) {
41
- const configPath = /** @type {any} */ (configParent).path;
42
- throw new Error(
43
- `Couldn't load the Origami configuration in ${configPath}/${configFileName}`
44
- );
41
+ if (buffer) {
42
+ // Project has configuration file
43
+ config = await fileTypeOrigami.unpack(buffer, {
44
+ key: configFileName,
45
+ parent: configParent,
46
+ });
47
+ if (!config) {
48
+ const configPath = /** @type {any} */ (configParent).path;
49
+ throw new Error(
50
+ `Couldn't load the Origami configuration in ${configPath}/${configFileName}`
51
+ );
52
+ }
45
53
  }
46
54
  } else {
47
- containerTree = currentTree;
48
- config = null;
55
+ rootTree = currentTree;
49
56
  }
50
57
 
51
58
  // Add the configuration as the context for the project root.
52
59
  const scope = new Scope(config, builtins);
53
- const result = Scope.treeWithScope(containerTree, scope);
60
+ const result = Scope.treeWithScope(rootTree, scope);
54
61
  return key === undefined ? result : result.get(key);
55
62
  }
56
63
 
57
- async function findConfigContainer(start) {
64
+ // Return the first ancestor of the given tree that contains a file with the
65
+ // given name.
66
+ async function findAncestorFile(start, fileName) {
58
67
  let current = start;
59
68
  while (current) {
60
- const config = await current.get(configFileName);
61
- if (config) {
62
- // Found a configuration; its container is the project root.
69
+ const value = await current.get(fileName);
70
+ if (value) {
71
+ // Found the desired file; its container is the project root.
63
72
  return current;
64
73
  }
65
74
  // Not found; try the parent.