baseguard 1.0.5 → 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 (80) 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/package.json +1 -1
  12. package/src/ai/__tests__/gemini-analyzer.test.ts +0 -181
  13. package/src/ai/agentkit-orchestrator.ts +0 -534
  14. package/src/ai/fix-manager.ts +0 -362
  15. package/src/ai/gemini-analyzer.ts +0 -665
  16. package/src/ai/gemini-code-fixer.ts +0 -539
  17. package/src/ai/index.ts +0 -4
  18. package/src/ai/jules-implementer.ts +0 -504
  19. package/src/ai/unified-code-fixer.ts +0 -347
  20. package/src/commands/automation.ts +0 -344
  21. package/src/commands/check.ts +0 -298
  22. package/src/commands/config.ts +0 -584
  23. package/src/commands/fix.ts +0 -269
  24. package/src/commands/index.ts +0 -7
  25. package/src/commands/init.ts +0 -156
  26. package/src/commands/status.ts +0 -307
  27. package/src/core/api-key-manager.ts +0 -298
  28. package/src/core/baseguard.ts +0 -757
  29. package/src/core/baseline-checker.ts +0 -566
  30. package/src/core/cache-manager.ts +0 -272
  31. package/src/core/configuration-recovery.ts +0 -672
  32. package/src/core/configuration.ts +0 -596
  33. package/src/core/debug-logger.ts +0 -590
  34. package/src/core/directory-filter.ts +0 -421
  35. package/src/core/error-handler.ts +0 -518
  36. package/src/core/file-processor.ts +0 -338
  37. package/src/core/gitignore-manager.ts +0 -169
  38. package/src/core/graceful-degradation-manager.ts +0 -596
  39. package/src/core/index.ts +0 -17
  40. package/src/core/lazy-loader.ts +0 -317
  41. package/src/core/logger.ts +0 -0
  42. package/src/core/memory-manager.ts +0 -290
  43. package/src/core/parser-worker.ts +0 -33
  44. package/src/core/startup-optimizer.ts +0 -246
  45. package/src/core/system-error-handler.ts +0 -755
  46. package/src/git/automation-engine.ts +0 -361
  47. package/src/git/github-manager.ts +0 -190
  48. package/src/git/hook-manager.ts +0 -210
  49. package/src/git/index.ts +0 -4
  50. package/src/index.ts +0 -8
  51. package/src/parsers/feature-validator.ts +0 -559
  52. package/src/parsers/index.ts +0 -8
  53. package/src/parsers/parser-manager.ts +0 -418
  54. package/src/parsers/parser.ts +0 -26
  55. package/src/parsers/react-parser-optimized.ts +0 -161
  56. package/src/parsers/react-parser.ts +0 -359
  57. package/src/parsers/svelte-parser.ts +0 -510
  58. package/src/parsers/vanilla-parser.ts +0 -685
  59. package/src/parsers/vue-parser.ts +0 -476
  60. package/src/types/index.ts +0 -96
  61. package/src/ui/components.ts +0 -567
  62. package/src/ui/help.ts +0 -193
  63. package/src/ui/index.ts +0 -4
  64. package/src/ui/prompts.ts +0 -681
  65. package/src/ui/terminal-header.ts +0 -59
  66. package/tests/e2e/baseguard.e2e.test.ts +0 -516
  67. package/tests/e2e/cross-platform.e2e.test.ts +0 -420
  68. package/tests/e2e/git-integration.e2e.test.ts +0 -487
  69. package/tests/fixtures/react-project/package.json +0 -14
  70. package/tests/fixtures/react-project/src/App.css +0 -76
  71. package/tests/fixtures/react-project/src/App.tsx +0 -77
  72. package/tests/fixtures/svelte-project/package.json +0 -11
  73. package/tests/fixtures/svelte-project/src/App.svelte +0 -369
  74. package/tests/fixtures/vanilla-project/index.html +0 -76
  75. package/tests/fixtures/vanilla-project/script.js +0 -331
  76. package/tests/fixtures/vanilla-project/styles.css +0 -359
  77. package/tests/fixtures/vue-project/package.json +0 -12
  78. package/tests/fixtures/vue-project/src/App.vue +0 -216
  79. package/tmp-smoke/.baseguard/backups/config-2026-02-19T12-04-11-067Z-auto.json +0 -30
  80. package/tmp-smoke/src/bad.css +0 -3
@@ -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
- }