slicejs-web-framework 3.4.3 → 3.4.4
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/.opencode/opencode.json +14 -14
- package/Slice/Components/Structural/ContextManager/ContextManager.js +422 -422
- package/Slice/Components/Structural/ContextManager/ContextManagerDebugger.js +468 -468
- package/Slice/Components/Structural/Controller/Controller.js +1271 -1198
- package/Slice/Components/Structural/Debugger/Debugger.js +1503 -1503
- package/Slice/Components/Structural/EventManager/EventManager.js +366 -366
- package/Slice/Components/Structural/Logger/Logger.js +225 -225
- package/Slice/Components/Structural/Router/Router.js +775 -775
- package/Slice/Components/Structural/StylesManager/StylesManager.js +82 -82
- package/Slice/Components/Structural/StylesManager/ThemeManager/ThemeManager.js +106 -106
- package/Slice/Slice.js +650 -627
- package/Slice/tests/build-singleton.test.js +244 -244
- package/Slice/tests/bundle-v2-runtime-contract.test.js +738 -738
- package/Slice/tests/context-patch-use.test.js +95 -95
- package/Slice/tests/fixtures/real-runtime-loader.mjs +21 -21
- package/Slice/tests/public-env-typed-accessors.test.js +66 -66
- package/api/index.js +281 -281
- package/api/utils/publicEnvResolver.js +123 -123
- package/package.json +44 -44
- package/slicejs-cli/.github/workflows/ci.yml +43 -0
- package/slicejs-cli/LICENSE +21 -0
- package/slicejs-cli/assets/Slice.js-logo.png +0 -0
- package/slicejs-cli/client.js +740 -0
- package/slicejs-cli/commands/Print.js +164 -0
- package/slicejs-cli/commands/Validations.js +92 -0
- package/slicejs-cli/commands/build/build.js +40 -0
- package/slicejs-cli/commands/buildProduction/buildProduction.js +577 -0
- package/slicejs-cli/commands/bundle/bundle.js +236 -0
- package/slicejs-cli/commands/createComponent/VisualComponentTemplate.js +55 -0
- package/slicejs-cli/commands/createComponent/createComponent.js +128 -0
- package/slicejs-cli/commands/deleteComponent/deleteComponent.js +81 -0
- package/slicejs-cli/commands/doctor/doctor.js +516 -0
- package/slicejs-cli/commands/getComponent/getComponent.js +695 -0
- package/slicejs-cli/commands/init/init.js +473 -0
- package/slicejs-cli/commands/listComponents/listComponents.js +172 -0
- package/slicejs-cli/commands/startServer/startServer.js +240 -0
- package/slicejs-cli/commands/startServer/watchServer.js +66 -0
- package/slicejs-cli/commands/types/types.js +583 -0
- package/slicejs-cli/commands/utils/LocalCliDelegation.js +53 -0
- package/slicejs-cli/commands/utils/PackageManager.js +148 -0
- package/slicejs-cli/commands/utils/PathHelper.js +75 -0
- package/slicejs-cli/commands/utils/VersionChecker.js +169 -0
- package/slicejs-cli/commands/utils/bundling/BundleGenerator.js +2525 -0
- package/slicejs-cli/commands/utils/bundling/DependencyAnalyzer.js +933 -0
- package/slicejs-cli/commands/utils/loadConfig.js +31 -0
- package/slicejs-cli/commands/utils/sliceScripts.js +48 -0
- package/slicejs-cli/commands/utils/updateManager.js +471 -0
- package/slicejs-cli/package.json +71 -0
- package/slicejs-cli/playwright.config.js +51 -0
- package/slicejs-cli/pnpm-lock.yaml +1629 -0
- package/slicejs-cli/tests/build-command-integration.test.js +87 -0
- package/slicejs-cli/tests/build-mangling-e2e.test.js +290 -0
- package/slicejs-cli/tests/build-production-e2e.test.js +140 -0
- package/slicejs-cli/tests/builder-edge-cases.test.js +322 -0
- package/slicejs-cli/tests/bundle-generate-e2e.test.js +115 -0
- package/slicejs-cli/tests/bundle-generator.test.js +691 -0
- package/slicejs-cli/tests/bundle-v2-register-output.test.js +470 -0
- package/slicejs-cli/tests/bundling-dependency-edges.test.js +127 -0
- package/slicejs-cli/tests/bundling-imports-unit.test.js +267 -0
- package/slicejs-cli/tests/client-launcher-contract.test.js +211 -0
- package/slicejs-cli/tests/commands-component-crud.test.js +128 -0
- package/slicejs-cli/tests/commands-doctor.test.js +152 -0
- package/slicejs-cli/tests/commands-version-checker.test.js +37 -0
- package/slicejs-cli/tests/component-registry-parse.test.js +34 -0
- package/slicejs-cli/tests/dependency-analyzer.test.js +24 -0
- package/slicejs-cli/tests/e2e/bundles.spec.js +91 -0
- package/slicejs-cli/tests/e2e/dependency-scenarios.spec.js +56 -0
- package/slicejs-cli/tests/e2e/fixtures/components/Service/FetchManager/FetchManager.js +136 -0
- package/slicejs-cli/tests/e2e/fixtures/components/Service/IndexedDbManager/IndexedDbManager.js +149 -0
- package/slicejs-cli/tests/e2e/fixtures/components/Service/LocalStorageManager/LocalStorageManager.js +45 -0
- package/slicejs-cli/tests/e2e/fixtures/components/Visual/Button/Button.css +106 -0
- package/slicejs-cli/tests/e2e/fixtures/components/Visual/Button/Button.html +5 -0
- package/slicejs-cli/tests/e2e/fixtures/components/Visual/Button/Button.js +158 -0
- package/slicejs-cli/tests/e2e/fixtures/components/Visual/Link/Link.js +33 -0
- package/slicejs-cli/tests/e2e/fixtures/components/Visual/Loading/Loading.css +56 -0
- package/slicejs-cli/tests/e2e/fixtures/components/Visual/Loading/Loading.html +83 -0
- package/slicejs-cli/tests/e2e/fixtures/components/Visual/Loading/Loading.js +164 -0
- package/slicejs-cli/tests/e2e/fixtures/components/Visual/MultiRoute/MultiRoute.js +167 -0
- package/slicejs-cli/tests/e2e/fixtures/components/Visual/Navbar/Navbar.css +116 -0
- package/slicejs-cli/tests/e2e/fixtures/components/Visual/Navbar/Navbar.html +44 -0
- package/slicejs-cli/tests/e2e/fixtures/components/Visual/Navbar/Navbar.js +180 -0
- package/slicejs-cli/tests/e2e/fixtures/components/Visual/NotFound/NotFound.js +20 -0
- package/slicejs-cli/tests/e2e/fixtures/components/Visual/Route/Route.js +181 -0
- package/slicejs-cli/tests/e2e/fixtures/components/registry.json +12 -0
- package/slicejs-cli/tests/e2e/fixtures/vendor-components.mjs +65 -0
- package/slicejs-cli/tests/e2e/navigation.spec.js +44 -0
- package/slicejs-cli/tests/e2e/render.spec.js +34 -0
- package/slicejs-cli/tests/e2e/serve.mjs +264 -0
- package/slicejs-cli/tests/e2e/shared-deps.spec.js +61 -0
- package/slicejs-cli/tests/e2e/unminified.spec.js +33 -0
- package/slicejs-cli/tests/e2e-serve.test.js +148 -0
- package/slicejs-cli/tests/fixtures/components.js +8 -0
- package/slicejs-cli/tests/fixtures/sliceConfig.json +74 -0
- package/slicejs-cli/tests/getcomponent.test.js +435 -0
- package/slicejs-cli/tests/helpers/setup.js +102 -0
- package/slicejs-cli/tests/init-command-contract.test.js +101 -0
- package/slicejs-cli/tests/init-e2e.test.js +82 -0
- package/slicejs-cli/tests/init-project-isolation.test.js +98 -0
- package/slicejs-cli/tests/local-cli-delegation.test.js +81 -0
- package/slicejs-cli/tests/package-manager.test.js +127 -0
- package/slicejs-cli/tests/path-helper.test.js +206 -0
- package/slicejs-cli/tests/perf-budget.test.js +84 -0
- package/slicejs-cli/tests/postinstall-command.test.js +103 -0
- package/slicejs-cli/tests/property-mangling-regression.test.js +182 -0
- package/slicejs-cli/tests/types-breakage.test.js +491 -0
- package/slicejs-cli/tests/types-generator-errors.test.js +361 -0
- package/slicejs-cli/tests/types-generator.test.js +348 -0
|
@@ -0,0 +1,236 @@
|
|
|
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
|
+
import { getProjectRoot, getSrcPath } from '../utils/PathHelper.js';
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Main bundling command
|
|
11
|
+
*/
|
|
12
|
+
export default async function bundle(options = {}) {
|
|
13
|
+
const startTime = Date.now();
|
|
14
|
+
const projectRoot = getProjectRoot(import.meta.url);
|
|
15
|
+
|
|
16
|
+
try {
|
|
17
|
+
Print.title('📦 Slice.js Bundle Generator');
|
|
18
|
+
Print.newLine();
|
|
19
|
+
|
|
20
|
+
// Validate that it's a Slice.js project
|
|
21
|
+
await validateProject(projectRoot);
|
|
22
|
+
|
|
23
|
+
// Create default bundle config if needed
|
|
24
|
+
const bundleGenerator = new BundleGenerator(import.meta.url, null);
|
|
25
|
+
await bundleGenerator.createDefaultBundleConfig();
|
|
26
|
+
|
|
27
|
+
// Phase 1: Analysis
|
|
28
|
+
Print.buildProgress('Analyzing project...');
|
|
29
|
+
const analyzer = new DependencyAnalyzer(import.meta.url);
|
|
30
|
+
const analysisData = await analyzer.analyze();
|
|
31
|
+
|
|
32
|
+
// Show report if in verbose mode
|
|
33
|
+
if (options.verbose || options.analyze) {
|
|
34
|
+
Print.newLine();
|
|
35
|
+
analyzer.generateReport(analysisData.metrics);
|
|
36
|
+
Print.newLine();
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
// If only analysis is requested, finish here
|
|
40
|
+
if (options.analyze) {
|
|
41
|
+
Print.success('Analysis completed');
|
|
42
|
+
return;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
// Phase 2: Bundle generation
|
|
46
|
+
Print.buildProgress('Generating bundles...');
|
|
47
|
+
const generator = new BundleGenerator(import.meta.url, analysisData, {
|
|
48
|
+
minify: !!options.minify,
|
|
49
|
+
obfuscate: !!options.obfuscate,
|
|
50
|
+
output: options.output || 'src'
|
|
51
|
+
});
|
|
52
|
+
const result = await generator.generate();
|
|
53
|
+
|
|
54
|
+
// Phase 3: Save configuration
|
|
55
|
+
Print.buildProgress('Saving configuration...');
|
|
56
|
+
await generator.saveBundleConfig(result.config);
|
|
57
|
+
|
|
58
|
+
// Phase 4: Summary
|
|
59
|
+
Print.newLine();
|
|
60
|
+
printSummary(result, startTime);
|
|
61
|
+
|
|
62
|
+
// Show next step
|
|
63
|
+
Print.newLine();
|
|
64
|
+
Print.info('💡 Next step:');
|
|
65
|
+
console.log(' Update your Slice.js to use the generated bundles');
|
|
66
|
+
console.log(' Bundles will load automatically in production\n');
|
|
67
|
+
|
|
68
|
+
} catch (error) {
|
|
69
|
+
Print.error('Error generating bundles:', error.message);
|
|
70
|
+
console.error('\n📍 Complete stack trace:');
|
|
71
|
+
console.error(error.stack);
|
|
72
|
+
|
|
73
|
+
if (error.code) {
|
|
74
|
+
console.error('\n📍 Error code:', error.code);
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
throw error;
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
/**
|
|
82
|
+
* Validates that the project has the correct structure
|
|
83
|
+
*/
|
|
84
|
+
async function validateProject(projectRoot) {
|
|
85
|
+
const requiredPaths = [
|
|
86
|
+
getSrcPath(import.meta.url, 'Components', 'components.js'),
|
|
87
|
+
getSrcPath(import.meta.url, 'routes.js')
|
|
88
|
+
];
|
|
89
|
+
|
|
90
|
+
for (const fullPath of requiredPaths) {
|
|
91
|
+
if (!await fs.pathExists(fullPath)) {
|
|
92
|
+
const relativePath = path.relative(projectRoot, fullPath);
|
|
93
|
+
throw new Error(`Required file not found: ${relativePath}`);
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
/**
|
|
99
|
+
* Prints summary of generated bundles
|
|
100
|
+
*/
|
|
101
|
+
function printSummary(result, startTime) {
|
|
102
|
+
const { bundles, config, files } = result;
|
|
103
|
+
const duration = ((Date.now() - startTime) / 1000).toFixed(2);
|
|
104
|
+
|
|
105
|
+
Print.success(`Bundles generated in ${duration}s\n`);
|
|
106
|
+
|
|
107
|
+
// Critical bundle
|
|
108
|
+
if (bundles.critical.components.length > 0) {
|
|
109
|
+
Print.info('📦 Critical Bundle:');
|
|
110
|
+
console.log(` Components: ${bundles.critical.components.length}`);
|
|
111
|
+
console.log(` Size: ${(bundles.critical.size / 1024).toFixed(1)} KB`);
|
|
112
|
+
console.log(` File: ${bundles.critical.file}\n`);
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
// Route bundles
|
|
116
|
+
const routeCount = Object.keys(bundles.routes).length;
|
|
117
|
+
if (routeCount > 0) {
|
|
118
|
+
Print.info(`📦 Route Bundles (${routeCount}):`);
|
|
119
|
+
|
|
120
|
+
for (const [key, bundle] of Object.entries(bundles.routes)) {
|
|
121
|
+
console.log(` ${key}:`);
|
|
122
|
+
const routeDisplay = Array.isArray(bundle.path) ? `${bundle.path.length} routes` : (bundle.path || key);
|
|
123
|
+
console.log(` Route: ${routeDisplay}`);
|
|
124
|
+
console.log(` Components: ${bundle.components.length}`);
|
|
125
|
+
console.log(` Size: ${(bundle.size / 1024).toFixed(1)} KB`);
|
|
126
|
+
console.log(` File: ${bundle.file}`);
|
|
127
|
+
}
|
|
128
|
+
Print.newLine();
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
// Global statistics
|
|
132
|
+
Print.info('📊 Statistics:');
|
|
133
|
+
console.log(` Strategy: ${config.strategy}`);
|
|
134
|
+
console.log(` Total components: ${config.stats.totalComponents}`);
|
|
135
|
+
console.log(` Shared components: ${config.stats.sharedComponents}`);
|
|
136
|
+
console.log(` Generated files: ${files.length}`);
|
|
137
|
+
|
|
138
|
+
// Calculate estimated improvement
|
|
139
|
+
const beforeRequests = config.stats.totalComponents;
|
|
140
|
+
const afterRequests = files.length;
|
|
141
|
+
const improvement = Math.round((1 - afterRequests / beforeRequests) * 100);
|
|
142
|
+
|
|
143
|
+
console.log(` Request reduction: ${improvement}% (${beforeRequests} → ${afterRequests})`);
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
/**
|
|
147
|
+
* Subcommand: Clean bundles
|
|
148
|
+
*/
|
|
149
|
+
export async function cleanBundles() {
|
|
150
|
+
const srcPath = getSrcPath(import.meta.url);
|
|
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 = getSrcPath(import.meta.url, '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
|
+
console.error(error.stack);
|
|
181
|
+
throw error;
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
/**
|
|
186
|
+
* Subcommand: Bundle information
|
|
187
|
+
*/
|
|
188
|
+
export async function bundleInfo() {
|
|
189
|
+
const configPath = getSrcPath(import.meta.url, '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
|
+
console.error(error.stack);
|
|
234
|
+
throw error;
|
|
235
|
+
}
|
|
236
|
+
}
|
|
@@ -0,0 +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
|
+
|
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
|
|
2
|
+
import componentTemplates from './VisualComponentTemplate.js';
|
|
3
|
+
import fs from 'fs-extra';
|
|
4
|
+
import path from 'path';
|
|
5
|
+
import Validations from '../Validations.js';
|
|
6
|
+
import Print from '../Print.js';
|
|
7
|
+
import { getSrcPath } from '../utils/PathHelper.js';
|
|
8
|
+
|
|
9
|
+
function createComponent(componentName, category) {
|
|
10
|
+
// Validation: Component name is required
|
|
11
|
+
if (!componentName) {
|
|
12
|
+
Print.error('Component name is required');
|
|
13
|
+
Print.commandExample("Create a component", "slice component create");
|
|
14
|
+
return false;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
// Validation: Valid component name
|
|
18
|
+
if (!Validations.isValidComponentName(componentName)) {
|
|
19
|
+
Print.error(`Invalid component name: '${componentName}'`);
|
|
20
|
+
Print.info('Component name must start with a letter and contain only alphanumeric characters');
|
|
21
|
+
Print.commandExample("Valid names", "Button, UserCard, NavBar");
|
|
22
|
+
Print.commandExample("Invalid names", "1Button, user-card, Nav_Bar");
|
|
23
|
+
return false;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
// Components follow a PascalCase convention: normalize the initial to
|
|
27
|
+
// uppercase so the folder name, registry entry and existence checks agree.
|
|
28
|
+
componentName = componentName.charAt(0).toUpperCase() + componentName.slice(1);
|
|
29
|
+
|
|
30
|
+
// Validation: Component already exists
|
|
31
|
+
if(Validations.componentExists(componentName)){
|
|
32
|
+
Print.error(`Component '${componentName}' already exists in your project`);
|
|
33
|
+
Print.info('Please use a different name or delete the existing component first');
|
|
34
|
+
Print.commandExample("Delete component", "slice component delete");
|
|
35
|
+
return false;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
// Validation: Valid category
|
|
39
|
+
let flagCategory = Validations.isValidCategory(category);
|
|
40
|
+
|
|
41
|
+
if (!flagCategory.isValid) {
|
|
42
|
+
Print.error(`Invalid category: '${category}'`);
|
|
43
|
+
const availableCategories = Object.keys(Validations.getCategories()).join(', ');
|
|
44
|
+
Print.info(`Available categories: ${availableCategories}`);
|
|
45
|
+
return false;
|
|
46
|
+
}
|
|
47
|
+
category = flagCategory.category;
|
|
48
|
+
|
|
49
|
+
// Create class name and file name (componentName is already PascalCase).
|
|
50
|
+
const className = componentName;
|
|
51
|
+
const fileName = `${className}.js`;
|
|
52
|
+
let template;
|
|
53
|
+
|
|
54
|
+
const type = Validations.getCategoryType(category);
|
|
55
|
+
|
|
56
|
+
// Generate template based on type
|
|
57
|
+
if(type === 'Visual'){
|
|
58
|
+
template = componentTemplates.visual(className);
|
|
59
|
+
} else if(type === 'Service'){
|
|
60
|
+
template = componentTemplates.service(className);
|
|
61
|
+
} else {
|
|
62
|
+
Print.error(`Unsupported component type: '${type}'`);
|
|
63
|
+
Print.info('Only Visual and Service components are currently supported');
|
|
64
|
+
return false;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
const categoryPath = Validations.getCategoryPath(category);
|
|
68
|
+
const categoryPathClean = categoryPath ? categoryPath.replace(/^[/\\]+/, '') : '';
|
|
69
|
+
const componentDir = getSrcPath(import.meta.url, categoryPathClean, className);
|
|
70
|
+
|
|
71
|
+
try {
|
|
72
|
+
// Create component directory
|
|
73
|
+
fs.ensureDirSync(componentDir);
|
|
74
|
+
} catch (error) {
|
|
75
|
+
Print.error(`Failed to create component directory: '${componentDir}'`);
|
|
76
|
+
Print.info(`Error details: ${error.message}`);
|
|
77
|
+
return false;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
// Determine the file path
|
|
81
|
+
let componentPath = path.join(componentDir, fileName);
|
|
82
|
+
|
|
83
|
+
// Verify if the file already exists (double check)
|
|
84
|
+
if (fs.existsSync(componentPath)) {
|
|
85
|
+
Print.error(`Component file already exists at: '${componentPath}'`);
|
|
86
|
+
Print.info('This component may have been created outside the CLI');
|
|
87
|
+
return false;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
try {
|
|
91
|
+
// Write component code to file
|
|
92
|
+
fs.writeFileSync(componentPath, template);
|
|
93
|
+
|
|
94
|
+
// If Visual, create additional files (CSS and HTML)
|
|
95
|
+
if(type === 'Visual'){
|
|
96
|
+
const cssPath = path.join(componentDir, `${className}.css`);
|
|
97
|
+
const htmlPath = path.join(componentDir, `${className}.html`);
|
|
98
|
+
|
|
99
|
+
fs.writeFileSync(cssPath, '/* Styles for ' + componentName + ' component */\n');
|
|
100
|
+
fs.writeFileSync(htmlPath, `<div class="${componentName.toLowerCase()}">\n ${componentName}\n</div>`);
|
|
101
|
+
|
|
102
|
+
Print.info(`Created files: ${fileName}, ${className}.css, ${className}.html`);
|
|
103
|
+
} else {
|
|
104
|
+
Print.info(`Created file: ${fileName}`);
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
return true;
|
|
108
|
+
} catch (error) {
|
|
109
|
+
Print.error(`Failed to create component files`);
|
|
110
|
+
Print.info(`Error details: ${error.message}`);
|
|
111
|
+
|
|
112
|
+
// Try to clean up partially created files
|
|
113
|
+
try {
|
|
114
|
+
if (fs.existsSync(componentDir)) {
|
|
115
|
+
fs.removeSync(componentDir);
|
|
116
|
+
Print.info('Cleaned up partial files');
|
|
117
|
+
}
|
|
118
|
+
} catch (cleanupError) {
|
|
119
|
+
Print.warning('Could not clean up partial files. You may need to delete them manually');
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
return false;
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
|
|
127
|
+
export default createComponent;
|
|
128
|
+
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
import fs from 'fs-extra';
|
|
2
|
+
import path from 'path';
|
|
3
|
+
import Validations from '../Validations.js';
|
|
4
|
+
import Print from '../Print.js';
|
|
5
|
+
import { fileURLToPath } from 'url';
|
|
6
|
+
import { getSrcPath } from '../utils/PathHelper.js';
|
|
7
|
+
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
8
|
+
|
|
9
|
+
function deleteComponent(componentName, category) {
|
|
10
|
+
// Validation: Component name is required
|
|
11
|
+
if (!componentName) {
|
|
12
|
+
Print.error('Component name is required to delete');
|
|
13
|
+
Print.commandExample("Delete a component", "slice component delete");
|
|
14
|
+
return false;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
// Validation: Valid component name
|
|
18
|
+
if (!Validations.isValidComponentName(componentName)) {
|
|
19
|
+
Print.error(`Invalid component name: '${componentName}'`);
|
|
20
|
+
Print.info('Component name must start with a letter and contain only alphanumeric characters');
|
|
21
|
+
return false;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
// Components follow a PascalCase convention: normalize the initial so the
|
|
25
|
+
// lookup matches the folder name created by `slice component create`.
|
|
26
|
+
componentName = componentName.charAt(0).toUpperCase() + componentName.slice(1);
|
|
27
|
+
|
|
28
|
+
// Validation: Valid category
|
|
29
|
+
let flagCategory = Validations.isValidCategory(category);
|
|
30
|
+
|
|
31
|
+
if (!flagCategory.isValid) {
|
|
32
|
+
Print.error(`Invalid category: '${category}'`);
|
|
33
|
+
const availableCategories = Object.keys(Validations.getCategories()).join(', ');
|
|
34
|
+
Print.info(`Available categories: ${availableCategories}`);
|
|
35
|
+
return false;
|
|
36
|
+
}
|
|
37
|
+
category = flagCategory.category;
|
|
38
|
+
|
|
39
|
+
const categoryPath = Validations.getCategoryPath(category);
|
|
40
|
+
const categoryPathClean = categoryPath ? categoryPath.replace(/^[/\\]+/, '') : '';
|
|
41
|
+
const componentDir = getSrcPath(import.meta.url, categoryPathClean, componentName);
|
|
42
|
+
|
|
43
|
+
// Check if component directory exists
|
|
44
|
+
if (!fs.existsSync(componentDir)) {
|
|
45
|
+
Print.error(`Component '${componentName}' does not exist in category '${category}'`);
|
|
46
|
+
Print.info('Make sure you selected the correct category');
|
|
47
|
+
Print.commandExample("List components", "slice component list");
|
|
48
|
+
return false;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
// Verify it's a directory
|
|
52
|
+
try {
|
|
53
|
+
const stats = fs.statSync(componentDir);
|
|
54
|
+
if (!stats.isDirectory()) {
|
|
55
|
+
Print.error(`'${componentName}' is not a valid component directory`);
|
|
56
|
+
Print.info('Components must be stored in directories');
|
|
57
|
+
return false;
|
|
58
|
+
}
|
|
59
|
+
} catch (error) {
|
|
60
|
+
Print.error(`Failed to access component directory: '${componentDir}'`);
|
|
61
|
+
Print.info(`Error details: ${error.message}`);
|
|
62
|
+
return false;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
// Try to delete the component directory and its contents
|
|
66
|
+
try {
|
|
67
|
+
const files = fs.readdirSync(componentDir);
|
|
68
|
+
Print.info(`Deleting ${files.length} file(s) from component directory...`);
|
|
69
|
+
|
|
70
|
+
fs.removeSync(componentDir);
|
|
71
|
+
return true;
|
|
72
|
+
} catch (error) {
|
|
73
|
+
Print.error(`Failed to delete component '${componentName}'`);
|
|
74
|
+
Print.info(`Error details: ${error.message}`);
|
|
75
|
+
Print.warning('You may need to delete the component files manually');
|
|
76
|
+
Print.info(`Component location: ${componentDir}`);
|
|
77
|
+
return false;
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
export default deleteComponent;
|