Summary
Custom Oxlint configuration defined by an environment is ignored when workspace configuration is generated.
Running either:
or
always generates .oxlintrc.json files that extend a cached configuration containing the default values, instead of the custom configuration from the environment.
Environment
- Windows 10 / Windows 11
- Bit v2.0.11
- Node.js v22.16
- Fresh workspace
- Clean pnpm store
Steps to reproduce
Create a custom environment:
bit init --default-scope projects
bit install
bit create react-env envs/my-react-env --aspect bitdev.react/react-env
bit create react-app apps/react-frontend --env projects/envs/my-react-env
Replace projects/envs/my-react-env/config/oxlintrc.json with:
{
"$schema": "https://raw.githubusercontent.com/oxc-project/oxc/main/npm/oxlint/configuration_schema.json",
"plugins": ["react", "unicorn", "typescript", "oxc"],
"settings": {
"react": {
"version": "19"
}
},
"options": {
"typeAware": true
},
"rules": {
"no-unused-vars": "off"
}
}
Then run either:
or
Bit reports:
5 config files added to workspace
2 typescript configurations added
projects\envs\tsconfig.json
tsconfig.json
2 oxlint configurations added
projects\envs\.oxlintrc.json
.oxlintrc.json
1 prettier configurations added
.prettierrc.cjs
Actual behavior
The generated .oxlintrc.json points to a cache:
{
"extends": [
"./node_modules/.cache/oxlintrc.bit.b3a5458e3e9b6949bee9213fc4dd6a329a94610a.json"
]
}
which contains the default configuration instead of the custom one:
{
"plugins": [
"react",
"unicorn",
"typescript",
"oxc"
],
"settings": {
"react": {
"version": "18"
}
},
"options": {
"typeAware": true
}
}
Expected behavior
The generated configuration should reference a cache file containing the effective configuration from the environment rather than the built-in defaults.
Root cause
After looking into EslintConfigWriter, the primary issue appears to be how it handles configurations provided via configPath.
When the writer is created like this:
EslintConfigWriter.from({
configPath: this.oxlintConfigPath,
tsconfig: this.tsconfigPath,
})
the configuration is supplied as a file path rather than an object.
The execution flow is:
EslintConfigWriter.create() calls computeEslintConfig().
computeEslintConfig() returns:
{ overrideConfigFile: configPath }
It sets overrideConfigFile, but never loads the file into overrideConfig.
Later, calcConfigFiles() only checks overrideConfig:
const config = this.eslintConfig.overrideConfig;
if (!config) {
return [];
}
Since overrideConfig is undefined, calcConfigFiles() returns an empty array without writing the environment's configuration to the cache.
As a result, Bit falls back to generating a cached .oxlintrc.bit.<hash>.json containing the default configuration, which is why custom values are lost and replaced with the defaults.
The fix would be to support both configuration modes consistently. Either:
calcConfigFiles() should read and serialize overrideConfigFile when overrideConfig is absent, or
computeEslintConfig() should populate overrideConfig from the supplied file before returning.
There is also a separate design limitation: EslintConfigWriter is still hardcoded around ESLint naming, so even with this bug fixed it would continue generating .eslintrc.json-style artifacts rather than Oxlint-specific ones.
Workaround
As a temporary workaround, I replaced the built-in EslintConfigWriter with a custom function that creates OxlintConfigWriter which reads the environment's config/oxlintrc.json directly, hashes its contents, writes the corresponding cached .oxlintrc.bit.<hash>.json and generates the extending .oxlintrc.json pointing to that cache file.
workspaceConfig(): ConfigWriterList {
const cwl = ConfigWriterList.from([
TypescriptConfigWriter.from({
tsconfig: this.tsconfigPath,
types: resolveTypes(__dirname, [this.tsTypesPath]),
}),
this.createOxlintConfigWriter(),
PrettierConfigWriter.from({
configPath: this.prettierConfigPath
})
]);
return cwl;
}
private createOxlintConfigWriter(): ConfigWriterHandler {
const oxlintConfigPath = this.oxlintConfigPath;
return {
name: "OxlintConfigWriter",
handler: () => {
const configContent = fs.readFileSync(oxlintConfigPath, "utf-8");
const configHash = sha1(configContent);
const configFileName = `.oxlintrc.bit.${configHash}.json`;
const entry: ConfigWriterEntry = {
id: "oxlint",
name: "OxlintConfigWriter",
patterns: ["**/.oxlintrc.json"],
calcConfigFiles: (): ConfigFile[] => {
console.log("Writing OXLint config from:", oxlintConfigPath);
return [
{
content: configContent,
name: configFileName,
hash: configHash,
},
];
},
generateExtendingFile: (args: GenerateExtendingConfigFilesArgs) => {
const oxlintConfigFile = args.writtenConfigFiles.find(
(file) => file.name.includes(".oxlintrc.bit")
);
if (!oxlintConfigFile) return undefined;
const extendingConfig = {
extends: [`{${oxlintConfigFile.name}}`],
};
const content = `${BIT_GENERATED_OXLINT_CONFIG_COMMENT}\n${JSON.stringify(extendingConfig, null, 2)}`;
return {
content,
name: ".oxlintrc.json",
extendingTarget: oxlintConfigFile,
useAbsPaths: false,
};
},
isBitGenerated: (filePath: string) => {
const content = fs.readFileSync(filePath).toString();
return content.includes(BIT_GENERATED_OXLINT_CONFIG_COMMENT);
},
};
return entry;
},
};
}
Additional observation: undocumented, dedicated Oxlint writer has similar issues
I just noticed there is an undocumented dedicated package, @teambit/oxc.linter.oxlint-linter, which looks to be the intended replacement for using EslintConfigWriter with Oxlint.
But, replacing EslintConfigWriter with it manually:
import { OxlintConfigWriter, OxlintLinter, OxlintTask } from "@teambit/oxc.linter.oxlint-linter";
OxlintConfigWriter.from({
configPath: this.oxlintConfigPath
}),
currently results in generating two configuration files, one that's correct (at the root of the workspace) and another one with default values (for the environment variable):
2 oxlint configurations added
projects\commonlib\envs\.oxlintrc.json
.oxlintrc.json
Summary
Custom Oxlint configuration defined by an environment is ignored when workspace configuration is generated.
Running either:
or
always generates
.oxlintrc.jsonfiles that extend a cached configuration containing the default values, instead of the custom configuration from the environment.Environment
Steps to reproduce
Create a custom environment:
Replace
projects/envs/my-react-env/config/oxlintrc.jsonwith:{ "$schema": "https://raw.githubusercontent.com/oxc-project/oxc/main/npm/oxlint/configuration_schema.json", "plugins": ["react", "unicorn", "typescript", "oxc"], "settings": { "react": { "version": "19" } }, "options": { "typeAware": true }, "rules": { "no-unused-vars": "off" } }Then run either:
bit ws-config writeor
Bit reports:
Actual behavior
The generated
.oxlintrc.jsonpoints to a cache:{ "extends": [ "./node_modules/.cache/oxlintrc.bit.b3a5458e3e9b6949bee9213fc4dd6a329a94610a.json" ] }which contains the default configuration instead of the custom one:
{ "plugins": [ "react", "unicorn", "typescript", "oxc" ], "settings": { "react": { "version": "18" } }, "options": { "typeAware": true } }Expected behavior
The generated configuration should reference a cache file containing the effective configuration from the environment rather than the built-in defaults.
Root cause
After looking into
EslintConfigWriter, the primary issue appears to be how it handles configurations provided viaconfigPath.When the writer is created like this:
the configuration is supplied as a file path rather than an object.
The execution flow is:
EslintConfigWriter.create()callscomputeEslintConfig().computeEslintConfig()returns:It sets
overrideConfigFile, but never loads the file intooverrideConfig.Later,
calcConfigFiles()only checksoverrideConfig:Since
overrideConfigisundefined,calcConfigFiles()returns an empty array without writing the environment's configuration to the cache.As a result, Bit falls back to generating a cached
.oxlintrc.bit.<hash>.jsoncontaining the default configuration, which is why custom values are lost and replaced with the defaults.The fix would be to support both configuration modes consistently. Either:
calcConfigFiles()should read and serializeoverrideConfigFilewhenoverrideConfigis absent, orcomputeEslintConfig()should populateoverrideConfigfrom the supplied file before returning.There is also a separate design limitation:
EslintConfigWriteris still hardcoded around ESLint naming, so even with this bug fixed it would continue generating.eslintrc.json-style artifacts rather than Oxlint-specific ones.Workaround
As a temporary workaround, I replaced the built-in
EslintConfigWriterwith a custom function that createsOxlintConfigWriterwhich reads the environment'sconfig/oxlintrc.jsondirectly, hashes its contents, writes the corresponding cached.oxlintrc.bit.<hash>.jsonand generates the extending.oxlintrc.jsonpointing to that cache file.Additional observation: undocumented, dedicated Oxlint writer has similar issues
I just noticed there is an undocumented dedicated package,
@teambit/oxc.linter.oxlint-linter, which looks to be the intended replacement for usingEslintConfigWriterwith Oxlint.But, replacing
EslintConfigWriterwith it manually:currently results in generating two configuration files, one that's correct (at the root of the workspace) and another one with default values (for the environment variable):