@primate/solid 0.1.0 → 0.2.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.
Files changed (3) hide show
  1. package/README.md +1 -2
  2. package/package.json +4 -3
  3. package/src/module.js +46 -132
package/README.md CHANGED
@@ -1,6 +1,5 @@
1
1
  # Solid
2
2
 
3
- This handler module supports SSR and serves SolidJS (JSX) components with the
4
- .jsx extension.
3
+ This handler serves Solid (JSX) components with the `jsx` file extension.
5
4
 
6
5
  ## [Documentation](https://primatejs.com/modules/solid)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@primate/solid",
3
- "version": "0.1.0",
3
+ "version": "0.2.0",
4
4
  "description": "Primate Solid module",
5
5
  "homepage": "https://primatejs.com/modules/solid",
6
6
  "bugs": "https://github.com/primatejs/primate/issues",
@@ -16,12 +16,13 @@
16
16
  "directory": "packages/solid"
17
17
  },
18
18
  "dependencies": {
19
+ "@primate/frontend": "^0.2.0",
19
20
  "babel-preset-solid": "^1.7.7",
20
- "runtime-compat": "^0.25.1",
21
+ "runtime-compat": "^0.25.7",
21
22
  "solid-js": "^1.7.11"
22
23
  },
23
24
  "peerDependencies": {
24
- "primate": "0.21"
25
+ "primate": "0.22"
25
26
  },
26
27
  "type": "module",
27
28
  "exports": "./src/exports.js"
package/src/module.js CHANGED
@@ -1,63 +1,38 @@
1
- import crypto from "runtime-compat/crypto";
2
1
  import {Path} from "runtime-compat/fs";
3
2
  import {Response, Status, MediaType} from "runtime-compat/http";
3
+ import * as base from "@primate/frontend";
4
+ import {renderToString as render} from "solid-js/web";
4
5
  import {transformAsync} from "@babel/core";
5
6
  import solid from "babel-preset-solid";
6
- import {renderToString} from "solid-js/web";
7
- import {tryreturn} from "runtime-compat/async";
8
- import errors from "./errors.js";
9
7
  import {client, create_root, rootname, hydrate} from "./client/exports.js";
10
8
 
11
- // do not hard-depend on node
12
- const packager = import.meta.runtime?.packager ?? "package.json";
13
- const library = import.meta.runtime?.library ?? "node_modules";
14
-
15
- const encoder = new TextEncoder();
16
- const hash = async (string, algorithm = "sha-256") => {
17
- const base = 16;
18
- const target_pad_length = 2;
19
- const target_slice = 12;
20
- const bytes = await crypto.subtle.digest(algorithm, encoder.encode(string));
21
- return Array.from(new Uint8Array(bytes))
22
- .map(byte => byte.toString(base).padStart(target_pad_length, "0"))
23
- .join("")
24
- .slice(0, target_slice);
25
- };
26
- const normalize = async path => `solid_${await hash(path)}`;
9
+ const normalize = base.normalize("solid");
27
10
 
28
11
  const import$ = async (module, app, copy_dependency) => {
29
- const {build: {modules}, http: {static: {root}}} = app.config;
12
+ const {library, packager} = app;
13
+ const {http: {static: {root}}} = app.config;
30
14
  const parts = module.split("/");
31
15
  const path = [library, ...parts];
32
16
  const pkg = await Path.resolve().join(...path, packager).json();
33
17
  if (copy_dependency) {
34
18
  const dependency = Path.resolve().join(...path);
35
- const to = app.build.paths.client.join(modules, ...parts);
19
+ const to = app.runpath(app.config.location.client, library, ...parts);
36
20
  await dependency.file.copy(`${to}`);
37
21
  }
38
22
 
39
23
  const entry = pkg.exports["."].browser.import;
40
- app.importmaps[module] = new Path(root, modules, module, entry).path;
24
+ app.importmaps[module] = new Path(root, library, module, entry).path;
41
25
  };
42
26
 
43
- const filename = `${rootname}.js`;
44
27
  const type = "module";
45
28
 
46
- const load = async path =>
47
- tryreturn(async () => (await import(`${path}.js`)).default)
48
- .orelse(error => {console.log(error);return errors.MissingComponent.throw(path.name, path)});
49
-
50
- const make_component = base => async (name, props) =>
51
- ({name, component: await load(base.join(name)), props});
52
-
53
- const handler = (name, props = {}, {status = Status.OK} = {}) =>
29
+ const handler = config => (name, props = {}, {status = Status.OK, page} = {}) =>
54
30
  async (app, {layouts = [], as_layout} = {}, request) => {
31
+ const {make, root} = config;
32
+ const {headers} = request;
55
33
  const options = {
56
34
  liveview: app.liveview !== undefined,
57
35
  };
58
- const {headers} = request;
59
- const {paths} = app.build;
60
- const make = make_component(paths.server.join(app.config.build.app));
61
36
  if (as_layout) {
62
37
  return make(name, props);
63
38
  }
@@ -78,9 +53,8 @@ const handler = (name, props = {}, {status = Status.OK} = {}) =>
78
53
  });
79
54
  }
80
55
 
81
- const root = paths.server.join(filename);
82
56
  const imported = (await import(root)).default;
83
- const body = renderToString(() => imported({
57
+ const body = render(() => imported({
84
58
  components: components.map(({component}) => component),
85
59
  data,
86
60
  }));
@@ -91,120 +65,60 @@ const handler = (name, props = {}, {status = Status.OK} = {}) =>
91
65
  // needs to be called before app.render
92
66
  const headers$ = await app.headers();
93
67
 
94
- return new Response(await app.render({body}), {
68
+ return new Response(await app.render({body, page}), {
95
69
  status,
96
70
  headers: {...headers$, "Content-Type": MediaType.TEXT_HTML},
97
71
  });
98
72
  };
99
73
 
100
- const options = {
101
- server: [solid, {
102
- generate: "ssr",
103
- hydratable: true,
104
- }],
105
- client: [solid, {
106
- generate: "dom",
107
- hydratable: true,
108
- }],
109
- };
110
-
111
- const transform = {
112
- async server(file) {
113
- return (await transformAsync(file, {presets: [options.server]})).code;
114
- },
115
- async client(file) {
116
- return (await transformAsync(file, {presets: [options.client]})).code;
117
- },
118
- };
119
-
120
74
  export default ({
121
- dynamicProps = "data",
75
+ directory,
76
+ dynamicProps,
122
77
  extension = "jsx",
123
78
  } = {}) => {
124
- const env = {};
125
- const copy_re = new RegExp(`^.*.(?:${extension})$`, "u");
126
- const collect_re = new RegExp(`^.*.${extension}$`, "u");
127
- const extensions = {
128
- from: `.${extension}`,
129
- to: `.${extension}.js`,
130
- };
131
-
132
79
  return {
133
- name: "@primate/solid",
134
- init(app, next/*0.21 compat, remove >0.21*/ = () => null) {
135
- env.source = app.build.paths.components;
136
-
137
- return next(app);
138
- },
80
+ name: "primate:solid",
139
81
  register(app, next) {
140
- app.register(extension, handler);
82
+ app.register(extension, handler(base.register({app, rootname})));
83
+
141
84
  return next(app);
142
85
  },
143
86
  async compile(app, next) {
144
- // copy components to build/components
145
- await app.copy(app.paths.components, env.source, copy_re);
146
- const components = await env.source.collect(collect_re);
147
- const target = app.build.paths.server.join(app.config.build.app);
148
- await target.file.create();
149
-
150
- await Promise.all(components.map(async component => {
151
- const file = await component.file.read();
152
- const code = await transform.server(file);
153
- const to = target.join(`${component.path}.js`.replace(env.source, ""));
154
- await to.directory.file.create();
155
- await to.file.write(code.replaceAll(extensions.from, extensions.to));
156
- }));
157
-
158
- const root = await transform.server(create_root(app.layoutDepth,
159
- dynamicProps));
160
- const to = app.build.paths.server.join(filename);
161
- await to.file.write(root);
87
+ await base.compile({
88
+ app,
89
+ directory: directory ?? app.config.location.components,
90
+ extension,
91
+ rootname,
92
+ create_root,
93
+ dynamicProps,
94
+ async compile(file) {
95
+ const presets = [[solid, {generate: "ssr", hydratable: true}]];
96
+ return (await transformAsync(file, {presets})).code;
97
+ },
98
+ });
162
99
 
163
100
  return next(app);
164
101
  },
165
102
  async publish(app, next) {
166
- const target = app.build.paths.client.join(app.config.build.app);
167
- await target.file.create();
168
-
169
- // create client components
170
- const components = await env.source.collect(collect_re);
171
- await Promise.all(components.map(async component => {
172
- const name = component.path.replace(`${env.source}/`, "");
173
- const file = await component.file.read();
174
- const code = await transform.client(file);
175
- const build = app.config.build.app;
176
- const {path} = component;
177
-
178
- {
179
- {
180
- const src = `${path}.js`.replace(`${env.source}`, _ => build);
181
- await app.publish({src,
182
- code: code.replaceAll(extensions.from, extensions.to),
183
- type});
184
- }
185
- const imported = await normalize(name);
186
- app.bootstrap({
187
- type: "script",
188
- code: `export {default as ${imported}} from "./${name}.js";\n`,
189
- });
190
- }
191
- }));
192
-
103
+ // import libs
193
104
  await import$("solid-js", app, true);
194
105
  await import$("solid-js/web", app);
195
-
196
- app.bootstrap({type: "script", code: hydrate});
197
-
198
- {
199
- const code = await transform.client(create_root(app.layoutDepth,
200
- dynamicProps));
201
- const src = new Path(app.config.http.static.root, filename);
202
- await app.publish({src, code, type});
203
- }
204
- {
205
- const code = `export {default as ${rootname}} from "../${filename}";\n`;
206
- app.bootstrap({type: "script", code});
207
- }
106
+ // export hydration code
107
+ app.export({type: "script", code: hydrate});
108
+
109
+ await base.publish({
110
+ app,
111
+ directory: directory ?? app.config.location.components,
112
+ extension,
113
+ rootname,
114
+ create_root,
115
+ dynamicProps,
116
+ normalize,
117
+ async compile(file) {
118
+ const presets = [[solid, {generate: "dom", hydratable: true}]];
119
+ return {js: (await transformAsync(file, {presets})).code};
120
+ },
121
+ });
208
122
 
209
123
  return next(app);
210
124
  },