climaybe 1.9.1 → 2.1.0

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
@@ -231,6 +231,14 @@ Enabled via `climaybe init` prompt (`Enable preview + cleanup workflows?`; defau
231
231
 
232
232
  Enabled via `climaybe init` prompt (`Enable build + Lighthouse workflows?`; default: yes).
233
233
 
234
+ When enabled, `init` validates required theme files and exits with an error if any are missing:
235
+ - `_scripts/main.js`
236
+ - `_styles/main.css`
237
+ - `assets/`
238
+ - `release-notes.md`
239
+
240
+ `climaybe` auto-installs the shared build script at `.climaybe/build-scripts.js` during workflow scaffolding.
241
+
234
242
  | Workflow | Trigger | What it does |
235
243
  |----------|---------|-------------|
236
244
  | `build-pipeline.yml` | Push to any branch | Runs reusable build and Lighthouse checks (when required secrets exist) |
package/bin/version.txt CHANGED
@@ -1 +1 @@
1
- 1.9.1
1
+ 2.1.0
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "climaybe",
3
- "version": "1.9.1",
3
+ "version": "2.1.0",
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": {
@@ -17,6 +17,7 @@ import { scaffoldWorkflows } from '../lib/workflows.js';
17
17
  import { createStoreDirectories } from '../lib/store-sync.js';
18
18
  import { scaffoldCommitlint } from '../lib/commit-tooling.js';
19
19
  import { scaffoldCursorBundle } from '../lib/cursor-bundle.js';
20
+ import { getMissingBuildWorkflowRequirements, getBuildScriptRelativePath } from '../lib/build-workflows.js';
20
21
  import {
21
22
  isGhAvailable,
22
23
  hasGitHubRemote,
@@ -47,6 +48,20 @@ async function runInitFlow() {
47
48
 
48
49
  console.log(pc.dim(`\n Mode: ${mode}-store (${stores.length} store(s))`));
49
50
 
51
+ if (enableBuildWorkflows) {
52
+ const missingBuildFiles = getMissingBuildWorkflowRequirements();
53
+ if (missingBuildFiles.length > 0) {
54
+ console.log(pc.red('\n Build workflows are enabled, but required files are missing:'));
55
+ for (const req of missingBuildFiles) {
56
+ const expected = req.kind === 'dir' ? `${req.path}/` : req.path;
57
+ console.log(pc.red(` - ${expected}`));
58
+ }
59
+ console.log(pc.dim(`\n climaybe will auto-install ${getBuildScriptRelativePath()} during workflow scaffolding.`));
60
+ console.log(pc.dim(' Add the missing files above, then run init again.\n'));
61
+ return;
62
+ }
63
+ }
64
+
50
65
  // 2. Build config
51
66
  const config = {
52
67
  project_type: 'theme',
@@ -0,0 +1,66 @@
1
+ import { copyFileSync, existsSync, mkdirSync, rmSync } from 'node:fs';
2
+ import { join } from 'node:path';
3
+ import { fileURLToPath } from 'node:url';
4
+ import { readFileSync, writeFileSync } from 'node:fs';
5
+
6
+ const SCRIPT_SOURCE = fileURLToPath(new URL('../workflows/build/build-scripts.js', import.meta.url));
7
+ const SCRIPT_TARGET = '.climaybe/build-scripts.js';
8
+ const SCRIPT_SHIM_TARGET = 'build-scripts.js';
9
+ const SCRIPT_SHIM_CONTENT = `// Auto-generated by climaybe. Keep for npm scripts compatibility.
10
+ // Delegates to the bundled implementation in .climaybe/.
11
+ const { buildScripts } = require('./.climaybe/build-scripts.js');
12
+
13
+ if (require.main === module) {
14
+ buildScripts();
15
+ }
16
+
17
+ module.exports = { buildScripts };
18
+ `;
19
+
20
+ const REQUIRED_PATHS = [
21
+ { path: '_scripts/main.js', kind: 'file' },
22
+ { path: '_styles/main.css', kind: 'file' },
23
+ { path: 'assets', kind: 'dir' },
24
+ { path: 'release-notes.md', kind: 'file' },
25
+ ];
26
+
27
+ function targetScriptPath(cwd = process.cwd()) {
28
+ return join(cwd, SCRIPT_TARGET);
29
+ }
30
+
31
+ export function installBuildScript(cwd = process.cwd()) {
32
+ const target = targetScriptPath(cwd);
33
+ mkdirSync(join(cwd, '.climaybe'), { recursive: true });
34
+ copyFileSync(SCRIPT_SOURCE, target);
35
+ const shimTarget = join(cwd, SCRIPT_SHIM_TARGET);
36
+ if (!existsSync(shimTarget)) {
37
+ writeFileSync(shimTarget, SCRIPT_SHIM_CONTENT, 'utf-8');
38
+ }
39
+ return target;
40
+ }
41
+
42
+ export function removeBuildScript(cwd = process.cwd()) {
43
+ const target = targetScriptPath(cwd);
44
+ if (existsSync(target)) rmSync(target);
45
+ const shimTarget = join(cwd, SCRIPT_SHIM_TARGET);
46
+ if (!existsSync(shimTarget)) return;
47
+ const content = readFileSync(shimTarget, 'utf-8');
48
+ if (content === SCRIPT_SHIM_CONTENT) {
49
+ rmSync(shimTarget);
50
+ }
51
+ }
52
+
53
+ export function getMissingBuildWorkflowRequirements(cwd = process.cwd()) {
54
+ const missing = [];
55
+ for (const req of REQUIRED_PATHS) {
56
+ const abs = join(cwd, req.path);
57
+ if (!existsSync(abs)) {
58
+ missing.push(req);
59
+ }
60
+ }
61
+ return missing;
62
+ }
63
+
64
+ export function getBuildScriptRelativePath() {
65
+ return SCRIPT_TARGET;
66
+ }
@@ -2,6 +2,7 @@ import { existsSync, mkdirSync, readdirSync, copyFileSync, rmSync } from 'node:f
2
2
  import { join, dirname } from 'node:path';
3
3
  import { fileURLToPath } from 'node:url';
4
4
  import pc from 'picocolors';
5
+ import { installBuildScript, removeBuildScript } from './build-workflows.js';
5
6
 
6
7
  const __dirname = dirname(fileURLToPath(import.meta.url));
7
8
  const TEMPLATES_DIR = join(__dirname, '..', 'workflows');
@@ -109,6 +110,9 @@ export function scaffoldWorkflows(mode = 'single', options = {}, cwd = process.c
109
110
  for (const f of listYmls(buildDir)) {
110
111
  copyWorkflow(buildDir, f, dest);
111
112
  }
113
+ installBuildScript(cwd);
114
+ } else {
115
+ removeBuildScript(cwd);
112
116
  }
113
117
 
114
118
  const total = readdirSync(dest).filter((f) => f.endsWith('.yml')).length;
@@ -0,0 +1,76 @@
1
+ const fs = require('fs');
2
+ const path = require('path');
3
+
4
+ function extractImports(content) {
5
+ const importRegex = /import\s+['"]([^'"]+)['"];?/g;
6
+ const imports = [];
7
+ let match;
8
+
9
+ while ((match = importRegex.exec(content)) !== null) {
10
+ imports.push(match[1]);
11
+ }
12
+
13
+ return imports;
14
+ }
15
+
16
+ function processScriptFile(filePath, processedFiles = new Set()) {
17
+ if (processedFiles.has(filePath)) {
18
+ return '';
19
+ }
20
+
21
+ processedFiles.add(filePath);
22
+
23
+ const fullPath = path.join(__dirname, '_scripts', filePath);
24
+
25
+ if (!fs.existsSync(fullPath)) {
26
+ console.warn(`Warning: File ${filePath} not found`);
27
+ return '';
28
+ }
29
+
30
+ let content = fs.readFileSync(fullPath, 'utf8');
31
+ const imports = extractImports(content);
32
+
33
+ let importedContent = '';
34
+ for (const importPath of imports) {
35
+ importedContent += processScriptFile(importPath, processedFiles);
36
+ }
37
+
38
+ content = content.replace(/import\s+['"][^'"]+['"];?\s*/g, '');
39
+
40
+ if (process.env.NODE_ENV === 'production') {
41
+ content = content.replace(/\/\*\*[\s\S]*?\*\//g, '');
42
+ content = content.replace(/^\s*\*.*$/gm, '');
43
+ content = content.replace(/console\.(log|warn|error)\([^)]*\);?\s*/g, '');
44
+ content = content.replace(/^\s*\n/gm, '');
45
+ }
46
+
47
+ return importedContent + '\n' + content;
48
+ }
49
+
50
+ function buildScripts() {
51
+ try {
52
+ if (global.gc) global.gc();
53
+
54
+ const mainPath = path.join(__dirname, '_scripts', 'main.js');
55
+ fs.readFileSync(mainPath, 'utf8');
56
+
57
+ const processedFiles = new Set();
58
+ const finalContent = processScriptFile('main.js', processedFiles);
59
+ const outputPath = path.join(__dirname, 'assets', 'index.js');
60
+ fs.writeFileSync(outputPath, finalContent.trim() + '\n');
61
+
62
+ const fileCount = processedFiles.size;
63
+ console.log(`✅ Scripts built (${fileCount} files)`);
64
+ processedFiles.clear();
65
+ if (global.gc) global.gc();
66
+ } catch (error) {
67
+ console.error('❌ Build error:', error.message);
68
+ process.exit(1);
69
+ }
70
+ }
71
+
72
+ if (require.main === module) {
73
+ buildScripts();
74
+ }
75
+
76
+ module.exports = { buildScripts };
@@ -28,7 +28,7 @@ jobs:
28
28
  run: npm ci
29
29
 
30
30
  - name: Build scripts
31
- run: node build-scripts.js
31
+ run: node .climaybe/build-scripts.js
32
32
 
33
33
  - name: Run Tailwind build
34
34
  id: build