baseguard 1.0.0

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