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