@zipadee/javascript 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/LICENSE +7 -0
- package/README.md +35 -0
- package/index.d.ts +2 -0
- package/index.d.ts.map +1 -0
- package/index.js +2 -0
- package/index.js.map +1 -0
- package/lib/serve.d.ts +27 -0
- package/lib/serve.d.ts.map +1 -0
- package/lib/serve.js +93 -0
- package/lib/serve.js.map +1 -0
- package/package.json +63 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
Copyright 2024 Justin Fagnani
|
|
2
|
+
|
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the “Software”), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
|
4
|
+
|
|
5
|
+
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
|
6
|
+
|
|
7
|
+
THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
# @zipadee/javascript
|
|
2
|
+
|
|
3
|
+
Zipadee middleware for serving JavaScript modules
|
|
4
|
+
|
|
5
|
+
> [!CAUTION] Zipadee is very early, under construction, will change a lot, and
|
|
6
|
+
> may never be sufficiently maintained for any level of use. If you want to try
|
|
7
|
+
> it, please consider contributing!
|
|
8
|
+
|
|
9
|
+
`@zipadee/javascript` serves static JavaScript files from disk and transforms
|
|
10
|
+
them to resolve bare import specifiers to relative paths.
|
|
11
|
+
|
|
12
|
+
## Usage
|
|
13
|
+
|
|
14
|
+
```ts
|
|
15
|
+
import {App} from 'zipadee';
|
|
16
|
+
import {serve as serveJS} from '@zipadee/javascript';
|
|
17
|
+
import {serve} from '@zipadee/static';
|
|
18
|
+
|
|
19
|
+
const app = new App();
|
|
20
|
+
|
|
21
|
+
// Serves all JS files in the directory {cwd}/files/
|
|
22
|
+
app.use(serveJS({root: 'files'}));
|
|
23
|
+
|
|
24
|
+
// Serves all other files in the directory {cwd}/files/
|
|
25
|
+
app.use(serve({root: 'files'}));
|
|
26
|
+
|
|
27
|
+
app.listen();
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
## Options
|
|
31
|
+
|
|
32
|
+
- `base`: The directory that paths are resolved against to find on disk. This directory is typically where first-party JavaScript files are located. Must be a subpath of `root`. Defaults to the value of `root`.
|
|
33
|
+
- `root`: The root directory that files are restricted to. Import specifiers can resolve to a path outside of `base` (this is common with npm dependencies, and monorepos), but they are disallowed to be served from outside of `root`. Defaults to the current working directory.
|
|
34
|
+
- `rootPathPrefix`: Imports that resolve to outside of the base directory are prefixed with the `rootPathPrefix` in order to resolve them against the `root` directory instead of the `base` directory. Defaults to `'/__root__'`.
|
|
35
|
+
- `extensions`: Array of file extensions to serve. Defaults to `['.js', '.mjs']`. Files not matching these extensions will not be handled by this middleware. They can be served by other downstream middleware.
|
package/index.d.ts
ADDED
package/index.d.ts.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["src/index.ts"],"names":[],"mappings":"AAAA,cAAc,gBAAgB,CAAC"}
|
package/index.js
ADDED
package/index.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["src/index.ts"],"names":[],"mappings":"AAAA,cAAc,gBAAgB,CAAC"}
|
package/lib/serve.d.ts
ADDED
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import {type Middleware} from '@zipadee/core';
|
|
2
|
+
export interface Options {
|
|
3
|
+
/**
|
|
4
|
+
* Root directory to restrict file access. Defaults to the current working
|
|
5
|
+
* directory.
|
|
6
|
+
*/
|
|
7
|
+
root?: string;
|
|
8
|
+
/**
|
|
9
|
+
* Base path to resolve imports from. Defaults to the current working
|
|
10
|
+
* directory.
|
|
11
|
+
*/
|
|
12
|
+
base?: string;
|
|
13
|
+
/**
|
|
14
|
+
* Imports resolved to outside of the base path will be prefixed with this
|
|
15
|
+
* string. Defaults to `/__root__`.
|
|
16
|
+
*/
|
|
17
|
+
rootPathPrefix?: string;
|
|
18
|
+
/**
|
|
19
|
+
* Array of file extensions to serve. Defaults to `['.js', '.mjs']`.
|
|
20
|
+
*/
|
|
21
|
+
extensions?: string[];
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* Serve static JavaScript files from a `root` directory.
|
|
25
|
+
*/
|
|
26
|
+
export declare const serve: (opts: Options) => Middleware;
|
|
27
|
+
//# sourceMappingURL=serve.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"serve.d.ts","sourceRoot":"","sources":["../src/lib/serve.ts"],"names":[],"mappings":"AAAA,OAAO,EAAY,KAAK,UAAU,EAAC,MAAM,eAAe,CAAC;AAczD,MAAM,WAAW,OAAO;IACtB;;;OAGG;IACH,IAAI,CAAC,EAAE,MAAM,CAAC;IAEd;;;OAGG;IACH,IAAI,CAAC,EAAE,MAAM,CAAC;IAEd;;;OAGG;IACH,cAAc,CAAC,EAAE,MAAM,CAAC;IAExB;;OAEG;IACH,UAAU,CAAC,EAAE,MAAM,EAAE,CAAC;CACvB;AAYD;;GAEG;AACH,eAAO,MAAM,KAAK,SAAU,OAAO,KAAG,UAiFrC,CAAC"}
|
package/lib/serve.js
ADDED
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
import {HttpError} from '@zipadee/core';
|
|
2
|
+
import {
|
|
3
|
+
decodePath,
|
|
4
|
+
pathIsHidden,
|
|
5
|
+
resolvePath,
|
|
6
|
+
stat,
|
|
7
|
+
} from '@zipadee/static/lib/utils.js';
|
|
8
|
+
import fs from 'node:fs/promises';
|
|
9
|
+
import path from 'node:path';
|
|
10
|
+
import {parse, init} from 'es-module-lexer';
|
|
11
|
+
import {moduleResolve} from 'import-meta-resolve';
|
|
12
|
+
await init;
|
|
13
|
+
// TODO:
|
|
14
|
+
// - Add caching for both specifier resolution and files
|
|
15
|
+
// - Prepopulate the cache based on the module graph
|
|
16
|
+
// - Add support for dynamic imports
|
|
17
|
+
// - Add support for import maps?
|
|
18
|
+
// - Test support for import attributes
|
|
19
|
+
// - Add option for import conditions
|
|
20
|
+
// - Add support for transforming <script type="module"> tags in HTML
|
|
21
|
+
// - Automatically handle CORS requests?
|
|
22
|
+
/**
|
|
23
|
+
* Serve static JavaScript files from a `root` directory.
|
|
24
|
+
*/
|
|
25
|
+
export const serve = (opts) => {
|
|
26
|
+
const root =
|
|
27
|
+
opts.root === undefined ? process.cwd() : path.resolve(opts.root);
|
|
28
|
+
const base = opts.base === undefined ? root : resolvePath(root, opts.base);
|
|
29
|
+
const rootFolderName = opts.rootPathPrefix ?? '/__root__';
|
|
30
|
+
const extensions = opts.extensions ?? ['.js', '.mjs'];
|
|
31
|
+
return async (req, res, next) => {
|
|
32
|
+
if (!(req.method === 'HEAD' || req.method === 'GET')) {
|
|
33
|
+
// TODO: implement HEAD?
|
|
34
|
+
return await next();
|
|
35
|
+
}
|
|
36
|
+
let filePath = decodePath(req.path);
|
|
37
|
+
const parsedPath = path.parse(filePath);
|
|
38
|
+
// Only serve JavaScript files
|
|
39
|
+
if (!extensions.includes(parsedPath.ext)) {
|
|
40
|
+
return await next();
|
|
41
|
+
}
|
|
42
|
+
if (filePath.startsWith(rootFolderName)) {
|
|
43
|
+
filePath = filePath.substring(rootFolderName.length);
|
|
44
|
+
filePath = filePath.slice(parsedPath.root.length);
|
|
45
|
+
filePath = resolvePath(root, filePath);
|
|
46
|
+
} else {
|
|
47
|
+
filePath = filePath.slice(parsedPath.root.length);
|
|
48
|
+
filePath = resolvePath(base, filePath);
|
|
49
|
+
}
|
|
50
|
+
if (pathIsHidden(base, filePath)) {
|
|
51
|
+
return await next();
|
|
52
|
+
}
|
|
53
|
+
const stats = await stat(filePath);
|
|
54
|
+
if (stats.isDirectory()) {
|
|
55
|
+
return await next();
|
|
56
|
+
}
|
|
57
|
+
const source = await fs.readFile(filePath, 'utf8');
|
|
58
|
+
const [imports, _exports, _facade, _hasModuleSyntax] = parse(
|
|
59
|
+
source,
|
|
60
|
+
filePath,
|
|
61
|
+
);
|
|
62
|
+
let output = '';
|
|
63
|
+
let lastIndex = 0;
|
|
64
|
+
for (const impt of imports) {
|
|
65
|
+
const {t: type, s: start, e: end, n: unescaped} = impt;
|
|
66
|
+
if (type === 1) {
|
|
67
|
+
// Static import
|
|
68
|
+
const importSpecifier = unescaped || source.substring(start, end);
|
|
69
|
+
const fileURL = new URL(filePath, 'file://');
|
|
70
|
+
const resolvedImportURL = moduleResolve(importSpecifier, fileURL);
|
|
71
|
+
const resolvedImportPath = resolvedImportURL.pathname;
|
|
72
|
+
if (!resolvedImportPath.startsWith(root)) {
|
|
73
|
+
console.log('A', resolvedImportPath);
|
|
74
|
+
throw new HttpError(500);
|
|
75
|
+
}
|
|
76
|
+
let resolvedimport;
|
|
77
|
+
if (resolvedImportPath.startsWith(base)) {
|
|
78
|
+
resolvedimport = resolvedImportPath.substring(base.length);
|
|
79
|
+
} else {
|
|
80
|
+
resolvedimport = path.join(
|
|
81
|
+
rootFolderName,
|
|
82
|
+
resolvedImportPath.substring(root.length),
|
|
83
|
+
);
|
|
84
|
+
}
|
|
85
|
+
output += `${source.substring(lastIndex, start)}${resolvedimport}`;
|
|
86
|
+
lastIndex = end;
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
res.type = 'text/javascript';
|
|
90
|
+
res.body = output + source.substring(lastIndex);
|
|
91
|
+
};
|
|
92
|
+
};
|
|
93
|
+
//# sourceMappingURL=serve.js.map
|
package/lib/serve.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"serve.js","sourceRoot":"","sources":["../src/lib/serve.ts"],"names":[],"mappings":"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"}
|
package/package.json
ADDED
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@zipadee/javascript",
|
|
3
|
+
"version": "0.0.6",
|
|
4
|
+
"type": "module",
|
|
5
|
+
"main": "index.js",
|
|
6
|
+
"scripts": {
|
|
7
|
+
"build": "wireit",
|
|
8
|
+
"test": "wireit"
|
|
9
|
+
},
|
|
10
|
+
"keywords": [],
|
|
11
|
+
"author": "",
|
|
12
|
+
"homepage": "https://github.com/justinfagnani/zipadee#readme",
|
|
13
|
+
"repository": {
|
|
14
|
+
"type": "git",
|
|
15
|
+
"url": "git+https://github.com/justinfagnani/zipadee.git",
|
|
16
|
+
"directory": "packages/javascript"
|
|
17
|
+
},
|
|
18
|
+
"license": "MIT",
|
|
19
|
+
"description": "Zipadee middleware for serving JavaScript files",
|
|
20
|
+
"files": [
|
|
21
|
+
"/index.{d.ts.map,d.ts,js.map,js}",
|
|
22
|
+
"/lib/"
|
|
23
|
+
],
|
|
24
|
+
"devDependencies": {
|
|
25
|
+
"@types/es-module-lexer": "^0.4.1",
|
|
26
|
+
"@types/node": "^20.14.10",
|
|
27
|
+
"@types/supertest": "^6.0.2",
|
|
28
|
+
"supertest": "^7.0.0"
|
|
29
|
+
},
|
|
30
|
+
"wireit": {
|
|
31
|
+
"build": {
|
|
32
|
+
"command": "tsc",
|
|
33
|
+
"dependencies": [
|
|
34
|
+
"../core:build",
|
|
35
|
+
"../static:build"
|
|
36
|
+
],
|
|
37
|
+
"files": [
|
|
38
|
+
"src/**/*.ts",
|
|
39
|
+
"tsconfig.json"
|
|
40
|
+
],
|
|
41
|
+
"output": [
|
|
42
|
+
"index.{js,js.map,d.ts,d.ts.map}",
|
|
43
|
+
".tsbuildinfo",
|
|
44
|
+
"lib"
|
|
45
|
+
],
|
|
46
|
+
"clean": "if-file-deleted"
|
|
47
|
+
},
|
|
48
|
+
"test": {
|
|
49
|
+
"command": "node --enable-source-maps --test --test-reporter=spec \"test/**/*_test.js\"",
|
|
50
|
+
"dependencies": [
|
|
51
|
+
"build"
|
|
52
|
+
],
|
|
53
|
+
"files": [
|
|
54
|
+
"test/fixtures/**/*"
|
|
55
|
+
]
|
|
56
|
+
}
|
|
57
|
+
},
|
|
58
|
+
"dependencies": {
|
|
59
|
+
"@zipadee/core": "^0.0.6",
|
|
60
|
+
"@zipadee/static": "^0.0.6",
|
|
61
|
+
"import-meta-resolve": "^4.1.0"
|
|
62
|
+
}
|
|
63
|
+
}
|