climaybe 3.1.1 → 3.1.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/README.md CHANGED
@@ -94,7 +94,7 @@ Switch your local dev environment to a specific store (multi-store only).
94
94
  npx climaybe switch voldt-norway
95
95
  ```
96
96
 
97
- Copies `stores/<alias>/` JSON files to the repo root so you can preview that store locally.
97
+ Copies `stores/<alias>/` JSON files to the repo root so you can preview that store locally, and sets `default_store` in `climaybe.config.json` so `climaybe serve` / `shopify theme dev` targets that store. If your current git branch is `staging-<alias>` or `live-<alias>`, `climaybe serve` uses that store’s domain from config (even if `default_store` differs), matching CI preview behavior.
98
98
 
99
99
  ### `climaybe sync [alias]` / `climaybe theme sync`
100
100
 
package/bin/version.txt CHANGED
@@ -1 +1 @@
1
- 3.1.1
1
+ 3.1.2
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "climaybe",
3
- "version": "3.1.1",
3
+ "version": "3.1.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": {
@@ -1,5 +1,5 @@
1
1
  import pc from 'picocolors';
2
- import { getStoreAliases, getMode } from '../lib/config.js';
2
+ import { getStoreAliases, getMode, readConfig, writeConfig } from '../lib/config.js';
3
3
  import { storesToRoot } from '../lib/store-sync.js';
4
4
  import { requireThemeProject } from '../lib/theme-guard.js';
5
5
 
@@ -23,6 +23,11 @@ export async function switchCommand(alias) {
23
23
 
24
24
  const ok = storesToRoot(alias);
25
25
  if (ok) {
26
+ const config = readConfig();
27
+ const domain = config?.stores?.[alias];
28
+ if (domain) {
29
+ writeConfig({ default_store: domain });
30
+ }
26
31
  console.log(pc.bold(pc.green(`\n Switched to store: ${alias}\n`)));
27
32
  console.log(pc.dim(' Root JSON files now reflect this store\'s data.'));
28
33
  console.log(pc.dim(' Use "climaybe theme sync" (or "climaybe sync") to write changes back.\n'));
package/src/index.js CHANGED
@@ -77,7 +77,7 @@ function registerThemeCommands(cmd) {
77
77
  cmd.command('build').description('Build assets (Tailwind + scripts build)').action(() => buildAll());
78
78
  cmd
79
79
  .command('build-scripts')
80
- .description('Build _scripts → assets/index.js')
80
+ .description('Build _scripts → assets/*.js (+ *.min.js)')
81
81
  .option('--minify', 'Minify output bundles')
82
82
  .action(buildScriptsCommand);
83
83
  cmd
@@ -40,6 +40,8 @@ function stripModuleSyntax(content) {
40
40
  );
41
41
 
42
42
  cleaned = cleaned.replace(/^\s*export\s+default\s+/gm, '');
43
+ // Remove named exports (single-line and multiline forms).
44
+ cleaned = cleaned.replace(/(^|\n)\s*export\s*\{[\s\S]*?\}\s*;?/g, '$1');
43
45
  cleaned = cleaned.replace(/^\s*export\s+\{[^}]*\}\s*;?\s*$/gm, '');
44
46
  cleaned = cleaned.replace(/^\s*export\s+(?=(const|let|var|function|class)\b)/gm, '');
45
47
  return cleaned;
@@ -172,6 +174,12 @@ function outputNameForEntrypoint(entryFile) {
172
174
  return basename(entryFile);
173
175
  }
174
176
 
177
+ function outputMinNameForEntrypoint(entryFile) {
178
+ const name = outputNameForEntrypoint(entryFile);
179
+ if (!name.endsWith('.js')) return `${name}.min.js`;
180
+ return name.replace(/\.js$/, '.min.js');
181
+ }
182
+
175
183
  function buildSingleEntrypoint({ cwd, entryFile, minify = false }) {
176
184
  const scriptsDir = join(cwd, '_scripts');
177
185
  const entryPath = join(scriptsDir, entryFile);
@@ -179,26 +187,43 @@ function buildSingleEntrypoint({ cwd, entryFile, minify = false }) {
179
187
  throw new Error(`Missing required file: _scripts/${entryFile}`);
180
188
  }
181
189
 
182
- const processedFiles = new Set();
190
+ const processedFilesReadable = new Set();
191
+ const processedFilesMinified = new Set();
183
192
  const isolateFiles = collectFilesToIsolate({ scriptsDir, entryFile });
184
- let finalContent = processScriptFile({
193
+ const readableContent = processScriptFile({
185
194
  scriptsDir,
186
195
  filePath: entryFile,
187
- processedFiles,
188
- minify,
196
+ processedFiles: processedFilesReadable,
197
+ minify: false,
198
+ isolateFiles
199
+ });
200
+
201
+ const minifiedContent = processScriptFile({
202
+ scriptsDir,
203
+ filePath: entryFile,
204
+ processedFiles: processedFilesMinified,
205
+ minify: true,
189
206
  isolateFiles
190
207
  });
191
- finalContent = stripModuleSyntax(finalContent);
192
- if (minify) finalContent = minifyScriptContent(finalContent);
193
208
 
194
209
  const assetsDir = join(cwd, 'assets');
195
210
  mkdirSync(assetsDir, { recursive: true });
196
211
 
197
212
  const outFile = outputNameForEntrypoint(entryFile);
198
213
  const outputPath = join(assetsDir, outFile);
199
- writeFileSync(outputPath, finalContent.trim() + '\n', 'utf-8');
214
+ const outReadable = stripModuleSyntax(readableContent).trim() + '\n';
215
+ const outMinified = minifyScriptContent(stripModuleSyntax(minifiedContent)).trim() + '\n';
216
+
217
+ // Back-compat: when minify=true, the primary output file is minified.
218
+ writeFileSync(outputPath, (minify ? outMinified : outReadable), 'utf-8');
219
+
220
+ // Always emit an explicit *.min.js asset so Shopify won't attempt CDN minification.
221
+ const outMinFile = outputMinNameForEntrypoint(entryFile);
222
+ const outputMinPath = join(assetsDir, outMinFile);
223
+ writeFileSync(outputMinPath, outMinified, 'utf-8');
200
224
 
201
- return { entryFile, fileCount: processedFiles.size, outputPath };
225
+ const fileCount = Math.max(processedFilesReadable.size, processedFilesMinified.size);
226
+ return { entryFile, fileCount, outputPath, outputMinPath };
202
227
  }
203
228
 
204
229
  export function buildScripts({ cwd = process.cwd(), entry = null, minify = false } = {}) {
package/src/lib/config.js CHANGED
@@ -1,6 +1,6 @@
1
1
  import { readFileSync, writeFileSync, existsSync, mkdirSync } from 'node:fs';
2
2
  import { join, dirname } from 'node:path';
3
- import { getLatestTagVersion } from './git.js';
3
+ import { getLatestTagVersion, isGitRepo, currentBranch } from './git.js';
4
4
 
5
5
  const PKG = 'package.json';
6
6
  const CLIMAYBE_CONFIG = 'climaybe.config.json';
@@ -188,6 +188,37 @@ export function getMode(cwd = process.cwd()) {
188
188
  return aliases.length > 1 ? 'multi' : 'single';
189
189
  }
190
190
 
191
+ /**
192
+ * When on git branches `staging-<alias>` or `live-<alias>`, resolve the Shopify domain
193
+ * for that store from config. Returns null if not a git repo, branch doesn't match,
194
+ * or the alias is unknown.
195
+ * @param {string} [cwd]
196
+ * @returns {string | null}
197
+ */
198
+ export function getStoreDomainFromBranch(cwd = process.cwd()) {
199
+ if (!isGitRepo(cwd)) return null;
200
+ let branch;
201
+ try {
202
+ branch = currentBranch(cwd);
203
+ } catch {
204
+ return null;
205
+ }
206
+ const config = readConfig(cwd);
207
+ const stores = config?.stores;
208
+ if (!stores || typeof stores !== 'object') return null;
209
+
210
+ let alias = '';
211
+ if (branch.startsWith('staging-')) {
212
+ alias = branch.slice('staging-'.length);
213
+ } else if (branch.startsWith('live-')) {
214
+ alias = branch.slice('live-'.length);
215
+ }
216
+ if (!alias) return null;
217
+
218
+ const domain = stores[alias];
219
+ return typeof domain === 'string' && domain.trim() ? domain.trim() : null;
220
+ }
221
+
191
222
  /**
192
223
  * Whether optional preview/cleanup workflows are enabled.
193
224
  */
@@ -3,7 +3,7 @@ import { existsSync } from 'node:fs';
3
3
  import { watchTree } from './watch.js';
4
4
  import { isAbsolute, join, relative } from 'node:path';
5
5
  import pc from 'picocolors';
6
- import { readConfig } from './config.js';
6
+ import { readConfig, getStoreDomainFromBranch } from './config.js';
7
7
  import { buildScripts } from './build-scripts.js';
8
8
  import { buildSchemas } from './schema-builder.js';
9
9
  import { runShopify } from './shopify-cli.js';
@@ -211,7 +211,8 @@ function runThemeCheckFiltered({ cwd = process.cwd() } = {}) {
211
211
 
212
212
  export function serveShopify({ cwd = process.cwd() } = {}) {
213
213
  const config = readConfig(cwd) || {};
214
- const store = config.default_store || config.store || '';
214
+ const branchStore = getStoreDomainFromBranch(cwd);
215
+ const store = branchStore || config.default_store || config.store || '';
215
216
  const args = ['theme', 'dev', '--theme-editor-sync'];
216
217
  if (store) args.push(`--store=${store}`);
217
218
  // Keep Shopify on inherited stdio so reconciliation prompts remain interactive.
@@ -95,6 +95,7 @@ node_modules/
95
95
  **/.DS_Store
96
96
  assets/style.css
97
97
  assets/index.js
98
+ assets/index.min.js
98
99
  .shopify
99
100
  .vercel
100
101
  `;