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.
- package/.eslintrc.json +25 -0
- package/.prettierrc +8 -0
- package/README.md +94 -0
- package/bin/base.js +494 -0
- package/dist/ai/fix-manager.d.ts +67 -0
- package/dist/ai/fix-manager.d.ts.map +1 -0
- package/dist/ai/fix-manager.js +326 -0
- package/dist/ai/fix-manager.js.map +1 -0
- package/dist/ai/gemini-analyzer.d.ts +116 -0
- package/dist/ai/gemini-analyzer.d.ts.map +1 -0
- package/dist/ai/gemini-analyzer.js +572 -0
- package/dist/ai/gemini-analyzer.js.map +1 -0
- package/dist/ai/index.d.ts +4 -0
- package/dist/ai/index.d.ts.map +1 -0
- package/dist/ai/index.js +5 -0
- package/dist/ai/index.js.map +1 -0
- package/dist/ai/jules-implementer.d.ts +115 -0
- package/dist/ai/jules-implementer.d.ts.map +1 -0
- package/dist/ai/jules-implementer.js +387 -0
- package/dist/ai/jules-implementer.js.map +1 -0
- package/dist/commands/automation.d.ts +5 -0
- package/dist/commands/automation.d.ts.map +1 -0
- package/dist/commands/automation.js +305 -0
- package/dist/commands/automation.js.map +1 -0
- package/dist/commands/check.d.ts +9 -0
- package/dist/commands/check.d.ts.map +1 -0
- package/dist/commands/check.js +113 -0
- package/dist/commands/check.js.map +1 -0
- package/dist/commands/config.d.ts +11 -0
- package/dist/commands/config.d.ts.map +1 -0
- package/dist/commands/config.js +324 -0
- package/dist/commands/config.js.map +1 -0
- package/dist/commands/fix.d.ts +9 -0
- package/dist/commands/fix.d.ts.map +1 -0
- package/dist/commands/fix.js +207 -0
- package/dist/commands/fix.js.map +1 -0
- package/dist/commands/index.d.ts +6 -0
- package/dist/commands/index.d.ts.map +1 -0
- package/dist/commands/index.js +7 -0
- package/dist/commands/index.js.map +1 -0
- package/dist/commands/init.d.ts +9 -0
- package/dist/commands/init.d.ts.map +1 -0
- package/dist/commands/init.js +125 -0
- package/dist/commands/init.js.map +1 -0
- package/dist/core/api-key-manager.d.ts +83 -0
- package/dist/core/api-key-manager.d.ts.map +1 -0
- package/dist/core/api-key-manager.js +244 -0
- package/dist/core/api-key-manager.js.map +1 -0
- package/dist/core/baseguard.d.ts +46 -0
- package/dist/core/baseguard.d.ts.map +1 -0
- package/dist/core/baseguard.js +132 -0
- package/dist/core/baseguard.js.map +1 -0
- package/dist/core/baseline-checker.d.ts +63 -0
- package/dist/core/baseline-checker.d.ts.map +1 -0
- package/dist/core/baseline-checker.js +502 -0
- package/dist/core/baseline-checker.js.map +1 -0
- package/dist/core/cache-manager.d.ts +88 -0
- package/dist/core/cache-manager.d.ts.map +1 -0
- package/dist/core/cache-manager.js +213 -0
- package/dist/core/cache-manager.js.map +1 -0
- package/dist/core/configuration.d.ts +140 -0
- package/dist/core/configuration.d.ts.map +1 -0
- package/dist/core/configuration.js +474 -0
- package/dist/core/configuration.js.map +1 -0
- package/dist/core/directory-filter.d.ts +90 -0
- package/dist/core/directory-filter.d.ts.map +1 -0
- package/dist/core/directory-filter.js +319 -0
- package/dist/core/directory-filter.js.map +1 -0
- package/dist/core/error-handler.d.ts +110 -0
- package/dist/core/error-handler.d.ts.map +1 -0
- package/dist/core/error-handler.js +392 -0
- package/dist/core/error-handler.js.map +1 -0
- package/dist/core/file-processor.d.ts +80 -0
- package/dist/core/file-processor.d.ts.map +1 -0
- package/dist/core/file-processor.js +259 -0
- package/dist/core/file-processor.js.map +1 -0
- package/dist/core/gitignore-manager.d.ts +44 -0
- package/dist/core/gitignore-manager.d.ts.map +1 -0
- package/dist/core/gitignore-manager.js +147 -0
- package/dist/core/gitignore-manager.js.map +1 -0
- package/dist/core/index.d.ts +13 -0
- package/dist/core/index.d.ts.map +1 -0
- package/dist/core/index.js +13 -0
- package/dist/core/index.js.map +1 -0
- package/dist/core/lazy-loader.d.ts +68 -0
- package/dist/core/lazy-loader.d.ts.map +1 -0
- package/dist/core/lazy-loader.js +260 -0
- package/dist/core/lazy-loader.js.map +1 -0
- package/dist/core/memory-manager.d.ts +1 -0
- package/dist/core/memory-manager.d.ts.map +1 -0
- package/dist/core/memory-manager.js +2 -0
- package/dist/core/memory-manager.js.map +1 -0
- package/dist/core/startup-optimizer.d.ts +45 -0
- package/dist/core/startup-optimizer.d.ts.map +1 -0
- package/dist/core/startup-optimizer.js +140 -0
- package/dist/core/startup-optimizer.js.map +1 -0
- package/dist/git/automation-engine.d.ts +58 -0
- package/dist/git/automation-engine.d.ts.map +1 -0
- package/dist/git/automation-engine.js +318 -0
- package/dist/git/automation-engine.js.map +1 -0
- package/dist/git/github-manager.d.ts +71 -0
- package/dist/git/github-manager.d.ts.map +1 -0
- package/dist/git/github-manager.js +226 -0
- package/dist/git/github-manager.js.map +1 -0
- package/dist/git/hook-manager.d.ts +43 -0
- package/dist/git/hook-manager.d.ts.map +1 -0
- package/dist/git/hook-manager.js +191 -0
- package/dist/git/hook-manager.js.map +1 -0
- package/dist/git/index.d.ts +4 -0
- package/dist/git/index.d.ts.map +1 -0
- package/dist/git/index.js +5 -0
- package/dist/git/index.js.map +1 -0
- package/dist/index.d.ts +8 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +9 -0
- package/dist/index.js.map +1 -0
- package/dist/parsers/feature-validator.d.ts +60 -0
- package/dist/parsers/feature-validator.d.ts.map +1 -0
- package/dist/parsers/feature-validator.js +483 -0
- package/dist/parsers/feature-validator.js.map +1 -0
- package/dist/parsers/index.d.ts +8 -0
- package/dist/parsers/index.d.ts.map +1 -0
- package/dist/parsers/index.js +9 -0
- package/dist/parsers/index.js.map +1 -0
- package/dist/parsers/parser-manager.d.ts +103 -0
- package/dist/parsers/parser-manager.d.ts.map +1 -0
- package/dist/parsers/parser-manager.js +321 -0
- package/dist/parsers/parser-manager.js.map +1 -0
- package/dist/parsers/parser.d.ts +23 -0
- package/dist/parsers/parser.d.ts.map +1 -0
- package/dist/parsers/parser.js +6 -0
- package/dist/parsers/parser.js.map +1 -0
- package/dist/parsers/react-parser.d.ts +22 -0
- package/dist/parsers/react-parser.d.ts.map +1 -0
- package/dist/parsers/react-parser.js +307 -0
- package/dist/parsers/react-parser.js.map +1 -0
- package/dist/parsers/svelte-parser.d.ts +33 -0
- package/dist/parsers/svelte-parser.d.ts.map +1 -0
- package/dist/parsers/svelte-parser.js +408 -0
- package/dist/parsers/svelte-parser.js.map +1 -0
- package/dist/parsers/vanilla-parser.d.ts +31 -0
- package/dist/parsers/vanilla-parser.d.ts.map +1 -0
- package/dist/parsers/vanilla-parser.js +590 -0
- package/dist/parsers/vanilla-parser.js.map +1 -0
- package/dist/parsers/vue-parser.d.ts +9 -0
- package/dist/parsers/vue-parser.d.ts.map +1 -0
- package/dist/parsers/vue-parser.js +16 -0
- package/dist/parsers/vue-parser.js.map +1 -0
- package/dist/terminal-header.d.ts +12 -0
- package/dist/terminal-header.js +45 -0
- package/dist/types/index.d.ts +83 -0
- package/dist/types/index.d.ts.map +1 -0
- package/dist/types/index.js +5 -0
- package/dist/types/index.js.map +1 -0
- package/dist/ui/components.d.ts +133 -0
- package/dist/ui/components.d.ts.map +1 -0
- package/dist/ui/components.js +482 -0
- package/dist/ui/components.js.map +1 -0
- package/dist/ui/help.d.ts +11 -0
- package/dist/ui/help.d.ts.map +1 -0
- package/dist/ui/help.js +161 -0
- package/dist/ui/help.js.map +1 -0
- package/dist/ui/index.d.ts +5 -0
- package/dist/ui/index.d.ts.map +1 -0
- package/dist/ui/index.js +5 -0
- package/dist/ui/index.js.map +1 -0
- package/dist/ui/prompts.d.ts +63 -0
- package/dist/ui/prompts.d.ts.map +1 -0
- package/dist/ui/prompts.js +611 -0
- package/dist/ui/prompts.js.map +1 -0
- package/dist/ui/terminal-header.d.ts +13 -0
- package/dist/ui/terminal-header.d.ts.map +1 -0
- package/dist/ui/terminal-header.js +46 -0
- package/dist/ui/terminal-header.js.map +1 -0
- package/package.json +80 -0
- package/src/ai/__tests__/gemini-analyzer.test.ts +181 -0
- package/src/ai/fix-manager.ts +362 -0
- package/src/ai/gemini-analyzer.ts +671 -0
- package/src/ai/index.ts +4 -0
- package/src/ai/jules-implementer.ts +459 -0
- package/src/commands/automation.ts +344 -0
- package/src/commands/check.ts +299 -0
- package/src/commands/config.ts +365 -0
- package/src/commands/fix.ts +234 -0
- package/src/commands/index.ts +6 -0
- package/src/commands/init.ts +142 -0
- package/src/commands/status.ts +0 -0
- package/src/core/api-key-manager.ts +298 -0
- package/src/core/baseguard.ts +742 -0
- package/src/core/baseline-checker.ts +563 -0
- package/src/core/cache-manager.ts +270 -0
- package/src/core/configuration-recovery.ts +676 -0
- package/src/core/configuration.ts +559 -0
- package/src/core/debug-logger.ts +590 -0
- package/src/core/directory-filter.ts +421 -0
- package/src/core/error-handler.ts +517 -0
- package/src/core/file-processor.ts +331 -0
- package/src/core/gitignore-manager.ts +169 -0
- package/src/core/graceful-degradation-manager.ts +596 -0
- package/src/core/index.ts +13 -0
- package/src/core/lazy-loader.ts +307 -0
- package/src/core/logger.ts +0 -0
- package/src/core/memory-manager.ts +294 -0
- package/src/core/startup-optimizer.ts +173 -0
- package/src/core/system-error-handler.ts +746 -0
- package/src/git/automation-engine.ts +361 -0
- package/src/git/github-manager.ts +260 -0
- package/src/git/hook-manager.ts +210 -0
- package/src/git/index.ts +4 -0
- package/src/index.ts +8 -0
- package/src/parsers/feature-validator.ts +559 -0
- package/src/parsers/index.ts +8 -0
- package/src/parsers/parser-manager.ts +419 -0
- package/src/parsers/parser.ts +26 -0
- package/src/parsers/react-parser-optimized.ts +161 -0
- package/src/parsers/react-parser.ts +359 -0
- package/src/parsers/svelte-parser.ts +506 -0
- package/src/parsers/vanilla-parser.ts +682 -0
- package/src/parsers/vue-parser.ts +472 -0
- package/src/types/index.ts +92 -0
- package/src/ui/components.ts +567 -0
- package/src/ui/help.ts +193 -0
- package/src/ui/index.ts +4 -0
- package/src/ui/prompts.ts +688 -0
- package/src/ui/terminal-header.ts +59 -0
- package/test-config-commands.js +56 -0
- package/test-header-simple.js +33 -0
- package/test-terminal-header.js +12 -0
- package/test-ui.js +29 -0
- package/tests/e2e/baseguard.e2e.test.ts +516 -0
- package/tests/e2e/cross-platform.e2e.test.ts +420 -0
- package/tests/e2e/git-integration.e2e.test.ts +487 -0
- package/tests/fixtures/react-project/package.json +14 -0
- package/tests/fixtures/react-project/src/App.css +76 -0
- package/tests/fixtures/react-project/src/App.tsx +77 -0
- package/tests/fixtures/svelte-project/package.json +11 -0
- package/tests/fixtures/svelte-project/src/App.svelte +369 -0
- package/tests/fixtures/vanilla-project/index.html +76 -0
- package/tests/fixtures/vanilla-project/script.js +331 -0
- package/tests/fixtures/vanilla-project/styles.css +359 -0
- package/tests/fixtures/vue-project/package.json +12 -0
- package/tests/fixtures/vue-project/src/App.vue +216 -0
- package/tsconfig.json +36 -0
- package/vitest.config.ts +10 -0
|
@@ -0,0 +1,472 @@
|
|
|
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
|
+
* Vue single-file component parser - extracts ALL web platform features
|
|
7
|
+
* while ignoring Vue-specific APIs
|
|
8
|
+
*/
|
|
9
|
+
export class VueParser extends Parser {
|
|
10
|
+
private readonly VUE_SPECIFIC_APIS = new Set([
|
|
11
|
+
// Vue 3 Composition API
|
|
12
|
+
'ref', 'reactive', 'computed', 'watch', 'watchEffect', 'onMounted', 'onUnmounted',
|
|
13
|
+
'onBeforeMount', 'onBeforeUnmount', 'onUpdated', 'onBeforeUpdate',
|
|
14
|
+
'onActivated', 'onDeactivated', 'onErrorCaptured', 'provide', 'inject',
|
|
15
|
+
'getCurrentInstance', 'nextTick', 'defineComponent', 'defineProps', 'defineEmits',
|
|
16
|
+
'defineExpose', 'withDefaults', 'toRef', 'toRefs', 'unref', 'isRef',
|
|
17
|
+
|
|
18
|
+
// Vue 2 Options API
|
|
19
|
+
'Vue', 'data', 'props', 'methods', 'computed', 'watch', 'created', 'mounted',
|
|
20
|
+
'updated', 'destroyed', 'beforeCreate', 'beforeMount', 'beforeUpdate', 'beforeDestroy',
|
|
21
|
+
'activated', 'deactivated', 'errorCaptured', 'mixins', 'extends', 'components',
|
|
22
|
+
|
|
23
|
+
// Vue Router
|
|
24
|
+
'useRouter', 'useRoute', '$router', '$route', 'router-link', 'router-view',
|
|
25
|
+
|
|
26
|
+
// Vuex/Pinia
|
|
27
|
+
'useStore', '$store', 'mapState', 'mapGetters', 'mapMutations', 'mapActions'
|
|
28
|
+
]);
|
|
29
|
+
|
|
30
|
+
private readonly VUE_DIRECTIVES = new Set([
|
|
31
|
+
'v-if', 'v-else', 'v-else-if', 'v-for', 'v-show', 'v-bind', 'v-on', 'v-model',
|
|
32
|
+
'v-slot', 'v-pre', 'v-cloak', 'v-once', 'v-memo', 'v-html', 'v-text'
|
|
33
|
+
]);
|
|
34
|
+
|
|
35
|
+
private readonly WEB_PLATFORM_APIS = new Set([
|
|
36
|
+
// Canvas APIs
|
|
37
|
+
'getContext', 'CanvasRenderingContext2D', 'WebGLRenderingContext', 'WebGL2RenderingContext',
|
|
38
|
+
'OffscreenCanvas', 'ImageBitmap', 'createImageBitmap', 'Path2D',
|
|
39
|
+
|
|
40
|
+
// WebRTC APIs
|
|
41
|
+
'RTCPeerConnection', 'RTCDataChannel', 'RTCSessionDescription', 'RTCIceCandidate',
|
|
42
|
+
'getUserMedia', 'getDisplayMedia', 'MediaStream', 'MediaStreamTrack',
|
|
43
|
+
|
|
44
|
+
// WebAssembly
|
|
45
|
+
'WebAssembly', 'instantiate', 'compile', 'validate',
|
|
46
|
+
|
|
47
|
+
// Service Workers & PWA
|
|
48
|
+
'ServiceWorker', 'serviceWorker', 'register', 'Cache', 'caches',
|
|
49
|
+
'PushManager', 'Notification', 'showNotification',
|
|
50
|
+
|
|
51
|
+
// DOM APIs
|
|
52
|
+
'querySelector', 'querySelectorAll', 'getElementById', 'getElementsByClassName',
|
|
53
|
+
'addEventListener', 'removeEventListener', 'dispatchEvent', 'CustomEvent',
|
|
54
|
+
'MutationObserver', 'ResizeObserver', 'IntersectionObserver', 'PerformanceObserver',
|
|
55
|
+
'AbortController', 'AbortSignal', 'FormData', 'URLSearchParams', 'URL',
|
|
56
|
+
'fetch', 'Request', 'Response', 'Headers', 'Blob', 'File', 'FileReader',
|
|
57
|
+
|
|
58
|
+
// Web APIs
|
|
59
|
+
'navigator', 'geolocation', 'permissions', 'clipboard', 'share',
|
|
60
|
+
'requestAnimationFrame', 'cancelAnimationFrame', 'requestIdleCallback',
|
|
61
|
+
'setTimeout', 'setInterval', 'clearTimeout', 'clearInterval',
|
|
62
|
+
'localStorage', 'sessionStorage', 'indexedDB', 'crypto', 'performance',
|
|
63
|
+
|
|
64
|
+
// Audio/Video APIs
|
|
65
|
+
'AudioContext', 'MediaRecorder', 'MediaSource', 'SourceBuffer',
|
|
66
|
+
'HTMLMediaElement', 'HTMLAudioElement', 'HTMLVideoElement',
|
|
67
|
+
|
|
68
|
+
// Modern JavaScript APIs
|
|
69
|
+
'structuredClone', 'queueMicrotask', 'reportError',
|
|
70
|
+
'WeakRef', 'FinalizationRegistry', 'AggregateError',
|
|
71
|
+
|
|
72
|
+
// Intl APIs
|
|
73
|
+
'Intl', 'DateTimeFormat', 'NumberFormat', 'Collator', 'PluralRules',
|
|
74
|
+
'RelativeTimeFormat', 'ListFormat', 'Locale'
|
|
75
|
+
]);
|
|
76
|
+
|
|
77
|
+
canParse(filePath: string): boolean {
|
|
78
|
+
return /\.vue$/.test(filePath);
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
async parseFeatures(content: string, filePath: string): Promise<DetectedFeature[]> {
|
|
82
|
+
const features: DetectedFeature[] = [];
|
|
83
|
+
|
|
84
|
+
try {
|
|
85
|
+
// Lazy load Vue compiler
|
|
86
|
+
const vueCompiler = await LazyLoader.getVueCompiler();
|
|
87
|
+
const { descriptor } = vueCompiler.parse(content, { filename: filePath });
|
|
88
|
+
|
|
89
|
+
// Parse script blocks for JavaScript features
|
|
90
|
+
if (descriptor.script) {
|
|
91
|
+
const scriptFeatures = await this.parseScriptBlock(
|
|
92
|
+
descriptor.script.content,
|
|
93
|
+
descriptor.script.lang || 'js',
|
|
94
|
+
filePath
|
|
95
|
+
);
|
|
96
|
+
features.push(...scriptFeatures);
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
if (descriptor.scriptSetup) {
|
|
100
|
+
const setupFeatures = await this.parseScriptBlock(
|
|
101
|
+
descriptor.scriptSetup.content,
|
|
102
|
+
descriptor.scriptSetup.lang || 'js',
|
|
103
|
+
filePath
|
|
104
|
+
);
|
|
105
|
+
features.push(...setupFeatures);
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
// Parse style blocks for CSS features
|
|
109
|
+
if (descriptor.styles) {
|
|
110
|
+
for (const style of descriptor.styles) {
|
|
111
|
+
const styleFeatures = await this.parseStyleBlock(
|
|
112
|
+
style.content,
|
|
113
|
+
style.lang || 'css',
|
|
114
|
+
filePath
|
|
115
|
+
);
|
|
116
|
+
features.push(...styleFeatures);
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
// Parse template for HTML features (standard elements only)
|
|
121
|
+
if (descriptor.template) {
|
|
122
|
+
const templateFeatures = this.parseTemplateBlock(
|
|
123
|
+
descriptor.template.content,
|
|
124
|
+
filePath
|
|
125
|
+
);
|
|
126
|
+
features.push(...templateFeatures);
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
} catch (error) {
|
|
130
|
+
console.warn(`Warning: Could not parse Vue file ${filePath}: ${error instanceof Error ? error.message : 'Unknown error'}`);
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
return features;
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
private async parseScriptBlock(content: string, lang: string, filePath: string): Promise<DetectedFeature[]> {
|
|
137
|
+
const features: DetectedFeature[] = [];
|
|
138
|
+
|
|
139
|
+
try {
|
|
140
|
+
const isTypeScript = lang === 'ts' || lang === 'typescript';
|
|
141
|
+
|
|
142
|
+
// Lazy load Babel dependencies
|
|
143
|
+
const [parser, traverse] = await Promise.all([
|
|
144
|
+
LazyLoader.getBabelParser(),
|
|
145
|
+
LazyLoader.getBabelTraverse()
|
|
146
|
+
]);
|
|
147
|
+
|
|
148
|
+
const ast = parser.parse(content, {
|
|
149
|
+
sourceType: 'module',
|
|
150
|
+
plugins: [
|
|
151
|
+
'typescript',
|
|
152
|
+
'decorators-legacy',
|
|
153
|
+
'classProperties',
|
|
154
|
+
'objectRestSpread',
|
|
155
|
+
'asyncGenerators',
|
|
156
|
+
'functionBind',
|
|
157
|
+
'exportDefaultFrom',
|
|
158
|
+
'exportNamespaceFrom',
|
|
159
|
+
'dynamicImport',
|
|
160
|
+
'nullishCoalescingOperator',
|
|
161
|
+
'optionalChaining',
|
|
162
|
+
'topLevelAwait'
|
|
163
|
+
].filter(plugin => isTypeScript || plugin !== 'typescript')
|
|
164
|
+
});
|
|
165
|
+
|
|
166
|
+
traverse(ast, {
|
|
167
|
+
// Extract JavaScript Web APIs
|
|
168
|
+
MemberExpression: (path) => {
|
|
169
|
+
const feature = this.extractWebAPIFeature(path.node, path, content);
|
|
170
|
+
if (feature) {
|
|
171
|
+
features.push({ ...feature, file: filePath });
|
|
172
|
+
}
|
|
173
|
+
},
|
|
174
|
+
|
|
175
|
+
// Extract function calls to Web APIs
|
|
176
|
+
CallExpression: (path) => {
|
|
177
|
+
const feature = this.extractWebAPICall(path.node, path, content);
|
|
178
|
+
if (feature) {
|
|
179
|
+
features.push({ ...feature, file: filePath });
|
|
180
|
+
}
|
|
181
|
+
},
|
|
182
|
+
|
|
183
|
+
// Extract modern JavaScript syntax features
|
|
184
|
+
OptionalMemberExpression: (path) => {
|
|
185
|
+
features.push({
|
|
186
|
+
feature: 'optional-chaining',
|
|
187
|
+
type: 'js',
|
|
188
|
+
context: this.getContext(content, path.node.loc?.start.line || 0),
|
|
189
|
+
line: path.node.loc?.start.line || 0,
|
|
190
|
+
column: path.node.loc?.start.column || 0,
|
|
191
|
+
file: filePath
|
|
192
|
+
});
|
|
193
|
+
},
|
|
194
|
+
|
|
195
|
+
// Nullish coalescing
|
|
196
|
+
LogicalExpression: (path) => {
|
|
197
|
+
if (path.node.operator === '??') {
|
|
198
|
+
features.push({
|
|
199
|
+
feature: 'nullish-coalescing',
|
|
200
|
+
type: 'js',
|
|
201
|
+
context: this.getContext(content, path.node.loc?.start.line || 0),
|
|
202
|
+
line: path.node.loc?.start.line || 0,
|
|
203
|
+
column: path.node.loc?.start.column || 0,
|
|
204
|
+
file: filePath
|
|
205
|
+
});
|
|
206
|
+
}
|
|
207
|
+
},
|
|
208
|
+
|
|
209
|
+
// Private class fields
|
|
210
|
+
ClassPrivateProperty: (path) => {
|
|
211
|
+
features.push({
|
|
212
|
+
feature: 'private-fields',
|
|
213
|
+
type: 'js',
|
|
214
|
+
context: this.getContext(content, path.node.loc?.start.line || 0),
|
|
215
|
+
line: path.node.loc?.start.line || 0,
|
|
216
|
+
column: path.node.loc?.start.column || 0,
|
|
217
|
+
file: filePath
|
|
218
|
+
});
|
|
219
|
+
},
|
|
220
|
+
|
|
221
|
+
// Top-level await
|
|
222
|
+
AwaitExpression: (path) => {
|
|
223
|
+
if (this.isTopLevelAwait(path)) {
|
|
224
|
+
features.push({
|
|
225
|
+
feature: 'top-level-await',
|
|
226
|
+
type: 'js',
|
|
227
|
+
context: this.getContext(content, path.node.loc?.start.line || 0),
|
|
228
|
+
line: path.node.loc?.start.line || 0,
|
|
229
|
+
column: path.node.loc?.start.column || 0,
|
|
230
|
+
file: filePath
|
|
231
|
+
});
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
});
|
|
235
|
+
|
|
236
|
+
} catch (error) {
|
|
237
|
+
console.warn(`Warning: Could not parse script block in ${filePath}: ${error instanceof Error ? error.message : 'Unknown error'}`);
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
return features;
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
private async parseStyleBlock(content: string, lang: string, filePath: string): Promise<DetectedFeature[]> {
|
|
244
|
+
const features: DetectedFeature[] = [];
|
|
245
|
+
|
|
246
|
+
try {
|
|
247
|
+
// For now, only handle CSS (not SCSS, Less, etc.)
|
|
248
|
+
if (lang === 'css' || !lang) {
|
|
249
|
+
// Lazy load PostCSS
|
|
250
|
+
const postcss = await LazyLoader.getPostCSS();
|
|
251
|
+
const root = postcss.parse(content);
|
|
252
|
+
|
|
253
|
+
root.walkDecls(decl => {
|
|
254
|
+
features.push({
|
|
255
|
+
feature: decl.prop,
|
|
256
|
+
type: 'css',
|
|
257
|
+
context: `${decl.prop}: ${decl.value}`,
|
|
258
|
+
line: decl.source?.start?.line || 0,
|
|
259
|
+
column: decl.source?.start?.column || 0,
|
|
260
|
+
file: filePath
|
|
261
|
+
});
|
|
262
|
+
});
|
|
263
|
+
|
|
264
|
+
root.walkRules(rule => {
|
|
265
|
+
// Extract CSS selectors that might be modern features
|
|
266
|
+
if (rule.selector.includes(':has(') ||
|
|
267
|
+
rule.selector.includes(':is(') ||
|
|
268
|
+
rule.selector.includes(':where(') ||
|
|
269
|
+
rule.selector.includes(':focus-visible')) {
|
|
270
|
+
features.push({
|
|
271
|
+
feature: this.extractSelectorFeature(rule.selector),
|
|
272
|
+
type: 'css',
|
|
273
|
+
context: rule.selector,
|
|
274
|
+
line: rule.source?.start?.line || 0,
|
|
275
|
+
column: rule.source?.start?.column || 0,
|
|
276
|
+
file: filePath
|
|
277
|
+
});
|
|
278
|
+
}
|
|
279
|
+
});
|
|
280
|
+
|
|
281
|
+
root.walkAtRules(atRule => {
|
|
282
|
+
// Extract at-rules like @supports, @container, etc.
|
|
283
|
+
features.push({
|
|
284
|
+
feature: `@${atRule.name}`,
|
|
285
|
+
type: 'css',
|
|
286
|
+
context: `@${atRule.name} ${atRule.params}`,
|
|
287
|
+
line: atRule.source?.start?.line || 0,
|
|
288
|
+
column: atRule.source?.start?.column || 0,
|
|
289
|
+
file: filePath
|
|
290
|
+
});
|
|
291
|
+
});
|
|
292
|
+
}
|
|
293
|
+
} catch (error) {
|
|
294
|
+
console.warn(`Warning: Could not parse style block in ${filePath}: ${error instanceof Error ? error.message : 'Unknown error'}`);
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
return features;
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
private parseTemplateBlock(content: string, filePath: string): DetectedFeature[] {
|
|
301
|
+
const features: DetectedFeature[] = [];
|
|
302
|
+
|
|
303
|
+
// Extract standard HTML elements and attributes (ignore Vue directives)
|
|
304
|
+
const htmlElementRegex = /<(\w+)([^>]*)>/g;
|
|
305
|
+
const lines = content.split('\n');
|
|
306
|
+
|
|
307
|
+
lines.forEach((line, index) => {
|
|
308
|
+
let match;
|
|
309
|
+
while ((match = htmlElementRegex.exec(line)) !== null) {
|
|
310
|
+
const tagName = match[1];
|
|
311
|
+
const attributes = match[2];
|
|
312
|
+
|
|
313
|
+
// Check for modern HTML elements
|
|
314
|
+
if (this.isModernHTMLElement(tagName)) {
|
|
315
|
+
features.push({
|
|
316
|
+
feature: tagName,
|
|
317
|
+
type: 'html',
|
|
318
|
+
context: line.trim(),
|
|
319
|
+
line: index + 1,
|
|
320
|
+
column: match.index,
|
|
321
|
+
file: filePath
|
|
322
|
+
});
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
// Check for modern HTML attributes (ignore Vue directives)
|
|
326
|
+
if (attributes) {
|
|
327
|
+
const modernAttrs = this.extractModernAttributes(attributes);
|
|
328
|
+
modernAttrs.forEach(attr => {
|
|
329
|
+
features.push({
|
|
330
|
+
feature: attr,
|
|
331
|
+
type: 'html',
|
|
332
|
+
context: line.trim(),
|
|
333
|
+
line: index + 1,
|
|
334
|
+
column: match.index,
|
|
335
|
+
file: filePath
|
|
336
|
+
});
|
|
337
|
+
});
|
|
338
|
+
}
|
|
339
|
+
}
|
|
340
|
+
});
|
|
341
|
+
|
|
342
|
+
return features;
|
|
343
|
+
}
|
|
344
|
+
|
|
345
|
+
private extractWebAPIFeature(node: t.MemberExpression, path: any, content: string): DetectedFeature | null {
|
|
346
|
+
const apiName = this.getMemberExpressionName(node);
|
|
347
|
+
|
|
348
|
+
if (!apiName || this.VUE_SPECIFIC_APIS.has(apiName)) {
|
|
349
|
+
return null;
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
if (this.WEB_PLATFORM_APIS.has(apiName)) {
|
|
353
|
+
return {
|
|
354
|
+
feature: apiName,
|
|
355
|
+
type: 'js',
|
|
356
|
+
context: this.getContext(content, node.loc?.start.line || 0),
|
|
357
|
+
line: node.loc?.start.line || 0,
|
|
358
|
+
column: node.loc?.start.column || 0
|
|
359
|
+
};
|
|
360
|
+
}
|
|
361
|
+
|
|
362
|
+
return null;
|
|
363
|
+
}
|
|
364
|
+
|
|
365
|
+
private extractWebAPICall(node: t.CallExpression, path: any, content: string): DetectedFeature | null {
|
|
366
|
+
let apiName = '';
|
|
367
|
+
|
|
368
|
+
if (t.isIdentifier(node.callee)) {
|
|
369
|
+
apiName = node.callee.name;
|
|
370
|
+
} else if (t.isMemberExpression(node.callee)) {
|
|
371
|
+
apiName = this.getMemberExpressionName(node.callee);
|
|
372
|
+
}
|
|
373
|
+
|
|
374
|
+
if (!apiName || this.VUE_SPECIFIC_APIS.has(apiName)) {
|
|
375
|
+
return null;
|
|
376
|
+
}
|
|
377
|
+
|
|
378
|
+
if (this.WEB_PLATFORM_APIS.has(apiName)) {
|
|
379
|
+
return {
|
|
380
|
+
feature: apiName,
|
|
381
|
+
type: 'js',
|
|
382
|
+
context: this.getContext(content, node.loc?.start.line || 0),
|
|
383
|
+
line: node.loc?.start.line || 0,
|
|
384
|
+
column: node.loc?.start.column || 0
|
|
385
|
+
};
|
|
386
|
+
}
|
|
387
|
+
|
|
388
|
+
return null;
|
|
389
|
+
}
|
|
390
|
+
|
|
391
|
+
private getMemberExpressionName(node: t.MemberExpression): string {
|
|
392
|
+
const parts: string[] = [];
|
|
393
|
+
|
|
394
|
+
const traverse = (n: t.Expression): void => {
|
|
395
|
+
if (t.isIdentifier(n)) {
|
|
396
|
+
parts.unshift(n.name);
|
|
397
|
+
} else if (t.isMemberExpression(n)) {
|
|
398
|
+
if (t.isIdentifier(n.property)) {
|
|
399
|
+
parts.unshift(n.property.name);
|
|
400
|
+
}
|
|
401
|
+
traverse(n.object);
|
|
402
|
+
}
|
|
403
|
+
};
|
|
404
|
+
|
|
405
|
+
traverse(node);
|
|
406
|
+
return parts.join('.');
|
|
407
|
+
}
|
|
408
|
+
|
|
409
|
+
private isTopLevelAwait(path: any): boolean {
|
|
410
|
+
let parent = path.parent;
|
|
411
|
+
while (parent) {
|
|
412
|
+
if (t.isFunction(parent) || t.isArrowFunctionExpression(parent)) {
|
|
413
|
+
return false;
|
|
414
|
+
}
|
|
415
|
+
parent = path.parentPath?.parent;
|
|
416
|
+
}
|
|
417
|
+
return true;
|
|
418
|
+
}
|
|
419
|
+
|
|
420
|
+
private extractSelectorFeature(selector: string): string {
|
|
421
|
+
if (selector.includes(':has(')) return ':has()';
|
|
422
|
+
if (selector.includes(':is(')) return ':is()';
|
|
423
|
+
if (selector.includes(':where(')) return ':where()';
|
|
424
|
+
if (selector.includes(':focus-visible')) return ':focus-visible';
|
|
425
|
+
return selector;
|
|
426
|
+
}
|
|
427
|
+
|
|
428
|
+
private isModernHTMLElement(tagName: string): boolean {
|
|
429
|
+
const modernElements = new Set([
|
|
430
|
+
'dialog', 'details', 'summary', 'main', 'article', 'section', 'nav', 'aside',
|
|
431
|
+
'header', 'footer', 'figure', 'figcaption', 'time', 'mark', 'progress', 'meter',
|
|
432
|
+
'canvas', 'video', 'audio', 'source', 'track', 'embed', 'object'
|
|
433
|
+
]);
|
|
434
|
+
return modernElements.has(tagName);
|
|
435
|
+
}
|
|
436
|
+
|
|
437
|
+
private extractModernAttributes(attributes: string): string[] {
|
|
438
|
+
const modernAttrs: string[] = [];
|
|
439
|
+
const modernAttrPatterns = [
|
|
440
|
+
/loading=["']lazy["']/,
|
|
441
|
+
/decoding=["']async["']/,
|
|
442
|
+
/fetchpriority=["'](high|low)["']/,
|
|
443
|
+
/enterkeyhint=["']\w+["']/,
|
|
444
|
+
/inputmode=["']\w+["']/
|
|
445
|
+
];
|
|
446
|
+
|
|
447
|
+
modernAttrPatterns.forEach(pattern => {
|
|
448
|
+
if (pattern.test(attributes)) {
|
|
449
|
+
const match = attributes.match(pattern);
|
|
450
|
+
if (match) {
|
|
451
|
+
modernAttrs.push(match[0].split('=')[0]);
|
|
452
|
+
}
|
|
453
|
+
}
|
|
454
|
+
});
|
|
455
|
+
|
|
456
|
+
return modernAttrs;
|
|
457
|
+
}
|
|
458
|
+
|
|
459
|
+
private getContext(content: string, line: number): string {
|
|
460
|
+
const lines = content.split('\n');
|
|
461
|
+
const targetLine = lines[line - 1] || '';
|
|
462
|
+
return targetLine.trim();
|
|
463
|
+
}
|
|
464
|
+
|
|
465
|
+
getSupportedExtensions(): string[] {
|
|
466
|
+
return ['.vue'];
|
|
467
|
+
}
|
|
468
|
+
|
|
469
|
+
getName(): string {
|
|
470
|
+
return 'VueParser';
|
|
471
|
+
}
|
|
472
|
+
}
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Core data models for BaseGuard
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
export interface Violation {
|
|
6
|
+
feature: string; // 'container-type'
|
|
7
|
+
featureId: string; // 'container-queries' (web-features ID)
|
|
8
|
+
file: string; // 'src/Card.css'
|
|
9
|
+
line: number; // 15
|
|
10
|
+
column: number; // 5
|
|
11
|
+
context: string; // ' container-type: inline-size;'
|
|
12
|
+
browser: string; // 'safari'
|
|
13
|
+
required: string; // '15'
|
|
14
|
+
actual: string | false; // '16' or false
|
|
15
|
+
baselineStatus: string; // 'newly' | 'widely' | false
|
|
16
|
+
reason: string; // Human-readable explanation
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export interface Analysis {
|
|
20
|
+
violation: Violation;
|
|
21
|
+
userImpact: string; // "8% of users on Safari 15"
|
|
22
|
+
marketShare: number; // 0.08
|
|
23
|
+
fixStrategy: string; // "progressive enhancement"
|
|
24
|
+
bestPractices: string[]; // ["Use @supports", "Add fallback"]
|
|
25
|
+
sources: string[]; // ["https://web.dev/...", "https://mdn..."]
|
|
26
|
+
plainEnglish: string; // Full explanation
|
|
27
|
+
confidence: number; // 0.0-1.0
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
export interface Fix {
|
|
31
|
+
violation: Violation;
|
|
32
|
+
analysis: Analysis;
|
|
33
|
+
patch: string; // Unified diff format
|
|
34
|
+
explanation: string; // What the fix does
|
|
35
|
+
filePath: string; // Target file
|
|
36
|
+
preview: string; // Human-readable preview
|
|
37
|
+
confidence: number; // 0.0-1.0
|
|
38
|
+
testable: boolean; // Can be automatically tested
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
export interface BrowserTarget {
|
|
42
|
+
browser: string; // 'chrome', 'safari', 'firefox', etc.
|
|
43
|
+
minVersion: string | 'baseline' | 'baseline-newly';
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
export interface Configuration {
|
|
47
|
+
version: string;
|
|
48
|
+
targets: BrowserTarget[];
|
|
49
|
+
apiKeys: {
|
|
50
|
+
jules: string | null;
|
|
51
|
+
gemini: string | null;
|
|
52
|
+
};
|
|
53
|
+
automation: {
|
|
54
|
+
enabled: boolean;
|
|
55
|
+
trigger: 'pre-commit' | 'pre-push';
|
|
56
|
+
autoAnalyze: boolean;
|
|
57
|
+
autoFix: boolean;
|
|
58
|
+
blockCommit: boolean;
|
|
59
|
+
};
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
export interface DetectedFeature {
|
|
63
|
+
feature: string; // e.g., 'container-type', 'dialog.showModal'
|
|
64
|
+
type: 'css' | 'js' | 'html';
|
|
65
|
+
line: number;
|
|
66
|
+
column: number;
|
|
67
|
+
context: string; // surrounding code for display
|
|
68
|
+
file?: string; // file path where feature was detected
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
export interface CompatibilityResult {
|
|
72
|
+
violations: Violation[];
|
|
73
|
+
featureData: any; // web-features data structure
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
export interface AutomationOptions {
|
|
77
|
+
trigger: 'pre-commit' | 'pre-push';
|
|
78
|
+
strict?: boolean;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
export interface JulesSession {
|
|
82
|
+
id: string;
|
|
83
|
+
status: string;
|
|
84
|
+
title: string;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
export interface JulesActivity {
|
|
88
|
+
id: string;
|
|
89
|
+
type: string;
|
|
90
|
+
status: string;
|
|
91
|
+
timestamp: string;
|
|
92
|
+
}
|