elm-pages 2.1.6 → 2.1.10
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/generator/review/elm.json +34 -0
- package/generator/review/src/ReviewConfig.elm +10 -0
- package/generator/src/basepath-middleware.js +15 -9
- package/generator/src/build.js +100 -6
- package/generator/src/cli.js +13 -9
- package/generator/src/compile-elm.js +43 -0
- package/generator/src/dev-server.js +63 -11
- package/generator/src/error-formatter.js +62 -9
- package/generator/src/generate-template-module-connector.js +17 -4
- package/generator/src/init.js +4 -0
- package/generator/src/pre-render-html.js +19 -12
- package/generator/src/render-worker.js +0 -1
- package/generator/src/render.js +1 -2
- package/generator/src/seo-renderer.js +21 -2
- package/generator/static-code/hmr.js +43 -6
- package/generator/template/elm-tooling.json +9 -0
- package/generator/template/package.json +5 -1
- package/package.json +16 -9
- package/src/ApiRoute.elm +178 -0
- package/src/AriaLiveAnnouncer.elm +36 -0
- package/src/BuildError.elm +60 -0
- package/src/DataSource/File.elm +288 -0
- package/src/DataSource/Glob.elm +1050 -0
- package/src/DataSource/Http.elm +467 -0
- package/src/DataSource/Internal/Glob.elm +74 -0
- package/src/DataSource/Port.elm +87 -0
- package/src/DataSource/ServerRequest.elm +60 -0
- package/src/DataSource.elm +801 -0
- package/src/Head/Seo.elm +516 -0
- package/src/Head/Twitter.elm +109 -0
- package/src/Head.elm +452 -0
- package/src/HtmlPrinter.elm +27 -0
- package/src/Internal/ApiRoute.elm +89 -0
- package/src/Internal/OptimizedDecoder.elm +18 -0
- package/src/KeepOrDiscard.elm +6 -0
- package/src/OptimizedDecoder/Pipeline.elm +335 -0
- package/src/OptimizedDecoder.elm +818 -0
- package/src/Pages/ContentCache.elm +248 -0
- package/src/Pages/Flags.elm +26 -0
- package/src/Pages/Http.elm +10 -0
- package/src/Pages/Internal/ApplicationType.elm +6 -0
- package/src/Pages/Internal/NotFoundReason.elm +256 -0
- package/src/Pages/Internal/Platform/Cli.elm +1015 -0
- package/src/Pages/Internal/Platform/Effect.elm +14 -0
- package/src/Pages/Internal/Platform/StaticResponses.elm +540 -0
- package/src/Pages/Internal/Platform/ToJsPayload.elm +138 -0
- package/src/Pages/Internal/Platform.elm +745 -0
- package/src/Pages/Internal/RoutePattern.elm +122 -0
- package/src/Pages/Internal/Router.elm +116 -0
- package/src/Pages/Internal/StaticHttpBody.elm +54 -0
- package/src/Pages/Internal/String.elm +39 -0
- package/src/Pages/Manifest/Category.elm +240 -0
- package/src/Pages/Manifest.elm +412 -0
- package/src/Pages/PageUrl.elm +38 -0
- package/src/Pages/ProgramConfig.elm +73 -0
- package/src/Pages/Review/NoContractViolations.elm +397 -0
- package/src/Pages/Secrets.elm +83 -0
- package/src/Pages/SiteConfig.elm +13 -0
- package/src/Pages/StaticHttp/Request.elm +42 -0
- package/src/Pages/StaticHttpRequest.elm +320 -0
- package/src/Pages/Url.elm +60 -0
- package/src/Path.elm +96 -0
- package/src/QueryParams.elm +216 -0
- package/src/RenderRequest.elm +163 -0
- package/src/RequestsAndPending.elm +20 -0
- package/src/Secrets.elm +111 -0
- package/src/SecretsDict.elm +45 -0
- package/src/StructuredData.elm +236 -0
- package/src/TerminalText.elm +242 -0
- package/src/Test/Html/Internal/ElmHtml/Constants.elm +53 -0
- package/src/Test/Html/Internal/ElmHtml/Helpers.elm +17 -0
- package/src/Test/Html/Internal/ElmHtml/InternalTypes.elm +529 -0
- package/src/Test/Html/Internal/ElmHtml/Markdown.elm +56 -0
- package/src/Test/Html/Internal/ElmHtml/ToString.elm +197 -0
- package/src/Test/Internal/KernelConstants.elm +34 -0
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
{
|
|
2
|
+
"type": "application",
|
|
3
|
+
"source-directories": [
|
|
4
|
+
"src",
|
|
5
|
+
"../../src"
|
|
6
|
+
],
|
|
7
|
+
"elm-version": "0.19.1",
|
|
8
|
+
"dependencies": {
|
|
9
|
+
"direct": {
|
|
10
|
+
"elm/core": "1.0.5",
|
|
11
|
+
"elm-community/result-extra": "2.4.0",
|
|
12
|
+
"jfmengels/elm-review": "2.4.2",
|
|
13
|
+
"stil4m/elm-syntax": "7.2.5"
|
|
14
|
+
},
|
|
15
|
+
"indirect": {
|
|
16
|
+
"elm/html": "1.0.0",
|
|
17
|
+
"elm/json": "1.1.3",
|
|
18
|
+
"elm/parser": "1.1.0",
|
|
19
|
+
"elm/project-metadata-utils": "1.0.2",
|
|
20
|
+
"elm/random": "1.0.0",
|
|
21
|
+
"elm/time": "1.0.0",
|
|
22
|
+
"elm/virtual-dom": "1.0.2",
|
|
23
|
+
"elm-community/list-extra": "8.3.0",
|
|
24
|
+
"elm-explorations/test": "1.2.2",
|
|
25
|
+
"miniBill/elm-unicode": "1.0.2",
|
|
26
|
+
"rtfeldman/elm-hex": "1.0.0",
|
|
27
|
+
"stil4m/structured-writer": "1.0.3"
|
|
28
|
+
}
|
|
29
|
+
},
|
|
30
|
+
"test-dependencies": {
|
|
31
|
+
"direct": {},
|
|
32
|
+
"indirect": {}
|
|
33
|
+
}
|
|
34
|
+
}
|
|
@@ -1,18 +1,23 @@
|
|
|
1
|
-
const parseUrl = require("url").parse;
|
|
2
|
-
|
|
3
1
|
// this middleware is only active when (config.base !== '/')
|
|
4
2
|
|
|
5
3
|
module.exports = function baseMiddleware(base) {
|
|
6
4
|
// Keep the named function. The name is visible in debug logs via `DEBUG=connect:dispatcher ...`
|
|
7
5
|
return function viteBaseMiddleware(req, res, next) {
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
const path =
|
|
6
|
+
// `req.url` only contains the path, since that is what gets passed in the HTTP request.
|
|
7
|
+
// See https://nodejs.org/api/http.html#http_message_url
|
|
8
|
+
const path = req.url;
|
|
11
9
|
|
|
12
|
-
if (
|
|
13
|
-
//
|
|
10
|
+
if (base === "/") {
|
|
11
|
+
// The RegExp will check for the slash, so we remove it here.
|
|
12
|
+
base = "";
|
|
13
|
+
}
|
|
14
|
+
// We want to detect the base at the beginning, hence the `^`,
|
|
15
|
+
// but also allow calling the base without a trailing slash, hence the `$`.
|
|
16
|
+
const baseRegExp = new RegExp(`^${base}(/|$)`);
|
|
17
|
+
if (baseRegExp.test(path)) {
|
|
18
|
+
// rewrite url to remove base. this ensures that other middleware does
|
|
14
19
|
// not need to consider base being prepended or not
|
|
15
|
-
req.url =
|
|
20
|
+
req.url = path.replace(baseRegExp, "/");
|
|
16
21
|
return next();
|
|
17
22
|
}
|
|
18
23
|
|
|
@@ -26,9 +31,10 @@ module.exports = function baseMiddleware(base) {
|
|
|
26
31
|
} else if (req.headers.accept && req.headers.accept.includes("text/html")) {
|
|
27
32
|
// non-based page visit
|
|
28
33
|
res.statusCode = 404;
|
|
34
|
+
const suggestionUrl = `${base}/${path.slice(1)}`;
|
|
29
35
|
res.end(
|
|
30
36
|
`The server is configured with a public base URL of ${base} - ` +
|
|
31
|
-
|
|
37
|
+
`did you mean to visit ${suggestionUrl} instead?`
|
|
32
38
|
);
|
|
33
39
|
return;
|
|
34
40
|
}
|
package/generator/src/build.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
const fs = require("./dir-helpers.js");
|
|
2
2
|
const fsPromises = require("fs").promises;
|
|
3
3
|
|
|
4
|
+
const { runElmReview } = require("./compile-elm.js");
|
|
4
5
|
const { restoreColor } = require("./error-formatter");
|
|
5
6
|
const path = require("path");
|
|
6
7
|
const spawnCallback = require("cross-spawn").spawn;
|
|
@@ -9,6 +10,7 @@ const terser = require("terser");
|
|
|
9
10
|
const os = require("os");
|
|
10
11
|
const { Worker, SHARE_ENV } = require("worker_threads");
|
|
11
12
|
const { ensureDirSync } = require("./file-helpers.js");
|
|
13
|
+
const which = require("which");
|
|
12
14
|
let pool = [];
|
|
13
15
|
let pagesReady;
|
|
14
16
|
let pages = new Promise((resolve, reject) => {
|
|
@@ -35,9 +37,28 @@ async function ensureRequiredDirs() {
|
|
|
35
37
|
ensureDirSync(path.join(process.cwd(), ".elm-pages", "http-response-cache"));
|
|
36
38
|
}
|
|
37
39
|
|
|
40
|
+
async function ensureRequiredExecutables() {
|
|
41
|
+
try {
|
|
42
|
+
await which("elm");
|
|
43
|
+
} catch (error) {
|
|
44
|
+
throw "I couldn't find elm on the PATH. Please ensure it's installed, either globally, or locally. If it's installed locally, ensure you're running through an NPM script or with npx so the PATH is configured to include it.";
|
|
45
|
+
}
|
|
46
|
+
try {
|
|
47
|
+
await which("elm-optimize-level-2");
|
|
48
|
+
} catch (error) {
|
|
49
|
+
throw "I couldn't find elm-optimize-level-2 on the PATH. Please ensure it's installed, either globally, or locally. If it's installed locally, ensure you're running through an NPM script or with npx so the PATH is configured to include it.";
|
|
50
|
+
}
|
|
51
|
+
try {
|
|
52
|
+
await which("elm-review");
|
|
53
|
+
} catch (error) {
|
|
54
|
+
throw "I couldn't find elm-review on the PATH. Please ensure it's installed, either globally, or locally. If it's installed locally, ensure you're running through an NPM script or with npx so the PATH is configured to include it.";
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
|
|
38
58
|
async function run(options) {
|
|
39
59
|
try {
|
|
40
60
|
await ensureRequiredDirs();
|
|
61
|
+
await ensureRequiredExecutables();
|
|
41
62
|
// since init/update are never called in pre-renders, and DataSource.Http is called using undici
|
|
42
63
|
// we can provide a fake HTTP instead of xhr2 (which is otherwise needed for Elm HTTP requests from Node)
|
|
43
64
|
XMLHttpRequest = {};
|
|
@@ -55,8 +76,22 @@ async function run(options) {
|
|
|
55
76
|
await Promise.all([copyDone, cliDone, compileClientDone]);
|
|
56
77
|
} catch (error) {
|
|
57
78
|
buildError = true;
|
|
58
|
-
|
|
59
|
-
|
|
79
|
+
try {
|
|
80
|
+
const reviewOutput = JSON.parse(await runElmReview());
|
|
81
|
+
const isParsingError = reviewOutput.errors.some((reviewError) => {
|
|
82
|
+
return reviewError.errors.some((item) => item.rule === "ParsingError");
|
|
83
|
+
});
|
|
84
|
+
if (isParsingError) {
|
|
85
|
+
console.error(error);
|
|
86
|
+
} else {
|
|
87
|
+
console.error(restoreColor(reviewOutput));
|
|
88
|
+
}
|
|
89
|
+
process.exit(1);
|
|
90
|
+
} catch (noElmReviewErrors) {
|
|
91
|
+
console.error(error);
|
|
92
|
+
} finally {
|
|
93
|
+
process.exit(1);
|
|
94
|
+
}
|
|
60
95
|
}
|
|
61
96
|
}
|
|
62
97
|
|
|
@@ -164,10 +199,15 @@ function elmOptimizeLevel2(elmEntrypointPath, outputPath, cwd) {
|
|
|
164
199
|
});
|
|
165
200
|
|
|
166
201
|
subprocess.on("close", async (code) => {
|
|
167
|
-
if (
|
|
202
|
+
if (
|
|
203
|
+
code === 0 &&
|
|
204
|
+
commandOutput === "" &&
|
|
205
|
+
(await fs.fileExists(fullOutputPath))
|
|
206
|
+
) {
|
|
168
207
|
resolve();
|
|
169
208
|
} else {
|
|
170
209
|
if (!buildError) {
|
|
210
|
+
buildError = true;
|
|
171
211
|
process.exitCode = 1;
|
|
172
212
|
reject(commandOutput);
|
|
173
213
|
} else {
|
|
@@ -227,8 +267,13 @@ function runElmMake(elmEntrypointPath, outputPath, cwd) {
|
|
|
227
267
|
if (code == 0 && (await fs.fileExists(fullOutputPath))) {
|
|
228
268
|
resolve();
|
|
229
269
|
} else {
|
|
230
|
-
|
|
231
|
-
|
|
270
|
+
if (!buildError) {
|
|
271
|
+
buildError = true;
|
|
272
|
+
process.exitCode = 1;
|
|
273
|
+
reject(restoreColor(JSON.parse(commandOutput)));
|
|
274
|
+
} else {
|
|
275
|
+
// avoid unhandled error printing duplicate message, let process.exit in top loop take over
|
|
276
|
+
}
|
|
232
277
|
}
|
|
233
278
|
});
|
|
234
279
|
});
|
|
@@ -299,12 +344,61 @@ async function compileCliApp(options) {
|
|
|
299
344
|
);
|
|
300
345
|
|
|
301
346
|
const elmFileContent = await fsPromises.readFile(ELM_FILE_PATH, "utf-8");
|
|
347
|
+
// Source: https://github.com/elm-explorations/test/blob/d5eb84809de0f8bbf50303efd26889092c800609/src/Elm/Kernel/HtmlAsJson.js
|
|
348
|
+
const forceThunksSource = ` _HtmlAsJson_toJson(x)
|
|
349
|
+
}
|
|
350
|
+
|
|
351
|
+
var virtualDomKernelConstants =
|
|
352
|
+
{
|
|
353
|
+
nodeTypeTagger: 4,
|
|
354
|
+
nodeTypeThunk: 5,
|
|
355
|
+
kids: "e",
|
|
356
|
+
refs: "l",
|
|
357
|
+
thunk: "m",
|
|
358
|
+
node: "k",
|
|
359
|
+
value: "a"
|
|
360
|
+
}
|
|
361
|
+
|
|
362
|
+
function forceThunks(vNode) {
|
|
363
|
+
if (typeof vNode !== "undefined" && vNode.$ === "#2") {
|
|
364
|
+
// This is a tuple (the kids : List (String, Html) field of a Keyed node); recurse into the right side of the tuple
|
|
365
|
+
vNode.b = forceThunks(vNode.b);
|
|
366
|
+
}
|
|
367
|
+
if (typeof vNode !== 'undefined' && vNode.$ === virtualDomKernelConstants.nodeTypeThunk && !vNode[virtualDomKernelConstants.node]) {
|
|
368
|
+
// This is a lazy node; evaluate it
|
|
369
|
+
var args = vNode[virtualDomKernelConstants.thunk];
|
|
370
|
+
vNode[virtualDomKernelConstants.node] = vNode[virtualDomKernelConstants.thunk].apply(args);
|
|
371
|
+
// And then recurse into the evaluated node
|
|
372
|
+
vNode[virtualDomKernelConstants.node] = forceThunks(vNode[virtualDomKernelConstants.node]);
|
|
373
|
+
}
|
|
374
|
+
if (typeof vNode !== 'undefined' && vNode.$ === virtualDomKernelConstants.nodeTypeTagger) {
|
|
375
|
+
// This is an Html.map; recurse into the node it is wrapping
|
|
376
|
+
vNode[virtualDomKernelConstants.node] = forceThunks(vNode[virtualDomKernelConstants.node]);
|
|
377
|
+
}
|
|
378
|
+
if (typeof vNode !== 'undefined' && typeof vNode[virtualDomKernelConstants.kids] !== 'undefined') {
|
|
379
|
+
// This is something with children (either a node with kids : List Html, or keyed with kids : List (String, Html));
|
|
380
|
+
// recurse into the children
|
|
381
|
+
vNode[virtualDomKernelConstants.kids] = vNode[virtualDomKernelConstants.kids].map(forceThunks);
|
|
382
|
+
}
|
|
383
|
+
return vNode;
|
|
384
|
+
}
|
|
385
|
+
|
|
386
|
+
function _HtmlAsJson_toJson(html) {
|
|
387
|
+
`;
|
|
388
|
+
|
|
302
389
|
await fsPromises.writeFile(
|
|
303
390
|
ELM_FILE_PATH,
|
|
304
391
|
elmFileContent
|
|
305
392
|
.replace(
|
|
306
393
|
/return \$elm\$json\$Json\$Encode\$string\(.REPLACE_ME_WITH_JSON_STRINGIFY.\)/g,
|
|
307
|
-
"return " +
|
|
394
|
+
"return " +
|
|
395
|
+
(options.debug
|
|
396
|
+
? `${forceThunksSource}
|
|
397
|
+
return _Json_wrap(forceThunks(html));
|
|
398
|
+
`
|
|
399
|
+
: `${forceThunksSource}
|
|
400
|
+
return forceThunks(html);
|
|
401
|
+
`)
|
|
308
402
|
)
|
|
309
403
|
.replace(
|
|
310
404
|
"return ports ? { ports: ports } : {};",
|
package/generator/src/cli.js
CHANGED
|
@@ -47,6 +47,7 @@ async function main() {
|
|
|
47
47
|
"Preserve the HTTP and JS Port cache instead of deleting it on server start"
|
|
48
48
|
)
|
|
49
49
|
.option("--base <basePath>", "serve site under a base path", "/")
|
|
50
|
+
.option("--https", "uses a https server")
|
|
50
51
|
.action(async (options) => {
|
|
51
52
|
if (!options.keepCache) {
|
|
52
53
|
clearHttpAndPortCache();
|
|
@@ -108,16 +109,19 @@ function clearHttpAndPortCache() {
|
|
|
108
109
|
}
|
|
109
110
|
|
|
110
111
|
/**
|
|
111
|
-
* @param {string}
|
|
112
|
+
* @param {string} rawPagePath
|
|
112
113
|
*/
|
|
113
|
-
function normalizeUrl(
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
114
|
+
function normalizeUrl(rawPagePath) {
|
|
115
|
+
const segments = rawPagePath
|
|
116
|
+
.split("/")
|
|
117
|
+
// Filter out all empty segments.
|
|
118
|
+
.filter((segment) => segment.length != 0);
|
|
119
|
+
|
|
120
|
+
// Do not add a trailing slash.
|
|
121
|
+
// The core issue is that `/base` is a prefix of `/base/`, but
|
|
122
|
+
// `/base/` is not a prefix of `/base`, which can later lead to issues
|
|
123
|
+
// with detecting whether the path contains the base.
|
|
124
|
+
return `/${segments.join("/")}`;
|
|
121
125
|
}
|
|
122
126
|
|
|
123
127
|
main();
|
|
@@ -90,9 +90,52 @@ async function runElm(elmEntrypointPath, outputPath, cwd) {
|
|
|
90
90
|
});
|
|
91
91
|
}
|
|
92
92
|
|
|
93
|
+
/**
|
|
94
|
+
* @param {string} [ cwd ]
|
|
95
|
+
*/
|
|
96
|
+
async function runElmReview(cwd) {
|
|
97
|
+
const startTime = Date.now();
|
|
98
|
+
return new Promise((resolve, reject) => {
|
|
99
|
+
const child = spawnCallback(
|
|
100
|
+
`elm-review`,
|
|
101
|
+
[
|
|
102
|
+
"--report",
|
|
103
|
+
"json",
|
|
104
|
+
"--namespace",
|
|
105
|
+
"elm-pages",
|
|
106
|
+
"--config",
|
|
107
|
+
path.join(__dirname, "../../generator/review"),
|
|
108
|
+
],
|
|
109
|
+
{ cwd: cwd }
|
|
110
|
+
);
|
|
111
|
+
|
|
112
|
+
let scriptOutput = "";
|
|
113
|
+
|
|
114
|
+
child.stdout.setEncoding("utf8");
|
|
115
|
+
child.stdout.on("data", function (/** @type {string} */ data) {
|
|
116
|
+
scriptOutput += data.toString();
|
|
117
|
+
});
|
|
118
|
+
|
|
119
|
+
child.stderr.setEncoding("utf8");
|
|
120
|
+
child.stderr.on("data", function (/** @type {string} */ data) {
|
|
121
|
+
scriptOutput += data.toString();
|
|
122
|
+
});
|
|
123
|
+
|
|
124
|
+
child.on("close", function (code) {
|
|
125
|
+
console.log(`Ran elm-review in ${timeFrom(startTime)}`);
|
|
126
|
+
if (code === 0) {
|
|
127
|
+
reject();
|
|
128
|
+
} else {
|
|
129
|
+
resolve(scriptOutput);
|
|
130
|
+
}
|
|
131
|
+
});
|
|
132
|
+
});
|
|
133
|
+
}
|
|
134
|
+
|
|
93
135
|
module.exports = {
|
|
94
136
|
spawnElmMake,
|
|
95
137
|
compileElmForBrowser,
|
|
138
|
+
runElmReview,
|
|
96
139
|
};
|
|
97
140
|
|
|
98
141
|
/**
|
|
@@ -1,8 +1,14 @@
|
|
|
1
1
|
const path = require("path");
|
|
2
2
|
const fs = require("fs");
|
|
3
|
+
const which = require("which");
|
|
3
4
|
const chokidar = require("chokidar");
|
|
4
|
-
const {
|
|
5
|
+
const {
|
|
6
|
+
spawnElmMake,
|
|
7
|
+
compileElmForBrowser,
|
|
8
|
+
runElmReview,
|
|
9
|
+
} = require("./compile-elm.js");
|
|
5
10
|
const http = require("http");
|
|
11
|
+
const https = require("https");
|
|
6
12
|
const codegen = require("./codegen.js");
|
|
7
13
|
const kleur = require("kleur");
|
|
8
14
|
const serveStatic = require("serve-static");
|
|
@@ -12,9 +18,10 @@ const { Worker, SHARE_ENV } = require("worker_threads");
|
|
|
12
18
|
const os = require("os");
|
|
13
19
|
const { ensureDirSync } = require("./file-helpers.js");
|
|
14
20
|
const baseMiddleware = require("./basepath-middleware.js");
|
|
21
|
+
const devcert = require("devcert");
|
|
15
22
|
|
|
16
23
|
/**
|
|
17
|
-
* @param {{ port: string; base: string }} options
|
|
24
|
+
* @param {{ port: string; base: string; https: boolean; }} options
|
|
18
25
|
*/
|
|
19
26
|
async function start(options) {
|
|
20
27
|
let threadReadyQueue = [];
|
|
@@ -23,6 +30,7 @@ async function start(options) {
|
|
|
23
30
|
const cpuCount = os.cpus().length;
|
|
24
31
|
|
|
25
32
|
const port = options.port;
|
|
33
|
+
const useHttps = options.https;
|
|
26
34
|
let elmMakeRunning = true;
|
|
27
35
|
|
|
28
36
|
const serve = serveStatic("public/", { index: false });
|
|
@@ -45,6 +53,12 @@ async function start(options) {
|
|
|
45
53
|
});
|
|
46
54
|
|
|
47
55
|
await codegen.generate(options.base);
|
|
56
|
+
try {
|
|
57
|
+
await ensureRequiredExecutables();
|
|
58
|
+
} catch (error) {
|
|
59
|
+
console.error(error);
|
|
60
|
+
process.exit(1);
|
|
61
|
+
}
|
|
48
62
|
let clientElmMakeProcess = compileElmForBrowser();
|
|
49
63
|
let pendingCliCompile = compileCliApp();
|
|
50
64
|
watchElmSourceDirs(true);
|
|
@@ -59,7 +73,7 @@ async function start(options) {
|
|
|
59
73
|
});
|
|
60
74
|
console.log(
|
|
61
75
|
`${kleur.dim(`elm-pages dev server running at`)} ${kleur.green(
|
|
62
|
-
|
|
76
|
+
`<${useHttps ? "https" : "http"}://localhost:${port}>`
|
|
63
77
|
)}`
|
|
64
78
|
);
|
|
65
79
|
const poolSize = Math.max(1, cpuCount / 2 - 1);
|
|
@@ -107,7 +121,12 @@ async function start(options) {
|
|
|
107
121
|
.use(serveStaticCode)
|
|
108
122
|
.use(serve)
|
|
109
123
|
.use(processRequest);
|
|
110
|
-
|
|
124
|
+
if (useHttps) {
|
|
125
|
+
const ssl = await devcert.certificateFor("localhost");
|
|
126
|
+
https.createServer(ssl, app).listen(port);
|
|
127
|
+
} else {
|
|
128
|
+
http.createServer(app).listen(port);
|
|
129
|
+
}
|
|
111
130
|
/**
|
|
112
131
|
* @param {http.IncomingMessage} request
|
|
113
132
|
* @param {http.ServerResponse} response
|
|
@@ -122,7 +141,6 @@ async function start(options) {
|
|
|
122
141
|
}
|
|
123
142
|
|
|
124
143
|
watcher.on("all", async function (eventName, pathThatChanged) {
|
|
125
|
-
// console.log({ pathThatChanged });
|
|
126
144
|
if (pathThatChanged === "elm.json") {
|
|
127
145
|
watchElmSourceDirs(false);
|
|
128
146
|
} else if (pathThatChanged.endsWith(".css")) {
|
|
@@ -288,13 +306,34 @@ async function start(options) {
|
|
|
288
306
|
try {
|
|
289
307
|
await pendingCliCompile;
|
|
290
308
|
} catch (error) {
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
309
|
+
let isImplicitContractError = false;
|
|
310
|
+
try {
|
|
311
|
+
isImplicitContractError = JSON.parse(error).errors.some(
|
|
312
|
+
(errorItem) => errorItem.name === "TemplateModulesBeta"
|
|
313
|
+
);
|
|
314
|
+
} catch (unexpectedError) {
|
|
315
|
+
console.log("Unexpected error", unexpectedError);
|
|
316
|
+
}
|
|
317
|
+
if (isImplicitContractError) {
|
|
318
|
+
const reviewOutput = await runElmReview();
|
|
319
|
+
console.log(restoreColor(JSON.parse(reviewOutput)));
|
|
320
|
+
|
|
321
|
+
if (req.url.includes("content.json")) {
|
|
322
|
+
res.writeHead(500, { "Content-Type": "application/json" });
|
|
323
|
+
res.end(reviewOutput);
|
|
324
|
+
} else {
|
|
325
|
+
res.writeHead(500, { "Content-Type": "text/html" });
|
|
326
|
+
res.end(errorHtml());
|
|
327
|
+
}
|
|
295
328
|
} else {
|
|
296
|
-
|
|
297
|
-
|
|
329
|
+
console.log(restoreColor(JSON.parse(error)));
|
|
330
|
+
if (req.url.includes("content.json")) {
|
|
331
|
+
res.writeHead(500, { "Content-Type": "application/json" });
|
|
332
|
+
res.end(error);
|
|
333
|
+
} else {
|
|
334
|
+
res.writeHead(500, { "Content-Type": "text/html" });
|
|
335
|
+
res.end(errorHtml());
|
|
336
|
+
}
|
|
298
337
|
}
|
|
299
338
|
return;
|
|
300
339
|
}
|
|
@@ -495,4 +534,17 @@ function errorHtml() {
|
|
|
495
534
|
`;
|
|
496
535
|
}
|
|
497
536
|
|
|
537
|
+
async function ensureRequiredExecutables() {
|
|
538
|
+
try {
|
|
539
|
+
await which("elm");
|
|
540
|
+
} catch (error) {
|
|
541
|
+
throw "I couldn't find elm on the PATH. Please ensure it's installed, either globally, or locally. If it's installed locally, ensure you're running through an NPM script or with npx so the PATH is configured to include it.";
|
|
542
|
+
}
|
|
543
|
+
try {
|
|
544
|
+
await which("elm-review");
|
|
545
|
+
} catch (error) {
|
|
546
|
+
throw "I couldn't find elm-review on the PATH. Please ensure it's installed, either globally, or locally. If it's installed locally, ensure you're running through an NPM script or with npx so the PATH is configured to include it.";
|
|
547
|
+
}
|
|
548
|
+
}
|
|
549
|
+
|
|
498
550
|
module.exports = { start };
|
|
@@ -10,12 +10,12 @@ const kleur = require("kleur");
|
|
|
10
10
|
* This function takes in the error title and the path to the file with the error and formats it like elm make's regular output
|
|
11
11
|
**/
|
|
12
12
|
/**
|
|
13
|
-
* @param {string}
|
|
13
|
+
* @param {string} rule
|
|
14
14
|
* @param {string} path
|
|
15
15
|
* */
|
|
16
|
-
const parseHeader = (
|
|
16
|
+
const parseHeader = (rule, path) =>
|
|
17
17
|
kleur.cyan(
|
|
18
|
-
`-- ${
|
|
18
|
+
`-- ${rule.replace("-", " ")} --------------- ${path || ""}
|
|
19
19
|
`
|
|
20
20
|
);
|
|
21
21
|
|
|
@@ -32,17 +32,45 @@ function parseMsg(msg) {
|
|
|
32
32
|
return msg;
|
|
33
33
|
} else {
|
|
34
34
|
if (msg.underline && msg.color) {
|
|
35
|
-
return kleur[msg.color
|
|
35
|
+
return kleur[toKleurColor(msg.color)]().underline(msg.string);
|
|
36
36
|
} else if (msg.underline) {
|
|
37
37
|
return kleur.underline(msg.string);
|
|
38
38
|
} else if (msg.color) {
|
|
39
|
-
return kleur[msg.color
|
|
39
|
+
return kleur[toKleurColor(msg.color)](msg.string);
|
|
40
40
|
} else {
|
|
41
41
|
return msg.string;
|
|
42
42
|
}
|
|
43
43
|
}
|
|
44
44
|
}
|
|
45
45
|
|
|
46
|
+
/**
|
|
47
|
+
* @param {string} color
|
|
48
|
+
* @returns {keyof import("kleur").Kleur}
|
|
49
|
+
* */
|
|
50
|
+
function toKleurColor(color) {
|
|
51
|
+
if (color.startsWith("#")) {
|
|
52
|
+
const hexCode = color.slice(1);
|
|
53
|
+
switch (hexCode) {
|
|
54
|
+
// color codes from https://github.com/jfmengels/node-elm-review/blob/d4a6de524cfc33c490c751a3bb084e86accf25fd/template/src/Elm/Review/Text.elm#L80
|
|
55
|
+
case "33BBC8": {
|
|
56
|
+
return "cyan";
|
|
57
|
+
}
|
|
58
|
+
case "33BBC8": {
|
|
59
|
+
return "cyan";
|
|
60
|
+
}
|
|
61
|
+
case "FFFF00": {
|
|
62
|
+
return "yellow";
|
|
63
|
+
}
|
|
64
|
+
case "008000": {
|
|
65
|
+
return "green";
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
return "red";
|
|
69
|
+
} else {
|
|
70
|
+
return color.toLowerCase();
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
|
|
46
74
|
/** @typedef {{problems: {title: string; message: unknown}[]; path: string}[]} Errors } */
|
|
47
75
|
|
|
48
76
|
/**
|
|
@@ -61,6 +89,12 @@ const restoreColor = (error) => {
|
|
|
61
89
|
problems.map(restoreProblem(path)).join("\n\n\n")
|
|
62
90
|
)
|
|
63
91
|
.join("\n\n\n\n\n");
|
|
92
|
+
} else if (error.type === "review-errors") {
|
|
93
|
+
return error.errors
|
|
94
|
+
.map(({ errors, path }) =>
|
|
95
|
+
errors.map(restoreProblem(path)).join("\n\n\n")
|
|
96
|
+
)
|
|
97
|
+
.join("\n\n\n\n\n");
|
|
64
98
|
} else if (error.type === "error") {
|
|
65
99
|
return restoreProblem(error.path)(error);
|
|
66
100
|
} else {
|
|
@@ -78,13 +112,23 @@ const restoreColor = (error) => {
|
|
|
78
112
|
* This function takes in the array of compiler errors and maps over them to generate a formatted compiler error
|
|
79
113
|
**/
|
|
80
114
|
const restoreProblem =
|
|
81
|
-
(/** @type {string} */ path) =>
|
|
82
|
-
|
|
83
|
-
|
|
115
|
+
(/** @type {string} */ path) => (/** @type {Problem | IError} */ info) => {
|
|
116
|
+
if (info.rule && info.formatted) {
|
|
117
|
+
return [
|
|
118
|
+
parseHeader(info.rule, path),
|
|
119
|
+
...info.formatted.map(parseMsg),
|
|
120
|
+
].join("");
|
|
121
|
+
} else {
|
|
122
|
+
return [
|
|
123
|
+
parseHeader(info.title, path),
|
|
124
|
+
...info.message.map(parseMsg),
|
|
125
|
+
].join("");
|
|
126
|
+
}
|
|
127
|
+
};
|
|
84
128
|
|
|
85
129
|
module.exports = { restoreColor };
|
|
86
130
|
|
|
87
|
-
/** @typedef { CompilerError | ReportError } RootObject */
|
|
131
|
+
/** @typedef { CompilerError | ReportError | IElmReviewError } RootObject */
|
|
88
132
|
|
|
89
133
|
/** @typedef { { type: "compile-errors"; errors: Error_[]; } } CompilerError */
|
|
90
134
|
/** @typedef { { type: "error"; path: string; title: string; message: Message[]; } } ReportError */
|
|
@@ -97,3 +141,12 @@ module.exports = { restoreColor };
|
|
|
97
141
|
/** @typedef {string | {underline: boolean; color: string?; string: string}} Message */
|
|
98
142
|
|
|
99
143
|
/** @typedef { { path: string; name: string; problems: Problem[]; } } Error_ */
|
|
144
|
+
|
|
145
|
+
/** @typedef { { type: "review-errors"; errors: IFileError[]; } } IElmReviewError */
|
|
146
|
+
|
|
147
|
+
/** @typedef { { path: string; errors: IError[]; } } IFileError */
|
|
148
|
+
|
|
149
|
+
/** @typedef { { rule: string; formatted: unknown[]; ruleLink: string; message: string; details: string[]; region: IRegion; fix?: { range: IRegion; string: string; }[]; } } IError */
|
|
150
|
+
|
|
151
|
+
/** @typedef { { start: IPosition; end: IPosition; } } IRegion */
|
|
152
|
+
/** @typedef { { line: number; column: number; } } IPosition */
|
|
@@ -168,12 +168,17 @@ view page maybePageUrl globalData pageData =
|
|
|
168
168
|
|
|
169
169
|
_ ->
|
|
170
170
|
{ title = "Model mismatch", body = Html.text <| "Model mismatch" }
|
|
171
|
-
, head =
|
|
171
|
+
, head = ${
|
|
172
|
+
phase === "browser"
|
|
173
|
+
? "[]"
|
|
174
|
+
: `Page.${moduleName(name)}.page.head
|
|
172
175
|
{ data = data
|
|
173
176
|
, sharedData = globalData
|
|
174
177
|
, routeParams = ${emptyRouteParams(name) ? "{}" : "s"}
|
|
175
178
|
, path = page.path
|
|
176
179
|
}
|
|
180
|
+
`
|
|
181
|
+
}
|
|
177
182
|
}
|
|
178
183
|
`
|
|
179
184
|
)
|
|
@@ -415,8 +420,12 @@ main =
|
|
|
415
420
|
{ init = init Nothing
|
|
416
421
|
, urlToRoute = Route.urlToRoute
|
|
417
422
|
, routeToPath = \\route -> route |> Maybe.map Route.routeToPath |> Maybe.withDefault []
|
|
418
|
-
, site = Site.config
|
|
419
|
-
, getStaticRoutes =
|
|
423
|
+
, site = ${phase === "browser" ? `Nothing` : `Just Site.config`}
|
|
424
|
+
, getStaticRoutes = ${
|
|
425
|
+
phase === "browser"
|
|
426
|
+
? `DataSource.succeed []`
|
|
427
|
+
: `getStaticRoutes |> DataSource.map (List.map Just)`
|
|
428
|
+
}
|
|
420
429
|
, handleRoute = handleRoute
|
|
421
430
|
, view = view
|
|
422
431
|
, update = update
|
|
@@ -431,7 +440,11 @@ main =
|
|
|
431
440
|
, fromJsPort = fromJsPort identity
|
|
432
441
|
, data = dataForRoute
|
|
433
442
|
, sharedData = Shared.template.data
|
|
434
|
-
, apiRoutes =
|
|
443
|
+
, apiRoutes = ${
|
|
444
|
+
phase === "browser"
|
|
445
|
+
? `\\_ -> []`
|
|
446
|
+
: `\\htmlToString -> pathsToGenerateHandler :: routePatterns :: manifestHandler :: Api.routes getStaticRoutes htmlToString`
|
|
447
|
+
}
|
|
435
448
|
, pathPatterns = routePatterns3
|
|
436
449
|
, basePath = [ ${basePath
|
|
437
450
|
.split("/")
|
package/generator/src/init.js
CHANGED
|
@@ -19,6 +19,10 @@ async function run(name) {
|
|
|
19
19
|
path.resolve(appRoot, "gitignore"),
|
|
20
20
|
path.resolve(appRoot, ".gitignore")
|
|
21
21
|
);
|
|
22
|
+
/* Since .elm-pages is in source-directories, make sure we create it before running any elm-pages commands
|
|
23
|
+
in case we run any install commands first. See: https://github.com/dillonkearns/elm-pages/issues/205
|
|
24
|
+
*/
|
|
25
|
+
fs.mkdirSync(path.join(appRoot, ".elm-pages"), { recursive: true });
|
|
22
26
|
} catch (err) {
|
|
23
27
|
console.log(err);
|
|
24
28
|
process.exit(1);
|