elm-pages 3.0.0-beta.1 → 3.0.0-beta.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/README.md +10 -1
- package/codegen/elm-pages-codegen.js +803 -284
- 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/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/Runner.elm.js +285 -101
- 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 +1 -1
- package/generator/dead-code-review/src/Pages/Review/DeadCodeEliminateData.elm +140 -17
- package/generator/dead-code-review/tests/Pages/Review/DeadCodeEliminateDataTest.elm +218 -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/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/node_runner.js +1 -1
- package/generator/review/elm-stuff/tests-0.19.1/js/node_supervisor.js +1 -1
- package/generator/src/SharedTemplate.elm +1 -1
- package/generator/src/build.js +75 -42
- package/generator/src/compatibility-key.js +1 -0
- package/generator/src/config.js +41 -0
- package/generator/src/dev-server.js +36 -56
- package/generator/src/generate-template-module-connector.js +0 -28
- package/generator/src/pre-render-html.js +31 -17
- package/generator/src/render.js +2 -0
- package/generator/src/seo-renderer.js +11 -4
- package/generator/src/vite-utils.js +78 -0
- package/generator/template/app/Api.elm +1 -1
- package/generator/template/app/Site.elm +6 -1
- package/package.json +2 -3
- package/src/ApiRoute.elm +0 -3
- package/src/DataSource/File.elm +1 -1
- package/src/DataSource/Internal/Request.elm +0 -5
- package/src/DataSource.elm +39 -31
- package/src/Form/Field.elm +1 -1
- package/src/Form.elm +1 -1
- package/src/Head/Seo.elm +16 -27
- package/src/Head.elm +126 -0
- package/src/HtmlPrinter.elm +7 -3
- package/src/Pages/Generate.elm +544 -102
- package/src/Pages/Internal/NotFoundReason.elm +3 -2
- package/src/Pages/Internal/Platform/Cli.elm +91 -27
- package/src/Pages/Internal/Platform/Cli.elm.bak +1276 -0
- package/src/Pages/Internal/Platform/CompatibilityKey.elm +6 -0
- package/src/Pages/Internal/Platform.elm +34 -27
- package/src/Pages/ProgramConfig.elm +6 -3
- package/src/Server/Session.elm +149 -83
- package/src/Server/SetCookie.elm +89 -31
package/generator/src/build.js
CHANGED
|
@@ -16,6 +16,9 @@ const { build } = require("vite");
|
|
|
16
16
|
const preRenderHtml = require("./pre-render-html.js");
|
|
17
17
|
const esbuild = require("esbuild");
|
|
18
18
|
const { createHash } = require("crypto");
|
|
19
|
+
const { merge_vite_configs } = require("./vite-utils.js");
|
|
20
|
+
const { resolveConfig } = require("./config.js");
|
|
21
|
+
const globby = require("globby");
|
|
19
22
|
|
|
20
23
|
let pool = [];
|
|
21
24
|
let pagesReady;
|
|
@@ -75,40 +78,31 @@ async function run(options) {
|
|
|
75
78
|
const generateCode = codegen.generate(options.base);
|
|
76
79
|
|
|
77
80
|
await generateCode;
|
|
81
|
+
|
|
82
|
+
const config = await resolveConfig();
|
|
78
83
|
await fsPromises.writeFile(
|
|
79
84
|
"elm-stuff/elm-pages/index.html",
|
|
80
|
-
preRenderHtml.templateHtml()
|
|
85
|
+
preRenderHtml.templateHtml(false, config.headTagsTemplate)
|
|
81
86
|
);
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
const viteConfig = config.vite || {};
|
|
96
|
-
|
|
97
|
-
const buildComplete = build({
|
|
98
|
-
configFile: false,
|
|
99
|
-
root: process.cwd(),
|
|
100
|
-
base: options.base,
|
|
101
|
-
ssr: false,
|
|
102
|
-
|
|
103
|
-
build: {
|
|
104
|
-
manifest: true,
|
|
105
|
-
outDir: "dist",
|
|
106
|
-
rollupOptions: {
|
|
107
|
-
input: "elm-stuff/elm-pages/index.html",
|
|
87
|
+
const viteConfig = merge_vite_configs(
|
|
88
|
+
{
|
|
89
|
+
configFile: false,
|
|
90
|
+
root: process.cwd(),
|
|
91
|
+
base: options.base,
|
|
92
|
+
ssr: false,
|
|
93
|
+
|
|
94
|
+
build: {
|
|
95
|
+
manifest: true,
|
|
96
|
+
outDir: "dist",
|
|
97
|
+
rollupOptions: {
|
|
98
|
+
input: "elm-stuff/elm-pages/index.html",
|
|
99
|
+
},
|
|
108
100
|
},
|
|
109
101
|
},
|
|
110
|
-
|
|
111
|
-
|
|
102
|
+
config.vite || {}
|
|
103
|
+
);
|
|
104
|
+
|
|
105
|
+
const buildComplete = build(viteConfig);
|
|
112
106
|
const compileClientDone = compileElm(options);
|
|
113
107
|
await buildComplete;
|
|
114
108
|
await compileClientDone;
|
|
@@ -124,10 +118,25 @@ async function run(options) {
|
|
|
124
118
|
"dist/elm-stuff/elm-pages/index.html",
|
|
125
119
|
"utf-8"
|
|
126
120
|
);
|
|
127
|
-
const
|
|
121
|
+
const preloadFiles = [
|
|
122
|
+
`elm.${browserElmHash}.js`,
|
|
123
|
+
...Object.entries(manifest).map((entry) => entry[1].file),
|
|
124
|
+
].map((file) => path.join(options.base, file));
|
|
125
|
+
const userProcessedPreloads = preloadFiles.flatMap((file) => {
|
|
126
|
+
const userPreloadForFile = config.preloadTagForFile(file);
|
|
127
|
+
if (userPreloadForFile === true) {
|
|
128
|
+
return [defaultPreloadForFile(file)];
|
|
129
|
+
} else if (userPreloadForFile === false) {
|
|
130
|
+
return [];
|
|
131
|
+
} else if (typeof userPreloadForFile === "string") {
|
|
132
|
+
return [userPreloadForFile];
|
|
133
|
+
} else {
|
|
134
|
+
throw `I expected preloadTagForFile in elm-pages.config.mjs to return a string or boolean, but instead it returned: ${userPreloadForFile}`;
|
|
135
|
+
}
|
|
136
|
+
});
|
|
128
137
|
|
|
129
138
|
const processedIndexTemplate = indexTemplate
|
|
130
|
-
.replace("<!-- PLACEHOLDER_PRELOADS -->",
|
|
139
|
+
.replace("<!-- PLACEHOLDER_PRELOADS -->", userProcessedPreloads.join(""))
|
|
131
140
|
.replace(
|
|
132
141
|
'<script defer src="/elm.js" type="text/javascript"></script>',
|
|
133
142
|
`<script defer src="/elm.${browserElmHash}.js" type="text/javascript"></script>`
|
|
@@ -146,21 +155,20 @@ async function run(options) {
|
|
|
146
155
|
metafile: true,
|
|
147
156
|
bundle: true,
|
|
148
157
|
watch: false,
|
|
149
|
-
logLevel: "
|
|
158
|
+
logLevel: "silent",
|
|
150
159
|
})
|
|
151
160
|
.then((result) => {
|
|
152
|
-
|
|
161
|
+
try {
|
|
162
|
+
global.portsFilePath = Object.keys(result.metafile.outputs)[0];
|
|
163
|
+
} catch (e) {}
|
|
153
164
|
})
|
|
154
165
|
.catch((error) => {
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
console.warn("No port-data-source file found.");
|
|
162
|
-
} else {
|
|
163
|
-
console.error("Failed to load port-data-source file", error);
|
|
166
|
+
const portDataSourceFileFound =
|
|
167
|
+
globby.sync("./port-data-source.*").length > 0;
|
|
168
|
+
if (portDataSourceFileFound) {
|
|
169
|
+
// don't present error if there are no files matching port-data-source
|
|
170
|
+
// if there are files matching port-data-source, warn the user in case something went wrong loading it
|
|
171
|
+
console.error("Failed to start port-data-source watcher", error);
|
|
164
172
|
}
|
|
165
173
|
});
|
|
166
174
|
// TODO extract common code for compiling ports file?
|
|
@@ -627,6 +635,31 @@ async function runAdapter(adaptFn, processedIndexTemplate) {
|
|
|
627
635
|
}
|
|
628
636
|
}
|
|
629
637
|
|
|
638
|
+
// Source: https://github.com/vitejs/vite/blob/c53ffec3465d2d28d08d29ca61313469e03f5dd6/playground/ssr-vue/src/entry-server.js#L50-L68
|
|
639
|
+
/**
|
|
640
|
+
* @param {string} file
|
|
641
|
+
*/
|
|
642
|
+
function defaultPreloadForFile(file) {
|
|
643
|
+
if (file.endsWith(".js")) {
|
|
644
|
+
return `<link rel="modulepreload" crossorigin href="${file}">`;
|
|
645
|
+
} else if (file.endsWith(".css")) {
|
|
646
|
+
return `<link rel="preload" href="${file}" as="style">`;
|
|
647
|
+
} else if (file.endsWith(".woff")) {
|
|
648
|
+
return ` <link rel="preload" href="${file}" as="font" type="font/woff" crossorigin>`;
|
|
649
|
+
} else if (file.endsWith(".woff2")) {
|
|
650
|
+
return ` <link rel="preload" href="${file}" as="font" type="font/woff2" crossorigin>`;
|
|
651
|
+
} else if (file.endsWith(".gif")) {
|
|
652
|
+
return ` <link rel="preload" href="${file}" as="image" type="image/gif">`;
|
|
653
|
+
} else if (file.endsWith(".jpg") || file.endsWith(".jpeg")) {
|
|
654
|
+
return ` <link rel="preload" href="${file}" as="image" type="image/jpeg">`;
|
|
655
|
+
} else if (file.endsWith(".png")) {
|
|
656
|
+
return ` <link rel="preload" href="${file}" as="image" type="image/png">`;
|
|
657
|
+
} else {
|
|
658
|
+
// TODO
|
|
659
|
+
return "";
|
|
660
|
+
}
|
|
661
|
+
}
|
|
662
|
+
|
|
630
663
|
/** @typedef { { route : string; contentJson : string; head : SeoTag[]; html: string; body: string; } } FromElm */
|
|
631
664
|
/** @typedef {HeadTag | JsonLdTag} SeoTag */
|
|
632
665
|
/** @typedef {{ name: string; attributes: string[][]; type: 'head' }} HeadTag */
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
module.exports = { compatibilityKey: 1 };
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
const path = require("path");
|
|
2
|
+
|
|
3
|
+
async function resolveConfig() {
|
|
4
|
+
const initialConfig = await await import(
|
|
5
|
+
path.join(process.cwd(), "elm-pages.config.mjs")
|
|
6
|
+
)
|
|
7
|
+
.then(async (elmPagesConfig) => {
|
|
8
|
+
return (
|
|
9
|
+
elmPagesConfig.default || {
|
|
10
|
+
headTagsTemplate: defaultHeadTagsTemplate,
|
|
11
|
+
}
|
|
12
|
+
);
|
|
13
|
+
})
|
|
14
|
+
.catch((error) => {
|
|
15
|
+
console.warn(
|
|
16
|
+
"No `elm-pages.config.mjs` file found. Using default config."
|
|
17
|
+
);
|
|
18
|
+
return {
|
|
19
|
+
headTagsTemplate: defaultHeadTagsTemplate,
|
|
20
|
+
vite: {},
|
|
21
|
+
};
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
return {
|
|
25
|
+
preloadTagForFile: function () {
|
|
26
|
+
return true;
|
|
27
|
+
},
|
|
28
|
+
headTagsTemplate: defaultHeadTagsTemplate,
|
|
29
|
+
vite: {},
|
|
30
|
+
...initialConfig,
|
|
31
|
+
};
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
function defaultHeadTagsTemplate(context) {
|
|
35
|
+
return `
|
|
36
|
+
<link rel="stylesheet" href="/style.css" />
|
|
37
|
+
<meta name="generator" content="elm-pages v${context.cliVersion}" />
|
|
38
|
+
`;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
module.exports = { resolveConfig };
|
|
@@ -24,6 +24,10 @@ const busboy = require("busboy");
|
|
|
24
24
|
const { createServer: createViteServer } = require("vite");
|
|
25
25
|
const cliVersion = require("../../package.json").version;
|
|
26
26
|
const esbuild = require("esbuild");
|
|
27
|
+
const { merge_vite_configs } = require("./vite-utils.js");
|
|
28
|
+
const { templateHtml } = require("./pre-render-html.js");
|
|
29
|
+
const { resolveConfig } = require("./config.js");
|
|
30
|
+
const globby = require("globby");
|
|
27
31
|
|
|
28
32
|
/**
|
|
29
33
|
* @param {{ port: string; base: string; https: boolean; debug: boolean; }} options
|
|
@@ -117,27 +121,23 @@ async function start(options) {
|
|
|
117
121
|
watcher.add(sourceDirs);
|
|
118
122
|
}
|
|
119
123
|
|
|
120
|
-
const
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
)
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
root: process.cwd(),
|
|
138
|
-
base: options.base,
|
|
139
|
-
...viteConfig,
|
|
140
|
-
});
|
|
124
|
+
const config = await resolveConfig();
|
|
125
|
+
const vite = await createViteServer(
|
|
126
|
+
merge_vite_configs(
|
|
127
|
+
{
|
|
128
|
+
server: {
|
|
129
|
+
middlewareMode: "ssr",
|
|
130
|
+
base: options.base,
|
|
131
|
+
port: options.port,
|
|
132
|
+
},
|
|
133
|
+
configFile: false,
|
|
134
|
+
root: process.cwd(),
|
|
135
|
+
base: options.base,
|
|
136
|
+
},
|
|
137
|
+
|
|
138
|
+
config.vite
|
|
139
|
+
)
|
|
140
|
+
);
|
|
141
141
|
esbuild
|
|
142
142
|
.build({
|
|
143
143
|
entryPoints: ["./port-data-source"],
|
|
@@ -148,7 +148,7 @@ async function start(options) {
|
|
|
148
148
|
metafile: true,
|
|
149
149
|
bundle: true,
|
|
150
150
|
watch: true,
|
|
151
|
-
logLevel: "
|
|
151
|
+
logLevel: "silent",
|
|
152
152
|
|
|
153
153
|
outdir: ".elm-pages/compiled-ports",
|
|
154
154
|
entryNames: "[dir]/[name]-[hash]",
|
|
@@ -158,11 +158,13 @@ async function start(options) {
|
|
|
158
158
|
name: "example",
|
|
159
159
|
setup(build) {
|
|
160
160
|
build.onEnd((result) => {
|
|
161
|
-
|
|
161
|
+
try {
|
|
162
|
+
global.portsFilePath = Object.keys(result.metafile.outputs)[0];
|
|
162
163
|
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
164
|
+
clients.forEach((client) => {
|
|
165
|
+
client.response.write(`data: content.dat\n\n`);
|
|
166
|
+
});
|
|
167
|
+
} catch (e) {}
|
|
166
168
|
});
|
|
167
169
|
},
|
|
168
170
|
},
|
|
@@ -172,7 +174,13 @@ async function start(options) {
|
|
|
172
174
|
console.log("Watching port-data-source...");
|
|
173
175
|
})
|
|
174
176
|
.catch((error) => {
|
|
175
|
-
|
|
177
|
+
const portDataSourceFileFound =
|
|
178
|
+
globby.sync("./port-data-source.*").length > 0;
|
|
179
|
+
if (portDataSourceFileFound) {
|
|
180
|
+
// don't present error if there are no files matching port-data-source
|
|
181
|
+
// if there are files matching port-data-source, warn the user in case something went wrong loading it
|
|
182
|
+
console.error("Failed to start port-data-source watcher", error);
|
|
183
|
+
}
|
|
176
184
|
});
|
|
177
185
|
|
|
178
186
|
const app = connect()
|
|
@@ -459,35 +467,7 @@ async function start(options) {
|
|
|
459
467
|
}
|
|
460
468
|
case "html": {
|
|
461
469
|
try {
|
|
462
|
-
const template =
|
|
463
|
-
/*html*/
|
|
464
|
-
`<!DOCTYPE html>
|
|
465
|
-
<!-- ROOT --><html lang="en">
|
|
466
|
-
<head>
|
|
467
|
-
<script src="/hmr.js" type="text/javascript"></script>
|
|
468
|
-
<script src="/elm.js" type="text/javascript"></script>
|
|
469
|
-
<link rel="stylesheet" href="/style.css">
|
|
470
|
-
<link rel="stylesheet" href="/dev-style.css">
|
|
471
|
-
<script src="/elm-pages.js" type="module"></script>
|
|
472
|
-
<meta charset="UTF-8" />
|
|
473
|
-
<meta name="viewport" content="width=device-width,initial-scale=1" />
|
|
474
|
-
<title><!-- PLACEHOLDER_TITLE --></title>
|
|
475
|
-
<meta name="generator" content="elm-pages v${cliVersion}" />
|
|
476
|
-
<meta name="mobile-web-app-capable" content="yes" />
|
|
477
|
-
<meta name="theme-color" content="#ffffff" />
|
|
478
|
-
<meta name="apple-mobile-web-app-capable" content="yes" />
|
|
479
|
-
<meta
|
|
480
|
-
name="apple-mobile-web-app-status-bar-style"
|
|
481
|
-
content="black-translucent"
|
|
482
|
-
/>
|
|
483
|
-
<!-- PLACEHOLDER_HEAD_AND_DATA -->
|
|
484
|
-
</head>
|
|
485
|
-
<body>
|
|
486
|
-
<div data-url="" display="none"></div>
|
|
487
|
-
<!-- PLACEHOLDER_HTML -->
|
|
488
|
-
</body>
|
|
489
|
-
</html>
|
|
490
|
-
`;
|
|
470
|
+
const template = templateHtml(true, config.headTagsTemplate);
|
|
491
471
|
const processedTemplate = await vite.transformIndexHtml(
|
|
492
472
|
req.originalUrl,
|
|
493
473
|
template
|
|
@@ -63,32 +63,10 @@ async function generateTemplateModuleConnector(basePath, phase) {
|
|
|
63
63
|
}
|
|
64
64
|
|
|
65
65
|
async function runElmCodegenCli(templates, basePath, phase) {
|
|
66
|
-
// await runElmCodegenInstall();
|
|
67
|
-
// try {
|
|
68
|
-
// await compileCliApp(
|
|
69
|
-
// // { debug: true },
|
|
70
|
-
// {},
|
|
71
|
-
// `Generate.elm`,
|
|
72
|
-
// path.join(process.cwd(), "elm-stuff/elm-pages-codegen.js"),
|
|
73
|
-
// path.join(__dirname, "../../codegen"),
|
|
74
|
-
|
|
75
|
-
// path.join(process.cwd(), "elm-stuff/elm-pages-codegen.js")
|
|
76
|
-
// );
|
|
77
|
-
// } catch (error) {
|
|
78
|
-
// console.log(restoreColorSafe(error));
|
|
79
|
-
// process.exit(1);
|
|
80
|
-
// // throw error;
|
|
81
|
-
// }
|
|
82
|
-
|
|
83
66
|
const filePath = path.join(__dirname, `../../codegen/elm-pages-codegen.js`);
|
|
84
67
|
|
|
85
|
-
// TODO use uncached require here to prevent stale code from running
|
|
86
|
-
|
|
87
68
|
const promise = new Promise((resolve, reject) => {
|
|
88
69
|
const elmPagesCodegen = require(filePath).Elm.Generate;
|
|
89
|
-
// path.join(
|
|
90
|
-
// process.cwd(),
|
|
91
|
-
// "./elm-stuff/elm-pages-codegen.js")
|
|
92
70
|
|
|
93
71
|
const app = elmPagesCodegen.init({
|
|
94
72
|
flags: { templates: templates, basePath, phase },
|
|
@@ -104,12 +82,10 @@ async function runElmCodegenCli(templates, basePath, phase) {
|
|
|
104
82
|
}
|
|
105
83
|
});
|
|
106
84
|
const filesToGenerate = await promise;
|
|
107
|
-
console.dir(filesToGenerate.map((file) => file.path));
|
|
108
85
|
|
|
109
86
|
return filesToGenerate;
|
|
110
87
|
}
|
|
111
88
|
|
|
112
|
-
|
|
113
89
|
/**
|
|
114
90
|
*
|
|
115
91
|
* @param {string[][]} templates
|
|
@@ -175,8 +151,6 @@ function sortScore(name) {
|
|
|
175
151
|
);
|
|
176
152
|
}
|
|
177
153
|
|
|
178
|
-
|
|
179
|
-
|
|
180
154
|
function fetcherModule(name) {
|
|
181
155
|
let moduleName = name.join(".");
|
|
182
156
|
// TODO need to account for splat routes/etc.
|
|
@@ -248,7 +222,6 @@ submit toMsg options =
|
|
|
248
222
|
`;
|
|
249
223
|
}
|
|
250
224
|
|
|
251
|
-
|
|
252
225
|
/**
|
|
253
226
|
* Convert Strings from camelCase to kebab-case
|
|
254
227
|
* @param {string} input
|
|
@@ -258,5 +231,4 @@ function camelToKebab(input) {
|
|
|
258
231
|
return input.replace(/([a-z])([A-Z])/g, "$1-$2").toLowerCase();
|
|
259
232
|
}
|
|
260
233
|
|
|
261
|
-
|
|
262
234
|
module.exports = { generateTemplateModuleConnector, sortTemplates };
|
|
@@ -17,28 +17,32 @@ function wrapHtml(basePath, fromElm, contentDatPayload) {
|
|
|
17
17
|
};
|
|
18
18
|
}
|
|
19
19
|
|
|
20
|
-
|
|
20
|
+
/**
|
|
21
|
+
* @param {boolean} devMode
|
|
22
|
+
* @param {(context: {cliVersion: string;}) => string} userHeadTagsTemplate
|
|
23
|
+
*/
|
|
24
|
+
function templateHtml(devMode, userHeadTagsTemplate) {
|
|
21
25
|
return /* html */ `<!DOCTYPE html>
|
|
22
26
|
<!-- ROOT --><html lang="en">
|
|
23
27
|
<head>
|
|
24
|
-
<!-- PLACEHOLDER_PRELOADS -->
|
|
25
|
-
<script defer src="/elm.js" type="text/javascript"></script>
|
|
26
|
-
<script defer src="${path.join(
|
|
27
|
-
__dirname,
|
|
28
|
-
"../static-code/elm-pages.js"
|
|
29
|
-
)}" type="module"></script>
|
|
30
|
-
<link rel="stylesheet" href="/style.css" />
|
|
31
28
|
<meta charset="UTF-8" />
|
|
32
|
-
<meta name="viewport" content="width=device-width,initial-scale=1" />
|
|
33
29
|
<title><!-- PLACEHOLDER_TITLE --></title>
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
<
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
30
|
+
${
|
|
31
|
+
devMode
|
|
32
|
+
? `<script src="/hmr.js" type="text/javascript"></script>
|
|
33
|
+
<link rel="stylesheet" href="/dev-style.css">`
|
|
34
|
+
: `<!-- PLACEHOLDER_PRELOADS -->`
|
|
35
|
+
}
|
|
36
|
+
<script defer src="/elm.js" type="text/javascript"></script>
|
|
37
|
+
${
|
|
38
|
+
devMode
|
|
39
|
+
? `<script src="/elm-pages.js" type="module"></script>`
|
|
40
|
+
: `<script defer src="${path.join(
|
|
41
|
+
__dirname,
|
|
42
|
+
"../static-code/elm-pages.js"
|
|
43
|
+
)}" type="module"></script>`
|
|
44
|
+
}
|
|
45
|
+
${indent(userHeadTagsTemplate({ cliVersion }))}
|
|
42
46
|
<!-- PLACEHOLDER_HEAD_AND_DATA -->
|
|
43
47
|
</head>
|
|
44
48
|
<body>
|
|
@@ -48,6 +52,16 @@ function templateHtml() {
|
|
|
48
52
|
</html>`;
|
|
49
53
|
}
|
|
50
54
|
|
|
55
|
+
/**
|
|
56
|
+
* @param {string} snippet
|
|
57
|
+
*/
|
|
58
|
+
function indent(snippet) {
|
|
59
|
+
return snippet
|
|
60
|
+
.split("\n")
|
|
61
|
+
.map((line) => ` ${line}`)
|
|
62
|
+
.join("\n");
|
|
63
|
+
}
|
|
64
|
+
|
|
51
65
|
/**
|
|
52
66
|
* @param {string} processedTemplate
|
|
53
67
|
*/
|
package/generator/src/render.js
CHANGED
|
@@ -9,6 +9,7 @@ const preRenderHtml = require("./pre-render-html.js");
|
|
|
9
9
|
const { lookupOrPerform } = require("./request-cache.js");
|
|
10
10
|
const kleur = require("kleur");
|
|
11
11
|
const cookie = require("cookie-signature");
|
|
12
|
+
const { compatibilityKey } = require("./compatibility-key.js");
|
|
12
13
|
kleur.enabled = true;
|
|
13
14
|
|
|
14
15
|
process.on("unhandledRejection", (error) => {
|
|
@@ -98,6 +99,7 @@ function runElmApp(
|
|
|
98
99
|
app = elmModule.Elm.Main.init({
|
|
99
100
|
flags: {
|
|
100
101
|
mode,
|
|
102
|
+
compatibilityKey,
|
|
101
103
|
request: {
|
|
102
104
|
payload: modifiedRequest,
|
|
103
105
|
kind: "single-page",
|
|
@@ -43,11 +43,16 @@ function headTag(rootModifiers) {
|
|
|
43
43
|
|
|
44
44
|
function toString(/** @type { SeoTag[] } */ tags) {
|
|
45
45
|
return tags
|
|
46
|
-
.
|
|
46
|
+
.flatMap((headTag) => {
|
|
47
47
|
if (headTag.type === "head") {
|
|
48
|
-
return appendTag(headTag);
|
|
48
|
+
return [appendTag(headTag)];
|
|
49
49
|
} else if (headTag.type === "json-ld") {
|
|
50
|
-
return appendJsonLdTag(headTag);
|
|
50
|
+
return [appendJsonLdTag(headTag)];
|
|
51
|
+
} else if (headTag.type === "stripped") {
|
|
52
|
+
console.warn(
|
|
53
|
+
`WARNING: Head.nonLoadingTag value ignored because it used a loading tag: ${headTag.message}`
|
|
54
|
+
);
|
|
55
|
+
return [];
|
|
51
56
|
} else {
|
|
52
57
|
throw new Error(`Unknown tag type ${JSON.stringify(headTag)}`);
|
|
53
58
|
}
|
|
@@ -55,7 +60,7 @@ function toString(/** @type { SeoTag[] } */ tags) {
|
|
|
55
60
|
.join("");
|
|
56
61
|
}
|
|
57
62
|
|
|
58
|
-
/** @typedef {HeadTag | JsonLdTag} SeoTag */
|
|
63
|
+
/** @typedef {HeadTag | JsonLdTag | StrippedTag} SeoTag */
|
|
59
64
|
|
|
60
65
|
/** @typedef {{ name: string; attributes: string[][]; type: 'head' }} HeadTag */
|
|
61
66
|
function appendTag(/** @type {HeadTag} */ tagDetails) {
|
|
@@ -66,6 +71,8 @@ function appendTag(/** @type {HeadTag} */ tagDetails) {
|
|
|
66
71
|
}
|
|
67
72
|
|
|
68
73
|
/** @typedef {{ contents: Object; type: 'json-ld' }} JsonLdTag */
|
|
74
|
+
/** @typedef {{ message: string; type: 'stripped' }} StrippedTag */
|
|
75
|
+
|
|
69
76
|
function appendJsonLdTag(/** @type {JsonLdTag} */ tagDetails) {
|
|
70
77
|
return `<script type="application/ld+json">
|
|
71
78
|
${JSON.stringify(tagDetails.contents)}
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
/** This code is from https://github.com/sveltejs/kit/blob/3b457f67d4d7c59fc63bb3f600a490e4dacc2e62/packages/kit/src/exports/vite/utils.js */
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* @param {...import('vite').UserConfig} configs
|
|
5
|
+
* @returns {import('vite').UserConfig}
|
|
6
|
+
*/
|
|
7
|
+
function merge_vite_configs(...configs) {
|
|
8
|
+
return deep_merge(
|
|
9
|
+
...configs.map((config) => ({
|
|
10
|
+
...config,
|
|
11
|
+
resolve: {
|
|
12
|
+
...config.resolve,
|
|
13
|
+
alias: normalize_alias(config.resolve?.alias || {}),
|
|
14
|
+
},
|
|
15
|
+
}))
|
|
16
|
+
);
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Takes zero or more objects and returns a new object that has all the values
|
|
21
|
+
* deeply merged together. None of the original objects will be mutated at any
|
|
22
|
+
* level, and the returned object will have no references to the original
|
|
23
|
+
* objects at any depth. If there's a conflict the last one wins, except for
|
|
24
|
+
* arrays which will be combined.
|
|
25
|
+
* @param {...Object} objects
|
|
26
|
+
* @returns {Record<string, any>} the merged object
|
|
27
|
+
*/
|
|
28
|
+
function deep_merge(...objects) {
|
|
29
|
+
const result = {};
|
|
30
|
+
/** @type {string[]} */
|
|
31
|
+
objects.forEach((o) => merge_into(result, o));
|
|
32
|
+
return result;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* normalize kit.vite.resolve.alias as an array
|
|
37
|
+
* @param {import('vite').AliasOptions} o
|
|
38
|
+
* @returns {import('vite').Alias[]}
|
|
39
|
+
*/
|
|
40
|
+
function normalize_alias(o) {
|
|
41
|
+
if (Array.isArray(o)) return o;
|
|
42
|
+
return Object.entries(o).map(([find, replacement]) => ({
|
|
43
|
+
find,
|
|
44
|
+
replacement,
|
|
45
|
+
}));
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* Merges b into a, recursively, mutating a.
|
|
50
|
+
* @param {Record<string, any>} a
|
|
51
|
+
* @param {Record<string, any>} b
|
|
52
|
+
*/
|
|
53
|
+
function merge_into(a, b) {
|
|
54
|
+
/**
|
|
55
|
+
* Checks for "plain old Javascript object", typically made as an object
|
|
56
|
+
* literal. Excludes Arrays and built-in types like Buffer.
|
|
57
|
+
* @param {any} x
|
|
58
|
+
*/
|
|
59
|
+
const is_plain_object = (x) =>
|
|
60
|
+
typeof x === "object" && x.constructor === Object;
|
|
61
|
+
|
|
62
|
+
for (const prop in b) {
|
|
63
|
+
if (is_plain_object(b[prop])) {
|
|
64
|
+
if (!is_plain_object(a[prop])) {
|
|
65
|
+
a[prop] = {};
|
|
66
|
+
}
|
|
67
|
+
merge_into(a[prop], b[prop]);
|
|
68
|
+
} else if (Array.isArray(b[prop])) {
|
|
69
|
+
if (!Array.isArray(a[prop])) {
|
|
70
|
+
a[prop] = [];
|
|
71
|
+
}
|
|
72
|
+
a[prop].push(...b[prop]);
|
|
73
|
+
} else {
|
|
74
|
+
a[prop] = b[prop];
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
module.exports = { merge_vite_configs };
|
|
@@ -14,6 +14,11 @@ config =
|
|
|
14
14
|
|
|
15
15
|
head : DataSource (List Head.Tag)
|
|
16
16
|
head =
|
|
17
|
-
[ Head.
|
|
17
|
+
[ Head.metaName "viewport" (Head.raw "width=device-width,initial-scale=1")
|
|
18
|
+
, Head.metaName "mobile-web-app-capable" (Head.raw "yes")
|
|
19
|
+
, Head.metaName "theme-color" (Head.raw "#ffffff")
|
|
20
|
+
, Head.metaName "apple-mobile-web-app-capable" (Head.raw "yes")
|
|
21
|
+
, Head.metaName "apple-mobile-web-app-status-bar-style" (Head.raw "black-translucent")
|
|
22
|
+
, Head.sitemapLink "/sitemap.xml"
|
|
18
23
|
]
|
|
19
24
|
|> DataSource.succeed
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "elm-pages",
|
|
3
|
-
"version": "3.0.0-beta.
|
|
3
|
+
"version": "3.0.0-beta.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.",
|
|
@@ -10,7 +10,6 @@
|
|
|
10
10
|
"test": "./test.sh",
|
|
11
11
|
"test:snapshot": "(cd examples/escaping && npm install && npm test) && (cd examples/base-path && npm install && npm test)",
|
|
12
12
|
"cypress": "npm start & cypress run",
|
|
13
|
-
"prepare": "npm run build:generator",
|
|
14
13
|
"build:generator": "elm-codegen install && (cd codegen && lamdera make Generate.elm --output elm-pages-codegen.js)",
|
|
15
14
|
"review": "elm-review"
|
|
16
15
|
},
|
|
@@ -46,7 +45,7 @@
|
|
|
46
45
|
"object-hash": "^2.2.0",
|
|
47
46
|
"serve-static": "^1.15.0",
|
|
48
47
|
"terser": "^5.14.2",
|
|
49
|
-
"vite": "^3.
|
|
48
|
+
"vite": "^3.1.8",
|
|
50
49
|
"which": "^2.0.2"
|
|
51
50
|
},
|
|
52
51
|
"devDependencies": {
|
package/src/ApiRoute.elm
CHANGED
|
@@ -11,9 +11,6 @@ module ApiRoute exposing
|
|
|
11
11
|
to a DataSource so you can pull in HTTP data, etc. Because ApiRoutes don't hydrate into Elm apps (like pages in elm-pages do), you can pull in as much data as you want in
|
|
12
12
|
the DataSource for your ApiRoutes, and it won't effect the payload size. Instead, the size of an ApiRoute is just the content you output for that route.
|
|
13
13
|
|
|
14
|
-
In a future release, ApiRoutes may be able to run at request-time in a serverless function, allowing you to use pure Elm code to create dynamic APIs, and even pulling in data from
|
|
15
|
-
DataSources dynamically.
|
|
16
|
-
|
|
17
14
|
@docs ApiRoute, ApiRouteBuilder, Response
|
|
18
15
|
|
|
19
16
|
@docs capture, literal, slash, succeed
|