pulse-js-framework 1.7.9 → 1.7.11

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.
@@ -15,10 +15,15 @@ export const VIEW_NODE_HANDLERS = {
15
15
  [NodeType.IfDirective]: 'transformIfDirective',
16
16
  [NodeType.EachDirective]: 'transformEachDirective',
17
17
  [NodeType.EventDirective]: 'transformEventDirective',
18
+ [NodeType.ModelDirective]: 'transformModelDirective',
18
19
  [NodeType.SlotElement]: 'transformSlot',
19
20
  [NodeType.LinkDirective]: 'transformLinkDirective',
20
21
  [NodeType.OutletDirective]: 'transformOutletDirective',
21
- [NodeType.NavigateDirective]: 'transformNavigateDirective'
22
+ [NodeType.NavigateDirective]: 'transformNavigateDirective',
23
+ // Accessibility directives
24
+ [NodeType.A11yDirective]: 'transformA11yDirective',
25
+ [NodeType.LiveDirective]: 'transformLiveDirective',
26
+ [NodeType.FocusTrapDirective]: 'transformFocusTrapDirective'
22
27
  };
23
28
 
24
29
  /**
@@ -85,6 +90,8 @@ export function transformViewNode(transformer, node, indent = 0) {
85
90
  return transformEachDirective(transformer, node, indent);
86
91
  case NodeType.EventDirective:
87
92
  return transformEventDirective(transformer, node, indent);
93
+ case NodeType.ModelDirective:
94
+ return transformModelDirective(transformer, node, indent);
88
95
  case NodeType.SlotElement:
89
96
  return transformSlot(transformer, node, indent);
90
97
  case NodeType.LinkDirective:
@@ -93,6 +100,12 @@ export function transformViewNode(transformer, node, indent = 0) {
93
100
  return transformOutletDirective(transformer, node, indent);
94
101
  case NodeType.NavigateDirective:
95
102
  return transformNavigateDirective(transformer, node, indent);
103
+ case NodeType.A11yDirective:
104
+ return transformA11yDirective(transformer, node, indent);
105
+ case NodeType.LiveDirective:
106
+ return transformLiveDirective(transformer, node, indent);
107
+ case NodeType.FocusTrapDirective:
108
+ return transformFocusTrapDirective(transformer, node, indent);
96
109
  default:
97
110
  return `${' '.repeat(indent)}/* unknown node: ${node.type} */`;
98
111
  }
@@ -192,6 +205,129 @@ export function transformNavigateDirective(transformer, node, indent) {
192
205
  return `${pad}router.navigate(${path}${options})`;
193
206
  }
194
207
 
208
+ /**
209
+ * Transform @a11y directive - sets ARIA attributes
210
+ * @param {Object} transformer - Transformer instance
211
+ * @param {Object} node - A11y directive node
212
+ * @param {number} indent - Indentation level
213
+ * @returns {string} JavaScript code
214
+ */
215
+ export function transformA11yDirective(transformer, node, indent) {
216
+ const pad = ' '.repeat(indent);
217
+ const attrs = node.attrs || {};
218
+
219
+ // Handle @srOnly - create visually hidden element
220
+ if (attrs.srOnly) {
221
+ return `${pad}srOnly(/* content */)`;
222
+ }
223
+
224
+ // Build ARIA attributes object
225
+ const ariaAttrs = Object.entries(attrs).map(([key, value]) => {
226
+ // Map short names to aria- attributes (role is not prefixed)
227
+ const ariaKey = key === 'role' ? key : (key.startsWith('aria-') ? key : `aria-${key}`);
228
+ const valueCode = typeof value === 'string' ? `'${value}'` : transformExpression(transformer, value);
229
+ return `'${ariaKey}': ${valueCode}`;
230
+ }).join(', ');
231
+
232
+ return `{ ${ariaAttrs} }`;
233
+ }
234
+
235
+ /**
236
+ * Build ARIA attributes object from a11y directive
237
+ * @param {Object} transformer - Transformer instance
238
+ * @param {Object} directive - A11y directive node
239
+ * @returns {Object} Object with key-value pairs for ARIA attributes
240
+ */
241
+ export function buildA11yAttributes(transformer, directive) {
242
+ const attrs = directive.attrs || {};
243
+ const result = {};
244
+
245
+ for (const [key, value] of Object.entries(attrs)) {
246
+ if (key === 'srOnly') continue;
247
+ // Map short names to aria- attributes (role is not prefixed)
248
+ const ariaKey = key === 'role' ? key : (key.startsWith('aria-') ? key : `aria-${key}`);
249
+
250
+ // Handle different value types
251
+ if (typeof value === 'string') {
252
+ result[ariaKey] = value;
253
+ } else if (typeof value === 'boolean') {
254
+ result[ariaKey] = String(value); // Convert true/false to "true"/"false"
255
+ } else if (typeof value === 'number') {
256
+ result[ariaKey] = String(value);
257
+ } else {
258
+ result[ariaKey] = transformExpression(transformer, value);
259
+ }
260
+ }
261
+
262
+ return result;
263
+ }
264
+
265
+ /**
266
+ * Transform @live directive - creates live region
267
+ * @param {Object} transformer - Transformer instance
268
+ * @param {Object} node - Live directive node
269
+ * @param {number} indent - Indentation level
270
+ * @returns {string} JavaScript code
271
+ */
272
+ export function transformLiveDirective(transformer, node, indent) {
273
+ const priority = node.priority || 'polite';
274
+ return `'${priority}'`;
275
+ }
276
+
277
+ /**
278
+ * Transform @focusTrap directive
279
+ * @param {Object} transformer - Transformer instance
280
+ * @param {Object} node - Focus trap directive node
281
+ * @param {number} indent - Indentation level
282
+ * @returns {string} JavaScript code
283
+ */
284
+ export function transformFocusTrapDirective(transformer, node, indent) {
285
+ const options = node.options || {};
286
+
287
+ if (Object.keys(options).length === 0) {
288
+ return '{}';
289
+ }
290
+
291
+ const optionsCode = Object.entries(options).map(([key, value]) => {
292
+ const valueCode = typeof value === 'boolean' ? String(value) :
293
+ typeof value === 'string' ? `'${value}'` :
294
+ transformExpression(transformer, value);
295
+ return `${key}: ${valueCode}`;
296
+ }).join(', ');
297
+
298
+ return `{ ${optionsCode} }`;
299
+ }
300
+
301
+ /**
302
+ * Extract dynamic attributes from a selector
303
+ * Returns { cleanSelector, dynamicAttrs } where dynamicAttrs is an array of { name, expr }
304
+ * @param {string} selector - CSS selector with potential dynamic attributes
305
+ * @returns {Object} { cleanSelector, dynamicAttrs }
306
+ */
307
+ function extractDynamicAttributes(selector) {
308
+ const dynamicAttrs = [];
309
+ // Match attributes with {expression} values: [name={expr}] or [name="{expr}"]
310
+ const attrPattern = /\[([a-zA-Z][a-zA-Z0-9-]*)\s*=\s*\{([^}]+)\}\]/g;
311
+ const attrPatternQuoted = /\[([a-zA-Z][a-zA-Z0-9-]*)\s*=\s*"\{([^}]+)\}"\]/g;
312
+
313
+ let cleanSelector = selector;
314
+
315
+ // Extract unquoted dynamic attributes: [value={expr}]
316
+ let match;
317
+ while ((match = attrPattern.exec(selector)) !== null) {
318
+ dynamicAttrs.push({ name: match[1], expr: match[2] });
319
+ }
320
+ cleanSelector = cleanSelector.replace(attrPattern, '');
321
+
322
+ // Extract quoted dynamic attributes: [value="{expr}"]
323
+ while ((match = attrPatternQuoted.exec(selector)) !== null) {
324
+ dynamicAttrs.push({ name: match[1], expr: match[2] });
325
+ }
326
+ cleanSelector = cleanSelector.replace(attrPatternQuoted, '');
327
+
328
+ return { cleanSelector, dynamicAttrs };
329
+ }
330
+
195
331
  /**
196
332
  * Transform element
197
333
  * @param {Object} transformer - Transformer instance
@@ -214,17 +350,88 @@ export function transformElement(transformer, node, indent) {
214
350
  return transformComponentCall(transformer, node, indent);
215
351
  }
216
352
 
353
+ // Extract dynamic attributes from selector (e.g., [value={searchQuery}])
354
+ let { cleanSelector, dynamicAttrs } = extractDynamicAttributes(node.selector);
355
+
217
356
  // Add scoped class to selector if CSS scoping is enabled
218
- let selector = node.selector;
357
+ let selector = cleanSelector;
219
358
  if (transformer.scopeId && selector) {
220
359
  selector = addScopeToSelector(transformer, selector);
221
360
  }
222
361
 
223
- // Start with el() call
224
- parts.push(`${pad}el('${selector}'`);
225
-
226
- // Add event handlers as on() chain
362
+ // Extract directives by type
227
363
  const eventHandlers = node.directives.filter(d => d.type === NodeType.EventDirective);
364
+ const modelDirectives = node.directives.filter(d => d.type === NodeType.ModelDirective);
365
+ const a11yDirectives = node.directives.filter(d => d.type === NodeType.A11yDirective);
366
+ const liveDirectives = node.directives.filter(d => d.type === NodeType.LiveDirective);
367
+ const focusTrapDirectives = node.directives.filter(d => d.type === NodeType.FocusTrapDirective);
368
+
369
+ // Check for @srOnly directive
370
+ const srOnlyDirective = a11yDirectives.find(d => d.attrs && d.attrs.srOnly);
371
+
372
+ // If @srOnly, wrap entire content
373
+ if (srOnlyDirective) {
374
+ transformer.usesA11y.srOnly = true;
375
+ const content = [];
376
+ for (const text of node.textContent) {
377
+ content.push(transformTextNode(transformer, text, 0).trim());
378
+ }
379
+ for (const child of node.children) {
380
+ content.push(transformViewNode(transformer, child, 0).trim());
381
+ }
382
+ const contentCode = content.length === 1 ? content[0] : `[${content.join(', ')}]`;
383
+ return `${pad}srOnly(${contentCode})`;
384
+ }
385
+
386
+ // Track focusTrap usage
387
+ if (focusTrapDirectives.length > 0) {
388
+ transformer.usesA11y.trapFocus = true;
389
+ }
390
+
391
+ // Build ARIA attributes from directives
392
+ const ariaAttrs = [];
393
+
394
+ // Process @a11y directives
395
+ for (const directive of a11yDirectives) {
396
+ const attrs = buildA11yAttributes(transformer, directive);
397
+ for (const [key, value] of Object.entries(attrs)) {
398
+ const valueCode = typeof value === 'string' ? `'${value}'` : value;
399
+ ariaAttrs.push(`'${key}': ${valueCode}`);
400
+ }
401
+ }
402
+
403
+ // Process @live directives (add aria-live and aria-atomic)
404
+ for (const directive of liveDirectives) {
405
+ const priority = directive.priority || 'polite';
406
+ ariaAttrs.push(`'aria-live': '${priority}'`);
407
+ ariaAttrs.push(`'aria-atomic': 'true'`);
408
+ }
409
+
410
+ // Build selector with inline ARIA attributes
411
+ let enhancedSelector = selector;
412
+ if (ariaAttrs.length > 0) {
413
+ // Convert ARIA attrs to selector attribute syntax where possible
414
+ // For dynamic values, we'll need to use setAriaAttributes
415
+ const staticAttrs = [];
416
+ const dynamicAttrs = [];
417
+
418
+ for (const attr of ariaAttrs) {
419
+ const match = attr.match(/^'([^']+)':\s*'([^']+)'$/);
420
+ if (match) {
421
+ // Static attribute - can embed in selector
422
+ staticAttrs.push(`[${match[1]}=${match[2]}]`);
423
+ } else {
424
+ dynamicAttrs.push(attr);
425
+ }
426
+ }
427
+
428
+ // Add static ARIA attributes to selector
429
+ enhancedSelector = selector + staticAttrs.join('');
430
+ }
431
+
432
+ // Start with el() call - escape single quotes in selector
433
+ const escapedSelector = enhancedSelector.replace(/'/g, "\\'");
434
+ parts.push(`${pad}el('${escapedSelector}'`);
228
435
 
229
436
  // Add text content
230
437
  if (node.textContent.length > 0) {
@@ -244,11 +451,49 @@ export function transformElement(transformer, node, indent) {
244
451
 
245
452
  parts.push(')');
246
453
 
247
- // Chain event handlers
454
+ // Chain event handlers with modifiers support
248
455
  let result = parts.join('');
249
456
  for (const handler of eventHandlers) {
250
457
  const handlerCode = transformExpression(transformer, handler.handler);
251
- result = `on(${result}, '${handler.event}', () => { ${handlerCode}; })`;
458
+ const modifiers = handler.modifiers || [];
459
+
460
+ if (modifiers.length === 0) {
461
+ // Always pass event parameter since handlers commonly use event.target, etc.
462
+ result = `on(${result}, '${handler.event}', (event) => { ${handlerCode}; })`;
463
+ } else {
464
+ const modifiedHandler = generateModifiedHandler(handler.event, handlerCode, modifiers);
465
+ result = `on(${result}, '${handler.event}', ${modifiedHandler})`;
466
+ }
467
+ }
468
+
469
+ // Chain model directives for two-way binding
470
+ for (const directive of modelDirectives) {
471
+ const binding = transformExpression(transformer, directive.binding);
472
+ const modifiers = directive.modifiers || [];
473
+
474
+ // Build options from modifiers
475
+ const options = [];
476
+ if (modifiers.includes('lazy')) options.push('lazy: true');
477
+ if (modifiers.includes('trim')) options.push('trim: true');
478
+ if (modifiers.includes('number')) options.push('number: true');
479
+
480
+ if (options.length > 0) {
481
+ result = `model(${result}, ${binding}, { ${options.join(', ')} })`;
482
+ } else {
483
+ result = `model(${result}, ${binding})`;
484
+ }
485
+ }
486
+
487
+ // Chain focus trap if present
488
+ for (const directive of focusTrapDirectives) {
489
+ const optionsCode = transformFocusTrapDirective(transformer, directive, 0);
490
+ result = `trapFocus(${result}, ${optionsCode})`;
491
+ }
492
+
493
+ // Chain dynamic attribute bindings (e.g., [value={searchQuery}])
494
+ for (const attr of dynamicAttrs) {
495
+ const exprCode = transformExpressionString(transformer, attr.expr);
496
+ result = `bind(${result}, '${attr.name}', () => ${exprCode})`;
252
497
  }
253
498
 
254
499
  return result;
@@ -363,25 +608,59 @@ export function transformTextNode(transformer, node, indent) {
363
608
  */
364
609
  export function transformIfDirective(transformer, node, indent) {
365
610
  const pad = ' '.repeat(indent);
366
- const condition = transformExpression(transformer, node.condition);
367
-
368
- const consequent = node.consequent.map(c =>
369
- transformViewNode(transformer, c, indent + 2)
370
- ).join(',\n');
371
611
 
372
- let code = `${pad}when(\n`;
373
- code += `${pad} () => ${condition},\n`;
374
- code += `${pad} () => (\n${consequent}\n${pad} )`;
612
+ // Helper to build nested when() calls for else-if chains
613
+ function buildConditionChain(condition, consequent, elseIfBranches, alternate, depth = 0) {
614
+ const innerPad = ' '.repeat(indent + depth * 2);
615
+ const conditionCode = transformExpression(transformer, condition);
616
+
617
+ // Wrap multiple children in array, single child returns directly
618
+ const consequentItems = consequent.map(c =>
619
+ transformViewNode(transformer, c, indent + depth * 2 + 4)
620
+ );
621
+ const consequentCode = consequentItems.length === 1
622
+ ? consequentItems[0]
623
+ : `[\n${consequentItems.join(',\n')}\n${innerPad} ]`;
624
+
625
+ let code = `${innerPad}when(\n`;
626
+ code += `${innerPad} () => ${conditionCode},\n`;
627
+ code += `${innerPad} () => ${consequentCode}`;
628
+
629
+ // Handle else-if branches
630
+ if (elseIfBranches && elseIfBranches.length > 0) {
631
+ const nextBranch = elseIfBranches[0];
632
+ const remainingBranches = elseIfBranches.slice(1);
633
+
634
+ code += `,\n${innerPad} () => (\n`;
635
+ code += buildConditionChain(
636
+ nextBranch.condition,
637
+ nextBranch.consequent,
638
+ remainingBranches,
639
+ alternate,
640
+ depth + 2
641
+ );
642
+ code += `\n${innerPad} )`;
643
+ } else if (alternate) {
644
+ // Final else branch - wrap multiple children in array
645
+ const alternateItems = alternate.map(c =>
646
+ transformViewNode(transformer, c, indent + depth * 2 + 4)
647
+ );
648
+ const alternateCode = alternateItems.length === 1
649
+ ? alternateItems[0]
650
+ : `[\n${alternateItems.join(',\n')}\n${innerPad} ]`;
651
+ code += `,\n${innerPad} () => ${alternateCode}`;
652
+ }
375
653
 
376
- if (node.alternate) {
377
- const alternate = node.alternate.map(c =>
378
- transformViewNode(transformer, c, indent + 2)
379
- ).join(',\n');
380
- code += `,\n${pad} () => (\n${alternate}\n${pad} )`;
654
+ code += `\n${innerPad})`;
655
+ return code;
381
656
  }
382
657
 
383
- code += `\n${pad})`;
384
- return code;
658
+ return buildConditionChain(
659
+ node.condition,
660
+ node.consequent,
661
+ node.elseIfBranches || [],
662
+ node.alternate
663
+ );
385
664
  }
386
665
 
387
666
  /**
@@ -399,10 +678,19 @@ export function transformEachDirective(transformer, node, indent) {
399
678
  transformViewNode(transformer, t, indent + 2)
400
679
  ).join(',\n');
401
680
 
402
- return `${pad}list(\n` +
403
- `${pad} () => ${iterable},\n` +
404
- `${pad} (${node.itemName}, _index) => (\n${template}\n${pad} )\n` +
405
- `${pad})`;
681
+ // Build list() call with optional key function
682
+ let code = `${pad}list(\n` +
683
+ `${pad} () => ${iterable},\n` +
684
+ `${pad} (${node.itemName}, _index) => (\n${template}\n${pad} )`;
685
+
686
+ // Add key function if provided
687
+ if (node.keyExpr) {
688
+ const keyExprCode = transformExpression(transformer, node.keyExpr);
689
+ code += `,\n${pad} (${node.itemName}) => ${keyExprCode}`;
690
+ }
691
+
692
+ code += `\n${pad})`;
693
+ return code;
406
694
  }
407
695
 
408
696
  /**
@@ -426,3 +714,85 @@ export function transformEventDirective(transformer, node, indent) {
426
714
 
427
715
  return `/* event: ${node.event} -> ${handler} */`;
428
716
  }
717
+
718
+ /**
719
+ * Transform @model directive for two-way binding
720
+ * @param {Object} transformer - Transformer instance
721
+ * @param {Object} node - Model directive node
722
+ * @param {number} indent - Indentation level
723
+ * @returns {string} JavaScript code
724
+ */
725
+ export function transformModelDirective(transformer, node, indent) {
726
+ const pad = ' '.repeat(indent);
727
+ const binding = transformExpression(transformer, node.binding);
728
+ const modifiers = node.modifiers || [];
729
+
730
+ // Build options from modifiers
731
+ const options = [];
732
+ if (modifiers.includes('lazy')) options.push('lazy: true');
733
+ if (modifiers.includes('trim')) options.push('trim: true');
734
+ if (modifiers.includes('number')) options.push('number: true');
735
+
736
+ if (options.length > 0) {
737
+ return `${pad}/* model: ${binding} { ${options.join(', ')} } */`;
738
+ }
739
+
740
+ return `${pad}/* model: ${binding} */`;
741
+ }
742
+
743
+ /**
744
+ * Generate event handler code with modifiers applied
745
+ * @param {string} event - Event name
746
+ * @param {string} handlerCode - Handler expression code
747
+ * @param {string[]} modifiers - Array of modifier names
748
+ * @returns {string} JavaScript handler code
749
+ */
750
+ function generateModifiedHandler(event, handlerCode, modifiers) {
751
+ // Key modifiers map
752
+ const keyMap = {
753
+ enter: 'Enter', tab: 'Tab', delete: 'Delete', esc: 'Escape', escape: 'Escape',
754
+ space: ' ', up: 'ArrowUp', down: 'ArrowDown', left: 'ArrowLeft', right: 'ArrowRight'
755
+ };
756
+
757
+ // System key modifiers
758
+ const systemModifiers = ['ctrl', 'alt', 'shift', 'meta'];
759
+
760
+ // Build handler code with checks
761
+ const checks = [];
762
+ let hasEventParam = false;
763
+
764
+ for (const mod of modifiers) {
765
+ if (mod === 'prevent') {
766
+ checks.push('event.preventDefault();');
767
+ hasEventParam = true;
768
+ } else if (mod === 'stop') {
769
+ checks.push('event.stopPropagation();');
770
+ hasEventParam = true;
771
+ } else if (mod === 'self') {
772
+ checks.push('if (event.target !== event.currentTarget) return;');
773
+ hasEventParam = true;
774
+ } else if (keyMap[mod]) {
775
+ checks.push(`if (event.key !== '${keyMap[mod]}') return;`);
776
+ hasEventParam = true;
777
+ } else if (systemModifiers.includes(mod)) {
778
+ checks.push(`if (!event.${mod}Key) return;`);
779
+ hasEventParam = true;
780
+ }
781
+ }
782
+
783
+ // Build options for addEventListener
784
+ const options = [];
785
+ if (modifiers.includes('capture')) options.push('capture: true');
786
+ if (modifiers.includes('once')) options.push('once: true');
787
+ if (modifiers.includes('passive')) options.push('passive: true');
788
+
789
+ const checksCode = checks.join(' ');
790
+ // Always pass event parameter since handler code commonly uses event.target, etc.
791
+ const handler = `(event) => { ${checksCode} ${handlerCode}; }`;
792
+
793
+ if (options.length > 0) {
794
+ return `${handler}, { ${options.join(', ')} }`;
795
+ }
796
+
797
+ return handler;
798
+ }
@@ -5,6 +5,8 @@
5
5
  */
6
6
 
7
7
  import { compile } from '../compiler/index.js';
8
+ import { existsSync } from 'fs';
9
+ import { resolve, dirname } from 'path';
8
10
 
9
11
  /**
10
12
  * Create Pulse Vite plugin
@@ -18,14 +20,35 @@ export default function pulsePlugin(options = {}) {
18
20
 
19
21
  return {
20
22
  name: 'vite-plugin-pulse',
23
+ enforce: 'pre',
21
24
 
22
25
  /**
23
- * Resolve .pulse files
26
+ * Resolve .pulse files and .js imports that map to .pulse files
27
+ * The compiler transforms .pulse imports to .js, so we need to
28
+ * resolve them back to .pulse for Vite to process them
24
29
  */
25
- resolveId(id) {
26
- if (id.endsWith('.pulse')) {
27
- return id;
30
+ resolveId(id, importer) {
31
+ // Direct .pulse imports - resolve to absolute path
32
+ if (id.endsWith('.pulse') && importer) {
33
+ const importerDir = dirname(importer);
34
+ const absolutePath = resolve(importerDir, id);
35
+ if (existsSync(absolutePath)) {
36
+ return absolutePath;
37
+ }
38
+ }
39
+
40
+ // Check if a .js import has a corresponding .pulse file
41
+ // This handles the compiler's transformation of .pulse -> .js imports
42
+ if (id.endsWith('.js') && importer) {
43
+ const pulseId = id.replace(/\.js$/, '.pulse');
44
+ const importerDir = dirname(importer);
45
+ const absolutePulsePath = resolve(importerDir, pulseId);
46
+
47
+ if (existsSync(absolutePulsePath)) {
48
+ return absolutePulsePath;
49
+ }
28
50
  }
51
+
29
52
  return null;
30
53
  },
31
54
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pulse-js-framework",
3
- "version": "1.7.9",
3
+ "version": "1.7.11",
4
4
  "description": "A declarative DOM framework with CSS selector-based structure and reactive pulsations",
5
5
  "type": "module",
6
6
  "main": "index.js",
@@ -68,6 +68,10 @@
68
68
  "types": "./types/http.d.ts",
69
69
  "default": "./runtime/http.js"
70
70
  },
71
+ "./runtime/a11y": {
72
+ "types": "./types/a11y.d.ts",
73
+ "default": "./runtime/a11y.js"
74
+ },
71
75
  "./runtime/devtools": "./runtime/devtools.js",
72
76
  "./compiler": {
73
77
  "types": "./types/index.d.ts",
@@ -97,7 +101,7 @@
97
101
  "LICENSE"
98
102
  ],
99
103
  "scripts": {
100
- "test": "npm run test:compiler && npm run test:sourcemap && npm run test:pulse && npm run test:dom && npm run test:dom-adapter && npm run test:router && npm run test:store && npm run test:hmr && npm run test:lint && npm run test:format && npm run test:analyze && npm run test:lru-cache && npm run test:utils && npm run test:docs && npm run test:async && npm run test:form && npm run test:http && npm run test:devtools && npm run test:native",
104
+ "test": "npm run test:compiler && npm run test:sourcemap && npm run test:pulse && npm run test:dom && npm run test:dom-adapter && npm run test:router && npm run test:store && npm run test:hmr && npm run test:lint && npm run test:format && npm run test:analyze && npm run test:lru-cache && npm run test:utils && npm run test:docs && npm run test:async && npm run test:form && npm run test:http && npm run test:devtools && npm run test:native && npm run test:a11y && npm run test:logger && npm run test:errors",
101
105
  "test:compiler": "node test/compiler.test.js",
102
106
  "test:sourcemap": "node test/sourcemap.test.js",
103
107
  "test:pulse": "node test/pulse.test.js",
@@ -117,6 +121,9 @@
117
121
  "test:http": "node test/http.test.js",
118
122
  "test:devtools": "node test/devtools.test.js",
119
123
  "test:native": "node test/native.test.js",
124
+ "test:a11y": "node test/a11y.test.js",
125
+ "test:logger": "node test/logger.test.js",
126
+ "test:errors": "node test/errors.test.js",
120
127
  "build:netlify": "node scripts/build-netlify.js",
121
128
  "version": "node scripts/sync-version.js",
122
129
  "docs": "node cli/index.js dev docs"