primate 0.16.3 → 0.18.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 +79 -51
- package/src/app.js +64 -53
- package/src/commands/exports.js +2 -5
- package/src/defaults/primate.config.js +1 -0
- package/src/errors.js +112 -0
- package/src/handlers/error.js +9 -0
- package/src/handlers/exports.js +1 -0
- package/src/handlers/redirect.js +3 -1
- package/src/handlers/view.js +4 -4
- package/src/hooks/bundle.js +1 -1
- package/src/hooks/compile.js +1 -1
- package/src/hooks/exports.js +1 -0
- package/src/hooks/handle/exports.js +0 -1
- package/src/hooks/handle.js +23 -100
- package/src/hooks/parse.js +59 -0
- package/src/hooks/parse.spec.js +72 -0
- package/src/hooks/publish.js +1 -1
- package/src/hooks/register.js +1 -1
- package/src/hooks/route.js +86 -48
- package/src/hooks/route.spec.js +181 -0
- package/src/http-statuses.js +4 -0
- package/src/run.js +52 -1
- package/src/start.js +14 -3
- package/src/commands/build.js +0 -1
- package/src/commands/create.js +0 -42
- package/src/commands/help.js +0 -3
- package/src/handlers/http.js +0 -1
- package/src/handlers/http404.js +0 -6
- package/src/hooks/handle/http-statuses.js +0 -5
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "primate",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.18.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",
|
|
@@ -16,7 +16,7 @@
|
|
|
16
16
|
"directory": "packages/primate"
|
|
17
17
|
},
|
|
18
18
|
"dependencies": {
|
|
19
|
-
"runtime-compat": "^0.
|
|
19
|
+
"runtime-compat": "^0.17.0"
|
|
20
20
|
},
|
|
21
21
|
"type": "module",
|
|
22
22
|
"exports": "./src/exports.js"
|
package/src/Logger.js
CHANGED
|
@@ -1,98 +1,126 @@
|
|
|
1
1
|
import {assert, is} from "runtime-compat/dyndef";
|
|
2
|
+
import {blue, bold, green, red, yellow, dim} from "runtime-compat/colors";
|
|
2
3
|
|
|
3
|
-
const
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
green: msg => `\x1b[32m${msg}\x1b[0m`,
|
|
8
|
-
yellow: msg => `\x1b[33m${msg}\x1b[0m`,
|
|
9
|
-
blue: msg => `\x1b[34m${msg}\x1b[0m`,
|
|
10
|
-
gray: msg => `\x1b[2m${msg}\x1b[0m`,
|
|
4
|
+
const errors = {
|
|
5
|
+
Error: 0,
|
|
6
|
+
Warn: 1,
|
|
7
|
+
Info: 2,
|
|
11
8
|
};
|
|
12
9
|
|
|
13
|
-
const
|
|
14
|
-
const
|
|
15
|
-
const
|
|
16
|
-
|
|
17
|
-
// Error natively provided
|
|
18
|
-
const Warn = class Warn extends Error {};
|
|
19
|
-
const Info = class Info extends Error {};
|
|
10
|
+
const print = (...messages) => process.stdout.write(messages.join(" "));
|
|
11
|
+
const bye = () => print(dim(yellow("~~ bye\n")));
|
|
12
|
+
const mark = (format, ...params) => params.reduce((formatted, param) =>
|
|
13
|
+
formatted.replace("%", bold(param)), format);
|
|
20
14
|
|
|
21
|
-
const
|
|
22
|
-
[Error, error],
|
|
23
|
-
[Warn, warn],
|
|
24
|
-
[Info, info],
|
|
25
|
-
]);
|
|
15
|
+
const reference = "https://primatejs.com/reference/errors";
|
|
26
16
|
|
|
27
|
-
const
|
|
17
|
+
const hyphenate = classCased => classCased
|
|
18
|
+
.split("")
|
|
19
|
+
.map(character => character
|
|
20
|
+
.replace(/[A-Z]/u, capital => `-${capital.toLowerCase()}`))
|
|
21
|
+
.join("")
|
|
22
|
+
.slice(1);
|
|
28
23
|
|
|
29
24
|
const Logger = class Logger {
|
|
30
25
|
#level; #trace;
|
|
31
26
|
|
|
32
|
-
|
|
33
|
-
|
|
27
|
+
static throwable(type, name, module) {
|
|
28
|
+
return {
|
|
29
|
+
throw(args = {}) {
|
|
30
|
+
const {message, level, fix} = type(args);
|
|
31
|
+
const error = new Error(mark(...message));
|
|
32
|
+
error.level = level;
|
|
33
|
+
error.fix = mark(...fix);
|
|
34
|
+
error.name = name;
|
|
35
|
+
error.module = module;
|
|
36
|
+
throw error;
|
|
37
|
+
},
|
|
38
|
+
warn(logger, ...args) {
|
|
39
|
+
const {message, level, fix} = type(...args);
|
|
40
|
+
const error = {level, message: mark(...message), fix: mark(...fix)};
|
|
41
|
+
logger.auto({...error, name, module});
|
|
42
|
+
},
|
|
43
|
+
};
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
constructor({level = errors.Error, trace = false} = {}) {
|
|
47
|
+
assert(level !== undefined && level <= errors.Info);
|
|
34
48
|
is(trace).boolean();
|
|
35
49
|
this.#level = level;
|
|
36
50
|
this.#trace = trace;
|
|
37
51
|
}
|
|
38
52
|
|
|
53
|
+
static print(...args) {
|
|
54
|
+
print(...args);
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
static get mark() {
|
|
58
|
+
return mark;
|
|
59
|
+
}
|
|
60
|
+
|
|
39
61
|
static get Error() {
|
|
40
|
-
return Error;
|
|
62
|
+
return errors.Error;
|
|
41
63
|
}
|
|
42
64
|
|
|
43
65
|
static get Warn() {
|
|
44
|
-
return Warn;
|
|
66
|
+
return errors.Warn;
|
|
45
67
|
}
|
|
46
68
|
|
|
47
69
|
static get Info() {
|
|
48
|
-
return Info;
|
|
70
|
+
return errors.Info;
|
|
49
71
|
}
|
|
50
72
|
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
print(
|
|
73
|
+
get class() {
|
|
74
|
+
return this.constructor;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
#print(pre, color, message, {fix, module, name} = {}, error) {
|
|
78
|
+
print(pre, `${module !== undefined ? `${color(module)} ` : ""}${message}`, "\n");
|
|
79
|
+
if (fix && this.level >= errors.Warn) {
|
|
80
|
+
print(blue("++"), fix);
|
|
81
|
+
name && print(dim(`\n -> ${reference}/${module ?? "primate"}#${hyphenate(name)}`), "\n");
|
|
82
|
+
}
|
|
83
|
+
if (this.#trace && error) {
|
|
84
|
+
print(pre, color(module), "trace follows\n");
|
|
85
|
+
console.log(error);
|
|
59
86
|
}
|
|
60
87
|
}
|
|
61
88
|
|
|
62
89
|
get level() {
|
|
63
|
-
return
|
|
90
|
+
return this.#level;
|
|
64
91
|
}
|
|
65
92
|
|
|
66
|
-
info(message) {
|
|
67
|
-
if (this.level >=
|
|
68
|
-
this.#print(
|
|
93
|
+
info(message, args) {
|
|
94
|
+
if (this.level >= errors.Info) {
|
|
95
|
+
this.#print(green("--"), green, message, args);
|
|
69
96
|
}
|
|
70
97
|
}
|
|
71
98
|
|
|
72
|
-
warn(message) {
|
|
73
|
-
if (this.level >=
|
|
74
|
-
this.#print(
|
|
99
|
+
warn(message, args) {
|
|
100
|
+
if (this.level >= errors.Warn) {
|
|
101
|
+
this.#print(yellow("??"), yellow, message, args);
|
|
75
102
|
}
|
|
76
103
|
}
|
|
77
104
|
|
|
78
|
-
error(message) {
|
|
79
|
-
if (this.level >=
|
|
80
|
-
this.#print(
|
|
105
|
+
error(message, args, error) {
|
|
106
|
+
if (this.level >= errors.Warn) {
|
|
107
|
+
this.#print(red("!!"), red, message, args, error);
|
|
81
108
|
}
|
|
82
109
|
}
|
|
83
110
|
|
|
84
|
-
auto(
|
|
85
|
-
|
|
86
|
-
|
|
111
|
+
auto(error) {
|
|
112
|
+
const {level, message, ...args} = error;
|
|
113
|
+
if (level === errors.Info) {
|
|
114
|
+
return this.info(message, args, error);
|
|
87
115
|
}
|
|
88
|
-
if (
|
|
89
|
-
return this.warn(message
|
|
116
|
+
if (level === errors.Warn) {
|
|
117
|
+
return this.warn(message, args, error);
|
|
90
118
|
}
|
|
91
119
|
|
|
92
|
-
return this.error(message);
|
|
120
|
+
return this.error(message, args, error);
|
|
93
121
|
}
|
|
94
122
|
};
|
|
95
123
|
|
|
96
124
|
export default Logger;
|
|
97
125
|
|
|
98
|
-
export {
|
|
126
|
+
export {print, bye};
|
package/src/app.js
CHANGED
|
@@ -1,10 +1,9 @@
|
|
|
1
1
|
import crypto from "runtime-compat/crypto";
|
|
2
|
-
import {is} from "runtime-compat/dyndef";
|
|
3
2
|
import {File, Path} from "runtime-compat/fs";
|
|
4
|
-
import
|
|
5
|
-
import
|
|
6
|
-
import {colors, print, default as Logger} from "./Logger.js";
|
|
3
|
+
import {bold, blue} from "runtime-compat/colors";
|
|
4
|
+
import errors from "./errors.js";
|
|
7
5
|
import * as handlers from "./handlers/exports.js";
|
|
6
|
+
import * as hooks from "./hooks/exports.js";
|
|
8
7
|
|
|
9
8
|
const qualify = (root, paths) =>
|
|
10
9
|
Object.keys(paths).reduce((sofar, key) => {
|
|
@@ -15,36 +14,6 @@ const qualify = (root, paths) =>
|
|
|
15
14
|
return sofar;
|
|
16
15
|
}, {});
|
|
17
16
|
|
|
18
|
-
const configName = "primate.config.js";
|
|
19
|
-
|
|
20
|
-
const getConfig = async (root, filename) => {
|
|
21
|
-
const config = root.join(filename);
|
|
22
|
-
if (await config.exists) {
|
|
23
|
-
try {
|
|
24
|
-
const imported = await import(config);
|
|
25
|
-
if (imported.default === undefined) {
|
|
26
|
-
print(`${colors.yellow("??")} ${configName} has no default export\n`);
|
|
27
|
-
}
|
|
28
|
-
return extend(defaults, imported.default);
|
|
29
|
-
} catch (error) {
|
|
30
|
-
print(`${colors.red("!!")} couldn't load config file\n`);
|
|
31
|
-
throw error;
|
|
32
|
-
}
|
|
33
|
-
} else {
|
|
34
|
-
return defaults;
|
|
35
|
-
}
|
|
36
|
-
};
|
|
37
|
-
|
|
38
|
-
const getRoot = async () => {
|
|
39
|
-
try {
|
|
40
|
-
// use module root if possible
|
|
41
|
-
return await Path.root();
|
|
42
|
-
} catch (error) {
|
|
43
|
-
// fall back to current directory
|
|
44
|
-
return Path.resolve();
|
|
45
|
-
}
|
|
46
|
-
};
|
|
47
|
-
|
|
48
17
|
const src = new Path(import.meta.url).up(1);
|
|
49
18
|
|
|
50
19
|
const index = async app => {
|
|
@@ -65,23 +34,48 @@ const hash = async (string, algorithm = "sha-384") => {
|
|
|
65
34
|
return `${algo}-${btoa(String.fromCharCode(...new Uint8Array(bytes)))}`;
|
|
66
35
|
};
|
|
67
36
|
|
|
68
|
-
export default async (
|
|
69
|
-
|
|
70
|
-
const root = await getRoot();
|
|
71
|
-
const config = await getConfig(root, filename);
|
|
72
|
-
|
|
73
|
-
const {name, version} = await src.up(1).join("package.json").json();
|
|
37
|
+
export default async (config, root, log) => {
|
|
38
|
+
const {http} = config;
|
|
74
39
|
|
|
75
40
|
// if ssl activated, resolve key and cert early
|
|
76
|
-
if (
|
|
77
|
-
|
|
78
|
-
|
|
41
|
+
if (http.ssl) {
|
|
42
|
+
http.ssl.key = root.join(http.ssl.key);
|
|
43
|
+
http.ssl.cert = root.join(http.ssl.cert);
|
|
79
44
|
}
|
|
80
45
|
|
|
46
|
+
const paths = qualify(root, config.paths);
|
|
47
|
+
|
|
48
|
+
const ending = ".js";
|
|
49
|
+
const routes = paths.routes === undefined ? [] : await Promise.all(
|
|
50
|
+
(await Path.collect(paths.routes, /^.*.js$/u))
|
|
51
|
+
.map(async route => [
|
|
52
|
+
`${route}`.replace(paths.routes, "").slice(1, -ending.length),
|
|
53
|
+
(await import(route)).default,
|
|
54
|
+
]));
|
|
55
|
+
|
|
56
|
+
const modules = config.modules === undefined ? [] : config.modules;
|
|
57
|
+
|
|
58
|
+
modules.every((module, n) => module.name !== undefined ||
|
|
59
|
+
errors.ModulesMustHaveNames.throw({n}));
|
|
60
|
+
|
|
61
|
+
new Set(modules.map(({name}) => name)).size !== modules.length &&
|
|
62
|
+
errors.DoubleModule.throw({
|
|
63
|
+
modules: modules.map(({name}) => name),
|
|
64
|
+
config: root.join("primate.config.js"),
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
const hookless = modules.filter(module =>
|
|
68
|
+
!Object.keys(module).some(key => Object.keys(hooks).includes(key)));
|
|
69
|
+
hookless.length > 0 && errors.ModuleHasNoHooks.warn(log, {hookless});
|
|
70
|
+
|
|
71
|
+
const {name, version} = await src.up(1).join("package.json").json();
|
|
72
|
+
|
|
81
73
|
const app = {
|
|
82
74
|
config,
|
|
83
|
-
|
|
84
|
-
|
|
75
|
+
routes,
|
|
76
|
+
secure: http?.ssl !== undefined,
|
|
77
|
+
name,
|
|
78
|
+
version,
|
|
85
79
|
library: {},
|
|
86
80
|
identifiers: {},
|
|
87
81
|
replace(code) {
|
|
@@ -96,9 +90,28 @@ export default async (filename = configName) => {
|
|
|
96
90
|
},
|
|
97
91
|
resources: [],
|
|
98
92
|
entrypoints: [],
|
|
99
|
-
paths
|
|
93
|
+
paths,
|
|
100
94
|
root,
|
|
101
|
-
log
|
|
95
|
+
log,
|
|
96
|
+
generateHeaders: () => {
|
|
97
|
+
const csp = Object.keys(http.csp).reduce((policy_string, key) =>
|
|
98
|
+
`${policy_string}${key} ${http.csp[key]};`, "");
|
|
99
|
+
const scripts = app.resources
|
|
100
|
+
.map(resource => `'${resource.integrity}'`).join(" ");
|
|
101
|
+
const _csp = scripts === "" ? csp : `${csp}script-src 'self' ${scripts};`;
|
|
102
|
+
// remove inline resources
|
|
103
|
+
for (let i = app.resources.length - 1; i >= 0; i--) {
|
|
104
|
+
const resource = app.resources[i];
|
|
105
|
+
if (resource.inline) {
|
|
106
|
+
app.resources.splice(i, 1);
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
return {
|
|
111
|
+
"Content-Security-Policy": _csp,
|
|
112
|
+
"Referrer-Policy": "same-origin",
|
|
113
|
+
};
|
|
114
|
+
},
|
|
102
115
|
handlers: {...handlers},
|
|
103
116
|
render: async ({body = "", head = ""} = {}) => {
|
|
104
117
|
const html = await index(app);
|
|
@@ -124,7 +137,7 @@ export default async (filename = configName) => {
|
|
|
124
137
|
// while integrity is only really needed for scripts, it is also later
|
|
125
138
|
// used for the etag header
|
|
126
139
|
const integrity = await hash(code);
|
|
127
|
-
const _src = new Path(
|
|
140
|
+
const _src = new Path(http.static.root).join(src ?? "");
|
|
128
141
|
app.resources.push({src: `${_src}`, code, type, inline, integrity});
|
|
129
142
|
return integrity;
|
|
130
143
|
},
|
|
@@ -140,12 +153,10 @@ export default async (filename = configName) => {
|
|
|
140
153
|
]));
|
|
141
154
|
app.identifiers = {...exports, ...app.identifiers};
|
|
142
155
|
},
|
|
143
|
-
modules
|
|
156
|
+
modules,
|
|
144
157
|
};
|
|
145
|
-
print(
|
|
146
|
-
|
|
147
|
-
const address = `${type}://${config.http.host}:${config.http.port}`;
|
|
148
|
-
print(colors.gray(`at ${address}`), "\n");
|
|
158
|
+
log.class.print(blue(bold(name)), blue(version),
|
|
159
|
+
`at http${app.secure ? "s" : ""}://${http.host}:${http.port}\n`);
|
|
149
160
|
// modules may load other modules
|
|
150
161
|
await Promise.all(app.modules
|
|
151
162
|
.filter(module => module.load !== undefined)
|
package/src/commands/exports.js
CHANGED
|
@@ -1,11 +1,8 @@
|
|
|
1
1
|
import {default as dev} from "./dev.js";
|
|
2
2
|
import {default as serve} from "./serve.js";
|
|
3
|
-
import {default as build} from "./build.js";
|
|
4
|
-
import {default as create} from "./create.js";
|
|
5
|
-
import {default as help} from "./help.js";
|
|
6
3
|
|
|
7
|
-
const commands = {dev, serve
|
|
4
|
+
const commands = {dev, serve};
|
|
8
5
|
|
|
9
|
-
const run = name => commands[name] ??
|
|
6
|
+
const run = name => commands[name] ?? dev;
|
|
10
7
|
|
|
11
8
|
export default name => name === undefined ? dev : run(name);
|
package/src/errors.js
ADDED
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
import Logger from "./Logger.js";
|
|
2
|
+
|
|
3
|
+
export default Object.fromEntries(Object.entries({
|
|
4
|
+
CannotParseBody({body, contentType}) {
|
|
5
|
+
return {
|
|
6
|
+
message: ["cannot parse body % as %", body, contentType],
|
|
7
|
+
fix: ["use a different content type or fix body"],
|
|
8
|
+
level: Logger.Warn,
|
|
9
|
+
};
|
|
10
|
+
},
|
|
11
|
+
DoubleModule({modules, config}) {
|
|
12
|
+
const double = modules.find((module, i, array) =>
|
|
13
|
+
array.filter((_, j) => i !== j).includes(module));
|
|
14
|
+
return {
|
|
15
|
+
message: ["double module % in %", double, config],
|
|
16
|
+
fix: ["load % only once", double],
|
|
17
|
+
level: Logger.Error,
|
|
18
|
+
};
|
|
19
|
+
},
|
|
20
|
+
DoublePathParameter({path, double}) {
|
|
21
|
+
return {
|
|
22
|
+
message: ["double path parameter % in route %", double, path],
|
|
23
|
+
fix: ["disambiguate path parameters in route names"],
|
|
24
|
+
level: Logger.Error,
|
|
25
|
+
};
|
|
26
|
+
},
|
|
27
|
+
DoubleRoute({double}) {
|
|
28
|
+
return {
|
|
29
|
+
message: ["double route %", double],
|
|
30
|
+
fix: ["disambiguate route % and %", double, `${double}/index`],
|
|
31
|
+
level: Logger.Error,
|
|
32
|
+
};
|
|
33
|
+
},
|
|
34
|
+
EmptyRouteFile({config: {paths}, route}) {
|
|
35
|
+
return {
|
|
36
|
+
message: ["empty route file at %", `${paths.routes}/${route}.js`],
|
|
37
|
+
fix: ["add routes or remove file"],
|
|
38
|
+
level: Logger.Warn,
|
|
39
|
+
};
|
|
40
|
+
},
|
|
41
|
+
ErrorInConfigFile({config, message}) {
|
|
42
|
+
return {
|
|
43
|
+
message: ["error in config %", message],
|
|
44
|
+
fix: ["check errors in config file by running %", `node ${config}`],
|
|
45
|
+
level: Logger.Error,
|
|
46
|
+
};
|
|
47
|
+
},
|
|
48
|
+
ModuleHasNoHooks({hookless}) {
|
|
49
|
+
return {
|
|
50
|
+
message: ["module % has no hooks", hookless.join(", ")],
|
|
51
|
+
fix: ["ensure every module uses at least one hook or deactivate it"],
|
|
52
|
+
level: Logger.Warn,
|
|
53
|
+
};
|
|
54
|
+
},
|
|
55
|
+
ModulesMustHaveNames({n}) {
|
|
56
|
+
return {
|
|
57
|
+
message: ["modules must have names"],
|
|
58
|
+
fix: ["update module at index % and inform maintainer", n],
|
|
59
|
+
level: Logger.Error,
|
|
60
|
+
};
|
|
61
|
+
},
|
|
62
|
+
EmptyConfigFile({config}) {
|
|
63
|
+
return {
|
|
64
|
+
message: ["empty config file at %", config],
|
|
65
|
+
fix: ["add configuration options or remove file"],
|
|
66
|
+
level: Logger.Warn,
|
|
67
|
+
};
|
|
68
|
+
},
|
|
69
|
+
NoFileForPath({pathname, config: {paths}}) {
|
|
70
|
+
return {
|
|
71
|
+
message: ["no file for %", pathname],
|
|
72
|
+
fix: ["if unintentional create a file at %%", paths.static, pathname],
|
|
73
|
+
level: Logger.Info,
|
|
74
|
+
};
|
|
75
|
+
},
|
|
76
|
+
NoHandlerForExtension({name, ending}) {
|
|
77
|
+
return {
|
|
78
|
+
message: ["no handler for % extension", ending],
|
|
79
|
+
fix: ["add handler module for % files or remove %", `.${ending}`, name],
|
|
80
|
+
level: Logger.Error,
|
|
81
|
+
};
|
|
82
|
+
},
|
|
83
|
+
NoRouteToPath({method, pathname, config: {paths}}) {
|
|
84
|
+
const route = `${paths.routes}/${pathname === "/" ? "index" : ""}.js`;
|
|
85
|
+
return {
|
|
86
|
+
message: ["no % route to %", method, pathname],
|
|
87
|
+
fix: ["if unintentional create a route at %", route],
|
|
88
|
+
level: Logger.Info,
|
|
89
|
+
};
|
|
90
|
+
},
|
|
91
|
+
InvalidPathParameter({named, path}) {
|
|
92
|
+
return {
|
|
93
|
+
message: ["invalid path parameter % in route %", named, path],
|
|
94
|
+
fix: ["use only latin letters and decimal digits in path parameters"],
|
|
95
|
+
level: Logger.Error,
|
|
96
|
+
};
|
|
97
|
+
},
|
|
98
|
+
InvalidRouteName({path}) {
|
|
99
|
+
return {
|
|
100
|
+
message: ["invalid route name %", path],
|
|
101
|
+
fix: ["do not use dots in route names"],
|
|
102
|
+
level: Logger.Error,
|
|
103
|
+
};
|
|
104
|
+
},
|
|
105
|
+
InvalidType({name}) {
|
|
106
|
+
return {
|
|
107
|
+
message: ["invalid type %", name],
|
|
108
|
+
fix: ["use only latin letters and decimal digits in types"],
|
|
109
|
+
level: Logger.Error,
|
|
110
|
+
};
|
|
111
|
+
},
|
|
112
|
+
}).map(([name, error]) => [name, Logger.throwable(error, name, "primate")]));
|
package/src/handlers/exports.js
CHANGED
package/src/handlers/redirect.js
CHANGED
package/src/handlers/view.js
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
|
+
import errors from "../errors.js";
|
|
2
|
+
|
|
1
3
|
export default (name, props, options) => async (app, headers) => {
|
|
2
4
|
const ending = name.slice(name.lastIndexOf(".") + 1);
|
|
3
5
|
const handler = app.handlers[ending];
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
}
|
|
7
|
-
return handler(name, {load: true, ...props}, options)(app, headers);
|
|
6
|
+
return handler?.(name, {load: true, ...props}, options)(app, headers)
|
|
7
|
+
?? errors.NoHandlerForExtension.throw({name, ending});
|
|
8
8
|
};
|
package/src/hooks/bundle.js
CHANGED
|
@@ -22,7 +22,7 @@ const pre = async app => {
|
|
|
22
22
|
export default async (app, bundle) => {
|
|
23
23
|
await pre(app);
|
|
24
24
|
if (bundle) {
|
|
25
|
-
app.log.info("running bundle hooks");
|
|
25
|
+
app.log.info("running bundle hooks", {module: "primate"});
|
|
26
26
|
await [...filter("bundle", app.modules), _ => _]
|
|
27
27
|
.reduceRight((acc, handler) => input => handler(input, acc))(app);
|
|
28
28
|
}
|
package/src/hooks/compile.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
const filter = (key, array) => array?.flatMap(m => m[key] ?? []) ?? [];
|
|
2
2
|
|
|
3
3
|
export default async app => {
|
|
4
|
-
app.log.info("running compile hooks");
|
|
4
|
+
app.log.info("running compile hooks", {module: "primate"});
|
|
5
5
|
await [...filter("compile", app.modules), _ => _]
|
|
6
6
|
.reduceRight((acc, handler) => input => handler(input, acc))(app);
|
|
7
7
|
};
|
package/src/hooks/exports.js
CHANGED