elm-pages 2.1.6 → 2.1.10

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 (75) hide show
  1. package/generator/review/elm.json +34 -0
  2. package/generator/review/src/ReviewConfig.elm +10 -0
  3. package/generator/src/basepath-middleware.js +15 -9
  4. package/generator/src/build.js +100 -6
  5. package/generator/src/cli.js +13 -9
  6. package/generator/src/compile-elm.js +43 -0
  7. package/generator/src/dev-server.js +63 -11
  8. package/generator/src/error-formatter.js +62 -9
  9. package/generator/src/generate-template-module-connector.js +17 -4
  10. package/generator/src/init.js +4 -0
  11. package/generator/src/pre-render-html.js +19 -12
  12. package/generator/src/render-worker.js +0 -1
  13. package/generator/src/render.js +1 -2
  14. package/generator/src/seo-renderer.js +21 -2
  15. package/generator/static-code/hmr.js +43 -6
  16. package/generator/template/elm-tooling.json +9 -0
  17. package/generator/template/package.json +5 -1
  18. package/package.json +16 -9
  19. package/src/ApiRoute.elm +178 -0
  20. package/src/AriaLiveAnnouncer.elm +36 -0
  21. package/src/BuildError.elm +60 -0
  22. package/src/DataSource/File.elm +288 -0
  23. package/src/DataSource/Glob.elm +1050 -0
  24. package/src/DataSource/Http.elm +467 -0
  25. package/src/DataSource/Internal/Glob.elm +74 -0
  26. package/src/DataSource/Port.elm +87 -0
  27. package/src/DataSource/ServerRequest.elm +60 -0
  28. package/src/DataSource.elm +801 -0
  29. package/src/Head/Seo.elm +516 -0
  30. package/src/Head/Twitter.elm +109 -0
  31. package/src/Head.elm +452 -0
  32. package/src/HtmlPrinter.elm +27 -0
  33. package/src/Internal/ApiRoute.elm +89 -0
  34. package/src/Internal/OptimizedDecoder.elm +18 -0
  35. package/src/KeepOrDiscard.elm +6 -0
  36. package/src/OptimizedDecoder/Pipeline.elm +335 -0
  37. package/src/OptimizedDecoder.elm +818 -0
  38. package/src/Pages/ContentCache.elm +248 -0
  39. package/src/Pages/Flags.elm +26 -0
  40. package/src/Pages/Http.elm +10 -0
  41. package/src/Pages/Internal/ApplicationType.elm +6 -0
  42. package/src/Pages/Internal/NotFoundReason.elm +256 -0
  43. package/src/Pages/Internal/Platform/Cli.elm +1015 -0
  44. package/src/Pages/Internal/Platform/Effect.elm +14 -0
  45. package/src/Pages/Internal/Platform/StaticResponses.elm +540 -0
  46. package/src/Pages/Internal/Platform/ToJsPayload.elm +138 -0
  47. package/src/Pages/Internal/Platform.elm +745 -0
  48. package/src/Pages/Internal/RoutePattern.elm +122 -0
  49. package/src/Pages/Internal/Router.elm +116 -0
  50. package/src/Pages/Internal/StaticHttpBody.elm +54 -0
  51. package/src/Pages/Internal/String.elm +39 -0
  52. package/src/Pages/Manifest/Category.elm +240 -0
  53. package/src/Pages/Manifest.elm +412 -0
  54. package/src/Pages/PageUrl.elm +38 -0
  55. package/src/Pages/ProgramConfig.elm +73 -0
  56. package/src/Pages/Review/NoContractViolations.elm +397 -0
  57. package/src/Pages/Secrets.elm +83 -0
  58. package/src/Pages/SiteConfig.elm +13 -0
  59. package/src/Pages/StaticHttp/Request.elm +42 -0
  60. package/src/Pages/StaticHttpRequest.elm +320 -0
  61. package/src/Pages/Url.elm +60 -0
  62. package/src/Path.elm +96 -0
  63. package/src/QueryParams.elm +216 -0
  64. package/src/RenderRequest.elm +163 -0
  65. package/src/RequestsAndPending.elm +20 -0
  66. package/src/Secrets.elm +111 -0
  67. package/src/SecretsDict.elm +45 -0
  68. package/src/StructuredData.elm +236 -0
  69. package/src/TerminalText.elm +242 -0
  70. package/src/Test/Html/Internal/ElmHtml/Constants.elm +53 -0
  71. package/src/Test/Html/Internal/ElmHtml/Helpers.elm +17 -0
  72. package/src/Test/Html/Internal/ElmHtml/InternalTypes.elm +529 -0
  73. package/src/Test/Html/Internal/ElmHtml/Markdown.elm +56 -0
  74. package/src/Test/Html/Internal/ElmHtml/ToString.elm +197 -0
  75. package/src/Test/Internal/KernelConstants.elm +34 -0
@@ -0,0 +1,34 @@
1
+ {
2
+ "type": "application",
3
+ "source-directories": [
4
+ "src",
5
+ "../../src"
6
+ ],
7
+ "elm-version": "0.19.1",
8
+ "dependencies": {
9
+ "direct": {
10
+ "elm/core": "1.0.5",
11
+ "elm-community/result-extra": "2.4.0",
12
+ "jfmengels/elm-review": "2.4.2",
13
+ "stil4m/elm-syntax": "7.2.5"
14
+ },
15
+ "indirect": {
16
+ "elm/html": "1.0.0",
17
+ "elm/json": "1.1.3",
18
+ "elm/parser": "1.1.0",
19
+ "elm/project-metadata-utils": "1.0.2",
20
+ "elm/random": "1.0.0",
21
+ "elm/time": "1.0.0",
22
+ "elm/virtual-dom": "1.0.2",
23
+ "elm-community/list-extra": "8.3.0",
24
+ "elm-explorations/test": "1.2.2",
25
+ "miniBill/elm-unicode": "1.0.2",
26
+ "rtfeldman/elm-hex": "1.0.0",
27
+ "stil4m/structured-writer": "1.0.3"
28
+ }
29
+ },
30
+ "test-dependencies": {
31
+ "direct": {},
32
+ "indirect": {}
33
+ }
34
+ }
@@ -0,0 +1,10 @@
1
+ module ReviewConfig exposing (config)
2
+
3
+ import Pages.Review.NoContractViolations
4
+ import Review.Rule as Rule exposing (Rule)
5
+
6
+
7
+ config : List Rule
8
+ config =
9
+ [ Pages.Review.NoContractViolations.rule ]
10
+
@@ -1,18 +1,23 @@
1
- const parseUrl = require("url").parse;
2
-
3
1
  // this middleware is only active when (config.base !== '/')
4
2
 
5
3
  module.exports = function baseMiddleware(base) {
6
4
  // Keep the named function. The name is visible in debug logs via `DEBUG=connect:dispatcher ...`
7
5
  return function viteBaseMiddleware(req, res, next) {
8
- const url = req.url;
9
- const parsed = parseUrl(url);
10
- const path = parsed.pathname || "/";
6
+ // `req.url` only contains the path, since that is what gets passed in the HTTP request.
7
+ // See https://nodejs.org/api/http.html#http_message_url
8
+ const path = req.url;
11
9
 
12
- if (path.startsWith(base)) {
13
- // rewrite url to remove base.. this ensures that other middleware does
10
+ if (base === "/") {
11
+ // The RegExp will check for the slash, so we remove it here.
12
+ base = "";
13
+ }
14
+ // We want to detect the base at the beginning, hence the `^`,
15
+ // but also allow calling the base without a trailing slash, hence the `$`.
16
+ const baseRegExp = new RegExp(`^${base}(/|$)`);
17
+ if (baseRegExp.test(path)) {
18
+ // rewrite url to remove base. this ensures that other middleware does
14
19
  // not need to consider base being prepended or not
15
- req.url = url.replace(base, "/");
20
+ req.url = path.replace(baseRegExp, "/");
16
21
  return next();
17
22
  }
18
23
 
@@ -26,9 +31,10 @@ module.exports = function baseMiddleware(base) {
26
31
  } else if (req.headers.accept && req.headers.accept.includes("text/html")) {
27
32
  // non-based page visit
28
33
  res.statusCode = 404;
34
+ const suggestionUrl = `${base}/${path.slice(1)}`;
29
35
  res.end(
30
36
  `The server is configured with a public base URL of ${base} - ` +
31
- `did you mean to visit ${base}${url.slice(1)} instead?`
37
+ `did you mean to visit ${suggestionUrl} instead?`
32
38
  );
33
39
  return;
34
40
  }
@@ -1,6 +1,7 @@
1
1
  const fs = require("./dir-helpers.js");
2
2
  const fsPromises = require("fs").promises;
3
3
 
4
+ const { runElmReview } = require("./compile-elm.js");
4
5
  const { restoreColor } = require("./error-formatter");
5
6
  const path = require("path");
6
7
  const spawnCallback = require("cross-spawn").spawn;
@@ -9,6 +10,7 @@ const terser = require("terser");
9
10
  const os = require("os");
10
11
  const { Worker, SHARE_ENV } = require("worker_threads");
11
12
  const { ensureDirSync } = require("./file-helpers.js");
13
+ const which = require("which");
12
14
  let pool = [];
13
15
  let pagesReady;
14
16
  let pages = new Promise((resolve, reject) => {
@@ -35,9 +37,28 @@ async function ensureRequiredDirs() {
35
37
  ensureDirSync(path.join(process.cwd(), ".elm-pages", "http-response-cache"));
36
38
  }
37
39
 
40
+ async function ensureRequiredExecutables() {
41
+ try {
42
+ await which("elm");
43
+ } 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.";
45
+ }
46
+ try {
47
+ await which("elm-optimize-level-2");
48
+ } catch (error) {
49
+ throw "I couldn't find elm-optimize-level-2 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.";
50
+ }
51
+ try {
52
+ await which("elm-review");
53
+ } catch (error) {
54
+ throw "I couldn't find elm-review 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.";
55
+ }
56
+ }
57
+
38
58
  async function run(options) {
39
59
  try {
40
60
  await ensureRequiredDirs();
61
+ await ensureRequiredExecutables();
41
62
  // since init/update are never called in pre-renders, and DataSource.Http is called using undici
42
63
  // we can provide a fake HTTP instead of xhr2 (which is otherwise needed for Elm HTTP requests from Node)
43
64
  XMLHttpRequest = {};
@@ -55,8 +76,22 @@ async function run(options) {
55
76
  await Promise.all([copyDone, cliDone, compileClientDone]);
56
77
  } catch (error) {
57
78
  buildError = true;
58
- console.error(error);
59
- process.exit(1);
79
+ try {
80
+ const reviewOutput = JSON.parse(await runElmReview());
81
+ const isParsingError = reviewOutput.errors.some((reviewError) => {
82
+ return reviewError.errors.some((item) => item.rule === "ParsingError");
83
+ });
84
+ if (isParsingError) {
85
+ console.error(error);
86
+ } else {
87
+ console.error(restoreColor(reviewOutput));
88
+ }
89
+ process.exit(1);
90
+ } catch (noElmReviewErrors) {
91
+ console.error(error);
92
+ } finally {
93
+ process.exit(1);
94
+ }
60
95
  }
61
96
  }
62
97
 
@@ -164,10 +199,15 @@ function elmOptimizeLevel2(elmEntrypointPath, outputPath, cwd) {
164
199
  });
165
200
 
166
201
  subprocess.on("close", async (code) => {
167
- if (code == 0 && (await fs.fileExists(fullOutputPath))) {
202
+ if (
203
+ code === 0 &&
204
+ commandOutput === "" &&
205
+ (await fs.fileExists(fullOutputPath))
206
+ ) {
168
207
  resolve();
169
208
  } else {
170
209
  if (!buildError) {
210
+ buildError = true;
171
211
  process.exitCode = 1;
172
212
  reject(commandOutput);
173
213
  } else {
@@ -227,8 +267,13 @@ function runElmMake(elmEntrypointPath, outputPath, cwd) {
227
267
  if (code == 0 && (await fs.fileExists(fullOutputPath))) {
228
268
  resolve();
229
269
  } else {
230
- process.exitCode = 1;
231
- reject(restoreColor(JSON.parse(commandOutput)));
270
+ if (!buildError) {
271
+ buildError = true;
272
+ process.exitCode = 1;
273
+ reject(restoreColor(JSON.parse(commandOutput)));
274
+ } else {
275
+ // avoid unhandled error printing duplicate message, let process.exit in top loop take over
276
+ }
232
277
  }
233
278
  });
234
279
  });
@@ -299,12 +344,61 @@ async function compileCliApp(options) {
299
344
  );
300
345
 
301
346
  const elmFileContent = await fsPromises.readFile(ELM_FILE_PATH, "utf-8");
347
+ // Source: https://github.com/elm-explorations/test/blob/d5eb84809de0f8bbf50303efd26889092c800609/src/Elm/Kernel/HtmlAsJson.js
348
+ const forceThunksSource = ` _HtmlAsJson_toJson(x)
349
+ }
350
+
351
+ var virtualDomKernelConstants =
352
+ {
353
+ nodeTypeTagger: 4,
354
+ nodeTypeThunk: 5,
355
+ kids: "e",
356
+ refs: "l",
357
+ thunk: "m",
358
+ node: "k",
359
+ value: "a"
360
+ }
361
+
362
+ function forceThunks(vNode) {
363
+ if (typeof vNode !== "undefined" && vNode.$ === "#2") {
364
+ // This is a tuple (the kids : List (String, Html) field of a Keyed node); recurse into the right side of the tuple
365
+ vNode.b = forceThunks(vNode.b);
366
+ }
367
+ if (typeof vNode !== 'undefined' && vNode.$ === virtualDomKernelConstants.nodeTypeThunk && !vNode[virtualDomKernelConstants.node]) {
368
+ // This is a lazy node; evaluate it
369
+ var args = vNode[virtualDomKernelConstants.thunk];
370
+ vNode[virtualDomKernelConstants.node] = vNode[virtualDomKernelConstants.thunk].apply(args);
371
+ // And then recurse into the evaluated node
372
+ vNode[virtualDomKernelConstants.node] = forceThunks(vNode[virtualDomKernelConstants.node]);
373
+ }
374
+ if (typeof vNode !== 'undefined' && vNode.$ === virtualDomKernelConstants.nodeTypeTagger) {
375
+ // This is an Html.map; recurse into the node it is wrapping
376
+ vNode[virtualDomKernelConstants.node] = forceThunks(vNode[virtualDomKernelConstants.node]);
377
+ }
378
+ if (typeof vNode !== 'undefined' && typeof vNode[virtualDomKernelConstants.kids] !== 'undefined') {
379
+ // This is something with children (either a node with kids : List Html, or keyed with kids : List (String, Html));
380
+ // recurse into the children
381
+ vNode[virtualDomKernelConstants.kids] = vNode[virtualDomKernelConstants.kids].map(forceThunks);
382
+ }
383
+ return vNode;
384
+ }
385
+
386
+ function _HtmlAsJson_toJson(html) {
387
+ `;
388
+
302
389
  await fsPromises.writeFile(
303
390
  ELM_FILE_PATH,
304
391
  elmFileContent
305
392
  .replace(
306
393
  /return \$elm\$json\$Json\$Encode\$string\(.REPLACE_ME_WITH_JSON_STRINGIFY.\)/g,
307
- "return " + (options.debug ? "_Json_wrap(x)" : "x")
394
+ "return " +
395
+ (options.debug
396
+ ? `${forceThunksSource}
397
+ return _Json_wrap(forceThunks(html));
398
+ `
399
+ : `${forceThunksSource}
400
+ return forceThunks(html);
401
+ `)
308
402
  )
309
403
  .replace(
310
404
  "return ports ? { ports: ports } : {};",
@@ -47,6 +47,7 @@ async function main() {
47
47
  "Preserve the HTTP and JS Port cache instead of deleting it on server start"
48
48
  )
49
49
  .option("--base <basePath>", "serve site under a base path", "/")
50
+ .option("--https", "uses a https server")
50
51
  .action(async (options) => {
51
52
  if (!options.keepCache) {
52
53
  clearHttpAndPortCache();
@@ -108,16 +109,19 @@ function clearHttpAndPortCache() {
108
109
  }
109
110
 
110
111
  /**
111
- * @param {string} pagePath
112
+ * @param {string} rawPagePath
112
113
  */
113
- function normalizeUrl(pagePath) {
114
- if (!pagePath.startsWith("/")) {
115
- pagePath = "/" + pagePath;
116
- }
117
- if (!pagePath.endsWith("/")) {
118
- pagePath = pagePath + "/";
119
- }
120
- return pagePath;
114
+ function normalizeUrl(rawPagePath) {
115
+ const segments = rawPagePath
116
+ .split("/")
117
+ // Filter out all empty segments.
118
+ .filter((segment) => segment.length != 0);
119
+
120
+ // Do not add a trailing slash.
121
+ // The core issue is that `/base` is a prefix of `/base/`, but
122
+ // `/base/` is not a prefix of `/base`, which can later lead to issues
123
+ // with detecting whether the path contains the base.
124
+ return `/${segments.join("/")}`;
121
125
  }
122
126
 
123
127
  main();
@@ -90,9 +90,52 @@ async function runElm(elmEntrypointPath, outputPath, cwd) {
90
90
  });
91
91
  }
92
92
 
93
+ /**
94
+ * @param {string} [ cwd ]
95
+ */
96
+ async function runElmReview(cwd) {
97
+ const startTime = Date.now();
98
+ return new Promise((resolve, reject) => {
99
+ const child = spawnCallback(
100
+ `elm-review`,
101
+ [
102
+ "--report",
103
+ "json",
104
+ "--namespace",
105
+ "elm-pages",
106
+ "--config",
107
+ path.join(__dirname, "../../generator/review"),
108
+ ],
109
+ { cwd: cwd }
110
+ );
111
+
112
+ let scriptOutput = "";
113
+
114
+ child.stdout.setEncoding("utf8");
115
+ child.stdout.on("data", function (/** @type {string} */ data) {
116
+ scriptOutput += data.toString();
117
+ });
118
+
119
+ child.stderr.setEncoding("utf8");
120
+ child.stderr.on("data", function (/** @type {string} */ data) {
121
+ scriptOutput += data.toString();
122
+ });
123
+
124
+ child.on("close", function (code) {
125
+ console.log(`Ran elm-review in ${timeFrom(startTime)}`);
126
+ if (code === 0) {
127
+ reject();
128
+ } else {
129
+ resolve(scriptOutput);
130
+ }
131
+ });
132
+ });
133
+ }
134
+
93
135
  module.exports = {
94
136
  spawnElmMake,
95
137
  compileElmForBrowser,
138
+ runElmReview,
96
139
  };
97
140
 
98
141
  /**
@@ -1,8 +1,14 @@
1
1
  const path = require("path");
2
2
  const fs = require("fs");
3
+ const which = require("which");
3
4
  const chokidar = require("chokidar");
4
- const { spawnElmMake, compileElmForBrowser } = require("./compile-elm.js");
5
+ const {
6
+ spawnElmMake,
7
+ compileElmForBrowser,
8
+ runElmReview,
9
+ } = require("./compile-elm.js");
5
10
  const http = require("http");
11
+ const https = require("https");
6
12
  const codegen = require("./codegen.js");
7
13
  const kleur = require("kleur");
8
14
  const serveStatic = require("serve-static");
@@ -12,9 +18,10 @@ const { Worker, SHARE_ENV } = require("worker_threads");
12
18
  const os = require("os");
13
19
  const { ensureDirSync } = require("./file-helpers.js");
14
20
  const baseMiddleware = require("./basepath-middleware.js");
21
+ const devcert = require("devcert");
15
22
 
16
23
  /**
17
- * @param {{ port: string; base: string }} options
24
+ * @param {{ port: string; base: string; https: boolean; }} options
18
25
  */
19
26
  async function start(options) {
20
27
  let threadReadyQueue = [];
@@ -23,6 +30,7 @@ async function start(options) {
23
30
  const cpuCount = os.cpus().length;
24
31
 
25
32
  const port = options.port;
33
+ const useHttps = options.https;
26
34
  let elmMakeRunning = true;
27
35
 
28
36
  const serve = serveStatic("public/", { index: false });
@@ -45,6 +53,12 @@ async function start(options) {
45
53
  });
46
54
 
47
55
  await codegen.generate(options.base);
56
+ try {
57
+ await ensureRequiredExecutables();
58
+ } catch (error) {
59
+ console.error(error);
60
+ process.exit(1);
61
+ }
48
62
  let clientElmMakeProcess = compileElmForBrowser();
49
63
  let pendingCliCompile = compileCliApp();
50
64
  watchElmSourceDirs(true);
@@ -59,7 +73,7 @@ async function start(options) {
59
73
  });
60
74
  console.log(
61
75
  `${kleur.dim(`elm-pages dev server running at`)} ${kleur.green(
62
- `<http://localhost:${port}>`
76
+ `<${useHttps ? "https" : "http"}://localhost:${port}>`
63
77
  )}`
64
78
  );
65
79
  const poolSize = Math.max(1, cpuCount / 2 - 1);
@@ -107,7 +121,12 @@ async function start(options) {
107
121
  .use(serveStaticCode)
108
122
  .use(serve)
109
123
  .use(processRequest);
110
- http.createServer(app).listen(port);
124
+ if (useHttps) {
125
+ const ssl = await devcert.certificateFor("localhost");
126
+ https.createServer(ssl, app).listen(port);
127
+ } else {
128
+ http.createServer(app).listen(port);
129
+ }
111
130
  /**
112
131
  * @param {http.IncomingMessage} request
113
132
  * @param {http.ServerResponse} response
@@ -122,7 +141,6 @@ async function start(options) {
122
141
  }
123
142
 
124
143
  watcher.on("all", async function (eventName, pathThatChanged) {
125
- // console.log({ pathThatChanged });
126
144
  if (pathThatChanged === "elm.json") {
127
145
  watchElmSourceDirs(false);
128
146
  } else if (pathThatChanged.endsWith(".css")) {
@@ -288,13 +306,34 @@ async function start(options) {
288
306
  try {
289
307
  await pendingCliCompile;
290
308
  } catch (error) {
291
- console.log(restoreColor(JSON.parse(error)));
292
- if (req.url.includes("content.json")) {
293
- res.writeHead(500, { "Content-Type": "application/json" });
294
- res.end(error);
309
+ let isImplicitContractError = false;
310
+ try {
311
+ isImplicitContractError = JSON.parse(error).errors.some(
312
+ (errorItem) => errorItem.name === "TemplateModulesBeta"
313
+ );
314
+ } catch (unexpectedError) {
315
+ console.log("Unexpected error", unexpectedError);
316
+ }
317
+ if (isImplicitContractError) {
318
+ const reviewOutput = await runElmReview();
319
+ console.log(restoreColor(JSON.parse(reviewOutput)));
320
+
321
+ if (req.url.includes("content.json")) {
322
+ res.writeHead(500, { "Content-Type": "application/json" });
323
+ res.end(reviewOutput);
324
+ } else {
325
+ res.writeHead(500, { "Content-Type": "text/html" });
326
+ res.end(errorHtml());
327
+ }
295
328
  } else {
296
- res.writeHead(500, { "Content-Type": "text/html" });
297
- res.end(errorHtml());
329
+ console.log(restoreColor(JSON.parse(error)));
330
+ if (req.url.includes("content.json")) {
331
+ res.writeHead(500, { "Content-Type": "application/json" });
332
+ res.end(error);
333
+ } else {
334
+ res.writeHead(500, { "Content-Type": "text/html" });
335
+ res.end(errorHtml());
336
+ }
298
337
  }
299
338
  return;
300
339
  }
@@ -495,4 +534,17 @@ function errorHtml() {
495
534
  `;
496
535
  }
497
536
 
537
+ async function ensureRequiredExecutables() {
538
+ try {
539
+ await which("elm");
540
+ } 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.";
542
+ }
543
+ try {
544
+ await which("elm-review");
545
+ } catch (error) {
546
+ throw "I couldn't find elm-review 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.";
547
+ }
548
+ }
549
+
498
550
  module.exports = { start };
@@ -10,12 +10,12 @@ const kleur = require("kleur");
10
10
  * This function takes in the error title and the path to the file with the error and formats it like elm make's regular output
11
11
  **/
12
12
  /**
13
- * @param {string} title
13
+ * @param {string} rule
14
14
  * @param {string} path
15
15
  * */
16
- const parseHeader = (title, path) =>
16
+ const parseHeader = (rule, path) =>
17
17
  kleur.cyan(
18
- `-- ${title.replace("-", " ")} --------------- ${path || ""}
18
+ `-- ${rule.replace("-", " ")} --------------- ${path || ""}
19
19
  `
20
20
  );
21
21
 
@@ -32,17 +32,45 @@ function parseMsg(msg) {
32
32
  return msg;
33
33
  } else {
34
34
  if (msg.underline && msg.color) {
35
- return kleur[msg.color.toLowerCase()]().underline(msg.string);
35
+ return kleur[toKleurColor(msg.color)]().underline(msg.string);
36
36
  } else if (msg.underline) {
37
37
  return kleur.underline(msg.string);
38
38
  } else if (msg.color) {
39
- return kleur[msg.color.toLowerCase()](msg.string);
39
+ return kleur[toKleurColor(msg.color)](msg.string);
40
40
  } else {
41
41
  return msg.string;
42
42
  }
43
43
  }
44
44
  }
45
45
 
46
+ /**
47
+ * @param {string} color
48
+ * @returns {keyof import("kleur").Kleur}
49
+ * */
50
+ function toKleurColor(color) {
51
+ if (color.startsWith("#")) {
52
+ const hexCode = color.slice(1);
53
+ switch (hexCode) {
54
+ // color codes from https://github.com/jfmengels/node-elm-review/blob/d4a6de524cfc33c490c751a3bb084e86accf25fd/template/src/Elm/Review/Text.elm#L80
55
+ case "33BBC8": {
56
+ return "cyan";
57
+ }
58
+ case "33BBC8": {
59
+ return "cyan";
60
+ }
61
+ case "FFFF00": {
62
+ return "yellow";
63
+ }
64
+ case "008000": {
65
+ return "green";
66
+ }
67
+ }
68
+ return "red";
69
+ } else {
70
+ return color.toLowerCase();
71
+ }
72
+ }
73
+
46
74
  /** @typedef {{problems: {title: string; message: unknown}[]; path: string}[]} Errors } */
47
75
 
48
76
  /**
@@ -61,6 +89,12 @@ const restoreColor = (error) => {
61
89
  problems.map(restoreProblem(path)).join("\n\n\n")
62
90
  )
63
91
  .join("\n\n\n\n\n");
92
+ } else if (error.type === "review-errors") {
93
+ return error.errors
94
+ .map(({ errors, path }) =>
95
+ errors.map(restoreProblem(path)).join("\n\n\n")
96
+ )
97
+ .join("\n\n\n\n\n");
64
98
  } else if (error.type === "error") {
65
99
  return restoreProblem(error.path)(error);
66
100
  } else {
@@ -78,13 +112,23 @@ const restoreColor = (error) => {
78
112
  * This function takes in the array of compiler errors and maps over them to generate a formatted compiler error
79
113
  **/
80
114
  const restoreProblem =
81
- (/** @type {string} */ path) =>
82
- (/** @type {{title:string; message: Message[]}} */ { title, message }) =>
83
- [parseHeader(title, path), ...message.map(parseMsg)].join("");
115
+ (/** @type {string} */ path) => (/** @type {Problem | IError} */ info) => {
116
+ if (info.rule && info.formatted) {
117
+ return [
118
+ parseHeader(info.rule, path),
119
+ ...info.formatted.map(parseMsg),
120
+ ].join("");
121
+ } else {
122
+ return [
123
+ parseHeader(info.title, path),
124
+ ...info.message.map(parseMsg),
125
+ ].join("");
126
+ }
127
+ };
84
128
 
85
129
  module.exports = { restoreColor };
86
130
 
87
- /** @typedef { CompilerError | ReportError } RootObject */
131
+ /** @typedef { CompilerError | ReportError | IElmReviewError } RootObject */
88
132
 
89
133
  /** @typedef { { type: "compile-errors"; errors: Error_[]; } } CompilerError */
90
134
  /** @typedef { { type: "error"; path: string; title: string; message: Message[]; } } ReportError */
@@ -97,3 +141,12 @@ module.exports = { restoreColor };
97
141
  /** @typedef {string | {underline: boolean; color: string?; string: string}} Message */
98
142
 
99
143
  /** @typedef { { path: string; name: string; problems: Problem[]; } } Error_ */
144
+
145
+ /** @typedef { { type: "review-errors"; errors: IFileError[]; } } IElmReviewError */
146
+
147
+ /** @typedef { { path: string; errors: IError[]; } } IFileError */
148
+
149
+ /** @typedef { { rule: string; formatted: unknown[]; ruleLink: string; message: string; details: string[]; region: IRegion; fix?: { range: IRegion; string: string; }[]; } } IError */
150
+
151
+ /** @typedef { { start: IPosition; end: IPosition; } } IRegion */
152
+ /** @typedef { { line: number; column: number; } } IPosition */
@@ -168,12 +168,17 @@ view page maybePageUrl globalData pageData =
168
168
 
169
169
  _ ->
170
170
  { title = "Model mismatch", body = Html.text <| "Model mismatch" }
171
- , head = Page.${moduleName(name)}.page.head
171
+ , head = ${
172
+ phase === "browser"
173
+ ? "[]"
174
+ : `Page.${moduleName(name)}.page.head
172
175
  { data = data
173
176
  , sharedData = globalData
174
177
  , routeParams = ${emptyRouteParams(name) ? "{}" : "s"}
175
178
  , path = page.path
176
179
  }
180
+ `
181
+ }
177
182
  }
178
183
  `
179
184
  )
@@ -415,8 +420,12 @@ main =
415
420
  { init = init Nothing
416
421
  , urlToRoute = Route.urlToRoute
417
422
  , routeToPath = \\route -> route |> Maybe.map Route.routeToPath |> Maybe.withDefault []
418
- , site = Site.config
419
- , getStaticRoutes = getStaticRoutes |> DataSource.map (List.map Just)
423
+ , site = ${phase === "browser" ? `Nothing` : `Just Site.config`}
424
+ , getStaticRoutes = ${
425
+ phase === "browser"
426
+ ? `DataSource.succeed []`
427
+ : `getStaticRoutes |> DataSource.map (List.map Just)`
428
+ }
420
429
  , handleRoute = handleRoute
421
430
  , view = view
422
431
  , update = update
@@ -431,7 +440,11 @@ main =
431
440
  , fromJsPort = fromJsPort identity
432
441
  , data = dataForRoute
433
442
  , sharedData = Shared.template.data
434
- , apiRoutes = \\htmlToString -> pathsToGenerateHandler :: routePatterns :: manifestHandler :: Api.routes getStaticRoutes htmlToString
443
+ , apiRoutes = ${
444
+ phase === "browser"
445
+ ? `\\_ -> []`
446
+ : `\\htmlToString -> pathsToGenerateHandler :: routePatterns :: manifestHandler :: Api.routes getStaticRoutes htmlToString`
447
+ }
435
448
  , pathPatterns = routePatterns3
436
449
  , basePath = [ ${basePath
437
450
  .split("/")
@@ -19,6 +19,10 @@ async function run(name) {
19
19
  path.resolve(appRoot, "gitignore"),
20
20
  path.resolve(appRoot, ".gitignore")
21
21
  );
22
+ /* Since .elm-pages is in source-directories, make sure we create it before running any elm-pages commands
23
+ in case we run any install commands first. See: https://github.com/dillonkearns/elm-pages/issues/205
24
+ */
25
+ fs.mkdirSync(path.join(appRoot, ".elm-pages"), { recursive: true });
22
26
  } catch (err) {
23
27
  console.log(err);
24
28
  process.exit(1);