wurzel 0.0.4 → 0.0.6
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/dist/bin/wurzel.js +1 -1
- package/dist/lib/index.d.ts +8 -3
- package/dist/lib/index.js +203 -187
- package/package.json +8 -4
package/dist/bin/wurzel.js
CHANGED
package/dist/lib/index.d.ts
CHANGED
|
@@ -1,8 +1,13 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
1
|
+
import type { TResolveImportPathFunc } from "es6-debug-server";
|
|
2
|
+
import type Express from "express";
|
|
3
|
+
declare const defaultResolveImportPath: TResolveImportPathFunc;
|
|
4
|
+
declare const expressRouter: ({ express, baseFolder, fileEndings, maxAnalyzeCacheSize, maxTranspileCacheSize, resolveImportPath }: {
|
|
5
|
+
express: typeof Express;
|
|
3
6
|
baseFolder: string;
|
|
4
7
|
fileEndings?: string[];
|
|
5
8
|
maxAnalyzeCacheSize?: number;
|
|
6
9
|
maxTranspileCacheSize?: number;
|
|
10
|
+
resolveImportPath?: TResolveImportPathFunc;
|
|
7
11
|
}) => any;
|
|
8
|
-
export { expressRouter, };
|
|
12
|
+
export { expressRouter, defaultResolveImportPath };
|
|
13
|
+
export type { TResolveImportPathFunc };
|
package/dist/lib/index.js
CHANGED
|
@@ -1,247 +1,263 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import {
|
|
2
|
+
create as createServer,
|
|
3
|
+
ETryReadErrorCode,
|
|
4
|
+
defaultCodeAnalyzer
|
|
5
|
+
} from "es6-debug-server";
|
|
6
|
+
/*import type {
|
|
7
|
+
ICodeAnalyzeResult,
|
|
8
|
+
TResolveImportPathFunc,
|
|
9
|
+
} from "es6-debug-server";*/
|
|
2
10
|
import { readFile } from "node:fs/promises";
|
|
3
11
|
import { pathToFileURL, fileURLToPath } from "node:url";
|
|
4
12
|
import { createHash } from "node:crypto";
|
|
5
13
|
import { resolve as resolveImport } from "import-meta-resolve";
|
|
6
|
-
// @ts-
|
|
14
|
+
// @ts-expect-error missing types
|
|
7
15
|
import { transpileCode } from "commentscript";
|
|
8
16
|
import { LRUCache } from "lru-cache";
|
|
9
|
-
|
|
10
|
-
// @ts-ignore
|
|
11
|
-
/*import type { ICodeAnalyzeResult } from "es6-debug-server";*/
|
|
17
|
+
/*import type Express from "express";*/
|
|
12
18
|
|
|
13
19
|
const md5 = ({ content }/*: { content: string }*/) => {
|
|
14
|
-
|
|
20
|
+
return createHash("md5").update(content).digest("hex");
|
|
15
21
|
};
|
|
16
22
|
|
|
17
23
|
/*interface ITranspileErrorResult {
|
|
18
|
-
|
|
19
|
-
|
|
24
|
+
error: Error;
|
|
25
|
+
transpiled?: undefined;
|
|
20
26
|
}*/;
|
|
21
27
|
|
|
22
28
|
/*interface ITranspileSuccessResult {
|
|
23
|
-
|
|
24
|
-
|
|
29
|
+
error: undefined;
|
|
30
|
+
transpiled: string;
|
|
25
31
|
}*/;
|
|
26
32
|
|
|
27
33
|
/*type TTranspileResult = ITranspileErrorResult | ITranspileSuccessResult;*/
|
|
28
34
|
|
|
35
|
+
const defaultResolveImportPath/*: TResolveImportPathFunc*/ = async ({ importer, specifier }) => {
|
|
36
|
+
const parentUrl = pathToFileURL(importer);
|
|
37
|
+
let resolvedUrl/*: string | undefined*/ = undefined;
|
|
38
|
+
|
|
39
|
+
try {
|
|
40
|
+
resolvedUrl = resolveImport(specifier, parentUrl.toString());
|
|
41
|
+
} catch (error) {
|
|
42
|
+
return {
|
|
43
|
+
error: error/* as Error*/
|
|
44
|
+
};
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
const resolvedPath = fileURLToPath(resolvedUrl);
|
|
48
|
+
return {
|
|
49
|
+
error: undefined,
|
|
50
|
+
filePath: resolvedPath
|
|
51
|
+
};
|
|
52
|
+
};
|
|
53
|
+
|
|
29
54
|
const expressRouter = ({
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
55
|
+
express,
|
|
56
|
+
baseFolder,
|
|
57
|
+
fileEndings = [".js", ".ts"],
|
|
33
58
|
|
|
34
|
-
|
|
35
|
-
|
|
59
|
+
maxAnalyzeCacheSize = 1 * 1024 * 1024,
|
|
60
|
+
maxTranspileCacheSize = 64 * 1024 * 1024,
|
|
61
|
+
|
|
62
|
+
resolveImportPath = defaultResolveImportPath
|
|
36
63
|
}/*: {
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
64
|
+
express: typeof Express,
|
|
65
|
+
baseFolder: string,
|
|
66
|
+
fileEndings?: string[],
|
|
40
67
|
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
}*/) => {
|
|
68
|
+
maxAnalyzeCacheSize?: number,
|
|
69
|
+
maxTranspileCacheSize?: number,
|
|
44
70
|
|
|
45
|
-
|
|
71
|
+
resolveImportPath?: TResolveImportPathFunc
|
|
72
|
+
}*/) => {
|
|
46
73
|
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
74
|
+
const router = express.Router();
|
|
75
|
+
|
|
76
|
+
const transpileCache = new LRUCache/*<string, string>*/({
|
|
77
|
+
maxSize: maxTranspileCacheSize,
|
|
78
|
+
sizeCalculation: (value, key) => {
|
|
79
|
+
return key.length + value.length;
|
|
80
|
+
}
|
|
81
|
+
});
|
|
82
|
+
|
|
83
|
+
const analyzeCache = new LRUCache/*<string, ICodeAnalyzeResult>*/({
|
|
84
|
+
maxSize: maxAnalyzeCacheSize,
|
|
85
|
+
sizeCalculation: (value, key) => {
|
|
86
|
+
return key.length + JSON.stringify(value).length;
|
|
87
|
+
}
|
|
88
|
+
});
|
|
89
|
+
|
|
90
|
+
// eslint-disable-next-line complexity
|
|
91
|
+
const maybeTranspile = async ({ filePath, code }/*: { filePath: string, code: string }*/)/*: Promise<TTranspileResult>*/ => {
|
|
92
|
+
if (filePath.endsWith(".js") || filePath.endsWith(".mjs")) {
|
|
93
|
+
return {
|
|
94
|
+
error: undefined,
|
|
95
|
+
transpiled: code
|
|
96
|
+
};
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
const hash = md5({ content: code });
|
|
100
|
+
|
|
101
|
+
const cached = transpileCache.get(hash);
|
|
102
|
+
if (cached !== undefined) {
|
|
103
|
+
return {
|
|
104
|
+
error: undefined,
|
|
105
|
+
transpiled: cached
|
|
106
|
+
};
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
let transpiled/*: string | undefined*/ = undefined;
|
|
110
|
+
|
|
111
|
+
try {
|
|
112
|
+
const result = await transpileCode({ code });
|
|
113
|
+
transpiled = result.transpiledCode;
|
|
114
|
+
} catch (ex) {
|
|
115
|
+
return {
|
|
116
|
+
error: ex/* as Error*/,
|
|
117
|
+
};
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
if (transpiled === undefined) {
|
|
121
|
+
throw Error("BUG: transpiled code is undefined");
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
transpileCache.set(hash, transpiled);
|
|
125
|
+
|
|
126
|
+
return {
|
|
127
|
+
error: undefined,
|
|
128
|
+
transpiled
|
|
129
|
+
};
|
|
130
|
+
};
|
|
53
131
|
|
|
54
|
-
|
|
55
|
-
maxSize: maxAnalyzeCacheSize,
|
|
56
|
-
sizeCalculation: (value, key) => {
|
|
57
|
-
return key.length + JSON.stringify(value).length;
|
|
58
|
-
}
|
|
59
|
-
});
|
|
132
|
+
const server = createServer({
|
|
60
133
|
|
|
61
|
-
|
|
134
|
+
scriptRootFolder: baseFolder,
|
|
62
135
|
|
|
63
|
-
|
|
64
|
-
return {
|
|
65
|
-
error: undefined,
|
|
66
|
-
transpiled: code
|
|
67
|
-
};
|
|
68
|
-
}
|
|
136
|
+
tryReadScriptAsString: async ({ filePath }) => {
|
|
69
137
|
|
|
70
|
-
|
|
138
|
+
const { error: readError, content } = await readFile(filePath, "utf8").then((fileContent) => {
|
|
139
|
+
return { error: undefined, content: fileContent };
|
|
140
|
+
}, (err/*: NodeJS.ErrnoException*/) => {
|
|
141
|
+
return { error: err, content: undefined };
|
|
142
|
+
});
|
|
71
143
|
|
|
72
|
-
|
|
73
|
-
if (
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
144
|
+
if (readError !== undefined) {
|
|
145
|
+
if (readError.code === "ENOENT") {
|
|
146
|
+
return {
|
|
147
|
+
error: {
|
|
148
|
+
code: ETryReadErrorCode.FILE_NOT_FOUND,
|
|
149
|
+
message: readError.message,
|
|
150
|
+
}
|
|
151
|
+
};
|
|
78
152
|
}
|
|
79
153
|
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
error: ex/* as Error*/,
|
|
88
|
-
};
|
|
89
|
-
}
|
|
154
|
+
return {
|
|
155
|
+
error: {
|
|
156
|
+
code: ETryReadErrorCode.IO_ERROR,
|
|
157
|
+
message: readError.message,
|
|
158
|
+
}
|
|
159
|
+
};
|
|
160
|
+
}
|
|
90
161
|
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
162
|
+
const { error: transpileError, transpiled } = await maybeTranspile({
|
|
163
|
+
filePath,
|
|
164
|
+
code: content
|
|
165
|
+
});
|
|
94
166
|
|
|
95
|
-
|
|
167
|
+
if (transpileError !== undefined) {
|
|
168
|
+
console.error(transpileError);
|
|
96
169
|
|
|
97
170
|
return {
|
|
98
|
-
|
|
99
|
-
|
|
171
|
+
error: {
|
|
172
|
+
code: ETryReadErrorCode.IO_ERROR,
|
|
173
|
+
message: transpileError.message,
|
|
174
|
+
}
|
|
100
175
|
};
|
|
101
|
-
|
|
176
|
+
}
|
|
102
177
|
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
const { error: readError, content } = await readFile(filePath, "utf8").then((content) => {
|
|
110
|
-
return { error: undefined, content };
|
|
111
|
-
}, (err/*: NodeJS.ErrnoException*/) => {
|
|
112
|
-
return { error: err, content: undefined };
|
|
113
|
-
});
|
|
114
|
-
|
|
115
|
-
if (readError !== undefined) {
|
|
116
|
-
if (readError.code === "ENOENT") {
|
|
117
|
-
return {
|
|
118
|
-
error: {
|
|
119
|
-
code: ETryReadErrorCode.FILE_NOT_FOUND,
|
|
120
|
-
message: readError.message,
|
|
121
|
-
}
|
|
122
|
-
};
|
|
123
|
-
}
|
|
124
|
-
|
|
125
|
-
return {
|
|
126
|
-
error: {
|
|
127
|
-
code: ETryReadErrorCode.IO_ERROR,
|
|
128
|
-
message: readError.message,
|
|
129
|
-
}
|
|
130
|
-
};
|
|
131
|
-
}
|
|
178
|
+
return {
|
|
179
|
+
error: undefined,
|
|
180
|
+
content: transpiled
|
|
181
|
+
};
|
|
182
|
+
},
|
|
132
183
|
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
});
|
|
184
|
+
// cache code analysis
|
|
185
|
+
analyzeCode: ({ code }) => {
|
|
186
|
+
const hash = md5({ content: code });
|
|
137
187
|
|
|
138
|
-
|
|
139
|
-
|
|
188
|
+
const cached = analyzeCache.get(hash);
|
|
189
|
+
if (cached !== undefined) {
|
|
190
|
+
return {
|
|
191
|
+
error: undefined,
|
|
192
|
+
result: cached
|
|
193
|
+
};
|
|
194
|
+
}
|
|
140
195
|
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
}
|
|
196
|
+
const analyzeResult = defaultCodeAnalyzer({ code });
|
|
197
|
+
if (analyzeResult.error !== undefined) {
|
|
198
|
+
return {
|
|
199
|
+
error: analyzeResult.error
|
|
200
|
+
};
|
|
201
|
+
}
|
|
148
202
|
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
};
|
|
153
|
-
},
|
|
203
|
+
analyzeCache.set(hash, analyzeResult.result);
|
|
204
|
+
return analyzeResult;
|
|
205
|
+
},
|
|
154
206
|
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
const hash = md5({ content: code });
|
|
207
|
+
resolveImportPath
|
|
208
|
+
});
|
|
158
209
|
|
|
159
|
-
|
|
160
|
-
if (cached !== undefined) {
|
|
161
|
-
return {
|
|
162
|
-
error: undefined,
|
|
163
|
-
result: cached
|
|
164
|
-
};
|
|
165
|
-
}
|
|
210
|
+
router.use((req, res, next) => {
|
|
166
211
|
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
error: analyzeResult.error
|
|
171
|
-
};
|
|
172
|
-
}
|
|
212
|
+
if (req.method === "HEAD") {
|
|
213
|
+
throw Error("HEAD not supported yet");
|
|
214
|
+
}
|
|
173
215
|
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
},
|
|
216
|
+
const isScript = fileEndings.some((ending) => req.url.endsWith(ending));
|
|
217
|
+
if (isScript) {
|
|
177
218
|
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
let resolvedUrl/*: string | undefined*/ = undefined;
|
|
219
|
+
server.handleRequest({
|
|
220
|
+
uri: req.url,
|
|
181
221
|
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
return {
|
|
187
|
-
error: error/* as Error*/
|
|
188
|
-
};
|
|
189
|
-
}
|
|
222
|
+
handleRedirect: ({ uri }) => {
|
|
223
|
+
const redirectLocation = `${req.baseUrl}${uri}`;
|
|
224
|
+
res.redirect(redirectLocation);
|
|
225
|
+
},
|
|
190
226
|
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
227
|
+
handleContent: ({ contentType, content }) => {
|
|
228
|
+
res.writeHead(200, {
|
|
229
|
+
"Content-Type": contentType
|
|
230
|
+
});
|
|
231
|
+
res.end(content);
|
|
196
232
|
},
|
|
197
|
-
});
|
|
198
233
|
|
|
199
|
-
|
|
200
|
-
|
|
234
|
+
handleFileNotFound: () => {
|
|
235
|
+
res.status(404).end("not found");
|
|
236
|
+
},
|
|
201
237
|
|
|
202
|
-
|
|
203
|
-
|
|
238
|
+
handleInternalError: ({ error }) => {
|
|
239
|
+
console.error(error);
|
|
240
|
+
res.status(500).end("internal server error");
|
|
204
241
|
}
|
|
242
|
+
});
|
|
205
243
|
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
server.handleRequest({
|
|
210
|
-
uri: req.url,
|
|
211
|
-
|
|
212
|
-
handleRedirect: ({ uri }) => {
|
|
213
|
-
const redirectLocation = `${req.baseUrl}${uri}`;
|
|
214
|
-
res.redirect(redirectLocation);
|
|
215
|
-
},
|
|
216
|
-
|
|
217
|
-
handleContent: ({ contentType, content }) => {
|
|
218
|
-
res.writeHead(200, {
|
|
219
|
-
"Content-Type": contentType
|
|
220
|
-
});
|
|
221
|
-
res.end(content);
|
|
222
|
-
},
|
|
223
|
-
|
|
224
|
-
handleFileNotFound: () => {
|
|
225
|
-
res.status(404).end("not found");
|
|
226
|
-
},
|
|
244
|
+
return;
|
|
245
|
+
}
|
|
227
246
|
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
res.status(500).end("internal server error");
|
|
231
|
-
}
|
|
232
|
-
});
|
|
247
|
+
next();
|
|
248
|
+
});
|
|
233
249
|
|
|
234
|
-
|
|
235
|
-
}
|
|
236
|
-
|
|
237
|
-
next();
|
|
238
|
-
});
|
|
239
|
-
|
|
240
|
-
router.use(express.static(baseFolder));
|
|
250
|
+
router.use(express.static(baseFolder));
|
|
241
251
|
|
|
242
|
-
|
|
252
|
+
return router;
|
|
243
253
|
};
|
|
244
254
|
|
|
245
255
|
export {
|
|
246
|
-
|
|
256
|
+
expressRouter,
|
|
257
|
+
|
|
258
|
+
defaultResolveImportPath
|
|
247
259
|
};
|
|
260
|
+
|
|
261
|
+
/*export type {
|
|
262
|
+
TResolveImportPathFunc
|
|
263
|
+
};*/
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "wurzel",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.6",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"files": [
|
|
6
6
|
"dist"
|
|
@@ -8,21 +8,25 @@
|
|
|
8
8
|
"main": "dist/lib/index.js",
|
|
9
9
|
"dependencies": {
|
|
10
10
|
"commentscript": "^0.0.5",
|
|
11
|
-
"es6-debug-server": "^0.0.
|
|
11
|
+
"es6-debug-server": "^0.0.4",
|
|
12
12
|
"esm-resource": "^0.0.1",
|
|
13
13
|
"express": "^4.19.2",
|
|
14
14
|
"import-meta-resolve": "^4.0.0",
|
|
15
15
|
"lru-cache": "^10.2.1"
|
|
16
16
|
},
|
|
17
17
|
"devDependencies": {
|
|
18
|
+
"@eslint/js": "^9.7.0",
|
|
18
19
|
"@types/express": "^4.17.21",
|
|
19
20
|
"@types/node": "^20.12.7",
|
|
20
|
-
"deno-node": "^0.0.
|
|
21
|
+
"deno-node": "^0.0.5",
|
|
22
|
+
"typescript-eslint": "^7.16.1"
|
|
21
23
|
},
|
|
22
24
|
"bin": {
|
|
23
25
|
"wurzel": "dist/bin/wurzel.js"
|
|
24
26
|
},
|
|
25
27
|
"scripts": {
|
|
26
|
-
"build": "rm -rf dist/ && deno-node-build --root . --out dist/ --entry lib/index.ts --entry bin/wurzel.ts"
|
|
28
|
+
"build": "rm -rf dist/ && deno-node-build --root . --out dist/ --entry lib/index.ts --entry bin/wurzel.ts",
|
|
29
|
+
"lint": "eslint .",
|
|
30
|
+
"test": "true"
|
|
27
31
|
}
|
|
28
32
|
}
|