elm-pages 2.1.11 → 3.0.0-beta.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 (136) hide show
  1. package/generator/review/elm-stuff/tests-0.19.1/elm-stuff/0.19.1/Pages-Internal-RoutePattern.elmi +0 -0
  2. package/generator/review/elm-stuff/tests-0.19.1/elm-stuff/0.19.1/Pages-Internal-RoutePattern.elmo +0 -0
  3. package/generator/review/elm-stuff/tests-0.19.1/elm-stuff/0.19.1/Pages-Review-NoContractViolations.elmi +0 -0
  4. package/generator/review/elm-stuff/tests-0.19.1/elm-stuff/0.19.1/Pages-Review-NoContractViolations.elmo +0 -0
  5. package/generator/review/elm-stuff/tests-0.19.1/elm-stuff/0.19.1/Pages-Review-NoContractViolationsTest.elmi +0 -0
  6. package/generator/review/elm-stuff/tests-0.19.1/elm-stuff/0.19.1/Pages-Review-NoContractViolationsTest.elmo +0 -0
  7. package/generator/review/elm-stuff/tests-0.19.1/elm-stuff/0.19.1/Reporter.elmi +0 -0
  8. package/generator/review/elm-stuff/tests-0.19.1/elm-stuff/0.19.1/Reporter.elmo +0 -0
  9. package/generator/review/elm-stuff/tests-0.19.1/elm-stuff/0.19.1/Runner.elmi +0 -0
  10. package/generator/review/elm-stuff/tests-0.19.1/elm-stuff/0.19.1/Runner.elmo +0 -0
  11. package/generator/review/elm-stuff/tests-0.19.1/elm-stuff/0.19.1/d.dat +0 -0
  12. package/generator/review/elm-stuff/tests-0.19.1/elm-stuff/0.19.1/i.dat +0 -0
  13. package/generator/{template/public/style.css → review/elm-stuff/tests-0.19.1/elm-stuff/0.19.1/lock} +0 -0
  14. package/generator/review/elm-stuff/tests-0.19.1/elm-stuff/0.19.1/o.dat +0 -0
  15. package/generator/review/elm-stuff/tests-0.19.1/elm.json +1 -0
  16. package/generator/review/elm-stuff/tests-0.19.1/js/Reporter.elm.js +6795 -0
  17. package/generator/review/elm-stuff/tests-0.19.1/js/Runner.elm.js +27617 -0
  18. package/generator/review/elm-stuff/tests-0.19.1/js/node_runner.js +110 -0
  19. package/generator/review/elm-stuff/tests-0.19.1/js/node_supervisor.js +187 -0
  20. package/generator/review/elm-stuff/tests-0.19.1/js/package.json +1 -0
  21. package/generator/review/elm-stuff/tests-0.19.1/src/Reporter.elm +26 -0
  22. package/generator/review/elm-stuff/tests-0.19.1/src/Runner.elm +62 -0
  23. package/generator/review/elm.json +13 -4
  24. package/{src → generator/review/src}/Pages/Review/NoContractViolations.elm +148 -148
  25. package/generator/review/tests/Pages/Review/NoContractViolationsTest.elm +331 -0
  26. package/generator/src/RouteBuilder.elm +420 -0
  27. package/generator/src/SharedTemplate.elm +4 -5
  28. package/generator/src/SiteConfig.elm +3 -9
  29. package/generator/src/build.js +308 -95
  30. package/generator/src/cli.js +103 -8
  31. package/generator/src/codegen.js +192 -35
  32. package/generator/src/compile-elm.js +183 -31
  33. package/generator/src/dev-server.js +353 -96
  34. package/generator/src/elm-application.json +3 -1
  35. package/generator/src/elm-codegen.js +34 -0
  36. package/generator/src/elm-file-constants.js +2 -0
  37. package/generator/src/error-formatter.js +20 -1
  38. package/generator/src/generate-template-module-connector.js +120 -924
  39. package/generator/src/hello.ts +5 -0
  40. package/generator/src/pre-render-html.js +58 -104
  41. package/generator/src/render-worker.js +27 -13
  42. package/generator/src/render.js +252 -197
  43. package/generator/src/request-cache-fs.js +18 -0
  44. package/generator/src/request-cache.js +128 -56
  45. package/generator/src/rewrite-client-elm-json.js +49 -0
  46. package/generator/src/route-codegen-helpers.js +62 -1
  47. package/generator/static-code/dev-style.css +22 -0
  48. package/generator/static-code/elm-pages.js +43 -39
  49. package/generator/static-code/hmr.js +98 -88
  50. package/generator/template/app/Api.elm +25 -0
  51. package/generator/template/app/ErrorPage.elm +38 -0
  52. package/generator/template/app/Route/Index.elm +87 -0
  53. package/generator/template/{src → app}/Shared.elm +34 -13
  54. package/generator/template/app/Site.elm +19 -0
  55. package/generator/template/{src → app}/View.elm +0 -0
  56. package/generator/template/elm-pages.config.mjs +5 -0
  57. package/generator/template/elm.json +1 -0
  58. package/generator/template/{public/index.js → index.ts} +7 -3
  59. package/generator/template/package.json +4 -4
  60. package/generator/template/public/favicon.ico +0 -0
  61. package/generator/template/public/images/icon-png.png +0 -0
  62. package/generator/template/src/.gitkeep +0 -0
  63. package/generator/template/style.css +4 -0
  64. package/package.json +30 -23
  65. package/src/ApiRoute.elm +176 -43
  66. package/src/BuildError.elm +10 -1
  67. package/src/CookieParser.elm +84 -0
  68. package/src/DataSource/Env.elm +38 -0
  69. package/src/DataSource/File.elm +27 -16
  70. package/src/DataSource/Glob.elm +126 -80
  71. package/src/DataSource/Http.elm +283 -304
  72. package/src/DataSource/Internal/Glob.elm +5 -21
  73. package/src/DataSource/Internal/Request.elm +25 -0
  74. package/src/DataSource/Port.elm +17 -14
  75. package/src/DataSource.elm +55 -318
  76. package/src/Form/Field.elm +717 -0
  77. package/src/Form/FieldStatus.elm +36 -0
  78. package/src/Form/FieldView.elm +417 -0
  79. package/src/Form/FormData.elm +22 -0
  80. package/src/Form/Validation.elm +391 -0
  81. package/src/Form/Value.elm +118 -0
  82. package/src/Form.elm +1683 -0
  83. package/src/FormData.elm +58 -0
  84. package/src/FormDecoder.elm +102 -0
  85. package/src/Head/Seo.elm +12 -4
  86. package/src/Head.elm +12 -2
  87. package/src/HtmlPrinter.elm +1 -1
  88. package/src/Internal/ApiRoute.elm +17 -4
  89. package/src/Internal/Request.elm +7 -0
  90. package/src/PageServerResponse.elm +68 -0
  91. package/src/Pages/ContentCache.elm +1 -229
  92. package/src/Pages/Fetcher.elm +58 -0
  93. package/src/Pages/FormState.elm +256 -0
  94. package/src/Pages/Generate.elm +800 -0
  95. package/src/Pages/Internal/Form.elm +17 -0
  96. package/src/Pages/Internal/NotFoundReason.elm +3 -55
  97. package/src/Pages/Internal/Platform/Cli.elm +777 -579
  98. package/src/Pages/Internal/Platform/Effect.elm +5 -5
  99. package/src/Pages/Internal/Platform/StaticResponses.elm +178 -394
  100. package/src/Pages/Internal/Platform/ToJsPayload.elm +24 -23
  101. package/src/Pages/Internal/Platform.elm +1244 -504
  102. package/src/Pages/Internal/ResponseSketch.elm +19 -0
  103. package/src/Pages/Internal/RoutePattern.elm +596 -45
  104. package/src/Pages/Manifest.elm +26 -0
  105. package/src/Pages/Msg.elm +79 -0
  106. package/src/Pages/ProgramConfig.elm +67 -14
  107. package/src/Pages/SiteConfig.elm +3 -6
  108. package/src/Pages/StaticHttp/Request.elm +4 -2
  109. package/src/Pages/StaticHttpRequest.elm +50 -215
  110. package/src/Pages/Transition.elm +70 -0
  111. package/src/Path.elm +1 -0
  112. package/src/Pattern.elm +98 -0
  113. package/src/RenderRequest.elm +2 -2
  114. package/src/RequestsAndPending.elm +111 -9
  115. package/src/Server/Request.elm +1253 -0
  116. package/src/Server/Response.elm +292 -0
  117. package/src/Server/Session.elm +316 -0
  118. package/src/Server/SetCookie.elm +169 -0
  119. package/src/TerminalText.elm +1 -1
  120. package/src/Test/Html/Internal/ElmHtml/Markdown.elm +0 -1
  121. package/src/Test/Html/Internal/ElmHtml/ToString.elm +1 -1
  122. package/generator/src/Page.elm +0 -359
  123. package/generator/src/codegen-template-module.js +0 -183
  124. package/generator/src/elm-pages-js-minified.js +0 -1
  125. package/generator/template/src/Api.elm +0 -14
  126. package/generator/template/src/Page/Index.elm +0 -69
  127. package/generator/template/src/Site.elm +0 -41
  128. package/src/DataSource/ServerRequest.elm +0 -60
  129. package/src/Internal/OptimizedDecoder.elm +0 -18
  130. package/src/KeepOrDiscard.elm +0 -6
  131. package/src/OptimizedDecoder/Pipeline.elm +0 -335
  132. package/src/OptimizedDecoder.elm +0 -818
  133. package/src/Pages/Internal/ApplicationType.elm +0 -6
  134. package/src/Pages/Secrets.elm +0 -83
  135. package/src/Secrets.elm +0 -111
  136. package/src/SecretsDict.elm +0 -45
@@ -2,7 +2,7 @@ const fs = require("./dir-helpers.js");
2
2
  const fsPromises = require("fs").promises;
3
3
 
4
4
  const { runElmReview } = require("./compile-elm.js");
5
- const { restoreColor } = require("./error-formatter");
5
+ const { restoreColorSafe } = require("./error-formatter");
6
6
  const path = require("path");
7
7
  const spawnCallback = require("cross-spawn").spawn;
8
8
  const codegen = require("./codegen.js");
@@ -10,19 +10,29 @@ const terser = require("terser");
10
10
  const os = require("os");
11
11
  const { Worker, SHARE_ENV } = require("worker_threads");
12
12
  const { ensureDirSync } = require("./file-helpers.js");
13
+ const { generateClientFolder } = require("./codegen.js");
13
14
  const which = require("which");
15
+ const { build } = require("vite");
16
+ const preRenderHtml = require("./pre-render-html.js");
17
+ const esbuild = require("esbuild");
18
+ const { createHash } = require("crypto");
19
+
14
20
  let pool = [];
15
21
  let pagesReady;
22
+ let pagesErrored;
16
23
  let pages = new Promise((resolve, reject) => {
17
24
  pagesReady = resolve;
25
+ pagesErrored = reject;
18
26
  });
27
+ let pagesReadyCalled = false;
28
+ let activeWorkers = 0;
19
29
  let buildError = false;
20
30
 
21
- const DIR_PATH = path.join(process.cwd());
31
+ const DIR_PATH = process.cwd();
22
32
  const OUTPUT_FILE_NAME = "elm.js";
23
33
 
24
34
  process.on("unhandledRejection", (error) => {
25
- console.trace("Unhandled: ", error);
35
+ console.log(error);
26
36
  process.exitCode = 1;
27
37
  });
28
38
 
@@ -39,9 +49,9 @@ async function ensureRequiredDirs() {
39
49
 
40
50
  async function ensureRequiredExecutables() {
41
51
  try {
42
- await which("elm");
52
+ await which("lamdera");
43
53
  } catch (error) {
44
- 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.";
54
+ 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.";
45
55
  }
46
56
  try {
47
57
  await which("elm-optimize-level-2");
@@ -59,47 +69,153 @@ async function run(options) {
59
69
  try {
60
70
  await ensureRequiredDirs();
61
71
  await ensureRequiredExecutables();
62
- // since init/update are never called in pre-renders, and DataSource.Http is called using undici
72
+ // since init/update are never called in pre-renders, and DataSource.Http is called using pure NodeJS HTTP fetching
63
73
  // we can provide a fake HTTP instead of xhr2 (which is otherwise needed for Elm HTTP requests from Node)
64
- XMLHttpRequest = {};
65
74
 
66
75
  const generateCode = codegen.generate(options.base);
67
76
 
68
- const copyDone = copyAssets();
69
77
  await generateCode;
78
+ await fsPromises.writeFile(
79
+ "elm-stuff/elm-pages/index.html",
80
+ preRenderHtml.templateHtml()
81
+ );
70
82
 
71
- const compileCli = compileCliApp(options);
83
+ const config = await import(
84
+ path.join(process.cwd(), "elm-pages.config.mjs")
85
+ )
86
+ .then(async (elmPagesConfig) => {
87
+ return elmPagesConfig.default || {};
88
+ })
89
+ .catch((error) => {
90
+ console.warn(
91
+ "No `elm-pages.config.mjs` file found. Using default config."
92
+ );
93
+ return {};
94
+ });
95
+ const viteConfig = config.vite || {};
96
+
97
+ const buildComplete = build({
98
+ configFile: false,
99
+ root: process.cwd(),
100
+ base: options.base,
101
+ ssr: false,
102
+
103
+ build: {
104
+ manifest: true,
105
+ outDir: "dist",
106
+ rollupOptions: {
107
+ input: "elm-stuff/elm-pages/index.html",
108
+ },
109
+ },
110
+ ...viteConfig,
111
+ });
72
112
  const compileClientDone = compileElm(options);
73
- await compileCli;
74
- const cliDone = runCli(options);
113
+ await buildComplete;
114
+ await compileClientDone;
115
+ const fullOutputPath = path.join(process.cwd(), `./dist/elm.js`);
116
+ const withoutExtension = path.join(process.cwd(), `./dist/elm`);
117
+ const browserElmHash = await fingerprintElmAsset(
118
+ fullOutputPath,
119
+ withoutExtension
120
+ );
121
+ const assetManifestPath = path.join(process.cwd(), "dist/manifest.json");
122
+ const manifest = require(assetManifestPath);
123
+ const indexTemplate = await fsPromises.readFile(
124
+ "dist/elm-stuff/elm-pages/index.html",
125
+ "utf-8"
126
+ );
127
+ const preloads = `<link rel="modulepreload" href="/${manifest["elm-stuff/elm-pages/index.html"]["file"]}" />`;
75
128
 
76
- await Promise.all([copyDone, cliDone, compileClientDone]);
77
- } catch (error) {
78
- buildError = true;
129
+ const processedIndexTemplate = indexTemplate
130
+ .replace("<!-- PLACEHOLDER_PRELOADS -->", preloads)
131
+ .replace(
132
+ '<script defer src="/elm.js" type="text/javascript"></script>',
133
+ `<script defer src="/elm.${browserElmHash}.js" type="text/javascript"></script>`
134
+ );
135
+ await fsPromises.writeFile("dist/template.html", processedIndexTemplate);
136
+ await fsPromises.unlink(assetManifestPath);
137
+
138
+ const portDataSourceCompiled = esbuild
139
+ .build({
140
+ entryPoints: ["./port-data-source"],
141
+ platform: "node",
142
+ outfile: ".elm-pages/compiled-ports/port-data-source.js",
143
+ assetNames: "[name]-[hash]",
144
+ chunkNames: "chunks/[name]-[hash]",
145
+ outExtension: { ".js": ".js" },
146
+ metafile: true,
147
+ bundle: true,
148
+ watch: false,
149
+ logLevel: "error",
150
+ })
151
+ .then((result) => {
152
+ global.portsFilePath = Object.keys(result.metafile.outputs)[0];
153
+ })
154
+ .catch((error) => {
155
+ if (
156
+ error.errors.length === 1 &&
157
+ error.errors[0].text.includes(
158
+ `Could not resolve "./port-data-source"`
159
+ )
160
+ ) {
161
+ console.warn("No port-data-source file found.");
162
+ } else {
163
+ console.error("Failed to load port-data-source file", error);
164
+ }
165
+ });
166
+ // TODO extract common code for compiling ports file?
167
+
168
+ XMLHttpRequest = {};
169
+ const compileCli = compileCliApp(options);
79
170
  try {
171
+ await compileCli;
172
+ await compileClientDone;
173
+ } catch (cliError) {
174
+ // TODO make sure not to print duplicate error output if cleaner review output is printed
175
+ console.error(cliError);
80
176
  const reviewOutput = JSON.parse(await runElmReview());
81
177
  const isParsingError = reviewOutput.errors.some((reviewError) => {
82
178
  return reviewError.errors.some((item) => item.rule === "ParsingError");
83
179
  });
84
180
  if (isParsingError) {
85
- console.error(error);
181
+ console.error(cliError);
86
182
  } else {
87
- console.error(restoreColor(reviewOutput));
183
+ console.error(restoreColorSafe(reviewOutput));
88
184
  }
89
185
  process.exit(1);
90
- } catch (noElmReviewErrors) {
186
+ }
187
+ await portDataSourceCompiled;
188
+ const cliDone = runCli(options);
189
+ await cliDone;
190
+
191
+ await fsPromises.rename(
192
+ "dist/____elm-pages-internal____/404/index.html",
193
+ "dist/404.html"
194
+ );
195
+ await runAdapter(
196
+ config.adapter ||
197
+ function () {
198
+ console.log(
199
+ "No adapter configured in elm-pages.config.mjs. Skipping adapter step."
200
+ );
201
+ },
202
+ processedIndexTemplate
203
+ );
204
+ } catch (error) {
205
+ if (error) {
91
206
  console.error(error);
92
- } finally {
93
- process.exit(1);
94
207
  }
208
+ buildError = true;
209
+ process.exitCode = 1;
95
210
  }
96
211
  }
97
212
 
98
213
  /**
99
214
  * @param {string} basePath
100
215
  */
101
- function initWorker(basePath) {
216
+ function initWorker(basePath, whenDone) {
102
217
  return new Promise((resolve, reject) => {
218
+ activeWorkers += 1;
103
219
  let newWorker = {
104
220
  worker: new Worker(path.join(__dirname, "./render-worker.js"), {
105
221
  env: SHARE_ENV,
@@ -109,21 +225,32 @@ function initWorker(basePath) {
109
225
  newWorker.worker.once("online", () => {
110
226
  newWorker.worker.on("message", (message) => {
111
227
  if (message.tag === "all-paths") {
112
- pagesReady(JSON.parse(message.data));
228
+ pagesReadyCalled = true;
229
+ pagesReady(
230
+ ["/____elm-pages-internal____/404"].concat(JSON.parse(message.data))
231
+ );
113
232
  } else if (message.tag === "error") {
114
233
  process.exitCode = 1;
115
- console.error(restoreColor(message.data));
116
- buildNextPage(newWorker);
234
+ console.error(restoreColorSafe(message.data));
235
+ if (!pagesReadyCalled) {
236
+ // when there is a build error while resolving all-paths, we don't know which pages to build so we need to short-circuit
237
+ // and give an error instead of trying to build the remaining pages to show as many errors as possible
238
+ pagesReady([]);
239
+ reject(message.data);
240
+ }
241
+ buildError = true;
242
+ buildNextPage(newWorker, whenDone);
117
243
  } else if (message.tag === "done") {
118
- buildNextPage(newWorker);
244
+ buildNextPage(newWorker, whenDone);
119
245
  } else {
120
246
  throw `Unhandled tag ${message.tag}`;
121
247
  }
122
248
  });
123
249
  newWorker.worker.on("error", (error) => {
124
250
  console.error("Unhandled worker exception", error);
251
+ buildError = true;
125
252
  process.exitCode = 1;
126
- buildNextPage(newWorker);
253
+ buildNextPage(newWorker, whenDone);
127
254
  });
128
255
  resolve(newWorker);
129
256
  });
@@ -134,57 +261,94 @@ function initWorker(basePath) {
134
261
  */
135
262
  function prepareStaticPathsNew(thread) {
136
263
  thread.worker.postMessage({
264
+ portsFilePath: global.portsFilePath,
137
265
  mode: "build",
138
266
  tag: "render",
139
267
  pathname: "/all-paths.json",
140
268
  });
141
269
  }
142
270
 
143
- async function buildNextPage(thread) {
271
+ async function buildNextPage(thread, allComplete) {
144
272
  let nextPage = (await pages).pop();
145
273
  if (nextPage) {
146
274
  thread.worker.postMessage({
275
+ portsFilePath: global.portsFilePath,
147
276
  mode: "build",
148
277
  tag: "render",
149
278
  pathname: nextPage,
150
279
  });
151
280
  } else {
152
281
  thread.worker.terminate();
282
+ activeWorkers -= 1;
283
+ allComplete();
153
284
  }
154
285
  }
155
286
 
156
- async function runCli(options) {
157
- const cpuCount = os.cpus().length;
158
- console.log("Threads: ", cpuCount);
159
-
160
- const getPathsWorker = initWorker(options.base);
161
- getPathsWorker.then(prepareStaticPathsNew);
162
- const threadsToCreate = Math.max(1, cpuCount / 2 - 1);
163
- pool.push(getPathsWorker);
164
- for (let index = 0; index < threadsToCreate - 1; index++) {
165
- pool.push(initWorker(options.base));
166
- }
167
- pool.forEach((threadPromise) => {
168
- threadPromise.then(buildNextPage);
287
+ function runCli(options) {
288
+ return new Promise((resolve, reject) => {
289
+ const whenDone = () => {
290
+ if (activeWorkers === 0) {
291
+ // wait for the remaining tasks in the pool to complete once the pages queue is emptied
292
+ Promise.all(pool).then((value) => {
293
+ if (buildError) {
294
+ reject();
295
+ } else {
296
+ resolve(value);
297
+ }
298
+ });
299
+ }
300
+ };
301
+ const cpuCount = os.cpus().length;
302
+ // const cpuCount = 1;
303
+ console.log("Threads: ", cpuCount);
304
+
305
+ const getPathsWorker = initWorker(options.base, whenDone);
306
+ getPathsWorker.then(prepareStaticPathsNew);
307
+ const threadsToCreate = Math.max(1, cpuCount - 1);
308
+ pool.push(getPathsWorker);
309
+ for (let index = 0; index < threadsToCreate - 1; index++) {
310
+ pool.push(initWorker(options.base, whenDone));
311
+ }
312
+ pool.forEach((threadPromise) => {
313
+ threadPromise.then((thread) => buildNextPage(thread, whenDone));
314
+ });
169
315
  });
170
316
  }
171
317
 
172
318
  async function compileElm(options) {
173
- const outputPath = `dist/elm.js`;
174
- const fullOutputPath = path.join(process.cwd(), `dist/elm.js`);
175
- await spawnElmMake(options, ".elm-pages/TemplateModulesBeta.elm", outputPath);
319
+ ensureDirSync("dist");
320
+ const fullOutputPath = path.join(process.cwd(), `./dist/elm.js`);
321
+ await generateClientFolder(options.base);
322
+ await spawnElmMake(
323
+ options.debug ? "debug" : "optimize",
324
+ options,
325
+ ".elm-pages/Main.elm",
326
+ fullOutputPath,
327
+ path.join(process.cwd(), "./elm-stuff/elm-pages/client")
328
+ );
176
329
 
177
330
  if (!options.debug) {
178
331
  await runTerser(fullOutputPath);
179
332
  }
180
333
  }
181
334
 
182
- function elmOptimizeLevel2(elmEntrypointPath, outputPath, cwd) {
335
+ async function fingerprintElmAsset(fullOutputPath, withoutExtension) {
336
+ const fileHash = await fsPromises
337
+ .readFile(fullOutputPath, "utf8")
338
+ .then(getAssetHash);
339
+ await fsPromises.copyFile(
340
+ fullOutputPath,
341
+ `${withoutExtension}.${fileHash}.js`
342
+ );
343
+ return fileHash;
344
+ }
345
+
346
+ function elmOptimizeLevel2(outputPath, cwd) {
183
347
  return new Promise((resolve, reject) => {
184
- const fullOutputPath = cwd ? path.join(cwd, outputPath) : outputPath;
348
+ const optimizedOutputPath = outputPath + ".opt";
185
349
  const subprocess = spawnCallback(
186
350
  `elm-optimize-level-2`,
187
- [elmEntrypointPath, "--output", outputPath],
351
+ [outputPath, "--output", optimizedOutputPath],
188
352
  {
189
353
  // ignore stdout
190
354
  // stdio: ["inherit", "ignore", "inherit"],
@@ -199,17 +363,16 @@ function elmOptimizeLevel2(elmEntrypointPath, outputPath, cwd) {
199
363
  });
200
364
 
201
365
  subprocess.on("close", async (code) => {
202
- if (
203
- code === 0 &&
204
- commandOutput === "" &&
205
- (await fs.fileExists(fullOutputPath))
206
- ) {
366
+ if (code === 0) {
367
+ await fs.copyFile(optimizedOutputPath, outputPath);
207
368
  resolve();
208
369
  } else {
209
370
  if (!buildError) {
210
371
  buildError = true;
211
372
  process.exitCode = 1;
212
- reject(commandOutput);
373
+ reject(
374
+ `I encountered an error when running elm-optimize-level-2:\n\n ${commandOutput}`
375
+ );
213
376
  } else {
214
377
  // avoid unhandled error printing duplicate message, let process.exit in top loop take over
215
378
  }
@@ -218,29 +381,66 @@ function elmOptimizeLevel2(elmEntrypointPath, outputPath, cwd) {
218
381
  });
219
382
  }
220
383
 
384
+ /** @typedef {"debug" | "optimize" | "default"} CompileMode */
385
+
221
386
  /**
387
+ * @param {CompileMode} mode
222
388
  * @param {string} elmEntrypointPath
223
389
  * @param {string} outputPath
224
390
  * @param {string | undefined} cwd
225
391
  */
226
- async function spawnElmMake(options, elmEntrypointPath, outputPath, cwd) {
227
- if (options.debug) {
228
- await runElmMake(elmEntrypointPath, outputPath, cwd);
392
+ async function spawnElmMake(mode, options, elmEntrypointPath, outputPath, cwd) {
393
+ await runElmMake(mode, options, elmEntrypointPath, outputPath, cwd);
394
+ if (mode === "optimize") {
395
+ await elmOptimizeLevel2(outputPath, cwd);
396
+ }
397
+ await fsPromises.writeFile(
398
+ outputPath,
399
+ (
400
+ await fsPromises.readFile(outputPath, "utf-8")
401
+ ).replace(
402
+ /return \$elm\$json\$Json\$Encode\$string\(.REPLACE_ME_WITH_FORM_TO_STRING.\)/g,
403
+ "function appendSubmitter (myFormData, event) { event.submitter && event.submitter.name && event.submitter.name.length > 0 ? myFormData.append(event.submitter.name, event.submitter.value) : myFormData; return myFormData }; return " +
404
+ (options.debug
405
+ ? "_Json_wrap(Array.from(appendSubmitter(new FormData(_Json_unwrap(event).target), _Json_unwrap(event))))"
406
+ : "Array.from(new FormData(event.target))")
407
+ )
408
+ );
409
+ }
410
+
411
+ function getAssetHash(content) {
412
+ return createHash("sha256").update(content).digest("hex").slice(0, 8);
413
+ }
414
+
415
+ /**
416
+ * @param {CompileMode} mode
417
+ */
418
+ function modeToOptions(mode) {
419
+ if (mode === "debug") {
420
+ return ["--debug"];
421
+ } else if (mode === "optimize") {
422
+ return ["--optimize"];
229
423
  } else {
230
- await elmOptimizeLevel2(elmEntrypointPath, outputPath, cwd);
424
+ return [];
231
425
  }
232
426
  }
233
427
 
234
- function runElmMake(elmEntrypointPath, outputPath, cwd) {
428
+ /**
429
+ * @param {CompileMode} mode
430
+ * @param {string} elmEntrypointPath
431
+ * @param {string} outputPath
432
+ * @param {string | undefined} cwd
433
+ */
434
+ function runElmMake(mode, options, elmEntrypointPath, outputPath, cwd) {
235
435
  return new Promise(async (resolve, reject) => {
236
436
  const subprocess = spawnCallback(
237
- `elm`,
437
+ `lamdera`,
238
438
  [
239
439
  "make",
240
440
  elmEntrypointPath,
241
441
  "--output",
242
442
  outputPath,
243
- "--debug",
443
+ ...modeToOptions(mode),
244
444
  "--report",
245
445
  "json",
246
446
  ],
@@ -251,9 +451,8 @@ function runElmMake(elmEntrypointPath, outputPath, cwd) {
251
451
  cwd: cwd,
252
452
  }
253
453
  );
254
- const fullOutputPath = cwd ? path.join(cwd, outputPath) : outputPath;
255
- if (await fs.fileExists(fullOutputPath)) {
256
- await fsPromises.unlink(fullOutputPath, {
454
+ if (await fs.fileExists(outputPath)) {
455
+ await fsPromises.unlink(outputPath, {
257
456
  force: true /* ignore errors if file doesn't exist */,
258
457
  });
259
458
  }
@@ -262,15 +461,25 @@ function runElmMake(elmEntrypointPath, outputPath, cwd) {
262
461
  subprocess.stderr.on("data", function (data) {
263
462
  commandOutput += data;
264
463
  });
464
+ subprocess.on("error", function () {
465
+ reject(commandOutput);
466
+ });
265
467
 
266
468
  subprocess.on("close", async (code) => {
267
- if (code == 0 && (await fs.fileExists(fullOutputPath))) {
469
+ if (
470
+ code == 0 &&
471
+ (await fs.fileExists(outputPath)) &&
472
+ commandOutput === ""
473
+ ) {
268
474
  resolve();
269
475
  } else {
270
476
  if (!buildError) {
271
477
  buildError = true;
272
- process.exitCode = 1;
273
- reject(restoreColor(JSON.parse(commandOutput)));
478
+ try {
479
+ reject(restoreColorSafe(commandOutput));
480
+ } catch (error) {
481
+ reject(commandOutput);
482
+ }
274
483
  } else {
275
484
  // avoid unhandled error printing duplicate message, let process.exit in top loop take over
276
485
  }
@@ -325,22 +534,14 @@ async function runTerser(filePath) {
325
534
  }
326
535
  }
327
536
 
328
- async function copyAssets() {
329
- await fsPromises.writeFile(
330
- "dist/elm-pages.js",
331
- await fsPromises.readFile(
332
- path.join(__dirname, "../static-code/elm-pages.js")
333
- )
334
- );
335
- fs.copyDirFlat("public", "dist");
336
- }
337
-
338
537
  async function compileCliApp(options) {
339
538
  await spawnElmMake(
539
+ // TODO should be --optimize, but there seems to be an issue with the html to JSON with --optimize
540
+ options.debug ? "debug" : "optimize",
340
541
  options,
341
- ".elm-pages/TemplateModulesBeta.elm",
342
- "elm.js",
343
- "./elm-stuff/elm-pages"
542
+ path.join(process.cwd(), "elm-stuff/elm-pages/.elm-pages/Main.elm"),
543
+ path.join(process.cwd(), "elm-stuff/elm-pages/elm.js"),
544
+ path.join(process.cwd(), "elm-stuff/elm-pages")
344
545
  );
345
546
 
346
547
  const elmFileContent = await fsPromises.readFile(ELM_FILE_PATH, "utf-8");
@@ -360,7 +561,9 @@ async function compileCliApp(options) {
360
561
  }
361
562
 
362
563
  function forceThunks(vNode) {
363
- if (typeof vNode !== "undefined" && vNode.$ === "#2") {
564
+ if ( (typeof vNode !== "undefined" && vNode.$ === "#2") // normal/debug mode
565
+ || (typeof vNode !== "undefined" && typeof vNode.$ === "undefined" && typeof vNode.a == "string" && typeof vNode.b == "object" ) // optimize mode
566
+ ) {
364
567
  // This is a tuple (the kids : List (String, Html) field of a Keyed node); recurse into the right side of the tuple
365
568
  vNode.b = forceThunks(vNode.b);
366
569
  }
@@ -391,29 +594,39 @@ function _HtmlAsJson_toJson(html) {
391
594
  elmFileContent
392
595
  .replace(
393
596
  /return \$elm\$json\$Json\$Encode\$string\(.REPLACE_ME_WITH_JSON_STRINGIFY.\)/g,
394
- "return " +
395
- (options.debug
396
- ? `${forceThunksSource}
597
+ `return ${forceThunksSource}
397
598
  return _Json_wrap(forceThunks(html));
398
599
  `
399
- : `${forceThunksSource}
400
- return forceThunks(html);
401
- `)
402
- )
403
- .replace(
404
- "return ports ? { ports: ports } : {};",
405
- `const die = function() {
406
- managers = null
407
- model = null
408
- stepper = null
409
- ports = null
410
- }
411
-
412
- return ports ? { ports: ports, die: die } : { die: die };`
413
600
  )
601
+ .replace(`console.log('App dying')`, "")
414
602
  );
415
603
  }
416
604
 
605
+ async function runAdapter(adaptFn, processedIndexTemplate) {
606
+ try {
607
+ await adaptFn({
608
+ renderFunctionFilePath: "./elm-stuff/elm-pages/elm.js",
609
+ routePatterns: JSON.parse(
610
+ await fsPromises.readFile("./dist/route-patterns.json", "utf-8")
611
+ ),
612
+ apiRoutePatterns: JSON.parse(
613
+ await fsPromises.readFile("./dist/api-patterns.json", "utf-8")
614
+ ),
615
+ portsFilePath: "./.elm-pages/compiled-ports/port-data-source.js",
616
+ htmlTemplate: processedIndexTemplate,
617
+ });
618
+ console.log("Success - Adapter script complete");
619
+ } catch (error) {
620
+ console.error("ERROR - Adapter script failed");
621
+ try {
622
+ console.error(JSON.stringify(error));
623
+ } catch (parsingError) {
624
+ console.error(error);
625
+ }
626
+ process.exit(1);
627
+ }
628
+ }
629
+
417
630
  /** @typedef { { route : string; contentJson : string; head : SeoTag[]; html: string; body: string; } } FromElm */
418
631
  /** @typedef {HeadTag | JsonLdTag} SeoTag */
419
632
  /** @typedef {{ name: string; attributes: string[][]; type: 'head' }} HeadTag */