vite-plugin-shopify-theme-islands 1.0.0 → 1.0.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/dist/index.js +37 -0
- package/package.json +4 -4
- package/skills/{vite-plugin-shopify-theme-islands/custom-directives → custom-directives}/SKILL.md +2 -2
- package/skills/{vite-plugin-shopify-theme-islands/directives → directives}/SKILL.md +40 -2
- package/skills/{vite-plugin-shopify-theme-islands/lifecycle → lifecycle}/SKILL.md +2 -2
- package/skills/{vite-plugin-shopify-theme-islands/setup → setup}/SKILL.md +83 -14
- package/skills/{vite-plugin-shopify-theme-islands/writing-islands → writing-islands}/SKILL.md +2 -2
package/dist/index.js
CHANGED
|
@@ -10,6 +10,42 @@ var islandPath = fileURLToPath(new URL("./island.js", import.meta.url));
|
|
|
10
10
|
var ISLAND_IMPORT_RE = /from\s+['"]vite-plugin-shopify-theme-islands\/island['"]/;
|
|
11
11
|
var TS_JS_RE = /\.(ts|js)$/;
|
|
12
12
|
var SKIP_DIRS = new Set(["node_modules", "dist", "build", "public", "assets", ".cache"]);
|
|
13
|
+
var PREFIX = "[vite-plugin-shopify-theme-islands]";
|
|
14
|
+
function validateOptions(options, directives) {
|
|
15
|
+
const customDefs = options.directives?.custom ?? [];
|
|
16
|
+
if (Array.isArray(options.directories) && options.directories.length === 0) {
|
|
17
|
+
throw new Error(`${PREFIX} "directories" must not be empty`);
|
|
18
|
+
}
|
|
19
|
+
const threshold = options.directives?.visible?.threshold;
|
|
20
|
+
if (threshold !== undefined && (threshold < 0 || threshold > 1)) {
|
|
21
|
+
throw new Error(`${PREFIX} "directives.visible.threshold" must be between 0 and 1, got ${threshold}`);
|
|
22
|
+
}
|
|
23
|
+
if (options.retry !== undefined) {
|
|
24
|
+
const { retries, delay } = options.retry;
|
|
25
|
+
if (retries !== undefined && retries < 0) {
|
|
26
|
+
throw new Error(`${PREFIX} "retry.retries" must be >= 0, got ${retries}`);
|
|
27
|
+
}
|
|
28
|
+
if (delay !== undefined && delay < 0) {
|
|
29
|
+
throw new Error(`${PREFIX} "retry.delay" must be >= 0, got ${delay}`);
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
const builtinAttributes = new Set([
|
|
33
|
+
directives.visible.attribute,
|
|
34
|
+
directives.idle.attribute,
|
|
35
|
+
directives.media.attribute,
|
|
36
|
+
directives.defer.attribute
|
|
37
|
+
]);
|
|
38
|
+
const seen = new Set;
|
|
39
|
+
for (const def of customDefs) {
|
|
40
|
+
if (seen.has(def.name)) {
|
|
41
|
+
throw new Error(`${PREFIX} Duplicate custom directive name: "${def.name}"`);
|
|
42
|
+
}
|
|
43
|
+
if (builtinAttributes.has(def.name)) {
|
|
44
|
+
throw new Error(`${PREFIX} Custom directive "${def.name}" conflicts with a built-in directive`);
|
|
45
|
+
}
|
|
46
|
+
seen.add(def.name);
|
|
47
|
+
}
|
|
48
|
+
}
|
|
13
49
|
var defaults = {
|
|
14
50
|
directories: ["/frontend/js/islands/"],
|
|
15
51
|
directives: {
|
|
@@ -71,6 +107,7 @@ function shopifyThemeIslands(options = {}) {
|
|
|
71
107
|
defer: { ...defaults.directives.defer, ...options.directives?.defer }
|
|
72
108
|
};
|
|
73
109
|
const clientDirectiveDefinitions = options.directives?.custom ?? [];
|
|
110
|
+
validateOptions(options, directives);
|
|
74
111
|
const debug = options.debug ?? false;
|
|
75
112
|
const log = debug ? (...args) => console.log("[islands]", ...args) : () => {};
|
|
76
113
|
let resolvedDirs = rawDirs;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "vite-plugin-shopify-theme-islands",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.2",
|
|
4
4
|
"description": "Vite plugin for island architecture in Shopify themes",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"packageManager": "bun@1.3.10",
|
|
@@ -78,11 +78,11 @@
|
|
|
78
78
|
},
|
|
79
79
|
"devDependencies": {
|
|
80
80
|
"@happy-dom/global-registrator": "^20.8.4",
|
|
81
|
-
"@tanstack/intent": "
|
|
81
|
+
"@tanstack/intent": "0.0.21",
|
|
82
82
|
"@types/bun": "^1.3.10",
|
|
83
83
|
"@types/node": "^22.0.0",
|
|
84
|
-
"oxfmt": "
|
|
85
|
-
"oxlint": "
|
|
84
|
+
"oxfmt": "0.41.0",
|
|
85
|
+
"oxlint": "1.56.0",
|
|
86
86
|
"typescript": "^5.0.0",
|
|
87
87
|
"vite": "^8.0.0"
|
|
88
88
|
}
|
package/skills/{vite-plugin-shopify-theme-islands/custom-directives → custom-directives}/SKILL.md
RENAMED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
---
|
|
2
|
-
name:
|
|
2
|
+
name: custom-directives
|
|
3
3
|
description: >
|
|
4
4
|
Custom client directives registered via directives.custom in vite.config.ts.
|
|
5
5
|
ClientDirective function signature (load, options, el). AND-latch: when
|
|
@@ -7,7 +7,7 @@ description: >
|
|
|
7
7
|
the island activates. Error handling — thrown errors fire islands:error.
|
|
8
8
|
type: core
|
|
9
9
|
library: vite-plugin-shopify-theme-islands
|
|
10
|
-
library_version: "1.0.
|
|
10
|
+
library_version: "1.0.2"
|
|
11
11
|
sources:
|
|
12
12
|
- Rees1993/vite-plugin-shopify-theme-islands:src/index.ts
|
|
13
13
|
- Rees1993/vite-plugin-shopify-theme-islands:src/runtime.ts
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
---
|
|
2
|
-
name:
|
|
2
|
+
name: directives
|
|
3
3
|
description: >
|
|
4
4
|
Built-in client directives: client:visible (IntersectionObserver, rootMargin),
|
|
5
5
|
client:media (matchMedia query), client:idle (requestIdleCallback),
|
|
@@ -7,7 +7,7 @@ description: >
|
|
|
7
7
|
all must resolve. Per-element value overrides. Empty client:media warning.
|
|
8
8
|
type: core
|
|
9
9
|
library: vite-plugin-shopify-theme-islands
|
|
10
|
-
library_version: "1.0.
|
|
10
|
+
library_version: "1.0.2"
|
|
11
11
|
sources:
|
|
12
12
|
- Rees1993/vite-plugin-shopify-theme-islands:src/runtime.ts
|
|
13
13
|
- Rees1993/vite-plugin-shopify-theme-islands:src/index.ts
|
|
@@ -165,3 +165,41 @@ Correct:
|
|
|
165
165
|
The attribute value is passed directly to `IntersectionObserver` as `rootMargin`, fully replacing the global default.
|
|
166
166
|
|
|
167
167
|
Source: src/runtime.ts — `await visible(el, visibleAttr || rootMargin, threshold, pendingVisible)`
|
|
168
|
+
|
|
169
|
+
### HIGH Directive attribute typo — island loads without condition
|
|
170
|
+
|
|
171
|
+
Wrong:
|
|
172
|
+
|
|
173
|
+
```html
|
|
174
|
+
<product-form client:visibled></product-form>
|
|
175
|
+
<product-form client:Visible></product-form>
|
|
176
|
+
```
|
|
177
|
+
|
|
178
|
+
Correct:
|
|
179
|
+
|
|
180
|
+
```html
|
|
181
|
+
<product-form client:visible></product-form>
|
|
182
|
+
```
|
|
183
|
+
|
|
184
|
+
Directive attributes are case-sensitive. An unrecognised attribute is silently ignored — the island loads immediately as if no directive were set. No warning is emitted. Check for typos if an island activates earlier than expected.
|
|
185
|
+
|
|
186
|
+
Source: src/runtime.ts — runtime checks exact attribute names from plugin config
|
|
187
|
+
|
|
188
|
+
### HIGH Agent uses default attribute name when developer has configured a custom one
|
|
189
|
+
|
|
190
|
+
Wrong:
|
|
191
|
+
|
|
192
|
+
```html
|
|
193
|
+
<!-- developer has set visible.attribute: "data:visible" in vite.config.ts -->
|
|
194
|
+
<product-form client:visible></product-form>
|
|
195
|
+
```
|
|
196
|
+
|
|
197
|
+
Correct:
|
|
198
|
+
|
|
199
|
+
```html
|
|
200
|
+
<product-form data:visible></product-form>
|
|
201
|
+
```
|
|
202
|
+
|
|
203
|
+
When `directives.visible.attribute` (or any directive's `attribute` option) is overridden in `vite.config.ts`, all Liquid templates must use the configured name. The default `client:*` names no longer apply. Always read `vite.config.ts` to check for overridden attribute names before writing directives in Liquid.
|
|
204
|
+
|
|
205
|
+
Source: src/index.ts:DirectivesConfig — `attribute` field per directive; src/runtime.ts reads configured attribute names at runtime
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
---
|
|
2
|
-
name:
|
|
2
|
+
name: lifecycle
|
|
3
3
|
description: >
|
|
4
4
|
Island lifecycle events and SPA teardown. onIslandLoad and onIslandError
|
|
5
5
|
helpers from vite-plugin-shopify-theme-islands/events — prefer these over
|
|
@@ -8,7 +8,7 @@ description: >
|
|
|
8
8
|
module revive for SPA navigation teardown.
|
|
9
9
|
type: core
|
|
10
10
|
library: vite-plugin-shopify-theme-islands
|
|
11
|
-
library_version: "1.0.
|
|
11
|
+
library_version: "1.0.2"
|
|
12
12
|
sources:
|
|
13
13
|
- Rees1993/vite-plugin-shopify-theme-islands:src/events.ts
|
|
14
14
|
- Rees1993/vite-plugin-shopify-theme-islands:src/index.ts
|
|
@@ -1,42 +1,59 @@
|
|
|
1
1
|
---
|
|
2
|
-
name:
|
|
2
|
+
name: setup
|
|
3
3
|
description: >
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
4
|
+
Getting-started journey and plugin configuration. Covers the full path from
|
|
5
|
+
install to first working island. shopifyThemeIslands() options: directories
|
|
6
|
+
(string | string[]), debug, directives deep-merge, and retry (retries, delay
|
|
7
|
+
with exponential backoff). Load when setting up the plugin, configuring
|
|
8
|
+
island scan directories, or enabling retry.
|
|
8
9
|
type: core
|
|
9
10
|
library: vite-plugin-shopify-theme-islands
|
|
10
|
-
library_version: "1.0.
|
|
11
|
+
library_version: "1.0.2"
|
|
11
12
|
sources:
|
|
12
13
|
- Rees1993/vite-plugin-shopify-theme-islands:src/index.ts
|
|
13
14
|
---
|
|
14
15
|
|
|
15
16
|
## Setup
|
|
16
17
|
|
|
18
|
+
This plugin is framework-agnostic but designed for Shopify themes. Most Shopify
|
|
19
|
+
projects also use
|
|
20
|
+
[vite-plugin-shopify](https://github.com/barrel/vite-plugin-shopify) to handle
|
|
21
|
+
Shopify-specific asset serving — if the project uses it, add this plugin
|
|
22
|
+
alongside it in the existing `plugins` array.
|
|
23
|
+
|
|
24
|
+
### 1. Add the plugin to `vite.config.ts`
|
|
25
|
+
|
|
17
26
|
```ts
|
|
18
27
|
// vite.config.ts
|
|
19
28
|
import { defineConfig } from "vite";
|
|
20
29
|
import shopifyThemeIslands from "vite-plugin-shopify-theme-islands";
|
|
21
30
|
|
|
22
31
|
export default defineConfig({
|
|
23
|
-
plugins: [
|
|
24
|
-
shopifyThemeIslands({
|
|
25
|
-
directories: ["/frontend/js/islands/"],
|
|
26
|
-
debug: false,
|
|
27
|
-
retry: { retries: 2, delay: 500 },
|
|
28
|
-
}),
|
|
29
|
-
],
|
|
32
|
+
plugins: [shopifyThemeIslands()],
|
|
30
33
|
});
|
|
31
34
|
```
|
|
32
35
|
|
|
33
|
-
|
|
36
|
+
All options are optional. The default islands directory is `/frontend/js/islands/`.
|
|
37
|
+
|
|
38
|
+
### 2. Import the virtual module in the theme JS entry point
|
|
34
39
|
|
|
35
40
|
```ts
|
|
36
41
|
// frontend/js/theme.ts
|
|
37
42
|
import "vite-plugin-shopify-theme-islands/revive";
|
|
38
43
|
```
|
|
39
44
|
|
|
45
|
+
This activates the runtime — islands are never loaded without this import.
|
|
46
|
+
|
|
47
|
+
### 3. Add directives to Liquid templates
|
|
48
|
+
|
|
49
|
+
```html
|
|
50
|
+
<!-- sections/product.liquid -->
|
|
51
|
+
<product-form client:visible></product-form>
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
That's a working setup. Islands in `/frontend/js/islands/` matching the tag
|
|
55
|
+
name are loaded lazily when the directive condition is met.
|
|
56
|
+
|
|
40
57
|
## Core Patterns
|
|
41
58
|
|
|
42
59
|
### Configure multiple island directories
|
|
@@ -101,6 +118,58 @@ The plugin generates the virtual module but has no effect until it is imported i
|
|
|
101
118
|
|
|
102
119
|
Source: src/index.ts — VIRTUAL_ID / RESOLVED_ID
|
|
103
120
|
|
|
121
|
+
### HIGH Agent hardcodes default values — unnecessary noise
|
|
122
|
+
|
|
123
|
+
Wrong:
|
|
124
|
+
|
|
125
|
+
```ts
|
|
126
|
+
shopifyThemeIslands({
|
|
127
|
+
directories: ["/frontend/js/islands/"],
|
|
128
|
+
debug: false,
|
|
129
|
+
directives: {
|
|
130
|
+
visible: { attribute: "client:visible", rootMargin: "200px", threshold: 0 },
|
|
131
|
+
idle: { attribute: "client:idle", timeout: 500 },
|
|
132
|
+
media: { attribute: "client:media" },
|
|
133
|
+
defer: { attribute: "client:defer", delay: 3000 },
|
|
134
|
+
},
|
|
135
|
+
});
|
|
136
|
+
```
|
|
137
|
+
|
|
138
|
+
Correct:
|
|
139
|
+
|
|
140
|
+
```ts
|
|
141
|
+
shopifyThemeIslands();
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
All options are optional and default to sensible values. Only include options that differ from the defaults.
|
|
145
|
+
|
|
146
|
+
### HIGH Agent overwrites existing `vite.config.ts` instead of appending
|
|
147
|
+
|
|
148
|
+
Before adding the plugin, read the existing `vite.config.ts`. Projects commonly
|
|
149
|
+
already have `vite-plugin-shopify` or other plugins — the island plugin must be
|
|
150
|
+
added to the existing `plugins` array, not replace it.
|
|
151
|
+
|
|
152
|
+
Wrong:
|
|
153
|
+
|
|
154
|
+
```ts
|
|
155
|
+
// Replaces existing plugins
|
|
156
|
+
export default defineConfig({
|
|
157
|
+
plugins: [shopifyThemeIslands()],
|
|
158
|
+
});
|
|
159
|
+
```
|
|
160
|
+
|
|
161
|
+
Correct:
|
|
162
|
+
|
|
163
|
+
```ts
|
|
164
|
+
// Appends to existing plugins
|
|
165
|
+
export default defineConfig({
|
|
166
|
+
plugins: [
|
|
167
|
+
shopify(), // pre-existing plugin preserved
|
|
168
|
+
shopifyThemeIslands(),
|
|
169
|
+
],
|
|
170
|
+
});
|
|
171
|
+
```
|
|
172
|
+
|
|
104
173
|
### HIGH `retry` nested inside `directives` — no retries happen
|
|
105
174
|
|
|
106
175
|
Wrong:
|
package/skills/{vite-plugin-shopify-theme-islands/writing-islands → writing-islands}/SKILL.md
RENAMED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
---
|
|
2
|
-
name:
|
|
2
|
+
name: writing-islands
|
|
3
3
|
description: >
|
|
4
4
|
Writing island files. Two discovery modes: directory scanning (files in
|
|
5
5
|
configured directories auto-discovered by tag name = filename) and Island
|
|
@@ -8,7 +8,7 @@ description: >
|
|
|
8
8
|
base class, and child island cascade behaviour.
|
|
9
9
|
type: core
|
|
10
10
|
library: vite-plugin-shopify-theme-islands
|
|
11
|
-
library_version: "1.0.
|
|
11
|
+
library_version: "1.0.2"
|
|
12
12
|
sources:
|
|
13
13
|
- Rees1993/vite-plugin-shopify-theme-islands:src/island.ts
|
|
14
14
|
- Rees1993/vite-plugin-shopify-theme-islands:src/runtime.ts
|