Skip to content

Commit caa0edb

Browse files
committed
feat: añade merge de packages y mejoras en gestión de carpetas v1.1.2
1 parent f400275 commit caa0edb

File tree

3 files changed

+193
-5
lines changed

3 files changed

+193
-5
lines changed

package-lock.json

Lines changed: 21 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
"name": "sf-packageduplicator",
33
"displayName": "SF Package Duplicator",
44
"description": "Duplica packages con formato CRMLEAD000Y",
5-
"version": "1.0.2",
5+
"version": "1.1.2",
66
"engines": {
77
"vscode": "^1.80.0"
88
},
@@ -28,6 +28,10 @@
2828
{
2929
"command": "extension.configurePackageDuplicator",
3030
"title": "SF: Configure Package Duplicator"
31+
},
32+
{
33+
"command": "extension.mergePackages",
34+
"title": "SF: Merge Packages"
3135
}
3236
],
3337
"configuration": {
@@ -70,5 +74,9 @@
7074
"description": "Esta extensión requiere acceso completo al workspace para funcionar correctamente."
7175
}
7276
},
73-
"icon": "images/icon.png"
77+
"icon": "images/icon.png",
78+
"dependencies": {
79+
"@types/xmldom": "^0.1.34",
80+
"xmldom": "^0.6.0"
81+
}
7482
}

src/extension.ts

Lines changed: 162 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import * as fs from 'fs';
33
import * as path from 'path';
44
import { exec } from 'child_process';
55
import { promisify } from 'util';
6+
import { DOMParser } from 'xmldom';
67

78
const execAsync = promisify(exec);
89

@@ -63,6 +64,50 @@ async function revertPackageXmlIfChanged(workspacePath: string): Promise<void> {
6364
}
6465
}
6566

67+
// Añadir esta función helper
68+
function mergePackageXmls(xmlFiles: string[]): string {
69+
const parser = new DOMParser();
70+
let mergedTypes: { [key: string]: Set<string> } = {};
71+
72+
xmlFiles.forEach(xmlContent => {
73+
const doc = parser.parseFromString(xmlContent, 'text/xml');
74+
const types = doc.getElementsByTagName('types');
75+
76+
for (const type of Array.from(types)) {
77+
const name = type.getElementsByTagName('name')[0].textContent;
78+
const members = Array.from(type.getElementsByTagName('members'))
79+
.map(member => member.textContent);
80+
81+
if (name) {
82+
if (!mergedTypes[name]) {
83+
mergedTypes[name] = new Set();
84+
}
85+
members.forEach(member => {
86+
if (member) mergedTypes[name].add(member);
87+
});
88+
}
89+
}
90+
});
91+
92+
// Crear el XML combinado
93+
let result = '<?xml version="1.0" encoding="UTF-8" standalone="yes"?>\n';
94+
result += '<Package xmlns="http://soap.sforce.com/2006/04/metadata">\n';
95+
96+
Object.entries(mergedTypes).forEach(([name, members]) => {
97+
result += '\t<types>\n';
98+
Array.from(members).sort().forEach(member => {
99+
result += `\t\t<members>${member}</members>\n`;
100+
});
101+
result += `\t\t<name>${name}</name>\n`;
102+
result += '\t</types>\n';
103+
});
104+
105+
result += '\t<version>60.0</version>\n';
106+
result += '</Package>';
107+
108+
return result;
109+
}
110+
66111
export function activate(context: vscode.ExtensionContext) {
67112
let configCommand = vscode.commands.registerCommand('extension.configurePackageDuplicator', async () => {
68113
const workspaceFolder = vscode.workspace.workspaceFolders?.[0];
@@ -173,5 +218,121 @@ export function activate(context: vscode.ExtensionContext) {
173218
}
174219
});
175220

176-
context.subscriptions.push(disposable, configCommand);
221+
// Añadir el nuevo comando en activate()
222+
let mergeCommand = vscode.commands.registerCommand('extension.mergePackages', async () => {
223+
const workspaceFolder = vscode.workspace.workspaceFolders?.[0];
224+
if (!workspaceFolder) {
225+
vscode.window.showErrorMessage('No se encontró la carpeta del workspace');
226+
return;
227+
}
228+
229+
const manifestFolder = path.join(workspaceFolder.uri.fsPath, 'manifest');
230+
231+
// Preguntar el modo de selección
232+
const selectionMode = await vscode.window.showQuickPick(
233+
[
234+
{ label: 'Seleccionar archivos individuales', value: 'files' },
235+
{ label: 'Seleccionar carpeta completa', value: 'folder' }
236+
],
237+
{ placeHolder: '¿Cómo quieres seleccionar los archivos a combinar?' }
238+
);
239+
240+
if (!selectionMode) return;
241+
242+
try {
243+
let xmlFiles: string[] = [];
244+
245+
if (selectionMode.value === 'folder') {
246+
// Obtener lista de carpetas
247+
const folders = ['manifest', ...fs.readdirSync(manifestFolder)
248+
.filter(item => {
249+
try {
250+
return fs.statSync(path.join(manifestFolder, item)).isDirectory();
251+
} catch (error) {
252+
return false;
253+
}
254+
})];
255+
256+
const selectedFolder = await vscode.window.showQuickPick(folders, {
257+
placeHolder: 'Selecciona la carpeta que contiene los archivos a combinar'
258+
});
259+
260+
if (!selectedFolder) return;
261+
262+
const folderPath = selectedFolder === 'manifest'
263+
? manifestFolder
264+
: path.join(manifestFolder, selectedFolder);
265+
266+
// Leer todos los XML de la carpeta seleccionada
267+
xmlFiles = fs.readdirSync(folderPath)
268+
.filter(file => file.endsWith('.xml'))
269+
.map(file => path.join(folderPath, file));
270+
271+
if (xmlFiles.length < 2) {
272+
vscode.window.showInformationMessage('Se necesitan al menos 2 archivos XML en la carpeta para combinar');
273+
return;
274+
}
275+
} else {
276+
// Modo selección de archivos individual (código existente)
277+
const processDirectory = (dir: string) => {
278+
const items = fs.readdirSync(dir);
279+
items.forEach(item => {
280+
const fullPath = path.join(dir, item);
281+
if (fs.statSync(fullPath).isDirectory()) {
282+
processDirectory(fullPath);
283+
} else if (item.endsWith('.xml')) {
284+
xmlFiles.push(fullPath);
285+
}
286+
});
287+
};
288+
processDirectory(manifestFolder);
289+
290+
const fileItems = xmlFiles.map(file => ({
291+
label: path.relative(manifestFolder, file),
292+
path: file
293+
}));
294+
295+
const selectedFiles = await vscode.window.showQuickPick(fileItems, {
296+
canPickMany: true,
297+
placeHolder: 'Selecciona los archivos package.xml a combinar'
298+
});
299+
300+
if (!selectedFiles || selectedFiles.length < 2) {
301+
vscode.window.showInformationMessage('Debes seleccionar al menos 2 archivos para combinar');
302+
return;
303+
}
304+
305+
xmlFiles = selectedFiles.map(file => file.path);
306+
}
307+
308+
// Leer y combinar los archivos
309+
const xmlContents = xmlFiles.map(file => fs.readFileSync(file, 'utf8'));
310+
const mergedContent = mergePackageXmls(xmlContents);
311+
312+
// Solicitar nombre del archivo
313+
const defaultFileName = `merged-package-${new Date().toISOString().replace(/[:.]/g, '-')}.xml`;
314+
const fileName = await vscode.window.showInputBox({
315+
prompt: 'Ingresa el nombre para el archivo combinado',
316+
placeHolder: 'Ejemplo: merged-feature-123.xml',
317+
value: defaultFileName
318+
});
319+
320+
if (!fileName) {
321+
return;
322+
}
323+
324+
// Asegurar que el archivo termine en .xml
325+
const finalFileName = fileName.endsWith('.xml') ? fileName : `${fileName}.xml`;
326+
const newFilePath = path.join(manifestFolder, finalFileName);
327+
328+
fs.writeFileSync(newFilePath, mergedContent);
329+
vscode.window.showInformationMessage(`Archivos combinados en: ${finalFileName}`);
330+
331+
} catch (error) {
332+
const errorMessage = error instanceof Error ? error.message : 'Error desconocido';
333+
vscode.window.showErrorMessage(`Error al combinar archivos: ${errorMessage}`);
334+
}
335+
});
336+
337+
context.subscriptions.push(disposable, configCommand, mergeCommand);
177338
}

0 commit comments

Comments
 (0)