elm-pages 3.0.23 → 3.0.25
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 +1 -1
- package/generator/src/build.js +3 -1
- package/generator/src/cli.js +20 -4
- package/generator/src/compatibility-key.js +1 -1
- package/generator/src/elm-codegen.js +24 -20
- package/generator/src/error-formatter.js +1 -1
- package/generator/src/render.js +53 -26
- package/generator/src/request-cache.js +1 -4
- package/generator/template/elm.json +1 -1
- package/generator/template/package.json +1 -1
- package/package.json +1 -1
- package/src/BackendTask/Custom.elm +2 -7
- package/src/BackendTask/File.elm +57 -20
- package/src/BackendTask/Internal/Request.elm +4 -5
- package/src/BackendTask.elm +27 -0
- package/src/Pages/Internal/Platform.elm +9 -1
- package/src/Pages/StaticHttpRequest.elm +30 -5
- package/src/RequestsAndPending.elm +6 -4
package/README.md
CHANGED
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
|
|
12
12
|
- [elm-pages Docs Site](https://elm-pages.com/docs)
|
|
13
13
|
- [elm-pages site showcase](https://elm-pages.com/showcase/)
|
|
14
|
-
- [elm-pages Elm API Docs](https://package.elm-lang.org/packages/dillonkearns/elm-pages/10.2.
|
|
14
|
+
- [elm-pages Elm API Docs](https://package.elm-lang.org/packages/dillonkearns/elm-pages/10.2.2/)
|
|
15
15
|
- [Quick start repo](https://github.com/dillonkearns/elm-pages-starter) [(live site hosted here)](https://elm-pages-starter.netlify.com)
|
|
16
16
|
- [Introducing `elm-pages` blog post](https://elm-pages.com/blog/introducing-elm-pages)
|
|
17
17
|
- [`examples` folder](https://github.com/dillonkearns/elm-pages/blob/master/examples/) (includes https://elm-pages.com site source) Use `git clone --recurse-submodules https://github.com/dillonkearns/elm-pages.git` so that there aren't missing files when you try to build the examples.
|
package/generator/src/build.js
CHANGED
|
@@ -741,7 +741,9 @@ async function runAdapter(adaptFn, processedIndexTemplate) {
|
|
|
741
741
|
* @param {string} file
|
|
742
742
|
*/
|
|
743
743
|
function defaultPreloadForFile(file) {
|
|
744
|
-
if (file
|
|
744
|
+
if (/\/elm\.[a-f0-9]+\.js$/.test(file)) {
|
|
745
|
+
return `<link rel="preload" as="script" href="${file}">`;
|
|
746
|
+
} else if (file.endsWith(".js")) {
|
|
745
747
|
return `<link rel="modulepreload" crossorigin href="${file}">`;
|
|
746
748
|
} else if (file.endsWith(".css")) {
|
|
747
749
|
return `<link rel="preload" href="${file}" as="style">`;
|
package/generator/src/cli.js
CHANGED
|
@@ -162,8 +162,7 @@ async function main() {
|
|
|
162
162
|
moduleName
|
|
163
163
|
);
|
|
164
164
|
} catch (error) {
|
|
165
|
-
|
|
166
|
-
console.log(restoreColorSafe(error));
|
|
165
|
+
printCaughtError(error);
|
|
167
166
|
process.exit(1);
|
|
168
167
|
}
|
|
169
168
|
});
|
|
@@ -283,7 +282,7 @@ await(async()=>{let{dirname:e}=await import("path"),{fileURLToPath:i}=await impo
|
|
|
283
282
|
});
|
|
284
283
|
// await runTerser(path.resolve(cwd, options.output));
|
|
285
284
|
} catch (error) {
|
|
286
|
-
|
|
285
|
+
printCaughtError(error);
|
|
287
286
|
process.exit(1);
|
|
288
287
|
}
|
|
289
288
|
});
|
|
@@ -327,6 +326,17 @@ function safeSubscribe(program, portName, subscribeFunction) {
|
|
|
327
326
|
program.ports[portName].subscribe(subscribeFunction);
|
|
328
327
|
}
|
|
329
328
|
|
|
329
|
+
/**
|
|
330
|
+
* @param {Error|string|any[]} error - Thing that was thrown and caught.
|
|
331
|
+
*/
|
|
332
|
+
function printCaughtError(error) {
|
|
333
|
+
if (typeof error === "string" || Array.isArray(error)) {
|
|
334
|
+
console.log(restoreColorSafe(error));
|
|
335
|
+
} else {
|
|
336
|
+
console.trace(error);
|
|
337
|
+
}
|
|
338
|
+
}
|
|
339
|
+
|
|
330
340
|
/**
|
|
331
341
|
* @param {string} rawPagePath
|
|
332
342
|
*/
|
|
@@ -404,7 +414,13 @@ async function compileElmForScript(elmModulePath) {
|
|
|
404
414
|
// await codegen.generate("");
|
|
405
415
|
ensureDirSync(path.join(process.cwd(), ".elm-pages", "http-response-cache"));
|
|
406
416
|
if (fs.existsSync("./codegen/") && process.env.SKIP_ELM_CODEGEN !== "true") {
|
|
407
|
-
await runElmCodegenInstall();
|
|
417
|
+
const result = await runElmCodegenInstall();
|
|
418
|
+
if (!result.success) {
|
|
419
|
+
console.error(`Warning: ${result.message}. This may cause stale generated code or missing module errors.\n`);
|
|
420
|
+
if (result.error) {
|
|
421
|
+
console.error(result.error);
|
|
422
|
+
}
|
|
423
|
+
}
|
|
408
424
|
}
|
|
409
425
|
|
|
410
426
|
ensureDirSync(`${projectDirectory}/elm-stuff`);
|
|
@@ -1,35 +1,39 @@
|
|
|
1
1
|
import { spawn as spawnCallback } from "cross-spawn";
|
|
2
|
+
import which from "which";
|
|
2
3
|
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
// force: true /* ignore errors if file doesn't exist */,
|
|
13
|
-
// });
|
|
14
|
-
// }
|
|
15
|
-
let commandOutput = "";
|
|
4
|
+
/**
|
|
5
|
+
* @returns {Promise<{ success: true } | { success: false; message: string; error?: Error }>}
|
|
6
|
+
*/
|
|
7
|
+
export async function runElmCodegenInstall() {
|
|
8
|
+
try {
|
|
9
|
+
await which("elm-codegen");
|
|
10
|
+
} catch (error) {
|
|
11
|
+
return { success: false, message: "Unable to find elm-codegen on the PATH" };
|
|
12
|
+
}
|
|
16
13
|
|
|
14
|
+
return new Promise((resolve) => {
|
|
15
|
+
const subprocess = spawnCallback("elm-codegen", ["install"]);
|
|
16
|
+
|
|
17
|
+
let commandOutput = "";
|
|
17
18
|
subprocess.stderr.on("data", function (data) {
|
|
18
19
|
commandOutput += data;
|
|
19
20
|
});
|
|
20
21
|
subprocess.stdout.on("data", function (data) {
|
|
21
22
|
commandOutput += data;
|
|
22
23
|
});
|
|
23
|
-
subprocess.on("error", function () {
|
|
24
|
-
|
|
24
|
+
subprocess.on("error", function (error) {
|
|
25
|
+
resolve({ success: false, message: "Failed to run elm-codegen", error });
|
|
25
26
|
});
|
|
26
27
|
|
|
27
|
-
subprocess.on("close",
|
|
28
|
-
if (code
|
|
29
|
-
resolve();
|
|
30
|
-
} else {
|
|
31
|
-
reject(commandOutput);
|
|
28
|
+
subprocess.on("close", (code) => {
|
|
29
|
+
if (code === 0) {
|
|
30
|
+
return resolve({ success: true });
|
|
32
31
|
}
|
|
32
|
+
resolve({
|
|
33
|
+
success: false,
|
|
34
|
+
message: `elm-codegen exited with code ${code}`,
|
|
35
|
+
error: commandOutput.length > 0 ? new Error(commandOutput) : undefined
|
|
36
|
+
});
|
|
33
37
|
});
|
|
34
38
|
});
|
|
35
39
|
}
|
package/generator/src/render.js
CHANGED
|
@@ -65,8 +65,7 @@ export async function render(
|
|
|
65
65
|
mode,
|
|
66
66
|
path,
|
|
67
67
|
request,
|
|
68
|
-
addBackendTaskWatcher
|
|
69
|
-
hasFsAccess
|
|
68
|
+
addBackendTaskWatcher
|
|
70
69
|
);
|
|
71
70
|
return result;
|
|
72
71
|
}
|
|
@@ -102,7 +101,6 @@ export async function runGenerator(
|
|
|
102
101
|
scriptModuleName,
|
|
103
102
|
"production",
|
|
104
103
|
"",
|
|
105
|
-
true,
|
|
106
104
|
versionMessage
|
|
107
105
|
);
|
|
108
106
|
return result;
|
|
@@ -120,7 +118,6 @@ export async function runGenerator(
|
|
|
120
118
|
* @param {string[]} cliOptions
|
|
121
119
|
* @param {any} portsFile
|
|
122
120
|
* @param {typeof import("fs") | import("memfs").IFs} fs
|
|
123
|
-
* @param {boolean} hasFsAccess
|
|
124
121
|
* @param {string} scriptModuleName
|
|
125
122
|
* @param {string} versionMessage
|
|
126
123
|
*/
|
|
@@ -132,7 +129,6 @@ function runGeneratorAppHelp(
|
|
|
132
129
|
scriptModuleName,
|
|
133
130
|
mode,
|
|
134
131
|
pagePath,
|
|
135
|
-
hasFsAccess,
|
|
136
132
|
versionMessage
|
|
137
133
|
) {
|
|
138
134
|
const isDevServer = mode !== "build";
|
|
@@ -155,7 +151,7 @@ function runGeneratorAppHelp(
|
|
|
155
151
|
flags: {
|
|
156
152
|
compatibilityKey,
|
|
157
153
|
argv: ["", `elm-pages run ${scriptModuleName}`, ...cliOptions],
|
|
158
|
-
versionMessage,
|
|
154
|
+
versionMessage: versionMessage || "",
|
|
159
155
|
},
|
|
160
156
|
});
|
|
161
157
|
|
|
@@ -208,9 +204,7 @@ function runGeneratorAppHelp(
|
|
|
208
204
|
return runInternalJob(
|
|
209
205
|
requestHash,
|
|
210
206
|
app,
|
|
211
|
-
mode,
|
|
212
207
|
requestToPerform,
|
|
213
|
-
hasFsAccess,
|
|
214
208
|
patternsToWatch,
|
|
215
209
|
portsFile
|
|
216
210
|
);
|
|
@@ -218,10 +212,7 @@ function runGeneratorAppHelp(
|
|
|
218
212
|
return runHttpJob(
|
|
219
213
|
requestHash,
|
|
220
214
|
portsFile,
|
|
221
|
-
app,
|
|
222
215
|
mode,
|
|
223
|
-
requestToPerform,
|
|
224
|
-
hasFsAccess,
|
|
225
216
|
requestToPerform
|
|
226
217
|
);
|
|
227
218
|
}
|
|
@@ -262,8 +253,7 @@ function runElmApp(
|
|
|
262
253
|
mode,
|
|
263
254
|
pagePath,
|
|
264
255
|
request,
|
|
265
|
-
addBackendTaskWatcher
|
|
266
|
-
hasFsAccess
|
|
256
|
+
addBackendTaskWatcher
|
|
267
257
|
) {
|
|
268
258
|
const isDevServer = mode !== "build";
|
|
269
259
|
let patternsToWatch = new Set();
|
|
@@ -347,9 +337,7 @@ function runElmApp(
|
|
|
347
337
|
return runInternalJob(
|
|
348
338
|
requestHash,
|
|
349
339
|
app,
|
|
350
|
-
mode,
|
|
351
340
|
requestToPerform,
|
|
352
|
-
hasFsAccess,
|
|
353
341
|
patternsToWatch,
|
|
354
342
|
portsFile
|
|
355
343
|
);
|
|
@@ -357,10 +345,7 @@ function runElmApp(
|
|
|
357
345
|
return runHttpJob(
|
|
358
346
|
requestHash,
|
|
359
347
|
portsFile,
|
|
360
|
-
app,
|
|
361
348
|
mode,
|
|
362
|
-
requestToPerform,
|
|
363
|
-
hasFsAccess,
|
|
364
349
|
requestToPerform
|
|
365
350
|
);
|
|
366
351
|
}
|
|
@@ -430,19 +415,14 @@ async function outputString(
|
|
|
430
415
|
async function runHttpJob(
|
|
431
416
|
requestHash,
|
|
432
417
|
portsFile,
|
|
433
|
-
app,
|
|
434
418
|
mode,
|
|
435
419
|
requestToPerform,
|
|
436
|
-
hasFsAccess,
|
|
437
|
-
useCache
|
|
438
420
|
) {
|
|
439
421
|
try {
|
|
440
422
|
const lookupResponse = await lookupOrPerform(
|
|
441
423
|
portsFile,
|
|
442
424
|
mode,
|
|
443
|
-
requestToPerform
|
|
444
|
-
hasFsAccess,
|
|
445
|
-
useCache
|
|
425
|
+
requestToPerform
|
|
446
426
|
);
|
|
447
427
|
|
|
448
428
|
if (lookupResponse.kind === "cache-response-path") {
|
|
@@ -485,13 +465,27 @@ function jsonResponse(request, json) {
|
|
|
485
465
|
response: { bodyKind: "json", body: json },
|
|
486
466
|
};
|
|
487
467
|
}
|
|
468
|
+
/**
|
|
469
|
+
* @param {any} request
|
|
470
|
+
* @param {WithImplicitCoercion<ArrayBuffer | SharedArrayBuffer>} buffer
|
|
471
|
+
*/
|
|
472
|
+
function bytesResponse(request, buffer) {
|
|
473
|
+
return {
|
|
474
|
+
request,
|
|
475
|
+
response: {
|
|
476
|
+
bodyKind: "bytes",
|
|
477
|
+
body: Buffer.from(buffer).toString("base64"),
|
|
478
|
+
},
|
|
479
|
+
};
|
|
480
|
+
}
|
|
488
481
|
|
|
482
|
+
/**
|
|
483
|
+
* @param {{ url: string; body: { args: any[] } }} requestToPerform
|
|
484
|
+
*/
|
|
489
485
|
async function runInternalJob(
|
|
490
486
|
requestHash,
|
|
491
487
|
app,
|
|
492
|
-
mode,
|
|
493
488
|
requestToPerform,
|
|
494
|
-
hasFsAccess,
|
|
495
489
|
patternsToWatch,
|
|
496
490
|
portsFile
|
|
497
491
|
) {
|
|
@@ -509,6 +503,11 @@ async function runInternalJob(
|
|
|
509
503
|
requestHash,
|
|
510
504
|
await readFileJobNew(requestToPerform, patternsToWatch, context),
|
|
511
505
|
];
|
|
506
|
+
case "elm-pages-internal://read-file-binary":
|
|
507
|
+
return [
|
|
508
|
+
requestHash,
|
|
509
|
+
await readFileBinaryJobNew(requestToPerform, patternsToWatch),
|
|
510
|
+
];
|
|
512
511
|
case "elm-pages-internal://glob":
|
|
513
512
|
return [
|
|
514
513
|
requestHash,
|
|
@@ -590,6 +589,34 @@ async function readFileJobNew(req, patternsToWatch, { cwd }) {
|
|
|
590
589
|
}
|
|
591
590
|
}
|
|
592
591
|
|
|
592
|
+
/**
|
|
593
|
+
* @param {{ url: string; body: { args: any[] } }} req
|
|
594
|
+
* @param {{ add: (arg0: string) => void; }} patternsToWatch
|
|
595
|
+
*/
|
|
596
|
+
async function readFileBinaryJobNew(req, patternsToWatch) {
|
|
597
|
+
const filePath = req.body.args[1];
|
|
598
|
+
try {
|
|
599
|
+
patternsToWatch.add(filePath);
|
|
600
|
+
|
|
601
|
+
const fileContents = await fsPromises.readFile(filePath);
|
|
602
|
+
// It's safe to use allocUnsafe here because we're going to overwrite it immediately anyway
|
|
603
|
+
const buffer = new Uint8Array(4 + fileContents.length);
|
|
604
|
+
const view = new DataView(
|
|
605
|
+
buffer.buffer,
|
|
606
|
+
buffer.byteOffset,
|
|
607
|
+
buffer.byteLength
|
|
608
|
+
);
|
|
609
|
+
view.setInt32(0, fileContents.length);
|
|
610
|
+
fileContents.copy(buffer, 4);
|
|
611
|
+
|
|
612
|
+
return bytesResponse(req, buffer);
|
|
613
|
+
} catch (error) {
|
|
614
|
+
const buffer = new Int32Array(1);
|
|
615
|
+
buffer[0] = -1;
|
|
616
|
+
return bytesResponse(req, buffer);
|
|
617
|
+
}
|
|
618
|
+
}
|
|
619
|
+
|
|
593
620
|
function runSleep(req) {
|
|
594
621
|
const { milliseconds } = req.body.args[0];
|
|
595
622
|
return new Promise((resolve) => {
|
|
@@ -11,15 +11,12 @@ const defaultHttpCachePath = "./.elm-pages/http-cache";
|
|
|
11
11
|
* @param {string} mode
|
|
12
12
|
* @param {{url: string;headers: {[x: string]: string;};method: string;body: Body; }} rawRequest
|
|
13
13
|
* @param {Record<string, unknown>} portsFile
|
|
14
|
-
* @param {boolean} hasFsAccess
|
|
15
14
|
* @returns {Promise<Response>}
|
|
16
15
|
*/
|
|
17
16
|
export function lookupOrPerform(
|
|
18
17
|
portsFile,
|
|
19
18
|
mode,
|
|
20
|
-
rawRequest
|
|
21
|
-
hasFsAccess,
|
|
22
|
-
useCache
|
|
19
|
+
rawRequest
|
|
23
20
|
) {
|
|
24
21
|
const uniqueTimeId =
|
|
25
22
|
Date.now().toString(36) + Math.random().toString(36).slice(2, 8);
|
|
@@ -14,7 +14,7 @@
|
|
|
14
14
|
"dillonkearns/elm-bcp47-language-tag": "2.0.0",
|
|
15
15
|
"dillonkearns/elm-form": "3.0.1",
|
|
16
16
|
"dillonkearns/elm-markdown": "7.0.1",
|
|
17
|
-
"dillonkearns/elm-pages": "10.2.
|
|
17
|
+
"dillonkearns/elm-pages": "10.2.2",
|
|
18
18
|
"elm/browser": "1.0.2",
|
|
19
19
|
"elm/bytes": "1.0.8",
|
|
20
20
|
"elm/core": "1.0.5",
|
package/package.json
CHANGED
|
@@ -126,6 +126,7 @@ import Date
|
|
|
126
126
|
import FatalError exposing (FatalError)
|
|
127
127
|
import Json.Decode as Decode exposing (Decoder)
|
|
128
128
|
import Json.Encode as Encode
|
|
129
|
+
import Pages.StaticHttpRequest
|
|
129
130
|
import TerminalText
|
|
130
131
|
import Time
|
|
131
132
|
|
|
@@ -331,7 +332,6 @@ request :
|
|
|
331
332
|
}
|
|
332
333
|
-> BackendTask { fatal : FatalError, recoverable : Error } a
|
|
333
334
|
request { body, expect } =
|
|
334
|
-
-- elm-review: known-unoptimized-recursion
|
|
335
335
|
BackendTask.Http.request
|
|
336
336
|
{ url = "elm-pages-internal://port"
|
|
337
337
|
, method = "GET"
|
|
@@ -343,8 +343,6 @@ request { body, expect } =
|
|
|
343
343
|
expect
|
|
344
344
|
|> BackendTask.onError
|
|
345
345
|
(\error ->
|
|
346
|
-
-- TODO avoid crash here, this should be handled as an internal error
|
|
347
|
-
--request params
|
|
348
346
|
case error.recoverable of
|
|
349
347
|
BackendTask.Http.BadBody (Just jsonError) _ ->
|
|
350
348
|
{ recoverable = DecodeError jsonError
|
|
@@ -353,8 +351,5 @@ request { body, expect } =
|
|
|
353
351
|
|> BackendTask.fail
|
|
354
352
|
|
|
355
353
|
_ ->
|
|
356
|
-
|
|
357
|
-
, fatal = error.fatal
|
|
358
|
-
}
|
|
359
|
-
|> BackendTask.fail
|
|
354
|
+
Pages.StaticHttpRequest.InternalError error.fatal
|
|
360
355
|
)
|
package/src/BackendTask/File.elm
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
module BackendTask.File exposing
|
|
2
2
|
( bodyWithFrontmatter, bodyWithoutFrontmatter, onlyFrontmatter
|
|
3
|
-
, jsonFile, rawFile
|
|
3
|
+
, jsonFile, rawFile, binaryFile
|
|
4
4
|
, FileReadError(..)
|
|
5
5
|
)
|
|
6
6
|
|
|
@@ -40,7 +40,7 @@ plain old JSON in Elm.
|
|
|
40
40
|
|
|
41
41
|
## Reading Files Without Frontmatter
|
|
42
42
|
|
|
43
|
-
@docs jsonFile, rawFile
|
|
43
|
+
@docs jsonFile, rawFile, binaryFile
|
|
44
44
|
|
|
45
45
|
|
|
46
46
|
## FatalErrors
|
|
@@ -52,6 +52,8 @@ plain old JSON in Elm.
|
|
|
52
52
|
import BackendTask exposing (BackendTask)
|
|
53
53
|
import BackendTask.Http
|
|
54
54
|
import BackendTask.Internal.Request
|
|
55
|
+
import Bytes exposing (Bytes)
|
|
56
|
+
import Bytes.Decode
|
|
55
57
|
import FatalError exposing (FatalError)
|
|
56
58
|
import Json.Decode as Decode exposing (Decoder)
|
|
57
59
|
import TerminalText
|
|
@@ -337,6 +339,39 @@ rawFile filePath =
|
|
|
337
339
|
read filePath (Decode.field "rawFile" Decode.string)
|
|
338
340
|
|
|
339
341
|
|
|
342
|
+
{-| Get the raw file content as `Bytes`.
|
|
343
|
+
|
|
344
|
+
You could read a file called `hello.jpg` in your root project directory like this:
|
|
345
|
+
|
|
346
|
+
import BackendTask exposing (BackendTask)
|
|
347
|
+
import BackendTask.File as File
|
|
348
|
+
import Bytes exposing (Bytes)
|
|
349
|
+
|
|
350
|
+
elmBinaryFile : BackendTask Bytes
|
|
351
|
+
elmBinaryFile =
|
|
352
|
+
File.binaryFile "hello.jpg"
|
|
353
|
+
|
|
354
|
+
-}
|
|
355
|
+
binaryFile : String -> BackendTask { fatal : FatalError, recoverable : FileReadError decoderError } Bytes
|
|
356
|
+
binaryFile filePath =
|
|
357
|
+
BackendTask.Internal.Request.request
|
|
358
|
+
{ name = "read-file-binary"
|
|
359
|
+
, body = BackendTask.Http.stringBody "" filePath
|
|
360
|
+
, expect =
|
|
361
|
+
Bytes.Decode.signedInt32 Bytes.BE
|
|
362
|
+
|> Bytes.Decode.andThen
|
|
363
|
+
(\length ->
|
|
364
|
+
if length < 0 then
|
|
365
|
+
Bytes.Decode.fail
|
|
366
|
+
|
|
367
|
+
else
|
|
368
|
+
Bytes.Decode.bytes length
|
|
369
|
+
)
|
|
370
|
+
|> BackendTask.Http.expectBytes
|
|
371
|
+
}
|
|
372
|
+
|> BackendTask.mapError (\_ -> fileNotFound filePath)
|
|
373
|
+
|
|
374
|
+
|
|
340
375
|
{-| Read a file as JSON.
|
|
341
376
|
|
|
342
377
|
The Decode will strip off any unused JSON data.
|
|
@@ -410,23 +445,25 @@ read filePath decoder =
|
|
|
410
445
|
|> BackendTask.andThen BackendTask.fromResult
|
|
411
446
|
|
|
412
447
|
|
|
413
|
-
errorDecoder :
|
|
448
|
+
errorDecoder : String -> Decoder { fatal : FatalError, recoverable : FileReadError decoding }
|
|
449
|
+
errorDecoder filePath =
|
|
450
|
+
Decode.succeed (fileNotFound filePath)
|
|
451
|
+
|
|
452
|
+
|
|
453
|
+
fileNotFound :
|
|
414
454
|
String
|
|
415
455
|
->
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
,
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
}
|
|
431
|
-
FileDoesntExist
|
|
432
|
-
)
|
|
456
|
+
{ fatal : FatalError
|
|
457
|
+
, recoverable : FileReadError decoding
|
|
458
|
+
}
|
|
459
|
+
fileNotFound filePath =
|
|
460
|
+
FatalError.recoverable
|
|
461
|
+
{ title = "File Doesn't Exist"
|
|
462
|
+
, body =
|
|
463
|
+
[ TerminalText.text "Couldn't find file at path `"
|
|
464
|
+
, TerminalText.yellow filePath
|
|
465
|
+
, TerminalText.text "`"
|
|
466
|
+
]
|
|
467
|
+
|> TerminalText.toString
|
|
468
|
+
}
|
|
469
|
+
FileDoesntExist
|
|
@@ -4,6 +4,7 @@ import BackendTask exposing (BackendTask)
|
|
|
4
4
|
import BackendTask.Http exposing (Body, Expect)
|
|
5
5
|
import Json.Decode exposing (Decoder)
|
|
6
6
|
import Json.Encode as Encode
|
|
7
|
+
import Pages.StaticHttpRequest
|
|
7
8
|
|
|
8
9
|
|
|
9
10
|
request :
|
|
@@ -12,8 +13,7 @@ request :
|
|
|
12
13
|
, expect : Expect a
|
|
13
14
|
}
|
|
14
15
|
-> BackendTask error a
|
|
15
|
-
request
|
|
16
|
-
-- elm-review: known-unoptimized-recursion
|
|
16
|
+
request { name, body, expect } =
|
|
17
17
|
BackendTask.Http.request
|
|
18
18
|
{ url = "elm-pages-internal://" ++ name
|
|
19
19
|
, method = "GET"
|
|
@@ -24,9 +24,8 @@ request ({ name, body, expect } as params) =
|
|
|
24
24
|
}
|
|
25
25
|
expect
|
|
26
26
|
|> BackendTask.onError
|
|
27
|
-
(\
|
|
28
|
-
|
|
29
|
-
request params
|
|
27
|
+
(\err ->
|
|
28
|
+
Pages.StaticHttpRequest.InternalError err.fatal
|
|
30
29
|
)
|
|
31
30
|
|
|
32
31
|
|
package/src/BackendTask.elm
CHANGED
|
@@ -143,6 +143,9 @@ but mapping allows you to change the resulting values by applying functions to t
|
|
|
143
143
|
map : (a -> b) -> BackendTask error a -> BackendTask error b
|
|
144
144
|
map fn requestInfo =
|
|
145
145
|
case requestInfo of
|
|
146
|
+
InternalError err ->
|
|
147
|
+
InternalError err
|
|
148
|
+
|
|
146
149
|
ApiRoute value ->
|
|
147
150
|
ApiRoute (Result.map fn value)
|
|
148
151
|
|
|
@@ -221,6 +224,9 @@ inDir dir backendTask =
|
|
|
221
224
|
-- elm-review: known-unoptimized-recursion
|
|
222
225
|
-- TODO try to find a way to optimize tail-call recursion here
|
|
223
226
|
case backendTask of
|
|
227
|
+
InternalError _ ->
|
|
228
|
+
backendTask
|
|
229
|
+
|
|
224
230
|
ApiRoute _ ->
|
|
225
231
|
backendTask
|
|
226
232
|
|
|
@@ -243,6 +249,9 @@ quiet backendTask =
|
|
|
243
249
|
-- elm-review: known-unoptimized-recursion
|
|
244
250
|
-- TODO try to find a way to optimize tail-call recursion here
|
|
245
251
|
case backendTask of
|
|
252
|
+
InternalError _ ->
|
|
253
|
+
backendTask
|
|
254
|
+
|
|
246
255
|
ApiRoute _ ->
|
|
247
256
|
backendTask
|
|
248
257
|
|
|
@@ -260,6 +269,9 @@ withEnv key value backendTask =
|
|
|
260
269
|
-- elm-review: known-unoptimized-recursion
|
|
261
270
|
-- TODO try to find a way to optimize tail-call recursion here
|
|
262
271
|
case backendTask of
|
|
272
|
+
InternalError _ ->
|
|
273
|
+
backendTask
|
|
274
|
+
|
|
263
275
|
ApiRoute _ ->
|
|
264
276
|
backendTask
|
|
265
277
|
|
|
@@ -422,6 +434,12 @@ map2 fn request1 request2 =
|
|
|
422
434
|
-- elm-review: known-unoptimized-recursion
|
|
423
435
|
-- TODO try to find a way to optimize tail-call recursion here
|
|
424
436
|
case ( request1, request2 ) of
|
|
437
|
+
( InternalError err1, _ ) ->
|
|
438
|
+
InternalError err1
|
|
439
|
+
|
|
440
|
+
( _, InternalError err2 ) ->
|
|
441
|
+
InternalError err2
|
|
442
|
+
|
|
425
443
|
( ApiRoute value1, ApiRoute value2 ) ->
|
|
426
444
|
ApiRoute (Result.map2 fn value1 value2)
|
|
427
445
|
|
|
@@ -478,6 +496,9 @@ andThen fn requestInfo =
|
|
|
478
496
|
-- elm-review: known-unoptimized-recursion
|
|
479
497
|
-- TODO try to find a way to optimize recursion here
|
|
480
498
|
case requestInfo of
|
|
499
|
+
InternalError errA ->
|
|
500
|
+
InternalError errA
|
|
501
|
+
|
|
481
502
|
ApiRoute a ->
|
|
482
503
|
case a of
|
|
483
504
|
Ok okA ->
|
|
@@ -503,6 +524,9 @@ onError : (error -> BackendTask mappedError value) -> BackendTask error value ->
|
|
|
503
524
|
onError fromError backendTask =
|
|
504
525
|
-- elm-review: known-unoptimized-recursion
|
|
505
526
|
case backendTask of
|
|
527
|
+
InternalError err ->
|
|
528
|
+
InternalError err
|
|
529
|
+
|
|
506
530
|
ApiRoute a ->
|
|
507
531
|
case a of
|
|
508
532
|
Ok okA ->
|
|
@@ -569,6 +593,9 @@ fromResult result =
|
|
|
569
593
|
mapError : (error -> errorMapped) -> BackendTask error value -> BackendTask errorMapped value
|
|
570
594
|
mapError mapFn requestInfo =
|
|
571
595
|
case requestInfo of
|
|
596
|
+
InternalError internal ->
|
|
597
|
+
InternalError internal
|
|
598
|
+
|
|
572
599
|
ApiRoute value ->
|
|
573
600
|
ApiRoute (Result.mapError mapFn value)
|
|
574
601
|
|
|
@@ -84,7 +84,15 @@ mainView config model =
|
|
|
84
84
|
{ path = ContentCache.pathForUrl urls |> UrlPath.join
|
|
85
85
|
, route = config.urlToRoute { currentUrl | path = model.currentPath }
|
|
86
86
|
}
|
|
87
|
-
|
|
87
|
+
(Just
|
|
88
|
+
{ protocol = currentUrl.protocol
|
|
89
|
+
, host = currentUrl.host
|
|
90
|
+
, port_ = currentUrl.port_
|
|
91
|
+
, path = ContentCache.pathForUrl urls
|
|
92
|
+
, query = currentUrl.query |> Maybe.map QueryParams.fromString |> Maybe.withDefault Dict.empty
|
|
93
|
+
, fragment = currentUrl.fragment
|
|
94
|
+
}
|
|
95
|
+
)
|
|
88
96
|
pageData.sharedData
|
|
89
97
|
pageData.pageData
|
|
90
98
|
pageData.actionData
|
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
module Pages.StaticHttpRequest exposing (Error(..), MockResolver, RawRequest(..), Status(..), cacheRequestResolution, mockResolve, toBuildError)
|
|
2
2
|
|
|
3
3
|
import BuildError exposing (BuildError)
|
|
4
|
+
import FatalError exposing (FatalError)
|
|
4
5
|
import Json.Encode
|
|
6
|
+
import Pages.Internal.FatalError
|
|
5
7
|
import Pages.StaticHttp.Request
|
|
6
8
|
import RequestsAndPending exposing (RequestsAndPending)
|
|
7
9
|
import TerminalText as Terminal
|
|
@@ -15,11 +17,13 @@ type alias MockResolver =
|
|
|
15
17
|
type RawRequest error value
|
|
16
18
|
= Request (List Pages.StaticHttp.Request.Request) (Maybe MockResolver -> RequestsAndPending -> RawRequest error value)
|
|
17
19
|
| ApiRoute (Result error value)
|
|
20
|
+
| InternalError FatalError
|
|
18
21
|
|
|
19
22
|
|
|
20
23
|
type Error
|
|
21
24
|
= DecoderError String
|
|
22
25
|
| UserCalledStaticHttpFail String
|
|
26
|
+
| InternalFailure FatalError
|
|
23
27
|
|
|
24
28
|
|
|
25
29
|
toBuildError : String -> Error -> BuildError
|
|
@@ -43,18 +47,36 @@ toBuildError path error =
|
|
|
43
47
|
, fatal = True
|
|
44
48
|
}
|
|
45
49
|
|
|
50
|
+
InternalFailure (Pages.Internal.FatalError.FatalError buildError) ->
|
|
51
|
+
{ title = "Internal error"
|
|
52
|
+
, message =
|
|
53
|
+
[ Terminal.text <| "Please report this error!"
|
|
54
|
+
, Terminal.text ""
|
|
55
|
+
, Terminal.text ""
|
|
56
|
+
, Terminal.text buildError.body
|
|
57
|
+
]
|
|
58
|
+
, path = path
|
|
59
|
+
, fatal = True
|
|
60
|
+
}
|
|
61
|
+
|
|
46
62
|
|
|
47
|
-
mockResolve : RawRequest error value -> MockResolver -> Result error value
|
|
48
|
-
mockResolve request mockResolver =
|
|
63
|
+
mockResolve : (FatalError -> error) -> RawRequest error value -> MockResolver -> Result error value
|
|
64
|
+
mockResolve onInternalError request mockResolver =
|
|
49
65
|
case request of
|
|
50
66
|
Request _ lookupFn ->
|
|
51
|
-
|
|
52
|
-
nextRequest
|
|
53
|
-
|
|
67
|
+
let
|
|
68
|
+
nextRequest : RawRequest error value
|
|
69
|
+
nextRequest =
|
|
70
|
+
lookupFn (Just mockResolver) (Json.Encode.object [])
|
|
71
|
+
in
|
|
72
|
+
mockResolve onInternalError nextRequest mockResolver
|
|
54
73
|
|
|
55
74
|
ApiRoute value ->
|
|
56
75
|
value
|
|
57
76
|
|
|
77
|
+
InternalError err ->
|
|
78
|
+
Err (onInternalError err)
|
|
79
|
+
|
|
58
80
|
|
|
59
81
|
cacheRequestResolution :
|
|
60
82
|
RawRequest error value
|
|
@@ -72,6 +94,9 @@ cacheRequestResolution request rawResponses =
|
|
|
72
94
|
ApiRoute value ->
|
|
73
95
|
Complete value
|
|
74
96
|
|
|
97
|
+
InternalError err ->
|
|
98
|
+
HasPermanentError (InternalFailure err)
|
|
99
|
+
|
|
75
100
|
|
|
76
101
|
type Status error value
|
|
77
102
|
= Incomplete (List Pages.StaticHttp.Request.Request) (RawRequest error value)
|
|
@@ -36,10 +36,12 @@ bodyDecoder =
|
|
|
36
36
|
Decode.string
|
|
37
37
|
|> Decode.andThen
|
|
38
38
|
(\base64String ->
|
|
39
|
-
base64String
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
39
|
+
case Base64.toBytes base64String of
|
|
40
|
+
Just bytes ->
|
|
41
|
+
Decode.succeed (BytesBody bytes)
|
|
42
|
+
|
|
43
|
+
Nothing ->
|
|
44
|
+
Decode.fail "Couldn't parse base64 string into Bytes."
|
|
43
45
|
)
|
|
44
46
|
|
|
45
47
|
"string" ->
|