elm-pages 3.0.0-beta.3 → 3.0.0-beta.30

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 (123) hide show
  1. package/README.md +10 -1
  2. package/codegen/{elm-pages-codegen.js → elm-pages-codegen.cjs} +2864 -2589
  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 +1327 -122
  11. package/generator/dead-code-review/elm-stuff/tests-0.19.1/js/Runner.elm.js +15295 -13271
  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 +4 -4
  14. package/generator/dead-code-review/elm.json +8 -6
  15. package/generator/dead-code-review/src/Pages/Review/DeadCodeEliminateData.elm +59 -10
  16. package/generator/dead-code-review/tests/Pages/Review/DeadCodeEliminateDataTest.elm +52 -36
  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 +1327 -122
  22. package/generator/review/elm-stuff/tests-0.19.1/js/Runner.elm.js +14621 -12637
  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 +4 -4
  25. package/generator/review/elm.json +8 -8
  26. package/generator/src/RouteBuilder.elm +113 -107
  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 +123 -87
  31. package/generator/src/cli.js +256 -77
  32. package/generator/src/codegen.js +29 -27
  33. package/generator/src/compatibility-key.js +3 -0
  34. package/generator/src/compile-elm.js +25 -25
  35. package/generator/src/config.js +39 -0
  36. package/generator/src/copy-dir.js +2 -2
  37. package/generator/src/dev-server.js +150 -133
  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 +23 -22
  44. package/generator/src/init.js +9 -8
  45. package/generator/src/pre-render-html.js +39 -28
  46. package/generator/src/render-test.js +109 -0
  47. package/generator/src/render-worker.js +25 -28
  48. package/generator/src/render.js +322 -142
  49. package/generator/src/request-cache.js +252 -163
  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 +79 -13
  56. package/generator/template/app/Api.elm +6 -5
  57. package/generator/template/app/Effect.elm +123 -0
  58. package/generator/template/app/ErrorPage.elm +37 -6
  59. package/generator/template/app/Route/Index.elm +17 -10
  60. package/generator/template/app/Shared.elm +24 -47
  61. package/generator/template/app/Site.elm +19 -6
  62. package/generator/template/app/View.elm +1 -8
  63. package/generator/template/elm-tooling.json +0 -3
  64. package/generator/template/elm.json +34 -25
  65. package/generator/template/package.json +10 -4
  66. package/package.json +23 -22
  67. package/src/ApiRoute.elm +199 -61
  68. package/src/BackendTask/Custom.elm +325 -0
  69. package/src/BackendTask/Env.elm +90 -0
  70. package/src/{DataSource → BackendTask}/File.elm +128 -43
  71. package/src/{DataSource → BackendTask}/Glob.elm +136 -125
  72. package/src/BackendTask/Http.elm +673 -0
  73. package/src/{DataSource → BackendTask}/Internal/Glob.elm +1 -1
  74. package/src/BackendTask/Internal/Request.elm +28 -0
  75. package/src/BackendTask/Random.elm +79 -0
  76. package/src/BackendTask/Time.elm +47 -0
  77. package/src/BackendTask.elm +537 -0
  78. package/src/FatalError.elm +89 -0
  79. package/src/Form/Field.elm +21 -9
  80. package/src/Form/FieldView.elm +94 -14
  81. package/src/Form.elm +275 -400
  82. package/src/Head.elm +237 -7
  83. package/src/HtmlPrinter.elm +7 -3
  84. package/src/Internal/ApiRoute.elm +7 -5
  85. package/src/PageServerResponse.elm +6 -1
  86. package/src/Pages/FormState.elm +6 -5
  87. package/src/Pages/GeneratorProgramConfig.elm +15 -0
  88. package/src/Pages/Internal/FatalError.elm +5 -0
  89. package/src/Pages/Internal/Form.elm +21 -1
  90. package/src/Pages/{Msg.elm → Internal/Msg.elm} +26 -16
  91. package/src/Pages/Internal/Platform/Cli.elm +507 -763
  92. package/src/Pages/Internal/Platform/CompatibilityKey.elm +6 -0
  93. package/src/Pages/Internal/Platform/Effect.elm +1 -2
  94. package/src/Pages/Internal/Platform/GeneratorApplication.elm +373 -0
  95. package/src/Pages/Internal/Platform/StaticResponses.elm +73 -270
  96. package/src/Pages/Internal/Platform/ToJsPayload.elm +4 -7
  97. package/src/Pages/Internal/Platform.elm +215 -102
  98. package/src/Pages/Internal/Script.elm +17 -0
  99. package/src/Pages/Internal/StaticHttpBody.elm +35 -1
  100. package/src/Pages/Manifest.elm +29 -4
  101. package/src/Pages/PageUrl.elm +23 -9
  102. package/src/Pages/ProgramConfig.elm +14 -10
  103. package/src/Pages/Script.elm +109 -0
  104. package/src/Pages/SiteConfig.elm +3 -2
  105. package/src/Pages/StaticHttp/Request.elm +2 -2
  106. package/src/Pages/StaticHttpRequest.elm +23 -98
  107. package/src/PagesMsg.elm +92 -0
  108. package/src/Path.elm +16 -19
  109. package/src/QueryParams.elm +21 -172
  110. package/src/RequestsAndPending.elm +8 -19
  111. package/src/Result/Extra.elm +26 -0
  112. package/src/Scaffold/Form.elm +484 -0
  113. package/src/Scaffold/Route.elm +1376 -0
  114. package/src/Server/Request.elm +43 -37
  115. package/src/Server/Session.elm +34 -34
  116. package/src/Server/SetCookie.elm +1 -1
  117. package/src/Test/Html/Internal/ElmHtml/ToString.elm +8 -9
  118. package/src/DataSource/Env.elm +0 -38
  119. package/src/DataSource/Http.elm +0 -446
  120. package/src/DataSource/Internal/Request.elm +0 -20
  121. package/src/DataSource/Port.elm +0 -90
  122. package/src/DataSource.elm +0 -538
  123. package/src/Pages/Generate.elm +0 -800
@@ -1,179 +1,245 @@
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];
72
-
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(),
40
+ if (request.url === "elm-pages-internal://port") {
41
+ try {
42
+ const { input, portName } = rawRequest.body.args[0];
43
+
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
+ console.time(`BackendTask.Custom.run "${portName}"`);
96
+ try {
97
+ resolve({
98
+ kind: "response-json",
99
+ value: jsonResponse(
100
+ toElmJson(await portBackendTask[portName](input))
101
+ ),
102
+ });
103
+ } catch (portCallError) {
104
+ if (portCallError instanceof Error) {
105
+ resolve({
106
+ kind: "response-json",
107
+ value: jsonResponse({
108
+ "elm-pages-internal-error": "NonJsonException",
109
+ error: portCallError.message,
110
+ stack: portCallError.stack || null,
111
+ }),
112
+ });
113
+ }
114
+ try {
115
+ resolve({
116
+ kind: "response-json",
117
+ value: jsonResponse({
118
+ "elm-pages-internal-error": "CustomBackendTaskException",
119
+ error: JSON.parse(JSON.stringify(portCallError, null, 0)),
120
+ }),
121
+ });
122
+ } catch (jsonDecodeError) {
123
+ resolve({
124
+ kind: "response-json",
125
+ value: jsonResponse({
126
+ "elm-pages-internal-error": "NonJsonException",
127
+ error: portCallError.toString(),
128
+ }),
129
+ });
130
+ }
131
+ }
132
+ console.timeEnd(`BackendTask.Custom.run "${portName}"`);
171
133
  }
134
+ } catch (error) {
135
+ console.trace(error);
136
+ reject({
137
+ title: "BackendTask.Custom Error",
138
+ message: error.toString(),
139
+ });
140
+ }
141
+ } else {
142
+ try {
143
+ console.time(`fetch ${request.url}`);
144
+ const response = await safeFetch(makeFetchHappen, request.url, {
145
+ method: request.method,
146
+ body: request.body,
147
+ headers: {
148
+ "User-Agent": "request",
149
+ ...request.headers,
150
+ },
151
+ ...rawRequest.cacheOptions,
152
+ });
153
+
154
+ console.timeEnd(`fetch ${request.url}`);
155
+ const expectString = request.headers["elm-pages-internal"];
156
+
157
+ let body;
158
+ let bodyKind;
159
+ if (expectString === "ExpectJson") {
160
+ try {
161
+ body = await response.buffer();
162
+ body = JSON.parse(body.toString("utf-8"));
163
+ bodyKind = "json";
164
+ } catch (error) {
165
+ body = body.toString("utf8");
166
+ bodyKind = "string";
167
+ }
168
+ } else if (
169
+ expectString === "ExpectBytes" ||
170
+ expectString === "ExpectBytesResponse"
171
+ ) {
172
+ body = await response.buffer();
173
+ try {
174
+ body = body.toString("base64");
175
+ bodyKind = "bytes";
176
+ } catch (e) {
177
+ body = body.toString("utf8");
178
+ bodyKind = "string";
179
+ }
180
+ } else if (expectString === "ExpectWhatever") {
181
+ bodyKind = "whatever";
182
+ body = null;
183
+ } else if (
184
+ expectString === "ExpectResponse" ||
185
+ expectString === "ExpectString"
186
+ ) {
187
+ bodyKind = "string";
188
+ body = await response.text();
189
+ } else {
190
+ throw `Unexpected expectString ${expectString}`;
191
+ }
192
+
193
+ resolve({
194
+ kind: "response-json",
195
+ value: {
196
+ headers: Object.fromEntries(response.headers.entries()),
197
+ statusCode: response.status,
198
+ body,
199
+ bodyKind,
200
+ url: response.url,
201
+ statusText: response.statusText,
202
+ },
203
+ });
204
+ } catch (error) {
205
+ console.trace("@@@ request-cache2 HTTP error", error);
206
+ reject({
207
+ title: "BackendTask.Http Error",
208
+ message: `${kleur.yellow().underline(request.url)} ${error.toString()}
209
+ `,
210
+ });
172
211
  }
173
212
  }
174
213
  });
175
214
  }
176
215
 
216
+ /**
217
+ * @param {unknown} obj
218
+ * @returns {JSON}
219
+ */
220
+ function toElmJson(obj) {
221
+ if (Array.isArray(obj)) {
222
+ return obj.map(toElmJson);
223
+ } else if (typeof obj === "object") {
224
+ for (let key in obj) {
225
+ const value = obj[key];
226
+ if (typeof value === "undefined") {
227
+ obj[key] = null;
228
+ } else if (value instanceof Date) {
229
+ obj[key] = {
230
+ "__elm-pages-normalized__": {
231
+ kind: "Date",
232
+ value: Math.floor(value.getTime()),
233
+ },
234
+ };
235
+ // } else if (value instanceof Object) {
236
+ // toElmJson(obj);
237
+ }
238
+ }
239
+ }
240
+ return obj;
241
+ }
242
+
177
243
  /**
178
244
  * @param {{url: string; headers: {[x: string]: string}; method: string; body: Body } } elmRequest
179
245
  */
@@ -188,15 +254,6 @@ function toRequest(elmRequest) {
188
254
  body: toBody(elmRequest.body),
189
255
  };
190
256
  }
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
257
  /**
201
258
  * @param {Body} body
202
259
  */
@@ -208,6 +265,9 @@ function toBody(body) {
208
265
  case "StringBody": {
209
266
  return body.args[1];
210
267
  }
268
+ case "BytesBody": {
269
+ return Buffer.from(body.args[1], "base64");
270
+ }
211
271
  case "JsonBody": {
212
272
  return JSON.stringify(body.args[0]);
213
273
  }
@@ -226,13 +286,16 @@ function toContentType(body) {
226
286
  case "StringBody": {
227
287
  return { "Content-Type": body.args[0] };
228
288
  }
289
+ case "BytesBody": {
290
+ return { "Content-Type": body.args[0] };
291
+ }
229
292
  case "JsonBody": {
230
293
  return { "Content-Type": "application/json" };
231
294
  }
232
295
  }
233
296
  }
234
297
 
235
- /** @typedef { { tag: 'EmptyBody'} | { tag: 'StringBody'; args: [string, string] } | {tag: 'JsonBody'; args: [ Object ] } } Body */
298
+ /** @typedef { { tag: 'EmptyBody'} |{ tag: 'BytesBody'; args: [string, string] } | { tag: 'StringBody'; args: [string, string] } | {tag: 'JsonBody'; args: [ Object ] } } Body */
236
299
  function requireUncached(mode, filePath) {
237
300
  if (mode === "dev-server") {
238
301
  // for the build command, we can skip clearing the cache because it won't change while the build is running
@@ -249,4 +312,30 @@ function jsonResponse(json) {
249
312
  return { bodyKind: "json", body: json };
250
313
  }
251
314
 
252
- module.exports = { lookupOrPerform };
315
+ async function safeFetch(makeFetchHappen, url, options) {
316
+ const { cachePath, ...optionsWithoutCachePath } = options;
317
+ const cachePathWithDefault = cachePath || defaultHttpCachePath;
318
+ if (await canAccess(cachePathWithDefault)) {
319
+ return await makeFetchHappen(url, {
320
+ cachePath: cachePathWithDefault,
321
+ ...options,
322
+ });
323
+ } else {
324
+ return await makeFetchHappen(url, {
325
+ cache: "no-store",
326
+ ...optionsWithoutCachePath,
327
+ });
328
+ }
329
+ }
330
+
331
+ async function canAccess(filePath) {
332
+ try {
333
+ await fsPromises.access(
334
+ filePath,
335
+ fsPromises.constants.R_OK | fsPromises.constants.W_OK
336
+ );
337
+ return true;
338
+ } catch {
339
+ return false;
340
+ }
341
+ }
@@ -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
- };