@qelos/plugins-cli 0.0.23 → 0.0.24
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 +1 -1
- package/services/components.mjs +66 -13
package/package.json
CHANGED
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
|
}
|