Skip to content

Commit

Permalink
Add app version and only reload if navigation fails and version changed
Browse files Browse the repository at this point in the history
  • Loading branch information
pzerelles committed Jan 31, 2022
1 parent 4873a9b commit 7c75a08
Show file tree
Hide file tree
Showing 9 changed files with 122 additions and 6 deletions.
2 changes: 2 additions & 0 deletions documentation/docs/05-modules.md
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,8 @@ The stores themselves attach to the correct context at the point of subscription
- `page` contains an object with the current [`url`](https://developer.mozilla.org/en-US/docs/Web/API/URL), [`params`](#loading-input-params), [`stuff`](#loading-output-stuff), [`status`](#loading-output-status) and [`error`](#loading-output-error).
- `session` is a [writable store](https://svelte.dev/tutorial/writable-stores) whose initial value is whatever was returned from [`getSession`](#hooks-getsession). It can be written to, but this will _not_ cause changes to persist on the server — this is something you must implement yourself.

- `updated` is a [writable store](https://svelte.dev/tutorial/writable-stores) whose initial value is false. The app's version will be checked every minute for changes, setting the value to true if the version does not match the version at the time the store was created. It can be written to when custom logic is required to detect updates. There is a `check` function to force the version check immediately, returning the result.

### $lib

This is a simple alias to `src/lib`, or whatever directory is specified as [`config.kit.files.lib`]. It allows you to access common components and utility modules without `../../../../` nonsense.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1732,7 +1732,7 @@ <h3 id="modules-$app-stores">$app/stores</h3>
<ul>
<li>
<code>getStores</code> is a convenience function around <code>getContext</code> that
returns <code>{ navigating, page, session }</code>. This needs to be called at the
returns <code>{ navigating, page, session, updating }</code>. This needs to be called at the
top-level or synchronously during component or page initialisation.
</li>
</ul>
Expand Down Expand Up @@ -1786,6 +1786,16 @@ <h3 id="modules-$app-stores">$app/stores</h3>
>. It can be written to, but this will <em>not</em> cause changes to persist on the
server — this is something you must implement yourself.
</li>
<li>
<code>updating</code> is a
<a
href="https://svelte.dev/tutorial/writable-stores"
target="_blank"
rel="noopener noreferrer"
>writable store</a
>
whose initial value is false. The app's version will be checked every minute for changes, setting the value to true if the version does not match the version at the time the store was created. It can be written to when custom logic is required to detect updates. There is a <code>check</code> function to force the version check immediately, returning the result.
</li>
</ul>
<h3 id="modules-$lib">$lib</h3>
<p>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,7 @@
"#loading-output-stuff",
"https://svelte.dev/tutorial/writable-stores",
"#hooks-getsession",
"https://svelte.dev/tutorial/writable-stores",
"#service-workers",
"#configuration",
"#configuration",
Expand Down
8 changes: 8 additions & 0 deletions packages/kit/src/core/build/build_client.js
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,8 @@ export async function build_client({
output_dir,
client_entry_file
}) {
if (!process.env.VITE_APP_VERSION) process.env.VITE_APP_VERSION = new Date().toISOString();

create_app({
manifest_data,
output: `${SVELTE_KIT}/generated`,
Expand Down Expand Up @@ -105,6 +107,12 @@ export async function build_client({
const entry_css = new Set();
find_deps(entry, vite_manifest, entry_js, entry_css);

fs.writeFileSync(
`${client_out_dir}/version.json`,
JSON.stringify(process.env.VITE_APP_VERSION),
'utf-8'
);

return {
assets,
chunks,
Expand Down
4 changes: 4 additions & 0 deletions packages/kit/src/runtime/app/env.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,5 +14,9 @@ export const mode = import.meta.env.MODE;
* @type {import('$app/env').amp}
*/
export const amp = !!import.meta.env.VITE_SVELTEKIT_AMP;
/**
* @type {import('$app/env').version}
*/
export const version = import.meta.env.VITE_APP_VERSION;

export { prerendering } from '../env.js';
21 changes: 20 additions & 1 deletion packages/kit/src/runtime/app/stores.js
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,8 @@ export const getStores = () => {
subscribe: stores.navigating.subscribe
};
},
session: stores.session
session: stores.session,
updated: stores.updated
};
};

Expand Down Expand Up @@ -77,3 +78,21 @@ export const session = {
set: () => throw_error('set'),
update: () => throw_error('update')
};

/** @type {typeof import('$app/stores').updated} */
export const updated = {
subscribe(fn) {
const store = getStores().updated;

if (browser) {
updated.set = store.set;
updated.update = store.update;
updated.check = store.check;
}

return store.subscribe(fn);
},
set: () => throw_error('set'),
update: () => throw_error('update'),
check: () => throw_error('check')
};
57 changes: 54 additions & 3 deletions packages/kit/src/runtime/client/renderer.js
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,44 @@ function notifiable_store(value) {
return { notify, set, subscribe };
}

/**
* @param {any} value
* @param {(set: (new_value: any) => void) => any} fn
* @param {number | undefined} interval
*/
function checkable_store(value, fn, interval) {
const { set, update, subscribe } = writable(value);

setInterval(() => {
fn(set);
}, interval);

return {
set,
update,
subscribe,
check: () => fn(set)
};
}

/**
* @returns {Promise<boolean>}
*/
async function has_version_changed() {
if (import.meta.env.DEV) return false;

const headers = new Headers();
headers.append('pragma', 'no-cache');
headers.append('cache-control', 'no-cache');

const current_version = import.meta.env.VITE_APP_VERSION;
const new_version = await fetch('_app/version.json', { headers })
.then((res) => res.json())
.catch(() => undefined);

return new_version && current_version && new_version !== current_version;
}

/**
* @param {RequestInfo} resource
* @param {RequestInit} [opts]
Expand Down Expand Up @@ -109,7 +147,17 @@ export class Renderer {
url: notifiable_store({}),
page: notifiable_store({}),
navigating: writable(/** @type {Navigating | null} */ (null)),
session: writable(session)
session: writable(session),
updated: checkable_store(
false,
(set) => {
return has_version_changed().then((changed) => {
if (changed) set(true);
return changed;
});
},
300000
)
};

this.$session = null;
Expand Down Expand Up @@ -279,8 +327,11 @@ export class Renderer {
return;
}
} else if (navigation_result.props?.page?.status >= 400 && onError !== 'fail') {
location.href = info.url.href;
return;
const updated = await this.stores.updated.check();
if (updated) {
location.href = info.url.href;
return;
}
}

this.updating = true;
Expand Down
13 changes: 12 additions & 1 deletion packages/kit/src/runtime/server/page/render.js
Original file line number Diff line number Diff line change
Expand Up @@ -80,12 +80,23 @@ export async function render_response({

const session = writable($session);

const updated = () => {
const { set, update, subscribe } = writable(false);
return {
set,
update,
subscribe,
check: () => {}
};
};

/** @type {Record<string, any>} */
const props = {
stores: {
page: writable(null),
navigating: writable(null),
session
session,
updated
},
page: {
url: state.prerender ? create_prerendering_url_proxy(url) : url,
Expand Down
10 changes: 10 additions & 0 deletions packages/kit/types/ambient-modules.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,10 @@ declare module '$app/env' {
* By default, `svelte-kit dev` runs with `mode=development` and `svelte-kit build` runs with `mode=production`.
*/
export const mode: string;
/**
* Version of the app
*/
export const version: string;
}

declare module '$app/navigation' {
Expand Down Expand Up @@ -115,6 +119,7 @@ declare module '$app/stores' {
error: Error | null;
}>;
session: Writable<Session>;
updated: Writable<boolean> & { check: () => boolean };
};
export const url: Readable<URL>;
/**
Expand All @@ -138,6 +143,11 @@ declare module '$app/stores' {
* It can be written to, but this will not cause changes to persist on the server — this is something you must implement yourself.
*/
export const session: Writable<any>;
/**
* A writable store indicating if the site was updated since the store was created.
* It can be written to when custom logic is required to detect updates.
*/
export const updated: Writable<boolean> & { check: () => boolean };
}

declare module '$service-worker' {
Expand Down

0 comments on commit 7c75a08

Please sign in to comment.