Skip to content

Commit c0c04c7

Browse files
committed
fix: pr feedback from ai
1 parent 1600037 commit c0c04c7

14 files changed

Lines changed: 99 additions & 91 deletions

File tree

packages/salesforcedx-aura-language-server/src/aura-indexer/__tests__/indexer.spec.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -88,6 +88,7 @@ describe('indexer parsing content', () => {
8888
return Promise.resolve([]);
8989
}
9090
});
91+
jest.spyOn(sfdxFileSystemAccessor, 'updateFileContent').mockResolvedValue(undefined);
9192
});
9293

9394
it('aura indexer', async () => {

packages/salesforcedx-aura-language-server/src/aura-indexer/indexer.ts

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -22,11 +22,11 @@ import { AuraWorkspaceContext } from '../context/auraContext';
2222
import * as auraStandardImport from '../resources/aura-standard.json';
2323
import * as transformedAuraSystemImport from '../resources/transformed-aura-system.json';
2424

25-
// line-column ships no .d.ts; wrap the CJS/ESM default export in a typed factory so all
26-
// call sites get { line, col } | false without any unsafe-call or unsafe-assignment lint hits.
27-
// eslint-disable-next-line @typescript-eslint/no-unsafe-call, @typescript-eslint/no-unsafe-member-access
28-
const makeLineColumnFinder = (str: string): { fromIndex(idx: number): { line: number; col: number } | false } =>
29-
new (LineColumnFinderModule.default ?? LineColumnFinderModule)(str);
25+
// Handle both ES module (default export) and CommonJS (namespace export) module resolution.
26+
// This is needed because Jest resolves modules differently than TypeScript's runtime,
27+
// and line-column may export differently depending on the module system.
28+
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment, @typescript-eslint/no-unsafe-member-access
29+
const LineColumnFinder = LineColumnFinderModule.default ?? LineColumnFinderModule;
3030

3131
export default class AuraIndexer implements Indexer {
3232
public readonly eventEmitter = new EventsEmitter();
@@ -111,8 +111,8 @@ export default class AuraIndexer implements Indexer {
111111
const documentation = this.trimQuotes(attributes.description ?? '');
112112
const jsName = this.trimQuotes(attributes.name ?? '');
113113
const type = this.trimQuotes(attributes.type ?? '');
114-
const startColumn = makeLineColumnFinder(markup).fromIndex(node.start) || { line: 0, col: 0 };
115-
const endColumn = makeLineColumnFinder(markup).fromIndex(node.end - 1) || { line: 0, col: 0 };
114+
const startColumn = new LineColumnFinder(markup).fromIndex(node.start);
115+
const endColumn = new LineColumnFinder(markup).fromIndex(node.end - 1);
116116

117117
const location: Location = {
118118
uri: URI.file(file).toString(),
@@ -299,8 +299,8 @@ export default class AuraIndexer implements Indexer {
299299
const attributes = node.attributes ?? {};
300300
const documentation = this.trimQuotes(attributes.description ?? '');
301301

302-
const startColumn = makeLineColumnFinder(contents).fromIndex(node.start) || { line: 0, col: 0 };
303-
const endColumn = makeLineColumnFinder(contents).fromIndex(node.end - 1) || { line: 0, col: 0 };
302+
const startColumn = new LineColumnFinder(contents).fromIndex(node.start);
303+
const endColumn = new LineColumnFinder(contents).fromIndex(node.end - 1);
304304

305305
const location: Location = {
306306
uri: URI.file(file).toString(),

packages/salesforcedx-aura-language-server/src/tern-server/ternServer.ts

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -90,12 +90,8 @@ const auraInstanceLastSort = (a: string, b: string): number =>
9090
* Collects all .js file paths under dirPath via workspace/findFiles (findFilesWithGlobAsync).
9191
* Returns an empty array when async find is not available.
9292
*/
93-
const getJsFilesRecursively = async (dirPath: string, fileSystemAccessor: LspFileSystemAccessor): Promise<string[]> => {
94-
if (!fileSystemAccessor.findFilesWithGlobAsync) {
95-
return [];
96-
}
97-
return (await fileSystemAccessor.findFilesWithGlobAsync('**/*.js', normalizePath(dirPath))) ?? [];
98-
};
93+
const getJsFilesRecursively = async (dirPath: string, fileSystemAccessor: LspFileSystemAccessor): Promise<string[]> =>
94+
(await fileSystemAccessor.findFilesWithGlobAsync('**/*.js', normalizePath(dirPath))) ?? [];
9995

10096
const loadPlugins = async (): Promise<{ aura: true; modules: true; doc_comment: true }> => {
10197
// Use require() to load plugins from file system (they're not bundled)

packages/salesforcedx-lightning-lsp-common/src/baseContext.ts

Lines changed: 18 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,7 @@ const isRecord = (value: unknown): value is Record<string, unknown> => typeof va
4444
/** Default config when sfdx-project.json is missing or unreadable (e.g. workspace/readFile not yet handled by client). */
4545
const DEFAULT_SFDX_PROJECT_CONFIG: SfdxProjectConfig = {
4646
packageDirectories: [],
47-
sfdxPackageDirsPattern: '{}'
47+
sfdxPackageDirsPattern: ''
4848
};
4949

5050
const readSfdxProjectConfig = async (
@@ -204,11 +204,23 @@ export abstract class BaseWorkspaceContext {
204204
public initSfdxProjectConfigCache: () => Promise<SfdxProjectConfig>;
205205
public fileSystemAccessor: LspFileSystemAccessor;
206206
private readonly sfdxTypingsDir?: string;
207-
public connection?: Connection;
207+
private _connection?: Connection;
208+
209+
/** The LSP connection. Setting this also propagates to the fileSystemAccessor. */
210+
public get connection(): Connection | undefined {
211+
return this._connection;
212+
}
213+
public set connection(conn: Connection | undefined) {
214+
this._connection = conn;
215+
if (conn) {
216+
this.fileSystemAccessor.setConnection(conn);
217+
}
218+
}
208219

209220
/**
210221
* @param workspaceRoots
211222
* @param fileSystemAccessor
223+
* @param connection
212224
* @param options Optional. Pass sfdxTypingsDir for typings copy (web-safe; avoids __dirname).
213225
*/
214226
constructor(
@@ -222,8 +234,8 @@ export abstract class BaseWorkspaceContext {
222234

223235
this.findNamespaceRootsUsingTypeCache = utils.memoize(() => this.findNamespaceRootsUsingType());
224236
this.initSfdxProjectConfigCache = utils.memoize(async () => this.initSfdxProject());
225-
this.connection = connection;
226237
this.fileSystemAccessor = fileSystemAccessor;
238+
this.connection = connection;
227239
this.sfdxTypingsDir = options?.sfdxTypingsDir;
228240
}
229241

@@ -480,7 +492,7 @@ export abstract class BaseWorkspaceContext {
480492
const destPath = path.join(typingsDir, 'lds.d.ts');
481493
const content = await this.fileSystemAccessor.getFileContent(sourcePath);
482494
if (content) {
483-
await this.fileSystemAccessor.updateFileContent(destPath, content, this.connection);
495+
await this.fileSystemAccessor.updateFileContent(destPath, content);
484496
}
485497
} catch {
486498
// ignore
@@ -490,7 +502,7 @@ export abstract class BaseWorkspaceContext {
490502
const destPath = path.join(typingsDir, 'messageservice.d.ts');
491503
const content = await this.fileSystemAccessor.getFileContent(sourcePath);
492504
if (content) {
493-
await this.fileSystemAccessor.updateFileContent(destPath, content, this.connection);
505+
await this.fileSystemAccessor.updateFileContent(destPath, content);
494506
}
495507
} catch {
496508
// ignore
@@ -504,7 +516,7 @@ export abstract class BaseWorkspaceContext {
504516
const destPath = path.join(typingsDir, file.name);
505517
const content = await this.fileSystemAccessor.getFileContent(sourcePath);
506518
if (content) {
507-
await this.fileSystemAccessor.updateFileContent(destPath, content, this.connection);
519+
await this.fileSystemAccessor.updateFileContent(destPath, content);
508520
}
509521
} catch {
510522
// ignore

packages/salesforcedx-lightning-lsp-common/src/providers/lspFileSystemAccessor.ts

Lines changed: 14 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -162,9 +162,9 @@ export class LspFileSystemAccessor {
162162
}
163163
}
164164

165-
public async updateFileContent(uri: string, content: string, connection?: Connection): Promise<void> {
165+
public async updateFileContent(uri: string, content: string): Promise<void> {
166166
const normalizedUri = normalizePath(uri);
167-
if (connection) {
167+
if (this.connection) {
168168
const fileUri = getFileUriForPath(normalizedUri, this.workspaceFolderUri);
169169
const edit: WorkspaceEdit = {
170170
documentChanges: [
@@ -173,7 +173,7 @@ export class LspFileSystemAccessor {
173173
]
174174
};
175175
try {
176-
const result = await connection.sendRequest(ApplyWorkspaceEditRequest.type, {
176+
const result = await this.connection.sendRequest(ApplyWorkspaceEditRequest.type, {
177177
label: `Create ${path.basename(normalizedUri)}`,
178178
edit
179179
});
@@ -254,16 +254,17 @@ export class LspFileSystemAccessor {
254254
}
255255
}
256256

257-
public async deleteFile(pathOrUri: string, connection?: Connection): Promise<void> {
258-
if (!connection) return;
259-
const key = normalizePath(pathOrUri);
260-
const fileUri = key.includes('://') ? key : getFileUriForPath(key, this.workspaceFolderUri);
261-
const result = await connection.sendRequest<WorkspaceDeleteFileResult>(WORKSPACE_DELETE_FILE_REQUEST, {
262-
uri: fileUri
263-
});
264-
if (result?.error) {
265-
Logger.error(`[LspFileSystemAccessor] workspace/deleteFile failed for ${fileUri}: ${result.error}`);
266-
throw new Error(result.error);
257+
public async deleteFile(pathOrUri: string): Promise<void> {
258+
if (this.connection) {
259+
const key = normalizePath(pathOrUri);
260+
const fileUri = key.includes('://') ? key : getFileUriForPath(key, this.workspaceFolderUri);
261+
const result = await this.connection.sendRequest<WorkspaceDeleteFileResult>(WORKSPACE_DELETE_FILE_REQUEST, {
262+
uri: fileUri
263+
});
264+
if (result?.error) {
265+
Logger.error(`[LspFileSystemAccessor] workspace/deleteFile failed for ${fileUri}: ${result.error}`);
266+
throw new Error(result.error);
267+
}
267268
}
268269
}
269270
}

packages/salesforcedx-lightning-lsp-common/src/workspaceReadFileHandler.ts

Lines changed: 13 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,8 @@ const getFs = Effect.flatMap(getServicesApi, api =>
5252
Effect.provide(api.services.FsService, api.services.FsService.Default)
5353
);
5454

55-
const logTo = (channel: vscode.OutputChannel, msg: string) => Effect.sync(() => channel.appendLine(msg));
55+
const logTo = (channel: Pick<vscode.OutputChannel, 'appendLine'>, msg: string) =>
56+
Effect.sync(() => channel.appendLine(msg));
5657

5758
/**
5859
* Register the workspace/readFile LSP request handler so the language server can request
@@ -61,10 +62,16 @@ const logTo = (channel: vscode.OutputChannel, msg: string) => Effect.sync(() =>
6162
*
6263
* Call this from the extension after the language client has started. Only extensions that
6364
* have salesforcedx-vscode-services as an extensionDependency should use this.
65+
*
66+
* @param outputChannel Optional channel to log handler activity. Pass the extension's existing
67+
* output channel (e.g. channelService) so logs appear there rather than in a new dedicated channel.
68+
* When omitted, handler activity is not logged.
6469
*/
65-
export const registerWorkspaceReadFileHandler = (client: WorkspaceReadFileClient): void => {
66-
const log = vscode.window.createOutputChannel('LWC workspace (client)');
67-
const findFilesLog = vscode.window.createOutputChannel('LWC workspace/findFiles (client)');
70+
export const registerWorkspaceReadFileHandler = (
71+
client: WorkspaceReadFileClient,
72+
outputChannel?: Pick<vscode.OutputChannel, 'appendLine'>
73+
): void => {
74+
const log = outputChannel ?? { appendLine: () => {} };
6875

6976
const handleReadFile = Effect.fn('WorkspaceHandler.readFile')(function* (params: WorkspaceReadFileParams) {
7077
const { uri } = params;
@@ -104,15 +111,10 @@ export const registerWorkspaceReadFileHandler = (client: WorkspaceReadFileClient
104111
const handleFindFiles = Effect.fn('WorkspaceHandler.findFiles')(function* (params: WorkspaceFindFilesParams) {
105112
const { baseFolderUri, pattern } = params;
106113
const baseUri = vscode.Uri.parse(baseFolderUri);
107-
yield* logTo(
108-
findFilesLog,
109-
`[findFiles] request baseFolderUri=${baseFolderUri} pattern=${pattern} scheme=${baseUri.scheme}`
110-
);
111-
yield* logTo(log, `[findFiles] request baseFolderUri=${baseFolderUri} pattern=${pattern}`);
114+
yield* logTo(log, `[findFiles] request baseFolderUri=${baseFolderUri} pattern=${pattern} scheme=${baseUri.scheme}`);
112115
const fs = yield* getFs;
113116
const uris = yield* fs.findFiles(new vscode.RelativePattern(baseUri, pattern));
114117
const urisStr = uris.map((u: vscode.Uri) => u.toString());
115-
yield* logTo(findFilesLog, `[findFiles] returned ${urisStr.length} uris`);
116118
yield* logTo(log, `[findFiles] success pattern=${pattern} uris=${urisStr.length}`);
117119
return { uris: urisStr };
118120
});
@@ -172,10 +174,8 @@ export const registerWorkspaceReadFileHandler = (client: WorkspaceReadFileClient
172174
Effect.sync(() => {
173175
const message = errorMessage(e);
174176
const stack = e instanceof Error ? e.stack : undefined;
175-
findFilesLog.appendLine(`[findFiles] error: ${message}`);
176-
if (stack) findFilesLog.appendLine(`[findFiles] stack: ${stack}`);
177-
findFilesLog.appendLine('[findFiles] returning undefined');
178177
log.appendLine(`[findFiles] error: ${message}`);
178+
if (stack) log.appendLine(`[findFiles] stack: ${stack}`);
179179
return { uris: undefined };
180180
})
181181
),

packages/salesforcedx-lwc-language-server/src/__tests__/lwcContext.test.ts

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
* For full license text, see LICENSE.txt file in the repo root or https://opensource.org/licenses/BSD-3-Clause
66
*/
77

8-
import { normalizePath } from '@salesforce/salesforcedx-lightning-lsp-common';
8+
import { normalizePath, NormalizedPath } from '@salesforce/salesforcedx-lightning-lsp-common';
99
import {
1010
buildSfdxContentMap,
1111
createMockWorkspaceFindFilesConnection,
@@ -19,6 +19,7 @@ import {
1919
sfdxFileSystemAccessor,
2020
UTILS_ROOT
2121
} from '@salesforce/salesforcedx-lightning-lsp-common/testUtils';
22+
import { minimatch } from 'minimatch';
2223
import { join, resolve } from 'node:path';
2324
import { Connection } from 'vscode-languageserver';
2425
import { URI } from 'vscode-uri';
@@ -54,6 +55,22 @@ beforeAll(() => {
5455
contentMap.delete(normalizePath(pathOrUri));
5556
return Promise.resolve();
5657
});
58+
jest
59+
.spyOn(sfdxFileSystemAccessor, 'findFilesWithGlobAsync')
60+
.mockImplementation((pattern: string, basePath: NormalizedPath) => {
61+
const base = normalizePath(basePath);
62+
const prefix = `${base}/`;
63+
const results: NormalizedPath[] = [];
64+
for (const key of contentMap.keys()) {
65+
if (key.startsWith(prefix)) {
66+
const relative = key.slice(prefix.length);
67+
if (minimatch(relative, pattern)) {
68+
results.push(key as NormalizedPath);
69+
}
70+
}
71+
}
72+
return Promise.resolve(results);
73+
});
5774
});
5875

5976
describe('LWCWorkspaceContext', () => {

packages/salesforcedx-lwc-language-server/src/__tests__/lwcDataProvider.test.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,10 @@ beforeAll(() => {
6767
const key = normalizePath(uri);
6868
return Promise.resolve(contentByPath[key]);
6969
});
70+
jest.spyOn(sfdxFileSystemAccessor, 'updateFileContent').mockImplementation((uri: string, content: string) => {
71+
contentByPath[normalizePath(uri)] = content;
72+
return Promise.resolve();
73+
});
7074
});
7175

7276
const componentIndexer: ComponentIndexer = new ComponentIndexer({

packages/salesforcedx-lwc-language-server/src/baseIndexer.ts

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,5 @@ export const getSfdxPackageDirsPattern = async (
9494
return '';
9595
}
9696

97-
const result = paths.length === 1 ? paths[0] : `{${paths.join(',')}}`;
98-
return result;
97+
return paths.length === 1 ? paths[0] : `{${paths.join(',')}}`;
9998
};

packages/salesforcedx-lwc-language-server/src/baseServer.ts

Lines changed: 1 addition & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -692,8 +692,6 @@ export abstract class BaseServer {
692692
}
693693
}
694694

695-
protected isInitializing = false;
696-
697695
/**
698696
* Performs delayed initialization of context and component indexer
699697
* Files are loaded into fileSystemAccessor via onDidOpen events
@@ -702,25 +700,11 @@ export abstract class BaseServer {
702700
try {
703701
this.context.initialize(this.workspaceType);
704702
const basePath = this.workspaceRoots[0];
705-
const findFilesTimeoutMs = 8000;
706-
const findFilesPromise = Promise.all([
703+
const [html, js, ts] = await Promise.all([
707704
this.fileSystemAccessor.findFilesWithGlobAsync('**/lwc/**/*.html', basePath),
708705
this.fileSystemAccessor.findFilesWithGlobAsync('**/lwc/**/*.js', basePath),
709706
this.fileSystemAccessor.findFilesWithGlobAsync('**/lwc/**/*.ts', basePath)
710707
]);
711-
let findFilesTimeoutId: ReturnType<typeof setTimeout>;
712-
const findFilesTimeoutPromise = new Promise<[NormalizedPath[], NormalizedPath[], NormalizedPath[]]>(
713-
(_, reject) => {
714-
findFilesTimeoutId = setTimeout(() => reject(new Error('findFiles timeout')), findFilesTimeoutMs);
715-
}
716-
);
717-
await findFilesPromise.finally(() => clearTimeout(findFilesTimeoutId!));
718-
const [html, js, ts] = await Promise.race([findFilesPromise, findFilesTimeoutPromise]).catch(err => {
719-
Logger.info(
720-
`[LWC] performDelayedInitialization: findFiles timed out or failed (${err instanceof Error ? err.message : String(err)}), continuing with hasLwcFiles=false`
721-
);
722-
return [[], [], []];
723-
});
724708
const hasLwcFiles = html.length > 0 || js.length > 0 || ts.length > 0;
725709
Logger.info(
726710
`[LWC] performDelayedInitialization: findFiles result html=${html.length} js=${js.length} ts=${ts.length} hasLwcFiles=${hasLwcFiles}`
@@ -738,7 +722,6 @@ export abstract class BaseServer {
738722
Logger.info(
739723
`[LWC] performDelayedInitialization: SFDX workspace but sfdx-project.json not found at ${sfdxProjectPath}, exiting early`
740724
);
741-
this.isInitializing = false;
742725
return;
743726
}
744727
}

0 commit comments

Comments
 (0)