@matter/cli-tool 0.11.0-alpha.0-20241013-d38e934bb
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/LICENSE +201 -0
- package/README.md +1 -0
- package/bin/matter.js +3 -0
- package/dist/esm/cli.js +19 -0
- package/dist/esm/cli.js.map +6 -0
- package/dist/esm/commands/cat.js +17 -0
- package/dist/esm/commands/cat.js.map +6 -0
- package/dist/esm/commands/cd.js +23 -0
- package/dist/esm/commands/cd.js.map +6 -0
- package/dist/esm/commands/cwd.js +16 -0
- package/dist/esm/commands/cwd.js.map +6 -0
- package/dist/esm/commands/help.js +11 -0
- package/dist/esm/commands/help.js.map +6 -0
- package/dist/esm/commands/index.js +13 -0
- package/dist/esm/commands/index.js.map +6 -0
- package/dist/esm/commands/ls.js +179 -0
- package/dist/esm/commands/ls.js.map +6 -0
- package/dist/esm/commands/rm.js +21 -0
- package/dist/esm/commands/rm.js.map +6 -0
- package/dist/esm/commands/set.js +28 -0
- package/dist/esm/commands/set.js.map +6 -0
- package/dist/esm/domain.js +178 -0
- package/dist/esm/domain.js.map +6 -0
- package/dist/esm/errors.js +54 -0
- package/dist/esm/errors.js.map +6 -0
- package/dist/esm/globals.js +28 -0
- package/dist/esm/globals.js.map +6 -0
- package/dist/esm/location.js +149 -0
- package/dist/esm/location.js.map +6 -0
- package/dist/esm/package.json +12 -0
- package/dist/esm/parser.js +174 -0
- package/dist/esm/parser.js.map +6 -0
- package/dist/esm/providers/endpoint.js +38 -0
- package/dist/esm/providers/endpoint.js.map +6 -0
- package/dist/esm/providers/index.js +9 -0
- package/dist/esm/providers/index.js.map +6 -0
- package/dist/esm/providers/model.js +40 -0
- package/dist/esm/providers/model.js.map +6 -0
- package/dist/esm/providers/module.js +26 -0
- package/dist/esm/providers/module.js.map +6 -0
- package/dist/esm/repl.js +134 -0
- package/dist/esm/repl.js.map +6 -0
- package/dist/esm/stat.js +63 -0
- package/dist/esm/stat.js.map +6 -0
- package/package.json +69 -0
- package/src/cli.ts +21 -0
- package/src/commands/cat.ts +19 -0
- package/src/commands/cd.ts +28 -0
- package/src/commands/cwd.ts +18 -0
- package/src/commands/help.ts +14 -0
- package/src/commands/index.ts +13 -0
- package/src/commands/ls.ts +232 -0
- package/src/commands/rm.ts +25 -0
- package/src/commands/set.ts +32 -0
- package/src/domain.ts +221 -0
- package/src/errors.ts +54 -0
- package/src/globals.ts +29 -0
- package/src/location.ts +193 -0
- package/src/parser.ts +254 -0
- package/src/providers/endpoint.ts +44 -0
- package/src/providers/index.ts +9 -0
- package/src/providers/model.ts +45 -0
- package/src/providers/module.ts +30 -0
- package/src/repl.ts +168 -0
- package/src/stat.ts +121 -0
- package/src/tsconfig.json +27 -0
package/src/location.ts
ADDED
|
@@ -0,0 +1,193 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @license
|
|
3
|
+
* Copyright 2022-2024 Matter.js Authors
|
|
4
|
+
* SPDX-License-Identifier: Apache-2.0
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import { NotADirectoryError, NotFoundError } from "#errors.js";
|
|
8
|
+
import { decamelize, MaybePromise } from "#general";
|
|
9
|
+
import { Stat } from "#stat.js";
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* We support a quick-and-dirty virtual "filesystem" for navigation of our object model.
|
|
13
|
+
*
|
|
14
|
+
* This is the interface to this filesystem.
|
|
15
|
+
*/
|
|
16
|
+
export interface Location {
|
|
17
|
+
kind: "directory" | "command" | "file";
|
|
18
|
+
basename: string;
|
|
19
|
+
path: string;
|
|
20
|
+
tag: string;
|
|
21
|
+
id?: string | number;
|
|
22
|
+
name?: string;
|
|
23
|
+
summary?: string;
|
|
24
|
+
parent?: Location;
|
|
25
|
+
definition: unknown;
|
|
26
|
+
paths: MaybePromise<string[]>;
|
|
27
|
+
at(path: string, searchedAs?: string): MaybePromise<Location>;
|
|
28
|
+
maybeAt(path: string, searchedAs?: string): MaybePromise<Location | undefined>;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
function isClass(fn: {}) {
|
|
32
|
+
return !Object.getOwnPropertyDescriptor(fn, "prototype")?.writable;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
export function Location(basename: string, definition: unknown, stat: Stat, parent: undefined | Location): Location {
|
|
36
|
+
let { tag } = stat;
|
|
37
|
+
|
|
38
|
+
if (tag === undefined) {
|
|
39
|
+
if (definition === undefined) {
|
|
40
|
+
tag = "undefined";
|
|
41
|
+
} else if (typeof definition === "object") {
|
|
42
|
+
if (definition === null) {
|
|
43
|
+
tag = "null";
|
|
44
|
+
} else if (Symbol.toStringTag in definition) {
|
|
45
|
+
tag = `${definition[Symbol.toStringTag]}`;
|
|
46
|
+
} else if (Array.isArray(definition)) {
|
|
47
|
+
tag = "array";
|
|
48
|
+
} else if (ArrayBuffer.isView(definition)) {
|
|
49
|
+
tag = "bytes";
|
|
50
|
+
} else if (definition.constructor.name !== "Object") {
|
|
51
|
+
tag = definition.constructor.name;
|
|
52
|
+
} else {
|
|
53
|
+
tag = "object";
|
|
54
|
+
}
|
|
55
|
+
} else if (typeof definition === "function") {
|
|
56
|
+
if (isClass(definition)) {
|
|
57
|
+
tag = "constructor";
|
|
58
|
+
} else {
|
|
59
|
+
tag = "function";
|
|
60
|
+
}
|
|
61
|
+
} else {
|
|
62
|
+
tag = typeof definition;
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
tag = decamelize(tag);
|
|
67
|
+
|
|
68
|
+
return {
|
|
69
|
+
kind: typeof definition === "function" && !isClass(definition) ? "command" : stat.kind,
|
|
70
|
+
basename,
|
|
71
|
+
name: stat.name,
|
|
72
|
+
summary: stat.summary,
|
|
73
|
+
id: stat.id,
|
|
74
|
+
tag,
|
|
75
|
+
parent,
|
|
76
|
+
definition,
|
|
77
|
+
|
|
78
|
+
get path() {
|
|
79
|
+
if (!this.parent) {
|
|
80
|
+
return "/";
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
let path = "";
|
|
84
|
+
let location = this;
|
|
85
|
+
while (location.parent) {
|
|
86
|
+
path = `/${location.basename}${path}`;
|
|
87
|
+
location = location.parent;
|
|
88
|
+
}
|
|
89
|
+
return path;
|
|
90
|
+
},
|
|
91
|
+
|
|
92
|
+
get paths() {
|
|
93
|
+
if (stat.kind !== "directory") {
|
|
94
|
+
throw new NotADirectoryError(this.path);
|
|
95
|
+
}
|
|
96
|
+
return stat.paths;
|
|
97
|
+
},
|
|
98
|
+
|
|
99
|
+
at(path, searchedAs): MaybePromise<Location> {
|
|
100
|
+
const location = this.maybeAt(path);
|
|
101
|
+
|
|
102
|
+
if (MaybePromise.is(location)) {
|
|
103
|
+
return location.then(accept);
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
return accept(location);
|
|
107
|
+
|
|
108
|
+
function accept(location: Location | undefined) {
|
|
109
|
+
if (location === undefined) {
|
|
110
|
+
throw new NotFoundError(searchedAs ?? path);
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
return location;
|
|
114
|
+
}
|
|
115
|
+
},
|
|
116
|
+
|
|
117
|
+
maybeAt(path, searchedAs): MaybePromise<Location | undefined> {
|
|
118
|
+
if (stat.kind !== "directory") {
|
|
119
|
+
throw new NotADirectoryError(searchedAs ?? path);
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
if (path === undefined || path === "") {
|
|
123
|
+
return this;
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
const segments = path.split("/");
|
|
127
|
+
|
|
128
|
+
// Handle absolute path. Does not happen on recursion
|
|
129
|
+
if (segments[0] === "") {
|
|
130
|
+
let location = this;
|
|
131
|
+
while (location.parent) {
|
|
132
|
+
location = location.parent;
|
|
133
|
+
}
|
|
134
|
+
return location.maybeAt(segments.slice(1).join("/"), "/");
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
while (segments[0] === "" || segments[0] === ".") {
|
|
138
|
+
searchedAs += `${segments.shift()}/`;
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
if (segments[0] === "..") {
|
|
142
|
+
return (this.parent ?? this).maybeAt(
|
|
143
|
+
segments.slice(1).join("/"),
|
|
144
|
+
searchedAs ? Location.join(searchedAs, "..") : "..",
|
|
145
|
+
);
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
if (!segments.length) {
|
|
149
|
+
return this;
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
const subsearchedAs = searchedAs ? Location.join(searchedAs, segments[0]) : segments[0];
|
|
153
|
+
const definition = stat.definitionAt(decodeURIComponent(segments[0]));
|
|
154
|
+
|
|
155
|
+
const accept = (definition: unknown) => {
|
|
156
|
+
if (definition === undefined || definition === null) {
|
|
157
|
+
if (segments.length > 1) {
|
|
158
|
+
throw new NotFoundError(subsearchedAs);
|
|
159
|
+
}
|
|
160
|
+
return;
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
const sublocation = Location(segments[0], definition, Stat.of(definition), this);
|
|
164
|
+
if (segments.length === 1) {
|
|
165
|
+
return sublocation;
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
return sublocation.at(segments.slice(1).join("/"), subsearchedAs);
|
|
169
|
+
};
|
|
170
|
+
|
|
171
|
+
if (MaybePromise.is(definition)) {
|
|
172
|
+
return definition.then(accept);
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
return accept(definition);
|
|
176
|
+
},
|
|
177
|
+
};
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
export namespace Location {
|
|
181
|
+
export function join(path: string, ...paths: string[]) {
|
|
182
|
+
for (const other of paths) {
|
|
183
|
+
if (other.startsWith("/")) {
|
|
184
|
+
path = other;
|
|
185
|
+
} else if (path.endsWith("/")) {
|
|
186
|
+
path = `${path}${other}`;
|
|
187
|
+
} else {
|
|
188
|
+
path = `${path}/${other}`;
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
return path;
|
|
192
|
+
}
|
|
193
|
+
}
|
package/src/parser.ts
ADDED
|
@@ -0,0 +1,254 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @license
|
|
3
|
+
* Copyright 2022-2024 Matter.js Authors
|
|
4
|
+
* SPDX-License-Identifier: Apache-2.0
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import { getLineInfo, Options, parse, parseExpressionAt } from "acorn";
|
|
8
|
+
import { generate } from "escodegen";
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* The result of parsing our command dialect.
|
|
12
|
+
*
|
|
13
|
+
* We support an expanded JS dialect that allows function invocation with the syntax "command [...text|expr]*". This
|
|
14
|
+
* allows us to support both JS less verbose "shell command" syntax that nonetheless supports full JS semantics.
|
|
15
|
+
*
|
|
16
|
+
* Differentiating between "shell commands" and JS is a bit fiddly but from a high level:
|
|
17
|
+
*
|
|
18
|
+
* - If it starts with a keyword that starts a JS statement or assignment then it is a statement
|
|
19
|
+
* - If it starts with a path (e.g. "foo", "foo/bar", "./foo/bar", etc.) it is a command
|
|
20
|
+
* - Otherwise it is a JS statement
|
|
21
|
+
*/
|
|
22
|
+
export type Input = EmptyInput | CommandInput | StatementInput | IncompleteInput;
|
|
23
|
+
|
|
24
|
+
export interface EmptyInput {
|
|
25
|
+
kind: "empty";
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export interface CommandArg {
|
|
29
|
+
line: number;
|
|
30
|
+
column: number;
|
|
31
|
+
js: string;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
export interface CommandInput {
|
|
35
|
+
kind: "command";
|
|
36
|
+
name: string;
|
|
37
|
+
args: CommandArg[];
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
export interface StatementInput {
|
|
41
|
+
kind: "statement";
|
|
42
|
+
js: string;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
export interface IncompleteInput {
|
|
46
|
+
kind: "incomplete";
|
|
47
|
+
error: SyntaxError;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
const ACORN_PARSE_OPTIONS: Options = {
|
|
51
|
+
ecmaVersion: "latest",
|
|
52
|
+
allowAwaitOutsideFunction: true,
|
|
53
|
+
};
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* Translate user input into a {@link Input}.
|
|
57
|
+
*/
|
|
58
|
+
export function parseInput(input: string): Input {
|
|
59
|
+
const inputIsCommand = isCommand(input);
|
|
60
|
+
try {
|
|
61
|
+
if (inputIsCommand) {
|
|
62
|
+
return parseCommand(input);
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
// Only parse so we can detect incomplete statements; otherwise let node do the work
|
|
66
|
+
parse(input, ACORN_PARSE_OPTIONS);
|
|
67
|
+
return { kind: "statement", js: input };
|
|
68
|
+
} catch (e) {
|
|
69
|
+
if (errorIndicatesIncomplete(e, input)) {
|
|
70
|
+
return { kind: "incomplete", error: e };
|
|
71
|
+
}
|
|
72
|
+
if (!inputIsCommand) {
|
|
73
|
+
// Let node throw its own error
|
|
74
|
+
return { kind: "statement", js: input };
|
|
75
|
+
}
|
|
76
|
+
throw e;
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
function errorIndicatesIncomplete(e: any, input: string): e is SyntaxError {
|
|
81
|
+
if (e instanceof SyntaxError) {
|
|
82
|
+
if (e.message.startsWith("Unexpected token") && (e as unknown as { pos: number }).pos === input.length) {
|
|
83
|
+
return true;
|
|
84
|
+
}
|
|
85
|
+
if (e.message.match(/^Unterminated (?:template literal|group|comment|regular expression|template)/)) {
|
|
86
|
+
return true;
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
return false;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
function parseCommand(command: string): Input {
|
|
93
|
+
// Find beginning of name
|
|
94
|
+
let start = 0;
|
|
95
|
+
while (start < command.length && command[start].match(/\s/)) {
|
|
96
|
+
start++;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
if (start === command.length) {
|
|
100
|
+
return { kind: "empty" };
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
// Find end of name. We identified command with regexp so this can be simplistic
|
|
104
|
+
const nameStart = start;
|
|
105
|
+
while (start < command.length && !command[start].match(/\s/)) {
|
|
106
|
+
start++;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
// We now know the command name
|
|
110
|
+
const name = command.slice(nameStart, start);
|
|
111
|
+
|
|
112
|
+
// Extract arguments. These are either unadorned text
|
|
113
|
+
const args = Array<CommandArg>();
|
|
114
|
+
while (command.trim().length) {
|
|
115
|
+
while (start < command.length && command[start].match(/^\s/)) {
|
|
116
|
+
start++;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
if (start === command.length) {
|
|
120
|
+
break;
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
let isExpression;
|
|
124
|
+
switch (command[start]) {
|
|
125
|
+
case "(":
|
|
126
|
+
case "[":
|
|
127
|
+
case "{":
|
|
128
|
+
case '"':
|
|
129
|
+
case "`":
|
|
130
|
+
case "'":
|
|
131
|
+
isExpression = true;
|
|
132
|
+
break;
|
|
133
|
+
|
|
134
|
+
default:
|
|
135
|
+
isExpression = false;
|
|
136
|
+
break;
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
const { line, column } = getLineInfo(command, start);
|
|
140
|
+
|
|
141
|
+
if (isExpression) {
|
|
142
|
+
try {
|
|
143
|
+
const ast = parseExpressionAt(command, start, ACORN_PARSE_OPTIONS);
|
|
144
|
+
args.push({ js: generate(ast), line, column });
|
|
145
|
+
start = ast.end;
|
|
146
|
+
} catch (e) {
|
|
147
|
+
// acorn sticks error onto end of message; move it into the stack like Node would
|
|
148
|
+
if (e instanceof SyntaxError) {
|
|
149
|
+
let message = e.message;
|
|
150
|
+
const match = message.match(/(.*) \(\d+:\d+\)$/);
|
|
151
|
+
let location;
|
|
152
|
+
if (match) {
|
|
153
|
+
message = match[1];
|
|
154
|
+
location = message[2];
|
|
155
|
+
} else {
|
|
156
|
+
const loc = getLineInfo(command, start);
|
|
157
|
+
location = `${loc.line}:${loc.column}`;
|
|
158
|
+
}
|
|
159
|
+
const error = new SyntaxError(message);
|
|
160
|
+
error.stack = `SyntaxError: ${message}\n at matter-cli-input:${location}`;
|
|
161
|
+
throw error;
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
} else {
|
|
165
|
+
let end = start;
|
|
166
|
+
|
|
167
|
+
while (end < command.length) {
|
|
168
|
+
if (command[end].match(/\s/)) {
|
|
169
|
+
break;
|
|
170
|
+
}
|
|
171
|
+
end++;
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
const str = command.slice(start, end);
|
|
175
|
+
start = end;
|
|
176
|
+
|
|
177
|
+
let js;
|
|
178
|
+
if (str === "true" || str === "false" || str === "NaN" || str === "Infinity" || isNumeric(str)) {
|
|
179
|
+
js = str;
|
|
180
|
+
} else {
|
|
181
|
+
js = JSON.stringify(str);
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
args.push({ js, line, column });
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
return { kind: "command", name, args };
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
export function isCommand(input: string) {
|
|
192
|
+
if (input.match(isCommand.STATEMENT_DETECTOR)) {
|
|
193
|
+
return false;
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
if (input.match(isCommand.COMMAND_DETECTOR)) {
|
|
197
|
+
return true;
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
return false;
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
export namespace isCommand {
|
|
204
|
+
const STATEMENT_KEYWORDS = [
|
|
205
|
+
"async",
|
|
206
|
+
"break",
|
|
207
|
+
"class",
|
|
208
|
+
"const",
|
|
209
|
+
"debugger",
|
|
210
|
+
"delete",
|
|
211
|
+
"do",
|
|
212
|
+
"for",
|
|
213
|
+
"function",
|
|
214
|
+
"if",
|
|
215
|
+
"new",
|
|
216
|
+
"return",
|
|
217
|
+
"switch",
|
|
218
|
+
"throw",
|
|
219
|
+
"try",
|
|
220
|
+
"let",
|
|
221
|
+
"while",
|
|
222
|
+
"with",
|
|
223
|
+
"export",
|
|
224
|
+
"import",
|
|
225
|
+
"var",
|
|
226
|
+
"void",
|
|
227
|
+
"true",
|
|
228
|
+
"false",
|
|
229
|
+
];
|
|
230
|
+
|
|
231
|
+
const IDENTIFIER = "[\\p{L}$_][\\p{L}$_0-9]*";
|
|
232
|
+
const EOW = "(?:\\s|$)";
|
|
233
|
+
|
|
234
|
+
const statementStarts = [...STATEMENT_KEYWORDS.map(kw => `${kw}${EOW}`), `${IDENTIFIER}\\s*=`];
|
|
235
|
+
|
|
236
|
+
// If this regexp matches, input is NOT a command
|
|
237
|
+
export const STATEMENT_DETECTOR = new RegExp(`^\\s*(?:${statementStarts.join("|")})`, "u");
|
|
238
|
+
|
|
239
|
+
// If above regexp does not match but this one does then input IS a command
|
|
240
|
+
export const COMMAND_DETECTOR = new RegExp(
|
|
241
|
+
`^\\s*(?:\\.?/)?${IDENTIFIER}(?:\\/(?:\\.?\\.|${IDENTIFIER}))*${EOW}`,
|
|
242
|
+
"u",
|
|
243
|
+
);
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
/**
|
|
247
|
+
* {@see https://stackoverflow.com/questions/175739/how-can-i-check-if-a-string-is-a-valid-number}
|
|
248
|
+
*/
|
|
249
|
+
function isNumeric(str: string) {
|
|
250
|
+
return (
|
|
251
|
+
!isNaN(str as unknown as number) && // use type coercion to parse the _entirety_ of the string (`parseFloat` alone does not do this)...
|
|
252
|
+
!isNaN(parseFloat(str))
|
|
253
|
+
); // ...and ensure strings of whitespace fail
|
|
254
|
+
}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @license
|
|
3
|
+
* Copyright 2022-2024 Matter.js Authors
|
|
4
|
+
* SPDX-License-Identifier: Apache-2.0
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* @license
|
|
9
|
+
* Copyright 2022-2024 Matter.js Authors
|
|
10
|
+
* SPDX-License-Identifier: Apache-2.0
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
import { Endpoint } from "#node";
|
|
14
|
+
import { Directory, Stat } from "#stat.js";
|
|
15
|
+
|
|
16
|
+
Stat.provide(endpoint => {
|
|
17
|
+
if (!isEndpoint(endpoint)) {
|
|
18
|
+
return;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
return Directory({
|
|
22
|
+
definitionAt(path) {
|
|
23
|
+
const part = endpoint.parts.get(path);
|
|
24
|
+
|
|
25
|
+
if (part) {
|
|
26
|
+
if (!part.lifecycle.isReady) {
|
|
27
|
+
return part.construction.then(() => Stat.of(part));
|
|
28
|
+
}
|
|
29
|
+
return Stat.of(part);
|
|
30
|
+
}
|
|
31
|
+
},
|
|
32
|
+
|
|
33
|
+
paths() {
|
|
34
|
+
if (!endpoint.lifecycle.isPartsReady) {
|
|
35
|
+
return endpoint.construction.then(() => endpoint.parts.map(part => part.id));
|
|
36
|
+
}
|
|
37
|
+
return endpoint.parts.map(part => part.id);
|
|
38
|
+
},
|
|
39
|
+
});
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
function isEndpoint(item: unknown): item is Endpoint {
|
|
43
|
+
return item instanceof Endpoint;
|
|
44
|
+
}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @license
|
|
3
|
+
* Copyright 2022-2024 Matter.js Authors
|
|
4
|
+
* SPDX-License-Identifier: Apache-2.0
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import { Model } from "#model";
|
|
8
|
+
import { Directory, Stat } from "#stat.js";
|
|
9
|
+
|
|
10
|
+
Stat.provide(model => {
|
|
11
|
+
if (!isModel(model)) {
|
|
12
|
+
return;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
const details: Stat.Base = {
|
|
16
|
+
name: model.description,
|
|
17
|
+
summary: model.details,
|
|
18
|
+
id: model.id,
|
|
19
|
+
tag: model.tag,
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
if (!model.children.length) {
|
|
23
|
+
return {
|
|
24
|
+
kind: "file",
|
|
25
|
+
...details,
|
|
26
|
+
};
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
return Directory({
|
|
30
|
+
...details,
|
|
31
|
+
paths() {
|
|
32
|
+
return model.children.map(child => child.name);
|
|
33
|
+
},
|
|
34
|
+
definitionAt(name: string) {
|
|
35
|
+
return model.children.find(model => model.name === name);
|
|
36
|
+
},
|
|
37
|
+
});
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
function isModel(model: unknown): model is Model {
|
|
41
|
+
if (model instanceof Model) {
|
|
42
|
+
return true;
|
|
43
|
+
}
|
|
44
|
+
return false;
|
|
45
|
+
}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @license
|
|
3
|
+
* Copyright 2022-2024 Matter.js Authors
|
|
4
|
+
* SPDX-License-Identifier: Apache-2.0
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import { Directory, Stat } from "#stat.js";
|
|
8
|
+
|
|
9
|
+
Stat.provide(definition => {
|
|
10
|
+
if (!isModule(definition)) {
|
|
11
|
+
return;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
return Directory({
|
|
15
|
+
paths() {
|
|
16
|
+
return Object.keys(definition);
|
|
17
|
+
},
|
|
18
|
+
|
|
19
|
+
definitionAt(path: string) {
|
|
20
|
+
return definition[path];
|
|
21
|
+
},
|
|
22
|
+
});
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
function isModule(definition: unknown): definition is Record<string, unknown> {
|
|
26
|
+
if (typeof definition !== "object" || definition === null) {
|
|
27
|
+
return false;
|
|
28
|
+
}
|
|
29
|
+
return (definition as any)[Symbol.toStringTag] === "Module";
|
|
30
|
+
}
|