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 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
- // Prefer version baked at publish (bin/version.txt) so the running binary always reports its own version
14
- const versionFile = join(binDir, 'version.txt');
15
- let version;
16
- if (existsSync(versionFile)) {
17
- version = readFileSync(versionFile, 'utf8').trim();
18
- } else {
19
- version = require(join(packageDir, 'package.json')).version;
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.2
1
+ 2.2.4
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "climaybe",
3
- "version": "2.2.2",
3
+ "version": "2.2.4",
4
4
  "description": "Shopify CLI — theme CI/CD (workflows, branches, stores) and app repo helpers; also: commitlint and Cursor rules/skills",
5
5
  "type": "module",
6
6
  "bin": {
@@ -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 = /import\s+['"]([^'"]+)['"];?/g;
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(/import\s+['"][^'"]+['"];?\s*/g, '');
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, '');