baseguard 1.0.4 → 1.0.6

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 (84) hide show
  1. package/dist/ai/gemini-analyzer.d.ts.map +1 -1
  2. package/dist/ai/gemini-analyzer.js +1 -1
  3. package/dist/ai/gemini-analyzer.js.map +1 -1
  4. package/dist/ai/gemini-code-fixer.d.ts.map +1 -1
  5. package/dist/ai/gemini-code-fixer.js +2 -7
  6. package/dist/ai/gemini-code-fixer.js.map +1 -1
  7. package/dist/ai/jules-implementer.d.ts +8 -0
  8. package/dist/ai/jules-implementer.d.ts.map +1 -1
  9. package/dist/ai/jules-implementer.js +115 -17
  10. package/dist/ai/jules-implementer.js.map +1 -1
  11. package/dist/commands/fix.d.ts.map +1 -1
  12. package/dist/commands/fix.js +5 -1
  13. package/dist/commands/fix.js.map +1 -1
  14. package/dist/core/baseline-checker.d.ts.map +1 -1
  15. package/dist/core/baseline-checker.js +6 -4
  16. package/dist/core/baseline-checker.js.map +1 -1
  17. package/package.json +1 -1
  18. package/src/ai/__tests__/gemini-analyzer.test.ts +0 -181
  19. package/src/ai/agentkit-orchestrator.ts +0 -534
  20. package/src/ai/fix-manager.ts +0 -362
  21. package/src/ai/gemini-analyzer.ts +0 -665
  22. package/src/ai/gemini-code-fixer.ts +0 -539
  23. package/src/ai/index.ts +0 -4
  24. package/src/ai/jules-implementer.ts +0 -504
  25. package/src/ai/unified-code-fixer.ts +0 -347
  26. package/src/commands/automation.ts +0 -344
  27. package/src/commands/check.ts +0 -298
  28. package/src/commands/config.ts +0 -584
  29. package/src/commands/fix.ts +0 -264
  30. package/src/commands/index.ts +0 -7
  31. package/src/commands/init.ts +0 -156
  32. package/src/commands/status.ts +0 -307
  33. package/src/core/api-key-manager.ts +0 -298
  34. package/src/core/baseguard.ts +0 -757
  35. package/src/core/baseline-checker.ts +0 -564
  36. package/src/core/cache-manager.ts +0 -272
  37. package/src/core/configuration-recovery.ts +0 -672
  38. package/src/core/configuration.ts +0 -596
  39. package/src/core/debug-logger.ts +0 -590
  40. package/src/core/directory-filter.ts +0 -421
  41. package/src/core/error-handler.ts +0 -518
  42. package/src/core/file-processor.ts +0 -338
  43. package/src/core/gitignore-manager.ts +0 -169
  44. package/src/core/graceful-degradation-manager.ts +0 -596
  45. package/src/core/index.ts +0 -17
  46. package/src/core/lazy-loader.ts +0 -317
  47. package/src/core/logger.ts +0 -0
  48. package/src/core/memory-manager.ts +0 -290
  49. package/src/core/parser-worker.ts +0 -33
  50. package/src/core/startup-optimizer.ts +0 -246
  51. package/src/core/system-error-handler.ts +0 -755
  52. package/src/git/automation-engine.ts +0 -361
  53. package/src/git/github-manager.ts +0 -190
  54. package/src/git/hook-manager.ts +0 -210
  55. package/src/git/index.ts +0 -4
  56. package/src/index.ts +0 -8
  57. package/src/parsers/feature-validator.ts +0 -559
  58. package/src/parsers/index.ts +0 -8
  59. package/src/parsers/parser-manager.ts +0 -418
  60. package/src/parsers/parser.ts +0 -26
  61. package/src/parsers/react-parser-optimized.ts +0 -161
  62. package/src/parsers/react-parser.ts +0 -359
  63. package/src/parsers/svelte-parser.ts +0 -510
  64. package/src/parsers/vanilla-parser.ts +0 -685
  65. package/src/parsers/vue-parser.ts +0 -476
  66. package/src/types/index.ts +0 -96
  67. package/src/ui/components.ts +0 -567
  68. package/src/ui/help.ts +0 -193
  69. package/src/ui/index.ts +0 -4
  70. package/src/ui/prompts.ts +0 -681
  71. package/src/ui/terminal-header.ts +0 -59
  72. package/tests/e2e/baseguard.e2e.test.ts +0 -516
  73. package/tests/e2e/cross-platform.e2e.test.ts +0 -420
  74. package/tests/e2e/git-integration.e2e.test.ts +0 -487
  75. package/tests/fixtures/react-project/package.json +0 -14
  76. package/tests/fixtures/react-project/src/App.css +0 -76
  77. package/tests/fixtures/react-project/src/App.tsx +0 -77
  78. package/tests/fixtures/svelte-project/package.json +0 -11
  79. package/tests/fixtures/svelte-project/src/App.svelte +0 -369
  80. package/tests/fixtures/vanilla-project/index.html +0 -76
  81. package/tests/fixtures/vanilla-project/script.js +0 -331
  82. package/tests/fixtures/vanilla-project/styles.css +0 -359
  83. package/tests/fixtures/vue-project/package.json +0 -12
  84. package/tests/fixtures/vue-project/src/App.vue +0 -216
@@ -1,685 +0,0 @@
1
- import { Parser } from './parser.js';
2
- import type { DetectedFeature } from '../types/index.js';
3
- import { parse as parseBabel } from '@babel/parser';
4
- import traverse from '@babel/traverse';
5
- import * as t from '@babel/types';
6
- import postcss from 'postcss';
7
-
8
- /**
9
- * Vanilla JavaScript/CSS/HTML parser - extracts ALL web platform features
10
- * Handles .js, .ts, .html, and .css files with comprehensive feature detection
11
- */
12
- export class VanillaParser extends Parser {
13
- private readonly WEB_PLATFORM_APIS = new Set([
14
- // Canvas APIs
15
- 'getContext', 'CanvasRenderingContext2D', 'WebGLRenderingContext', 'WebGL2RenderingContext',
16
- 'OffscreenCanvas', 'ImageBitmap', 'createImageBitmap', 'Path2D', 'ImageData',
17
- 'CanvasGradient', 'CanvasPattern', 'TextMetrics',
18
-
19
- // WebGL specific
20
- 'createShader', 'shaderSource', 'compileShader', 'createProgram', 'attachShader',
21
- 'linkProgram', 'useProgram', 'getAttribLocation', 'getUniformLocation',
22
- 'enableVertexAttribArray', 'vertexAttribPointer', 'uniform1f', 'uniform2f',
23
- 'uniform3f', 'uniform4f', 'uniformMatrix4fv', 'drawArrays', 'drawElements',
24
-
25
- // WebRTC APIs
26
- 'RTCPeerConnection', 'RTCDataChannel', 'RTCSessionDescription', 'RTCIceCandidate',
27
- 'getUserMedia', 'getDisplayMedia', 'MediaStream', 'MediaStreamTrack',
28
- 'RTCRtpSender', 'RTCRtpReceiver', 'RTCStatsReport', 'RTCIceServer',
29
-
30
- // WebAssembly
31
- 'WebAssembly', 'instantiate', 'compile', 'validate', 'Module', 'Instance',
32
- 'Memory', 'Table', 'Global', 'CompileError', 'LinkError', 'RuntimeError',
33
-
34
- // Service Workers & PWA
35
- 'ServiceWorker', 'serviceWorker', 'register', 'unregister', 'update',
36
- 'Cache', 'caches', 'open', 'match', 'matchAll', 'add', 'addAll', 'put', 'delete',
37
- 'PushManager', 'subscribe', 'getSubscription', 'permissionState',
38
- 'Notification', 'showNotification', 'getNotifications', 'requestPermission',
39
- 'BackgroundSync', 'sync', 'getTags', 'BackgroundFetch',
40
-
41
- // DOM APIs
42
- 'querySelector', 'querySelectorAll', 'getElementById', 'getElementsByClassName',
43
- 'getElementsByTagName', 'createElement', 'createTextNode', 'createDocumentFragment',
44
- 'addEventListener', 'removeEventListener', 'dispatchEvent', 'CustomEvent',
45
- 'MutationObserver', 'observe', 'disconnect', 'takeRecords',
46
- 'ResizeObserver', 'IntersectionObserver', 'PerformanceObserver',
47
- 'AbortController', 'AbortSignal', 'abort', 'signal',
48
- 'FormData', 'append', 'set', 'get', 'getAll', 'has', 'entries', 'keys', 'values',
49
- 'URLSearchParams', 'URL', 'createObjectURL', 'revokeObjectURL',
50
-
51
- // Fetch API
52
- 'fetch', 'Request', 'Response', 'Headers', 'clone', 'json', 'text', 'blob',
53
- 'arrayBuffer', 'formData', 'ok', 'status', 'statusText', 'redirected',
54
-
55
- // File APIs
56
- 'Blob', 'File', 'FileReader', 'FileList', 'readAsText', 'readAsDataURL',
57
- 'readAsArrayBuffer', 'readAsBinaryString', 'slice',
58
-
59
- // Web APIs
60
- 'navigator', 'geolocation', 'getCurrentPosition', 'watchPosition', 'clearWatch',
61
- 'permissions', 'query', 'clipboard', 'writeText', 'readText', 'write', 'read',
62
- 'share', 'canShare', 'requestAnimationFrame', 'cancelAnimationFrame',
63
- 'requestIdleCallback', 'cancelIdleCallback', 'setTimeout', 'setInterval',
64
- 'clearTimeout', 'clearInterval', 'queueMicrotask',
65
-
66
- // Storage APIs
67
- 'localStorage', 'sessionStorage', 'getItem', 'setItem', 'removeItem', 'clear',
68
- 'indexedDB', 'open', 'deleteDatabase', 'cmp', 'IDBDatabase', 'IDBTransaction',
69
- 'IDBObjectStore', 'IDBIndex', 'IDBCursor', 'IDBKeyRange',
70
-
71
- // Crypto APIs
72
- 'crypto', 'getRandomValues', 'randomUUID', 'subtle', 'encrypt', 'decrypt',
73
- 'sign', 'verify', 'digest', 'generateKey', 'importKey', 'exportKey',
74
-
75
- // Performance APIs
76
- 'performance', 'now', 'mark', 'measure', 'getEntries', 'getEntriesByName',
77
- 'getEntriesByType', 'clearMarks', 'clearMeasures', 'timing', 'navigation',
78
-
79
- // Audio/Video APIs
80
- 'AudioContext', 'createOscillator', 'createGain', 'createAnalyser',
81
- 'createBiquadFilter', 'createBufferSource', 'createMediaElementSource',
82
- 'createScriptProcessor', 'createDynamicsCompressor', 'createConvolver',
83
- 'MediaRecorder', 'start', 'stop', 'pause', 'resume', 'requestData',
84
- 'MediaSource', 'SourceBuffer', 'appendBuffer', 'remove', 'abort',
85
- 'HTMLMediaElement', 'HTMLAudioElement', 'HTMLVideoElement', 'play', 'pause',
86
- 'load', 'canPlayType', 'addTextTrack', 'captureStream',
87
-
88
- // Modern JavaScript APIs
89
- 'structuredClone', 'reportError', 'WeakRef', 'deref', 'FinalizationRegistry',
90
- 'AggregateError', 'Promise', 'allSettled', 'any', 'race', 'resolve', 'reject',
91
- 'AsyncIterator', 'Symbol', 'iterator', 'asyncIterator', 'BigInt', 'asIntN', 'asUintN',
92
-
93
- // Intl APIs
94
- 'Intl', 'DateTimeFormat', 'format', 'formatToParts', 'resolvedOptions',
95
- 'NumberFormat', 'Collator', 'compare', 'PluralRules', 'select',
96
- 'RelativeTimeFormat', 'ListFormat', 'Locale', 'getCanonicalLocales',
97
- 'DisplayNames', 'of', 'Segmenter', 'segment',
98
-
99
- // Streams API
100
- 'ReadableStream', 'WritableStream', 'TransformStream', 'getReader', 'getWriter',
101
- 'readable', 'writable', 'pipeThrough', 'pipeTo', 'tee',
102
-
103
- // Web Components
104
- 'customElements', 'define', 'get', 'whenDefined', 'upgrade', 'ShadowRoot',
105
- 'attachShadow', 'shadowRoot', 'HTMLTemplateElement', 'content', 'HTMLSlotElement',
106
-
107
- // Pointer Events
108
- 'PointerEvent', 'pointerId', 'pointerType', 'isPrimary', 'setPointerCapture',
109
- 'releasePointerCapture', 'hasPointerCapture',
110
-
111
- // Touch Events
112
- 'TouchEvent', 'touches', 'targetTouches', 'changedTouches', 'Touch',
113
-
114
- // Gamepad API
115
- 'navigator.getGamepads', 'Gamepad', 'GamepadButton', 'pressed', 'value',
116
-
117
- // Battery API
118
- 'navigator.getBattery', 'BatteryManager', 'charging', 'chargingTime',
119
- 'dischargingTime', 'level',
120
-
121
- // Device APIs
122
- 'DeviceOrientationEvent', 'alpha', 'beta', 'gamma', 'absolute',
123
- 'DeviceMotionEvent', 'acceleration', 'accelerationIncludingGravity',
124
- 'rotationRate', 'interval'
125
- ]);
126
-
127
- private readonly CSS_PROPERTIES = new Set([
128
- // Container Queries
129
- 'container-type', 'container-name', 'container', 'container-query-length',
130
-
131
- // Grid Layout
132
- 'display', 'grid', 'inline-grid', 'grid-template-columns', 'grid-template-rows',
133
- 'grid-template-areas', 'grid-template', 'grid-column-start', 'grid-column-end',
134
- 'grid-row-start', 'grid-row-end', 'grid-column', 'grid-row', 'grid-area',
135
- 'grid-auto-columns', 'grid-auto-rows', 'grid-auto-flow', 'gap', 'grid-gap',
136
- 'column-gap', 'row-gap', 'grid-column-gap', 'grid-row-gap',
137
-
138
- // Flexbox
139
- 'flex', 'inline-flex', 'flex-direction', 'flex-wrap', 'flex-flow',
140
- 'justify-content', 'align-items', 'align-content', 'align-self',
141
- 'flex-grow', 'flex-shrink', 'flex-basis', 'order',
142
-
143
- // Modern Layout
144
- 'aspect-ratio', 'object-fit', 'object-position', 'place-items', 'place-content',
145
- 'place-self', 'justify-items', 'justify-self', 'align-tracks', 'justify-tracks',
146
-
147
- // Visual Effects
148
- 'backdrop-filter', 'filter', 'mix-blend-mode', 'isolation', 'clip-path',
149
- 'mask', 'mask-image', 'mask-mode', 'mask-repeat', 'mask-position',
150
- 'mask-clip', 'mask-origin', 'mask-size', 'mask-composite',
151
-
152
- // Color & Appearance
153
- 'color-scheme', 'accent-color', 'color-mix', 'color-contrast',
154
- 'forced-color-adjust', 'print-color-adjust',
155
-
156
- // Scrolling
157
- 'scroll-behavior', 'scroll-snap-type', 'scroll-snap-align', 'scroll-snap-stop',
158
- 'scroll-margin', 'scroll-margin-top', 'scroll-margin-right', 'scroll-margin-bottom',
159
- 'scroll-margin-left', 'scroll-padding', 'scroll-padding-top', 'scroll-padding-right',
160
- 'scroll-padding-bottom', 'scroll-padding-left', 'overscroll-behavior',
161
- 'overscroll-behavior-x', 'overscroll-behavior-y', 'scroll-timeline',
162
- 'scroll-timeline-name', 'scroll-timeline-axis',
163
-
164
- // Interaction
165
- 'touch-action', 'user-select', 'pointer-events', 'cursor',
166
-
167
- // CSS Custom Properties & Functions
168
- '--', 'var(', 'calc(', 'clamp(', 'min(', 'max(', 'minmax(',
169
-
170
- // Transforms & Animations
171
- 'transform', 'transform-origin', 'transform-style', 'perspective',
172
- 'perspective-origin', 'backface-visibility', 'animation', 'animation-name',
173
- 'animation-duration', 'animation-timing-function', 'animation-delay',
174
- 'animation-iteration-count', 'animation-direction', 'animation-fill-mode',
175
- 'animation-play-state', 'animation-timeline', 'transition', 'transition-property',
176
- 'transition-duration', 'transition-timing-function', 'transition-delay',
177
- 'will-change', 'contain', 'content-visibility',
178
-
179
- // Typography
180
- 'font-display', 'font-variation-settings', 'font-optical-sizing',
181
- 'font-palette', 'text-decoration-thickness', 'text-decoration-skip-ink',
182
- 'text-underline-offset', 'text-underline-position', 'hanging-punctuation',
183
- 'hyphens', 'line-break', 'word-break', 'overflow-wrap', 'text-overflow',
184
- 'white-space', 'tab-size', 'writing-mode', 'text-orientation',
185
-
186
- // Layout & Positioning
187
- 'position', 'top', 'right', 'bottom', 'left', 'z-index', 'inset',
188
- 'inset-block', 'inset-inline', 'width', 'height', 'min-width', 'min-height',
189
- 'max-width', 'max-height', 'margin', 'margin-top', 'margin-right',
190
- 'margin-bottom', 'margin-left', 'padding', 'padding-top', 'padding-right',
191
- 'padding-bottom', 'padding-left', 'border', 'border-radius', 'outline',
192
- 'box-sizing', 'overflow', 'overflow-x', 'overflow-y', 'resize',
193
-
194
- // Logical Properties
195
- 'margin-block', 'margin-inline', 'padding-block', 'padding-inline',
196
- 'border-block', 'border-inline', 'inset-block-start', 'inset-block-end',
197
- 'inset-inline-start', 'inset-inline-end'
198
- ]);
199
-
200
- private readonly CSS_SELECTORS = new Set([
201
- ':has(', ':is(', ':where(', ':not(', ':focus-visible', ':focus-within',
202
- ':target-within', ':any-link', ':local-link', ':scope', ':current',
203
- ':past', ':future', ':playing', ':paused', ':seeking', ':buffering',
204
- ':stalled', ':muted', ':volume-locked', ':fullscreen', ':picture-in-picture',
205
- ':user-invalid', ':user-valid', ':blank', ':placeholder-shown',
206
- '::backdrop', '::placeholder', '::marker', '::selection', '::first-letter',
207
- '::first-line', '::before', '::after', '::file-selector-button'
208
- ]);
209
-
210
- private readonly HTML_ELEMENTS = new Set([
211
- // Modern semantic elements
212
- 'dialog', 'details', 'summary', 'main', 'article', 'section', 'nav', 'aside',
213
- 'header', 'footer', 'figure', 'figcaption', 'time', 'mark', 'progress', 'meter',
214
-
215
- // Media elements
216
- 'canvas', 'video', 'audio', 'source', 'track', 'embed', 'object', 'picture',
217
-
218
- // Form elements
219
- 'datalist', 'output', 'keygen', 'fieldset', 'legend',
220
-
221
- // Interactive elements
222
- 'slot', 'template'
223
- ]);
224
-
225
- private readonly HTML_ATTRIBUTES = new Set([
226
- 'loading', 'decoding', 'fetchpriority', 'enterkeyhint', 'inputmode',
227
- 'autocomplete', 'autofocus', 'capture', 'crossorigin', 'dirname',
228
- 'download', 'draggable', 'enctype', 'formaction', 'formenctype',
229
- 'formmethod', 'formnovalidate', 'formtarget', 'hidden', 'integrity',
230
- 'is', 'itemid', 'itemprop', 'itemref', 'itemscope', 'itemtype',
231
- 'kind', 'label', 'lang', 'list', 'loop', 'max', 'maxlength', 'media',
232
- 'method', 'min', 'minlength', 'multiple', 'muted', 'novalidate',
233
- 'open', 'optimum', 'pattern', 'placeholder', 'playsinline', 'poster',
234
- 'preload', 'readonly', 'referrerpolicy', 'rel', 'required', 'reversed',
235
- 'rows', 'rowspan', 'sandbox', 'scope', 'selected', 'shape', 'size',
236
- 'sizes', 'span', 'spellcheck', 'srcdoc', 'srclang', 'srcset', 'start',
237
- 'step', 'target', 'translate', 'type', 'usemap', 'value', 'wrap'
238
- ]);
239
-
240
- canParse(filePath: string): boolean {
241
- return /\.(js|ts|html|css)$/.test(filePath);
242
- }
243
-
244
- async parseFeatures(content: string, filePath: string): Promise<DetectedFeature[]> {
245
- const extension = this.getFileExtension(filePath);
246
-
247
- switch (extension) {
248
- case '.js':
249
- case '.ts':
250
- return this.parseJavaScript(content, filePath, extension === '.ts');
251
- case '.css':
252
- return this.parseCSS(content, filePath);
253
- case '.html':
254
- return await this.parseHTML(content, filePath);
255
- default:
256
- return [];
257
- }
258
- }
259
-
260
- private async parseJavaScript(content: string, filePath: string, isTypeScript: boolean): Promise<DetectedFeature[]> {
261
- const features: DetectedFeature[] = [];
262
-
263
- try {
264
- const ast = parseBabel(content, {
265
- sourceType: 'module',
266
- plugins: [
267
- 'typescript' as any,
268
- 'decorators-legacy' as any,
269
- 'classProperties' as any,
270
- 'objectRestSpread' as any,
271
- 'asyncGenerators' as any,
272
- 'functionBind' as any,
273
- 'exportDefaultFrom' as any,
274
- 'exportNamespaceFrom' as any,
275
- 'dynamicImport' as any,
276
- 'nullishCoalescingOperator',
277
- 'optionalChaining',
278
- 'topLevelAwait',
279
- 'privateIn',
280
- 'importMeta'
281
- ].filter(plugin => isTypeScript || plugin !== 'typescript')
282
- });
283
-
284
- traverse(ast, {
285
- // Extract Web API member expressions
286
- MemberExpression: (path: any) => {
287
- const feature = this.extractWebAPIFeature(path.node, content);
288
- if (feature) {
289
- features.push({ ...feature, file: filePath });
290
- }
291
- },
292
-
293
- // Extract Web API function calls
294
- CallExpression: (path: any) => {
295
- const feature = this.extractWebAPICall(path.node, content);
296
- if (feature) {
297
- features.push({ ...feature, file: filePath });
298
- }
299
- },
300
-
301
- // Modern JavaScript syntax features
302
- OptionalMemberExpression: (path: any) => {
303
- features.push({
304
- feature: 'optional-chaining',
305
- type: 'js',
306
- context: this.getContext(content, path.node.loc?.start.line || 0),
307
- line: path.node.loc?.start.line || 0,
308
- column: path.node.loc?.start.column || 0,
309
- file: filePath
310
- });
311
- },
312
-
313
- OptionalCallExpression: (path: any) => {
314
- features.push({
315
- feature: 'optional-chaining',
316
- type: 'js',
317
- context: this.getContext(content, path.node.loc?.start.line || 0),
318
- line: path.node.loc?.start.line || 0,
319
- column: path.node.loc?.start.column || 0,
320
- file: filePath
321
- });
322
- },
323
-
324
- // Nullish coalescing
325
- LogicalExpression: (path: any) => {
326
- if (path.node.operator === '??') {
327
- features.push({
328
- feature: 'nullish-coalescing',
329
- type: 'js',
330
- context: this.getContext(content, path.node.loc?.start.line || 0),
331
- line: path.node.loc?.start.line || 0,
332
- column: path.node.loc?.start.column || 0,
333
- file: filePath
334
- });
335
- }
336
- },
337
-
338
- // Private class fields
339
- ClassPrivateProperty: (path: any) => {
340
- features.push({
341
- feature: 'private-fields',
342
- type: 'js',
343
- context: this.getContext(content, path.node.loc?.start.line || 0),
344
- line: path.node.loc?.start.line || 0,
345
- column: path.node.loc?.start.column || 0,
346
- file: filePath
347
- });
348
- },
349
-
350
- ClassPrivateMethod: (path: any) => {
351
- features.push({
352
- feature: 'private-methods',
353
- type: 'js',
354
- context: this.getContext(content, path.node.loc?.start.line || 0),
355
- line: path.node.loc?.start.line || 0,
356
- column: path.node.loc?.start.column || 0,
357
- file: filePath
358
- });
359
- },
360
-
361
- // Top-level await
362
- AwaitExpression: (path: any) => {
363
- if (this.isTopLevelAwait(path)) {
364
- features.push({
365
- feature: 'top-level-await',
366
- type: 'js',
367
- context: this.getContext(content, path.node.loc?.start.line || 0),
368
- line: path.node.loc?.start.line || 0,
369
- column: path.node.loc?.start.column || 0,
370
- file: filePath
371
- });
372
- }
373
- },
374
-
375
- // Dynamic imports
376
- Import: (path: any) => {
377
- features.push({
378
- feature: 'dynamic-import',
379
- type: 'js',
380
- context: this.getContext(content, path.node.loc?.start.line || 0),
381
- line: path.node.loc?.start.line || 0,
382
- column: path.node.loc?.start.column || 0,
383
- file: filePath
384
- });
385
- },
386
-
387
- // BigInt literals
388
- BigIntLiteral: (path: any) => {
389
- features.push({
390
- feature: 'bigint',
391
- type: 'js',
392
- context: this.getContext(content, path.node.loc?.start.line || 0),
393
- line: path.node.loc?.start.line || 0,
394
- column: path.node.loc?.start.column || 0,
395
- file: filePath
396
- });
397
- },
398
-
399
- // Numeric separators
400
- NumericLiteral: (path: any) => {
401
- if ((path.node.extra as any)?.raw?.includes('_')) {
402
- features.push({
403
- feature: 'numeric-separators',
404
- type: 'js',
405
- context: this.getContext(content, path.node.loc?.start.line || 0),
406
- line: path.node.loc?.start.line || 0,
407
- column: path.node.loc?.start.column || 0,
408
- file: filePath
409
- });
410
- }
411
- }
412
- });
413
-
414
- } catch (error) {
415
- console.warn(`Warning: Could not parse JavaScript file ${filePath}: ${error instanceof Error ? error.message : 'Unknown error'}`);
416
- }
417
-
418
- return features;
419
- }
420
-
421
- private async parseCSS(content: string, filePath: string): Promise<DetectedFeature[]> {
422
- const features: DetectedFeature[] = [];
423
-
424
- try {
425
- const root = postcss.parse(content);
426
-
427
- // Extract CSS properties
428
- root.walkDecls((decl: any) => {
429
- if (this.CSS_PROPERTIES.has(decl.prop) || decl.prop.startsWith('--')) {
430
- features.push({
431
- feature: decl.prop,
432
- type: 'css',
433
- context: `${decl.prop}: ${decl.value}`,
434
- line: decl.source?.start?.line || 0,
435
- column: decl.source?.start?.column || 0,
436
- file: filePath
437
- });
438
- }
439
-
440
- // Check for CSS functions in values
441
- if (decl.value.includes('var(') || decl.value.includes('calc(') ||
442
- decl.value.includes('clamp(') || decl.value.includes('min(') ||
443
- decl.value.includes('max(')) {
444
- const func = this.extractCSSFunction(decl.value);
445
- if (func) {
446
- features.push({
447
- feature: func,
448
- type: 'css',
449
- context: `${decl.prop}: ${decl.value}`,
450
- line: decl.source?.start?.line || 0,
451
- column: decl.source?.start?.column || 0,
452
- file: filePath
453
- });
454
- }
455
- }
456
- });
457
-
458
- // Extract CSS selectors
459
- root.walkRules((rule: any) => {
460
- this.CSS_SELECTORS.forEach(selector => {
461
- if (rule.selector.includes(selector)) {
462
- features.push({
463
- feature: selector.replace('(', '()'),
464
- type: 'css',
465
- context: rule.selector,
466
- line: rule.source?.start?.line || 0,
467
- column: rule.source?.start?.column || 0,
468
- file: filePath
469
- });
470
- }
471
- });
472
- });
473
-
474
- // Extract at-rules
475
- root.walkAtRules((atRule: any) => {
476
- const atRuleName = `@${atRule.name}`;
477
- features.push({
478
- feature: atRuleName,
479
- type: 'css',
480
- context: `${atRuleName} ${atRule.params}`,
481
- line: atRule.source?.start?.line || 0,
482
- column: atRule.source?.start?.column || 0,
483
- file: filePath
484
- });
485
- });
486
-
487
- } catch (error) {
488
- console.warn(`Warning: Could not parse CSS file ${filePath}: ${error instanceof Error ? error.message : 'Unknown error'}`);
489
- }
490
-
491
- return features;
492
- }
493
-
494
- private async parseHTML(content: string, filePath: string): Promise<DetectedFeature[]> {
495
- const features: DetectedFeature[] = [];
496
- const lines = content.split('\n');
497
-
498
- // Parse HTML elements and attributes
499
- const elementRegex = /<(\w+)([^>]*)>/g;
500
-
501
- lines.forEach((line, index) => {
502
- let match: RegExpExecArray | null;
503
- while ((match = elementRegex.exec(line)) !== null) {
504
- const tagName = match[1];
505
- const attributes = match[2];
506
-
507
- // Check for modern HTML elements
508
- if (tagName && this.HTML_ELEMENTS.has(tagName)) {
509
- features.push({
510
- feature: tagName!,
511
- type: 'html',
512
- context: line.trim(),
513
- line: index + 1,
514
- column: match.index,
515
- file: filePath
516
- });
517
- }
518
-
519
- // Check for modern HTML attributes
520
- if (attributes) {
521
- const modernAttrs = this.extractHTMLAttributes(attributes);
522
- modernAttrs.forEach(attr => {
523
- features.push({
524
- feature: attr,
525
- type: 'html',
526
- context: line.trim(),
527
- line: index + 1,
528
- column: match?.index || 0,
529
- file: filePath
530
- });
531
- });
532
- }
533
- }
534
- });
535
-
536
- // Extract inline CSS and JavaScript
537
- const styleRegex = /<style[^>]*>([\s\S]*?)<\/style>/gi;
538
- const scriptRegex = /<script[^>]*>([\s\S]*?)<\/script>/gi;
539
-
540
- // Parse inline styles
541
- let styleMatch;
542
- while ((styleMatch = styleRegex.exec(content)) !== null) {
543
- const cssContent = styleMatch[1];
544
- const cssFeatures = cssContent ? await this.parseInlineCSS(cssContent, filePath) : [];
545
- features.push(...cssFeatures);
546
- }
547
-
548
- // Parse inline scripts
549
- let scriptMatch;
550
- while ((scriptMatch = scriptRegex.exec(content)) !== null) {
551
- const jsContent = scriptMatch[1];
552
- const jsFeatures = jsContent ? await this.parseInlineJS(jsContent, filePath) : [];
553
- features.push(...jsFeatures);
554
- }
555
-
556
- return features;
557
- }
558
-
559
- private async parseInlineCSS(cssContent: string, filePath: string): Promise<DetectedFeature[]> {
560
- try {
561
- return await this.parseCSS(cssContent, filePath);
562
- } catch (error) {
563
- console.warn(`Warning: Could not parse inline CSS in ${filePath}: ${error instanceof Error ? error.message : 'Unknown error'}`);
564
- return [];
565
- }
566
- }
567
-
568
- private async parseInlineJS(jsContent: string, filePath: string): Promise<DetectedFeature[]> {
569
- try {
570
- return await this.parseJavaScript(jsContent, filePath, false);
571
- } catch (error) {
572
- console.warn(`Warning: Could not parse inline JavaScript in ${filePath}: ${error instanceof Error ? error.message : 'Unknown error'}`);
573
- return [];
574
- }
575
- }
576
-
577
- private extractWebAPIFeature(node: t.MemberExpression, content: string): DetectedFeature | null {
578
- const apiName = this.getMemberExpressionName(node);
579
-
580
- if (this.WEB_PLATFORM_APIS.has(apiName)) {
581
- return {
582
- feature: apiName,
583
- type: 'js',
584
- context: this.getContext(content, node.loc?.start.line || 0),
585
- line: node.loc?.start.line || 0,
586
- column: node.loc?.start.column || 0
587
- };
588
- }
589
-
590
- return null;
591
- }
592
-
593
- private extractWebAPICall(node: t.CallExpression, content: string): DetectedFeature | null {
594
- let apiName = '';
595
-
596
- if (t.isIdentifier(node.callee)) {
597
- apiName = node.callee.name;
598
- } else if (t.isMemberExpression(node.callee)) {
599
- apiName = this.getMemberExpressionName(node.callee);
600
- }
601
-
602
- if (this.WEB_PLATFORM_APIS.has(apiName)) {
603
- return {
604
- feature: apiName,
605
- type: 'js',
606
- context: this.getContext(content, node.loc?.start.line || 0),
607
- line: node.loc?.start.line || 0,
608
- column: node.loc?.start.column || 0
609
- };
610
- }
611
-
612
- return null;
613
- }
614
-
615
- private extractCSSFunction(value: string): string | null {
616
- const functions = ['var(', 'calc(', 'clamp(', 'min(', 'max(', 'minmax('];
617
- for (const func of functions) {
618
- if (value.includes(func)) {
619
- return func.replace('(', '()');
620
- }
621
- }
622
- return null;
623
- }
624
-
625
- private extractHTMLAttributes(attributes: string): string[] {
626
- const modernAttrs: string[] = [];
627
-
628
- this.HTML_ATTRIBUTES.forEach(attr => {
629
- const attrRegex = new RegExp(`\\b${attr}\\b`, 'i');
630
- if (attrRegex.test(attributes)) {
631
- modernAttrs.push(attr);
632
- }
633
- });
634
-
635
- return modernAttrs;
636
- }
637
-
638
- private getMemberExpressionName(node: t.MemberExpression): string {
639
- const parts: string[] = [];
640
-
641
- const traverse = (n: t.Expression): void => {
642
- if (t.isIdentifier(n)) {
643
- parts.unshift(n.name);
644
- } else if (t.isMemberExpression(n)) {
645
- if (t.isIdentifier(n.property)) {
646
- parts.unshift(n.property.name);
647
- }
648
- traverse(n.object);
649
- }
650
- };
651
-
652
- traverse(node);
653
- return parts.join('.');
654
- }
655
-
656
- private isTopLevelAwait(path: any): boolean {
657
- let parent = path.parent;
658
- while (parent) {
659
- if (t.isFunction(parent) || t.isArrowFunctionExpression(parent)) {
660
- return false;
661
- }
662
- parent = path.parentPath?.parent;
663
- }
664
- return true;
665
- }
666
-
667
- private getFileExtension(filePath: string): string {
668
- const match = filePath.match(/\.[^.]+$/);
669
- return match ? match[0] : '';
670
- }
671
-
672
- private getContext(content: string, line: number): string {
673
- const lines = content.split('\n');
674
- const targetLine = lines[line - 1] || '';
675
- return targetLine.trim();
676
- }
677
-
678
- getSupportedExtensions(): string[] {
679
- return ['.js', '.ts', '.html', '.css'];
680
- }
681
-
682
- getName(): string {
683
- return 'VanillaParser';
684
- }
685
- }