@adobe-commerce/elsie 1.4.1-alpha008 → 1.4.1-alpha009

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.
@@ -77,20 +77,24 @@ module.exports = async function generateResourceBuilder(yargs) {
77
77
  alias: 's',
78
78
  describe: 'Path to the source code containing GraphQL operations',
79
79
  type: 'array',
80
- string: true,
81
80
  demandOption: true,
82
81
  })
82
+ .option('excluded', {
83
+ alias: 'x',
84
+ describe: 'Paths to exclude from validation',
85
+ type: 'array',
86
+ demandOption: false,
87
+ })
83
88
  .option('endpoints', {
84
89
  alias: 'e',
85
90
  describe: 'Path to GraphQL endpoints',
86
91
  type: 'array',
87
- string: true,
88
92
  demandOption: true,
89
93
  });
90
94
  },
91
95
  async (argv) => {
92
- const { source, endpoints } = argv;
93
- await validate(source, endpoints);
96
+ const { source, excluded, endpoints } = argv;
97
+ await validate(source, endpoints, excluded);
94
98
  },
95
99
  )
96
100
  .demandCommand(1, 1, 'choose a command: types, mocks or validate');
@@ -5,17 +5,20 @@ const parser = require('@babel/parser');
5
5
  const traverse = require('@babel/traverse');
6
6
  const { getIntrospectionQuery, buildClientSchema, parse, validate } = require('graphql');
7
7
 
8
- async function walk(dir, collected = []) {
8
+ async function walk(dir, excludedPaths = [], collected = []) {
9
+ if (excludedPaths.includes(dir)) return collected;
10
+
9
11
  const dirents = await fsPromises.readdir(dir, { withFileTypes: true });
10
12
 
11
13
  for (const d of dirents) {
12
14
  const full = path.resolve(dir, d.name);
13
15
 
14
16
  if (d.isDirectory()) {
15
- // skip node_modules and “hidden” folders such as .git
17
+ if (excludedPaths.includes(full)) continue;
16
18
  if (d.name === 'node_modules' || d.name.startsWith('.')) continue;
17
- await walk(full, collected);
19
+ await walk(full, excludedPaths, collected);
18
20
  } else if (/\.(c?m?js|ts|tsx)$/.test(d.name)) {
21
+ if (excludedPaths.includes(full)) continue;
19
22
  collected.push(full);
20
23
  }
21
24
  }
@@ -97,10 +100,10 @@ async function validateGqlOperations(endpoint, operation) {
97
100
  }
98
101
  }
99
102
 
100
- async function getAllOperations(directories) {
103
+ async function getAllOperations(directories, excludedPaths = []) {
101
104
  let fullContent = '';
102
105
  for (const directory of directories) {
103
- const files = await walk(path.resolve(directory));
106
+ const files = await walk(path.resolve(directory), excludedPaths.map(p => path.resolve(p)));
104
107
  for (const f of files) {
105
108
  const code = await fsPromises.readFile(f, 'utf8');
106
109
 
@@ -120,11 +123,9 @@ async function getAllOperations(directories) {
120
123
  return fullContent;
121
124
  }
122
125
 
123
-
124
-
125
- module.exports = async function main(sources, endpoints) {
126
+ module.exports = async function main(sources, endpoints, excluded) {
126
127
  for (const endpoint of endpoints) {
127
- const operations = await getAllOperations(sources);
128
+ const operations = await getAllOperations(sources, excluded);
128
129
  if (!operations) {
129
130
  console.error('No GraphQL operations found in the specified directories.');
130
131
  process.exitCode = 0;
@@ -132,4 +133,4 @@ module.exports = async function main(sources, endpoints) {
132
133
  }
133
134
  await validateGqlOperations(endpoint, operations);
134
135
  }
135
- }
136
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@adobe-commerce/elsie",
3
- "version": "1.4.1-alpha008",
3
+ "version": "1.4.1-alpha009",
4
4
  "license": "SEE LICENSE IN LICENSE.md",
5
5
  "description": "Domain Package SDK",
6
6
  "engines": {
@@ -26,7 +26,7 @@
26
26
  "cleanup": "rimraf dist"
27
27
  },
28
28
  "devDependencies": {
29
- "@adobe-commerce/event-bus": "1.0.1-beta1",
29
+ "@adobe-commerce/event-bus": "~1.0.0",
30
30
  "@adobe-commerce/fetch-graphql": "~1.1.0",
31
31
  "@adobe-commerce/recaptcha": "~1.0.1",
32
32
  "@adobe-commerce/storefront-design": "~1.0.0",
@@ -36,7 +36,6 @@ export interface CartItemProps
36
36
  totalExcludingTax?: VNode;
37
37
  sku?: VNode;
38
38
  quantity?: number;
39
- quantityContent?: VNode;
40
39
  description?: VNode;
41
40
  attributes?: VNode;
42
41
  footer?: VNode;
@@ -46,7 +45,6 @@ export interface CartItemProps
46
45
  discount?: VNode;
47
46
  savings?: VNode;
48
47
  actions?: VNode;
49
- removeContent?: VNode;
50
48
  loading?: boolean;
51
49
  updating?: boolean;
52
50
  onRemove?: () => void;
@@ -73,9 +71,7 @@ export const CartItem: FunctionComponent<CartItemProps> = ({
73
71
  discount,
74
72
  savings,
75
73
  actions,
76
- removeContent,
77
74
  quantity,
78
- quantityContent,
79
75
  description,
80
76
  attributes,
81
77
  footer,
@@ -308,20 +304,18 @@ export const CartItem: FunctionComponent<CartItemProps> = ({
308
304
  ['dropin-cart-item__quantity--edit', !!onQuantity],
309
305
  ])}
310
306
  >
311
- {quantityContent ? (
312
- <VComponent node={quantityContent} />
313
- ) : onQuantity ? (
314
- quantityComponent
315
- ) : (
316
- quantity && (
317
- <span className={classes(['dropin-cart-item__quantity__value'])}>
318
- {labels.quantity}:{' '}
319
- <strong className="dropin-cart-item__quantity__number">
320
- {Number(quantity).toLocaleString(locale)}
321
- </strong>
322
- </span>
323
- )
324
- )}
307
+ {onQuantity
308
+ ? quantityComponent
309
+ : quantity && (
310
+ <span
311
+ className={classes(['dropin-cart-item__quantity__value'])}
312
+ >
313
+ {labels.quantity}:{' '}
314
+ <strong className="dropin-cart-item__quantity__number">
315
+ {Number(quantity).toLocaleString(locale)}
316
+ </strong>
317
+ </span>
318
+ )}
325
319
 
326
320
  {/* Warning */}
327
321
  {warning && (
@@ -438,17 +432,12 @@ export const CartItem: FunctionComponent<CartItemProps> = ({
438
432
 
439
433
  {/* Footer */}
440
434
  {footer && (
441
- <VComponent
442
- node={footer}
443
- className={classes(['dropin-cart-item__footer'])}
444
- />
435
+ <VComponent node={footer} className={classes(['dropin-cart-item__footer'])} />
445
436
  )}
446
437
  </div>
447
438
 
448
439
  {/* Remove Item */}
449
- {removeContent ? (
450
- <VComponent node={removeContent} />
451
- ) : onRemove ? (
440
+ {onRemove && (
452
441
  <Button
453
442
  data-testid="cart-item-remove-button"
454
443
  className={classes(['dropin-cart-item__remove'])}
@@ -470,7 +459,7 @@ export const CartItem: FunctionComponent<CartItemProps> = ({
470
459
  }
471
460
  disabled={updating}
472
461
  />
473
- ) : null}
462
+ )}
474
463
  </div>
475
464
  );
476
465
  };
@@ -5,265 +5,48 @@ import { Meta, Unstyled } from '@storybook/blocks';
5
5
 
6
6
  # Event Bus
7
7
 
8
- The Event Bus provides a communication system for different parts of your application to exchange messages and stay synchronized. It enables event-driven architecture for drop-ins, allowing Containers to react to changes from other Containers and communicate data changes to the storefront.
9
-
10
- ## Import
11
-
12
- From drop-in project using the SDK
8
+ ## Usage
13
9
 
14
10
  ```ts
11
+ // from drop-in project (SDK)
15
12
  import { events } from '@adobe-commerce/elsie/lib';
16
- ```
17
-
18
13
 
19
- From integration project (storefront)
20
-
21
- ```js
14
+ // from host site
22
15
  import { events } from '@dropins/tools/event-bus.js';
23
16
  ```
24
17
 
25
- ## Core Methods
26
-
27
- ### Subscribe to Events
28
-
29
- Subscribe to events and receive notifications when they occur.
30
-
31
- ```ts
32
- const eventListener = events.on('<event>', (payload) => {
33
- // Handle the event payload
34
- console.log('Event received:', payload);
35
- });
36
-
37
- // Stop listening to the event
38
- eventListener.off();
39
- ```
18
+ ## Methods
40
19
 
41
- **Example:**
20
+ ### Listener
42
21
  ```ts
43
- // Listen for cart updates
44
- const cartListener = events.on('cart/data', (cartData) => {
45
- if (cartData) {
46
- console.log(`Cart has ${cartData.totalQuantity} items`);
47
- updateCartUI(cartData);
48
- } else {
49
- console.log('Cart is empty');
50
- showEmptyCart();
51
- }
22
+ const onEvent = events.on('<event>', (payload) => {
23
+ //...handle payload
52
24
  });
53
25
 
54
- // Later, when you want to stop listening
55
- cartListener.off();
56
- ```
57
-
58
- ### Emit Events
59
-
60
- Broadcast events to all listeners across your application.
61
-
62
- ```ts
63
- events.emit('<event>', payload);
64
- ```
65
-
66
- **Examples:**
67
- ```ts
68
- // Emit cart data
69
- const cartData = {
70
- id: 'cart-123',
71
- totalQuantity: 2,
72
- items: [
73
- { uid: 'item-1', quantity: 1, sku: 'PROD-001', name: 'Product Name' }
74
- ]
75
- };
76
-
77
- events.emit('cart/data', cartData);
26
+ // Stop listening to event
27
+ onEvent.off();
78
28
  ```
79
29
 
80
- ### Get Last Event Payload
81
-
82
- Retrieve the most recent payload for a specific event.
83
-
84
- ```ts
85
- const lastPayload = events.lastPayload('<event>');
86
- ```
30
+ ### Emit
87
31
 
88
- **Example:**
89
32
  ```ts
90
- // Get the current cart state without waiting for an event
91
- const currentCart = events.lastPayload('cart/data');
92
-
93
- if (currentCart) {
94
- console.log('Current cart total:', currentCart.totalQuantity);
95
- }
33
+ events.emit('<event>', <payload>);
96
34
  ```
97
35
 
98
- ### Enable Debug Logging
99
-
100
- Turn on console logging to debug event flow.
36
+ ### Logging
101
37
 
102
38
  ```ts
103
- // Enable logging to see all events in console
39
+ // Enable logging
104
40
  events.enableLogger(true);
105
- ```
106
41
 
107
- ## Advanced Features
108
-
109
- ### Eager Loading
110
-
111
- Execute the event handler immediately with the last known payload when subscribing. This is useful for getting the current state without waiting for the next event.
112
-
113
- ```ts
114
- // Handler will execute immediately if there's a previous payload
115
- const listener = events.on('cart/data', (cartData) => {
116
- console.log('Cart data received:', cartData);
117
- }, { eager: true });
42
+ // Disable logging
43
+ events.enableLogger(false);
118
44
  ```
119
45
 
120
- **Use Cases:**
121
- - Initialize UI components with current state
122
- - Avoid waiting for the first event emission
123
- - Ensure components have the latest data on mount
124
-
125
- ### Event Scoping
126
-
127
- Create namespaced events to avoid conflicts between different parts of your application.
128
-
129
- ```ts
130
- // Subscribe to a scoped event
131
- const scopedListener = events.on('data/update', (data) => {
132
- console.log('Scoped data received:', data);
133
- }, { scope: 'feature-a' });
134
-
135
- // Emit a scoped event
136
- events.emit('data/update', payload, { scope: 'feature-a' });
137
-
138
- // Get last payload for a scoped event
139
- const lastScopedData = events.lastPayload('data/update', { scope: 'feature-a' });
140
- ```
141
-
142
- **Scoped Event Names:**
143
- When using scopes, the actual event name becomes `scope/event`. For example:
144
- - `'feature-a/data/update'` instead of `'data/update'`
145
- - `'module-b/user/action'` instead of `'user/action'`
146
-
147
- **Use Cases:**
148
- - Separate different features or modules
149
- - Different contexts within the same application
150
- - Component-specific event handling
151
-
152
- ### Combining Options
153
-
154
- Use both eager loading and scoping together for powerful event handling.
46
+ ### Get Latest Payload
155
47
 
156
48
  ```ts
157
- // Subscribe to a scoped event with eager loading
158
- const listener = events.on('locale', (locale) => {
159
- console.log('Current locale:', locale);
160
- }, {
161
- eager: true,
162
- scope: 'user-preferences'
163
- });
49
+ events.lastPayload('<event>'): EventPayload | undefined;
164
50
  ```
165
51
 
166
- ## Event-Driven Drop-ins
167
-
168
- The Event Bus enables drop-ins to be truly event-driven, allowing for loose coupling between components and seamless communication across the application.
169
-
170
- ### Container-to-Container Communication
171
-
172
- Containers can react to changes from other Containers, enabling complex interactions without direct dependencies.
173
-
174
- ```ts
175
- // Product Container: Emits when a product is added to cart
176
- function ProductContainer() {
177
- const handleAddToCart = (product) => {
178
- // Add to cart logic...
179
-
180
- // Notify other containers about the cart change
181
- events.emit('cart/data', updatedCartData);
182
- };
183
-
184
- return (
185
- <button onClick={() => handleAddToCart(product)}>
186
- Add to Cart
187
- </button>
188
- );
189
- }
190
-
191
- // Cart Container: Reacts to cart changes from any source
192
- function CartContainer() {
193
- useEffect(() => {
194
- const cartListener = events.on('cart/data', (cartData) => {
195
- updateCartDisplay(cartData);
196
- updateCartBadge(cartData.totalQuantity);
197
- }, { eager: true });
198
-
199
- return () => cartListener.off();
200
- }, []);
201
-
202
- return <CartDisplay />;
203
- }
204
-
205
- // Mini Cart Container: Also reacts to the same cart changes
206
- function MiniCartContainer() {
207
- useEffect(() => {
208
- const cartListener = events.on('cart/data', (cartData) => {
209
- updateMiniCart(cartData);
210
- }, { eager: true });
211
-
212
- return () => cartListener.off();
213
- }, []);
214
-
215
- return <MiniCart />;
216
- }
217
- ```
218
-
219
- ### Storefront Communication
220
-
221
- Drop-ins can communicate data changes to the storefront, enabling seamless integration with the host application.
222
-
223
- ```ts
224
- // Authentication Container: Notifies storefront of login/logout
225
- function AuthContainer() {
226
- const handleLogin = (userData) => {
227
- // Login logic...
228
-
229
- // Notify storefront of authentication change
230
- events.emit('authenticated', true);
231
- };
232
-
233
- const handleLogout = () => {
234
- // Logout logic...
235
-
236
- // Notify storefront of authentication change
237
- events.emit('authenticated', false);
238
- };
239
-
240
- return <AuthForm onLogin={handleLogin} onLogout={handleLogout} />;
241
- }
242
-
243
- // Storefront can listen for authentication changes
244
- // This would be in the host application
245
- const authListener = events.on('authenticated', (isAuthenticated) => {
246
- if (isAuthenticated) {
247
- showUserMenu();
248
- enableCheckout();
249
- } else {
250
- hideUserMenu();
251
- disableCheckout();
252
- }
253
- }, { eager: true });
254
- ```
255
-
256
-
257
-
258
- ## Best Practices
259
-
260
- 1. **Always unsubscribe** from events when components unmount to prevent memory leaks
261
- 2. **Use scopes** to organize events by feature or component
262
- 3. **Enable eager loading** when you need immediate access to current state
263
- 4. **Use descriptive event names** that clearly indicate what data they contain
264
- 5. **Handle null/undefined payloads** gracefully in your event handlers
265
- 6. **Enable logging during development** to debug event flow
266
- 7. **Keep event payloads lightweight** to avoid performance issues
267
- 8. **Document your event contracts** so other developers know what to expect
268
-
269
52
  </Unstyled>