elm-pages 2.1.11 → 3.0.0-beta.0
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-stuff/tests-0.19.1/elm-stuff/0.19.1/Pages-Internal-RoutePattern.elmi +0 -0
- package/generator/review/elm-stuff/tests-0.19.1/elm-stuff/0.19.1/Pages-Internal-RoutePattern.elmo +0 -0
- package/generator/review/elm-stuff/tests-0.19.1/elm-stuff/0.19.1/Pages-Review-NoContractViolations.elmi +0 -0
- package/generator/review/elm-stuff/tests-0.19.1/elm-stuff/0.19.1/Pages-Review-NoContractViolations.elmo +0 -0
- package/generator/review/elm-stuff/tests-0.19.1/elm-stuff/0.19.1/Pages-Review-NoContractViolationsTest.elmi +0 -0
- package/generator/review/elm-stuff/tests-0.19.1/elm-stuff/0.19.1/Pages-Review-NoContractViolationsTest.elmo +0 -0
- package/generator/review/elm-stuff/tests-0.19.1/elm-stuff/0.19.1/Reporter.elmi +0 -0
- package/generator/review/elm-stuff/tests-0.19.1/elm-stuff/0.19.1/Reporter.elmo +0 -0
- package/generator/review/elm-stuff/tests-0.19.1/elm-stuff/0.19.1/Runner.elmi +0 -0
- package/generator/review/elm-stuff/tests-0.19.1/elm-stuff/0.19.1/Runner.elmo +0 -0
- package/generator/review/elm-stuff/tests-0.19.1/elm-stuff/0.19.1/d.dat +0 -0
- package/generator/review/elm-stuff/tests-0.19.1/elm-stuff/0.19.1/i.dat +0 -0
- package/generator/{template/public/style.css → review/elm-stuff/tests-0.19.1/elm-stuff/0.19.1/lock} +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 -0
- package/generator/review/elm-stuff/tests-0.19.1/js/Reporter.elm.js +6795 -0
- package/generator/review/elm-stuff/tests-0.19.1/js/Runner.elm.js +27617 -0
- package/generator/review/elm-stuff/tests-0.19.1/js/node_runner.js +110 -0
- package/generator/review/elm-stuff/tests-0.19.1/js/node_supervisor.js +187 -0
- package/generator/review/elm-stuff/tests-0.19.1/js/package.json +1 -0
- package/generator/review/elm-stuff/tests-0.19.1/src/Reporter.elm +26 -0
- package/generator/review/elm-stuff/tests-0.19.1/src/Runner.elm +62 -0
- package/generator/review/elm.json +13 -4
- package/{src → generator/review/src}/Pages/Review/NoContractViolations.elm +148 -148
- package/generator/review/tests/Pages/Review/NoContractViolationsTest.elm +331 -0
- package/generator/src/RouteBuilder.elm +420 -0
- package/generator/src/SharedTemplate.elm +4 -5
- package/generator/src/SiteConfig.elm +3 -9
- package/generator/src/build.js +308 -95
- package/generator/src/cli.js +103 -8
- package/generator/src/codegen.js +192 -35
- package/generator/src/compile-elm.js +183 -31
- package/generator/src/dev-server.js +353 -96
- package/generator/src/elm-application.json +3 -1
- package/generator/src/elm-codegen.js +34 -0
- package/generator/src/elm-file-constants.js +2 -0
- package/generator/src/error-formatter.js +20 -1
- package/generator/src/generate-template-module-connector.js +120 -924
- package/generator/src/hello.ts +5 -0
- package/generator/src/pre-render-html.js +58 -104
- package/generator/src/render-worker.js +27 -13
- package/generator/src/render.js +252 -197
- package/generator/src/request-cache-fs.js +18 -0
- package/generator/src/request-cache.js +128 -56
- package/generator/src/rewrite-client-elm-json.js +49 -0
- package/generator/src/route-codegen-helpers.js +62 -1
- package/generator/static-code/dev-style.css +22 -0
- package/generator/static-code/elm-pages.js +43 -39
- package/generator/static-code/hmr.js +98 -88
- package/generator/template/app/Api.elm +25 -0
- package/generator/template/app/ErrorPage.elm +38 -0
- package/generator/template/app/Route/Index.elm +87 -0
- package/generator/template/{src → app}/Shared.elm +34 -13
- package/generator/template/app/Site.elm +19 -0
- package/generator/template/{src → app}/View.elm +0 -0
- package/generator/template/elm-pages.config.mjs +5 -0
- package/generator/template/elm.json +1 -0
- package/generator/template/{public/index.js → index.ts} +7 -3
- package/generator/template/package.json +4 -4
- package/generator/template/public/favicon.ico +0 -0
- package/generator/template/public/images/icon-png.png +0 -0
- package/generator/template/src/.gitkeep +0 -0
- package/generator/template/style.css +4 -0
- package/package.json +30 -23
- package/src/ApiRoute.elm +176 -43
- package/src/BuildError.elm +10 -1
- package/src/CookieParser.elm +84 -0
- package/src/DataSource/Env.elm +38 -0
- package/src/DataSource/File.elm +27 -16
- package/src/DataSource/Glob.elm +126 -80
- package/src/DataSource/Http.elm +283 -304
- package/src/DataSource/Internal/Glob.elm +5 -21
- package/src/DataSource/Internal/Request.elm +25 -0
- package/src/DataSource/Port.elm +17 -14
- package/src/DataSource.elm +55 -318
- package/src/Form/Field.elm +717 -0
- package/src/Form/FieldStatus.elm +36 -0
- package/src/Form/FieldView.elm +417 -0
- package/src/Form/FormData.elm +22 -0
- package/src/Form/Validation.elm +391 -0
- package/src/Form/Value.elm +118 -0
- package/src/Form.elm +1683 -0
- package/src/FormData.elm +58 -0
- package/src/FormDecoder.elm +102 -0
- package/src/Head/Seo.elm +12 -4
- package/src/Head.elm +12 -2
- package/src/HtmlPrinter.elm +1 -1
- package/src/Internal/ApiRoute.elm +17 -4
- package/src/Internal/Request.elm +7 -0
- package/src/PageServerResponse.elm +68 -0
- package/src/Pages/ContentCache.elm +1 -229
- package/src/Pages/Fetcher.elm +58 -0
- package/src/Pages/FormState.elm +256 -0
- package/src/Pages/Generate.elm +800 -0
- package/src/Pages/Internal/Form.elm +17 -0
- package/src/Pages/Internal/NotFoundReason.elm +3 -55
- package/src/Pages/Internal/Platform/Cli.elm +777 -579
- package/src/Pages/Internal/Platform/Effect.elm +5 -5
- package/src/Pages/Internal/Platform/StaticResponses.elm +178 -394
- package/src/Pages/Internal/Platform/ToJsPayload.elm +24 -23
- package/src/Pages/Internal/Platform.elm +1244 -504
- package/src/Pages/Internal/ResponseSketch.elm +19 -0
- package/src/Pages/Internal/RoutePattern.elm +596 -45
- package/src/Pages/Manifest.elm +26 -0
- package/src/Pages/Msg.elm +79 -0
- package/src/Pages/ProgramConfig.elm +67 -14
- package/src/Pages/SiteConfig.elm +3 -6
- package/src/Pages/StaticHttp/Request.elm +4 -2
- package/src/Pages/StaticHttpRequest.elm +50 -215
- package/src/Pages/Transition.elm +70 -0
- package/src/Path.elm +1 -0
- package/src/Pattern.elm +98 -0
- package/src/RenderRequest.elm +2 -2
- package/src/RequestsAndPending.elm +111 -9
- package/src/Server/Request.elm +1253 -0
- package/src/Server/Response.elm +292 -0
- package/src/Server/Session.elm +316 -0
- package/src/Server/SetCookie.elm +169 -0
- package/src/TerminalText.elm +1 -1
- package/src/Test/Html/Internal/ElmHtml/Markdown.elm +0 -1
- package/src/Test/Html/Internal/ElmHtml/ToString.elm +1 -1
- package/generator/src/Page.elm +0 -359
- package/generator/src/codegen-template-module.js +0 -183
- package/generator/src/elm-pages-js-minified.js +0 -1
- package/generator/template/src/Api.elm +0 -14
- package/generator/template/src/Page/Index.elm +0 -69
- package/generator/template/src/Site.elm +0 -41
- package/src/DataSource/ServerRequest.elm +0 -60
- package/src/Internal/OptimizedDecoder.elm +0 -18
- package/src/KeepOrDiscard.elm +0 -6
- package/src/OptimizedDecoder/Pipeline.elm +0 -335
- package/src/OptimizedDecoder.elm +0 -818
- package/src/Pages/Internal/ApplicationType.elm +0 -6
- package/src/Pages/Secrets.elm +0 -83
- package/src/Secrets.elm +0 -111
- package/src/SecretsDict.elm +0 -45
package/generator/src/build.js
CHANGED
|
@@ -2,7 +2,7 @@ const fs = require("./dir-helpers.js");
|
|
|
2
2
|
const fsPromises = require("fs").promises;
|
|
3
3
|
|
|
4
4
|
const { runElmReview } = require("./compile-elm.js");
|
|
5
|
-
const {
|
|
5
|
+
const { restoreColorSafe } = require("./error-formatter");
|
|
6
6
|
const path = require("path");
|
|
7
7
|
const spawnCallback = require("cross-spawn").spawn;
|
|
8
8
|
const codegen = require("./codegen.js");
|
|
@@ -10,19 +10,29 @@ const terser = require("terser");
|
|
|
10
10
|
const os = require("os");
|
|
11
11
|
const { Worker, SHARE_ENV } = require("worker_threads");
|
|
12
12
|
const { ensureDirSync } = require("./file-helpers.js");
|
|
13
|
+
const { generateClientFolder } = require("./codegen.js");
|
|
13
14
|
const which = require("which");
|
|
15
|
+
const { build } = require("vite");
|
|
16
|
+
const preRenderHtml = require("./pre-render-html.js");
|
|
17
|
+
const esbuild = require("esbuild");
|
|
18
|
+
const { createHash } = require("crypto");
|
|
19
|
+
|
|
14
20
|
let pool = [];
|
|
15
21
|
let pagesReady;
|
|
22
|
+
let pagesErrored;
|
|
16
23
|
let pages = new Promise((resolve, reject) => {
|
|
17
24
|
pagesReady = resolve;
|
|
25
|
+
pagesErrored = reject;
|
|
18
26
|
});
|
|
27
|
+
let pagesReadyCalled = false;
|
|
28
|
+
let activeWorkers = 0;
|
|
19
29
|
let buildError = false;
|
|
20
30
|
|
|
21
|
-
const DIR_PATH =
|
|
31
|
+
const DIR_PATH = process.cwd();
|
|
22
32
|
const OUTPUT_FILE_NAME = "elm.js";
|
|
23
33
|
|
|
24
34
|
process.on("unhandledRejection", (error) => {
|
|
25
|
-
console.
|
|
35
|
+
console.log(error);
|
|
26
36
|
process.exitCode = 1;
|
|
27
37
|
});
|
|
28
38
|
|
|
@@ -39,9 +49,9 @@ async function ensureRequiredDirs() {
|
|
|
39
49
|
|
|
40
50
|
async function ensureRequiredExecutables() {
|
|
41
51
|
try {
|
|
42
|
-
await which("
|
|
52
|
+
await which("lamdera");
|
|
43
53
|
} catch (error) {
|
|
44
|
-
throw "I couldn't find
|
|
54
|
+
throw "I couldn't find lamdera 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
55
|
}
|
|
46
56
|
try {
|
|
47
57
|
await which("elm-optimize-level-2");
|
|
@@ -59,47 +69,153 @@ async function run(options) {
|
|
|
59
69
|
try {
|
|
60
70
|
await ensureRequiredDirs();
|
|
61
71
|
await ensureRequiredExecutables();
|
|
62
|
-
// since init/update are never called in pre-renders, and DataSource.Http is called using
|
|
72
|
+
// since init/update are never called in pre-renders, and DataSource.Http is called using pure NodeJS HTTP fetching
|
|
63
73
|
// we can provide a fake HTTP instead of xhr2 (which is otherwise needed for Elm HTTP requests from Node)
|
|
64
|
-
XMLHttpRequest = {};
|
|
65
74
|
|
|
66
75
|
const generateCode = codegen.generate(options.base);
|
|
67
76
|
|
|
68
|
-
const copyDone = copyAssets();
|
|
69
77
|
await generateCode;
|
|
78
|
+
await fsPromises.writeFile(
|
|
79
|
+
"elm-stuff/elm-pages/index.html",
|
|
80
|
+
preRenderHtml.templateHtml()
|
|
81
|
+
);
|
|
70
82
|
|
|
71
|
-
const
|
|
83
|
+
const config = await import(
|
|
84
|
+
path.join(process.cwd(), "elm-pages.config.mjs")
|
|
85
|
+
)
|
|
86
|
+
.then(async (elmPagesConfig) => {
|
|
87
|
+
return elmPagesConfig.default || {};
|
|
88
|
+
})
|
|
89
|
+
.catch((error) => {
|
|
90
|
+
console.warn(
|
|
91
|
+
"No `elm-pages.config.mjs` file found. Using default config."
|
|
92
|
+
);
|
|
93
|
+
return {};
|
|
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",
|
|
108
|
+
},
|
|
109
|
+
},
|
|
110
|
+
...viteConfig,
|
|
111
|
+
});
|
|
72
112
|
const compileClientDone = compileElm(options);
|
|
73
|
-
await
|
|
74
|
-
|
|
113
|
+
await buildComplete;
|
|
114
|
+
await compileClientDone;
|
|
115
|
+
const fullOutputPath = path.join(process.cwd(), `./dist/elm.js`);
|
|
116
|
+
const withoutExtension = path.join(process.cwd(), `./dist/elm`);
|
|
117
|
+
const browserElmHash = await fingerprintElmAsset(
|
|
118
|
+
fullOutputPath,
|
|
119
|
+
withoutExtension
|
|
120
|
+
);
|
|
121
|
+
const assetManifestPath = path.join(process.cwd(), "dist/manifest.json");
|
|
122
|
+
const manifest = require(assetManifestPath);
|
|
123
|
+
const indexTemplate = await fsPromises.readFile(
|
|
124
|
+
"dist/elm-stuff/elm-pages/index.html",
|
|
125
|
+
"utf-8"
|
|
126
|
+
);
|
|
127
|
+
const preloads = `<link rel="modulepreload" href="/${manifest["elm-stuff/elm-pages/index.html"]["file"]}" />`;
|
|
75
128
|
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
129
|
+
const processedIndexTemplate = indexTemplate
|
|
130
|
+
.replace("<!-- PLACEHOLDER_PRELOADS -->", preloads)
|
|
131
|
+
.replace(
|
|
132
|
+
'<script defer src="/elm.js" type="text/javascript"></script>',
|
|
133
|
+
`<script defer src="/elm.${browserElmHash}.js" type="text/javascript"></script>`
|
|
134
|
+
);
|
|
135
|
+
await fsPromises.writeFile("dist/template.html", processedIndexTemplate);
|
|
136
|
+
await fsPromises.unlink(assetManifestPath);
|
|
137
|
+
|
|
138
|
+
const portDataSourceCompiled = esbuild
|
|
139
|
+
.build({
|
|
140
|
+
entryPoints: ["./port-data-source"],
|
|
141
|
+
platform: "node",
|
|
142
|
+
outfile: ".elm-pages/compiled-ports/port-data-source.js",
|
|
143
|
+
assetNames: "[name]-[hash]",
|
|
144
|
+
chunkNames: "chunks/[name]-[hash]",
|
|
145
|
+
outExtension: { ".js": ".js" },
|
|
146
|
+
metafile: true,
|
|
147
|
+
bundle: true,
|
|
148
|
+
watch: false,
|
|
149
|
+
logLevel: "error",
|
|
150
|
+
})
|
|
151
|
+
.then((result) => {
|
|
152
|
+
global.portsFilePath = Object.keys(result.metafile.outputs)[0];
|
|
153
|
+
})
|
|
154
|
+
.catch((error) => {
|
|
155
|
+
if (
|
|
156
|
+
error.errors.length === 1 &&
|
|
157
|
+
error.errors[0].text.includes(
|
|
158
|
+
`Could not resolve "./port-data-source"`
|
|
159
|
+
)
|
|
160
|
+
) {
|
|
161
|
+
console.warn("No port-data-source file found.");
|
|
162
|
+
} else {
|
|
163
|
+
console.error("Failed to load port-data-source file", error);
|
|
164
|
+
}
|
|
165
|
+
});
|
|
166
|
+
// TODO extract common code for compiling ports file?
|
|
167
|
+
|
|
168
|
+
XMLHttpRequest = {};
|
|
169
|
+
const compileCli = compileCliApp(options);
|
|
79
170
|
try {
|
|
171
|
+
await compileCli;
|
|
172
|
+
await compileClientDone;
|
|
173
|
+
} catch (cliError) {
|
|
174
|
+
// TODO make sure not to print duplicate error output if cleaner review output is printed
|
|
175
|
+
console.error(cliError);
|
|
80
176
|
const reviewOutput = JSON.parse(await runElmReview());
|
|
81
177
|
const isParsingError = reviewOutput.errors.some((reviewError) => {
|
|
82
178
|
return reviewError.errors.some((item) => item.rule === "ParsingError");
|
|
83
179
|
});
|
|
84
180
|
if (isParsingError) {
|
|
85
|
-
console.error(
|
|
181
|
+
console.error(cliError);
|
|
86
182
|
} else {
|
|
87
|
-
console.error(
|
|
183
|
+
console.error(restoreColorSafe(reviewOutput));
|
|
88
184
|
}
|
|
89
185
|
process.exit(1);
|
|
90
|
-
}
|
|
186
|
+
}
|
|
187
|
+
await portDataSourceCompiled;
|
|
188
|
+
const cliDone = runCli(options);
|
|
189
|
+
await cliDone;
|
|
190
|
+
|
|
191
|
+
await fsPromises.rename(
|
|
192
|
+
"dist/____elm-pages-internal____/404/index.html",
|
|
193
|
+
"dist/404.html"
|
|
194
|
+
);
|
|
195
|
+
await runAdapter(
|
|
196
|
+
config.adapter ||
|
|
197
|
+
function () {
|
|
198
|
+
console.log(
|
|
199
|
+
"No adapter configured in elm-pages.config.mjs. Skipping adapter step."
|
|
200
|
+
);
|
|
201
|
+
},
|
|
202
|
+
processedIndexTemplate
|
|
203
|
+
);
|
|
204
|
+
} catch (error) {
|
|
205
|
+
if (error) {
|
|
91
206
|
console.error(error);
|
|
92
|
-
} finally {
|
|
93
|
-
process.exit(1);
|
|
94
207
|
}
|
|
208
|
+
buildError = true;
|
|
209
|
+
process.exitCode = 1;
|
|
95
210
|
}
|
|
96
211
|
}
|
|
97
212
|
|
|
98
213
|
/**
|
|
99
214
|
* @param {string} basePath
|
|
100
215
|
*/
|
|
101
|
-
function initWorker(basePath) {
|
|
216
|
+
function initWorker(basePath, whenDone) {
|
|
102
217
|
return new Promise((resolve, reject) => {
|
|
218
|
+
activeWorkers += 1;
|
|
103
219
|
let newWorker = {
|
|
104
220
|
worker: new Worker(path.join(__dirname, "./render-worker.js"), {
|
|
105
221
|
env: SHARE_ENV,
|
|
@@ -109,21 +225,32 @@ function initWorker(basePath) {
|
|
|
109
225
|
newWorker.worker.once("online", () => {
|
|
110
226
|
newWorker.worker.on("message", (message) => {
|
|
111
227
|
if (message.tag === "all-paths") {
|
|
112
|
-
|
|
228
|
+
pagesReadyCalled = true;
|
|
229
|
+
pagesReady(
|
|
230
|
+
["/____elm-pages-internal____/404"].concat(JSON.parse(message.data))
|
|
231
|
+
);
|
|
113
232
|
} else if (message.tag === "error") {
|
|
114
233
|
process.exitCode = 1;
|
|
115
|
-
console.error(
|
|
116
|
-
|
|
234
|
+
console.error(restoreColorSafe(message.data));
|
|
235
|
+
if (!pagesReadyCalled) {
|
|
236
|
+
// when there is a build error while resolving all-paths, we don't know which pages to build so we need to short-circuit
|
|
237
|
+
// and give an error instead of trying to build the remaining pages to show as many errors as possible
|
|
238
|
+
pagesReady([]);
|
|
239
|
+
reject(message.data);
|
|
240
|
+
}
|
|
241
|
+
buildError = true;
|
|
242
|
+
buildNextPage(newWorker, whenDone);
|
|
117
243
|
} else if (message.tag === "done") {
|
|
118
|
-
buildNextPage(newWorker);
|
|
244
|
+
buildNextPage(newWorker, whenDone);
|
|
119
245
|
} else {
|
|
120
246
|
throw `Unhandled tag ${message.tag}`;
|
|
121
247
|
}
|
|
122
248
|
});
|
|
123
249
|
newWorker.worker.on("error", (error) => {
|
|
124
250
|
console.error("Unhandled worker exception", error);
|
|
251
|
+
buildError = true;
|
|
125
252
|
process.exitCode = 1;
|
|
126
|
-
buildNextPage(newWorker);
|
|
253
|
+
buildNextPage(newWorker, whenDone);
|
|
127
254
|
});
|
|
128
255
|
resolve(newWorker);
|
|
129
256
|
});
|
|
@@ -134,57 +261,94 @@ function initWorker(basePath) {
|
|
|
134
261
|
*/
|
|
135
262
|
function prepareStaticPathsNew(thread) {
|
|
136
263
|
thread.worker.postMessage({
|
|
264
|
+
portsFilePath: global.portsFilePath,
|
|
137
265
|
mode: "build",
|
|
138
266
|
tag: "render",
|
|
139
267
|
pathname: "/all-paths.json",
|
|
140
268
|
});
|
|
141
269
|
}
|
|
142
270
|
|
|
143
|
-
async function buildNextPage(thread) {
|
|
271
|
+
async function buildNextPage(thread, allComplete) {
|
|
144
272
|
let nextPage = (await pages).pop();
|
|
145
273
|
if (nextPage) {
|
|
146
274
|
thread.worker.postMessage({
|
|
275
|
+
portsFilePath: global.portsFilePath,
|
|
147
276
|
mode: "build",
|
|
148
277
|
tag: "render",
|
|
149
278
|
pathname: nextPage,
|
|
150
279
|
});
|
|
151
280
|
} else {
|
|
152
281
|
thread.worker.terminate();
|
|
282
|
+
activeWorkers -= 1;
|
|
283
|
+
allComplete();
|
|
153
284
|
}
|
|
154
285
|
}
|
|
155
286
|
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
287
|
+
function runCli(options) {
|
|
288
|
+
return new Promise((resolve, reject) => {
|
|
289
|
+
const whenDone = () => {
|
|
290
|
+
if (activeWorkers === 0) {
|
|
291
|
+
// wait for the remaining tasks in the pool to complete once the pages queue is emptied
|
|
292
|
+
Promise.all(pool).then((value) => {
|
|
293
|
+
if (buildError) {
|
|
294
|
+
reject();
|
|
295
|
+
} else {
|
|
296
|
+
resolve(value);
|
|
297
|
+
}
|
|
298
|
+
});
|
|
299
|
+
}
|
|
300
|
+
};
|
|
301
|
+
const cpuCount = os.cpus().length;
|
|
302
|
+
// const cpuCount = 1;
|
|
303
|
+
console.log("Threads: ", cpuCount);
|
|
304
|
+
|
|
305
|
+
const getPathsWorker = initWorker(options.base, whenDone);
|
|
306
|
+
getPathsWorker.then(prepareStaticPathsNew);
|
|
307
|
+
const threadsToCreate = Math.max(1, cpuCount - 1);
|
|
308
|
+
pool.push(getPathsWorker);
|
|
309
|
+
for (let index = 0; index < threadsToCreate - 1; index++) {
|
|
310
|
+
pool.push(initWorker(options.base, whenDone));
|
|
311
|
+
}
|
|
312
|
+
pool.forEach((threadPromise) => {
|
|
313
|
+
threadPromise.then((thread) => buildNextPage(thread, whenDone));
|
|
314
|
+
});
|
|
169
315
|
});
|
|
170
316
|
}
|
|
171
317
|
|
|
172
318
|
async function compileElm(options) {
|
|
173
|
-
|
|
174
|
-
const fullOutputPath = path.join(process.cwd(),
|
|
175
|
-
await
|
|
319
|
+
ensureDirSync("dist");
|
|
320
|
+
const fullOutputPath = path.join(process.cwd(), `./dist/elm.js`);
|
|
321
|
+
await generateClientFolder(options.base);
|
|
322
|
+
await spawnElmMake(
|
|
323
|
+
options.debug ? "debug" : "optimize",
|
|
324
|
+
options,
|
|
325
|
+
".elm-pages/Main.elm",
|
|
326
|
+
fullOutputPath,
|
|
327
|
+
path.join(process.cwd(), "./elm-stuff/elm-pages/client")
|
|
328
|
+
);
|
|
176
329
|
|
|
177
330
|
if (!options.debug) {
|
|
178
331
|
await runTerser(fullOutputPath);
|
|
179
332
|
}
|
|
180
333
|
}
|
|
181
334
|
|
|
182
|
-
function
|
|
335
|
+
async function fingerprintElmAsset(fullOutputPath, withoutExtension) {
|
|
336
|
+
const fileHash = await fsPromises
|
|
337
|
+
.readFile(fullOutputPath, "utf8")
|
|
338
|
+
.then(getAssetHash);
|
|
339
|
+
await fsPromises.copyFile(
|
|
340
|
+
fullOutputPath,
|
|
341
|
+
`${withoutExtension}.${fileHash}.js`
|
|
342
|
+
);
|
|
343
|
+
return fileHash;
|
|
344
|
+
}
|
|
345
|
+
|
|
346
|
+
function elmOptimizeLevel2(outputPath, cwd) {
|
|
183
347
|
return new Promise((resolve, reject) => {
|
|
184
|
-
const
|
|
348
|
+
const optimizedOutputPath = outputPath + ".opt";
|
|
185
349
|
const subprocess = spawnCallback(
|
|
186
350
|
`elm-optimize-level-2`,
|
|
187
|
-
[
|
|
351
|
+
[outputPath, "--output", optimizedOutputPath],
|
|
188
352
|
{
|
|
189
353
|
// ignore stdout
|
|
190
354
|
// stdio: ["inherit", "ignore", "inherit"],
|
|
@@ -199,17 +363,16 @@ function elmOptimizeLevel2(elmEntrypointPath, outputPath, cwd) {
|
|
|
199
363
|
});
|
|
200
364
|
|
|
201
365
|
subprocess.on("close", async (code) => {
|
|
202
|
-
if (
|
|
203
|
-
|
|
204
|
-
commandOutput === "" &&
|
|
205
|
-
(await fs.fileExists(fullOutputPath))
|
|
206
|
-
) {
|
|
366
|
+
if (code === 0) {
|
|
367
|
+
await fs.copyFile(optimizedOutputPath, outputPath);
|
|
207
368
|
resolve();
|
|
208
369
|
} else {
|
|
209
370
|
if (!buildError) {
|
|
210
371
|
buildError = true;
|
|
211
372
|
process.exitCode = 1;
|
|
212
|
-
reject(
|
|
373
|
+
reject(
|
|
374
|
+
`I encountered an error when running elm-optimize-level-2:\n\n ${commandOutput}`
|
|
375
|
+
);
|
|
213
376
|
} else {
|
|
214
377
|
// avoid unhandled error printing duplicate message, let process.exit in top loop take over
|
|
215
378
|
}
|
|
@@ -218,29 +381,66 @@ function elmOptimizeLevel2(elmEntrypointPath, outputPath, cwd) {
|
|
|
218
381
|
});
|
|
219
382
|
}
|
|
220
383
|
|
|
384
|
+
/** @typedef {"debug" | "optimize" | "default"} CompileMode */
|
|
385
|
+
|
|
221
386
|
/**
|
|
387
|
+
* @param {CompileMode} mode
|
|
222
388
|
* @param {string} elmEntrypointPath
|
|
223
389
|
* @param {string} outputPath
|
|
224
390
|
* @param {string | undefined} cwd
|
|
225
391
|
*/
|
|
226
|
-
async function spawnElmMake(options, elmEntrypointPath, outputPath, cwd) {
|
|
227
|
-
|
|
228
|
-
|
|
392
|
+
async function spawnElmMake(mode, options, elmEntrypointPath, outputPath, cwd) {
|
|
393
|
+
await runElmMake(mode, options, elmEntrypointPath, outputPath, cwd);
|
|
394
|
+
if (mode === "optimize") {
|
|
395
|
+
await elmOptimizeLevel2(outputPath, cwd);
|
|
396
|
+
}
|
|
397
|
+
await fsPromises.writeFile(
|
|
398
|
+
outputPath,
|
|
399
|
+
(
|
|
400
|
+
await fsPromises.readFile(outputPath, "utf-8")
|
|
401
|
+
).replace(
|
|
402
|
+
/return \$elm\$json\$Json\$Encode\$string\(.REPLACE_ME_WITH_FORM_TO_STRING.\)/g,
|
|
403
|
+
"function appendSubmitter (myFormData, event) { event.submitter && event.submitter.name && event.submitter.name.length > 0 ? myFormData.append(event.submitter.name, event.submitter.value) : myFormData; return myFormData }; return " +
|
|
404
|
+
(options.debug
|
|
405
|
+
? "_Json_wrap(Array.from(appendSubmitter(new FormData(_Json_unwrap(event).target), _Json_unwrap(event))))"
|
|
406
|
+
: "Array.from(new FormData(event.target))")
|
|
407
|
+
)
|
|
408
|
+
);
|
|
409
|
+
}
|
|
410
|
+
|
|
411
|
+
function getAssetHash(content) {
|
|
412
|
+
return createHash("sha256").update(content).digest("hex").slice(0, 8);
|
|
413
|
+
}
|
|
414
|
+
|
|
415
|
+
/**
|
|
416
|
+
* @param {CompileMode} mode
|
|
417
|
+
*/
|
|
418
|
+
function modeToOptions(mode) {
|
|
419
|
+
if (mode === "debug") {
|
|
420
|
+
return ["--debug"];
|
|
421
|
+
} else if (mode === "optimize") {
|
|
422
|
+
return ["--optimize"];
|
|
229
423
|
} else {
|
|
230
|
-
|
|
424
|
+
return [];
|
|
231
425
|
}
|
|
232
426
|
}
|
|
233
427
|
|
|
234
|
-
|
|
428
|
+
/**
|
|
429
|
+
* @param {CompileMode} mode
|
|
430
|
+
* @param {string} elmEntrypointPath
|
|
431
|
+
* @param {string} outputPath
|
|
432
|
+
* @param {string | undefined} cwd
|
|
433
|
+
*/
|
|
434
|
+
function runElmMake(mode, options, elmEntrypointPath, outputPath, cwd) {
|
|
235
435
|
return new Promise(async (resolve, reject) => {
|
|
236
436
|
const subprocess = spawnCallback(
|
|
237
|
-
`
|
|
437
|
+
`lamdera`,
|
|
238
438
|
[
|
|
239
439
|
"make",
|
|
240
440
|
elmEntrypointPath,
|
|
241
441
|
"--output",
|
|
242
442
|
outputPath,
|
|
243
|
-
|
|
443
|
+
...modeToOptions(mode),
|
|
244
444
|
"--report",
|
|
245
445
|
"json",
|
|
246
446
|
],
|
|
@@ -251,9 +451,8 @@ function runElmMake(elmEntrypointPath, outputPath, cwd) {
|
|
|
251
451
|
cwd: cwd,
|
|
252
452
|
}
|
|
253
453
|
);
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
await fsPromises.unlink(fullOutputPath, {
|
|
454
|
+
if (await fs.fileExists(outputPath)) {
|
|
455
|
+
await fsPromises.unlink(outputPath, {
|
|
257
456
|
force: true /* ignore errors if file doesn't exist */,
|
|
258
457
|
});
|
|
259
458
|
}
|
|
@@ -262,15 +461,25 @@ function runElmMake(elmEntrypointPath, outputPath, cwd) {
|
|
|
262
461
|
subprocess.stderr.on("data", function (data) {
|
|
263
462
|
commandOutput += data;
|
|
264
463
|
});
|
|
464
|
+
subprocess.on("error", function () {
|
|
465
|
+
reject(commandOutput);
|
|
466
|
+
});
|
|
265
467
|
|
|
266
468
|
subprocess.on("close", async (code) => {
|
|
267
|
-
if (
|
|
469
|
+
if (
|
|
470
|
+
code == 0 &&
|
|
471
|
+
(await fs.fileExists(outputPath)) &&
|
|
472
|
+
commandOutput === ""
|
|
473
|
+
) {
|
|
268
474
|
resolve();
|
|
269
475
|
} else {
|
|
270
476
|
if (!buildError) {
|
|
271
477
|
buildError = true;
|
|
272
|
-
|
|
273
|
-
|
|
478
|
+
try {
|
|
479
|
+
reject(restoreColorSafe(commandOutput));
|
|
480
|
+
} catch (error) {
|
|
481
|
+
reject(commandOutput);
|
|
482
|
+
}
|
|
274
483
|
} else {
|
|
275
484
|
// avoid unhandled error printing duplicate message, let process.exit in top loop take over
|
|
276
485
|
}
|
|
@@ -325,22 +534,14 @@ async function runTerser(filePath) {
|
|
|
325
534
|
}
|
|
326
535
|
}
|
|
327
536
|
|
|
328
|
-
async function copyAssets() {
|
|
329
|
-
await fsPromises.writeFile(
|
|
330
|
-
"dist/elm-pages.js",
|
|
331
|
-
await fsPromises.readFile(
|
|
332
|
-
path.join(__dirname, "../static-code/elm-pages.js")
|
|
333
|
-
)
|
|
334
|
-
);
|
|
335
|
-
fs.copyDirFlat("public", "dist");
|
|
336
|
-
}
|
|
337
|
-
|
|
338
537
|
async function compileCliApp(options) {
|
|
339
538
|
await spawnElmMake(
|
|
539
|
+
// TODO should be --optimize, but there seems to be an issue with the html to JSON with --optimize
|
|
540
|
+
options.debug ? "debug" : "optimize",
|
|
340
541
|
options,
|
|
341
|
-
"
|
|
342
|
-
"elm.js",
|
|
343
|
-
"
|
|
542
|
+
path.join(process.cwd(), "elm-stuff/elm-pages/.elm-pages/Main.elm"),
|
|
543
|
+
path.join(process.cwd(), "elm-stuff/elm-pages/elm.js"),
|
|
544
|
+
path.join(process.cwd(), "elm-stuff/elm-pages")
|
|
344
545
|
);
|
|
345
546
|
|
|
346
547
|
const elmFileContent = await fsPromises.readFile(ELM_FILE_PATH, "utf-8");
|
|
@@ -360,7 +561,9 @@ async function compileCliApp(options) {
|
|
|
360
561
|
}
|
|
361
562
|
|
|
362
563
|
function forceThunks(vNode) {
|
|
363
|
-
|
|
564
|
+
if ( (typeof vNode !== "undefined" && vNode.$ === "#2") // normal/debug mode
|
|
565
|
+
|| (typeof vNode !== "undefined" && typeof vNode.$ === "undefined" && typeof vNode.a == "string" && typeof vNode.b == "object" ) // optimize mode
|
|
566
|
+
) {
|
|
364
567
|
// This is a tuple (the kids : List (String, Html) field of a Keyed node); recurse into the right side of the tuple
|
|
365
568
|
vNode.b = forceThunks(vNode.b);
|
|
366
569
|
}
|
|
@@ -391,29 +594,39 @@ function _HtmlAsJson_toJson(html) {
|
|
|
391
594
|
elmFileContent
|
|
392
595
|
.replace(
|
|
393
596
|
/return \$elm\$json\$Json\$Encode\$string\(.REPLACE_ME_WITH_JSON_STRINGIFY.\)/g,
|
|
394
|
-
|
|
395
|
-
(options.debug
|
|
396
|
-
? `${forceThunksSource}
|
|
597
|
+
`return ${forceThunksSource}
|
|
397
598
|
return _Json_wrap(forceThunks(html));
|
|
398
599
|
`
|
|
399
|
-
: `${forceThunksSource}
|
|
400
|
-
return forceThunks(html);
|
|
401
|
-
`)
|
|
402
|
-
)
|
|
403
|
-
.replace(
|
|
404
|
-
"return ports ? { ports: ports } : {};",
|
|
405
|
-
`const die = function() {
|
|
406
|
-
managers = null
|
|
407
|
-
model = null
|
|
408
|
-
stepper = null
|
|
409
|
-
ports = null
|
|
410
|
-
}
|
|
411
|
-
|
|
412
|
-
return ports ? { ports: ports, die: die } : { die: die };`
|
|
413
600
|
)
|
|
601
|
+
.replace(`console.log('App dying')`, "")
|
|
414
602
|
);
|
|
415
603
|
}
|
|
416
604
|
|
|
605
|
+
async function runAdapter(adaptFn, processedIndexTemplate) {
|
|
606
|
+
try {
|
|
607
|
+
await adaptFn({
|
|
608
|
+
renderFunctionFilePath: "./elm-stuff/elm-pages/elm.js",
|
|
609
|
+
routePatterns: JSON.parse(
|
|
610
|
+
await fsPromises.readFile("./dist/route-patterns.json", "utf-8")
|
|
611
|
+
),
|
|
612
|
+
apiRoutePatterns: JSON.parse(
|
|
613
|
+
await fsPromises.readFile("./dist/api-patterns.json", "utf-8")
|
|
614
|
+
),
|
|
615
|
+
portsFilePath: "./.elm-pages/compiled-ports/port-data-source.js",
|
|
616
|
+
htmlTemplate: processedIndexTemplate,
|
|
617
|
+
});
|
|
618
|
+
console.log("Success - Adapter script complete");
|
|
619
|
+
} catch (error) {
|
|
620
|
+
console.error("ERROR - Adapter script failed");
|
|
621
|
+
try {
|
|
622
|
+
console.error(JSON.stringify(error));
|
|
623
|
+
} catch (parsingError) {
|
|
624
|
+
console.error(error);
|
|
625
|
+
}
|
|
626
|
+
process.exit(1);
|
|
627
|
+
}
|
|
628
|
+
}
|
|
629
|
+
|
|
417
630
|
/** @typedef { { route : string; contentJson : string; head : SeoTag[]; html: string; body: string; } } FromElm */
|
|
418
631
|
/** @typedef {HeadTag | JsonLdTag} SeoTag */
|
|
419
632
|
/** @typedef {{ name: string; attributes: string[][]; type: 'head' }} HeadTag */
|