climaybe 2.2.2 → 2.2.4
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 +2 -0
- package/bin/cli.js +10 -10
- package/bin/version.txt +1 -1
- package/package.json +1 -1
- package/src/lib/cli-version.js +20 -0
- package/src/lib/update-notifier.js +81 -0
- package/src/workflows/build/build-scripts.js +2 -2
package/README.md
CHANGED
|
@@ -25,6 +25,8 @@ npm install -D climaybe
|
|
|
25
25
|
|
|
26
26
|
Run commands with `npx climaybe` (or add scripts to your `package.json`).
|
|
27
27
|
|
|
28
|
+
When a newer `climaybe` is available, the CLI can prompt at startup to update. Press **Enter** to accept the update (`npm install -g climaybe@latest`) or type `n` to skip.
|
|
29
|
+
|
|
28
30
|
## Quick Start (theme)
|
|
29
31
|
|
|
30
32
|
```bash
|
package/bin/cli.js
CHANGED
|
@@ -1,22 +1,22 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
|
-
import { readFileSync, existsSync } from 'node:fs';
|
|
4
3
|
import { createRequire } from 'node:module';
|
|
5
4
|
import { dirname, join } from 'node:path';
|
|
6
5
|
import { fileURLToPath } from 'node:url';
|
|
7
6
|
import { run } from '../src/index.js';
|
|
7
|
+
import { resolveCliVersion } from '../src/lib/cli-version.js';
|
|
8
|
+
import { maybeOfferCliUpdate } from '../src/lib/update-notifier.js';
|
|
8
9
|
|
|
9
10
|
const require = createRequire(import.meta.url);
|
|
10
11
|
const binDir = dirname(fileURLToPath(import.meta.url));
|
|
11
12
|
const packageDir = join(binDir, '..');
|
|
12
13
|
|
|
13
|
-
|
|
14
|
-
const
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
version
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
}
|
|
21
|
-
|
|
14
|
+
const pkg = require(join(packageDir, 'package.json'));
|
|
15
|
+
const version = resolveCliVersion({
|
|
16
|
+
packageDir,
|
|
17
|
+
binDir,
|
|
18
|
+
packageVersion: pkg.version,
|
|
19
|
+
});
|
|
20
|
+
const packageName = pkg.name || 'climaybe';
|
|
21
|
+
await maybeOfferCliUpdate({ packageName, currentVersion: version, packageDir });
|
|
22
22
|
run(process.argv, version, packageDir);
|
package/bin/version.txt
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
2.2.
|
|
1
|
+
2.2.4
|
package/package.json
CHANGED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { existsSync, readFileSync } from 'node:fs';
|
|
2
|
+
import { join } from 'node:path';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Resolve the version shown by the CLI.
|
|
6
|
+
* - In dev checkouts (e.g. npm link), prefer package.json.
|
|
7
|
+
* - In packaged installs, prefer bin/version.txt and fallback to package.json.
|
|
8
|
+
*/
|
|
9
|
+
export function resolveCliVersion({ packageDir, binDir, packageVersion }) {
|
|
10
|
+
const devCheckout = existsSync(join(packageDir, '.git'));
|
|
11
|
+
if (devCheckout) return packageVersion;
|
|
12
|
+
|
|
13
|
+
const versionFile = join(binDir, 'version.txt');
|
|
14
|
+
if (existsSync(versionFile)) {
|
|
15
|
+
const baked = readFileSync(versionFile, 'utf8').trim();
|
|
16
|
+
if (baked) return baked;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
return packageVersion;
|
|
20
|
+
}
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
import { execSync } from 'node:child_process';
|
|
2
|
+
import { stdin as input, stdout as output } from 'node:process';
|
|
3
|
+
import { createInterface } from 'node:readline/promises';
|
|
4
|
+
import pc from 'picocolors';
|
|
5
|
+
|
|
6
|
+
const NPM_REGISTRY_BASE = 'https://registry.npmjs.org';
|
|
7
|
+
const DEFAULT_TIMEOUT_MS = 1200;
|
|
8
|
+
|
|
9
|
+
function parseSemver(version) {
|
|
10
|
+
const match = String(version || '').trim().match(/^v?(\d+)\.(\d+)\.(\d+)(?:[-+].*)?$/);
|
|
11
|
+
if (!match) return null;
|
|
12
|
+
return [Number(match[1]), Number(match[2]), Number(match[3])];
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export function isVersionGreater(candidate, current) {
|
|
16
|
+
const a = parseSemver(candidate);
|
|
17
|
+
const b = parseSemver(current);
|
|
18
|
+
if (!a || !b) return false;
|
|
19
|
+
if (a[0] !== b[0]) return a[0] > b[0];
|
|
20
|
+
if (a[1] !== b[1]) return a[1] > b[1];
|
|
21
|
+
return a[2] > b[2];
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
async function fetchLatestVersion(packageName, timeoutMs = DEFAULT_TIMEOUT_MS) {
|
|
25
|
+
const controller = new AbortController();
|
|
26
|
+
const timeout = setTimeout(() => controller.abort(), timeoutMs);
|
|
27
|
+
try {
|
|
28
|
+
const response = await fetch(`${NPM_REGISTRY_BASE}/${encodeURIComponent(packageName)}/latest`, {
|
|
29
|
+
signal: controller.signal,
|
|
30
|
+
headers: { accept: 'application/json' },
|
|
31
|
+
});
|
|
32
|
+
if (!response.ok) return null;
|
|
33
|
+
const json = await response.json();
|
|
34
|
+
return typeof json.version === 'string' ? json.version : null;
|
|
35
|
+
} catch {
|
|
36
|
+
return null;
|
|
37
|
+
} finally {
|
|
38
|
+
clearTimeout(timeout);
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
function canPromptForUpdate() {
|
|
43
|
+
return Boolean(input.isTTY && output.isTTY && process.env.CI !== 'true');
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
function runGlobalUpdate(packageName) {
|
|
47
|
+
execSync(`npm install -g ${packageName}@latest`, { stdio: 'inherit' });
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
export async function maybeOfferCliUpdate({
|
|
51
|
+
packageName,
|
|
52
|
+
currentVersion,
|
|
53
|
+
packageDir,
|
|
54
|
+
timeoutMs = DEFAULT_TIMEOUT_MS,
|
|
55
|
+
} = {}) {
|
|
56
|
+
if (!packageName || !currentVersion || !canPromptForUpdate()) return;
|
|
57
|
+
|
|
58
|
+
const latestVersion = await fetchLatestVersion(packageName, timeoutMs);
|
|
59
|
+
if (!latestVersion || !isVersionGreater(latestVersion, currentVersion)) return;
|
|
60
|
+
|
|
61
|
+
console.log(pc.yellow(`\nA newer ${packageName} is available: ${currentVersion} -> ${latestVersion}`));
|
|
62
|
+
console.log(pc.dim('Press Enter to update now, or type "n" to continue without updating.'));
|
|
63
|
+
|
|
64
|
+
const rl = createInterface({ input, output });
|
|
65
|
+
try {
|
|
66
|
+
const answer = (await rl.question('Update now? [Y/n] ')).trim().toLowerCase();
|
|
67
|
+
const shouldUpdate = answer === '' || answer === 'y' || answer === 'yes';
|
|
68
|
+
if (!shouldUpdate) return;
|
|
69
|
+
|
|
70
|
+
try {
|
|
71
|
+
runGlobalUpdate(packageName);
|
|
72
|
+
console.log(pc.green(`Updated ${packageName} to latest. Restarting command...`));
|
|
73
|
+
process.chdir(packageDir || process.cwd());
|
|
74
|
+
} catch (err) {
|
|
75
|
+
console.log(pc.red('Update failed. Continuing with current version.'));
|
|
76
|
+
if (err?.message) console.log(pc.dim(err.message));
|
|
77
|
+
}
|
|
78
|
+
} finally {
|
|
79
|
+
rl.close();
|
|
80
|
+
}
|
|
81
|
+
}
|
|
@@ -3,7 +3,7 @@ const path = require('path');
|
|
|
3
3
|
const ROOT_DIR = process.cwd();
|
|
4
4
|
|
|
5
5
|
function extractImports(content) {
|
|
6
|
-
const importRegex =
|
|
6
|
+
const importRegex = /^\s*import\s+(?:[^'"\n;]+?\s+from\s+)?['"]([^'"]+)['"]\s*;?\s*$/gm;
|
|
7
7
|
const imports = [];
|
|
8
8
|
let match;
|
|
9
9
|
|
|
@@ -36,7 +36,7 @@ function processScriptFile(filePath, processedFiles = new Set()) {
|
|
|
36
36
|
importedContent += processScriptFile(importPath, processedFiles);
|
|
37
37
|
}
|
|
38
38
|
|
|
39
|
-
content = content.replace(
|
|
39
|
+
content = content.replace(/^\s*import\s+(?:[^'"\n;]+?\s+from\s+)?['"][^'"]+['"]\s*;?\s*$/gm, '');
|
|
40
40
|
|
|
41
41
|
if (process.env.NODE_ENV === 'production') {
|
|
42
42
|
content = content.replace(/\/\*\*[\s\S]*?\*\//g, '');
|