primate 0.19.3 → 0.20.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 +48 -64
- package/src/app.js +86 -121
- package/src/bin.js +0 -1
- package/src/defaults/primate.config.js +12 -5
- package/src/dispatch.js +8 -13
- package/src/errors.js +6 -146
- package/src/exports.js +1 -1
- package/src/handlers/error.js +4 -4
- package/src/handlers/html.js +5 -6
- package/src/handlers/json.js +2 -2
- package/src/handlers/redirect.js +3 -3
- package/src/handlers/stream.js +2 -2
- package/src/handlers/text.js +2 -2
- package/src/handlers/view.js +3 -3
- package/src/hooks/bundle.js +4 -15
- package/src/hooks/compile.js +21 -2
- package/src/hooks/copy_includes.js +24 -0
- package/src/hooks/handle.js +55 -42
- package/src/hooks/parse.js +13 -16
- package/src/hooks/publish.js +42 -23
- package/src/hooks/register.js +1 -3
- package/src/hooks/{handle → respond}/respond.js +1 -1
- package/src/hooks/route.js +25 -85
- package/src/loaders/common.js +34 -0
- package/src/loaders/exports.js +3 -0
- package/src/loaders/modules.js +40 -0
- package/src/loaders/routes/exports.js +3 -0
- package/src/loaders/routes/guards.js +3 -0
- package/src/loaders/routes/layouts.js +3 -0
- package/src/loaders/routes/load.js +17 -0
- package/src/loaders/routes/routes.js +22 -0
- package/src/loaders/routes.js +45 -0
- package/src/loaders/types.js +19 -0
- package/src/run.js +13 -24
- package/src/start.js +11 -15
- package/src/defaults/index.html +0 -9
- package/src/extend.js +0 -10
- package/src/http-statuses.js +0 -4
- /package/src/hooks/{handle → respond}/duck.js +0 -0
- /package/src/hooks/{handle → respond}/exports.js +0 -0
- /package/src/hooks/{handle → respond}/mime.js +0 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "primate",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.20.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",
|
|
@@ -17,7 +17,7 @@
|
|
|
17
17
|
"directory": "packages/primate"
|
|
18
18
|
},
|
|
19
19
|
"dependencies": {
|
|
20
|
-
"runtime-compat": "^0.
|
|
20
|
+
"runtime-compat": "^0.20.2"
|
|
21
21
|
},
|
|
22
22
|
"type": "module",
|
|
23
23
|
"exports": "./src/exports.js"
|
package/src/Logger.js
CHANGED
|
@@ -1,16 +1,17 @@
|
|
|
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, valmap} from "runtime-compat/object";
|
|
3
4
|
|
|
4
|
-
const
|
|
5
|
+
const levels = {
|
|
5
6
|
Error: 0,
|
|
6
7
|
Warn: 1,
|
|
7
8
|
Info: 2,
|
|
8
9
|
};
|
|
9
10
|
|
|
10
11
|
const print = (...messages) => process.stdout.write(messages.join(" "));
|
|
11
|
-
const bye =
|
|
12
|
-
const mark = (format, ...params) => params.reduce((formatted, param) =>
|
|
13
|
-
formatted.replace(
|
|
12
|
+
const bye = _ => print(dim(yellow("~~ bye\n")));
|
|
13
|
+
const mark = (format, ...params) => params.reduce((formatted, param, i) =>
|
|
14
|
+
formatted.replace(`{${i}}`, bold(param)), format);
|
|
14
15
|
|
|
15
16
|
const reference = "https://primatejs.com/reference/errors";
|
|
16
17
|
|
|
@@ -21,103 +22,86 @@ const hyphenate = classCased => classCased
|
|
|
21
22
|
.join("")
|
|
22
23
|
.slice(1);
|
|
23
24
|
|
|
25
|
+
const throwable = ({message, level, fix}, name, module) => ({
|
|
26
|
+
new(...args) {
|
|
27
|
+
const error = new Error(mark(message, ...args));
|
|
28
|
+
error.level = Logger[level];
|
|
29
|
+
error.fix = mark(fix, ...args);
|
|
30
|
+
error.name = name;
|
|
31
|
+
error.module = module;
|
|
32
|
+
return error;
|
|
33
|
+
},
|
|
34
|
+
throw(...args) {
|
|
35
|
+
throw this.new(...args);
|
|
36
|
+
},
|
|
37
|
+
warn(logger, ...args) {
|
|
38
|
+
const error = {level: Logger[level], message: mark(message, ...args),
|
|
39
|
+
fix: mark(fix, ...args)};
|
|
40
|
+
logger.auto({...error, name, module});
|
|
41
|
+
},
|
|
42
|
+
});
|
|
43
|
+
|
|
24
44
|
const Logger = class Logger {
|
|
25
45
|
#level; #trace;
|
|
26
46
|
|
|
27
|
-
static
|
|
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
|
-
};
|
|
47
|
+
static err(errors, module) {
|
|
48
|
+
return map(errors, ([key, value]) => [key, throwable(value, key, module)]);
|
|
44
49
|
}
|
|
45
50
|
|
|
46
|
-
constructor({level =
|
|
47
|
-
assert(level !== undefined && level <=
|
|
51
|
+
constructor({level = levels.Error, trace = false} = {}) {
|
|
52
|
+
assert(level !== undefined && level <= levels.Info);
|
|
48
53
|
is(trace).boolean();
|
|
49
54
|
this.#level = level;
|
|
50
55
|
this.#trace = trace;
|
|
51
56
|
}
|
|
52
57
|
|
|
53
|
-
static print(...args) {
|
|
54
|
-
print(...args);
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
static get mark() {
|
|
58
|
-
return mark;
|
|
59
|
-
}
|
|
60
|
-
|
|
61
58
|
static get Error() {
|
|
62
|
-
return
|
|
59
|
+
return levels.Error;
|
|
63
60
|
}
|
|
64
61
|
|
|
65
62
|
static get Warn() {
|
|
66
|
-
return
|
|
63
|
+
return levels.Warn;
|
|
67
64
|
}
|
|
68
65
|
|
|
69
66
|
static get Info() {
|
|
70
|
-
return
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
get class() {
|
|
74
|
-
return this.constructor;
|
|
67
|
+
return levels.Info;
|
|
75
68
|
}
|
|
76
69
|
|
|
77
|
-
#print(pre, color, message,
|
|
78
|
-
|
|
79
|
-
|
|
70
|
+
#print(pre, color, message, error = {}) {
|
|
71
|
+
const {fix, module, name, level} = error;
|
|
72
|
+
print(color(pre), `${module !== undefined ? `${color(module)} ` : ""}${message}`, "\n");
|
|
73
|
+
if (fix) {
|
|
80
74
|
print(blue("++"), fix);
|
|
81
75
|
name && print(dim(`\n -> ${reference}/${module ?? "primate"}#${hyphenate(name)}`), "\n");
|
|
82
76
|
}
|
|
83
|
-
|
|
77
|
+
if (level === levels.Error || level === undefined && error.message) {
|
|
78
|
+
this.#trace && console.log(error);
|
|
79
|
+
}
|
|
84
80
|
}
|
|
85
81
|
|
|
86
82
|
get level() {
|
|
87
83
|
return this.#level;
|
|
88
84
|
}
|
|
89
85
|
|
|
90
|
-
info(
|
|
91
|
-
|
|
92
|
-
this.#print(green("--"), green, message, args);
|
|
93
|
-
}
|
|
86
|
+
info(...args) {
|
|
87
|
+
this.level >= levels.Info && this.#print("--", green, ...args);
|
|
94
88
|
}
|
|
95
89
|
|
|
96
|
-
warn(
|
|
97
|
-
|
|
98
|
-
this.#print(yellow("??"), yellow, message, args);
|
|
99
|
-
}
|
|
90
|
+
warn(...args) {
|
|
91
|
+
this.level >= levels.Warn && this.#print("??", yellow, ...args);
|
|
100
92
|
}
|
|
101
93
|
|
|
102
|
-
error(
|
|
103
|
-
|
|
104
|
-
this.#print(red("!!"), red, message, args, error);
|
|
105
|
-
}
|
|
94
|
+
error(...args) {
|
|
95
|
+
this.level >= levels.Warn && this.#print("!!", red, ...args);
|
|
106
96
|
}
|
|
107
97
|
|
|
108
98
|
auto(error) {
|
|
109
|
-
const {
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
}
|
|
113
|
-
if (level === errors.Warn) {
|
|
114
|
-
return this.warn(message, args, error);
|
|
115
|
-
}
|
|
116
|
-
|
|
117
|
-
return this.error(message, args, error);
|
|
99
|
+
const {message} = error;
|
|
100
|
+
const matches = map(levels, ([name, level]) => [level, name.toLowerCase()]);
|
|
101
|
+
return this[matches[error.level] ?? "error"](message, error);
|
|
118
102
|
}
|
|
119
103
|
};
|
|
120
104
|
|
|
121
105
|
export default Logger;
|
|
122
106
|
|
|
123
|
-
export {print, bye};
|
|
107
|
+
export {print, bye, mark};
|
package/src/app.js
CHANGED
|
@@ -1,38 +1,28 @@
|
|
|
1
1
|
import crypto from "runtime-compat/crypto";
|
|
2
|
+
import {tryreturn} from "runtime-compat/flow";
|
|
2
3
|
import {File, Path} from "runtime-compat/fs";
|
|
3
4
|
import {bold, blue} from "runtime-compat/colors";
|
|
4
|
-
import
|
|
5
|
+
import {transform, valmap} from "runtime-compat/object";
|
|
5
6
|
import * as handlers from "./handlers/exports.js";
|
|
6
7
|
import * as hooks from "./hooks/exports.js";
|
|
8
|
+
import * as loaders from "./loaders/exports.js";
|
|
7
9
|
import dispatch from "./dispatch.js";
|
|
8
|
-
|
|
9
|
-
const qualify = (root, paths) =>
|
|
10
|
-
Object.keys(paths).reduce((sofar, key) => {
|
|
11
|
-
const value = paths[key];
|
|
12
|
-
sofar[key] = typeof value === "string"
|
|
13
|
-
? new Path(root, value)
|
|
14
|
-
: qualify(`${root}/${key}`, value);
|
|
15
|
-
return sofar;
|
|
16
|
-
}, {});
|
|
10
|
+
import {print} from "./Logger.js";
|
|
17
11
|
|
|
18
12
|
const base = new Path(import.meta.url).up(1);
|
|
19
|
-
|
|
13
|
+
// do not hard-depend on node
|
|
14
|
+
const packager = import.meta.runtime?.packager ?? "package.json";
|
|
15
|
+
const library = import.meta.runtime?.library ?? "node_modules";
|
|
20
16
|
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
return await File.read(`${app.paths.layouts.join(name)}`);
|
|
26
|
-
} catch (error) {
|
|
27
|
-
// fallback
|
|
28
|
-
return base.join("defaults", defaultLayout).text();
|
|
29
|
-
}
|
|
30
|
-
};
|
|
17
|
+
// use user-provided file or fall back to default
|
|
18
|
+
const index = (app, name) =>
|
|
19
|
+
tryreturn(async _ => File.read(`${app.paths.pages.join(name)}`))
|
|
20
|
+
.orelse(async _ => base.join("defaults", app.config.index).text());
|
|
31
21
|
|
|
32
22
|
const hash = async (string, algorithm = "sha-384") => {
|
|
33
23
|
const encoder = new TextEncoder();
|
|
34
24
|
const bytes = await crypto.subtle.digest(algorithm, encoder.encode(string));
|
|
35
|
-
const algo = algorithm.replace("-",
|
|
25
|
+
const algo = algorithm.replace("-", _ => "");
|
|
36
26
|
return `${algo}-${btoa(String.fromCharCode(...new Uint8Array(bytes)))}`;
|
|
37
27
|
};
|
|
38
28
|
|
|
@@ -45,92 +35,53 @@ const tag = ({name, attributes = {}, code = "", close = true}) =>
|
|
|
45
35
|
|
|
46
36
|
export default async (config, root, log) => {
|
|
47
37
|
const {http} = config;
|
|
38
|
+
const secure = http?.ssl !== undefined;
|
|
39
|
+
const {name, version} = await base.up(1).join(packager).json();
|
|
40
|
+
const paths = valmap(config.paths, value => root.join(value));
|
|
41
|
+
paths.client = paths.build.join("client");
|
|
42
|
+
paths.server = paths.build.join("server");
|
|
43
|
+
|
|
44
|
+
const at = `at http${secure ? "s" : ""}://${http.host}:${http.port}\n`;
|
|
45
|
+
print(blue(bold(name)), blue(version), at);
|
|
48
46
|
|
|
49
47
|
// if ssl activated, resolve key and cert early
|
|
50
|
-
if (
|
|
48
|
+
if (secure) {
|
|
51
49
|
http.ssl.key = root.join(http.ssl.key);
|
|
52
50
|
http.ssl.cert = root.join(http.ssl.cert);
|
|
53
51
|
}
|
|
54
52
|
|
|
55
|
-
const
|
|
56
|
-
|
|
57
|
-
const ending = ".js";
|
|
58
|
-
const routes = paths.routes === undefined ? [] : await Promise.all(
|
|
59
|
-
(await Path.collect(paths.routes, /^.*.js$/u))
|
|
60
|
-
.map(async route => [
|
|
61
|
-
`${route}`.replace(paths.routes, "").slice(1, -ending.length),
|
|
62
|
-
(await import(route)).default,
|
|
63
|
-
]));
|
|
64
|
-
const types = Object.fromEntries(
|
|
65
|
-
paths.types === undefined ? [] : await Promise.all(
|
|
66
|
-
(await Path.collect(paths.types , /^.*.js$/u))
|
|
67
|
-
/* accept only lowercase-first files in type filename */
|
|
68
|
-
.filter(path => /^[a-z]/u.test(path.name))
|
|
69
|
-
.map(async type => [
|
|
70
|
-
`${type}`.replace(paths.types, "").slice(1, -ending.length),
|
|
71
|
-
(await import(type)).default,
|
|
72
|
-
])));
|
|
73
|
-
if (await paths.types.exists) {
|
|
74
|
-
Object.keys(types).length === 0
|
|
75
|
-
&& errors.EmptyTypeDirectory.warn(log, {root: paths.types});
|
|
76
|
-
}
|
|
77
|
-
Object.entries(types).some(([name, type]) =>
|
|
78
|
-
typeof type !== "function" && errors.InvalidType.throw({name}));
|
|
79
|
-
|
|
80
|
-
const modules = config.modules === undefined ? [] : config.modules;
|
|
81
|
-
|
|
82
|
-
modules.every((module, n) => module.name !== undefined ||
|
|
83
|
-
errors.ModulesMustHaveNames.throw({n}));
|
|
84
|
-
|
|
85
|
-
new Set(modules.map(({name}) => name)).size !== modules.length &&
|
|
86
|
-
errors.DoubleModule.throw({
|
|
87
|
-
modules: modules.map(({name}) => name),
|
|
88
|
-
config: root.join("primate.config.js"),
|
|
89
|
-
});
|
|
90
|
-
|
|
91
|
-
const hookless = modules.filter(module => !Object.keys(module).some(key =>
|
|
92
|
-
[...Object.keys(hooks), "load"].includes(key)));
|
|
93
|
-
hookless.length > 0 && errors.ModuleHasNoHooks.warn(log, {hookless});
|
|
94
|
-
|
|
95
|
-
const {name, version} = await base.up(1).join("package.json").json();
|
|
53
|
+
const types = await loaders.types(log, paths.types);
|
|
96
54
|
|
|
97
55
|
const app = {
|
|
98
56
|
config,
|
|
99
|
-
|
|
100
|
-
secure: http?.ssl !== undefined,
|
|
57
|
+
secure,
|
|
101
58
|
name,
|
|
102
59
|
version,
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
replace(code) {
|
|
106
|
-
const joined = Object.keys(app.identifiers).join("|");
|
|
107
|
-
const re = `(?<=import (?:.*) from ['|"])(${joined})(?=['|"])`;
|
|
108
|
-
return code.replaceAll(new RegExp(re, "gus"), (_, p1) => {
|
|
109
|
-
if (app.library[p1] === undefined) {
|
|
110
|
-
app.library[p1] = app.identifiers[p1];
|
|
111
|
-
}
|
|
112
|
-
return app.identifiers[p1];
|
|
113
|
-
});
|
|
114
|
-
},
|
|
115
|
-
resources: [],
|
|
60
|
+
importmaps: {},
|
|
61
|
+
assets: [],
|
|
116
62
|
entrypoints: [],
|
|
117
63
|
paths,
|
|
118
64
|
root,
|
|
119
65
|
log,
|
|
120
|
-
|
|
66
|
+
async copy(source, target, filter = /^.*.js$/u) {
|
|
67
|
+
const jss = await source.collect(filter);
|
|
68
|
+
await Promise.all(jss.map(async js => {
|
|
69
|
+
const file = await js.file.read();
|
|
70
|
+
const to = await target.join(js.path.replace(source, ""));
|
|
71
|
+
await to.directory.file.create();
|
|
72
|
+
await to.file.write(file);
|
|
73
|
+
}));
|
|
74
|
+
},
|
|
75
|
+
headers: _ => {
|
|
121
76
|
const csp = Object.keys(http.csp).reduce((policy_string, key) =>
|
|
122
77
|
`${policy_string}${key} ${http.csp[key]};`, "");
|
|
123
|
-
const scripts = app.
|
|
78
|
+
const scripts = app.assets
|
|
124
79
|
.filter(({type}) => type !== "style")
|
|
125
|
-
.map(
|
|
80
|
+
.map(asset => `'${asset.integrity}'`).join(" ");
|
|
126
81
|
const _csp = scripts === "" ? csp : `${csp}script-src 'self' ${scripts};`;
|
|
127
|
-
// remove inline
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
if (resource.inline) {
|
|
131
|
-
app.resources.splice(i, 1);
|
|
132
|
-
}
|
|
133
|
-
}
|
|
82
|
+
// remove inline assets
|
|
83
|
+
app.assets = app.assets.filter(({inline, type}) => !inline
|
|
84
|
+
|| type === "importmap");
|
|
134
85
|
|
|
135
86
|
return {
|
|
136
87
|
"Content-Security-Policy": _csp,
|
|
@@ -138,8 +89,8 @@ export default async (config, root, log) => {
|
|
|
138
89
|
};
|
|
139
90
|
},
|
|
140
91
|
handlers: {...handlers},
|
|
141
|
-
render: async ({body = "", head = "",
|
|
142
|
-
const html = await index(app,
|
|
92
|
+
render: async ({body = "", head = "", page} = {}) => {
|
|
93
|
+
const html = await index(app, page ?? config.index);
|
|
143
94
|
// inline: <script type integrity>...</script>
|
|
144
95
|
// outline: <script type integrity src></script>
|
|
145
96
|
const script = ({inline, code, type, integrity, src}) => inline
|
|
@@ -150,50 +101,64 @@ export default async (config, root, log) => {
|
|
|
150
101
|
const style = ({inline, code, href, rel = "stylesheet"}) => inline
|
|
151
102
|
? tag({name: "style", code})
|
|
152
103
|
: tag({name: "link", attributes: {rel, href}, close: false});
|
|
153
|
-
const heads = app.
|
|
154
|
-
type === "
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
104
|
+
const heads = app.assets
|
|
105
|
+
.toSorted(({type}) => -1 * (type === "importmap"))
|
|
106
|
+
.map(({src, code, type, inline, integrity}) =>
|
|
107
|
+
type === "style"
|
|
108
|
+
? style({inline, code, href: src})
|
|
109
|
+
: script({inline, code, type, integrity, src})
|
|
110
|
+
).join("\n");
|
|
158
111
|
return html
|
|
159
|
-
.replace("%body%",
|
|
160
|
-
.replace("%head%",
|
|
112
|
+
.replace("%body%", _ => body)
|
|
113
|
+
.replace("%head%", _ => `${head}${heads}`);
|
|
161
114
|
},
|
|
162
115
|
publish: async ({src, code, type = "", inline = false}) => {
|
|
163
|
-
if (
|
|
164
|
-
|
|
116
|
+
if (!inline) {
|
|
117
|
+
const base = paths.client.join(src);
|
|
118
|
+
await base.directory.file.create();
|
|
119
|
+
await base.file.write(code);
|
|
165
120
|
}
|
|
166
121
|
const integrity = await hash(code);
|
|
167
122
|
const _src = new Path(http.static.root).join(src ?? "");
|
|
168
|
-
app.
|
|
123
|
+
app.assets.push({src: `${_src}`, code: inline ? code : "", type, inline, integrity});
|
|
169
124
|
return integrity;
|
|
170
125
|
},
|
|
171
126
|
bootstrap: ({type, code}) => {
|
|
172
127
|
app.entrypoints.push({type, code});
|
|
173
128
|
},
|
|
174
|
-
|
|
175
|
-
const
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
]
|
|
181
|
-
|
|
129
|
+
async import(module) {
|
|
130
|
+
const {build} = config;
|
|
131
|
+
const {root} = http.static;
|
|
132
|
+
const path = [library, module];
|
|
133
|
+
const pkg = await Path.resolve().join(...path, packager).json();
|
|
134
|
+
const exports = pkg.exports === undefined
|
|
135
|
+
? {[module]: `/${module}/${pkg.main}`}
|
|
136
|
+
: transform(pkg.exports, entry => entry
|
|
137
|
+
.filter(([, _export]) => _export.import !== undefined)
|
|
138
|
+
.map(([key, value]) => [
|
|
139
|
+
key.replace(".", module),
|
|
140
|
+
value.import.replace(".", `./${module}`),
|
|
141
|
+
]));
|
|
142
|
+
await Promise.all(Object.values(exports).map(async name => app.publish({
|
|
143
|
+
code: await Path.resolve().join(library, name).text(),
|
|
144
|
+
src: new Path(root, build.modules, name),
|
|
145
|
+
type: "module",
|
|
146
|
+
})));
|
|
147
|
+
this.importmaps = {
|
|
148
|
+
...valmap(exports, value => new Path(root, build.modules, value).path),
|
|
149
|
+
...this.importmaps};
|
|
182
150
|
},
|
|
183
|
-
modules,
|
|
184
151
|
types,
|
|
152
|
+
routes: await loaders.routes(log, paths.routes),
|
|
153
|
+
dispatch: dispatch(types),
|
|
185
154
|
};
|
|
186
|
-
log.class.print(blue(bold(name)), blue(version),
|
|
187
|
-
`at http${app.secure ? "s" : ""}://${http.host}:${http.port}\n`);
|
|
188
|
-
// modules may load other modules
|
|
189
|
-
await Promise.all(app.modules
|
|
190
|
-
.filter(module => module.load !== undefined)
|
|
191
|
-
.map(module => module.load({...app, load(dependent) {
|
|
192
|
-
app.modules.push(dependent);
|
|
193
|
-
}})));
|
|
194
155
|
|
|
195
|
-
|
|
196
|
-
app.parse = hooks.parse(dispatch(types));
|
|
156
|
+
const modules = await loaders.modules(app, root, config);
|
|
197
157
|
|
|
198
|
-
return app
|
|
158
|
+
return {...app,
|
|
159
|
+
modules,
|
|
160
|
+
layoutDepth: Math.max(...app.routes.map(({layouts}) => layouts.length)) + 1,
|
|
161
|
+
route: hooks.route({...app, modules}),
|
|
162
|
+
parse: hooks.parse(dispatch(types)),
|
|
163
|
+
};
|
|
199
164
|
};
|
package/src/bin.js
CHANGED
|
@@ -18,19 +18,26 @@ export default {
|
|
|
18
18
|
},
|
|
19
19
|
static: {
|
|
20
20
|
root: "/",
|
|
21
|
-
pure: false,
|
|
22
21
|
},
|
|
23
22
|
},
|
|
23
|
+
index: "app.html",
|
|
24
24
|
paths: {
|
|
25
|
-
|
|
25
|
+
build: "build",
|
|
26
26
|
static: "static",
|
|
27
|
-
public: "public",
|
|
28
|
-
routes: "routes",
|
|
29
27
|
components: "components",
|
|
28
|
+
routes: "routes",
|
|
30
29
|
types: "types",
|
|
30
|
+
pages: "pages",
|
|
31
|
+
layouts: "layouts",
|
|
32
|
+
},
|
|
33
|
+
build: {
|
|
34
|
+
includes: [],
|
|
35
|
+
static: "static",
|
|
36
|
+
app: "app",
|
|
37
|
+
modules: "modules",
|
|
38
|
+
index: "index.js",
|
|
31
39
|
},
|
|
32
40
|
modules: [],
|
|
33
|
-
dist: "app",
|
|
34
41
|
types: {
|
|
35
42
|
explicit: false,
|
|
36
43
|
},
|
package/src/dispatch.js
CHANGED
|
@@ -1,24 +1,19 @@
|
|
|
1
1
|
import {is, maybe} from "runtime-compat/dyndef";
|
|
2
|
+
import {tryreturn} from "runtime-compat/flow";
|
|
3
|
+
import {map} from "runtime-compat/object";
|
|
2
4
|
import errors from "./errors.js";
|
|
3
5
|
|
|
4
6
|
export default (patches = {}) => value => {
|
|
5
7
|
is(patches.get).undefined();
|
|
6
8
|
return Object.assign(Object.create(null), {
|
|
7
|
-
...
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
} catch (error) {
|
|
13
|
-
errors.MismatchedType.throw({message: error.message});
|
|
14
|
-
}
|
|
15
|
-
}])),
|
|
9
|
+
...map(patches, ([name, patch]) => [name, property => {
|
|
10
|
+
is(property).defined(`\`${name}\` called without property`);
|
|
11
|
+
return tryreturn(_ => patch(value[property], property))
|
|
12
|
+
.orelse(({message}) => errors.MismatchedType.throw(message));
|
|
13
|
+
}]),
|
|
16
14
|
get(property) {
|
|
17
15
|
maybe(property).string();
|
|
18
|
-
|
|
19
|
-
return value[property];
|
|
20
|
-
}
|
|
21
|
-
return value;
|
|
16
|
+
return property === undefined ? value : value[property];
|
|
22
17
|
},
|
|
23
18
|
});
|
|
24
19
|
};
|