climaybe 3.3.0 → 3.4.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 +2 -0
- package/bin/version.txt +1 -1
- package/package.json +1 -1
- package/src/commands/build-scripts.js +7 -1
- package/src/cursor/rules/00-rule-index.mdc +1 -0
- package/src/cursor/rules/javascript-standards.mdc +3 -1
- package/src/cursor/rules/js-refactor-tasks.mdc +1 -1
- package/src/cursor/rules/project-overview.mdc +20 -2
- package/src/cursor/rules/tailwindcss-rules.mdc +2 -1
- package/src/lib/build-scripts.js +36 -3
- package/src/lib/dev-runtime.js +12 -3
package/README.md
CHANGED
|
@@ -250,6 +250,8 @@ When enabled, builds are **resilient**:
|
|
|
250
250
|
- `init` may offer to create entrypoints; **default answer is No**.
|
|
251
251
|
- Script bundling preserves comments/spacing and emits bundles only for root entry files (files imported by other top-level `_scripts/*.js` are inlined, not emitted separately).
|
|
252
252
|
- Script bundles are written to `assets/*.js` (readable by default; use `climaybe build-scripts --minify` if you want minified output).
|
|
253
|
+
- `assets/*.js`, `assets/style.css`, and the injected `{% schema %}` blocks are **generated outputs** — edit the source in `_scripts/`, `_styles/`, and `_schemas/` instead, since the watcher/build regenerate (and overwrite) the outputs on save and in CI.
|
|
254
|
+
- **Orphan cleanup:** when `_scripts/` is in use, a full build (`climaybe build`, `climaybe build-scripts`, and the `serve` watcher) deletes any `assets/*.js` that no longer has a matching `_scripts/` source. Add new JS via a top-level `_scripts/<name>.js` entry, not by hand-editing `assets/`. Liquid-processed `*.js.liquid` assets and non-JS files are left untouched; targeted single-entry builds (`build-scripts <entry>`) skip pruning.
|
|
253
255
|
- Live minified `assets/*` changes are intentionally excluded from hotfix backports to `main` (no branch-specific `.gitignore` split required).
|
|
254
256
|
|
|
255
257
|
Build workflows install deps with `npm ci` and run `npx --no-install climaybe build-scripts` plus `npx --no-install climaybe build`, so CI uses lockfile-pinned versions (no `@latest` drift).
|
package/bin/version.txt
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
3.
|
|
1
|
+
3.4.0
|
package/package.json
CHANGED
|
@@ -9,7 +9,7 @@ export async function buildScriptsCommand(opts = {}) {
|
|
|
9
9
|
try {
|
|
10
10
|
const minify = opts.minify === true;
|
|
11
11
|
if (global.gc) global.gc();
|
|
12
|
-
const { bundles } = buildScripts({ cwd: process.cwd(), minify });
|
|
12
|
+
const { bundles, removed } = buildScripts({ cwd: process.cwd(), minify });
|
|
13
13
|
if (!bundles || bundles.length === 0) {
|
|
14
14
|
console.log(pc.yellow(' No _scripts/*.js entrypoints found; nothing to build.'));
|
|
15
15
|
return;
|
|
@@ -20,6 +20,12 @@ export async function buildScriptsCommand(opts = {}) {
|
|
|
20
20
|
for (const b of bundles) {
|
|
21
21
|
console.log(pc.dim(` - ${b.entryFile} → ${b.outputPath}`));
|
|
22
22
|
}
|
|
23
|
+
if (removed && removed.length > 0) {
|
|
24
|
+
console.log(pc.yellow(` Removed ${removed.length} orphan asset(s) with no _scripts source:`));
|
|
25
|
+
for (const name of removed) {
|
|
26
|
+
console.log(pc.dim(` - assets/${name}`));
|
|
27
|
+
}
|
|
28
|
+
}
|
|
23
29
|
if (global.gc) global.gc();
|
|
24
30
|
} catch (err) {
|
|
25
31
|
console.log(pc.red(` Build error: ${err.message}`));
|
|
@@ -8,6 +8,7 @@ alwaysApply: true
|
|
|
8
8
|
|
|
9
9
|
When you perform any of the following, **read and apply** the corresponding rule file from `.cursor/rules/`:
|
|
10
10
|
|
|
11
|
+
- **Editing `assets/*.js` or `assets/style.css`, build outputs vs source, where to edit JS/CSS** → `project-overview.mdc` (Build Outputs vs Source — edit `_scripts/`/`_styles/`, never the generated `assets/` files)
|
|
11
12
|
- **Git commits, commit messages** → `commit-rules.mdc`
|
|
12
13
|
- **Accessibility, a11y, focus, WCAG, UI behavior** → `accessibility-rules.mdc`
|
|
13
14
|
- **JavaScript, web components, _scripts/, *.js** → `javascript-standards.mdc`
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
description: Professional JavaScript standards for Electric Maybe Shopify Theme. All JavaScript files must follow these patterns for consistency, performance, and maintainability.
|
|
3
3
|
globs:
|
|
4
|
-
- "
|
|
4
|
+
- "_scripts/**/*.js"
|
|
5
5
|
- "sections/*.liquid"
|
|
6
6
|
- "snippets/*.liquid"
|
|
7
7
|
alwaysApply: false
|
|
@@ -9,6 +9,8 @@ alwaysApply: false
|
|
|
9
9
|
|
|
10
10
|
# JavaScript Development Standards
|
|
11
11
|
|
|
12
|
+
> **Source vs build output:** Edit JavaScript in `_scripts/`. The CLI bundles `_scripts/*.js` into `assets/*.js` (`main.js` → `assets/index.js`) on every save and in CI, so `assets/*.js` is a **generated artifact** — never edit it directly, your changes will be overwritten. See `project-overview.mdc` (Build Outputs vs Source).
|
|
13
|
+
|
|
12
14
|
## Core Principles
|
|
13
15
|
|
|
14
16
|
- **Zero external dependencies** - Use native browser APIs
|
|
@@ -55,8 +55,10 @@ Use `_scripts/electric-modal.js` as the gold standard for all web components.
|
|
|
55
55
|
|
|
56
56
|
## File Organization
|
|
57
57
|
```
|
|
58
|
-
_scripts/ #
|
|
59
|
-
|
|
58
|
+
_scripts/ # Source: web components and JS utilities (edit here)
|
|
59
|
+
_styles/ # Source: Tailwind/CSS entrypoint (_styles/main.css)
|
|
60
|
+
_schemas/ # Source: section/block schema definitions
|
|
61
|
+
assets/ # GENERATED build outputs + static assets (do not hand-edit generated files)
|
|
60
62
|
sections/ # Liquid section files
|
|
61
63
|
snippets/ # Reusable Liquid snippets
|
|
62
64
|
templates/ # Page templates
|
|
@@ -64,6 +66,22 @@ locales/ # Translation files
|
|
|
64
66
|
config/ # Theme settings
|
|
65
67
|
```
|
|
66
68
|
|
|
69
|
+
## Build Outputs vs Source
|
|
70
|
+
|
|
71
|
+
`assets/` contains both static assets and **generated** build outputs. The CLI (`climaybe serve` / `climaybe build`) compiles sources into `assets/` and regenerates them on every save and in CI. Always edit the **source**, never the generated output:
|
|
72
|
+
|
|
73
|
+
| Source (edit this) | Generated output (do NOT edit) |
|
|
74
|
+
| --- | --- |
|
|
75
|
+
| `_scripts/main.js` | `assets/index.js` |
|
|
76
|
+
| `_scripts/<name>.js` (top-level entry) | `assets/<name>.js` |
|
|
77
|
+
| `_styles/main.css` | `assets/style.css` |
|
|
78
|
+
| `_schemas/<name>.js` / `.json` | injected `{% schema %}` block in `sections/*.liquid` & `blocks/*.liquid` |
|
|
79
|
+
|
|
80
|
+
Notes:
|
|
81
|
+
- JS bundling follows `import` statements: files imported by a top-level `_scripts/*.js` entry are **inlined** into that bundle, not emitted separately.
|
|
82
|
+
- Any manual edit to `assets/index.js`, `assets/*.js`, or `assets/style.css` is **overwritten** on the next save (watcher) or build. Make JS changes in `_scripts/`, CSS in `_styles/`, and schema changes in `_schemas/`.
|
|
83
|
+
- **Orphan cleanup:** on a full build (`climaybe build` / `climaybe build-scripts`, and the `serve` watcher), any `assets/*.js` that the current `_scripts/` build does not produce is **deleted** as a stale artifact. To add a new JS asset, create a top-level `_scripts/<name>.js` entry — do not drop a hand-written `.js` into `assets/` (it will be removed). Liquid-processed assets (`*.js.liquid`) and non-JS files are never touched.
|
|
84
|
+
|
|
67
85
|
## Code Review Requirements
|
|
68
86
|
All code must pass the checklist in `javascript-standards.mdc` before merge.
|
|
69
87
|
|
|
@@ -3,13 +3,14 @@ description: Tailwind CSS and theme token standards for Liquid and theme styles.
|
|
|
3
3
|
globs:
|
|
4
4
|
- "**/*.liquid"
|
|
5
5
|
- "_styles/**/*.css"
|
|
6
|
-
- "assets/*.css"
|
|
7
6
|
alwaysApply: false
|
|
8
7
|
---
|
|
9
8
|
# Tailwind CSS Development Rules
|
|
10
9
|
|
|
11
10
|
Apply when editing Liquid templates or theme CSS that use Tailwind classes or theme tokens. Use semantic tokens (surface, subtle, emphasis, accent, text-primary, border-secondary) from this project's `@theme`; see `_styles/02_base/02.02_colors.css`.
|
|
12
11
|
|
|
12
|
+
> **Source vs build output:** Edit CSS in `_styles/` (entrypoint `_styles/main.css`). The CLI compiles it into `assets/style.css` on every save and in CI, so `assets/style.css` is a **generated artifact** — never edit it directly, your changes will be overwritten. See `project-overview.mdc` (Build Outputs vs Source).
|
|
13
|
+
|
|
13
14
|
## Core Principles
|
|
14
15
|
|
|
15
16
|
### 1. Static Class Generation
|
package/src/lib/build-scripts.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { existsSync, readFileSync, writeFileSync, readdirSync, mkdirSync } from 'node:fs';
|
|
1
|
+
import { existsSync, readFileSync, writeFileSync, readdirSync, mkdirSync, unlinkSync } from 'node:fs';
|
|
2
2
|
import { join, basename, dirname, normalize } from 'node:path';
|
|
3
3
|
|
|
4
4
|
function extractImportRecords(content) {
|
|
@@ -161,6 +161,30 @@ function collectFilesToIsolate({ scriptsDir, entryFile }) {
|
|
|
161
161
|
return isolateFiles;
|
|
162
162
|
}
|
|
163
163
|
|
|
164
|
+
function removeOrphanScriptAssets({ cwd, keepNames }) {
|
|
165
|
+
// Delete assets/*.js that this build did not produce. The Electric Maybe build
|
|
166
|
+
// model treats assets/*.js as generated output of _scripts/, so any *.js without
|
|
167
|
+
// a matching bundle output is stale and should be removed. Only plain *.js files
|
|
168
|
+
// are considered — Liquid-processed assets (e.g. *.js.liquid) are left untouched.
|
|
169
|
+
const assetsDir = join(cwd, 'assets');
|
|
170
|
+
if (!existsSync(assetsDir)) return [];
|
|
171
|
+
|
|
172
|
+
const removed = [];
|
|
173
|
+
for (const dirent of readdirSync(assetsDir, { withFileTypes: true })) {
|
|
174
|
+
if (!dirent.isFile()) continue;
|
|
175
|
+
const name = dirent.name;
|
|
176
|
+
if (!name.endsWith('.js')) continue;
|
|
177
|
+
if (keepNames.has(name)) continue;
|
|
178
|
+
try {
|
|
179
|
+
unlinkSync(join(assetsDir, name));
|
|
180
|
+
removed.push(name);
|
|
181
|
+
} catch {
|
|
182
|
+
// Best effort: a failed unlink shouldn't break the build.
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
return removed;
|
|
186
|
+
}
|
|
187
|
+
|
|
164
188
|
function listTopLevelEntrypoints(scriptsDir) {
|
|
165
189
|
if (!existsSync(scriptsDir)) return [];
|
|
166
190
|
return readdirSync(scriptsDir, { withFileTypes: true })
|
|
@@ -234,9 +258,18 @@ export function buildScripts({ cwd = process.cwd(), entry = null, minify = false
|
|
|
234
258
|
}
|
|
235
259
|
}
|
|
236
260
|
if (entrypoints.length === 0) {
|
|
237
|
-
return { bundles: [] };
|
|
261
|
+
return { bundles: [], removed: [] };
|
|
238
262
|
}
|
|
239
263
|
const bundles = entrypoints.map((entryFile) => buildSingleEntrypoint({ cwd, entryFile, minify }));
|
|
240
|
-
|
|
264
|
+
|
|
265
|
+
// Only prune orphans on a full build (no explicit entry). A targeted single-entry
|
|
266
|
+
// build must not delete the other bundles' outputs.
|
|
267
|
+
let removed = [];
|
|
268
|
+
if (!entry) {
|
|
269
|
+
const keepNames = new Set(bundles.map((b) => basename(b.outputPath)));
|
|
270
|
+
removed = removeOrphanScriptAssets({ cwd, keepNames });
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
return { bundles, removed };
|
|
241
274
|
}
|
|
242
275
|
|
package/src/lib/dev-runtime.js
CHANGED
|
@@ -253,8 +253,11 @@ export function serveAssets({ cwd = process.cwd(), includeThemeCheck = false } =
|
|
|
253
253
|
const scriptsDir = join(cwd, '_scripts');
|
|
254
254
|
if (existsSync(scriptsDir)) {
|
|
255
255
|
try {
|
|
256
|
-
buildScripts({ cwd });
|
|
256
|
+
const { removed } = buildScripts({ cwd });
|
|
257
257
|
writeTaggedLine('scripts', pc.yellow, 'built (initial)');
|
|
258
|
+
if (removed?.length) {
|
|
259
|
+
writeTaggedLine('scripts', pc.yellow, `removed ${removed.length} orphan asset(s): ${removed.join(', ')}`);
|
|
260
|
+
}
|
|
258
261
|
} catch (err) {
|
|
259
262
|
writeTaggedLine('scripts', pc.yellow, `initial build failed: ${err.message}`, process.stderr);
|
|
260
263
|
}
|
|
@@ -268,8 +271,11 @@ export function serveAssets({ cwd = process.cwd(), includeThemeCheck = false } =
|
|
|
268
271
|
debounceMs: 300,
|
|
269
272
|
onChange: () => {
|
|
270
273
|
try {
|
|
271
|
-
buildScripts({ cwd });
|
|
274
|
+
const { removed } = buildScripts({ cwd });
|
|
272
275
|
writeTaggedLine('scripts', pc.yellow, 'rebuilt');
|
|
276
|
+
if (removed?.length) {
|
|
277
|
+
writeTaggedLine('scripts', pc.yellow, `removed ${removed.length} orphan asset(s): ${removed.join(', ')}`);
|
|
278
|
+
}
|
|
273
279
|
} catch (err) {
|
|
274
280
|
writeTaggedLine('scripts', pc.yellow, `build failed: ${err.message}`, process.stderr);
|
|
275
281
|
}
|
|
@@ -449,7 +455,10 @@ export function buildAll({ cwd = process.cwd() } = {}) {
|
|
|
449
455
|
|
|
450
456
|
let scriptsOk = true;
|
|
451
457
|
try {
|
|
452
|
-
buildScripts({ cwd });
|
|
458
|
+
const { removed } = buildScripts({ cwd });
|
|
459
|
+
if (removed?.length) {
|
|
460
|
+
writeTaggedLine('scripts', pc.yellow, `removed ${removed.length} orphan asset(s): ${removed.join(', ')}`);
|
|
461
|
+
}
|
|
453
462
|
} catch (err) {
|
|
454
463
|
console.log(pc.red(`\n build-scripts failed: ${err.message}\n`));
|
|
455
464
|
scriptsOk = false;
|