elm-pages 2.1.11 → 3.0.0-beta.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.
Files changed (161) hide show
  1. package/codegen/elm-pages-codegen.js +38507 -0
  2. package/generator/dead-code-review/elm-stuff/tests-0.19.1/elm-stuff/0.19.1/Pages-Review-DeadCodeEliminateData.elmi +0 -0
  3. package/generator/dead-code-review/elm-stuff/tests-0.19.1/elm-stuff/0.19.1/Pages-Review-DeadCodeEliminateData.elmo +0 -0
  4. package/generator/dead-code-review/elm-stuff/tests-0.19.1/elm-stuff/0.19.1/Pages-Review-DeadCodeEliminateDataTest.elmi +0 -0
  5. package/generator/dead-code-review/elm-stuff/tests-0.19.1/elm-stuff/0.19.1/Pages-Review-DeadCodeEliminateDataTest.elmo +0 -0
  6. package/generator/dead-code-review/elm-stuff/tests-0.19.1/elm-stuff/0.19.1/Reporter.elmi +0 -0
  7. package/generator/dead-code-review/elm-stuff/tests-0.19.1/elm-stuff/0.19.1/Reporter.elmo +0 -0
  8. package/generator/dead-code-review/elm-stuff/tests-0.19.1/elm-stuff/0.19.1/Runner.elmi +0 -0
  9. package/generator/dead-code-review/elm-stuff/tests-0.19.1/elm-stuff/0.19.1/Runner.elmo +0 -0
  10. package/generator/dead-code-review/elm-stuff/tests-0.19.1/elm-stuff/0.19.1/d.dat +0 -0
  11. package/generator/dead-code-review/elm-stuff/tests-0.19.1/elm-stuff/0.19.1/i.dat +0 -0
  12. package/generator/{template/public/style.css → dead-code-review/elm-stuff/tests-0.19.1/elm-stuff/0.19.1/lock} +0 -0
  13. package/generator/dead-code-review/elm-stuff/tests-0.19.1/elm-stuff/0.19.1/o.dat +0 -0
  14. package/generator/dead-code-review/elm-stuff/tests-0.19.1/elm.json +1 -0
  15. package/generator/dead-code-review/elm-stuff/tests-0.19.1/js/Reporter.elm.js +6795 -0
  16. package/generator/dead-code-review/elm-stuff/tests-0.19.1/js/Runner.elm.js +25651 -0
  17. package/generator/dead-code-review/elm-stuff/tests-0.19.1/js/node_runner.js +110 -0
  18. package/generator/dead-code-review/elm-stuff/tests-0.19.1/js/node_supervisor.js +187 -0
  19. package/generator/dead-code-review/elm-stuff/tests-0.19.1/js/package.json +1 -0
  20. package/generator/dead-code-review/elm-stuff/tests-0.19.1/src/Reporter.elm +26 -0
  21. package/generator/dead-code-review/elm-stuff/tests-0.19.1/src/Runner.elm +62 -0
  22. package/generator/dead-code-review/elm.json +35 -0
  23. package/generator/dead-code-review/src/Pages/Review/DeadCodeEliminateData.elm +181 -0
  24. package/generator/dead-code-review/src/ReviewConfig.elm +9 -0
  25. package/generator/dead-code-review/tests/Pages/Review/DeadCodeEliminateDataTest.elm +455 -0
  26. package/generator/review/elm-stuff/tests-0.19.1/elm-stuff/0.19.1/Pages-Internal-RoutePattern.elmi +0 -0
  27. package/generator/review/elm-stuff/tests-0.19.1/elm-stuff/0.19.1/Pages-Internal-RoutePattern.elmo +0 -0
  28. package/generator/review/elm-stuff/tests-0.19.1/elm-stuff/0.19.1/Pages-Review-NoContractViolations.elmi +0 -0
  29. package/generator/review/elm-stuff/tests-0.19.1/elm-stuff/0.19.1/Pages-Review-NoContractViolations.elmo +0 -0
  30. package/generator/review/elm-stuff/tests-0.19.1/elm-stuff/0.19.1/Pages-Review-NoContractViolationsTest.elmi +0 -0
  31. package/generator/review/elm-stuff/tests-0.19.1/elm-stuff/0.19.1/Pages-Review-NoContractViolationsTest.elmo +0 -0
  32. package/generator/review/elm-stuff/tests-0.19.1/elm-stuff/0.19.1/Reporter.elmi +0 -0
  33. package/generator/review/elm-stuff/tests-0.19.1/elm-stuff/0.19.1/Reporter.elmo +0 -0
  34. package/generator/review/elm-stuff/tests-0.19.1/elm-stuff/0.19.1/Runner.elmi +0 -0
  35. package/generator/review/elm-stuff/tests-0.19.1/elm-stuff/0.19.1/Runner.elmo +0 -0
  36. package/generator/review/elm-stuff/tests-0.19.1/elm-stuff/0.19.1/d.dat +0 -0
  37. package/generator/review/elm-stuff/tests-0.19.1/elm-stuff/0.19.1/i.dat +0 -0
  38. package/generator/review/elm-stuff/tests-0.19.1/elm-stuff/0.19.1/lock +0 -0
  39. package/generator/review/elm-stuff/tests-0.19.1/elm-stuff/0.19.1/o.dat +0 -0
  40. package/generator/review/elm-stuff/tests-0.19.1/elm.json +1 -0
  41. package/generator/review/elm-stuff/tests-0.19.1/js/Reporter.elm.js +6795 -0
  42. package/generator/review/elm-stuff/tests-0.19.1/js/Runner.elm.js +27617 -0
  43. package/generator/review/elm-stuff/tests-0.19.1/js/node_runner.js +110 -0
  44. package/generator/review/elm-stuff/tests-0.19.1/js/node_supervisor.js +187 -0
  45. package/generator/review/elm-stuff/tests-0.19.1/js/package.json +1 -0
  46. package/generator/review/elm-stuff/tests-0.19.1/src/Reporter.elm +26 -0
  47. package/generator/review/elm-stuff/tests-0.19.1/src/Runner.elm +62 -0
  48. package/generator/review/elm.json +13 -4
  49. package/{src → generator/review/src}/Pages/Review/NoContractViolations.elm +148 -148
  50. package/generator/review/tests/Pages/Review/NoContractViolationsTest.elm +331 -0
  51. package/generator/src/RouteBuilder.elm +420 -0
  52. package/generator/src/SharedTemplate.elm +4 -5
  53. package/generator/src/SiteConfig.elm +3 -9
  54. package/generator/src/build.js +308 -95
  55. package/generator/src/cli.js +103 -8
  56. package/generator/src/codegen.js +192 -35
  57. package/generator/src/compile-elm.js +183 -31
  58. package/generator/src/dev-server.js +353 -96
  59. package/generator/src/elm-application.json +3 -1
  60. package/generator/src/elm-codegen.js +34 -0
  61. package/generator/src/elm-file-constants.js +2 -0
  62. package/generator/src/error-formatter.js +20 -1
  63. package/generator/src/generate-template-module-connector.js +125 -927
  64. package/generator/src/hello.ts +5 -0
  65. package/generator/src/pre-render-html.js +58 -104
  66. package/generator/src/render-worker.js +27 -13
  67. package/generator/src/render.js +252 -197
  68. package/generator/src/request-cache-fs.js +18 -0
  69. package/generator/src/request-cache.js +128 -56
  70. package/generator/src/rewrite-client-elm-json.js +49 -0
  71. package/generator/src/route-codegen-helpers.js +62 -1
  72. package/generator/static-code/dev-style.css +22 -0
  73. package/generator/static-code/elm-pages.js +43 -39
  74. package/generator/static-code/hmr.js +98 -88
  75. package/generator/template/app/Api.elm +25 -0
  76. package/generator/template/app/ErrorPage.elm +38 -0
  77. package/generator/template/app/Route/Index.elm +87 -0
  78. package/generator/template/{src → app}/Shared.elm +34 -13
  79. package/generator/template/app/Site.elm +19 -0
  80. package/generator/template/{src → app}/View.elm +0 -0
  81. package/generator/template/elm-pages.config.mjs +5 -0
  82. package/generator/template/elm.json +1 -0
  83. package/generator/template/{public/index.js → index.ts} +7 -3
  84. package/generator/template/package.json +4 -4
  85. package/generator/template/public/favicon.ico +0 -0
  86. package/generator/template/public/images/icon-png.png +0 -0
  87. package/generator/template/src/.gitkeep +0 -0
  88. package/generator/template/style.css +4 -0
  89. package/package.json +33 -23
  90. package/src/ApiRoute.elm +176 -43
  91. package/src/BuildError.elm +10 -1
  92. package/src/CookieParser.elm +84 -0
  93. package/src/DataSource/Env.elm +38 -0
  94. package/src/DataSource/File.elm +27 -16
  95. package/src/DataSource/Glob.elm +126 -80
  96. package/src/DataSource/Http.elm +283 -304
  97. package/src/DataSource/Internal/Glob.elm +5 -21
  98. package/src/DataSource/Internal/Request.elm +25 -0
  99. package/src/DataSource/Port.elm +17 -14
  100. package/src/DataSource.elm +55 -318
  101. package/src/Form/Field.elm +717 -0
  102. package/src/Form/FieldStatus.elm +36 -0
  103. package/src/Form/FieldView.elm +417 -0
  104. package/src/Form/FormData.elm +22 -0
  105. package/src/Form/Validation.elm +391 -0
  106. package/src/Form/Value.elm +118 -0
  107. package/src/Form.elm +1683 -0
  108. package/src/FormData.elm +58 -0
  109. package/src/FormDecoder.elm +102 -0
  110. package/src/Head/Seo.elm +12 -4
  111. package/src/Head.elm +12 -2
  112. package/src/HtmlPrinter.elm +1 -1
  113. package/src/Internal/ApiRoute.elm +17 -4
  114. package/src/Internal/Request.elm +7 -0
  115. package/src/PageServerResponse.elm +68 -0
  116. package/src/Pages/ContentCache.elm +1 -229
  117. package/src/Pages/Fetcher.elm +58 -0
  118. package/src/Pages/FormState.elm +256 -0
  119. package/src/Pages/Generate.elm +800 -0
  120. package/src/Pages/Internal/Form.elm +17 -0
  121. package/src/Pages/Internal/NotFoundReason.elm +3 -55
  122. package/src/Pages/Internal/Platform/Cli.elm +777 -579
  123. package/src/Pages/Internal/Platform/Effect.elm +5 -5
  124. package/src/Pages/Internal/Platform/StaticResponses.elm +178 -394
  125. package/src/Pages/Internal/Platform/ToJsPayload.elm +24 -23
  126. package/src/Pages/Internal/Platform.elm +1244 -504
  127. package/src/Pages/Internal/ResponseSketch.elm +19 -0
  128. package/src/Pages/Internal/RoutePattern.elm +596 -45
  129. package/src/Pages/Manifest.elm +26 -0
  130. package/src/Pages/Msg.elm +79 -0
  131. package/src/Pages/ProgramConfig.elm +67 -14
  132. package/src/Pages/SiteConfig.elm +3 -6
  133. package/src/Pages/StaticHttp/Request.elm +4 -2
  134. package/src/Pages/StaticHttpRequest.elm +50 -215
  135. package/src/Pages/Transition.elm +70 -0
  136. package/src/Path.elm +1 -0
  137. package/src/Pattern.elm +98 -0
  138. package/src/RenderRequest.elm +2 -2
  139. package/src/RequestsAndPending.elm +111 -9
  140. package/src/Server/Request.elm +1253 -0
  141. package/src/Server/Response.elm +292 -0
  142. package/src/Server/Session.elm +316 -0
  143. package/src/Server/SetCookie.elm +169 -0
  144. package/src/TerminalText.elm +1 -1
  145. package/src/Test/Html/Internal/ElmHtml/Markdown.elm +0 -1
  146. package/src/Test/Html/Internal/ElmHtml/ToString.elm +1 -1
  147. package/generator/src/Page.elm +0 -359
  148. package/generator/src/codegen-template-module.js +0 -183
  149. package/generator/src/elm-pages-js-minified.js +0 -1
  150. package/generator/template/src/Api.elm +0 -14
  151. package/generator/template/src/Page/Index.elm +0 -69
  152. package/generator/template/src/Site.elm +0 -41
  153. package/src/DataSource/ServerRequest.elm +0 -60
  154. package/src/Internal/OptimizedDecoder.elm +0 -18
  155. package/src/KeepOrDiscard.elm +0 -6
  156. package/src/OptimizedDecoder/Pipeline.elm +0 -335
  157. package/src/OptimizedDecoder.elm +0 -818
  158. package/src/Pages/Internal/ApplicationType.elm +0 -6
  159. package/src/Pages/Secrets.elm +0 -83
  160. package/src/Secrets.elm +0 -111
  161. package/src/SecretsDict.elm +0 -45
@@ -2,10 +2,11 @@ const path = require("path");
2
2
  const fs = require("fs");
3
3
  const which = require("which");
4
4
  const chokidar = require("chokidar");
5
+ const { URL } = require("url");
5
6
  const {
6
- spawnElmMake,
7
7
  compileElmForBrowser,
8
8
  runElmReview,
9
+ compileCliApp,
9
10
  } = require("./compile-elm.js");
10
11
  const http = require("http");
11
12
  const https = require("https");
@@ -13,15 +14,19 @@ const codegen = require("./codegen.js");
13
14
  const kleur = require("kleur");
14
15
  const serveStatic = require("serve-static");
15
16
  const connect = require("connect");
16
- const { restoreColor } = require("./error-formatter");
17
+ const { restoreColorSafe } = require("./error-formatter");
17
18
  const { Worker, SHARE_ENV } = require("worker_threads");
18
19
  const os = require("os");
19
20
  const { ensureDirSync } = require("./file-helpers.js");
20
21
  const baseMiddleware = require("./basepath-middleware.js");
21
22
  const devcert = require("devcert");
23
+ const busboy = require("busboy");
24
+ const { createServer: createViteServer } = require("vite");
25
+ const cliVersion = require("../../package.json").version;
26
+ const esbuild = require("esbuild");
22
27
 
23
28
  /**
24
- * @param {{ port: string; base: string; https: boolean; }} options
29
+ * @param {{ port: string; base: string; https: boolean; debug: boolean; }} options
25
30
  */
26
31
  async function start(options) {
27
32
  let threadReadyQueue = [];
@@ -33,7 +38,6 @@ async function start(options) {
33
38
  const useHttps = options.https;
34
39
  let elmMakeRunning = true;
35
40
 
36
- const serve = serveStatic("public/", { index: false });
37
41
  fs.mkdirSync(".elm-pages/cache", { recursive: true });
38
42
  const serveCachedFiles = serveStatic(".elm-pages/cache", { index: false });
39
43
  const generatedFilesDirectory = "elm-stuff/elm-pages/generated-files";
@@ -59,8 +63,18 @@ async function start(options) {
59
63
  console.error(error);
60
64
  process.exit(1);
61
65
  }
62
- let clientElmMakeProcess = compileElmForBrowser();
63
- let pendingCliCompile = compileCliApp();
66
+ let clientElmMakeProcess = compileElmForBrowser(options);
67
+ let pendingCliCompile = compileCliApp(
68
+ options,
69
+ ".elm-pages/Main.elm",
70
+
71
+ path.join(process.cwd(), "elm-stuff/elm-pages/", "elm.js"),
72
+
73
+ // "elm.js",
74
+ "elm-stuff/elm-pages/",
75
+ path.join("elm-stuff/elm-pages/", "elm.js")
76
+ );
77
+
64
78
  watchElmSourceDirs(true);
65
79
 
66
80
  async function setup() {
@@ -101,26 +115,75 @@ async function start(options) {
101
115
  );
102
116
 
103
117
  watcher.add(sourceDirs);
104
- watcher.add("./public/*.css");
105
- watcher.add("./port-data-source.js");
106
118
  }
107
119
 
108
- async function compileCliApp() {
109
- await spawnElmMake(
110
- ".elm-pages/TemplateModulesBeta.elm",
111
- "elm.js",
112
- "elm-stuff/elm-pages/"
113
- );
114
- }
120
+ const viteConfig = await import(
121
+ path.join(process.cwd(), "elm-pages.config.mjs")
122
+ )
123
+ .then(async (elmPagesConfig) => {
124
+ return elmPagesConfig.default.vite || {};
125
+ })
126
+ .catch((error) => {
127
+ console.warn(
128
+ kleur.yellow(
129
+ "No `elm-pages.config.mjs` file found. Using default config."
130
+ )
131
+ );
132
+ return {};
133
+ });
134
+ const vite = await createViteServer({
135
+ server: { middlewareMode: "ssr", base: options.base, port: options.port },
136
+ configFile: false,
137
+ root: process.cwd(),
138
+ base: options.base,
139
+ ...viteConfig,
140
+ });
141
+ esbuild
142
+ .build({
143
+ entryPoints: ["./port-data-source"],
144
+ platform: "node",
145
+ assetNames: "[name]-[hash]",
146
+ chunkNames: "chunks/[name]-[hash]",
147
+ outExtension: { ".js": ".js" },
148
+ metafile: true,
149
+ bundle: true,
150
+ watch: true,
151
+ logLevel: "error",
152
+
153
+ outdir: ".elm-pages/compiled-ports",
154
+ entryNames: "[dir]/[name]-[hash]",
155
+
156
+ plugins: [
157
+ {
158
+ name: "example",
159
+ setup(build) {
160
+ build.onEnd((result) => {
161
+ global.portsFilePath = Object.keys(result.metafile.outputs)[0];
162
+
163
+ clients.forEach((client) => {
164
+ client.response.write(`data: content.dat\n\n`);
165
+ });
166
+ });
167
+ },
168
+ },
169
+ ],
170
+ })
171
+ .then((result) => {
172
+ console.log("Watching port-data-source...");
173
+ })
174
+ .catch((error) => {
175
+ console.error("Failed to start port-data-source watcher", error);
176
+ });
115
177
 
116
178
  const app = connect()
117
179
  .use(timeMiddleware())
118
- .use(baseMiddleware(options.base))
180
+ .use(serveStaticCode)
119
181
  .use(awaitElmMiddleware)
182
+ .use(baseMiddleware(options.base))
120
183
  .use(serveCachedFiles)
121
- .use(serveStaticCode)
122
- .use(serve)
184
+ .use(vite.middlewares)
123
185
  .use(processRequest);
186
+
124
187
  if (useHttps) {
125
188
  const ssl = await devcert.certificateFor("localhost");
126
189
  https.createServer(ssl, app).listen(port);
@@ -143,10 +206,6 @@ async function start(options) {
143
206
  watcher.on("all", async function (eventName, pathThatChanged) {
144
207
  if (pathThatChanged === "elm.json") {
145
208
  watchElmSourceDirs(false);
146
- } else if (pathThatChanged.endsWith(".css")) {
147
- clients.forEach((client) => {
148
- client.response.write(`data: style.css\n\n`);
149
- });
150
209
  } else if (pathThatChanged.endsWith(".elm")) {
151
210
  if (elmMakeRunning) {
152
211
  } else {
@@ -154,8 +213,15 @@ async function start(options) {
154
213
  if (needToRerunCodegen(eventName, pathThatChanged)) {
155
214
  try {
156
215
  await codegen.generate(options.base);
157
- clientElmMakeProcess = compileElmForBrowser();
158
- pendingCliCompile = compileCliApp();
216
+ clientElmMakeProcess = compileElmForBrowser(options);
217
+ pendingCliCompile = compileCliApp(
218
+ options,
219
+ ".elm-pages/Main.elm",
220
+ path.join(process.cwd(), "elm-stuff/elm-pages/", "elm.js"),
221
+ // "elm.js",
222
+ "elm-stuff/elm-pages/",
223
+ path.join("elm-stuff/elm-pages/", "elm.js")
224
+ );
159
225
 
160
226
  Promise.all([clientElmMakeProcess, pendingCliCompile])
161
227
  .then(() => {
@@ -180,8 +246,17 @@ async function start(options) {
180
246
  clientElmMakeProcess = Promise.reject(errorJson);
181
247
  pendingCliCompile = Promise.reject(errorJson);
182
248
  } else {
183
- clientElmMakeProcess = compileElmForBrowser();
184
- pendingCliCompile = compileCliApp();
249
+ clientElmMakeProcess = compileElmForBrowser(options);
250
+ pendingCliCompile = compileCliApp(
251
+ options,
252
+ ".elm-pages/Main.elm",
253
+
254
+ path.join(process.cwd(), "elm-stuff/elm-pages/", "elm.js"),
255
+
256
+ // "elm.js",
257
+ "elm-stuff/elm-pages/",
258
+ path.join("elm-stuff/elm-pages/", "elm.js")
259
+ );
185
260
  }
186
261
 
187
262
  Promise.all([clientElmMakeProcess, pendingCliCompile])
@@ -192,7 +267,7 @@ async function start(options) {
192
267
  elmMakeRunning = false;
193
268
  });
194
269
  clients.forEach((client) => {
195
- client.response.write(`data: content.json\n\n`);
270
+ client.response.write(`data: content.dat\n\n`);
196
271
  });
197
272
  }
198
273
  } else {
@@ -214,7 +289,7 @@ async function start(options) {
214
289
  // }
215
290
  // });
216
291
  clients.forEach((client) => {
217
- client.response.write(`data: content.json\n\n`);
292
+ client.response.write(`data: content.dat\n\n`);
218
293
  });
219
294
  }
220
295
  });
@@ -242,7 +317,7 @@ async function start(options) {
242
317
  function needToRerunCodegen(eventName, pathThatChanged) {
243
318
  return (
244
319
  (eventName === "add" || eventName === "unlink") &&
245
- pathThatChanged.match(/src\/Page\/.*\.elm/)
320
+ pathThatChanged.match(/app\/Route\/.*\.elm/)
246
321
  );
247
322
  }
248
323
 
@@ -250,12 +325,12 @@ async function start(options) {
250
325
  * @param {string} pathname
251
326
  * @param {((value: any) => any) | null | undefined} onOk
252
327
  * @param {((reason: any) => PromiseLike<never>) | null | undefined} onErr
328
+ * @param {{ method: string; hostname: string; query: string; headers: Object; host: string; pathname: string; port: string; protocol: string; rawUrl: string; }} serverRequest
253
329
  */
254
- function runRenderThread(pathname, onOk, onErr) {
330
+ function runRenderThread(serverRequest, pathname, onOk, onErr) {
255
331
  let cleanUpThread = () => {};
256
332
  return new Promise(async (resolve, reject) => {
257
333
  const readyThread = await waitForThread();
258
- console.log(`Rendering ${pathname}`, readyThread.worker.threadId);
259
334
  cleanUpThread = () => {
260
335
  cleanUp(readyThread);
261
336
  };
@@ -264,6 +339,8 @@ async function start(options) {
264
339
  readyThread.worker.postMessage({
265
340
  mode: "dev-server",
266
341
  pathname,
342
+ serverRequest,
343
+ portsFilePath: global.portsFilePath,
267
344
  });
268
345
  readyThread.worker.on("message", (message) => {
269
346
  if (message.tag === "done") {
@@ -308,26 +385,31 @@ async function start(options) {
308
385
  } catch (error) {
309
386
  let isImplicitContractError = false;
310
387
  try {
311
- isImplicitContractError = JSON.parse(error).errors.some(
312
- (errorItem) => errorItem.name === "TemplateModulesBeta"
313
- );
388
+ let jsonParsed = JSON.parse(error);
389
+ isImplicitContractError =
390
+ jsonParsed.errors &&
391
+ jsonParsed.errors.some((errorItem) => errorItem.name === "Main");
314
392
  } catch (unexpectedError) {
315
393
  console.log("Unexpected error", unexpectedError);
316
394
  }
317
395
  if (isImplicitContractError) {
318
396
  const reviewOutput = await runElmReview();
319
- console.log(restoreColor(JSON.parse(reviewOutput)));
397
+ console.log(restoreColorSafe(reviewOutput));
320
398
 
321
- if (req.url.includes("content.json")) {
399
+ if (req.url.includes("content.dat")) {
322
400
  res.writeHead(500, { "Content-Type": "application/json" });
323
- res.end(reviewOutput);
401
+ if (emptyReviewError(reviewOutput)) {
402
+ res.end(error);
403
+ } else {
404
+ res.end(reviewOutput);
405
+ }
324
406
  } else {
325
407
  res.writeHead(500, { "Content-Type": "text/html" });
326
408
  res.end(errorHtml());
327
409
  }
328
410
  } else {
329
- console.log(restoreColor(JSON.parse(error)));
330
- if (req.url.includes("content.json")) {
411
+ console.log(restoreColorSafe(error));
412
+ if (req.url.includes("content.dat")) {
331
413
  res.writeHead(500, { "Content-Type": "application/json" });
332
414
  res.end(error);
333
415
  } else {
@@ -338,51 +420,161 @@ async function start(options) {
338
420
  return;
339
421
  }
340
422
 
341
- await runRenderThread(
342
- pathname,
343
- function (renderResult) {
344
- const is404 = renderResult.is404;
345
- switch (renderResult.kind) {
346
- case "json": {
347
- res.writeHead(is404 ? 404 : 200, {
348
- "Content-Type": "application/json",
349
- });
350
- res.end(renderResult.contentJson);
351
- break;
352
- }
353
- case "html": {
354
- res.writeHead(is404 ? 404 : 200, {
355
- "Content-Type": "text/html",
356
- });
357
- res.end(renderResult.htmlString);
358
- break;
423
+ const requestTime = new Date();
424
+ /** @type {string | null} */
425
+ let body = null;
426
+
427
+ req.on("data", function (data) {
428
+ if (!body) {
429
+ body = "";
430
+ }
431
+ body += data;
432
+ });
433
+
434
+ req.on("end", async function () {
435
+ // TODO run render directly instead of in worker thread
436
+ await runRenderThread(
437
+ await reqToJson(req, body, requestTime),
438
+ pathname,
439
+ async function (renderResult) {
440
+ const is404 = renderResult.is404;
441
+ switch (renderResult.kind) {
442
+ case "bytes": {
443
+ res.writeHead(is404 ? 404 : renderResult.statusCode, {
444
+ "Content-Type": "application/octet-stream",
445
+ ...renderResult.headers,
446
+ });
447
+ res.end(Buffer.from(renderResult.contentDatPayload.buffer));
448
+ break;
449
+ }
450
+ case "json": {
451
+ // TODO is this used anymore? I Think it's a dead code path and can be deleted
452
+ res.writeHead(is404 ? 404 : renderResult.statusCode, {
453
+ "Content-Type": "application/json",
454
+ ...renderResult.headers,
455
+ });
456
+ // is contentJson used any more? I think it can safely be deleted
457
+ res.end(renderResult.contentJson);
458
+ break;
459
+ }
460
+ case "html": {
461
+ try {
462
+ const template =
463
+ /*html*/
464
+ `<!DOCTYPE html>
465
+ <!-- ROOT --><html lang="en">
466
+ <head>
467
+ <script src="/hmr.js" type="text/javascript"></script>
468
+ <script src="/elm.js" type="text/javascript"></script>
469
+ <link rel="stylesheet" href="/style.css">
470
+ <link rel="stylesheet" href="/dev-style.css">
471
+ <script src="/elm-pages.js" type="module"></script>
472
+ <meta charset="UTF-8" />
473
+ <meta name="viewport" content="width=device-width,initial-scale=1" />
474
+ <title><!-- PLACEHOLDER_TITLE --></title>
475
+ <meta name="generator" content="elm-pages v${cliVersion}" />
476
+ <meta name="mobile-web-app-capable" content="yes" />
477
+ <meta name="theme-color" content="#ffffff" />
478
+ <meta name="apple-mobile-web-app-capable" content="yes" />
479
+ <meta
480
+ name="apple-mobile-web-app-status-bar-style"
481
+ content="black-translucent"
482
+ />
483
+ <!-- PLACEHOLDER_HEAD_AND_DATA -->
484
+ </head>
485
+ <body>
486
+ <div data-url="" display="none"></div>
487
+ <!-- PLACEHOLDER_HTML -->
488
+ </body>
489
+ </html>
490
+ `;
491
+ const processedTemplate = await vite.transformIndexHtml(
492
+ req.originalUrl,
493
+ template
494
+ );
495
+ const info = renderResult.htmlString;
496
+ const renderedHtml = processedTemplate
497
+ .replace(
498
+ /<!--\s*PLACEHOLDER_HEAD_AND_DATA\s*-->/,
499
+ `${info.headTags}
500
+ <script id="__ELM_PAGES_BYTES_DATA__" type="application/octet-stream">${info.bytesData}</script>`
501
+ )
502
+ .replace(/<!--\s*PLACEHOLDER_TITLE\s*-->/, info.title)
503
+ .replace(/<!--\s*PLACEHOLDER_HTML\s* -->/, info.html)
504
+ .replace(
505
+ /<!-- ROOT -->\S*<html lang="en">/m,
506
+ info.rootElement
507
+ );
508
+ res.writeHead(renderResult.statusCode, {
509
+ "Content-Type": "text/html",
510
+ ...renderResult.headers,
511
+ });
512
+ res.end(renderedHtml);
513
+ } catch (e) {
514
+ vite.ssrFixStacktrace(e);
515
+ next(e);
516
+ }
517
+ break;
518
+ }
519
+ case "api-response": {
520
+ if (renderResult.body.kind === "server-response") {
521
+ const serverResponse = renderResult.body;
522
+ res.writeHead(
523
+ serverResponse.statusCode,
524
+ serverResponse.headers
525
+ );
526
+ res.end(serverResponse.body);
527
+ } else if (renderResult.body.kind === "static-file") {
528
+ let mimeType = serveStatic.mime.lookup(pathname || "text/html");
529
+ mimeType =
530
+ mimeType === "application/octet-stream"
531
+ ? "text/html"
532
+ : mimeType;
533
+ res.writeHead(renderResult.statusCode, {
534
+ "Content-Type": mimeType,
535
+ });
536
+ res.end(renderResult.body.body);
537
+ // TODO - if route is static, write file to api-route-cache/ directory
538
+ // TODO - get 404 or other status code from elm-pages renderer
539
+ } else {
540
+ throw (
541
+ "Unexpected api-response renderResult: " +
542
+ JSON.stringify(renderResult, null, 2)
543
+ );
544
+ }
545
+ break;
546
+ }
547
+ default: {
548
+ console.dir(renderResult);
549
+ throw "Unexpected renderResult kind: " + renderResult.kind;
550
+ }
359
551
  }
360
- case "api-response": {
361
- let mimeType = serveStatic.mime.lookup(pathname || "text/html");
362
- mimeType =
363
- mimeType === "application/octet-stream" ? "text/html" : mimeType;
364
- res.writeHead(renderResult.statusCode, {
365
- "Content-Type": mimeType,
366
- });
367
- res.end(renderResult.body);
368
- // TODO - if route is static, write file to api-route-cache/ directory
369
- // TODO - get 404 or other status code from elm-pages renderer
370
- break;
552
+ },
553
+
554
+ function (error) {
555
+ console.log(restoreColorSafe(error));
556
+ if (req.url.includes("content.dat")) {
557
+ res.writeHead(500, { "Content-Type": "application/json" });
558
+ res.end(JSON.stringify(error));
559
+ } else {
560
+ res.writeHead(500, { "Content-Type": "text/html" });
561
+ res.end(errorHtml());
371
562
  }
372
563
  }
373
- },
564
+ );
565
+ });
566
+ }
374
567
 
375
- function (error) {
376
- console.log(restoreColor(error));
377
- if (req.url.includes("content.json")) {
378
- res.writeHead(500, { "Content-Type": "application/json" });
379
- res.end(JSON.stringify(error));
380
- } else {
381
- res.writeHead(500, { "Content-Type": "text/html" });
382
- res.end(errorHtml());
383
- }
384
- }
385
- );
568
+ /**
569
+ * @param {string} reviewReportJsonString
570
+ */
571
+ function emptyReviewError(reviewReportJsonString) {
572
+ try {
573
+ return JSON.parse(reviewReportJsonString).errors.length === 0;
574
+ } catch (e) {
575
+ console.trace("problem with format in reviewReportJsonString", e);
576
+ return true;
577
+ }
386
578
  }
387
579
 
388
580
  async function awaitElmMiddleware(req, res, next) {
@@ -495,23 +687,12 @@ function errorHtml() {
495
687
  return `<!DOCTYPE html>
496
688
  <html lang="en">
497
689
  <head>
498
- <link rel="stylesheet" href="/style.css"></link>
499
- <style>
500
- @keyframes lds-default {
501
- 0%, 20%, 80%, 100% {
502
- transform: scale(1);
503
- }
504
- 50% {
505
- transform: scale(1.5);
506
- }
507
- }
508
- </style>
509
- <!--<link rel="preload" href="/elm-pages.js" as="script"> -->
690
+ <link rel="stylesheet" href="/style.css">
691
+ <link rel="stylesheet" href="/dev-style.css">
510
692
  <link rel="preload" href="/index.js" as="script">
511
693
  <!--<link rel="preload" href="/elm.js" as="script">-->
512
694
  <script src="/hmr.js" type="text/javascript"></script>
513
- <!--<script defer="defer" src="/elm.js" type="text/javascript"></script>-->
514
- <!--<script defer="defer" src="/elm-pages.js" type="module"></script>-->
695
+ <script src="/elm.js" type="text/javascript"></script>
515
696
  <meta charset="UTF-8">
516
697
  <meta name="viewport" content="width=device-width,initial-scale=1">
517
698
  <script>
@@ -536,9 +717,9 @@ function errorHtml() {
536
717
 
537
718
  async function ensureRequiredExecutables() {
538
719
  try {
539
- await which("elm");
720
+ await which("lamdera");
540
721
  } catch (error) {
541
- throw "I couldn't find elm on the PATH. Please ensure it's installed, either globally, or locally. If it's installed locally, ensure you're running through an NPM script or with npx so the PATH is configured to include it.";
722
+ throw "I couldn't find lamdera on the PATH. Please ensure it's installed, either globally, or locally. If it's installed locally, ensure you're running through an NPM script or with npx so the PATH is configured to include it.";
542
723
  }
543
724
  try {
544
725
  await which("elm-review");
@@ -547,4 +728,80 @@ async function ensureRequiredExecutables() {
547
728
  }
548
729
  }
549
730
 
731
+ /**
732
+ * @param {http.IncomingMessage} req
733
+ * @param {string | null} body
734
+ * @param {Date} requestTime
735
+ */
736
+ function reqToJson(req, body, requestTime) {
737
+ return new Promise((resolve, reject) => {
738
+ if (
739
+ req.method === "POST" &&
740
+ req.headers["content-type"] &&
741
+ req.headers["content-type"].includes("multipart/form-data") &&
742
+ body
743
+ ) {
744
+ try {
745
+ const bb = busboy({
746
+ headers: req.headers,
747
+ });
748
+ let fields = {};
749
+
750
+ bb.on("file", (fieldname, file, info) => {
751
+ const { filename, encoding, mimeType } = info;
752
+
753
+ file.on("data", (data) => {
754
+ fields[fieldname] = {
755
+ filename,
756
+ mimeType,
757
+ body: data.toString(),
758
+ };
759
+ });
760
+ });
761
+
762
+ bb.on("field", (fieldName, value) => {
763
+ fields[fieldName] = value;
764
+ });
765
+
766
+ // TODO skip parsing JSON and form data body if busboy doesn't run
767
+ bb.on("close", () => {
768
+ resolve(toJsonHelper(req, body, requestTime, fields));
769
+ });
770
+ bb.write(body);
771
+ } catch (error) {
772
+ resolve(toJsonHelper(req, body, requestTime, null));
773
+ }
774
+ } else {
775
+ resolve(toJsonHelper(req, body, requestTime, null));
776
+ }
777
+ });
778
+ }
779
+
780
+ /**
781
+ * @param {http.IncomingMessage} req
782
+ * @param {string | null} body
783
+ * @param {Date} requestTime
784
+ * @param {Object | null} multiPartFormData
785
+ * @returns {{method: string; rawUrl: string; body: string?; }}
786
+ */
787
+ function toJsonHelper(req, body, requestTime, multiPartFormData) {
788
+ const url = new URL(req.url, `http://${req.headers.host}`);
789
+ return {
790
+ method: req.method,
791
+ headers: req.headers || {},
792
+ rawUrl: url.toString(),
793
+ body: body,
794
+ requestTime: Math.round(requestTime.getTime()),
795
+ multiPartFormData: multiPartFormData,
796
+ };
797
+ }
798
+ // TODO capture repeat entries into a list of values
799
+ // TODO have expect functions in Elm to handle expecting exactly one value, or getting first value only without failing if more
800
+ function paramsToObject(entries) {
801
+ const result = {};
802
+ for (const [key, value] of entries) {
803
+ result[key] = value;
804
+ }
805
+ return result;
806
+ }
550
807
  module.exports = { start };
@@ -3,7 +3,9 @@
3
3
  "summary": "elm-pages local generated module documentation",
4
4
  "version": "1.0.0",
5
5
  "exposed-modules": [
6
- "Page",
6
+ "RouteBuilder",
7
+ "View",
8
+ "Effect",
7
9
  "Route"
8
10
  ]
9
11
  }
@@ -0,0 +1,34 @@
1
+ const spawnCallback = require("cross-spawn").spawn;
2
+
3
+ function runElmCodegenInstall() {
4
+ return new Promise(async (resolve, reject) => {
5
+ const subprocess = spawnCallback(`elm-codegen`, ["install"], {
6
+ // ignore stdout
7
+ // stdio: ["inherit", "ignore", "inherit"],
8
+ // cwd: cwd,
9
+ });
10
+ // if (await fsHelpers.fileExists(outputPath)) {
11
+ // await fsPromises.unlink(outputPath, {
12
+ // force: true /* ignore errors if file doesn't exist */,
13
+ // });
14
+ // }
15
+ let commandOutput = "";
16
+
17
+ subprocess.stderr.on("data", function (data) {
18
+ commandOutput += data;
19
+ });
20
+ subprocess.on("error", function () {
21
+ reject(commandOutput);
22
+ });
23
+
24
+ subprocess.on("close", async (code) => {
25
+ if (code == 0) {
26
+ resolve();
27
+ } else {
28
+ reject(commandOutput);
29
+ }
30
+ });
31
+ });
32
+ }
33
+
34
+ module.exports = { runElmCodegenInstall };
@@ -2,6 +2,8 @@ function elmPagesUiFile() {
2
2
  return `module Pages exposing (builtAt)
3
3
 
4
4
  import Time
5
+ import Json.Decode
6
+ import Json.Encode
5
7
 
6
8
 
7
9
  builtAt : Time.Posix