elm-pages 3.0.0-beta.21 → 3.0.0-beta.23

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 CHANGED
@@ -81,7 +81,7 @@ https://github.com/dillonkearns/elm-pages/projects
81
81
  You will see an error if the NPM and Elm package do not have a matching Compatibility Key. Usually it's best to upgrade to the latest version of both the Elm and NPM
82
82
  packages when you upgrade. However, in case you want to install versions that are behind the latest, the Compatibility Key is included here for reference.
83
83
 
84
- Current Compatibility Key: 7.
84
+ Current Compatibility Key: 8.
85
85
 
86
86
  ## Contributors ✨
87
87
 
@@ -12,7 +12,7 @@ import * as renderer from "./render.js";
12
12
  import { globbySync } from "globby";
13
13
  import * as esbuild from "esbuild";
14
14
  import { rewriteElmJson } from "./rewrite-elm-json.js";
15
- import { ensureDirSync, deleteIfExists } from "./file-helpers.js";
15
+ import { ensureDirSync } from "./file-helpers.js";
16
16
  import * as url from "url";
17
17
 
18
18
  import * as commander from "commander";
@@ -43,9 +43,6 @@ async function main() {
43
43
  )
44
44
  .description("run a full site build")
45
45
  .action(async (options) => {
46
- if (!options.keepCache) {
47
- clearHttpAndPortCache();
48
- }
49
46
  options.base = normalizeUrl(options.base);
50
47
  await build.run(options);
51
48
  });
@@ -76,9 +73,6 @@ async function main() {
76
73
  .option("--base <basePath>", "serve site under a base path", "/")
77
74
  .option("--https", "uses a https server")
78
75
  .action(async (options) => {
79
- if (!options.keepCache) {
80
- clearHttpAndPortCache();
81
- }
82
76
  options.base = normalizeUrl(options.base);
83
77
  await dev.start(options);
84
78
  });
@@ -334,25 +328,6 @@ function safeSubscribe(program, portName, subscribeFunction) {
334
328
  program.ports[portName].subscribe(subscribeFunction);
335
329
  }
336
330
 
337
- function clearHttpAndPortCache() {
338
- const directory = ".elm-pages/http-response-cache";
339
- if (fs.existsSync(directory)) {
340
- fs.readdir(directory, (err, files) => {
341
- if (err) {
342
- throw err;
343
- }
344
-
345
- for (const file of files) {
346
- fs.unlink(path.join(directory, file), (err) => {
347
- if (err) {
348
- throw err;
349
- }
350
- });
351
- }
352
- });
353
- }
354
- }
355
-
356
331
  /**
357
332
  * @param {string} rawPagePath
358
333
  */
@@ -1,3 +1,3 @@
1
- export const compatibilityKey = 7;
1
+ export const compatibilityKey = 8;
2
2
 
3
- export const packageVersion = "3.0.0-beta.21";
3
+ export const packageVersion = "3.0.0-beta.23";
@@ -136,9 +136,11 @@ function spawnElmMake(options, elmEntrypointPath, outputPath, cwd) {
136
136
  }
137
137
  );
138
138
  if (await fsHelpers.fileExists(outputPath)) {
139
- await fsPromises.unlink(outputPath, {
140
- force: true /* ignore errors if file doesn't exist */,
141
- });
139
+ try {
140
+ await fsPromises.unlink(outputPath, {
141
+ force: true /* ignore errors if file doesn't exist */,
142
+ });
143
+ } catch (e) {}
142
144
  }
143
145
  let commandOutput = "";
144
146
 
@@ -39,6 +39,40 @@ export async function start(options) {
39
39
  let threadReadyQueue = [];
40
40
  let pool = [];
41
41
 
42
+ function invalidatePool() {
43
+ pool.forEach((thread) => {
44
+ if (thread.used) {
45
+ thread.stale = true;
46
+ }
47
+ });
48
+ restartIdleWorkersIfStale();
49
+ }
50
+
51
+ function restartIdleWorkersIfStale() {
52
+ pool.forEach((thread) => {
53
+ if (thread.stale && thread.ready) {
54
+ reinitThread(thread);
55
+ }
56
+ });
57
+ }
58
+
59
+ function reinitThread(thisThread) {
60
+ thisThread.worker && thisThread.worker.terminate();
61
+ // TODO remove event listeners to avoid memory leak?
62
+ // thread.worker.removeAllListeners("message");
63
+ // thread.worker.removeAllListeners("error");
64
+ thisThread.ready = false;
65
+ thisThread.stale = false;
66
+ thisThread.used = false;
67
+ thisThread.worker = new Worker(path.join(__dirname, "./render-worker.js"), {
68
+ env: SHARE_ENV,
69
+ workerData: { basePath: options.base },
70
+ });
71
+ thisThread.worker.once("online", () => {
72
+ thisThread.ready = true;
73
+ });
74
+ }
75
+
42
76
  ensureDirSync(path.join(process.cwd(), ".elm-pages", "http-response-cache"));
43
77
  const cpuCount = os.cpus().length;
44
78
 
@@ -232,32 +266,13 @@ export async function start(options) {
232
266
  if (pathThatChanged === "elm.json") {
233
267
  watchElmSourceDirs(false);
234
268
  } else if (pathThatChanged.endsWith(".elm")) {
269
+ invalidatePool();
235
270
  if (elmMakeRunning) {
236
271
  } else {
237
272
  let codegenError = null;
238
273
  if (needToRerunCodegen(eventName, pathThatChanged)) {
239
274
  try {
240
275
  await codegen.generate(options.base);
241
- clientElmMakeProcess = compileElmForBrowser(options);
242
- pendingCliCompile = compileCliApp(
243
- options,
244
- ".elm-pages/Main.elm",
245
- path.join(process.cwd(), "elm-stuff/elm-pages/", "elm.js"),
246
- // "elm.js",
247
- "elm-stuff/elm-pages/",
248
- path.join("elm-stuff/elm-pages/", "elm.js")
249
- );
250
-
251
- Promise.all([clientElmMakeProcess, pendingCliCompile])
252
- .then(() => {
253
- elmMakeRunning = false;
254
- })
255
- .catch(() => {
256
- elmMakeRunning = false;
257
- });
258
- clients.forEach((client) => {
259
- client.response.write(`data: elm.js\n\n`);
260
- });
261
276
  } catch (error) {
262
277
  codegenError = error;
263
278
  }
@@ -275,10 +290,7 @@ export async function start(options) {
275
290
  pendingCliCompile = compileCliApp(
276
291
  options,
277
292
  ".elm-pages/Main.elm",
278
-
279
293
  path.join(process.cwd(), "elm-stuff/elm-pages/", "elm.js"),
280
-
281
- // "elm.js",
282
294
  "elm-stuff/elm-pages/",
283
295
  path.join("elm-stuff/elm-pages/", "elm.js")
284
296
  );
@@ -361,6 +373,8 @@ export async function start(options) {
361
373
  };
362
374
 
363
375
  readyThread.ready = false;
376
+ await pendingCliCompile;
377
+ readyThread.used = true;
364
378
  readyThread.worker.postMessage({
365
379
  mode: "dev-server",
366
380
  pathname,
@@ -607,6 +621,7 @@ export async function start(options) {
607
621
  }
608
622
 
609
623
  function runPendingWork() {
624
+ restartIdleWorkersIfStale();
610
625
  const readyThreads = pool.filter((thread) => thread.ready);
611
626
  readyThreads.forEach((readyThread) => {
612
627
  const startTask = threadReadyQueue.shift();
@@ -631,6 +646,7 @@ export async function start(options) {
631
646
  workerData: { basePath },
632
647
  }),
633
648
  ready: false,
649
+ used: false,
634
650
  };
635
651
  newWorker.worker.once("online", () => {
636
652
  newWorker.ready = true;
@@ -2,7 +2,6 @@ import * as renderer from "../../generator/src/render.js";
2
2
  import * as path from "node:path";
3
3
  import * as fs from "./dir-helpers.js";
4
4
  import { readFileSync, writeFileSync } from "node:fs";
5
- import { stat } from "fs/promises";
6
5
  import { parentPort, threadId, workerData } from "node:worker_threads";
7
6
  import * as url from "node:url";
8
7
 
@@ -48,13 +47,10 @@ async function requireElm(mode) {
48
47
  process.cwd(),
49
48
  "elm-stuff/elm-pages/elm.cjs"
50
49
  );
51
- let elmImportPath = compiledElmPath;
52
50
  let pathAsUrl = url.pathToFileURL(compiledElmPath);
53
- const changedTime = (await stat(compiledElmPath)).mtimeMs;
54
- pathAsUrl.searchParams.append("changed", `${changedTime}`);
55
51
  const warnOriginal = console.warn;
56
52
  console.warn = function () {};
57
- const Elm = (await import(elmImportPath)).default;
53
+ const Elm = (await import(pathAsUrl.toString())).default;
58
54
  console.warn = warnOriginal;
59
55
  return Elm;
60
56
  }
@@ -5,15 +5,69 @@ var eventSource = null;
5
5
  let updateAppContentJson = new Promise((resolve, reject) => resolve(() => {}));
6
6
 
7
7
  function connect(sendContentJsonPort, initialErrorPage) {
8
+ let reconnectFrequencySeconds = 1;
9
+ // reconnect logic based on: https://stackoverflow.com/a/61148682/383983
8
10
  // Listen for the server to tell us that an HMR update is available
9
- eventSource = new EventSource("/stream");
10
- window.reloadOnOk = initialErrorPage;
11
- if (initialErrorPage) {
12
- handleEvent(sendContentJsonPort, { data: "content.dat" });
11
+ function waitFunc() {
12
+ return reconnectFrequencySeconds * 1000;
13
13
  }
14
- eventSource.onmessage = async function (evt) {
15
- handleEvent(sendContentJsonPort, evt);
16
- };
14
+ function tryToSetupFunc() {
15
+ setupEventSource();
16
+ reconnectFrequencySeconds *= 2;
17
+ if (reconnectFrequencySeconds >= 8) {
18
+ reconnectFrequencySeconds = 8;
19
+ }
20
+ }
21
+ function reconnectFunc() {
22
+ console.log(
23
+ `Attempting dev server reconnect in ${reconnectFrequencySeconds}...`
24
+ );
25
+ setTimeout(tryToSetupFunc, waitFunc());
26
+ }
27
+ function setupEventSource() {
28
+ eventSource = new EventSource("/stream");
29
+ window.reloadOnOk = initialErrorPage;
30
+
31
+ try {
32
+ if (initialErrorPage) {
33
+ handleEvent(sendContentJsonPort, { data: "content.dat" });
34
+ }
35
+ } catch (e) {}
36
+ eventSource.onopen = async function () {
37
+ hideError();
38
+ reconnectFrequencySeconds = 1;
39
+ };
40
+ eventSource.onerror = async function (evt) {
41
+ eventSource && eventSource.close();
42
+ reconnectFunc();
43
+
44
+ showReconnectBanner();
45
+ };
46
+ eventSource.onmessage = async function (evt) {
47
+ handleEvent(sendContentJsonPort, evt);
48
+ };
49
+ }
50
+
51
+ setupEventSource();
52
+ }
53
+
54
+ function showReconnectBanner() {
55
+ showError({
56
+ type: "compile-errors",
57
+ errors: [
58
+ {
59
+ path: "",
60
+ name: "",
61
+ problems: [
62
+ {
63
+ title: "",
64
+ // region: "",
65
+ message: ["Dev server is disconnected..."],
66
+ },
67
+ ],
68
+ },
69
+ ],
70
+ });
17
71
  }
18
72
 
19
73
  async function handleEvent(sendContentJsonPort, evt) {
@@ -28,8 +82,7 @@ async function handleEvent(sendContentJsonPort, evt) {
28
82
 
29
83
  try {
30
84
  await fetchContentJson;
31
- const elmJsResponse = await elmJsRequest;
32
- thenApplyHmr(elmJsResponse);
85
+ thenApplyHmr(await elmJsRequest);
33
86
  } catch (errorJson) {
34
87
  if (typeof errorJson === "string") {
35
88
  errorJson = JSON.parse(errorJson);
@@ -116,8 +169,7 @@ var myDisposeCallback = function () {
116
169
  var module = {
117
170
  hot: {
118
171
  accept: async function () {
119
- const sendInUpdatedContentJson = await updateAppContentJson;
120
- sendInUpdatedContentJson();
172
+ (await updateAppContentJson)();
121
173
  },
122
174
 
123
175
  dispose: function (callback) {
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "elm-pages",
3
3
  "type": "module",
4
- "version": "3.0.0-beta.21",
4
+ "version": "3.0.0-beta.23",
5
5
  "homepage": "https://elm-pages.com",
6
6
  "moduleResolution": "node",
7
7
  "description": "Type-safe static sites, written in pure elm with your own custom elm-markup syntax.",
@@ -3,4 +3,4 @@ module Pages.Internal.Platform.CompatibilityKey exposing (currentCompatibilityKe
3
3
 
4
4
  currentCompatibilityKey : Int
5
5
  currentCompatibilityKey =
6
- 7
6
+ 8
@@ -269,7 +269,7 @@ init config flags url key =
269
269
  , url = url
270
270
  , currentPath = url.path
271
271
  , pageData = Err "Not found"
272
- , ariaNavigationAnnouncement = "Error" -- TODO use error page title for announcement?
272
+ , ariaNavigationAnnouncement = "Page Not Found" -- TODO use error page title for announcement?
273
273
  , userFlags = flags
274
274
  , notFound = Just info
275
275
  , transition = Nothing
@@ -764,7 +764,86 @@ update config appMsg model =
764
764
  _ ->
765
765
  ( model, NoEffect )
766
766
  )
767
- |> Result.withDefault ( model, NoEffect )
767
+ |> Result.withDefault
768
+ (let
769
+ pageDataResult : Maybe (InitKind sharedData pageData actionData errorPage)
770
+ pageDataResult =
771
+ case Bytes.Decode.decode config.decodeResponse pageDataBytes of
772
+ Just (ResponseSketch.RenderPage _ _) ->
773
+ Nothing
774
+
775
+ Just (ResponseSketch.HotUpdate pageData shared actionData) ->
776
+ OkPage shared pageData actionData
777
+ |> Just
778
+
779
+ Just (ResponseSketch.NotFound notFound) ->
780
+ NotFound notFound
781
+ |> Just
782
+
783
+ _ ->
784
+ Nothing
785
+ in
786
+ case pageDataResult of
787
+ Just (OkPage sharedData pageData actionData) ->
788
+ let
789
+ urls : { currentUrl : Url, basePath : List String }
790
+ urls =
791
+ { currentUrl = model.url
792
+ , basePath = config.basePath
793
+ }
794
+
795
+ pagePath : Path
796
+ pagePath =
797
+ urlsToPagePath urls
798
+
799
+ userFlags : Pages.Flags.Flags
800
+ userFlags =
801
+ model.userFlags
802
+ |> Decode.decodeValue
803
+ (Decode.field "userFlags" Decode.value)
804
+ |> Result.withDefault Json.Encode.null
805
+ |> Pages.Flags.BrowserFlags
806
+
807
+ ( userModel, userCmd ) =
808
+ Just
809
+ { path =
810
+ { path = pagePath
811
+ , query = model.url.query
812
+ , fragment = model.url.fragment
813
+ }
814
+ , metadata = config.urlToRoute model.url
815
+ , pageUrl =
816
+ Just
817
+ { protocol = model.url.protocol
818
+ , host = model.url.host
819
+ , port_ = model.url.port_
820
+ , path = pagePath
821
+ , query = model.url.query |> Maybe.map QueryParams.fromString
822
+ , fragment = model.url.fragment
823
+ }
824
+ }
825
+ |> config.init userFlags sharedData pageData actionData
826
+
827
+ cmd : Effect userMsg pageData actionData sharedData userEffect errorPage
828
+ cmd =
829
+ UserCmd userCmd
830
+ in
831
+ ( { model
832
+ | pageData =
833
+ Ok
834
+ { userModel = userModel
835
+ , sharedData = sharedData
836
+ , pageData = pageData
837
+ , actionData = actionData
838
+ }
839
+ , notFound = Nothing
840
+ }
841
+ , cmd
842
+ )
843
+
844
+ _ ->
845
+ ( model, NoEffect )
846
+ )
768
847
 
769
848
  FetcherStarted fetcherKey transitionId fetcherData initiatedAt ->
770
849
  ( { model