@skgo/sveltekit-adapter 0.22.1 → 0.31.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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@skgo/sveltekit-adapter",
3
- "version": "0.22.1",
3
+ "version": "0.31.0",
4
4
  "description": "SvelteKit adapter for skgo: builds a frontend that a Go binary renders and answers",
5
5
  "keywords": [
6
6
  "adapter",
@@ -1014,8 +1014,8 @@ export function gojaDevEnvironment({ outDir = '.svelte-kit' } = {}) {
1014
1014
  * way. Kit's own dev server has no answer to it: vite turns each report into a
1015
1015
  * hot update or a full page reload, and a reload throws away what a visitor
1016
1016
  * had typed and aborts the documents Go was rendering for them. So the dev
1017
- * server remembers the content it last loaded or was told about, and lets
1018
- * through only the reports whose content differs.
1017
+ * server initializes its baseline from the first load, then advances it when
1018
+ * the watcher reports a change, and lets through reports whose content differs.
1019
1019
  *
1020
1020
  * Content means the file's bytes, not the text vite decoded from them: two
1021
1021
  * different invalid byte sequences decode to the same replacement characters,
@@ -1040,7 +1040,7 @@ export function gojaDevEnvironment({ outDir = '.svelte-kit' } = {}) {
1040
1040
  * @returns {import('vite').Plugin}
1041
1041
  */
1042
1042
  export function gojaDevUnchangedFiles({ out = 'build' } = {}) {
1043
- /** What vite last loaded for a file, or last reported changed: its byte digest. */
1043
+ /** The initial load or last delivered watcher content: its byte digest. */
1044
1044
  const known = new Map();
1045
1045
  const digest = (/** @type {Uint8Array} */ bytes) => createHash('sha256').update(bytes).digest('base64');
1046
1046
  const read = (/** @type {string} */ file) => {
@@ -1050,8 +1050,12 @@ export function gojaDevUnchangedFiles({ out = 'build' } = {}) {
1050
1050
  return null;
1051
1051
  }
1052
1052
  };
1053
- /** Remember a file by the bytes vite decoded `code` from, if they are the ones on disk now. */
1053
+ /** Initialize a file from the bytes vite decoded, without consuming pending watcher changes. */
1054
1054
  const remember = (/** @type {string} */ file, /** @type {string} */ code) => {
1055
+ // A module request can transform new bytes while awaitWriteFinish is
1056
+ // still settling their event. Only watcher delivery advances this baseline
1057
+ // after initialization, or that real change would be suppressed.
1058
+ if (known.has(posix(file))) return;
1055
1059
  try {
1056
1060
  const bytes = readFileSync(file);
1057
1061
  if (bytes.toString('utf8') === code) known.set(posix(file), digest(bytes));
@@ -1067,7 +1071,12 @@ export function gojaDevUnchangedFiles({ out = 'build' } = {}) {
1067
1071
 
1068
1072
  config(config) {
1069
1073
  const emitted = posix(resolve(config.root ?? process.cwd(), out));
1070
- return { server: { watch: { ignored: [emitted, `${emitted}/**`] } } };
1074
+ // Chokidar's ordinary change path drops a second edit inside 50 ms.
1075
+ // Its supported pending-write path instead delivers settled contents.
1076
+ const stability = config.server?.watch?.awaitWriteFinish === undefined
1077
+ ? { awaitWriteFinish: { stabilityThreshold: 200, pollInterval: 25 } }
1078
+ : {};
1079
+ return { server: { watch: { ...stability, ignored: [emitted, `${emitted}/**`] } } };
1071
1080
  },
1072
1081
 
1073
1082
  configureServer(server) {
@@ -1087,13 +1096,18 @@ export function gojaDevUnchangedFiles({ out = 'build' } = {}) {
1087
1096
  // Kit validates templates synchronously from its watcher. An in-place
1088
1097
  // save can expose truncated bytes; report that validation error but
1089
1098
  // keep the watcher alive so the completed save can regenerate Kit.
1099
+ // Kit's listener was registered before this post plugin; wrapping emit
1100
+ // catches its synchronous throw before it escapes the filesystem callback.
1090
1101
  const notify = (receiver, event, args, file) => {
1091
1102
  try {
1092
1103
  return emit.call(receiver, event, ...args);
1093
1104
  } catch (error) {
1094
1105
  const authored = typeof file === 'string' ? relative(server.config.root, file) : null;
1095
- if (!(error instanceof Error) || authored === null ||
1096
- !['%sveltekit.head%', '%sveltekit.body%'].some(tag => error.message === `${authored} is missing ${tag}`)) throw error;
1106
+ const [code, body] = error instanceof Error ? error.message.split('\n') : [];
1107
+ const templateError = code === 'app_template_missing'
1108
+ ? body === `${authored} does not exist`
1109
+ : code === 'app_template_tag_missing' && ['%sveltekit.head%', '%sveltekit.body%'].some(tag => body === `${authored} is missing \`${tag}\``);
1110
+ if (!(error instanceof Error) || error.name !== 'SvelteKit error' || authored === null || !templateError) throw error;
1097
1111
  known.delete(posix(file));
1098
1112
  server.config.logger.error(error.stack ?? error.message);
1099
1113
  server.ws.send({ type: 'error', err: { message: error.message, stack: error.stack ?? '', plugin: 'skgo-dev-unchanged-files' } });