assign-gingerly 0.0.36 → 0.0.38

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.
@@ -138,7 +138,7 @@ class ElementEnhancementContainer {
138
138
  }
139
139
  // Check if there's an enhKey
140
140
  if (registryItem.enhKey) {
141
- const ctx = { config: registryItem, mountCtx };
141
+ const ctx = { config: registryItem, mountCtx, emc: mountCtx?.emc };
142
142
  const self = this;
143
143
  // Get existing initVals from enhKey
144
144
  const existingInitVals = self[registryItem.enhKey] &&
@@ -155,7 +155,7 @@ class ElementEnhancementContainer {
155
155
  }
156
156
  else {
157
157
  // No enhKey, still pass attrInitVals
158
- const ctx = { config: registryItem, mountCtx };
158
+ const ctx = { config: registryItem, mountCtx, emc: mountCtx?.emc };
159
159
  instance = new SpawnClass(element, ctx, attrInitVals);
160
160
  }
161
161
  // Store in global instance map
@@ -1,5 +1,5 @@
1
1
  import assignGingerly, { EnhancementRegistry, ItemscopeRegistry, IAssignGingerlyOptions, getInstanceMap, INSTANCE_MAP_GUID } from './assignGingerly.js';
2
- import { EnhancementConfig } from './types/assign-gingerly/types.js';
2
+ import { EnhancementConfig, SpawnContext } from './types/assign-gingerly/types.js';
3
3
  import { parseWithAttrs } from './parseWithAttrs.js';
4
4
 
5
5
  /**
@@ -225,7 +225,7 @@ class ElementEnhancementContainer {
225
225
 
226
226
  // Check if there's an enhKey
227
227
  if (registryItem.enhKey) {
228
- const ctx = { config: registryItem, mountCtx };
228
+ const ctx: SpawnContext = { config: registryItem, mountCtx, emc: (mountCtx as any)?.emc };
229
229
  const self = this as any;
230
230
 
231
231
  // Get existing initVals from enhKey
@@ -245,7 +245,7 @@ class ElementEnhancementContainer {
245
245
  self[registryItem.enhKey] = instance;
246
246
  } else {
247
247
  // No enhKey, still pass attrInitVals
248
- const ctx = { config: registryItem, mountCtx };
248
+ const ctx: SpawnContext = { config: registryItem, mountCtx, emc: (mountCtx as any)?.emc };
249
249
  instance = new SpawnClass(element, ctx, attrInitVals);
250
250
  }
251
251
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "assign-gingerly",
3
- "version": "0.0.36",
3
+ "version": "0.0.38",
4
4
  "description": "This package provides a utility function for carefully merging one object into another.",
5
5
  "homepage": "https://github.com/bahrus/assign-gingerly#readme",
6
6
  "bugs": {
package/parseWithAttrs.js CHANGED
@@ -143,10 +143,15 @@ function hasDashOrNonASCII(str) {
143
143
  * Gets attribute value with smart enh- prefix handling
144
144
  * @param element - The element to read from
145
145
  * @param attrName - The attribute name (without enh- prefix)
146
- * @param allowUnprefixed - Pattern (string or RegExp) that element tag name must match to allow unprefixed attributes
146
+ * @param allowUnprefixed - Pattern (string or RegExp) that element tag name must match to allow unprefixed attributes,
147
+ * or `true` for custom element mode (read attributes directly, no enh- prefix)
147
148
  * @returns The attribute value or null
148
149
  */
149
150
  function getAttributeValue(element, attrName, allowUnprefixed) {
151
+ // Custom element mode - read attribute directly, no enh- prefix
152
+ if (allowUnprefixed === true) {
153
+ return element.getAttribute(attrName);
154
+ }
150
155
  const { localName } = element;
151
156
  const isCustomElement = localName.includes('-');
152
157
  const isSVGElement = typeof SVGElement !== 'undefined' && element instanceof SVGElement;
@@ -222,15 +227,18 @@ function getDefaultParser(instanceOf) {
222
227
  * Parses attributes from an element based on AttrPatterns configuration
223
228
  * @param element - The DOM element to read attributes from
224
229
  * @param attrPatterns - The attribute patterns configuration
225
- * @param allowUnprefixed - Pattern (string or RegExp) that element tag name must match to allow unprefixed attributes
230
+ * @param allowUnprefixed - Pattern (string or RegExp) that element tag name must match to allow unprefixed attributes,
231
+ * or `true` for custom element mode: reads attributes directly (no enh- prefix),
232
+ * skips base attribute dash validation, and skips properties already set on the element
226
233
  * @param spawnContext - Optional spawn context containing enhancement config and synthesizer element
227
234
  * @returns Object with parsed attribute values ready for initVals
228
235
  */
229
236
  export function parseWithAttrs(element, attrPatterns, allowUnprefixed, spawnContext) {
230
237
  // Extract synthesizerElement from spawnContext for backward compatibility
231
238
  const synthesizerElement = spawnContext?.synthesizerElement;
232
- // Validate base attribute if present
233
- if ('base' in attrPatterns) {
239
+ const isCustomElementMode = allowUnprefixed === true;
240
+ // Validate base attribute if present (skip in custom element mode)
241
+ if ('base' in attrPatterns && !isCustomElementMode) {
234
242
  const baseValue = attrPatterns.base;
235
243
  if (!hasDashOrNonASCII(baseValue)) {
236
244
  throw new Error(`Invalid base attribute "${baseValue}": must contain a dash (-) or non-ASCII character. ` +
@@ -284,6 +292,13 @@ export function parseWithAttrs(element, attrPatterns, allowUnprefixed, spawnCont
284
292
  }
285
293
  // Second pass: read attributes and parse values
286
294
  for (const [key, { attrName, config }] of resolvedAttrs) {
295
+ // In custom element mode, skip properties already set on the element
296
+ if (isCustomElementMode) {
297
+ const mapsTo = config.mapsTo ?? (key === 'base' ? '.' : key);
298
+ if (mapsTo !== '.' && element[mapsTo] !== undefined) {
299
+ continue;
300
+ }
301
+ }
287
302
  const attrValue = getAttributeValue(element, attrName, allowUnprefixed);
288
303
  // Create parser context
289
304
  const parserContext = {
package/parseWithAttrs.ts CHANGED
@@ -178,14 +178,20 @@ function hasDashOrNonASCII(str: string): boolean {
178
178
  * Gets attribute value with smart enh- prefix handling
179
179
  * @param element - The element to read from
180
180
  * @param attrName - The attribute name (without enh- prefix)
181
- * @param allowUnprefixed - Pattern (string or RegExp) that element tag name must match to allow unprefixed attributes
181
+ * @param allowUnprefixed - Pattern (string or RegExp) that element tag name must match to allow unprefixed attributes,
182
+ * or `true` for custom element mode (read attributes directly, no enh- prefix)
182
183
  * @returns The attribute value or null
183
184
  */
184
185
  function getAttributeValue(
185
186
  element: Element,
186
187
  attrName: string,
187
- allowUnprefixed?: string | RegExp
188
+ allowUnprefixed?: string | RegExp | true
188
189
  ): string | null {
190
+ // Custom element mode - read attribute directly, no enh- prefix
191
+ if (allowUnprefixed === true) {
192
+ return element.getAttribute(attrName);
193
+ }
194
+
189
195
  const { localName } = element;
190
196
  const isCustomElement = localName.includes('-');
191
197
  const isSVGElement = typeof SVGElement !== 'undefined' && element instanceof SVGElement;
@@ -261,21 +267,25 @@ function getDefaultParser(instanceOf?: string | Function): ParserFunction {
261
267
  * Parses attributes from an element based on AttrPatterns configuration
262
268
  * @param element - The DOM element to read attributes from
263
269
  * @param attrPatterns - The attribute patterns configuration
264
- * @param allowUnprefixed - Pattern (string or RegExp) that element tag name must match to allow unprefixed attributes
270
+ * @param allowUnprefixed - Pattern (string or RegExp) that element tag name must match to allow unprefixed attributes,
271
+ * or `true` for custom element mode: reads attributes directly (no enh- prefix),
272
+ * skips base attribute dash validation, and skips properties already set on the element
265
273
  * @param spawnContext - Optional spawn context containing enhancement config and synthesizer element
266
274
  * @returns Object with parsed attribute values ready for initVals
267
275
  */
268
276
  export function parseWithAttrs<T = any>(
269
277
  element: Element,
270
278
  attrPatterns: AttrPatterns<T>,
271
- allowUnprefixed?: string | RegExp,
279
+ allowUnprefixed?: string | RegExp | true,
272
280
  spawnContext?: SpawnContext<T>
273
281
  ): Partial<T> {
274
282
  // Extract synthesizerElement from spawnContext for backward compatibility
275
283
  const synthesizerElement = spawnContext?.synthesizerElement;
276
284
 
277
- // Validate base attribute if present
278
- if ('base' in attrPatterns) {
285
+ const isCustomElementMode = allowUnprefixed === true;
286
+
287
+ // Validate base attribute if present (skip in custom element mode)
288
+ if ('base' in attrPatterns && !isCustomElementMode) {
279
289
  const baseValue = attrPatterns.base as string;
280
290
  if (!hasDashOrNonASCII(baseValue)) {
281
291
  throw new Error(
@@ -341,6 +351,14 @@ export function parseWithAttrs<T = any>(
341
351
 
342
352
  // Second pass: read attributes and parse values
343
353
  for (const [key, { attrName, config }] of resolvedAttrs) {
354
+ // In custom element mode, skip properties already set on the element
355
+ if (isCustomElementMode) {
356
+ const mapsTo = config.mapsTo ?? (key === 'base' ? '.' : key);
357
+ if (mapsTo !== '.' && (element as any)[mapsTo as string] !== undefined) {
358
+ continue;
359
+ }
360
+ }
361
+
344
362
  const attrValue = getAttributeValue(element, attrName, allowUnprefixed);
345
363
 
346
364
  // Create parser context
@@ -212,6 +212,13 @@ export interface SpawnContext<T = any, TMountContext = any> {
212
212
  * Used for scoped parser registry access during attribute parsing.
213
213
  */
214
214
  synthesizerElement?: Element;
215
+ /**
216
+ * The full EMC configuration object that triggered this spawn.
217
+ * Passed through so enhancement classes can access their full configuration
218
+ * (including customData) without needing to separately import the JSON file.
219
+ * This avoids duplicate JSON imports when using emoji shorthand aliases.
220
+ */
221
+ emc?: any;
215
222
  }
216
223
 
217
224
  /**