Skip to content

Commit adaccc8

Browse files
author
Shailesh Pachbhai
committed
feat: new branch with build issue fix
1 parent 88bafd6 commit adaccc8

File tree

13 files changed

+338
-77
lines changed

13 files changed

+338
-77
lines changed

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@
3636
"@salesforce/ts-sinon": "^1",
3737
"@types/babel__traverse": "^7.20.6",
3838
"@types/jsforce": "^1.11.5",
39+
"@types/mocha": "^10.0.8",
3940
"@typescript-eslint/eslint-plugin": "^4.2.0",
4041
"@typescript-eslint/parser": "^4.2.0",
4142
"chai": "^4.4.1",

src/commands/omnistudio/migration/OmnistudioRelatedObjectMigrationFacade.ts

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import { RelatedObjectsMigrate } from '../../../migration/interfaces';
88
import { sfProject } from '../../../utils/sfcli/project/sfProject';
99
import { Logger } from '../../../utils/logger';
1010
import { ApexMigration } from '../../../migration/related/ApexMigration';
11+
import { LwcMigration } from '../../../migration/related/LwcMigration';
1112

1213
// Initialize Messages with the current plugin directory
1314
// Messages.importMessagesDirectory(__dirname);
@@ -44,7 +45,7 @@ export default class OmnistudioRelatedObjectMigrationFacade {
4445
debugTimer.start();
4546
// Initialize migration tools based on the relatedObjects parameter
4647
if (relatedObjects.includes('lwc')) {
47-
migrationTools.push(this.createLWCComponentMigrationTool(this.namespace, this.org));
48+
migrationTools.push(this.createLWCComponentMigrationTool(projectDirectory));
4849
}
4950
if (relatedObjects.includes('labels')) {
5051
migrationTools.push(this.createCustomLabelMigrationTool(this.namespace, this.org));
@@ -75,9 +76,9 @@ export default class OmnistudioRelatedObjectMigrationFacade {
7576
}
7677

7778
// Factory methods to create instances of specific tools
78-
private createLWCComponentMigrationTool(namespace: string, org: Org): RelatedObjectsMigrate {
79+
private createLWCComponentMigrationTool(projectPath: string): LwcMigration {
7980
// Return an instance of LWCComponentMigrationTool when implemented
80-
throw new Error('LWCComponentMigrationTool implementation is not provided yet.');
81+
return new LwcMigration(projectPath, this.namespace, this.org);
8182
}
8283

8384
private createCustomLabelMigrationTool(namespace: string, org: Org): RelatedObjectsMigrate {
Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
/* eslint-disable @typescript-eslint/no-unsafe-assignment */
2+
/* eslint-disable @typescript-eslint/explicit-member-accessibility */
3+
import * as shell from 'shelljs';
4+
import { Org } from '@salesforce/core';
5+
import { fileutil, File } from '../../utils/file/fileutil';
6+
import { MigrationResult } from '../interfaces';
7+
import { sfProject } from '../../utils/sfcli/project/sfProject';
8+
import { JavaScriptParser } from '../../utils/lwcparser/jsparser/JavaScriptParser';
9+
import { HTMLParser } from '../../utils/lwcparser/htmlparser/HTMLParser';
10+
import { XmlParser } from '../../utils/lwcparser/xmlparser/XmlParser';
11+
import { BaseRelatedObjectMigration } from './BaseRealtedObjectMigration';
12+
13+
const LWC_DIR_PATH = '/force-app/main/default/lwc';
14+
const LWCTYPE = 'LightningComponentBundle';
15+
const XML_TAG_TO_REMOVE = 'runtimeNamespace';
16+
const NAMESPACE = 'vlocity_ins';
17+
18+
export class LwcMigration extends BaseRelatedObjectMigration {
19+
public identifyObjects(migrationResults: MigrationResult[]): Promise<JSON[]> {
20+
throw new Error('Method not implemented.');
21+
}
22+
public migrateRelatedObjects(migrationResults: MigrationResult[], migrationCandidates: JSON[]): void {
23+
this.migrate();
24+
}
25+
// eslint-disable-next-line @typescript-eslint/member-ordering
26+
27+
public migrate(): void {
28+
const pwd = shell.pwd();
29+
shell.cd(this.projectPath);
30+
const targetOrg: Org = this.org;
31+
sfProject.retrieve(LWCTYPE, targetOrg.getUsername());
32+
this.processLwcFiles(this.projectPath);
33+
sfProject.deploy(LWCTYPE, targetOrg.getUsername());
34+
shell.cd(pwd);
35+
}
36+
37+
public processLwcFiles(dir: string): File[] {
38+
dir += LWC_DIR_PATH;
39+
let files: File[] = [];
40+
// files = fileutil.readFilesSync(dir);
41+
files = fileutil.readAllFiles(dir);
42+
// TODO: Add logging
43+
for (const file of files) {
44+
if (file.ext === '.js') {
45+
this.processJavascriptFile(file);
46+
} else if (file.ext === '.html') {
47+
this.processHtmlFile(file);
48+
} else if (file.ext === '.xml') {
49+
this.processXMLFile(file);
50+
}
51+
}
52+
return files;
53+
}
54+
55+
processJavascriptFile(file: File): void {
56+
const jsParser = new JavaScriptParser();
57+
const filePath = file.location;
58+
const namespace = NAMESPACE;
59+
jsParser.replaceImportSource(filePath, namespace);
60+
}
61+
62+
processHtmlFile(file: File): void {
63+
const filePath: string = file.location;
64+
// eslint-disable-next-line @typescript-eslint/no-unsafe-call
65+
const parse = new HTMLParser(filePath);
66+
// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access, @typescript-eslint/no-unsafe-call
67+
parse.replaceTags(NAMESPACE);
68+
parse.saveToFile(filePath);
69+
}
70+
71+
processXMLFile(file: File): void {
72+
const filePath: string = file.location;
73+
const parser = new XmlParser(filePath);
74+
75+
parser.removeNode(XML_TAG_TO_REMOVE);
76+
// eslint-disable-next-line no-console
77+
console.log(parser.getXmlString());
78+
}
79+
}

src/utils/file/fileutil.ts

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,32 @@ export class fileutil {
1515
});
1616
return files;
1717
}
18+
19+
public static readAllFiles(dirPath: string, fileList: File[] = []): File[] {
20+
// Read the directory contents
21+
const files = fs.readdirSync(dirPath);
22+
23+
files.forEach((filename) => {
24+
// Construct the full file/directory path
25+
const filePath = path.join(dirPath, filename);
26+
27+
// Check if the current path is a directory or a file
28+
if (fs.statSync(filePath).isDirectory()) {
29+
// If it's a directory, recurse into it
30+
fileutil.readAllFiles(filePath, fileList);
31+
} else {
32+
const name = path.parse(filename).name;
33+
const ext = path.parse(filename).ext;
34+
const filepath = path.resolve(dirPath, filename);
35+
const stat = fs.statSync(filepath);
36+
const isFile = stat.isFile();
37+
// If it's a file, add it to the fileList
38+
if (isFile) fileList.push(new File(name, filepath, ext));
39+
}
40+
});
41+
42+
return fileList;
43+
}
1844
}
1945

2046
export class File {

src/utils/lwcparser/htmlParser/HTMLParser.ts

Lines changed: 24 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,10 @@
55
import * as fs from 'fs';
66
import * as cheerio from 'cheerio';
77

8-
class HTMLParser {
8+
const DEFAULT_NAMESPACE = 'c';
9+
const TAG = 'tag';
10+
11+
export class HTMLParser {
912
private parser: cheerio.CheerioAPI;
1013

1114
// eslint-disable-next-line @typescript-eslint/explicit-member-accessibility
@@ -26,11 +29,27 @@ class HTMLParser {
2629
}
2730

2831
// Method to replace custom tags
29-
public replaceCustomTag(oldTag: string, newTag: string): void {
30-
this.parser(oldTag).each((_, element) => {
31-
const newElement = this.parser(`<${newTag}></${newTag}>`).html(this.parser(element).html());
32-
this.parser(element).replaceWith(newElement);
32+
public replaceTags(namespaceTag: string): string {
33+
// Load the HTML into cheerio
34+
const $ = this.parser;
35+
36+
// Find all tags that contain the substring "omnistudio" in their tag name
37+
$('*').each((i, element) => {
38+
if (element.type === TAG && element.name && element.name.includes(namespaceTag + '-')) {
39+
// Create a new tag with the same content and attributes as the old tag
40+
const newTag = DEFAULT_NAMESPACE + element.name.substring(element.name.indexOf('-'));
41+
const newElement = $(`<${newTag}>`).html($(element).html());
42+
43+
// Copy all attributes from the old element to the new one
44+
Object.keys(element.attribs).forEach((attr) => {
45+
newElement.attr(attr, $(element).attr(attr));
46+
});
47+
48+
// Replace the old element with the new one
49+
$(element).replaceWith(newElement);
50+
}
3351
});
52+
return $.html();
3453
}
3554

3655
// Method to save modified HTML back to a file
@@ -50,5 +69,3 @@ class HTMLParser {
5069
return this.parser.html();
5170
}
5271
}
53-
54-
export default HTMLParser;

src/utils/lwcparser/input/test.js

Lines changed: 10 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,25 +1,26 @@
11
import { LightningElement, track, api } from 'lwc';
22
import * as LABELS from './labels';
33
import { cloneDeep } from 'runtime_omnistudio_common/lodash';
4-
54
export default class TestInputJsFile extends LightningElement {
65
labels = LABELS;
7-
@api set actionData(val) {
6+
@api
7+
set actionData(val) {
88
if (val) {
99
this.actionJson = cloneDeep(val);
1010
}
1111
}
1212
get actionData() {
1313
return this.actionJson;
1414
}
15-
16-
@api attrsToBeRemoved = [];
17-
18-
@track actionJson = [];
19-
@track filteredLogs = [];
20-
@track actionSearchInput;
15+
@api
16+
attrsToBeRemoved = [];
17+
@track
18+
actionJson = [];
19+
@track
20+
filteredLogs = [];
21+
@track
22+
actionSearchInput;
2123
_displayFilteredLogs = false;
22-
2324
toggle(event) {
2425
const index = event.currentTarget.dataset.index;
2526
this.actionJson[index].expanded = !this.actionJson[index].expanded;
@@ -37,16 +38,13 @@ export default class TestInputJsFile extends LightningElement {
3738
// Display entire debug logs
3839
return this.actionJson;
3940
}
40-
4141
clearLogs() {
4242
this._displayFilteredLogs = false;
4343
this.actionSearchInput = '';
4444
this.actionJson = [];
4545
}
46-
4746
searchActionLogs(event) {
4847
event.preventDefault();
49-
5048
if (event.target.value) {
5149
this._displayFilteredLogs = true;
5250
const valueToSearch = event.target.value.toLowerCase();

src/utils/lwcparser/input/test.xml

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<LightningComponentBundle xmlns="http://soap.sforce.com/2006/04/metadata">
3+
<apiVersion>60.0</apiVersion>
4+
<isExposed>true</isExposed>
5+
6+
<masterLabel>OmniExample - Custom Component Action Example</masterLabel>
7+
</LightningComponentBundle>
Lines changed: 40 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -1,49 +1,47 @@
1-
/* eslint-disable @typescript-eslint/no-unsafe-call */
2-
/* eslint-disable @typescript-eslint/no-unsafe-assignment */
1+
/* eslint-disable @typescript-eslint/no-unsafe-member-access */
32
/* eslint-disable no-console */
4-
/* eslint-disable @typescript-eslint/explicit-member-accessibility */
5-
// import * as fs from 'fs';
6-
// import { parse, type ParseResult } from '@babel/parser'; // Import all types from @babel/types
3+
import * as fs from 'fs';
4+
import * as parser from '@babel/parser';
5+
import traverse from '@babel/traverse';
6+
import generate from '@babel/generator';
7+
import * as t from '@babel/types';
78

8-
class JavaScriptParser {
9-
// private fileContent: string;
10-
private ast: File | null = null; // Specify the generic type argument
9+
const NAMESPACE = 'c';
1110

12-
constructor(filePath: string) {
13-
// this.fileContent = fs.readFileSync(filePath, 'utf-8');
14-
this.ast = null;
15-
}
11+
export class JavaScriptParser {
12+
// Function to replace strings in import declarations and write back to file
13+
public replaceImportSource(filePath: string, oldSource: string): void {
14+
// Read the JavaScript file
15+
const code = fs.readFileSync(filePath, 'utf-8');
1616

17-
// public parseCode(): void {
18-
// const parseResult: File = parse(this.fileContent, {
19-
// sourceType: 'module', // Use 'script' if you're parsing non-module code
20-
// plugins: ['jsx', 'typescript'], // Add plugins as needed
21-
// });
22-
23-
// if (parseResult.type === 'File') {
24-
// this.ast = parseResult;
25-
// } else {
26-
// throw new Error("Parsing did not return a 'File' node as expected.");
27-
// }
28-
// }
29-
30-
// Method to get the AST as a string
31-
getAST(): string | null {
32-
if (!this.ast) {
33-
console.error('AST is not available. Please parse the code first.');
34-
return null;
35-
}
36-
return JSON.stringify(this.ast, null, 2);
37-
}
17+
// Parse the code into an AST (Abstract Syntax Tree)
18+
const ast = parser.parse(code, {
19+
sourceType: 'module', // Specify that we are parsing an ES module
20+
plugins: ['decorators'], // Include any relevant plugins if necessary (e.g., 'jsx', 'flow', etc.)
21+
});
22+
23+
// Traverse the AST and modify import declarations
24+
traverse(ast, {
25+
ImportDeclaration(path) {
26+
const importSource = path.node.source.value;
27+
28+
// Check if the import source contains the old substring
29+
if (importSource.includes(oldSource + '/')) {
30+
// Replace the old substring with the new substring
31+
const updatedSource = importSource.replace(oldSource, NAMESPACE);
32+
// Update the AST with the new source
33+
path.node.source = t.stringLiteral(updatedSource);
34+
}
35+
},
36+
});
3837

39-
// Main method to process the file
40-
processFile(): void {
41-
// this.parseCode(); // Parse the JavaScript code
42-
const astString = this.getAST(); // Get the AST as a string
43-
if (astString) {
44-
console.log(astString); // Output the AST
45-
}
38+
// Generate the updated code from the modified AST
39+
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment, @typescript-eslint/no-unsafe-call
40+
const output = generate(ast, {}, code);
41+
42+
// Write the modified code back to the file
43+
fs.writeFileSync(filePath, output.code, 'utf-8');
44+
45+
console.log(`Replaced import '${oldSource}' with '${NAMESPACE}' in file: ${filePath}`);
4646
}
4747
}
48-
49-
export default JavaScriptParser;

0 commit comments

Comments
 (0)