elm-pages 3.0.0-beta.2 → 3.0.0-beta.20

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 (109) hide show
  1. package/README.md +10 -1
  2. package/codegen/{elm-pages-codegen.js → elm-pages-codegen.cjs} +2420 -1592
  3. package/generator/dead-code-review/elm-stuff/tests-0.19.1/elm-stuff/0.19.1/Pages-Review-DeadCodeEliminateData.elmi +0 -0
  4. package/generator/dead-code-review/elm-stuff/tests-0.19.1/elm-stuff/0.19.1/Pages-Review-DeadCodeEliminateData.elmo +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/d.dat +0 -0
  7. package/generator/dead-code-review/elm-stuff/tests-0.19.1/elm-stuff/0.19.1/i.dat +0 -0
  8. package/generator/dead-code-review/elm-stuff/tests-0.19.1/elm-stuff/0.19.1/o.dat +0 -0
  9. package/generator/dead-code-review/elm-stuff/tests-0.19.1/elm.json +1 -1
  10. package/generator/dead-code-review/elm-stuff/tests-0.19.1/js/Reporter.elm.js +1326 -121
  11. package/generator/dead-code-review/elm-stuff/tests-0.19.1/js/Runner.elm.js +15215 -13007
  12. package/generator/dead-code-review/elm-stuff/tests-0.19.1/js/node_runner.js +1 -1
  13. package/generator/dead-code-review/elm-stuff/tests-0.19.1/js/node_supervisor.js +1 -1
  14. package/generator/dead-code-review/elm.json +8 -6
  15. package/generator/dead-code-review/src/Pages/Review/DeadCodeEliminateData.elm +189 -17
  16. package/generator/dead-code-review/tests/Pages/Review/DeadCodeEliminateDataTest.elm +255 -21
  17. package/generator/review/elm-stuff/tests-0.19.1/elm-stuff/0.19.1/d.dat +0 -0
  18. package/generator/review/elm-stuff/tests-0.19.1/elm-stuff/0.19.1/i.dat +0 -0
  19. package/generator/review/elm-stuff/tests-0.19.1/elm-stuff/0.19.1/o.dat +0 -0
  20. package/generator/review/elm-stuff/tests-0.19.1/elm.json +1 -1
  21. package/generator/review/elm-stuff/tests-0.19.1/js/Reporter.elm.js +1326 -121
  22. package/generator/review/elm-stuff/tests-0.19.1/js/Runner.elm.js +14620 -12636
  23. package/generator/review/elm-stuff/tests-0.19.1/js/node_runner.js +1 -1
  24. package/generator/review/elm-stuff/tests-0.19.1/js/node_supervisor.js +1 -1
  25. package/generator/review/elm.json +8 -8
  26. package/generator/src/RouteBuilder.elm +66 -52
  27. package/generator/src/SharedTemplate.elm +3 -2
  28. package/generator/src/SiteConfig.elm +3 -2
  29. package/generator/src/basepath-middleware.js +3 -3
  30. package/generator/src/build.js +122 -86
  31. package/generator/src/cli.js +247 -51
  32. package/generator/src/codegen.js +29 -27
  33. package/generator/src/compatibility-key.js +1 -0
  34. package/generator/src/compile-elm.js +20 -22
  35. package/generator/src/config.js +39 -0
  36. package/generator/src/copy-dir.js +2 -2
  37. package/generator/src/dev-server.js +119 -110
  38. package/generator/src/dir-helpers.js +9 -26
  39. package/generator/src/elm-codegen.js +5 -4
  40. package/generator/src/elm-file-constants.js +2 -3
  41. package/generator/src/error-formatter.js +12 -11
  42. package/generator/src/file-helpers.js +3 -4
  43. package/generator/src/generate-template-module-connector.js +14 -21
  44. package/generator/src/init.js +8 -7
  45. package/generator/src/pre-render-html.js +41 -28
  46. package/generator/src/render-test.js +109 -0
  47. package/generator/src/render-worker.js +26 -28
  48. package/generator/src/render.js +322 -142
  49. package/generator/src/request-cache.js +200 -162
  50. package/generator/src/rewrite-client-elm-json.js +5 -5
  51. package/generator/src/rewrite-elm-json.js +7 -7
  52. package/generator/src/route-codegen-helpers.js +16 -31
  53. package/generator/src/seo-renderer.js +12 -7
  54. package/generator/src/vite-utils.js +77 -0
  55. package/generator/static-code/hmr.js +16 -2
  56. package/generator/template/app/Api.elm +3 -3
  57. package/generator/template/app/Route/Index.elm +3 -3
  58. package/generator/template/app/Shared.elm +3 -3
  59. package/generator/template/app/Site.elm +9 -4
  60. package/package.json +21 -21
  61. package/src/ApiRoute.elm +199 -61
  62. package/src/BackendTask/Custom.elm +214 -0
  63. package/src/BackendTask/Env.elm +90 -0
  64. package/src/{DataSource → BackendTask}/File.elm +128 -43
  65. package/src/{DataSource → BackendTask}/Glob.elm +136 -125
  66. package/src/BackendTask/Http.elm +673 -0
  67. package/src/{DataSource → BackendTask}/Internal/Glob.elm +1 -1
  68. package/src/BackendTask/Internal/Request.elm +28 -0
  69. package/src/BackendTask/Random.elm +79 -0
  70. package/src/BackendTask/Time.elm +47 -0
  71. package/src/BackendTask.elm +537 -0
  72. package/src/FatalError.elm +89 -0
  73. package/src/Form/Field.elm +1 -1
  74. package/src/Form.elm +72 -92
  75. package/src/Head/Seo.elm +4 -4
  76. package/src/Head.elm +237 -7
  77. package/src/HtmlPrinter.elm +7 -3
  78. package/src/Internal/ApiRoute.elm +7 -5
  79. package/src/PageServerResponse.elm +6 -1
  80. package/src/Pages/Generate.elm +775 -132
  81. package/src/Pages/GeneratorProgramConfig.elm +15 -0
  82. package/src/Pages/Internal/FatalError.elm +5 -0
  83. package/src/Pages/Internal/Form.elm +21 -1
  84. package/src/Pages/Internal/Platform/Cli.elm +479 -747
  85. package/src/Pages/Internal/Platform/Cli.elm.bak +1276 -0
  86. package/src/Pages/Internal/Platform/CompatibilityKey.elm +6 -0
  87. package/src/Pages/Internal/Platform/Effect.elm +1 -2
  88. package/src/Pages/Internal/Platform/GeneratorApplication.elm +373 -0
  89. package/src/Pages/Internal/Platform/StaticResponses.elm +73 -270
  90. package/src/Pages/Internal/Platform/ToJsPayload.elm +4 -7
  91. package/src/Pages/Internal/Platform.elm +54 -53
  92. package/src/Pages/Internal/Script.elm +17 -0
  93. package/src/Pages/Internal/StaticHttpBody.elm +35 -1
  94. package/src/Pages/Manifest.elm +29 -4
  95. package/src/Pages/ProgramConfig.elm +12 -8
  96. package/src/Pages/Script.elm +109 -0
  97. package/src/Pages/SiteConfig.elm +3 -2
  98. package/src/Pages/StaticHttp/Request.elm +2 -2
  99. package/src/Pages/StaticHttpRequest.elm +23 -98
  100. package/src/RequestsAndPending.elm +8 -19
  101. package/src/Result/Extra.elm +21 -0
  102. package/src/Server/Request.elm +43 -34
  103. package/src/Server/Session.elm +166 -100
  104. package/src/Server/SetCookie.elm +89 -31
  105. package/src/DataSource/Env.elm +0 -38
  106. package/src/DataSource/Http.elm +0 -446
  107. package/src/DataSource/Internal/Request.elm +0 -20
  108. package/src/DataSource/Port.elm +0 -90
  109. package/src/DataSource.elm +0 -538
@@ -1,174 +1,189 @@
1
- const path = require("path");
2
- const fetch = require("node-fetch");
3
- const objectHash = require("object-hash");
4
- const kleur = require("kleur");
1
+ import * as path from "path";
2
+ import * as fsPromises from "fs/promises";
3
+ import * as kleur from "kleur/colors";
4
+ import { default as makeFetchHappenOriginal } from "make-fetch-happen";
5
5
 
6
- /**
7
- * To cache HTTP requests on disk with quick lookup and insertion, we store the hashed request.
8
- * This uses SHA1 hashes. They are uni-directional hashes, which works for this use case. Most importantly,
9
- * they're unique enough and can be expressed in a case-insensitive way so it works on Windows filesystems.
10
- * And they are 40 hex characters, so the length won't be too long no matter what the request payload.
11
- * @param {Object} request
12
- */
13
- function requestToString(request) {
14
- return objectHash(request);
15
- }
16
- /**
17
- * @param {Object} request
18
- */
19
- function fullPath(portsHash, request, hasFsAccess) {
20
- const requestWithPortHash =
21
- request.url === "elm-pages-internal://port"
22
- ? { portsHash, ...request }
23
- : request;
24
- if (hasFsAccess) {
25
- return path.join(
26
- process.cwd(),
27
- ".elm-pages",
28
- "http-response-cache",
29
- requestToString(requestWithPortHash)
30
- );
31
- } else {
32
- return path.join("/", requestToString(requestWithPortHash));
33
- }
34
- }
6
+ const defaultHttpCachePath = "./.elm-pages/http-cache";
7
+
8
+ /** @typedef {{kind: 'cache-response-path', value: string} | {kind: 'response-json', value: JSON}} Response */
35
9
 
36
10
  /**
37
11
  * @param {string} mode
38
12
  * @param {{url: string;headers: {[x: string]: string;};method: string;body: Body;}} rawRequest
39
- * @returns {Promise<string>}
40
- * @param {string} portsFile
13
+ * @param {Record<string, unknown>} portsFile
41
14
  * @param {boolean} hasFsAccess
15
+ * @returns {Promise<Response>}
42
16
  */
43
- function lookupOrPerform(portsFile, mode, rawRequest, hasFsAccess, useCache) {
44
- const { fs } = require("./request-cache-fs.js")(hasFsAccess);
17
+ export function lookupOrPerform(
18
+ portsFile,
19
+ mode,
20
+ rawRequest,
21
+ hasFsAccess,
22
+ useCache
23
+ ) {
24
+ const makeFetchHappen = makeFetchHappenOriginal.defaults({
25
+ cache: mode === "build" ? "no-cache" : "default",
26
+ });
45
27
  return new Promise(async (resolve, reject) => {
46
28
  const request = toRequest(rawRequest);
47
- const portsHash = (portsFile && portsFile.match(/-([^-]+)\.js$/)[1]) || "";
48
- const responsePath = fullPath(portsHash, request, hasFsAccess);
49
29
 
50
- // TODO check cache expiration time and delete and go to else if expired
51
- if (useCache && (await checkFileExists(fs, responsePath))) {
52
- // console.log("Skipping request, found file.");
53
- resolve(responsePath);
54
- } else {
55
- let portDataSource = {};
56
- let portDataSourceImportError = null;
57
- try {
58
- if (portsFile === undefined) {
59
- throw "missing";
60
- }
61
- const portDataSourcePath = path.resolve(portsFile);
62
- // On Windows, we need cannot use paths directly and instead must use a file:// URL.
63
- // portDataSource = await require(url.pathToFileURL(portDataSourcePath).href);
64
- portDataSource = require(portDataSourcePath);
65
- } catch (e) {
66
- portDataSourceImportError = e;
30
+ let portBackendTask = portsFile;
31
+ let portBackendTaskImportError = null;
32
+ try {
33
+ if (portsFile === undefined) {
34
+ throw "missing";
67
35
  }
36
+ } catch (e) {
37
+ portBackendTaskImportError = e;
38
+ }
68
39
 
69
- if (request.url === "elm-pages-internal://port") {
70
- try {
71
- const { input, portName } = rawRequest.body.args[0];
40
+ if (request.url === "elm-pages-internal://port") {
41
+ try {
42
+ const { input, portName } = rawRequest.body.args[0];
72
43
 
73
- if (!portDataSource[portName]) {
74
- if (portDataSourceImportError === null) {
75
- throw `DataSource.Port.send "${portName}" was called, but I couldn't find a function with that name in the port definitions file. Is it exported correctly?`;
76
- } else if (portDataSourceImportError === "missing") {
77
- throw `DataSource.Port.send "${portName}" was called, but I couldn't find the port definitions file. Be sure to create a 'port-data-source.ts' or 'port-data-source.js' file and maybe restart the dev server.`;
78
- } else {
79
- throw `DataSource.Port.send "${portName}" was called, but I couldn't import the port definitions file, because of this exception: \`${portDataSourceImportError}\` Are there syntax errors or expections thrown during import?`;
80
- }
81
- } else if (typeof portDataSource[portName] !== "function") {
82
- throw `DataSource.Port.send "${portName}" was called, but it is not a function. Be sure to export a function with that name from port-data-source.js`;
83
- }
84
- await fs.promises.writeFile(
85
- responsePath,
86
- JSON.stringify(jsonResponse(await portDataSource[portName](input)))
87
- );
88
- resolve(responsePath);
89
- } catch (error) {
90
- console.trace(error);
91
- reject({
92
- title: "DataSource.Port Error",
93
- message: error.toString(),
44
+ if (portBackendTask === null) {
45
+ resolve({
46
+ kind: "response-json",
47
+ value: jsonResponse({
48
+ "elm-pages-internal-error": "MissingCustomBackendTaskFile",
49
+ }),
94
50
  });
95
- }
96
- } else {
97
- try {
98
- console.time(`fetch ${request.url}`);
99
- const response = await fetch(request.url, {
100
- method: request.method,
101
- body: request.body,
102
- headers: {
103
- "User-Agent": "request",
104
- ...request.headers,
105
- },
51
+ } else if (portBackendTask && portBackendTask.__internalElmPagesError) {
52
+ resolve({
53
+ kind: "response-json",
54
+ value: jsonResponse({
55
+ "elm-pages-internal-error": "ErrorInCustomBackendTaskFile",
56
+ error: portBackendTask.__internalElmPagesError,
57
+ }),
106
58
  });
107
-
108
- console.timeEnd(`fetch ${request.url}`);
109
- const expectString = request.headers["elm-pages-internal"];
110
-
111
- if (response.ok || expectString === "ExpectResponse") {
112
- let body;
113
- let bodyKind;
114
- if (expectString === "ExpectJson") {
115
- body = await response.json();
116
- bodyKind = "json";
117
- } else if (
118
- expectString === "ExpectBytes" ||
119
- expectString === "ExpectBytesResponse"
120
- ) {
121
- bodyKind = "bytes";
122
- const arrayBuffer = await response.arrayBuffer();
123
- body = Buffer.from(arrayBuffer).toString("base64");
124
- } else if (expectString === "ExpectWhatever") {
125
- bodyKind = "whatever";
126
- body = null;
127
- } else if (
128
- expectString === "ExpectResponse" ||
129
- expectString === "ExpectString"
130
- ) {
131
- bodyKind = "string";
132
- body = await response.text();
133
- } else {
134
- throw `Unexpected expectString ${expectString}`;
135
- }
136
-
137
- await fs.promises.writeFile(
138
- responsePath,
139
- JSON.stringify({
140
- headers: Object.fromEntries(response.headers.entries()),
141
- statusCode: response.status,
142
- body: body,
143
- bodyKind,
144
- url: response.url,
145
- statusText: response.statusText,
146
- })
147
- );
148
-
149
- resolve(responsePath);
59
+ } else if (portBackendTask && !portBackendTask[portName]) {
60
+ if (portBackendTaskImportError === null) {
61
+ resolve({
62
+ kind: "response-json",
63
+ value: jsonResponse({
64
+ "elm-pages-internal-error": "CustomBackendTaskNotDefined",
65
+ }),
66
+ });
67
+ } else if (portBackendTaskImportError === "missing") {
68
+ resolve({
69
+ kind: "response-json",
70
+ value: jsonResponse({
71
+ "elm-pages-internal-error": "MissingCustomBackendTaskFile",
72
+ }),
73
+ });
150
74
  } else {
151
- console.log("@@@ request-cache1 bad HTTP response");
152
- reject({
153
- title: "DataSource.Http Error",
154
- message: `${kleur
155
- .yellow()
156
- .underline(request.url)} Bad HTTP response ${response.status} ${
157
- response.statusText
158
- }
159
- `,
75
+ resolve({
76
+ kind: "response-json",
77
+ value: jsonResponse({
78
+ "elm-pages-internal-error": "ErrorInCustomBackendTaskFile",
79
+ error:
80
+ (portBackendTaskImportError &&
81
+ portBackendTaskImportError.stack) ||
82
+ "",
83
+ }),
160
84
  });
161
85
  }
162
- } catch (error) {
163
- console.trace("@@@ request-cache2 HTTP error", error);
164
- reject({
165
- title: "DataSource.Http Error",
166
- message: `${kleur
167
- .yellow()
168
- .underline(request.url)} ${error.toString()}
169
- `,
86
+ } else if (typeof portBackendTask[portName] !== "function") {
87
+ resolve({
88
+ kind: "response-json",
89
+ value: jsonResponse({
90
+ "elm-pages-internal-error": "ExportIsNotFunction",
91
+ error: typeof portBackendTask[portName],
92
+ }),
170
93
  });
94
+ } else {
95
+ try {
96
+ resolve({
97
+ kind: "response-json",
98
+ value: jsonResponse(await portBackendTask[portName](input)),
99
+ });
100
+ } catch (portCallError) {
101
+ resolve({
102
+ kind: "response-json",
103
+ value: jsonResponse({
104
+ "elm-pages-internal-error": "CustomBackendTaskException",
105
+ error: portCallError,
106
+ }),
107
+ });
108
+ }
171
109
  }
110
+ } catch (error) {
111
+ console.trace(error);
112
+ reject({
113
+ title: "BackendTask.Custom Error",
114
+ message: error.toString(),
115
+ });
116
+ }
117
+ } else {
118
+ try {
119
+ console.time(`fetch ${request.url}`);
120
+ const response = await safeFetch(makeFetchHappen, request.url, {
121
+ method: request.method,
122
+ body: request.body,
123
+ headers: {
124
+ "User-Agent": "request",
125
+ ...request.headers,
126
+ },
127
+ ...rawRequest.cacheOptions,
128
+ });
129
+
130
+ console.timeEnd(`fetch ${request.url}`);
131
+ const expectString = request.headers["elm-pages-internal"];
132
+
133
+ let body;
134
+ let bodyKind;
135
+ if (expectString === "ExpectJson") {
136
+ try {
137
+ body = await response.buffer();
138
+ body = JSON.parse(body.toString("utf-8"));
139
+ bodyKind = "json";
140
+ } catch (error) {
141
+ body = body.toString("utf8");
142
+ bodyKind = "string";
143
+ }
144
+ } else if (
145
+ expectString === "ExpectBytes" ||
146
+ expectString === "ExpectBytesResponse"
147
+ ) {
148
+ body = await response.buffer();
149
+ try {
150
+ body = body.toString("base64");
151
+ bodyKind = "bytes";
152
+ } catch (e) {
153
+ body = body.toString("utf8");
154
+ bodyKind = "string";
155
+ }
156
+ } else if (expectString === "ExpectWhatever") {
157
+ bodyKind = "whatever";
158
+ body = null;
159
+ } else if (
160
+ expectString === "ExpectResponse" ||
161
+ expectString === "ExpectString"
162
+ ) {
163
+ bodyKind = "string";
164
+ body = await response.text();
165
+ } else {
166
+ throw `Unexpected expectString ${expectString}`;
167
+ }
168
+
169
+ resolve({
170
+ kind: "response-json",
171
+ value: {
172
+ headers: Object.fromEntries(response.headers.entries()),
173
+ statusCode: response.status,
174
+ body,
175
+ bodyKind,
176
+ url: response.url,
177
+ statusText: response.statusText,
178
+ },
179
+ });
180
+ } catch (error) {
181
+ console.trace("@@@ request-cache2 HTTP error", error);
182
+ reject({
183
+ title: "BackendTask.Http Error",
184
+ message: `${kleur.yellow().underline(request.url)} ${error.toString()}
185
+ `,
186
+ });
172
187
  }
173
188
  }
174
189
  });
@@ -188,15 +203,6 @@ function toRequest(elmRequest) {
188
203
  body: toBody(elmRequest.body),
189
204
  };
190
205
  }
191
- /**
192
- * @param {string} file
193
- */
194
- function checkFileExists(fs, file) {
195
- return fs.promises
196
- .access(file, fs.constants.F_OK)
197
- .then(() => true)
198
- .catch(() => false);
199
- }
200
206
  /**
201
207
  * @param {Body} body
202
208
  */
@@ -208,6 +214,9 @@ function toBody(body) {
208
214
  case "StringBody": {
209
215
  return body.args[1];
210
216
  }
217
+ case "BytesBody": {
218
+ return Buffer.from(body.args[1], "base64");
219
+ }
211
220
  case "JsonBody": {
212
221
  return JSON.stringify(body.args[0]);
213
222
  }
@@ -226,13 +235,16 @@ function toContentType(body) {
226
235
  case "StringBody": {
227
236
  return { "Content-Type": body.args[0] };
228
237
  }
238
+ case "BytesBody": {
239
+ return { "Content-Type": body.args[0] };
240
+ }
229
241
  case "JsonBody": {
230
242
  return { "Content-Type": "application/json" };
231
243
  }
232
244
  }
233
245
  }
234
246
 
235
- /** @typedef { { tag: 'EmptyBody'} | { tag: 'StringBody'; args: [string, string] } | {tag: 'JsonBody'; args: [ Object ] } } Body */
247
+ /** @typedef { { tag: 'EmptyBody'} |{ tag: 'BytesBody'; args: [string, string] } | { tag: 'StringBody'; args: [string, string] } | {tag: 'JsonBody'; args: [ Object ] } } Body */
236
248
  function requireUncached(mode, filePath) {
237
249
  if (mode === "dev-server") {
238
250
  // for the build command, we can skip clearing the cache because it won't change while the build is running
@@ -249,4 +261,30 @@ function jsonResponse(json) {
249
261
  return { bodyKind: "json", body: json };
250
262
  }
251
263
 
252
- module.exports = { lookupOrPerform };
264
+ async function safeFetch(makeFetchHappen, url, options) {
265
+ const { cachePath, ...optionsWithoutCachePath } = options;
266
+ const cachePathWithDefault = cachePath || defaultHttpCachePath;
267
+ if (await canAccess(cachePathWithDefault)) {
268
+ return await makeFetchHappen(url, {
269
+ cachePath: cachePathWithDefault,
270
+ ...options,
271
+ });
272
+ } else {
273
+ return await makeFetchHappen(url, {
274
+ cache: "no-store",
275
+ ...optionsWithoutCachePath,
276
+ });
277
+ }
278
+ }
279
+
280
+ async function canAccess(filePath) {
281
+ try {
282
+ await fsPromises.access(
283
+ filePath,
284
+ fsPromises.constants.R_OK | fsPromises.constants.W_OK
285
+ );
286
+ return true;
287
+ } catch {
288
+ return false;
289
+ }
290
+ }
@@ -1,6 +1,6 @@
1
- const fs = require("fs");
1
+ import * as fs from "node:fs";
2
2
 
3
- module.exports = async function () {
3
+ export async function rewriteClientElmJson() {
4
4
  var elmJson = JSON.parse(
5
5
  (await fs.promises.readFile("./elm.json")).toString()
6
6
  );
@@ -9,11 +9,11 @@ module.exports = async function () {
9
9
 
10
10
  await writeFileIfChanged(
11
11
  "./elm-stuff/elm-pages/client/elm.json",
12
- JSON.stringify(rewriteElmJson(elmJson))
12
+ JSON.stringify(rewriteClientElmJsonHelp(elmJson))
13
13
  );
14
- };
14
+ }
15
15
 
16
- function rewriteElmJson(elmJson) {
16
+ function rewriteClientElmJsonHelp(elmJson) {
17
17
  // The internal generated file will be at:
18
18
  // ./elm-stuff/elm-pages/
19
19
  // So, we need to take the existing elmJson and
@@ -1,19 +1,19 @@
1
- const fs = require("fs");
1
+ import * as fs from "node:fs";
2
2
 
3
- module.exports = async function () {
3
+ export async function rewriteElmJson(sourceElmJsonPath, targetElmJsonPath) {
4
4
  var elmJson = JSON.parse(
5
- (await fs.promises.readFile("./elm.json")).toString()
5
+ (await fs.promises.readFile(sourceElmJsonPath)).toString()
6
6
  );
7
7
 
8
8
  // write new elm.json
9
9
 
10
10
  await writeFileIfChanged(
11
- "./elm-stuff/elm-pages/elm.json",
12
- JSON.stringify(rewriteElmJson(elmJson))
11
+ targetElmJsonPath,
12
+ JSON.stringify(rewriteElmJsonHelp(elmJson))
13
13
  );
14
- };
14
+ }
15
15
 
16
- function rewriteElmJson(elmJson) {
16
+ function rewriteElmJsonHelp(elmJson) {
17
17
  // The internal generated file will be at:
18
18
  // ./elm-stuff/elm-pages/
19
19
  // So, we need to take the existing elmJson and
@@ -1,7 +1,7 @@
1
1
  /**
2
2
  * @param {string[]} name
3
3
  */
4
- function routeParams(name) {
4
+ export function routeParams(name) {
5
5
  return name
6
6
  .map((section) => {
7
7
  const routeParamMatch = section.match(/([A-Z][A-Za-z0-9]*)__?$/);
@@ -17,7 +17,7 @@ function routeParams(name) {
17
17
  * @param {string[]} name
18
18
  * @returns {Segment[]}
19
19
  */
20
- function parseRouteParams(name) {
20
+ export function parseRouteParams(name) {
21
21
  return name.flatMap((section) => {
22
22
  const routeParamMatch = section.match(/([A-Z][A-Za-z0-9]*)(_?_?)$/);
23
23
  const maybeParam = (routeParamMatch && routeParamMatch[1]) || "TODO";
@@ -68,7 +68,7 @@ function parseRouteParams(name) {
68
68
  * @param {string[]} name
69
69
  * @returns {( Segment | {kind: 'static'; name: string})[]}
70
70
  */
71
- function parseRouteParamsWithStatic(name) {
71
+ export function parseRouteParamsWithStatic(name) {
72
72
  return name.flatMap((section) => {
73
73
  const routeParamMatch = section.match(/([A-Z][A-Za-z0-9]*)(_?_?)$/);
74
74
  const maybeParam = (routeParamMatch && routeParamMatch[1]) || "TODO";
@@ -123,7 +123,7 @@ function parseRouteParamsWithStatic(name) {
123
123
  * @param {string[]} name
124
124
  * @returns {string}
125
125
  */
126
- function routeVariantDefinition(name) {
126
+ export function routeVariantDefinition(name) {
127
127
  const newLocal = parseRouteParams(name);
128
128
  if (newLocal.length == 0) {
129
129
  return routeVariant(name);
@@ -151,7 +151,7 @@ function routeVariantDefinition(name) {
151
151
  * @param {string[]} name
152
152
  * @returns {string}
153
153
  */
154
- function toPathPattern(name) {
154
+ export function toPathPattern(name) {
155
155
  return (
156
156
  "/" +
157
157
  parseRouteParamsWithStatic(name)
@@ -181,7 +181,7 @@ function toPathPattern(name) {
181
181
  * @param {string[]} name
182
182
  * @returns {string[]}
183
183
  */
184
- function toPathPatterns(name) {
184
+ export function toPathPatterns(name) {
185
185
  const segments = parseRouteParamsWithStatic(name);
186
186
 
187
187
  const lastSegment = segments[segments.length - 1];
@@ -199,7 +199,7 @@ function toPathPatterns(name) {
199
199
  /**
200
200
  * @param {string[]} segments
201
201
  */
202
- function joinPath(segments) {
202
+ export function joinPath(segments) {
203
203
  const joined = segments.join("/");
204
204
  if (joined.startsWith("/")) {
205
205
  return joined;
@@ -211,7 +211,7 @@ function joinPath(segments) {
211
211
  /**
212
212
  * @return {string[]}
213
213
  */
214
- function newHelper(segments) {
214
+ export function newHelper(segments) {
215
215
  return segments.map((param) => {
216
216
  switch (param.kind) {
217
217
  case "static": {
@@ -238,7 +238,7 @@ function newHelper(segments) {
238
238
  * @param {string[]} name
239
239
  * @returns {string}
240
240
  */
241
- function toElmPathPattern(name) {
241
+ export function toElmPathPattern(name) {
242
242
  const parsedSegments = parseRouteParamsWithStatic(name);
243
243
 
244
244
  const foundEndings = parsedSegments.flatMap((segment) => {
@@ -297,14 +297,14 @@ function toElmPathPattern(name) {
297
297
  * @param {string} input
298
298
  * @returns {string}
299
299
  */
300
- function camelToKebab(input) {
300
+ export function camelToKebab(input) {
301
301
  return input.replace(/([a-z])([A-Z])/g, "$1-$2").toLowerCase();
302
302
  }
303
303
 
304
304
  /**
305
305
  * @param {string[]} name
306
306
  */
307
- function paramsRecord(name) {
307
+ export function paramsRecord(name) {
308
308
  return `{ ${parseRouteParams(name).map((param) => {
309
309
  switch (param.kind) {
310
310
  case "dynamic": {
@@ -326,7 +326,7 @@ function paramsRecord(name) {
326
326
  /**
327
327
  * @param {string[]} name
328
328
  */
329
- function routeVariant(name) {
329
+ export function routeVariant(name) {
330
330
  return `${name.join("__")}`;
331
331
  }
332
332
 
@@ -334,44 +334,29 @@ function routeVariant(name) {
334
334
  * @param {string[]} name
335
335
  * @param {string} paramsName
336
336
  */
337
- function destructureRoute(name, paramsName) {
337
+ export function destructureRoute(name, paramsName) {
338
338
  return emptyRouteParams(name)
339
339
  ? `Route.${routeVariant(name)}`
340
340
  : `(Route.${routeVariant(name)} ${paramsName})`;
341
341
  }
342
342
 
343
- function referenceRouteParams(name, paramsName) {
343
+ export function referenceRouteParams(name, paramsName) {
344
344
  return emptyRouteParams(name) ? `{}` : paramsName;
345
345
  }
346
346
  /**
347
347
  * @param {string[]} name
348
348
  */
349
- function emptyRouteParams(name) {
349
+ export function emptyRouteParams(name) {
350
350
  return parseRouteParams(name).length === 0;
351
351
  }
352
352
 
353
353
  /**
354
354
  * @param {string} name
355
355
  */
356
- function toFieldName(name) {
356
+ export function toFieldName(name) {
357
357
  if (name === "SPLAT") {
358
358
  return "splat";
359
359
  } else {
360
360
  return name.charAt(0).toLowerCase() + name.slice(1);
361
361
  }
362
362
  }
363
-
364
- module.exports = {
365
- routeParams,
366
- routeVariantDefinition,
367
- routeVariant,
368
- toFieldName,
369
- paramsRecord,
370
- toPathPattern,
371
- toPathPatterns,
372
- parseRouteParams,
373
- parseRouteParamsWithStatic,
374
- toElmPathPattern,
375
- destructureRoute,
376
- referenceRouteParams,
377
- };
@@ -1,11 +1,9 @@
1
- module.exports = { gather };
2
-
3
1
  /** @typedef { { type: 'root'; keyValuePair: [string, string] } } RootTagModifier */
4
2
 
5
3
  /**
6
4
  * @param {( SeoTag | RootTagModifier )[]} tags
7
5
  */
8
- function gather(tags) {
6
+ export function gather(tags) {
9
7
  const withoutRootModifiers = tags.flatMap((value) => {
10
8
  if (value.type === "root") {
11
9
  return [];
@@ -43,11 +41,16 @@ function headTag(rootModifiers) {
43
41
 
44
42
  function toString(/** @type { SeoTag[] } */ tags) {
45
43
  return tags
46
- .map((headTag) => {
44
+ .flatMap((headTag) => {
47
45
  if (headTag.type === "head") {
48
- return appendTag(headTag);
46
+ return [appendTag(headTag)];
49
47
  } else if (headTag.type === "json-ld") {
50
- return appendJsonLdTag(headTag);
48
+ return [appendJsonLdTag(headTag)];
49
+ } else if (headTag.type === "stripped") {
50
+ console.warn(
51
+ `WARNING: Head.nonLoadingTag value ignored because it used a loading tag: ${headTag.message}`
52
+ );
53
+ return [];
51
54
  } else {
52
55
  throw new Error(`Unknown tag type ${JSON.stringify(headTag)}`);
53
56
  }
@@ -55,7 +58,7 @@ function toString(/** @type { SeoTag[] } */ tags) {
55
58
  .join("");
56
59
  }
57
60
 
58
- /** @typedef {HeadTag | JsonLdTag} SeoTag */
61
+ /** @typedef {HeadTag | JsonLdTag | StrippedTag} SeoTag */
59
62
 
60
63
  /** @typedef {{ name: string; attributes: string[][]; type: 'head' }} HeadTag */
61
64
  function appendTag(/** @type {HeadTag} */ tagDetails) {
@@ -66,6 +69,8 @@ function appendTag(/** @type {HeadTag} */ tagDetails) {
66
69
  }
67
70
 
68
71
  /** @typedef {{ contents: Object; type: 'json-ld' }} JsonLdTag */
72
+ /** @typedef {{ message: string; type: 'stripped' }} StrippedTag */
73
+
69
74
  function appendJsonLdTag(/** @type {JsonLdTag} */ tagDetails) {
70
75
  return `<script type="application/ld+json">
71
76
  ${JSON.stringify(tagDetails.contents)}