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

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 (129) 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 +1447 -342
  11. package/generator/dead-code-review/elm-stuff/tests-0.19.1/js/Runner.elm.js +16458 -13724
  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 +9 -7
  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/Pages-Internal-RoutePattern.elmi +0 -0
  18. package/generator/review/elm-stuff/tests-0.19.1/elm-stuff/0.19.1/Pages-Internal-RoutePattern.elmo +0 -0
  19. package/generator/review/elm-stuff/tests-0.19.1/elm-stuff/0.19.1/Pages-Review-NoContractViolations.elmi +0 -0
  20. package/generator/review/elm-stuff/tests-0.19.1/elm-stuff/0.19.1/Pages-Review-NoContractViolations.elmo +0 -0
  21. package/generator/review/elm-stuff/tests-0.19.1/elm-stuff/0.19.1/d.dat +0 -0
  22. package/generator/review/elm-stuff/tests-0.19.1/elm-stuff/0.19.1/i.dat +0 -0
  23. package/generator/review/elm-stuff/tests-0.19.1/elm-stuff/0.19.1/o.dat +0 -0
  24. package/generator/review/elm-stuff/tests-0.19.1/elm.json +1 -1
  25. package/generator/review/elm-stuff/tests-0.19.1/js/Reporter.elm.js +1447 -342
  26. package/generator/review/elm-stuff/tests-0.19.1/js/Runner.elm.js +24542 -21748
  27. package/generator/review/elm-stuff/tests-0.19.1/js/node_runner.js +1 -1
  28. package/generator/review/elm-stuff/tests-0.19.1/js/node_supervisor.js +4 -4
  29. package/generator/review/elm.json +10 -10
  30. package/generator/src/RouteBuilder.elm +113 -107
  31. package/generator/src/SharedTemplate.elm +3 -2
  32. package/generator/src/SiteConfig.elm +3 -2
  33. package/generator/src/basepath-middleware.js +3 -3
  34. package/generator/src/build.js +125 -88
  35. package/generator/src/cli.js +273 -88
  36. package/generator/src/codegen.js +29 -27
  37. package/generator/src/compatibility-key.js +3 -0
  38. package/generator/src/compile-elm.js +43 -26
  39. package/generator/src/config.js +39 -0
  40. package/generator/src/copy-dir.js +2 -2
  41. package/generator/src/dev-server.js +150 -133
  42. package/generator/src/dir-helpers.js +9 -26
  43. package/generator/src/elm-codegen.js +5 -4
  44. package/generator/src/elm-file-constants.js +2 -3
  45. package/generator/src/error-formatter.js +12 -11
  46. package/generator/src/file-helpers.js +3 -4
  47. package/generator/src/generate-template-module-connector.js +23 -22
  48. package/generator/src/init.js +9 -8
  49. package/generator/src/pre-render-html.js +39 -28
  50. package/generator/src/render-test.js +109 -0
  51. package/generator/src/render-worker.js +25 -28
  52. package/generator/src/render.js +320 -142
  53. package/generator/src/request-cache.js +252 -163
  54. package/generator/src/resolve-elm-module.js +63 -0
  55. package/generator/src/rewrite-client-elm-json.js +6 -5
  56. package/generator/src/rewrite-elm-json-help.js +56 -0
  57. package/generator/src/rewrite-elm-json.js +17 -7
  58. package/generator/src/route-codegen-helpers.js +16 -31
  59. package/generator/src/seo-renderer.js +12 -7
  60. package/generator/src/vite-utils.js +77 -0
  61. package/generator/static-code/hmr.js +79 -13
  62. package/generator/template/app/Api.elm +6 -5
  63. package/generator/template/app/Effect.elm +123 -0
  64. package/generator/template/app/ErrorPage.elm +37 -6
  65. package/generator/template/app/Route/Index.elm +17 -10
  66. package/generator/template/app/Shared.elm +24 -47
  67. package/generator/template/app/Site.elm +19 -6
  68. package/generator/template/app/View.elm +1 -8
  69. package/generator/template/elm-tooling.json +0 -3
  70. package/generator/template/elm.json +32 -24
  71. package/generator/template/package.json +10 -4
  72. package/package.json +29 -27
  73. package/src/ApiRoute.elm +199 -61
  74. package/src/BackendTask/Custom.elm +325 -0
  75. package/src/BackendTask/Env.elm +90 -0
  76. package/src/{DataSource → BackendTask}/File.elm +128 -43
  77. package/src/{DataSource → BackendTask}/Glob.elm +136 -125
  78. package/src/BackendTask/Http.elm +673 -0
  79. package/src/{DataSource → BackendTask}/Internal/Glob.elm +1 -1
  80. package/src/BackendTask/Internal/Request.elm +28 -0
  81. package/src/BackendTask/Random.elm +79 -0
  82. package/src/BackendTask/Time.elm +47 -0
  83. package/src/BackendTask.elm +537 -0
  84. package/src/FatalError.elm +89 -0
  85. package/src/Form/Field.elm +21 -9
  86. package/src/Form/FieldView.elm +94 -14
  87. package/src/Form.elm +275 -400
  88. package/src/Head.elm +237 -7
  89. package/src/HtmlPrinter.elm +7 -3
  90. package/src/Internal/ApiRoute.elm +7 -5
  91. package/src/PageServerResponse.elm +6 -1
  92. package/src/Pages/FormState.elm +6 -5
  93. package/src/Pages/GeneratorProgramConfig.elm +15 -0
  94. package/src/Pages/Internal/FatalError.elm +5 -0
  95. package/src/Pages/Internal/Form.elm +21 -1
  96. package/src/Pages/{Msg.elm → Internal/Msg.elm} +26 -16
  97. package/src/Pages/Internal/Platform/Cli.elm +598 -763
  98. package/src/Pages/Internal/Platform/CompatibilityKey.elm +6 -0
  99. package/src/Pages/Internal/Platform/Effect.elm +1 -2
  100. package/src/Pages/Internal/Platform/GeneratorApplication.elm +373 -0
  101. package/src/Pages/Internal/Platform/StaticResponses.elm +73 -270
  102. package/src/Pages/Internal/Platform/ToJsPayload.elm +4 -7
  103. package/src/Pages/Internal/Platform.elm +216 -102
  104. package/src/Pages/Internal/Script.elm +17 -0
  105. package/src/Pages/Internal/StaticHttpBody.elm +35 -1
  106. package/src/Pages/Manifest.elm +29 -4
  107. package/src/Pages/PageUrl.elm +23 -9
  108. package/src/Pages/ProgramConfig.elm +14 -10
  109. package/src/Pages/Script.elm +109 -0
  110. package/src/Pages/SiteConfig.elm +3 -2
  111. package/src/Pages/StaticHttp/Request.elm +2 -2
  112. package/src/Pages/StaticHttpRequest.elm +23 -98
  113. package/src/PagesMsg.elm +92 -0
  114. package/src/Path.elm +16 -19
  115. package/src/QueryParams.elm +21 -172
  116. package/src/RequestsAndPending.elm +8 -19
  117. package/src/Result/Extra.elm +26 -0
  118. package/src/Scaffold/Form.elm +560 -0
  119. package/src/Scaffold/Route.elm +1388 -0
  120. package/src/Server/Request.elm +43 -37
  121. package/src/Server/Session.elm +62 -42
  122. package/src/Server/SetCookie.elm +12 -4
  123. package/src/Test/Html/Internal/ElmHtml/ToString.elm +8 -9
  124. package/src/DataSource/Env.elm +0 -38
  125. package/src/DataSource/Http.elm +0 -446
  126. package/src/DataSource/Internal/Request.elm +0 -20
  127. package/src/DataSource/Port.elm +0 -90
  128. package/src/DataSource.elm +0 -538
  129. 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
+ }
@@ -0,0 +1,63 @@
1
+ import * as fs from "node:fs";
2
+ import * as path from "node:path";
3
+
4
+ function findNearestElmJson(filePath) {
5
+ function searchForElmJson(directory) {
6
+ if (directory === "/") {
7
+ return null;
8
+ }
9
+
10
+ const elmJsonPath = path.join(directory, "elm.json");
11
+ return fs.existsSync(elmJsonPath)
12
+ ? elmJsonPath
13
+ : searchForElmJson(path.dirname(directory));
14
+ }
15
+
16
+ return searchForElmJson(path.dirname(filePath));
17
+ }
18
+
19
+ function getElmModuleName(inputPath) {
20
+ const filePath = path.normalize(
21
+ path.isAbsolute(inputPath) ? inputPath : path.resolve(inputPath)
22
+ );
23
+ const elmJsonPath = findNearestElmJson(filePath);
24
+
25
+ if (!elmJsonPath) {
26
+ throw new Error("No elm.json found");
27
+ }
28
+
29
+ const elmJson = JSON.parse(fs.readFileSync(elmJsonPath, "utf8"));
30
+ const sourceDirectories = elmJson["source-directories"];
31
+ const projectDirectory = path.dirname(elmJsonPath);
32
+
33
+ const matchingSourceDir = sourceDirectories
34
+ .map((sourceDir) => path.join(projectDirectory, sourceDir))
35
+ .find((absoluteSourceDir) => filePath.startsWith(absoluteSourceDir));
36
+
37
+ if (!matchingSourceDir) {
38
+ throw new Error(
39
+ "File is not in any source-directories specified in elm.json"
40
+ );
41
+ }
42
+
43
+ const relativePath = path.relative(matchingSourceDir, filePath);
44
+ const moduleName = relativePath
45
+ .replace(path.extname(relativePath), "")
46
+ .replace("/", ".");
47
+
48
+ return { projectDirectory, moduleName, sourceDirectory: matchingSourceDir };
49
+ }
50
+
51
+ export function resolveInputPathOrModuleName(inputPathOrModuleName) {
52
+ if (
53
+ /^[A-Z][a-zA-Z0-9_]*(\.[A-Z][a-zA-Z0-9_]*)*$/.test(inputPathOrModuleName)
54
+ ) {
55
+ return {
56
+ moduleName: inputPathOrModuleName,
57
+ projectDirectory: "./script",
58
+ sourceDirectory: "./script/src",
59
+ };
60
+ } else {
61
+ return getElmModuleName(inputPathOrModuleName);
62
+ }
63
+ }
@@ -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
@@ -27,6 +27,7 @@ function rewriteElmJson(elmJson) {
27
27
  elmJson["source-directories"] = elmJson["source-directories"].map((item) => {
28
28
  return "../../../" + item;
29
29
  });
30
+ elmJson["dependencies"]["direct"]["lamdera/codecs"] = "1.0.0";
30
31
  // 3. add our own secret My.elm module 😈
31
32
  elmJson["source-directories"].push(".elm-pages");
32
33
  elmJson["source-directories"].push("app");
@@ -0,0 +1,56 @@
1
+ import * as fs from "node:fs";
2
+ import * as path from "node:path";
3
+
4
+ /**
5
+ * @param {string} sourceElmJsonPath
6
+ * @param {string} targetElmJsonPath
7
+ * @param {( (arg0: JSON) => JSON )?} modifyElmJson
8
+ */
9
+ export async function rewriteElmJson(
10
+ sourceElmJsonPath,
11
+ targetElmJsonPath,
12
+ modifyElmJson
13
+ ) {
14
+ if (!modifyElmJson) {
15
+ modifyElmJson = function (json) {
16
+ return json;
17
+ };
18
+ }
19
+ var elmJson = JSON.parse(
20
+ (
21
+ await fs.promises.readFile(path.join(sourceElmJsonPath, "elm.json"))
22
+ ).toString()
23
+ );
24
+
25
+ let modifiedElmJson = modifyElmJson(elmJson);
26
+ // always add `lamdera/codecs` dependency
27
+ modifiedElmJson["dependencies"]["direct"]["lamdera/codecs"] = "1.0.0";
28
+
29
+ // write new elm.json
30
+ await writeFileIfChanged(
31
+ path.join(targetElmJsonPath, "elm.json"),
32
+ JSON.stringify(modifiedElmJson)
33
+ );
34
+ }
35
+
36
+ /**
37
+ * @param {fs.PathLike | fs.promises.FileHandle} filePath
38
+ * @param {string | NodeJS.ArrayBufferView | Iterable<string | NodeJS.ArrayBufferView> | AsyncIterable<string | NodeJS.ArrayBufferView> | import("stream").Stream} content
39
+ */
40
+ async function writeFileIfChanged(filePath, content) {
41
+ if (
42
+ !(await fileExists(filePath)) ||
43
+ (await fs.promises.readFile(filePath, "utf8")) !== content
44
+ ) {
45
+ await fs.promises.writeFile(filePath, content);
46
+ }
47
+ }
48
+ /**
49
+ * @param {fs.PathLike} file
50
+ */
51
+ function fileExists(file) {
52
+ return fs.promises
53
+ .access(file, fs.constants.F_OK)
54
+ .then(() => true)
55
+ .catch(() => false);
56
+ }
@@ -1,19 +1,23 @@
1
- const fs = require("fs");
1
+ import * as fs from "node:fs";
2
2
 
3
- module.exports = async function () {
3
+ export async function rewriteElmJson(
4
+ sourceElmJsonPath,
5
+ targetElmJsonPath,
6
+ options
7
+ ) {
4
8
  var elmJson = JSON.parse(
5
- (await fs.promises.readFile("./elm.json")).toString()
9
+ (await fs.promises.readFile(sourceElmJsonPath)).toString()
6
10
  );
7
11
 
8
12
  // write new elm.json
9
13
 
10
14
  await writeFileIfChanged(
11
- "./elm-stuff/elm-pages/elm.json",
12
- JSON.stringify(rewriteElmJson(elmJson))
15
+ targetElmJsonPath,
16
+ JSON.stringify(rewriteElmJsonHelp(elmJson, options))
13
17
  );
14
- };
18
+ }
15
19
 
16
- function rewriteElmJson(elmJson) {
20
+ function rewriteElmJsonHelp(elmJson, options) {
17
21
  // The internal generated file will be at:
18
22
  // ./elm-stuff/elm-pages/
19
23
  // So, we need to take the existing elmJson and
@@ -27,6 +31,12 @@ function rewriteElmJson(elmJson) {
27
31
  elmJson["source-directories"] = elmJson["source-directories"].map((item) => {
28
32
  return "../../" + item;
29
33
  });
34
+ if (options && options.executableName === "elm") {
35
+ // elm, don't add lamdera/codecs
36
+ } else {
37
+ // lamdera, add codecs dependency
38
+ elmJson["dependencies"]["direct"]["lamdera/codecs"] = "1.0.0";
39
+ }
30
40
  // 3. add our own secret My.elm module 😈
31
41
  elmJson["source-directories"].push(".elm-pages");
32
42
  return elmJson;