Skip to content

Commit 9d9edb9

Browse files
author
Shailesh Pachbhai
authored
Merge pull request #231 from salesforcecli/u/shailesh/W-16480196/lwc-parser-v1
@W-16480196@ : "feat: LWC parser v1"
2 parents 1545bf6 + 6f37f34 commit 9d9edb9

File tree

9 files changed

+244
-3
lines changed

9 files changed

+244
-3
lines changed

.vscode/launch.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,4 +45,4 @@
4545
"preLaunchTask": "Compile"
4646
}
4747
]
48-
}
48+
}

package.json

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,18 +5,24 @@
55
"author": "Salesforce",
66
"bugs": "https://github.com/forcedotcom/cli/issues",
77
"dependencies": {
8+
"@babel/parser": "^7.25.4",
89
"@apexdevtools/apex-parser": "^4.1.0",
910
"@oclif/command": "^1",
1011
"@oclif/config": "^1",
1112
"@oclif/errors": "^1",
1213
"@salesforce/command": "^4.2.1",
1314
"@salesforce/core": "^2.37.1",
15+
"@types/jsdom": "^21.1.7",
1416
"@types/lodash.chunk": "^4.2.9",
17+
"cheerio": "^1.0.0",
18+
"jsdom": "^25.0.0",
1519
"lodash.chunk": "^4.2.0",
1620
"open": "^8.4.2",
17-
"tslib": "^2"
21+
"tslib": "^2",
22+
"xmldom": "^0.6.0"
1823
},
1924
"devDependencies": {
25+
"@babel/parser": "^7.25.3",
2026
"@oclif/dev-cli": "^1",
2127
"@oclif/plugin-command-snapshot": "^3.3.15",
2228
"@oclif/plugin-help": "^3",
@@ -26,6 +32,7 @@
2632
"@salesforce/plugin-command-reference": "^1.4.7",
2733
"@salesforce/prettier-config": "^0.0.3",
2834
"@salesforce/ts-sinon": "^1",
35+
"@types/babel__traverse": "^7.20.6",
2936
"@types/jsforce": "^1.11.5",
3037
"@typescript-eslint/eslint-plugin": "^4.2.0",
3138
"@typescript-eslint/parser": "^4.2.0",
Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
/* eslint-disable @typescript-eslint/explicit-member-accessibility */
2+
/* eslint-disable @typescript-eslint/restrict-template-expressions */
3+
/* eslint-disable @typescript-eslint/member-ordering */
4+
/* eslint-disable no-console */
5+
import * as fs from 'fs';
6+
import * as cheerio from 'cheerio';
7+
8+
class HTMLParser {
9+
private parser: cheerio.CheerioAPI;
10+
11+
// eslint-disable-next-line @typescript-eslint/explicit-member-accessibility
12+
constructor(htmlFilePath: string) {
13+
// Load the HTML file and initialize cheerio
14+
const html = this.loadHTMLFromFile(htmlFilePath);
15+
this.parser = cheerio.load(html);
16+
}
17+
18+
// Method to load HTML from a file
19+
private loadHTMLFromFile(filePath: string): string {
20+
try {
21+
return fs.readFileSync(filePath, 'utf8');
22+
} catch (error) {
23+
console.error(`Error reading file from disk: ${error}`);
24+
throw error;
25+
}
26+
}
27+
28+
// 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);
33+
});
34+
}
35+
36+
// Method to save modified HTML back to a file
37+
public saveToFile(outputFilePath: string): void {
38+
try {
39+
const modifiedHtml = this.parser.html();
40+
fs.writeFileSync(outputFilePath, modifiedHtml);
41+
console.log(`Modified HTML saved to ${outputFilePath}`);
42+
} catch (error) {
43+
console.error(`Error writing file to disk: ${error}`);
44+
throw error;
45+
}
46+
}
47+
48+
// Optional: Method to get the modified HTML as a string
49+
public getModifiedHTML(): string {
50+
return this.parser.html();
51+
}
52+
}
53+
54+
export default HTMLParser;
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
<!DOCTYPE html>
2+
<html lang="en">
3+
<head>
4+
<meta charset="UTF-8" />
5+
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
6+
<title>Replace Tags Example</title>
7+
</head>
8+
<body>
9+
<template>
10+
<omnistudio-input>Hello Shailesh</omnistudio-input>
11+
<omnistudio-input>Another Input</omnistudio-input>
12+
</template>
13+
</body>
14+
</html>

src/utils/lwcparser/input/test.js

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
import { LightningElement, track, api } from 'lwc';
2+
import * as LABELS from './labels';
3+
import { cloneDeep } from 'runtime_omnistudio_common/lodash';
4+
5+
export default class TestInputJsFile extends LightningElement {
6+
labels = LABELS;
7+
@api set actionData(val) {
8+
if (val) {
9+
this.actionJson = cloneDeep(val);
10+
}
11+
}
12+
get actionData() {
13+
return this.actionJson;
14+
}
15+
16+
@api attrsToBeRemoved = [];
17+
18+
@track actionJson = [];
19+
@track filteredLogs = [];
20+
@track actionSearchInput;
21+
_displayFilteredLogs = false;
22+
23+
toggle(event) {
24+
const index = event.currentTarget.dataset.index;
25+
this.actionJson[index].expanded = !this.actionJson[index].expanded;
26+
}
27+
28+
// Search
29+
get actionLogs() {
30+
const imports = "'import fs from 'fssss'";
31+
console.log(imports);
32+
// Display filtered debug logs
33+
if (Array.isArray(this.filteredLogs) && this._displayFilteredLogs) {
34+
return this.filteredLogs;
35+
}
36+
37+
// Display entire debug logs
38+
return this.actionJson;
39+
}
40+
41+
clearLogs() {
42+
this._displayFilteredLogs = false;
43+
this.actionSearchInput = '';
44+
this.actionJson = [];
45+
}
46+
47+
searchActionLogs(event) {
48+
event.preventDefault();
49+
50+
if (event.target.value) {
51+
this._displayFilteredLogs = true;
52+
const valueToSearch = event.target.value.toLowerCase();
53+
this.filteredLogs = this.actionJson.filter((action) => {
54+
return action.title && action.title.toLowerCase().includes(valueToSearch);
55+
});
56+
} else {
57+
// Clear filtered debug logs and set flag to display entire debug logs
58+
this.filteredLogs = [];
59+
this._displayFilteredLogs = false;
60+
}
61+
}
62+
}
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
/* eslint-disable no-console */
2+
/* eslint-disable @typescript-eslint/explicit-member-accessibility */
3+
import * as fs from 'fs';
4+
import * as parser from '@babel/parser';
5+
import traverse, { NodePath } from '@babel/traverse';
6+
import * as t from '@babel/types'; // Import all types from @babel/types
7+
8+
class JavaScriptParser {
9+
private fileContent: string;
10+
private ast: parser.ParseResult<t.File> | null; // Specify the generic type argument
11+
12+
constructor(filePath: string) {
13+
this.fileContent = fs.readFileSync(filePath, 'utf-8');
14+
this.ast = null;
15+
}
16+
17+
public parseFile(): void {
18+
this.ast = parser.parse(this.fileContent, {
19+
sourceType: 'module',
20+
plugins: ['decorators'],
21+
});
22+
}
23+
24+
public traverseAST(): string[] {
25+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
26+
const labels: string[] = [];
27+
if (!this.ast) {
28+
throw new Error('AST has not been generated. Call parseFile() first.');
29+
}
30+
31+
traverse(this.ast, {
32+
ImportDeclaration(path: NodePath<t.ImportDeclaration>) {
33+
console.log('Import found:', path.node.source.value);
34+
labels.push(path.node.source.value);
35+
},
36+
});
37+
// eslint-disable-next-line @typescript-eslint/no-unsafe-return
38+
return labels;
39+
}
40+
}
41+
42+
export default JavaScriptParser;
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
<!DOCTYPE html>
2+
<html lang="en">
3+
<head>
4+
<meta charset="UTF-8" />
5+
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
6+
<title>Replace Tags Example</title>
7+
</head>
8+
<body>
9+
<template>
10+
<c-input>Hello Shailesh</c-input>
11+
<c-input>Another Input</c-input>
12+
</template>
13+
</body>
14+
</html>
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
/* eslint-disable @typescript-eslint/no-inferrable-types */
2+
/* eslint-disable @typescript-eslint/no-unsafe-return */
3+
/* eslint-disable @typescript-eslint/member-ordering */
4+
/* eslint-disable @typescript-eslint/no-unsafe-member-access */
5+
/* eslint-disable @typescript-eslint/no-unsafe-call */
6+
/* eslint-disable @typescript-eslint/no-unsafe-assignment */
7+
/* eslint-disable @typescript-eslint/explicit-member-accessibility */
8+
import { DOMParser, XMLSerializer } from 'xmldom';
9+
10+
export class XmlParser {
11+
private xmlDoc: Document | null = null;
12+
13+
constructor(private xmlString: string) {
14+
this.parseXml();
15+
}
16+
17+
private parseXml(): void {
18+
const parser = new DOMParser();
19+
this.xmlDoc = parser.parseFromString(this.xmlString, 'text/xml');
20+
}
21+
22+
public removeNode(tagName: string, index: number = 0): void {
23+
if (!this.xmlDoc) {
24+
throw new Error('XML document has not been parsed.');
25+
}
26+
27+
const nodes = this.xmlDoc.getElementsByTagName(tagName);
28+
29+
if (nodes.length > index) {
30+
const nodeToRemove = nodes[index];
31+
nodeToRemove.parentNode?.removeChild(nodeToRemove);
32+
} else {
33+
throw new Error(`No node found with tag name "${tagName}" at index ${index}.`);
34+
}
35+
}
36+
37+
public getXmlString(): string {
38+
if (!this.xmlDoc) {
39+
throw new Error('XML document has not been parsed.');
40+
}
41+
42+
const serializer = new XMLSerializer();
43+
return serializer.serializeToString(this.xmlDoc);
44+
}
45+
}

tsconfig.json

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,12 @@
11
{
22
"extends": "@salesforce/dev-config/tsconfig",
33
"compilerOptions": {
4+
"lib": ["dom"],
45
"outDir": "lib",
56
"rootDir": "./src",
6-
"esModuleInterop": true
7+
"esModuleInterop": true,
8+
"types": ["node"],
9+
"skipLibCheck": true
710
},
811
"include": ["./src/**/*.ts"]
912
}

0 commit comments

Comments
 (0)