pulse-js-framework 1.10.0 → 1.10.3

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.
Files changed (37) hide show
  1. package/compiler/parser/_extract.js +393 -0
  2. package/compiler/parser/blocks.js +361 -0
  3. package/compiler/parser/core.js +306 -0
  4. package/compiler/parser/expressions.js +386 -0
  5. package/compiler/parser/imports.js +108 -0
  6. package/compiler/parser/index.js +47 -0
  7. package/compiler/parser/state.js +155 -0
  8. package/compiler/parser/style.js +445 -0
  9. package/compiler/parser/view.js +632 -0
  10. package/compiler/parser.js +15 -2372
  11. package/compiler/parser.js.original +2376 -0
  12. package/package.json +2 -1
  13. package/runtime/a11y/announcements.js +213 -0
  14. package/runtime/a11y/contrast.js +125 -0
  15. package/runtime/a11y/focus.js +412 -0
  16. package/runtime/a11y/index.js +35 -0
  17. package/runtime/a11y/preferences.js +121 -0
  18. package/runtime/a11y/utils.js +164 -0
  19. package/runtime/a11y/validation.js +258 -0
  20. package/runtime/a11y/widgets.js +545 -0
  21. package/runtime/a11y.js +15 -1840
  22. package/runtime/a11y.js.original +1844 -0
  23. package/runtime/graphql/cache.js +69 -0
  24. package/runtime/graphql/client.js +563 -0
  25. package/runtime/graphql/hooks.js +492 -0
  26. package/runtime/graphql/index.js +62 -0
  27. package/runtime/graphql/subscriptions.js +241 -0
  28. package/runtime/graphql.js +12 -1322
  29. package/runtime/graphql.js.original +1326 -0
  30. package/runtime/router/core.js +956 -0
  31. package/runtime/router/guards.js +90 -0
  32. package/runtime/router/history.js +204 -0
  33. package/runtime/router/index.js +36 -0
  34. package/runtime/router/lazy.js +180 -0
  35. package/runtime/router/utils.js +226 -0
  36. package/runtime/router.js +12 -1600
  37. package/runtime/router.js.original +1605 -0
@@ -0,0 +1,393 @@
1
+ /**
2
+ * Parser extraction script - splits parser.js into sub-modules
3
+ * Run: node compiler/parser/_extract.js
4
+ */
5
+ import fs from 'fs';
6
+ import path from 'path';
7
+ import { fileURLToPath } from 'url';
8
+
9
+ const __dirname = path.dirname(fileURLToPath(import.meta.url));
10
+ const source = fs.readFileSync(path.join(__dirname, '..', 'parser.js.original'), 'utf-8');
11
+ const lines = source.split('\n');
12
+
13
+ // Helper: extract lines (1-indexed, inclusive)
14
+ function extract(start, end) {
15
+ return lines.slice(start - 1, end).join('\n');
16
+ }
17
+
18
+ // Helper: extract a method body and convert to prototype assignment
19
+ function extractMethod(methodName, start, end) {
20
+ const methodLines = lines.slice(start - 1, end);
21
+ const body = [];
22
+
23
+ // Find the method signature line
24
+ let sigIdx = 0;
25
+ for (let i = 0; i < methodLines.length; i++) {
26
+ if (methodLines[i].match(new RegExp(`^\\s+${methodName}\\s*\\(`)) ||
27
+ methodLines[i].match(new RegExp(`^\\s+async\\s+${methodName}\\s*\\(`))) {
28
+ sigIdx = i;
29
+ break;
30
+ }
31
+ }
32
+
33
+ // Find where JSDoc starts (look backwards from signature)
34
+ let docStart = sigIdx;
35
+ for (let i = sigIdx - 1; i >= 0; i--) {
36
+ if (methodLines[i].trim().startsWith('/**') || methodLines[i].trim().startsWith('*')) {
37
+ docStart = i;
38
+ } else if (methodLines[i].trim() === '') {
39
+ // Skip blank lines before JSDoc
40
+ if (docStart < sigIdx) break;
41
+ } else {
42
+ break;
43
+ }
44
+ }
45
+
46
+ // Extract JSDoc (if any) - keep indentation as-is
47
+ const jsdocLines = methodLines.slice(docStart, sigIdx);
48
+
49
+ // Extract method signature
50
+ const sigLine = methodLines[sigIdx];
51
+ const isAsync = sigLine.includes('async');
52
+ const paramsMatch = sigLine.match(/\(([^)]*)\)/);
53
+ const params = paramsMatch ? paramsMatch[1] : '';
54
+
55
+ // Extract body (everything after the signature line until the closing brace)
56
+ const bodyLines = methodLines.slice(sigIdx + 1, methodLines.length);
57
+
58
+ // Build prototype assignment
59
+ const jsdoc = jsdocLines.length > 0 ? jsdocLines.join('\n') + '\n' : '';
60
+ const asyncPrefix = isAsync ? 'async ' : '';
61
+
62
+ return `${jsdoc} ${asyncPrefix}${methodName}(${params}) {\n${bodyLines.join('\n')}`;
63
+ }
64
+
65
+ // Define module structure with method line ranges
66
+ const modules = {
67
+ // core.js: lines 1-290 (NodeType, ASTNode, Parser class with core methods + parse())
68
+ 'core.js': {
69
+ header: `/**
70
+ * Pulse Parser - Core
71
+ *
72
+ * Parser class, AST node types, and infrastructure methods
73
+ *
74
+ * @module pulse-js-framework/compiler/parser/core
75
+ */
76
+
77
+ import { TokenType } from '../lexer.js';
78
+ import { ParserError, SUGGESTIONS, getDocsUrl } from '../../runtime/errors.js';
79
+ `,
80
+ content: extract(10, 290),
81
+ // We'll close the class later after all methods are extracted
82
+ },
83
+
84
+ // imports.js: lines 300-380
85
+ 'imports.js': {
86
+ header: `/**
87
+ * Pulse Parser - Import Parsing
88
+ *
89
+ * Import, page, and route declaration parsing
90
+ *
91
+ * @module pulse-js-framework/compiler/parser/imports
92
+ */
93
+
94
+ import { TokenType } from '../lexer.js';
95
+ import { Parser, NodeType, ASTNode } from './core.js';
96
+ `,
97
+ methods: [
98
+ { name: 'parseImportDeclaration', start: 292, end: 362 },
99
+ { name: 'parsePageDeclaration', start: 364, end: 371 },
100
+ { name: 'parseRouteDeclaration', start: 373, end: 380 },
101
+ ]
102
+ },
103
+
104
+ // state.js: lines 389-513
105
+ 'state.js': {
106
+ header: `/**
107
+ * Pulse Parser - State & Props
108
+ *
109
+ * Props block, state block, and value/literal parsing
110
+ *
111
+ * @module pulse-js-framework/compiler/parser/state
112
+ */
113
+
114
+ import { TokenType } from '../lexer.js';
115
+ import { Parser, NodeType, ASTNode } from './core.js';
116
+ `,
117
+ methods: [
118
+ { name: 'parsePropsBlock', start: 382, end: 400 },
119
+ { name: 'parsePropsProperty', start: 402, end: 410 },
120
+ { name: 'parseStateBlock', start: 412, end: 426 },
121
+ { name: 'parseStateProperty', start: 428, end: 436 },
122
+ { name: 'tryParseLiteral', start: 438, end: 455 },
123
+ { name: 'parseValue', start: 457, end: 472 },
124
+ { name: 'parseObjectLiteral', start: 474, end: 494 },
125
+ { name: 'parseArrayLiteral', start: 496, end: 513 },
126
+ ]
127
+ },
128
+
129
+ // view.js: lines 518-735 + directives 740-1133
130
+ 'view.js': {
131
+ header: `/**
132
+ * Pulse Parser - View & Directives
133
+ *
134
+ * View block, elements, text nodes, and all directive parsing
135
+ *
136
+ * @module pulse-js-framework/compiler/parser/view
137
+ */
138
+
139
+ import { TokenType } from '../lexer.js';
140
+ import { Parser, NodeType, ASTNode } from './core.js';
141
+ `,
142
+ methods: [
143
+ { name: 'parseViewBlock', start: 515, end: 529 },
144
+ { name: 'parseViewChild', start: 531, end: 554 },
145
+ { name: 'parseSlotElement', start: 556, end: 588 },
146
+ { name: 'parseElement', start: 590, end: 652 },
147
+ { name: 'parseComponentProp', start: 654, end: 678 },
148
+ { name: 'couldBeElement', start: 680, end: 688 },
149
+ { name: 'parseTextNode', start: 690, end: 697 },
150
+ { name: 'parseInterpolatedString', start: 699, end: 735 },
151
+ // Directives (tightly coupled with view)
152
+ { name: 'parseDirective', start: 737, end: 821 },
153
+ { name: 'parseInlineDirective', start: 823, end: 869 },
154
+ { name: 'parseIfDirective', start: 871, end: 952 },
155
+ { name: 'parseEachDirective', start: 954, end: 989 },
156
+ { name: 'parseEventDirective', start: 991, end: 1011 },
157
+ { name: 'parseModelDirective', start: 1013, end: 1024 },
158
+ { name: 'parseA11yDirective', start: 1026, end: 1066 },
159
+ { name: 'parseLiveDirective', start: 1068, end: 1083 },
160
+ { name: 'parseFocusTrapDirective', start: 1085, end: 1124 },
161
+ { name: 'parseSrOnlyDirective', start: 1126, end: 1133 },
162
+ ]
163
+ },
164
+
165
+ // expressions.js: lines 1138-1500
166
+ 'expressions.js': {
167
+ header: `/**
168
+ * Pulse Parser - Expression Parsing
169
+ *
170
+ * Expression parsing with precedence climbing algorithm
171
+ *
172
+ * @module pulse-js-framework/compiler/parser/expressions
173
+ */
174
+
175
+ import { TokenType } from '../lexer.js';
176
+ import { Parser, NodeType, ASTNode } from './core.js';
177
+ `,
178
+ methods: [
179
+ { name: 'parseExpression', start: 1135, end: 1140 },
180
+ { name: 'parseAssignmentExpression', start: 1142, end: 1171 },
181
+ { name: 'parseConditionalExpression', start: 1173, end: 1188 },
182
+ // BINARY_OPS static property needs special handling
183
+ { name: 'parseBinaryExpr', start: 1202, end: 1220 },
184
+ { name: 'parseOrExpression', start: 1222, end: 1223 },
185
+ { name: 'parseUnaryExpression', start: 1225, end: 1241 },
186
+ { name: 'parsePostfixExpression', start: 1243, end: 1259 },
187
+ { name: 'parsePrimaryExpression', start: 1261, end: 1326 },
188
+ { name: 'tryParseArrowFunction', start: 1328, end: 1351 },
189
+ { name: 'parseArrowFunction', start: 1353, end: 1394 },
190
+ { name: 'parseArrayLiteralExpr', start: 1396, end: 1419 },
191
+ { name: 'parseObjectLiteralExpr', start: 1421, end: 1456 },
192
+ { name: 'parseIdentifierOrExpression', start: 1458, end: 1500 },
193
+ ],
194
+ extraContent: extract(1190, 1200) // BINARY_OPS static property
195
+ },
196
+
197
+ // style.js: lines 1584-2359
198
+ 'style.js': {
199
+ header: `/**
200
+ * Pulse Parser - Style Block
201
+ *
202
+ * CSS parsing with preprocessor support
203
+ *
204
+ * @module pulse-js-framework/compiler/parser/style
205
+ */
206
+
207
+ import { TokenType } from '../lexer.js';
208
+ import { Parser, NodeType, ASTNode } from './core.js';
209
+ `,
210
+ methods: [
211
+ { name: 'parseStyleBlock', start: 1581, end: 1637 },
212
+ { name: 'reconstructCSS', start: 1639, end: 1663 },
213
+ { name: 'parseStyleRule', start: 1665, end: 1770 },
214
+ { name: 'isNestedRule', start: 1772, end: 1822 },
215
+ { name: 'parseStyleProperty', start: 1824, end: 2047 },
216
+ { name: 'isPropertyStart', start: 2341, end: 2359 },
217
+ ]
218
+ },
219
+
220
+ // blocks.js: lines 1505-1579 + 2063-2336
221
+ 'blocks.js': {
222
+ header: `/**
223
+ * Pulse Parser - Block Parsing
224
+ *
225
+ * Actions, router, store blocks, and function/guard parsing
226
+ *
227
+ * @module pulse-js-framework/compiler/parser/blocks
228
+ */
229
+
230
+ import { TokenType } from '../lexer.js';
231
+ import { Parser, NodeType, ASTNode } from './core.js';
232
+ `,
233
+ methods: [
234
+ { name: 'parseActionsBlock', start: 1502, end: 1516 },
235
+ { name: 'parseFunctionDeclaration', start: 1518, end: 1555 },
236
+ { name: 'parseFunctionBody', start: 1557, end: 1579 },
237
+ { name: 'parseRouterBlock', start: 2049, end: 2110 },
238
+ { name: 'parseRoutesBlock', start: 2112, end: 2133 },
239
+ { name: 'parseGuardHook', start: 2135, end: 2159 },
240
+ { name: 'parseStoreBlock', start: 2161, end: 2237 },
241
+ { name: 'parseGettersBlock', start: 2239, end: 2256 },
242
+ { name: 'parseGetterDeclaration', start: 2258, end: 2270 },
243
+ { name: 'parseLinkDirective', start: 2272, end: 2304 },
244
+ { name: 'parseOutletDirective', start: 2306, end: 2319 },
245
+ { name: 'parseNavigateDirective', start: 2321, end: 2336 },
246
+ ]
247
+ }
248
+ };
249
+
250
+ // Process core.js specially (it keeps the class definition open for prototype extensions)
251
+ const coreContent = modules['core.js'].content;
252
+ // The core.js content includes lines 10-290, which ends with parse() method
253
+ // We need to close the class properly
254
+ const coreFile = modules['core.js'].header + '\n' + coreContent + '\n}\n';
255
+ fs.writeFileSync(path.join(__dirname, 'core.js'), coreFile);
256
+ console.log('✓ core.js written');
257
+
258
+ // Process each sub-module
259
+ for (const [filename, config] of Object.entries(modules)) {
260
+ if (filename === 'core.js') continue;
261
+
262
+ let content = config.header + '\n';
263
+
264
+ // Add extra content (like BINARY_OPS)
265
+ if (config.extraContent) {
266
+ content += '// Static properties\n';
267
+ content += config.extraContent + '\n\n';
268
+ }
269
+
270
+ // Extract each method and convert to prototype assignment
271
+ for (const method of config.methods) {
272
+ const methodLines = lines.slice(method.start - 1, method.end);
273
+
274
+ // Find the actual method signature
275
+ let sigIdx = -1;
276
+ for (let i = 0; i < methodLines.length; i++) {
277
+ const line = methodLines[i].trimStart();
278
+ if (line.startsWith(`${method.name}(`) || line.startsWith(`async ${method.name}(`)) {
279
+ sigIdx = i;
280
+ break;
281
+ }
282
+ }
283
+
284
+ if (sigIdx === -1) {
285
+ console.error(` ✗ Could not find method signature for ${method.name} in lines ${method.start}-${method.end}`);
286
+ // Try broader search
287
+ for (let i = 0; i < methodLines.length; i++) {
288
+ if (methodLines[i].includes(method.name)) {
289
+ console.error(` Found "${methodLines[i].trim()}" at offset ${i} (line ${method.start + i})`);
290
+ }
291
+ }
292
+ continue;
293
+ }
294
+
295
+ // Get JSDoc (lines before signature)
296
+ let docStart = sigIdx;
297
+ for (let i = sigIdx - 1; i >= 0; i--) {
298
+ const trimmed = methodLines[i].trim();
299
+ if (trimmed.startsWith('/**') || trimmed.startsWith('*') || trimmed.startsWith('*/')) {
300
+ docStart = i;
301
+ } else if (trimmed === '') {
302
+ continue; // skip blank lines between JSDoc blocks
303
+ } else {
304
+ break;
305
+ }
306
+ }
307
+
308
+ // Build JSDoc as standalone comment
309
+ const jsdocLines = methodLines.slice(docStart, sigIdx);
310
+ const jsdoc = jsdocLines.map(l => l.replace(/^ /, '')).join('\n');
311
+
312
+ // Parse method signature
313
+ const sigLine = methodLines[sigIdx];
314
+ const isAsync = sigLine.trimStart().startsWith('async');
315
+
316
+ // Extract parameter string
317
+ const paramMatch = sigLine.match(/\(([^)]*)\)/);
318
+ const params = paramMatch ? paramMatch[1] : '';
319
+
320
+ // Extract body (everything from line after signature to end, minus last closing brace)
321
+ const bodyLines = methodLines.slice(sigIdx + 1);
322
+
323
+ // Remove the last closing brace (which was the method's closing brace)
324
+ let bodyStr = bodyLines.join('\n');
325
+ // Find last } and remove it
326
+ const lastBraceIdx = bodyStr.lastIndexOf('}');
327
+ if (lastBraceIdx >= 0) {
328
+ bodyStr = bodyStr.substring(0, lastBraceIdx) + bodyStr.substring(lastBraceIdx + 1);
329
+ }
330
+
331
+ // Build prototype assignment
332
+ if (jsdoc.trim()) {
333
+ content += `${jsdoc}\n`;
334
+ }
335
+ content += `Parser.prototype.${method.name} = ${isAsync ? 'async ' : ''}function(${params}) {\n`;
336
+ content += bodyStr;
337
+ content += '};\n\n';
338
+ }
339
+
340
+ fs.writeFileSync(path.join(__dirname, filename), content);
341
+ console.log(`✓ ${filename} written (${config.methods.length} methods)`);
342
+ }
343
+
344
+ // Create index.js barrel export
345
+ const indexContent = `/**
346
+ * Pulse Parser - Main Entry Point
347
+ *
348
+ * Barrel export for all parser modules.
349
+ * Sub-modules extend Parser.prototype with their methods.
350
+ *
351
+ * @module pulse-js-framework/compiler/parser
352
+ */
353
+
354
+ import { tokenize } from '../lexer.js';
355
+
356
+ // Core must be imported first (defines the Parser class)
357
+ export { NodeType, ASTNode, Parser } from './core.js';
358
+ import { Parser } from './core.js';
359
+
360
+ // Each sub-module extends Parser.prototype with its methods
361
+ import './imports.js';
362
+ import './state.js';
363
+ import './view.js';
364
+ import './expressions.js';
365
+ import './style.js';
366
+ import './blocks.js';
367
+
368
+ // Re-export NodeType for convenience
369
+ export { NodeType } from './core.js';
370
+
371
+ /**
372
+ * Parse a .pulse source string into an AST
373
+ * @param {string} source - Source code
374
+ * @returns {ASTNode} Program AST node
375
+ */
376
+ export function parse(source) {
377
+ const tokens = tokenize(source);
378
+ const parser = new Parser(tokens);
379
+ return parser.parse();
380
+ }
381
+
382
+ export default {
383
+ NodeType: (await import('./core.js')).NodeType,
384
+ ASTNode: (await import('./core.js')).ASTNode,
385
+ Parser,
386
+ parse
387
+ };
388
+ `;
389
+
390
+ fs.writeFileSync(path.join(__dirname, 'index.js'), indexContent);
391
+ console.log('✓ index.js written');
392
+
393
+ console.log('\nExtraction complete!');