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 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 check` for CI pipelines
17
- - **Programmatic API** — `checkBuildFreshness()` for tool integrations
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 { TsdownStaleGuard } from 'tsdown-stale-guard'
34
+ import { StaleGuardRecorder } from 'tsdown-stale-guard'
33
35
 
34
36
  export default defineConfig({
35
37
  entry: ['src/index.ts'],
36
38
  plugins: [
37
- TsdownStaleGuard(),
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`. Since it lives inside `node_modules`, it does not need to be gitignored.
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
- TsdownStaleGuard({
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 check
81
+ tsdown-stale-guard
80
82
 
81
83
  # Use a custom hash file path
82
- tsdown-stale-guard check --hash-file custom.hash.yaml
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 { checkBuildFreshness } from 'tsdown-stale-guard'
92
+ import { checkBuildState } from 'tsdown-stale-guard'
91
93
 
92
- const result = await checkBuildFreshness()
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.config) obj.config = { [data.config.file]: data.config.hash };
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
- config: parseSection(obj.config),
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 checkBuildFreshness(options = {}) {
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.config) await checkEntry(data.config, "config", root, changes);
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.config ? [data.config] : [],
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: changes.length === 0 && currentHash === data.hash,
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, checkBuildFreshness as t };
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 checkBuildFreshness } from "./check-YFLy7mcd.mjs";
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 args = process.argv.slice(2);
6
- const command = args[0];
7
- if (command === "check") {
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
- } else {
19
- console.log(`tsdown-stale-guard - Build freshness validation for tsdown
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 { Plugin } from "rolldown";
1
+ import { TsdownPlugin } from "tsdown";
2
2
 
3
3
  //#region src/types.d.ts
4
- interface TsdownStaleGuardEntry {
4
+ interface StaleGuardEntry {
5
5
  file: string;
6
6
  hash: string;
7
7
  }
8
- interface TsdownStaleGuardData {
8
+ interface StaleGuardData {
9
9
  version: 1;
10
10
  hash: string;
11
- config?: TsdownStaleGuardEntry;
12
- lockfile?: TsdownStaleGuardEntry;
13
- sources: TsdownStaleGuardEntry[];
14
- outputs: TsdownStaleGuardEntry[];
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 TsdownStaleGuardPluginOptions {
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 checkBuildFreshness(options?: CheckOptions): Promise<CheckResult>;
57
+ declare function checkBuildState(options?: CheckOptions): Promise<CheckResult>;
57
58
  //#endregion
58
59
  //#region src/lockfile.d.ts
59
- declare function serializeHashFile(data: TsdownStaleGuardData): string;
60
- declare function parseHashFile(content: string): TsdownStaleGuardData;
61
- declare function readHashFile(path: string): Promise<TsdownStaleGuardData>;
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 TsdownStaleGuard(options?: TsdownStaleGuardPluginOptions): Plugin;
65
+ declare function StaleGuardRecorder(options?: StaleGuardRecorderOptions): TsdownPlugin;
65
66
  //#endregion
66
- export { type CheckChange, type CheckOptions, type CheckResult, TsdownStaleGuard, type TsdownStaleGuardData, type TsdownStaleGuardEntry, type TsdownStaleGuardPluginOptions, checkBuildFreshness, parseHashFile, readHashFile, serializeHashFile };
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 checkBuildFreshness } from "./check-YFLy7mcd.mjs";
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 TsdownStaleGuard(options = {}) {
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
- buildStart() {
54
- root = options.root || process.cwd();
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(opts) {
64
- const outDir = opts.dir || resolve(root, "dist");
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
- let config;
78
- const configPath = detectTsdownConfig(root);
79
- if (configPath) {
80
- const hash = await hashFile(configPath);
81
- config = {
82
- file: toForwardSlash(relative(root, configPath)),
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
- ...config ? [config] : [],
93
+ ...configs,
101
94
  ...lockfileEntry ? [lockfileEntry] : []
102
95
  ]),
103
- config,
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 { TsdownStaleGuard, checkBuildFreshness, parseHashFile, readHashFile, serializeHashFile };
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.0.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.8",
50
- "tsnapi": "^0.2.1",
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",