rflib-plugin 0.1.2

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/LICENSE ADDED
@@ -0,0 +1,28 @@
1
+ BSD 3-Clause License
2
+
3
+ Copyright (c) 2024, Johannes Fischer
4
+
5
+ Redistribution and use in source and binary forms, with or without
6
+ modification, are permitted provided that the following conditions are met:
7
+
8
+ 1. Redistributions of source code must retain the above copyright notice, this
9
+ list of conditions and the following disclaimer.
10
+
11
+ 2. Redistributions in binary form must reproduce the above copyright notice,
12
+ this list of conditions and the following disclaimer in the documentation
13
+ and/or other materials provided with the distribution.
14
+
15
+ 3. Neither the name of the copyright holder nor the names of its
16
+ contributors may be used to endorse or promote products derived from
17
+ this software without specific prior written permission.
18
+
19
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
20
+ AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21
+ IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
22
+ DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
23
+ FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24
+ DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
25
+ SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
26
+ CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
27
+ OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
28
+ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
package/README.md ADDED
@@ -0,0 +1,66 @@
1
+ # RFLIB Plugin for Salesforce CLI
2
+
3
+ [![NPM](https://img.shields.io/npm/v/rflib-plugin.svg?label=rflib-plugin)](https://www.npmjs.com/package/rflib-plugin) [![Downloads/week](https://img.shields.io/npm/dw/rflib-plugin.svg)](https://npmjs.org/package/rflib-plugin) [![License](https://img.shields.io/badge/License-BSD%203--Clause-brightgreen.svg)](https://raw.githubusercontent.com/salesforcecli/rflib-plugin/main/LICENSE)
4
+
5
+ Plugin for Salesforce CLI to help with the adoption of [RFLIB](https://github.com/j-fischer/rflib) - an open-source logging framework for Salesforce.
6
+
7
+ ## Features
8
+
9
+ - Automatically instruments Apex classes with RFLIB logging statements
10
+ - Automatically instruments LWC components with RFLIB logging statements
11
+
12
+ ## Installation
13
+
14
+ ```bash
15
+ sf plugins install rflib-plugin
16
+ ```
17
+
18
+ ## Commands
19
+
20
+ ### `sf rflib logging apex instrument`
21
+
22
+ Adds RFLIB logging statements to Apex classes.
23
+
24
+ ```bash
25
+ # Add logging to all classes in a directory
26
+ sf rflib logging apex instrument --sourcepath force-app/main/default/classes
27
+
28
+ # Preview changes without modifying files
29
+ sf rflib logging apex instrument --sourcepath force-app/main/default/classes --dryrun
30
+
31
+ # Format modified files with Prettier
32
+ sf rflib logging apex instrument --sourcepath force-app/main/default/classes --prettier
33
+ ```
34
+
35
+ #### Command Options
36
+
37
+ - `--sourcepath (-s)`: Directory containing Apex classes to instrument
38
+ - `--dryrun (-d)`: Preview changes without modifying files
39
+ - `--prettier (-p)`: Format modified files using Prettier
40
+
41
+ ### `sf rflib logging lwc instrument`
42
+
43
+ Adds RFLIB logging statements to Lightning Web Components.
44
+
45
+ ```bash
46
+ # Add logging to all LWC files
47
+ sf rflib logging lwc instrument --sourcepath force-app/main/default/lwc
48
+
49
+ # Preview changes without modifying files
50
+ sf rflib logging lwc instrument --sourcepath force-app/main/default/lwc --dryrun
51
+
52
+ # Add logging and format code
53
+ sf rflib logging lwc instrument --sourcepath force-app/main/default/lwc --prettier
54
+ ```
55
+
56
+ ## Contributing
57
+
58
+ 1. Fork the repository
59
+ 2. Create a feature branch
60
+ 3. Make your changes
61
+ 4. Submit a pull request
62
+
63
+ ## Learn More
64
+
65
+ - [RFLIB Documentation](https://github.com/j-fischer/rflib)
66
+ - [Salesforce CLI Plugin Developer Guide](https://developer.salesforce.com/docs/atlas.en-us.sfdx_cli_plugins.meta/sfdx_cli_plugins/cli_plugins_architecture_sf_cli.htm)
@@ -0,0 +1,31 @@
1
+ import { SfCommand } from '@salesforce/sf-plugins-core';
2
+ export type RflibLoggingApexInstrumentResult = {
3
+ processedFiles: number;
4
+ modifiedFiles: number;
5
+ formattedFiles: number;
6
+ };
7
+ export default class RflibLoggingApexInstrument extends SfCommand<RflibLoggingApexInstrumentResult> {
8
+ static readonly summary: string;
9
+ static readonly description: string;
10
+ static readonly examples: string[];
11
+ static readonly flags: {
12
+ sourcepath: import("@oclif/core/interfaces").OptionFlag<string, import("@oclif/core/interfaces").CustomOptions>;
13
+ dryrun: import("@oclif/core/interfaces").BooleanFlag<boolean>;
14
+ prettier: import("@oclif/core/interfaces").BooleanFlag<boolean>;
15
+ };
16
+ private logger;
17
+ private processedFiles;
18
+ private modifiedFiles;
19
+ private formattedFiles;
20
+ private readonly prettierConfig;
21
+ private static isComplexType;
22
+ private static detectExistingLogger;
23
+ private static addLoggerDeclaration;
24
+ private static processParameters;
25
+ private static processMethodDeclarations;
26
+ private static processCatchBlocks;
27
+ run(): Promise<RflibLoggingApexInstrumentResult>;
28
+ private processDirectory;
29
+ private processTestFile;
30
+ private instrumentApexClass;
31
+ }
@@ -0,0 +1,232 @@
1
+ /* eslint-disable no-await-in-loop */
2
+ /* eslint-disable @typescript-eslint/quotes */
3
+ /* eslint-disable sf-plugin/no-missing-messages */
4
+ import * as fs from 'node:fs';
5
+ import * as path from 'node:path';
6
+ import { SfCommand, Flags } from '@salesforce/sf-plugins-core';
7
+ import { Messages, Logger } from '@salesforce/core';
8
+ // eslint-disable-next-line import/no-extraneous-dependencies
9
+ import * as prettier from 'prettier';
10
+ Messages.importMessagesDirectoryFromMetaUrl(import.meta.url);
11
+ const messages = Messages.loadMessages('rflib-plugin', 'rflib.logging.apex.instrument');
12
+ const methodRegex = /(@AuraEnabled\s*[\s\S]*?)?\b(public|private|protected|global)\s+(static\s+)?(?:(\w+(?:\s*<(?:[^<>]|<[^<>]*>)*>)?)|void)\s+(\w+)\s*\(([\s\S]*?)\)\s*{/g;
13
+ const classRegex = /\bclass\s+\w+\s*{/;
14
+ const classLevelLoggerRegex = /\bprivate\s+(?:static\s+)?(?:final\s+)?rflib_Logger\s+(\w+)\b/;
15
+ const genericArgsRegex = /<[^>]+>/g;
16
+ const catchRegex = /catch\s*\(\s*\w+\s+(\w+)\s*\)\s*{/g;
17
+ const testSetupRegex = /@TestSetup\s+((public|private|protected|global)s+)?(?:static\s+)?void\s+(\w+)\s*\([^)]*\)\s*{/g;
18
+ const PRIMITIVE_TYPES = new Set([
19
+ 'STRING',
20
+ 'INTEGER',
21
+ 'LONG',
22
+ 'DECIMAL',
23
+ 'DOUBLE',
24
+ 'BOOLEAN',
25
+ 'DATE',
26
+ 'DATETIME',
27
+ 'TIME',
28
+ 'ID',
29
+ ]);
30
+ export default class RflibLoggingApexInstrument extends SfCommand {
31
+ static summary = messages.getMessage('summary');
32
+ static description = messages.getMessage('description');
33
+ static examples = messages.getMessages('examples');
34
+ static flags = {
35
+ sourcepath: Flags.string({
36
+ summary: messages.getMessage('flags.sourcepath.summary'),
37
+ description: messages.getMessage('flags.sourcepath.description'),
38
+ char: 's',
39
+ required: true,
40
+ }),
41
+ dryrun: Flags.boolean({
42
+ summary: messages.getMessage('flags.dryrun.summary'),
43
+ description: messages.getMessage('flags.dryrun.description'),
44
+ char: 'd',
45
+ default: false,
46
+ }),
47
+ prettier: Flags.boolean({
48
+ summary: messages.getMessage('flags.prettier.summary'),
49
+ description: messages.getMessage('flags.prettier.description'),
50
+ char: 'p',
51
+ default: false,
52
+ }),
53
+ };
54
+ logger;
55
+ processedFiles = 0;
56
+ modifiedFiles = 0;
57
+ formattedFiles = 0;
58
+ prettierConfig = {
59
+ parser: 'apex',
60
+ plugins: ['prettier-plugin-apex'],
61
+ printWidth: 120,
62
+ tabWidth: 4,
63
+ useTabs: false,
64
+ singleQuote: true,
65
+ };
66
+ static isComplexType(paramType) {
67
+ return (paramType.includes('<') ||
68
+ paramType.includes('[') ||
69
+ paramType === 'Object' ||
70
+ !PRIMITIVE_TYPES.has(paramType.toUpperCase()));
71
+ }
72
+ static detectExistingLogger(content) {
73
+ const match = content.match(classLevelLoggerRegex);
74
+ return {
75
+ exists: classLevelLoggerRegex.test(content),
76
+ loggerVariableName: match ? match[1] : 'LOGGER',
77
+ };
78
+ }
79
+ static addLoggerDeclaration(content, className) {
80
+ const { exists, loggerVariableName } = RflibLoggingApexInstrument.detectExistingLogger(content);
81
+ if (!exists) {
82
+ const loggerDeclaration = `private static final rflib_Logger ${loggerVariableName} = rflib_LoggerUtil.getFactory().createLogger('${className}');`;
83
+ return content.replace(classRegex, `$&\n ${loggerDeclaration}`);
84
+ }
85
+ return content;
86
+ }
87
+ static processParameters(args) {
88
+ const parameters = args
89
+ ? args
90
+ .replaceAll(genericArgsRegex, '')
91
+ .split(',')
92
+ .map((param) => param.trim())
93
+ : [];
94
+ const logArgs = parameters.length > 0 && parameters[0] !== ''
95
+ ? `, new Object[] { ${parameters
96
+ .map((p) => {
97
+ const parts = p.split(' ');
98
+ const paramType = parts[0];
99
+ const paramName = parts.length > 1 ? parts[1] : parts[0];
100
+ return this.isComplexType(paramType) ? `JSON.serialize(${paramName})` : paramName;
101
+ })
102
+ .join(', ')} }`
103
+ : '';
104
+ return { paramList: parameters, logArgs };
105
+ }
106
+ static processMethodDeclarations(content, loggerName) {
107
+ return content.replace(methodRegex, (match, auraEnabled, access, isStatic, returnType, methodName, args) => {
108
+ const { paramList, logArgs } = RflibLoggingApexInstrument.processParameters(args);
109
+ let newMethod = match + '\n';
110
+ newMethod += ` ${loggerName}.info('${methodName}(${paramList.map((_, i) => `{${i}}`).join(', ')})'${logArgs});\n`;
111
+ return newMethod;
112
+ });
113
+ }
114
+ static processCatchBlocks(content, loggerName) {
115
+ return content.replace(catchRegex, (match, exceptionVar, offset) => {
116
+ // Get content before catch block
117
+ const contentBeforeCatch = content.substring(0, offset);
118
+ // Find all method declarations before this catch block
119
+ const methodMatches = [...contentBeforeCatch.matchAll(methodRegex)];
120
+ // Get last method match (closest to catch block)
121
+ const lastMethodMatch = methodMatches[methodMatches.length - 1];
122
+ // Extract method name from match or use 'unknown'
123
+ const methodName = lastMethodMatch
124
+ ? lastMethodMatch[5] // Group 4 contains method name in methodRegex
125
+ : 'unknown';
126
+ return `${match}\n ${loggerName}.error('An error occurred in ${methodName}()', ${exceptionVar.trim()});`;
127
+ });
128
+ }
129
+ async run() {
130
+ this.logger = await Logger.child(this.ctor.name);
131
+ const startTime = Date.now();
132
+ const { flags } = await this.parse(RflibLoggingApexInstrument);
133
+ const sourcePath = flags.sourcepath;
134
+ const isDryRun = flags.dryrun;
135
+ const usePrettier = flags.prettier;
136
+ this.log(`Scanning Apex classes in ${sourcePath} and sub directories`);
137
+ this.spinner.start('Running...');
138
+ await this.processDirectory(sourcePath, isDryRun, usePrettier);
139
+ this.spinner.stop();
140
+ const duration = Date.now() - startTime;
141
+ this.logger.debug(`Completed instrumentation in ${duration}ms`);
142
+ this.log(`\nInstrumentation complete.`);
143
+ this.log(`Processed files: ${this.processedFiles}`);
144
+ this.log(`Modified files: ${this.modifiedFiles}`);
145
+ this.log(`Formatted files: ${this.formattedFiles}`);
146
+ return {
147
+ processedFiles: this.processedFiles,
148
+ modifiedFiles: this.modifiedFiles,
149
+ formattedFiles: this.formattedFiles,
150
+ };
151
+ }
152
+ async processDirectory(dirPath, isDryRun, usePrettier) {
153
+ this.logger.debug(`Processing directory: ${dirPath}`);
154
+ const files = await fs.promises.readdir(dirPath);
155
+ for (const file of files) {
156
+ const filePath = path.join(dirPath, file);
157
+ const stat = await fs.promises.stat(filePath);
158
+ if (stat.isDirectory()) {
159
+ await this.processDirectory(filePath, isDryRun, usePrettier);
160
+ }
161
+ else if (file.endsWith('Test.cls')) {
162
+ await this.processTestFile(filePath, isDryRun);
163
+ }
164
+ else if (file.endsWith('.cls')) {
165
+ await this.instrumentApexClass(filePath, isDryRun, usePrettier);
166
+ }
167
+ }
168
+ }
169
+ async processTestFile(filePath, isDryRun) {
170
+ this.logger.debug(`Processing test file: ${filePath}`);
171
+ let content = await fs.promises.readFile(filePath, 'utf8');
172
+ const originalContent = content;
173
+ content = content.replace(testSetupRegex, (match) => `${match}\n rflib_TestUtil.prepareLoggerForUnitTests();`);
174
+ if (content !== originalContent) {
175
+ this.modifiedFiles++;
176
+ if (!isDryRun) {
177
+ await fs.promises.writeFile(filePath, content);
178
+ this.logger.info(`Modified test file: ${filePath}`);
179
+ }
180
+ else {
181
+ this.logger.info(`Would modify test file: ${filePath}`);
182
+ }
183
+ }
184
+ }
185
+ async instrumentApexClass(filePath, isDryRun, usePrettier) {
186
+ const className = path.basename(filePath, '.cls');
187
+ this.logger.debug(`Processing class: ${className}`);
188
+ try {
189
+ this.processedFiles++;
190
+ let content = await fs.promises.readFile(filePath, 'utf8');
191
+ const originalContent = content;
192
+ const { loggerVariableName } = RflibLoggingApexInstrument.detectExistingLogger(content);
193
+ content = RflibLoggingApexInstrument.addLoggerDeclaration(content, className);
194
+ content = RflibLoggingApexInstrument.processMethodDeclarations(content, loggerVariableName);
195
+ content = RflibLoggingApexInstrument.processCatchBlocks(content, loggerVariableName);
196
+ if (content !== originalContent) {
197
+ this.modifiedFiles++;
198
+ if (!isDryRun) {
199
+ try {
200
+ const finalContent = usePrettier ? await prettier.format(content, this.prettierConfig) : content;
201
+ await fs.promises.writeFile(filePath, finalContent);
202
+ if (usePrettier) {
203
+ this.formattedFiles++;
204
+ this.logger.info(`Modified and formatted: ${filePath}`);
205
+ }
206
+ else {
207
+ this.logger.info(`Modified: ${filePath}`);
208
+ }
209
+ }
210
+ catch (error) {
211
+ if (error instanceof Error) {
212
+ this.logger.warn(`Failed to format ${filePath}: ${error.message}`);
213
+ }
214
+ else {
215
+ this.logger.warn(`Failed to format ${filePath}: ${String(error)}`);
216
+ }
217
+ await fs.promises.writeFile(filePath, content);
218
+ this.logger.info(`Modified without formatting: ${filePath}`);
219
+ }
220
+ }
221
+ else {
222
+ this.logger.info(`Would modify: ${filePath}`);
223
+ }
224
+ }
225
+ }
226
+ catch (error) {
227
+ this.logger.error(`Error processing class ${className}`, error);
228
+ throw error;
229
+ }
230
+ }
231
+ }
232
+ //# sourceMappingURL=instrument.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"instrument.js","sourceRoot":"","sources":["../../../../../src/commands/rflib/logging/apex/instrument.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,32 @@
1
+ import { SfCommand } from '@salesforce/sf-plugins-core';
2
+ export type RflibLoggingLwcInstrumentResult = {
3
+ processedFiles: number;
4
+ modifiedFiles: number;
5
+ formattedFiles: number;
6
+ };
7
+ export default class RflibLoggingLwcInstrument extends SfCommand<RflibLoggingLwcInstrumentResult> {
8
+ static readonly summary: string;
9
+ static readonly description: string;
10
+ static readonly examples: string[];
11
+ static readonly flags: {
12
+ sourcepath: import("@oclif/core/interfaces").OptionFlag<string, import("@oclif/core/interfaces").CustomOptions>;
13
+ dryrun: import("@oclif/core/interfaces").BooleanFlag<boolean>;
14
+ prettier: import("@oclif/core/interfaces").BooleanFlag<boolean>;
15
+ };
16
+ private logger;
17
+ private processedFiles;
18
+ private modifiedFiles;
19
+ private formattedFiles;
20
+ private readonly prettierConfig;
21
+ private static detectExistingImport;
22
+ private static detectExistingLogger;
23
+ private static addImportAndLogger;
24
+ private static findEnclosingMethod;
25
+ private static processIfStatements;
26
+ private static processMethodLogging;
27
+ private static processPromiseChains;
28
+ private static processTryCatchBlocks;
29
+ run(): Promise<RflibLoggingLwcInstrumentResult>;
30
+ private processDirectory;
31
+ private instrumentLwcFile;
32
+ }
@@ -0,0 +1,238 @@
1
+ /* eslint-disable @typescript-eslint/prefer-nullish-coalescing */
2
+ /* eslint-disable no-await-in-loop */
3
+ /* eslint-disable @typescript-eslint/quotes */
4
+ /* eslint-disable sf-plugin/no-missing-messages */
5
+ import * as fs from 'node:fs';
6
+ import * as path from 'node:path';
7
+ import { SfCommand, Flags } from '@salesforce/sf-plugins-core';
8
+ import { Messages, Logger } from '@salesforce/core';
9
+ // eslint-disable-next-line import/no-extraneous-dependencies
10
+ import * as prettier from 'prettier';
11
+ Messages.importMessagesDirectoryFromMetaUrl(import.meta.url);
12
+ const messages = Messages.loadMessages('rflib-plugin', 'rflib.logging.lwc.instrument');
13
+ const importRegex = /import\s*{\s*createLogger\s*}\s*from\s*['"]c\/rflibLogger['"]/;
14
+ const loggerRegex = /const\s+(\w+)\s*=\s*createLogger\s*\(['"]([\w-]+)['"]\)/;
15
+ const methodRegex = /(?:async\s+)?(?!(?:if|switch|case|while|for|catch)\b)(\b\w+)\s*\((.*?)\)\s*{/g;
16
+ const exportDefaultRegex = /export\s+default\s+class\s+(\w+)/;
17
+ const ifStatementRegex = /if\s*\((.*?)\)\s*{([\s\S]*?)}|if\s*\((.*?)\)\s*([^{\n].*?)(?=\n|$)/g;
18
+ const elseRegex = /}\s*else\s*{([\s\S]*?)}|}\s*else\s+([^{\n].*?)(?=\n|$)/g;
19
+ const promiseChainRegex = /\.(then|catch|finally)\s*\(\s*(?:async\s+)?(?:\(?([^)]*)\)?)?\s*=>\s*(?:\{((?:[^{}]|`[^`]*`)*?)\}|([^{;]*(?:\([^)]*\))*)(?=(?:\)\))|\)|\.))/g;
20
+ const tryCatchBlockRegex = /try\s*{[\s\S]*?}\s*catch\s*\(([^)]*)\)\s*{/g;
21
+ export default class RflibLoggingLwcInstrument extends SfCommand {
22
+ static summary = messages.getMessage('summary');
23
+ static description = messages.getMessage('description');
24
+ static examples = messages.getMessages('examples');
25
+ static flags = {
26
+ sourcepath: Flags.string({
27
+ char: 's',
28
+ required: true,
29
+ summary: messages.getMessage('flags.sourcepath.summary'),
30
+ description: messages.getMessage('flags.sourcepath.description')
31
+ }),
32
+ dryrun: Flags.boolean({
33
+ char: 'd',
34
+ default: false,
35
+ summary: messages.getMessage('flags.dryrun.summary'),
36
+ description: messages.getMessage('flags.dryrun.description')
37
+ }),
38
+ prettier: Flags.boolean({
39
+ char: 'p',
40
+ default: false,
41
+ summary: messages.getMessage('flags.prettier.summary'),
42
+ description: messages.getMessage('flags.prettier.description')
43
+ })
44
+ };
45
+ logger;
46
+ processedFiles = 0;
47
+ modifiedFiles = 0;
48
+ formattedFiles = 0;
49
+ prettierConfig = {
50
+ parser: 'babel',
51
+ printWidth: 120,
52
+ tabWidth: 4,
53
+ useTabs: false,
54
+ singleQuote: true
55
+ };
56
+ static detectExistingImport(content) {
57
+ return importRegex.test(content);
58
+ }
59
+ static detectExistingLogger(content) {
60
+ const match = content.match(loggerRegex);
61
+ return {
62
+ exists: match !== null,
63
+ loggerName: match ? match[1] : 'logger'
64
+ };
65
+ }
66
+ static addImportAndLogger(content, componentName) {
67
+ let modified = content;
68
+ if (!this.detectExistingImport(content)) {
69
+ modified = `import { createLogger } from 'c/rflibLogger';\n${modified}`;
70
+ }
71
+ const { exists, loggerName } = this.detectExistingLogger(content);
72
+ if (!exists) {
73
+ const exportMatch = content.match(exportDefaultRegex);
74
+ const className = exportMatch ? exportMatch[1] : componentName;
75
+ const loggerDeclaration = `\nconst ${loggerName} = createLogger('${className}');\n`;
76
+ modified = modified.replace(exportDefaultRegex, `${loggerDeclaration}$&`);
77
+ }
78
+ return modified;
79
+ }
80
+ static findEnclosingMethod(content, position) {
81
+ const beforeCatch = content.substring(0, position);
82
+ const methods = [...beforeCatch.matchAll(methodRegex)].reverse();
83
+ const closestMethod = methods[0];
84
+ return closestMethod ? closestMethod[1] : 'unknown';
85
+ }
86
+ static processIfStatements(content, loggerName) {
87
+ let lastCondition = '';
88
+ // First process if statements and store conditions
89
+ let modified = content.replace(ifStatementRegex, (match, blockCondition, blockBody, lineCondition, lineBody) => {
90
+ const condition = typeof blockCondition === 'string' ? blockCondition : (typeof lineCondition === 'string' ? lineCondition : '');
91
+ lastCondition = condition.trim();
92
+ const logStatement = `${loggerName}.debug(\`if (${lastCondition})\`);`;
93
+ if (blockBody) {
94
+ return `if (${condition}) {\n ${logStatement}${blockBody}}`;
95
+ }
96
+ else {
97
+ return `if (${condition}) {\n ${logStatement}\n ${lineBody}\n }`;
98
+ }
99
+ });
100
+ // Then process else blocks using stored condition
101
+ modified = modified.replace(elseRegex, (match, blockBody, lineBody) => {
102
+ const logStatement = `${loggerName}.debug(\`else for if (${lastCondition})\`);`;
103
+ if (blockBody) {
104
+ return `} else {\n ${logStatement}${blockBody}}`;
105
+ }
106
+ else {
107
+ return `} else {\n ${logStatement}\n ${lineBody}\n }`;
108
+ }
109
+ });
110
+ return modified;
111
+ }
112
+ static processMethodLogging(content, loggerName) {
113
+ // First handle methods
114
+ let modified = content.replace(methodRegex, (match, methodName, args) => {
115
+ const parameters = args.split(',').map(p => p.trim()).filter(p => p);
116
+ const placeholders = parameters.map((_, i) => `{${i}}`).join(', ');
117
+ const logArgs = parameters.length > 0 ? `, ${parameters.join(', ')}` : '';
118
+ return `${match}\n ${loggerName}.info('${methodName}(${placeholders})'${logArgs});`;
119
+ });
120
+ // Then handle if statements
121
+ modified = this.processIfStatements(modified, loggerName);
122
+ return modified;
123
+ }
124
+ static processPromiseChains(content, loggerName) {
125
+ return content.replace(promiseChainRegex, (match, type, param, blockBody, singleLineBody, offset) => {
126
+ const methodName = this.findEnclosingMethod(content, offset);
127
+ const paramName = typeof param === 'string' ? param.trim() : (type === 'then' ? 'result' : 'error');
128
+ const indentation = match.match(/\n\s*/)?.[0] || '\n ';
129
+ let logStatement;
130
+ switch (type) {
131
+ case 'then':
132
+ logStatement = `${loggerName}.info('${methodName}() promise resolved. Result={0}', ${paramName});`;
133
+ break;
134
+ case 'catch':
135
+ logStatement = `${loggerName}.error('An error occurred in function ${methodName}()', ${paramName});`;
136
+ break;
137
+ case 'finally':
138
+ logStatement = `${loggerName}.info('${methodName}() promise chain completed');`;
139
+ break;
140
+ default:
141
+ logStatement = '';
142
+ }
143
+ if (singleLineBody) {
144
+ let trimmedSingleLineBody = singleLineBody.trim();
145
+ if (trimmedSingleLineBody.split(')').length > trimmedSingleLineBody.split('(').length) {
146
+ trimmedSingleLineBody = trimmedSingleLineBody.slice(0, -1);
147
+ }
148
+ return `.${type}((${paramName}) => {
149
+ ${logStatement}
150
+ return ${trimmedSingleLineBody};
151
+ }`;
152
+ }
153
+ if (blockBody) {
154
+ return `.${type}((${paramName}) => {${indentation}${logStatement}${indentation}${blockBody}}`;
155
+ }
156
+ return match;
157
+ });
158
+ }
159
+ static processTryCatchBlocks(content, loggerName) {
160
+ return content.replace(tryCatchBlockRegex, (match, exceptionVar, offset) => {
161
+ const methodName = this.findEnclosingMethod(content, offset);
162
+ const errorVar = exceptionVar.trim().split(' ')[0] || 'error';
163
+ return match.replace(/catch\s*\(([^)]*)\)\s*{/, `catch(${exceptionVar}) {
164
+ ${loggerName}.error('An error occurred in function ${methodName}()', ${errorVar});`);
165
+ });
166
+ }
167
+ async run() {
168
+ this.logger = await Logger.child(this.ctor.name);
169
+ const { flags } = await this.parse(RflibLoggingLwcInstrument);
170
+ this.log(`Scanning LWC components in ${flags.sourcepath}...`);
171
+ await this.processDirectory(flags.sourcepath, flags.dryrun, flags.prettier);
172
+ this.log(`\nInstrumentation complete.`);
173
+ this.log(`Processed files: ${this.processedFiles}`);
174
+ this.log(`Modified files: ${this.modifiedFiles}`);
175
+ this.log(`Formatted files: ${this.formattedFiles}`);
176
+ return {
177
+ processedFiles: this.processedFiles,
178
+ modifiedFiles: this.modifiedFiles,
179
+ formattedFiles: this.formattedFiles
180
+ };
181
+ }
182
+ async processDirectory(dirPath, isDryRun, usePrettier) {
183
+ const files = await fs.promises.readdir(dirPath);
184
+ for (const file of files) {
185
+ const filePath = path.join(dirPath, file);
186
+ const stat = await fs.promises.stat(filePath);
187
+ if (stat.isDirectory()) {
188
+ await this.processDirectory(filePath, isDryRun, usePrettier);
189
+ }
190
+ else if (file.endsWith('.js') && !path.dirname(filePath).includes('aura')) {
191
+ await this.instrumentLwcFile(filePath, isDryRun, usePrettier);
192
+ }
193
+ }
194
+ }
195
+ async instrumentLwcFile(filePath, isDryRun, usePrettier) {
196
+ const componentName = path.basename(path.dirname(filePath));
197
+ this.logger.debug(`Processing LWC: ${componentName}`);
198
+ try {
199
+ this.processedFiles++;
200
+ let content = await fs.promises.readFile(filePath, 'utf8');
201
+ const originalContent = content;
202
+ const { loggerName } = RflibLoggingLwcInstrument.detectExistingLogger(content);
203
+ content = RflibLoggingLwcInstrument.addImportAndLogger(content, componentName);
204
+ content = RflibLoggingLwcInstrument.processMethodLogging(content, loggerName);
205
+ content = RflibLoggingLwcInstrument.processTryCatchBlocks(content, loggerName);
206
+ content = RflibLoggingLwcInstrument.processPromiseChains(content, loggerName);
207
+ if (content !== originalContent) {
208
+ this.modifiedFiles++;
209
+ if (!isDryRun) {
210
+ try {
211
+ const finalContent = usePrettier ? await prettier.format(content, this.prettierConfig) : content;
212
+ await fs.promises.writeFile(filePath, finalContent);
213
+ if (usePrettier) {
214
+ this.formattedFiles++;
215
+ this.logger.info(`Modified and formatted: ${filePath}`);
216
+ }
217
+ else {
218
+ this.logger.info(`Modified: ${filePath}`);
219
+ }
220
+ }
221
+ catch (error) {
222
+ this.logger.warn(`Failed to format ${filePath}: ${error instanceof Error ? error.message : String(error)}`);
223
+ await fs.promises.writeFile(filePath, content);
224
+ this.logger.info(`Modified without formatting: ${filePath}`);
225
+ }
226
+ }
227
+ else {
228
+ this.logger.info(`Would modify: ${filePath}`);
229
+ }
230
+ }
231
+ }
232
+ catch (error) {
233
+ this.logger.error(`Error processing LWC ${componentName}`, error);
234
+ throw error;
235
+ }
236
+ }
237
+ }
238
+ //# sourceMappingURL=instrument.js.map