primate 0.13.1 → 0.14.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/bin.js +7 -1
- package/src/commands/create.js +19 -0
- package/src/commands/exports.js +3 -0
- package/src/commands/help.js +3 -0
- package/src/commands/start.js +19 -0
- package/src/config.js +28 -6
- package/src/exports.js +4 -0
- package/src/publish.js +5 -0
- package/src/route.js +1 -1
- package/src/run.js +3 -11
- package/src/serve.js +30 -3
- package/exports.js +0 -3
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "primate",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.14.0",
|
|
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",
|
|
@@ -22,5 +22,5 @@
|
|
|
22
22
|
"node": ">=17.9.0"
|
|
23
23
|
},
|
|
24
24
|
"type": "module",
|
|
25
|
-
"exports": "./exports.js"
|
|
25
|
+
"exports": "./src/exports.js"
|
|
26
26
|
}
|
package/src/bin.js
CHANGED
|
@@ -1,2 +1,8 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
|
|
2
|
+
import args from "runtime-compat/args";
|
|
3
|
+
import * as commands from "./commands/exports.js";
|
|
4
|
+
import run from "./run.js";
|
|
5
|
+
|
|
6
|
+
const command = name => commands[name] ?? commands.help;
|
|
7
|
+
|
|
8
|
+
await run(args[0] === undefined ? commands.start : command(args[0]));
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import {Path} from "runtime-compat/fs";
|
|
2
|
+
const name = "primate.config.js";
|
|
3
|
+
|
|
4
|
+
const template = "export default {};";
|
|
5
|
+
|
|
6
|
+
const createConfig = async env => {
|
|
7
|
+
const root = (await Path.root()).join(name);
|
|
8
|
+
if (await root.exists) {
|
|
9
|
+
env.log.warn(`${root} already exists`);
|
|
10
|
+
} else {
|
|
11
|
+
await root.file.write(template);
|
|
12
|
+
env.log.info(`created config at ${root}`);
|
|
13
|
+
}
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
export default async env => {
|
|
17
|
+
await createConfig(env);
|
|
18
|
+
};
|
|
19
|
+
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import register from "../register.js";
|
|
2
|
+
import compile from "../compile.js";
|
|
3
|
+
import publish from "../publish.js";
|
|
4
|
+
import bundle from "../bundle.js";
|
|
5
|
+
import route from "../route.js";
|
|
6
|
+
import serve from "../serve.js";
|
|
7
|
+
|
|
8
|
+
export default async env => {
|
|
9
|
+
// register handlers
|
|
10
|
+
await register(env);
|
|
11
|
+
// compile server-side code
|
|
12
|
+
await compile(env);
|
|
13
|
+
// publish client-side code
|
|
14
|
+
await publish(env);
|
|
15
|
+
// bundle client-side code
|
|
16
|
+
await bundle(env);
|
|
17
|
+
// serve
|
|
18
|
+
serve({router: await route(env.paths.routes, env.handlers), ...env});
|
|
19
|
+
};
|
package/src/config.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
|
-
import
|
|
1
|
+
import crypto from "runtime-compat/crypto";
|
|
2
2
|
import {is} from "runtime-compat/dyndef";
|
|
3
|
+
import {File, Path} from "runtime-compat/fs";
|
|
3
4
|
import cache from "./cache.js";
|
|
4
5
|
import extend from "./extend.js";
|
|
5
6
|
import defaults from "./primate.config.js";
|
|
@@ -45,11 +46,19 @@ const index = async env => {
|
|
|
45
46
|
}
|
|
46
47
|
};
|
|
47
48
|
|
|
49
|
+
const hash = async (string, algorithm = "sha-384") => {
|
|
50
|
+
const encoder = new TextEncoder();
|
|
51
|
+
const bytes = await crypto.subtle.digest(algorithm, encoder.encode(string));
|
|
52
|
+
const algo = algorithm.replace("-", () => "");
|
|
53
|
+
return `${algo}-${btoa(String.fromCharCode(...new Uint8Array(bytes)))}`;
|
|
54
|
+
};
|
|
55
|
+
|
|
48
56
|
export default async (filename = "primate.config.js") => {
|
|
49
57
|
is(filename).string();
|
|
50
58
|
const root = await getRoot();
|
|
51
59
|
const config = await getConfig(root, filename);
|
|
52
60
|
|
|
61
|
+
const resources = [];
|
|
53
62
|
const env = {
|
|
54
63
|
...config,
|
|
55
64
|
paths: qualify(root, config.paths),
|
|
@@ -61,16 +70,29 @@ export default async (filename = "primate.config.js") => {
|
|
|
61
70
|
handlers: {...handlers},
|
|
62
71
|
render: async ({body = "", head = ""} = {}) => {
|
|
63
72
|
const html = await index(env);
|
|
64
|
-
|
|
73
|
+
const heads = resources.map(({src, code, type, inline, integrity}) => {
|
|
74
|
+
const tag = "script";
|
|
75
|
+
const pre = `<${tag} type="${type}" integrity="${integrity}"`;
|
|
76
|
+
const post = `</${tag}>`;
|
|
77
|
+
return inline ? `${pre}>${code}${post}` : `${pre} src="${src}">${post}`;
|
|
78
|
+
}).join("\n");
|
|
79
|
+
return html
|
|
80
|
+
.replace("%body%", () => body)
|
|
81
|
+
.replace("%head%", () => `${head}${heads}`);
|
|
82
|
+
},
|
|
83
|
+
publish: async ({src, code, type = "", inline = false}) => {
|
|
84
|
+
const integrity = await hash(code);
|
|
85
|
+
resources.push({src, code, type, inline, integrity});
|
|
86
|
+
return integrity;
|
|
65
87
|
},
|
|
66
88
|
};
|
|
67
89
|
env.log.info(`${package_json.name} \x1b[34m${package_json.version}\x1b[0m`);
|
|
68
|
-
const modules =
|
|
90
|
+
const {modules} = config;
|
|
69
91
|
// modules may load other modules
|
|
70
92
|
const loads = await Promise.all(modules
|
|
71
93
|
.filter(module => module.load !== undefined)
|
|
72
|
-
.map(module => module.load()
|
|
94
|
+
.map(module => module.load()));
|
|
73
95
|
|
|
74
|
-
return cache("config", filename, () => ({...env,
|
|
75
|
-
modules: modules.concat(loads)}));
|
|
96
|
+
return cache("config", filename, () => ({...env, resources,
|
|
97
|
+
modules: modules.concat(loads.flat())}));
|
|
76
98
|
};
|
package/src/exports.js
ADDED
package/src/publish.js
ADDED
package/src/route.js
CHANGED
|
@@ -35,7 +35,7 @@ export default async (definitions, handlers) => {
|
|
|
35
35
|
[verb, (path, callback) => add(verb, path, callback)])),
|
|
36
36
|
map: (path, callback) => add("map", path, callback),
|
|
37
37
|
alias: (key, value) => aliases.push({key, value}),
|
|
38
|
-
route: async request => {
|
|
38
|
+
route: async ({request}) => {
|
|
39
39
|
const {method} = request.original;
|
|
40
40
|
const url = new URL(`https://primatejs.com${request.pathname}`);
|
|
41
41
|
const {pathname, searchParams} = url;
|
package/src/run.js
CHANGED
|
@@ -1,14 +1,6 @@
|
|
|
1
1
|
import config from "./config.js";
|
|
2
|
-
import register from "./register.js";
|
|
3
|
-
import compile from "./compile.js";
|
|
4
|
-
import bundle from "./bundle.js";
|
|
5
|
-
import route from "./route.js";
|
|
6
|
-
import serve from "./serve.js";
|
|
7
2
|
|
|
8
|
-
export default async
|
|
9
|
-
|
|
10
|
-
await
|
|
11
|
-
await compile(env);
|
|
12
|
-
await bundle(env);
|
|
13
|
-
serve({router: await route(env.paths.routes, env.handlers), ...env});
|
|
3
|
+
export default async command => {
|
|
4
|
+
// env should initialised before any commands run
|
|
5
|
+
await command(await config());
|
|
14
6
|
};
|
package/src/serve.js
CHANGED
|
@@ -22,8 +22,19 @@ export default env => {
|
|
|
22
22
|
const _respond = async request => {
|
|
23
23
|
const csp = Object.keys(env.http.csp).reduce((policy_string, key) =>
|
|
24
24
|
`${policy_string}${key} ${env.http.csp[key]};`, "");
|
|
25
|
+
const scripts = env.resources
|
|
26
|
+
.map(resource => `'${resource.integrity}'`).join(" ");
|
|
27
|
+
const _csp = scripts === "" ? csp : `${csp}script-src 'self' ${scripts};`;
|
|
28
|
+
// remove inline resources
|
|
29
|
+
for (let i = env.resources.length - 1; i >= 0; i--) {
|
|
30
|
+
const resource = env.resources[i];
|
|
31
|
+
if (resource.inline) {
|
|
32
|
+
env.resources.splice(i, 1);
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
|
|
25
36
|
const headers = {
|
|
26
|
-
"Content-Security-Policy":
|
|
37
|
+
"Content-Security-Policy": _csp,
|
|
27
38
|
"Referrer-Policy": "same-origin",
|
|
28
39
|
};
|
|
29
40
|
|
|
@@ -33,7 +44,7 @@ export default env => {
|
|
|
33
44
|
// handle is the last module to be executed
|
|
34
45
|
const handlers = [...modules, router.route].reduceRight((acc, handler) =>
|
|
35
46
|
input => handler(input, acc));
|
|
36
|
-
return await respond(await handlers(request))(env, headers);
|
|
47
|
+
return await respond(await handlers({request, env}))(env, headers);
|
|
37
48
|
} catch (error) {
|
|
38
49
|
env.log.error(error);
|
|
39
50
|
return http404()(env, headers);
|
|
@@ -53,9 +64,25 @@ export default env => {
|
|
|
53
64
|
},
|
|
54
65
|
});
|
|
55
66
|
|
|
67
|
+
const publishedResource = request => {
|
|
68
|
+
const published = env.resources.find(resource =>
|
|
69
|
+
`/${resource.src}` === request.pathname);
|
|
70
|
+
if (published !== undefined) {
|
|
71
|
+
return new Response(published.code, {
|
|
72
|
+
status: statuses.OK,
|
|
73
|
+
headers: {
|
|
74
|
+
"Content-Type": mime(published.src),
|
|
75
|
+
Etag: published.integrity,
|
|
76
|
+
},
|
|
77
|
+
});
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
return route(request);
|
|
81
|
+
};
|
|
82
|
+
|
|
56
83
|
const _serve = async request => {
|
|
57
84
|
const path = new Path(env.paths.public, request.pathname);
|
|
58
|
-
return await path.isFile ? resource(path.file) :
|
|
85
|
+
return await path.isFile ? resource(path.file) : publishedResource(request);
|
|
59
86
|
};
|
|
60
87
|
|
|
61
88
|
const handle = async request => {
|
package/exports.js
DELETED