elm-pages 2.1.9 → 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/src/basepath-middleware.js +15 -9
- package/generator/src/build.js +50 -1
- package/generator/src/cli.js +13 -9
- package/generator/src/dev-server.js +11 -4
- package/generator/src/generate-template-module-connector.js +17 -4
- package/generator/src/init.js +4 -0
- package/generator/src/pre-render-html.js +9 -9
- package/generator/src/seo-renderer.js +21 -2
- package/package.json +4 -2
- package/src/AriaLiveAnnouncer.elm +2 -2
- package/src/DataSource/File.elm +2 -1
- package/src/DataSource/Glob.elm +30 -3
- package/src/DataSource.elm +5 -5
- package/src/Head.elm +17 -2
- package/src/HtmlPrinter.elm +4 -3
- package/src/Pages/Internal/Platform/Cli.elm +57 -33
- package/src/Pages/Internal/Platform/StaticResponses.elm +1 -1
- package/src/Pages/ProgramConfig.elm +1 -1
- package/src/Pages/Review/NoContractViolations.elm +4 -5
- package/src/Pages/Secrets.elm +2 -0
- package/src/{ElmHtml → Test/Html/Internal/ElmHtml}/Constants.elm +16 -10
- package/src/{ElmHtml → Test/Html/Internal/ElmHtml}/Helpers.elm +2 -2
- package/src/{ElmHtml → Test/Html/Internal/ElmHtml}/InternalTypes.elm +69 -132
- package/src/{ElmHtml → Test/Html/Internal/ElmHtml}/Markdown.elm +5 -22
- package/src/{ElmHtml → Test/Html/Internal/ElmHtml}/ToString.elm +13 -15
- package/src/Test/Internal/KernelConstants.elm +34 -0
- package/src/ElmHtml/ToElmString.elm +0 -151
- package/src/ElmHtml/ToHtml.elm +0 -82
|
@@ -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
|
@@ -344,12 +344,61 @@ async function compileCliApp(options) {
|
|
|
344
344
|
);
|
|
345
345
|
|
|
346
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
|
+
|
|
347
389
|
await fsPromises.writeFile(
|
|
348
390
|
ELM_FILE_PATH,
|
|
349
391
|
elmFileContent
|
|
350
392
|
.replace(
|
|
351
393
|
/return \$elm\$json\$Json\$Encode\$string\(.REPLACE_ME_WITH_JSON_STRINGIFY.\)/g,
|
|
352
|
-
"return " +
|
|
394
|
+
"return " +
|
|
395
|
+
(options.debug
|
|
396
|
+
? `${forceThunksSource}
|
|
397
|
+
return _Json_wrap(forceThunks(html));
|
|
398
|
+
`
|
|
399
|
+
: `${forceThunksSource}
|
|
400
|
+
return forceThunks(html);
|
|
401
|
+
`)
|
|
353
402
|
)
|
|
354
403
|
.replace(
|
|
355
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();
|
|
@@ -8,6 +8,7 @@ const {
|
|
|
8
8
|
runElmReview,
|
|
9
9
|
} = require("./compile-elm.js");
|
|
10
10
|
const http = require("http");
|
|
11
|
+
const https = require("https");
|
|
11
12
|
const codegen = require("./codegen.js");
|
|
12
13
|
const kleur = require("kleur");
|
|
13
14
|
const serveStatic = require("serve-static");
|
|
@@ -17,9 +18,10 @@ const { Worker, SHARE_ENV } = require("worker_threads");
|
|
|
17
18
|
const os = require("os");
|
|
18
19
|
const { ensureDirSync } = require("./file-helpers.js");
|
|
19
20
|
const baseMiddleware = require("./basepath-middleware.js");
|
|
21
|
+
const devcert = require("devcert");
|
|
20
22
|
|
|
21
23
|
/**
|
|
22
|
-
* @param {{ port: string; base: string }} options
|
|
24
|
+
* @param {{ port: string; base: string; https: boolean; }} options
|
|
23
25
|
*/
|
|
24
26
|
async function start(options) {
|
|
25
27
|
let threadReadyQueue = [];
|
|
@@ -28,6 +30,7 @@ async function start(options) {
|
|
|
28
30
|
const cpuCount = os.cpus().length;
|
|
29
31
|
|
|
30
32
|
const port = options.port;
|
|
33
|
+
const useHttps = options.https;
|
|
31
34
|
let elmMakeRunning = true;
|
|
32
35
|
|
|
33
36
|
const serve = serveStatic("public/", { index: false });
|
|
@@ -70,7 +73,7 @@ async function start(options) {
|
|
|
70
73
|
});
|
|
71
74
|
console.log(
|
|
72
75
|
`${kleur.dim(`elm-pages dev server running at`)} ${kleur.green(
|
|
73
|
-
|
|
76
|
+
`<${useHttps ? "https" : "http"}://localhost:${port}>`
|
|
74
77
|
)}`
|
|
75
78
|
);
|
|
76
79
|
const poolSize = Math.max(1, cpuCount / 2 - 1);
|
|
@@ -118,7 +121,12 @@ async function start(options) {
|
|
|
118
121
|
.use(serveStaticCode)
|
|
119
122
|
.use(serve)
|
|
120
123
|
.use(processRequest);
|
|
121
|
-
|
|
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
|
+
}
|
|
122
130
|
/**
|
|
123
131
|
* @param {http.IncomingMessage} request
|
|
124
132
|
* @param {http.ServerResponse} response
|
|
@@ -133,7 +141,6 @@ async function start(options) {
|
|
|
133
141
|
}
|
|
134
142
|
|
|
135
143
|
watcher.on("all", async function (eventName, pathThatChanged) {
|
|
136
|
-
// console.log({ pathThatChanged });
|
|
137
144
|
if (pathThatChanged === "elm.json") {
|
|
138
145
|
watchElmSourceDirs(false);
|
|
139
146
|
} else if (pathThatChanged.endsWith(".css")) {
|
|
@@ -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);
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
const cliVersion = require("../../package.json").version;
|
|
2
2
|
const seo = require("./seo-renderer.js");
|
|
3
3
|
const elmPagesJsMinified = require("./elm-pages-js-minified.js");
|
|
4
|
-
const path = require("path");
|
|
4
|
+
const path = require("path").posix;
|
|
5
5
|
const jsesc = require("jsesc");
|
|
6
6
|
|
|
7
7
|
/** @typedef { { head: any[]; errors: any[]; contentJson: any[]; html: string; route: string; title: string; } } Arg */
|
|
@@ -29,9 +29,9 @@ module.exports =
|
|
|
29
29
|
<link rel="modulepreload" href="${path.join(basePath, "index.js")}">
|
|
30
30
|
${devServerOnly(
|
|
31
31
|
/* html */ `<script defer="defer" src="${path.join(
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
32
|
+
basePath,
|
|
33
|
+
"hmr.js"
|
|
34
|
+
)}" type="text/javascript"></script>`
|
|
35
35
|
)}
|
|
36
36
|
<script defer="defer" src="${path.join(
|
|
37
37
|
basePath,
|
|
@@ -45,7 +45,7 @@ ${elmPagesJsMinified}
|
|
|
45
45
|
</script>
|
|
46
46
|
<title>${fromElm.title}</title>
|
|
47
47
|
<meta name="generator" content="elm-pages v${cliVersion}">
|
|
48
|
-
<link rel="manifest" href="
|
|
48
|
+
<link rel="manifest" href="${path.join(basePath, "manifest.json")}">
|
|
49
49
|
<meta name="mobile-web-app-capable" content="yes">
|
|
50
50
|
<meta name="theme-color" content="#ffffff">
|
|
51
51
|
<meta name="apple-mobile-web-app-capable" content="yes">
|
|
@@ -81,10 +81,10 @@ function pathToRoot(cleanedRoute) {
|
|
|
81
81
|
return cleanedRoute === ""
|
|
82
82
|
? cleanedRoute
|
|
83
83
|
: cleanedRoute
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
84
|
+
.split("/")
|
|
85
|
+
.map((_) => "..")
|
|
86
|
+
.join("/")
|
|
87
|
+
.replace(/\.$/, "./");
|
|
88
88
|
}
|
|
89
89
|
|
|
90
90
|
function devServerStyleTag() {
|
|
@@ -60,7 +60,7 @@ function toString(/** @type { SeoTag[] } */ tags) {
|
|
|
60
60
|
/** @typedef {{ name: string; attributes: string[][]; type: 'head' }} HeadTag */
|
|
61
61
|
function appendTag(/** @type {HeadTag} */ tagDetails) {
|
|
62
62
|
const tagsString = tagDetails.attributes.map(([name, value]) => {
|
|
63
|
-
return
|
|
63
|
+
return pairToAttribute([name, value]);
|
|
64
64
|
});
|
|
65
65
|
return ` <${tagDetails.name} ${tagsString.join(" ")} />`;
|
|
66
66
|
}
|
|
@@ -77,5 +77,24 @@ ${JSON.stringify(tagDetails.contents)}
|
|
|
77
77
|
* @returns string
|
|
78
78
|
*/
|
|
79
79
|
function pairToAttribute([name, value]) {
|
|
80
|
-
return `${name}="${value}"`;
|
|
80
|
+
return `${name}="${quoteattr(value)}"`;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
function quoteattr(s, preserveCR) {
|
|
84
|
+
preserveCR = preserveCR ? " " : "\n";
|
|
85
|
+
return (
|
|
86
|
+
("" + s) /* Forces the conversion to string. */
|
|
87
|
+
.replace(/&/g, "&") /* This MUST be the 1st replacement. */
|
|
88
|
+
.replace(/'/g, "'") /* The 4 other predefined entities, required. */
|
|
89
|
+
.replace(/"/g, """)
|
|
90
|
+
.replace(/</g, "<")
|
|
91
|
+
.replace(/>/g, ">")
|
|
92
|
+
/*
|
|
93
|
+
You may add other replacements here for HTML only
|
|
94
|
+
(but it's not necessary).
|
|
95
|
+
Or for XML, only if the named entities are defined in its DTD.
|
|
96
|
+
*/
|
|
97
|
+
.replace(/\r\n/g, preserveCR) /* Must be before the next replacement. */
|
|
98
|
+
.replace(/[\r\n]/g, preserveCR)
|
|
99
|
+
);
|
|
81
100
|
}
|
package/package.json
CHANGED
|
@@ -1,13 +1,14 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "elm-pages",
|
|
3
|
-
"version": "2.1.
|
|
3
|
+
"version": "2.1.10",
|
|
4
4
|
"homepage": "https://elm-pages.com",
|
|
5
5
|
"moduleResolution": "node",
|
|
6
6
|
"description": "Type-safe static sites, written in pure elm with your own custom elm-markup syntax.",
|
|
7
7
|
"main": "index.js",
|
|
8
8
|
"scripts": {
|
|
9
9
|
"start": "cd examples/end-to-end && npm start",
|
|
10
|
-
"test": "npx elmi-to-json --version && elm-verify-examples --run-tests && elm-test && (cd generator && mocha) && (cd examples/routing && npm i && npm run build -- --debug && elm-test)",
|
|
10
|
+
"test": "npx elmi-to-json --version && elm-verify-examples --run-tests && elm-test && (cd generator && mocha) && (cd examples/routing && npm i && npm run build -- --debug && elm-test) && npm run test:snapshot",
|
|
11
|
+
"test:snapshot": "(cd examples/escaping && npm install && npm test && cd ../..) && (cd examples/base-path && npm install && npm test && cd ../..)",
|
|
11
12
|
"cypress": "npm start & cypress run --spec cypress/integration/elm-pages-dev.spec.js",
|
|
12
13
|
"review": "elm-review"
|
|
13
14
|
},
|
|
@@ -26,6 +27,7 @@
|
|
|
26
27
|
"commander": "^8.1.0",
|
|
27
28
|
"connect": "^3.7.0",
|
|
28
29
|
"cross-spawn": "7.0.3",
|
|
30
|
+
"devcert": "^1.2.0",
|
|
29
31
|
"elm-doc-preview": "^5.0.5",
|
|
30
32
|
"elm-hot": "^1.1.6",
|
|
31
33
|
"fs-extra": "^10.0.0",
|
|
@@ -25,8 +25,8 @@ mainView title =
|
|
|
25
25
|
--, Attr.attribute "ref" reference
|
|
26
26
|
, Attr.style "position" "absolute"
|
|
27
27
|
, Attr.style "top" "0"
|
|
28
|
-
, Attr.style "width" "
|
|
29
|
-
, Attr.style "height" "
|
|
28
|
+
, Attr.style "width" "1px"
|
|
29
|
+
, Attr.style "height" "1px"
|
|
30
30
|
, Attr.style "padding" "0"
|
|
31
31
|
, Attr.style "overflow" "hidden"
|
|
32
32
|
, Attr.style "clip" "rect(0, 0, 0, 0)"
|
package/src/DataSource/File.elm
CHANGED
|
@@ -155,8 +155,9 @@ just the metadata.
|
|
|
155
155
|
|
|
156
156
|
blogPost : DataSource BlogPostMetadata
|
|
157
157
|
blogPost =
|
|
158
|
-
File.onlyFrontmatter
|
|
158
|
+
File.onlyFrontmatter
|
|
159
159
|
blogPostDecoder
|
|
160
|
+
"blog/hello-world.md"
|
|
160
161
|
|
|
161
162
|
type alias BlogPostMetadata =
|
|
162
163
|
{ title : String
|
package/src/DataSource/Glob.elm
CHANGED
|
@@ -4,7 +4,7 @@ module DataSource.Glob exposing
|
|
|
4
4
|
, captureFilePath
|
|
5
5
|
, wildcard, recursiveWildcard
|
|
6
6
|
, int, digits
|
|
7
|
-
, expectUniqueMatch
|
|
7
|
+
, expectUniqueMatch, expectUniqueMatchFromList
|
|
8
8
|
, literal
|
|
9
9
|
, map, succeed, toDataSource
|
|
10
10
|
, oneOf
|
|
@@ -195,7 +195,7 @@ That will give us
|
|
|
195
195
|
|
|
196
196
|
## Matching a Specific Number of Files
|
|
197
197
|
|
|
198
|
-
@docs expectUniqueMatch
|
|
198
|
+
@docs expectUniqueMatch, expectUniqueMatchFromList
|
|
199
199
|
|
|
200
200
|
|
|
201
201
|
## Glob Patterns
|
|
@@ -1016,8 +1016,35 @@ expectUniqueMatch glob =
|
|
|
1016
1016
|
DataSource.succeed file
|
|
1017
1017
|
|
|
1018
1018
|
[] ->
|
|
1019
|
-
DataSource.fail "No files matched
|
|
1019
|
+
DataSource.fail <| "No files matched the pattern: " ++ toPatternString glob
|
|
1020
1020
|
|
|
1021
1021
|
_ ->
|
|
1022
1022
|
DataSource.fail "More than one file matched."
|
|
1023
1023
|
)
|
|
1024
|
+
|
|
1025
|
+
|
|
1026
|
+
{-| -}
|
|
1027
|
+
expectUniqueMatchFromList : List (Glob a) -> DataSource a
|
|
1028
|
+
expectUniqueMatchFromList globs =
|
|
1029
|
+
globs
|
|
1030
|
+
|> List.map toDataSource
|
|
1031
|
+
|> DataSource.combine
|
|
1032
|
+
|> DataSource.andThen
|
|
1033
|
+
(\matchingFiles ->
|
|
1034
|
+
case List.concat matchingFiles of
|
|
1035
|
+
[ file ] ->
|
|
1036
|
+
DataSource.succeed file
|
|
1037
|
+
|
|
1038
|
+
[] ->
|
|
1039
|
+
DataSource.fail <| "No files matched the patterns: " ++ (globs |> List.map toPatternString |> String.join ", ")
|
|
1040
|
+
|
|
1041
|
+
_ ->
|
|
1042
|
+
DataSource.fail "More than one file matched."
|
|
1043
|
+
)
|
|
1044
|
+
|
|
1045
|
+
|
|
1046
|
+
toPatternString : Glob a -> String
|
|
1047
|
+
toPatternString glob =
|
|
1048
|
+
case glob of
|
|
1049
|
+
Glob pattern_ _ _ ->
|
|
1050
|
+
pattern_
|
package/src/DataSource.elm
CHANGED
|
@@ -555,20 +555,20 @@ lookupUrls requestInfo =
|
|
|
555
555
|
[]
|
|
556
556
|
|
|
557
557
|
|
|
558
|
-
{-| Build off of the response from a previous `
|
|
558
|
+
{-| Build off of the response from a previous `DataSource` request to build a follow-up request. You can use the data
|
|
559
559
|
from the previous response to build up the URL, headers, etc. that you send to the subsequent request.
|
|
560
560
|
|
|
561
561
|
import DataSource
|
|
562
562
|
import Json.Decode as Decode exposing (Decoder)
|
|
563
563
|
|
|
564
|
-
licenseData :
|
|
564
|
+
licenseData : DataSource String
|
|
565
565
|
licenseData =
|
|
566
|
-
|
|
566
|
+
DataSource.Http.get
|
|
567
567
|
(Secrets.succeed "https://api.github.com/repos/dillonkearns/elm-pages")
|
|
568
568
|
(Decode.at [ "license", "url" ] Decode.string)
|
|
569
|
-
|>
|
|
569
|
+
|> DataSource.andThen
|
|
570
570
|
(\licenseUrl ->
|
|
571
|
-
|
|
571
|
+
DataSource.Http.get (Secrets.succeed licenseUrl) (Decode.field "description" Decode.string)
|
|
572
572
|
)
|
|
573
573
|
|
|
574
574
|
-}
|
package/src/Head.elm
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
module Head exposing
|
|
2
|
-
( Tag, metaName, metaProperty
|
|
2
|
+
( Tag, metaName, metaProperty, metaRedirect
|
|
3
3
|
, rssLink, sitemapLink, rootLanguage
|
|
4
4
|
, structuredData
|
|
5
5
|
, AttributeValue
|
|
@@ -17,7 +17,7 @@ so you can use the `Tag` type in your type annotations.
|
|
|
17
17
|
But this module might be useful if you have a special use case, or if you are
|
|
18
18
|
writing a plugin package to extend `elm-pages`.
|
|
19
19
|
|
|
20
|
-
@docs Tag, metaName, metaProperty
|
|
20
|
+
@docs Tag, metaName, metaProperty, metaRedirect
|
|
21
21
|
@docs rssLink, sitemapLink, rootLanguage
|
|
22
22
|
|
|
23
23
|
|
|
@@ -383,6 +383,21 @@ metaName name content =
|
|
|
383
383
|
]
|
|
384
384
|
|
|
385
385
|
|
|
386
|
+
{-| Example:
|
|
387
|
+
|
|
388
|
+
metaRedirect (Raw "0; url=https://google.com")
|
|
389
|
+
|
|
390
|
+
Results in `<meta http-equiv="refresh" content="0; url=https://google.com" />`
|
|
391
|
+
|
|
392
|
+
-}
|
|
393
|
+
metaRedirect : AttributeValue -> Tag
|
|
394
|
+
metaRedirect content =
|
|
395
|
+
node "meta"
|
|
396
|
+
[ ( "http-equiv", Raw "refresh" )
|
|
397
|
+
, ( "content", content )
|
|
398
|
+
]
|
|
399
|
+
|
|
400
|
+
|
|
386
401
|
{-| Low-level function for creating a tag for the HTML document's `<head>`.
|
|
387
402
|
-}
|
|
388
403
|
node : String -> List ( String, AttributeValue ) -> Tag
|
package/src/HtmlPrinter.elm
CHANGED
|
@@ -1,17 +1,18 @@
|
|
|
1
1
|
module HtmlPrinter exposing (htmlToString)
|
|
2
2
|
|
|
3
|
-
import ElmHtml.InternalTypes exposing (decodeElmHtml)
|
|
4
|
-
import ElmHtml.ToString exposing (defaultFormatOptions, nodeToStringWithOptions)
|
|
5
3
|
import Html exposing (Html)
|
|
6
4
|
import Json.Decode as Decode
|
|
7
5
|
import Json.Encode
|
|
6
|
+
import Test.Html.Internal.ElmHtml.InternalTypes exposing (decodeElmHtml)
|
|
7
|
+
import Test.Html.Internal.ElmHtml.ToString exposing (defaultFormatOptions, nodeToStringWithOptions)
|
|
8
|
+
import VirtualDom
|
|
8
9
|
|
|
9
10
|
|
|
10
11
|
htmlToString : Html msg -> String
|
|
11
12
|
htmlToString viewHtml =
|
|
12
13
|
case
|
|
13
14
|
Decode.decodeValue
|
|
14
|
-
(decodeElmHtml (\_ _ -> Decode.succeed ()))
|
|
15
|
+
(decodeElmHtml (\_ _ -> VirtualDom.Normal (Decode.succeed ())))
|
|
15
16
|
(asJsonView viewHtml)
|
|
16
17
|
of
|
|
17
18
|
Ok str ->
|