climaybe 3.0.1 → 3.0.2

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/bin/version.txt CHANGED
@@ -1 +1 @@
1
- 3.0.1
1
+ 3.0.2
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "climaybe",
3
- "version": "3.0.1",
3
+ "version": "3.0.2",
4
4
  "description": "Shopify CLI by Electric Maybe for theme CI/CD workflows, branch orchestration, app setup, and dev tooling",
5
5
  "type": "module",
6
6
  "bin": {
@@ -81,14 +81,14 @@
81
81
  "@shopify/prettier-plugin-liquid": "^1.6.3",
82
82
  "@tailwindcss/cli": "^4.1.17",
83
83
  "@tailwindcss/typography": "^0.5.16",
84
+ "commander": "^14.0.3",
84
85
  "eslint": "^9.11.0",
86
+ "picocolors": "^1.1.1",
85
87
  "prettier": "^3.4.2",
88
+ "prompts": "^2.4.2",
86
89
  "stylelint": "^16.9.0",
87
90
  "stylelint-config-standard": "^36.0.1",
88
- "tailwindcss": "^4.1.18",
89
- "commander": "^14.0.3",
90
- "picocolors": "^1.1.1",
91
- "prompts": "^2.4.2"
91
+ "tailwindcss": "^4.1.18"
92
92
  },
93
93
  "devDependencies": {
94
94
  "@commitlint/cli": "^20.4.4",
@@ -128,8 +128,21 @@ function mergeGitignore(cwd = process.cwd()) {
128
128
  return;
129
129
  }
130
130
  const current = readFileSync(path, 'utf-8');
131
- if (current.includes('# climaybe: theme dev kit (managed)')) return;
132
- const next = `${current.trimEnd()}\n\n${GITIGNORE_BLOCK}`;
131
+ const marker = '# climaybe: theme dev kit (managed)';
132
+ if (current.includes(marker)) {
133
+ const lines = current.split('\n');
134
+ const start = lines.findIndex((line) => line.trim() === marker);
135
+ if (start >= 0) {
136
+ let end = start + 1;
137
+ while (end < lines.length && lines[end].trim() !== '') end += 1;
138
+ const before = lines.slice(0, start).join('\n').trimEnd();
139
+ const after = lines.slice(end).join('\n').trim();
140
+ const segments = [before, GITIGNORE_BLOCK.trimEnd(), after].filter(Boolean);
141
+ writeFileSync(path, `${segments.join('\n\n')}\n`, 'utf-8');
142
+ return;
143
+ }
144
+ }
145
+ const next = `${current.trimEnd()}\n\n${GITIGNORE_BLOCK.trimEnd()}`;
133
146
  writeFileSync(path, `${next}\n`, 'utf-8');
134
147
  }
135
148
 
@@ -151,12 +164,14 @@ function mergePackageJson({ packageName = 'shopify-theme', cwd = process.cwd() }
151
164
  if (!pkg.author) {
152
165
  pkg.author = 'Electric Maybe <hello@electricmaybe.com>';
153
166
  }
167
+ pkg.dependencies = { ...(pkg.dependencies || {}) };
154
168
  pkg.devDependencies = { ...(pkg.devDependencies || {}) };
155
169
 
156
170
  // Ensure teammates can run climaybe + Tailwind after plain npm install.
157
171
  const cliVersion = process.env.CLIMAYBE_PACKAGE_VERSION;
158
172
  const climaybeRange = /^\d+\.\d+\.\d+/.test(String(cliVersion || '')) ? `^${cliVersion}` : 'latest';
159
- if (!pkg.devDependencies.climaybe) pkg.devDependencies.climaybe = climaybeRange;
173
+ if (!pkg.dependencies.climaybe) pkg.dependencies.climaybe = climaybeRange;
174
+ if (pkg.devDependencies.climaybe) delete pkg.devDependencies.climaybe;
160
175
  if (!pkg.devDependencies.tailwindcss) pkg.devDependencies.tailwindcss = 'latest';
161
176
 
162
177
  writePkg(pkg, cwd);
@@ -1,6 +1,8 @@
1
1
  import { execSync } from 'node:child_process';
2
2
  import { stdin as input, stdout as output } from 'node:process';
3
3
  import { createInterface } from 'node:readline/promises';
4
+ import { existsSync, readFileSync } from 'node:fs';
5
+ import { join, resolve } from 'node:path';
4
6
  import pc from 'picocolors';
5
7
 
6
8
  const NPM_REGISTRY_BASE = 'https://registry.npmjs.org';
@@ -43,8 +45,40 @@ function canPromptForUpdate() {
43
45
  return Boolean(input.isTTY && output.isTTY && process.env.CI !== 'true');
44
46
  }
45
47
 
46
- function runGlobalUpdate(packageName) {
47
- execSync(`npm install -g ${packageName}@latest`, { stdio: 'inherit' });
48
+ export function resolveInstallScope({ packageDir, cwd = process.cwd() } = {}) {
49
+ try {
50
+ const globalRoot = execSync('npm root -g', { encoding: 'utf-8', stdio: 'pipe' }).trim();
51
+ if (packageDir && resolve(packageDir).startsWith(resolve(globalRoot))) return 'global';
52
+ } catch {
53
+ // ignore and fallback to local checks
54
+ }
55
+
56
+ if (existsSync(join(cwd, 'package.json'))) return 'local';
57
+ return 'global';
58
+ }
59
+
60
+ function getLocalInstallFlag({ packageName, cwd = process.cwd() } = {}) {
61
+ try {
62
+ const pkgPath = join(cwd, 'package.json');
63
+ if (!existsSync(pkgPath)) return '--save-dev';
64
+ const pkg = JSON.parse(readFileSync(pkgPath, 'utf-8'));
65
+ if (pkg?.dependencies?.[packageName]) return '--save';
66
+ if (pkg?.devDependencies?.[packageName]) return '--save-dev';
67
+ return '--save-dev';
68
+ } catch {
69
+ return '--save-dev';
70
+ }
71
+ }
72
+
73
+ function runUpdate(packageName, { packageDir, cwd = process.cwd() } = {}) {
74
+ const scope = resolveInstallScope({ packageDir, cwd });
75
+ if (scope === 'global') {
76
+ execSync(`npm install -g ${packageName}@latest`, { stdio: 'inherit' });
77
+ return 'global';
78
+ }
79
+ const flag = getLocalInstallFlag({ packageName, cwd });
80
+ execSync(`npm install ${packageName}@latest ${flag}`, { cwd, stdio: 'inherit' });
81
+ return 'local';
48
82
  }
49
83
 
50
84
  export async function maybeOfferCliUpdate({
@@ -68,8 +102,8 @@ export async function maybeOfferCliUpdate({
68
102
  if (!shouldUpdate) return;
69
103
 
70
104
  try {
71
- runGlobalUpdate(packageName);
72
- console.log(pc.green(`Updated ${packageName} to latest. Restarting command...`));
105
+ const updatedScope = runUpdate(packageName, { packageDir, cwd: process.cwd() });
106
+ console.log(pc.green(`Updated ${packageName} (${updatedScope}) to latest. Restarting command...`));
73
107
  process.chdir(packageDir || process.cwd());
74
108
  } catch (err) {
75
109
  console.log(pc.red('Update failed. Continuing with current version.'));