@webgal/language-service 0.0.2-alpha.0
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 +373 -0
- package/build/chunk-C0xms8kb.cjs +34 -0
- package/build/index.cjs +297 -0
- package/build/index.d.cts +103 -0
- package/build/index.d.mts +103 -0
- package/build/index.mjs +293 -0
- package/build/language-configuration-BeRkRSII.mjs +214 -0
- package/build/language-configuration-D41FLA6b.cjs +232 -0
- package/build/monaco-init.cjs +83 -0
- package/build/monaco-init.d.cts +7 -0
- package/build/monaco-init.d.mts +7 -0
- package/build/monaco-init.mjs +78 -0
- package/build/monaco.cjs +138 -0
- package/build/monaco.d.cts +32 -0
- package/build/monaco.d.mts +32 -0
- package/build/monaco.mjs +134 -0
- package/build/node.cjs +374 -0
- package/build/node.d.cts +11 -0
- package/build/node.d.mts +11 -0
- package/build/node.mjs +370 -0
- package/build/syntaxes.cjs +12 -0
- package/build/syntaxes.d.cts +293 -0
- package/build/syntaxes.d.mts +293 -0
- package/build/syntaxes.mjs +9 -0
- package/build/themes.cjs +10 -0
- package/build/themes.d.cts +286 -0
- package/build/themes.d.mts +286 -0
- package/build/themes.mjs +8 -0
- package/build/white-Be4QIaif.cjs +1007 -0
- package/build/white-CaNrG0B0.mjs +995 -0
- package/package.json +71 -0
package/build/monaco.mjs
ADDED
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
import { createMemoryFileSystem, createWebgalClientHandlers, registerWebgalClientHandlers } from "./index.mjs";
|
|
2
|
+
import { WebSocketMessageReader, WebSocketMessageWriter, toSocket } from "vscode-ws-jsonrpc";
|
|
3
|
+
import { BrowserMessageReader, BrowserMessageWriter, CloseAction, ErrorAction } from "vscode-languageclient/browser.js";
|
|
4
|
+
import { MonacoLanguageClient } from "monaco-languageclient";
|
|
5
|
+
|
|
6
|
+
//#region src/monaco.ts
|
|
7
|
+
const createWebgalMonacoLanguageClient = (options) => {
|
|
8
|
+
const { languageServerUrl, editor } = options;
|
|
9
|
+
const editorInstance = editor;
|
|
10
|
+
const vfs = options.virtualFileSystem || createMemoryFileSystem({ root: "file:///game" });
|
|
11
|
+
if (!options.virtualFileSystem) {
|
|
12
|
+
vfs.writeFile("file:///game/scene/start.txt", "WebGal:Start;");
|
|
13
|
+
vfs.writeFile("file:///game/config.txt", "");
|
|
14
|
+
}
|
|
15
|
+
const webSocket = new WebSocket(languageServerUrl);
|
|
16
|
+
webSocket.onopen = () => {
|
|
17
|
+
const socket = toSocket(webSocket);
|
|
18
|
+
const reader = new WebSocketMessageReader(socket);
|
|
19
|
+
const languageClient = createLanguageClient({
|
|
20
|
+
reader,
|
|
21
|
+
writer: new WebSocketMessageWriter(socket)
|
|
22
|
+
}, {
|
|
23
|
+
editor: editorInstance,
|
|
24
|
+
vfs
|
|
25
|
+
});
|
|
26
|
+
languageClient.start();
|
|
27
|
+
reader.onClose(() => languageClient.stop());
|
|
28
|
+
};
|
|
29
|
+
return {
|
|
30
|
+
webSocket,
|
|
31
|
+
vfs
|
|
32
|
+
};
|
|
33
|
+
};
|
|
34
|
+
const createWebgalMonacoLanguageClientWithWorker = (options) => {
|
|
35
|
+
const { worker, editor } = options;
|
|
36
|
+
const editorInstance = editor;
|
|
37
|
+
const vfs = options.virtualFileSystem || createMemoryFileSystem({ root: "file:///game" });
|
|
38
|
+
if (!options.virtualFileSystem) {
|
|
39
|
+
vfs.writeFile("file:///game/scene/start.txt", "WebGal:Start;");
|
|
40
|
+
vfs.writeFile("file:///game/config.txt", "");
|
|
41
|
+
}
|
|
42
|
+
const languageClient = createLanguageClient({
|
|
43
|
+
reader: new BrowserMessageReader(worker),
|
|
44
|
+
writer: new BrowserMessageWriter(worker)
|
|
45
|
+
}, {
|
|
46
|
+
editor: editorInstance,
|
|
47
|
+
vfs
|
|
48
|
+
});
|
|
49
|
+
languageClient.start();
|
|
50
|
+
worker.onerror = () => languageClient.stop();
|
|
51
|
+
worker.onmessageerror = () => languageClient.stop();
|
|
52
|
+
return {
|
|
53
|
+
worker,
|
|
54
|
+
vfs
|
|
55
|
+
};
|
|
56
|
+
};
|
|
57
|
+
const createWebgalMonacoLanguageClientWithPort = (options) => {
|
|
58
|
+
const { port, editor } = options;
|
|
59
|
+
const editorInstance = editor;
|
|
60
|
+
const vfs = options.virtualFileSystem || createMemoryFileSystem({ root: "file:///game" });
|
|
61
|
+
if (!options.virtualFileSystem) {
|
|
62
|
+
vfs.writeFile("file:///game/scene/start.txt", "WebGal:Start;");
|
|
63
|
+
vfs.writeFile("file:///game/config.txt", "");
|
|
64
|
+
}
|
|
65
|
+
const languageClient = createLanguageClient({
|
|
66
|
+
reader: new BrowserMessageReader(port),
|
|
67
|
+
writer: new BrowserMessageWriter(port)
|
|
68
|
+
}, {
|
|
69
|
+
editor: editorInstance,
|
|
70
|
+
vfs
|
|
71
|
+
});
|
|
72
|
+
languageClient.start();
|
|
73
|
+
port.onmessageerror = () => languageClient.stop();
|
|
74
|
+
return {
|
|
75
|
+
port,
|
|
76
|
+
vfs
|
|
77
|
+
};
|
|
78
|
+
};
|
|
79
|
+
const createLanguageClient = (messageTransports, options) => {
|
|
80
|
+
const handlers = createWebgalClientHandlers({
|
|
81
|
+
vfs: options.vfs,
|
|
82
|
+
overrides: { "client/showTip": function(message) {
|
|
83
|
+
console.log(message);
|
|
84
|
+
} }
|
|
85
|
+
});
|
|
86
|
+
const client = new MonacoLanguageClient({
|
|
87
|
+
name: "WebGAL Language Client",
|
|
88
|
+
clientOptions: {
|
|
89
|
+
documentSelector: [
|
|
90
|
+
{
|
|
91
|
+
scheme: "file",
|
|
92
|
+
language: "webgal"
|
|
93
|
+
},
|
|
94
|
+
{
|
|
95
|
+
scheme: "file",
|
|
96
|
+
language: "webgal-config"
|
|
97
|
+
},
|
|
98
|
+
{
|
|
99
|
+
scheme: "inmemory",
|
|
100
|
+
language: "webgal"
|
|
101
|
+
},
|
|
102
|
+
{
|
|
103
|
+
scheme: "inmemory",
|
|
104
|
+
language: "webgal-config"
|
|
105
|
+
}
|
|
106
|
+
],
|
|
107
|
+
errorHandler: {
|
|
108
|
+
error: () => ({ action: ErrorAction.Continue }),
|
|
109
|
+
closed: () => ({ action: CloseAction.DoNotRestart })
|
|
110
|
+
},
|
|
111
|
+
synchronize: { configurationSection: ["webgal", "http"] },
|
|
112
|
+
initializationOptions() {
|
|
113
|
+
const model = options.editor.getModel();
|
|
114
|
+
return {
|
|
115
|
+
processId: Math.random(),
|
|
116
|
+
rootPath: "file:///game",
|
|
117
|
+
rootUri: "file:///game",
|
|
118
|
+
capabilities: {},
|
|
119
|
+
workspaceFolders: [{
|
|
120
|
+
uri: "file:///game",
|
|
121
|
+
name: "example"
|
|
122
|
+
}],
|
|
123
|
+
...model ? { textDocument: { uri: model.uri.toString() } } : {}
|
|
124
|
+
};
|
|
125
|
+
}
|
|
126
|
+
},
|
|
127
|
+
messageTransports
|
|
128
|
+
});
|
|
129
|
+
registerWebgalClientHandlers(client, handlers);
|
|
130
|
+
return client;
|
|
131
|
+
};
|
|
132
|
+
|
|
133
|
+
//#endregion
|
|
134
|
+
export { createWebgalMonacoLanguageClient, createWebgalMonacoLanguageClientWithPort, createWebgalMonacoLanguageClientWithWorker };
|
package/build/node.cjs
ADDED
|
@@ -0,0 +1,374 @@
|
|
|
1
|
+
Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
|
|
2
|
+
const require_chunk = require('./chunk-C0xms8kb.cjs');
|
|
3
|
+
let fs_promises = require("fs/promises");
|
|
4
|
+
fs_promises = require_chunk.__toESM(fs_promises);
|
|
5
|
+
let path = require("path");
|
|
6
|
+
path = require_chunk.__toESM(path);
|
|
7
|
+
|
|
8
|
+
//#region src/node.ts
|
|
9
|
+
const listDirectory = async (dirPath) => {
|
|
10
|
+
try {
|
|
11
|
+
return (await fs_promises.default.readdir(dirPath, { withFileTypes: true })).map((entry) => ({
|
|
12
|
+
name: entry.name,
|
|
13
|
+
isDirectory: entry.isDirectory()
|
|
14
|
+
}));
|
|
15
|
+
} catch {
|
|
16
|
+
return null;
|
|
17
|
+
}
|
|
18
|
+
};
|
|
19
|
+
const findFileInDirectory = async (dirPath, targetName, ignoreDirs) => {
|
|
20
|
+
const entries = await fs_promises.default.readdir(dirPath, { withFileTypes: true });
|
|
21
|
+
for (const entry of entries) {
|
|
22
|
+
if (entry.isFile() && entry.name === targetName) return path.default.join(dirPath, entry.name);
|
|
23
|
+
if (entry.isDirectory() && !ignoreDirs.has(entry.name)) {
|
|
24
|
+
const result = await findFileInDirectory(path.default.join(dirPath, entry.name), targetName, ignoreDirs);
|
|
25
|
+
if (result) return result;
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
return null;
|
|
29
|
+
};
|
|
30
|
+
function createNodeFileSystem(options) {
|
|
31
|
+
const root = path.default.resolve(options.root);
|
|
32
|
+
const ignoreDirs = new Set(options.ignoreDirs ?? ["node_modules", ".git"]);
|
|
33
|
+
const sceneDir = options.sceneDir ?? "scene";
|
|
34
|
+
let rootEntry = {
|
|
35
|
+
type: "dir",
|
|
36
|
+
children: {}
|
|
37
|
+
};
|
|
38
|
+
const loadedDirectories = /* @__PURE__ */ new Set();
|
|
39
|
+
const loadedFiles = /* @__PURE__ */ new Set();
|
|
40
|
+
const listeners = /* @__PURE__ */ new Set();
|
|
41
|
+
const emit = (changes) => {
|
|
42
|
+
for (const listener of listeners) listener(changes);
|
|
43
|
+
};
|
|
44
|
+
const isWithinRoot = (targetPath) => {
|
|
45
|
+
const absolute = path.default.resolve(targetPath);
|
|
46
|
+
const rel = path.default.relative(root, absolute);
|
|
47
|
+
if (rel === "" || rel === ".") return true;
|
|
48
|
+
return !rel.startsWith("..") && !path.default.isAbsolute(rel);
|
|
49
|
+
};
|
|
50
|
+
const toSegments = (targetPath) => {
|
|
51
|
+
const absolute = path.default.resolve(targetPath);
|
|
52
|
+
const rel = path.default.relative(root, absolute);
|
|
53
|
+
if (rel === "" || rel === ".") return [];
|
|
54
|
+
if (rel.startsWith("..") || path.default.isAbsolute(rel)) return null;
|
|
55
|
+
return rel.split(path.default.sep).filter((s) => s.length > 0);
|
|
56
|
+
};
|
|
57
|
+
const getEntryBySegments = (segments) => {
|
|
58
|
+
let current = rootEntry;
|
|
59
|
+
for (const segment of segments) {
|
|
60
|
+
if (current.type !== "dir") return null;
|
|
61
|
+
const next = current.children[segment];
|
|
62
|
+
if (!next) return null;
|
|
63
|
+
current = next;
|
|
64
|
+
}
|
|
65
|
+
return current;
|
|
66
|
+
};
|
|
67
|
+
const ensureDirectoryBySegments = (segments) => {
|
|
68
|
+
let current = rootEntry;
|
|
69
|
+
for (const segment of segments) {
|
|
70
|
+
if (current.type !== "dir") throw new Error("Path segment is not a directory");
|
|
71
|
+
const dir = current;
|
|
72
|
+
const existing = dir.children[segment];
|
|
73
|
+
if (!existing) {
|
|
74
|
+
const created = {
|
|
75
|
+
type: "dir",
|
|
76
|
+
children: {}
|
|
77
|
+
};
|
|
78
|
+
dir.children[segment] = created;
|
|
79
|
+
current = created;
|
|
80
|
+
continue;
|
|
81
|
+
}
|
|
82
|
+
if (existing.type !== "dir") throw new Error("Path segment is not a directory");
|
|
83
|
+
current = existing;
|
|
84
|
+
}
|
|
85
|
+
if (current.type !== "dir") throw new Error("Path is not a directory");
|
|
86
|
+
return current;
|
|
87
|
+
};
|
|
88
|
+
const ensureDirectoryLoaded = async (dirPath) => {
|
|
89
|
+
const absolute = path.default.resolve(dirPath);
|
|
90
|
+
if (!isWithinRoot(absolute)) return;
|
|
91
|
+
if (loadedDirectories.has(absolute)) return;
|
|
92
|
+
const segments = toSegments(absolute);
|
|
93
|
+
if (!segments) return;
|
|
94
|
+
const dirEntry = ensureDirectoryBySegments(segments);
|
|
95
|
+
const entries = await listDirectory(absolute);
|
|
96
|
+
if (!entries) {
|
|
97
|
+
loadedDirectories.add(absolute);
|
|
98
|
+
return;
|
|
99
|
+
}
|
|
100
|
+
for (const entry of entries) if (entry.isDirectory) dirEntry.children[entry.name] ??= {
|
|
101
|
+
type: "dir",
|
|
102
|
+
children: {}
|
|
103
|
+
};
|
|
104
|
+
else dirEntry.children[entry.name] ??= {
|
|
105
|
+
type: "file",
|
|
106
|
+
content: ""
|
|
107
|
+
};
|
|
108
|
+
loadedDirectories.add(absolute);
|
|
109
|
+
};
|
|
110
|
+
const getTree = () => rootEntry;
|
|
111
|
+
const setTree = (tree) => {
|
|
112
|
+
rootEntry = tree.type === "dir" ? tree : {
|
|
113
|
+
type: "dir",
|
|
114
|
+
children: {}
|
|
115
|
+
};
|
|
116
|
+
loadedDirectories.clear();
|
|
117
|
+
loadedFiles.clear();
|
|
118
|
+
emit([{
|
|
119
|
+
type: "setTree",
|
|
120
|
+
tree: rootEntry
|
|
121
|
+
}]);
|
|
122
|
+
};
|
|
123
|
+
const writeFile = async (targetPath, content) => {
|
|
124
|
+
const absolute = path.default.resolve(targetPath);
|
|
125
|
+
if (!isWithinRoot(absolute)) return;
|
|
126
|
+
const segments = toSegments(absolute);
|
|
127
|
+
if (!segments || segments.length === 0) return;
|
|
128
|
+
const fileName = segments[segments.length - 1];
|
|
129
|
+
const parent = ensureDirectoryBySegments(segments.slice(0, -1));
|
|
130
|
+
parent.children[fileName] = {
|
|
131
|
+
type: "file",
|
|
132
|
+
content
|
|
133
|
+
};
|
|
134
|
+
loadedFiles.add(absolute);
|
|
135
|
+
emit([{
|
|
136
|
+
type: "writeFile",
|
|
137
|
+
path: absolute,
|
|
138
|
+
content
|
|
139
|
+
}]);
|
|
140
|
+
};
|
|
141
|
+
const mkdir = async (targetPath) => {
|
|
142
|
+
const absolute = path.default.resolve(targetPath);
|
|
143
|
+
if (!isWithinRoot(absolute)) return;
|
|
144
|
+
const segments = toSegments(absolute);
|
|
145
|
+
if (!segments) return;
|
|
146
|
+
ensureDirectoryBySegments(segments);
|
|
147
|
+
emit([{
|
|
148
|
+
type: "mkdir",
|
|
149
|
+
path: absolute
|
|
150
|
+
}]);
|
|
151
|
+
};
|
|
152
|
+
const deletePath = async (targetPath) => {
|
|
153
|
+
const absolute = path.default.resolve(targetPath);
|
|
154
|
+
if (!isWithinRoot(absolute)) return;
|
|
155
|
+
const segments = toSegments(absolute);
|
|
156
|
+
if (!segments || segments.length === 0) return;
|
|
157
|
+
const name = segments[segments.length - 1];
|
|
158
|
+
const parentSegments = segments.slice(0, -1);
|
|
159
|
+
const parentEntry = parentSegments.length === 0 ? rootEntry : getEntryBySegments(parentSegments);
|
|
160
|
+
if (!parentEntry || parentEntry.type !== "dir") return;
|
|
161
|
+
delete parentEntry.children[name];
|
|
162
|
+
loadedFiles.delete(absolute);
|
|
163
|
+
loadedDirectories.delete(absolute);
|
|
164
|
+
emit([{
|
|
165
|
+
type: "deletePath",
|
|
166
|
+
path: absolute
|
|
167
|
+
}]);
|
|
168
|
+
};
|
|
169
|
+
const rename = async (from, to) => {
|
|
170
|
+
const fromAbs = path.default.resolve(from);
|
|
171
|
+
const toAbs = path.default.resolve(to);
|
|
172
|
+
if (!isWithinRoot(fromAbs) || !isWithinRoot(toAbs)) return;
|
|
173
|
+
const fromSegments = toSegments(fromAbs);
|
|
174
|
+
if (!fromSegments || fromSegments.length === 0) return;
|
|
175
|
+
const fromName = fromSegments[fromSegments.length - 1];
|
|
176
|
+
const fromParentSegments = fromSegments.slice(0, -1);
|
|
177
|
+
const fromParentEntry = fromParentSegments.length === 0 ? rootEntry : getEntryBySegments(fromParentSegments);
|
|
178
|
+
if (!fromParentEntry || fromParentEntry.type !== "dir") return;
|
|
179
|
+
const entry = fromParentEntry.children[fromName];
|
|
180
|
+
if (!entry) return;
|
|
181
|
+
delete fromParentEntry.children[fromName];
|
|
182
|
+
const toPathSegments = toSegments(toAbs);
|
|
183
|
+
if (!toPathSegments || toPathSegments.length === 0) return;
|
|
184
|
+
const toName = toPathSegments[toPathSegments.length - 1];
|
|
185
|
+
const toParent = ensureDirectoryBySegments(toPathSegments.slice(0, -1));
|
|
186
|
+
toParent.children[toName] = entry;
|
|
187
|
+
if (loadedFiles.has(fromAbs)) {
|
|
188
|
+
loadedFiles.delete(fromAbs);
|
|
189
|
+
loadedFiles.add(toAbs);
|
|
190
|
+
}
|
|
191
|
+
if (loadedDirectories.has(fromAbs)) {
|
|
192
|
+
loadedDirectories.delete(fromAbs);
|
|
193
|
+
loadedDirectories.add(toAbs);
|
|
194
|
+
}
|
|
195
|
+
emit([{
|
|
196
|
+
type: "rename",
|
|
197
|
+
from: fromAbs,
|
|
198
|
+
to: toAbs
|
|
199
|
+
}]);
|
|
200
|
+
};
|
|
201
|
+
const applyChanges = async (changes) => {
|
|
202
|
+
for (const change of changes) {
|
|
203
|
+
if (change.type === "writeFile") {
|
|
204
|
+
await writeFile(change.path, change.content);
|
|
205
|
+
continue;
|
|
206
|
+
}
|
|
207
|
+
if (change.type === "mkdir") {
|
|
208
|
+
await mkdir(change.path);
|
|
209
|
+
continue;
|
|
210
|
+
}
|
|
211
|
+
if (change.type === "deletePath") {
|
|
212
|
+
await deletePath(change.path);
|
|
213
|
+
continue;
|
|
214
|
+
}
|
|
215
|
+
if (change.type === "rename") {
|
|
216
|
+
await rename(change.from, change.to);
|
|
217
|
+
continue;
|
|
218
|
+
}
|
|
219
|
+
if (change.type === "setTree") setTree(change.tree);
|
|
220
|
+
}
|
|
221
|
+
};
|
|
222
|
+
const onDidChange = (listener) => {
|
|
223
|
+
listeners.add(listener);
|
|
224
|
+
return () => {
|
|
225
|
+
listeners.delete(listener);
|
|
226
|
+
};
|
|
227
|
+
};
|
|
228
|
+
const stat = async (targetPath) => {
|
|
229
|
+
const absolute = path.default.resolve(targetPath);
|
|
230
|
+
if (isWithinRoot(absolute)) {
|
|
231
|
+
const segments = toSegments(absolute);
|
|
232
|
+
if (segments) {
|
|
233
|
+
const existing = getEntryBySegments(segments);
|
|
234
|
+
if (existing) return {
|
|
235
|
+
isFile: existing.type === "file",
|
|
236
|
+
isDirectory: existing.type === "dir"
|
|
237
|
+
};
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
try {
|
|
241
|
+
const stats = await fs_promises.default.stat(absolute);
|
|
242
|
+
if (isWithinRoot(absolute)) {
|
|
243
|
+
const segments = toSegments(absolute);
|
|
244
|
+
if (segments) {
|
|
245
|
+
if (stats.isDirectory()) ensureDirectoryBySegments(segments);
|
|
246
|
+
else if (segments.length > 0) {
|
|
247
|
+
const name = segments[segments.length - 1];
|
|
248
|
+
const parent = ensureDirectoryBySegments(segments.slice(0, -1));
|
|
249
|
+
parent.children[name] ??= {
|
|
250
|
+
type: "file",
|
|
251
|
+
content: ""
|
|
252
|
+
};
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
return {
|
|
257
|
+
isFile: stats.isFile(),
|
|
258
|
+
isDirectory: stats.isDirectory()
|
|
259
|
+
};
|
|
260
|
+
} catch {
|
|
261
|
+
return null;
|
|
262
|
+
}
|
|
263
|
+
};
|
|
264
|
+
const readDirectory = async (targetPath) => {
|
|
265
|
+
const absolute = path.default.resolve(targetPath);
|
|
266
|
+
if (isWithinRoot(absolute)) {
|
|
267
|
+
await ensureDirectoryLoaded(absolute);
|
|
268
|
+
const segments = toSegments(absolute);
|
|
269
|
+
if (!segments) return null;
|
|
270
|
+
const entry = getEntryBySegments(segments);
|
|
271
|
+
if (!entry || entry.type !== "dir") return null;
|
|
272
|
+
return Object.entries(entry.children).map(([name, child]) => ({
|
|
273
|
+
name,
|
|
274
|
+
isDirectory: child.type === "dir"
|
|
275
|
+
}));
|
|
276
|
+
}
|
|
277
|
+
return listDirectory(absolute);
|
|
278
|
+
};
|
|
279
|
+
const readFile = async (targetPath) => {
|
|
280
|
+
const absolute = path.default.resolve(targetPath);
|
|
281
|
+
if (isWithinRoot(absolute)) {
|
|
282
|
+
const segments = toSegments(absolute);
|
|
283
|
+
if (segments) {
|
|
284
|
+
const existing = getEntryBySegments(segments);
|
|
285
|
+
if (existing && existing.type === "file") {
|
|
286
|
+
if (loadedFiles.has(absolute)) return existing.content;
|
|
287
|
+
try {
|
|
288
|
+
const content = await fs_promises.default.readFile(absolute, "utf-8");
|
|
289
|
+
existing.content = content;
|
|
290
|
+
loadedFiles.add(absolute);
|
|
291
|
+
return content;
|
|
292
|
+
} catch {
|
|
293
|
+
return null;
|
|
294
|
+
}
|
|
295
|
+
}
|
|
296
|
+
}
|
|
297
|
+
try {
|
|
298
|
+
const content = await fs_promises.default.readFile(absolute, "utf-8");
|
|
299
|
+
await writeFile(absolute, content);
|
|
300
|
+
return content;
|
|
301
|
+
} catch {
|
|
302
|
+
return null;
|
|
303
|
+
}
|
|
304
|
+
}
|
|
305
|
+
try {
|
|
306
|
+
return await fs_promises.default.readFile(absolute, "utf-8");
|
|
307
|
+
} catch {
|
|
308
|
+
return null;
|
|
309
|
+
}
|
|
310
|
+
};
|
|
311
|
+
const findFile = async (startPath, targetName) => {
|
|
312
|
+
const absoluteStart = path.default.resolve(startPath);
|
|
313
|
+
try {
|
|
314
|
+
const found = await findFileInDirectory(absoluteStart, targetName, ignoreDirs);
|
|
315
|
+
if (found && isWithinRoot(found)) {
|
|
316
|
+
const foundSegments = toSegments(found);
|
|
317
|
+
if (foundSegments && foundSegments.length > 0) {
|
|
318
|
+
const name = foundSegments[foundSegments.length - 1];
|
|
319
|
+
const parent = ensureDirectoryBySegments(foundSegments.slice(0, -1));
|
|
320
|
+
parent.children[name] ??= {
|
|
321
|
+
type: "file",
|
|
322
|
+
content: ""
|
|
323
|
+
};
|
|
324
|
+
}
|
|
325
|
+
}
|
|
326
|
+
return found;
|
|
327
|
+
} catch {
|
|
328
|
+
return null;
|
|
329
|
+
}
|
|
330
|
+
};
|
|
331
|
+
const getResourceDirectory = async (urls) => {
|
|
332
|
+
return readDirectory(path.default.join(root, ...urls));
|
|
333
|
+
};
|
|
334
|
+
const getAllTextWithScene = async () => {
|
|
335
|
+
const target = path.default.join(root, sceneDir);
|
|
336
|
+
const entries = await readDirectory(target);
|
|
337
|
+
if (!entries) return null;
|
|
338
|
+
const result = {};
|
|
339
|
+
for (const entry of entries) if (!entry.isDirectory && entry.name.endsWith(".txt")) {
|
|
340
|
+
const fullPath = path.default.join(target, entry.name);
|
|
341
|
+
const text = await readFile(fullPath);
|
|
342
|
+
if (text === null) continue;
|
|
343
|
+
result[entry.name] = {
|
|
344
|
+
path: fullPath,
|
|
345
|
+
name: entry.name,
|
|
346
|
+
text,
|
|
347
|
+
fullPath
|
|
348
|
+
};
|
|
349
|
+
}
|
|
350
|
+
return result;
|
|
351
|
+
};
|
|
352
|
+
return {
|
|
353
|
+
root,
|
|
354
|
+
currentDirectory: () => root,
|
|
355
|
+
join: (...parts) => path.default.join(...parts),
|
|
356
|
+
stat,
|
|
357
|
+
readDirectory,
|
|
358
|
+
readFile,
|
|
359
|
+
findFile,
|
|
360
|
+
getResourceDirectory,
|
|
361
|
+
getAllTextWithScene,
|
|
362
|
+
getTree,
|
|
363
|
+
setTree,
|
|
364
|
+
writeFile,
|
|
365
|
+
deletePath,
|
|
366
|
+
mkdir,
|
|
367
|
+
rename,
|
|
368
|
+
applyChanges,
|
|
369
|
+
onDidChange
|
|
370
|
+
};
|
|
371
|
+
}
|
|
372
|
+
|
|
373
|
+
//#endregion
|
|
374
|
+
exports.createNodeFileSystem = createNodeFileSystem;
|
package/build/node.d.cts
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { VirtualFileSystem } from "./index.cjs";
|
|
2
|
+
|
|
3
|
+
//#region src/node.d.ts
|
|
4
|
+
type NodeFileSystemOptions = {
|
|
5
|
+
root: string;
|
|
6
|
+
ignoreDirs?: string[];
|
|
7
|
+
sceneDir?: string;
|
|
8
|
+
};
|
|
9
|
+
declare function createNodeFileSystem(options: NodeFileSystemOptions): VirtualFileSystem;
|
|
10
|
+
//#endregion
|
|
11
|
+
export { NodeFileSystemOptions, createNodeFileSystem };
|
package/build/node.d.mts
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { VirtualFileSystem } from "./index.mjs";
|
|
2
|
+
|
|
3
|
+
//#region src/node.d.ts
|
|
4
|
+
type NodeFileSystemOptions = {
|
|
5
|
+
root: string;
|
|
6
|
+
ignoreDirs?: string[];
|
|
7
|
+
sceneDir?: string;
|
|
8
|
+
};
|
|
9
|
+
declare function createNodeFileSystem(options: NodeFileSystemOptions): VirtualFileSystem;
|
|
10
|
+
//#endregion
|
|
11
|
+
export { NodeFileSystemOptions, createNodeFileSystem };
|