@runwell/shopify-toolkit 0.11.0 → 0.12.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/bin/runwell-shopify +7 -0
- package/lib/diff-baseline.js +131 -0
- package/package.json +1 -1
package/bin/runwell-shopify
CHANGED
|
@@ -10,6 +10,7 @@ import { validate } from '../lib/validate.js';
|
|
|
10
10
|
import { qa } from '../lib/qa.js';
|
|
11
11
|
import { init } from '../lib/init.js';
|
|
12
12
|
import { upgradeBaseline } from '../lib/upgrade-baseline.js';
|
|
13
|
+
import { diffBaseline } from '../lib/diff-baseline.js';
|
|
13
14
|
|
|
14
15
|
const __filename = fileURLToPath(import.meta.url);
|
|
15
16
|
const __dirname = path.dirname(__filename);
|
|
@@ -56,6 +57,9 @@ Commands:
|
|
|
56
57
|
to override the default pin.
|
|
57
58
|
upgrade-baseline <pkg@version> Bump the baseline pin in runwell.config.json.
|
|
58
59
|
Run "runwell-shopify sync" after to apply.
|
|
60
|
+
diff-baseline <pkg@version> Compare the currently pinned baseline against
|
|
61
|
+
a target version. Lists files added, removed,
|
|
62
|
+
modified, plus patch deltas. Run before upgrade.
|
|
59
63
|
help Show this help
|
|
60
64
|
|
|
61
65
|
Common options:
|
|
@@ -99,6 +103,9 @@ flags.toolkitRoot = TOOLKIT_ROOT;
|
|
|
99
103
|
case 'upgrade-baseline':
|
|
100
104
|
await upgradeBaseline(flags);
|
|
101
105
|
break;
|
|
106
|
+
case 'diff-baseline':
|
|
107
|
+
await diffBaseline(flags);
|
|
108
|
+
break;
|
|
102
109
|
case undefined:
|
|
103
110
|
case 'help':
|
|
104
111
|
case '--help':
|
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
import fs from 'node:fs';
|
|
2
|
+
import path from 'node:path';
|
|
3
|
+
import crypto from 'node:crypto';
|
|
4
|
+
import { loadConfig } from './config-loader.js';
|
|
5
|
+
import { resolveBaseline, parseBaselinePin, loadBaselineManifest } from './baseline.js';
|
|
6
|
+
|
|
7
|
+
/* runwell-shopify diff-baseline <pin>: compare the current pinned
|
|
8
|
+
baseline against a target version. Reports owned files added /
|
|
9
|
+
removed / modified plus patch file changes. Useful before running
|
|
10
|
+
upgrade-baseline so the operator sees what will change.
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
export async function diffBaseline(flags) {
|
|
14
|
+
const targetPin = flags.positional && flags.positional[0];
|
|
15
|
+
if (!targetPin) {
|
|
16
|
+
console.error('Usage: runwell-shopify diff-baseline <package@version>');
|
|
17
|
+
console.error('Example: runwell-shopify diff-baseline @runwell/dawn-runwell@1.1.0');
|
|
18
|
+
process.exit(1);
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
const configPath = path.resolve(flags.config || './runwell.config.json');
|
|
22
|
+
const { config } = loadConfig(configPath);
|
|
23
|
+
const currentPin = config.baseline;
|
|
24
|
+
if (!currentPin) {
|
|
25
|
+
console.error('runwell.config.json has no "baseline" pin. Set one with runwell-shopify init or upgrade-baseline first.');
|
|
26
|
+
process.exit(1);
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
console.log(`current: ${currentPin}`);
|
|
30
|
+
console.log(`target: ${targetPin}`);
|
|
31
|
+
console.log('');
|
|
32
|
+
|
|
33
|
+
// Resolve both versions
|
|
34
|
+
const currentRoot = resolveBaseline(flags, config);
|
|
35
|
+
const targetRoot = resolveBaseline({}, { baseline: targetPin });
|
|
36
|
+
if (!currentRoot) {
|
|
37
|
+
console.error(`Could not resolve current baseline ${currentPin}`);
|
|
38
|
+
process.exit(1);
|
|
39
|
+
}
|
|
40
|
+
if (!targetRoot) {
|
|
41
|
+
console.error(`Could not resolve target baseline ${targetPin}`);
|
|
42
|
+
process.exit(1);
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
const currentMan = loadBaselineManifest(currentRoot);
|
|
46
|
+
const targetMan = loadBaselineManifest(targetRoot);
|
|
47
|
+
|
|
48
|
+
// Compare owned files
|
|
49
|
+
const currentFiles = enumerateOwned(currentMan);
|
|
50
|
+
const targetFiles = enumerateOwned(targetMan);
|
|
51
|
+
|
|
52
|
+
const added = [...targetFiles].filter(f => !currentFiles.has(f));
|
|
53
|
+
const removed = [...currentFiles].filter(f => !targetFiles.has(f));
|
|
54
|
+
const common = [...targetFiles].filter(f => currentFiles.has(f));
|
|
55
|
+
|
|
56
|
+
const modified = [];
|
|
57
|
+
for (const rel of common) {
|
|
58
|
+
const a = path.join(currentRoot, rel);
|
|
59
|
+
const b = path.join(targetRoot, rel);
|
|
60
|
+
if (!fs.existsSync(a) || !fs.existsSync(b)) continue;
|
|
61
|
+
if (fileHash(a) !== fileHash(b)) modified.push(rel);
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
// Compare patches
|
|
65
|
+
const patchKeys = new Set([...Object.keys(currentMan.patches || {}), ...Object.keys(targetMan.patches || {})]);
|
|
66
|
+
const patchChanges = [];
|
|
67
|
+
for (const k of patchKeys) {
|
|
68
|
+
const cur = (currentMan.patches || {})[k];
|
|
69
|
+
const tgt = (targetMan.patches || {})[k];
|
|
70
|
+
if (!cur && tgt) patchChanges.push({ kind: 'added-patch', file: k });
|
|
71
|
+
else if (cur && !tgt) patchChanges.push({ kind: 'removed-patch', file: k });
|
|
72
|
+
else if (cur && tgt) {
|
|
73
|
+
const ca = path.join(currentRoot, cur);
|
|
74
|
+
const cb = path.join(targetRoot, tgt);
|
|
75
|
+
if (fs.existsSync(ca) && fs.existsSync(cb) && fileHash(ca) !== fileHash(cb)) {
|
|
76
|
+
patchChanges.push({ kind: 'modified-patch', file: k });
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
// Compatibility / metadata diff
|
|
82
|
+
const metaChanges = [];
|
|
83
|
+
if (currentMan.compatibility?.toolkit !== targetMan.compatibility?.toolkit) {
|
|
84
|
+
metaChanges.push(`toolkit compatibility: ${currentMan.compatibility?.toolkit || '(none)'} -> ${targetMan.compatibility?.toolkit || '(none)'}`);
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
// Print summary
|
|
88
|
+
console.log(`Owned files: ${added.length} added, ${removed.length} removed, ${modified.length} modified, ${common.length - modified.length} unchanged`);
|
|
89
|
+
console.log(`Patches: ${patchChanges.length} changes`);
|
|
90
|
+
console.log('');
|
|
91
|
+
if (added.length) {
|
|
92
|
+
console.log('Added (will be created on sync):');
|
|
93
|
+
added.forEach(f => console.log(` + ${f}`));
|
|
94
|
+
console.log('');
|
|
95
|
+
}
|
|
96
|
+
if (removed.length) {
|
|
97
|
+
console.log('Removed (will NOT be auto-deleted; manually clean if needed):');
|
|
98
|
+
removed.forEach(f => console.log(` - ${f}`));
|
|
99
|
+
console.log('');
|
|
100
|
+
}
|
|
101
|
+
if (modified.length) {
|
|
102
|
+
console.log('Modified (sync will overwrite tenant theme dir):');
|
|
103
|
+
modified.forEach(f => console.log(` ~ ${f}`));
|
|
104
|
+
console.log('');
|
|
105
|
+
}
|
|
106
|
+
if (patchChanges.length) {
|
|
107
|
+
console.log('Patch changes (sync will deep-merge):');
|
|
108
|
+
patchChanges.forEach(p => console.log(` ${p.kind === 'added-patch' ? '+' : p.kind === 'removed-patch' ? '-' : '~'} ${p.file}`));
|
|
109
|
+
console.log('');
|
|
110
|
+
}
|
|
111
|
+
if (metaChanges.length) {
|
|
112
|
+
console.log('Metadata:');
|
|
113
|
+
metaChanges.forEach(m => console.log(` ${m}`));
|
|
114
|
+
console.log('');
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
console.log(`Apply with: runwell-shopify upgrade-baseline ${targetPin} && runwell-shopify sync`);
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
function enumerateOwned(manifest) {
|
|
121
|
+
const out = new Set();
|
|
122
|
+
for (const [bucket, files] of Object.entries(manifest.files || {})) {
|
|
123
|
+
if (!Array.isArray(files)) continue;
|
|
124
|
+
for (const rel of files) out.add(`${bucket}/${rel}`);
|
|
125
|
+
}
|
|
126
|
+
return out;
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
function fileHash(p) {
|
|
130
|
+
return crypto.createHash('sha1').update(fs.readFileSync(p)).digest('hex');
|
|
131
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@runwell/shopify-toolkit",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.12.0",
|
|
4
4
|
"description": "Reusable Shopify theme modules from Runwell. Replaces typically app-driven features (reviews, wishlist, urgency, FAQ, post-purchase upsell, exit popups, free-ship progress, sticky ATC, testimonials, badges, bundles) with native Liquid + JS + CSS that ship across multiple client themes via a config-driven sync CLI.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "lib/index.js",
|