diff --git a/.changeset/pink-poets-begin.md b/.changeset/pink-poets-begin.md new file mode 100644 index 000000000000..78fad02b937b --- /dev/null +++ b/.changeset/pink-poets-begin.md @@ -0,0 +1,5 @@ +--- +'@sveltejs/kit': patch +--- + +ServiceWorker files exclusion support available through svelte.config.js diff --git a/documentation/docs/05-modules.md b/documentation/docs/05-modules.md index 25d8608c6b28..292182ea2218 100644 --- a/documentation/docs/05-modules.md +++ b/documentation/docs/05-modules.md @@ -66,5 +66,5 @@ import { build, files, timestamp } from '$service-worker'; ``` - `build` is an array of URL strings representing the files generated by Vite, suitable for caching with `cache.addAll(build)` -- `files` is an array of URL strings representing the files in your `static` directory, or whatever directory is specified by [`config.kit.files.assets`](#configuration) +- `files` is an array of URL strings representing the files in your `static` directory, or whatever directory is specified by [`config.kit.files.assets`](#configuration). You can exclude certain files from `static` directory using [`config.kit.serviceWorker.exclude`](#configuration) - `timestamp` is the result of calling `Date.now()` at build time. It's useful for generating unique cache names inside your service worker, so that a later deployment of your app can invalidate old caches diff --git a/documentation/docs/14-configuration.md b/documentation/docs/14-configuration.md index 598704a5d02b..830c8c180642 100644 --- a/documentation/docs/14-configuration.md +++ b/documentation/docs/14-configuration.md @@ -25,6 +25,9 @@ const config = { serviceWorker: 'src/service-worker', template: 'src/app.html' }, + serviceWorker: { + exclude: [] + }, floc: false, host: null, hostHeader: null, @@ -76,6 +79,12 @@ An object containing zero or more of the following `string` values: - `hooks` — the location of your hooks module (see [Hooks](#hooks)) - `template` — the location of the template for HTML responses +### serviceWorker + +An object containing zero or more of the following values: + +- `exclude` - an array of glob patterns relative to `files.assets` dir. Files matching any of these would not be available in `$service-worker.files` e.g. if `files.assets` has value `static` then ['og-tags-images/**/*'] would match all files under `static/og-tags-images` dir. + ### floc Google's [FLoC](https://github.com/WICG/floc) is a technology for targeted advertising that the [Electronic Frontier Foundation](https://www.eff.org/) has deemed [harmful](https://www.eff.org/deeplinks/2021/03/googles-floc-terrible-idea) to user privacy. [Browsers other than Chrome](https://www.theverge.com/2021/4/16/22387492/google-floc-ad-tech-privacy-browsers-brave-vivaldi-edge-mozilla-chrome-safari) have declined to implement it. diff --git a/packages/kit/src/core/config/index.spec.js b/packages/kit/src/core/config/index.spec.js index 1c6b7b8a1f0d..02334f1a04e2 100644 --- a/packages/kit/src/core/config/index.spec.js +++ b/packages/kit/src/core/config/index.spec.js @@ -38,6 +38,9 @@ test('fills in defaults', () => { exclude: [] } }, + serviceWorker: { + exclude: [] + }, paths: { base: '', assets: '/.' @@ -133,6 +136,9 @@ test('fills in partial blanks', () => { exclude: [] } }, + serviceWorker: { + exclude: [] + }, paths: { base: '', assets: '/.' diff --git a/packages/kit/src/core/config/options.js b/packages/kit/src/core/config/options.js index c763465039e9..c0fed8614f7a 100644 --- a/packages/kit/src/core/config/options.js +++ b/packages/kit/src/core/config/options.js @@ -79,6 +79,12 @@ const options = { hostHeader: expect_string(null), hydrate: expect_boolean(true), + serviceWorker: { + type: 'branch', + children: { + exclude: expect_array_of_strings([]) + } + }, package: { type: 'branch', diff --git a/packages/kit/src/core/config/test/index.js b/packages/kit/src/core/config/test/index.js index eb41d36ad697..8a0d8e0779fd 100644 --- a/packages/kit/src/core/config/test/index.js +++ b/packages/kit/src/core/config/test/index.js @@ -48,6 +48,9 @@ async function testLoadDefaultConfig(path) { exclude: [] } }, + serviceWorker: { + exclude: [] + }, paths: { base: '', assets: '/.' }, prerender: { crawl: true, enabled: true, force: false, pages: ['*'] }, router: true, diff --git a/packages/kit/src/core/create_manifest_data/index.js b/packages/kit/src/core/create_manifest_data/index.js index e03d2223e58a..6c1c8e580d04 100644 --- a/packages/kit/src/core/create_manifest_data/index.js +++ b/packages/kit/src/core/create_manifest_data/index.js @@ -2,6 +2,7 @@ import fs from 'fs'; import path from 'path'; import mime from 'mime'; import { posixify } from '../utils.js'; +import glob from 'tiny-glob/sync.js'; /** @typedef {{ * content: string; @@ -22,6 +23,44 @@ import { posixify } from '../utils.js'; const specials = new Set(['__layout', '__layout.reset', '__error']); +/** + * + * @param {import('types/config').ValidatedConfig} config + * @returns {import('types/internal').ManifestData['assets']} + */ +function get_assets_list(config) { + const assets_dir = config.kit.files.assets; + /** + * @type {import('types/internal').Asset[]} + */ + let assets = []; + if (fs.existsSync(assets_dir)) { + /** + * @type {string[]} + */ + const exclusions = config.kit.serviceWorker.exclude || []; + + exclusions.push('**/.DS_STORE'); + + /** + * @type {string[]} + */ + let excluded_paths = []; + + exclusions.forEach((exclusion) => { + excluded_paths = [ + ...excluded_paths, + ...glob(exclusion, { + cwd: assets_dir, + dot: true + }) + ]; + }); + assets = list_files(assets_dir, '', [], excluded_paths); + } + return assets; +} + /** * @param {{ * config: import('types/config').ValidatedConfig; @@ -237,10 +276,8 @@ export default function create_manifest_data({ config, output, cwd = process.cwd walk(config.kit.files.routes, [], [], [layout], [error]); - const assets_dir = config.kit.files.assets; - return { - assets: fs.existsSync(assets_dir) ? list_files(assets_dir, '') : [], + assets: get_assets_list(config), layout, error, components, @@ -375,8 +412,9 @@ function get_pattern(segments, add_trailing_slash) { * @param {string} dir * @param {string} path * @param {import('types/internal').Asset[]} files + * @param {string[]} excluded_paths Paths relative to dir which should be excluded from files list. */ -function list_files(dir, path, files = []) { +function list_files(dir, path, files = [], excluded_paths = []) { fs.readdirSync(dir).forEach((file) => { const full = `${dir}/${file}`; @@ -384,9 +422,11 @@ function list_files(dir, path, files = []) { const joined = path ? `${path}/${file}` : file; if (stats.isDirectory()) { - list_files(full, joined, files); + list_files(full, joined, files, excluded_paths); } else { - if (file === '.DS_Store') return; + if (excluded_paths.includes(joined)) { + return; + } files.push({ file: joined, size: stats.size, diff --git a/packages/kit/src/core/create_manifest_data/index.spec.js b/packages/kit/src/core/create_manifest_data/index.spec.js index 2b543a4613e8..bd07cbc93826 100644 --- a/packages/kit/src/core/create_manifest_data/index.spec.js +++ b/packages/kit/src/core/create_manifest_data/index.spec.js @@ -21,7 +21,10 @@ const create = (dir, extensions = ['.svelte']) => { assets: path.resolve(cwd, 'static'), routes: path.resolve(cwd, dir) }, - appDir: '_app' + appDir: '_app', + serviceWorker: { + exclude: [] + } } }, cwd, diff --git a/packages/kit/types/config.d.ts b/packages/kit/types/config.d.ts index 5471b5dbdc89..48c95bf93425 100644 --- a/packages/kit/types/config.d.ts +++ b/packages/kit/types/config.d.ts @@ -44,6 +44,9 @@ export type Config = { host?: string; hostHeader?: string; hydrate?: boolean; + serviceWorker?: { + exclude?: string[]; + }; package?: { dir?: string; exports?: { @@ -94,6 +97,9 @@ export type ValidatedConfig = { host: string; hostHeader: string; hydrate: boolean; + serviceWorker: { + exclude: string[]; + }; package: { dir: string; exports: {