Skip to content

Commit 1c4f845

Browse files
authored
feat!: rolldown native watcher (#329)
1 parent 647adde commit 1c4f845

File tree

12 files changed

+390
-299
lines changed

12 files changed

+390
-299
lines changed

eslint.config.js

Lines changed: 13 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,23 @@
1+
// @ts-check
12
import { sxzz } from '@sxzz/eslint-config'
23

3-
export default sxzz({
4-
pnpm: true,
5-
})
6-
.append({
4+
export default sxzz(
5+
{
6+
pnpm: true,
7+
baseline: {
8+
ignoreFeatures: ['explicit-resource-management'],
9+
},
10+
},
11+
{
712
files: ['templates/**'],
813
rules: {
914
'pnpm/json-enforce-catalog': 'off',
1015
},
11-
})
12-
.append({
16+
},
17+
{
1318
files: ['docs/**/*.md/**'],
1419
rules: {
1520
'unicorn/prefer-node-protocol': 'off',
1621
},
17-
})
22+
},
23+
)

package.json

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -91,7 +91,6 @@
9191
"dependencies": {
9292
"ansis": "catalog:prod",
9393
"cac": "catalog:prod",
94-
"chokidar": "catalog:prod",
9594
"diff": "catalog:prod",
9695
"empathic": "catalog:prod",
9796
"hookable": "catalog:prod",

pnpm-lock.yaml

Lines changed: 11 additions & 31 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

pnpm-workspace.yaml

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ overrides:
88

99
catalogs:
1010
dev:
11-
'@sxzz/eslint-config': ^7.3.2
11+
'@sxzz/eslint-config': ^7.4.0
1212
'@sxzz/prettier-config': ^2.2.6
1313
'@sxzz/test-utils': ^0.5.13
1414
'@types/node': ^24.10.1
@@ -49,7 +49,6 @@ catalogs:
4949
'@clack/prompts': ^0.11.0
5050
ansis: ^4.2.0
5151
cac: ^6.7.14
52-
chokidar: ^5.0.0
5352
diff: ^8.0.2
5453
empathic: ^2.0.0
5554
giget: ^2.0.0

src/config/config.ts

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -52,10 +52,10 @@ export async function loadViteConfig(
5252
}
5353

5454
const configPrefix = 'tsdown.config'
55-
let isWatch = false
55+
let noCacheLoad = false
5656

57-
export function setWatch(): void {
58-
isWatch = true
57+
export function setNoCacheLoad(): void {
58+
noCacheLoad = true
5959
}
6060

6161
export async function loadConfigFile(
@@ -110,7 +110,7 @@ export async function loadConfigFile(
110110
sources,
111111
cwd,
112112
stopAt: workspace && path.dirname(workspace),
113-
}).load(isWatch)
113+
}).load(noCacheLoad)
114114

115115
let exported: UserConfigExport = []
116116
let file: string | undefined
@@ -146,7 +146,7 @@ type Parser = 'native' | 'unrun'
146146
function resolveConfigLoader(
147147
configLoader: InlineConfig['configLoader'] = 'auto',
148148
): Parser {
149-
if (isWatch) {
149+
if (noCacheLoad) {
150150
return 'unrun'
151151
} else if (configLoader === 'auto') {
152152
const nativeTS = !!(

src/config/index.ts

Lines changed: 37 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -331,48 +331,56 @@ async function resolveUserConfig(
331331

332332
debug = resolveFeatureOption(debug, {})
333333
if (debug) {
334-
debug.devtools ??= !!pkgExists('@vitejs/devtools/cli')
334+
if (watch) {
335+
if (debug.devtools) {
336+
logger.warn('Devtools is not supported in watch mode, disabling it.')
337+
}
338+
debug.devtools = false
339+
} else {
340+
debug.devtools ??= !!pkgExists('@vitejs/devtools/cli')
341+
}
335342
}
336343

344+
/// keep-sorted
337345
const config: ResolvedConfig = {
338346
...userConfig,
347+
alias,
348+
attw,
349+
cjsDefault,
350+
clean,
351+
copy: publicDir || copy,
352+
cwd,
353+
debug,
354+
dts,
339355
entry,
340-
plugins,
356+
env,
357+
exports,
358+
external,
359+
fixedExtension,
341360
format: normalizeFormat(format),
342-
target,
343-
outDir,
344-
clean,
361+
globImport,
362+
hash,
363+
ignoreWatch,
364+
inlineOnly,
345365
logger,
346-
treeshake,
366+
name,
367+
nodeProtocol,
368+
noExternal,
369+
outDir,
370+
pkg,
347371
platform,
348-
sourcemap,
349-
dts,
372+
plugins,
373+
publint,
350374
report,
351-
unused,
352-
watch,
353-
ignoreWatch,
354375
shims,
355376
skipNodeModulesBundle,
356-
publint,
357-
attw,
358-
alias,
377+
sourcemap,
378+
target,
379+
treeshake,
359380
tsconfig,
360-
cwd,
361-
env,
362-
pkg,
363-
copy: publicDir || copy,
364-
hash,
365-
name,
366-
external,
367-
noExternal,
368-
exports,
369381
unbundle,
370-
nodeProtocol,
371-
cjsDefault,
372-
globImport,
373-
inlineOnly,
374-
fixedExtension,
375-
debug,
382+
unused,
383+
watch,
376384
}
377385

378386
return config

src/config/types.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -380,6 +380,9 @@ export interface UserConfig {
380380
* @default false
381381
*/
382382
watch?: boolean | Arrayable<string>
383+
/**
384+
* Files or patterns to not watch while in watch mode.
385+
*/
383386
ignoreWatch?: Arrayable<string | RegExp>
384387

385388
/**

src/features/clean.ts

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import { fsRemove } from '../utils/fs.ts'
55
import { slash } from '../utils/general.ts'
66
import { globalLogger } from '../utils/logger.ts'
77
import type { ResolvedConfig, UserConfig } from '../config/index.ts'
8+
import type { OutputAsset, OutputChunk } from 'rolldown'
89

910
const debug = createDebug('tsdown:clean')
1011

@@ -64,3 +65,16 @@ export function resolveClean(
6465

6566
return clean
6667
}
68+
69+
export async function cleanupChunks(
70+
outDir: string,
71+
chunks: Array<OutputAsset | OutputChunk>,
72+
): Promise<void> {
73+
await Promise.all(
74+
chunks.map(async (chunk) => {
75+
const filePath = path.resolve(outDir, chunk.fileName)
76+
debug('Removing chunk file', filePath)
77+
await fsRemove(filePath)
78+
}),
79+
)
80+
}

src/features/hooks.ts

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,7 @@
1+
import process from 'node:process'
12
import { createHooks as create, type Hookable } from 'hookable'
3+
import { exec } from 'tinyexec'
4+
import treeKill from 'tree-kill'
25
import type { ResolvedConfig } from '../config/index.ts'
36
import type { BuildOptions } from 'rolldown'
47

@@ -49,3 +52,33 @@ export async function createHooks(options: ResolvedConfig): Promise<{
4952
}
5053
return { hooks, context }
5154
}
55+
56+
export function executeOnSuccess(
57+
config: ResolvedConfig,
58+
): AbortController | undefined {
59+
if (!config.onSuccess) return
60+
61+
const ab = new AbortController()
62+
if (typeof config.onSuccess === 'string') {
63+
const p = exec(config.onSuccess, [], {
64+
nodeOptions: {
65+
shell: true,
66+
stdio: 'inherit',
67+
},
68+
})
69+
p.then(({ exitCode }) => {
70+
if (exitCode) {
71+
process.exitCode = exitCode
72+
}
73+
})
74+
ab.signal.addEventListener('abort', () => {
75+
if (typeof p.pid === 'number') {
76+
treeKill(p.pid)
77+
}
78+
})
79+
} else {
80+
config.onSuccess(config, ab.signal)
81+
}
82+
83+
return ab
84+
}

0 commit comments

Comments
 (0)