primate 0.15.5 → 0.15.7
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/config.js +4 -109
- package/src/env.js +120 -0
- package/src/run.js +2 -2
- package/src/serve.js +0 -3
- package/src/start.js +3 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "primate",
|
|
3
|
-
"version": "0.15.
|
|
3
|
+
"version": "0.15.7",
|
|
4
4
|
"description": "Expressive, minimal and extensible framework for JavaScript",
|
|
5
5
|
"homepage": "https://primatejs.com",
|
|
6
6
|
"bugs": "https://github.com/primatejs/primate/issues",
|
|
@@ -18,7 +18,7 @@
|
|
|
18
18
|
"lint": "npx eslint ."
|
|
19
19
|
},
|
|
20
20
|
"dependencies": {
|
|
21
|
-
"runtime-compat": "^0.15.
|
|
21
|
+
"runtime-compat": "^0.15.1"
|
|
22
22
|
},
|
|
23
23
|
"devDependencies": {
|
|
24
24
|
"maximin": "^0.1.2"
|
package/src/config.js
CHANGED
|
@@ -1,110 +1,5 @@
|
|
|
1
|
-
|
|
2
|
-
import {is} from "runtime-compat/dyndef";
|
|
3
|
-
import {File, Path} from "runtime-compat/fs";
|
|
4
|
-
import cache from "./cache.js";
|
|
5
|
-
import extend from "./extend.js";
|
|
6
|
-
import defaults from "./primate.config.js";
|
|
7
|
-
import {colors, print, default as Logger} from "./Logger.js";
|
|
8
|
-
import * as handlers from "./handlers/exports.js";
|
|
1
|
+
const filter = (key, array) => array?.flatMap(m => m[key] ?? []) ?? [];
|
|
9
2
|
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
sofar[key] = typeof value === "string"
|
|
14
|
-
? new Path(root, value)
|
|
15
|
-
: qualify(`${root}/${key}`, value);
|
|
16
|
-
return sofar;
|
|
17
|
-
}, {});
|
|
18
|
-
|
|
19
|
-
const getConfig = async (root, filename) => {
|
|
20
|
-
try {
|
|
21
|
-
return extend(defaults, (await import(root.join(filename))).default);
|
|
22
|
-
} catch (error) {
|
|
23
|
-
return defaults;
|
|
24
|
-
}
|
|
25
|
-
};
|
|
26
|
-
|
|
27
|
-
const getRoot = async () => {
|
|
28
|
-
try {
|
|
29
|
-
// use module root if possible
|
|
30
|
-
return await Path.root();
|
|
31
|
-
} catch (error) {
|
|
32
|
-
// fall back to current directory
|
|
33
|
-
return Path.resolve();
|
|
34
|
-
}
|
|
35
|
-
};
|
|
36
|
-
|
|
37
|
-
const index = async env => {
|
|
38
|
-
const name = "index.html";
|
|
39
|
-
try {
|
|
40
|
-
// user-provided file
|
|
41
|
-
return await File.read(`${env.paths.static.join(name)}`);
|
|
42
|
-
} catch (error) {
|
|
43
|
-
// fallback
|
|
44
|
-
return new Path(import.meta.url).directory.join(name).file.read();
|
|
45
|
-
}
|
|
46
|
-
};
|
|
47
|
-
|
|
48
|
-
const hash = async (string, algorithm = "sha-384") => {
|
|
49
|
-
const encoder = new TextEncoder();
|
|
50
|
-
const bytes = await crypto.subtle.digest(algorithm, encoder.encode(string));
|
|
51
|
-
const algo = algorithm.replace("-", () => "");
|
|
52
|
-
return `${algo}-${btoa(String.fromCharCode(...new Uint8Array(bytes)))}`;
|
|
53
|
-
};
|
|
54
|
-
|
|
55
|
-
export default async (filename = "primate.config.js") => {
|
|
56
|
-
is(filename).string();
|
|
57
|
-
const root = await getRoot();
|
|
58
|
-
const config = await getConfig(root, filename);
|
|
59
|
-
|
|
60
|
-
const {name, version} = JSON.parse(await new Path(import.meta.url)
|
|
61
|
-
.directory.directory.join("package.json").file.read());
|
|
62
|
-
|
|
63
|
-
const env = {
|
|
64
|
-
...config,
|
|
65
|
-
name, version,
|
|
66
|
-
resources: [],
|
|
67
|
-
entrypoints: [],
|
|
68
|
-
paths: qualify(root, config.paths),
|
|
69
|
-
root,
|
|
70
|
-
log: new Logger(config.logger),
|
|
71
|
-
register: (name, handler) => {
|
|
72
|
-
env.handlers[name] = handler;
|
|
73
|
-
},
|
|
74
|
-
handlers: {...handlers},
|
|
75
|
-
render: async ({body = "", head = ""} = {}) => {
|
|
76
|
-
const html = await index(env);
|
|
77
|
-
const heads = env.resources.map(({src, code, type, inline, integrity}) => {
|
|
78
|
-
const tag = type === "style" ? "link" : "script";
|
|
79
|
-
const pre = type === "style"
|
|
80
|
-
? `<${tag} rel="stylesheet" integrity="${integrity}"`
|
|
81
|
-
: `<${tag} type="${type}" integrity="${integrity}"`;
|
|
82
|
-
const middle = type === "style"
|
|
83
|
-
? ` href="${src}">`
|
|
84
|
-
: ` src="${src}">`;
|
|
85
|
-
const post = type === "style" ? "" : `</${tag}>`;
|
|
86
|
-
return inline ? `${pre}>${code}${post}` : `${pre}${middle}${post}`;
|
|
87
|
-
}).join("\n");
|
|
88
|
-
return html
|
|
89
|
-
.replace("%body%", () => body)
|
|
90
|
-
.replace("%head%", () => `${head}${heads}`);
|
|
91
|
-
},
|
|
92
|
-
publish: async ({src, code, type = "", inline = false}) => {
|
|
93
|
-
const integrity = await hash(code);
|
|
94
|
-
env.resources.push({src, code, type, inline, integrity});
|
|
95
|
-
return integrity;
|
|
96
|
-
},
|
|
97
|
-
bootstrap: ({type, code}) => {
|
|
98
|
-
env.entrypoints.push({type, code});
|
|
99
|
-
},
|
|
100
|
-
};
|
|
101
|
-
print(colors.blue(colors.bold(name)), colors.blue(version), "");
|
|
102
|
-
const {modules} = config;
|
|
103
|
-
// modules may load other modules
|
|
104
|
-
const loads = await Promise.all(modules
|
|
105
|
-
.filter(module => module.load !== undefined)
|
|
106
|
-
.map(module => module.load()));
|
|
107
|
-
|
|
108
|
-
return cache("config", filename, () => ({...env,
|
|
109
|
-
modules: modules.concat(loads.flat())}));
|
|
110
|
-
};
|
|
3
|
+
export default async env =>
|
|
4
|
+
[...filter("config", env.modules), _ => _].reduceRight((acc, handler) =>
|
|
5
|
+
input => handler(input, acc))(env);
|
package/src/env.js
ADDED
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
import crypto from "runtime-compat/crypto";
|
|
2
|
+
import {is} from "runtime-compat/dyndef";
|
|
3
|
+
import {File, Path} from "runtime-compat/fs";
|
|
4
|
+
import cache from "./cache.js";
|
|
5
|
+
import extend from "./extend.js";
|
|
6
|
+
import defaults from "./primate.config.js";
|
|
7
|
+
import {colors, print, default as Logger} from "./Logger.js";
|
|
8
|
+
import * as handlers from "./handlers/exports.js";
|
|
9
|
+
|
|
10
|
+
const qualify = (root, paths) =>
|
|
11
|
+
Object.keys(paths).reduce((sofar, key) => {
|
|
12
|
+
const value = paths[key];
|
|
13
|
+
sofar[key] = typeof value === "string"
|
|
14
|
+
? new Path(root, value)
|
|
15
|
+
: qualify(`${root}/${key}`, value);
|
|
16
|
+
return sofar;
|
|
17
|
+
}, {});
|
|
18
|
+
|
|
19
|
+
const getConfig = async (root, filename) => {
|
|
20
|
+
try {
|
|
21
|
+
return extend(defaults, (await import(root.join(filename))).default);
|
|
22
|
+
} catch (error) {
|
|
23
|
+
return defaults;
|
|
24
|
+
}
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
const getRoot = async () => {
|
|
28
|
+
try {
|
|
29
|
+
// use module root if possible
|
|
30
|
+
return await Path.root();
|
|
31
|
+
} catch (error) {
|
|
32
|
+
// fall back to current directory
|
|
33
|
+
return Path.resolve();
|
|
34
|
+
}
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
const index = async env => {
|
|
38
|
+
const name = "index.html";
|
|
39
|
+
try {
|
|
40
|
+
// user-provided file
|
|
41
|
+
return await File.read(`${env.paths.static.join(name)}`);
|
|
42
|
+
} catch (error) {
|
|
43
|
+
// fallback
|
|
44
|
+
return new Path(import.meta.url).directory.join(name).file.read();
|
|
45
|
+
}
|
|
46
|
+
};
|
|
47
|
+
|
|
48
|
+
const hash = async (string, algorithm = "sha-384") => {
|
|
49
|
+
const encoder = new TextEncoder();
|
|
50
|
+
const bytes = await crypto.subtle.digest(algorithm, encoder.encode(string));
|
|
51
|
+
const algo = algorithm.replace("-", () => "");
|
|
52
|
+
return `${algo}-${btoa(String.fromCharCode(...new Uint8Array(bytes)))}`;
|
|
53
|
+
};
|
|
54
|
+
|
|
55
|
+
export default async (filename = "primate.config.js") => {
|
|
56
|
+
is(filename).string();
|
|
57
|
+
const root = await getRoot();
|
|
58
|
+
const config = await getConfig(root, filename);
|
|
59
|
+
|
|
60
|
+
const {name, version} = JSON.parse(await new Path(import.meta.url)
|
|
61
|
+
.directory.directory.join("package.json").file.read());
|
|
62
|
+
|
|
63
|
+
// if ssl activated, resolve key and cert early
|
|
64
|
+
if (config.http.ssl) {
|
|
65
|
+
config.http.ssl.key = root.join(config.http.ssl.key);
|
|
66
|
+
config.http.ssl.cert = root.join(config.http.ssl.cert);
|
|
67
|
+
config.secure = true;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
const env = {
|
|
71
|
+
...config,
|
|
72
|
+
name, version,
|
|
73
|
+
resources: [],
|
|
74
|
+
entrypoints: [],
|
|
75
|
+
paths: qualify(root, config.paths),
|
|
76
|
+
root,
|
|
77
|
+
log: new Logger(config.logger),
|
|
78
|
+
register: (name, handler) => {
|
|
79
|
+
env.handlers[name] = handler;
|
|
80
|
+
},
|
|
81
|
+
handlers: {...handlers},
|
|
82
|
+
render: async ({body = "", head = ""} = {}) => {
|
|
83
|
+
const html = await index(env);
|
|
84
|
+
const heads = env.resources.map(({src, code, type, inline, integrity}) => {
|
|
85
|
+
const tag = type === "style" ? "link" : "script";
|
|
86
|
+
const pre = type === "style"
|
|
87
|
+
? `<${tag} rel="stylesheet" integrity="${integrity}"`
|
|
88
|
+
: `<${tag} type="${type}" integrity="${integrity}"`;
|
|
89
|
+
const middle = type === "style"
|
|
90
|
+
? ` href="${src}">`
|
|
91
|
+
: ` src="${src}">`;
|
|
92
|
+
const post = type === "style" ? "" : `</${tag}>`;
|
|
93
|
+
return inline ? `${pre}>${code}${post}` : `${pre}${middle}${post}`;
|
|
94
|
+
}).join("\n");
|
|
95
|
+
return html
|
|
96
|
+
.replace("%body%", () => body)
|
|
97
|
+
.replace("%head%", () => `${head}${heads}`);
|
|
98
|
+
},
|
|
99
|
+
publish: async ({src, code, type = "", inline = false}) => {
|
|
100
|
+
const integrity = await hash(code);
|
|
101
|
+
env.resources.push({src, code, type, inline, integrity});
|
|
102
|
+
return integrity;
|
|
103
|
+
},
|
|
104
|
+
bootstrap: ({type, code}) => {
|
|
105
|
+
env.entrypoints.push({type, code});
|
|
106
|
+
},
|
|
107
|
+
};
|
|
108
|
+
print(colors.blue(colors.bold(name)), colors.blue(version), "");
|
|
109
|
+
const type = env.secure ? "https" : "http";
|
|
110
|
+
const address = `${type}://${config.http.host}:${config.http.port}`;
|
|
111
|
+
print(colors.gray(`at ${address}`), "\n");
|
|
112
|
+
const {modules} = config;
|
|
113
|
+
// modules may load other modules
|
|
114
|
+
const loads = await Promise.all(modules
|
|
115
|
+
.filter(module => module.load !== undefined)
|
|
116
|
+
.map(module => module.load()));
|
|
117
|
+
|
|
118
|
+
return cache("config", filename, () => ({...env,
|
|
119
|
+
modules: modules.concat(loads.flat())}));
|
|
120
|
+
};
|
package/src/run.js
CHANGED
package/src/serve.js
CHANGED
|
@@ -5,7 +5,6 @@ import mimes from "./mimes.js";
|
|
|
5
5
|
import {http404} from "./handlers/http.js";
|
|
6
6
|
import {isResponse} from "./duck.js";
|
|
7
7
|
import respond from "./respond.js";
|
|
8
|
-
import {colors, print} from "./Logger.js";
|
|
9
8
|
|
|
10
9
|
const regex = /\.([a-z1-9]*)$/u;
|
|
11
10
|
const mime = filename => mimes[filename.match(regex)[1]] ?? mimes.binary;
|
|
@@ -136,6 +135,4 @@ export default env => {
|
|
|
136
135
|
|
|
137
136
|
return handlers({original: request, pathname: pathname + search, body});
|
|
138
137
|
}, http);
|
|
139
|
-
|
|
140
|
-
print(colors.gray(`at http://${http.host}:${http.port}`), "\n");
|
|
141
138
|
};
|
package/src/start.js
CHANGED
|
@@ -4,8 +4,11 @@ import publish from "./publish.js";
|
|
|
4
4
|
import bundle from "./bundle.js";
|
|
5
5
|
import route from "./route.js";
|
|
6
6
|
import serve from "./serve.js";
|
|
7
|
+
import config from "./config.js";
|
|
7
8
|
|
|
8
9
|
export default async (env, operations = {}) => {
|
|
10
|
+
// read/write configuration
|
|
11
|
+
await config(env);
|
|
9
12
|
// register handlers
|
|
10
13
|
await register(env);
|
|
11
14
|
// compile server-side code
|