Skip to content

NEW(esint): @W-18695515@ Manually resolve plugin conflicts … #304

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
Jun 5, 2025

Conversation

stephen-carter-at-sf
Copy link
Collaborator

… and forward events from background worker

// eslint-disable-next-line @typescript-eslint/no-explicit-any
async function dynamicallyImport(absJavaScriptFilePath: string): Promise<any> {
// To avoid issues with dynamically importing absolute paths on Windows, we need to convert to url with pathToFileURL.
const moduleUrl: string = pathToFileURL(absJavaScriptFilePath).href;
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

While working on this PR, I realized the more robust way to do dynamic imports is to use the pathToFileURL utility instead of manually replacing slashes for windows.

*/
export abstract class Engine {
export abstract class EngineEventEmitter {
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I split up the Engine class into 2 abstract classes so we can very easily reuse the event emitter methods for helper instances inside of an Engine. This made everything super clean instead of passing around function handles.

// Calculate configs for files
const calculatedConfigs: Linter.Config[] = await Promise.all(context.filesToScan.map(
f => eslint.calculateConfigForFile(f) as Linter.Config));
async calculateESLintContext(engineConfig: ESLintEngineConfig, eslintWorkspace: ESLintWorkspace, userConfigFile?: string): Promise<ESLintContext> {
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The implementation details did not change at all... just moved the function inside of a class so that we could make it an event emitter and forward the events from the eslint factory.

Comment on lines +58 to +64
worker.on('message', (msg: Event | {type: "output", output: Output}) => {
if (msg.type === "output") {
resolve(msg.output);
} else {
this.emitEvent(msg);
}
});
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Super elegant in my opinion! We can can forward all events from background worker threads to the main thread very cleanly.

Comment on lines +93 to +97
` for (const eventType of Object.values(engineApi.EventType)) {\n` +
` task.onEvent(eventType, (evt) => {\n` +
` parentPort.postMessage(evt);\n` +
` });\n` +
` }\n` +
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Where the magic happens. :-)

Co-authored-by: Juliet Shackell <[email protected]>
@@ -626,6 +604,53 @@ describe('Typical tests for the runRules method of ESLintEngine', () => {
expect(results.violations).toContainEqual(expectedTsViolation_noInvalidRegexp);
expect(results.violations).toContainEqual(expectedTsViolation_noWrapperObjectTypes);
});

it('When workspace contains custom config that installs same plugin as one of our base plugins, we resolve plugins, describe and run rules properly', async () => {
if (os.platform().startsWith('win')) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm fine with us disabling the test on Windows for runtime reasons, but I do want to be sure that you actually ran it at least once on Windows to make sure it passes?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done manually just now - yes it passes.

@@ -740,3 +740,12 @@ function wasFieldSuppliedByUser(engineOverrides: EngineOverrides, fieldName: str
function findCaseInsensitiveKey(obj: object, key: string): string | undefined {
return Object.keys(obj).find(k => k.toLowerCase() === key.toLowerCase());
}

// eslint-disable-next-line @typescript-eslint/no-explicit-any
async function dynamicallyImport(absJavaScriptFilePath: string): Promise<any> {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is the explicit any here truly necessary? Could you use an as-cast in the return statement?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Unfortunately no - the any is necessary. I tried even using unknown but we need to do post import validation that needs it to be any sadly.

@stephen-carter-at-sf stephen-carter-at-sf merged commit ef5b97a into dev Jun 5, 2025
7 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants