@qelos/plugins-cli 0.0.23 → 0.0.25
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/package.json
CHANGED
package/services/blueprints.mjs
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import fs from 'node:fs';
|
|
2
2
|
import { join } from 'node:path';
|
|
3
3
|
import { logger } from './logger.mjs';
|
|
4
|
+
import { removeIdFromObject } from '../utils/object-utils.mjs';
|
|
4
5
|
|
|
5
6
|
/**
|
|
6
7
|
* Push blueprints from local directory to remote
|
|
@@ -137,11 +138,6 @@ export async function pullBlueprints(sdk, targetPath) {
|
|
|
137
138
|
// Fetch full blueprint details
|
|
138
139
|
const fullBlueprint = await sdk.manageBlueprints.getBlueprint(blueprint.identifier);
|
|
139
140
|
|
|
140
|
-
function removeIdFromObject(obj) {
|
|
141
|
-
const { _id, ...rest } = obj;
|
|
142
|
-
return rest;
|
|
143
|
-
}
|
|
144
|
-
|
|
145
141
|
const relevantFields = {
|
|
146
142
|
identifier: fullBlueprint.identifier,
|
|
147
143
|
name: fullBlueprint.name,
|
package/services/components.mjs
CHANGED
|
@@ -1,23 +1,57 @@
|
|
|
1
1
|
import fs from 'node:fs';
|
|
2
|
-
import { join } from 'node:path';
|
|
2
|
+
import path, { join } from 'node:path';
|
|
3
3
|
import { logger } from './logger.mjs';
|
|
4
4
|
|
|
5
|
+
function normalizeRelativeIdentifier(value) {
|
|
6
|
+
return String(value || '')
|
|
7
|
+
.replaceAll('\\', '/')
|
|
8
|
+
.replace(/^\//, '')
|
|
9
|
+
.trim();
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
function isSafeRelativeIdentifier(value) {
|
|
13
|
+
if (!value) return false;
|
|
14
|
+
if (value.includes('..')) return false;
|
|
15
|
+
if (value.startsWith('/')) return false;
|
|
16
|
+
return true;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
function listVueFilesRecursively(rootDir, currentDir = rootDir, out = []) {
|
|
20
|
+
const entries = fs.readdirSync(currentDir, { withFileTypes: true });
|
|
21
|
+
for (const entry of entries) {
|
|
22
|
+
const fullPath = join(currentDir, entry.name);
|
|
23
|
+
if (entry.isDirectory()) {
|
|
24
|
+
if (entry.name === 'node_modules' || entry.name === '.git') continue;
|
|
25
|
+
listVueFilesRecursively(rootDir, fullPath, out);
|
|
26
|
+
continue;
|
|
27
|
+
}
|
|
28
|
+
if (!entry.isFile()) continue;
|
|
29
|
+
if (!entry.name.endsWith('.vue')) continue;
|
|
30
|
+
if (entry.name === 'components.json') continue;
|
|
31
|
+
out.push(fullPath);
|
|
32
|
+
}
|
|
33
|
+
return out;
|
|
34
|
+
}
|
|
35
|
+
|
|
5
36
|
/**
|
|
6
37
|
* Push components from local directory to remote
|
|
7
38
|
* @param {Object} sdk - Initialized SDK instance
|
|
8
39
|
* @param {string} path - Path to components directory
|
|
9
40
|
*/
|
|
10
|
-
export async function pushComponents(sdk,
|
|
41
|
+
export async function pushComponents(sdk, componentsPath, options = {}) {
|
|
11
42
|
const { targetFile } = options;
|
|
12
|
-
const
|
|
13
|
-
|
|
14
|
-
|
|
43
|
+
const vueFilePaths = targetFile
|
|
44
|
+
? [join(componentsPath, targetFile)]
|
|
45
|
+
: listVueFilesRecursively(componentsPath);
|
|
46
|
+
const vueFiles = vueFilePaths
|
|
47
|
+
.filter(filePath => fs.existsSync(filePath))
|
|
48
|
+
.filter(filePath => filePath.endsWith('.vue'));
|
|
15
49
|
|
|
16
50
|
if (vueFiles.length === 0) {
|
|
17
51
|
if (targetFile) {
|
|
18
52
|
logger.warning(`File ${targetFile} is not a .vue component. Skipping.`);
|
|
19
53
|
} else {
|
|
20
|
-
logger.warning(`No .vue files found in ${
|
|
54
|
+
logger.warning(`No .vue files found in ${componentsPath}`);
|
|
21
55
|
}
|
|
22
56
|
return;
|
|
23
57
|
}
|
|
@@ -26,7 +60,7 @@ export async function pushComponents(sdk, path, options = {}) {
|
|
|
26
60
|
let componentsJson = {};
|
|
27
61
|
|
|
28
62
|
try {
|
|
29
|
-
const jsonPath = join(
|
|
63
|
+
const jsonPath = join(componentsPath, 'components.json');
|
|
30
64
|
if (fs.existsSync(jsonPath)) {
|
|
31
65
|
componentsJson = JSON.parse(fs.readFileSync(jsonPath));
|
|
32
66
|
}
|
|
@@ -38,11 +72,17 @@ export async function pushComponents(sdk, path, options = {}) {
|
|
|
38
72
|
|
|
39
73
|
await Promise.all(vueFiles.map(async (file) => {
|
|
40
74
|
if (file.endsWith('.vue')) {
|
|
41
|
-
const
|
|
75
|
+
const relativePath = normalizeRelativeIdentifier(path.relative(componentsPath, file));
|
|
76
|
+
const componentName = relativePath.split('/').pop().replace('.vue', '');
|
|
42
77
|
const info = componentsJson[componentName] || {};
|
|
43
|
-
const content = fs.readFileSync(
|
|
44
|
-
const targetIdentifier = info.identifier ||
|
|
78
|
+
const content = fs.readFileSync(file, 'utf-8');
|
|
79
|
+
const targetIdentifier = normalizeRelativeIdentifier(info.identifier || relativePath.replace('.vue', ''));
|
|
45
80
|
const targetDescription = info.description || 'Component description';
|
|
81
|
+
|
|
82
|
+
if (!isSafeRelativeIdentifier(targetIdentifier)) {
|
|
83
|
+
logger.error(`Skipping component with unsafe identifier: ${targetIdentifier}`);
|
|
84
|
+
return;
|
|
85
|
+
}
|
|
46
86
|
|
|
47
87
|
logger.step(`Pushing component: ${componentName}`);
|
|
48
88
|
|
|
@@ -102,9 +142,20 @@ export async function pullComponents(sdk, targetPath) {
|
|
|
102
142
|
logger.info(`Found ${components.length} component(s) to pull`);
|
|
103
143
|
|
|
104
144
|
const componentsInformation = await Promise.all(components.map(async (component) => {
|
|
105
|
-
const
|
|
145
|
+
const normalizedIdentifier = normalizeRelativeIdentifier(component.identifier);
|
|
146
|
+
const fileName = `${normalizedIdentifier}.vue`;
|
|
106
147
|
const filePath = join(targetPath, fileName);
|
|
107
148
|
|
|
149
|
+
if (!isSafeRelativeIdentifier(normalizedIdentifier)) {
|
|
150
|
+
logger.error(`Skipping pull for unsafe identifier: ${component.identifier}`);
|
|
151
|
+
return null;
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
const targetDir = join(targetPath, fileName.split('/').slice(0, -1).join('/'));
|
|
155
|
+
if (targetDir && !fs.existsSync(targetDir)) {
|
|
156
|
+
fs.mkdirSync(targetDir, { recursive: true });
|
|
157
|
+
}
|
|
158
|
+
|
|
108
159
|
const { content, description } = await sdk.components.getComponent(component._id);
|
|
109
160
|
|
|
110
161
|
fs.writeFileSync(filePath, content, 'utf-8');
|
|
@@ -118,10 +169,12 @@ export async function pullComponents(sdk, targetPath) {
|
|
|
118
169
|
};
|
|
119
170
|
}));
|
|
120
171
|
|
|
172
|
+
const filteredComponentsInformation = componentsInformation.filter(Boolean);
|
|
173
|
+
|
|
121
174
|
fs.writeFileSync(
|
|
122
175
|
join(targetPath, 'components.json'),
|
|
123
176
|
JSON.stringify(
|
|
124
|
-
|
|
177
|
+
filteredComponentsInformation.reduce((obj, current) => {
|
|
125
178
|
obj[current.componentName] = current;
|
|
126
179
|
return obj;
|
|
127
180
|
}, {}),
|
|
@@ -131,5 +184,5 @@ export async function pullComponents(sdk, targetPath) {
|
|
|
131
184
|
);
|
|
132
185
|
|
|
133
186
|
logger.info(`Saved components.json with metadata`);
|
|
134
|
-
logger.info(`Pulled ${
|
|
187
|
+
logger.info(`Pulled ${filteredComponentsInformation.length} component(s)`);
|
|
135
188
|
}
|
package/services/git-files.mjs
CHANGED
|
@@ -3,6 +3,61 @@ import { logger } from './logger.mjs';
|
|
|
3
3
|
import path from 'node:path';
|
|
4
4
|
import fs from 'node:fs';
|
|
5
5
|
|
|
6
|
+
/**
|
|
7
|
+
* Resolve the actual file path from Git output
|
|
8
|
+
* Git might show temp paths or different formats, so we need to find the real file
|
|
9
|
+
* @param {string} gitPath - Path as reported by Git
|
|
10
|
+
* @param {string} basePath - Base path to search for the actual file
|
|
11
|
+
* @returns {string|null} The actual file path or null if not found
|
|
12
|
+
*/
|
|
13
|
+
function resolveActualFilePath(gitPath, basePath) {
|
|
14
|
+
// First try the direct path
|
|
15
|
+
const directPath = path.resolve(basePath, gitPath);
|
|
16
|
+
if (fs.existsSync(directPath)) {
|
|
17
|
+
return directPath;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
// If it's an HTML file, it might be in a micro-frontends directory
|
|
21
|
+
if (gitPath.endsWith('.html')) {
|
|
22
|
+
// Try to find it in any plugin's micro-frontends directory
|
|
23
|
+
const pluginsDir = path.join(basePath, 'plugins');
|
|
24
|
+
if (fs.existsSync(pluginsDir)) {
|
|
25
|
+
const plugins = fs.readdirSync(pluginsDir);
|
|
26
|
+
const filename = path.basename(gitPath);
|
|
27
|
+
|
|
28
|
+
for (const plugin of plugins) {
|
|
29
|
+
const mfPath = path.join(pluginsDir, plugin, 'micro-frontends', filename);
|
|
30
|
+
if (fs.existsSync(mfPath)) {
|
|
31
|
+
return mfPath;
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
// For other files, try searching in common directories
|
|
38
|
+
const filename = path.basename(gitPath);
|
|
39
|
+
const searchDirs = [
|
|
40
|
+
'components',
|
|
41
|
+
'blueprints',
|
|
42
|
+
'configs',
|
|
43
|
+
'plugins',
|
|
44
|
+
'blocks',
|
|
45
|
+
'integrations',
|
|
46
|
+
'connections',
|
|
47
|
+
'prompts'
|
|
48
|
+
];
|
|
49
|
+
|
|
50
|
+
for (const dir of searchDirs) {
|
|
51
|
+
const searchPath = path.join(basePath, dir, filename);
|
|
52
|
+
if (fs.existsSync(searchPath)) {
|
|
53
|
+
return searchPath;
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
// If still not found, return null
|
|
58
|
+
return null;
|
|
59
|
+
}
|
|
60
|
+
|
|
6
61
|
/**
|
|
7
62
|
* Get the list of files committed in the last commit
|
|
8
63
|
* @returns {string[]} Array of file paths
|
|
@@ -114,8 +169,22 @@ function classifyFiles(files, basePath) {
|
|
|
114
169
|
};
|
|
115
170
|
|
|
116
171
|
for (const file of files) {
|
|
172
|
+
// Try to resolve the actual file path (handles Git temp paths)
|
|
173
|
+
let fullPath = path.resolve(basePath, file);
|
|
174
|
+
|
|
175
|
+
// If the direct path doesn't exist, try to find the actual file
|
|
176
|
+
if (!fs.existsSync(fullPath)) {
|
|
177
|
+
const resolvedPath = resolveActualFilePath(file, basePath);
|
|
178
|
+
if (resolvedPath) {
|
|
179
|
+
logger.debug(`Resolved Git path ${file} to actual path ${resolvedPath}`);
|
|
180
|
+
fullPath = resolvedPath;
|
|
181
|
+
} else {
|
|
182
|
+
logger.warning(`File not found, skipping: ${file}`);
|
|
183
|
+
continue;
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
|
|
117
187
|
// Make sure the file exists
|
|
118
|
-
const fullPath = path.resolve(basePath, file);
|
|
119
188
|
if (!fs.existsSync(fullPath)) {
|
|
120
189
|
logger.warning(`File not found, skipping: ${file}`);
|
|
121
190
|
continue;
|
|
@@ -164,8 +233,33 @@ function classifyFiles(files, basePath) {
|
|
|
164
233
|
|
|
165
234
|
// For HTML files, we need to find which plugin contains them
|
|
166
235
|
// HTML files in plugins are typically part of the plugin structure
|
|
167
|
-
|
|
168
|
-
|
|
236
|
+
let pluginDir = path.dirname(fullPath);
|
|
237
|
+
let pluginJson = path.join(pluginDir, 'plugin.json');
|
|
238
|
+
|
|
239
|
+
// If the file is in a temp path or unusual location, try to find the actual plugin
|
|
240
|
+
if (!fs.existsSync(pluginJson)) {
|
|
241
|
+
// Check if we're in a micro-frontends subdirectory
|
|
242
|
+
if (path.basename(pluginDir) === 'micro-frontends' ||
|
|
243
|
+
relativePath.includes('micro-frontends/') ||
|
|
244
|
+
relativePath.includes('micro-frontends\\')) {
|
|
245
|
+
// Go up one more level to find the plugin directory
|
|
246
|
+
pluginDir = path.dirname(pluginDir);
|
|
247
|
+
pluginJson = path.join(pluginDir, 'plugin.json');
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
// If still not found, try searching for plugin.json in parent directories
|
|
251
|
+
if (!fs.existsSync(pluginJson)) {
|
|
252
|
+
let searchDir = pluginDir;
|
|
253
|
+
for (let i = 0; i < 3; i++) { // Search up to 3 levels up
|
|
254
|
+
searchDir = path.dirname(searchDir);
|
|
255
|
+
const testPluginJson = path.join(searchDir, 'plugin.json');
|
|
256
|
+
if (fs.existsSync(testPluginJson)) {
|
|
257
|
+
pluginJson = testPluginJson;
|
|
258
|
+
break;
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
}
|
|
262
|
+
}
|
|
169
263
|
|
|
170
264
|
if (fs.existsSync(pluginJson)) {
|
|
171
265
|
// This HTML file is part of a plugin
|
|
@@ -173,6 +267,8 @@ function classifyFiles(files, basePath) {
|
|
|
173
267
|
classified.plugins.push(pluginJson);
|
|
174
268
|
logger.debug(`Found plugin containing HTML ${relativePath}: ${path.basename(pluginJson)}`);
|
|
175
269
|
}
|
|
270
|
+
} else {
|
|
271
|
+
logger.warning(`Could not find plugin.json for HTML file: ${relativePath}`);
|
|
176
272
|
}
|
|
177
273
|
} else {
|
|
178
274
|
logger.debug(`Unclassified file: ${relativePath}`);
|
|
@@ -2,6 +2,7 @@ import fs from 'node:fs';
|
|
|
2
2
|
import path from 'node:path';
|
|
3
3
|
import { logger } from './logger.mjs';
|
|
4
4
|
import { loadReference } from './file-refs.mjs';
|
|
5
|
+
import { removeIdFromObject } from '../utils/object-utils.mjs';
|
|
5
6
|
|
|
6
7
|
/**
|
|
7
8
|
* Convert a string to kebab-case
|
|
@@ -55,6 +56,7 @@ export function extractMicroFrontendStructures(microFrontends, pluginPath) {
|
|
|
55
56
|
// Return micro-frontend with $ref instead of structure
|
|
56
57
|
return {
|
|
57
58
|
...mf,
|
|
59
|
+
requirements: (mf.requirements || []).map(removeIdFromObject),
|
|
58
60
|
structure: { $ref: relativeRef }
|
|
59
61
|
};
|
|
60
62
|
});
|
package/services/plugins.mjs
CHANGED
|
@@ -3,6 +3,7 @@ import path from 'node:path';
|
|
|
3
3
|
import { join } from 'node:path';
|
|
4
4
|
import { logger } from './logger.mjs';
|
|
5
5
|
import { extractMicroFrontendStructures, resolveMicroFrontendStructures } from './micro-frontends.mjs';
|
|
6
|
+
import { removeIdFromObject } from '../utils/object-utils.mjs';
|
|
6
7
|
|
|
7
8
|
function sanitizePluginForFile(plugin) {
|
|
8
9
|
const sanitized = JSON.parse(JSON.stringify(plugin));
|
|
@@ -194,11 +195,6 @@ export async function pullPlugins(sdk, targetPath) {
|
|
|
194
195
|
// Fetch full plugin details
|
|
195
196
|
const fullPlugin = await sdk.managePlugins.getById(plugin._id);
|
|
196
197
|
|
|
197
|
-
function removeIdFromObject(obj) {
|
|
198
|
-
const { _id, ...rest } = obj;
|
|
199
|
-
return rest;
|
|
200
|
-
}
|
|
201
|
-
|
|
202
198
|
// Extract micro-frontend structures to separate files
|
|
203
199
|
const processedMicroFrontends = extractMicroFrontendStructures(
|
|
204
200
|
(fullPlugin.microFrontends || []).map(removeIdFromObject),
|