slicejs-cli 3.4.0 → 3.4.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/AGENTS.md +247 -0
- package/client.js +63 -64
- package/commands/Print.js +11 -15
- package/commands/Validations.js +12 -23
- package/commands/buildProduction/buildProduction.js +23 -26
- package/commands/bundle/bundle.js +10 -11
- package/commands/createComponent/createComponent.js +14 -16
- package/commands/deleteComponent/deleteComponent.js +6 -6
- package/commands/doctor/doctor.js +11 -14
- package/commands/getComponent/getComponent.js +99 -162
- package/commands/init/init.js +25 -21
- package/commands/listComponents/listComponents.js +18 -21
- package/commands/startServer/startServer.js +21 -24
- package/commands/startServer/watchServer.js +7 -7
- package/commands/types/types.js +53 -18
- package/commands/utils/PathHelper.js +9 -2
- package/commands/utils/VersionChecker.js +3 -3
- package/commands/utils/bundling/DependencyAnalyzer.js +8 -16
- package/commands/utils/loadConfig.js +31 -0
- package/commands/utils/updateManager.js +3 -4
- package/docs/superpowers/specs/2026-05-10-pwa-generate-design.md +105 -105
- package/package.json +15 -3
- package/post.js +2 -2
- package/tests/bundle-generator.test.js +3 -20
- package/tests/component-registry-parse.test.js +34 -0
- package/tests/fixtures/components.js +8 -0
- package/tests/fixtures/sliceConfig.json +74 -0
- package/tests/getcomponent.test.js +407 -0
- package/tests/helpers/setup.js +97 -0
- package/tests/init-command-contract.test.js +46 -0
- package/tests/local-cli-delegation.test.js +7 -5
- package/tests/path-helper.test.js +206 -0
- package/tests/types-breakage.test.js +491 -0
- package/tests/types-generator-errors.test.js +361 -0
- package/tests/types-generator.test.js +172 -184
|
@@ -1,16 +1,13 @@
|
|
|
1
1
|
import fs from 'fs';
|
|
2
2
|
import path from 'path';
|
|
3
|
-
import { fileURLToPath } from 'url';
|
|
4
3
|
import Table from 'cli-table3';
|
|
5
4
|
import chalk from 'chalk';
|
|
6
5
|
import Print from '../Print.js';
|
|
7
6
|
import { getSrcPath, getComponentsJsPath, getConfigPath } from '../utils/PathHelper.js';
|
|
8
7
|
|
|
9
|
-
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
10
|
-
|
|
11
8
|
/**
|
|
12
|
-
*
|
|
13
|
-
* @returns {object} -
|
|
9
|
+
* Loads configuration from sliceConfig.json
|
|
10
|
+
* @returns {object} - Configuration object
|
|
14
11
|
*/
|
|
15
12
|
const loadConfig = () => {
|
|
16
13
|
try {
|
|
@@ -30,9 +27,9 @@ const loadConfig = () => {
|
|
|
30
27
|
};
|
|
31
28
|
|
|
32
29
|
/**
|
|
33
|
-
*
|
|
34
|
-
* @param {string} folderPath -
|
|
35
|
-
* @returns {string[]} -
|
|
30
|
+
* Lists files in a given folder, filtering only .js files
|
|
31
|
+
* @param {string} folderPath - Path of the folder to read
|
|
32
|
+
* @returns {string[]} - List of found files
|
|
36
33
|
*/
|
|
37
34
|
const listComponents = (folderPath) => {
|
|
38
35
|
try {
|
|
@@ -48,7 +45,7 @@ const listComponents = (folderPath) => {
|
|
|
48
45
|
};
|
|
49
46
|
|
|
50
47
|
/**
|
|
51
|
-
*
|
|
48
|
+
* Counts files in a component directory
|
|
52
49
|
*/
|
|
53
50
|
const countComponentFiles = (componentPath) => {
|
|
54
51
|
try {
|
|
@@ -61,14 +58,14 @@ const countComponentFiles = (componentPath) => {
|
|
|
61
58
|
};
|
|
62
59
|
|
|
63
60
|
/**
|
|
64
|
-
*
|
|
65
|
-
* @returns {object} -
|
|
61
|
+
* Gets components dynamically from sliceConfig.json
|
|
62
|
+
* @returns {object} - Component mapping with their category
|
|
66
63
|
*/
|
|
67
64
|
const getComponents = () => {
|
|
68
65
|
const config = loadConfig();
|
|
69
66
|
if (!config) return {};
|
|
70
67
|
|
|
71
|
-
const folderSuffix = 'src'; //
|
|
68
|
+
const folderSuffix = 'src'; // Always use 'src' for development
|
|
72
69
|
const componentPaths = config.paths?.components || {};
|
|
73
70
|
let allComponents = new Map();
|
|
74
71
|
|
|
@@ -91,7 +88,7 @@ const getComponents = () => {
|
|
|
91
88
|
|
|
92
89
|
function listComponentsReal() {
|
|
93
90
|
try {
|
|
94
|
-
//
|
|
91
|
+
// Get components dynamically
|
|
95
92
|
const components = getComponents();
|
|
96
93
|
|
|
97
94
|
if (Object.keys(components).length === 0) {
|
|
@@ -100,7 +97,7 @@ function listComponentsReal() {
|
|
|
100
97
|
return;
|
|
101
98
|
}
|
|
102
99
|
|
|
103
|
-
//
|
|
100
|
+
// Create table with cli-table3
|
|
104
101
|
const table = new Table({
|
|
105
102
|
head: [
|
|
106
103
|
chalk.cyan.bold('Component'),
|
|
@@ -114,7 +111,7 @@ function listComponentsReal() {
|
|
|
114
111
|
}
|
|
115
112
|
});
|
|
116
113
|
|
|
117
|
-
//
|
|
114
|
+
// Group by category for better visualization
|
|
118
115
|
const byCategory = {};
|
|
119
116
|
Object.entries(components).forEach(([name, data]) => {
|
|
120
117
|
if (!byCategory[data.category]) {
|
|
@@ -123,18 +120,18 @@ function listComponentsReal() {
|
|
|
123
120
|
byCategory[data.category].push({ name, files: data.files });
|
|
124
121
|
});
|
|
125
122
|
|
|
126
|
-
//
|
|
123
|
+
// Add rows to the table
|
|
127
124
|
Object.entries(byCategory).forEach(([category, comps]) => {
|
|
128
125
|
comps.forEach((comp, index) => {
|
|
129
126
|
if (index === 0) {
|
|
130
|
-
//
|
|
127
|
+
// First row of the category
|
|
131
128
|
table.push([
|
|
132
129
|
chalk.bold(comp.name),
|
|
133
130
|
chalk.yellow(category),
|
|
134
131
|
comp.files.toString()
|
|
135
132
|
]);
|
|
136
133
|
} else {
|
|
137
|
-
//
|
|
134
|
+
// Rest of components in the category
|
|
138
135
|
table.push([
|
|
139
136
|
chalk.bold(comp.name),
|
|
140
137
|
chalk.gray('″'), // Ditto mark
|
|
@@ -151,16 +148,16 @@ function listComponentsReal() {
|
|
|
151
148
|
Print.newLine();
|
|
152
149
|
Print.info(`Total: ${Object.keys(components).length} component${Object.keys(components).length !== 1 ? 's' : ''} found`);
|
|
153
150
|
|
|
154
|
-
//
|
|
151
|
+
// Path where components.js will be generated
|
|
155
152
|
const outputPath = getComponentsJsPath(import.meta.url);
|
|
156
153
|
|
|
157
|
-
//
|
|
154
|
+
// Ensure the directory exists
|
|
158
155
|
const outputDir = path.dirname(outputPath);
|
|
159
156
|
if (!fs.existsSync(outputDir)) {
|
|
160
157
|
fs.mkdirSync(outputDir, { recursive: true });
|
|
161
158
|
}
|
|
162
159
|
|
|
163
|
-
//
|
|
160
|
+
// Generate components.js file with detected components
|
|
164
161
|
const componentsForExport = Object.fromEntries(
|
|
165
162
|
Object.entries(components).map(([name, data]) => [name, data.category])
|
|
166
163
|
);
|
|
@@ -1,19 +1,16 @@
|
|
|
1
|
-
// commands/startServer/startServer.js -
|
|
1
|
+
// commands/startServer/startServer.js - IMPROVED WITH VALIDATION AND FEEDBACK
|
|
2
2
|
|
|
3
3
|
import fs from 'fs-extra';
|
|
4
4
|
import path from 'path';
|
|
5
|
-
import { fileURLToPath } from 'url';
|
|
6
5
|
import { spawn } from 'child_process';
|
|
7
6
|
import { createServer } from 'net';
|
|
8
7
|
import setupWatcher, { stopWatcher } from './watchServer.js';
|
|
9
8
|
import Print from '../Print.js';
|
|
10
|
-
import { getConfigPath, getApiPath, getSrcPath, getDistPath } from '../utils/PathHelper.js';
|
|
9
|
+
import { getConfigPath, getApiPath, getSrcPath, getDistPath, getPath } from '../utils/PathHelper.js';
|
|
11
10
|
import build from '../build/build.js';
|
|
12
11
|
|
|
13
|
-
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
14
|
-
|
|
15
12
|
/**
|
|
16
|
-
*
|
|
13
|
+
* Loads configuration from sliceConfig.json
|
|
17
14
|
*/
|
|
18
15
|
const loadConfig = () => {
|
|
19
16
|
try {
|
|
@@ -27,7 +24,7 @@ const loadConfig = () => {
|
|
|
27
24
|
};
|
|
28
25
|
|
|
29
26
|
/**
|
|
30
|
-
*
|
|
27
|
+
* Checks if a port is available
|
|
31
28
|
*/
|
|
32
29
|
async function isPortAvailable(port) {
|
|
33
30
|
return new Promise((resolve) => {
|
|
@@ -51,16 +48,16 @@ async function isPortAvailable(port) {
|
|
|
51
48
|
}
|
|
52
49
|
|
|
53
50
|
/**
|
|
54
|
-
*
|
|
51
|
+
* Checks if a production build exists
|
|
55
52
|
*/
|
|
56
53
|
async function checkProductionBuild() {
|
|
57
54
|
const distDir = getDistPath(import.meta.url);
|
|
58
|
-
const bundleConfigPath =
|
|
55
|
+
const bundleConfigPath = getPath(import.meta.url, 'dist', 'bundles', 'bundle.config.json');
|
|
59
56
|
return (await fs.pathExists(distDir)) && (await fs.pathExists(bundleConfigPath));
|
|
60
57
|
}
|
|
61
58
|
|
|
62
59
|
/**
|
|
63
|
-
*
|
|
60
|
+
* Checks if the development structure exists
|
|
64
61
|
*/
|
|
65
62
|
async function checkDevelopmentStructure() {
|
|
66
63
|
const srcDir = getSrcPath(import.meta.url);
|
|
@@ -70,13 +67,13 @@ async function checkDevelopmentStructure() {
|
|
|
70
67
|
}
|
|
71
68
|
|
|
72
69
|
/**
|
|
73
|
-
*
|
|
70
|
+
* Starts the Node.js server with arguments and improved feedback
|
|
74
71
|
*/
|
|
75
72
|
function startNodeServer(port, mode) {
|
|
76
73
|
return new Promise((resolve, reject) => {
|
|
77
74
|
const apiIndexPath = getApiPath(import.meta.url, 'index.js');
|
|
78
75
|
|
|
79
|
-
//
|
|
76
|
+
// Verify the file exists
|
|
80
77
|
if (!fs.existsSync(apiIndexPath)) {
|
|
81
78
|
reject(new Error(`Server file not found: ${apiIndexPath}`));
|
|
82
79
|
return;
|
|
@@ -84,7 +81,7 @@ function startNodeServer(port, mode) {
|
|
|
84
81
|
|
|
85
82
|
Print.serverStatus('starting', 'Starting server...');
|
|
86
83
|
|
|
87
|
-
//
|
|
84
|
+
// Build arguments based on mode
|
|
88
85
|
const args = [apiIndexPath];
|
|
89
86
|
if (mode === 'production') {
|
|
90
87
|
args.push('--production');
|
|
@@ -109,12 +106,12 @@ function startNodeServer(port, mode) {
|
|
|
109
106
|
let serverStarted = false;
|
|
110
107
|
let outputBuffer = '';
|
|
111
108
|
|
|
112
|
-
//
|
|
109
|
+
// Capture output to detect when the server is ready
|
|
113
110
|
serverProcess.stdout.on('data', (data) => {
|
|
114
111
|
const output = data.toString();
|
|
115
112
|
outputBuffer += output;
|
|
116
113
|
|
|
117
|
-
//
|
|
114
|
+
// Detect common messages indicating the server has started
|
|
118
115
|
if (!serverStarted && (
|
|
119
116
|
output.includes('Server running') ||
|
|
120
117
|
output.includes('listening on') ||
|
|
@@ -125,7 +122,7 @@ function startNodeServer(port, mode) {
|
|
|
125
122
|
Print.serverReady(port);
|
|
126
123
|
}
|
|
127
124
|
|
|
128
|
-
//
|
|
125
|
+
// Display server output
|
|
129
126
|
process.stdout.write(output);
|
|
130
127
|
});
|
|
131
128
|
|
|
@@ -161,7 +158,7 @@ function startNodeServer(port, mode) {
|
|
|
161
158
|
serverProcess.kill('SIGTERM');
|
|
162
159
|
});
|
|
163
160
|
|
|
164
|
-
//
|
|
161
|
+
// If after 3 seconds we haven't detected startup, assume it's ready
|
|
165
162
|
setTimeout(() => {
|
|
166
163
|
if (!serverStarted) {
|
|
167
164
|
serverStarted = true;
|
|
@@ -173,7 +170,7 @@ function startNodeServer(port, mode) {
|
|
|
173
170
|
}
|
|
174
171
|
|
|
175
172
|
/**
|
|
176
|
-
*
|
|
173
|
+
* Main function to start the server
|
|
177
174
|
*/
|
|
178
175
|
export default async function startServer(options = {}) {
|
|
179
176
|
const config = loadConfig();
|
|
@@ -185,7 +182,7 @@ export default async function startServer(options = {}) {
|
|
|
185
182
|
Print.title(`🚀 Starting Slice.js ${mode} server...`);
|
|
186
183
|
Print.newLine();
|
|
187
184
|
|
|
188
|
-
//
|
|
185
|
+
// Verify project structure
|
|
189
186
|
if (!await checkDevelopmentStructure()) {
|
|
190
187
|
throw new Error('Project structure not found. Run "slice init" first.');
|
|
191
188
|
}
|
|
@@ -204,7 +201,7 @@ export default async function startServer(options = {}) {
|
|
|
204
201
|
Print.newLine();
|
|
205
202
|
|
|
206
203
|
if (mode === 'production') {
|
|
207
|
-
//
|
|
204
|
+
// Verify production build exists
|
|
208
205
|
if (!await checkProductionBuild()) {
|
|
209
206
|
Print.info('No production build found. Running "slice build"...');
|
|
210
207
|
const success = await build({});
|
|
@@ -219,10 +216,10 @@ export default async function startServer(options = {}) {
|
|
|
219
216
|
|
|
220
217
|
Print.newLine();
|
|
221
218
|
|
|
222
|
-
//
|
|
219
|
+
// Start the server with arguments
|
|
223
220
|
let serverProcess = await startNodeServer(actualPort, mode);
|
|
224
221
|
|
|
225
|
-
//
|
|
222
|
+
// Configure watch mode if enabled
|
|
226
223
|
if (watch) {
|
|
227
224
|
Print.newLine();
|
|
228
225
|
const watcher = setupWatcher(serverProcess, async (changedPath) => {
|
|
@@ -253,12 +250,12 @@ export default async function startServer(options = {}) {
|
|
|
253
250
|
|
|
254
251
|
} catch (error) {
|
|
255
252
|
Print.newLine();
|
|
256
|
-
Print.error(error.message);
|
|
253
|
+
Print.error(`Failed to start development server: ${error.message}`);
|
|
257
254
|
throw error;
|
|
258
255
|
}
|
|
259
256
|
}
|
|
260
257
|
|
|
261
258
|
/**
|
|
262
|
-
*
|
|
259
|
+
* Exported utility functions
|
|
263
260
|
*/
|
|
264
261
|
export { checkProductionBuild, checkDevelopmentStructure, isPortAvailable };
|
|
@@ -3,9 +3,9 @@ import chalk from 'chalk';
|
|
|
3
3
|
import Print from '../Print.js';
|
|
4
4
|
|
|
5
5
|
/**
|
|
6
|
-
*
|
|
7
|
-
* @param {ChildProcess} serverProcess -
|
|
8
|
-
* @returns {FSWatcher} -
|
|
6
|
+
* Configures the watcher for project files
|
|
7
|
+
* @param {ChildProcess} serverProcess - Server process
|
|
8
|
+
* @returns {FSWatcher} - Chokidar watcher
|
|
9
9
|
*/
|
|
10
10
|
export default function setupWatcher(serverProcess, onRestart) {
|
|
11
11
|
Print.info('Watch mode enabled - monitoring file changes...');
|
|
@@ -13,7 +13,7 @@ export default function setupWatcher(serverProcess, onRestart) {
|
|
|
13
13
|
|
|
14
14
|
const watcher = chokidar.watch(['src/**/*', 'api/**/*'], {
|
|
15
15
|
ignored: [
|
|
16
|
-
/(^|[\/\\])\../, //
|
|
16
|
+
/(^|[\/\\])\../, // hidden files
|
|
17
17
|
'**/node_modules/**',
|
|
18
18
|
'**/dist/**',
|
|
19
19
|
'**/bundles/**',
|
|
@@ -31,7 +31,7 @@ export default function setupWatcher(serverProcess, onRestart) {
|
|
|
31
31
|
|
|
32
32
|
watcher
|
|
33
33
|
.on('change', (path) => {
|
|
34
|
-
// Debounce
|
|
34
|
+
// Debounce to avoid multiple reloads
|
|
35
35
|
clearTimeout(reloadTimeout);
|
|
36
36
|
reloadTimeout = setTimeout(() => {
|
|
37
37
|
if(onRestart) {
|
|
@@ -68,8 +68,8 @@ export default function setupWatcher(serverProcess, onRestart) {
|
|
|
68
68
|
}
|
|
69
69
|
|
|
70
70
|
/**
|
|
71
|
-
*
|
|
72
|
-
* @param {FSWatcher} watcher - Watcher
|
|
71
|
+
* Stops the watcher safely
|
|
72
|
+
* @param {FSWatcher} watcher - Watcher to stop
|
|
73
73
|
*/
|
|
74
74
|
export function stopWatcher(watcher) {
|
|
75
75
|
if (watcher) {
|
package/commands/types/types.js
CHANGED
|
@@ -4,6 +4,7 @@ import { parse } from '@babel/parser';
|
|
|
4
4
|
import traverse from '@babel/traverse';
|
|
5
5
|
|
|
6
6
|
import Print from '../Print.js';
|
|
7
|
+
import { getConfigPath, getComponentsJsPath, joinRoot } from '../utils/PathHelper.js';
|
|
7
8
|
|
|
8
9
|
const TYPE_MAP = {
|
|
9
10
|
string: 'string',
|
|
@@ -114,22 +115,29 @@ const sortKeys = (obj) => {
|
|
|
114
115
|
}, {});
|
|
115
116
|
};
|
|
116
117
|
|
|
117
|
-
const parseComponentsRegistry = (content) => {
|
|
118
|
+
const parseComponentsRegistry = (content, filePath) => {
|
|
118
119
|
const match = content.match(/const components = ({[\s\S]*?});/);
|
|
119
120
|
if (!match) {
|
|
120
|
-
throw new Error(
|
|
121
|
+
throw new Error(`Invalid format in ${filePath}. Expected: const components = { ... };`);
|
|
122
|
+
}
|
|
123
|
+
try {
|
|
124
|
+
return JSON.parse(match[1]);
|
|
125
|
+
} catch (parseError) {
|
|
126
|
+
throw new Error(`Failed to parse components registry in ${filePath}: ${parseError.message}`);
|
|
121
127
|
}
|
|
122
|
-
return JSON.parse(match[1]);
|
|
123
128
|
};
|
|
124
129
|
|
|
125
|
-
const extractStaticPropsFromSource = (source) => {
|
|
130
|
+
const extractStaticPropsFromSource = (source, filePath) => {
|
|
126
131
|
let ast;
|
|
127
132
|
try {
|
|
128
133
|
ast = parse(source, {
|
|
129
134
|
sourceType: 'module',
|
|
130
135
|
plugins: ['classProperties']
|
|
131
136
|
});
|
|
132
|
-
} catch {
|
|
137
|
+
} catch (parseError) {
|
|
138
|
+
const sourceDesc = filePath || 'unknown file';
|
|
139
|
+
const loc = parseError.loc ? ` at line ${parseError.loc.line}, column ${parseError.loc.column}` : '';
|
|
140
|
+
Print.warning(`Parse error in ${sourceDesc}${loc}: ${parseError.message}`);
|
|
133
141
|
return null;
|
|
134
142
|
}
|
|
135
143
|
|
|
@@ -194,7 +202,7 @@ const DEFAULT_EDITOR_EXCLUDE = ['node_modules', 'dist', 'src/libs/**', 'tests/**
|
|
|
194
202
|
const NOISY_INCLUDE_PATTERNS = new Set(['src/**/*.js', 'api/**/*.js', 'tests/**/*.js']);
|
|
195
203
|
|
|
196
204
|
const readPublicFolderExcludes = async (projectRoot) => {
|
|
197
|
-
const configPath =
|
|
205
|
+
const configPath = getConfigPath(import.meta.url, projectRoot);
|
|
198
206
|
if (!(await fs.pathExists(configPath))) return [];
|
|
199
207
|
|
|
200
208
|
try {
|
|
@@ -230,7 +238,7 @@ const collectJavaScriptFiles = async (dirPath) => {
|
|
|
230
238
|
};
|
|
231
239
|
|
|
232
240
|
const ensureNoCheckInPublicVendorFiles = async (projectRoot) => {
|
|
233
|
-
const configPath =
|
|
241
|
+
const configPath = getConfigPath(import.meta.url, projectRoot);
|
|
234
242
|
if (!(await fs.pathExists(configPath))) return { updatedFiles: 0, scannedFiles: 0 };
|
|
235
243
|
|
|
236
244
|
let parsed;
|
|
@@ -245,7 +253,7 @@ const ensureNoCheckInPublicVendorFiles = async (projectRoot) => {
|
|
|
245
253
|
.map((folder) => String(folder || '').trim())
|
|
246
254
|
.filter(Boolean)
|
|
247
255
|
.map((folder) => folder.replace(/^[/\\]+/, ''))
|
|
248
|
-
.map((folder) =>
|
|
256
|
+
.map((folder) => joinRoot(projectRoot, 'src', folder));
|
|
249
257
|
|
|
250
258
|
const uniqueDirs = Array.from(new Set(candidateDirs));
|
|
251
259
|
let scannedFiles = 0;
|
|
@@ -360,9 +368,11 @@ const readCategoryPathFromConfig = async (configPath, category) => {
|
|
|
360
368
|
};
|
|
361
369
|
|
|
362
370
|
const loadComponentStaticProps = async ({ projectRoot, registryMap }) => {
|
|
363
|
-
const configPath =
|
|
371
|
+
const configPath = getConfigPath(import.meta.url, projectRoot);
|
|
364
372
|
const categoryPathCache = new Map();
|
|
365
373
|
const componentPropsMap = {};
|
|
374
|
+
let skippedCount = 0;
|
|
375
|
+
let processedCount = 0;
|
|
366
376
|
|
|
367
377
|
for (const [componentName, category] of Object.entries(sortKeys(registryMap))) {
|
|
368
378
|
if (!categoryPathCache.has(category)) {
|
|
@@ -372,26 +382,50 @@ const loadComponentStaticProps = async ({ projectRoot, registryMap }) => {
|
|
|
372
382
|
|
|
373
383
|
const categoryPath = categoryPathCache.get(category);
|
|
374
384
|
if (!categoryPath) {
|
|
385
|
+
skippedCount++;
|
|
375
386
|
continue;
|
|
376
387
|
}
|
|
377
388
|
|
|
378
|
-
const componentFile =
|
|
389
|
+
const componentFile = joinRoot(projectRoot, 'src', categoryPath, componentName, `${componentName}.js`);
|
|
379
390
|
if (!(await fs.pathExists(componentFile))) {
|
|
391
|
+
skippedCount++;
|
|
392
|
+
continue;
|
|
393
|
+
}
|
|
394
|
+
|
|
395
|
+
let source;
|
|
396
|
+
try {
|
|
397
|
+
source = await fs.readFile(componentFile, 'utf8');
|
|
398
|
+
} catch (readError) {
|
|
399
|
+
Print.warning(`Cannot read ${componentFile}: ${readError.message}`);
|
|
400
|
+
skippedCount++;
|
|
380
401
|
continue;
|
|
381
402
|
}
|
|
382
403
|
|
|
383
|
-
const
|
|
384
|
-
|
|
385
|
-
|
|
404
|
+
const props = extractStaticPropsFromSource(source, componentFile);
|
|
405
|
+
if (props) {
|
|
406
|
+
componentPropsMap[componentName] = props;
|
|
407
|
+
processedCount++;
|
|
408
|
+
} else {
|
|
409
|
+
componentPropsMap[componentName] = { [DYNAMIC_FALLBACK_PROP]: { type: 'any', required: false } };
|
|
410
|
+
}
|
|
411
|
+
}
|
|
412
|
+
|
|
413
|
+
if (skippedCount > 0) {
|
|
414
|
+
Print.info(`Skipped ${skippedCount} component(s) with missing or unreadable files`);
|
|
386
415
|
}
|
|
387
416
|
|
|
388
417
|
return componentPropsMap;
|
|
389
418
|
};
|
|
390
419
|
|
|
391
420
|
const generateTypesFile = async ({ projectRoot, outputPath }) => {
|
|
392
|
-
const registryPath =
|
|
393
|
-
|
|
394
|
-
|
|
421
|
+
const registryPath = getComponentsJsPath(import.meta.url, projectRoot);
|
|
422
|
+
let registryContent;
|
|
423
|
+
try {
|
|
424
|
+
registryContent = await fs.readFile(registryPath, 'utf8');
|
|
425
|
+
} catch (readError) {
|
|
426
|
+
throw new Error(`Cannot read components registry at ${registryPath}: ${readError.message}`);
|
|
427
|
+
}
|
|
428
|
+
const registryMap = parseComponentsRegistry(registryContent, registryPath);
|
|
395
429
|
|
|
396
430
|
const componentPropsMap = await loadComponentStaticProps({ projectRoot, registryMap });
|
|
397
431
|
|
|
@@ -412,12 +446,12 @@ const toPosixRelative = (projectRoot, targetPath) => {
|
|
|
412
446
|
|
|
413
447
|
const ensureEditorConfigForTypes = async ({ projectRoot, outputPath }) => {
|
|
414
448
|
try {
|
|
415
|
-
const tsconfigPath =
|
|
449
|
+
const tsconfigPath = joinRoot(projectRoot, 'tsconfig.json');
|
|
416
450
|
if (await fs.pathExists(tsconfigPath)) {
|
|
417
451
|
return { mode: 'tsconfig_exists', filePath: tsconfigPath, includeAdded: false };
|
|
418
452
|
}
|
|
419
453
|
|
|
420
|
-
const jsconfigPath =
|
|
454
|
+
const jsconfigPath = joinRoot(projectRoot, 'jsconfig.json');
|
|
421
455
|
const declarationGlob = (() => {
|
|
422
456
|
const relative = toPosixRelative(projectRoot, outputPath);
|
|
423
457
|
if (!relative) return 'src/**/*.d.ts';
|
|
@@ -541,5 +575,6 @@ export {
|
|
|
541
575
|
extractStaticPropsFromSource,
|
|
542
576
|
generateDeclarationContent,
|
|
543
577
|
generateTypesFile,
|
|
578
|
+
parseComponentsRegistry,
|
|
544
579
|
runGenerateTypes
|
|
545
580
|
};
|
|
@@ -59,10 +59,17 @@ export function getDistPath(moduleUrl, ...segments) {
|
|
|
59
59
|
return joinProject(moduleUrl, 'dist', ...segments)
|
|
60
60
|
}
|
|
61
61
|
|
|
62
|
-
export function getConfigPath(moduleUrl) {
|
|
62
|
+
export function getConfigPath(moduleUrl, root) {
|
|
63
|
+
if (root) return path.join(root, 'src', 'sliceConfig.json')
|
|
63
64
|
return joinProject(moduleUrl, 'src', 'sliceConfig.json')
|
|
64
65
|
}
|
|
65
66
|
|
|
66
|
-
export function getComponentsJsPath(moduleUrl) {
|
|
67
|
+
export function getComponentsJsPath(moduleUrl, root) {
|
|
68
|
+
if (root) return path.join(root, 'src', 'Components', 'components.js')
|
|
67
69
|
return joinProject(moduleUrl, 'src', 'Components', 'components.js')
|
|
68
70
|
}
|
|
71
|
+
|
|
72
|
+
export function joinRoot(root, ...segments) {
|
|
73
|
+
const clean = segments.map(sanitize)
|
|
74
|
+
return path.join(root, ...clean)
|
|
75
|
+
}
|
|
@@ -4,7 +4,7 @@ import fs from "fs-extra";
|
|
|
4
4
|
import path from "path";
|
|
5
5
|
import { fileURLToPath } from "url";
|
|
6
6
|
import Print from "../Print.js";
|
|
7
|
-
import { getProjectRoot } from "../utils/PathHelper.js";
|
|
7
|
+
import { getProjectRoot, getPath } from "../utils/PathHelper.js";
|
|
8
8
|
|
|
9
9
|
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
10
10
|
|
|
@@ -25,14 +25,14 @@ class VersionChecker {
|
|
|
25
25
|
|
|
26
26
|
// Get Framework version from project node_modules
|
|
27
27
|
const projectRoot = getProjectRoot(import.meta.url);
|
|
28
|
-
const frameworkPackagePath =
|
|
28
|
+
const frameworkPackagePath = getPath(import.meta.url, 'node_modules', 'slicejs-web-framework', 'package.json');
|
|
29
29
|
if (await fs.pathExists(frameworkPackagePath)) {
|
|
30
30
|
const frameworkPackage = await fs.readJson(frameworkPackagePath);
|
|
31
31
|
this.currentFrameworkVersion = frameworkPackage.version;
|
|
32
32
|
}
|
|
33
33
|
|
|
34
34
|
// Get Project's CLI version
|
|
35
|
-
const projectPackagePath =
|
|
35
|
+
const projectPackagePath = getPath(import.meta.url, 'package.json');
|
|
36
36
|
if (await fs.pathExists(projectPackagePath)) {
|
|
37
37
|
const projectPackage = await fs.readJson(projectPackagePath);
|
|
38
38
|
if (projectPackage.dependencies && projectPackage.dependencies['slicejs-cli']) {
|
|
@@ -3,21 +3,13 @@ import fs from 'fs-extra';
|
|
|
3
3
|
import path from 'path';
|
|
4
4
|
import { parse } from '@babel/parser';
|
|
5
5
|
import traverse from '@babel/traverse';
|
|
6
|
-
import { getSrcPath, getComponentsJsPath,
|
|
6
|
+
import { getSrcPath, getComponentsJsPath, getConfigPath, getPath } from '../PathHelper.js';
|
|
7
7
|
|
|
8
8
|
export default class DependencyAnalyzer {
|
|
9
9
|
constructor(moduleUrl) {
|
|
10
10
|
this.moduleUrl = moduleUrl;
|
|
11
|
-
this.projectRoot = getProjectRoot(moduleUrl);
|
|
12
11
|
this.componentsPath = path.dirname(getComponentsJsPath(moduleUrl));
|
|
13
|
-
this.frameworkComponentsPath =
|
|
14
|
-
this.projectRoot,
|
|
15
|
-
'node_modules',
|
|
16
|
-
'slicejs-web-framework',
|
|
17
|
-
'Slice',
|
|
18
|
-
'Components',
|
|
19
|
-
'Structural'
|
|
20
|
-
);
|
|
12
|
+
this.frameworkComponentsPath = getPath(moduleUrl, 'node_modules', 'slicejs-web-framework', 'Slice', 'Components', 'Structural');
|
|
21
13
|
this.routesPath = getSrcPath(moduleUrl, 'routes.js');
|
|
22
14
|
|
|
23
15
|
// Analysis storage
|
|
@@ -229,7 +221,7 @@ export default class DependencyAnalyzer {
|
|
|
229
221
|
if (!await fs.pathExists(this.frameworkComponentsPath)) {
|
|
230
222
|
return;
|
|
231
223
|
}
|
|
232
|
-
const frameworkConfigPath =
|
|
224
|
+
const frameworkConfigPath = getConfigPath(this.moduleUrl);
|
|
233
225
|
let frameworkConfig = {};
|
|
234
226
|
try {
|
|
235
227
|
frameworkConfig = await fs.readJson(frameworkConfigPath);
|
|
@@ -723,7 +715,7 @@ export default class DependencyAnalyzer {
|
|
|
723
715
|
*/
|
|
724
716
|
async analyzeRoutes() {
|
|
725
717
|
if (!await fs.pathExists(this.routesPath)) {
|
|
726
|
-
throw new Error(
|
|
718
|
+
throw new Error(`routes.js not found at expected path: ${this.routesPath}. Ensure your routes.js file exists in the src directory.`);
|
|
727
719
|
}
|
|
728
720
|
|
|
729
721
|
const content = await fs.readFile(this.routesPath, 'utf-8');
|
|
@@ -735,11 +727,11 @@ export default class DependencyAnalyzer {
|
|
|
735
727
|
});
|
|
736
728
|
|
|
737
729
|
let currentRoute = null;
|
|
738
|
-
const self = this; //
|
|
730
|
+
const self = this; // Save reference to instance
|
|
739
731
|
|
|
740
732
|
traverse.default(ast, {
|
|
741
733
|
ObjectExpression(path) {
|
|
742
|
-
//
|
|
734
|
+
// Look for route objects: { path: '/', component: 'HomePage' }
|
|
743
735
|
const properties = path.node.properties;
|
|
744
736
|
const pathProp = properties.find(p => p.key?.name === 'path');
|
|
745
737
|
const componentProp = properties.find(p => p.key?.name === 'component');
|
|
@@ -756,7 +748,7 @@ export default class DependencyAnalyzer {
|
|
|
756
748
|
|
|
757
749
|
self.routes.set(routePath, currentRoute);
|
|
758
750
|
|
|
759
|
-
//
|
|
751
|
+
// Mark the component as used by this route
|
|
760
752
|
if (self.components.has(componentName)) {
|
|
761
753
|
self.components.get(componentName).routes.add(routePath);
|
|
762
754
|
}
|
|
@@ -768,7 +760,7 @@ export default class DependencyAnalyzer {
|
|
|
768
760
|
this.routeGroups = this.detectRouteGroups();
|
|
769
761
|
|
|
770
762
|
} catch (error) {
|
|
771
|
-
console.warn(`⚠️ Error
|
|
763
|
+
console.warn(`⚠️ Error parsing routes at ${this.routesPath}: ${error.message}`);
|
|
772
764
|
}
|
|
773
765
|
}
|
|
774
766
|
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import fs from 'fs-extra';
|
|
2
|
+
import { getConfigPath } from './PathHelper.js';
|
|
3
|
+
import Print from '../Print.js';
|
|
4
|
+
|
|
5
|
+
export async function loadConfig(moduleUrl) {
|
|
6
|
+
try {
|
|
7
|
+
const configPath = getConfigPath(moduleUrl);
|
|
8
|
+
if (!await fs.pathExists(configPath)) {
|
|
9
|
+
return null;
|
|
10
|
+
}
|
|
11
|
+
const rawData = await fs.readFile(configPath, 'utf-8');
|
|
12
|
+
return JSON.parse(rawData);
|
|
13
|
+
} catch (error) {
|
|
14
|
+
Print.error(`Error loading configuration: ${error.message}`);
|
|
15
|
+
return null;
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export function loadConfigSync(moduleUrl) {
|
|
20
|
+
try {
|
|
21
|
+
const configPath = getConfigPath(moduleUrl);
|
|
22
|
+
if (!fs.existsSync(configPath)) {
|
|
23
|
+
return null;
|
|
24
|
+
}
|
|
25
|
+
const rawData = fs.readFileSync(configPath, 'utf-8');
|
|
26
|
+
return JSON.parse(rawData);
|
|
27
|
+
} catch (error) {
|
|
28
|
+
Print.error(`Error loading configuration: ${error.message}`);
|
|
29
|
+
return null;
|
|
30
|
+
}
|
|
31
|
+
}
|
|
@@ -6,7 +6,7 @@ import inquirer from "inquirer";
|
|
|
6
6
|
import ora from "ora";
|
|
7
7
|
import Print from "../Print.js";
|
|
8
8
|
import versionChecker from "./VersionChecker.js";
|
|
9
|
-
import { getProjectRoot } from "../utils/PathHelper.js";
|
|
9
|
+
import { getProjectRoot, getApiPath, getPath } from "../utils/PathHelper.js";
|
|
10
10
|
import path from "path";
|
|
11
11
|
import { fileURLToPath } from "url";
|
|
12
12
|
import fs from "fs-extra";
|
|
@@ -401,9 +401,8 @@ export class UpdateManager {
|
|
|
401
401
|
|
|
402
402
|
async updateApiIndexIfNeeded(options = {}) {
|
|
403
403
|
try {
|
|
404
|
-
const
|
|
405
|
-
const
|
|
406
|
-
const frameworkApiPath = path.join(projectRoot, 'node_modules', 'slicejs-web-framework', 'api', 'index.js');
|
|
404
|
+
const projectApiPath = getApiPath(import.meta.url, 'index.js');
|
|
405
|
+
const frameworkApiPath = getPath(import.meta.url, 'node_modules', 'slicejs-web-framework', 'api', 'index.js');
|
|
407
406
|
|
|
408
407
|
if (!await fs.pathExists(projectApiPath) || !await fs.pathExists(frameworkApiPath)) {
|
|
409
408
|
return;
|