tsdown-stale-guard 0.0.0 → 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +14 -12
- package/dist/{check-YFLy7mcd.mjs → check-CWbzg4YR.mjs} +9 -7
- package/dist/cli.mjs +8 -18
- package/dist/index.d.mts +15 -14
- package/dist/index.mjs +22 -29
- package/package.json +10 -7
package/README.md
CHANGED
|
@@ -8,13 +8,15 @@
|
|
|
8
8
|
|
|
9
9
|
Build freshness validation for [tsdown](https://github.com/rolldown/tsdown). Records hashes of all files involved in a build, enabling fast up-to-date checks without re-building.
|
|
10
10
|
|
|
11
|
+
> This plugin requires `tsdown@0.21.9` or later.
|
|
12
|
+
|
|
11
13
|
## Features
|
|
12
14
|
|
|
13
15
|
- **tsdown/Rolldown plugin** — automatically tracks source files, output files, config, and package lock file
|
|
14
16
|
- **Composite hash** — single hash for quick freshness checks
|
|
15
17
|
- **Find-up search** — detects lock files and configs in monorepo setups
|
|
16
|
-
- **CLI** — `tsdown-stale-guard
|
|
17
|
-
- **Programmatic API** — `
|
|
18
|
+
- **CLI** — `tsdown-stale-guard` for CI pipelines
|
|
19
|
+
- **Programmatic API** — `checkBuildState()` for tool integrations
|
|
18
20
|
|
|
19
21
|
## Install
|
|
20
22
|
|
|
@@ -29,17 +31,17 @@ npm i tsdown-stale-guard
|
|
|
29
31
|
```ts
|
|
30
32
|
// tsdown.config.ts
|
|
31
33
|
import { defineConfig } from 'tsdown'
|
|
32
|
-
import {
|
|
34
|
+
import { StaleGuardRecorder } from 'tsdown-stale-guard'
|
|
33
35
|
|
|
34
36
|
export default defineConfig({
|
|
35
37
|
entry: ['src/index.ts'],
|
|
36
38
|
plugins: [
|
|
37
|
-
|
|
39
|
+
StaleGuardRecorder(),
|
|
38
40
|
],
|
|
39
41
|
})
|
|
40
42
|
```
|
|
41
43
|
|
|
42
|
-
After building, a hash file will be generated at `node_modules/.cache/tsdown-stale-guard/hash.yaml`.
|
|
44
|
+
After building, a hash file will be generated at `node_modules/.cache/tsdown-stale-guard/hash.yaml`.
|
|
43
45
|
|
|
44
46
|
Example of the generated hash file:
|
|
45
47
|
|
|
@@ -65,7 +67,7 @@ outputs:
|
|
|
65
67
|
### Plugin Options
|
|
66
68
|
|
|
67
69
|
```ts
|
|
68
|
-
|
|
70
|
+
StaleGuardRecorder({
|
|
69
71
|
hashFile: 'node_modules/.cache/tsdown-stale-guard/hash.yaml', // hash file path (default)
|
|
70
72
|
root: process.cwd(), // root directory (default)
|
|
71
73
|
hashOutputs: true, // hash output files (default)
|
|
@@ -76,10 +78,10 @@ TsdownStaleGuard({
|
|
|
76
78
|
|
|
77
79
|
```bash
|
|
78
80
|
# Check if the build is up to date
|
|
79
|
-
tsdown-stale-guard
|
|
81
|
+
tsdown-stale-guard
|
|
80
82
|
|
|
81
83
|
# Use a custom hash file path
|
|
82
|
-
tsdown-stale-guard
|
|
84
|
+
tsdown-stale-guard --hash-file custom.hash.yaml
|
|
83
85
|
```
|
|
84
86
|
|
|
85
87
|
Exit code `0` if fresh, `1` if stale.
|
|
@@ -87,9 +89,9 @@ Exit code `0` if fresh, `1` if stale.
|
|
|
87
89
|
### Programmatic API
|
|
88
90
|
|
|
89
91
|
```ts
|
|
90
|
-
import {
|
|
92
|
+
import { checkBuildState } from 'tsdown-stale-guard'
|
|
91
93
|
|
|
92
|
-
const result = await
|
|
94
|
+
const result = await checkBuildState()
|
|
93
95
|
|
|
94
96
|
if (result.fresh) {
|
|
95
97
|
console.log('Build is up to date')
|
|
@@ -133,7 +135,7 @@ Package lock files (`pnpm-lock.yaml`, `yarn.lock`, `package-lock.json`, `bun.loc
|
|
|
133
135
|
[npm-downloads-href]: https://npmjs.com/package/tsdown-stale-guard
|
|
134
136
|
[bundle-src]: https://img.shields.io/bundlephobia/minzip/tsdown-stale-guard?style=flat&colorA=080f12&colorB=1fa669&label=minzip
|
|
135
137
|
[bundle-href]: https://bundlephobia.com/result?p=tsdown-stale-guard
|
|
136
|
-
[license-src]: https://img.shields.io/github/license/antfu/tsdown-stale-guard.svg?style=flat&colorA=080f12&colorB=1fa669
|
|
137
|
-
[license-href]: https://github.com/antfu/tsdown-stale-guard/blob/main/LICENSE
|
|
138
|
+
[license-src]: https://img.shields.io/github/license/antfu-collective/tsdown-stale-guard.svg?style=flat&colorA=080f12&colorB=1fa669
|
|
139
|
+
[license-href]: https://github.com/antfu-collective/tsdown-stale-guard/blob/main/LICENSE
|
|
138
140
|
[jsdocs-src]: https://img.shields.io/badge/jsdocs-reference-080f12?style=flat&colorA=080f12&colorB=1fa669
|
|
139
141
|
[jsdocs-href]: https://www.jsdocs.io/package/tsdown-stale-guard
|
|
@@ -34,7 +34,7 @@ function serializeHashFile(data) {
|
|
|
34
34
|
version: data.version,
|
|
35
35
|
hash: data.hash
|
|
36
36
|
};
|
|
37
|
-
if (data.
|
|
37
|
+
if (data.configs) obj.configs = Object.fromEntries(data.configs.map((e) => [e.file, e.hash]));
|
|
38
38
|
if (data.lockfile) obj.lockfile = { [data.lockfile.file]: data.lockfile.hash };
|
|
39
39
|
if (data.sources.length > 0) obj.sources = Object.fromEntries(data.sources.map((e) => [e.file, e.hash]));
|
|
40
40
|
if (data.outputs.length > 0) obj.outputs = Object.fromEntries(data.outputs.map((e) => [e.file, e.hash]));
|
|
@@ -45,7 +45,7 @@ function parseHashFile(content) {
|
|
|
45
45
|
return {
|
|
46
46
|
version: obj.version,
|
|
47
47
|
hash: obj.hash,
|
|
48
|
-
|
|
48
|
+
configs: parseSectionList(obj.configs),
|
|
49
49
|
lockfile: parseSection(obj.lockfile),
|
|
50
50
|
sources: parseSectionList(obj.sources),
|
|
51
51
|
outputs: parseSectionList(obj.outputs)
|
|
@@ -78,22 +78,24 @@ async function writeHashFile(path, data) {
|
|
|
78
78
|
//#endregion
|
|
79
79
|
//#region src/check.ts
|
|
80
80
|
const DEFAULT_HASH_FILE = "node_modules/.cache/tsdown-stale-guard/hash.yaml";
|
|
81
|
-
async function
|
|
81
|
+
async function checkBuildState(options = {}) {
|
|
82
82
|
const root = options.root || process.cwd();
|
|
83
83
|
const data = await readHashFile(resolve(root, options.hashFile || DEFAULT_HASH_FILE));
|
|
84
84
|
const changes = [];
|
|
85
|
-
if (data.
|
|
85
|
+
if (data.configs) for (const entry of data.configs) await checkEntry(entry, "config", root, changes);
|
|
86
86
|
if (data.lockfile) await checkEntry(data.lockfile, "lockfile", root, changes);
|
|
87
87
|
for (const entry of data.sources) await checkEntry(entry, "source", root, changes);
|
|
88
88
|
for (const entry of data.outputs) await checkEntry(entry, "output", root, changes);
|
|
89
89
|
const currentHash = computeCompositeHash([
|
|
90
90
|
...data.sources,
|
|
91
91
|
...data.outputs,
|
|
92
|
-
...data.
|
|
92
|
+
...data.configs || [],
|
|
93
93
|
...data.lockfile ? [data.lockfile] : []
|
|
94
94
|
]);
|
|
95
|
+
const fresh = changes.length === 0 && currentHash === data.hash;
|
|
95
96
|
return {
|
|
96
|
-
fresh
|
|
97
|
+
fresh,
|
|
98
|
+
stale: !fresh,
|
|
97
99
|
changes
|
|
98
100
|
};
|
|
99
101
|
}
|
|
@@ -114,4 +116,4 @@ async function checkEntry(entry, category, root, changes) {
|
|
|
114
116
|
});
|
|
115
117
|
}
|
|
116
118
|
//#endregion
|
|
117
|
-
export { writeHashFile as a, hashFiles as c, serializeHashFile as i, parseHashFile as n, computeCompositeHash as o, readHashFile as r, hashFile as s,
|
|
119
|
+
export { writeHashFile as a, hashFiles as c, serializeHashFile as i, parseHashFile as n, computeCompositeHash as o, readHashFile as r, hashFile as s, checkBuildState as t };
|
package/dist/cli.mjs
CHANGED
|
@@ -1,12 +1,11 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import { t as
|
|
2
|
+
import { t as checkBuildState } from "./check-CWbzg4YR.mjs";
|
|
3
3
|
import process from "node:process";
|
|
4
|
+
import cac from "cac";
|
|
4
5
|
//#region src/cli.ts
|
|
5
|
-
const
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
const hashFileIdx = args.indexOf("--hash-file");
|
|
9
|
-
const result = await checkBuildFreshness({ hashFile: hashFileIdx !== -1 ? args[hashFileIdx + 1] : void 0 });
|
|
6
|
+
const cli = cac("tsdown-stale-guard");
|
|
7
|
+
cli.command("[...args]", "Check if the build is up to date").option("--hash-file <path>", "Path to the hash file").action(async (_args, options) => {
|
|
8
|
+
const result = await checkBuildState({ hashFile: options.hashFile });
|
|
10
9
|
if (result.fresh) {
|
|
11
10
|
console.log("Build is up to date.");
|
|
12
11
|
process.exit(0);
|
|
@@ -15,17 +14,8 @@ if (command === "check") {
|
|
|
15
14
|
for (const change of result.changes) console.log(` ${change.type}: [${change.category}] ${change.file}`);
|
|
16
15
|
process.exit(1);
|
|
17
16
|
}
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
Usage:
|
|
22
|
-
tsdown-stale-guard check Check if the build is up to date
|
|
23
|
-
tsdown-stale-guard check --hash-file <path> Use a custom hash file path
|
|
24
|
-
|
|
25
|
-
Options:
|
|
26
|
-
--hash-file <path> Path to the hash file (default: node_modules/.cache/tsdown-stale-guard/hash.yaml)
|
|
27
|
-
--help Show this help message`);
|
|
28
|
-
process.exit(command === "--help" || command === "-h" ? 0 : 1);
|
|
29
|
-
}
|
|
17
|
+
});
|
|
18
|
+
cli.help();
|
|
19
|
+
cli.parse();
|
|
30
20
|
//#endregion
|
|
31
21
|
export {};
|
package/dist/index.d.mts
CHANGED
|
@@ -1,20 +1,21 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { TsdownPlugin } from "tsdown";
|
|
2
2
|
|
|
3
3
|
//#region src/types.d.ts
|
|
4
|
-
interface
|
|
4
|
+
interface StaleGuardEntry {
|
|
5
5
|
file: string;
|
|
6
6
|
hash: string;
|
|
7
7
|
}
|
|
8
|
-
interface
|
|
8
|
+
interface StaleGuardData {
|
|
9
9
|
version: 1;
|
|
10
10
|
hash: string;
|
|
11
|
-
|
|
12
|
-
lockfile?:
|
|
13
|
-
sources:
|
|
14
|
-
outputs:
|
|
11
|
+
configs?: StaleGuardEntry[];
|
|
12
|
+
lockfile?: StaleGuardEntry;
|
|
13
|
+
sources: StaleGuardEntry[];
|
|
14
|
+
outputs: StaleGuardEntry[];
|
|
15
15
|
}
|
|
16
16
|
interface CheckResult {
|
|
17
17
|
fresh: boolean;
|
|
18
|
+
stale: boolean;
|
|
18
19
|
changes: CheckChange[];
|
|
19
20
|
}
|
|
20
21
|
interface CheckChange {
|
|
@@ -22,7 +23,7 @@ interface CheckChange {
|
|
|
22
23
|
category: 'config' | 'lockfile' | 'source' | 'output';
|
|
23
24
|
file: string;
|
|
24
25
|
}
|
|
25
|
-
interface
|
|
26
|
+
interface StaleGuardRecorderOptions {
|
|
26
27
|
/**
|
|
27
28
|
* Path to the hash file.
|
|
28
29
|
* @default 'node_modules/.cache/tsdown-stale-guard/hash.yaml'
|
|
@@ -53,14 +54,14 @@ interface CheckOptions {
|
|
|
53
54
|
}
|
|
54
55
|
//#endregion
|
|
55
56
|
//#region src/check.d.ts
|
|
56
|
-
declare function
|
|
57
|
+
declare function checkBuildState(options?: CheckOptions): Promise<CheckResult>;
|
|
57
58
|
//#endregion
|
|
58
59
|
//#region src/lockfile.d.ts
|
|
59
|
-
declare function serializeHashFile(data:
|
|
60
|
-
declare function parseHashFile(content: string):
|
|
61
|
-
declare function readHashFile(path: string): Promise<
|
|
60
|
+
declare function serializeHashFile(data: StaleGuardData): string;
|
|
61
|
+
declare function parseHashFile(content: string): StaleGuardData;
|
|
62
|
+
declare function readHashFile(path: string): Promise<StaleGuardData>;
|
|
62
63
|
//#endregion
|
|
63
64
|
//#region src/plugin.d.ts
|
|
64
|
-
declare function
|
|
65
|
+
declare function StaleGuardRecorder(options?: StaleGuardRecorderOptions): TsdownPlugin;
|
|
65
66
|
//#endregion
|
|
66
|
-
export { type CheckChange, type CheckOptions, type CheckResult,
|
|
67
|
+
export { type CheckChange, type CheckOptions, type CheckResult, type StaleGuardData, type StaleGuardEntry, StaleGuardRecorder, type StaleGuardRecorderOptions, checkBuildState, parseHashFile, readHashFile, serializeHashFile };
|
package/dist/index.mjs
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
1
|
-
import { a as writeHashFile, c as hashFiles, i as serializeHashFile, n as parseHashFile, o as computeCompositeHash, r as readHashFile, s as hashFile, t as
|
|
1
|
+
import { a as writeHashFile, c as hashFiles, i as serializeHashFile, n as parseHashFile, o as computeCompositeHash, r as readHashFile, s as hashFile, t as checkBuildState } from "./check-CWbzg4YR.mjs";
|
|
2
2
|
import { existsSync } from "node:fs";
|
|
3
3
|
import { dirname, join, relative, resolve } from "node:path";
|
|
4
|
-
import process from "node:process";
|
|
5
4
|
import { readdir } from "node:fs/promises";
|
|
6
5
|
//#region src/detect.ts
|
|
7
6
|
const PACKAGE_LOCK_FILES = [
|
|
@@ -11,15 +10,6 @@ const PACKAGE_LOCK_FILES = [
|
|
|
11
10
|
"bun.lockb",
|
|
12
11
|
"bun.lock"
|
|
13
12
|
];
|
|
14
|
-
const TSDOWN_CONFIG_FILES = [
|
|
15
|
-
"tsdown.config.ts",
|
|
16
|
-
"tsdown.config.mts",
|
|
17
|
-
"tsdown.config.cts",
|
|
18
|
-
"tsdown.config.js",
|
|
19
|
-
"tsdown.config.mjs",
|
|
20
|
-
"tsdown.config.cjs",
|
|
21
|
-
"tsdown.config.json"
|
|
22
|
-
];
|
|
23
13
|
function findUp(names, cwd) {
|
|
24
14
|
let dir = resolve(cwd);
|
|
25
15
|
while (true) {
|
|
@@ -35,23 +25,26 @@ function findUp(names, cwd) {
|
|
|
35
25
|
function detectPackageLock(cwd) {
|
|
36
26
|
return findUp(PACKAGE_LOCK_FILES, cwd);
|
|
37
27
|
}
|
|
38
|
-
function detectTsdownConfig(cwd) {
|
|
39
|
-
return findUp(TSDOWN_CONFIG_FILES, cwd);
|
|
40
|
-
}
|
|
41
28
|
//#endregion
|
|
42
29
|
//#region src/plugin.ts
|
|
43
30
|
const RE_QUERY = /\?.*$/;
|
|
44
31
|
const RE_WINDOWS_DRIVE = /^[a-z]:\\/i;
|
|
45
32
|
const RE_NODE_MODULES = /node_modules/;
|
|
46
33
|
const DEFAULT_HASH_FILE = "node_modules/.cache/tsdown-stale-guard/hash.yaml";
|
|
47
|
-
function
|
|
34
|
+
function StaleGuardRecorder(options = {}) {
|
|
48
35
|
const { hashFile: hashFilePath = DEFAULT_HASH_FILE, hashOutputs = true } = options;
|
|
49
36
|
const sourceIds = /* @__PURE__ */ new Set();
|
|
50
37
|
let root;
|
|
38
|
+
let configDeps;
|
|
39
|
+
let outDir;
|
|
40
|
+
let isTsdownConfigResolvedCalled = false;
|
|
51
41
|
return {
|
|
52
42
|
name: "tsdown-stale-guard",
|
|
53
|
-
|
|
54
|
-
|
|
43
|
+
tsdownConfigResolved(config) {
|
|
44
|
+
isTsdownConfigResolvedCalled = true;
|
|
45
|
+
root = options.root || config.cwd;
|
|
46
|
+
configDeps = config.configDeps;
|
|
47
|
+
outDir = config.outDir;
|
|
55
48
|
},
|
|
56
49
|
transform: {
|
|
57
50
|
filter: { id: { exclude: [RE_NODE_MODULES] } },
|
|
@@ -60,8 +53,8 @@ function TsdownStaleGuard(options = {}) {
|
|
|
60
53
|
if (cleanId.startsWith("/") || RE_WINDOWS_DRIVE.test(cleanId)) sourceIds.add(cleanId);
|
|
61
54
|
}
|
|
62
55
|
},
|
|
63
|
-
async writeBundle(
|
|
64
|
-
|
|
56
|
+
async writeBundle() {
|
|
57
|
+
if (!isTsdownConfigResolvedCalled) throw new Error("tsdownConfigResolved is not being called correctly. `tsdown-stale-guard` requires `tsdown@0.21.9` or later, please check your tsdown version.");
|
|
65
58
|
const resolvedHashFile = resolve(root, hashFilePath);
|
|
66
59
|
const sources = await hashFiles([...sourceIds].filter((f) => existsSync(f)), root);
|
|
67
60
|
let outputs = [];
|
|
@@ -74,14 +67,14 @@ function TsdownStaleGuard(options = {}) {
|
|
|
74
67
|
for (const file of files) if (file.isFile()) outputPaths.push(resolve(file.parentPath, file.name));
|
|
75
68
|
outputs = await hashFiles(outputPaths, root);
|
|
76
69
|
}
|
|
77
|
-
|
|
78
|
-
const
|
|
79
|
-
|
|
80
|
-
const
|
|
81
|
-
|
|
82
|
-
file
|
|
70
|
+
const configs = [];
|
|
71
|
+
if (configDeps) for (const dep of configDeps) {
|
|
72
|
+
const hash = await hashFile(dep);
|
|
73
|
+
const file = toForwardSlash(relative(root, dep));
|
|
74
|
+
configs.push({
|
|
75
|
+
file,
|
|
83
76
|
hash
|
|
84
|
-
};
|
|
77
|
+
});
|
|
85
78
|
}
|
|
86
79
|
let lockfileEntry;
|
|
87
80
|
const lockfilePath = detectPackageLock(root);
|
|
@@ -97,10 +90,10 @@ function TsdownStaleGuard(options = {}) {
|
|
|
97
90
|
hash: computeCompositeHash([
|
|
98
91
|
...sources,
|
|
99
92
|
...outputs,
|
|
100
|
-
...
|
|
93
|
+
...configs,
|
|
101
94
|
...lockfileEntry ? [lockfileEntry] : []
|
|
102
95
|
]),
|
|
103
|
-
|
|
96
|
+
configs,
|
|
104
97
|
lockfile: lockfileEntry,
|
|
105
98
|
sources,
|
|
106
99
|
outputs
|
|
@@ -112,4 +105,4 @@ function toForwardSlash(p) {
|
|
|
112
105
|
return p.replace(/\\/g, "/");
|
|
113
106
|
}
|
|
114
107
|
//#endregion
|
|
115
|
-
export {
|
|
108
|
+
export { StaleGuardRecorder, checkBuildState, parseHashFile, readHashFile, serializeHashFile };
|
package/package.json
CHANGED
|
@@ -1,17 +1,17 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "tsdown-stale-guard",
|
|
3
3
|
"type": "module",
|
|
4
|
-
"version": "0.
|
|
4
|
+
"version": "0.1.0",
|
|
5
5
|
"description": "Build freshness validation for tsdown",
|
|
6
6
|
"author": "Anthony Fu <anthonyfu117@hotmail.com>",
|
|
7
7
|
"license": "MIT",
|
|
8
8
|
"funding": "https://github.com/sponsors/antfu",
|
|
9
|
-
"homepage": "https://github.com/antfu/tsdown-stale-guard#readme",
|
|
9
|
+
"homepage": "https://github.com/antfu-collective/tsdown-stale-guard#readme",
|
|
10
10
|
"repository": {
|
|
11
11
|
"type": "git",
|
|
12
|
-
"url": "git+https://github.com/antfu/tsdown-stale-guard.git"
|
|
12
|
+
"url": "git+https://github.com/antfu-collective/tsdown-stale-guard.git"
|
|
13
13
|
},
|
|
14
|
-
"bugs": "https://github.com/antfu/tsdown-stale-guard/issues",
|
|
14
|
+
"bugs": "https://github.com/antfu-collective/tsdown-stale-guard/issues",
|
|
15
15
|
"keywords": [
|
|
16
16
|
"tsdown",
|
|
17
17
|
"build",
|
|
@@ -32,7 +32,11 @@
|
|
|
32
32
|
"files": [
|
|
33
33
|
"dist"
|
|
34
34
|
],
|
|
35
|
+
"peerDependencies": {
|
|
36
|
+
"tsdown": "^0.21.9"
|
|
37
|
+
},
|
|
35
38
|
"dependencies": {
|
|
39
|
+
"cac": "^7.0.0",
|
|
36
40
|
"yaml": "^2.8.3"
|
|
37
41
|
},
|
|
38
42
|
"devDependencies": {
|
|
@@ -44,10 +48,9 @@
|
|
|
44
48
|
"eslint": "^10.2.0",
|
|
45
49
|
"lint-staged": "^16.4.0",
|
|
46
50
|
"publint": "^0.3.18",
|
|
47
|
-
"rolldown": "1.0.0-rc.15",
|
|
48
51
|
"simple-git-hooks": "^2.13.1",
|
|
49
|
-
"tsdown": "^0.21.
|
|
50
|
-
"tsnapi": "^0.
|
|
52
|
+
"tsdown": "^0.21.9",
|
|
53
|
+
"tsnapi": "^0.3.0",
|
|
51
54
|
"tsx": "^4.21.0",
|
|
52
55
|
"typescript": "^6.0.2",
|
|
53
56
|
"vite": "^8.0.8",
|