@primate/svelte 0.14.0 → 0.15.1
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 +4 -4
- package/src/client/client.js +18 -0
- package/src/client/create-root.js +25 -0
- package/src/client/exports.js +3 -0
- package/src/client/liveview.js +16 -0
- package/src/client/rootname.js +1 -0
- package/src/module.js +115 -145
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@primate/svelte",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.15.1",
|
|
4
4
|
"description": "Primate Svelte module",
|
|
5
5
|
"homepage": "https://primatejs.com/modules/svelte",
|
|
6
6
|
"bugs": "https://github.com/primatejs/primate/issues",
|
|
@@ -16,11 +16,11 @@
|
|
|
16
16
|
"directory": "packages/svelte"
|
|
17
17
|
},
|
|
18
18
|
"dependencies": {
|
|
19
|
-
"runtime-compat": "^0.
|
|
20
|
-
"svelte": "^4.
|
|
19
|
+
"runtime-compat": "^0.24.1",
|
|
20
|
+
"svelte": "^4.1.1"
|
|
21
21
|
},
|
|
22
22
|
"peerDependencies": {
|
|
23
|
-
"primate": "0.
|
|
23
|
+
"primate": "0.21"
|
|
24
24
|
},
|
|
25
25
|
"type": "module",
|
|
26
26
|
"exports": "./src/exports.js"
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import rootname from "./rootname.js";
|
|
2
|
+
import liveview from "./liveview.js";
|
|
3
|
+
|
|
4
|
+
export default ({names, data}, options) => `
|
|
5
|
+
import * as components from "app";
|
|
6
|
+
const getComponents = names =>
|
|
7
|
+
Object.entries(components).filter(([name, component]) =>
|
|
8
|
+
names.includes(name))
|
|
9
|
+
.map(([, component]) => component);
|
|
10
|
+
let root = new components.${rootname}({
|
|
11
|
+
target: document.body,
|
|
12
|
+
hydrate: true,
|
|
13
|
+
props: {
|
|
14
|
+
components: getComponents([${names.map(name => `"${name}"`)}]),
|
|
15
|
+
data: JSON.parse(${JSON.stringify(JSON.stringify(data))}),
|
|
16
|
+
},
|
|
17
|
+
});
|
|
18
|
+
${options.liveview ? liveview : ""}`;
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
export default (length, data) => {
|
|
2
|
+
const n = length - 1;
|
|
3
|
+
const body = Array.from({length: n}, (_, i) => i - 1)
|
|
4
|
+
.reduceRight((child, _, i) => `
|
|
5
|
+
{#if components[${i + 1}]}
|
|
6
|
+
<svelte:component this={components[${i}]} ${data}={data[${i}]}>
|
|
7
|
+
${child}
|
|
8
|
+
</svelte:component>
|
|
9
|
+
{:else}
|
|
10
|
+
<svelte:component this={components[${i}]} ${data}={data[${i}]} />
|
|
11
|
+
{/if}
|
|
12
|
+
`, `<svelte:component this={components[${n}]} ${data}={data[${n}]} />`);
|
|
13
|
+
|
|
14
|
+
return `
|
|
15
|
+
<script>
|
|
16
|
+
import {afterUpdate} from "svelte";
|
|
17
|
+
export let components;
|
|
18
|
+
export let data;
|
|
19
|
+
export let update = () => undefined;
|
|
20
|
+
|
|
21
|
+
afterUpdate(update);
|
|
22
|
+
</script>
|
|
23
|
+
${body}
|
|
24
|
+
`;
|
|
25
|
+
};
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import rootname from "./rootname.js";
|
|
2
|
+
|
|
3
|
+
export default `
|
|
4
|
+
const {liveview} = components;
|
|
5
|
+
window.addEventListener("DOMContentLoaded", _ => liveview((props, update) => {
|
|
6
|
+
root.$destroy();
|
|
7
|
+
root = new components.${rootname}({
|
|
8
|
+
target: document.body,
|
|
9
|
+
hydrate: true,
|
|
10
|
+
props: {
|
|
11
|
+
components: getComponents(props.names),
|
|
12
|
+
data: props.data,
|
|
13
|
+
update,
|
|
14
|
+
},
|
|
15
|
+
});
|
|
16
|
+
}));`;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export default "root";
|
package/src/module.js
CHANGED
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
import crypto from "runtime-compat/crypto";
|
|
2
2
|
import {Path} from "runtime-compat/fs";
|
|
3
|
+
import {Response, Status, MediaType} from "runtime-compat/http";
|
|
3
4
|
import * as compiler from "svelte/compiler";
|
|
4
5
|
import {tryreturn} from "runtime-compat/async";
|
|
5
6
|
import errors from "./errors.js";
|
|
7
|
+
import {client, create_root, rootname} from "./client/exports.js";
|
|
6
8
|
|
|
7
9
|
const endings = {
|
|
8
10
|
svelte: ".svelte",
|
|
@@ -21,190 +23,158 @@ const hash = async (string, algorithm = "sha-256") => {
|
|
|
21
23
|
};
|
|
22
24
|
const normalize = async path => `_${await hash(path)}`;
|
|
23
25
|
|
|
24
|
-
const rootname = "root";
|
|
25
26
|
const filename = `${rootname}.js`;
|
|
26
27
|
|
|
27
|
-
const options = {
|
|
28
|
-
server: {generate: "ssr", hydratable: true},
|
|
29
|
-
client: {generate: "dom", hydratable: true},
|
|
30
|
-
};
|
|
31
|
-
|
|
32
28
|
const type = "module";
|
|
33
29
|
|
|
34
30
|
const load = async path =>
|
|
35
31
|
tryreturn(async () => (await import(`${path}.js`)).default)
|
|
36
|
-
.orelse(
|
|
32
|
+
.orelse(_ => errors.MissingComponent.throw(path.name, path));
|
|
37
33
|
|
|
38
34
|
const make_component = base => async (name, props) =>
|
|
39
35
|
({name, component: await load(base.join(name)), props});
|
|
40
36
|
|
|
41
37
|
const encoder = new TextEncoder();
|
|
42
38
|
|
|
43
|
-
const handler = (
|
|
44
|
-
async (app, {layouts = [], as_layout} = {}) => {
|
|
45
|
-
const
|
|
39
|
+
const handler = (name, props = {}, {status = Status.OK, page} = {}) =>
|
|
40
|
+
async (app, {layouts = [], as_layout} = {}, request) => {
|
|
41
|
+
const options = {
|
|
42
|
+
liveview: app.liveview !== undefined,
|
|
43
|
+
};
|
|
44
|
+
const {headers} = request;
|
|
45
|
+
const {paths} = app.build;
|
|
46
|
+
const make = make_component(paths.server.join(app.config.build.app));
|
|
46
47
|
if (as_layout) {
|
|
47
48
|
return make(name, props);
|
|
48
49
|
}
|
|
49
50
|
const components = (await Promise.all(layouts.map(layout =>
|
|
50
|
-
layout(app, {as_layout: true})
|
|
51
|
+
layout(app, {as_layout: true}, request)
|
|
51
52
|
)))
|
|
52
53
|
/* set the actual page as the last component */
|
|
53
54
|
.concat(await make(name, props));
|
|
54
55
|
|
|
55
56
|
const data = components.map(component => component.props);
|
|
56
|
-
|
|
57
|
-
|
|
57
|
+
const names = await Promise.all(components.map(component =>
|
|
58
|
+
normalize(component.name)));
|
|
59
|
+
|
|
60
|
+
if (options.liveview && headers.get(app.liveview.header) !== undefined) {
|
|
61
|
+
return new Response(JSON.stringify({names, data}), {
|
|
62
|
+
status,
|
|
63
|
+
headers: {...await app.headers(),
|
|
64
|
+
"Content-Type": MediaType.APPLICATION_JSON},
|
|
65
|
+
});
|
|
66
|
+
}
|
|
67
|
+
const root = paths.server.join(filename);
|
|
58
68
|
const imported = (await import(root)).default;
|
|
59
69
|
const {html} = imported.render({
|
|
60
70
|
components: components.map(({component}) => component),
|
|
61
71
|
data,
|
|
62
72
|
});
|
|
63
73
|
|
|
64
|
-
const
|
|
65
|
-
normalize(component.name)))).join(", ");
|
|
66
|
-
const code = `
|
|
67
|
-
import {${rootname}, ${names}} from "app";
|
|
68
|
-
new ${rootname}({
|
|
69
|
-
target: document.body,
|
|
70
|
-
hydrate: true,
|
|
71
|
-
props: {
|
|
72
|
-
components: [${names}],
|
|
73
|
-
data: JSON.parse(${JSON.stringify(JSON.stringify(data))}),
|
|
74
|
-
},
|
|
75
|
-
});
|
|
76
|
-
`;
|
|
74
|
+
const code = client({names, data}, options);
|
|
77
75
|
|
|
78
76
|
await app.publish({code, type, inline: true});
|
|
79
|
-
// needs to be called before app.render
|
|
80
|
-
const headers = app.headers();
|
|
81
77
|
|
|
82
|
-
|
|
83
|
-
|
|
78
|
+
const headers$ = await app.headers();
|
|
79
|
+
|
|
80
|
+
return new Response(await app.render({body: html, page}), {
|
|
84
81
|
status,
|
|
85
|
-
headers: {...headers
|
|
86
|
-
}
|
|
82
|
+
headers: {...headers$, "Content-Type": MediaType.TEXT_HTML},
|
|
83
|
+
});
|
|
87
84
|
};
|
|
88
85
|
|
|
89
|
-
const
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
.reduceRight((child, _, i) => `
|
|
93
|
-
{#if components[${i + 1}]}
|
|
94
|
-
<svelte:component this={components[${i}]} ${data}={data[${i}]}>
|
|
95
|
-
${child}
|
|
96
|
-
</svelte:component>
|
|
97
|
-
{:else}
|
|
98
|
-
<svelte:component this={components[${i}]} ${data}={data[${i}]} />
|
|
99
|
-
{/if}
|
|
100
|
-
`, `<svelte:component this={components[${n}]} ${data}={data[${n}]} />`);
|
|
101
|
-
|
|
102
|
-
return `
|
|
103
|
-
<script>
|
|
104
|
-
export let components;
|
|
105
|
-
export let data;
|
|
106
|
-
</script>
|
|
107
|
-
${body}
|
|
108
|
-
`;
|
|
86
|
+
const options = {
|
|
87
|
+
server: {generate: "ssr", hydratable: true},
|
|
88
|
+
client: {generate: "dom", hydratable: true},
|
|
109
89
|
};
|
|
110
90
|
|
|
111
91
|
const {svelte, js} = endings;
|
|
112
|
-
export default ({
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
};
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
92
|
+
export default ({dynamicProps = "data"} = {}) => ({
|
|
93
|
+
name: "@primate/svelte",
|
|
94
|
+
register(app, next) {
|
|
95
|
+
app.register("svelte", handler);
|
|
96
|
+
return next(app);
|
|
97
|
+
},
|
|
98
|
+
async compile(app, next) {
|
|
99
|
+
const source = app.build.paths.components;
|
|
100
|
+
// copy components to build/components
|
|
101
|
+
await app.copy(app.paths.components, source, /^.*.(?:js|svelte)$/u);
|
|
102
|
+
const components = await source.collect(/^.*.svelte$/u);
|
|
103
|
+
const target = app.build.paths.server.join(app.config.build.app);
|
|
104
|
+
await target.file.create();
|
|
105
|
+
|
|
106
|
+
await Promise.all(components.map(async component => {
|
|
107
|
+
const file = await component.file.read();
|
|
108
|
+
const server = compiler.compile(file, options.server);
|
|
109
|
+
const to = target.join(`${component.path}.js`.replace(source, ""));
|
|
110
|
+
await to.directory.file.create();
|
|
111
|
+
await to.file.write(server.js.code.replaceAll(svelte, js));
|
|
112
|
+
}));
|
|
113
|
+
|
|
114
|
+
const root = create_root(app.layoutDepth, dynamicProps);
|
|
115
|
+
const server = compiler.compile(root, options.server).js.code;
|
|
116
|
+
const to = app.build.paths.server.join(filename);
|
|
117
|
+
await to.file.write(server);
|
|
118
|
+
|
|
119
|
+
return next(app);
|
|
120
|
+
},
|
|
121
|
+
async publish(app, next) {
|
|
122
|
+
const source = app.build.paths.components;
|
|
123
|
+
const components = await source.collect(/^.*.svelte$/u);
|
|
124
|
+
await app.import("svelte");
|
|
125
|
+
|
|
126
|
+
// liveview
|
|
127
|
+
if (app.liveview !== undefined) {
|
|
128
|
+
app.bootstrap({
|
|
129
|
+
type: "script",
|
|
130
|
+
code: "export {default as liveview} from \"@primate/liveview\";\n",
|
|
131
|
+
});
|
|
132
|
+
}
|
|
133
|
+
await Promise.all(components.map(async component => {
|
|
134
|
+
const name = component.path.replace(`${source}/`, "");
|
|
135
|
+
const file = await component.file.read();
|
|
136
|
+
const client = compiler.compile(file, options.client);
|
|
137
|
+
const build = app.config.build.app;
|
|
138
|
+
const {path} = component;
|
|
151
139
|
|
|
152
|
-
|
|
153
|
-
const server = compiler.compile(root, options.server).js.code;
|
|
154
|
-
const to = app.paths.server.join(filename);
|
|
155
|
-
await to.file.write(server);
|
|
156
|
-
|
|
157
|
-
return next(app);
|
|
158
|
-
},
|
|
159
|
-
async publish(app, next) {
|
|
160
|
-
await app.import("svelte");
|
|
161
|
-
const {source, build} = env;
|
|
162
|
-
|
|
163
|
-
const target = env.paths.client;
|
|
164
|
-
await target.file.create();
|
|
165
|
-
|
|
166
|
-
await Promise.all(env.components.map(async component => {
|
|
167
|
-
const name = component.path.replace(`${source}/`, "");
|
|
168
|
-
const file = await component.file.read();
|
|
169
|
-
const client = compiler.compile(file, options.client);
|
|
140
|
+
{
|
|
170
141
|
{
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
await app.publish({src, code, type});
|
|
175
|
-
}
|
|
176
|
-
const imported = await normalize(name);
|
|
177
|
-
app.bootstrap({
|
|
178
|
-
type: "script",
|
|
179
|
-
code: `export {default as ${imported}} from "./${name}.js";\n`,
|
|
180
|
-
});
|
|
181
|
-
}
|
|
182
|
-
if (client.css.code !== null) {
|
|
183
|
-
{
|
|
184
|
-
const {code} = client.css;
|
|
185
|
-
const src = `${component.path}.css`.replace(`${source}`, build);
|
|
186
|
-
await app.publish({src, code, type: "style"});
|
|
187
|
-
}
|
|
188
|
-
{
|
|
189
|
-
const src = `${component.path}.css`.replace(`${source}`, "");
|
|
190
|
-
app.bootstrap({type: "style", code: `import ".${src}";`});
|
|
191
|
-
}
|
|
142
|
+
const code = client.js.code.replaceAll(svelte, js);
|
|
143
|
+
const src = `${path}.js`.replace(`${source}`, _ => build);
|
|
144
|
+
await app.publish({src, code, type});
|
|
192
145
|
}
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
const code = client.js.code.replaceAll(svelte, js);
|
|
199
|
-
const src = new Path(app.config.http.static.root, filename);
|
|
200
|
-
await app.publish({src, code, type});
|
|
146
|
+
const imported = await normalize(name);
|
|
147
|
+
app.bootstrap({
|
|
148
|
+
type: "script",
|
|
149
|
+
code: `export {default as ${imported}} from "./${name}.js";\n`,
|
|
150
|
+
});
|
|
201
151
|
}
|
|
202
|
-
{
|
|
203
|
-
|
|
204
|
-
|
|
152
|
+
if (client.css.code !== null) {
|
|
153
|
+
{
|
|
154
|
+
const {code} = client.css;
|
|
155
|
+
const src = `${path}.css`.replace(`${source}`, _ => build);
|
|
156
|
+
await app.publish({src, code, type: "style"});
|
|
157
|
+
}
|
|
158
|
+
{
|
|
159
|
+
// sans app.config.build.app removal, for a bundler to resolve,
|
|
160
|
+
// irrelevant without bundling
|
|
161
|
+
const src = `${path}.css`.replace(`${source}`, "");
|
|
162
|
+
app.bootstrap({type: "style", code: `import ".${src}";`});
|
|
163
|
+
}
|
|
205
164
|
}
|
|
165
|
+
}));
|
|
206
166
|
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
167
|
+
{
|
|
168
|
+
const root = create_root(app.layoutDepth, dynamicProps);
|
|
169
|
+
const client = compiler.compile(root, options.client);
|
|
170
|
+
const code = client.js.code.replaceAll(svelte, js);
|
|
171
|
+
const src = new Path(app.config.http.static.root, filename);
|
|
172
|
+
await app.publish({src, code, type});
|
|
173
|
+
}
|
|
174
|
+
{
|
|
175
|
+
const code = `export {default as ${rootname}} from "../${filename}";\n`;
|
|
176
|
+
app.bootstrap({type: "script", code});
|
|
177
|
+
}
|
|
178
|
+
return next(app);
|
|
179
|
+
},
|
|
180
|
+
});
|