Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[feat] prefer functions to globs #2430

Merged
merged 11 commits into from
Sep 24, 2021
5 changes: 5 additions & 0 deletions .changeset/nasty-seas-reflect.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@sveltejs/kit': patch
---

Exports and files property in config.kit.package now accepts a function rather than an object
5 changes: 5 additions & 0 deletions .changeset/stupid-laws-jog.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@sveltejs/kit': patch
---

Renamed property exclude to files in config.kit.serviceWorker and now accepts a function instead
35 changes: 23 additions & 12 deletions documentation/docs/14-configuration.md
Original file line number Diff line number Diff line change
Expand Up @@ -32,14 +32,8 @@ const config = {
package: {
dir: 'package',
emitTypes: true,
exports: {
include: ['**'],
exclude: ['**/_*']
},
files: {
include: ['**'],
exclude: []
}
exports: (filepath) => !/^_|\/_|\.d\.ts$/.test(filepath),
files: () => true
},
paths: {
assets: '',
Expand All @@ -53,7 +47,7 @@ const config = {
},
router: true,
serviceWorker: {
exclude: []
files: (filepath) => !/\.DS_STORE/.test(filepath)
},
ssr: true,
target: null,
Expand Down Expand Up @@ -132,8 +126,25 @@ Options related to [creating a package](#packaging).

- `dir` - output directory
- `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)
- `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
- `files` - contains an `includes` and an `excludes` array which specifies which files to process and copy over when packaging
- `exports` - a function with the type of `(filepath: string) => boolean`. When `true`, the following export 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
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

what does "the following export" refer to?

- `files` - a function with the type of `(filepath: string) => boolean`. When `true`, the following file will be processed and copied over to the final output folder, specified in `dir`

```js
// svelte.config.js
import mm from 'micromatch';

export default {
kit: {
package: {
exports: (filepath) => {
if (filepath.endsWith('.d.ts')) return false;
return mm.isMatch(filepath, ['!**/_*', '!**/internal/**'])
},
files: mm.matcher('!**/build.*')
}
}
};
```

### paths

Expand Down Expand Up @@ -181,7 +192,7 @@ Enables or disables the client-side [router](#ssr-and-javascript-router) app-wid

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.
- `files` - a function with the type of `(filepath: string) => boolean`. When `true`, the following file will be available in `$service-worker.files`, otherwise it will be excluded.

### ssr

Expand Down
2 changes: 0 additions & 2 deletions packages/kit/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,6 @@
"@types/amphtml-validator": "^1.0.1",
"@types/cookie": "^0.4.0",
"@types/marked": "^2.0.2",
"@types/micromatch": "^4.0.2",
"@types/mime": "^2.0.3",
"@types/node": "^14.14.43",
"@types/rimraf": "^3.0.0",
Expand All @@ -25,7 +24,6 @@
"kleur": "^4.1.4",
"locate-character": "^2.0.5",
"marked": "^2.0.3",
"micromatch": "^4.0.4",
"mime": "^2.5.2",
"node-fetch": "^3.0.0-beta.9",
"port-authority": "^1.1.2",
Expand Down
44 changes: 16 additions & 28 deletions packages/kit/src/core/config/index.spec.js
Original file line number Diff line number Diff line change
@@ -1,12 +1,18 @@
import { test } from 'uvu';
import * as assert from 'uvu/assert';

import { remove_keys } from '../../utils/object.js';
import { validate_config } from './index.js';

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

// @ts-expect-error - can't test equality of a function
delete validated.kit.vite;
assert.equal(validated.kit.package.exports(''), true);
assert.equal(validated.kit.package.files(''), true);
assert.equal(validated.kit.serviceWorker.files(''), true);
assert.equal(validated.kit.vite(), {});

remove_keys(validated, ([, v]) => typeof v === 'function');

assert.equal(validated, {
extensions: ['.svelte'],
Expand All @@ -28,19 +34,9 @@ test('fills in defaults', () => {
hydrate: true,
package: {
dir: 'package',
emitTypes: true,
exports: {
include: ['**'],
exclude: ['**/_*']
},
files: {
include: ['**'],
exclude: []
}
},
serviceWorker: {
exclude: []
emitTypes: true
},
serviceWorker: {},
paths: {
base: '',
assets: ''
Expand Down Expand Up @@ -111,10 +107,12 @@ test('fills in partial blanks', () => {
}
});

assert.equal(validated.kit.package.exports(''), true);
assert.equal(validated.kit.package.files(''), true);
assert.equal(validated.kit.serviceWorker.files(''), true);
assert.equal(validated.kit.vite(), {});

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

assert.equal(validated, {
extensions: ['.svelte'],
Expand All @@ -136,19 +134,9 @@ test('fills in partial blanks', () => {
hydrate: true,
package: {
dir: 'package',
emitTypes: true,
exports: {
include: ['**'],
exclude: ['**/_*']
},
files: {
include: ['**'],
exclude: []
}
},
serviceWorker: {
exclude: []
emitTypes: true
},
serviceWorker: {},
paths: {
base: '',
assets: ''
Expand Down
38 changes: 16 additions & 22 deletions packages/kit/src/core/config/options.js
Original file line number Diff line number Diff line change
Expand Up @@ -75,14 +75,8 @@ const options = object(

package: object({
dir: string('package'),
exports: object({
include: array_of_strings(['**']),
exclude: array_of_strings(['**/_*'])
}),
files: object({
include: array_of_strings(['**']),
exclude: array_of_strings([])
}),
exports: fun((filepath) => !/^_|\/_|\.d\.ts$/.test(filepath)),
files: fun(() => true),
emitTypes: boolean(true)
}),

Expand Down Expand Up @@ -167,7 +161,7 @@ const options = object(
router: boolean(true),

serviceWorker: object({
exclude: array_of_strings([])
files: fun((filename) => !/\.DS_STORE/.test(filename))
}),

ssr: boolean(true),
Expand Down Expand Up @@ -266,19 +260,6 @@ function string(fallback, allow_empty = true) {
});
}

/**
* @param {string[]} array
* @returns {Validator}
*/
function array_of_strings(array) {
return validate(array, (input, keypath) => {
if (!Array.isArray(input) || !input.every((glob) => typeof glob === 'string')) {
throw new Error(`${keypath} must be an array of strings`);
}
return input;
});
}

/**
* @param {boolean} fallback
* @returns {Validator}
Expand Down Expand Up @@ -310,6 +291,19 @@ function list(options, fallback = options[0]) {
});
}

/**
* @param {(filename: string) => boolean} fallback
* @returns {Validator}
*/
function fun(fallback) {
return validate(fallback, (input, keypath) => {
if (typeof input !== 'function') {
throw new Error(`${keypath} should be a function, if specified`);
}
return input;
});
}

/**
* @param {string} input
* @param {string} keypath
Expand Down
25 changes: 8 additions & 17 deletions packages/kit/src/core/config/test/index.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
import { join } from 'path';
import { test } from 'uvu';
import * as assert from 'uvu/assert';
import { fileURLToPath } from 'url';

import * as assert from 'uvu/assert';
import { test } from 'uvu';

import { remove_keys } from '../../../utils/object.js';
import { load_config } from '../index.js';

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

const config = await load_config({ cwd });

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

assert.equal(config, {
extensions: ['.svelte'],
Expand All @@ -38,19 +39,9 @@ async function testLoadDefaultConfig(path) {
hydrate: true,
package: {
dir: 'package',
emitTypes: true,
exports: {
include: ['**'],
exclude: ['**/_*']
},
files: {
include: ['**'],
exclude: []
}
},
serviceWorker: {
exclude: []
emitTypes: true
},
serviceWorker: {},
paths: { base: '', assets: '' },
prerender: {
crawl: true,
Expand Down
64 changes: 14 additions & 50 deletions packages/kit/src/core/create_manifest_data/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ 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;
Expand All @@ -23,44 +22,6 @@ import glob from 'tiny-glob/sync.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;
Expand Down Expand Up @@ -273,8 +234,12 @@ export default function create_manifest_data({ config, output, cwd = process.cwd

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

const assets = fs.existsSync(config.kit.files.assets)
? list_files({ config, dir: config.kit.files.assets, path: '' })
: [];

return {
assets: get_assets_list(config),
assets,
layout,
error,
components,
Expand Down Expand Up @@ -404,24 +369,23 @@ 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.
* @param {{
* config: import('types/config').ValidatedConfig;
* dir: string;
* path: string;
* files?: import('types/internal').Asset[]
* }} args
*/
function list_files(dir, path, files = [], excluded_paths = []) {
function list_files({ config, dir, path, files = [] }) {
fs.readdirSync(dir).forEach((file) => {
const full = `${dir}/${file}`;

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

if (stats.isDirectory()) {
list_files(full, joined, files, excluded_paths);
} else {
if (excluded_paths.includes(joined)) {
return;
}
list_files({ config, dir: full, path: joined, files });
} else if (config.kit.serviceWorker.files(joined)) {
files.push({
file: joined,
size: stats.size,
Expand Down
2 changes: 1 addition & 1 deletion packages/kit/src/core/create_manifest_data/index.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ const create = (dir, extensions = ['.svelte']) => {
},
appDir: '_app',
serviceWorker: {
exclude: []
files: (filepath) => !/\.DS_STORE/.test(filepath)
}
}
};
Expand Down
Loading