@qiu_qiu/vue-auto-component-framework 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/README.md +72 -0
- package/dist/core/AppCreator.d.ts +33 -0
- package/dist/core/AppCreator.d.ts.map +1 -0
- package/dist/core/AppCreator.js +100 -0
- package/dist/core/ComponentRegistry.d.ts +44 -0
- package/dist/core/ComponentRegistry.d.ts.map +1 -0
- package/dist/core/ComponentRegistry.js +110 -0
- package/dist/core/ComponentScanner.d.ts +50 -0
- package/dist/core/ComponentScanner.d.ts.map +1 -0
- package/dist/core/ComponentScanner.js +203 -0
- package/dist/core/ErrorHandler.d.ts +51 -0
- package/dist/core/ErrorHandler.d.ts.map +1 -0
- package/dist/core/ErrorHandler.js +124 -0
- package/dist/core/FileScanner.d.ts +26 -0
- package/dist/core/FileScanner.d.ts.map +1 -0
- package/dist/core/FileScanner.js +102 -0
- package/dist/index.d.ts +21 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +34 -0
- package/dist/layout/LayoutManager.d.ts +56 -0
- package/dist/layout/LayoutManager.d.ts.map +1 -0
- package/dist/layout/LayoutManager.js +146 -0
- package/dist/layout/index.d.ts +4 -0
- package/dist/layout/index.d.ts.map +1 -0
- package/dist/layout/index.js +2 -0
- package/dist/types/index.d.ts +140 -0
- package/dist/types/index.d.ts.map +1 -0
- package/dist/types/index.js +78 -0
- package/package.json +47 -0
package/README.md
ADDED
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
# Vue Auto Component Framework
|
|
2
|
+
|
|
3
|
+
A Vue3 TypeScript framework that provides automatic component registration and layout system functionality.
|
|
4
|
+
|
|
5
|
+
## Features
|
|
6
|
+
|
|
7
|
+
- 🚀 Automatic component registration from `@/components` directory
|
|
8
|
+
- 🎨 Built-in layout system with responsive design
|
|
9
|
+
- 📦 TypeScript support with full type definitions
|
|
10
|
+
- 🔧 Compatible with Vue 3 Composition API
|
|
11
|
+
- 🛠️ Error handling and recovery mechanisms
|
|
12
|
+
|
|
13
|
+
## Installation
|
|
14
|
+
|
|
15
|
+
```bash
|
|
16
|
+
npm install vue-auto-component-framework
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
## Usage
|
|
20
|
+
|
|
21
|
+
```typescript
|
|
22
|
+
import { createApp } from 'vue-auto-component-framework'
|
|
23
|
+
import App from './App.vue'
|
|
24
|
+
|
|
25
|
+
const app = await createApp(App, {
|
|
26
|
+
componentsPath: '@/components', // optional, defaults to '@/components'
|
|
27
|
+
layoutConfig: {
|
|
28
|
+
header: true,
|
|
29
|
+
sidebar: true,
|
|
30
|
+
footer: true,
|
|
31
|
+
responsive: true
|
|
32
|
+
}
|
|
33
|
+
})
|
|
34
|
+
|
|
35
|
+
app.mount('#app')
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
## Development
|
|
39
|
+
|
|
40
|
+
```bash
|
|
41
|
+
# Install dependencies
|
|
42
|
+
npm install
|
|
43
|
+
|
|
44
|
+
# Build the project
|
|
45
|
+
npm run build
|
|
46
|
+
|
|
47
|
+
# Run tests
|
|
48
|
+
npm test
|
|
49
|
+
|
|
50
|
+
# Run tests in watch mode
|
|
51
|
+
npm run test:watch
|
|
52
|
+
|
|
53
|
+
# Type checking
|
|
54
|
+
npm run type-check
|
|
55
|
+
|
|
56
|
+
# Linting
|
|
57
|
+
npm run lint
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
## Project Structure
|
|
61
|
+
|
|
62
|
+
```
|
|
63
|
+
src/
|
|
64
|
+
├── core/ # Core framework classes
|
|
65
|
+
├── layout/ # Layout components
|
|
66
|
+
├── types/ # TypeScript type definitions
|
|
67
|
+
└── index.ts # Main entry point
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
## License
|
|
71
|
+
|
|
72
|
+
MIT
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { type App, type Component } from 'vue';
|
|
2
|
+
import type { FrameworkOptions, LayoutConfig } from '@/types';
|
|
3
|
+
import { LayoutManager } from '@/layout/LayoutManager';
|
|
4
|
+
/**
|
|
5
|
+
* App creator implementation
|
|
6
|
+
* Handles Vue app creation with framework features
|
|
7
|
+
*/
|
|
8
|
+
export declare class AppCreator {
|
|
9
|
+
private componentScanner;
|
|
10
|
+
private layoutManager;
|
|
11
|
+
constructor();
|
|
12
|
+
/**
|
|
13
|
+
* Create Vue app with framework features and error recovery
|
|
14
|
+
*/
|
|
15
|
+
createApp(rootComponent?: Component, options?: FrameworkOptions): Promise<App>;
|
|
16
|
+
/**
|
|
17
|
+
* Get current layout configuration
|
|
18
|
+
*/
|
|
19
|
+
getLayoutConfig(): Required<LayoutConfig>;
|
|
20
|
+
/**
|
|
21
|
+
* Update layout configuration
|
|
22
|
+
*/
|
|
23
|
+
updateLayoutConfig(config: LayoutConfig): void;
|
|
24
|
+
/**
|
|
25
|
+
* Register a custom layout component
|
|
26
|
+
*/
|
|
27
|
+
registerLayout(name: string, component: Component): void;
|
|
28
|
+
/**
|
|
29
|
+
* Get layout manager instance
|
|
30
|
+
*/
|
|
31
|
+
getLayoutManager(): LayoutManager;
|
|
32
|
+
}
|
|
33
|
+
//# sourceMappingURL=AppCreator.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"AppCreator.d.ts","sourceRoot":"","sources":["../../src/core/AppCreator.ts"],"names":[],"mappings":"AAAA,OAAO,EAA6B,KAAK,GAAG,EAAE,KAAK,SAAS,EAAE,MAAM,KAAK,CAAA;AACzE,OAAO,KAAK,EAAE,gBAAgB,EAAE,YAAY,EAAE,MAAM,SAAS,CAAA;AAG7D,OAAO,EAAE,aAAa,EAAE,MAAM,wBAAwB,CAAA;AAEtD;;;GAGG;AACH,qBAAa,UAAU;IACrB,OAAO,CAAC,gBAAgB,CAAkB;IAC1C,OAAO,CAAC,aAAa,CAAe;;IAOpC;;OAEG;IACG,SAAS,CAAC,aAAa,CAAC,EAAE,SAAS,EAAE,OAAO,GAAE,gBAAqB,GAAG,OAAO,CAAC,GAAG,CAAC;IAmFxF;;OAEG;IACH,eAAe,IAAI,QAAQ,CAAC,YAAY,CAAC;IAIzC;;OAEG;IACH,kBAAkB,CAAC,MAAM,EAAE,YAAY,GAAG,IAAI;IAI9C;;OAEG;IACH,cAAc,CAAC,IAAI,EAAE,MAAM,EAAE,SAAS,EAAE,SAAS,GAAG,IAAI;IAIxD;;OAEG;IACH,gBAAgB,IAAI,aAAa;CAGlC"}
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
import { createApp as vueCreateApp } from 'vue';
|
|
2
|
+
import { ComponentScanner } from './ComponentScanner';
|
|
3
|
+
import { ErrorHandler } from './ErrorHandler';
|
|
4
|
+
import { LayoutManager } from '@/layout/LayoutManager';
|
|
5
|
+
/**
|
|
6
|
+
* App creator implementation
|
|
7
|
+
* Handles Vue app creation with framework features
|
|
8
|
+
*/
|
|
9
|
+
export class AppCreator {
|
|
10
|
+
constructor() {
|
|
11
|
+
this.componentScanner = new ComponentScanner();
|
|
12
|
+
this.layoutManager = new LayoutManager();
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* Create Vue app with framework features and error recovery
|
|
16
|
+
*/
|
|
17
|
+
async createApp(rootComponent, options = {}) {
|
|
18
|
+
try {
|
|
19
|
+
const app = vueCreateApp(rootComponent || {});
|
|
20
|
+
// Initialize layout system with custom configuration
|
|
21
|
+
if (options.layoutConfig) {
|
|
22
|
+
this.layoutManager.updateConfig(options.layoutConfig);
|
|
23
|
+
}
|
|
24
|
+
try {
|
|
25
|
+
this.layoutManager.initializeLayouts(app);
|
|
26
|
+
}
|
|
27
|
+
catch (error) {
|
|
28
|
+
const layoutError = ErrorHandler.createLayoutError('Failed to initialize layout system', { layoutConfig: options.layoutConfig }, error);
|
|
29
|
+
ErrorHandler.logError(layoutError);
|
|
30
|
+
// Continue without layout system - app can still function
|
|
31
|
+
}
|
|
32
|
+
// Install additional plugins with error handling
|
|
33
|
+
if (options.plugins) {
|
|
34
|
+
options.plugins.forEach((plugin, index) => {
|
|
35
|
+
try {
|
|
36
|
+
app.use(plugin);
|
|
37
|
+
}
|
|
38
|
+
catch (error) {
|
|
39
|
+
const pluginError = ErrorHandler.createAppError(`Failed to install plugin at index ${index}`, { pluginIndex: index }, error);
|
|
40
|
+
ErrorHandler.logError(pluginError);
|
|
41
|
+
// Continue with other plugins - partial functionality is better than none
|
|
42
|
+
}
|
|
43
|
+
});
|
|
44
|
+
}
|
|
45
|
+
// Scan and register components with comprehensive error handling
|
|
46
|
+
const componentsPath = options.componentsPath || '@/components';
|
|
47
|
+
try {
|
|
48
|
+
const components = await this.componentScanner.scanComponents(componentsPath);
|
|
49
|
+
this.componentScanner.registerComponents(app, components);
|
|
50
|
+
// Report any errors that occurred during scanning/registration
|
|
51
|
+
const scanErrors = this.componentScanner.getErrors();
|
|
52
|
+
if (scanErrors.length > 0) {
|
|
53
|
+
console.warn(`Component scanning completed with ${scanErrors.length} errors`);
|
|
54
|
+
// Log summary of errors for debugging
|
|
55
|
+
const errorSummary = ErrorHandler.formatErrorSummary(scanErrors);
|
|
56
|
+
console.warn(errorSummary);
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
catch (error) {
|
|
60
|
+
// Critical component scanning error - log but don't fail app creation
|
|
61
|
+
const criticalError = ErrorHandler.createAppError('Critical error during component scanning - app will continue without auto-registered components', { componentsPath }, error);
|
|
62
|
+
ErrorHandler.logError(criticalError);
|
|
63
|
+
// App can still function without auto-registered components
|
|
64
|
+
console.warn('Application created successfully but component auto-registration failed');
|
|
65
|
+
}
|
|
66
|
+
return app;
|
|
67
|
+
}
|
|
68
|
+
catch (error) {
|
|
69
|
+
// Critical app creation error
|
|
70
|
+
const appError = ErrorHandler.createAppError('Failed to create Vue application', { rootComponent: rootComponent?.name || 'anonymous', options }, error);
|
|
71
|
+
ErrorHandler.logError(appError);
|
|
72
|
+
// Re-throw critical errors as they prevent app from functioning
|
|
73
|
+
throw appError;
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
/**
|
|
77
|
+
* Get current layout configuration
|
|
78
|
+
*/
|
|
79
|
+
getLayoutConfig() {
|
|
80
|
+
return this.layoutManager.getConfig();
|
|
81
|
+
}
|
|
82
|
+
/**
|
|
83
|
+
* Update layout configuration
|
|
84
|
+
*/
|
|
85
|
+
updateLayoutConfig(config) {
|
|
86
|
+
this.layoutManager.updateConfig(config);
|
|
87
|
+
}
|
|
88
|
+
/**
|
|
89
|
+
* Register a custom layout component
|
|
90
|
+
*/
|
|
91
|
+
registerLayout(name, component) {
|
|
92
|
+
this.layoutManager.registerLayout(name, component);
|
|
93
|
+
}
|
|
94
|
+
/**
|
|
95
|
+
* Get layout manager instance
|
|
96
|
+
*/
|
|
97
|
+
getLayoutManager() {
|
|
98
|
+
return this.layoutManager;
|
|
99
|
+
}
|
|
100
|
+
}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import type { Component } from 'vue';
|
|
2
|
+
import type { ComponentRegistry as IComponentRegistry } from '@/types';
|
|
3
|
+
/**
|
|
4
|
+
* Component registry implementation
|
|
5
|
+
* Manages registration and retrieval of Vue components with conflict detection
|
|
6
|
+
*/
|
|
7
|
+
export declare class ComponentRegistry implements IComponentRegistry {
|
|
8
|
+
private components;
|
|
9
|
+
private componentPaths;
|
|
10
|
+
/**
|
|
11
|
+
* Register a component with the given name
|
|
12
|
+
* Handles name conflicts by generating unique names using file paths
|
|
13
|
+
*/
|
|
14
|
+
register(name: string, component: Component, filePath?: string): void;
|
|
15
|
+
/**
|
|
16
|
+
* Get a component by name
|
|
17
|
+
*/
|
|
18
|
+
get(name: string): Component | undefined;
|
|
19
|
+
/**
|
|
20
|
+
* Get all registered components
|
|
21
|
+
*/
|
|
22
|
+
getAll(): Map<string, Component>;
|
|
23
|
+
/**
|
|
24
|
+
* Check if a component is registered
|
|
25
|
+
*/
|
|
26
|
+
has(name: string): boolean;
|
|
27
|
+
/**
|
|
28
|
+
* Get the file path for a registered component
|
|
29
|
+
*/
|
|
30
|
+
getPath(name: string): string | undefined;
|
|
31
|
+
/**
|
|
32
|
+
* Get all component names
|
|
33
|
+
*/
|
|
34
|
+
getNames(): string[];
|
|
35
|
+
/**
|
|
36
|
+
* Clear all registered components
|
|
37
|
+
*/
|
|
38
|
+
clear(): void;
|
|
39
|
+
/**
|
|
40
|
+
* Resolve name conflicts by using file path information
|
|
41
|
+
*/
|
|
42
|
+
private resolveNameConflict;
|
|
43
|
+
}
|
|
44
|
+
//# sourceMappingURL=ComponentRegistry.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ComponentRegistry.d.ts","sourceRoot":"","sources":["../../src/core/ComponentRegistry.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,KAAK,CAAA;AACpC,OAAO,KAAK,EAAE,iBAAiB,IAAI,kBAAkB,EAAE,MAAM,SAAS,CAAA;AAGtE;;;GAGG;AACH,qBAAa,iBAAkB,YAAW,kBAAkB;IAC1D,OAAO,CAAC,UAAU,CAAoC;IACtD,OAAO,CAAC,cAAc,CAAiC;IAEvD;;;OAGG;IACH,QAAQ,CAAC,IAAI,EAAE,MAAM,EAAE,SAAS,EAAE,SAAS,EAAE,QAAQ,CAAC,EAAE,MAAM,GAAG,IAAI;IAoDrE;;OAEG;IACH,GAAG,CAAC,IAAI,EAAE,MAAM,GAAG,SAAS,GAAG,SAAS;IAIxC;;OAEG;IACH,MAAM,IAAI,GAAG,CAAC,MAAM,EAAE,SAAS,CAAC;IAIhC;;OAEG;IACH,GAAG,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO;IAI1B;;OAEG;IACH,OAAO,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS;IAIzC;;OAEG;IACH,QAAQ,IAAI,MAAM,EAAE;IAIpB;;OAEG;IACH,KAAK,IAAI,IAAI;IAKb;;OAEG;IACH,OAAO,CAAC,mBAAmB;CA2B5B"}
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
import { ErrorHandler } from './ErrorHandler';
|
|
2
|
+
/**
|
|
3
|
+
* Component registry implementation
|
|
4
|
+
* Manages registration and retrieval of Vue components with conflict detection
|
|
5
|
+
*/
|
|
6
|
+
export class ComponentRegistry {
|
|
7
|
+
constructor() {
|
|
8
|
+
this.components = new Map();
|
|
9
|
+
this.componentPaths = new Map();
|
|
10
|
+
}
|
|
11
|
+
/**
|
|
12
|
+
* Register a component with the given name
|
|
13
|
+
* Handles name conflicts by generating unique names using file paths
|
|
14
|
+
*/
|
|
15
|
+
register(name, component, filePath) {
|
|
16
|
+
if (!name || typeof name !== 'string') {
|
|
17
|
+
throw ErrorHandler.createRegistrationError('Component name must be a non-empty string', name, filePath);
|
|
18
|
+
}
|
|
19
|
+
if (!component) {
|
|
20
|
+
throw ErrorHandler.createRegistrationError('Component cannot be null or undefined', name, filePath);
|
|
21
|
+
}
|
|
22
|
+
let finalName = name;
|
|
23
|
+
// Handle name conflicts by generating unique names
|
|
24
|
+
if (this.components.has(name)) {
|
|
25
|
+
const existingPath = this.componentPaths.get(name);
|
|
26
|
+
// Log the conflict for debugging
|
|
27
|
+
const conflictError = ErrorHandler.createNameConflictError(name, existingPath || 'unknown', filePath || 'unknown');
|
|
28
|
+
ErrorHandler.logError(conflictError);
|
|
29
|
+
// Resolve conflict by generating unique name
|
|
30
|
+
if (filePath) {
|
|
31
|
+
finalName = this.resolveNameConflict(name, filePath);
|
|
32
|
+
}
|
|
33
|
+
else {
|
|
34
|
+
// If no file path provided, append a counter
|
|
35
|
+
let counter = 1;
|
|
36
|
+
while (this.components.has(`${name}_${counter}`)) {
|
|
37
|
+
counter++;
|
|
38
|
+
}
|
|
39
|
+
finalName = `${name}_${counter}`;
|
|
40
|
+
}
|
|
41
|
+
console.info(`Resolved name conflict: ${name} -> ${finalName}`);
|
|
42
|
+
}
|
|
43
|
+
this.components.set(finalName, component);
|
|
44
|
+
if (filePath) {
|
|
45
|
+
this.componentPaths.set(finalName, filePath);
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* Get a component by name
|
|
50
|
+
*/
|
|
51
|
+
get(name) {
|
|
52
|
+
return this.components.get(name);
|
|
53
|
+
}
|
|
54
|
+
/**
|
|
55
|
+
* Get all registered components
|
|
56
|
+
*/
|
|
57
|
+
getAll() {
|
|
58
|
+
return new Map(this.components);
|
|
59
|
+
}
|
|
60
|
+
/**
|
|
61
|
+
* Check if a component is registered
|
|
62
|
+
*/
|
|
63
|
+
has(name) {
|
|
64
|
+
return this.components.has(name);
|
|
65
|
+
}
|
|
66
|
+
/**
|
|
67
|
+
* Get the file path for a registered component
|
|
68
|
+
*/
|
|
69
|
+
getPath(name) {
|
|
70
|
+
return this.componentPaths.get(name);
|
|
71
|
+
}
|
|
72
|
+
/**
|
|
73
|
+
* Get all component names
|
|
74
|
+
*/
|
|
75
|
+
getNames() {
|
|
76
|
+
return Array.from(this.components.keys());
|
|
77
|
+
}
|
|
78
|
+
/**
|
|
79
|
+
* Clear all registered components
|
|
80
|
+
*/
|
|
81
|
+
clear() {
|
|
82
|
+
this.components.clear();
|
|
83
|
+
this.componentPaths.clear();
|
|
84
|
+
}
|
|
85
|
+
/**
|
|
86
|
+
* Resolve name conflicts by using file path information
|
|
87
|
+
*/
|
|
88
|
+
resolveNameConflict(baseName, filePath) {
|
|
89
|
+
// Extract directory structure to create unique suffix
|
|
90
|
+
const pathParts = filePath.split(/[/\\]/).filter(part => part && part !== '.' && part !== '..');
|
|
91
|
+
// Remove file extension and base name from path parts
|
|
92
|
+
const fileName = pathParts[pathParts.length - 1]?.replace(/\.(vue|ts|js)$/, '');
|
|
93
|
+
const dirParts = pathParts.slice(0, -1);
|
|
94
|
+
// Create suffix from directory structure
|
|
95
|
+
let suffix = '';
|
|
96
|
+
if (dirParts.length > 0) {
|
|
97
|
+
suffix = dirParts.join('_');
|
|
98
|
+
}
|
|
99
|
+
// Generate unique name
|
|
100
|
+
let uniqueName = suffix ? `${baseName}_${suffix}` : baseName;
|
|
101
|
+
// If still conflicts, add counter
|
|
102
|
+
let counter = 1;
|
|
103
|
+
const originalUniqueName = uniqueName;
|
|
104
|
+
while (this.components.has(uniqueName)) {
|
|
105
|
+
uniqueName = `${originalUniqueName}_${counter}`;
|
|
106
|
+
counter++;
|
|
107
|
+
}
|
|
108
|
+
return uniqueName;
|
|
109
|
+
}
|
|
110
|
+
}
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import type { App } from 'vue';
|
|
2
|
+
import type { ComponentScanner as IComponentScanner, ComponentInfo, FrameworkError } from '@/types';
|
|
3
|
+
import { ComponentRegistry } from './ComponentRegistry';
|
|
4
|
+
/**
|
|
5
|
+
* Component scanner implementation
|
|
6
|
+
* Handles scanning and registering Vue components with error recovery
|
|
7
|
+
*/
|
|
8
|
+
export declare class ComponentScanner implements IComponentScanner {
|
|
9
|
+
private _fileScanner;
|
|
10
|
+
private _registry;
|
|
11
|
+
private _errors;
|
|
12
|
+
constructor();
|
|
13
|
+
/**
|
|
14
|
+
* Get all errors encountered during scanning and registration
|
|
15
|
+
*/
|
|
16
|
+
getErrors(): FrameworkError[];
|
|
17
|
+
/**
|
|
18
|
+
* Clear accumulated errors
|
|
19
|
+
*/
|
|
20
|
+
clearErrors(): void;
|
|
21
|
+
/**
|
|
22
|
+
* Scan components in the specified base path with error recovery
|
|
23
|
+
*/
|
|
24
|
+
scanComponents(basePath: string): Promise<ComponentInfo[]>;
|
|
25
|
+
/**
|
|
26
|
+
* Register components in the Vue app with conflict detection and error recovery
|
|
27
|
+
*/
|
|
28
|
+
registerComponents(app: App, components: ComponentInfo[]): void;
|
|
29
|
+
/**
|
|
30
|
+
* Get the internal component registry
|
|
31
|
+
*/
|
|
32
|
+
getRegistry(): ComponentRegistry;
|
|
33
|
+
/**
|
|
34
|
+
* Generate component name from file path
|
|
35
|
+
*/
|
|
36
|
+
private _generateComponentName;
|
|
37
|
+
/**
|
|
38
|
+
* Convert string to PascalCase
|
|
39
|
+
*/
|
|
40
|
+
private _toPascalCase;
|
|
41
|
+
/**
|
|
42
|
+
* Dynamically load a Vue component from file path with enhanced error handling
|
|
43
|
+
*/
|
|
44
|
+
private _loadComponent;
|
|
45
|
+
/**
|
|
46
|
+
* Validate if an object is a valid Vue component
|
|
47
|
+
*/
|
|
48
|
+
private _isValidVueComponent;
|
|
49
|
+
}
|
|
50
|
+
//# sourceMappingURL=ComponentScanner.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ComponentScanner.d.ts","sourceRoot":"","sources":["../../src/core/ComponentScanner.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,GAAG,EAAE,MAAM,KAAK,CAAA;AAC9B,OAAO,KAAK,EAAE,gBAAgB,IAAI,iBAAiB,EAAE,aAAa,EAAE,cAAc,EAAE,MAAM,SAAS,CAAA;AAEnG,OAAO,EAAE,iBAAiB,EAAE,MAAM,qBAAqB,CAAA;AAGvD;;;GAGG;AACH,qBAAa,gBAAiB,YAAW,iBAAiB;IACxD,OAAO,CAAC,YAAY,CAAa;IACjC,OAAO,CAAC,SAAS,CAAmB;IACpC,OAAO,CAAC,OAAO,CAAuB;;IAOtC;;OAEG;IACH,SAAS,IAAI,cAAc,EAAE;IAI7B;;OAEG;IACH,WAAW,IAAI,IAAI;IAInB;;OAEG;IACG,cAAc,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,aAAa,EAAE,CAAC;IAiEhE;;OAEG;IACH,kBAAkB,CAAC,GAAG,EAAE,GAAG,EAAE,UAAU,EAAE,aAAa,EAAE,GAAG,IAAI;IA8D/D;;OAEG;IACH,WAAW,IAAI,iBAAiB;IAIhC;;OAEG;IACH,OAAO,CAAC,sBAAsB;IAuB9B;;OAEG;IACH,OAAO,CAAC,aAAa;IAIrB;;OAEG;YACW,cAAc;IAkC5B;;OAEG;IACH,OAAO,CAAC,oBAAoB;CAqB7B"}
|
|
@@ -0,0 +1,203 @@
|
|
|
1
|
+
import { FileScanner } from './FileScanner';
|
|
2
|
+
import { ComponentRegistry } from './ComponentRegistry';
|
|
3
|
+
import { ErrorHandler } from './ErrorHandler';
|
|
4
|
+
/**
|
|
5
|
+
* Component scanner implementation
|
|
6
|
+
* Handles scanning and registering Vue components with error recovery
|
|
7
|
+
*/
|
|
8
|
+
export class ComponentScanner {
|
|
9
|
+
constructor() {
|
|
10
|
+
this._errors = [];
|
|
11
|
+
this._fileScanner = new FileScanner();
|
|
12
|
+
this._registry = new ComponentRegistry();
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* Get all errors encountered during scanning and registration
|
|
16
|
+
*/
|
|
17
|
+
getErrors() {
|
|
18
|
+
return [...this._errors];
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* Clear accumulated errors
|
|
22
|
+
*/
|
|
23
|
+
clearErrors() {
|
|
24
|
+
this._errors = [];
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* Scan components in the specified base path with error recovery
|
|
28
|
+
*/
|
|
29
|
+
async scanComponents(basePath) {
|
|
30
|
+
this.clearErrors();
|
|
31
|
+
try {
|
|
32
|
+
const scanResult = await this._fileScanner.scanDirectory(basePath, ['.vue']);
|
|
33
|
+
const components = [];
|
|
34
|
+
// Handle file scanning errors
|
|
35
|
+
if (scanResult.errors.length > 0) {
|
|
36
|
+
scanResult.errors.forEach(error => {
|
|
37
|
+
const frameworkError = ErrorHandler.createScanError(`File scanning error: ${error.message}`, undefined, error);
|
|
38
|
+
this._errors.push(frameworkError);
|
|
39
|
+
ErrorHandler.logError(frameworkError);
|
|
40
|
+
});
|
|
41
|
+
}
|
|
42
|
+
// Process each found file with individual error handling
|
|
43
|
+
for (const filePath of scanResult.files) {
|
|
44
|
+
try {
|
|
45
|
+
// Generate component name from file path
|
|
46
|
+
const name = this._generateComponentName(filePath, basePath);
|
|
47
|
+
// Dynamically import the Vue component
|
|
48
|
+
const component = await this._loadComponent(filePath);
|
|
49
|
+
components.push({
|
|
50
|
+
name,
|
|
51
|
+
component,
|
|
52
|
+
path: filePath
|
|
53
|
+
});
|
|
54
|
+
}
|
|
55
|
+
catch (error) {
|
|
56
|
+
// Create detailed error but continue processing other components
|
|
57
|
+
const frameworkError = ErrorHandler.createScanError(`Failed to process component file: ${filePath}`, filePath, error);
|
|
58
|
+
this._errors.push(frameworkError);
|
|
59
|
+
ErrorHandler.logError(frameworkError);
|
|
60
|
+
// Continue with next component (error recovery)
|
|
61
|
+
continue;
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
return components;
|
|
65
|
+
}
|
|
66
|
+
catch (error) {
|
|
67
|
+
// Critical scanning error - still try to return what we have
|
|
68
|
+
const frameworkError = ErrorHandler.createScanError(`Critical error during component scanning: ${error.message}`, basePath, error);
|
|
69
|
+
this._errors.push(frameworkError);
|
|
70
|
+
ErrorHandler.logError(frameworkError);
|
|
71
|
+
// Return empty array but don't throw - allow app to continue
|
|
72
|
+
return [];
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
/**
|
|
76
|
+
* Register components in the Vue app with conflict detection and error recovery
|
|
77
|
+
*/
|
|
78
|
+
registerComponents(app, components) {
|
|
79
|
+
// Clear registry for fresh registration
|
|
80
|
+
this._registry.clear();
|
|
81
|
+
let successCount = 0;
|
|
82
|
+
let errorCount = 0;
|
|
83
|
+
components.forEach(({ name, component, path }) => {
|
|
84
|
+
try {
|
|
85
|
+
// Validate component before registration
|
|
86
|
+
if (!this._isValidVueComponent(component)) {
|
|
87
|
+
const error = ErrorHandler.createInvalidComponentError(name, path, 'Component does not have required Vue component structure');
|
|
88
|
+
this._errors.push(error);
|
|
89
|
+
ErrorHandler.logError(error);
|
|
90
|
+
errorCount++;
|
|
91
|
+
return;
|
|
92
|
+
}
|
|
93
|
+
// Register in internal registry with conflict detection
|
|
94
|
+
this._registry.register(name, component, path);
|
|
95
|
+
// Get the final name (may be different due to conflict resolution)
|
|
96
|
+
const finalName = this._registry.getNames().find(registeredName => this._registry.get(registeredName) === component) || name;
|
|
97
|
+
// Register in Vue app
|
|
98
|
+
app.component(finalName, component);
|
|
99
|
+
successCount++;
|
|
100
|
+
}
|
|
101
|
+
catch (error) {
|
|
102
|
+
// Handle registration errors gracefully
|
|
103
|
+
const frameworkError = ErrorHandler.createRegistrationError(`Failed to register component: ${error.message}`, name, path, error);
|
|
104
|
+
this._errors.push(frameworkError);
|
|
105
|
+
ErrorHandler.logError(frameworkError);
|
|
106
|
+
errorCount++;
|
|
107
|
+
// Continue with next component (error recovery)
|
|
108
|
+
}
|
|
109
|
+
});
|
|
110
|
+
// Log summary of registration results
|
|
111
|
+
if (successCount > 0 || errorCount > 0) {
|
|
112
|
+
console.info(`Component registration completed: ${successCount} successful, ${errorCount} failed`);
|
|
113
|
+
}
|
|
114
|
+
// If we have errors but some components were registered successfully,
|
|
115
|
+
// the app can still continue (graceful degradation)
|
|
116
|
+
if (errorCount > 0 && successCount === 0) {
|
|
117
|
+
console.warn('No components were successfully registered, but application will continue');
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
/**
|
|
121
|
+
* Get the internal component registry
|
|
122
|
+
*/
|
|
123
|
+
getRegistry() {
|
|
124
|
+
return this._registry;
|
|
125
|
+
}
|
|
126
|
+
/**
|
|
127
|
+
* Generate component name from file path
|
|
128
|
+
*/
|
|
129
|
+
_generateComponentName(filePath, basePath) {
|
|
130
|
+
// Remove base path to get relative path
|
|
131
|
+
let relativePath = filePath.replace(basePath, '').replace(/^[/\\]/, '');
|
|
132
|
+
// Remove file extension
|
|
133
|
+
relativePath = relativePath.replace(/\.(vue|ts|js)$/, '');
|
|
134
|
+
// Handle special characters and path separators
|
|
135
|
+
// Replace path separators and special characters with dashes
|
|
136
|
+
let normalizedName = relativePath
|
|
137
|
+
.replace(/[/\\]/g, '-') // Replace path separators
|
|
138
|
+
.replace(/[^a-zA-Z0-9-_]/g, '-') // Replace special characters
|
|
139
|
+
.replace(/-+/g, '-') // Replace multiple dashes with single dash
|
|
140
|
+
.replace(/^-|-$/g, ''); // Remove leading/trailing dashes
|
|
141
|
+
// Convert to PascalCase
|
|
142
|
+
return normalizedName
|
|
143
|
+
.split('-')
|
|
144
|
+
.filter(part => part.length > 0) // Remove empty parts
|
|
145
|
+
.map(part => this._toPascalCase(part))
|
|
146
|
+
.join('');
|
|
147
|
+
}
|
|
148
|
+
/**
|
|
149
|
+
* Convert string to PascalCase
|
|
150
|
+
*/
|
|
151
|
+
_toPascalCase(str) {
|
|
152
|
+
return str.charAt(0).toUpperCase() + str.slice(1).toLowerCase();
|
|
153
|
+
}
|
|
154
|
+
/**
|
|
155
|
+
* Dynamically load a Vue component from file path with enhanced error handling
|
|
156
|
+
*/
|
|
157
|
+
async _loadComponent(filePath) {
|
|
158
|
+
try {
|
|
159
|
+
// Dynamic import of the component module
|
|
160
|
+
const componentModule = await import(filePath);
|
|
161
|
+
// Handle different export patterns
|
|
162
|
+
let component = componentModule.default || componentModule;
|
|
163
|
+
// Validate that it's a valid Vue component
|
|
164
|
+
if (!component || (typeof component !== 'object' && typeof component !== 'function')) {
|
|
165
|
+
throw ErrorHandler.createInvalidComponentError(this._generateComponentName(filePath, ''), filePath, 'Invalid component export - must be object or function');
|
|
166
|
+
}
|
|
167
|
+
// For Vue SFC, ensure we have the component definition
|
|
168
|
+
if (component.__vccOpts) {
|
|
169
|
+
component = component.__vccOpts;
|
|
170
|
+
}
|
|
171
|
+
return component;
|
|
172
|
+
}
|
|
173
|
+
catch (error) {
|
|
174
|
+
// If it's already a FrameworkError, re-throw it
|
|
175
|
+
if (error instanceof Error && error.name === 'FrameworkError') {
|
|
176
|
+
throw error;
|
|
177
|
+
}
|
|
178
|
+
// Otherwise, wrap in import error
|
|
179
|
+
throw ErrorHandler.createImportError(filePath, error);
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
/**
|
|
183
|
+
* Validate if an object is a valid Vue component
|
|
184
|
+
*/
|
|
185
|
+
_isValidVueComponent(component) {
|
|
186
|
+
if (!component)
|
|
187
|
+
return false;
|
|
188
|
+
// Check for function component
|
|
189
|
+
if (typeof component === 'function')
|
|
190
|
+
return true;
|
|
191
|
+
// Check for object component with render function or template
|
|
192
|
+
if (typeof component === 'object') {
|
|
193
|
+
return !!(component.render ||
|
|
194
|
+
component.template ||
|
|
195
|
+
component.setup ||
|
|
196
|
+
component.__vccOpts ||
|
|
197
|
+
component.components ||
|
|
198
|
+
component.data ||
|
|
199
|
+
component.methods);
|
|
200
|
+
}
|
|
201
|
+
return false;
|
|
202
|
+
}
|
|
203
|
+
}
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import { FrameworkError } from '@/types';
|
|
2
|
+
/**
|
|
3
|
+
* Error handler utility class for creating and managing framework errors
|
|
4
|
+
*/
|
|
5
|
+
export declare class ErrorHandler {
|
|
6
|
+
/**
|
|
7
|
+
* Create a component scan error
|
|
8
|
+
*/
|
|
9
|
+
static createScanError(message: string, filePath?: string, cause?: Error): FrameworkError;
|
|
10
|
+
/**
|
|
11
|
+
* Create a component registration error
|
|
12
|
+
*/
|
|
13
|
+
static createRegistrationError(message: string, componentName?: string, filePath?: string, cause?: Error): FrameworkError;
|
|
14
|
+
/**
|
|
15
|
+
* Create a layout initialization error
|
|
16
|
+
*/
|
|
17
|
+
static createLayoutError(message: string, context?: Record<string, any>, cause?: Error): FrameworkError;
|
|
18
|
+
/**
|
|
19
|
+
* Create an app creation error
|
|
20
|
+
*/
|
|
21
|
+
static createAppError(message: string, context?: Record<string, any>, cause?: Error): FrameworkError;
|
|
22
|
+
/**
|
|
23
|
+
* Create a file not found error
|
|
24
|
+
*/
|
|
25
|
+
static createFileNotFoundError(filePath: string, cause?: Error): FrameworkError;
|
|
26
|
+
/**
|
|
27
|
+
* Create an invalid component error
|
|
28
|
+
*/
|
|
29
|
+
static createInvalidComponentError(componentName: string, filePath: string, reason?: string, cause?: Error): FrameworkError;
|
|
30
|
+
/**
|
|
31
|
+
* Create a name conflict error
|
|
32
|
+
*/
|
|
33
|
+
static createNameConflictError(componentName: string, existingPath: string, newPath: string): FrameworkError;
|
|
34
|
+
/**
|
|
35
|
+
* Create an import error
|
|
36
|
+
*/
|
|
37
|
+
static createImportError(filePath: string, cause?: Error): FrameworkError;
|
|
38
|
+
/**
|
|
39
|
+
* Log error with appropriate level based on error type
|
|
40
|
+
*/
|
|
41
|
+
static logError(error: FrameworkError, logger?: Console): void;
|
|
42
|
+
/**
|
|
43
|
+
* Check if an error is recoverable (app can continue running)
|
|
44
|
+
*/
|
|
45
|
+
static isRecoverable(error: FrameworkError): boolean;
|
|
46
|
+
/**
|
|
47
|
+
* Collect and format multiple errors for reporting
|
|
48
|
+
*/
|
|
49
|
+
static formatErrorSummary(errors: FrameworkError[]): string;
|
|
50
|
+
}
|
|
51
|
+
//# sourceMappingURL=ErrorHandler.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ErrorHandler.d.ts","sourceRoot":"","sources":["../../src/core/ErrorHandler.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAyC,MAAM,SAAS,CAAA;AAE/E;;GAEG;AACH,qBAAa,YAAY;IACvB;;OAEG;IACH,MAAM,CAAC,eAAe,CAAC,OAAO,EAAE,MAAM,EAAE,QAAQ,CAAC,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,KAAK,GAAG,cAAc;IASzF;;OAEG;IACH,MAAM,CAAC,uBAAuB,CAC5B,OAAO,EAAE,MAAM,EACf,aAAa,CAAC,EAAE,MAAM,EACtB,QAAQ,CAAC,EAAE,MAAM,EACjB,KAAK,CAAC,EAAE,KAAK,GACZ,cAAc;IASjB;;OAEG;IACH,MAAM,CAAC,iBAAiB,CAAC,OAAO,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,EAAE,KAAK,CAAC,EAAE,KAAK,GAAG,cAAc;IASvG;;OAEG;IACH,MAAM,CAAC,cAAc,CAAC,OAAO,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,EAAE,KAAK,CAAC,EAAE,KAAK,GAAG,cAAc;IASpG;;OAEG;IACH,MAAM,CAAC,uBAAuB,CAAC,QAAQ,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,KAAK,GAAG,cAAc;IAS/E;;OAEG;IACH,MAAM,CAAC,2BAA2B,CAChC,aAAa,EAAE,MAAM,EACrB,QAAQ,EAAE,MAAM,EAChB,MAAM,CAAC,EAAE,MAAM,EACf,KAAK,CAAC,EAAE,KAAK,GACZ,cAAc;IAajB;;OAEG;IACH,MAAM,CAAC,uBAAuB,CAC5B,aAAa,EAAE,MAAM,EACrB,YAAY,EAAE,MAAM,EACpB,OAAO,EAAE,MAAM,GACd,cAAc;IAcjB;;OAEG;IACH,MAAM,CAAC,iBAAiB,CAAC,QAAQ,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,KAAK,GAAG,cAAc;IASzE;;OAEG;IACH,MAAM,CAAC,QAAQ,CAAC,KAAK,EAAE,cAAc,EAAE,MAAM,GAAE,OAAiB,GAAG,IAAI;IAuBvE;;OAEG;IACH,MAAM,CAAC,aAAa,CAAC,KAAK,EAAE,cAAc,GAAG,OAAO;IAapD;;OAEG;IACH,MAAM,CAAC,kBAAkB,CAAC,MAAM,EAAE,cAAc,EAAE,GAAG,MAAM;CAuB5D"}
|