pulse-js-framework 1.7.10 → 1.7.12
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 +3 -1
- package/compiler/lexer.js +23 -1
- package/compiler/parser.js +118 -17
- package/compiler/transformer/export.js +41 -2
- package/compiler/transformer/expressions.js +148 -5
- package/compiler/transformer/imports.js +1 -0
- package/compiler/transformer/view.js +219 -24
- package/loader/vite-plugin.js +27 -4
- package/package.json +15 -2
- package/runtime/context.js +374 -0
- package/runtime/dom-binding.js +32 -0
- package/runtime/graphql.js +1356 -0
- package/runtime/index.js +6 -0
- package/runtime/logger.js +2 -1
- package/runtime/websocket.js +874 -0
- package/types/context.d.ts +171 -0
- package/types/graphql.d.ts +490 -0
- package/types/index.d.ts +15 -0
- package/types/websocket.d.ts +347 -0
|
@@ -15,6 +15,7 @@ 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',
|
|
@@ -89,6 +90,8 @@ export function transformViewNode(transformer, node, indent = 0) {
|
|
|
89
90
|
return transformEachDirective(transformer, node, indent);
|
|
90
91
|
case NodeType.EventDirective:
|
|
91
92
|
return transformEventDirective(transformer, node, indent);
|
|
93
|
+
case NodeType.ModelDirective:
|
|
94
|
+
return transformModelDirective(transformer, node, indent);
|
|
92
95
|
case NodeType.SlotElement:
|
|
93
96
|
return transformSlot(transformer, node, indent);
|
|
94
97
|
case NodeType.LinkDirective:
|
|
@@ -295,6 +298,36 @@ export function transformFocusTrapDirective(transformer, node, indent) {
|
|
|
295
298
|
return `{ ${optionsCode} }`;
|
|
296
299
|
}
|
|
297
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
|
+
|
|
298
331
|
/**
|
|
299
332
|
* Transform element
|
|
300
333
|
* @param {Object} transformer - Transformer instance
|
|
@@ -317,14 +350,18 @@ export function transformElement(transformer, node, indent) {
|
|
|
317
350
|
return transformComponentCall(transformer, node, indent);
|
|
318
351
|
}
|
|
319
352
|
|
|
353
|
+
// Extract dynamic attributes from selector (e.g., [value={searchQuery}])
|
|
354
|
+
let { cleanSelector, dynamicAttrs } = extractDynamicAttributes(node.selector);
|
|
355
|
+
|
|
320
356
|
// Add scoped class to selector if CSS scoping is enabled
|
|
321
|
-
let selector =
|
|
357
|
+
let selector = cleanSelector;
|
|
322
358
|
if (transformer.scopeId && selector) {
|
|
323
359
|
selector = addScopeToSelector(transformer, selector);
|
|
324
360
|
}
|
|
325
361
|
|
|
326
362
|
// Extract directives by type
|
|
327
363
|
const eventHandlers = node.directives.filter(d => d.type === NodeType.EventDirective);
|
|
364
|
+
const modelDirectives = node.directives.filter(d => d.type === NodeType.ModelDirective);
|
|
328
365
|
const a11yDirectives = node.directives.filter(d => d.type === NodeType.A11yDirective);
|
|
329
366
|
const liveDirectives = node.directives.filter(d => d.type === NodeType.LiveDirective);
|
|
330
367
|
const focusTrapDirectives = node.directives.filter(d => d.type === NodeType.FocusTrapDirective);
|
|
@@ -392,8 +429,9 @@ export function transformElement(transformer, node, indent) {
|
|
|
392
429
|
enhancedSelector = selector + staticAttrs.join('');
|
|
393
430
|
}
|
|
394
431
|
|
|
395
|
-
// Start with el() call
|
|
396
|
-
|
|
432
|
+
// Start with el() call - escape single quotes in selector
|
|
433
|
+
const escapedSelector = enhancedSelector.replace(/'/g, "\\'");
|
|
434
|
+
parts.push(`${pad}el('${escapedSelector}'`);
|
|
397
435
|
|
|
398
436
|
// Add text content
|
|
399
437
|
if (node.textContent.length > 0) {
|
|
@@ -413,11 +451,37 @@ export function transformElement(transformer, node, indent) {
|
|
|
413
451
|
|
|
414
452
|
parts.push(')');
|
|
415
453
|
|
|
416
|
-
// Chain event handlers
|
|
454
|
+
// Chain event handlers with modifiers support
|
|
417
455
|
let result = parts.join('');
|
|
418
456
|
for (const handler of eventHandlers) {
|
|
419
457
|
const handlerCode = transformExpression(transformer, handler.handler);
|
|
420
|
-
|
|
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
|
+
}
|
|
421
485
|
}
|
|
422
486
|
|
|
423
487
|
// Chain focus trap if present
|
|
@@ -426,6 +490,12 @@ export function transformElement(transformer, node, indent) {
|
|
|
426
490
|
result = `trapFocus(${result}, ${optionsCode})`;
|
|
427
491
|
}
|
|
428
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})`;
|
|
497
|
+
}
|
|
498
|
+
|
|
429
499
|
return result;
|
|
430
500
|
}
|
|
431
501
|
|
|
@@ -538,25 +608,59 @@ export function transformTextNode(transformer, node, indent) {
|
|
|
538
608
|
*/
|
|
539
609
|
export function transformIfDirective(transformer, node, indent) {
|
|
540
610
|
const pad = ' '.repeat(indent);
|
|
541
|
-
const condition = transformExpression(transformer, node.condition);
|
|
542
|
-
|
|
543
|
-
const consequent = node.consequent.map(c =>
|
|
544
|
-
transformViewNode(transformer, c, indent + 2)
|
|
545
|
-
).join(',\n');
|
|
546
611
|
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
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
|
+
}
|
|
550
653
|
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
transformViewNode(transformer, c, indent + 2)
|
|
554
|
-
).join(',\n');
|
|
555
|
-
code += `,\n${pad} () => (\n${alternate}\n${pad} )`;
|
|
654
|
+
code += `\n${innerPad})`;
|
|
655
|
+
return code;
|
|
556
656
|
}
|
|
557
657
|
|
|
558
|
-
|
|
559
|
-
|
|
658
|
+
return buildConditionChain(
|
|
659
|
+
node.condition,
|
|
660
|
+
node.consequent,
|
|
661
|
+
node.elseIfBranches || [],
|
|
662
|
+
node.alternate
|
|
663
|
+
);
|
|
560
664
|
}
|
|
561
665
|
|
|
562
666
|
/**
|
|
@@ -574,10 +678,19 @@ export function transformEachDirective(transformer, node, indent) {
|
|
|
574
678
|
transformViewNode(transformer, t, indent + 2)
|
|
575
679
|
).join(',\n');
|
|
576
680
|
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
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;
|
|
581
694
|
}
|
|
582
695
|
|
|
583
696
|
/**
|
|
@@ -601,3 +714,85 @@ export function transformEventDirective(transformer, node, indent) {
|
|
|
601
714
|
|
|
602
715
|
return `/* event: ${node.event} -> ${handler} */`;
|
|
603
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
|
+
}
|
package/loader/vite-plugin.js
CHANGED
|
@@ -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
|
-
|
|
27
|
-
|
|
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.
|
|
3
|
+
"version": "1.7.12",
|
|
4
4
|
"description": "A declarative DOM framework with CSS selector-based structure and reactive pulsations",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "index.js",
|
|
@@ -73,6 +73,14 @@
|
|
|
73
73
|
"default": "./runtime/a11y.js"
|
|
74
74
|
},
|
|
75
75
|
"./runtime/devtools": "./runtime/devtools.js",
|
|
76
|
+
"./runtime/websocket": {
|
|
77
|
+
"types": "./types/websocket.d.ts",
|
|
78
|
+
"default": "./runtime/websocket.js"
|
|
79
|
+
},
|
|
80
|
+
"./runtime/graphql": {
|
|
81
|
+
"types": "./types/graphql.d.ts",
|
|
82
|
+
"default": "./runtime/graphql.js"
|
|
83
|
+
},
|
|
76
84
|
"./compiler": {
|
|
77
85
|
"types": "./types/index.d.ts",
|
|
78
86
|
"default": "./compiler/index.js"
|
|
@@ -101,7 +109,7 @@
|
|
|
101
109
|
"LICENSE"
|
|
102
110
|
],
|
|
103
111
|
"scripts": {
|
|
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",
|
|
112
|
+
"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:cli && npm run test:cli-ui && 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 && npm run test:security && npm run test:websocket && npm run test:graphql",
|
|
105
113
|
"test:compiler": "node test/compiler.test.js",
|
|
106
114
|
"test:sourcemap": "node test/sourcemap.test.js",
|
|
107
115
|
"test:pulse": "node test/pulse.test.js",
|
|
@@ -113,6 +121,8 @@
|
|
|
113
121
|
"test:lint": "node test/lint.test.js",
|
|
114
122
|
"test:format": "node test/format.test.js",
|
|
115
123
|
"test:analyze": "node test/analyze.test.js",
|
|
124
|
+
"test:cli": "node test/cli.test.js",
|
|
125
|
+
"test:cli-ui": "node test/cli-ui.test.js",
|
|
116
126
|
"test:lru-cache": "node test/lru-cache.test.js",
|
|
117
127
|
"test:utils": "node test/utils.test.js",
|
|
118
128
|
"test:docs": "node test/docs.test.js",
|
|
@@ -124,6 +134,9 @@
|
|
|
124
134
|
"test:a11y": "node test/a11y.test.js",
|
|
125
135
|
"test:logger": "node test/logger.test.js",
|
|
126
136
|
"test:errors": "node test/errors.test.js",
|
|
137
|
+
"test:security": "node test/security.test.js",
|
|
138
|
+
"test:websocket": "node test/websocket.test.js",
|
|
139
|
+
"test:graphql": "node test/graphql.test.js",
|
|
127
140
|
"build:netlify": "node scripts/build-netlify.js",
|
|
128
141
|
"version": "node scripts/sync-version.js",
|
|
129
142
|
"docs": "node cli/index.js dev docs"
|