slicejs-cli 2.8.6 ā 2.9.1
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 +347 -315
- package/client.js +526 -539
- package/commands/Print.js +167 -167
- package/commands/Validations.js +103 -103
- package/commands/build/build.js +40 -0
- package/commands/buildProduction/buildProduction.js +45 -10
- package/commands/bundle/bundle.js +235 -231
- package/commands/createComponent/VisualComponentTemplate.js +55 -55
- package/commands/createComponent/createComponent.js +126 -126
- package/commands/deleteComponent/deleteComponent.js +77 -77
- package/commands/doctor/doctor.js +369 -369
- package/commands/getComponent/getComponent.js +747 -747
- package/commands/init/init.js +261 -261
- package/commands/listComponents/listComponents.js +175 -175
- package/commands/startServer/startServer.js +260 -270
- package/commands/startServer/watchServer.js +79 -79
- package/commands/utils/PathHelper.js +68 -68
- package/commands/utils/VersionChecker.js +167 -167
- package/commands/utils/bundling/BundleGenerator.js +1331 -783
- package/commands/utils/bundling/DependencyAnalyzer.js +859 -679
- package/commands/utils/updateManager.js +437 -384
- package/package.json +46 -46
- package/post.js +25 -25
- package/refactor.md +271 -271
- package/tests/bundle-generator.test.js +38 -0
- package/tests/dependency-analyzer.test.js +24 -0
|
@@ -1,231 +1,235 @@
|
|
|
1
|
-
// cli/commands/bundle/bundle.js
|
|
2
|
-
import path from 'path';
|
|
3
|
-
import fs from 'fs-extra';
|
|
4
|
-
import DependencyAnalyzer from '../utils/bundling/DependencyAnalyzer.js';
|
|
5
|
-
import BundleGenerator from '../utils/bundling/BundleGenerator.js';
|
|
6
|
-
import Print from '..//Print.js';
|
|
7
|
-
|
|
8
|
-
/**
|
|
9
|
-
* Main bundling command
|
|
10
|
-
*/
|
|
11
|
-
export default async function bundle(options = {}) {
|
|
12
|
-
const startTime = Date.now();
|
|
13
|
-
const projectRoot = process.cwd();
|
|
14
|
-
|
|
15
|
-
try {
|
|
16
|
-
Print.title('š¦ Slice.js Bundle Generator');
|
|
17
|
-
Print.newLine();
|
|
18
|
-
|
|
19
|
-
// Validate that it's a Slice.js project
|
|
20
|
-
await validateProject(projectRoot);
|
|
21
|
-
|
|
22
|
-
// Create default bundle config if needed
|
|
23
|
-
const bundleGenerator = new BundleGenerator(import.meta.url, null);
|
|
24
|
-
await bundleGenerator.createDefaultBundleConfig();
|
|
25
|
-
|
|
26
|
-
// Phase 1: Analysis
|
|
27
|
-
Print.buildProgress('Analyzing project...');
|
|
28
|
-
const analyzer = new DependencyAnalyzer(import.meta.url);
|
|
29
|
-
const analysisData = await analyzer.analyze();
|
|
30
|
-
|
|
31
|
-
// Show report if in verbose mode
|
|
32
|
-
if (options.verbose || options.analyze) {
|
|
33
|
-
Print.newLine();
|
|
34
|
-
analyzer.generateReport(analysisData.metrics);
|
|
35
|
-
Print.newLine();
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
// If only analysis is requested, finish here
|
|
39
|
-
if (options.analyze) {
|
|
40
|
-
Print.success('Analysis completed');
|
|
41
|
-
return;
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
// Phase 2: Bundle generation
|
|
45
|
-
Print.buildProgress('Generating bundles...');
|
|
46
|
-
const generator = new BundleGenerator(import.meta.url, analysisData
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
await generator.
|
|
52
|
-
|
|
53
|
-
// Phase
|
|
54
|
-
Print.
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
//
|
|
58
|
-
Print.newLine();
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
console.
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
console.log(`
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
Print.
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
}
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
Print.
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
console.log(`
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
}
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
}
|
|
1
|
+
// cli/commands/bundle/bundle.js
|
|
2
|
+
import path from 'path';
|
|
3
|
+
import fs from 'fs-extra';
|
|
4
|
+
import DependencyAnalyzer from '../utils/bundling/DependencyAnalyzer.js';
|
|
5
|
+
import BundleGenerator from '../utils/bundling/BundleGenerator.js';
|
|
6
|
+
import Print from '..//Print.js';
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Main bundling command
|
|
10
|
+
*/
|
|
11
|
+
export default async function bundle(options = {}) {
|
|
12
|
+
const startTime = Date.now();
|
|
13
|
+
const projectRoot = process.cwd();
|
|
14
|
+
|
|
15
|
+
try {
|
|
16
|
+
Print.title('š¦ Slice.js Bundle Generator');
|
|
17
|
+
Print.newLine();
|
|
18
|
+
|
|
19
|
+
// Validate that it's a Slice.js project
|
|
20
|
+
await validateProject(projectRoot);
|
|
21
|
+
|
|
22
|
+
// Create default bundle config if needed
|
|
23
|
+
const bundleGenerator = new BundleGenerator(import.meta.url, null);
|
|
24
|
+
await bundleGenerator.createDefaultBundleConfig();
|
|
25
|
+
|
|
26
|
+
// Phase 1: Analysis
|
|
27
|
+
Print.buildProgress('Analyzing project...');
|
|
28
|
+
const analyzer = new DependencyAnalyzer(import.meta.url);
|
|
29
|
+
const analysisData = await analyzer.analyze();
|
|
30
|
+
|
|
31
|
+
// Show report if in verbose mode
|
|
32
|
+
if (options.verbose || options.analyze) {
|
|
33
|
+
Print.newLine();
|
|
34
|
+
analyzer.generateReport(analysisData.metrics);
|
|
35
|
+
Print.newLine();
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
// If only analysis is requested, finish here
|
|
39
|
+
if (options.analyze) {
|
|
40
|
+
Print.success('Analysis completed');
|
|
41
|
+
return;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
// Phase 2: Bundle generation
|
|
45
|
+
Print.buildProgress('Generating bundles...');
|
|
46
|
+
const generator = new BundleGenerator(import.meta.url, analysisData, {
|
|
47
|
+
minify: !!options.minify,
|
|
48
|
+
obfuscate: !!options.obfuscate,
|
|
49
|
+
output: options.output || 'src'
|
|
50
|
+
});
|
|
51
|
+
const result = await generator.generate();
|
|
52
|
+
|
|
53
|
+
// Phase 3: Save configuration
|
|
54
|
+
Print.buildProgress('Saving configuration...');
|
|
55
|
+
await generator.saveBundleConfig(result.config);
|
|
56
|
+
|
|
57
|
+
// Phase 4: Summary
|
|
58
|
+
Print.newLine();
|
|
59
|
+
printSummary(result, startTime);
|
|
60
|
+
|
|
61
|
+
// Show next step
|
|
62
|
+
Print.newLine();
|
|
63
|
+
Print.info('š” Next step:');
|
|
64
|
+
console.log(' Update your Slice.js to use the generated bundles');
|
|
65
|
+
console.log(' Bundles will load automatically in production\n');
|
|
66
|
+
|
|
67
|
+
} catch (error) {
|
|
68
|
+
Print.error('Error generating bundles:', error.message);
|
|
69
|
+
console.error('\nš Complete stack trace:');
|
|
70
|
+
console.error(error.stack);
|
|
71
|
+
|
|
72
|
+
if (error.code) {
|
|
73
|
+
console.error('\nš Error code:', error.code);
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
process.exit(1);
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
/**
|
|
81
|
+
* Validates that the project has the correct structure
|
|
82
|
+
*/
|
|
83
|
+
async function validateProject(projectRoot) {
|
|
84
|
+
const requiredPaths = [
|
|
85
|
+
'src/Components/components.js',
|
|
86
|
+
'src/routes.js'
|
|
87
|
+
];
|
|
88
|
+
|
|
89
|
+
for (const reqPath of requiredPaths) {
|
|
90
|
+
const fullPath = path.join(projectRoot, reqPath);
|
|
91
|
+
if (!await fs.pathExists(fullPath)) {
|
|
92
|
+
throw new Error(`Required file not found: ${reqPath}`);
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
/**
|
|
98
|
+
* Prints summary of generated bundles
|
|
99
|
+
*/
|
|
100
|
+
function printSummary(result, startTime) {
|
|
101
|
+
const { bundles, config, files } = result;
|
|
102
|
+
const duration = ((Date.now() - startTime) / 1000).toFixed(2);
|
|
103
|
+
|
|
104
|
+
Print.success(`Bundles generated in ${duration}s\n`);
|
|
105
|
+
|
|
106
|
+
// Critical bundle
|
|
107
|
+
if (bundles.critical.components.length > 0) {
|
|
108
|
+
Print.info('š¦ Critical Bundle:');
|
|
109
|
+
console.log(` Components: ${bundles.critical.components.length}`);
|
|
110
|
+
console.log(` Size: ${(bundles.critical.size / 1024).toFixed(1)} KB`);
|
|
111
|
+
console.log(` File: ${bundles.critical.file}\n`);
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
// Route bundles
|
|
115
|
+
const routeCount = Object.keys(bundles.routes).length;
|
|
116
|
+
if (routeCount > 0) {
|
|
117
|
+
Print.info(`š¦ Route Bundles (${routeCount}):`);
|
|
118
|
+
|
|
119
|
+
for (const [key, bundle] of Object.entries(bundles.routes)) {
|
|
120
|
+
console.log(` ${key}:`);
|
|
121
|
+
const routeDisplay = Array.isArray(bundle.path) ? `${bundle.path.length} routes` : (bundle.path || key);
|
|
122
|
+
console.log(` Route: ${routeDisplay}`);
|
|
123
|
+
console.log(` Components: ${bundle.components.length}`);
|
|
124
|
+
console.log(` Size: ${(bundle.size / 1024).toFixed(1)} KB`);
|
|
125
|
+
console.log(` File: ${bundle.file}`);
|
|
126
|
+
}
|
|
127
|
+
Print.newLine();
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
// Global statistics
|
|
131
|
+
Print.info('š Statistics:');
|
|
132
|
+
console.log(` Strategy: ${config.strategy}`);
|
|
133
|
+
console.log(` Total components: ${config.stats.totalComponents}`);
|
|
134
|
+
console.log(` Shared components: ${config.stats.sharedComponents}`);
|
|
135
|
+
console.log(` Generated files: ${files.length}`);
|
|
136
|
+
|
|
137
|
+
// Calculate estimated improvement
|
|
138
|
+
const beforeRequests = config.stats.totalComponents;
|
|
139
|
+
const afterRequests = files.length;
|
|
140
|
+
const improvement = Math.round((1 - afterRequests / beforeRequests) * 100);
|
|
141
|
+
|
|
142
|
+
console.log(` Request reduction: ${improvement}% (${beforeRequests} ā ${afterRequests})`);
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
/**
|
|
146
|
+
* Subcommand: Clean bundles
|
|
147
|
+
*/
|
|
148
|
+
export async function cleanBundles() {
|
|
149
|
+
const projectRoot = process.cwd();
|
|
150
|
+
const srcPath = path.join(projectRoot, 'src');
|
|
151
|
+
|
|
152
|
+
try {
|
|
153
|
+
Print.title('š§¹ Cleaning bundles...');
|
|
154
|
+
|
|
155
|
+
const files = await fs.readdir(srcPath);
|
|
156
|
+
const bundleFiles = files.filter(f => f.startsWith('slice-bundle.'));
|
|
157
|
+
|
|
158
|
+
if (bundleFiles.length === 0) {
|
|
159
|
+
Print.warning('No bundles found to clean');
|
|
160
|
+
return;
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
for (const file of bundleFiles) {
|
|
164
|
+
await fs.remove(path.join(srcPath, file));
|
|
165
|
+
console.log(` ā Deleted: ${file}`);
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
// Remove config
|
|
169
|
+
const configPath = path.join(srcPath, 'bundle.config.json');
|
|
170
|
+
if (await fs.pathExists(configPath)) {
|
|
171
|
+
await fs.remove(configPath);
|
|
172
|
+
console.log(` ā Deleted: bundle.config.json`);
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
Print.newLine();
|
|
176
|
+
Print.success(`${bundleFiles.length} files deleted`);
|
|
177
|
+
|
|
178
|
+
} catch (error) {
|
|
179
|
+
Print.error('Error cleaning bundles:', error.message);
|
|
180
|
+
process.exit(1);
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
/**
|
|
185
|
+
* Subcommand: Bundle information
|
|
186
|
+
*/
|
|
187
|
+
export async function bundleInfo() {
|
|
188
|
+
const projectRoot = process.cwd();
|
|
189
|
+
const configPath = path.join(projectRoot, 'src/bundle.config.json');
|
|
190
|
+
|
|
191
|
+
try {
|
|
192
|
+
if (!await fs.pathExists(configPath)) {
|
|
193
|
+
Print.warning('Bundle configuration not found');
|
|
194
|
+
Print.info('Run "slice bundle" to generate bundles');
|
|
195
|
+
return;
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
const config = await fs.readJson(configPath);
|
|
199
|
+
|
|
200
|
+
Print.title('š¦ Bundle Information');
|
|
201
|
+
Print.newLine();
|
|
202
|
+
|
|
203
|
+
Print.info('Configuration:');
|
|
204
|
+
console.log(` Version: ${config.version}`);
|
|
205
|
+
console.log(` Strategy: ${config.strategy}`);
|
|
206
|
+
console.log(` Generated: ${new Date(config.generated).toLocaleString()}`);
|
|
207
|
+
Print.newLine();
|
|
208
|
+
|
|
209
|
+
Print.info('Statistics:');
|
|
210
|
+
console.log(` Total components: ${config.stats.totalComponents}`);
|
|
211
|
+
console.log(` Total routes: ${config.stats.totalRoutes}`);
|
|
212
|
+
console.log(` Shared components: ${config.stats.sharedComponents} (${config.stats.sharedPercentage}%)`);
|
|
213
|
+
console.log(` Total size: ${(config.stats.totalSize / 1024).toFixed(1)} KB`);
|
|
214
|
+
Print.newLine();
|
|
215
|
+
|
|
216
|
+
Print.info('Bundles:');
|
|
217
|
+
|
|
218
|
+
// Critical
|
|
219
|
+
if (config.bundles.critical) {
|
|
220
|
+
console.log(` Critical: ${config.bundles.critical.components.length} components, ${(config.bundles.critical.size / 1024).toFixed(1)} KB`);
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
// Routes
|
|
224
|
+
const routeCount = Object.keys(config.bundles.routes).length;
|
|
225
|
+
console.log(` Routes: ${routeCount} bundles`);
|
|
226
|
+
|
|
227
|
+
for (const [key, bundle] of Object.entries(config.bundles.routes)) {
|
|
228
|
+
console.log(` ${key}: ${bundle.components.length} components, ${(bundle.size / 1024).toFixed(1)} KB`);
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
} catch (error) {
|
|
232
|
+
Print.error('Error reading information:', error.message);
|
|
233
|
+
process.exit(1);
|
|
234
|
+
}
|
|
235
|
+
}
|
|
@@ -1,55 +1,55 @@
|
|
|
1
|
-
export default class componentTemplates{
|
|
2
|
-
|
|
3
|
-
static visual(componentName){
|
|
4
|
-
return `export default class ${componentName} extends HTMLElement {
|
|
5
|
-
|
|
6
|
-
static props = {
|
|
7
|
-
// Define your component props here
|
|
8
|
-
// Example:
|
|
9
|
-
/*
|
|
10
|
-
"value": {
|
|
11
|
-
type: 'string',
|
|
12
|
-
default: 'Button',
|
|
13
|
-
required: false
|
|
14
|
-
},
|
|
15
|
-
*/
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
constructor(props) {
|
|
19
|
-
super();
|
|
20
|
-
slice.attachTemplate(this);
|
|
21
|
-
slice.controller.setComponentProps(this, props);
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
init() {
|
|
25
|
-
// Component initialization logic (can be async)
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
update() {
|
|
29
|
-
// Component update logic (can be async)
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
// Add your custom methods here
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
customElements.define("slice-${componentName.toLowerCase()}", ${componentName});
|
|
36
|
-
`;
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
static service(componentName) {
|
|
40
|
-
return `export default class ${componentName} {
|
|
41
|
-
constructor(props) {
|
|
42
|
-
// Initialize service with props
|
|
43
|
-
this.props = props || {};
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
init() {
|
|
47
|
-
// Service initialization logic (can be async)
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
// Add your service methods here
|
|
51
|
-
}
|
|
52
|
-
`;
|
|
53
|
-
}
|
|
54
|
-
}
|
|
55
|
-
|
|
1
|
+
export default class componentTemplates{
|
|
2
|
+
|
|
3
|
+
static visual(componentName){
|
|
4
|
+
return `export default class ${componentName} extends HTMLElement {
|
|
5
|
+
|
|
6
|
+
static props = {
|
|
7
|
+
// Define your component props here
|
|
8
|
+
// Example:
|
|
9
|
+
/*
|
|
10
|
+
"value": {
|
|
11
|
+
type: 'string',
|
|
12
|
+
default: 'Button',
|
|
13
|
+
required: false
|
|
14
|
+
},
|
|
15
|
+
*/
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
constructor(props) {
|
|
19
|
+
super();
|
|
20
|
+
slice.attachTemplate(this);
|
|
21
|
+
slice.controller.setComponentProps(this, props);
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
init() {
|
|
25
|
+
// Component initialization logic (can be async)
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
update() {
|
|
29
|
+
// Component update logic (can be async)
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
// Add your custom methods here
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
customElements.define("slice-${componentName.toLowerCase()}", ${componentName});
|
|
36
|
+
`;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
static service(componentName) {
|
|
40
|
+
return `export default class ${componentName} {
|
|
41
|
+
constructor(props) {
|
|
42
|
+
// Initialize service with props
|
|
43
|
+
this.props = props || {};
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
init() {
|
|
47
|
+
// Service initialization logic (can be async)
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
// Add your service methods here
|
|
51
|
+
}
|
|
52
|
+
`;
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
|