cache-cmd 0.2.0-dev.c487faf7d6de493f66be37b5874e9e8efea51c76 → 0.3.0-dev.57b573000c4461fbe9a6a0d0537bba295407310b
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 +19 -17
- package/dist/cli.js +241 -0
- package/dist/cli.js.map +7 -0
- package/package.json +31 -24
- package/src/cache-clear.ts +2 -2
- package/src/run.ts +36 -14
- package/src/utils/get-cache-dir.ts +0 -17
- package/src/utils/get-file-hashes.ts +4 -6
- package/dist/cli.cjs +0 -262
- package/dist/cli.cjs.map +0 -1
package/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# cache-cmd
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Run and cache a command based on
|
|
4
4
|
|
|
5
5
|
- time since last run
|
|
6
6
|
- file change
|
|
@@ -8,38 +8,40 @@ Cache a command based on
|
|
|
8
8
|
## Install
|
|
9
9
|
|
|
10
10
|
```sh
|
|
11
|
+
# Using npm
|
|
12
|
+
npm add --save-dev cache-cmd
|
|
13
|
+
# Using pnpm
|
|
14
|
+
pnpm add --save-dev cache-cmd
|
|
15
|
+
# Using yarn
|
|
11
16
|
yarn add --dev cache-cmd
|
|
12
17
|
```
|
|
13
18
|
|
|
14
|
-
or
|
|
15
|
-
|
|
16
|
-
```sh
|
|
17
|
-
npm install --save-dev cache-cmd
|
|
18
|
-
```
|
|
19
|
-
|
|
20
19
|
## Usage
|
|
21
20
|
|
|
22
21
|
```sh
|
|
23
22
|
# Shows help
|
|
24
|
-
|
|
23
|
+
npm exec -- cache-cmd --help
|
|
25
24
|
|
|
26
25
|
# Runs command if it was not run in the last 20s
|
|
27
|
-
|
|
26
|
+
npm exec -- cache-cmd "echo ran this command" --time 20s
|
|
28
27
|
|
|
29
|
-
# Runs
|
|
30
|
-
|
|
28
|
+
# Runs command if package-lock.json in current directory changed since last run
|
|
29
|
+
npm exec -- cache-cmd "npm install" --file package-lock.json
|
|
31
30
|
|
|
32
31
|
# Additionally uses custom cache directory instead of default in node_modules
|
|
33
|
-
|
|
32
|
+
npm exec -- cache-cmd "npm install" --file package-lock.json --cache-dir .config/cache
|
|
34
33
|
|
|
35
34
|
# Runs command if it was not run in a month or any of the files changed
|
|
36
|
-
|
|
35
|
+
npm exec -- cache-cmd "npm install" --time 1mo --file package-lock.json --file package.json
|
|
37
36
|
|
|
38
37
|
# Shows path to cache directory
|
|
39
|
-
|
|
38
|
+
npm exec -- cache-cmd cache dir
|
|
40
39
|
|
|
41
40
|
# Clear cache
|
|
42
|
-
|
|
41
|
+
npm exec -- cache-cmd cache clear
|
|
42
|
+
|
|
43
|
+
# You can also run it with npx to skip the install step
|
|
44
|
+
npx cache-cmd "echo ran this command" --time 20s
|
|
43
45
|
```
|
|
44
46
|
|
|
45
47
|
You can use it to execute commands conditionally in `package.json` scripts.
|
|
@@ -47,11 +49,11 @@ You can use it to execute commands conditionally in `package.json` scripts.
|
|
|
47
49
|
```json
|
|
48
50
|
{
|
|
49
51
|
"scripts": {
|
|
50
|
-
"dev": "cache-cmd \"
|
|
52
|
+
"start-dev": "cache-cmd \"npm install\" --file package-lock.json && start-dev-server"
|
|
51
53
|
}
|
|
52
54
|
}
|
|
53
55
|
```
|
|
54
56
|
|
|
55
57
|
## Contribute
|
|
56
58
|
|
|
57
|
-
If you find a bug or something you don't like, please [submit an issue](https://github.com/dcastil/
|
|
59
|
+
If you find a bug or something you don't like, please [submit an issue](https://github.com/dcastil/cache-cmd/issues/new) or a pull request. I'm happy about any kind of feedback!
|
package/dist/cli.js
ADDED
|
@@ -0,0 +1,241 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
// src/cli.ts
|
|
4
|
+
import hardRejection from "hard-rejection";
|
|
5
|
+
import sade from "sade";
|
|
6
|
+
|
|
7
|
+
// package.json
|
|
8
|
+
var name = "cache-cmd";
|
|
9
|
+
var version = "0.3.0";
|
|
10
|
+
|
|
11
|
+
// src/cache-clear.ts
|
|
12
|
+
import { deleteSync } from "del";
|
|
13
|
+
|
|
14
|
+
// src/utils/get-cache-dir.ts
|
|
15
|
+
import path from "path";
|
|
16
|
+
import findCacheDir from "find-cache-dir";
|
|
17
|
+
function getCacheDirectoryPath(relativeCacheDirectoryPath) {
|
|
18
|
+
if (relativeCacheDirectoryPath) {
|
|
19
|
+
return path.resolve(process.cwd(), relativeCacheDirectoryPath);
|
|
20
|
+
}
|
|
21
|
+
const resolvedCacheDirectory = findCacheDir({ name });
|
|
22
|
+
if (!resolvedCacheDirectory) {
|
|
23
|
+
throw Error("Could not find cache directory. Please provide a cache directory manually.");
|
|
24
|
+
}
|
|
25
|
+
return resolvedCacheDirectory;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
// src/cache-clear.ts
|
|
29
|
+
function clearCacheDirectory(relativeCacheDirectory) {
|
|
30
|
+
const deletedPaths = deleteSync(getCacheDirectoryPath(relativeCacheDirectory));
|
|
31
|
+
if (deletedPaths.length === 0) {
|
|
32
|
+
console.log("No cache to clear");
|
|
33
|
+
} else if (deletedPaths.length === 1) {
|
|
34
|
+
console.log(`Deleted: ${deletedPaths[0]}`);
|
|
35
|
+
} else {
|
|
36
|
+
console.log("Deleted:\n", deletedPaths.join("\n"));
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
// src/cache-dir.ts
|
|
41
|
+
function showCacheDirectory(relativeCacheDirectory) {
|
|
42
|
+
console.log(getCacheDirectoryPath(relativeCacheDirectory));
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
// src/run.ts
|
|
46
|
+
import { add, isAfter } from "date-fns";
|
|
47
|
+
import execSh from "exec-sh";
|
|
48
|
+
import { create } from "flat-cache";
|
|
49
|
+
import { isEqual } from "lodash-es";
|
|
50
|
+
|
|
51
|
+
// src/utils/get-cache-key.ts
|
|
52
|
+
function getCacheKey({ duration, filePaths, command }) {
|
|
53
|
+
return [
|
|
54
|
+
"cwd:" + process.cwd(),
|
|
55
|
+
"cmd:" + command,
|
|
56
|
+
duration && "time:" + JSON.stringify(duration),
|
|
57
|
+
filePaths.length !== 0 && "files:" + filePaths.join(",")
|
|
58
|
+
].filter(Boolean).join(" ---");
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
// src/utils/get-duration.ts
|
|
62
|
+
function getDuration(durationString) {
|
|
63
|
+
if (!/^(\d+[a-z]+)+$/gi.test(durationString)) {
|
|
64
|
+
throw Error(`Invalid duration: ${durationString}`);
|
|
65
|
+
}
|
|
66
|
+
const duration = {
|
|
67
|
+
years: void 0,
|
|
68
|
+
months: void 0,
|
|
69
|
+
weeks: void 0,
|
|
70
|
+
days: void 0,
|
|
71
|
+
hours: void 0,
|
|
72
|
+
minutes: void 0,
|
|
73
|
+
seconds: void 0
|
|
74
|
+
};
|
|
75
|
+
durationString.match(/\d+[a-z]+/gi).forEach((durationPart) => {
|
|
76
|
+
const [, stringQuantity, unitShort] = /(\d+)([a-z]+)/gi.exec(durationPart);
|
|
77
|
+
const quantity = Number(stringQuantity);
|
|
78
|
+
const unitLong = {
|
|
79
|
+
y: "years",
|
|
80
|
+
mo: "months",
|
|
81
|
+
w: "weeks",
|
|
82
|
+
d: "days",
|
|
83
|
+
h: "hours",
|
|
84
|
+
m: "minutes",
|
|
85
|
+
s: "seconds"
|
|
86
|
+
}[unitShort];
|
|
87
|
+
if (Number.isNaN(quantity) || !unitLong) {
|
|
88
|
+
throw Error(`Invalid duration part: ${durationPart}`);
|
|
89
|
+
}
|
|
90
|
+
if (duration[unitLong] !== void 0) {
|
|
91
|
+
throw Error(`Duration with unit ${unitLong} was supplied multiple times`);
|
|
92
|
+
}
|
|
93
|
+
duration[unitLong] = quantity;
|
|
94
|
+
});
|
|
95
|
+
return duration;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
// src/utils/get-file-hashes.ts
|
|
99
|
+
import { hashFile } from "hasha";
|
|
100
|
+
async function getFileHashes(filePaths) {
|
|
101
|
+
return Object.fromEntries(
|
|
102
|
+
await Promise.all(
|
|
103
|
+
filePaths.map(
|
|
104
|
+
(filePath) => hashFile(filePath, { algorithm: "md5" }).then((hash) => [filePath, hash])
|
|
105
|
+
)
|
|
106
|
+
)
|
|
107
|
+
);
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
// src/utils/get-file-paths.ts
|
|
111
|
+
import path2 from "path";
|
|
112
|
+
function getFilePaths(relativeFilePaths) {
|
|
113
|
+
const cwd = process.cwd();
|
|
114
|
+
return relativeFilePaths.map((file) => path2.resolve(cwd, file)).sort();
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
// src/run.ts
|
|
118
|
+
async function runCommand({
|
|
119
|
+
relativeCacheDirectory,
|
|
120
|
+
command,
|
|
121
|
+
cacheByTime,
|
|
122
|
+
cacheByFiles,
|
|
123
|
+
shouldCacheOnError
|
|
124
|
+
}) {
|
|
125
|
+
if (!cacheByTime && cacheByFiles.length === 0) {
|
|
126
|
+
console.log(`Executing command "${command}" due to no caching options provided`);
|
|
127
|
+
await execSh.promise(command);
|
|
128
|
+
return;
|
|
129
|
+
}
|
|
130
|
+
const cache = create({
|
|
131
|
+
cacheId: "commands-cache.json",
|
|
132
|
+
cacheDir: getCacheDirectoryPath(relativeCacheDirectory),
|
|
133
|
+
lruSize: 1e4
|
|
134
|
+
});
|
|
135
|
+
const filePaths = getFilePaths(cacheByFiles);
|
|
136
|
+
const duration = cacheByTime ? getDuration(cacheByTime) : void 0;
|
|
137
|
+
const cacheKey = getCacheKey({ duration, filePaths, command });
|
|
138
|
+
const cacheData = cache.getKey(cacheKey);
|
|
139
|
+
const fileHashes = filePaths.length === 0 ? void 0 : await getFileHashes(filePaths);
|
|
140
|
+
const currentDate = /* @__PURE__ */ new Date();
|
|
141
|
+
const areFileHashesEqual = isEqual(cacheData?.fileHashes, fileHashes);
|
|
142
|
+
const isWithinCacheTime = (() => {
|
|
143
|
+
if (!duration) {
|
|
144
|
+
return true;
|
|
145
|
+
}
|
|
146
|
+
const lastRun = cacheData?.lastRun;
|
|
147
|
+
if (!lastRun) {
|
|
148
|
+
return false;
|
|
149
|
+
}
|
|
150
|
+
return isAfter(add(new Date(lastRun), duration), currentDate);
|
|
151
|
+
})();
|
|
152
|
+
if (areFileHashesEqual && isWithinCacheTime) {
|
|
153
|
+
console.log(
|
|
154
|
+
[
|
|
155
|
+
`Skipping command "${command}" due to`,
|
|
156
|
+
[fileHashes && "unchanged files", duration && "being within cache time"].filter(Boolean).join(" and ")
|
|
157
|
+
].join(" ")
|
|
158
|
+
);
|
|
159
|
+
return;
|
|
160
|
+
}
|
|
161
|
+
console.log(
|
|
162
|
+
[
|
|
163
|
+
`Executing command "${command}" due to`,
|
|
164
|
+
[fileHashes && "changed files", duration && "cache time passing"].filter(Boolean).join(" and ")
|
|
165
|
+
].join(" ")
|
|
166
|
+
);
|
|
167
|
+
let execPromise = execSh.promise(command);
|
|
168
|
+
function setCache() {
|
|
169
|
+
cache.setKey(cacheKey, {
|
|
170
|
+
lastRun: currentDate,
|
|
171
|
+
fileHashes
|
|
172
|
+
});
|
|
173
|
+
cache.save(true);
|
|
174
|
+
console.log(`Cache saved for command "${command}"`);
|
|
175
|
+
}
|
|
176
|
+
if (shouldCacheOnError) {
|
|
177
|
+
execPromise = execPromise.catch((error) => {
|
|
178
|
+
setCache();
|
|
179
|
+
throw error;
|
|
180
|
+
});
|
|
181
|
+
}
|
|
182
|
+
await execPromise;
|
|
183
|
+
setCache();
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
// src/cli.ts
|
|
187
|
+
hardRejection();
|
|
188
|
+
var program = sade("cache-cmd");
|
|
189
|
+
program.version(version).describe("Run and cache a command based on various factors").option(
|
|
190
|
+
"-c, --cache-dir",
|
|
191
|
+
"Cache directory to use (default: .cache/cache-cmd in nearest node_modules)"
|
|
192
|
+
);
|
|
193
|
+
program.command(
|
|
194
|
+
"run <command>",
|
|
195
|
+
"Run cached command (if no <command> provided, this is the default)",
|
|
196
|
+
{ default: true }
|
|
197
|
+
).option("-f, --file", "Run command only when file content changes").option("-t, --time", "Run command only after specified time (unit with s,m,h,d,w,mo,y)").option("--cache-on-error", "Cache command run even when command exits with non-zero exit code").example('run "echo ran this command" --time 20s').example('run "./may-fail" --time 20s --cache-on-error').example('run "yarn install" --file yarn.lock').example('run "yarn install" --file yarn.lock --cache-dir .config/cache').example('run "yarn install" --time 1mo --file yarn.lock --file package.json').action((command, options) => {
|
|
198
|
+
const cacheDirectory = options["cache-dir"];
|
|
199
|
+
const time = options.time;
|
|
200
|
+
const file = options.file;
|
|
201
|
+
const shouldCacheOnError = options["cache-on-error"];
|
|
202
|
+
const files = file === void 0 ? [] : Array.isArray(file) ? file : [file];
|
|
203
|
+
if (typeof command !== "string") {
|
|
204
|
+
throw Error("Invalid <command> supplied");
|
|
205
|
+
}
|
|
206
|
+
if (cacheDirectory !== void 0 && typeof cacheDirectory !== "string") {
|
|
207
|
+
throw Error("Invalid --cache-dir supplied");
|
|
208
|
+
}
|
|
209
|
+
if (time !== void 0 && typeof time !== "string") {
|
|
210
|
+
throw Error("Invalid --time supplied");
|
|
211
|
+
}
|
|
212
|
+
if (files.some((file2) => typeof file2 !== "string")) {
|
|
213
|
+
throw Error("Invalid --file supplied");
|
|
214
|
+
}
|
|
215
|
+
if (shouldCacheOnError !== void 0 && typeof shouldCacheOnError !== "boolean") {
|
|
216
|
+
throw Error("Invalid --cache-on-error supplied");
|
|
217
|
+
}
|
|
218
|
+
runCommand({
|
|
219
|
+
relativeCacheDirectory: cacheDirectory,
|
|
220
|
+
command,
|
|
221
|
+
cacheByTime: time,
|
|
222
|
+
cacheByFiles: files,
|
|
223
|
+
shouldCacheOnError
|
|
224
|
+
});
|
|
225
|
+
});
|
|
226
|
+
program.command("cache dir", "Show cache directory path used by cache-cmd").action((options) => {
|
|
227
|
+
const cacheDirectory = options["cache-dir"];
|
|
228
|
+
if (cacheDirectory !== void 0 && typeof cacheDirectory !== "string") {
|
|
229
|
+
throw Error("Invalid --cache-dir supplied");
|
|
230
|
+
}
|
|
231
|
+
showCacheDirectory(cacheDirectory);
|
|
232
|
+
});
|
|
233
|
+
program.command("cache clear", "Clear cache used by cache-cmd").action((options) => {
|
|
234
|
+
const cacheDirectory = options["cache-dir"];
|
|
235
|
+
if (cacheDirectory !== void 0 && typeof cacheDirectory !== "string") {
|
|
236
|
+
throw Error("Invalid --cache-dir supplied");
|
|
237
|
+
}
|
|
238
|
+
clearCacheDirectory(cacheDirectory);
|
|
239
|
+
});
|
|
240
|
+
program.parse(process.argv);
|
|
241
|
+
//# sourceMappingURL=cli.js.map
|
package/dist/cli.js.map
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../src/cli.ts", "../package.json", "../src/cache-clear.ts", "../src/utils/get-cache-dir.ts", "../src/cache-dir.ts", "../src/run.ts", "../src/utils/get-cache-key.ts", "../src/utils/get-duration.ts", "../src/utils/get-file-hashes.ts", "../src/utils/get-file-paths.ts"],
|
|
4
|
+
"sourcesContent": ["#!/usr/bin/env node\n\nimport hardRejection from 'hard-rejection'\nimport sade from 'sade'\n\nimport { version } from '../package.json'\n\nimport { clearCacheDirectory } from './cache-clear'\nimport { showCacheDirectory } from './cache-dir'\nimport { runCommand } from './run'\n\nhardRejection()\n\nconst program = sade('cache-cmd')\n\nprogram\n .version(version)\n .describe('Run and cache a command based on various factors')\n .option(\n '-c, --cache-dir',\n 'Cache directory to use (default: .cache/cache-cmd in nearest node_modules)'\n )\n\nprogram\n .command(\n 'run <command>',\n 'Run cached command (if no <command> provided, this is the default)',\n { default: true }\n )\n .option('-f, --file', 'Run command only when file content changes')\n .option('-t, --time', 'Run command only after specified time (unit with s,m,h,d,w,mo,y)')\n .option('--cache-on-error', 'Cache command run even when command exits with non-zero exit code')\n .example('run \"echo ran this command\" --time 20s')\n .example('run \"./may-fail\" --time 20s --cache-on-error')\n .example('run \"yarn install\" --file yarn.lock')\n .example('run \"yarn install\" --file yarn.lock --cache-dir .config/cache')\n .example('run \"yarn install\" --time 1mo --file yarn.lock --file package.json')\n .action((command: unknown, options: Record<string, unknown>) => {\n const cacheDirectory = options['cache-dir']\n const time = options.time\n const file = options.file\n const shouldCacheOnError = options['cache-on-error']\n const files: unknown[] = file === undefined ? [] : Array.isArray(file) ? file : [file]\n\n if (typeof command !== 'string') {\n throw Error('Invalid <command> supplied')\n }\n\n if (cacheDirectory !== undefined && typeof cacheDirectory !== 'string') {\n throw Error('Invalid --cache-dir supplied')\n }\n\n if (time !== undefined && typeof time !== 'string') {\n throw Error('Invalid --time supplied')\n }\n\n if (files.some((file) => typeof file !== 'string')) {\n throw Error('Invalid --file supplied')\n }\n\n if (shouldCacheOnError !== undefined && typeof shouldCacheOnError !== 'boolean') {\n throw Error('Invalid --cache-on-error supplied')\n }\n\n runCommand({\n relativeCacheDirectory: cacheDirectory,\n command,\n cacheByTime: time,\n cacheByFiles: files as string[],\n shouldCacheOnError,\n })\n })\n\nprogram\n .command('cache dir', 'Show cache directory path used by cache-cmd')\n .action((options: Record<string, unknown>) => {\n const cacheDirectory = options['cache-dir']\n\n if (cacheDirectory !== undefined && typeof cacheDirectory !== 'string') {\n throw Error('Invalid --cache-dir supplied')\n }\n\n showCacheDirectory(cacheDirectory)\n })\n\nprogram\n .command('cache clear', 'Clear cache used by cache-cmd')\n .action((options: Record<string, unknown>) => {\n const cacheDirectory = options['cache-dir']\n\n if (cacheDirectory !== undefined && typeof cacheDirectory !== 'string') {\n throw Error('Invalid --cache-dir supplied')\n }\n\n clearCacheDirectory(cacheDirectory)\n })\n\nprogram.parse(process.argv)\n", "{\n \"name\": \"cache-cmd\",\n \"version\": \"0.3.0\",\n \"description\": \"Run and cache a command based on various factors\",\n \"keywords\": [\n \"cache\",\n \"debounce\",\n \"cli\",\n \"conditional\",\n \"run\",\n \"cmd\",\n \"command\",\n \"time\",\n \"hash\"\n ],\n \"homepage\": \"https://github.com/dcastil/cache-cmd\",\n \"bugs\": {\n \"url\": \"https://github.com/dcastil/cache-cmd/issues\"\n },\n \"funding\": {\n \"type\": \"github\",\n \"url\": \"https://github.com/sponsors/dcastil\"\n },\n \"license\": \"MIT\",\n \"author\": \"Dany Castillo\",\n \"files\": [\n \"dist\",\n \"src\"\n ],\n \"bin\": \"dist/cli.js\",\n \"type\": \"module\",\n \"source\": \"src/cli.ts\",\n \"repository\": {\n \"type\": \"git\",\n \"url\": \"https://github.com/dcastil/cache-cmd.git\"\n },\n \"scripts\": {\n \"cache-cmd\": \"node ./dist/cli.js\",\n \"build\": \"rm -rf dist/* && esbuild src/cli.ts --bundle --platform=node --target=node20 --packages=external --format=esm --sourcemap --outfile=dist/cli.js\",\n \"type-check\": \"tsc --build\",\n \"preversion\": \"if [ -n \\\"$DANYS_MACHINE\\\" ]; then git checkout main && git pull; fi\",\n \"postversion\": \"if [ -n \\\"$DANYS_MACHINE\\\" ]; then git push --follow-tags && open https://github.com/dcastil/cache-cmd/releases; fi\"\n },\n \"dependencies\": {\n \"date-fns\": \"^4.1.0\",\n \"del\": \"^8.0.0\",\n \"exec-sh\": \"^0.4.0\",\n \"find-cache-dir\": \"^5.0.0\",\n \"flat-cache\": \"^6.1.3\",\n \"hard-rejection\": \"^2.1.0\",\n \"hasha\": \"^6.0.0\",\n \"lodash-es\": \"^4.17.21\",\n \"sade\": \"^1.8.1\"\n },\n \"devDependencies\": {\n \"@types/find-cache-dir\": \"^5.0.2\",\n \"@types/flat-cache\": \"^2.0.2\",\n \"@types/lodash-es\": \"^4.17.12\",\n \"@types/node\": \"^22.10.1\",\n \"@types/sade\": \"^1.8.0\",\n \"esbuild\": \"^0.24.0\",\n \"prettier\": \"^3.4.2\",\n \"typescript\": \"^5.7.2\"\n },\n \"publishConfig\": {\n \"provenance\": true\n }\n}\n", "import { deleteSync } from 'del'\n\nimport { getCacheDirectoryPath } from './utils/get-cache-dir'\n\nexport function clearCacheDirectory(relativeCacheDirectory: string | undefined) {\n const deletedPaths = deleteSync(getCacheDirectoryPath(relativeCacheDirectory))\n\n if (deletedPaths.length === 0) {\n console.log('No cache to clear')\n } else if (deletedPaths.length === 1) {\n console.log(`Deleted: ${deletedPaths[0]}`)\n } else {\n console.log('Deleted:\\n', deletedPaths.join('\\n'))\n }\n}\n", "import path from 'path'\n\nimport findCacheDir from 'find-cache-dir'\n\nimport { name } from '../../package.json'\n\nexport function getCacheDirectoryPath(relativeCacheDirectoryPath: string | undefined) {\n if (relativeCacheDirectoryPath) {\n return path.resolve(process.cwd(), relativeCacheDirectoryPath)\n }\n\n const resolvedCacheDirectory = findCacheDir({ name })\n\n if (!resolvedCacheDirectory) {\n throw Error('Could not find cache directory. Please provide a cache directory manually.')\n }\n\n return resolvedCacheDirectory\n}\n", "import { getCacheDirectoryPath } from './utils/get-cache-dir'\n\nexport function showCacheDirectory(relativeCacheDirectory: string | undefined) {\n console.log(getCacheDirectoryPath(relativeCacheDirectory))\n}\n", "import { add, isAfter } from 'date-fns'\nimport execSh from 'exec-sh'\nimport { create } from 'flat-cache'\nimport { isEqual } from 'lodash-es'\n\nimport { getCacheDirectoryPath } from './utils/get-cache-dir'\nimport { getCacheKey } from './utils/get-cache-key'\nimport { getDuration } from './utils/get-duration'\nimport { getFileHashes } from './utils/get-file-hashes'\nimport { getFilePaths } from './utils/get-file-paths'\n\ninterface RunCommandProps {\n relativeCacheDirectory: string | undefined\n command: string\n cacheByTime: string | undefined\n cacheByFiles: string[]\n shouldCacheOnError: boolean | undefined\n}\n\nexport async function runCommand({\n relativeCacheDirectory,\n command,\n cacheByTime: cacheByTime,\n cacheByFiles: cacheByFiles,\n shouldCacheOnError,\n}: RunCommandProps) {\n if (!cacheByTime && cacheByFiles.length === 0) {\n console.log(`Executing command \"${command}\" due to no caching options provided`)\n await execSh.promise(command)\n return\n }\n\n const cache = create({\n cacheId: 'commands-cache.json',\n cacheDir: getCacheDirectoryPath(relativeCacheDirectory),\n lruSize: 10_000,\n })\n const filePaths = getFilePaths(cacheByFiles)\n const duration = cacheByTime ? getDuration(cacheByTime) : undefined\n\n const cacheKey = getCacheKey({ duration, filePaths, command })\n\n const cacheData: unknown = cache.getKey(cacheKey)\n\n const fileHashes = filePaths.length === 0 ? undefined : await getFileHashes(filePaths)\n const currentDate = new Date()\n\n const areFileHashesEqual = isEqual((cacheData as any)?.fileHashes, fileHashes)\n const isWithinCacheTime = (() => {\n if (!duration) {\n return true\n }\n\n const lastRun = (cacheData as any)?.lastRun\n\n if (!lastRun) {\n return false\n }\n\n return isAfter(add(new Date(lastRun), duration), currentDate)\n })()\n\n if (areFileHashesEqual && isWithinCacheTime) {\n console.log(\n [\n `Skipping command \"${command}\" due to`,\n [fileHashes && 'unchanged files', duration && 'being within cache time']\n .filter(Boolean)\n .join(' and '),\n ].join(' '),\n )\n return\n }\n\n console.log(\n [\n `Executing command \"${command}\" due to`,\n [fileHashes && 'changed files', duration && 'cache time passing']\n .filter(Boolean)\n .join(' and '),\n ].join(' '),\n )\n let execPromise = execSh.promise(command)\n\n function setCache() {\n cache.setKey(cacheKey, {\n lastRun: currentDate,\n fileHashes,\n })\n cache.save(true)\n console.log(`Cache saved for command \"${command}\"`)\n }\n\n if (shouldCacheOnError) {\n execPromise = execPromise.catch((error: unknown) => {\n setCache()\n\n throw error\n })\n }\n\n await execPromise\n\n setCache()\n}\n", "interface GetCacheKeyProps {\n duration: Duration | undefined\n filePaths: string[]\n command: string\n}\n\ninterface Duration {\n years: number | undefined\n months: number | undefined\n weeks: number | undefined\n days: number | undefined\n hours: number | undefined\n minutes: number | undefined\n seconds: number | undefined\n}\n\nexport function getCacheKey({ duration, filePaths, command }: GetCacheKeyProps) {\n return [\n 'cwd:' + process.cwd(),\n 'cmd:' + command,\n duration && 'time:' + JSON.stringify(duration),\n filePaths.length !== 0 && 'files:' + filePaths.join(','),\n ]\n .filter(Boolean)\n .join(' ---')\n}\n", "export function getDuration(durationString: string) {\n if (!/^(\\d+[a-z]+)+$/gi.test(durationString)) {\n throw Error(`Invalid duration: ${durationString}`)\n }\n\n const duration = {\n years: undefined as undefined | number,\n months: undefined as undefined | number,\n weeks: undefined as undefined | number,\n days: undefined as undefined | number,\n hours: undefined as undefined | number,\n minutes: undefined as undefined | number,\n seconds: undefined as undefined | number,\n }\n\n durationString.match(/\\d+[a-z]+/gi)!.forEach((durationPart) => {\n const [, stringQuantity, unitShort] = /(\\d+)([a-z]+)/gi.exec(durationPart)!\n const quantity = Number(stringQuantity)\n\n const unitLong = (\n {\n y: 'years',\n mo: 'months',\n w: 'weeks',\n d: 'days',\n h: 'hours',\n m: 'minutes',\n s: 'seconds',\n } as const\n )[unitShort!]\n\n if (Number.isNaN(quantity) || !unitLong) {\n throw Error(`Invalid duration part: ${durationPart}`)\n }\n\n if (duration[unitLong] !== undefined) {\n throw Error(`Duration with unit ${unitLong} was supplied multiple times`)\n }\n\n duration[unitLong] = quantity\n })\n\n return duration\n}\n", "import { hashFile } from 'hasha'\n\nexport async function getFileHashes(filePaths: string[]) {\n return Object.fromEntries(\n await Promise.all(\n filePaths.map((filePath) =>\n hashFile(filePath, { algorithm: 'md5' }).then((hash) => [filePath, hash] as const),\n ),\n ),\n )\n}\n", "import path from 'path'\n\nexport function getFilePaths(relativeFilePaths: string[]) {\n const cwd = process.cwd()\n\n return relativeFilePaths.map((file) => path.resolve(cwd, file)).sort()\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
|
+
"names": ["path", "file"]
|
|
7
|
+
}
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "cache-cmd",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"description": "
|
|
3
|
+
"version": "0.3.0-dev.57b573000c4461fbe9a6a0d0537bba295407310b",
|
|
4
|
+
"description": "Run and cache a command based on various factors",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"cache",
|
|
7
7
|
"debounce",
|
|
@@ -13,9 +13,13 @@
|
|
|
13
13
|
"time",
|
|
14
14
|
"hash"
|
|
15
15
|
],
|
|
16
|
-
"homepage": "https://github.com/dcastil/
|
|
16
|
+
"homepage": "https://github.com/dcastil/cache-cmd",
|
|
17
17
|
"bugs": {
|
|
18
|
-
"url": "https://github.com/dcastil/
|
|
18
|
+
"url": "https://github.com/dcastil/cache-cmd/issues"
|
|
19
|
+
},
|
|
20
|
+
"funding": {
|
|
21
|
+
"type": "github",
|
|
22
|
+
"url": "https://github.com/sponsors/dcastil"
|
|
19
23
|
},
|
|
20
24
|
"license": "MIT",
|
|
21
25
|
"author": "Dany Castillo",
|
|
@@ -23,39 +27,42 @@
|
|
|
23
27
|
"dist",
|
|
24
28
|
"src"
|
|
25
29
|
],
|
|
26
|
-
"bin": "dist/cli.
|
|
30
|
+
"bin": "dist/cli.js",
|
|
27
31
|
"type": "module",
|
|
28
32
|
"source": "src/cli.ts",
|
|
29
33
|
"repository": {
|
|
30
34
|
"type": "git",
|
|
31
|
-
"url": "https://github.com/dcastil/
|
|
35
|
+
"url": "https://github.com/dcastil/cache-cmd.git"
|
|
32
36
|
},
|
|
33
37
|
"scripts": {
|
|
34
|
-
"cache-cmd": "node ./dist/cli.
|
|
35
|
-
"build": "rm -rf dist/* &&
|
|
38
|
+
"cache-cmd": "node ./dist/cli.js",
|
|
39
|
+
"build": "rm -rf dist/* && esbuild src/cli.ts --bundle --platform=node --target=node20 --packages=external --format=esm --sourcemap --outfile=dist/cli.js",
|
|
36
40
|
"type-check": "tsc --build",
|
|
37
41
|
"preversion": "if [ -n \"$DANYS_MACHINE\" ]; then git checkout main && git pull; fi",
|
|
38
|
-
"postversion": "if [ -n \"$DANYS_MACHINE\" ]; then git push --follow-tags && open https://github.com/dcastil/
|
|
42
|
+
"postversion": "if [ -n \"$DANYS_MACHINE\" ]; then git push --follow-tags && open https://github.com/dcastil/cache-cmd/releases; fi"
|
|
39
43
|
},
|
|
40
44
|
"dependencies": {
|
|
41
|
-
"date-fns": "^
|
|
42
|
-
"del": "^
|
|
45
|
+
"date-fns": "^4.1.0",
|
|
46
|
+
"del": "^8.0.0",
|
|
43
47
|
"exec-sh": "^0.4.0",
|
|
44
|
-
"find-cache-dir": "^
|
|
45
|
-
"flat-cache": "^
|
|
48
|
+
"find-cache-dir": "^5.0.0",
|
|
49
|
+
"flat-cache": "^6.1.3",
|
|
46
50
|
"hard-rejection": "^2.1.0",
|
|
47
|
-
"hasha": "^
|
|
48
|
-
"lodash": "^4.17.21",
|
|
49
|
-
"
|
|
50
|
-
"sade": "^1.7.4"
|
|
51
|
+
"hasha": "^6.0.0",
|
|
52
|
+
"lodash-es": "^4.17.21",
|
|
53
|
+
"sade": "^1.8.1"
|
|
51
54
|
},
|
|
52
55
|
"devDependencies": {
|
|
53
|
-
"@types/find-cache-dir": "^
|
|
54
|
-
"@types/flat-cache": "^2.0.
|
|
55
|
-
"@types/lodash": "^4.
|
|
56
|
-
"@types/
|
|
57
|
-
"
|
|
58
|
-
"
|
|
59
|
-
"
|
|
56
|
+
"@types/find-cache-dir": "^5.0.2",
|
|
57
|
+
"@types/flat-cache": "^2.0.2",
|
|
58
|
+
"@types/lodash-es": "^4.17.12",
|
|
59
|
+
"@types/node": "^22.10.1",
|
|
60
|
+
"@types/sade": "^1.8.0",
|
|
61
|
+
"esbuild": "^0.24.0",
|
|
62
|
+
"prettier": "^3.4.2",
|
|
63
|
+
"typescript": "^5.7.2"
|
|
64
|
+
},
|
|
65
|
+
"publishConfig": {
|
|
66
|
+
"provenance": true
|
|
60
67
|
}
|
|
61
68
|
}
|
package/src/cache-clear.ts
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
import
|
|
1
|
+
import { deleteSync } from 'del'
|
|
2
2
|
|
|
3
3
|
import { getCacheDirectoryPath } from './utils/get-cache-dir'
|
|
4
4
|
|
|
5
5
|
export function clearCacheDirectory(relativeCacheDirectory: string | undefined) {
|
|
6
|
-
const deletedPaths =
|
|
6
|
+
const deletedPaths = deleteSync(getCacheDirectoryPath(relativeCacheDirectory))
|
|
7
7
|
|
|
8
8
|
if (deletedPaths.length === 0) {
|
|
9
9
|
console.log('No cache to clear')
|
package/src/run.ts
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import { add, isAfter } from 'date-fns'
|
|
2
2
|
import execSh from 'exec-sh'
|
|
3
|
-
import
|
|
4
|
-
import { isEqual } from 'lodash'
|
|
3
|
+
import { create } from 'flat-cache'
|
|
4
|
+
import { isEqual } from 'lodash-es'
|
|
5
5
|
|
|
6
|
-
import {
|
|
6
|
+
import { getCacheDirectoryPath } from './utils/get-cache-dir'
|
|
7
7
|
import { getCacheKey } from './utils/get-cache-key'
|
|
8
8
|
import { getDuration } from './utils/get-duration'
|
|
9
9
|
import { getFileHashes } from './utils/get-file-hashes'
|
|
@@ -25,11 +25,16 @@ export async function runCommand({
|
|
|
25
25
|
shouldCacheOnError,
|
|
26
26
|
}: RunCommandProps) {
|
|
27
27
|
if (!cacheByTime && cacheByFiles.length === 0) {
|
|
28
|
+
console.log(`Executing command "${command}" due to no caching options provided`)
|
|
28
29
|
await execSh.promise(command)
|
|
29
30
|
return
|
|
30
31
|
}
|
|
31
32
|
|
|
32
|
-
const cache =
|
|
33
|
+
const cache = create({
|
|
34
|
+
cacheId: 'commands-cache.json',
|
|
35
|
+
cacheDir: getCacheDirectoryPath(relativeCacheDirectory),
|
|
36
|
+
lruSize: 10_000,
|
|
37
|
+
})
|
|
33
38
|
const filePaths = getFilePaths(cacheByFiles)
|
|
34
39
|
const duration = cacheByTime ? getDuration(cacheByTime) : undefined
|
|
35
40
|
|
|
@@ -56,18 +61,39 @@ export async function runCommand({
|
|
|
56
61
|
})()
|
|
57
62
|
|
|
58
63
|
if (areFileHashesEqual && isWithinCacheTime) {
|
|
64
|
+
console.log(
|
|
65
|
+
[
|
|
66
|
+
`Skipping command "${command}" due to`,
|
|
67
|
+
[fileHashes && 'unchanged files', duration && 'being within cache time']
|
|
68
|
+
.filter(Boolean)
|
|
69
|
+
.join(' and '),
|
|
70
|
+
].join(' '),
|
|
71
|
+
)
|
|
59
72
|
return
|
|
60
73
|
}
|
|
61
74
|
|
|
75
|
+
console.log(
|
|
76
|
+
[
|
|
77
|
+
`Executing command "${command}" due to`,
|
|
78
|
+
[fileHashes && 'changed files', duration && 'cache time passing']
|
|
79
|
+
.filter(Boolean)
|
|
80
|
+
.join(' and '),
|
|
81
|
+
].join(' '),
|
|
82
|
+
)
|
|
62
83
|
let execPromise = execSh.promise(command)
|
|
63
84
|
|
|
85
|
+
function setCache() {
|
|
86
|
+
cache.setKey(cacheKey, {
|
|
87
|
+
lastRun: currentDate,
|
|
88
|
+
fileHashes,
|
|
89
|
+
})
|
|
90
|
+
cache.save(true)
|
|
91
|
+
console.log(`Cache saved for command "${command}"`)
|
|
92
|
+
}
|
|
93
|
+
|
|
64
94
|
if (shouldCacheOnError) {
|
|
65
95
|
execPromise = execPromise.catch((error: unknown) => {
|
|
66
|
-
|
|
67
|
-
lastRun: currentDate,
|
|
68
|
-
fileHashes,
|
|
69
|
-
})
|
|
70
|
-
cache.save(true)
|
|
96
|
+
setCache()
|
|
71
97
|
|
|
72
98
|
throw error
|
|
73
99
|
})
|
|
@@ -75,9 +101,5 @@ export async function runCommand({
|
|
|
75
101
|
|
|
76
102
|
await execPromise
|
|
77
103
|
|
|
78
|
-
|
|
79
|
-
lastRun: currentDate,
|
|
80
|
-
fileHashes,
|
|
81
|
-
})
|
|
82
|
-
cache.save(true)
|
|
104
|
+
setCache()
|
|
83
105
|
}
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import path from 'path'
|
|
2
2
|
|
|
3
3
|
import findCacheDir from 'find-cache-dir'
|
|
4
|
-
import makeDir from 'make-dir'
|
|
5
4
|
|
|
6
5
|
import { name } from '../../package.json'
|
|
7
6
|
|
|
@@ -18,19 +17,3 @@ export function getCacheDirectoryPath(relativeCacheDirectoryPath: string | undef
|
|
|
18
17
|
|
|
19
18
|
return resolvedCacheDirectory
|
|
20
19
|
}
|
|
21
|
-
|
|
22
|
-
export function createCache(relativeCacheDirectoryPath: string | undefined) {
|
|
23
|
-
if (relativeCacheDirectoryPath) {
|
|
24
|
-
const absoluteCacheDirectoryPath = makeDir.sync(relativeCacheDirectoryPath)
|
|
25
|
-
|
|
26
|
-
return absoluteCacheDirectoryPath
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
const resolvedCachePath = findCacheDir({ name, create: true })
|
|
30
|
-
|
|
31
|
-
if (!resolvedCachePath) {
|
|
32
|
-
throw Error('Could not find cache directory. Please provide a cache directory manually.')
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
return resolvedCachePath
|
|
36
|
-
}
|
|
@@ -1,13 +1,11 @@
|
|
|
1
|
-
import
|
|
1
|
+
import { hashFile } from 'hasha'
|
|
2
2
|
|
|
3
3
|
export async function getFileHashes(filePaths: string[]) {
|
|
4
4
|
return Object.fromEntries(
|
|
5
5
|
await Promise.all(
|
|
6
6
|
filePaths.map((filePath) =>
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
)
|
|
11
|
-
)
|
|
7
|
+
hashFile(filePath, { algorithm: 'md5' }).then((hash) => [filePath, hash] as const),
|
|
8
|
+
),
|
|
9
|
+
),
|
|
12
10
|
)
|
|
13
11
|
}
|
package/dist/cli.cjs
DELETED
|
@@ -1,262 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
'use strict';
|
|
3
|
-
|
|
4
|
-
var hardRejection = require('hard-rejection');
|
|
5
|
-
var sade = require('sade');
|
|
6
|
-
var del = require('del');
|
|
7
|
-
var path = require('path');
|
|
8
|
-
var findCacheDir = require('find-cache-dir');
|
|
9
|
-
var makeDir = require('make-dir');
|
|
10
|
-
var dateFns = require('date-fns');
|
|
11
|
-
var execSh = require('exec-sh');
|
|
12
|
-
var flatCache = require('flat-cache');
|
|
13
|
-
var lodash = require('lodash');
|
|
14
|
-
var hasha = require('hasha');
|
|
15
|
-
|
|
16
|
-
function _interopDefaultLegacy (e) { return e && typeof e === 'object' && 'default' in e ? e : { 'default': e }; }
|
|
17
|
-
|
|
18
|
-
var hardRejection__default = /*#__PURE__*/_interopDefaultLegacy(hardRejection);
|
|
19
|
-
var sade__default = /*#__PURE__*/_interopDefaultLegacy(sade);
|
|
20
|
-
var del__default = /*#__PURE__*/_interopDefaultLegacy(del);
|
|
21
|
-
var path__default = /*#__PURE__*/_interopDefaultLegacy(path);
|
|
22
|
-
var findCacheDir__default = /*#__PURE__*/_interopDefaultLegacy(findCacheDir);
|
|
23
|
-
var makeDir__default = /*#__PURE__*/_interopDefaultLegacy(makeDir);
|
|
24
|
-
var execSh__default = /*#__PURE__*/_interopDefaultLegacy(execSh);
|
|
25
|
-
var flatCache__default = /*#__PURE__*/_interopDefaultLegacy(flatCache);
|
|
26
|
-
var hasha__default = /*#__PURE__*/_interopDefaultLegacy(hasha);
|
|
27
|
-
|
|
28
|
-
var name = "cache-cmd";
|
|
29
|
-
var version = "0.2.0";
|
|
30
|
-
|
|
31
|
-
function getCacheDirectoryPath(relativeCacheDirectoryPath) {
|
|
32
|
-
if (relativeCacheDirectoryPath) {
|
|
33
|
-
return path__default["default"].resolve(process.cwd(), relativeCacheDirectoryPath);
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
const resolvedCacheDirectory = findCacheDir__default["default"]({
|
|
37
|
-
name
|
|
38
|
-
});
|
|
39
|
-
|
|
40
|
-
if (!resolvedCacheDirectory) {
|
|
41
|
-
throw Error('Could not find cache directory. Please provide a cache directory manually.');
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
return resolvedCacheDirectory;
|
|
45
|
-
}
|
|
46
|
-
function createCache(relativeCacheDirectoryPath) {
|
|
47
|
-
if (relativeCacheDirectoryPath) {
|
|
48
|
-
const absoluteCacheDirectoryPath = makeDir__default["default"].sync(relativeCacheDirectoryPath);
|
|
49
|
-
return absoluteCacheDirectoryPath;
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
const resolvedCachePath = findCacheDir__default["default"]({
|
|
53
|
-
name,
|
|
54
|
-
create: true
|
|
55
|
-
});
|
|
56
|
-
|
|
57
|
-
if (!resolvedCachePath) {
|
|
58
|
-
throw Error('Could not find cache directory. Please provide a cache directory manually.');
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
return resolvedCachePath;
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
function clearCacheDirectory(relativeCacheDirectory) {
|
|
65
|
-
const deletedPaths = del__default["default"].sync(getCacheDirectoryPath(relativeCacheDirectory));
|
|
66
|
-
|
|
67
|
-
if (deletedPaths.length === 0) {
|
|
68
|
-
console.log('No cache to clear');
|
|
69
|
-
} else if (deletedPaths.length === 1) {
|
|
70
|
-
console.log(`Deleted: ${deletedPaths[0]}`);
|
|
71
|
-
} else {
|
|
72
|
-
console.log('Deleted:\n', deletedPaths.join('\n'));
|
|
73
|
-
}
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
function showCacheDirectory(relativeCacheDirectory) {
|
|
77
|
-
console.log(getCacheDirectoryPath(relativeCacheDirectory));
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
function getCacheKey({
|
|
81
|
-
duration,
|
|
82
|
-
filePaths,
|
|
83
|
-
command
|
|
84
|
-
}) {
|
|
85
|
-
return ['cwd:' + process.cwd(), 'cmd:' + command, duration && 'time:' + JSON.stringify(duration), filePaths.length !== 0 && 'files:' + filePaths.join(',')].filter(Boolean).join(' ---');
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
function getDuration(durationString) {
|
|
89
|
-
if (!/^(\d+[a-z]+)+$/gi.test(durationString)) {
|
|
90
|
-
throw Error(`Invalid duration: ${durationString}`);
|
|
91
|
-
}
|
|
92
|
-
|
|
93
|
-
const duration = {
|
|
94
|
-
years: undefined,
|
|
95
|
-
months: undefined,
|
|
96
|
-
weeks: undefined,
|
|
97
|
-
days: undefined,
|
|
98
|
-
hours: undefined,
|
|
99
|
-
minutes: undefined,
|
|
100
|
-
seconds: undefined
|
|
101
|
-
};
|
|
102
|
-
durationString.match(/\d+[a-z]+/gi).forEach(durationPart => {
|
|
103
|
-
const [, stringQuantity, unitShort] = /(\d+)([a-z]+)/gi.exec(durationPart);
|
|
104
|
-
const quantity = Number(stringQuantity);
|
|
105
|
-
const unitLong = {
|
|
106
|
-
y: 'years',
|
|
107
|
-
mo: 'months',
|
|
108
|
-
w: 'weeks',
|
|
109
|
-
d: 'days',
|
|
110
|
-
h: 'hours',
|
|
111
|
-
m: 'minutes',
|
|
112
|
-
s: 'seconds'
|
|
113
|
-
}[unitShort];
|
|
114
|
-
|
|
115
|
-
if (Number.isNaN(quantity) || !unitLong) {
|
|
116
|
-
throw Error(`Invalid duration part: ${durationPart}`);
|
|
117
|
-
}
|
|
118
|
-
|
|
119
|
-
if (duration[unitLong] !== undefined) {
|
|
120
|
-
throw Error(`Duration with unit ${unitLong} was supplied multiple times`);
|
|
121
|
-
}
|
|
122
|
-
|
|
123
|
-
duration[unitLong] = quantity;
|
|
124
|
-
});
|
|
125
|
-
return duration;
|
|
126
|
-
}
|
|
127
|
-
|
|
128
|
-
async function getFileHashes(filePaths) {
|
|
129
|
-
return Object.fromEntries(await Promise.all(filePaths.map(filePath => hasha__default["default"].fromFile(filePath, {
|
|
130
|
-
algorithm: 'md5'
|
|
131
|
-
}).then(hash => [filePath, hash]))));
|
|
132
|
-
}
|
|
133
|
-
|
|
134
|
-
function getFilePaths(relativeFilePaths) {
|
|
135
|
-
const cwd = process.cwd();
|
|
136
|
-
return relativeFilePaths.map(file => path__default["default"].resolve(cwd, file)).sort();
|
|
137
|
-
}
|
|
138
|
-
|
|
139
|
-
async function runCommand({
|
|
140
|
-
relativeCacheDirectory,
|
|
141
|
-
command,
|
|
142
|
-
cacheByTime: cacheByTime,
|
|
143
|
-
cacheByFiles: cacheByFiles,
|
|
144
|
-
shouldCacheOnError
|
|
145
|
-
}) {
|
|
146
|
-
if (!cacheByTime && cacheByFiles.length === 0) {
|
|
147
|
-
await execSh__default["default"].promise(command);
|
|
148
|
-
return;
|
|
149
|
-
}
|
|
150
|
-
|
|
151
|
-
const cache = flatCache__default["default"].load('commands-cache.json', createCache(relativeCacheDirectory));
|
|
152
|
-
const filePaths = getFilePaths(cacheByFiles);
|
|
153
|
-
const duration = cacheByTime ? getDuration(cacheByTime) : undefined;
|
|
154
|
-
const cacheKey = getCacheKey({
|
|
155
|
-
duration,
|
|
156
|
-
filePaths,
|
|
157
|
-
command
|
|
158
|
-
});
|
|
159
|
-
const cacheData = cache.getKey(cacheKey);
|
|
160
|
-
const fileHashes = filePaths.length === 0 ? undefined : await getFileHashes(filePaths);
|
|
161
|
-
const currentDate = new Date();
|
|
162
|
-
const areFileHashesEqual = lodash.isEqual(cacheData == null ? void 0 : cacheData.fileHashes, fileHashes);
|
|
163
|
-
|
|
164
|
-
const isWithinCacheTime = (() => {
|
|
165
|
-
if (!duration) {
|
|
166
|
-
return true;
|
|
167
|
-
}
|
|
168
|
-
|
|
169
|
-
const lastRun = cacheData == null ? void 0 : cacheData.lastRun;
|
|
170
|
-
|
|
171
|
-
if (!lastRun) {
|
|
172
|
-
return false;
|
|
173
|
-
}
|
|
174
|
-
|
|
175
|
-
return dateFns.isAfter(dateFns.add(new Date(lastRun), duration), currentDate);
|
|
176
|
-
})();
|
|
177
|
-
|
|
178
|
-
if (areFileHashesEqual && isWithinCacheTime) {
|
|
179
|
-
return;
|
|
180
|
-
}
|
|
181
|
-
|
|
182
|
-
let execPromise = execSh__default["default"].promise(command);
|
|
183
|
-
|
|
184
|
-
if (shouldCacheOnError) {
|
|
185
|
-
execPromise = execPromise.catch(error => {
|
|
186
|
-
cache.setKey(cacheKey, {
|
|
187
|
-
lastRun: currentDate,
|
|
188
|
-
fileHashes
|
|
189
|
-
});
|
|
190
|
-
cache.save(true);
|
|
191
|
-
throw error;
|
|
192
|
-
});
|
|
193
|
-
}
|
|
194
|
-
|
|
195
|
-
await execPromise;
|
|
196
|
-
cache.setKey(cacheKey, {
|
|
197
|
-
lastRun: currentDate,
|
|
198
|
-
fileHashes
|
|
199
|
-
});
|
|
200
|
-
cache.save(true);
|
|
201
|
-
}
|
|
202
|
-
|
|
203
|
-
hardRejection__default["default"]();
|
|
204
|
-
const program = sade__default["default"]('cache-cmd');
|
|
205
|
-
program.version(version).describe('Run and cache a command based on various factors').option('-c, --cache-dir', 'Cache directory to use (default: .cache/cache-cmd in nearest node_modules)');
|
|
206
|
-
program.command('run <command>', 'Run cached command (if no <command> provided, this is the default)', {
|
|
207
|
-
default: true
|
|
208
|
-
}).option('-f, --file', 'Run command only when file content changes').option('-t, --time', 'Run command only after specified time (unit with s,m,h,d,w,mo,y)').option('--cache-on-error', 'Cache command run even when command exits with non-zero exit code').example('run "echo ran this command" --time 20s').example('run "./may-fail" --time 20s --cache-on-error').example('run "yarn install" --file yarn.lock').example('run "yarn install" --file yarn.lock --cache-dir .config/cache').example('run "yarn install" --time 1mo --file yarn.lock --file package.json').action((command, options) => {
|
|
209
|
-
const cacheDirectory = options['cache-dir'];
|
|
210
|
-
const time = options.time;
|
|
211
|
-
const file = options.file;
|
|
212
|
-
const shouldCacheOnError = options['cache-on-error'];
|
|
213
|
-
const files = file === undefined ? [] : Array.isArray(file) ? file : [file];
|
|
214
|
-
|
|
215
|
-
if (typeof command !== 'string') {
|
|
216
|
-
throw Error('Invalid <command> supplied');
|
|
217
|
-
}
|
|
218
|
-
|
|
219
|
-
if (cacheDirectory !== undefined && typeof cacheDirectory !== 'string') {
|
|
220
|
-
throw Error('Invalid --cache-dir supplied');
|
|
221
|
-
}
|
|
222
|
-
|
|
223
|
-
if (time !== undefined && typeof time !== 'string') {
|
|
224
|
-
throw Error('Invalid --time supplied');
|
|
225
|
-
}
|
|
226
|
-
|
|
227
|
-
if (files.some(file => typeof file !== 'string')) {
|
|
228
|
-
throw Error('Invalid --file supplied');
|
|
229
|
-
}
|
|
230
|
-
|
|
231
|
-
if (shouldCacheOnError !== undefined && typeof shouldCacheOnError !== 'boolean') {
|
|
232
|
-
throw Error('Invalid --cache-on-error supplied');
|
|
233
|
-
}
|
|
234
|
-
|
|
235
|
-
runCommand({
|
|
236
|
-
relativeCacheDirectory: cacheDirectory,
|
|
237
|
-
command,
|
|
238
|
-
cacheByTime: time,
|
|
239
|
-
cacheByFiles: files,
|
|
240
|
-
shouldCacheOnError
|
|
241
|
-
});
|
|
242
|
-
});
|
|
243
|
-
program.command('cache dir', 'Show cache directory path used by cache-cmd').action(options => {
|
|
244
|
-
const cacheDirectory = options['cache-dir'];
|
|
245
|
-
|
|
246
|
-
if (cacheDirectory !== undefined && typeof cacheDirectory !== 'string') {
|
|
247
|
-
throw Error('Invalid --cache-dir supplied');
|
|
248
|
-
}
|
|
249
|
-
|
|
250
|
-
showCacheDirectory(cacheDirectory);
|
|
251
|
-
});
|
|
252
|
-
program.command('cache clear', 'Clear cache used by cache-cmd').action(options => {
|
|
253
|
-
const cacheDirectory = options['cache-dir'];
|
|
254
|
-
|
|
255
|
-
if (cacheDirectory !== undefined && typeof cacheDirectory !== 'string') {
|
|
256
|
-
throw Error('Invalid --cache-dir supplied');
|
|
257
|
-
}
|
|
258
|
-
|
|
259
|
-
clearCacheDirectory(cacheDirectory);
|
|
260
|
-
});
|
|
261
|
-
program.parse(process.argv);
|
|
262
|
-
//# sourceMappingURL=cli.cjs.map
|
package/dist/cli.cjs.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"cli.cjs","sources":["../src/utils/get-cache-dir.ts","../src/cache-clear.ts","../src/cache-dir.ts","../src/utils/get-cache-key.ts","../src/utils/get-duration.ts","../src/utils/get-file-hashes.ts","../src/utils/get-file-paths.ts","../src/run.ts","../src/cli.ts"],"sourcesContent":["import path from 'path'\n\nimport findCacheDir from 'find-cache-dir'\nimport makeDir from 'make-dir'\n\nimport { name } from '../../package.json'\n\nexport function getCacheDirectoryPath(relativeCacheDirectoryPath: string | undefined) {\n if (relativeCacheDirectoryPath) {\n return path.resolve(process.cwd(), relativeCacheDirectoryPath)\n }\n\n const resolvedCacheDirectory = findCacheDir({ name })\n\n if (!resolvedCacheDirectory) {\n throw Error('Could not find cache directory. Please provide a cache directory manually.')\n }\n\n return resolvedCacheDirectory\n}\n\nexport function createCache(relativeCacheDirectoryPath: string | undefined) {\n if (relativeCacheDirectoryPath) {\n const absoluteCacheDirectoryPath = makeDir.sync(relativeCacheDirectoryPath)\n\n return absoluteCacheDirectoryPath\n }\n\n const resolvedCachePath = findCacheDir({ name, create: true })\n\n if (!resolvedCachePath) {\n throw Error('Could not find cache directory. Please provide a cache directory manually.')\n }\n\n return resolvedCachePath\n}\n","import del from 'del'\n\nimport { getCacheDirectoryPath } from './utils/get-cache-dir'\n\nexport function clearCacheDirectory(relativeCacheDirectory: string | undefined) {\n const deletedPaths = del.sync(getCacheDirectoryPath(relativeCacheDirectory))\n\n if (deletedPaths.length === 0) {\n console.log('No cache to clear')\n } else if (deletedPaths.length === 1) {\n console.log(`Deleted: ${deletedPaths[0]}`)\n } else {\n console.log('Deleted:\\n', deletedPaths.join('\\n'))\n }\n}\n","import { getCacheDirectoryPath } from './utils/get-cache-dir'\n\nexport function showCacheDirectory(relativeCacheDirectory: string | undefined) {\n console.log(getCacheDirectoryPath(relativeCacheDirectory))\n}\n","interface GetCacheKeyProps {\n duration: Duration | undefined\n filePaths: string[]\n command: string\n}\n\ninterface Duration {\n years: number | undefined\n months: number | undefined\n weeks: number | undefined\n days: number | undefined\n hours: number | undefined\n minutes: number | undefined\n seconds: number | undefined\n}\n\nexport function getCacheKey({ duration, filePaths, command }: GetCacheKeyProps) {\n return [\n 'cwd:' + process.cwd(),\n 'cmd:' + command,\n duration && 'time:' + JSON.stringify(duration),\n filePaths.length !== 0 && 'files:' + filePaths.join(','),\n ]\n .filter(Boolean)\n .join(' ---')\n}\n","export function getDuration(durationString: string) {\n if (!/^(\\d+[a-z]+)+$/gi.test(durationString)) {\n throw Error(`Invalid duration: ${durationString}`)\n }\n\n const duration = {\n years: undefined as undefined | number,\n months: undefined as undefined | number,\n weeks: undefined as undefined | number,\n days: undefined as undefined | number,\n hours: undefined as undefined | number,\n minutes: undefined as undefined | number,\n seconds: undefined as undefined | number,\n }\n\n durationString.match(/\\d+[a-z]+/gi)!.forEach((durationPart) => {\n const [, stringQuantity, unitShort] = /(\\d+)([a-z]+)/gi.exec(durationPart)!\n const quantity = Number(stringQuantity)\n\n const unitLong = (\n {\n y: 'years',\n mo: 'months',\n w: 'weeks',\n d: 'days',\n h: 'hours',\n m: 'minutes',\n s: 'seconds',\n } as const\n )[unitShort!]\n\n if (Number.isNaN(quantity) || !unitLong) {\n throw Error(`Invalid duration part: ${durationPart}`)\n }\n\n if (duration[unitLong] !== undefined) {\n throw Error(`Duration with unit ${unitLong} was supplied multiple times`)\n }\n\n duration[unitLong] = quantity\n })\n\n return duration\n}\n","import hasha from 'hasha'\n\nexport async function getFileHashes(filePaths: string[]) {\n return Object.fromEntries(\n await Promise.all(\n filePaths.map((filePath) =>\n hasha\n .fromFile(filePath, { algorithm: 'md5' })\n .then((hash) => [filePath, hash] as const)\n )\n )\n )\n}\n","import path from 'path'\n\nexport function getFilePaths(relativeFilePaths: string[]) {\n const cwd = process.cwd()\n\n return relativeFilePaths.map((file) => path.resolve(cwd, file)).sort()\n}\n","import { add, isAfter } from 'date-fns'\nimport execSh from 'exec-sh'\nimport flatCache from 'flat-cache'\nimport { isEqual } from 'lodash'\n\nimport { createCache } from './utils/get-cache-dir'\nimport { getCacheKey } from './utils/get-cache-key'\nimport { getDuration } from './utils/get-duration'\nimport { getFileHashes } from './utils/get-file-hashes'\nimport { getFilePaths } from './utils/get-file-paths'\n\ninterface RunCommandProps {\n relativeCacheDirectory: string | undefined\n command: string\n cacheByTime: string | undefined\n cacheByFiles: string[]\n shouldCacheOnError: boolean | undefined\n}\n\nexport async function runCommand({\n relativeCacheDirectory,\n command,\n cacheByTime: cacheByTime,\n cacheByFiles: cacheByFiles,\n shouldCacheOnError,\n}: RunCommandProps) {\n if (!cacheByTime && cacheByFiles.length === 0) {\n await execSh.promise(command)\n return\n }\n\n const cache = flatCache.load('commands-cache.json', createCache(relativeCacheDirectory))\n const filePaths = getFilePaths(cacheByFiles)\n const duration = cacheByTime ? getDuration(cacheByTime) : undefined\n\n const cacheKey = getCacheKey({ duration, filePaths, command })\n\n const cacheData: unknown = cache.getKey(cacheKey)\n\n const fileHashes = filePaths.length === 0 ? undefined : await getFileHashes(filePaths)\n const currentDate = new Date()\n\n const areFileHashesEqual = isEqual((cacheData as any)?.fileHashes, fileHashes)\n const isWithinCacheTime = (() => {\n if (!duration) {\n return true\n }\n\n const lastRun = (cacheData as any)?.lastRun\n\n if (!lastRun) {\n return false\n }\n\n return isAfter(add(new Date(lastRun), duration), currentDate)\n })()\n\n if (areFileHashesEqual && isWithinCacheTime) {\n return\n }\n\n let execPromise = execSh.promise(command)\n\n if (shouldCacheOnError) {\n execPromise = execPromise.catch((error: unknown) => {\n cache.setKey(cacheKey, {\n lastRun: currentDate,\n fileHashes,\n })\n cache.save(true)\n\n throw error\n })\n }\n\n await execPromise\n\n cache.setKey(cacheKey, {\n lastRun: currentDate,\n fileHashes,\n })\n cache.save(true)\n}\n","#!/usr/bin/env node\n\nimport hardRejection from 'hard-rejection'\nimport sade from 'sade'\n\nimport { version } from '../package.json'\n\nimport { clearCacheDirectory } from './cache-clear'\nimport { showCacheDirectory } from './cache-dir'\nimport { runCommand } from './run'\n\nhardRejection()\n\nconst program = sade('cache-cmd')\n\nprogram\n .version(version)\n .describe('Run and cache a command based on various factors')\n .option(\n '-c, --cache-dir',\n 'Cache directory to use (default: .cache/cache-cmd in nearest node_modules)'\n )\n\nprogram\n .command(\n 'run <command>',\n 'Run cached command (if no <command> provided, this is the default)',\n { default: true }\n )\n .option('-f, --file', 'Run command only when file content changes')\n .option('-t, --time', 'Run command only after specified time (unit with s,m,h,d,w,mo,y)')\n .option('--cache-on-error', 'Cache command run even when command exits with non-zero exit code')\n .example('run \"echo ran this command\" --time 20s')\n .example('run \"./may-fail\" --time 20s --cache-on-error')\n .example('run \"yarn install\" --file yarn.lock')\n .example('run \"yarn install\" --file yarn.lock --cache-dir .config/cache')\n .example('run \"yarn install\" --time 1mo --file yarn.lock --file package.json')\n .action((command: unknown, options: Record<string, unknown>) => {\n const cacheDirectory = options['cache-dir']\n const time = options.time\n const file = options.file\n const shouldCacheOnError = options['cache-on-error']\n const files: unknown[] = file === undefined ? [] : Array.isArray(file) ? file : [file]\n\n if (typeof command !== 'string') {\n throw Error('Invalid <command> supplied')\n }\n\n if (cacheDirectory !== undefined && typeof cacheDirectory !== 'string') {\n throw Error('Invalid --cache-dir supplied')\n }\n\n if (time !== undefined && typeof time !== 'string') {\n throw Error('Invalid --time supplied')\n }\n\n if (files.some((file) => typeof file !== 'string')) {\n throw Error('Invalid --file supplied')\n }\n\n if (shouldCacheOnError !== undefined && typeof shouldCacheOnError !== 'boolean') {\n throw Error('Invalid --cache-on-error supplied')\n }\n\n runCommand({\n relativeCacheDirectory: cacheDirectory,\n command,\n cacheByTime: time,\n cacheByFiles: files as string[],\n shouldCacheOnError,\n })\n })\n\nprogram\n .command('cache dir', 'Show cache directory path used by cache-cmd')\n .action((options: Record<string, unknown>) => {\n const cacheDirectory = options['cache-dir']\n\n if (cacheDirectory !== undefined && typeof cacheDirectory !== 'string') {\n throw Error('Invalid --cache-dir supplied')\n }\n\n showCacheDirectory(cacheDirectory)\n })\n\nprogram\n .command('cache clear', 'Clear cache used by cache-cmd')\n .action((options: Record<string, unknown>) => {\n const cacheDirectory = options['cache-dir']\n\n if (cacheDirectory !== undefined && typeof cacheDirectory !== 'string') {\n throw Error('Invalid --cache-dir supplied')\n }\n\n clearCacheDirectory(cacheDirectory)\n })\n\nprogram.parse(process.argv)\n"],"names":["getCacheDirectoryPath","relativeCacheDirectoryPath","path","resolve","process","cwd","resolvedCacheDirectory","findCacheDir","name","Error","createCache","absoluteCacheDirectoryPath","makeDir","sync","resolvedCachePath","create","clearCacheDirectory","relativeCacheDirectory","deletedPaths","del","length","console","log","join","showCacheDirectory","getCacheKey","duration","filePaths","command","JSON","stringify","filter","Boolean","getDuration","durationString","test","years","undefined","months","weeks","days","hours","minutes","seconds","match","forEach","durationPart","stringQuantity","unitShort","exec","quantity","Number","unitLong","y","mo","w","d","h","m","s","isNaN","getFileHashes","Object","fromEntries","Promise","all","map","filePath","hasha","fromFile","algorithm","then","hash","getFilePaths","relativeFilePaths","file","sort","runCommand","cacheByTime","cacheByFiles","shouldCacheOnError","execSh","promise","cache","flatCache","load","cacheKey","cacheData","getKey","fileHashes","currentDate","Date","areFileHashesEqual","isEqual","isWithinCacheTime","lastRun","isAfter","add","execPromise","catch","error","setKey","save","hardRejection","program","sade","version","describe","option","default","example","action","options","cacheDirectory","time","files","Array","isArray","some","parse","argv"],"mappings":"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"}
|