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,506 @@
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
+ * Parser for Svelte files (.svelte) - extracts ALL web platform features
7
+ * while ignoring Svelte-specific syntax
8
+ */
9
+ export class SvelteParser extends Parser {
10
+ private readonly SVELTE_SPECIFIC_APIS = new Set([
11
+ // Svelte stores
12
+ 'writable', 'readable', 'derived', 'get', 'subscribe', 'set', 'update',
13
+
14
+ // Svelte lifecycle
15
+ 'onMount', 'onDestroy', 'beforeUpdate', 'afterUpdate', 'tick',
16
+ 'setContext', 'getContext', 'hasContext', 'getAllContexts',
17
+
18
+ // Svelte actions and transitions
19
+ 'createEventDispatcher', 'dispatch',
20
+
21
+ // SvelteKit specific
22
+ 'page', 'navigating', 'updated', 'goto', 'prefetch', 'prefetchRoutes',
23
+ 'invalidate', 'invalidateAll', 'preloadData', 'preloadCode',
24
+
25
+ // Svelte compiler directives (handled separately)
26
+ 'bind', 'on', 'use', 'transition', 'in', 'out', 'animate'
27
+ ]);
28
+
29
+ private readonly SVELTE_DIRECTIVES = new Set([
30
+ 'bind:', 'on:', 'use:', 'transition:', 'in:', 'out:', 'animate:', 'class:', 'style:'
31
+ ]);
32
+
33
+ private readonly WEB_PLATFORM_APIS = new Set([
34
+ // Canvas APIs
35
+ 'getContext', 'CanvasRenderingContext2D', 'WebGLRenderingContext', 'WebGL2RenderingContext',
36
+ 'OffscreenCanvas', 'ImageBitmap', 'createImageBitmap', 'Path2D',
37
+
38
+ // WebRTC APIs
39
+ 'RTCPeerConnection', 'RTCDataChannel', 'RTCSessionDescription', 'RTCIceCandidate',
40
+ 'getUserMedia', 'getDisplayMedia', 'MediaStream', 'MediaStreamTrack',
41
+
42
+ // WebAssembly
43
+ 'WebAssembly', 'instantiate', 'compile', 'validate',
44
+
45
+ // Service Workers & PWA
46
+ 'ServiceWorker', 'serviceWorker', 'register', 'Cache', 'caches',
47
+ 'PushManager', 'Notification', 'showNotification',
48
+
49
+ // DOM APIs
50
+ 'querySelector', 'querySelectorAll', 'getElementById', 'getElementsByClassName',
51
+ 'addEventListener', 'removeEventListener', 'dispatchEvent', 'CustomEvent',
52
+ 'MutationObserver', 'ResizeObserver', 'IntersectionObserver', 'PerformanceObserver',
53
+ 'AbortController', 'AbortSignal', 'FormData', 'URLSearchParams', 'URL',
54
+ 'fetch', 'Request', 'Response', 'Headers', 'Blob', 'File', 'FileReader',
55
+
56
+ // Web APIs
57
+ 'navigator', 'geolocation', 'permissions', 'clipboard', 'share',
58
+ 'requestAnimationFrame', 'cancelAnimationFrame', 'requestIdleCallback',
59
+ 'setTimeout', 'setInterval', 'clearTimeout', 'clearInterval',
60
+ 'localStorage', 'sessionStorage', 'indexedDB', 'crypto', 'performance',
61
+
62
+ // Audio/Video APIs
63
+ 'AudioContext', 'MediaRecorder', 'MediaSource', 'SourceBuffer',
64
+ 'HTMLMediaElement', 'HTMLAudioElement', 'HTMLVideoElement',
65
+
66
+ // Modern JavaScript APIs
67
+ 'structuredClone', 'queueMicrotask', 'reportError',
68
+ 'WeakRef', 'FinalizationRegistry', 'AggregateError',
69
+
70
+ // Intl APIs
71
+ 'Intl', 'DateTimeFormat', 'NumberFormat', 'Collator', 'PluralRules',
72
+ 'RelativeTimeFormat', 'ListFormat', 'Locale'
73
+ ]);
74
+
75
+ canParse(filePath: string): boolean {
76
+ return filePath.endsWith('.svelte');
77
+ }
78
+
79
+ getSupportedExtensions(): string[] {
80
+ return ['.svelte'];
81
+ }
82
+
83
+ getName(): string {
84
+ return 'SvelteParser';
85
+ }
86
+
87
+ async parseFeatures(content: string, filePath: string): Promise<DetectedFeature[]> {
88
+ const features: DetectedFeature[] = [];
89
+
90
+ try {
91
+ // Lazy load Svelte compiler
92
+ const svelteCompiler = await LazyLoader.getSvelteCompiler();
93
+ const ast = svelteCompiler.parse(content, { filename: filePath });
94
+
95
+ // Parse script sections for JavaScript features
96
+ if (ast.instance) {
97
+ const scriptFeatures = await this.parseScriptSection(
98
+ ast.instance,
99
+ content,
100
+ filePath,
101
+ 'instance'
102
+ );
103
+ features.push(...scriptFeatures);
104
+ }
105
+
106
+ if (ast.module) {
107
+ const moduleFeatures = await this.parseScriptSection(
108
+ ast.module,
109
+ content,
110
+ filePath,
111
+ 'module'
112
+ );
113
+ features.push(...moduleFeatures);
114
+ }
115
+
116
+ // Parse CSS from style sections
117
+ if (ast.css) {
118
+ const styleFeatures = await this.parseStyleSection(
119
+ ast.css,
120
+ content,
121
+ filePath
122
+ );
123
+ features.push(...styleFeatures);
124
+ }
125
+
126
+ // Parse HTML template for standard elements
127
+ if (ast.html) {
128
+ const templateFeatures = this.parseTemplateSection(
129
+ ast.html,
130
+ content,
131
+ filePath
132
+ );
133
+ features.push(...templateFeatures);
134
+ }
135
+
136
+ } catch (error) {
137
+ console.warn(`Warning: Could not parse Svelte file ${filePath}: ${error instanceof Error ? error.message : 'Unknown error'}`);
138
+ }
139
+
140
+ return features;
141
+ }
142
+
143
+ private async parseScriptSection(
144
+ scriptNode: any,
145
+ fullContent: string,
146
+ filePath: string,
147
+ sectionType: 'instance' | 'module'
148
+ ): Promise<DetectedFeature[]> {
149
+ const features: DetectedFeature[] = [];
150
+
151
+ try {
152
+ // Extract the script content from the full file
153
+ const scriptContent = this.extractScriptContent(scriptNode, fullContent);
154
+
155
+ // Determine if TypeScript
156
+ const isTypeScript = scriptNode.attributes?.some((attr: any) =>
157
+ attr.name === 'lang' && (attr.value[0]?.data === 'ts' || attr.value[0]?.data === 'typescript')
158
+ );
159
+
160
+ const ast = parseBabel(scriptContent, {
161
+ sourceType: 'module',
162
+ plugins: [
163
+ 'typescript' as any,
164
+ 'decorators-legacy' as any,
165
+ 'classProperties' as any,
166
+ 'objectRestSpread' as any,
167
+ 'asyncGenerators' as any,
168
+ 'functionBind' as any,
169
+ 'exportDefaultFrom',
170
+ 'exportNamespaceFrom',
171
+ 'dynamicImport',
172
+ 'nullishCoalescingOperator',
173
+ 'optionalChaining',
174
+ 'topLevelAwait'
175
+ ].filter(plugin => isTypeScript || plugin !== 'typescript')
176
+ });
177
+
178
+ traverse(ast, {
179
+ // Extract JavaScript Web APIs
180
+ MemberExpression: (path) => {
181
+ const feature = this.extractWebAPIFeature(path.node, scriptContent, scriptNode.start);
182
+ if (feature) {
183
+ features.push({ ...feature, file: filePath });
184
+ }
185
+ },
186
+
187
+ // Extract function calls to Web APIs
188
+ CallExpression: (path) => {
189
+ const feature = this.extractWebAPICall(path.node, scriptContent, scriptNode.start);
190
+ if (feature) {
191
+ features.push({ ...feature, file: filePath });
192
+ }
193
+ },
194
+
195
+ // Extract modern JavaScript syntax features
196
+ OptionalMemberExpression: (path) => {
197
+ features.push({
198
+ feature: 'optional-chaining',
199
+ type: 'js',
200
+ context: this.getContext(scriptContent, path.node.loc?.start.line || 0),
201
+ line: (path.node.loc?.start.line || 0) + this.getLineOffset(scriptNode.start, fullContent),
202
+ column: path.node.loc?.start.column || 0,
203
+ file: filePath
204
+ });
205
+ },
206
+
207
+ // Nullish coalescing
208
+ LogicalExpression: (path) => {
209
+ if (path.node.operator === '??') {
210
+ features.push({
211
+ feature: 'nullish-coalescing',
212
+ type: 'js',
213
+ context: this.getContext(scriptContent, path.node.loc?.start.line || 0),
214
+ line: (path.node.loc?.start.line || 0) + this.getLineOffset(scriptNode.start, fullContent),
215
+ column: path.node.loc?.start.column || 0,
216
+ file: filePath
217
+ });
218
+ }
219
+ },
220
+
221
+ // Private class fields
222
+ ClassPrivateProperty: (path) => {
223
+ features.push({
224
+ feature: 'private-fields',
225
+ type: 'js',
226
+ context: this.getContext(scriptContent, path.node.loc?.start.line || 0),
227
+ line: (path.node.loc?.start.line || 0) + this.getLineOffset(scriptNode.start, fullContent),
228
+ column: path.node.loc?.start.column || 0,
229
+ file: filePath
230
+ });
231
+ },
232
+
233
+ // Top-level await
234
+ AwaitExpression: (path) => {
235
+ if (this.isTopLevelAwait(path)) {
236
+ features.push({
237
+ feature: 'top-level-await',
238
+ type: 'js',
239
+ context: this.getContext(scriptContent, path.node.loc?.start.line || 0),
240
+ line: (path.node.loc?.start.line || 0) + this.getLineOffset(scriptNode.start, fullContent),
241
+ column: path.node.loc?.start.column || 0,
242
+ file: filePath
243
+ });
244
+ }
245
+ }
246
+ });
247
+
248
+ } catch (error) {
249
+ console.warn(`Warning: Could not parse ${sectionType} script in ${filePath}: ${error instanceof Error ? error.message : 'Unknown error'}`);
250
+ }
251
+
252
+ return features;
253
+ }
254
+
255
+ private async parseStyleSection(
256
+ styleNode: any,
257
+ fullContent: string,
258
+ filePath: string
259
+ ): Promise<DetectedFeature[]> {
260
+ const features: DetectedFeature[] = [];
261
+
262
+ try {
263
+ const styleContent = this.extractStyleContent(styleNode, fullContent);
264
+ const lineOffset = this.getLineOffset(styleNode.start, fullContent);
265
+
266
+ const root = postcss.parse(styleContent);
267
+
268
+ root.walkDecls(decl => {
269
+ features.push({
270
+ feature: decl.prop,
271
+ type: 'css',
272
+ context: `${decl.prop}: ${decl.value}`,
273
+ line: (decl.source?.start?.line || 0) + lineOffset,
274
+ column: decl.source?.start?.column || 0,
275
+ file: filePath
276
+ });
277
+ });
278
+
279
+ root.walkRules(rule => {
280
+ // Extract CSS selectors that might be modern features
281
+ if (rule.selector.includes(':has(') ||
282
+ rule.selector.includes(':is(') ||
283
+ rule.selector.includes(':where(') ||
284
+ rule.selector.includes(':focus-visible')) {
285
+ features.push({
286
+ feature: this.extractSelectorFeature(rule.selector),
287
+ type: 'css',
288
+ context: rule.selector,
289
+ line: (rule.source?.start?.line || 0) + lineOffset,
290
+ column: rule.source?.start?.column || 0,
291
+ file: filePath
292
+ });
293
+ }
294
+ });
295
+
296
+ root.walkAtRules(atRule => {
297
+ // Extract at-rules like @supports, @container, etc.
298
+ features.push({
299
+ feature: `@${atRule.name}`,
300
+ type: 'css',
301
+ context: `@${atRule.name} ${atRule.params}`,
302
+ line: (atRule.source?.start?.line || 0) + lineOffset,
303
+ column: atRule.source?.start?.column || 0,
304
+ file: filePath
305
+ });
306
+ });
307
+
308
+ } catch (error) {
309
+ console.warn(`Warning: Could not parse style section in ${filePath}: ${error instanceof Error ? error.message : 'Unknown error'}`);
310
+ }
311
+
312
+ return features;
313
+ }
314
+
315
+ private parseTemplateSection(
316
+ htmlNode: any,
317
+ fullContent: string,
318
+ filePath: string
319
+ ): DetectedFeature[] {
320
+ const features: DetectedFeature[] = [];
321
+
322
+ try {
323
+ // Walk through the HTML AST to find standard HTML elements
324
+ this.walkHtmlNode(htmlNode, (node: any) => {
325
+ if (node.type === 'Element') {
326
+ const tagName = node.name;
327
+
328
+ // Check for modern HTML elements
329
+ if (this.isModernHTMLElement(tagName)) {
330
+ const lineOffset = this.getLineOffset(node.start, fullContent);
331
+ features.push({
332
+ feature: tagName,
333
+ type: 'html',
334
+ context: this.getNodeContext(node, fullContent),
335
+ line: lineOffset,
336
+ column: 0,
337
+ file: filePath
338
+ });
339
+ }
340
+
341
+ // Check for modern HTML attributes (ignore Svelte directives)
342
+ if (node.attributes) {
343
+ node.attributes.forEach((attr: any) => {
344
+ if (attr.type === 'Attribute' && !this.isSvelteDirective(attr.name)) {
345
+ if (this.isModernHTMLAttribute(attr.name, attr.value)) {
346
+ const lineOffset = this.getLineOffset(node.start, fullContent);
347
+ features.push({
348
+ feature: attr.name,
349
+ type: 'html',
350
+ context: this.getNodeContext(node, fullContent),
351
+ line: lineOffset,
352
+ column: 0,
353
+ file: filePath
354
+ });
355
+ }
356
+ }
357
+ });
358
+ }
359
+ }
360
+ });
361
+
362
+ } catch (error) {
363
+ console.warn(`Warning: Could not parse template section in ${filePath}: ${error instanceof Error ? error.message : 'Unknown error'}`);
364
+ }
365
+
366
+ return features;
367
+ }
368
+
369
+ private extractWebAPIFeature(node: t.MemberExpression, content: string, offset: number): DetectedFeature | null {
370
+ const apiName = this.getMemberExpressionName(node);
371
+
372
+ if (!apiName || this.SVELTE_SPECIFIC_APIS.has(apiName)) {
373
+ return null;
374
+ }
375
+
376
+ if (this.WEB_PLATFORM_APIS.has(apiName)) {
377
+ return {
378
+ feature: apiName,
379
+ type: 'js',
380
+ context: this.getContext(content, node.loc?.start.line || 0),
381
+ line: (node.loc?.start.line || 0) + this.getLineOffset(offset, content),
382
+ column: node.loc?.start.column || 0
383
+ };
384
+ }
385
+
386
+ return null;
387
+ }
388
+
389
+ private extractWebAPICall(node: t.CallExpression, content: string, offset: number): DetectedFeature | null {
390
+ let apiName = '';
391
+
392
+ if (t.isIdentifier(node.callee)) {
393
+ apiName = node.callee.name;
394
+ } else if (t.isMemberExpression(node.callee)) {
395
+ apiName = this.getMemberExpressionName(node.callee);
396
+ }
397
+
398
+ if (!apiName || this.SVELTE_SPECIFIC_APIS.has(apiName)) {
399
+ return null;
400
+ }
401
+
402
+ if (this.WEB_PLATFORM_APIS.has(apiName)) {
403
+ return {
404
+ feature: apiName,
405
+ type: 'js',
406
+ context: this.getContext(content, node.loc?.start.line || 0),
407
+ line: (node.loc?.start.line || 0) + this.getLineOffset(offset, content),
408
+ column: node.loc?.start.column || 0
409
+ };
410
+ }
411
+
412
+ return null;
413
+ }
414
+
415
+ private extractScriptContent(scriptNode: any, fullContent: string): string {
416
+ const start = scriptNode.content.start;
417
+ const end = scriptNode.content.end;
418
+ return fullContent.slice(start, end);
419
+ }
420
+
421
+ private extractStyleContent(styleNode: any, fullContent: string): string {
422
+ const start = styleNode.content.start;
423
+ const end = styleNode.content.end;
424
+ return fullContent.slice(start, end);
425
+ }
426
+
427
+ private getLineOffset(position: number, content: string): number {
428
+ return content.slice(0, position).split('\n').length - 1;
429
+ }
430
+
431
+ private walkHtmlNode(node: any, callback: (node: any) => void): void {
432
+ callback(node);
433
+ if (node.children) {
434
+ node.children.forEach((child: any) => this.walkHtmlNode(child, callback));
435
+ }
436
+ }
437
+
438
+ private getMemberExpressionName(node: t.MemberExpression): string {
439
+ const parts: string[] = [];
440
+
441
+ const traverse = (n: t.Expression): void => {
442
+ if (t.isIdentifier(n)) {
443
+ parts.unshift(n.name);
444
+ } else if (t.isMemberExpression(n)) {
445
+ if (t.isIdentifier(n.property)) {
446
+ parts.unshift(n.property.name);
447
+ }
448
+ traverse(n.object);
449
+ }
450
+ };
451
+
452
+ traverse(node);
453
+ return parts.join('.');
454
+ }
455
+
456
+ private isTopLevelAwait(path: any): boolean {
457
+ let parent = path.parent;
458
+ while (parent) {
459
+ if (t.isFunction(parent) || t.isArrowFunctionExpression(parent)) {
460
+ return false;
461
+ }
462
+ parent = path.parentPath?.parent;
463
+ }
464
+ return true;
465
+ }
466
+
467
+ private extractSelectorFeature(selector: string): string {
468
+ if (selector.includes(':has(')) return ':has()';
469
+ if (selector.includes(':is(')) return ':is()';
470
+ if (selector.includes(':where(')) return ':where()';
471
+ if (selector.includes(':focus-visible')) return ':focus-visible';
472
+ return selector;
473
+ }
474
+
475
+ private isModernHTMLElement(tagName: string): boolean {
476
+ const modernElements = new Set([
477
+ 'dialog', 'details', 'summary', 'main', 'article', 'section', 'nav', 'aside',
478
+ 'header', 'footer', 'figure', 'figcaption', 'time', 'mark', 'progress', 'meter',
479
+ 'canvas', 'video', 'audio', 'source', 'track', 'embed', 'object'
480
+ ]);
481
+ return modernElements.has(tagName);
482
+ }
483
+
484
+ private isSvelteDirective(attrName: string): boolean {
485
+ return Array.from(this.SVELTE_DIRECTIVES).some((directive: string) => attrName.startsWith(directive));
486
+ }
487
+
488
+ private isModernHTMLAttribute(attrName: string, attrValue: any): boolean {
489
+ const modernAttrs = new Set([
490
+ 'loading', 'decoding', 'fetchpriority', 'enterkeyhint', 'inputmode'
491
+ ]);
492
+ return modernAttrs.has(attrName);
493
+ }
494
+
495
+ private getNodeContext(node: any, fullContent: string): string {
496
+ const start = node.start;
497
+ const end = Math.min(node.end, start + 100); // Limit context length
498
+ return fullContent.slice(start, end).trim();
499
+ }
500
+
501
+ private getContext(content: string, line: number): string {
502
+ const lines = content.split('\n');
503
+ const targetLine = lines[line - 1] || '';
504
+ return targetLine.trim();
505
+ }
506
+ }