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.
- package/README.md +10 -1
- package/codegen/{elm-pages-codegen.js → elm-pages-codegen.cjs} +2864 -2589
- package/generator/dead-code-review/elm-stuff/tests-0.19.1/elm-stuff/0.19.1/Pages-Review-DeadCodeEliminateData.elmi +0 -0
- package/generator/dead-code-review/elm-stuff/tests-0.19.1/elm-stuff/0.19.1/Pages-Review-DeadCodeEliminateData.elmo +0 -0
- package/generator/dead-code-review/elm-stuff/tests-0.19.1/elm-stuff/0.19.1/Pages-Review-DeadCodeEliminateDataTest.elmo +0 -0
- package/generator/dead-code-review/elm-stuff/tests-0.19.1/elm-stuff/0.19.1/d.dat +0 -0
- package/generator/dead-code-review/elm-stuff/tests-0.19.1/elm-stuff/0.19.1/i.dat +0 -0
- package/generator/dead-code-review/elm-stuff/tests-0.19.1/elm-stuff/0.19.1/o.dat +0 -0
- package/generator/dead-code-review/elm-stuff/tests-0.19.1/elm.json +1 -1
- package/generator/dead-code-review/elm-stuff/tests-0.19.1/js/Reporter.elm.js +1447 -342
- package/generator/dead-code-review/elm-stuff/tests-0.19.1/js/Runner.elm.js +16458 -13724
- package/generator/dead-code-review/elm-stuff/tests-0.19.1/js/node_runner.js +1 -1
- package/generator/dead-code-review/elm-stuff/tests-0.19.1/js/node_supervisor.js +4 -4
- package/generator/dead-code-review/elm.json +9 -7
- package/generator/dead-code-review/src/Pages/Review/DeadCodeEliminateData.elm +59 -10
- package/generator/dead-code-review/tests/Pages/Review/DeadCodeEliminateDataTest.elm +52 -36
- package/generator/review/elm-stuff/tests-0.19.1/elm-stuff/0.19.1/Pages-Internal-RoutePattern.elmi +0 -0
- package/generator/review/elm-stuff/tests-0.19.1/elm-stuff/0.19.1/Pages-Internal-RoutePattern.elmo +0 -0
- package/generator/review/elm-stuff/tests-0.19.1/elm-stuff/0.19.1/Pages-Review-NoContractViolations.elmi +0 -0
- package/generator/review/elm-stuff/tests-0.19.1/elm-stuff/0.19.1/Pages-Review-NoContractViolations.elmo +0 -0
- package/generator/review/elm-stuff/tests-0.19.1/elm-stuff/0.19.1/d.dat +0 -0
- package/generator/review/elm-stuff/tests-0.19.1/elm-stuff/0.19.1/i.dat +0 -0
- package/generator/review/elm-stuff/tests-0.19.1/elm-stuff/0.19.1/o.dat +0 -0
- package/generator/review/elm-stuff/tests-0.19.1/elm.json +1 -1
- package/generator/review/elm-stuff/tests-0.19.1/js/Reporter.elm.js +1447 -342
- package/generator/review/elm-stuff/tests-0.19.1/js/Runner.elm.js +24542 -21748
- package/generator/review/elm-stuff/tests-0.19.1/js/node_runner.js +1 -1
- package/generator/review/elm-stuff/tests-0.19.1/js/node_supervisor.js +4 -4
- package/generator/review/elm.json +10 -10
- package/generator/src/RouteBuilder.elm +113 -107
- package/generator/src/SharedTemplate.elm +3 -2
- package/generator/src/SiteConfig.elm +3 -2
- package/generator/src/basepath-middleware.js +3 -3
- package/generator/src/build.js +125 -88
- package/generator/src/cli.js +273 -88
- package/generator/src/codegen.js +29 -27
- package/generator/src/compatibility-key.js +3 -0
- package/generator/src/compile-elm.js +43 -26
- package/generator/src/config.js +39 -0
- package/generator/src/copy-dir.js +2 -2
- package/generator/src/dev-server.js +150 -133
- package/generator/src/dir-helpers.js +9 -26
- package/generator/src/elm-codegen.js +5 -4
- package/generator/src/elm-file-constants.js +2 -3
- package/generator/src/error-formatter.js +12 -11
- package/generator/src/file-helpers.js +3 -4
- package/generator/src/generate-template-module-connector.js +23 -22
- package/generator/src/init.js +9 -8
- package/generator/src/pre-render-html.js +39 -28
- package/generator/src/render-test.js +109 -0
- package/generator/src/render-worker.js +25 -28
- package/generator/src/render.js +320 -142
- package/generator/src/request-cache.js +252 -163
- package/generator/src/resolve-elm-module.js +63 -0
- package/generator/src/rewrite-client-elm-json.js +6 -5
- package/generator/src/rewrite-elm-json-help.js +56 -0
- package/generator/src/rewrite-elm-json.js +17 -7
- package/generator/src/route-codegen-helpers.js +16 -31
- package/generator/src/seo-renderer.js +12 -7
- package/generator/src/vite-utils.js +77 -0
- package/generator/static-code/hmr.js +79 -13
- package/generator/template/app/Api.elm +6 -5
- package/generator/template/app/Effect.elm +123 -0
- package/generator/template/app/ErrorPage.elm +37 -6
- package/generator/template/app/Route/Index.elm +17 -10
- package/generator/template/app/Shared.elm +24 -47
- package/generator/template/app/Site.elm +19 -6
- package/generator/template/app/View.elm +1 -8
- package/generator/template/elm-tooling.json +0 -3
- package/generator/template/elm.json +32 -24
- package/generator/template/package.json +10 -4
- package/package.json +29 -27
- package/src/ApiRoute.elm +199 -61
- package/src/BackendTask/Custom.elm +325 -0
- package/src/BackendTask/Env.elm +90 -0
- package/src/{DataSource → BackendTask}/File.elm +128 -43
- package/src/{DataSource → BackendTask}/Glob.elm +136 -125
- package/src/BackendTask/Http.elm +673 -0
- package/src/{DataSource → BackendTask}/Internal/Glob.elm +1 -1
- package/src/BackendTask/Internal/Request.elm +28 -0
- package/src/BackendTask/Random.elm +79 -0
- package/src/BackendTask/Time.elm +47 -0
- package/src/BackendTask.elm +537 -0
- package/src/FatalError.elm +89 -0
- package/src/Form/Field.elm +21 -9
- package/src/Form/FieldView.elm +94 -14
- package/src/Form.elm +275 -400
- package/src/Head.elm +237 -7
- package/src/HtmlPrinter.elm +7 -3
- package/src/Internal/ApiRoute.elm +7 -5
- package/src/PageServerResponse.elm +6 -1
- package/src/Pages/FormState.elm +6 -5
- package/src/Pages/GeneratorProgramConfig.elm +15 -0
- package/src/Pages/Internal/FatalError.elm +5 -0
- package/src/Pages/Internal/Form.elm +21 -1
- package/src/Pages/{Msg.elm → Internal/Msg.elm} +26 -16
- package/src/Pages/Internal/Platform/Cli.elm +598 -763
- package/src/Pages/Internal/Platform/CompatibilityKey.elm +6 -0
- package/src/Pages/Internal/Platform/Effect.elm +1 -2
- package/src/Pages/Internal/Platform/GeneratorApplication.elm +373 -0
- package/src/Pages/Internal/Platform/StaticResponses.elm +73 -270
- package/src/Pages/Internal/Platform/ToJsPayload.elm +4 -7
- package/src/Pages/Internal/Platform.elm +216 -102
- package/src/Pages/Internal/Script.elm +17 -0
- package/src/Pages/Internal/StaticHttpBody.elm +35 -1
- package/src/Pages/Manifest.elm +29 -4
- package/src/Pages/PageUrl.elm +23 -9
- package/src/Pages/ProgramConfig.elm +14 -10
- package/src/Pages/Script.elm +109 -0
- package/src/Pages/SiteConfig.elm +3 -2
- package/src/Pages/StaticHttp/Request.elm +2 -2
- package/src/Pages/StaticHttpRequest.elm +23 -98
- package/src/PagesMsg.elm +92 -0
- package/src/Path.elm +16 -19
- package/src/QueryParams.elm +21 -172
- package/src/RequestsAndPending.elm +8 -19
- package/src/Result/Extra.elm +26 -0
- package/src/Scaffold/Form.elm +560 -0
- package/src/Scaffold/Route.elm +1388 -0
- package/src/Server/Request.elm +43 -37
- package/src/Server/Session.elm +62 -42
- package/src/Server/SetCookie.elm +12 -4
- package/src/Test/Html/Internal/ElmHtml/ToString.elm +8 -9
- package/src/DataSource/Env.elm +0 -38
- package/src/DataSource/Http.elm +0 -446
- package/src/DataSource/Internal/Request.elm +0 -20
- package/src/DataSource/Port.elm +0 -90
- package/src/DataSource.elm +0 -538
- package/src/Pages/Generate.elm +0 -800
|
@@ -1,179 +1,245 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
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
|
-
|
|
8
|
-
|
|
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
|
-
* @
|
|
40
|
-
* @param {string} portsFile
|
|
13
|
+
* @param {Record<string, unknown>} portsFile
|
|
41
14
|
* @param {boolean} hasFsAccess
|
|
15
|
+
* @returns {Promise<Response>}
|
|
42
16
|
*/
|
|
43
|
-
function lookupOrPerform(
|
|
44
|
-
|
|
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
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
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
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
}
|
|
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
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
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
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
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
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
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
|
-
}
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
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
|
-
|
|
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
|
-
|
|
1
|
+
import * as fs from "node:fs";
|
|
2
2
|
|
|
3
|
-
|
|
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(
|
|
12
|
+
JSON.stringify(rewriteClientElmJsonHelp(elmJson))
|
|
13
13
|
);
|
|
14
|
-
}
|
|
14
|
+
}
|
|
15
15
|
|
|
16
|
-
function
|
|
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
|
-
|
|
1
|
+
import * as fs from "node:fs";
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
export async function rewriteElmJson(
|
|
4
|
+
sourceElmJsonPath,
|
|
5
|
+
targetElmJsonPath,
|
|
6
|
+
options
|
|
7
|
+
) {
|
|
4
8
|
var elmJson = JSON.parse(
|
|
5
|
-
(await fs.promises.readFile(
|
|
9
|
+
(await fs.promises.readFile(sourceElmJsonPath)).toString()
|
|
6
10
|
);
|
|
7
11
|
|
|
8
12
|
// write new elm.json
|
|
9
13
|
|
|
10
14
|
await writeFileIfChanged(
|
|
11
|
-
|
|
12
|
-
JSON.stringify(
|
|
15
|
+
targetElmJsonPath,
|
|
16
|
+
JSON.stringify(rewriteElmJsonHelp(elmJson, options))
|
|
13
17
|
);
|
|
14
|
-
}
|
|
18
|
+
}
|
|
15
19
|
|
|
16
|
-
function
|
|
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;
|