climaybe 2.2.2 → 2.2.3

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
@@ -5,6 +5,7 @@ import { createRequire } from 'node:module';
5
5
  import { dirname, join } from 'node:path';
6
6
  import { fileURLToPath } from 'node:url';
7
7
  import { run } from '../src/index.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));
@@ -19,4 +20,6 @@ if (existsSync(versionFile)) {
19
20
  version = require(join(packageDir, 'package.json')).version;
20
21
  }
21
22
 
23
+ const packageName = require(join(packageDir, 'package.json')).name || 'climaybe';
24
+ await maybeOfferCliUpdate({ packageName, currentVersion: version, packageDir });
22
25
  run(process.argv, version, packageDir);
package/bin/version.txt CHANGED
@@ -1 +1 @@
1
- 2.2.2
1
+ 2.2.3
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "climaybe",
3
- "version": "2.2.2",
3
+ "version": "2.2.3",
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,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, '');