Skip to content

Commit e1b788f

Browse files
authored
[feat] prefer functions to globs (#2430)
1 parent 7260ac5 commit e1b788f

File tree

15 files changed

+121
-182
lines changed

15 files changed

+121
-182
lines changed

.changeset/nasty-seas-reflect.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
'@sveltejs/kit': patch
3+
---
4+
5+
Exports and files property in config.kit.package now accepts a function rather than an object

.changeset/stupid-laws-jog.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
'@sveltejs/kit': patch
3+
---
4+
5+
Renamed property exclude to files in config.kit.serviceWorker and now accepts a function instead

documentation/docs/14-configuration.md

Lines changed: 26 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -32,14 +32,9 @@ const config = {
3232
package: {
3333
dir: 'package',
3434
emitTypes: true,
35-
exports: {
36-
include: ['**'],
37-
exclude: ['**/_*']
38-
},
39-
files: {
40-
include: ['**'],
41-
exclude: []
42-
}
35+
// excludes all .d.ts and files starting with _ as the name
36+
exports: (filepath) => !/^_|\/_|\.d\.ts$/.test(filepath),
37+
files: () => true
4338
},
4439
paths: {
4540
assets: '',
@@ -53,7 +48,7 @@ const config = {
5348
},
5449
router: true,
5550
serviceWorker: {
56-
exclude: []
51+
files: (filepath) => !/\.DS_STORE/.test(filepath)
5752
},
5853
ssr: true,
5954
target: null,
@@ -132,8 +127,27 @@ Options related to [creating a package](#packaging).
132127

133128
- `dir` - output directory
134129
- `emitTypes` - by default, `svelte-kit package` will automatically generate types for your package in the form of `d.ts.` files. While generating types is configurable, we believe it is best for the ecosystem quality to generate types, always. Please make sure you have a good reason when setting it to `false` (for example when you want to provide handwritten type definitions instead)
135-
- `exports` - contains an `includes` and an `excludes` array which specifies which files to mark as exported from the `exports` field of the `package.json`. Will merge existing values if available with values from `package.json` taking precedence
136-
- `files` - contains an `includes` and an `excludes` array which specifies which files to process and copy over when packaging
130+
- `exports` - a function with the type of `(filepath: string) => boolean`. When `true`, the filepath will be included in the `exports` field of the `package.json`. Any existing values in the `package.json` source will be merged with values from the original `exports` field taking precedence
131+
- `files` - a function with the type of `(filepath: string) => boolean`. When `true`, the file will be processed and copied over to the final output folder, specified in `dir`
132+
133+
For advanced `filepath` matching, you can use `exports` and `files` options in conjunction with a globbing library:
134+
135+
```js
136+
// svelte.config.js
137+
import mm from 'micromatch';
138+
139+
export default {
140+
kit: {
141+
package: {
142+
exports: (filepath) => {
143+
if (filepath.endsWith('.d.ts')) return false;
144+
return mm.isMatch(filepath, ['!**/_*', '!**/internal/**'])
145+
},
146+
files: mm.matcher('!**/build.*')
147+
}
148+
}
149+
};
150+
```
137151

138152
### paths
139153

@@ -181,7 +195,7 @@ Enables or disables the client-side [router](#ssr-and-javascript-router) app-wid
181195

182196
An object containing zero or more of the following values:
183197

184-
- `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.
198+
- `files` - a function with the type of `(filepath: string) => boolean`. When `true`, the given file will be available in `$service-worker.files`, otherwise it will be excluded.
185199
186200
### ssr
187201

packages/kit/package.json

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,6 @@
1919
"@types/amphtml-validator": "^1.0.1",
2020
"@types/cookie": "^0.4.1",
2121
"@types/marked": "^3.0.1",
22-
"@types/micromatch": "^4.0.2",
2322
"@types/mime": "^2.0.3",
2423
"@types/node": "^16.9.1",
2524
"@types/rimraf": "^3.0.2",
@@ -31,7 +30,6 @@
3130
"kleur": "^4.1.4",
3231
"locate-character": "^2.0.5",
3332
"marked": "^3.0.4",
34-
"micromatch": "^4.0.4",
3533
"mime": "^2.5.2",
3634
"node-fetch": "3.0.0-beta.9",
3735
"port-authority": "^1.1.2",

packages/kit/src/core/config/index.spec.js

Lines changed: 16 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,18 @@
11
import { test } from 'uvu';
22
import * as assert from 'uvu/assert';
3+
4+
import { remove_keys } from '../../utils/object.js';
35
import { validate_config } from './index.js';
46

57
test('fills in defaults', () => {
68
const validated = validate_config({});
79

8-
// @ts-expect-error - can't test equality of a function
9-
delete validated.kit.vite;
10+
assert.equal(validated.kit.package.exports(''), true);
11+
assert.equal(validated.kit.package.files(''), true);
12+
assert.equal(validated.kit.serviceWorker.files(''), true);
13+
assert.equal(validated.kit.vite(), {});
14+
15+
remove_keys(validated, ([, v]) => typeof v === 'function');
1016

1117
assert.equal(validated, {
1218
extensions: ['.svelte'],
@@ -28,19 +34,9 @@ test('fills in defaults', () => {
2834
hydrate: true,
2935
package: {
3036
dir: 'package',
31-
emitTypes: true,
32-
exports: {
33-
include: ['**'],
34-
exclude: ['**/_*']
35-
},
36-
files: {
37-
include: ['**'],
38-
exclude: []
39-
}
40-
},
41-
serviceWorker: {
42-
exclude: []
37+
emitTypes: true
4338
},
39+
serviceWorker: {},
4440
paths: {
4541
base: '',
4642
assets: ''
@@ -111,10 +107,12 @@ test('fills in partial blanks', () => {
111107
}
112108
});
113109

110+
assert.equal(validated.kit.package.exports(''), true);
111+
assert.equal(validated.kit.package.files(''), true);
112+
assert.equal(validated.kit.serviceWorker.files(''), true);
114113
assert.equal(validated.kit.vite(), {});
115114

116-
// @ts-expect-error - can't test equality of a function
117-
delete validated.kit.vite;
115+
remove_keys(validated, ([, v]) => typeof v === 'function');
118116

119117
assert.equal(validated, {
120118
extensions: ['.svelte'],
@@ -136,19 +134,9 @@ test('fills in partial blanks', () => {
136134
hydrate: true,
137135
package: {
138136
dir: 'package',
139-
emitTypes: true,
140-
exports: {
141-
include: ['**'],
142-
exclude: ['**/_*']
143-
},
144-
files: {
145-
include: ['**'],
146-
exclude: []
147-
}
148-
},
149-
serviceWorker: {
150-
exclude: []
137+
emitTypes: true
151138
},
139+
serviceWorker: {},
152140
paths: {
153141
base: '',
154142
assets: ''

packages/kit/src/core/config/options.js

Lines changed: 17 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -75,14 +75,9 @@ const options = object(
7575

7676
package: object({
7777
dir: string('package'),
78-
exports: object({
79-
include: array_of_strings(['**']),
80-
exclude: array_of_strings(['**/_*'])
81-
}),
82-
files: object({
83-
include: array_of_strings(['**']),
84-
exclude: array_of_strings([])
85-
}),
78+
// excludes all .d.ts and filename starting with _
79+
exports: fun((filepath) => !/^_|\/_|\.d\.ts$/.test(filepath)),
80+
files: fun(() => true),
8681
emitTypes: boolean(true)
8782
}),
8883

@@ -167,7 +162,7 @@ const options = object(
167162
router: boolean(true),
168163

169164
serviceWorker: object({
170-
exclude: array_of_strings([])
165+
files: fun((filename) => !/\.DS_STORE/.test(filename))
171166
}),
172167

173168
ssr: boolean(true),
@@ -266,19 +261,6 @@ function string(fallback, allow_empty = true) {
266261
});
267262
}
268263

269-
/**
270-
* @param {string[]} array
271-
* @returns {Validator}
272-
*/
273-
function array_of_strings(array) {
274-
return validate(array, (input, keypath) => {
275-
if (!Array.isArray(input) || !input.every((glob) => typeof glob === 'string')) {
276-
throw new Error(`${keypath} must be an array of strings`);
277-
}
278-
return input;
279-
});
280-
}
281-
282264
/**
283265
* @param {boolean} fallback
284266
* @returns {Validator}
@@ -310,6 +292,19 @@ function list(options, fallback = options[0]) {
310292
});
311293
}
312294

295+
/**
296+
* @param {(filename: string) => boolean} fallback
297+
* @returns {Validator}
298+
*/
299+
function fun(fallback) {
300+
return validate(fallback, (input, keypath) => {
301+
if (typeof input !== 'function') {
302+
throw new Error(`${keypath} should be a function, if specified`);
303+
}
304+
return input;
305+
});
306+
}
307+
313308
/**
314309
* @param {string} input
315310
* @param {string} keypath

packages/kit/src/core/config/test/index.js

Lines changed: 8 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,10 @@
11
import { join } from 'path';
2-
import { test } from 'uvu';
3-
import * as assert from 'uvu/assert';
42
import { fileURLToPath } from 'url';
3+
4+
import * as assert from 'uvu/assert';
5+
import { test } from 'uvu';
6+
7+
import { remove_keys } from '../../../utils/object.js';
58
import { load_config } from '../index.js';
69

710
const __filename = fileURLToPath(import.meta.url);
@@ -14,9 +17,7 @@ async function testLoadDefaultConfig(path) {
1417
const cwd = join(__dirname, 'fixtures', path);
1518

1619
const config = await load_config({ cwd });
17-
18-
// @ts-expect-error - can't test equality of a function
19-
delete config.kit.vite;
20+
remove_keys(config, ([, v]) => typeof v === 'function');
2021

2122
assert.equal(config, {
2223
extensions: ['.svelte'],
@@ -38,19 +39,9 @@ async function testLoadDefaultConfig(path) {
3839
hydrate: true,
3940
package: {
4041
dir: 'package',
41-
emitTypes: true,
42-
exports: {
43-
include: ['**'],
44-
exclude: ['**/_*']
45-
},
46-
files: {
47-
include: ['**'],
48-
exclude: []
49-
}
50-
},
51-
serviceWorker: {
52-
exclude: []
42+
emitTypes: true
5343
},
44+
serviceWorker: {},
5445
paths: { base: '', assets: '' },
5546
prerender: {
5647
crawl: true,

packages/kit/src/core/create_manifest_data/index.js

Lines changed: 14 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@ import fs from 'fs';
22
import path from 'path';
33
import mime from 'mime';
44
import { posixify } from '../utils.js';
5-
import glob from 'tiny-glob/sync.js';
65

76
/**
87
* A portion of a file or directory name where the name has been split into
@@ -26,44 +25,6 @@ import glob from 'tiny-glob/sync.js';
2625

2726
const specials = new Set(['__layout', '__layout.reset', '__error']);
2827

29-
/**
30-
*
31-
* @param {import('types/config').ValidatedConfig} config
32-
* @returns {import('types/internal').ManifestData['assets']}
33-
*/
34-
function get_assets_list(config) {
35-
const assets_dir = config.kit.files.assets;
36-
/**
37-
* @type {import('types/internal').Asset[]}
38-
*/
39-
let assets = [];
40-
if (fs.existsSync(assets_dir)) {
41-
/**
42-
* @type {string[]}
43-
*/
44-
const exclusions = config.kit.serviceWorker.exclude || [];
45-
46-
exclusions.push('**/.DS_STORE');
47-
48-
/**
49-
* @type {string[]}
50-
*/
51-
let excluded_paths = [];
52-
53-
exclusions.forEach((exclusion) => {
54-
excluded_paths = [
55-
...excluded_paths,
56-
...glob(exclusion, {
57-
cwd: assets_dir,
58-
dot: true
59-
})
60-
];
61-
});
62-
assets = list_files(assets_dir, '', [], excluded_paths);
63-
}
64-
return assets;
65-
}
66-
6728
/**
6829
* @param {{
6930
* config: import('types/config').ValidatedConfig;
@@ -276,8 +237,12 @@ export default function create_manifest_data({ config, output, cwd = process.cwd
276237

277238
walk(config.kit.files.routes, [], [], [layout], [error]);
278239

240+
const assets = fs.existsSync(config.kit.files.assets)
241+
? list_files({ config, dir: config.kit.files.assets, path: '' })
242+
: [];
243+
279244
return {
280-
assets: get_assets_list(config),
245+
assets,
281246
layout,
282247
error,
283248
components,
@@ -419,24 +384,23 @@ function get_pattern(segments, add_trailing_slash) {
419384
}
420385

421386
/**
422-
* @param {string} dir
423-
* @param {string} path
424-
* @param {import('types/internal').Asset[]} files
425-
* @param {string[]} excluded_paths Paths relative to dir which should be excluded from files list.
387+
* @param {{
388+
* config: import('types/config').ValidatedConfig;
389+
* dir: string;
390+
* path: string;
391+
* files?: import('types/internal').Asset[]
392+
* }} args
426393
*/
427-
function list_files(dir, path, files = [], excluded_paths = []) {
394+
function list_files({ config, dir, path, files = [] }) {
428395
fs.readdirSync(dir).forEach((file) => {
429396
const full = `${dir}/${file}`;
430397

431398
const stats = fs.statSync(full);
432399
const joined = path ? `${path}/${file}` : file;
433400

434401
if (stats.isDirectory()) {
435-
list_files(full, joined, files, excluded_paths);
436-
} else {
437-
if (excluded_paths.includes(joined)) {
438-
return;
439-
}
402+
list_files({ config, dir: full, path: joined, files });
403+
} else if (config.kit.serviceWorker.files(joined)) {
440404
files.push({
441405
file: joined,
442406
size: stats.size,

packages/kit/src/core/create_manifest_data/index.spec.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ const create = (dir, extensions = ['.svelte']) => {
2222
},
2323
appDir: '_app',
2424
serviceWorker: {
25-
exclude: []
25+
files: (filepath) => !/\.DS_STORE/.test(filepath)
2626
}
2727
}
2828
};

0 commit comments

Comments
 (0)