Skip to content

Commit 9de1322

Browse files
committed
perf(linter/plugins): lazily deserialize settings JSON (#15395)
Follow-on after #14724. Deserialize and deep freeze settings lazily on demand. These operations are not so expensive, but if settings object is large, they're not so cheap either. So avoid that work unless `context.settings` is accessed. A better eventual solution will be to send all settings objects over to JS in one go after parsing all configs, rather than again and again for every file, but this is a simple optimization to get a bit of gain in the meantime.
1 parent fde753e commit 9de1322

File tree

2 files changed

+29
-12
lines changed

2 files changed

+29
-12
lines changed

apps/oxlint/src-js/plugins/context.ts

Lines changed: 26 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -55,23 +55,30 @@ let cwd: string | null = null;
5555
*/
5656
export let setupContextForFile: (context: Context, ruleIndex: number, filePath: string) => void;
5757

58-
// Settings for current file. Set before linting a file by `setSettingsForFile`.
59-
let settings: Record<string, unknown> = null;
58+
// Settings for current file.
59+
// `settingsJSON` is set before linting a file by `setSettingsForFile`.
60+
// `settings` is deserialized from `settingsJSON` lazily upon first access.
61+
let settingsJSON: string | null = null;
62+
let settings: Record<string, unknown> | null = null;
6063

6164
/**
62-
* Updates the settings record for the file.
63-
* Settings are made immutable so that plugins can't change other plugin's behavior.
65+
* Updates the settings for the file.
6466
*
6567
* TODO(perf): Settings are de/serialized once per file to accommodate folder level settings,
6668
* even if the settings haven't changed.
6769
*
68-
* @param settingsJSON - Settings for the file as JSON
70+
* @param settingsJSONInput - Settings for the file as JSON
6971
*/
70-
export function setSettingsForFile(settingsJSON: string) {
71-
// Deep freeze the settings object, to prevent any mutation of the settings from plugins.
72-
// If there's a use case for mutation, we can relax this restriction.
73-
settings = JSON.parse(settingsJSON);
74-
deepFreezeSettings(settings);
72+
export function setSettingsForFile(settingsJSONInput: string) {
73+
settingsJSON = settingsJSONInput;
74+
}
75+
76+
/**
77+
* Reset settings.
78+
*/
79+
export function resetSettings() {
80+
settings = null;
81+
settingsJSON = null;
7582
}
7683

7784
// Internal data within `Context` that don't want to expose to plugins.
@@ -165,6 +172,15 @@ export class Context {
165172

166173
get settings() {
167174
getInternal(this, 'access `context.settings`');
175+
176+
// Lazily deserialize settings from JSON
177+
if (settings === null) {
178+
// Deep freeze the settings object, to prevent any mutation of the settings from plugins.
179+
// If there's a use case for mutation, we can relax this restriction.
180+
settings = JSON.parse(settingsJSON);
181+
deepFreezeSettings(settings);
182+
}
183+
168184
return settings;
169185
}
170186

apps/oxlint/src-js/plugins/lint.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { diagnostics, setSettingsForFile, setupContextForFile } from './context.js';
1+
import { diagnostics, setSettingsForFile, resetSettings, setupContextForFile } from './context.js';
22
import { registeredRules } from './load.js';
33
import { ast, initAst, resetSourceAndAst, setupSourceForFile } from './source_code.js';
44
import { assertIs, getErrorMessage } from './utils.js';
@@ -181,6 +181,7 @@ function lintFileImpl(
181181
afterHooks.length = 0;
182182
}
183183

184-
// Reset source and AST, to free memory
184+
// Reset source, AST, and settings, to free memory
185185
resetSourceAndAst();
186+
resetSettings();
186187
}

0 commit comments

Comments
 (0)