primate 0.20.2 → 0.20.3
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/package.json +2 -2
- package/src/Logger.js +2 -3
- package/src/app.js +30 -26
- package/src/defaults/primate.config.js +4 -3
- package/src/dispatch.js +1 -1
- package/src/handlers/html.js +6 -13
- package/src/hooks/compile.js +2 -3
- package/src/hooks/handle.js +6 -7
- package/src/hooks/parse.js +22 -40
- package/src/hooks/publish.js +2 -2
- package/src/hooks/route.js +5 -4
- package/src/hooks/serve.js +4 -4
- package/src/loaders/common.js +0 -2
- package/src/loaders/routes/routes.js +2 -1
- package/src/loaders/routes.js +1 -1
- package/src/loaders/types.js +3 -1
- package/src/run.js +1 -1
- package/src/start.js +1 -1
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "primate",
|
|
3
|
-
"version": "0.20.
|
|
3
|
+
"version": "0.20.3",
|
|
4
4
|
"description": "Expressive, minimal and extensible web framework",
|
|
5
5
|
"homepage": "https://primatejs.com",
|
|
6
6
|
"bugs": "https://github.com/primatejs/primate/issues",
|
|
@@ -18,7 +18,7 @@
|
|
|
18
18
|
"directory": "packages/primate"
|
|
19
19
|
},
|
|
20
20
|
"dependencies": {
|
|
21
|
-
"runtime-compat": "^0.
|
|
21
|
+
"runtime-compat": "^0.21.0"
|
|
22
22
|
},
|
|
23
23
|
"engines": {
|
|
24
24
|
"node": ">=18.16"
|
package/src/Logger.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import {assert, is} from "runtime-compat/dyndef";
|
|
2
2
|
import {blue, bold, green, red, yellow, dim} from "runtime-compat/colors";
|
|
3
|
-
import {map
|
|
3
|
+
import {map} from "runtime-compat/object";
|
|
4
4
|
|
|
5
5
|
const levels = {
|
|
6
6
|
Error: 0,
|
|
@@ -17,8 +17,7 @@ const reference = "https://primatejs.com/reference/errors";
|
|
|
17
17
|
|
|
18
18
|
const hyphenate = classCased => classCased
|
|
19
19
|
.split("")
|
|
20
|
-
.map(
|
|
21
|
-
.replace(/[A-Z]/u, capital => `-${capital.toLowerCase()}`))
|
|
20
|
+
.map(letter => letter.replace(/[A-Z]/u, upper => `-${upper.toLowerCase()}`))
|
|
22
21
|
.join("")
|
|
23
22
|
.slice(1);
|
|
24
23
|
|
package/src/app.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import crypto from "runtime-compat/crypto";
|
|
2
|
-
import {tryreturn} from "runtime-compat/
|
|
2
|
+
import {tryreturn} from "runtime-compat/async";
|
|
3
3
|
import {File, Path} from "runtime-compat/fs";
|
|
4
4
|
import {bold, blue} from "runtime-compat/colors";
|
|
5
5
|
import {transform, valmap} from "runtime-compat/object";
|
|
@@ -17,18 +17,18 @@ const library = import.meta.runtime?.library ?? "node_modules";
|
|
|
17
17
|
|
|
18
18
|
// use user-provided file or fall back to default
|
|
19
19
|
const index = (app, name) =>
|
|
20
|
-
tryreturn(
|
|
21
|
-
.orelse(
|
|
20
|
+
tryreturn(_ => File.read(`${app.paths.pages.join(name)}`))
|
|
21
|
+
.orelse(_ => base.join("defaults", app.config.index).text());
|
|
22
22
|
|
|
23
|
+
const encoder = new TextEncoder();
|
|
23
24
|
const hash = async (string, algorithm = "sha-384") => {
|
|
24
|
-
const encoder = new TextEncoder();
|
|
25
25
|
const bytes = await crypto.subtle.digest(algorithm, encoder.encode(string));
|
|
26
26
|
const algo = algorithm.replace("-", _ => "");
|
|
27
27
|
return `${algo}-${btoa(String.fromCharCode(...new Uint8Array(bytes)))}`;
|
|
28
28
|
};
|
|
29
29
|
|
|
30
|
-
const attribute = attributes => Object.keys(attributes).length > 0
|
|
31
|
-
" ".concat(Object.entries(attributes)
|
|
30
|
+
const attribute = attributes => Object.keys(attributes).length > 0
|
|
31
|
+
? " ".concat(Object.entries(attributes)
|
|
32
32
|
.map(([key, value]) => `${key}="${value}"`).join(" "))
|
|
33
33
|
: "";
|
|
34
34
|
const tag = ({name, attributes = {}, code = "", close = true}) =>
|
|
@@ -75,22 +75,25 @@ export default async (config, root, log) => {
|
|
|
75
75
|
},
|
|
76
76
|
headers: _ => {
|
|
77
77
|
const csp = Object.keys(http.csp).reduce((policy_string, key) =>
|
|
78
|
-
`${policy_string}${key} ${http.csp[key]};`, "")
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
78
|
+
`${policy_string}${key} ${http.csp[key]};`, "")
|
|
79
|
+
.replace("script-src 'self'", `script-src 'self' ${
|
|
80
|
+
app.assets
|
|
81
|
+
.filter(({type}) => type !== "style")
|
|
82
|
+
.map(asset => `'${asset.integrity}'`).join(" ")
|
|
83
|
+
} `)
|
|
84
|
+
.replace("style-src 'self'", `style-src 'self' ${
|
|
85
|
+
app.assets
|
|
86
|
+
.filter(({type}) => type === "style")
|
|
87
|
+
.map(asset => `'${asset.integrity}'`).join(" ")
|
|
88
|
+
} `);
|
|
86
89
|
|
|
87
90
|
return {
|
|
88
|
-
"Content-Security-Policy":
|
|
91
|
+
"Content-Security-Policy": csp,
|
|
89
92
|
"Referrer-Policy": "same-origin",
|
|
90
93
|
};
|
|
91
94
|
},
|
|
92
95
|
handlers: {...handlers},
|
|
93
|
-
render: async ({body = "",
|
|
96
|
+
render: async ({body = "", page} = {}) => {
|
|
94
97
|
const html = await index(app, page ?? config.index);
|
|
95
98
|
// inline: <script type integrity>...</script>
|
|
96
99
|
// outline: <script type integrity src></script>
|
|
@@ -102,16 +105,17 @@ export default async (config, root, log) => {
|
|
|
102
105
|
const style = ({inline, code, href, rel = "stylesheet"}) => inline
|
|
103
106
|
? tag({name: "style", code})
|
|
104
107
|
: tag({name: "link", attributes: {rel, href}, close: false});
|
|
105
|
-
const
|
|
108
|
+
const head = toSorted(app.assets,
|
|
106
109
|
({type}) => -1 * (type === "importmap"))
|
|
107
110
|
.map(({src, code, type, inline, integrity}) =>
|
|
108
111
|
type === "style"
|
|
109
112
|
? style({inline, code, href: src})
|
|
110
113
|
: script({inline, code, type, integrity, src})
|
|
111
114
|
).join("\n");
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
+
// remove inline assets
|
|
116
|
+
app.assets = app.assets.filter(({inline, type}) => !inline
|
|
117
|
+
|| type === "importmap");
|
|
118
|
+
return html.replace("%body%", _ => body).replace("%head%", _ => head);
|
|
115
119
|
},
|
|
116
120
|
publish: async ({src, code, type = "", inline = false}) => {
|
|
117
121
|
if (!inline) {
|
|
@@ -119,10 +123,10 @@ export default async (config, root, log) => {
|
|
|
119
123
|
await base.directory.file.create();
|
|
120
124
|
await base.file.write(code);
|
|
121
125
|
}
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
+
if (inline || type === "style") {
|
|
127
|
+
app.assets.push({src: new Path(http.static.root).join(src ?? "").path,
|
|
128
|
+
code: inline ? code : "", type, inline, integrity: await hash(code)});
|
|
129
|
+
}
|
|
126
130
|
},
|
|
127
131
|
bootstrap: ({type, code}) => {
|
|
128
132
|
app.entrypoints.push({type, code});
|
|
@@ -135,10 +139,10 @@ export default async (config, root, log) => {
|
|
|
135
139
|
const exports = pkg.exports === undefined
|
|
136
140
|
? {[module]: `/${module}/${pkg.main}`}
|
|
137
141
|
: transform(pkg.exports, entry => entry
|
|
138
|
-
.filter(([, _export]) => _export.import !== undefined)
|
|
142
|
+
.filter(([, _export]) => _export.import !== undefined || _export.default !== undefined)
|
|
139
143
|
.map(([key, value]) => [
|
|
140
144
|
key.replace(".", module),
|
|
141
|
-
value.import.replace(".", `./${module}`),
|
|
145
|
+
value.import?.replace(".", `./${module}`) ?? value.default.replace(".", `./${module}`),
|
|
142
146
|
]));
|
|
143
147
|
await Promise.all(Object.values(exports).map(async name => app.publish({
|
|
144
148
|
code: await Path.resolve().join(library, name).text(),
|
|
@@ -4,6 +4,7 @@ export default {
|
|
|
4
4
|
base: "/",
|
|
5
5
|
logger: {
|
|
6
6
|
level: Logger.Warn,
|
|
7
|
+
trace: false,
|
|
7
8
|
},
|
|
8
9
|
http: {
|
|
9
10
|
host: "localhost",
|
|
@@ -11,6 +12,7 @@ export default {
|
|
|
11
12
|
csp: {
|
|
12
13
|
"default-src": "'self'",
|
|
13
14
|
"style-src": "'self'",
|
|
15
|
+
"script-src": "'self'",
|
|
14
16
|
"object-src": "'none'",
|
|
15
17
|
"frame-ancestors": "'none'",
|
|
16
18
|
"form-action": "'self'",
|
|
@@ -23,12 +25,11 @@ export default {
|
|
|
23
25
|
index: "app.html",
|
|
24
26
|
paths: {
|
|
25
27
|
build: "build",
|
|
26
|
-
static: "static",
|
|
27
28
|
components: "components",
|
|
29
|
+
pages: "pages",
|
|
28
30
|
routes: "routes",
|
|
31
|
+
static: "static",
|
|
29
32
|
types: "types",
|
|
30
|
-
pages: "pages",
|
|
31
|
-
layouts: "layouts",
|
|
32
33
|
},
|
|
33
34
|
build: {
|
|
34
35
|
includes: [],
|
package/src/dispatch.js
CHANGED
package/src/handlers/html.js
CHANGED
|
@@ -1,19 +1,11 @@
|
|
|
1
1
|
const script = /(?<=<script)>(?<code>.*?)(?=<\/script>)/gus;
|
|
2
2
|
const style = /(?<=<style)>(?<code>.*?)(?=<\/style>)/gus;
|
|
3
3
|
|
|
4
|
-
const integrate = async (html, publish
|
|
5
|
-
|
|
4
|
+
const integrate = async (html, publish) => {
|
|
5
|
+
await Promise.all([...html.matchAll(script)]
|
|
6
6
|
.map(({groups: {code}}) => publish({code, inline: true})));
|
|
7
|
-
|
|
8
|
-
headers["Content-Security-Policy"] = headers["Content-Security-Policy"]
|
|
9
|
-
.replace("script-src 'self' ", `script-src 'self' '${integrity}' `);
|
|
10
|
-
}
|
|
11
|
-
const styles = await Promise.all([...html.matchAll(style)]
|
|
7
|
+
await Promise.all([...html.matchAll(style)]
|
|
12
8
|
.map(({groups: {code}}) => publish({code, type: "style", inline: true})));
|
|
13
|
-
for (const integrity of styles) {
|
|
14
|
-
headers["Content-Security-Policy"] = headers["Content-Security-Policy"]
|
|
15
|
-
.replace("style-src 'self'", `style-src 'self' '${integrity}' `);
|
|
16
|
-
}
|
|
17
9
|
return html.replaceAll(/<(?<tag>script|style)>.*?<\/\k<tag>>/gus, _ => "");
|
|
18
10
|
};
|
|
19
11
|
|
|
@@ -21,10 +13,11 @@ export default (component, options = {}) => {
|
|
|
21
13
|
const {status = 200, partial = false, load = false} = options;
|
|
22
14
|
|
|
23
15
|
return async app => {
|
|
24
|
-
const headers = app.headers();
|
|
25
16
|
const body = await integrate(await load ?
|
|
26
17
|
await app.paths.components.join(component).text() : component,
|
|
27
|
-
app.publish
|
|
18
|
+
app.publish);
|
|
19
|
+
// needs to happen before app.render()
|
|
20
|
+
const headers = app.headers();
|
|
28
21
|
|
|
29
22
|
return [partial ? body : await app.render({body}), {
|
|
30
23
|
status,
|
package/src/hooks/compile.js
CHANGED
|
@@ -1,8 +1,7 @@
|
|
|
1
|
-
import copy_includes from "./copy_includes.js"
|
|
1
|
+
import copy_includes from "./copy_includes.js";
|
|
2
2
|
|
|
3
3
|
const pre = async app => {
|
|
4
|
-
const {paths, config} = app;
|
|
5
|
-
const build = config.build;
|
|
4
|
+
const {paths, config: {build}} = app;
|
|
6
5
|
|
|
7
6
|
// remove build directory in case exists
|
|
8
7
|
if (await paths.build.exists) {
|
package/src/hooks/handle.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import {Response, Status} from "runtime-compat/http";
|
|
2
|
-
import {tryreturn} from "runtime-compat/
|
|
2
|
+
import {tryreturn} from "runtime-compat/async";
|
|
3
3
|
import {mime, isResponse, respond} from "./respond/exports.js";
|
|
4
4
|
import {invalid} from "./route.js";
|
|
5
5
|
import {error as clientError} from "../handlers/exports.js";
|
|
@@ -8,7 +8,7 @@ import errors from "../errors.js";
|
|
|
8
8
|
const guardError = Symbol("guardError");
|
|
9
9
|
|
|
10
10
|
export default app => {
|
|
11
|
-
const {config: {http, build}, paths} = app;
|
|
11
|
+
const {config: {http: {static: {root}}, build}, paths} = app;
|
|
12
12
|
|
|
13
13
|
const run = async request => {
|
|
14
14
|
const {pathname} = request.url;
|
|
@@ -18,10 +18,10 @@ export default app => {
|
|
|
18
18
|
|
|
19
19
|
// handle guards
|
|
20
20
|
try {
|
|
21
|
-
guards.
|
|
21
|
+
guards.every(guard => {
|
|
22
22
|
const result = guard(request);
|
|
23
23
|
if (result === true) {
|
|
24
|
-
return
|
|
24
|
+
return true;
|
|
25
25
|
}
|
|
26
26
|
const error = new Error();
|
|
27
27
|
error.result = result;
|
|
@@ -38,7 +38,7 @@ export default app => {
|
|
|
38
38
|
|
|
39
39
|
// handle request
|
|
40
40
|
const handlers = [...app.modules.route, handler]
|
|
41
|
-
.reduceRight((
|
|
41
|
+
.reduceRight((next, last) => input => last(input, next));
|
|
42
42
|
|
|
43
43
|
return (await respond(await handlers({...request, path})))(app, {
|
|
44
44
|
layouts: await Promise.all(layouts.map(layout => layout(request))),
|
|
@@ -64,7 +64,6 @@ export default app => {
|
|
|
64
64
|
|
|
65
65
|
const handle = async request => {
|
|
66
66
|
const {pathname} = request.url;
|
|
67
|
-
const {root} = http.static;
|
|
68
67
|
if (pathname.startsWith(root)) {
|
|
69
68
|
const debased = pathname.replace(root, _ => "");
|
|
70
69
|
// try static first
|
|
@@ -79,5 +78,5 @@ export default app => {
|
|
|
79
78
|
};
|
|
80
79
|
|
|
81
80
|
return [...app.modules.handle, handle]
|
|
82
|
-
.reduceRight((
|
|
81
|
+
.reduceRight((next, last) => input => last(input, next));
|
|
83
82
|
};
|
package/src/hooks/parse.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import {URL} from "runtime-compat/http";
|
|
2
|
-
import {tryreturn} from "runtime-compat/
|
|
2
|
+
import {tryreturn} from "runtime-compat/sync";
|
|
3
|
+
import {stringify} from "runtime-compat/streams";
|
|
3
4
|
import errors from "../errors.js";
|
|
4
5
|
|
|
5
6
|
const {fromEntries: from} = Object;
|
|
@@ -10,47 +11,28 @@ const contents = {
|
|
|
10
11
|
.map(subpart => decodeURIComponent(subpart).replaceAll("+", " ")))),
|
|
11
12
|
"application/json": body => JSON.parse(body),
|
|
12
13
|
};
|
|
13
|
-
const decoder = new TextDecoder();
|
|
14
14
|
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
}
|
|
29
|
-
const reader = request.body.getReader();
|
|
30
|
-
const chunks = [];
|
|
31
|
-
let result;
|
|
32
|
-
do {
|
|
33
|
-
result = await reader.read();
|
|
34
|
-
if (result.value !== undefined) {
|
|
35
|
-
chunks.push(decoder.decode(result.value));
|
|
36
|
-
}
|
|
37
|
-
} while (!result.done);
|
|
38
|
-
|
|
39
|
-
return parseContent(request.headers.get("content-type"), chunks.join());
|
|
40
|
-
};
|
|
15
|
+
const parse = {
|
|
16
|
+
content(content_type, body) {
|
|
17
|
+
return tryreturn(_ => {
|
|
18
|
+
const type = contents[content_type];
|
|
19
|
+
return type === undefined ? body : type(body);
|
|
20
|
+
}).orelse(_ => errors.CannotParseBody.throw(body, content_type));
|
|
21
|
+
},
|
|
22
|
+
async body({body, headers}) {
|
|
23
|
+
return body === null
|
|
24
|
+
? null
|
|
25
|
+
: this.content(headers.get("content-type"), await stringify(body));
|
|
26
|
+
},
|
|
27
|
+
};
|
|
41
28
|
|
|
42
|
-
|
|
29
|
+
export default dispatch => async request => {
|
|
30
|
+
const body = dispatch(await parse.body(request));
|
|
31
|
+
const cookies = dispatch(from(request.headers.get("cookie")?.split(";")
|
|
32
|
+
.map(cookie => cookie.trim().split("=")) ?? []));
|
|
33
|
+
const headers = dispatch(from(request.headers));
|
|
43
34
|
const url = new URL(request.url);
|
|
35
|
+
const query = dispatch(from(url.searchParams));
|
|
44
36
|
|
|
45
|
-
|
|
46
|
-
return {
|
|
47
|
-
original: request,
|
|
48
|
-
url,
|
|
49
|
-
body: dispatch(body),
|
|
50
|
-
cookies: dispatch(cookies === null
|
|
51
|
-
? {}
|
|
52
|
-
: from(cookies.split(";").map(c => c.trim().split("=")))),
|
|
53
|
-
headers: dispatch(from(request.headers)),
|
|
54
|
-
query: dispatch(from(url.searchParams)),
|
|
55
|
-
};
|
|
37
|
+
return {original: request, url, body, cookies, headers, query};
|
|
56
38
|
};
|
package/src/hooks/publish.js
CHANGED
|
@@ -22,6 +22,7 @@ const post = async app => {
|
|
|
22
22
|
});
|
|
23
23
|
}
|
|
24
24
|
|
|
25
|
+
// copy JavaScript and CSS files from `app.paths.static`
|
|
25
26
|
const imports = await Path.collect(app.paths.static, /\.(?:js|css)$/u);
|
|
26
27
|
await Promise.all(imports.map(async file => {
|
|
27
28
|
const code = await file.text();
|
|
@@ -40,9 +41,8 @@ const post = async app => {
|
|
|
40
41
|
// copy additional subdirectories to build/client
|
|
41
42
|
await copy_includes(app, "client", async to =>
|
|
42
43
|
Promise.all((await to.collect(/\.js$/u)).map(async script => {
|
|
43
|
-
const code = await script.text();
|
|
44
44
|
const src = new Path(root, script.path.replace(source, () => ""));
|
|
45
|
-
await app.publish({src, code, type: "module"});
|
|
45
|
+
await app.publish({src, code: await script.text(), type: "module"});
|
|
46
46
|
}))
|
|
47
47
|
);
|
|
48
48
|
};
|
package/src/hooks/route.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import {keymap} from "runtime-compat/object";
|
|
2
|
-
import {tryreturn} from "runtime-compat/
|
|
2
|
+
import {tryreturn} from "runtime-compat/sync";
|
|
3
3
|
import errors from "../errors.js";
|
|
4
4
|
|
|
5
5
|
// insensitive-case equal
|
|
@@ -18,8 +18,9 @@ export default app => {
|
|
|
18
18
|
.filter(([name]) => name.includes("$"))
|
|
19
19
|
.map(([name, value]) => [name.split("$")[1], value])
|
|
20
20
|
.every(([name, value]) =>
|
|
21
|
-
tryreturn(_ => types?.[name](value) === true)
|
|
22
|
-
.orelse(({message}) => errors.MismatchedPath.throw(pathname, message))
|
|
21
|
+
tryreturn(_ => types?.[name].type(value) === true)
|
|
22
|
+
.orelse(({message}) => errors.MismatchedPath.throw(pathname, message))
|
|
23
|
+
);
|
|
23
24
|
const isPath = ({route, pathname}) => {
|
|
24
25
|
const result = route.pathname.exec(pathname);
|
|
25
26
|
return result === null ? false : isType(result.groups, pathname);
|
|
@@ -29,7 +30,7 @@ export default app => {
|
|
|
29
30
|
const find = (method, pathname) => routes.find(route =>
|
|
30
31
|
isMethod({route, method, pathname}));
|
|
31
32
|
|
|
32
|
-
const index = path => `${paths.routes}${path === "" ? "index" : path}`;
|
|
33
|
+
const index = path => `${paths.routes}${path === "/" ? "/index" : path}`;
|
|
33
34
|
const deroot = pathname => pathname.endsWith("/") && pathname !== "/"
|
|
34
35
|
? pathname.slice(0, -1) : pathname;
|
|
35
36
|
|
package/src/hooks/serve.js
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
|
|
1
|
+
import {identity} from "runtime-compat/function";
|
|
2
2
|
|
|
3
3
|
export default async (app, server) => {
|
|
4
4
|
app.log.info("running serve hooks", {module: "primate"});
|
|
5
|
-
await [...
|
|
6
|
-
.reduceRight((
|
|
7
|
-
|
|
5
|
+
await [...app.modules.serve, identity]
|
|
6
|
+
.reduceRight((next, previous) =>
|
|
7
|
+
input => previous(input, next))({...app, server});
|
|
8
8
|
};
|
package/src/loaders/common.js
CHANGED
|
@@ -8,7 +8,8 @@ const normalize = route => {
|
|
|
8
8
|
};
|
|
9
9
|
|
|
10
10
|
// index -> ""
|
|
11
|
-
const deindex = path => path.endsWith("index") ?
|
|
11
|
+
const deindex = path => path.endsWith("index") ?
|
|
12
|
+
path.replace("index", "") : path;
|
|
12
13
|
|
|
13
14
|
export default async (log, directory, load) => {
|
|
14
15
|
const filter = path => /^[^+].*.js$/u.test(path.name);
|
package/src/loaders/routes.js
CHANGED
package/src/loaders/types.js
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import {Path} from "runtime-compat/fs";
|
|
2
2
|
import errors from "../errors.js";
|
|
3
|
-
import
|
|
3
|
+
import fs from "./common.js";
|
|
4
|
+
|
|
5
|
+
const filter = path => /^[a-z]/u.test(path.name);
|
|
4
6
|
|
|
5
7
|
export default async (log, directory, load = fs) => {
|
|
6
8
|
const types = await load({log, directory, name: "types", filter});
|
package/src/run.js
CHANGED
package/src/start.js
CHANGED