primate 0.24.0 → 0.26.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/package.json +2 -2
- package/src/Logger.js +20 -16
- package/src/app.js +75 -69
- package/src/bin.js +1 -1
- package/src/commands/dev.js +1 -1
- package/src/commands/exports.js +3 -3
- package/src/commands/serve.js +1 -1
- package/src/cwd.js +1 -1
- package/src/defaults/primate.config.js +1 -1
- package/src/dispatch.js +13 -10
- package/src/errors.js +1 -1
- package/src/errors.json +5 -0
- package/src/exports.js +2 -2
- package/src/handlers/error.js +4 -4
- package/src/handlers/exports.js +7 -7
- package/src/handlers/html.js +7 -7
- package/src/handlers/json.js +3 -3
- package/src/handlers/redirect.js +3 -3
- package/src/handlers/stream.js +4 -4
- package/src/handlers/text.js +3 -3
- package/src/hooks/bundle.js +1 -1
- package/src/hooks/copy_includes.js +8 -8
- package/src/hooks/exports.js +9 -9
- package/src/hooks/handle.js +44 -37
- package/src/hooks/init.js +1 -1
- package/src/hooks/parse.js +8 -8
- package/src/hooks/publish.js +8 -42
- package/src/hooks/register.js +73 -2
- package/src/hooks/respond/duck.js +1 -1
- package/src/hooks/respond/exports.js +2 -2
- package/src/hooks/respond/respond.js +4 -4
- package/src/hooks/route.js +11 -11
- package/src/hooks/serve.js +3 -3
- package/src/hooks/stage.js +48 -0
- package/src/loaders/common.js +3 -3
- package/src/loaders/exports.js +3 -3
- package/src/loaders/modules.js +10 -7
- package/src/loaders/routes/exports.js +6 -4
- package/src/loaders/routes/load.js +2 -2
- package/src/loaders/routes/routes.js +2 -2
- package/src/loaders/routes.js +16 -15
- package/src/loaders/types.js +11 -4
- package/src/run.js +8 -8
- package/src/start.js +19 -10
- package/src/validate.js +1 -1
- package/src/hooks/compile.js +0 -45
- package/src/loaders/routes/errors.js +0 -3
- package/src/loaders/routes/guards.js +0 -3
- package/src/loaders/routes/layouts.js +0 -3
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "primate",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.26.0",
|
|
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
|
-
"
|
|
21
|
+
"rcompat": "^0.3.3"
|
|
22
22
|
},
|
|
23
23
|
"engines": {
|
|
24
24
|
"node": ">=18"
|
package/src/Logger.js
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
|
-
import {assert, is} from "
|
|
2
|
-
import {blue, bold, green, red, yellow, dim} from "
|
|
3
|
-
import {map} from "
|
|
4
|
-
import console from "
|
|
1
|
+
import { assert, is } from "rcompat/invariant";
|
|
2
|
+
import { blue, bold, green, red, yellow, dim } from "rcompat/colors";
|
|
3
|
+
import { map } from "rcompat/object";
|
|
4
|
+
import console from "rcompat/console";
|
|
5
|
+
import { stdout } from "rcompat/stdio";
|
|
5
6
|
|
|
6
7
|
const levels = {
|
|
7
8
|
Error: 0,
|
|
@@ -9,20 +10,23 @@ const levels = {
|
|
|
9
10
|
Info: 2,
|
|
10
11
|
};
|
|
11
12
|
|
|
12
|
-
const print = (...messages) =>
|
|
13
|
+
const print = (...messages) => stdout.write(messages.join(" "));
|
|
13
14
|
const bye = _ => print(dim(yellow("~~ bye\n")));
|
|
14
15
|
const mark = (format, ...params) => params.reduce((formatted, param, i) =>
|
|
15
16
|
formatted.replace(`{${i}}`, bold(param)), format);
|
|
16
17
|
|
|
17
|
-
const reference =
|
|
18
|
+
const reference = (module, error) => {
|
|
19
|
+
const base = module ? `modules/${module}` : "guide/logging";
|
|
20
|
+
return `https://primatejs.com/${base}#${hyphenate(error)}`;
|
|
21
|
+
};
|
|
18
22
|
|
|
19
|
-
const hyphenate =
|
|
23
|
+
const hyphenate = class_cased => class_cased
|
|
20
24
|
.split("")
|
|
21
25
|
.map(letter => letter.replace(/[A-Z]/u, upper => `-${upper.toLowerCase()}`))
|
|
22
26
|
.join("")
|
|
23
27
|
.slice(1);
|
|
24
28
|
|
|
25
|
-
const throwable = ({message, level, fix}, name, module) => ({
|
|
29
|
+
const throwable = ({ message, level, fix }, name, module) => ({
|
|
26
30
|
new(...args) {
|
|
27
31
|
const error = new Error(mark(message, ...args));
|
|
28
32
|
error.level = Logger[level];
|
|
@@ -35,9 +39,9 @@ const throwable = ({message, level, fix}, name, module) => ({
|
|
|
35
39
|
throw this.new(...args);
|
|
36
40
|
},
|
|
37
41
|
warn(logger, ...args) {
|
|
38
|
-
const error = {level: Logger[level], message: mark(message, ...args),
|
|
39
|
-
fix: mark(fix, ...args)};
|
|
40
|
-
logger.auto({...error, name, module});
|
|
42
|
+
const error = { level: Logger[level], message: mark(message, ...args),
|
|
43
|
+
fix: mark(fix, ...args) };
|
|
44
|
+
logger.auto({ ...error, name, module });
|
|
41
45
|
},
|
|
42
46
|
});
|
|
43
47
|
|
|
@@ -48,7 +52,7 @@ const Logger = class Logger {
|
|
|
48
52
|
return map(errors, ([key, value]) => [key, throwable(value, key, module)]);
|
|
49
53
|
}
|
|
50
54
|
|
|
51
|
-
constructor({level = levels.Error, trace = false} = {}) {
|
|
55
|
+
constructor({ level = levels.Error, trace = false } = {}) {
|
|
52
56
|
assert(level !== undefined && level <= levels.Info);
|
|
53
57
|
is(trace).boolean();
|
|
54
58
|
this.#level = level;
|
|
@@ -68,11 +72,11 @@ const Logger = class Logger {
|
|
|
68
72
|
}
|
|
69
73
|
|
|
70
74
|
#print(pre, color, message, error = {}) {
|
|
71
|
-
const {fix, module, name, level} = error;
|
|
75
|
+
const { fix, module, name, level } = error;
|
|
72
76
|
print(color(pre), `${module !== undefined ? `${color(module)} ` : ""}${message}`, "\n");
|
|
73
77
|
if (fix) {
|
|
74
78
|
print(blue("++"), fix);
|
|
75
|
-
name && print(dim(`\n -> ${reference
|
|
79
|
+
name && print(dim(`\n -> ${reference(module, name)}`), "\n");
|
|
76
80
|
}
|
|
77
81
|
if (level === levels.Error || level === undefined && error.message) {
|
|
78
82
|
this.#trace && console.log(error);
|
|
@@ -96,7 +100,7 @@ const Logger = class Logger {
|
|
|
96
100
|
}
|
|
97
101
|
|
|
98
102
|
auto(error) {
|
|
99
|
-
const {message} = error;
|
|
103
|
+
const { message } = error;
|
|
100
104
|
const matches = map(levels, ([name, level]) => [level, name.toLowerCase()]);
|
|
101
105
|
return this[matches[error.level] ?? "error"](message, error);
|
|
102
106
|
}
|
|
@@ -104,4 +108,4 @@ const Logger = class Logger {
|
|
|
104
108
|
|
|
105
109
|
export default Logger;
|
|
106
110
|
|
|
107
|
-
export {print, bye, mark};
|
|
111
|
+
export { print, bye, mark };
|
package/src/app.js
CHANGED
|
@@ -1,25 +1,22 @@
|
|
|
1
|
-
import crypto from "
|
|
2
|
-
import {tryreturn} from "
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
7
|
-
import
|
|
8
|
-
import * as runtime from "runtime-compat/meta";
|
|
1
|
+
import crypto from "rcompat/crypto";
|
|
2
|
+
import { tryreturn } from "rcompat/async";
|
|
3
|
+
import { Path } from "rcompat/fs";
|
|
4
|
+
import { is } from "rcompat/invariant";
|
|
5
|
+
import { transform, valmap } from "rcompat/object";
|
|
6
|
+
import { globify } from "rcompat/string";
|
|
7
|
+
import * as runtime from "rcompat/meta";
|
|
9
8
|
|
|
10
9
|
import errors from "./errors.js";
|
|
11
|
-
import {print} from "./Logger.js";
|
|
12
|
-
import dispatch from "./dispatch.js";
|
|
13
10
|
import to_sorted from "./to_sorted.js";
|
|
14
11
|
import * as handlers from "./handlers/exports.js";
|
|
15
12
|
import * as loaders from "./loaders/exports.js";
|
|
16
13
|
|
|
17
|
-
const {DoubleFileExtension} = errors;
|
|
14
|
+
const { DoubleFileExtension } = errors;
|
|
18
15
|
|
|
19
16
|
// use user-provided file or fall back to default
|
|
20
17
|
const index = (base, page, fallback) =>
|
|
21
|
-
tryreturn(_ =>
|
|
22
|
-
.orelse(_ =>
|
|
18
|
+
tryreturn(_ => Path.read(`${base.join(page)}`))
|
|
19
|
+
.orelse(_ => Path.read(`${base.join(fallback)}`));
|
|
23
20
|
|
|
24
21
|
const encoder = new TextEncoder();
|
|
25
22
|
|
|
@@ -27,45 +24,40 @@ const attribute = attributes => Object.keys(attributes).length > 0
|
|
|
27
24
|
? " ".concat(Object.entries(attributes)
|
|
28
25
|
.map(([key, value]) => `${key}="${value}"`).join(" "))
|
|
29
26
|
: "";
|
|
30
|
-
const tag = ({name, attributes = {}, code = "", close = true}) =>
|
|
27
|
+
const tag = ({ name, attributes = {}, code = "", close = true }) =>
|
|
31
28
|
`<${name}${attribute(attributes)}${close ? `>${code}</${name}>` : "/>"}`;
|
|
32
29
|
const tags = {
|
|
33
30
|
// inline: <script type integrity>...</script>
|
|
34
31
|
// outline: <script type integrity src></script>
|
|
35
|
-
script({inline, code, type, integrity, src}) {
|
|
32
|
+
script({ inline, code, type, integrity, src }) {
|
|
36
33
|
return inline
|
|
37
|
-
? tag({name: "script", attributes: {type, integrity}, code})
|
|
38
|
-
: tag({name: "script", attributes: {type, integrity, src}});
|
|
34
|
+
? tag({ name: "script", attributes: { type, integrity }, code })
|
|
35
|
+
: tag({ name: "script", attributes: { type, integrity, src } });
|
|
39
36
|
},
|
|
40
37
|
// inline: <style>...</style>
|
|
41
38
|
// outline: <link rel="stylesheet" href/>
|
|
42
|
-
style({inline, code, href, rel = "stylesheet"}) {
|
|
39
|
+
style({ inline, code, href, rel = "stylesheet" }) {
|
|
43
40
|
return inline
|
|
44
|
-
? tag({name: "style", code})
|
|
45
|
-
: tag({name: "link", attributes: {rel, href}, close: false});
|
|
41
|
+
? tag({ name: "style", code })
|
|
42
|
+
: tag({ name: "link", attributes: { rel, href }, close: false });
|
|
46
43
|
},
|
|
47
44
|
};
|
|
48
45
|
|
|
49
|
-
const {name, version} = await new Path(import.meta.url).up(2)
|
|
46
|
+
const { name, version } = await new Path(import.meta.url).up(2)
|
|
50
47
|
.join(runtime.manifest).json();
|
|
51
48
|
|
|
52
49
|
export default async (log, root, config) => {
|
|
53
|
-
const {http} = config;
|
|
50
|
+
const { http } = config;
|
|
54
51
|
const secure = http?.ssl !== undefined;
|
|
55
52
|
const path = valmap(config.location, value => root.join(value));
|
|
56
53
|
|
|
57
|
-
const at = `at http${secure ? "s" : ""}://${http.host}:${http.port}\n`;
|
|
58
|
-
print(blue(bold(name)), blue(version), at);
|
|
59
|
-
|
|
60
54
|
// if ssl activated, resolve key and cert early
|
|
61
55
|
if (secure) {
|
|
62
56
|
http.ssl.key = root.join(http.ssl.key);
|
|
63
57
|
http.ssl.cert = root.join(http.ssl.cert);
|
|
64
58
|
}
|
|
65
59
|
|
|
66
|
-
const types = await loaders.types(log, path.types);
|
|
67
60
|
const error = await path.routes.join("+error.js");
|
|
68
|
-
const routes = await loaders.routes(log, path.routes);
|
|
69
61
|
|
|
70
62
|
return {
|
|
71
63
|
config,
|
|
@@ -79,83 +71,96 @@ export default async (log, root, config) => {
|
|
|
79
71
|
root,
|
|
80
72
|
log,
|
|
81
73
|
error: {
|
|
82
|
-
default: await error.exists ? (await import(error)).default : undefined,
|
|
83
|
-
},
|
|
84
|
-
handlers: {...handlers},
|
|
85
|
-
types,
|
|
86
|
-
routes,
|
|
87
|
-
layout: {
|
|
88
|
-
depth: Math.max(...routes.map(({layouts}) => layouts.length)) + 1,
|
|
74
|
+
default: await error.exists() ? (await import(error)).default : undefined,
|
|
89
75
|
},
|
|
90
|
-
|
|
76
|
+
handlers: { ...handlers },
|
|
77
|
+
extensions: {},
|
|
91
78
|
modules: await loaders.modules(log, root, config),
|
|
92
79
|
...runtime,
|
|
93
80
|
// copy files to build folder, potentially transforming them
|
|
94
81
|
async stage(source, directory, filter) {
|
|
95
|
-
const {paths, mapper} = this.config.build.transform;
|
|
82
|
+
const { paths, mapper } = this.config.build.transform;
|
|
96
83
|
is(paths).array();
|
|
97
84
|
is(mapper).function();
|
|
98
85
|
|
|
99
86
|
const regexs = paths.map(file => globify(file));
|
|
100
|
-
const
|
|
87
|
+
const target_base = this.runpath(directory);
|
|
101
88
|
|
|
102
89
|
await Promise.all((await source.collect(filter)).map(async path => {
|
|
103
90
|
const debased = path.debase(this.root).path.slice(1);
|
|
104
91
|
const filename = new Path(directory).join(path.debase(source));
|
|
105
|
-
const
|
|
106
|
-
await
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
} else {
|
|
111
|
-
await path.file.copy(to);
|
|
112
|
-
}
|
|
92
|
+
const target = await target_base.join(filename.debase(directory));
|
|
93
|
+
await target.directory.create();
|
|
94
|
+
await (regexs.some(regex => regex.test(debased))
|
|
95
|
+
? target.write(mapper(await path.text()))
|
|
96
|
+
: path.copy(target));
|
|
113
97
|
}));
|
|
114
98
|
},
|
|
115
|
-
|
|
99
|
+
async compile(component) {
|
|
100
|
+
const { location: { server, client, components } } = this.config;
|
|
101
|
+
|
|
102
|
+
const source = this.path.components;
|
|
103
|
+
const compile = this.extensions[component.extension]?.compile;
|
|
104
|
+
if (compile === undefined) {
|
|
105
|
+
const debased = `${component.path}`.replace(source, "");
|
|
106
|
+
|
|
107
|
+
const server_target = this.runpath(server, components);
|
|
108
|
+
await component.copy(server_target.join(debased));
|
|
109
|
+
|
|
110
|
+
const client_target = this.runpath(client, components);
|
|
111
|
+
await component.copy(client_target.join(debased));
|
|
112
|
+
} else {
|
|
113
|
+
// compile server components
|
|
114
|
+
await compile.server(component);
|
|
115
|
+
|
|
116
|
+
// compile client components
|
|
117
|
+
await compile.client(component);
|
|
118
|
+
}
|
|
119
|
+
},
|
|
120
|
+
headers({ script = "", style = "" } = {}) {
|
|
116
121
|
const csp = Object.keys(http.csp).reduce((policy, key) =>
|
|
117
122
|
`${policy}${key} ${http.csp[key]};`, "")
|
|
118
123
|
.replace("script-src 'self'", `script-src 'self' ${script} ${
|
|
119
124
|
this.assets
|
|
120
|
-
.filter(({type}) => type !== "style")
|
|
125
|
+
.filter(({ type }) => type !== "style")
|
|
121
126
|
.map(asset => `'${asset.integrity}'`).join(" ")
|
|
122
127
|
}`)
|
|
123
128
|
.replace("style-src 'self'", `style-src 'self' ${style} ${
|
|
124
129
|
this.assets
|
|
125
|
-
.filter(({type}) => type === "style")
|
|
130
|
+
.filter(({ type }) => type === "style")
|
|
126
131
|
.map(asset => `'${asset.integrity}'`).join(" ")
|
|
127
132
|
}`);
|
|
128
133
|
|
|
129
|
-
return {"Content-Security-Policy": csp, "Referrer-Policy": "same-origin"};
|
|
134
|
+
return { "Content-Security-Policy": csp, "Referrer-Policy": "same-origin" };
|
|
130
135
|
},
|
|
131
136
|
runpath(...directories) {
|
|
132
137
|
return this.path.build.join(...directories);
|
|
133
138
|
},
|
|
134
|
-
async render({body = "", head = "", page = config.pages.index} = {}) {
|
|
135
|
-
const {location: {pages}} = this.config;
|
|
139
|
+
async render({ body = "", head = "", page = config.pages.index } = {}) {
|
|
140
|
+
const { location: { pages } } = this.config;
|
|
136
141
|
|
|
137
142
|
const html = await index(this.runpath(pages), page, config.pages.index);
|
|
138
143
|
|
|
139
144
|
const heads = to_sorted(this.assets,
|
|
140
|
-
({type}) => -1 * (type === "importmap"))
|
|
141
|
-
.map(({src, code, type, inline, integrity}) =>
|
|
145
|
+
({ type }) => -1 * (type === "importmap"))
|
|
146
|
+
.map(({ src, code, type, inline, integrity }) =>
|
|
142
147
|
type === "style"
|
|
143
|
-
? tags.style({inline, code, href: src})
|
|
144
|
-
: tags.script({inline, code, type, integrity, src})
|
|
148
|
+
? tags.style({ inline, code, href: src })
|
|
149
|
+
: tags.script({ inline, code, type, integrity, src }),
|
|
145
150
|
).join("\n").concat("\n", head);
|
|
146
151
|
return html.replace("%body%", _ => body).replace("%head%", _ => heads);
|
|
147
152
|
},
|
|
148
153
|
async inline(code, type) {
|
|
149
154
|
const integrity = await this.hash(code);
|
|
150
155
|
const tag_name = type === "style" ? "style" : "script";
|
|
151
|
-
const head = tags[tag_name]({code, type, inline: true, integrity});
|
|
152
|
-
return {head, csp: `'${integrity}'`};
|
|
156
|
+
const head = tags[tag_name]({ code, type, inline: true, integrity });
|
|
157
|
+
return { head, csp: `'${integrity}'` };
|
|
153
158
|
},
|
|
154
|
-
async publish({src, code, type = "", inline = false, copy = true}) {
|
|
159
|
+
async publish({ src, code, type = "", inline = false, copy = true }) {
|
|
155
160
|
if (!inline && copy) {
|
|
156
161
|
const base = this.runpath(this.config.location.client).join(src);
|
|
157
|
-
await base.directory.
|
|
158
|
-
await base.
|
|
162
|
+
await base.directory.create();
|
|
163
|
+
await base.write(code);
|
|
159
164
|
}
|
|
160
165
|
if (inline || type === "style") {
|
|
161
166
|
this.assets.push({
|
|
@@ -167,12 +172,13 @@ export default async (log, root, config) => {
|
|
|
167
172
|
});
|
|
168
173
|
}
|
|
169
174
|
},
|
|
170
|
-
export({type, code}) {
|
|
171
|
-
this.exports.push({type, code});
|
|
175
|
+
export({ type, code }) {
|
|
176
|
+
this.exports.push({ type, code });
|
|
172
177
|
},
|
|
173
|
-
register(extension,
|
|
178
|
+
register(extension, operations) {
|
|
174
179
|
is(this.handlers[extension]).undefined(DoubleFileExtension.new(extension));
|
|
175
|
-
this.handlers[extension] =
|
|
180
|
+
this.handlers[extension] = operations.handle;
|
|
181
|
+
this.extensions[extension] = operations;
|
|
176
182
|
},
|
|
177
183
|
async hash(data, algorithm = "sha-384") {
|
|
178
184
|
const bytes = await crypto.subtle.digest(algorithm, encoder.encode(data));
|
|
@@ -180,13 +186,13 @@ export default async (log, root, config) => {
|
|
|
180
186
|
return `${prefix}-${btoa(String.fromCharCode(...new Uint8Array(bytes)))}`;
|
|
181
187
|
},
|
|
182
188
|
async import(module, deep_import) {
|
|
183
|
-
const {http: {static: {root}}, location: {client}} = this.config;
|
|
189
|
+
const { http: { static: { root } }, location: { client } } = this.config;
|
|
184
190
|
|
|
185
191
|
const parts = module.split("/");
|
|
186
192
|
const path = [this.library, ...parts];
|
|
187
193
|
const pkg = await Path.resolve().join(...path, this.manifest).json();
|
|
188
194
|
const exports = pkg.exports === undefined
|
|
189
|
-
? {[module]: `/${module}/${pkg.main}`}
|
|
195
|
+
? { [module]: `/${module}/${pkg.main}` }
|
|
190
196
|
: transform(pkg.exports, entry => entry
|
|
191
197
|
.filter(([, export$]) =>
|
|
192
198
|
export$.browser?.[deep_import] !== undefined
|
|
@@ -202,11 +208,11 @@ export default async (log, root, config) => {
|
|
|
202
208
|
?? value.import?.replace(".", `./${module}`),
|
|
203
209
|
]));
|
|
204
210
|
const dependency = Path.resolve().join(...path);
|
|
205
|
-
const
|
|
206
|
-
await dependency.
|
|
211
|
+
const target = new Path(this.runpath(client), this.library, ...parts);
|
|
212
|
+
await dependency.copy(target);
|
|
207
213
|
this.importmaps = {
|
|
208
214
|
...valmap(exports, value => new Path(root, this.library, value).path),
|
|
209
|
-
...this.importmaps};
|
|
215
|
+
...this.importmaps };
|
|
210
216
|
},
|
|
211
217
|
};
|
|
212
218
|
};
|
package/src/bin.js
CHANGED
package/src/commands/dev.js
CHANGED
package/src/commands/exports.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {default as dev} from "./dev.js";
|
|
2
|
-
import {default as serve} from "./serve.js";
|
|
1
|
+
import { default as dev } from "./dev.js";
|
|
2
|
+
import { default as serve } from "./serve.js";
|
|
3
3
|
|
|
4
|
-
export default name => ({dev, serve})[name] ?? dev;
|
|
4
|
+
export default name => ({ dev, serve })[name] ?? dev;
|
package/src/commands/serve.js
CHANGED
package/src/cwd.js
CHANGED
package/src/dispatch.js
CHANGED
|
@@ -1,21 +1,24 @@
|
|
|
1
|
-
import {is
|
|
2
|
-
import {tryreturn} from "
|
|
3
|
-
import {map} from "
|
|
4
|
-
import {camelcased} from "
|
|
1
|
+
import { is } from "rcompat/invariant";
|
|
2
|
+
import { tryreturn } from "rcompat/sync";
|
|
3
|
+
import { map } from "rcompat/object";
|
|
4
|
+
import { camelcased } from "rcompat/string";
|
|
5
5
|
import errors from "./errors.js";
|
|
6
6
|
import validate from "./validate.js";
|
|
7
7
|
|
|
8
|
-
export default (patches = {}) => (
|
|
8
|
+
export default (patches = {}) => (object, raw, cased = true) => {
|
|
9
9
|
return Object.assign(Object.create(null), {
|
|
10
10
|
...map(patches, ([name, patch]) => [`get${camelcased(name)}`, property => {
|
|
11
11
|
is(property).defined(`\`${name}\` called without property`);
|
|
12
|
-
return tryreturn(_ => validate(patch,
|
|
13
|
-
.orelse(({message}) => errors.MismatchedType.throw(message));
|
|
12
|
+
return tryreturn(_ => validate(patch, object[property], property))
|
|
13
|
+
.orelse(({ message }) => errors.MismatchedType.throw(message));
|
|
14
14
|
}]),
|
|
15
15
|
get(property) {
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
16
|
+
is(property).string();
|
|
17
|
+
|
|
18
|
+
return object[cased ? property : property.toLowerCase()];
|
|
19
|
+
},
|
|
20
|
+
getAll() {
|
|
21
|
+
return object;
|
|
19
22
|
},
|
|
20
23
|
raw,
|
|
21
24
|
});
|
package/src/errors.js
CHANGED
package/src/errors.json
CHANGED
|
@@ -56,6 +56,11 @@
|
|
|
56
56
|
"fix": "do not use dots in route names",
|
|
57
57
|
"level": "Error"
|
|
58
58
|
},
|
|
59
|
+
"InvalidTypeExport": {
|
|
60
|
+
"message": "invalid type export at {0}",
|
|
61
|
+
"fix": "export object with a `base` string and a `validate` function",
|
|
62
|
+
"level": "Error"
|
|
63
|
+
},
|
|
59
64
|
"InvalidTypeName": {
|
|
60
65
|
"message": "invalid type name {0}",
|
|
61
66
|
"fix": "use lowercase-first latin letters and decimals in type names",
|
package/src/exports.js
CHANGED
|
@@ -2,8 +2,8 @@ import run from "./run.js";
|
|
|
2
2
|
|
|
3
3
|
export * from "./handlers/exports.js";
|
|
4
4
|
|
|
5
|
-
export {default as Logger} from "./Logger.js";
|
|
5
|
+
export { default as Logger } from "./Logger.js";
|
|
6
6
|
|
|
7
|
-
export {URL, Response, Status, MediaType} from "
|
|
7
|
+
export { URL, Response, Status, MediaType } from "rcompat/http";
|
|
8
8
|
|
|
9
9
|
export default command => run(command);
|
package/src/handlers/error.js
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
import {Response, Status, MediaType} from "
|
|
1
|
+
import { Response, Status, MediaType } from "rcompat/http";
|
|
2
2
|
|
|
3
|
-
export default (body = "Not Found", {status = Status.NOT_FOUND, page} = {}) =>
|
|
3
|
+
export default (body = "Not Found", { status = Status.NOT_FOUND, page } = {}) =>
|
|
4
4
|
async app => new Response(await app.render({
|
|
5
5
|
body,
|
|
6
|
-
page: page ?? app.config.pages.error}), {
|
|
6
|
+
page: page ?? app.config.pages.error }), {
|
|
7
7
|
status,
|
|
8
|
-
headers: {...app.headers(), "Content-Type": MediaType.TEXT_HTML},
|
|
8
|
+
headers: { ...app.headers(), "Content-Type": MediaType.TEXT_HTML },
|
|
9
9
|
});
|
package/src/handlers/exports.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
export {default as text} from "./text.js";
|
|
2
|
-
export {default as json} from "./json.js";
|
|
3
|
-
export {default as stream} from "./stream.js";
|
|
4
|
-
export {default as redirect} from "./redirect.js";
|
|
5
|
-
export {default as html} from "./html.js";
|
|
6
|
-
export {default as view} from "./view.js";
|
|
7
|
-
export {default as error} from "./error.js";
|
|
1
|
+
export { default as text } from "./text.js";
|
|
2
|
+
export { default as json } from "./json.js";
|
|
3
|
+
export { default as stream } from "./stream.js";
|
|
4
|
+
export { default as redirect } from "./redirect.js";
|
|
5
|
+
export { default as html } from "./html.js";
|
|
6
|
+
export { default as view } from "./view.js";
|
|
7
|
+
export { default as error } from "./error.js";
|
package/src/handlers/html.js
CHANGED
|
@@ -1,29 +1,29 @@
|
|
|
1
|
-
import {Response, Status, MediaType} from "
|
|
1
|
+
import { Response, Status, MediaType } from "rcompat/http";
|
|
2
2
|
|
|
3
3
|
const script_re = /(?<=<script)>(?<code>.*?)(?=<\/script>)/gus;
|
|
4
4
|
const style_re = /(?<=<style)>(?<code>.*?)(?=<\/style>)/gus;
|
|
5
5
|
const remove = /<(?<tag>script|style)>.*?<\/\k<tag>>/gus;
|
|
6
6
|
|
|
7
7
|
export default (name, options = {}) => {
|
|
8
|
-
const {status = Status.OK, partial = false} = options;
|
|
8
|
+
const { status = Status.OK, partial = false } = options;
|
|
9
9
|
|
|
10
10
|
return async app => {
|
|
11
11
|
const html = await app.path.components.join(name).text();
|
|
12
12
|
const scripts = await Promise.all([...html.matchAll(script_re)]
|
|
13
|
-
.map(({groups: {code}}) => app.inline(code, "module")));
|
|
13
|
+
.map(({ groups: { code } }) => app.inline(code, "module")));
|
|
14
14
|
const styles = await Promise.all([...html.matchAll(style_re)]
|
|
15
|
-
.map(({groups: {code}}) => app.inline(code, "style")));
|
|
15
|
+
.map(({ groups: { code } }) => app.inline(code, "style")));
|
|
16
16
|
const assets = [...scripts, ...styles];
|
|
17
17
|
|
|
18
18
|
const body = html.replaceAll(remove, _ => "");
|
|
19
19
|
const head = assets.map(asset => asset.head).join("\n");
|
|
20
20
|
const script = scripts.map(asset => asset.csp).join(" ");
|
|
21
21
|
const style = styles.map(asset => asset.csp).join(" ");
|
|
22
|
-
const headers = {script, style};
|
|
22
|
+
const headers = { script, style };
|
|
23
23
|
|
|
24
|
-
return new Response(partial ? body : await app.render({body, head}), {
|
|
24
|
+
return new Response(partial ? body : await app.render({ body, head }), {
|
|
25
25
|
status,
|
|
26
|
-
headers: {...app.headers(headers), "Content-Type": MediaType.TEXT_HTML},
|
|
26
|
+
headers: { ...app.headers(headers), "Content-Type": MediaType.TEXT_HTML },
|
|
27
27
|
});
|
|
28
28
|
};
|
|
29
29
|
};
|
package/src/handlers/json.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import {Response, Status, MediaType} from "
|
|
1
|
+
import { Response, Status, MediaType } from "rcompat/http";
|
|
2
2
|
|
|
3
|
-
export default (body, {status = Status.OK} = {}) => app =>
|
|
3
|
+
export default (body, { status = Status.OK } = {}) => app =>
|
|
4
4
|
new Response(JSON.stringify(body), {
|
|
5
5
|
status,
|
|
6
|
-
headers: {...app.headers(), "Content-Type": MediaType.APPLICATION_JSON},
|
|
6
|
+
headers: { ...app.headers(), "Content-Type": MediaType.APPLICATION_JSON },
|
|
7
7
|
});
|
package/src/handlers/redirect.js
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import {Response, Status} from "
|
|
1
|
+
import { Response, Status } from "rcompat/http";
|
|
2
2
|
|
|
3
|
-
export default (Location, {status = Status.FOUND} = {}) => app =>
|
|
3
|
+
export default (Location, { status = Status.FOUND } = {}) => app =>
|
|
4
4
|
/* no body */
|
|
5
5
|
new Response(null, {
|
|
6
6
|
status,
|
|
7
|
-
headers: {...app.headers(), Location},
|
|
7
|
+
headers: { ...app.headers(), Location },
|
|
8
8
|
});
|
package/src/handlers/stream.js
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import {Response, Status, MediaType} from "
|
|
1
|
+
import { Response, Status, MediaType } from "rcompat/http";
|
|
2
2
|
|
|
3
|
-
export default (body, {status = Status.OK} = {}) => app =>
|
|
3
|
+
export default (body, { status = Status.OK } = {}) => app =>
|
|
4
4
|
new Response(body, {
|
|
5
5
|
status,
|
|
6
|
-
headers: {...app.headers(), "Content-Type":
|
|
7
|
-
MediaType.APPLICATION_OCTET_STREAM},
|
|
6
|
+
headers: { ...app.headers(), "Content-Type":
|
|
7
|
+
MediaType.APPLICATION_OCTET_STREAM },
|
|
8
8
|
});
|
package/src/handlers/text.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import {Response, Status, MediaType} from "
|
|
1
|
+
import { Response, Status, MediaType } from "rcompat/http";
|
|
2
2
|
|
|
3
|
-
export default (body, {status = Status.OK} = {}) => app =>
|
|
3
|
+
export default (body, { status = Status.OK } = {}) => app =>
|
|
4
4
|
new Response(body, {
|
|
5
5
|
status,
|
|
6
|
-
headers: {...app.headers(), "Content-Type": MediaType.TEXT_PLAIN},
|
|
6
|
+
headers: { ...app.headers(), "Content-Type": MediaType.TEXT_PLAIN },
|
|
7
7
|
});
|
package/src/hooks/bundle.js
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
import {Path} from "
|
|
1
|
+
import { Path } from "rcompat/fs";
|
|
2
2
|
|
|
3
3
|
export default async (app, type, post = () => undefined) => {
|
|
4
|
-
const {config} = app;
|
|
5
|
-
const {build} = config;
|
|
6
|
-
const {includes} = build;
|
|
4
|
+
const { config } = app;
|
|
5
|
+
const { build } = config;
|
|
6
|
+
const { includes } = build;
|
|
7
7
|
|
|
8
8
|
const reserved = Object.values(app.config.location);
|
|
9
9
|
|
|
@@ -13,10 +13,10 @@ export default async (app, type, post = () => undefined) => {
|
|
|
13
13
|
.filter(include => /^[^/]*$/u.test(include))
|
|
14
14
|
.map(async include => {
|
|
15
15
|
const path = app.root.join(include);
|
|
16
|
-
if (await path.exists) {
|
|
17
|
-
const
|
|
18
|
-
await app.stage(path,
|
|
19
|
-
await post(
|
|
16
|
+
if (await path.exists()) {
|
|
17
|
+
const target = Path.join(type, include);
|
|
18
|
+
await app.stage(path, target);
|
|
19
|
+
await post(target);
|
|
20
20
|
}
|
|
21
21
|
}));
|
|
22
22
|
}
|