Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/quiet-owls-watch.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@sveltejs/package': patch
---

chore: remove dependency on chokidar
1 change: 0 additions & 1 deletion packages/package/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,6 @@
"homepage": "https://svelte.dev",
"type": "module",
"dependencies": {
"chokidar": "^5.0.0",
"sade": "^1.8.1",
"semver": "^7.8.5",
"svelte2tsx": "~0.7.56"
Expand Down
49 changes: 38 additions & 11 deletions packages/package/src/index.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import * as fs from 'node:fs';
import * as path from 'node:path';
import { styleText } from 'node:util';
import chokidar from 'chokidar';
import { preprocess } from 'svelte/compiler';
import { copy, mkdirp, posixify, rimraf } from './filesystem.js';
import {
Expand Down Expand Up @@ -94,6 +93,9 @@ export async function watch(options) {
/** @type {Array<{ file: import('./types.js').File, type: string }>} */
const pending = [];

// Remember files because deleted paths cannot be stat-ed to distinguish them from directories.
const known_files = new Set(scan(input, extensions).map((file) => file.name));

/** @type {Array<(value?: any) => void>} */
const fulfillers = [];

Expand All @@ -103,22 +105,48 @@ export async function watch(options) {
/** @type {Map<string, import('typescript').CompilerOptions>} */
const tsconfig_cache = new Map();

const watcher = chokidar.watch(input, { ignoreInitial: true });
/** @type {Promise<void>} */
const ready = new Promise((resolve) => watcher.on('ready', resolve));

watcher.on('all', (type, filepath) => {
const file = analyze(path.relative(input, filepath), extensions);
/**
* @param {string} name
* @param {string} type
*/
function enqueue(name, type) {
const file = analyze(name, extensions);

pending.push({ file, type });
if (!pending.some((event) => event.type === type && event.file.name === file.name)) {
pending.push({ file, type });
}

if (
file.name.endsWith('tsconfig.json') ||
file.name.endsWith('jsconfig.json') ||
(options.tsconfig && posixify(filepath) === posixify(options.tsconfig))
(options.tsconfig && posixify(path.join(input, name)) === posixify(options.tsconfig))
) {
tsconfig_cache.clear();
}
}

const watcher = fs.watch(input, { recursive: true }, (_, filename) => {
if (filename !== null) {
const name = posixify(filename);
const stats = fs.statSync(path.join(input, filename), { throwIfNoEntry: false });

if (stats) {
if (!stats.isFile()) return;
known_files.add(name);
enqueue(name, 'change');
} else if (known_files.delete(name)) {
enqueue(name, 'unlink');
} else {
// a removed directory only fires an event for itself, not for the files inside it
const children = [...known_files].filter((child) => child.startsWith(name + '/'));
if (children.length === 0) return;

for (const child of children) {
known_files.delete(child);
enqueue(child, 'unlink');
}
}
}

clearTimeout(timeout);
timeout = setTimeout(async () => {
Expand Down Expand Up @@ -151,7 +179,7 @@ export async function watch(options) {
console.log(`Removed ${file.dest}`);
}

if (type === 'add' || type === 'change') {
if (type === 'change') {
console.log(`Processing ${file.name}`);
try {
await process_file(
Expand Down Expand Up @@ -193,7 +221,6 @@ export async function watch(options) {

return {
watcher,
ready,
settled: () =>
new Promise((fulfil, reject) => {
fulfillers.push(fulfil);
Expand Down
27 changes: 22 additions & 5 deletions packages/package/test/index.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -181,7 +181,7 @@ test('create package with tsconfig specified', async () => {
await test_make_package('tsconfig-specified', { tsconfig: 'tsconfig.build.json' });
});

// chokidar doesn't fire events in github actions :shrug:
// File watching is unreliable in GitHub Actions
if (!process.env.CI) {
test('watches for changes', async () => {
const cwd = join(import.meta.dirname, 'watch');
Expand All @@ -190,7 +190,7 @@ if (!process.env.CI) {
const config = await load_config();
process.chdir(original_cwd);

const { watcher, ready, settled } = await watch({
const { watcher, settled } = await watch({
cwd,
input: 'src/lib',
output: 'package',
Expand Down Expand Up @@ -224,8 +224,6 @@ if (!process.env.CI) {
}

try {
await ready;

// completes initial build
compare('index.js');

Expand Down Expand Up @@ -255,13 +253,32 @@ if (!process.env.CI) {
write('src/lib/post-error.svelte', '<button on:click={foo}>click me</button>');
await settled();
compare('post-error.svelte');

// removes outputs when a source file is deleted
remove('src/lib/a.js');
await settled();
expect(fs.existsSync(join(cwd, 'package/a.js'))).toBe(false);
expect(fs.existsSync(join(cwd, 'package/a.d.ts'))).toBe(false);

// removes outputs when a directory is renamed
fs.mkdirSync(join(cwd, 'src/lib/sub'));
write('src/lib/sub/c.js', "export const c = 'c';");
await settled();
expect(fs.existsSync(join(cwd, 'package/sub/c.js'))).toBe(true);

fs.renameSync(join(cwd, 'src/lib/sub'), join(cwd, 'src/lib/sub2'));
await settled();
expect(fs.existsSync(join(cwd, 'package/sub/c.js'))).toBe(false);
expect(fs.existsSync(join(cwd, 'package/sub2/c.js'))).toBe(true);
} finally {
await watcher.close();
watcher.close();

remove('src/lib/Test.svelte');
remove('src/lib/a.js');
remove('src/lib/b.ts');
remove('src/lib/post-error.svelte');
fs.rmSync(join(cwd, 'src/lib/sub'), { recursive: true, force: true });
fs.rmSync(join(cwd, 'src/lib/sub2'), { recursive: true, force: true });
}
}, 30_000);
}
Expand Down
17 changes: 0 additions & 17 deletions pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading