@primate/core 0.2.3 → 0.3.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/lib/private/App.d.ts +7 -0
- package/lib/private/App.js +18 -12
- package/lib/private/Binder.d.ts +1 -1
- package/lib/private/BindingContext.d.ts +1 -1
- package/lib/private/BuildApp.d.ts +7 -1
- package/lib/private/BuildApp.js +35 -31
- package/lib/private/Loader.d.ts +1 -1
- package/lib/private/Loader.js +4 -3
- package/lib/private/ServeApp.d.ts +3 -2
- package/lib/private/ServeApp.js +27 -15
- package/lib/private/ServeInit.d.ts +1 -1
- package/lib/private/backend/TAG.d.ts +3 -0
- package/lib/private/backend/TAG.js +2 -0
- package/lib/private/builtin/DevModule.js +1 -3
- package/lib/private/client/Data.d.ts +1 -1
- package/lib/private/client/app.d.ts +2 -0
- package/lib/private/client/{App.js → app.js} +4 -2
- package/lib/private/client/spa/index.js +4 -0
- package/lib/private/database/Query.d.ts +3 -3
- package/lib/private/database/QueryBuilder.d.ts +2 -2
- package/lib/private/database/{DataRecord.d.ts → Schema.d.ts} +3 -3
- package/lib/private/database/Schema.js +3 -0
- package/lib/private/database/Store.d.ts +11 -11
- package/lib/private/frontend/Module.d.ts +2 -2
- package/lib/private/frontend/Module.js +47 -39
- package/lib/private/frontend/Render.d.ts +1 -1
- package/lib/private/frontend/ServerData.d.ts +2 -2
- package/lib/private/frontend/ServerView.d.ts +5 -0
- package/lib/private/frontend/ServerView.js +2 -0
- package/lib/private/frontend/View.d.ts +8 -0
- package/lib/private/frontend/View.js +2 -0
- package/lib/private/frontend/bundle-server.js +3 -3
- package/lib/private/hook/build.js +83 -69
- package/lib/private/i18n/Module.js +16 -3
- package/lib/private/location.d.ts +3 -0
- package/lib/private/location.js +6 -0
- package/lib/private/request/RequestBody.d.ts +8 -7
- package/lib/private/request/RequestBody.js +30 -14
- package/lib/private/response/ResponseFunction.d.ts +2 -2
- package/lib/private/response/redirect.js +5 -1
- package/lib/private/response/view.d.ts +3 -3
- package/lib/private/response/view.js +14 -12
- package/lib/private/route/wrap.d.ts +1 -5
- package/lib/private/route/wrap.js +4 -9
- package/lib/private/target/Manager.js +1 -1
- package/lib/private/target/web.js +0 -10
- package/lib/private/wasm/encode-request.js +25 -28
- package/lib/public/backend/TAG.d.ts +2 -0
- package/lib/public/backend/TAG.js +2 -0
- package/lib/public/client/app.d.ts +2 -0
- package/lib/public/client/app.js +2 -0
- package/lib/public/fail.d.ts +2 -0
- package/lib/public/fail.js +2 -0
- package/package.json +4 -4
- package/lib/private/client/App.d.ts +0 -4
- package/lib/private/database/DataRecord.js +0 -3
- package/lib/private/frontend/Component.d.ts +0 -8
- package/lib/private/frontend/Component.js +0 -2
- package/lib/private/frontend/ServerComponent.d.ts +0 -5
- package/lib/private/frontend/ServerComponent.js +0 -2
- package/lib/public/client/App.d.ts +0 -2
- package/lib/public/client/App.js +0 -2
- package/lib/public/route/wrap.d.ts +0 -2
- package/lib/public/route/wrap.js +0 -2
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import
|
|
1
|
+
import fail from "#fail";
|
|
2
2
|
import bundle from "#frontend/bundle-server";
|
|
3
3
|
import inline from "#inline";
|
|
4
4
|
import location from "#location";
|
|
@@ -13,12 +13,13 @@ import pema from "pema";
|
|
|
13
13
|
import array from "pema/array";
|
|
14
14
|
import boolean from "pema/boolean";
|
|
15
15
|
import string from "pema/string";
|
|
16
|
+
const contexts = ["views", "components"];
|
|
16
17
|
async function normalize(path) {
|
|
17
18
|
return `p_${await hash(path)}`;
|
|
18
19
|
}
|
|
19
20
|
export default class FrontendModule extends Module {
|
|
20
21
|
#options;
|
|
21
|
-
render = async (
|
|
22
|
+
render = async (view, props) => ({ body: await view(props) });
|
|
22
23
|
root;
|
|
23
24
|
compile = {};
|
|
24
25
|
css;
|
|
@@ -49,39 +50,41 @@ export default class FrontendModule extends Module {
|
|
|
49
50
|
return this.#options.spa;
|
|
50
51
|
}
|
|
51
52
|
#load(name, props, app) {
|
|
52
|
-
const
|
|
53
|
-
return {
|
|
53
|
+
const view = app.loadView(name);
|
|
54
|
+
return { view, name, props };
|
|
54
55
|
}
|
|
55
56
|
;
|
|
56
57
|
async #render(server, client, app) {
|
|
57
58
|
const { body, head = "", headers = {} } = this.ssr
|
|
58
|
-
? await this.render(server.
|
|
59
|
+
? await this.render(server.view, server.props)
|
|
59
60
|
: { body: "", head: "" };
|
|
60
61
|
if (!this.client) {
|
|
61
62
|
return { body, head, headers };
|
|
62
63
|
}
|
|
63
|
-
const
|
|
64
|
-
|
|
64
|
+
const app_asset = app.assets.find(asset => asset.src?.includes("app") && asset.src.endsWith(".js"));
|
|
65
|
+
if (!app_asset)
|
|
66
|
+
throw fail("Could not find app.js in assets");
|
|
67
|
+
const app_script = `<script type="module" src="${app_asset.src}" integrity="${app_asset.integrity}"></script>`;
|
|
65
68
|
const json_props = JSON.stringify({ frontend: this.name, ...client });
|
|
66
69
|
const hydrated = await inline(json_props, APPLICATION_JSON, "hydration");
|
|
67
|
-
const script_src = [
|
|
70
|
+
const script_src = [hydrated.integrity, `'${app_asset.integrity}'`];
|
|
68
71
|
return {
|
|
69
72
|
body,
|
|
70
|
-
head: head.concat(
|
|
73
|
+
head: head.concat(app_script, hydrated.head),
|
|
71
74
|
headers: app.headers({ "script-src": script_src }),
|
|
72
75
|
};
|
|
73
76
|
}
|
|
74
77
|
normalize(name) {
|
|
75
78
|
return normalize(name);
|
|
76
79
|
}
|
|
77
|
-
respond = (
|
|
80
|
+
respond = (view, props = {}, options = {}) => async (app, { as_layout, layouts = [] } = {}, request) => {
|
|
78
81
|
if (as_layout) {
|
|
79
|
-
return this.#load(
|
|
82
|
+
return this.#load(view, props, app);
|
|
80
83
|
}
|
|
81
|
-
const
|
|
84
|
+
const views = (await Promise.all(layouts
|
|
82
85
|
.map(layout => layout(app, { as_layout: true }, request))))
|
|
83
|
-
/* set the actual page as the last
|
|
84
|
-
.concat(this.#load(
|
|
86
|
+
/* set the actual page as the last view */
|
|
87
|
+
.concat(this.#load(view, props, app));
|
|
85
88
|
const $request = {
|
|
86
89
|
context: request.context,
|
|
87
90
|
cookies: request.cookies.toJSON(),
|
|
@@ -92,35 +95,41 @@ export default class FrontendModule extends Module {
|
|
|
92
95
|
};
|
|
93
96
|
const $props = this.layouts
|
|
94
97
|
? {
|
|
95
|
-
|
|
96
|
-
props:
|
|
98
|
+
views: await map(views, ({ name }) => normalize(name)),
|
|
99
|
+
props: views.map(c => c.props),
|
|
97
100
|
request: $request,
|
|
98
101
|
}
|
|
99
102
|
: { props, request: $request };
|
|
100
103
|
const client = {
|
|
101
|
-
|
|
104
|
+
view: this.layouts ? "root" : await normalize(view),
|
|
102
105
|
spa: this.spa,
|
|
103
106
|
ssr: this.ssr,
|
|
104
107
|
...$props,
|
|
105
108
|
};
|
|
106
109
|
if (this.spa && request.headers.get("Accept") === APPLICATION_JSON) {
|
|
107
|
-
|
|
108
|
-
|
|
110
|
+
const json_body = JSON.stringify(client);
|
|
111
|
+
return new Response(json_body, {
|
|
112
|
+
headers: {
|
|
113
|
+
...app.headers(),
|
|
114
|
+
"Content-Type": APPLICATION_JSON,
|
|
115
|
+
"Content-Length": String(app.body_length(json_body)),
|
|
116
|
+
"Cache-Control": "no-store",
|
|
117
|
+
},
|
|
109
118
|
status: options.status ?? Status.OK,
|
|
110
119
|
});
|
|
111
120
|
}
|
|
112
121
|
try {
|
|
113
122
|
const server = this.layouts
|
|
114
123
|
? {
|
|
115
|
-
|
|
124
|
+
view: app.loadView(`${this.rootname}.js`),
|
|
116
125
|
props: {
|
|
117
|
-
|
|
118
|
-
props:
|
|
126
|
+
views: views.map(c => c.view),
|
|
127
|
+
props: views.map(c => c.props),
|
|
119
128
|
request: $request,
|
|
120
129
|
},
|
|
121
130
|
}
|
|
122
131
|
: {
|
|
123
|
-
|
|
132
|
+
view: app.loadView(view),
|
|
124
133
|
props,
|
|
125
134
|
request: $request,
|
|
126
135
|
};
|
|
@@ -128,8 +137,8 @@ export default class FrontendModule extends Module {
|
|
|
128
137
|
return app.view({ body, head, headers, ...options });
|
|
129
138
|
}
|
|
130
139
|
catch (error) {
|
|
131
|
-
const path = `${location.
|
|
132
|
-
throw
|
|
140
|
+
const path = `${location.views}/${view}`;
|
|
141
|
+
throw fail("error in view{0}\n{1}", path, error);
|
|
133
142
|
}
|
|
134
143
|
};
|
|
135
144
|
serve(app, next) {
|
|
@@ -167,19 +176,19 @@ export default class FrontendModule extends Module {
|
|
|
167
176
|
: null;
|
|
168
177
|
});
|
|
169
178
|
}
|
|
170
|
-
const
|
|
171
|
-
|
|
179
|
+
const views_filter = new RegExp(`^${name}:views`);
|
|
180
|
+
const views_base = app.root.join(location.views);
|
|
181
|
+
build.onResolve({ filter: views_filter }, ({ path }) => {
|
|
172
182
|
return { namespace: `${name}`, path };
|
|
173
183
|
});
|
|
174
|
-
build.onLoad({ filter:
|
|
175
|
-
const
|
|
176
|
-
.join(location.components)
|
|
184
|
+
build.onLoad({ filter: views_filter }, async () => {
|
|
185
|
+
const views = await views_base
|
|
177
186
|
.collect(c => fileExtensions.includes(c.fullExtension));
|
|
178
187
|
let contents = "";
|
|
179
|
-
for (const
|
|
180
|
-
const { path } =
|
|
188
|
+
for (const view of views) {
|
|
189
|
+
const { path } = view.debase(views_base, "/");
|
|
181
190
|
contents += `export { default as ${await normalize(path)} }
|
|
182
|
-
from "#
|
|
191
|
+
from "#view/${path}";\n`;
|
|
183
192
|
}
|
|
184
193
|
return { contents, resolveDir: app.root.path };
|
|
185
194
|
});
|
|
@@ -206,20 +215,19 @@ export default class FrontendModule extends Module {
|
|
|
206
215
|
init(app, next) {
|
|
207
216
|
this.fileExtensions.forEach(e => {
|
|
208
217
|
app.bind(e, async (file, { context }) => {
|
|
209
|
-
assert(context
|
|
218
|
+
assert(contexts.includes(context), `${this.name}: only components supported`);
|
|
210
219
|
if (this.compile.server) {
|
|
211
|
-
const
|
|
212
|
-
const source = app.path.components.join(original);
|
|
213
|
-
const code = await this.compile.server(await source.text());
|
|
220
|
+
const code = await this.compile.server(await file.text());
|
|
214
221
|
const bundled = await bundle({
|
|
215
222
|
code,
|
|
216
|
-
source,
|
|
223
|
+
source: file,
|
|
217
224
|
root: app.root,
|
|
218
225
|
extensions: this.fileExtensions,
|
|
219
226
|
compile: async (s) => this.compile.server(s),
|
|
220
227
|
});
|
|
221
|
-
|
|
228
|
+
return bundled;
|
|
222
229
|
}
|
|
230
|
+
return await file.text();
|
|
223
231
|
});
|
|
224
232
|
});
|
|
225
233
|
return next(app);
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type Dict from "@rcompat/type/Dict";
|
|
2
2
|
import type MaybePromise from "@rcompat/type/MaybePromise";
|
|
3
|
-
type Render<
|
|
3
|
+
type Render<V = unknown> = (view: V, props: Dict) => MaybePromise<{
|
|
4
4
|
body: string;
|
|
5
5
|
head?: string;
|
|
6
6
|
headers?: Record<string, string>;
|
|
@@ -13,12 +13,12 @@ export default async function bundleServer(init) {
|
|
|
13
13
|
const contents = await compile(src, file);
|
|
14
14
|
return { contents, loader: "js", resolveDir: file.directory.path };
|
|
15
15
|
});
|
|
16
|
-
// externalise anything not relative nor "#
|
|
16
|
+
// externalise anything not relative nor "#view/"
|
|
17
17
|
build.onResolve({ filter: /.*/ }, args => {
|
|
18
18
|
const p = args.path;
|
|
19
19
|
const relative = p.startsWith("./") || p.startsWith("../");
|
|
20
|
-
const
|
|
21
|
-
if (!relative && !
|
|
20
|
+
const views = p.startsWith("#view/");
|
|
21
|
+
if (!relative && !views)
|
|
22
22
|
return { path: p, external: true };
|
|
23
23
|
return null;
|
|
24
24
|
});
|
|
@@ -1,8 +1,9 @@
|
|
|
1
|
-
import
|
|
1
|
+
import fail from "#fail";
|
|
2
2
|
import location from "#location";
|
|
3
3
|
import log from "#log";
|
|
4
4
|
import reducer from "#reducer";
|
|
5
5
|
import $router from "#request/router";
|
|
6
|
+
import wrap from "#route/wrap";
|
|
6
7
|
import s_layout_depth from "#symbol/layout-depth";
|
|
7
8
|
import FileRef from "@rcompat/fs/FileRef";
|
|
8
9
|
import json from "@rcompat/package/json";
|
|
@@ -12,10 +13,10 @@ const pre = async (app) => {
|
|
|
12
13
|
// remove build directory in case exists
|
|
13
14
|
await app.path.build.remove();
|
|
14
15
|
await app.path.build.create();
|
|
15
|
-
await Promise.all(["server", "client", "
|
|
16
|
+
await Promise.all(["server", "client", "views"]
|
|
16
17
|
.map(directory => app.runpath(directory).create()));
|
|
17
18
|
// this has to occur before post, so that layout depth is available for
|
|
18
|
-
// compiling root
|
|
19
|
+
// compiling root views
|
|
19
20
|
// bindings should have been registered during `init`
|
|
20
21
|
const router = await $router(app.path.routes, app.extensions);
|
|
21
22
|
app.set(s_layout_depth, router.depth("layout"));
|
|
@@ -27,33 +28,36 @@ const pre = async (app) => {
|
|
|
27
28
|
app.i18n_active = has_i18n_config;
|
|
28
29
|
return app;
|
|
29
30
|
};
|
|
30
|
-
async function
|
|
31
|
-
|
|
32
|
-
const default_database = `${export_from} "#stage/config/database/index.js";`;
|
|
33
|
-
// app/config/database does not exist
|
|
31
|
+
async function index_database(base) {
|
|
32
|
+
// app/config/database does not exist -> use prelayered default
|
|
34
33
|
if (!await base.exists())
|
|
35
|
-
return
|
|
34
|
+
return "";
|
|
36
35
|
const databases = await base.list();
|
|
37
36
|
const n = databases.length;
|
|
38
|
-
// none in app/config/database ->
|
|
37
|
+
// none in app/config/database -> use prelayered default
|
|
39
38
|
if (n === 0)
|
|
40
|
-
return default_database;
|
|
41
|
-
// index database file found, will be overwritten in next step
|
|
42
|
-
if (databases.some(d => d.base === "index"))
|
|
43
39
|
return "";
|
|
44
40
|
const names = databases.map(d => d.name);
|
|
45
|
-
//
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
// app/config/
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
//
|
|
54
|
-
if (
|
|
55
|
-
return
|
|
56
|
-
|
|
41
|
+
// index database file found, will be overwritten in next step -> do nothing
|
|
42
|
+
if (names.includes("index.ts") || names.includes("index.js")) {
|
|
43
|
+
return ""; // empty string means: don't generate, user provided own
|
|
44
|
+
}
|
|
45
|
+
// app/config/default.ts -> reexport
|
|
46
|
+
if (names.includes("default.ts")) {
|
|
47
|
+
return "export { default } from \"./default.js\";";
|
|
48
|
+
}
|
|
49
|
+
// app/config/default.js -> reexport
|
|
50
|
+
if (names.includes("default.js")) {
|
|
51
|
+
return "export { default } from \"./default.js\";";
|
|
52
|
+
}
|
|
53
|
+
// exactly one in app/config/database -> reexport that
|
|
54
|
+
if (n === 1) {
|
|
55
|
+
const onlyFile = names[0];
|
|
56
|
+
const withoutExt = onlyFile.replace(/\.(ts|js)$/, ".js");
|
|
57
|
+
return `export { default } from "./${withoutExt}";`;
|
|
58
|
+
}
|
|
59
|
+
// multiple files, none is index or default -> error
|
|
60
|
+
throw fail("multiple database drivers, add index or default.(ts|js); found {0}", names.join(", "));
|
|
57
61
|
}
|
|
58
62
|
const js_re = /^.*.js$/;
|
|
59
63
|
const write_directories = async (build_directory, app) => {
|
|
@@ -81,22 +85,22 @@ store.push(["${FileRef.webpath(bare)}", store${i}]);`).join("\n")}
|
|
|
81
85
|
export default store;`;
|
|
82
86
|
await build_directory.join("stores.js").write(stores_js);
|
|
83
87
|
};
|
|
84
|
-
const
|
|
85
|
-
const d2 = app.runpath(location.
|
|
88
|
+
const write_views = async (build_directory, app) => {
|
|
89
|
+
const d2 = app.runpath(location.views);
|
|
86
90
|
const e = await Promise.all((await FileRef.collect(d2, file => js_re.test(file.path)))
|
|
87
91
|
.map(async (path) => `${path}`.replace(d2.toString(), _ => "")));
|
|
88
|
-
const
|
|
89
|
-
const
|
|
90
|
-
${e.map(path => path.slice(1, -".js".length)).map((bare, i) => `import * as
|
|
91
|
-
|
|
92
|
+
const views_js = `
|
|
93
|
+
const view = [];
|
|
94
|
+
${e.map(path => path.slice(1, -".js".length)).map((bare, i) => `import * as view${i} from "${FileRef.webpath(`#view/${bare}`)}";
|
|
95
|
+
view.push(["${FileRef.webpath(bare)}", view${i}]);`).join("\n")}
|
|
92
96
|
|
|
93
97
|
${app.roots.map((root, i) => `
|
|
94
98
|
import * as root${i} from "${FileRef.webpath(`../server/${root.name}`)}";
|
|
95
|
-
|
|
99
|
+
view.push(["${root.name.slice(0, -".js".length)}", root${i}]);
|
|
96
100
|
`).join("\n")}
|
|
97
101
|
|
|
98
|
-
export default
|
|
99
|
-
await build_directory.join("
|
|
102
|
+
export default view;`;
|
|
103
|
+
await build_directory.join("views.js").write(views_js);
|
|
100
104
|
};
|
|
101
105
|
const write_bootstrap = async (app, mode, i18n_active) => {
|
|
102
106
|
const build_start_script = `
|
|
@@ -104,7 +108,7 @@ import serve from "primate/serve";
|
|
|
104
108
|
const files = {};
|
|
105
109
|
${app.server_build.map(name => `${name}s`).map(name => `import ${name} from "./${app.id}/${name}.js";
|
|
106
110
|
files.${name} = ${name};`).join("\n")}
|
|
107
|
-
import
|
|
111
|
+
import views from "./${app.id}/views.js";
|
|
108
112
|
import stores from "./${app.id}/stores.js";
|
|
109
113
|
import target from "./target.js";
|
|
110
114
|
import session from "#session";
|
|
@@ -122,7 +126,7 @@ const app = await serve(import.meta.url, {
|
|
|
122
126
|
...target,
|
|
123
127
|
config,
|
|
124
128
|
files,
|
|
125
|
-
|
|
129
|
+
views,
|
|
126
130
|
stores,
|
|
127
131
|
mode: "${mode}",
|
|
128
132
|
session_config: session[s_config],
|
|
@@ -135,35 +139,50 @@ export default app;
|
|
|
135
139
|
};
|
|
136
140
|
const post = async (app) => {
|
|
137
141
|
const defaults = FileRef.join(import.meta.url, "../../defaults");
|
|
138
|
-
await app.
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
142
|
+
await app.compile(app.path.routes, "routes", {
|
|
143
|
+
loader: (source, file) => {
|
|
144
|
+
const path = app.basename(file, app.path.routes);
|
|
145
|
+
return wrap(source, path, app.id);
|
|
146
|
+
},
|
|
147
|
+
});
|
|
148
|
+
await app.compile(app.path.stores, "stores", {
|
|
149
|
+
resolver: basename => `${basename}.original`, // First pass: save original
|
|
150
|
+
});
|
|
151
|
+
if (await app.path.stores.exists()) {
|
|
152
|
+
const stores = await app.path.stores.collect(({ path }) => app.extensions.some(e => path.endsWith(e)));
|
|
153
|
+
for (const file of stores) {
|
|
154
|
+
const basename = app.basename(file, app.path.stores);
|
|
155
|
+
const wrapper = dedent `
|
|
156
|
+
import database from "#database";
|
|
157
|
+
import store from "./${basename}.original.js";
|
|
158
|
+
import wrap from "primate/database/wrap";
|
|
159
|
+
export default wrap("${basename}", store, database);
|
|
160
|
+
`;
|
|
161
|
+
const target = app.runpath("stores", `${basename}.js`);
|
|
162
|
+
await target.write(wrapper);
|
|
163
|
+
}
|
|
164
|
+
}
|
|
148
165
|
const configs = FileRef.join(dirname, "../../private/config/config");
|
|
149
166
|
const database_base = app.path.config.join("database");
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
167
|
+
// prelayer config default
|
|
168
|
+
await app.compile(configs, "config", {
|
|
169
|
+
loader: async (source, file) => {
|
|
170
|
+
const relative = file.debase(configs);
|
|
171
|
+
if (relative.path === "/database/index.js") {
|
|
172
|
+
const indexContent = await index_database(database_base);
|
|
173
|
+
// if empty, user provided own index -> use the compiled source
|
|
174
|
+
return indexContent || source;
|
|
175
|
+
}
|
|
176
|
+
return source;
|
|
177
|
+
},
|
|
154
178
|
});
|
|
155
|
-
await app.
|
|
156
|
-
|
|
157
|
-
await app.
|
|
158
|
-
//
|
|
159
|
-
await app.
|
|
160
|
-
//
|
|
161
|
-
await app.
|
|
162
|
-
import * as component from "#stage/component${file}";
|
|
163
|
-
|
|
164
|
-
export * from "#stage/component${file}";
|
|
165
|
-
export default component?.default;
|
|
166
|
-
`);
|
|
179
|
+
await app.compile(app.path.modules, "modules");
|
|
180
|
+
await app.compile(app.path.locales, "locales");
|
|
181
|
+
await app.compile(app.path.config, "config");
|
|
182
|
+
// reusable components
|
|
183
|
+
await app.compile(app.path.components, "components");
|
|
184
|
+
// view components
|
|
185
|
+
await app.compile(app.path.views, "views");
|
|
167
186
|
// copy framework pages
|
|
168
187
|
await defaults.copy(app.runpath(location.server, location.pages));
|
|
169
188
|
// copy pages to build
|
|
@@ -180,7 +199,7 @@ const post = async (app) => {
|
|
|
180
199
|
const src = file.debase(app.path.static);
|
|
181
200
|
app.build.export(`import "./${location.static}${src}";`);
|
|
182
201
|
}));
|
|
183
|
-
app.build.export("
|
|
202
|
+
app.build.export("import \"primate/client/app\";");
|
|
184
203
|
app.build.plugin({
|
|
185
204
|
name: "@primate/core/frontend",
|
|
186
205
|
setup(build) {
|
|
@@ -209,7 +228,7 @@ const post = async (app) => {
|
|
|
209
228
|
const build_directory = app.path.build.join(app.id);
|
|
210
229
|
// TODO: remove after rcompat automatically creates directories
|
|
211
230
|
await build_directory.create();
|
|
212
|
-
await
|
|
231
|
+
await write_views(build_directory, app);
|
|
213
232
|
await write_stores(build_directory, app);
|
|
214
233
|
await write_directories(build_directory, app);
|
|
215
234
|
await write_bootstrap(app, app.mode, app.i18n_active);
|
|
@@ -223,16 +242,11 @@ const post = async (app) => {
|
|
|
223
242
|
"#database/*": "./config/database/*.js",
|
|
224
243
|
"#session": "./config/session.js",
|
|
225
244
|
"#i18n": "./config/i18n.js",
|
|
245
|
+
"#view/*": "./views/*.js",
|
|
226
246
|
"#component/*": "./components/*.js",
|
|
227
247
|
"#locale/*": "./locales/*.js",
|
|
228
248
|
"#module/*": "./modules/*.js",
|
|
229
249
|
"#route/*": "./routes/*.js",
|
|
230
|
-
"#stage/component/*": "./stage/components/*.js",
|
|
231
|
-
"#stage/locale/*": "./stage/locales/*.js",
|
|
232
|
-
"#stage/config/*": "./stage/config/*.js",
|
|
233
|
-
"#stage/module/*": "./stage/modules/*.js",
|
|
234
|
-
"#stage/route/*": "./stage/routes/*.js",
|
|
235
|
-
"#stage/store/*": "./stage/stores/*.js",
|
|
236
250
|
"#store/*": "./stores/*.js",
|
|
237
251
|
},
|
|
238
252
|
};
|
|
@@ -66,17 +66,30 @@ export default class I18NModule extends Module {
|
|
|
66
66
|
return next(request);
|
|
67
67
|
// only cookie-persistance is server-supported
|
|
68
68
|
if (this.#persist !== "cookie")
|
|
69
|
-
return new Response(null, {
|
|
69
|
+
return new Response(null, {
|
|
70
|
+
headers: {
|
|
71
|
+
"Content-Length": String(0),
|
|
72
|
+
},
|
|
73
|
+
status: Status.NO_CONTENT
|
|
74
|
+
});
|
|
70
75
|
// only accept configured locales
|
|
71
76
|
if (!this.#configured(requested))
|
|
72
|
-
return new Response(null, {
|
|
77
|
+
return new Response(null, {
|
|
78
|
+
headers: {
|
|
79
|
+
"Content-Length": String(0),
|
|
80
|
+
},
|
|
81
|
+
status: Status.NO_CONTENT
|
|
82
|
+
});
|
|
73
83
|
const header = cookie(COOKIE_NAME, requested, {
|
|
74
84
|
secure: this.#secure,
|
|
75
85
|
path: "/",
|
|
76
86
|
sameSite: "Strict",
|
|
77
87
|
});
|
|
78
88
|
return new Response(null, {
|
|
79
|
-
headers: {
|
|
89
|
+
headers: {
|
|
90
|
+
"Set-Cookie": header,
|
|
91
|
+
"Content-Length": String(0),
|
|
92
|
+
},
|
|
80
93
|
status: Status.NO_CONTENT,
|
|
81
94
|
});
|
|
82
95
|
}
|
|
@@ -2,6 +2,7 @@ declare const _default: {
|
|
|
2
2
|
readonly app_html: "app.html";
|
|
3
3
|
readonly build: "build";
|
|
4
4
|
readonly client: "client";
|
|
5
|
+
readonly lib: "lib";
|
|
5
6
|
readonly components: "components";
|
|
6
7
|
readonly config: "config";
|
|
7
8
|
readonly error_html: "error.html";
|
|
@@ -12,6 +13,8 @@ declare const _default: {
|
|
|
12
13
|
readonly static: "static";
|
|
13
14
|
readonly stores: "stores";
|
|
14
15
|
readonly locales: "locales";
|
|
16
|
+
readonly views: "views";
|
|
17
|
+
readonly hooks: "hooks";
|
|
15
18
|
};
|
|
16
19
|
export default _default;
|
|
17
20
|
//# sourceMappingURL=location.d.ts.map
|
package/lib/private/location.js
CHANGED
|
@@ -5,6 +5,8 @@ export default {
|
|
|
5
5
|
build: "build",
|
|
6
6
|
// client build
|
|
7
7
|
client: "client",
|
|
8
|
+
// component library
|
|
9
|
+
lib: "lib",
|
|
8
10
|
// renderable components
|
|
9
11
|
components: "components",
|
|
10
12
|
// config
|
|
@@ -25,5 +27,9 @@ export default {
|
|
|
25
27
|
stores: "stores",
|
|
26
28
|
// locales
|
|
27
29
|
locales: "locales",
|
|
30
|
+
// views
|
|
31
|
+
views: "views",
|
|
32
|
+
// hooks
|
|
33
|
+
hooks: "hooks",
|
|
28
34
|
};
|
|
29
35
|
//# sourceMappingURL=location.js.map
|
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
import type Dict from "@rcompat/type/Dict";
|
|
2
2
|
import type JSONValue from "@rcompat/type/JSONValue";
|
|
3
3
|
import type Schema from "@rcompat/type/Schema";
|
|
4
|
-
type
|
|
4
|
+
type Form = Dict<string>;
|
|
5
5
|
type Parsed = {
|
|
6
6
|
type: "binary";
|
|
7
7
|
value: Blob;
|
|
8
8
|
} | {
|
|
9
|
-
type: "
|
|
10
|
-
value:
|
|
9
|
+
type: "form";
|
|
10
|
+
value: Dict<string>;
|
|
11
11
|
} | {
|
|
12
12
|
type: "json";
|
|
13
13
|
value: JSONValue;
|
|
@@ -25,12 +25,13 @@ export default class RequestBody {
|
|
|
25
25
|
#private;
|
|
26
26
|
static parse(request: Request, url: URL): Promise<RequestBody>;
|
|
27
27
|
static none(): RequestBody;
|
|
28
|
-
constructor(
|
|
29
|
-
get type(): "binary" | "
|
|
28
|
+
constructor(parsed: Parsed, files?: Dict<File>);
|
|
29
|
+
get type(): "binary" | "form" | "json" | "none" | "text";
|
|
30
30
|
json(): JSONValue;
|
|
31
31
|
json<S extends Schema<unknown>>(schema: S): ParseReturn<S>;
|
|
32
|
-
|
|
33
|
-
|
|
32
|
+
form(): Form;
|
|
33
|
+
form<S extends Schema<unknown>>(schema: S): ParseReturn<S>;
|
|
34
|
+
files(): Dict<File>;
|
|
34
35
|
text(): string;
|
|
35
36
|
binary(): Blob;
|
|
36
37
|
none(): null;
|