shopify-theme-devtools 2.2.0 → 2.2.1

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
@@ -6,6 +6,20 @@
6
6
 
7
7
  A powerful in-browser developer tools panel for Shopify theme development. Inspect Liquid objects, evaluate expressions, explore metafields, manipulate cart state, detect Liquid errors, and much more — all without leaving the browser.
8
8
 
9
+ ## Table of Contents
10
+
11
+ - [Features](#features)
12
+ - [Quick Start](#quick-start)
13
+ - [Keyboard Shortcuts](#keyboard-shortcuts)
14
+ - [Panel Reference](#panel-reference)
15
+ - [Configuration](#configuration)
16
+ - [Development](#development)
17
+ - [How It Works](#how-it-works)
18
+ - [Tech Stack](#tech-stack)
19
+ - [Browser Support](#browser-support)
20
+ - [Contributing](#contributing)
21
+ - [License](#license)
22
+
9
23
  ## Features
10
24
 
11
25
  ### Core Panels
@@ -118,29 +132,55 @@ Customize your devtools layout like Chrome DevTools:
118
132
 
119
133
  ## Quick Start
120
134
 
121
- ### 1. Add the snippet to your theme
122
-
123
- Copy the Liquid bridge to your theme's snippets folder:
135
+ ### Option A: Using npm (Recommended)
124
136
 
125
137
  ```bash
126
- # Option A: Download directly
127
- curl -o snippets/theme-devtools-bridge.liquid \
128
- https://raw.githubusercontent.com/yakohere/shopify-theme-devtools/main/src/liquid/theme-devtools-bridge.liquid
138
+ # Run directly with npx (no install needed)
139
+ npx shopify-theme-devtools init
129
140
 
130
- # Option B: Copy manually from GitHub
131
- # Visit: https://github.com/yakohere/shopify-theme-devtools/blob/main/src/liquid/theme-devtools-bridge.liquid
132
- # Click "Raw" and save the file to your theme's snippets/ folder
141
+ # Or install globally for repeated use
142
+ npm install -g shopify-theme-devtools
143
+ shopify-theme-devtools init
133
144
  ```
134
145
 
135
- ### 2. Include in your layout
136
-
137
- Add this line to `layout/theme.liquid` just before `</body>`:
146
+ This copies the Liquid snippet to your `snippets/` folder. Then add this line to `layout/theme.liquid` just before `</body>`:
138
147
 
139
148
  ```liquid
140
149
  {% render 'theme-devtools-bridge' %}
141
150
  ```
142
151
 
143
- ### 3. You're done!
152
+ Or use `--inject` to add it automatically:
153
+
154
+ ```bash
155
+ npx shopify-theme-devtools init --inject
156
+ ```
157
+
158
+ #### CLI Options
159
+
160
+ | Option | Description |
161
+ |--------|-------------|
162
+ | `--local` | Copy JS/CSS to `assets/` folder instead of using CDN |
163
+ | `--inject` | Automatically add render tag to `layout/theme.liquid` |
164
+ | `--force` | Overwrite existing files without prompting |
165
+
166
+ ```bash
167
+ # Examples
168
+ npx shopify-theme-devtools init # Uses CDN (recommended)
169
+ npx shopify-theme-devtools init --local # Self-hosted assets
170
+ npx shopify-theme-devtools init --local --inject # Self-hosted + auto-inject
171
+ ```
172
+
173
+ ### Option B: Manual Download
174
+
175
+ ```bash
176
+ # Download directly
177
+ curl -o snippets/theme-devtools-bridge.liquid \
178
+ https://raw.githubusercontent.com/yakohere/shopify-theme-devtools/main/src/liquid/theme-devtools-bridge.liquid
179
+ ```
180
+
181
+ Then add `{% render 'theme-devtools-bridge' %}` to `layout/theme.liquid` before `</body>`.
182
+
183
+ ### You're done!
144
184
 
145
185
  The devtools panel automatically appears on **unpublished/development themes only**. It's safe to commit — it won't render on your live published theme.
146
186
 
@@ -278,7 +318,6 @@ src/
278
318
  - [Lit](https://lit.dev/) — Lightweight Web Components
279
319
  - [LiquidJS](https://liquidjs.com/) — Liquid template engine
280
320
  - [Vite](https://vitejs.dev/) — Fast build tooling
281
- - Single IIFE bundle (~55KB gzipped)
282
321
 
283
322
  ## Browser Support
284
323
 
package/bin/cli.js ADDED
@@ -0,0 +1,273 @@
1
+ #!/usr/bin/env node
2
+
3
+ import { existsSync, mkdirSync, copyFileSync, readFileSync, writeFileSync } from 'fs';
4
+ import { dirname, join, resolve } from 'path';
5
+ import { fileURLToPath } from 'url';
6
+
7
+ const __filename = fileURLToPath(import.meta.url);
8
+ const __dirname = dirname(__filename);
9
+ const packageRoot = resolve(__dirname, '..');
10
+
11
+ const COLORS = {
12
+ reset: '\x1b[0m',
13
+ green: '\x1b[32m',
14
+ yellow: '\x1b[33m',
15
+ blue: '\x1b[34m',
16
+ red: '\x1b[31m',
17
+ dim: '\x1b[2m',
18
+ bold: '\x1b[1m'
19
+ };
20
+
21
+ function log(message, color = '') {
22
+ console.log(`${color}${message}${COLORS.reset}`);
23
+ }
24
+
25
+ function success(message) {
26
+ log(`✓ ${message}`, COLORS.green);
27
+ }
28
+
29
+ function warn(message) {
30
+ log(`⚠ ${message}`, COLORS.yellow);
31
+ }
32
+
33
+ function error(message) {
34
+ log(`✗ ${message}`, COLORS.red);
35
+ }
36
+
37
+ function info(message) {
38
+ log(` ${message}`, COLORS.dim);
39
+ }
40
+
41
+ function printHelp() {
42
+ console.log(`
43
+ ${COLORS.bold}Shopify Theme Devtools CLI${COLORS.reset}
44
+
45
+ ${COLORS.bold}Usage:${COLORS.reset}
46
+ npx shopify-theme-devtools <command> [options]
47
+
48
+ ${COLORS.bold}Commands:${COLORS.reset}
49
+ init Initialize devtools in your Shopify theme
50
+
51
+ ${COLORS.bold}Options for init:${COLORS.reset}
52
+ --local Copy JS/CSS to assets folder instead of using CDN
53
+ --inject Add render tag to layout/theme.liquid automatically
54
+ --force Overwrite existing files without prompting
55
+ --help, -h Show this help message
56
+
57
+ ${COLORS.bold}Examples:${COLORS.reset}
58
+ npx shopify-theme-devtools init
59
+ npx shopify-theme-devtools init --local
60
+ npx shopify-theme-devtools init --local --inject
61
+ `);
62
+ }
63
+
64
+ function ensureDir(dir) {
65
+ if (!existsSync(dir)) {
66
+ mkdirSync(dir, { recursive: true });
67
+ }
68
+ }
69
+
70
+ function copyFile(src, dest, force = false) {
71
+ if (existsSync(dest) && !force) {
72
+ warn(`File already exists: ${dest}`);
73
+ info('Use --force to overwrite');
74
+ return false;
75
+ }
76
+ copyFileSync(src, dest);
77
+ return true;
78
+ }
79
+
80
+ function patchLiquidForLocal(content) {
81
+ // Change CDN to asset_url
82
+ let patched = content.replace(
83
+ /{%- assign devtools_cdn_base = '[^']+' -%}/,
84
+ "{%- assign devtools_cdn_base = '' -%}"
85
+ );
86
+
87
+ // Replace the script loading section for local assets
88
+ patched = patched.replace(
89
+ /<script>\s*window\.__THEME_DEVTOOLS_CSS_URL__[^<]+<\/script>\s*<script src="{{ devtools_cdn_base }}\/theme-devtools\.js" defer><\/script>/s,
90
+ `<script>
91
+ window.__THEME_DEVTOOLS_CSS_URL__ = '{{ 'theme-devtools.css' | asset_url }}';
92
+ </script>
93
+ <script src="{{ 'theme-devtools.js' | asset_url }}" defer></script>`
94
+ );
95
+
96
+ return patched;
97
+ }
98
+
99
+ function injectRenderTag(themeLiquidPath) {
100
+ if (!existsSync(themeLiquidPath)) {
101
+ warn(`layout/theme.liquid not found at: ${themeLiquidPath}`);
102
+ return false;
103
+ }
104
+
105
+ let content = readFileSync(themeLiquidPath, 'utf-8');
106
+ const renderTag = "{% render 'theme-devtools-bridge' %}";
107
+
108
+ if (content.includes('theme-devtools-bridge')) {
109
+ info('Render tag already exists in theme.liquid');
110
+ return true;
111
+ }
112
+
113
+ // Insert before </body>
114
+ const bodyCloseRegex = /<\/body>/i;
115
+ if (!bodyCloseRegex.test(content)) {
116
+ warn('Could not find </body> tag in theme.liquid');
117
+ return false;
118
+ }
119
+
120
+ content = content.replace(bodyCloseRegex, ` ${renderTag}\n </body>`);
121
+ writeFileSync(themeLiquidPath, content);
122
+ return true;
123
+ }
124
+
125
+ function detectThemeRoot(cwd) {
126
+ // Check if we're in a Shopify theme directory
127
+ const markers = ['layout', 'snippets', 'templates', 'config'];
128
+ const hasMarkers = markers.filter(dir => existsSync(join(cwd, dir)));
129
+
130
+ if (hasMarkers.length >= 2) {
131
+ return cwd;
132
+ }
133
+
134
+ // Check if there's a theme subdirectory
135
+ const possibleThemeDirs = ['theme', 'shopify', 'src'];
136
+ for (const dir of possibleThemeDirs) {
137
+ const themePath = join(cwd, dir);
138
+ if (existsSync(themePath)) {
139
+ const subMarkers = markers.filter(m => existsSync(join(themePath, m)));
140
+ if (subMarkers.length >= 2) {
141
+ return themePath;
142
+ }
143
+ }
144
+ }
145
+
146
+ return null;
147
+ }
148
+
149
+ async function init(args) {
150
+ const cwd = process.cwd();
151
+ const useLocal = args.includes('--local');
152
+ const shouldInject = args.includes('--inject');
153
+ const force = args.includes('--force');
154
+
155
+ console.log();
156
+ log('Shopify Theme Devtools', COLORS.bold);
157
+ console.log();
158
+
159
+ // Detect theme root
160
+ const themeRoot = detectThemeRoot(cwd);
161
+
162
+ if (!themeRoot) {
163
+ error('Could not detect Shopify theme directory');
164
+ info('Make sure you run this command from your theme root');
165
+ info('(should contain layout/, snippets/, templates/ folders)');
166
+ process.exit(1);
167
+ }
168
+
169
+ if (themeRoot !== cwd) {
170
+ info(`Detected theme root: ${themeRoot}`);
171
+ }
172
+
173
+ const snippetsDir = join(themeRoot, 'snippets');
174
+ const assetsDir = join(themeRoot, 'assets');
175
+ const layoutDir = join(themeRoot, 'layout');
176
+
177
+ // Ensure directories exist
178
+ ensureDir(snippetsDir);
179
+ if (useLocal) {
180
+ ensureDir(assetsDir);
181
+ }
182
+
183
+ // Source files
184
+ const liquidSrc = join(packageRoot, 'src', 'liquid', 'theme-devtools-bridge.liquid');
185
+ const jsSrc = join(packageRoot, 'dist', 'theme-devtools.js');
186
+ const cssSrc = join(packageRoot, 'dist', 'theme-devtools.css');
187
+
188
+ // Destination files
189
+ const liquidDest = join(snippetsDir, 'theme-devtools-bridge.liquid');
190
+ const jsDest = join(assetsDir, 'theme-devtools.js');
191
+ const cssDest = join(assetsDir, 'theme-devtools.css');
192
+
193
+ let liquidContent = readFileSync(liquidSrc, 'utf-8');
194
+
195
+ // Set devtools_local to false for production use
196
+ liquidContent = liquidContent.replace(
197
+ /{%- assign devtools_local = true -%}/,
198
+ '{%- assign devtools_local = false -%}'
199
+ );
200
+
201
+ if (useLocal) {
202
+ liquidContent = patchLiquidForLocal(liquidContent);
203
+ }
204
+
205
+ // Copy liquid snippet
206
+ if (existsSync(liquidDest) && !force) {
207
+ warn(`Snippet already exists: snippets/theme-devtools-bridge.liquid`);
208
+ info('Use --force to overwrite');
209
+ } else {
210
+ writeFileSync(liquidDest, liquidContent);
211
+ success('Created snippets/theme-devtools-bridge.liquid');
212
+ }
213
+
214
+ // Copy assets if --local
215
+ if (useLocal) {
216
+ if (!existsSync(jsSrc)) {
217
+ error('dist/theme-devtools.js not found. Run npm run build first.');
218
+ process.exit(1);
219
+ }
220
+
221
+ if (copyFile(jsSrc, jsDest, force)) {
222
+ success('Copied assets/theme-devtools.js');
223
+ }
224
+
225
+ if (existsSync(cssSrc)) {
226
+ if (copyFile(cssSrc, cssDest, force)) {
227
+ success('Copied assets/theme-devtools.css');
228
+ }
229
+ }
230
+ }
231
+
232
+ // Inject render tag if --inject
233
+ if (shouldInject) {
234
+ const themeLiquid = join(layoutDir, 'theme.liquid');
235
+ if (injectRenderTag(themeLiquid)) {
236
+ success('Added render tag to layout/theme.liquid');
237
+ }
238
+ }
239
+
240
+ console.log();
241
+ log('Setup complete!', COLORS.green + COLORS.bold);
242
+ console.log();
243
+
244
+ if (!shouldInject) {
245
+ log('Next step:', COLORS.bold);
246
+ info("Add this line to layout/theme.liquid before </body>:");
247
+ console.log();
248
+ log(" {% render 'theme-devtools-bridge' %}", COLORS.blue);
249
+ console.log();
250
+ info('Or run with --inject to do this automatically');
251
+ }
252
+
253
+ console.log();
254
+ info('The devtools panel will only appear on unpublished/development themes.');
255
+ console.log();
256
+ }
257
+
258
+ // Parse arguments
259
+ const args = process.argv.slice(2);
260
+ const command = args[0];
261
+
262
+ if (!command || command === '--help' || command === '-h') {
263
+ printHelp();
264
+ process.exit(0);
265
+ }
266
+
267
+ if (command === 'init') {
268
+ init(args);
269
+ } else {
270
+ error(`Unknown command: ${command}`);
271
+ printHelp();
272
+ process.exit(1);
273
+ }
package/package.json CHANGED
@@ -1,10 +1,13 @@
1
1
  {
2
2
  "name": "shopify-theme-devtools",
3
- "version": "2.2.0",
3
+ "version": "2.2.1",
4
4
  "description": "In-browser devtools panel for Shopify theme development - inspect Liquid context, metafields, settings, sections, and cart state",
5
5
  "type": "module",
6
6
  "main": "dist/theme-devtools.js",
7
7
  "module": "dist/theme-devtools.js",
8
+ "bin": {
9
+ "shopify-theme-devtools": "./bin/cli.js"
10
+ },
8
11
  "exports": {
9
12
  ".": {
10
13
  "import": "./dist/theme-devtools.js",
@@ -14,6 +17,7 @@
14
17
  "./dist/*": "./dist/*"
15
18
  },
16
19
  "files": [
20
+ "bin",
17
21
  "dist",
18
22
  "src/liquid",
19
23
  "README.md",
File without changes