├── .node-version ├── plugin-verifier-ignored-problems.txt ├── sandbox ├── oxfmt │ └── nested-configs │ │ ├── .oxfmtrc.json │ │ ├── .gitignore │ │ ├── index.js │ │ ├── package.json │ │ └── package-lock.json └── oxlint │ ├── nested-configs │ ├── .gitignore │ ├── package-b │ │ ├── index.js │ │ └── .oxlintrc.json │ ├── index.js │ ├── package-c │ │ ├── index.js │ │ └── .oxlintrc.json │ ├── package-a │ │ ├── index.js │ │ ├── test.jsx │ │ └── .oxlintrc.json │ ├── package.json │ ├── .oxlintrc.json │ └── package-lock.json │ └── custom-config │ ├── .gitignore │ ├── .vscode │ └── settings.json │ ├── index.js │ ├── custom-oxlint.jsonc │ ├── package.json │ ├── .idea │ └── OxcSettings.xml │ └── package-lock.json ├── .gitignore ├── gradle ├── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties └── libs.versions.toml ├── src ├── test │ ├── testData │ │ └── oxlint │ │ │ └── highlighting │ │ │ ├── nested-config │ │ │ ├── subdirectory │ │ │ │ ├── index.js │ │ │ │ ├── index.expected.js │ │ │ │ └── .oxlintrc.json │ │ │ ├── index.js │ │ │ ├── package.json │ │ │ ├── index.expected.js │ │ │ ├── .oxlintrc.json │ │ │ └── package-lock.json │ │ │ ├── no-config │ │ │ ├── index.js │ │ │ ├── package.json │ │ │ ├── index.expected.js │ │ │ └── package-lock.json │ │ │ └── custom-config │ │ │ ├── index.js │ │ │ ├── package.json │ │ │ ├── custom-oxlint.jsonc │ │ │ ├── index.expected.js │ │ │ └── package-lock.json │ └── kotlin │ │ └── com │ │ └── github │ │ └── oxc │ │ └── project │ │ └── oxcintellijplugin │ │ ├── oxlint │ │ ├── NoConfigHighlightingTest.kt │ │ ├── NestedConfigHighlightingTest.kt │ │ └── CustomConfigHighlightingTest.kt │ │ └── extensions │ │ └── TestCaseExt.kt └── main │ ├── kotlin │ └── com │ │ └── github │ │ └── oxc │ │ └── project │ │ └── oxcintellijplugin │ │ ├── Constants.kt │ │ ├── ConfigurationMode.kt │ │ ├── OxcIcons.kt │ │ ├── oxlint │ │ ├── OxlintRunTrigger.kt │ │ ├── OxlintUnusedDisableDirectivesSeverity.kt │ │ ├── OxlintFixKind.kt │ │ ├── OxlintBundle.kt │ │ ├── listeners │ │ │ ├── OxlintEditorWatcher.kt │ │ │ └── OxlintConfigWatcher.kt │ │ ├── lsp │ │ │ ├── OxlintLspDiagnosticsSupport.kt │ │ │ ├── OxlintLspServerSupportProvider.kt │ │ │ └── OxlintLspServerDescriptor.kt │ │ ├── OxlintSchemaProviderFactory.kt │ │ ├── settings │ │ │ ├── OxlintSettingsState.kt │ │ │ ├── OxlintOnSaveFixAllActionInfo.kt │ │ │ └── OxlintSettings.kt │ │ ├── actions │ │ │ ├── OxlintFixAllOnSaveAction.kt │ │ │ └── OxlintFixAllAction.kt │ │ ├── services │ │ │ └── OxlintServerService.kt │ │ └── OxlintPackage.kt │ │ ├── ProcessCommandParameter.kt │ │ ├── ProcessCommandBuilder.kt │ │ ├── oxfmt │ │ ├── OxfmtBundle.kt │ │ ├── lsp │ │ │ ├── OxfmtLspFormattingSupport.kt │ │ │ ├── OxfmtLspServerSupportProvider.kt │ │ │ └── OxfmtLspServerDescriptor.kt │ │ ├── settings │ │ │ ├── OxfmtSettingsState.kt │ │ │ ├── OxfmtOnSaveFixAllActionInfo.kt │ │ │ ├── OxfmtSettings.kt │ │ │ └── OxfmtConfigurable.kt │ │ ├── listeners │ │ │ ├── OxfmtEditorWatcher.kt │ │ │ └── OxfmtConfigWatcher.kt │ │ ├── OxfmtSchemaProviderFactory.kt │ │ ├── actions │ │ │ ├── OxfmtFixAllOnSaveAction.kt │ │ │ └── OxfmtFixAllAction.kt │ │ ├── services │ │ │ └── OxfmtServerService.kt │ │ └── OxfmtPackage.kt │ │ ├── settings │ │ └── OxcOnSaveInfoProvider.kt │ │ ├── OxcIconProvider.kt │ │ ├── extensions │ │ └── FileExt.kt │ │ ├── NodeProcessCommandBuilder.kt │ │ ├── GeneralProcessCommandBuilder.kt │ │ └── OxcTargetRun.kt │ └── resources │ ├── messages │ ├── OxfmtBundle.properties │ └── OxlintBundle.properties │ ├── icons │ └── oxcRound.svg │ └── META-INF │ ├── pluginIcon.svg │ └── plugin.xml ├── settings.gradle.kts ├── .github ├── SECURITY.md ├── renovate.json ├── CONTRIBUTING.md └── workflows │ ├── run-ui-tests.yml │ └── release.yml ├── qodana.yml ├── .idea └── gradle.xml ├── MAINTENANCE.md ├── .run ├── Run Tests.run.xml ├── Run Plugin.run.xml └── Run Verifications.run.xml ├── LICENSE ├── README.md ├── gradle.properties ├── gradlew.bat └── CHANGELOG.md /.node-version: -------------------------------------------------------------------------------- 1 | 22.14.0 2 | -------------------------------------------------------------------------------- /plugin-verifier-ignored-problems.txt: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /sandbox/oxfmt/nested-configs/.oxfmtrc.json: -------------------------------------------------------------------------------- 1 | { 2 | } 3 | -------------------------------------------------------------------------------- /sandbox/oxfmt/nested-configs/.gitignore: -------------------------------------------------------------------------------- 1 | node_modules/ 2 | .idea/ 3 | -------------------------------------------------------------------------------- /sandbox/oxlint/nested-configs/.gitignore: -------------------------------------------------------------------------------- 1 | node_modules/ 2 | .idea/ 3 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .gradle 2 | .idea 3 | .intellijPlatform 4 | .qodana 5 | build 6 | node_modules 7 | -------------------------------------------------------------------------------- /sandbox/oxlint/custom-config/.gitignore: -------------------------------------------------------------------------------- 1 | node_modules/ 2 | .idea/ 3 | !.idea/OxcSettings.xml 4 | -------------------------------------------------------------------------------- /sandbox/oxlint/custom-config/.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "oxc.configPath": "./custom-oxlint.jsonc" 3 | } 4 | -------------------------------------------------------------------------------- /sandbox/oxlint/nested-configs/package-b/index.js: -------------------------------------------------------------------------------- 1 | console.log('Happy developing ✨') 2 | 3 | 4 | new Array([]) -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/oxc-project/oxc-intellij-plugin/HEAD/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /sandbox/oxlint/custom-config/index.js: -------------------------------------------------------------------------------- 1 | console.log('Happy developing ✨') 2 | 3 | debugger 4 | new Array([]) 5 | 6 | if (foo) foo++; 7 | -------------------------------------------------------------------------------- /sandbox/oxlint/nested-configs/index.js: -------------------------------------------------------------------------------- 1 | console.log('Happy developing ✨') 2 | 3 | debugger 4 | new Array([]) 5 | 6 | if (foo) foo++; 7 | -------------------------------------------------------------------------------- /sandbox/oxfmt/nested-configs/index.js: -------------------------------------------------------------------------------- 1 | console.log("Happy developing ✨"); 2 | 3 | debugger; 4 | new Array([]); 5 | 6 | if (foo) {foo++;} 7 | -------------------------------------------------------------------------------- /src/test/testData/oxlint/highlighting/nested-config/subdirectory/index.js: -------------------------------------------------------------------------------- 1 | console.log('Happy developing ✨') 2 | 3 | 4 | new Array([]) 5 | -------------------------------------------------------------------------------- /sandbox/oxlint/nested-configs/package-c/index.js: -------------------------------------------------------------------------------- 1 | console.log('Happy developing ✨') 2 | 3 | debugger 4 | new Array([]) 5 | 6 | if (foo) foo++; 7 | -------------------------------------------------------------------------------- /src/test/testData/oxlint/highlighting/no-config/index.js: -------------------------------------------------------------------------------- 1 | console.log('Happy developing ✨') 2 | 3 | debugger 4 | new Array([]) 5 | 6 | if (foo) foo++; 7 | -------------------------------------------------------------------------------- /src/test/testData/oxlint/highlighting/custom-config/index.js: -------------------------------------------------------------------------------- 1 | console.log('Happy developing ✨') 2 | 3 | debugger 4 | new Array([]) 5 | 6 | if (foo) foo++; 7 | -------------------------------------------------------------------------------- /src/test/testData/oxlint/highlighting/nested-config/index.js: -------------------------------------------------------------------------------- 1 | console.log('Happy developing ✨') 2 | 3 | debugger 4 | new Array([]) 5 | 6 | if (foo) foo++; 7 | -------------------------------------------------------------------------------- /settings.gradle.kts: -------------------------------------------------------------------------------- 1 | plugins { 2 | id("org.gradle.toolchains.foojay-resolver-convention") version "1.0.0" 3 | } 4 | 5 | rootProject.name = "oxc-intellij-plugin" 6 | -------------------------------------------------------------------------------- /src/main/kotlin/com/github/oxc/project/oxcintellijplugin/Constants.kt: -------------------------------------------------------------------------------- 1 | package com.github.oxc.project.oxcintellijplugin 2 | 3 | const val NOTIFICATION_GROUP = "Oxc" 4 | -------------------------------------------------------------------------------- /sandbox/oxlint/nested-configs/package-a/index.js: -------------------------------------------------------------------------------- 1 | console.log('Happy developing ✨') 2 | debugger; 3 | 4 | new Array([]) 5 | 6 | debugger 7 | 8 | (x < y || x === y) 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /src/test/testData/oxlint/highlighting/custom-config/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "oxc-lint", 3 | "version": "1.0.0", 4 | "private": true, 5 | "devDependencies": { 6 | "oxlint": "1.34.0" 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /src/test/testData/oxlint/highlighting/nested-config/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "oxc-lint", 3 | "version": "1.0.0", 4 | "private": true, 5 | "devDependencies": { 6 | "oxlint": "1.34.0" 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /src/test/testData/oxlint/highlighting/no-config/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "oxc-lint", 3 | "version": "1.0.0", 4 | "private": true, 5 | "devDependencies": { 6 | "oxlint": "1.34.0" 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /sandbox/oxlint/nested-configs/package-a/test.jsx: -------------------------------------------------------------------------------- 1 | var React = require('react'); 2 | 3 | var Component = React.forwardRef((props) => ( 4 |
5 | )); 6 | 7 | (x < y || x === y) 8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /src/main/kotlin/com/github/oxc/project/oxcintellijplugin/ConfigurationMode.kt: -------------------------------------------------------------------------------- 1 | package com.github.oxc.project.oxcintellijplugin 2 | 3 | enum class ConfigurationMode { 4 | DISABLED, 5 | AUTOMATIC, 6 | MANUAL 7 | } 8 | -------------------------------------------------------------------------------- /.github/SECURITY.md: -------------------------------------------------------------------------------- 1 | # Security Policy 2 | 3 | ## Supported Versions 4 | 5 | ## Reporting a Vulnerability 6 | 7 | If you find any potential vulnerability, join our [discord channel](https://discord.gg/9uXCAwqQZW) and contact Boshen. 8 | -------------------------------------------------------------------------------- /sandbox/oxlint/custom-config/custom-oxlint.jsonc: -------------------------------------------------------------------------------- 1 | { 2 | "plugins": [], 3 | "categories": {}, 4 | "rules": { 5 | "curly": "deny" 6 | }, 7 | "env": { 8 | "builtin": true 9 | }, 10 | "globals": {}, 11 | "ignorePatterns": [] 12 | } 13 | -------------------------------------------------------------------------------- /qodana.yml: -------------------------------------------------------------------------------- 1 | # Qodana configuration: 2 | # https://www.jetbrains.com/help/qodana/qodana-yaml.html 3 | 4 | version: 1.0 5 | linter: jetbrains/qodana-jvm-community:latest 6 | projectJDK: "21" 7 | profile: 8 | name: qodana.recommended 9 | exclude: 10 | - name: All 11 | paths: 12 | - .qodana 13 | -------------------------------------------------------------------------------- /sandbox/oxlint/custom-config/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "oxc-lint", 3 | "version": "1.0.0", 4 | "description": "", 5 | "scripts": { 6 | "test": "echo \"Error: no test specified\" && exit 1" 7 | }, 8 | "private": true, 9 | "devDependencies": { 10 | "oxlint": "1.34.0" 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionBase=GRADLE_USER_HOME 2 | distributionPath=wrapper/dists 3 | distributionUrl=https\://services.gradle.org/distributions/gradle-9.2.1-bin.zip 4 | networkTimeout=10000 5 | validateDistributionUrl=true 6 | zipStoreBase=GRADLE_USER_HOME 7 | zipStorePath=wrapper/dists 8 | -------------------------------------------------------------------------------- /sandbox/oxlint/nested-configs/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "oxc-lint", 3 | "version": "1.0.0", 4 | "description": "", 5 | "scripts": { 6 | "test": "echo \"Error: no test specified\" && exit 1" 7 | }, 8 | "private": true, 9 | "devDependencies": { 10 | "oxlint": "1.34.0" 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /src/main/kotlin/com/github/oxc/project/oxcintellijplugin/OxcIcons.kt: -------------------------------------------------------------------------------- 1 | package com.github.oxc.project.oxcintellijplugin 2 | 3 | import com.intellij.openapi.util.IconLoader 4 | 5 | object OxcIcons { 6 | 7 | @JvmField 8 | val OxcRound = IconLoader.getIcon("/icons/oxcRound.svg", javaClass) 9 | 10 | } 11 | -------------------------------------------------------------------------------- /sandbox/oxfmt/nested-configs/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "oxfmt-nested-configs", 3 | "version": "1.0.0", 4 | "description": "", 5 | "scripts": { 6 | "test": "echo \"Error: no test specified\" && exit 1" 7 | }, 8 | "private": true, 9 | "devDependencies": { 10 | "oxfmt": "0.19.0" 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /src/test/testData/oxlint/highlighting/custom-config/custom-oxlint.jsonc: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "./node_modules/oxlint/configuration_schema.json", 3 | "plugins": [], 4 | "categories": {}, 5 | "rules": { 6 | "curly": "deny" 7 | }, 8 | "env": { 9 | "builtin": true 10 | }, 11 | "globals": {}, 12 | "ignorePatterns": [] 13 | } 14 | -------------------------------------------------------------------------------- /.github/renovate.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://docs.renovatebot.com/renovate-schema.json", 3 | "extends": ["github>Boshen/renovate", "helpers:pinGitHubActionDigestsToSemver"], 4 | "packageRules": [ 5 | { 6 | "groupName": "java", 7 | "matchManagers": ["gradle", "gradle-wrapper"], 8 | "rangeStrategy": "auto" 9 | } 10 | ] 11 | } 12 | -------------------------------------------------------------------------------- /src/main/kotlin/com/github/oxc/project/oxcintellijplugin/oxlint/OxlintRunTrigger.kt: -------------------------------------------------------------------------------- 1 | package com.github.oxc.project.oxcintellijplugin.oxlint 2 | 3 | enum class OxlintRunTrigger { 4 | ON_SAVE, 5 | ON_TYPE; 6 | 7 | fun toLspValue(): String { 8 | return when (this) { 9 | ON_SAVE -> "onSave" 10 | ON_TYPE -> "onType" 11 | } 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /src/test/testData/oxlint/highlighting/custom-config/index.expected.js: -------------------------------------------------------------------------------- 1 | console.log('Happy developing ✨') 2 | 3 | debugger 5 | new Array([]) 6 | 7 | if (foo) foo++; 9 | -------------------------------------------------------------------------------- /src/main/kotlin/com/github/oxc/project/oxcintellijplugin/oxlint/OxlintUnusedDisableDirectivesSeverity.kt: -------------------------------------------------------------------------------- 1 | package com.github.oxc.project.oxcintellijplugin.oxlint 2 | 3 | enum class OxlintUnusedDisableDirectivesSeverity { 4 | ALLOW, 5 | WARN, 6 | DENY; 7 | 8 | fun toLspValue(): String { 9 | return when (this) { 10 | ALLOW -> "allow" 11 | WARN -> "warn" 12 | DENY -> "deny" 13 | } 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /src/test/testData/oxlint/highlighting/nested-config/subdirectory/index.expected.js: -------------------------------------------------------------------------------- 1 | console.log('Happy developing ✨') 2 | 3 | 4 | new Array([]) 6 | -------------------------------------------------------------------------------- /src/main/kotlin/com/github/oxc/project/oxcintellijplugin/ProcessCommandParameter.kt: -------------------------------------------------------------------------------- 1 | package com.github.oxc.project.oxcintellijplugin 2 | 3 | import java.nio.file.Path 4 | 5 | sealed interface ProcessCommandParameter { 6 | class Value(val value: String) : ProcessCommandParameter { 7 | override fun toString() = value 8 | } 9 | 10 | class FilePath(val path: Path) : ProcessCommandParameter { 11 | override fun toString() = path.toString() 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /sandbox/oxlint/custom-config/.idea/OxcSettings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 9 | 13 | -------------------------------------------------------------------------------- /.idea/gradle.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 15 | 16 | -------------------------------------------------------------------------------- /src/test/testData/oxlint/highlighting/no-config/index.expected.js: -------------------------------------------------------------------------------- 1 | console.log('Happy developing ✨') 2 | 3 | debugger 5 | new Array([]) 7 | 8 | if (foo) foo++; 9 | -------------------------------------------------------------------------------- /src/main/kotlin/com/github/oxc/project/oxcintellijplugin/ProcessCommandBuilder.kt: -------------------------------------------------------------------------------- 1 | package com.github.oxc.project.oxcintellijplugin 2 | 3 | import com.intellij.openapi.vfs.VirtualFile 4 | import java.nio.charset.Charset 5 | 6 | interface ProcessCommandBuilder { 7 | fun setWorkingDirectory(path: String?): ProcessCommandBuilder 8 | fun setInputFile(file: VirtualFile?): ProcessCommandBuilder 9 | fun addParameters(params: List): ProcessCommandBuilder 10 | fun setExecutable(executable: String): ProcessCommandBuilder 11 | fun setCharset(charset: Charset): ProcessCommandBuilder 12 | fun build(): OxcTargetRun 13 | } 14 | -------------------------------------------------------------------------------- /src/main/kotlin/com/github/oxc/project/oxcintellijplugin/oxlint/OxlintFixKind.kt: -------------------------------------------------------------------------------- 1 | package com.github.oxc.project.oxcintellijplugin.oxlint 2 | 3 | enum class OxlintFixKind { 4 | SAFE_FIX, 5 | SAFE_FIX_OR_SUGGESTION, 6 | DANGEROUS_FIX, 7 | DANGEROUS_FIX_OR_SUGGESTION, 8 | NONE, 9 | ALL; 10 | 11 | fun toLspValue(): String { 12 | return when (this) { 13 | SAFE_FIX -> "safe_fix" 14 | SAFE_FIX_OR_SUGGESTION -> "safe_fix_or_suggestion" 15 | DANGEROUS_FIX -> "dangerous_fix" 16 | DANGEROUS_FIX_OR_SUGGESTION -> "dangerous_fix_or_suggestion" 17 | NONE -> "none" 18 | ALL -> "all" 19 | } 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /gradle/libs.versions.toml: -------------------------------------------------------------------------------- 1 | [versions] 2 | # libraries 3 | junit = "4.13.2" 4 | 5 | # plugins 6 | changelog = "2.5.0" 7 | intelliJPlatform = "2.10.5" 8 | kotlin = "2.2.21" 9 | kover = "0.9.4" 10 | qodana = "2025.2.4" 11 | 12 | [libraries] 13 | junit = { group = "junit", name = "junit", version.ref = "junit" } 14 | 15 | [plugins] 16 | changelog = { id = "org.jetbrains.changelog", version.ref = "changelog" } 17 | intelliJPlatform = { id = "org.jetbrains.intellij.platform", version.ref = "intelliJPlatform" } 18 | kotlin = { id = "org.jetbrains.kotlin.jvm", version.ref = "kotlin" } 19 | kover = { id = "org.jetbrains.kotlinx.kover", version.ref = "kover" } 20 | qodana = { id = "org.jetbrains.qodana", version.ref = "qodana" } 21 | -------------------------------------------------------------------------------- /src/test/testData/oxlint/highlighting/nested-config/index.expected.js: -------------------------------------------------------------------------------- 1 | console.log('Happy developing ✨') 2 | 3 | debugger 5 | new Array([]) 7 | 8 | if (foo) foo++; 10 | -------------------------------------------------------------------------------- /MAINTENANCE.md: -------------------------------------------------------------------------------- 1 | ## Release 2 | 3 | 1. Ensure that the UNRELEASED section of the [CHANGELOG.md](./CHANGELOG.md) is up to date with all relevant changes. 4 | See https://github.com/JetBrains/gradle-changelog-plugin for the changelog format. 5 | 2. Update the `pluginVersion` within [gradle.properties](./gradle.properties). 6 | 3. Every build creates a draft release that can be found 7 | here https://github.com/oxc-project/oxc-intellij-plugin/releases. These are all release candidates. 8 | 4. When ready to release, go to the current draft release and publish it. This will 9 | run [release.yml](./.github/workflows/release.yml) which will automatically create a PR to move the changes from the 10 | UNRELEASED section of the changelog to the appropriate version. 11 | -------------------------------------------------------------------------------- /src/main/kotlin/com/github/oxc/project/oxcintellijplugin/oxfmt/OxfmtBundle.kt: -------------------------------------------------------------------------------- 1 | package com.github.oxc.project.oxcintellijplugin.oxfmt 2 | 3 | import com.intellij.DynamicBundle 4 | import org.jetbrains.annotations.NonNls 5 | import org.jetbrains.annotations.PropertyKey 6 | 7 | @NonNls 8 | private const val OXFMT_BUNDLE = "messages.OxfmtBundle" 9 | 10 | object OxfmtBundle : DynamicBundle(OXFMT_BUNDLE) { 11 | 12 | @JvmStatic 13 | fun message(@PropertyKey(resourceBundle = OXFMT_BUNDLE) key: String, 14 | vararg params: Any) = 15 | getMessage(key, *params) 16 | 17 | @Suppress("unused") 18 | @JvmStatic 19 | fun messagePointer(@PropertyKey(resourceBundle = OXFMT_BUNDLE) key: String, 20 | vararg params: Any) = 21 | getLazyMessage(key, *params) 22 | } 23 | -------------------------------------------------------------------------------- /src/main/kotlin/com/github/oxc/project/oxcintellijplugin/oxlint/OxlintBundle.kt: -------------------------------------------------------------------------------- 1 | package com.github.oxc.project.oxcintellijplugin.oxlint 2 | 3 | import com.intellij.DynamicBundle 4 | import org.jetbrains.annotations.NonNls 5 | import org.jetbrains.annotations.PropertyKey 6 | 7 | @NonNls 8 | private const val OXLINT_BUNDLE = "messages.OxlintBundle" 9 | 10 | object OxlintBundle : DynamicBundle(OXLINT_BUNDLE) { 11 | 12 | @JvmStatic 13 | fun message(@PropertyKey(resourceBundle = OXLINT_BUNDLE) key: String, 14 | vararg params: Any) = 15 | getMessage(key, *params) 16 | 17 | @Suppress("unused") 18 | @JvmStatic 19 | fun messagePointer(@PropertyKey(resourceBundle = OXLINT_BUNDLE) key: String, 20 | vararg params: Any) = 21 | getLazyMessage(key, *params) 22 | } 23 | -------------------------------------------------------------------------------- /src/main/kotlin/com/github/oxc/project/oxcintellijplugin/oxfmt/lsp/OxfmtLspFormattingSupport.kt: -------------------------------------------------------------------------------- 1 | package com.github.oxc.project.oxcintellijplugin.oxfmt.lsp 2 | 3 | import com.github.oxc.project.oxcintellijplugin.oxfmt.settings.OxfmtSettings 4 | import com.intellij.openapi.diagnostic.thisLogger 5 | import com.intellij.openapi.project.Project 6 | import com.intellij.openapi.vfs.VirtualFile 7 | import com.intellij.platform.lsp.api.customization.LspFormattingSupport 8 | 9 | class OxfmtLspFormattingSupport(private val project: Project) : LspFormattingSupport() { 10 | 11 | override fun shouldFormatThisFileExclusivelyByServer(file: VirtualFile, 12 | ideCanFormatThisFileItself: Boolean, 13 | serverExplicitlyWantsToFormatThisFile: Boolean): Boolean { 14 | val shouldFormatFile = OxfmtSettings.getInstance(project).fileSupported(file) 15 | thisLogger().debug("Should format ${file.path} = $shouldFormatFile") 16 | 17 | return shouldFormatFile 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /src/main/kotlin/com/github/oxc/project/oxcintellijplugin/oxfmt/settings/OxfmtSettingsState.kt: -------------------------------------------------------------------------------- 1 | package com.github.oxc.project.oxcintellijplugin.oxfmt.settings 2 | 3 | import com.github.oxc.project.oxcintellijplugin.ConfigurationMode 4 | import com.intellij.openapi.components.BaseState 5 | import com.intellij.util.xml.Attribute 6 | 7 | class OxfmtSettingsState : BaseState() { 8 | 9 | @get:Attribute("binaryPath") 10 | var binaryPath by string() 11 | 12 | @get:Attribute("configurationMode") 13 | var configurationMode by enum(ConfigurationMode.AUTOMATIC) 14 | 15 | @get:Attribute("configPath") 16 | var configPath by string() 17 | 18 | @get:Attribute("fixAllOnSave") 19 | var fixAllOnSave by property(false) 20 | 21 | @get:Attribute("supportedExtensions") 22 | var supportedExtensions by list() 23 | 24 | companion object { 25 | 26 | val DEFAULT_EXTENSION_LIST = listOf( 27 | ".js", ".jsx", ".cjs", ".mjs", 28 | ".ts", ".tsx", ".cts", ".mts", 29 | ) 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /sandbox/oxlint/nested-configs/package-b/.oxlintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "../node_modules/oxlint/configuration_schema.json", 3 | "plugins": [ 4 | "react", 5 | "unicorn", 6 | "typescript", 7 | "oxc" 8 | ], 9 | "categories": {}, 10 | "rules": { 11 | "unicorn/no-new-array": "error" 12 | }, 13 | "settings": { 14 | "jsx-a11y": { 15 | "polymorphicPropName": null, 16 | "components": {} 17 | }, 18 | "next": { 19 | "rootDir": [] 20 | }, 21 | "react": { 22 | "formComponents": [], 23 | "linkComponents": [] 24 | }, 25 | "jsdoc": { 26 | "ignorePrivate": false, 27 | "ignoreInternal": false, 28 | "ignoreReplacesDocs": true, 29 | "overrideReplacesDocs": true, 30 | "augmentsExtendsReplacesDocs": false, 31 | "implementsReplacesDocs": false, 32 | "exemptDestructuredRootsFromChecks": false, 33 | "tagNamePreference": {} 34 | } 35 | }, 36 | "env": { 37 | "builtin": true 38 | }, 39 | "globals": {}, 40 | "ignorePatterns": [] 41 | } 42 | -------------------------------------------------------------------------------- /src/test/testData/oxlint/highlighting/nested-config/subdirectory/.oxlintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "../node_modules/oxlint/configuration_schema.json", 3 | "plugins": [ 4 | "react", 5 | "unicorn", 6 | "typescript", 7 | "oxc" 8 | ], 9 | "categories": {}, 10 | "rules": { 11 | "unicorn/no-new-array": "error" 12 | }, 13 | "settings": { 14 | "jsx-a11y": { 15 | "polymorphicPropName": null, 16 | "components": {} 17 | }, 18 | "next": { 19 | "rootDir": [] 20 | }, 21 | "react": { 22 | "formComponents": [], 23 | "linkComponents": [] 24 | }, 25 | "jsdoc": { 26 | "ignorePrivate": false, 27 | "ignoreInternal": false, 28 | "ignoreReplacesDocs": true, 29 | "overrideReplacesDocs": true, 30 | "augmentsExtendsReplacesDocs": false, 31 | "implementsReplacesDocs": false, 32 | "exemptDestructuredRootsFromChecks": false, 33 | "tagNamePreference": {} 34 | } 35 | }, 36 | "env": { 37 | "builtin": true 38 | }, 39 | "globals": {}, 40 | "ignorePatterns": [] 41 | } 42 | -------------------------------------------------------------------------------- /.run/Run Tests.run.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 12 | 17 | 19 | true 20 | true 21 | false 22 | true 23 | 24 | 25 | 26 | -------------------------------------------------------------------------------- /src/main/kotlin/com/github/oxc/project/oxcintellijplugin/oxfmt/listeners/OxfmtEditorWatcher.kt: -------------------------------------------------------------------------------- 1 | package com.github.oxc.project.oxcintellijplugin.oxfmt.listeners 2 | 3 | import com.github.oxc.project.oxcintellijplugin.oxfmt.services.OxfmtServerService 4 | import com.github.oxc.project.oxcintellijplugin.oxfmt.settings.OxfmtSettings 5 | import com.intellij.openapi.fileEditor.FileEditorManager 6 | import com.intellij.openapi.fileEditor.FileEditorManagerListener 7 | import com.intellij.openapi.vfs.VirtualFile 8 | 9 | class OxfmtEditorWatcher : FileEditorManagerListener { 10 | 11 | override fun fileClosed(source: FileEditorManager, file: VirtualFile) { 12 | val project = source.project 13 | if (source.allEditors.isEmpty()) { 14 | OxfmtServerService.getInstance(project).stopServer() 15 | return 16 | } 17 | 18 | val stillHasSupportedFileOpen = source.allEditors.any { 19 | OxfmtSettings.getInstance(project).fileSupported(it.file) 20 | } 21 | if (!stillHasSupportedFileOpen) { 22 | OxfmtServerService.getInstance(project).stopServer() 23 | } 24 | } 25 | 26 | } 27 | -------------------------------------------------------------------------------- /.run/Run Plugin.run.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 12 | 17 | 19 | true 20 | true 21 | false 22 | false 23 | 24 | 25 | 26 | -------------------------------------------------------------------------------- /.github/CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | ### Running Tests 2 | 3 | Tests can be executed with `./gradlew check`. Prior to running tests, you'll need to execute `npm install` within each 4 | of the test directories found at `src/test/testData`. 5 | 6 | If you fail to execute `npm install` within a test directory, you'll typically receive an exception like the below. 7 | 8 | ``` 9 | Caused by: java.io.FileNotFoundException: /private/var/folders/y9/7_0wzf515vsdjc62stxjngnc0000gn/T/unitTest_rootFileHighlighting_2yF2NjR6MfHBbuoM6d9V6JBZdFT/unitTest13365413598591665525/node_modules/oxlint/bin/oxc_language_server (No such file or directory) 10 | at java.base/java.io.FileInputStream.open0(Native Method) 11 | at java.base/java.io.FileInputStream.open(FileInputStream.java:213) 12 | at java.base/java.io.FileInputStream.(FileInputStream.java:152) 13 | at com.github.oxc.project.oxcintellijplugin.OxcTargetRunBuilder.getBuilder(OxcTargetRun.kt:99) 14 | at com.github.oxc.project.oxcintellijplugin.lsp.OxcLspServerDescriptor.(OxcLspServerDescriptor.kt:24) 15 | at com.github.oxc.project.oxcintellijplugin.lsp.OxcLspServerSupportProvider.fileOpened(OxcLspServerSupportProvider.kt:41) 16 | ``` 17 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2024-present VoidZero Inc. & Contributors 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /src/main/kotlin/com/github/oxc/project/oxcintellijplugin/oxlint/listeners/OxlintEditorWatcher.kt: -------------------------------------------------------------------------------- 1 | package com.github.oxc.project.oxcintellijplugin.oxlint.listeners 2 | 3 | import com.github.oxc.project.oxcintellijplugin.oxlint.services.OxlintServerService 4 | import com.github.oxc.project.oxcintellijplugin.oxlint.settings.OxlintSettings 5 | import com.intellij.openapi.fileEditor.FileEditorManager 6 | import com.intellij.openapi.fileEditor.FileEditorManagerListener 7 | import com.intellij.openapi.vfs.VirtualFile 8 | 9 | class OxlintEditorWatcher : FileEditorManagerListener { 10 | 11 | override fun fileClosed(source: FileEditorManager, file: VirtualFile) { 12 | val project = source.project 13 | if (source.allEditors.isEmpty()) { 14 | OxlintServerService.getInstance(project).stopServer() 15 | return 16 | } 17 | 18 | val stillHasSupportedFileOpen = source.allEditors.any { 19 | OxlintSettings.getInstance(project).fileSupported(it.file) 20 | } 21 | if (!stillHasSupportedFileOpen) { 22 | OxlintServerService.getInstance(project).stopServer() 23 | } 24 | } 25 | 26 | } 27 | -------------------------------------------------------------------------------- /sandbox/oxlint/nested-configs/package-a/.oxlintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "../node_modules/oxlint/configuration_schema.json", 3 | "plugins": [ 4 | "react", 5 | "unicorn", 6 | "typescript", 7 | "oxc" 8 | ], 9 | "categories": {}, 10 | "rules": { 11 | "unicorn/no-new-array": "warn", 12 | "eslint/no-debugger": "error", 13 | "oxc/double-comparisons": "error", 14 | "react/forward-ref-uses-ref": "error" 15 | }, 16 | "settings": { 17 | "jsx-a11y": { 18 | "polymorphicPropName": null, 19 | "components": {} 20 | }, 21 | "next": { 22 | "rootDir": [] 23 | }, 24 | "react": { 25 | "formComponents": [], 26 | "linkComponents": [] 27 | }, 28 | "jsdoc": { 29 | "ignorePrivate": false, 30 | "ignoreInternal": false, 31 | "ignoreReplacesDocs": true, 32 | "overrideReplacesDocs": true, 33 | "augmentsExtendsReplacesDocs": false, 34 | "implementsReplacesDocs": false, 35 | "exemptDestructuredRootsFromChecks": false, 36 | "tagNamePreference": {} 37 | } 38 | }, 39 | "env": { 40 | "builtin": true 41 | }, 42 | "globals": {}, 43 | "ignorePatterns": [] 44 | } 45 | -------------------------------------------------------------------------------- /.run/Run Verifications.run.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 12 | 18 | 20 | true 21 | true 22 | false 23 | false 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /src/main/kotlin/com/github/oxc/project/oxcintellijplugin/settings/OxcOnSaveInfoProvider.kt: -------------------------------------------------------------------------------- 1 | package com.github.oxc.project.oxcintellijplugin.settings 2 | 3 | import com.github.oxc.project.oxcintellijplugin.oxfmt.OxfmtBundle 4 | import com.github.oxc.project.oxcintellijplugin.oxfmt.settings.OxfmtOnSaveFixAllActionInfo 5 | import com.github.oxc.project.oxcintellijplugin.oxlint.OxlintBundle 6 | import com.github.oxc.project.oxcintellijplugin.oxlint.settings.OxlintOnSaveFixAllActionInfo 7 | import com.intellij.ide.actionsOnSave.ActionOnSaveContext 8 | import com.intellij.ide.actionsOnSave.ActionOnSaveInfo 9 | import com.intellij.ide.actionsOnSave.ActionOnSaveInfoProvider 10 | 11 | class OxcOnSaveInfoProvider : ActionOnSaveInfoProvider() { 12 | 13 | override fun getActionOnSaveInfos(context: ActionOnSaveContext): List = 14 | listOf(OxlintOnSaveFixAllActionInfo(context), OxfmtOnSaveFixAllActionInfo(context)) 15 | 16 | override fun getSearchableOptions(): Collection { 17 | return listOf( 18 | OxlintBundle.message("oxlint.fix.all.on.save.checkbox.on.actions.on.save.page"), 19 | OxfmtBundle.message("oxfmt.fix.all.on.save.checkbox.on.actions.on.save.page")) 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /src/main/kotlin/com/github/oxc/project/oxcintellijplugin/oxlint/lsp/OxlintLspDiagnosticsSupport.kt: -------------------------------------------------------------------------------- 1 | package com.github.oxc.project.oxcintellijplugin.oxlint.lsp 2 | 3 | import com.github.oxc.project.oxcintellijplugin.oxlint.OxlintBundle 4 | import com.intellij.openapi.diagnostic.thisLogger 5 | import com.intellij.platform.lsp.api.customization.LspDiagnosticsSupport 6 | import org.eclipse.lsp4j.Diagnostic 7 | 8 | class OxlintLspDiagnosticsSupport : LspDiagnosticsSupport() { 9 | 10 | override fun getMessage(diagnostic: Diagnostic): String { 11 | thisLogger().debug("Creating message for diagnostic: $diagnostic") 12 | return "${diagnostic.source}: ${diagnostic.message} ${ 13 | diagnostic.code?.get() ?: OxlintBundle.message("oxlint.diagnostic.unknown.code") 14 | }" 15 | } 16 | 17 | override fun getTooltip(diagnostic: Diagnostic): String { 18 | thisLogger().debug("Creating tooltip for diagnostic: $diagnostic") 19 | var rule = diagnostic.code?.get() ?: OxlintBundle.message("oxlint.diagnostic.unknown.code") 20 | if (diagnostic.codeDescription?.href != null) { 21 | rule = "${rule}" 22 | } 23 | 24 | return "${diagnostic.source}: ${diagnostic.message} $rule" 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /src/test/kotlin/com/github/oxc/project/oxcintellijplugin/oxlint/NoConfigHighlightingTest.kt: -------------------------------------------------------------------------------- 1 | package com.github.oxc.project.oxcintellijplugin.oxlint 2 | 3 | import com.github.oxc.project.oxcintellijplugin.extensions.configureByFileAndCheckLanguageServerHighlighting 4 | import com.github.oxc.project.oxcintellijplugin.oxlint.lsp.OxlintLspServerDescriptor 5 | import com.intellij.testFramework.TestDataPath 6 | import com.intellij.testFramework.builders.ModuleFixtureBuilder 7 | import com.intellij.testFramework.fixtures.CodeInsightFixtureTestCase 8 | import com.intellij.testFramework.fixtures.ModuleFixture 9 | import com.intellij.testFramework.fixtures.impl.CodeInsightTestFixtureImpl 10 | 11 | @TestDataPath("\$CONTENT_ROOT/testData/oxlint/highlighting") 12 | class NoConfigHighlightingTest : 13 | CodeInsightFixtureTestCase>() { 14 | 15 | override fun setUp() { 16 | super.setUp() 17 | (myFixture as CodeInsightTestFixtureImpl).canChangeDocumentDuringHighlighting(true) 18 | myFixture.testDataPath = "src/test/testData/oxlint/highlighting" 19 | } 20 | 21 | fun testRootFileHighlighting() { 22 | myFixture.copyDirectoryToProject("no-config", "") 23 | 24 | myFixture.configureByFileAndCheckLanguageServerHighlighting(OxlintLspServerDescriptor::class.java, "index.expected.js") 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /src/main/kotlin/com/github/oxc/project/oxcintellijplugin/OxcIconProvider.kt: -------------------------------------------------------------------------------- 1 | package com.github.oxc.project.oxcintellijplugin 2 | 3 | import com.github.oxc.project.oxcintellijplugin.extensions.isOxfmtConfigFile 4 | import com.github.oxc.project.oxcintellijplugin.extensions.isOxlintConfigFile 5 | import com.github.oxc.project.oxcintellijplugin.oxlint.settings.OxlintSettings 6 | import com.intellij.ide.IconProvider 7 | import com.intellij.openapi.project.DumbAware 8 | import com.intellij.psi.PsiElement 9 | import com.intellij.psi.PsiFile 10 | import javax.swing.Icon 11 | 12 | class OxcIconProvider : IconProvider(), DumbAware { 13 | 14 | override fun getIcon(element: PsiElement, 15 | flags: Int): Icon? { 16 | if (element !is PsiFile) { 17 | return null 18 | } 19 | val file = element.viewProvider.virtualFile 20 | if (!file.isValid || file.isDirectory) { 21 | return null 22 | } 23 | val settings = OxlintSettings.getInstance(element.project) 24 | if (settings.state.configPath == file.path) { 25 | return OxcIcons.OxcRound 26 | } 27 | if (file.isOxlintConfigFile()) { 28 | return OxcIcons.OxcRound 29 | } 30 | if (file.isOxfmtConfigFile()) { 31 | return OxcIcons.OxcRound 32 | } 33 | 34 | return null 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /src/main/kotlin/com/github/oxc/project/oxcintellijplugin/oxfmt/listeners/OxfmtConfigWatcher.kt: -------------------------------------------------------------------------------- 1 | package com.github.oxc.project.oxcintellijplugin.oxfmt.listeners 2 | 3 | import com.github.oxc.project.oxcintellijplugin.extensions.isOxfmtConfigFile 4 | import com.github.oxc.project.oxcintellijplugin.oxfmt.services.OxfmtServerService 5 | import com.intellij.openapi.application.ApplicationManager 6 | import com.intellij.openapi.components.service 7 | import com.intellij.openapi.project.ProjectManager 8 | import com.intellij.openapi.vfs.newvfs.BulkFileListener 9 | import com.intellij.openapi.vfs.newvfs.events.VFileEvent 10 | 11 | class OxfmtConfigWatcher : BulkFileListener { 12 | 13 | override fun after(events: List) { 14 | val configChanged = events.any { event -> 15 | return@any event.file?.isOxfmtConfigFile() == true 16 | } 17 | 18 | if (configChanged) { 19 | val projectManager = ApplicationManager.getApplication().service() 20 | val openProjects = projectManager.openProjects 21 | openProjects.forEach { project -> 22 | ApplicationManager.getApplication().invokeLater { 23 | if (!project.isDisposed) { 24 | OxfmtServerService.getInstance(project).restartServer() 25 | } 26 | } 27 | } 28 | } 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /src/main/kotlin/com/github/oxc/project/oxcintellijplugin/oxlint/listeners/OxlintConfigWatcher.kt: -------------------------------------------------------------------------------- 1 | package com.github.oxc.project.oxcintellijplugin.oxlint.listeners 2 | 3 | import com.github.oxc.project.oxcintellijplugin.extensions.isOxlintConfigFile 4 | import com.github.oxc.project.oxcintellijplugin.oxlint.services.OxlintServerService 5 | import com.intellij.openapi.application.ApplicationManager 6 | import com.intellij.openapi.components.service 7 | import com.intellij.openapi.project.ProjectManager 8 | import com.intellij.openapi.vfs.newvfs.BulkFileListener 9 | import com.intellij.openapi.vfs.newvfs.events.VFileEvent 10 | 11 | class OxlintConfigWatcher : BulkFileListener { 12 | override fun after(events: List) { 13 | val configChanged = events.any { event -> 14 | return@any event.file?.isOxlintConfigFile() == true 15 | } 16 | 17 | if (configChanged) { 18 | val projectManager = ApplicationManager.getApplication().service() 19 | val openProjects = projectManager.openProjects 20 | openProjects.forEach { project -> 21 | ApplicationManager.getApplication().invokeLater { 22 | if (!project.isDisposed) { 23 | OxlintServerService.getInstance(project).restartServer() 24 | } 25 | } 26 | } 27 | } 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /src/main/resources/messages/OxfmtBundle.properties: -------------------------------------------------------------------------------- 1 | oxfmt.config.path.label=Path to Oxfmt Config: 2 | oxfmt.configure.extensions.link=Configure file extensions 3 | oxfmt.file.not.supported.description=The file "{0}" is not supported by Oxfmt. 4 | oxfmt.file.not.supported.title=Unsupported file 5 | oxfmt.fix.all.failure.description=An error occurred while applying 'Fix All': {0} 6 | oxfmt.fix.all.failure.label=Failed to Apply 'Fix All' 7 | oxfmt.fix.all.on.save.checkbox.on.actions.on.save.page=Run 'Fix All' with Oxfmt on Save 8 | oxfmt.fix.all.on.save.failure.description=An error occurred while running 'Fix All' on save: {0} 9 | oxfmt.fix.all.on.save.failure.label=Failed to apply 'Fix all' on save 10 | oxfmt.fix.all.success.description='Fix All' successfully applied. 11 | oxfmt.fix.all.success.label=Fixes applied successfully 12 | oxfmt.language.server.restarted=Oxfmt language server restarted. 13 | oxfmt.name=Oxfmt 14 | oxfmt.on.save.comment.disabled=Oxfmt integration is disabled. 15 | oxfmt.on.save.comment.fix.all=Oxfmt 'Fix All' 16 | oxfmt.run.fix.all=Running Oxfmt 'Fix All' 17 | oxfmt.run.fix.all.on.save.checkbox.on.actions.on.save.page=Run Oxfmt 'Fix All' on Save 18 | oxfmt.run.fix.all.on.save.label=Run 'Fix All' on &Save 19 | oxfmt.run.quickfix=Running Oxfmt Quickfix 20 | oxfmt.schema.name=Oxfmt 21 | oxfmt.settings.languageServerPath=Path to Oxfmt Language Server: 22 | oxfmt.supported.extensions.comment=Enter file extensions separated by commas (e.g., .js, .ts, .jsx). Each extension must start with '.' and contain only alphanumeric characters. 23 | oxfmt.supported.extensions.label=Supported File Extensions: 24 | -------------------------------------------------------------------------------- /src/test/kotlin/com/github/oxc/project/oxcintellijplugin/oxlint/NestedConfigHighlightingTest.kt: -------------------------------------------------------------------------------- 1 | package com.github.oxc.project.oxcintellijplugin.oxlint 2 | 3 | import com.github.oxc.project.oxcintellijplugin.extensions.configureByFileAndCheckLanguageServerHighlighting 4 | import com.github.oxc.project.oxcintellijplugin.oxlint.lsp.OxlintLspServerDescriptor 5 | import com.intellij.testFramework.TestDataPath 6 | import com.intellij.testFramework.builders.ModuleFixtureBuilder 7 | import com.intellij.testFramework.fixtures.CodeInsightFixtureTestCase 8 | import com.intellij.testFramework.fixtures.ModuleFixture 9 | import com.intellij.testFramework.fixtures.impl.CodeInsightTestFixtureImpl 10 | 11 | @TestDataPath("\$CONTENT_ROOT/testData/oxlint/highlighting") 12 | class NestedConfigHighlightingTest : 13 | CodeInsightFixtureTestCase>() { 14 | 15 | override fun setUp() { 16 | super.setUp() 17 | (myFixture as CodeInsightTestFixtureImpl).canChangeDocumentDuringHighlighting(true) 18 | myFixture.testDataPath = "src/test/testData/oxlint/highlighting" 19 | } 20 | 21 | fun testRootFileHighlighting() { 22 | myFixture.copyDirectoryToProject("nested-config", "") 23 | 24 | myFixture.configureByFileAndCheckLanguageServerHighlighting(OxlintLspServerDescriptor::class.java, "index.expected.js") 25 | } 26 | 27 | fun testSubdirectoryFileHighlighting() { 28 | myFixture.copyDirectoryToProject("nested-config", "") 29 | 30 | myFixture.configureByFileAndCheckLanguageServerHighlighting(OxlintLspServerDescriptor::class.java, "subdirectory/index.expected.js") 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /src/main/kotlin/com/github/oxc/project/oxcintellijplugin/oxfmt/OxfmtSchemaProviderFactory.kt: -------------------------------------------------------------------------------- 1 | package com.github.oxc.project.oxcintellijplugin.oxfmt 2 | 3 | import com.github.oxc.project.oxcintellijplugin.extensions.isOxfmtConfigFile 4 | import com.github.oxc.project.oxcintellijplugin.oxfmt.settings.OxfmtSettings 5 | import com.intellij.openapi.project.Project 6 | import com.intellij.openapi.vfs.VirtualFile 7 | import com.jetbrains.jsonSchema.extension.JsonSchemaFileProvider 8 | import com.jetbrains.jsonSchema.extension.JsonSchemaProviderFactory 9 | import com.jetbrains.jsonSchema.extension.SchemaType 10 | import com.jetbrains.jsonSchema.remote.JsonFileResolver 11 | import org.jetbrains.annotations.Nls 12 | 13 | class OxfmtSchemaProviderFactory : JsonSchemaProviderFactory { 14 | 15 | override fun getProviders(project: Project): List { 16 | return listOf(object : JsonSchemaFileProvider { 17 | override fun isAvailable(file: VirtualFile): Boolean { 18 | val settings = OxfmtSettings.getInstance(project) 19 | return settings.state.configPath == file.path || file.isOxfmtConfigFile() 20 | } 21 | 22 | override fun getName(): @Nls String { 23 | return OxfmtBundle.message("oxfmt.schema.name") 24 | } 25 | 26 | override fun getSchemaFile(): VirtualFile? { 27 | return JsonFileResolver.urlToFile( 28 | "https://raw.githubusercontent.com/oxc-project/oxc/main/npm/oxfmt/configuration_schema.json") 29 | } 30 | 31 | override fun getSchemaType(): SchemaType { 32 | return SchemaType.remoteSchema 33 | } 34 | }) 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /src/main/kotlin/com/github/oxc/project/oxcintellijplugin/oxlint/OxlintSchemaProviderFactory.kt: -------------------------------------------------------------------------------- 1 | package com.github.oxc.project.oxcintellijplugin.oxlint 2 | 3 | import com.github.oxc.project.oxcintellijplugin.extensions.isOxlintConfigFile 4 | import com.github.oxc.project.oxcintellijplugin.oxlint.settings.OxlintSettings 5 | import com.intellij.openapi.project.Project 6 | import com.intellij.openapi.vfs.VirtualFile 7 | import com.jetbrains.jsonSchema.extension.JsonSchemaFileProvider 8 | import com.jetbrains.jsonSchema.extension.JsonSchemaProviderFactory 9 | import com.jetbrains.jsonSchema.extension.SchemaType 10 | import com.jetbrains.jsonSchema.remote.JsonFileResolver 11 | import org.jetbrains.annotations.Nls 12 | 13 | class OxlintSchemaProviderFactory : JsonSchemaProviderFactory { 14 | 15 | override fun getProviders(project: Project): List { 16 | return listOf(object : JsonSchemaFileProvider { 17 | override fun isAvailable(file: VirtualFile): Boolean { 18 | val settings = OxlintSettings.getInstance(project) 19 | return settings.state.configPath == file.path || file.isOxlintConfigFile() 20 | } 21 | 22 | override fun getName(): @Nls String { 23 | return OxlintBundle.message("oxlint.schema.name") 24 | } 25 | 26 | override fun getSchemaFile(): VirtualFile? { 27 | return JsonFileResolver.urlToFile( 28 | "https://raw.githubusercontent.com/oxc-project/oxc/main/npm/oxlint/configuration_schema.json") 29 | } 30 | 31 | override fun getSchemaType(): SchemaType { 32 | return SchemaType.remoteSchema 33 | } 34 | }) 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # oxc-intellij-plugin 2 | 3 | [![Build](https://github.com/oxc-project/oxc-intellij-plugin/workflows/Build/badge.svg)](https://github.com/oxc-project/oxc-intellij-plugin/actions/workflows/build.yml?query=branch%3Amain) 4 | [![Version](https://img.shields.io/jetbrains/plugin/v/27061.svg)](https://plugins.jetbrains.com/plugin/27061-oxc) 5 | [![Downloads](https://img.shields.io/jetbrains/plugin/d/27061.svg)](https://plugins.jetbrains.com/plugin/27061-oxc) 6 | 7 | 8 | 9 | # Oxc 10 | 11 | The Oxidation Compiler is creating a suite of high-performance tools for JavaScript and TypeScript. 12 | 13 | ## Oxlint 14 | 15 | This is the linter for oxc. The currently supported features are listed below. 16 | 17 | - Highlighting for warnings or errors identified by Oxlint. 18 | - Quick fixes to fix a warning or error when possible. 19 | - JSON schema validation for `.oxlintrc.json` configuration files. (Note: Comments within the .oxlintrc.json 20 | file are supported, however they show as an error within the IDE due to jsonc not being supported by the IDE.) 21 | - Command to fix all auto-fixable content within the current text editor. 22 | - Automatically apply fixes on save. 23 | 24 | 25 | 26 | ## Installation 27 | 28 | - Using the IDE built-in plugin system: 29 | 30 | Settings/Preferences > Plugins > Marketplace > Search for "Oxc" > 31 | Install 32 | 33 | - Manually: 34 | 35 | Download the [latest release](https://github.com/oxc-project/oxc-intellij-plugin/releases/latest) and install it 36 | manually using 37 | Settings/Preferences > Plugins > ⚙️ > Install plugin from disk... 38 | 39 | --- 40 | -------------------------------------------------------------------------------- /src/test/kotlin/com/github/oxc/project/oxcintellijplugin/oxlint/CustomConfigHighlightingTest.kt: -------------------------------------------------------------------------------- 1 | package com.github.oxc.project.oxcintellijplugin.oxlint 2 | 3 | import com.github.oxc.project.oxcintellijplugin.ConfigurationMode 4 | import com.github.oxc.project.oxcintellijplugin.extensions.configureByFileAndCheckLanguageServerHighlighting 5 | import com.github.oxc.project.oxcintellijplugin.oxlint.lsp.OxlintLspServerDescriptor 6 | import com.github.oxc.project.oxcintellijplugin.oxlint.settings.OxlintSettings 7 | import com.intellij.testFramework.TestDataPath 8 | import com.intellij.testFramework.builders.ModuleFixtureBuilder 9 | import com.intellij.testFramework.fixtures.CodeInsightFixtureTestCase 10 | import com.intellij.testFramework.fixtures.ModuleFixture 11 | import com.intellij.testFramework.fixtures.impl.CodeInsightTestFixtureImpl 12 | 13 | @TestDataPath("\$CONTENT_ROOT/testData/oxlint/highlighting") 14 | class CustomConfigHighlightingTest : 15 | CodeInsightFixtureTestCase>() { 16 | 17 | override fun setUp() { 18 | super.setUp() 19 | (myFixture as CodeInsightTestFixtureImpl).canChangeDocumentDuringHighlighting(true) 20 | myFixture.testDataPath = "src/test/testData/oxlint/highlighting" 21 | 22 | val oxlintSettings = OxlintSettings.getInstance(myFixture.project) 23 | oxlintSettings.configPath = "${myFixture.tempDirPath}/custom-oxlint.jsonc" 24 | oxlintSettings.configurationMode = ConfigurationMode.MANUAL 25 | } 26 | 27 | fun testRootFileHighlighting() { 28 | myFixture.copyDirectoryToProject("custom-config", "") 29 | 30 | myFixture.configureByFileAndCheckLanguageServerHighlighting(OxlintLspServerDescriptor::class.java, "index.expected.js") 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /src/main/kotlin/com/github/oxc/project/oxcintellijplugin/extensions/FileExt.kt: -------------------------------------------------------------------------------- 1 | package com.github.oxc.project.oxcintellijplugin.extensions 2 | 3 | import com.github.oxc.project.oxcintellijplugin.oxfmt.OxfmtPackage 4 | import com.github.oxc.project.oxcintellijplugin.oxlint.OxlintPackage 5 | import com.intellij.openapi.vfs.VfsUtil 6 | import com.intellij.openapi.vfs.VirtualFile 7 | 8 | /** 9 | * Find the nearest file that satisfies the predicate. 10 | * If `root` is not null, stops finding at the specified directory. 11 | */ 12 | private fun VirtualFile.findNearestFile( 13 | predicate: (file: VirtualFile) -> Boolean, 14 | root: VirtualFile? = null, 15 | ): VirtualFile? { 16 | var cur = this.parent 17 | while (cur != null && VfsUtil.isUnder(cur, mutableSetOf(root))) { 18 | val f = cur.children.find(predicate) 19 | if (f != null) { 20 | return f 21 | } 22 | cur = cur.parent 23 | } 24 | return null 25 | } 26 | 27 | fun VirtualFile.isOxlintConfigFile(): Boolean = 28 | OxlintPackage.configValidExtensions.map { "${OxlintPackage.CONFIG_NAME}.$it" }.contains(this.name) 29 | 30 | fun VirtualFile.isOxfmtConfigFile(): Boolean = 31 | OxfmtPackage.CONFIG_VALID_EXTENSIONS.map { "${OxfmtPackage.CONFIG_NAME}.$it" }.contains(this.name) 32 | 33 | fun VirtualFile.isPackageJsonFile(): Boolean = this.name == "package.json" 34 | 35 | fun VirtualFile.findNearestOxfmtConfig(root: VirtualFile? = null): VirtualFile? = 36 | this.findNearestFile({ f -> f.isOxfmtConfigFile() }, root) 37 | 38 | fun VirtualFile.findNearestOxlintConfig(root: VirtualFile? = null): VirtualFile? = 39 | this.findNearestFile({ f -> f.isOxlintConfigFile() }, root) 40 | 41 | fun VirtualFile.findNearestPackageJson(root: VirtualFile? = null): VirtualFile? = 42 | this.findNearestFile({ f -> f.isPackageJsonFile() }, root) 43 | -------------------------------------------------------------------------------- /src/main/kotlin/com/github/oxc/project/oxcintellijplugin/oxlint/settings/OxlintSettingsState.kt: -------------------------------------------------------------------------------- 1 | package com.github.oxc.project.oxcintellijplugin.oxlint.settings 2 | 3 | import com.github.oxc.project.oxcintellijplugin.ConfigurationMode 4 | import com.github.oxc.project.oxcintellijplugin.oxlint.OxlintRunTrigger 5 | import com.github.oxc.project.oxcintellijplugin.oxlint.OxlintUnusedDisableDirectivesSeverity 6 | import com.intellij.openapi.components.BaseState 7 | import com.intellij.util.xml.Attribute 8 | import com.intellij.util.xmlb.annotations.XMap 9 | 10 | class OxlintSettingsState : BaseState() { 11 | 12 | @get:Attribute("binaryPath") 13 | var binaryPath by string() 14 | 15 | @get:Attribute("binaryParameters") 16 | var binaryParameters by list() 17 | 18 | @get:Attribute("runTrigger") 19 | var runTrigger by enum(OxlintRunTrigger.ON_TYPE) 20 | 21 | @get:Attribute("configurationMode") 22 | var configurationMode by enum(ConfigurationMode.AUTOMATIC) 23 | 24 | @get:Attribute("configPath") 25 | var configPath by string() 26 | 27 | @get:Attribute("fixAllOnSave") 28 | var fixAllOnSave by property(false) 29 | 30 | @get:Attribute("flags") 31 | @get:XMap 32 | var flags by map() 33 | 34 | @get:Attribute("supportedExtensions") 35 | var supportedExtensions by list() 36 | 37 | @get:Attribute("typeAware") 38 | var typeAware by property(false) 39 | 40 | @get:Attribute("unusedDisableDirectives") 41 | var unusedDisableDirectives by enum(OxlintUnusedDisableDirectivesSeverity.ALLOW) 42 | 43 | companion object { 44 | val DEFAULT_EXTENSION_LIST = listOf( 45 | ".astro", 46 | ".js", ".jsx", ".cjs", ".mjs", 47 | ".svelte", 48 | ".ts", ".tsx", ".cts", ".mts", 49 | ".vue" 50 | ) 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /gradle.properties: -------------------------------------------------------------------------------- 1 | # IntelliJ Platform Artifacts Repositories -> https://plugins.jetbrains.com/docs/intellij/intellij-artifacts.html 2 | 3 | pluginGroup = com.github.oxc.project.oxcintellijplugin 4 | pluginName = Oxc 5 | pluginRepositoryUrl = https://github.com/oxc-project/oxc-intellij-plugin 6 | # SemVer format -> https://semver.org 7 | pluginVersion = 0.0.21 8 | 9 | # Supported build number ranges and IntelliJ Platform versions -> https://plugins.jetbrains.com/docs/intellij/build-number-ranges.html 10 | pluginSinceBuild = 251 11 | # This is unset so that we don't have to publish a new plugin version for every major.minor version of IntelliJ IDE. 12 | # https://github.com/oxc-project/oxc-intellij-plugin/issues/231 13 | # pluginUntilBuild = 14 | 15 | # IntelliJ Platform Properties -> https://plugins.jetbrains.com/docs/intellij/tools-gradle-intellij-plugin.html#configuration-intellij-extension 16 | platformType = WS 17 | platformVersion = 2025.1.3 18 | 19 | # Plugin Dependencies -> https://plugins.jetbrains.com/docs/intellij/plugin-dependencies.html 20 | # Example: platformPlugins = com.intellij.java, com.jetbrains.php:203.4449.22 21 | platformPlugins = 22 | # Example: platformBundledPlugins = com.intellij.java 23 | platformBundledPlugins = JavaScript 24 | 25 | # Opt-out flag for bundling Kotlin standard library -> https://jb.gg/intellij-platform-kotlin-stdlib 26 | kotlin.stdlib.default.dependency = false 27 | 28 | # Enable Gradle Configuration Cache -> https://docs.gradle.org/current/userguide/configuration_cache.html 29 | org.gradle.configuration-cache = true 30 | 31 | # Enable Gradle Build Cache -> https://docs.gradle.org/current/userguide/build_cache.html 32 | org.gradle.caching = true 33 | 34 | # Enable Gradle Kotlin DSL Lazy Property Assignment -> https://docs.gradle.org/current/userguide/kotlin_dsl.html#kotdsl:assignment 35 | systemProp.org.gradle.unsafe.kotlin.assignment = true 36 | 37 | kotlin.daemon.jvmargs=-Xmx1024M 38 | -------------------------------------------------------------------------------- /.github/workflows/run-ui-tests.yml: -------------------------------------------------------------------------------- 1 | # GitHub Actions Workflow for launching UI tests on Linux, Windows, and Mac in the following steps: 2 | # - Prepare and launch IDE with your plugin and robot-server plugin, which is needed to interact with the UI. 3 | # - Wait for IDE to start. 4 | # - Run UI tests with a separate Gradle task. 5 | # 6 | # Please check https://github.com/JetBrains/intellij-ui-test-robot for information about UI tests with IntelliJ Platform. 7 | # 8 | # Workflow is triggered manually. 9 | 10 | permissions: {} 11 | 12 | name: Run UI Tests 13 | on: 14 | workflow_dispatch 15 | 16 | jobs: 17 | 18 | testUI: 19 | runs-on: ${{ matrix.os }} 20 | strategy: 21 | fail-fast: false 22 | matrix: 23 | include: 24 | - os: ubuntu-latest 25 | runIde: | 26 | export DISPLAY=:99.0 27 | Xvfb -ac :99 -screen 0 1920x1080x16 & 28 | gradle runIdeForUiTests & 29 | - os: windows-latest 30 | runIde: start gradlew.bat runIdeForUiTests 31 | - os: macos-latest 32 | runIde: ./gradlew runIdeForUiTests & 33 | 34 | steps: 35 | 36 | # Check out current repository 37 | - name: Fetch Sources 38 | uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 39 | 40 | # Set up Java environment for the next steps 41 | - name: Setup Java 42 | uses: actions/setup-java@f2beeb24e141e01a676f977032f5a29d81c9e27e # v5.1.0 43 | with: 44 | distribution: zulu 45 | java-version: 21 46 | 47 | # Setup Gradle 48 | - name: Setup Gradle 49 | uses: gradle/actions/setup-gradle@4d9f0ba0025fe599b4ebab900eb7f3a1d93ef4c2 # v5.0.0 50 | with: 51 | cache-read-only: ${{ github.ref != 'refs/heads/main' }} 52 | 53 | # Run IDEA prepared for UI testing 54 | - name: Run IDE 55 | run: ${{ matrix.runIde }} 56 | 57 | # Wait for IDEA to be started 58 | - name: Health Check 59 | uses: jtalk/url-health-check-action@v4 60 | with: 61 | url: http://127.0.0.1:8082 62 | max-attempts: 15 63 | retry-delay: 30s 64 | 65 | # Run tests 66 | - name: Tests 67 | run: ./gradlew test 68 | -------------------------------------------------------------------------------- /src/main/kotlin/com/github/oxc/project/oxcintellijplugin/oxlint/lsp/OxlintLspServerSupportProvider.kt: -------------------------------------------------------------------------------- 1 | package com.github.oxc.project.oxcintellijplugin.oxlint.lsp 2 | 3 | import com.github.oxc.project.oxcintellijplugin.OxcIcons 4 | import com.github.oxc.project.oxcintellijplugin.oxlint.OxlintPackage 5 | import com.github.oxc.project.oxcintellijplugin.oxlint.settings.OxlintConfigurable 6 | import com.github.oxc.project.oxcintellijplugin.oxlint.settings.OxlintSettings 7 | import com.intellij.openapi.diagnostic.thisLogger 8 | import com.intellij.openapi.project.Project 9 | import com.intellij.openapi.roots.ProjectRootManager 10 | import com.intellij.openapi.vfs.VirtualFile 11 | import com.intellij.openapi.vfs.VirtualFileManager 12 | import com.intellij.platform.lsp.api.LspServer 13 | import com.intellij.platform.lsp.api.LspServerSupportProvider 14 | import com.intellij.platform.lsp.api.lsWidget.LspServerWidgetItem 15 | import kotlin.io.path.Path 16 | 17 | class OxlintLspServerSupportProvider : LspServerSupportProvider { 18 | override fun fileOpened(project: Project, 19 | file: VirtualFile, 20 | serverStarter: LspServerSupportProvider.LspServerStarter) { 21 | thisLogger().debug("Handling fileOpened for ${file.path}") 22 | 23 | if (!OxlintSettings.getInstance(project).fileSupported(file)) { 24 | return 25 | } 26 | 27 | val oxc = OxlintPackage(project) 28 | if (!oxc.isEnabled()) { 29 | return 30 | } 31 | val executable = oxc.binaryPath(file) ?: return 32 | val nodePackage = oxc.getPackage(file) 33 | val root = if (nodePackage != null) { 34 | VirtualFileManager.getInstance().findFileByNioPath(Path(nodePackage.systemIndependentPath))?.parent?.parent ?: return 35 | } else { 36 | ProjectRootManager.getInstance(project).fileIndex.getContentRootForFile(file) ?: return 37 | } 38 | 39 | serverStarter.ensureServerStarted(OxlintLspServerDescriptor(project, root, executable, oxc.binaryParameters(file))) 40 | } 41 | 42 | override fun createLspServerWidgetItem(lspServer: LspServer, 43 | currentFile: VirtualFile?): LspServerWidgetItem { 44 | return LspServerWidgetItem(lspServer, currentFile, OxcIcons.OxcRound, OxlintConfigurable::class.java) 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /src/main/kotlin/com/github/oxc/project/oxcintellijplugin/oxfmt/lsp/OxfmtLspServerSupportProvider.kt: -------------------------------------------------------------------------------- 1 | package com.github.oxc.project.oxcintellijplugin.oxfmt.lsp 2 | 3 | import com.github.oxc.project.oxcintellijplugin.OxcIcons 4 | import com.github.oxc.project.oxcintellijplugin.oxfmt.OxfmtPackage 5 | import com.github.oxc.project.oxcintellijplugin.oxfmt.settings.OxfmtConfigurable 6 | import com.github.oxc.project.oxcintellijplugin.oxfmt.settings.OxfmtSettings 7 | import com.intellij.openapi.diagnostic.thisLogger 8 | import com.intellij.openapi.project.Project 9 | import com.intellij.openapi.roots.ProjectRootManager 10 | import com.intellij.openapi.vfs.VirtualFile 11 | import com.intellij.openapi.vfs.VirtualFileManager 12 | import com.intellij.platform.lsp.api.LspServer 13 | import com.intellij.platform.lsp.api.LspServerSupportProvider 14 | import com.intellij.platform.lsp.api.lsWidget.LspServerWidgetItem 15 | import kotlin.io.path.Path 16 | 17 | class OxfmtLspServerSupportProvider : LspServerSupportProvider { 18 | 19 | override fun fileOpened(project: Project, 20 | file: VirtualFile, 21 | serverStarter: LspServerSupportProvider.LspServerStarter) { 22 | thisLogger().debug("Handling fileOpened for ${file.path}") 23 | 24 | if (!OxfmtSettings.getInstance(project).fileSupported(file)) { 25 | return 26 | } 27 | 28 | val oxfmt = OxfmtPackage(project) 29 | if (!oxfmt.isEnabled()) { 30 | return 31 | } 32 | val executable = oxfmt.binaryPath(file) ?: return 33 | val nodePackage = oxfmt.getPackage(file) 34 | val root = if (nodePackage != null) { 35 | VirtualFileManager.getInstance() 36 | .findFileByNioPath(Path(nodePackage.systemIndependentPath))?.parent?.parent 37 | ?: return 38 | } else { 39 | ProjectRootManager.getInstance(project).fileIndex.getContentRootForFile(file) ?: return 40 | } 41 | 42 | serverStarter.ensureServerStarted( 43 | OxfmtLspServerDescriptor(project, root, executable, oxfmt.binaryParameters(file))) 44 | } 45 | 46 | override fun createLspServerWidgetItem(lspServer: LspServer, 47 | currentFile: VirtualFile?): LspServerWidgetItem { 48 | return LspServerWidgetItem(lspServer, currentFile, OxcIcons.OxcRound, 49 | OxfmtConfigurable::class.java) 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /src/main/kotlin/com/github/oxc/project/oxcintellijplugin/oxlint/settings/OxlintOnSaveFixAllActionInfo.kt: -------------------------------------------------------------------------------- 1 | package com.github.oxc.project.oxcintellijplugin.oxlint.settings 2 | 3 | import com.github.oxc.project.oxcintellijplugin.ConfigurationMode 4 | import com.github.oxc.project.oxcintellijplugin.oxlint.OxlintBundle 5 | import com.intellij.ide.actionsOnSave.ActionOnSaveBackedByOwnConfigurable 6 | import com.intellij.ide.actionsOnSave.ActionOnSaveComment 7 | import com.intellij.ide.actionsOnSave.ActionOnSaveContext 8 | 9 | class OxlintOnSaveFixAllActionInfo(actionOnSaveContext: ActionOnSaveContext) : 10 | ActionOnSaveBackedByOwnConfigurable(actionOnSaveContext, 11 | OxlintConfigurable.CONFIGURABLE_ID, 12 | OxlintConfigurable::class.java) { 13 | 14 | override fun getActionOnSaveName() = 15 | OxlintBundle.message("oxlint.run.fix.all.on.save.checkbox.on.actions.on.save.page") 16 | 17 | override fun isApplicableAccordingToStoredState(): Boolean = 18 | OxlintSettings.getInstance(project).configurationMode != ConfigurationMode.DISABLED 19 | 20 | override fun isApplicableAccordingToUiState(configurable: OxlintConfigurable): Boolean = 21 | !configurable.disabledConfiguration.isSelected 22 | 23 | override fun isActionOnSaveEnabledAccordingToStoredState() = OxlintSettings.getInstance(project).fixAllOnSave 24 | 25 | override fun isActionOnSaveEnabledAccordingToUiState(configurable: OxlintConfigurable) = 26 | configurable.fixAllOnSaveCheckBox.isSelected 27 | 28 | override fun setActionOnSaveEnabled(configurable: OxlintConfigurable, 29 | enabled: Boolean) { 30 | configurable.fixAllOnSaveCheckBox.isSelected = enabled 31 | } 32 | 33 | override fun getCommentAccordingToUiState(configurable: OxlintConfigurable): ActionOnSaveComment? { 34 | return comment() 35 | } 36 | 37 | override fun getCommentAccordingToStoredState(): ActionOnSaveComment? { 38 | return comment() 39 | } 40 | 41 | override fun getActionLinks() = listOf(createGoToPageInSettingsLink(OxlintConfigurable.CONFIGURABLE_ID)) 42 | 43 | private fun comment(): ActionOnSaveComment? { 44 | if (!isSaveActionApplicable) return ActionOnSaveComment.info(OxlintBundle.message("oxlint.on.save.comment.disabled")) 45 | 46 | return ActionOnSaveComment.info(OxlintBundle.message("oxlint.on.save.comment.fix.all")) 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /src/main/kotlin/com/github/oxc/project/oxcintellijplugin/oxfmt/settings/OxfmtOnSaveFixAllActionInfo.kt: -------------------------------------------------------------------------------- 1 | package com.github.oxc.project.oxcintellijplugin.oxfmt.settings 2 | 3 | import com.github.oxc.project.oxcintellijplugin.ConfigurationMode 4 | import com.github.oxc.project.oxcintellijplugin.oxfmt.OxfmtBundle 5 | import com.intellij.ide.actionsOnSave.ActionOnSaveBackedByOwnConfigurable 6 | import com.intellij.ide.actionsOnSave.ActionOnSaveComment 7 | import com.intellij.ide.actionsOnSave.ActionOnSaveContext 8 | 9 | class OxfmtOnSaveFixAllActionInfo(actionOnSaveContext: ActionOnSaveContext) : 10 | ActionOnSaveBackedByOwnConfigurable(actionOnSaveContext, 11 | OxfmtConfigurable.CONFIGURABLE_ID, OxfmtConfigurable::class.java) { 12 | 13 | override fun getActionOnSaveName() = 14 | OxfmtBundle.message("oxfmt.run.fix.all.on.save.checkbox.on.actions.on.save.page") 15 | 16 | override fun isApplicableAccordingToStoredState(): Boolean = 17 | OxfmtSettings.getInstance(project).configurationMode != ConfigurationMode.DISABLED 18 | 19 | override fun isApplicableAccordingToUiState(configurable: OxfmtConfigurable): Boolean = 20 | !configurable.disabledConfiguration.isSelected 21 | 22 | override fun isActionOnSaveEnabledAccordingToStoredState() = 23 | OxfmtSettings.getInstance(project).fixAllOnSave 24 | 25 | override fun isActionOnSaveEnabledAccordingToUiState(configurable: OxfmtConfigurable) = 26 | configurable.fixAllOnSaveCheckBox.isSelected 27 | 28 | override fun setActionOnSaveEnabled(configurable: OxfmtConfigurable, enabled: Boolean) { 29 | configurable.fixAllOnSaveCheckBox.isSelected = enabled 30 | } 31 | 32 | override fun getCommentAccordingToUiState( 33 | configurable: OxfmtConfigurable): ActionOnSaveComment? { 34 | return comment() 35 | } 36 | 37 | override fun getCommentAccordingToStoredState(): ActionOnSaveComment? { 38 | return comment() 39 | } 40 | 41 | override fun getActionLinks() = 42 | listOf(createGoToPageInSettingsLink(OxfmtConfigurable.CONFIGURABLE_ID)) 43 | 44 | private fun comment(): ActionOnSaveComment? { 45 | if (!isSaveActionApplicable) return ActionOnSaveComment.info( 46 | OxfmtBundle.message("oxfmt.on.save.comment.disabled")) 47 | 48 | return ActionOnSaveComment.info(OxfmtBundle.message("oxfmt.on.save.comment.fix.all")) 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /src/main/resources/messages/OxlintBundle.properties: -------------------------------------------------------------------------------- 1 | oxlint.config.path.label=Path to Oxlint Config: 2 | oxlint.configure.extensions.link=Configure file extensions 3 | oxlint.file.not.supported.description=The file "{0}" is not supported by Oxlint. 4 | oxlint.file.not.supported.title=Unsupported file 5 | oxlint.fix.all.failure.description=An error occurred while applying 'Fix All': {0} 6 | oxlint.fix.all.failure.label=Failed to Apply 'Fix All' 7 | oxlint.fix.all.on.save.checkbox.on.actions.on.save.page=Run 'Fix All' with Oxlint on Save 8 | oxlint.fix.all.on.save.failure.description=An error occurred while running 'Fix All' on save: {0} 9 | oxlint.fix.all.on.save.failure.label=Failed to apply 'Fix all' on save 10 | oxlint.fix.all.success.description='Fix All' successfully applied. 11 | oxlint.fix.all.success.label=Fixes applied successfully 12 | oxlint.language.server.not.found=Oxlint Language Server not found. 13 | oxlint.language.server.restarted=Oxlint language server restarted. 14 | oxlint.name=Oxlint 15 | oxlint.on.save.comment.disabled=Oxlint integration is disabled. 16 | oxlint.on.save.comment.fix.all=Oxlint 'Fix All' 17 | oxlint.run.fix.all=Running Oxlint 'Fix All' 18 | oxlint.run.fix.all.on.save.checkbox.on.actions.on.save.page=Run Oxlint 'Fix All' on Save 19 | oxlint.run.fix.all.on.save.label=Run 'Fix All' on &Save 20 | oxlint.run.quickfix=Running Oxlint Quickfix 21 | oxlint.schema.name=Oxlint 22 | oxlint.settings.languageServerPath=Path to Oxlint Language Server: 23 | oxlint.settings.oxlintRunTrigger=Oxlint Execution Trigger: 24 | oxlint.settings.unusedDisableDirectives=Unused Disable Directives: 25 | oxlint.supported.extensions.comment=Enter file extensions separated by commas (e.g., .js, .ts, .jsx). Each extension must start with '.' and contain only alphanumeric characters. 26 | oxlint.supported.extensions.label=Supported File Extensions: 27 | oxlint.type.aware.label=Enable type aware rules 28 | oxlint.diagnostic.unknown.code=Unknown Code 29 | oxlint.disable.nested.config.label=Disable nested config lookups 30 | oxlint.fix.kind.label=Fix Kind 31 | oxlint.manual.add.lsp.argument=Add --lsp CLI argument 32 | oxlint.manual.add.lsp.argument.help=Oxlint 1.29.0 or newer had a change in architecture. As a result when manually configuring the LSP path to the `oxlint` executable (not the `oxc_language_server` executable), the IDE needs to know when to supply the new required --lsp flag. Checking this box will cause that flag to be supplied. 33 | -------------------------------------------------------------------------------- /src/main/kotlin/com/github/oxc/project/oxcintellijplugin/oxfmt/actions/OxfmtFixAllOnSaveAction.kt: -------------------------------------------------------------------------------- 1 | package com.github.oxc.project.oxcintellijplugin.oxfmt.actions 2 | 3 | import com.github.oxc.project.oxcintellijplugin.NOTIFICATION_GROUP 4 | import com.github.oxc.project.oxcintellijplugin.oxfmt.OxfmtBundle 5 | import com.github.oxc.project.oxcintellijplugin.oxfmt.services.OxfmtServerService 6 | import com.github.oxc.project.oxcintellijplugin.oxfmt.settings.OxfmtSettings 7 | import com.intellij.ide.actionsOnSave.impl.ActionsOnSaveFileDocumentManagerListener 8 | import com.intellij.notification.NotificationGroupManager 9 | import com.intellij.notification.NotificationType 10 | import com.intellij.openapi.editor.Document 11 | import com.intellij.openapi.fileEditor.FileDocumentManager 12 | import com.intellij.openapi.project.Project 13 | import com.intellij.platform.ide.progress.runWithModalProgressBlocking 14 | import kotlinx.coroutines.withTimeout 15 | 16 | class OxfmtFixAllOnSaveAction : ActionsOnSaveFileDocumentManagerListener.ActionOnSave() { 17 | 18 | override fun isEnabledForProject(project: Project): Boolean { 19 | return OxfmtSettings.getInstance(project).fixAllOnSave 20 | } 21 | 22 | override fun processDocuments(project: Project, documents: Array) { 23 | val notificationGroup = NotificationGroupManager.getInstance().getNotificationGroup(NOTIFICATION_GROUP) 24 | 25 | runWithModalProgressBlocking(project, OxfmtBundle.message("oxfmt.run.fix.all")) { 26 | try { 27 | withTimeout(5_000) { 28 | documents.filter { 29 | val settings = OxfmtSettings.getInstance(project) 30 | val manager = FileDocumentManager.getInstance() 31 | val virtualFile = manager.getFile(it) ?: return@filter false 32 | return@filter settings.fileSupported(virtualFile) 33 | }.forEach { 34 | OxfmtServerService.getInstance(project).fixAll(it) 35 | } 36 | } 37 | } catch (e: Exception) { 38 | notificationGroup.createNotification( 39 | title = OxfmtBundle.message("oxfmt.fix.all.on.save.failure.label"), 40 | content = OxfmtBundle.message("oxfmt.fix.all.on.save.failure.description", 41 | e.message.toString()), type = NotificationType.ERROR).notify(project) 42 | } 43 | } 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /src/main/resources/icons/oxcRound.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /src/main/resources/META-INF/pluginIcon.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /src/main/kotlin/com/github/oxc/project/oxcintellijplugin/oxlint/actions/OxlintFixAllOnSaveAction.kt: -------------------------------------------------------------------------------- 1 | package com.github.oxc.project.oxcintellijplugin.oxlint.actions 2 | 3 | import com.github.oxc.project.oxcintellijplugin.NOTIFICATION_GROUP 4 | import com.github.oxc.project.oxcintellijplugin.oxlint.OxlintBundle 5 | import com.github.oxc.project.oxcintellijplugin.oxlint.services.OxlintServerService 6 | import com.github.oxc.project.oxcintellijplugin.oxlint.settings.OxlintSettings 7 | import com.intellij.ide.actionsOnSave.impl.ActionsOnSaveFileDocumentManagerListener 8 | import com.intellij.notification.NotificationGroupManager 9 | import com.intellij.notification.NotificationType 10 | import com.intellij.openapi.editor.Document 11 | import com.intellij.openapi.fileEditor.FileDocumentManager 12 | import com.intellij.openapi.project.Project 13 | import com.intellij.platform.ide.progress.runWithModalProgressBlocking 14 | import kotlinx.coroutines.withTimeout 15 | 16 | class OxlintFixAllOnSaveAction : ActionsOnSaveFileDocumentManagerListener.ActionOnSave() { 17 | override fun isEnabledForProject(project: Project): Boolean { 18 | return OxlintSettings.getInstance(project).fixAllOnSave 19 | } 20 | 21 | override fun processDocuments(project: Project, 22 | documents: Array) { 23 | val notificationGroup = NotificationGroupManager.getInstance().getNotificationGroup(NOTIFICATION_GROUP) 24 | 25 | runWithModalProgressBlocking(project, 26 | OxlintBundle.message("oxlint.run.fix.all")) { 27 | try { 28 | withTimeout(5_000) { 29 | documents.filter { 30 | val settings = OxlintSettings.getInstance(project) 31 | val manager = FileDocumentManager.getInstance() 32 | val virtualFile = manager.getFile(it) ?: return@filter false 33 | return@filter settings.fileSupported(virtualFile) 34 | }.forEach { 35 | OxlintServerService.getInstance(project).fixAll(it) 36 | } 37 | } 38 | } catch (e: Exception) { 39 | notificationGroup.createNotification( 40 | title = OxlintBundle.message("oxlint.fix.all.on.save.failure.label"), 41 | content = OxlintBundle.message( 42 | "oxlint.fix.all.on.save.failure.description", 43 | e.message.toString() 44 | ), 45 | type = NotificationType.ERROR).notify(project) 46 | } 47 | } 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /src/main/kotlin/com/github/oxc/project/oxcintellijplugin/NodeProcessCommandBuilder.kt: -------------------------------------------------------------------------------- 1 | package com.github.oxc.project.oxcintellijplugin 2 | 3 | import com.github.oxc.project.oxcintellijplugin.oxlint.OxlintBundle 4 | import com.intellij.execution.ExecutionException 5 | import com.intellij.execution.target.value.TargetValue 6 | import com.intellij.javascript.nodejs.execution.NodeTargetRun 7 | import com.intellij.javascript.nodejs.execution.NodeTargetRunOptions.Companion.of 8 | import com.intellij.javascript.nodejs.interpreter.NodeJsInterpreter 9 | import com.intellij.openapi.project.Project 10 | import com.intellij.openapi.vfs.VirtualFile 11 | import java.nio.charset.Charset 12 | 13 | class NodeProcessCommandBuilder( 14 | project: Project, 15 | interpreter: NodeJsInterpreter, 16 | ) : ProcessCommandBuilder { 17 | 18 | private val target = NodeTargetRun(interpreter, project, null, of(false)) 19 | private val builder = target.commandLineBuilder 20 | private var executable: String? = null 21 | private var workingDir: String? = null 22 | private var inputFile: VirtualFile? = null 23 | private var charset: Charset? = null 24 | private val parameters = mutableListOf() 25 | 26 | override fun setWorkingDirectory(path: String?): ProcessCommandBuilder { 27 | workingDir = path 28 | return this 29 | } 30 | 31 | override fun setInputFile(file: VirtualFile?): ProcessCommandBuilder { 32 | inputFile = file 33 | return this 34 | } 35 | 36 | override fun addParameters(params: List): ProcessCommandBuilder { 37 | parameters.addAll(params.map { 38 | when (it) { 39 | is ProcessCommandParameter.Value -> it.value 40 | is ProcessCommandParameter.FilePath -> target.convertLocalPathToTargetPath(it.path.toString()) 41 | } 42 | }) 43 | return this 44 | } 45 | 46 | override fun setExecutable(executable: String): ProcessCommandBuilder { 47 | this.executable = executable 48 | return this 49 | } 50 | 51 | override fun setCharset(charset: Charset): ProcessCommandBuilder { 52 | this.charset = charset 53 | return this 54 | } 55 | 56 | override fun build(): OxcTargetRun { 57 | val exec = executable ?: throw ExecutionException(OxlintBundle.message("oxlint.language.server.not.found")) 58 | 59 | workingDir?.let { builder.setWorkingDirectory(target.path(it)) } 60 | builder.addParameter(target.path(exec)) 61 | parameters.forEach { builder.addParameter(it) } 62 | inputFile?.let { builder.setInputFile(TargetValue.fixed(it.path)) } 63 | charset?.let { builder.charset = it } 64 | 65 | return OxcTargetRun.Node(target) 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /src/main/kotlin/com/github/oxc/project/oxcintellijplugin/oxfmt/settings/OxfmtSettings.kt: -------------------------------------------------------------------------------- 1 | package com.github.oxc.project.oxcintellijplugin.oxfmt.settings 2 | 3 | import com.github.oxc.project.oxcintellijplugin.ConfigurationMode 4 | import com.github.oxc.project.oxcintellijplugin.oxfmt.settings.OxfmtSettingsState.Companion.DEFAULT_EXTENSION_LIST 5 | import com.intellij.openapi.components.Service 6 | import com.intellij.openapi.components.SettingsCategory 7 | import com.intellij.openapi.components.SimplePersistentStateComponent 8 | import com.intellij.openapi.components.State 9 | import com.intellij.openapi.components.Storage 10 | import com.intellij.openapi.components.service 11 | import com.intellij.openapi.project.Project 12 | import com.intellij.openapi.vfs.VirtualFile 13 | import java.io.File 14 | 15 | @Service(Service.Level.PROJECT) 16 | @State(name = "OxfmtSettings", storages = [Storage("OxfmtSettings.xml")], 17 | category = SettingsCategory.TOOLS) 18 | class OxfmtSettings(private val project: Project) : 19 | SimplePersistentStateComponent(OxfmtSettingsState()) { 20 | 21 | var configurationMode: ConfigurationMode 22 | get() = state.configurationMode 23 | set(value) { 24 | state.configurationMode = value 25 | } 26 | 27 | var supportedExtensions: MutableList 28 | get() = state.supportedExtensions.takeIf { it.isNotEmpty() } 29 | ?: DEFAULT_EXTENSION_LIST.toMutableList() 30 | set(value) { 31 | state.supportedExtensions.clear() 32 | state.supportedExtensions.addAll(value) 33 | } 34 | 35 | var binaryPath: String 36 | get() = state.binaryPath ?: "" 37 | set(value) { 38 | state.binaryPath = value 39 | } 40 | 41 | var configPath: String 42 | get() = state.configPath ?: "" 43 | set(value) { 44 | val file = File(value) 45 | if (file.isFile) { 46 | state.configPath = file.path 47 | return 48 | } 49 | state.configPath = value 50 | } 51 | 52 | var fixAllOnSave: Boolean 53 | get() = isEnabled() && state.fixAllOnSave 54 | set(value) { 55 | state.fixAllOnSave = value 56 | } 57 | 58 | fun isEnabled(): Boolean { 59 | return configurationMode !== ConfigurationMode.DISABLED 60 | } 61 | 62 | fun fileSupported(file: VirtualFile): Boolean { 63 | val fileExtension = file.extension 64 | return if (fileExtension != null) { 65 | supportedExtensions.contains(".$fileExtension") 66 | } else { 67 | false 68 | } 69 | } 70 | 71 | companion object { 72 | 73 | @JvmStatic 74 | fun getInstance(project: Project): OxfmtSettings = project.service() 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /gradlew.bat: -------------------------------------------------------------------------------- 1 | @rem 2 | @rem Copyright 2015 the original author or authors. 3 | @rem 4 | @rem Licensed under the Apache License, Version 2.0 (the "License"); 5 | @rem you may not use this file except in compliance with the License. 6 | @rem You may obtain a copy of the License at 7 | @rem 8 | @rem https://www.apache.org/licenses/LICENSE-2.0 9 | @rem 10 | @rem Unless required by applicable law or agreed to in writing, software 11 | @rem distributed under the License is distributed on an "AS IS" BASIS, 12 | @rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | @rem See the License for the specific language governing permissions and 14 | @rem limitations under the License. 15 | @rem 16 | @rem SPDX-License-Identifier: Apache-2.0 17 | @rem 18 | 19 | @if "%DEBUG%"=="" @echo off 20 | @rem ########################################################################## 21 | @rem 22 | @rem Gradle startup script for Windows 23 | @rem 24 | @rem ########################################################################## 25 | 26 | @rem Set local scope for the variables with windows NT shell 27 | if "%OS%"=="Windows_NT" setlocal 28 | 29 | set DIRNAME=%~dp0 30 | if "%DIRNAME%"=="" set DIRNAME=. 31 | @rem This is normally unused 32 | set APP_BASE_NAME=%~n0 33 | set APP_HOME=%DIRNAME% 34 | 35 | @rem Resolve any "." and ".." in APP_HOME to make it shorter. 36 | for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi 37 | 38 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 39 | set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" 40 | 41 | @rem Find java.exe 42 | if defined JAVA_HOME goto findJavaFromJavaHome 43 | 44 | set JAVA_EXE=java.exe 45 | %JAVA_EXE% -version >NUL 2>&1 46 | if %ERRORLEVEL% equ 0 goto execute 47 | 48 | echo. 1>&2 49 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 1>&2 50 | echo. 1>&2 51 | echo Please set the JAVA_HOME variable in your environment to match the 1>&2 52 | echo location of your Java installation. 1>&2 53 | 54 | goto fail 55 | 56 | :findJavaFromJavaHome 57 | set JAVA_HOME=%JAVA_HOME:"=% 58 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 59 | 60 | if exist "%JAVA_EXE%" goto execute 61 | 62 | echo. 1>&2 63 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 1>&2 64 | echo. 1>&2 65 | echo Please set the JAVA_HOME variable in your environment to match the 1>&2 66 | echo location of your Java installation. 1>&2 67 | 68 | goto fail 69 | 70 | :execute 71 | @rem Setup the command line 72 | 73 | 74 | 75 | @rem Execute Gradle 76 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -jar "%APP_HOME%\gradle\wrapper\gradle-wrapper.jar" %* 77 | 78 | :end 79 | @rem End local scope for the variables with windows NT shell 80 | if %ERRORLEVEL% equ 0 goto mainEnd 81 | 82 | :fail 83 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 84 | rem the _cmd.exe /c_ return code! 85 | set EXIT_CODE=%ERRORLEVEL% 86 | if %EXIT_CODE% equ 0 set EXIT_CODE=1 87 | if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE% 88 | exit /b %EXIT_CODE% 89 | 90 | :mainEnd 91 | if "%OS%"=="Windows_NT" endlocal 92 | 93 | :omega 94 | -------------------------------------------------------------------------------- /src/main/kotlin/com/github/oxc/project/oxcintellijplugin/GeneralProcessCommandBuilder.kt: -------------------------------------------------------------------------------- 1 | package com.github.oxc.project.oxcintellijplugin 2 | 3 | import com.github.oxc.project.oxcintellijplugin.oxlint.OxlintBundle 4 | import com.intellij.execution.ExecutionException 5 | import com.intellij.execution.configurations.GeneralCommandLine 6 | import com.intellij.execution.wsl.WSLCommandLineOptions 7 | import com.intellij.execution.wsl.WslPath 8 | import com.intellij.execution.wsl.getWslPathSafe 9 | import com.intellij.openapi.vfs.VirtualFile 10 | import java.io.File 11 | import java.nio.charset.Charset 12 | import kotlin.io.path.Path 13 | 14 | class GeneralProcessCommandBuilder : ProcessCommandBuilder { 15 | private val command = 16 | GeneralCommandLine().withParentEnvironmentType(GeneralCommandLine.ParentEnvironmentType.CONSOLE) 17 | private var executable: String? = null 18 | private var workingDir: String? = null 19 | private var inputFile: VirtualFile? = null 20 | private var charset: Charset? = null 21 | private val parameters = mutableListOf() 22 | 23 | override fun setWorkingDirectory(path: String?): ProcessCommandBuilder { 24 | workingDir = path 25 | return this 26 | } 27 | 28 | override fun setInputFile(file: VirtualFile?): ProcessCommandBuilder { 29 | inputFile = file 30 | return this 31 | } 32 | 33 | override fun addParameters(params: List): ProcessCommandBuilder { 34 | parameters.addAll(params) 35 | return this 36 | } 37 | 38 | override fun setExecutable(executable: String): ProcessCommandBuilder { 39 | this.executable = executable 40 | return this 41 | } 42 | 43 | override fun setCharset(charset: Charset): ProcessCommandBuilder { 44 | this.charset = charset 45 | return this 46 | } 47 | 48 | override fun build(): OxcTargetRun { 49 | val exec = executable ?: throw ExecutionException(OxlintBundle.message("oxlint.language.server.not.found")) 50 | 51 | command.withExePath(exec) 52 | workingDir?.let { command.withWorkingDirectory(Path(it)) } 53 | inputFile?.let { command.withInput(File(it.path)) } 54 | charset?.let { command.withCharset(it) } 55 | 56 | // patch the command line to be run on WSL 2 instead of the host 57 | val wslDistribution = WslPath.getDistributionByWindowsUncPath(exec) 58 | if (wslDistribution != null) { 59 | command.withExePath(wslDistribution.getWslPathSafe(Path(exec))) 60 | 61 | // add command parameters, converting file paths if needed 62 | command.addParameters(parameters.map { 63 | when (it) { 64 | is ProcessCommandParameter.Value -> it.value 65 | is ProcessCommandParameter.FilePath -> wslDistribution.getWslPathSafe(it.path) 66 | } 67 | }) 68 | 69 | val options = WSLCommandLineOptions().apply { 70 | isPassEnvVarsUsingInterop = true 71 | 72 | workingDir?.let { 73 | remoteWorkingDirectory = wslDistribution.getWslPathSafe(Path(it)) 74 | } 75 | } 76 | 77 | wslDistribution.patchCommandLine(command, null, options) 78 | } else { 79 | command.addParameters(parameters.map { it.toString() }) 80 | } 81 | 82 | return OxcTargetRun.General(command, wslDistribution) 83 | } 84 | } 85 | -------------------------------------------------------------------------------- /src/main/kotlin/com/github/oxc/project/oxcintellijplugin/oxfmt/services/OxfmtServerService.kt: -------------------------------------------------------------------------------- 1 | package com.github.oxc.project.oxcintellijplugin.oxfmt.services 2 | 3 | import com.github.oxc.project.oxcintellijplugin.NOTIFICATION_GROUP 4 | import com.github.oxc.project.oxcintellijplugin.oxfmt.OxfmtBundle 5 | import com.github.oxc.project.oxcintellijplugin.oxfmt.lsp.OxfmtLspServerSupportProvider 6 | import com.intellij.application.options.CodeStyle 7 | import com.intellij.notification.NotificationGroupManager 8 | import com.intellij.notification.NotificationType 9 | import com.intellij.openapi.command.WriteCommandAction 10 | import com.intellij.openapi.components.Service 11 | import com.intellij.openapi.editor.Document 12 | import com.intellij.openapi.fileEditor.FileDocumentManager 13 | import com.intellij.openapi.project.Project 14 | import com.intellij.openapi.vfs.VirtualFile 15 | import com.intellij.platform.lsp.api.LspServerManager 16 | import org.eclipse.lsp4j.DocumentFormattingParams 17 | import org.eclipse.lsp4j.FormattingOptions 18 | 19 | @Service(Service.Level.PROJECT) 20 | class OxfmtServerService(private val project: Project) { 21 | 22 | private val PROVIDER_CLASS = OxfmtLspServerSupportProvider::class.java 23 | private val GROUP_ID = "Oxc" 24 | 25 | companion object { 26 | 27 | fun getInstance(project: Project): OxfmtServerService = 28 | project.getService(OxfmtServerService::class.java) 29 | } 30 | 31 | private fun getServer(file: VirtualFile) = 32 | LspServerManager.getInstance(project).getServersForProvider(PROVIDER_CLASS) 33 | .firstOrNull { server -> server.descriptor.isSupportedFile(file) } 34 | 35 | suspend fun fixAll(document: Document) { 36 | val manager = FileDocumentManager.getInstance() 37 | val file = manager.getFile(document) ?: return 38 | 39 | fixAll(file, document) 40 | } 41 | 42 | suspend fun fixAll(file: VirtualFile, document: Document) { 43 | val server = getServer(file) ?: return 44 | val codeStyleSettings = CodeStyle.getSettings(project, document) 45 | val indentOptions = codeStyleSettings.getIndentOptionsByDocument(project, document) 46 | 47 | val documentFormattingParams = DocumentFormattingParams(server.getDocumentIdentifier(file), 48 | FormattingOptions(indentOptions.INDENT_SIZE, !indentOptions.USE_TAB_CHARACTER)) 49 | 50 | val formattingResults = server.sendRequest { 51 | it.textDocumentService.formatting(documentFormattingParams) 52 | } 53 | 54 | WriteCommandAction.runWriteCommandAction(project, OxfmtBundle.message("oxfmt.run.quickfix"), 55 | GROUP_ID, { 56 | formattingResults?.forEach { 57 | val startLineOffset = document.getLineStartOffset(it.range.start.line) 58 | val endLineOffset = document.getLineStartOffset(it.range.end.line) 59 | document.replaceString(startLineOffset + it.range.start.character, 60 | endLineOffset + it.range.end.character, it.newText) 61 | } 62 | }) 63 | } 64 | 65 | fun restartServer() { 66 | LspServerManager.getInstance(project).stopAndRestartIfNeeded(PROVIDER_CLASS) 67 | } 68 | 69 | fun stopServer() { 70 | LspServerManager.getInstance(project).stopServers(PROVIDER_CLASS) 71 | } 72 | 73 | fun notifyRestart() { 74 | NotificationGroupManager.getInstance().getNotificationGroup(NOTIFICATION_GROUP) 75 | .createNotification(OxfmtBundle.message("oxfmt.language.server.restarted"), "", 76 | NotificationType.INFORMATION).notify(project) 77 | } 78 | } 79 | -------------------------------------------------------------------------------- /src/test/kotlin/com/github/oxc/project/oxcintellijplugin/extensions/TestCaseExt.kt: -------------------------------------------------------------------------------- 1 | package com.github.oxc.project.oxcintellijplugin.extensions 2 | 3 | import com.intellij.openapi.project.Project 4 | import com.intellij.openapi.util.Disposer 5 | import com.intellij.openapi.vfs.VirtualFile 6 | import com.intellij.platform.lsp.api.LspServer 7 | import com.intellij.platform.lsp.api.LspServerDescriptor 8 | import com.intellij.platform.lsp.api.LspServerManager 9 | import com.intellij.platform.lsp.api.LspServerManagerListener 10 | import com.intellij.platform.lsp.api.LspServerState 11 | import com.intellij.testFramework.ExpectedHighlightingData 12 | import com.intellij.testFramework.PlatformTestUtil 13 | import com.intellij.testFramework.fixtures.CodeInsightTestFixture 14 | import com.intellij.testFramework.fixtures.impl.CodeInsightTestFixtureImpl 15 | import com.intellij.tool.withRetryAsync 16 | import java.util.concurrent.atomic.AtomicBoolean 17 | import kotlin.time.Duration 18 | import kotlin.time.Duration.Companion.seconds 19 | import kotlinx.coroutines.runBlocking 20 | 21 | fun CodeInsightTestFixture.configureByFileAndCheckLanguageServerHighlighting(lspServerDescriptorClass: Class, filePath: String) { 22 | val configuredFile = this.configureByFile(filePath).virtualFile 23 | val project = this.project 24 | val fixture = this 25 | 26 | runBlocking { 27 | withRetryAsync(retries = 3, delayBetweenRetries = 1.seconds, retryAction = { 28 | val expectedHighlightingData = ExpectedHighlightingData(editor.document, true, true, 29 | true, false) 30 | expectedHighlightingData.init() 31 | waitForLanguageServerDiagnostics(project, lspServerDescriptorClass, configuredFile) 32 | 33 | (fixture as CodeInsightTestFixtureImpl).collectAndCheckHighlighting( 34 | expectedHighlightingData) 35 | }) 36 | } 37 | } 38 | 39 | private fun waitForLanguageServerDiagnostics(project: Project, lspServerDescriptorClass: Class, 40 | fileNeedingDiagnostics: VirtualFile, timeout: Duration = 10.seconds) { 41 | val diagnosticsReceived = AtomicBoolean() 42 | val shutdownReceived = AtomicBoolean() 43 | 44 | val disposable = Disposer.newDisposable() 45 | try { 46 | LspServerManager.getInstance(project) 47 | .addLspServerManagerListener(object : LspServerManagerListener { 48 | override fun serverStateChanged(lspServer: LspServer) { 49 | if (!lspServerDescriptorClass.isInstance(lspServer.descriptor)) { 50 | return 51 | } 52 | if (lspServer.state in arrayOf(LspServerState.ShutdownNormally, 53 | LspServerState.ShutdownUnexpectedly) 54 | ) { 55 | shutdownReceived.set(true) 56 | } 57 | } 58 | 59 | override fun diagnosticsReceived(lspServer: LspServer, file: VirtualFile) { 60 | if (!lspServerDescriptorClass.isInstance(lspServer.descriptor)) { 61 | return 62 | } 63 | if (file != fileNeedingDiagnostics) { 64 | return 65 | } 66 | diagnosticsReceived.set(true) 67 | } 68 | }, disposable, true) 69 | 70 | PlatformTestUtil.waitWithEventsDispatching( 71 | "Timeout waiting for diagnostics for $fileNeedingDiagnostics", 72 | { diagnosticsReceived.get() || shutdownReceived.get() }, timeout.inWholeSeconds.toInt()) 73 | } finally { 74 | Disposer.dispose(disposable) 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /src/main/kotlin/com/github/oxc/project/oxcintellijplugin/oxfmt/OxfmtPackage.kt: -------------------------------------------------------------------------------- 1 | package com.github.oxc.project.oxcintellijplugin.oxfmt 2 | 3 | import com.github.oxc.project.oxcintellijplugin.ConfigurationMode 4 | import com.github.oxc.project.oxcintellijplugin.ProcessCommandParameter 5 | import com.github.oxc.project.oxcintellijplugin.oxfmt.settings.OxfmtSettings 6 | import com.intellij.javascript.nodejs.interpreter.NodeJsInterpreterManager 7 | import com.intellij.javascript.nodejs.util.NodePackage 8 | import com.intellij.javascript.nodejs.util.NodePackageDescriptor 9 | import com.intellij.openapi.project.Project 10 | import com.intellij.openapi.vfs.VirtualFile 11 | import java.nio.file.Paths 12 | 13 | class OxfmtPackage(private val project: Project) { 14 | 15 | private val packageName = "oxfmt" 16 | private val packageDescription = NodePackageDescriptor(packageName) 17 | 18 | fun getPackage(virtualFile: VirtualFile?): NodePackage? { 19 | if (virtualFile != null) { 20 | val available = packageDescription.listAvailable( 21 | project, 22 | NodeJsInterpreterManager.getInstance(project).interpreter, 23 | virtualFile, 24 | false, 25 | true 26 | ) 27 | if (available.isNotEmpty()) { 28 | return available[0] 29 | } 30 | } 31 | 32 | val pkg = packageDescription.findUnambiguousDependencyPackage(project) 33 | ?: NodePackage.findDefaultPackage( 34 | project, 35 | packageName, 36 | NodeJsInterpreterManager.getInstance(project).interpreter 37 | ) 38 | 39 | return pkg 40 | } 41 | 42 | fun configPath(): String? { 43 | val settings = OxfmtSettings.getInstance(project) 44 | val configurationMode = settings.configurationMode 45 | return when (configurationMode) { 46 | ConfigurationMode.DISABLED -> null 47 | ConfigurationMode.AUTOMATIC -> null 48 | ConfigurationMode.MANUAL -> settings.configPath 49 | } 50 | } 51 | 52 | fun binaryPath( 53 | virtualFile: VirtualFile, 54 | ): String? { 55 | val settings = OxfmtSettings.getInstance(project) 56 | val configurationMode = settings.configurationMode 57 | 58 | return when (configurationMode) { 59 | ConfigurationMode.DISABLED -> null 60 | ConfigurationMode.AUTOMATIC -> findOxfmtExecutable(virtualFile) 61 | ConfigurationMode.MANUAL -> settings.binaryPath.ifBlank { 62 | findOxfmtExecutable(virtualFile) 63 | } 64 | } 65 | } 66 | 67 | fun binaryParameters(virtualFile: VirtualFile): List { 68 | return findOxfmtParameters(virtualFile) 69 | } 70 | 71 | fun isEnabled(): Boolean { 72 | val settings = OxfmtSettings.getInstance(project) 73 | return settings.configurationMode != ConfigurationMode.DISABLED 74 | } 75 | 76 | private fun findOxfmtExecutable(virtualFile: VirtualFile): String? { 77 | val oxfmtPackage = getPackage(virtualFile) ?: return null 78 | val path = oxfmtPackage.getAbsolutePackagePathToRequire(project) 79 | if (path != null) { 80 | return Paths.get(path, "bin/oxfmt").toString() 81 | } 82 | 83 | return null 84 | } 85 | 86 | private fun findOxfmtParameters(virtualFile: VirtualFile): List { 87 | return listOf(ProcessCommandParameter.Value("--lsp")) 88 | } 89 | 90 | companion object { 91 | 92 | const val CONFIG_NAME = ".oxfmtrc" 93 | val CONFIG_VALID_EXTENSIONS = listOf("json", "jsonc") 94 | } 95 | } 96 | -------------------------------------------------------------------------------- /src/main/kotlin/com/github/oxc/project/oxcintellijplugin/oxlint/services/OxlintServerService.kt: -------------------------------------------------------------------------------- 1 | package com.github.oxc.project.oxcintellijplugin.oxlint.services 2 | 3 | import com.github.oxc.project.oxcintellijplugin.NOTIFICATION_GROUP 4 | import com.github.oxc.project.oxcintellijplugin.oxlint.OxlintBundle 5 | import com.github.oxc.project.oxcintellijplugin.oxlint.lsp.OxlintLspServerSupportProvider 6 | import com.intellij.notification.NotificationGroupManager 7 | import com.intellij.notification.NotificationType 8 | import com.intellij.openapi.command.WriteCommandAction 9 | import com.intellij.openapi.components.Service 10 | import com.intellij.openapi.editor.Document 11 | import com.intellij.openapi.fileEditor.FileDocumentManager 12 | import com.intellij.openapi.project.Project 13 | import com.intellij.openapi.vfs.VirtualFile 14 | import com.intellij.platform.lsp.api.LspServerManager 15 | import com.intellij.platform.lsp.api.customization.LspIntentionAction 16 | import com.intellij.platform.lsp.util.getLsp4jRange 17 | import org.eclipse.lsp4j.CodeActionContext 18 | import org.eclipse.lsp4j.CodeActionParams 19 | import org.eclipse.lsp4j.CodeActionTriggerKind 20 | 21 | @Service(Service.Level.PROJECT) 22 | class OxlintServerService(private val project: Project) { 23 | private val groupId = "Oxc" 24 | 25 | companion object { 26 | fun getInstance(project: Project): OxlintServerService = project.getService(OxlintServerService::class.java) 27 | } 28 | 29 | private fun getServer(file: VirtualFile) = 30 | LspServerManager.getInstance(project).getServersForProvider(OxlintLspServerSupportProvider::class.java) 31 | .firstOrNull { server -> server.descriptor.isSupportedFile(file) } 32 | 33 | suspend fun fixAll(document: Document) { 34 | val manager = FileDocumentManager.getInstance() 35 | val file = manager.getFile(document) ?: return 36 | 37 | fixAll(file, document) 38 | } 39 | 40 | suspend fun fixAll(file: VirtualFile, document: Document) { 41 | val server = getServer(file) ?: return 42 | 43 | val commandName = OxlintBundle.message("oxlint.run.quickfix") 44 | 45 | val codeActionParams = CodeActionParams(server.getDocumentIdentifier(file), 46 | getLsp4jRange(document, 0, document.textLength), 47 | CodeActionContext().apply { 48 | diagnostics = emptyList() 49 | only = listOf("source.fixAll.oxc") 50 | triggerKind = CodeActionTriggerKind.Automatic 51 | }) 52 | 53 | val codeActionResults = server.sendRequest { it.textDocumentService.codeAction(codeActionParams) } 54 | 55 | WriteCommandAction.runWriteCommandAction(project, commandName, groupId, { 56 | codeActionResults?.forEach { 57 | // Only apply preferred actions which contain real fixes. 58 | // non-preferred options contain fixes such as disable-next-line. 59 | if (it.isRight && it.right.isPreferred) { 60 | val action = LspIntentionAction(server, it.right) 61 | if (action.isAvailable()) { 62 | action.invoke(null) 63 | } 64 | } 65 | } 66 | }) 67 | } 68 | 69 | fun restartServer() { 70 | LspServerManager.getInstance(project).stopAndRestartIfNeeded(OxlintLspServerSupportProvider::class.java) 71 | } 72 | 73 | fun stopServer() { 74 | LspServerManager.getInstance(project).stopServers(OxlintLspServerSupportProvider::class.java) 75 | } 76 | 77 | fun notifyRestart() { 78 | NotificationGroupManager.getInstance() 79 | .getNotificationGroup(NOTIFICATION_GROUP) 80 | .createNotification( 81 | OxlintBundle.message("oxlint.language.server.restarted"), 82 | "", 83 | NotificationType.INFORMATION 84 | ) 85 | .notify(project) 86 | } 87 | } 88 | -------------------------------------------------------------------------------- /.github/workflows/release.yml: -------------------------------------------------------------------------------- 1 | # GitHub Actions Workflow created for handling the release process based on the draft release prepared with the Build workflow. 2 | # Running the publishPlugin task requires all following secrets to be provided: PUBLISH_TOKEN, PRIVATE_KEY, PRIVATE_KEY_PASSWORD, CERTIFICATE_CHAIN. 3 | # See https://plugins.jetbrains.com/docs/intellij/plugin-signing.html for more information. 4 | 5 | permissions: {} 6 | 7 | name: Release 8 | on: 9 | release: 10 | types: [prereleased, released] 11 | 12 | jobs: 13 | 14 | # Prepare and publish the plugin to JetBrains Marketplace repository 15 | release: 16 | name: Publish Plugin 17 | runs-on: ubuntu-latest 18 | permissions: 19 | contents: write 20 | pull-requests: write 21 | steps: 22 | 23 | # Check out current repository 24 | - name: Fetch Sources 25 | uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 26 | with: 27 | ref: ${{ github.event.release.tag_name }} 28 | 29 | # Set up Java environment for the next steps 30 | - name: Setup Java 31 | uses: actions/setup-java@f2beeb24e141e01a676f977032f5a29d81c9e27e # v5.1.0 32 | with: 33 | distribution: zulu 34 | java-version: 21 35 | 36 | # Setup Gradle 37 | - name: Setup Gradle 38 | uses: gradle/actions/setup-gradle@4d9f0ba0025fe599b4ebab900eb7f3a1d93ef4c2 # v5.0.0 39 | with: 40 | cache-read-only: ${{ github.ref != 'refs/heads/main' }} 41 | 42 | # Set environment variables 43 | - name: Export Properties 44 | id: properties 45 | shell: bash 46 | run: | 47 | CHANGELOG="$(cat << 'EOM' | sed -e 's/^[[:space:]]*$//g' -e '/./,$!d' 48 | ${{ github.event.release.body }} 49 | EOM 50 | )" 51 | 52 | echo "changelog<> $GITHUB_OUTPUT 53 | echo "$CHANGELOG" >> $GITHUB_OUTPUT 54 | echo "EOF" >> $GITHUB_OUTPUT 55 | 56 | # Update Unreleased section with the current release note 57 | - name: Patch Changelog 58 | if: ${{ steps.properties.outputs.changelog != '' }} 59 | env: 60 | CHANGELOG: ${{ steps.properties.outputs.changelog }} 61 | run: | 62 | ./gradlew patchChangelog --release-note="$CHANGELOG" 63 | 64 | # Publish the plugin to JetBrains Marketplace 65 | - name: Publish Plugin 66 | env: 67 | PUBLISH_TOKEN: ${{ secrets.PUBLISH_TOKEN }} 68 | CERTIFICATE_CHAIN: ${{ secrets.CERTIFICATE_CHAIN }} 69 | PRIVATE_KEY: ${{ secrets.PRIVATE_KEY }} 70 | PRIVATE_KEY_PASSWORD: ${{ secrets.PRIVATE_KEY_PASSWORD }} 71 | run: ./gradlew publishPlugin 72 | 73 | # Upload artifact as a release asset 74 | - name: Upload Release Asset 75 | env: 76 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 77 | run: gh release upload ${{ github.event.release.tag_name }} ./build/distributions/* 78 | 79 | # Create a pull request 80 | - name: Create Pull Request 81 | if: ${{ steps.properties.outputs.changelog != '' }} 82 | env: 83 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 84 | run: | 85 | VERSION="${{ github.event.release.tag_name }}" 86 | BRANCH="changelog-update-$VERSION" 87 | LABEL="release changelog" 88 | 89 | git config user.email "action@github.com" 90 | git config user.name "GitHub Action" 91 | 92 | git checkout -b $BRANCH 93 | git commit -am "Changelog update - $VERSION" 94 | git push --set-upstream origin $BRANCH 95 | 96 | gh label create "$LABEL" \ 97 | --description "Pull requests with release changelog update" \ 98 | --force \ 99 | || true 100 | 101 | gh pr create \ 102 | --title "Changelog update - \`$VERSION\`" \ 103 | --body "Current pull request contains patched \`CHANGELOG.md\` file for the \`$VERSION\` version." \ 104 | --label "$LABEL" \ 105 | --head $BRANCH 106 | -------------------------------------------------------------------------------- /src/main/kotlin/com/github/oxc/project/oxcintellijplugin/oxfmt/lsp/OxfmtLspServerDescriptor.kt: -------------------------------------------------------------------------------- 1 | package com.github.oxc.project.oxcintellijplugin.oxfmt.lsp 2 | 3 | import com.github.oxc.project.oxcintellijplugin.OxcTargetRun 4 | import com.github.oxc.project.oxcintellijplugin.OxcTargetRunBuilder 5 | import com.github.oxc.project.oxcintellijplugin.ProcessCommandParameter 6 | import com.github.oxc.project.oxcintellijplugin.oxfmt.OxfmtPackage 7 | import com.github.oxc.project.oxcintellijplugin.oxfmt.settings.OxfmtSettings 8 | import com.intellij.execution.configurations.GeneralCommandLine 9 | import com.intellij.execution.process.OSProcessHandler 10 | import com.intellij.openapi.diagnostic.thisLogger 11 | import com.intellij.openapi.project.Project 12 | import com.intellij.openapi.vfs.VirtualFile 13 | import com.intellij.platform.lsp.api.LspServerDescriptor 14 | import org.eclipse.lsp4j.ClientCapabilities 15 | import org.eclipse.lsp4j.ConfigurationItem 16 | import org.eclipse.lsp4j.InitializeParams 17 | 18 | class OxfmtLspServerDescriptor( 19 | project: Project, 20 | root: VirtualFile, 21 | executable: String, 22 | executableParameters: List, 23 | ) : LspServerDescriptor(project, "Oxfmt", root) { 24 | 25 | private val targetRun: OxcTargetRun = run { 26 | val settings = OxfmtSettings.getInstance(project) 27 | val builder = OxcTargetRunBuilder(project).getBuilder(settings.configurationMode, 28 | executable).setWorkingDirectory(root.path).addParameters(executableParameters) 29 | 30 | builder.build() 31 | } 32 | 33 | override fun isSupportedFile(file: VirtualFile): Boolean { 34 | thisLogger().debug("file.path ${file.path}") 35 | return OxfmtSettings.getInstance(project).fileSupported(file) && roots.any { root -> 36 | file.toNioPath().startsWith(root.toNioPath()) 37 | } 38 | } 39 | 40 | override fun createCommandLine(): GeneralCommandLine { 41 | throw RuntimeException( 42 | "Not expected to be called because startServerProcess() is overridden") 43 | } 44 | 45 | override fun startServerProcess(): OSProcessHandler = targetRun.startProcess() 46 | 47 | override fun getFilePath(file: VirtualFile): String = targetRun.toTargetPath(file.path) 48 | 49 | override fun findLocalFileByPath(path: String): VirtualFile? = 50 | super.findLocalFileByPath(targetRun.toLocalPath(path)) 51 | 52 | override fun createInitializationOptions(): Any { 53 | val initializationOptions = roots.map { 54 | return@map mapOf("workspaceUri" to it.toNioPath().toUri().toString(), 55 | "options" to createWorkspaceConfig(it)) 56 | } 57 | thisLogger().debug("Initialization options: $initializationOptions") 58 | return initializationOptions 59 | } 60 | 61 | override fun createInitializeParams(): InitializeParams { 62 | val params = super.createInitializeParams() 63 | thisLogger().debug("Initialization params: $params") 64 | return params 65 | } 66 | 67 | override fun getWorkspaceConfiguration(item: ConfigurationItem): Any? { 68 | val myRoot = roots.find { 69 | return@find it.toNioPath().toUri().toString() == item.scopeUri 70 | } ?: return null 71 | return createWorkspaceConfig(myRoot) 72 | } 73 | 74 | override val clientCapabilities: ClientCapabilities 75 | get() { 76 | thisLogger().debug("Client Capabilities: ${super.clientCapabilities}") 77 | return super.clientCapabilities.apply { 78 | workspace.apply { 79 | configuration = true 80 | } 81 | } 82 | } 83 | 84 | override val lspGoToDefinitionSupport = false 85 | 86 | override val lspCompletionSupport = null 87 | 88 | override val lspFormattingSupport = OxfmtLspFormattingSupport(project) 89 | 90 | override val lspHoverSupport = false 91 | 92 | override val lspDiagnosticsSupport = null 93 | 94 | private fun createWorkspaceConfig(workspace: VirtualFile): Map { 95 | val oxfmtPackage = OxfmtPackage(project) 96 | 97 | return mapOf( 98 | "configPath" to null, 99 | "flags" to emptyMap(), 100 | "fmt.experimental" to true, 101 | "fmt.configPath" to oxfmtPackage.configPath(), 102 | "run" to "onSave", 103 | "typeAware" to false, 104 | "unusedDisableDirectives" to false, 105 | ) 106 | } 107 | } 108 | -------------------------------------------------------------------------------- /src/main/kotlin/com/github/oxc/project/oxcintellijplugin/OxcTargetRun.kt: -------------------------------------------------------------------------------- 1 | package com.github.oxc.project.oxcintellijplugin 2 | 3 | import com.github.oxc.project.oxcintellijplugin.oxlint.OxlintBundle 4 | import com.intellij.execution.ExecutionException 5 | import com.intellij.execution.configurations.GeneralCommandLine 6 | import com.intellij.execution.process.CapturingProcessHandler 7 | import com.intellij.execution.process.OSProcessHandler 8 | import com.intellij.execution.wsl.WSLDistribution 9 | import com.intellij.execution.wsl.WslPath 10 | import com.intellij.javascript.nodejs.execution.NodeTargetRun 11 | import com.intellij.javascript.nodejs.interpreter.NodeJsInterpreterManager 12 | import com.intellij.javascript.nodejs.interpreter.local.NodeJsLocalInterpreter 13 | import com.intellij.javascript.nodejs.interpreter.wsl.WslNodeInterpreter 14 | import com.intellij.lang.javascript.JavaScriptBundle 15 | import com.intellij.openapi.diagnostic.Logger 16 | import com.intellij.openapi.progress.EmptyProgressIndicator 17 | import com.intellij.openapi.progress.ProgressManager 18 | import com.intellij.openapi.project.Project 19 | import com.intellij.openapi.util.Computable 20 | import com.intellij.util.io.BaseOutputReader 21 | import java.io.File 22 | import kotlin.io.path.Path 23 | 24 | 25 | fun wrapStartProcess(processCreator: () -> OSProcessHandler): OSProcessHandler = 26 | ProgressManager.getInstance().runProcess(Computable(processCreator), EmptyProgressIndicator()) 27 | 28 | sealed interface OxcTargetRun { 29 | fun startProcess(): OSProcessHandler 30 | fun toTargetPath(path: String): String 31 | fun toLocalPath(path: String): String 32 | 33 | class Node(private val run: NodeTargetRun) : OxcTargetRun { 34 | override fun startProcess(): OSProcessHandler = 35 | wrapStartProcess { 36 | if (!run.envData.envs.contains("RUST_LOG")) { 37 | val logger = Logger.getInstance("#com.github.oxc.project.oxcintellijplugin") 38 | val level = if (logger.isTraceEnabled) "TRACE" else if (logger.isDebugEnabled) "DEBUG" else "INFO" 39 | run.envData = run.envData.with(mapOf("RUST_LOG" to level)) 40 | } 41 | run.startProcessEx().processHandler 42 | } 43 | 44 | override fun toTargetPath(path: String) = run.convertLocalPathToTargetPath(path) 45 | override fun toLocalPath(path: String) = run.convertTargetPathToLocalPath(path) 46 | } 47 | 48 | class General( 49 | private val command: GeneralCommandLine, 50 | private val wslDistribution: WSLDistribution? = null, 51 | ) : OxcTargetRun { 52 | override fun startProcess(): OSProcessHandler = 53 | wrapStartProcess { 54 | object : CapturingProcessHandler(command) { 55 | override fun readerOptions(): BaseOutputReader.Options { 56 | return object : BaseOutputReader.Options() { 57 | override fun splitToLines(): Boolean = false 58 | } 59 | } 60 | } 61 | } 62 | 63 | override fun toTargetPath(path: String) = wslDistribution?.getWslPath(Path(path)) ?: path 64 | override fun toLocalPath(path: String) = wslDistribution?.getWindowsPath(path) ?: path 65 | } 66 | } 67 | 68 | class OxcTargetRunBuilder(val project: Project) { 69 | fun getBuilder( 70 | configMode: ConfigurationMode, 71 | executable: String, 72 | ): ProcessCommandBuilder { 73 | if (executable.isEmpty()) { 74 | throw ExecutionException(OxlintBundle.message("oxlint.language.server.not.found")) 75 | } 76 | 77 | val wslPath = WslPath.parseWindowsUncPath(executable) 78 | 79 | // For WSL paths, always use NodeProcessCommandBuilder as it handles WSL correctly 80 | val isNodeJs = if (wslPath != null) { 81 | true 82 | } else { 83 | runCatching { 84 | File(executable).useLines { it.firstOrNull() } 85 | ?.startsWith("#!/usr/bin/env node") 86 | }.getOrNull() == true 87 | } 88 | 89 | val builder: ProcessCommandBuilder = if (configMode == ConfigurationMode.MANUAL && !isNodeJs) { 90 | GeneralProcessCommandBuilder() 91 | } else { 92 | val interpreter = NodeJsInterpreterManager.getInstance(project).interpreter 93 | if (interpreter !is NodeJsLocalInterpreter && interpreter !is WslNodeInterpreter) { 94 | throw ExecutionException(JavaScriptBundle.message("lsp.interpreter.error")) 95 | } 96 | NodeProcessCommandBuilder(project, interpreter) 97 | } 98 | 99 | return builder.setExecutable(executable).setCharset(Charsets.UTF_8) 100 | } 101 | } 102 | -------------------------------------------------------------------------------- /src/main/kotlin/com/github/oxc/project/oxcintellijplugin/oxlint/lsp/OxlintLspServerDescriptor.kt: -------------------------------------------------------------------------------- 1 | package com.github.oxc.project.oxcintellijplugin.oxlint.lsp 2 | 3 | import com.github.oxc.project.oxcintellijplugin.OxcTargetRun 4 | import com.github.oxc.project.oxcintellijplugin.OxcTargetRunBuilder 5 | import com.github.oxc.project.oxcintellijplugin.ProcessCommandParameter 6 | import com.github.oxc.project.oxcintellijplugin.oxlint.OxlintPackage 7 | import com.github.oxc.project.oxcintellijplugin.oxlint.settings.OxlintSettings 8 | import com.intellij.execution.configurations.GeneralCommandLine 9 | import com.intellij.execution.process.OSProcessHandler 10 | import com.intellij.openapi.diagnostic.thisLogger 11 | import com.intellij.openapi.project.Project 12 | import com.intellij.openapi.vfs.VirtualFile 13 | import com.intellij.platform.lsp.api.LspServerDescriptor 14 | import com.intellij.platform.lsp.api.customization.LspDiagnosticsSupport 15 | import org.eclipse.lsp4j.ClientCapabilities 16 | import org.eclipse.lsp4j.ConfigurationItem 17 | import org.eclipse.lsp4j.InitializeParams 18 | 19 | class OxlintLspServerDescriptor( 20 | project: Project, 21 | root: VirtualFile, 22 | executable: String, 23 | executableParameters: List, 24 | ) : LspServerDescriptor(project, "Oxlint", root) { 25 | private val targetRun: OxcTargetRun = run { 26 | val oxlintSettings = OxlintSettings.getInstance(project) 27 | val builder = OxcTargetRunBuilder(project).getBuilder(oxlintSettings.configurationMode, executable).setWorkingDirectory(root.path).addParameters(executableParameters) 28 | 29 | builder.build() 30 | } 31 | 32 | override fun isSupportedFile(file: VirtualFile): Boolean { 33 | thisLogger().debug("file.path ${file.path}") 34 | return OxlintSettings.getInstance(project).fileSupported(file) && roots.any { root -> 35 | file.toNioPath().startsWith(root.toNioPath()) 36 | } 37 | } 38 | 39 | override fun createCommandLine(): GeneralCommandLine { 40 | throw RuntimeException("Not expected to be called because startServerProcess() is overridden") 41 | } 42 | 43 | override fun startServerProcess(): OSProcessHandler = 44 | targetRun.startProcess() 45 | 46 | override fun getFilePath(file: VirtualFile): String = 47 | targetRun.toTargetPath(file.path) 48 | 49 | override fun findLocalFileByPath(path: String): VirtualFile? = 50 | super.findLocalFileByPath(targetRun.toLocalPath(path)) 51 | 52 | override fun createInitializationOptions(): Any { 53 | val initializationOptions = roots.map { 54 | return@map mapOf( 55 | "workspaceUri" to it.toNioPath().toUri().toString(), 56 | "options" to createWorkspaceConfig(it) 57 | ) 58 | } 59 | thisLogger().debug("Initialization options: $initializationOptions") 60 | return initializationOptions 61 | } 62 | 63 | override fun createInitializeParams(): InitializeParams { 64 | val params = super.createInitializeParams() 65 | thisLogger().debug("Initialization params: $params") 66 | return params 67 | } 68 | 69 | override fun getWorkspaceConfiguration(item: ConfigurationItem): Any? { 70 | val myRoot = roots.find { 71 | return@find it.toNioPath().toUri().toString() == item.scopeUri 72 | } ?: return null 73 | return createWorkspaceConfig(myRoot) 74 | } 75 | 76 | override val clientCapabilities: ClientCapabilities 77 | get() { 78 | thisLogger().debug("Client Capabilities: ${super.clientCapabilities}") 79 | return super.clientCapabilities.apply { 80 | workspace.apply { 81 | configuration = true 82 | } 83 | } 84 | } 85 | 86 | override val lspGoToDefinitionSupport = false 87 | 88 | override val lspCompletionSupport = null 89 | 90 | override val lspFormattingSupport = null 91 | 92 | override val lspHoverSupport = false 93 | 94 | override val lspDiagnosticsSupport: LspDiagnosticsSupport = OxlintLspDiagnosticsSupport() 95 | 96 | private fun createWorkspaceConfig(workspace: VirtualFile): Map { 97 | val oxlintPackage = OxlintPackage(project) 98 | val settings = OxlintSettings.getInstance(project) 99 | 100 | return mapOf( 101 | "configPath" to oxlintPackage.configPath(), 102 | "flags" to mapOf( 103 | "disable_nested_config" to settings.disableNestedConfig.toString(), 104 | "fix_kind" to settings.fixKind.toLspValue(), 105 | ), 106 | "run" to settings.state.runTrigger.toLspValue(), 107 | "typeAware" to settings.typeAware, 108 | "unusedDisableDirectives" to settings.state.unusedDisableDirectives.toLspValue(), 109 | ) 110 | } 111 | } 112 | -------------------------------------------------------------------------------- /src/main/kotlin/com/github/oxc/project/oxcintellijplugin/oxlint/OxlintPackage.kt: -------------------------------------------------------------------------------- 1 | package com.github.oxc.project.oxcintellijplugin.oxlint 2 | 3 | import com.github.oxc.project.oxcintellijplugin.ConfigurationMode 4 | import com.github.oxc.project.oxcintellijplugin.ProcessCommandParameter 5 | import com.github.oxc.project.oxcintellijplugin.oxlint.settings.OxlintSettings 6 | import com.intellij.javascript.nodejs.interpreter.NodeJsInterpreterManager 7 | import com.intellij.javascript.nodejs.util.NodePackage 8 | import com.intellij.javascript.nodejs.util.NodePackageDescriptor 9 | import com.intellij.openapi.project.Project 10 | import com.intellij.openapi.vfs.VirtualFile 11 | import com.intellij.util.text.SemVer 12 | import java.nio.file.Paths 13 | 14 | class OxlintPackage(private val project: Project) { 15 | private val packageName = "oxlint" 16 | private val packageDescription = NodePackageDescriptor(packageName) 17 | 18 | fun getPackage(virtualFile: VirtualFile?): NodePackage? { 19 | if (virtualFile != null) { 20 | val available = packageDescription.listAvailable( 21 | project, 22 | NodeJsInterpreterManager.getInstance(project).interpreter, 23 | virtualFile, 24 | false, 25 | true 26 | ) 27 | if (available.isNotEmpty()) { 28 | return available[0] 29 | } 30 | } 31 | 32 | val pkg = packageDescription.findUnambiguousDependencyPackage(project) ?: NodePackage.findDefaultPackage( 33 | project, 34 | packageName, 35 | NodeJsInterpreterManager.getInstance(project).interpreter 36 | ) 37 | 38 | return pkg 39 | } 40 | 41 | fun configPath(): String? { 42 | val settings = OxlintSettings.getInstance(project) 43 | val configurationMode = settings.configurationMode 44 | return when (configurationMode) { 45 | ConfigurationMode.DISABLED -> null 46 | ConfigurationMode.AUTOMATIC -> null 47 | ConfigurationMode.MANUAL -> settings.configPath 48 | } 49 | } 50 | 51 | fun binaryPath( 52 | virtualFile: VirtualFile, 53 | ): String? { 54 | val settings = OxlintSettings.getInstance(project) 55 | val configurationMode = settings.configurationMode 56 | 57 | return when (configurationMode) { 58 | ConfigurationMode.DISABLED -> null 59 | ConfigurationMode.AUTOMATIC -> findOxlintExecutable(virtualFile) 60 | ConfigurationMode.MANUAL -> settings.binaryPath.ifBlank { findOxlintExecutable(virtualFile) } 61 | } 62 | } 63 | 64 | fun binaryParameters(virtualFile: VirtualFile): List { 65 | val settings = OxlintSettings.getInstance(project) 66 | val configurationMode = settings.configurationMode 67 | 68 | return when (configurationMode) { 69 | ConfigurationMode.DISABLED -> emptyList() 70 | ConfigurationMode.AUTOMATIC -> { 71 | findOxlintParameters(virtualFile) 72 | } 73 | ConfigurationMode.MANUAL -> { 74 | if (settings.binaryPath.isBlank()) { 75 | findOxlintParameters(virtualFile) 76 | } else { 77 | settings.binaryParameters.map { ProcessCommandParameter.Value(it) } 78 | } 79 | } 80 | } 81 | } 82 | 83 | fun isEnabled(): Boolean { 84 | val settings = OxlintSettings.getInstance(project) 85 | return settings.configurationMode != ConfigurationMode.DISABLED 86 | } 87 | 88 | private fun findOxlintExecutable(virtualFile: VirtualFile): String? { 89 | val oxlintPackage = getPackage(virtualFile) ?: return null 90 | val path = oxlintPackage.getAbsolutePackagePathToRequire(project) 91 | if (path != null) { 92 | val version = oxlintPackage.getVersion(project) 93 | 94 | return if (version?.isGreaterOrEqualThan(OXLINT_FIRST_LSP_VERSION) == true) { 95 | Paths.get(path, "bin/oxlint").toString() 96 | } else { 97 | Paths.get(path, "bin/oxc_language_server").toString() 98 | } 99 | } 100 | 101 | return null 102 | } 103 | 104 | private fun findOxlintParameters(virtualFile: VirtualFile): List { 105 | val oxlintPackage = getPackage(virtualFile) ?: return emptyList() 106 | val version = oxlintPackage.getVersion(project) 107 | 108 | return if (version?.isGreaterOrEqualThan(OXLINT_FIRST_LSP_VERSION) == true) { 109 | listOf(ProcessCommandParameter.Value("--lsp")) 110 | } else { 111 | emptyList() 112 | } 113 | } 114 | 115 | companion object { 116 | const val CONFIG_NAME = ".oxlintrc" 117 | val OXLINT_FIRST_LSP_VERSION = SemVer("1.29.0", 1, 29, 0) 118 | val configValidExtensions = listOf("json") 119 | } 120 | } 121 | -------------------------------------------------------------------------------- /src/main/kotlin/com/github/oxc/project/oxcintellijplugin/oxlint/actions/OxlintFixAllAction.kt: -------------------------------------------------------------------------------- 1 | package com.github.oxc.project.oxcintellijplugin.oxlint.actions 2 | 3 | import com.github.oxc.project.oxcintellijplugin.NOTIFICATION_GROUP 4 | import com.github.oxc.project.oxcintellijplugin.OxcIcons 5 | import com.github.oxc.project.oxcintellijplugin.oxlint.OxlintBundle 6 | import com.github.oxc.project.oxcintellijplugin.oxlint.services.OxlintServerService 7 | import com.github.oxc.project.oxcintellijplugin.oxlint.settings.OxlintConfigurable 8 | import com.github.oxc.project.oxcintellijplugin.oxlint.settings.OxlintSettings 9 | import com.intellij.notification.NotificationAction 10 | import com.intellij.notification.NotificationGroupManager 11 | import com.intellij.notification.NotificationType 12 | import com.intellij.openapi.actionSystem.ActionUpdateThread 13 | import com.intellij.openapi.actionSystem.AnAction 14 | import com.intellij.openapi.actionSystem.AnActionEvent 15 | import com.intellij.openapi.actionSystem.CommonDataKeys 16 | import com.intellij.openapi.editor.Document 17 | import com.intellij.openapi.editor.EditorFactory 18 | import com.intellij.openapi.fileEditor.FileDocumentManager 19 | import com.intellij.openapi.options.ShowSettingsUtil 20 | import com.intellij.openapi.project.DumbAware 21 | import com.intellij.openapi.vfs.VirtualFile 22 | import com.intellij.openapi.vfs.readText 23 | import com.intellij.platform.ide.progress.runWithModalProgressBlocking 24 | import java.io.IOException 25 | import kotlinx.coroutines.withTimeout 26 | 27 | class OxlintFixAllAction : AnAction(), DumbAware { 28 | init { 29 | templatePresentation.icon = OxcIcons.OxcRound 30 | } 31 | 32 | override fun actionPerformed(event: AnActionEvent) { 33 | val project = event.project ?: return 34 | val (virtualFile, document) = event.getFileAndDocument() ?: return 35 | 36 | val notificationGroup = NotificationGroupManager.getInstance().getNotificationGroup(NOTIFICATION_GROUP) 37 | 38 | val settings = OxlintSettings.getInstance(project) 39 | 40 | if (!settings.fileSupported(virtualFile)) { 41 | notificationGroup.createNotification(title = OxlintBundle.message("oxlint.file.not.supported.title"), 42 | content = OxlintBundle.message("oxlint.file.not.supported.description", virtualFile.name), 43 | type = NotificationType.WARNING) 44 | .addAction(NotificationAction.createSimple(OxlintBundle.message("oxlint.configure.extensions.link")) { 45 | ShowSettingsUtil.getInstance().showSettingsDialog(project, OxlintConfigurable::class.java) 46 | }).notify(project) 47 | return 48 | } 49 | 50 | runWithModalProgressBlocking(project, 51 | OxlintBundle.message("oxlint.run.fix.all")) { 52 | try { 53 | withTimeout(5_000) { 54 | OxlintServerService.getInstance(project).fixAll(virtualFile, document) 55 | } 56 | notificationGroup.createNotification(title = OxlintBundle.message("oxlint.fix.all.success.label"), 57 | content = OxlintBundle.message("oxlint.fix.all.success.description"), 58 | type = NotificationType.INFORMATION).notify(project) 59 | } catch (e: Exception) { 60 | notificationGroup.createNotification(title = OxlintBundle.message("oxlint.fix.all.failure.label"), 61 | content = OxlintBundle.message("oxlint.fix.all.failure.description", e.message.toString()), 62 | type = NotificationType.ERROR).notify(project) 63 | } 64 | } 65 | } 66 | 67 | override fun update(e: AnActionEvent) { 68 | val project = e.project ?: return 69 | val settings = OxlintSettings.getInstance(project) 70 | 71 | val file = e.getData(CommonDataKeys.VIRTUAL_FILE) 72 | val enabled = file != null && settings.fileSupported(file) && settings.isEnabled() 73 | 74 | if (e.isFromContextMenu) { 75 | e.presentation.isVisible = enabled 76 | } 77 | e.presentation.isEnabled = enabled 78 | } 79 | 80 | override fun getActionUpdateThread(): ActionUpdateThread { 81 | return ActionUpdateThread.BGT 82 | } 83 | 84 | private fun AnActionEvent.getFileAndDocument(): Pair? { 85 | getData(CommonDataKeys.EDITOR)?.let { 86 | val document = it.document 87 | 88 | val manager = FileDocumentManager.getInstance() 89 | val file = manager.getFile(document) ?: return null 90 | 91 | return file to document 92 | } 93 | 94 | val file = getData(CommonDataKeys.VIRTUAL_FILE) ?: return null 95 | val text = try { 96 | file.readText() 97 | } catch (_: IOException) { 98 | return null 99 | } 100 | 101 | val document = EditorFactory.getInstance().createDocument(text) 102 | 103 | return file to document 104 | } 105 | } 106 | -------------------------------------------------------------------------------- /src/main/resources/META-INF/plugin.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | com.github.oxc.project.oxcintellijplugin 4 | Oxc 5 | Oxc 6 | 7 | com.intellij.modules.platform 8 | JavaScript 9 | 10 | messages.OxfmtBundle 11 | messages.OxlintBundle 12 | 13 | 14 | 15 | 16 | 17 | 19 | 21 | 22 | 30 | 38 | 39 | 44 | 45 | 50 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 69 | 70 | 71 | 72 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 88 | 92 | 93 | 94 | 95 | 97 | 99 | 100 | 101 | -------------------------------------------------------------------------------- /src/main/kotlin/com/github/oxc/project/oxcintellijplugin/oxlint/settings/OxlintSettings.kt: -------------------------------------------------------------------------------- 1 | package com.github.oxc.project.oxcintellijplugin.oxlint.settings 2 | 3 | import com.github.oxc.project.oxcintellijplugin.ConfigurationMode 4 | import com.github.oxc.project.oxcintellijplugin.oxlint.OxlintFixKind 5 | import com.github.oxc.project.oxcintellijplugin.oxlint.settings.OxlintSettingsState.Companion.DEFAULT_EXTENSION_LIST 6 | import com.intellij.openapi.components.Service 7 | import com.intellij.openapi.components.SettingsCategory 8 | import com.intellij.openapi.components.SimplePersistentStateComponent 9 | import com.intellij.openapi.components.State 10 | import com.intellij.openapi.components.Storage 11 | import com.intellij.openapi.components.service 12 | import com.intellij.openapi.diagnostic.thisLogger 13 | import com.intellij.openapi.project.Project 14 | import com.intellij.openapi.vfs.VirtualFile 15 | import java.io.File 16 | 17 | @Service(Service.Level.PROJECT) 18 | @State(name = "OxcSettings", storages = [Storage("OxcSettings.xml")], 19 | category = SettingsCategory.TOOLS) 20 | class OxlintSettings(private val project: Project) : 21 | SimplePersistentStateComponent(OxlintSettingsState()) { 22 | 23 | var configurationMode: ConfigurationMode 24 | get() = state.configurationMode 25 | set(value) { 26 | state.configurationMode = value 27 | } 28 | 29 | var supportedExtensions: MutableList 30 | get() = state.supportedExtensions.takeIf { it.isNotEmpty() } ?: DEFAULT_EXTENSION_LIST.toMutableList() 31 | set(value) { 32 | state.supportedExtensions.clear() 33 | state.supportedExtensions.addAll(value) 34 | } 35 | 36 | var binaryPath: String 37 | get() = state.binaryPath ?: "" 38 | set(value) { 39 | state.binaryPath = value 40 | } 41 | 42 | var binaryParameters: MutableList 43 | get() = state.binaryParameters 44 | set(value) { 45 | state.binaryParameters.clear() 46 | state.binaryParameters.addAll(value) 47 | } 48 | 49 | var configPath: String 50 | get() = state.configPath ?: "" 51 | set(value) { 52 | val file = File(value) 53 | if (file.isFile) { 54 | state.configPath = file.path 55 | return 56 | } 57 | state.configPath = value 58 | } 59 | 60 | var disableNestedConfig: Boolean 61 | get() { 62 | try { 63 | return flags.getOrDefault(DISABLE_NESTED_CONFIG_KEY, "false").toBoolean() 64 | } catch (exception: Exception) { 65 | thisLogger().warn("Invalid value found for $DISABLE_NESTED_CONFIG_KEY", exception) 66 | return false 67 | } 68 | } 69 | set(value) { 70 | flags[DISABLE_NESTED_CONFIG_KEY] = value.toString() 71 | } 72 | 73 | var fixAllOnSave: Boolean 74 | get() = isEnabled() && state.fixAllOnSave 75 | set(value) { 76 | state.fixAllOnSave = value 77 | } 78 | 79 | var fixKind: OxlintFixKind 80 | get() { 81 | try { 82 | return OxlintFixKind.valueOf( 83 | flags.getOrDefault(FIX_KIND_KEY, OxlintFixKind.SAFE_FIX.name).uppercase()) 84 | } catch (exception: Exception) { 85 | thisLogger().warn("Invalid value found for $FIX_KIND_KEY", exception) 86 | return OxlintFixKind.SAFE_FIX 87 | } 88 | } 89 | set(value) { 90 | flags[FIX_KIND_KEY] = value.name 91 | } 92 | 93 | /** 94 | * Only intended for legacy use. New config values should be in a separate property. 95 | */ 96 | private var flags: MutableMap 97 | get() = state.flags 98 | set(value) { 99 | state.flags = value 100 | } 101 | 102 | var runTrigger 103 | get() = state.runTrigger 104 | set(value) { 105 | state.runTrigger = value 106 | } 107 | 108 | var typeAware 109 | get() = state.typeAware 110 | set(value) { 111 | state.typeAware = value 112 | } 113 | 114 | var unusedDisableDirectivesSeverity 115 | get() = state.unusedDisableDirectives 116 | set(value) { 117 | state.unusedDisableDirectives = value 118 | } 119 | 120 | fun isEnabled(): Boolean { 121 | return configurationMode !== ConfigurationMode.DISABLED 122 | } 123 | 124 | fun fileSupported(file: VirtualFile): Boolean { 125 | val fileExtension = file.extension 126 | return if (fileExtension != null) { 127 | supportedExtensions.contains(".$fileExtension") 128 | } else { 129 | false 130 | } 131 | } 132 | 133 | companion object { 134 | 135 | const val DISABLE_NESTED_CONFIG_KEY = "disable_nested_config"; 136 | const val FIX_KIND_KEY = "fix_kind"; 137 | 138 | @JvmStatic 139 | fun getInstance(project: Project): OxlintSettings = project.service() 140 | } 141 | } 142 | -------------------------------------------------------------------------------- /src/main/kotlin/com/github/oxc/project/oxcintellijplugin/oxfmt/actions/OxfmtFixAllAction.kt: -------------------------------------------------------------------------------- 1 | package com.github.oxc.project.oxcintellijplugin.oxfmt.actions 2 | 3 | import com.github.oxc.project.oxcintellijplugin.NOTIFICATION_GROUP 4 | import com.github.oxc.project.oxcintellijplugin.OxcIcons 5 | import com.github.oxc.project.oxcintellijplugin.oxfmt.OxfmtBundle 6 | import com.github.oxc.project.oxcintellijplugin.oxfmt.services.OxfmtServerService 7 | import com.github.oxc.project.oxcintellijplugin.oxfmt.settings.OxfmtConfigurable 8 | import com.github.oxc.project.oxcintellijplugin.oxfmt.settings.OxfmtSettings 9 | import com.intellij.notification.NotificationAction 10 | import com.intellij.notification.NotificationGroupManager 11 | import com.intellij.notification.NotificationType 12 | import com.intellij.openapi.actionSystem.ActionUpdateThread 13 | import com.intellij.openapi.actionSystem.AnAction 14 | import com.intellij.openapi.actionSystem.AnActionEvent 15 | import com.intellij.openapi.actionSystem.CommonDataKeys 16 | import com.intellij.openapi.editor.Document 17 | import com.intellij.openapi.editor.EditorFactory 18 | import com.intellij.openapi.fileEditor.FileDocumentManager 19 | import com.intellij.openapi.options.ShowSettingsUtil 20 | import com.intellij.openapi.project.DumbAware 21 | import com.intellij.openapi.vfs.VirtualFile 22 | import com.intellij.openapi.vfs.readText 23 | import com.intellij.platform.ide.progress.runWithModalProgressBlocking 24 | import java.io.IOException 25 | import kotlinx.coroutines.withTimeout 26 | 27 | class OxfmtFixAllAction : AnAction(), DumbAware { 28 | init { 29 | templatePresentation.icon = OxcIcons.OxcRound 30 | } 31 | 32 | override fun actionPerformed(event: AnActionEvent) { 33 | val project = event.project ?: return 34 | val (virtualFile, document) = event.getFileAndDocument() ?: return 35 | 36 | val notificationGroup = NotificationGroupManager.getInstance().getNotificationGroup(NOTIFICATION_GROUP) 37 | 38 | val settings = OxfmtSettings.getInstance(project) 39 | 40 | if (!settings.fileSupported(virtualFile)) { 41 | notificationGroup.createNotification( 42 | title = OxfmtBundle.message("oxfmt.file.not.supported.title"), 43 | content = OxfmtBundle.message("oxfmt.file.not.supported.description", 44 | virtualFile.name), 45 | type = NotificationType.WARNING) 46 | .addAction(NotificationAction.createSimple( 47 | OxfmtBundle.message("oxfmt.configure.extensions.link")) { 48 | ShowSettingsUtil.getInstance() 49 | .showSettingsDialog(project, OxfmtConfigurable::class.java) 50 | }).notify(project) 51 | return 52 | } 53 | 54 | runWithModalProgressBlocking(project, 55 | OxfmtBundle.message("oxfmt.run.fix.all")) { 56 | try { 57 | withTimeout(5_000) { 58 | OxfmtServerService.getInstance(project).fixAll(virtualFile, document) 59 | } 60 | notificationGroup.createNotification( 61 | title = OxfmtBundle.message("oxfmt.fix.all.success.label"), 62 | content = OxfmtBundle.message("oxfmt.fix.all.success.description"), 63 | type = NotificationType.INFORMATION).notify(project) 64 | } catch (e: Exception) { 65 | notificationGroup.createNotification( 66 | title = OxfmtBundle.message("oxfmt.fix.all.failure.label"), 67 | content = OxfmtBundle.message("oxfmt.fix.all.failure.description", 68 | e.message.toString()), 69 | type = NotificationType.ERROR).notify(project) 70 | } 71 | } 72 | } 73 | 74 | override fun update(e: AnActionEvent) { 75 | val project = e.project ?: return 76 | val settings = OxfmtSettings.getInstance(project) 77 | 78 | val file = e.getData(CommonDataKeys.VIRTUAL_FILE) 79 | val enabled = file != null && settings.fileSupported(file) && settings.isEnabled() 80 | 81 | if (e.isFromContextMenu) { 82 | e.presentation.isVisible = enabled 83 | } 84 | e.presentation.isEnabled = enabled 85 | } 86 | 87 | override fun getActionUpdateThread(): ActionUpdateThread { 88 | return ActionUpdateThread.BGT 89 | } 90 | 91 | private fun AnActionEvent.getFileAndDocument(): Pair? { 92 | getData(CommonDataKeys.EDITOR)?.let { 93 | val document = it.document 94 | 95 | val manager = FileDocumentManager.getInstance() 96 | val file = manager.getFile(document) ?: return null 97 | 98 | return file to document 99 | } 100 | 101 | val file = getData(CommonDataKeys.VIRTUAL_FILE) ?: return null 102 | val text = try { 103 | file.readText() 104 | } catch (_: IOException) { 105 | return null 106 | } 107 | 108 | val document = EditorFactory.getInstance().createDocument(text) 109 | 110 | return file to document 111 | } 112 | } 113 | -------------------------------------------------------------------------------- /sandbox/oxlint/nested-configs/.oxlintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "plugins": [ 3 | "react", 4 | "unicorn", 5 | "typescript", 6 | "oxc" 7 | ], 8 | "categories": {}, 9 | "rules": { 10 | "curly": "deny", 11 | "for-direction": "warn", 12 | "no-async-promise-executor": "warn", 13 | "no-caller": "warn", 14 | "no-class-assign": "warn", 15 | "no-compare-neg-zero": "warn", 16 | "no-cond-assign": "warn", 17 | "no-const-assign": "warn", 18 | "no-constant-binary-expression": "warn", 19 | "no-constant-condition": "warn", 20 | "no-control-regex": "warn", 21 | "no-debugger": "warn", 22 | "no-delete-var": "warn", 23 | "no-dupe-class-members": "warn", 24 | "no-dupe-else-if": "warn", 25 | "no-dupe-keys": "warn", 26 | "no-duplicate-case": "warn", 27 | "no-empty-character-class": "warn", 28 | "no-empty-pattern": "warn", 29 | "no-empty-static-block": "warn", 30 | "no-eval": "warn", 31 | "no-ex-assign": "warn", 32 | "no-extra-boolean-cast": "warn", 33 | "no-func-assign": "warn", 34 | "no-global-assign": "warn", 35 | "no-import-assign": "warn", 36 | "no-invalid-regexp": "warn", 37 | "no-irregular-whitespace": "warn", 38 | "no-loss-of-precision": "warn", 39 | "no-new-native-nonconstructor": "warn", 40 | "no-nonoctal-decimal-escape": "warn", 41 | "no-obj-calls": "warn", 42 | "no-self-assign": "warn", 43 | "no-setter-return": "warn", 44 | "no-shadow-restricted-names": "warn", 45 | "no-sparse-arrays": "warn", 46 | "no-this-before-super": "warn", 47 | "no-unsafe-finally": "warn", 48 | "no-unsafe-negation": "warn", 49 | "no-unsafe-optional-chaining": "warn", 50 | "no-unused-labels": "warn", 51 | "no-unused-private-class-members": "warn", 52 | "no-unused-vars": "warn", 53 | "no-useless-catch": "warn", 54 | "no-useless-escape": "warn", 55 | "no-useless-rename": "warn", 56 | "no-with": "warn", 57 | "require-yield": "warn", 58 | "use-isnan": "warn", 59 | "valid-typeof": "warn", 60 | "oxc/bad-array-method-on-arguments": "warn", 61 | "oxc/bad-char-at-comparison": "warn", 62 | "oxc/bad-comparison-sequence": "warn", 63 | "oxc/bad-min-max-func": "warn", 64 | "oxc/bad-object-literal-comparison": "warn", 65 | "oxc/bad-replace-all-arg": "warn", 66 | "oxc/const-comparisons": "warn", 67 | "oxc/double-comparisons": "warn", 68 | "oxc/erasing-op": "warn", 69 | "oxc/missing-throw": "warn", 70 | "oxc/number-arg-out-of-range": "warn", 71 | "oxc/only-used-in-recursion": "warn", 72 | "oxc/uninvoked-array-callback": "warn", 73 | "react/jsx-key": "warn", 74 | "react/jsx-no-duplicate-props": "warn", 75 | "react/jsx-no-target-blank": "warn", 76 | "react/jsx-no-undef": "warn", 77 | "react/jsx-props-no-spread-multi": "warn", 78 | "react/no-children-prop": "warn", 79 | "react/no-danger-with-children": "warn", 80 | "react/no-direct-mutation-state": "warn", 81 | "react/no-find-dom-node": "warn", 82 | "react/no-is-mounted": "warn", 83 | "react/no-render-return-value": "warn", 84 | "react/no-string-refs": "warn", 85 | "react/void-dom-elements-no-children": "warn", 86 | "typescript/no-duplicate-enum-values": "warn", 87 | "typescript/no-extra-non-null-assertion": "warn", 88 | "typescript/no-misused-new": "warn", 89 | "typescript/no-non-null-asserted-optional-chain": "warn", 90 | "typescript/no-this-alias": "warn", 91 | "typescript/no-unnecessary-parameter-property-assignment": "warn", 92 | "typescript/no-unsafe-declaration-merging": "warn", 93 | "typescript/no-useless-empty-export": "warn", 94 | "typescript/no-wrapper-object-types": "warn", 95 | "typescript/prefer-as-const": "warn", 96 | "typescript/triple-slash-reference": "warn", 97 | "unicorn/no-await-in-promise-methods": "warn", 98 | "unicorn/no-empty-file": "warn", 99 | "unicorn/no-invalid-fetch-options": "warn", 100 | "unicorn/no-invalid-remove-event-listener": "warn", 101 | "unicorn/no-new-array": "warn", 102 | "unicorn/no-single-promise-in-promise-methods": "warn", 103 | "unicorn/no-thenable": "warn", 104 | "unicorn/no-unnecessary-await": "warn", 105 | "unicorn/no-useless-fallback-in-spread": "warn", 106 | "unicorn/no-useless-length-check": "warn", 107 | "unicorn/no-useless-spread": "warn", 108 | "unicorn/prefer-set-size": "warn", 109 | "unicorn/prefer-string-starts-ends-with": "warn" 110 | }, 111 | "settings": { 112 | "jsx-a11y": { 113 | "polymorphicPropName": null, 114 | "components": {} 115 | }, 116 | "next": { 117 | "rootDir": [] 118 | }, 119 | "react": { 120 | "formComponents": [], 121 | "linkComponents": [] 122 | }, 123 | "jsdoc": { 124 | "ignorePrivate": false, 125 | "ignoreInternal": false, 126 | "ignoreReplacesDocs": true, 127 | "overrideReplacesDocs": true, 128 | "augmentsExtendsReplacesDocs": false, 129 | "implementsReplacesDocs": false, 130 | "exemptDestructuredRootsFromChecks": false, 131 | "tagNamePreference": {} 132 | } 133 | }, 134 | "env": { 135 | "builtin": true 136 | }, 137 | "globals": {}, 138 | "ignorePatterns": [] 139 | } -------------------------------------------------------------------------------- /sandbox/oxlint/nested-configs/package-c/.oxlintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "plugins": [ 3 | "react", 4 | "unicorn", 5 | "typescript", 6 | "oxc" 7 | ], 8 | "categories": {}, 9 | "rules": { 10 | "curly": "deny", 11 | "for-direction": "warn", 12 | "no-async-promise-executor": "warn", 13 | "no-caller": "warn", 14 | "no-class-assign": "warn", 15 | "no-compare-neg-zero": "warn", 16 | "no-cond-assign": "warn", 17 | "no-const-assign": "warn", 18 | "no-constant-binary-expression": "warn", 19 | "no-constant-condition": "warn", 20 | "no-control-regex": "warn", 21 | "no-debugger": "warn", 22 | "no-delete-var": "warn", 23 | "no-dupe-class-members": "warn", 24 | "no-dupe-else-if": "warn", 25 | "no-dupe-keys": "warn", 26 | "no-duplicate-case": "warn", 27 | "no-empty-character-class": "warn", 28 | "no-empty-pattern": "warn", 29 | "no-empty-static-block": "warn", 30 | "no-eval": "warn", 31 | "no-ex-assign": "warn", 32 | "no-extra-boolean-cast": "warn", 33 | "no-func-assign": "warn", 34 | "no-global-assign": "warn", 35 | "no-import-assign": "warn", 36 | "no-invalid-regexp": "warn", 37 | "no-irregular-whitespace": "warn", 38 | "no-loss-of-precision": "warn", 39 | "no-new-native-nonconstructor": "warn", 40 | "no-nonoctal-decimal-escape": "warn", 41 | "no-obj-calls": "warn", 42 | "no-self-assign": "warn", 43 | "no-setter-return": "warn", 44 | "no-shadow-restricted-names": "warn", 45 | "no-sparse-arrays": "warn", 46 | "no-this-before-super": "warn", 47 | "no-unsafe-finally": "warn", 48 | "no-unsafe-negation": "warn", 49 | "no-unsafe-optional-chaining": "warn", 50 | "no-unused-labels": "warn", 51 | "no-unused-private-class-members": "warn", 52 | "no-unused-vars": "warn", 53 | "no-useless-catch": "warn", 54 | "no-useless-escape": "warn", 55 | "no-useless-rename": "warn", 56 | "no-with": "warn", 57 | "require-yield": "warn", 58 | "use-isnan": "warn", 59 | "valid-typeof": "warn", 60 | "oxc/bad-array-method-on-arguments": "warn", 61 | "oxc/bad-char-at-comparison": "warn", 62 | "oxc/bad-comparison-sequence": "warn", 63 | "oxc/bad-min-max-func": "warn", 64 | "oxc/bad-object-literal-comparison": "warn", 65 | "oxc/bad-replace-all-arg": "warn", 66 | "oxc/const-comparisons": "warn", 67 | "oxc/double-comparisons": "warn", 68 | "oxc/erasing-op": "warn", 69 | "oxc/missing-throw": "warn", 70 | "oxc/number-arg-out-of-range": "warn", 71 | "oxc/only-used-in-recursion": "warn", 72 | "oxc/uninvoked-array-callback": "warn", 73 | "react/jsx-key": "warn", 74 | "react/jsx-no-duplicate-props": "warn", 75 | "react/jsx-no-target-blank": "warn", 76 | "react/jsx-no-undef": "warn", 77 | "react/jsx-props-no-spread-multi": "warn", 78 | "react/no-children-prop": "warn", 79 | "react/no-danger-with-children": "warn", 80 | "react/no-direct-mutation-state": "warn", 81 | "react/no-find-dom-node": "warn", 82 | "react/no-is-mounted": "warn", 83 | "react/no-render-return-value": "warn", 84 | "react/no-string-refs": "warn", 85 | "react/void-dom-elements-no-children": "warn", 86 | "typescript/no-duplicate-enum-values": "warn", 87 | "typescript/no-extra-non-null-assertion": "warn", 88 | "typescript/no-misused-new": "warn", 89 | "typescript/no-non-null-asserted-optional-chain": "warn", 90 | "typescript/no-this-alias": "warn", 91 | "typescript/no-unnecessary-parameter-property-assignment": "warn", 92 | "typescript/no-unsafe-declaration-merging": "warn", 93 | "typescript/no-useless-empty-export": "warn", 94 | "typescript/no-wrapper-object-types": "warn", 95 | "typescript/prefer-as-const": "warn", 96 | "typescript/triple-slash-reference": "warn", 97 | "unicorn/no-await-in-promise-methods": "warn", 98 | "unicorn/no-empty-file": "warn", 99 | "unicorn/no-invalid-fetch-options": "warn", 100 | "unicorn/no-invalid-remove-event-listener": "warn", 101 | "unicorn/no-new-array": "warn", 102 | "unicorn/no-single-promise-in-promise-methods": "warn", 103 | "unicorn/no-thenable": "warn", 104 | "unicorn/no-unnecessary-await": "warn", 105 | "unicorn/no-useless-fallback-in-spread": "warn", 106 | "unicorn/no-useless-length-check": "warn", 107 | "unicorn/no-useless-spread": "warn", 108 | "unicorn/prefer-set-size": "warn", 109 | "unicorn/prefer-string-starts-ends-with": "warn" 110 | }, 111 | "settings": { 112 | "jsx-a11y": { 113 | "polymorphicPropName": null, 114 | "components": {} 115 | }, 116 | "next": { 117 | "rootDir": [] 118 | }, 119 | "react": { 120 | "formComponents": [], 121 | "linkComponents": [] 122 | }, 123 | "jsdoc": { 124 | "ignorePrivate": false, 125 | "ignoreInternal": false, 126 | "ignoreReplacesDocs": true, 127 | "overrideReplacesDocs": true, 128 | "augmentsExtendsReplacesDocs": false, 129 | "implementsReplacesDocs": false, 130 | "exemptDestructuredRootsFromChecks": false, 131 | "tagNamePreference": {} 132 | } 133 | }, 134 | "env": { 135 | "builtin": true 136 | }, 137 | "globals": {}, 138 | "ignorePatterns": [] 139 | } -------------------------------------------------------------------------------- /src/test/testData/oxlint/highlighting/nested-config/.oxlintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "./node_modules/oxlint/configuration_schema.json", 3 | "plugins": [ 4 | "react", 5 | "unicorn", 6 | "typescript", 7 | "oxc" 8 | ], 9 | "categories": {}, 10 | "rules": { 11 | "curly": "deny", 12 | "for-direction": "warn", 13 | "no-async-promise-executor": "warn", 14 | "no-caller": "warn", 15 | "no-class-assign": "warn", 16 | "no-compare-neg-zero": "warn", 17 | "no-cond-assign": "warn", 18 | "no-const-assign": "warn", 19 | "no-constant-binary-expression": "warn", 20 | "no-constant-condition": "warn", 21 | "no-control-regex": "warn", 22 | "no-debugger": "warn", 23 | "no-delete-var": "warn", 24 | "no-dupe-class-members": "warn", 25 | "no-dupe-else-if": "warn", 26 | "no-dupe-keys": "warn", 27 | "no-duplicate-case": "warn", 28 | "no-empty-character-class": "warn", 29 | "no-empty-pattern": "warn", 30 | "no-empty-static-block": "warn", 31 | "no-eval": "warn", 32 | "no-ex-assign": "warn", 33 | "no-extra-boolean-cast": "warn", 34 | "no-func-assign": "warn", 35 | "no-global-assign": "warn", 36 | "no-import-assign": "warn", 37 | "no-invalid-regexp": "warn", 38 | "no-irregular-whitespace": "warn", 39 | "no-loss-of-precision": "warn", 40 | "no-new-native-nonconstructor": "warn", 41 | "no-nonoctal-decimal-escape": "warn", 42 | "no-obj-calls": "warn", 43 | "no-self-assign": "warn", 44 | "no-setter-return": "warn", 45 | "no-shadow-restricted-names": "warn", 46 | "no-sparse-arrays": "warn", 47 | "no-this-before-super": "warn", 48 | "no-unsafe-finally": "warn", 49 | "no-unsafe-negation": "warn", 50 | "no-unsafe-optional-chaining": "warn", 51 | "no-unused-labels": "warn", 52 | "no-unused-private-class-members": "warn", 53 | "no-unused-vars": "warn", 54 | "no-useless-catch": "warn", 55 | "no-useless-escape": "warn", 56 | "no-useless-rename": "warn", 57 | "no-with": "warn", 58 | "require-yield": "warn", 59 | "use-isnan": "warn", 60 | "valid-typeof": "warn", 61 | "oxc/bad-array-method-on-arguments": "warn", 62 | "oxc/bad-char-at-comparison": "warn", 63 | "oxc/bad-comparison-sequence": "warn", 64 | "oxc/bad-min-max-func": "warn", 65 | "oxc/bad-object-literal-comparison": "warn", 66 | "oxc/bad-replace-all-arg": "warn", 67 | "oxc/const-comparisons": "warn", 68 | "oxc/double-comparisons": "warn", 69 | "oxc/erasing-op": "warn", 70 | "oxc/missing-throw": "warn", 71 | "oxc/number-arg-out-of-range": "warn", 72 | "oxc/only-used-in-recursion": "warn", 73 | "oxc/uninvoked-array-callback": "warn", 74 | "react/jsx-key": "warn", 75 | "react/jsx-no-duplicate-props": "warn", 76 | "react/jsx-no-target-blank": "warn", 77 | "react/jsx-no-undef": "warn", 78 | "react/jsx-props-no-spread-multi": "warn", 79 | "react/no-children-prop": "warn", 80 | "react/no-danger-with-children": "warn", 81 | "react/no-direct-mutation-state": "warn", 82 | "react/no-find-dom-node": "warn", 83 | "react/no-is-mounted": "warn", 84 | "react/no-render-return-value": "warn", 85 | "react/no-string-refs": "warn", 86 | "react/void-dom-elements-no-children": "warn", 87 | "typescript/no-duplicate-enum-values": "warn", 88 | "typescript/no-extra-non-null-assertion": "warn", 89 | "typescript/no-misused-new": "warn", 90 | "typescript/no-non-null-asserted-optional-chain": "warn", 91 | "typescript/no-this-alias": "warn", 92 | "typescript/no-unnecessary-parameter-property-assignment": "warn", 93 | "typescript/no-unsafe-declaration-merging": "warn", 94 | "typescript/no-useless-empty-export": "warn", 95 | "typescript/no-wrapper-object-types": "warn", 96 | "typescript/prefer-as-const": "warn", 97 | "typescript/triple-slash-reference": "warn", 98 | "unicorn/no-await-in-promise-methods": "warn", 99 | "unicorn/no-empty-file": "warn", 100 | "unicorn/no-invalid-fetch-options": "warn", 101 | "unicorn/no-invalid-remove-event-listener": "warn", 102 | "unicorn/no-new-array": "warn", 103 | "unicorn/no-single-promise-in-promise-methods": "warn", 104 | "unicorn/no-thenable": "warn", 105 | "unicorn/no-unnecessary-await": "warn", 106 | "unicorn/no-useless-fallback-in-spread": "warn", 107 | "unicorn/no-useless-length-check": "warn", 108 | "unicorn/no-useless-spread": "warn", 109 | "unicorn/prefer-set-size": "warn", 110 | "unicorn/prefer-string-starts-ends-with": "warn" 111 | }, 112 | "settings": { 113 | "jsx-a11y": { 114 | "polymorphicPropName": null, 115 | "components": {} 116 | }, 117 | "next": { 118 | "rootDir": [] 119 | }, 120 | "react": { 121 | "formComponents": [], 122 | "linkComponents": [] 123 | }, 124 | "jsdoc": { 125 | "ignorePrivate": false, 126 | "ignoreInternal": false, 127 | "ignoreReplacesDocs": true, 128 | "overrideReplacesDocs": true, 129 | "augmentsExtendsReplacesDocs": false, 130 | "implementsReplacesDocs": false, 131 | "exemptDestructuredRootsFromChecks": false, 132 | "tagNamePreference": {} 133 | } 134 | }, 135 | "env": { 136 | "builtin": true 137 | }, 138 | "globals": {}, 139 | "ignorePatterns": [] 140 | } 141 | -------------------------------------------------------------------------------- /sandbox/oxlint/custom-config/package-lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "oxc-lint", 3 | "version": "1.0.0", 4 | "lockfileVersion": 3, 5 | "requires": true, 6 | "packages": { 7 | "": { 8 | "name": "oxc-lint", 9 | "version": "1.0.0", 10 | "devDependencies": { 11 | "oxlint": "1.34.0" 12 | } 13 | }, 14 | "node_modules/@oxlint/darwin-arm64": { 15 | "version": "1.34.0", 16 | "resolved": "https://registry.npmjs.org/@oxlint/darwin-arm64/-/darwin-arm64-1.34.0.tgz", 17 | "integrity": "sha512-euz3Dtp5/UE9axFkQnllOWp3gOwoqaxfZPUUwiW8HBelqhI9PRMVIfQ/akmwl+G5XixQZIgXkXQ5SJxnb1+Qww==", 18 | "cpu": [ 19 | "arm64" 20 | ], 21 | "dev": true, 22 | "license": "MIT", 23 | "optional": true, 24 | "os": [ 25 | "darwin" 26 | ] 27 | }, 28 | "node_modules/@oxlint/darwin-x64": { 29 | "version": "1.34.0", 30 | "resolved": "https://registry.npmjs.org/@oxlint/darwin-x64/-/darwin-x64-1.34.0.tgz", 31 | "integrity": "sha512-XpmNviE5KOnHkhmQPwJJIBs+mJkr0qItTZBN4dz+O3p9gWN+gCqi3CBP71RiVahZw4qAEQSgY4wro+z0kx+erg==", 32 | "cpu": [ 33 | "x64" 34 | ], 35 | "dev": true, 36 | "license": "MIT", 37 | "optional": true, 38 | "os": [ 39 | "darwin" 40 | ] 41 | }, 42 | "node_modules/@oxlint/linux-arm64-gnu": { 43 | "version": "1.34.0", 44 | "resolved": "https://registry.npmjs.org/@oxlint/linux-arm64-gnu/-/linux-arm64-gnu-1.34.0.tgz", 45 | "integrity": "sha512-aCPdoEUGsJGF9y88vDYoaugG4IEGwSBa+usyuAvEVl3vTfuTmE0RDQEC1Z+WnJ3J/cIEpbgKOzS12VwbzFicjg==", 46 | "cpu": [ 47 | "arm64" 48 | ], 49 | "dev": true, 50 | "license": "MIT", 51 | "optional": true, 52 | "os": [ 53 | "linux" 54 | ] 55 | }, 56 | "node_modules/@oxlint/linux-arm64-musl": { 57 | "version": "1.34.0", 58 | "resolved": "https://registry.npmjs.org/@oxlint/linux-arm64-musl/-/linux-arm64-musl-1.34.0.tgz", 59 | "integrity": "sha512-cMo72LQBFmdnVLRKLAHD94ZUBq5Z+aA9Y+RKzkjhCmJuef5ZAfKC24TJD/6c5LxGYzkwwmyySoQAHq5B69i3PQ==", 60 | "cpu": [ 61 | "arm64" 62 | ], 63 | "dev": true, 64 | "license": "MIT", 65 | "optional": true, 66 | "os": [ 67 | "linux" 68 | ] 69 | }, 70 | "node_modules/@oxlint/linux-x64-gnu": { 71 | "version": "1.34.0", 72 | "resolved": "https://registry.npmjs.org/@oxlint/linux-x64-gnu/-/linux-x64-gnu-1.34.0.tgz", 73 | "integrity": "sha512-+9xFhhkqgNIysEh+uHvcba8v4UtL1YzxuyDS2wTLdWrkGvIllCx5WjJItt3K/AhwatciksgNEXSo2Hh4fcQRog==", 74 | "cpu": [ 75 | "x64" 76 | ], 77 | "dev": true, 78 | "license": "MIT", 79 | "optional": true, 80 | "os": [ 81 | "linux" 82 | ] 83 | }, 84 | "node_modules/@oxlint/linux-x64-musl": { 85 | "version": "1.34.0", 86 | "resolved": "https://registry.npmjs.org/@oxlint/linux-x64-musl/-/linux-x64-musl-1.34.0.tgz", 87 | "integrity": "sha512-qa7TL2DfEDdMeSP5UiU5JMs6D2PW7ZJAQ0WZYTgqDV8BlZ6nMkIYVBVIk3QPxIfkyxvfJVbG1RB3PkSWDcfwpA==", 88 | "cpu": [ 89 | "x64" 90 | ], 91 | "dev": true, 92 | "license": "MIT", 93 | "optional": true, 94 | "os": [ 95 | "linux" 96 | ] 97 | }, 98 | "node_modules/@oxlint/win32-arm64": { 99 | "version": "1.34.0", 100 | "resolved": "https://registry.npmjs.org/@oxlint/win32-arm64/-/win32-arm64-1.34.0.tgz", 101 | "integrity": "sha512-mSJumUveg1S3DiOgvsrVNAGuvenBbbC/zsfT1qhltT+GLhJ7RPBK2I/jz0fTdE+I7M9/as8yc0XJ/eY23y2amA==", 102 | "cpu": [ 103 | "arm64" 104 | ], 105 | "dev": true, 106 | "license": "MIT", 107 | "optional": true, 108 | "os": [ 109 | "win32" 110 | ] 111 | }, 112 | "node_modules/@oxlint/win32-x64": { 113 | "version": "1.34.0", 114 | "resolved": "https://registry.npmjs.org/@oxlint/win32-x64/-/win32-x64-1.34.0.tgz", 115 | "integrity": "sha512-izsDDt5WY4FSISCkPRLUYQD1aRaaEJkPLtEZe3DlioSUdUVAdvVbE+BGllFqR16DWfTTwO/6K4jDeooxQzTMjw==", 116 | "cpu": [ 117 | "x64" 118 | ], 119 | "dev": true, 120 | "license": "MIT", 121 | "optional": true, 122 | "os": [ 123 | "win32" 124 | ] 125 | }, 126 | "node_modules/oxlint": { 127 | "version": "1.34.0", 128 | "resolved": "https://registry.npmjs.org/oxlint/-/oxlint-1.34.0.tgz", 129 | "integrity": "sha512-Ni0N8wAiKlgaYkI/Yz4VrutfVIZgd2shDtS+loQxyBTwO8YUAnk3+g7OQ1cyI/aatHiFwvFNfV/uvZyHUtyPpA==", 130 | "dev": true, 131 | "license": "MIT", 132 | "bin": { 133 | "oxc_language_server": "bin/oxc_language_server", 134 | "oxlint": "bin/oxlint" 135 | }, 136 | "engines": { 137 | "node": "^20.19.0 || >=22.12.0" 138 | }, 139 | "funding": { 140 | "url": "https://github.com/sponsors/Boshen" 141 | }, 142 | "optionalDependencies": { 143 | "@oxlint/darwin-arm64": "1.34.0", 144 | "@oxlint/darwin-x64": "1.34.0", 145 | "@oxlint/linux-arm64-gnu": "1.34.0", 146 | "@oxlint/linux-arm64-musl": "1.34.0", 147 | "@oxlint/linux-x64-gnu": "1.34.0", 148 | "@oxlint/linux-x64-musl": "1.34.0", 149 | "@oxlint/win32-arm64": "1.34.0", 150 | "@oxlint/win32-x64": "1.34.0" 151 | }, 152 | "peerDependencies": { 153 | "oxlint-tsgolint": ">=0.9.2" 154 | }, 155 | "peerDependenciesMeta": { 156 | "oxlint-tsgolint": { 157 | "optional": true 158 | } 159 | } 160 | } 161 | } 162 | } 163 | -------------------------------------------------------------------------------- /sandbox/oxlint/nested-configs/package-lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "oxc-lint", 3 | "version": "1.0.0", 4 | "lockfileVersion": 3, 5 | "requires": true, 6 | "packages": { 7 | "": { 8 | "name": "oxc-lint", 9 | "version": "1.0.0", 10 | "devDependencies": { 11 | "oxlint": "1.34.0" 12 | } 13 | }, 14 | "node_modules/@oxlint/darwin-arm64": { 15 | "version": "1.34.0", 16 | "resolved": "https://registry.npmjs.org/@oxlint/darwin-arm64/-/darwin-arm64-1.34.0.tgz", 17 | "integrity": "sha512-euz3Dtp5/UE9axFkQnllOWp3gOwoqaxfZPUUwiW8HBelqhI9PRMVIfQ/akmwl+G5XixQZIgXkXQ5SJxnb1+Qww==", 18 | "cpu": [ 19 | "arm64" 20 | ], 21 | "dev": true, 22 | "license": "MIT", 23 | "optional": true, 24 | "os": [ 25 | "darwin" 26 | ] 27 | }, 28 | "node_modules/@oxlint/darwin-x64": { 29 | "version": "1.34.0", 30 | "resolved": "https://registry.npmjs.org/@oxlint/darwin-x64/-/darwin-x64-1.34.0.tgz", 31 | "integrity": "sha512-XpmNviE5KOnHkhmQPwJJIBs+mJkr0qItTZBN4dz+O3p9gWN+gCqi3CBP71RiVahZw4qAEQSgY4wro+z0kx+erg==", 32 | "cpu": [ 33 | "x64" 34 | ], 35 | "dev": true, 36 | "license": "MIT", 37 | "optional": true, 38 | "os": [ 39 | "darwin" 40 | ] 41 | }, 42 | "node_modules/@oxlint/linux-arm64-gnu": { 43 | "version": "1.34.0", 44 | "resolved": "https://registry.npmjs.org/@oxlint/linux-arm64-gnu/-/linux-arm64-gnu-1.34.0.tgz", 45 | "integrity": "sha512-aCPdoEUGsJGF9y88vDYoaugG4IEGwSBa+usyuAvEVl3vTfuTmE0RDQEC1Z+WnJ3J/cIEpbgKOzS12VwbzFicjg==", 46 | "cpu": [ 47 | "arm64" 48 | ], 49 | "dev": true, 50 | "license": "MIT", 51 | "optional": true, 52 | "os": [ 53 | "linux" 54 | ] 55 | }, 56 | "node_modules/@oxlint/linux-arm64-musl": { 57 | "version": "1.34.0", 58 | "resolved": "https://registry.npmjs.org/@oxlint/linux-arm64-musl/-/linux-arm64-musl-1.34.0.tgz", 59 | "integrity": "sha512-cMo72LQBFmdnVLRKLAHD94ZUBq5Z+aA9Y+RKzkjhCmJuef5ZAfKC24TJD/6c5LxGYzkwwmyySoQAHq5B69i3PQ==", 60 | "cpu": [ 61 | "arm64" 62 | ], 63 | "dev": true, 64 | "license": "MIT", 65 | "optional": true, 66 | "os": [ 67 | "linux" 68 | ] 69 | }, 70 | "node_modules/@oxlint/linux-x64-gnu": { 71 | "version": "1.34.0", 72 | "resolved": "https://registry.npmjs.org/@oxlint/linux-x64-gnu/-/linux-x64-gnu-1.34.0.tgz", 73 | "integrity": "sha512-+9xFhhkqgNIysEh+uHvcba8v4UtL1YzxuyDS2wTLdWrkGvIllCx5WjJItt3K/AhwatciksgNEXSo2Hh4fcQRog==", 74 | "cpu": [ 75 | "x64" 76 | ], 77 | "dev": true, 78 | "license": "MIT", 79 | "optional": true, 80 | "os": [ 81 | "linux" 82 | ] 83 | }, 84 | "node_modules/@oxlint/linux-x64-musl": { 85 | "version": "1.34.0", 86 | "resolved": "https://registry.npmjs.org/@oxlint/linux-x64-musl/-/linux-x64-musl-1.34.0.tgz", 87 | "integrity": "sha512-qa7TL2DfEDdMeSP5UiU5JMs6D2PW7ZJAQ0WZYTgqDV8BlZ6nMkIYVBVIk3QPxIfkyxvfJVbG1RB3PkSWDcfwpA==", 88 | "cpu": [ 89 | "x64" 90 | ], 91 | "dev": true, 92 | "license": "MIT", 93 | "optional": true, 94 | "os": [ 95 | "linux" 96 | ] 97 | }, 98 | "node_modules/@oxlint/win32-arm64": { 99 | "version": "1.34.0", 100 | "resolved": "https://registry.npmjs.org/@oxlint/win32-arm64/-/win32-arm64-1.34.0.tgz", 101 | "integrity": "sha512-mSJumUveg1S3DiOgvsrVNAGuvenBbbC/zsfT1qhltT+GLhJ7RPBK2I/jz0fTdE+I7M9/as8yc0XJ/eY23y2amA==", 102 | "cpu": [ 103 | "arm64" 104 | ], 105 | "dev": true, 106 | "license": "MIT", 107 | "optional": true, 108 | "os": [ 109 | "win32" 110 | ] 111 | }, 112 | "node_modules/@oxlint/win32-x64": { 113 | "version": "1.34.0", 114 | "resolved": "https://registry.npmjs.org/@oxlint/win32-x64/-/win32-x64-1.34.0.tgz", 115 | "integrity": "sha512-izsDDt5WY4FSISCkPRLUYQD1aRaaEJkPLtEZe3DlioSUdUVAdvVbE+BGllFqR16DWfTTwO/6K4jDeooxQzTMjw==", 116 | "cpu": [ 117 | "x64" 118 | ], 119 | "dev": true, 120 | "license": "MIT", 121 | "optional": true, 122 | "os": [ 123 | "win32" 124 | ] 125 | }, 126 | "node_modules/oxlint": { 127 | "version": "1.34.0", 128 | "resolved": "https://registry.npmjs.org/oxlint/-/oxlint-1.34.0.tgz", 129 | "integrity": "sha512-Ni0N8wAiKlgaYkI/Yz4VrutfVIZgd2shDtS+loQxyBTwO8YUAnk3+g7OQ1cyI/aatHiFwvFNfV/uvZyHUtyPpA==", 130 | "dev": true, 131 | "license": "MIT", 132 | "bin": { 133 | "oxc_language_server": "bin/oxc_language_server", 134 | "oxlint": "bin/oxlint" 135 | }, 136 | "engines": { 137 | "node": "^20.19.0 || >=22.12.0" 138 | }, 139 | "funding": { 140 | "url": "https://github.com/sponsors/Boshen" 141 | }, 142 | "optionalDependencies": { 143 | "@oxlint/darwin-arm64": "1.34.0", 144 | "@oxlint/darwin-x64": "1.34.0", 145 | "@oxlint/linux-arm64-gnu": "1.34.0", 146 | "@oxlint/linux-arm64-musl": "1.34.0", 147 | "@oxlint/linux-x64-gnu": "1.34.0", 148 | "@oxlint/linux-x64-musl": "1.34.0", 149 | "@oxlint/win32-arm64": "1.34.0", 150 | "@oxlint/win32-x64": "1.34.0" 151 | }, 152 | "peerDependencies": { 153 | "oxlint-tsgolint": ">=0.9.2" 154 | }, 155 | "peerDependenciesMeta": { 156 | "oxlint-tsgolint": { 157 | "optional": true 158 | } 159 | } 160 | } 161 | } 162 | } 163 | -------------------------------------------------------------------------------- /src/test/testData/oxlint/highlighting/no-config/package-lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "oxc-lint", 3 | "version": "1.0.0", 4 | "lockfileVersion": 3, 5 | "requires": true, 6 | "packages": { 7 | "": { 8 | "name": "oxc-lint", 9 | "version": "1.0.0", 10 | "devDependencies": { 11 | "oxlint": "1.34.0" 12 | } 13 | }, 14 | "node_modules/@oxlint/darwin-arm64": { 15 | "version": "1.34.0", 16 | "resolved": "https://registry.npmjs.org/@oxlint/darwin-arm64/-/darwin-arm64-1.34.0.tgz", 17 | "integrity": "sha512-euz3Dtp5/UE9axFkQnllOWp3gOwoqaxfZPUUwiW8HBelqhI9PRMVIfQ/akmwl+G5XixQZIgXkXQ5SJxnb1+Qww==", 18 | "cpu": [ 19 | "arm64" 20 | ], 21 | "dev": true, 22 | "license": "MIT", 23 | "optional": true, 24 | "os": [ 25 | "darwin" 26 | ] 27 | }, 28 | "node_modules/@oxlint/darwin-x64": { 29 | "version": "1.34.0", 30 | "resolved": "https://registry.npmjs.org/@oxlint/darwin-x64/-/darwin-x64-1.34.0.tgz", 31 | "integrity": "sha512-XpmNviE5KOnHkhmQPwJJIBs+mJkr0qItTZBN4dz+O3p9gWN+gCqi3CBP71RiVahZw4qAEQSgY4wro+z0kx+erg==", 32 | "cpu": [ 33 | "x64" 34 | ], 35 | "dev": true, 36 | "license": "MIT", 37 | "optional": true, 38 | "os": [ 39 | "darwin" 40 | ] 41 | }, 42 | "node_modules/@oxlint/linux-arm64-gnu": { 43 | "version": "1.34.0", 44 | "resolved": "https://registry.npmjs.org/@oxlint/linux-arm64-gnu/-/linux-arm64-gnu-1.34.0.tgz", 45 | "integrity": "sha512-aCPdoEUGsJGF9y88vDYoaugG4IEGwSBa+usyuAvEVl3vTfuTmE0RDQEC1Z+WnJ3J/cIEpbgKOzS12VwbzFicjg==", 46 | "cpu": [ 47 | "arm64" 48 | ], 49 | "dev": true, 50 | "license": "MIT", 51 | "optional": true, 52 | "os": [ 53 | "linux" 54 | ] 55 | }, 56 | "node_modules/@oxlint/linux-arm64-musl": { 57 | "version": "1.34.0", 58 | "resolved": "https://registry.npmjs.org/@oxlint/linux-arm64-musl/-/linux-arm64-musl-1.34.0.tgz", 59 | "integrity": "sha512-cMo72LQBFmdnVLRKLAHD94ZUBq5Z+aA9Y+RKzkjhCmJuef5ZAfKC24TJD/6c5LxGYzkwwmyySoQAHq5B69i3PQ==", 60 | "cpu": [ 61 | "arm64" 62 | ], 63 | "dev": true, 64 | "license": "MIT", 65 | "optional": true, 66 | "os": [ 67 | "linux" 68 | ] 69 | }, 70 | "node_modules/@oxlint/linux-x64-gnu": { 71 | "version": "1.34.0", 72 | "resolved": "https://registry.npmjs.org/@oxlint/linux-x64-gnu/-/linux-x64-gnu-1.34.0.tgz", 73 | "integrity": "sha512-+9xFhhkqgNIysEh+uHvcba8v4UtL1YzxuyDS2wTLdWrkGvIllCx5WjJItt3K/AhwatciksgNEXSo2Hh4fcQRog==", 74 | "cpu": [ 75 | "x64" 76 | ], 77 | "dev": true, 78 | "license": "MIT", 79 | "optional": true, 80 | "os": [ 81 | "linux" 82 | ] 83 | }, 84 | "node_modules/@oxlint/linux-x64-musl": { 85 | "version": "1.34.0", 86 | "resolved": "https://registry.npmjs.org/@oxlint/linux-x64-musl/-/linux-x64-musl-1.34.0.tgz", 87 | "integrity": "sha512-qa7TL2DfEDdMeSP5UiU5JMs6D2PW7ZJAQ0WZYTgqDV8BlZ6nMkIYVBVIk3QPxIfkyxvfJVbG1RB3PkSWDcfwpA==", 88 | "cpu": [ 89 | "x64" 90 | ], 91 | "dev": true, 92 | "license": "MIT", 93 | "optional": true, 94 | "os": [ 95 | "linux" 96 | ] 97 | }, 98 | "node_modules/@oxlint/win32-arm64": { 99 | "version": "1.34.0", 100 | "resolved": "https://registry.npmjs.org/@oxlint/win32-arm64/-/win32-arm64-1.34.0.tgz", 101 | "integrity": "sha512-mSJumUveg1S3DiOgvsrVNAGuvenBbbC/zsfT1qhltT+GLhJ7RPBK2I/jz0fTdE+I7M9/as8yc0XJ/eY23y2amA==", 102 | "cpu": [ 103 | "arm64" 104 | ], 105 | "dev": true, 106 | "license": "MIT", 107 | "optional": true, 108 | "os": [ 109 | "win32" 110 | ] 111 | }, 112 | "node_modules/@oxlint/win32-x64": { 113 | "version": "1.34.0", 114 | "resolved": "https://registry.npmjs.org/@oxlint/win32-x64/-/win32-x64-1.34.0.tgz", 115 | "integrity": "sha512-izsDDt5WY4FSISCkPRLUYQD1aRaaEJkPLtEZe3DlioSUdUVAdvVbE+BGllFqR16DWfTTwO/6K4jDeooxQzTMjw==", 116 | "cpu": [ 117 | "x64" 118 | ], 119 | "dev": true, 120 | "license": "MIT", 121 | "optional": true, 122 | "os": [ 123 | "win32" 124 | ] 125 | }, 126 | "node_modules/oxlint": { 127 | "version": "1.34.0", 128 | "resolved": "https://registry.npmjs.org/oxlint/-/oxlint-1.34.0.tgz", 129 | "integrity": "sha512-Ni0N8wAiKlgaYkI/Yz4VrutfVIZgd2shDtS+loQxyBTwO8YUAnk3+g7OQ1cyI/aatHiFwvFNfV/uvZyHUtyPpA==", 130 | "dev": true, 131 | "license": "MIT", 132 | "bin": { 133 | "oxc_language_server": "bin/oxc_language_server", 134 | "oxlint": "bin/oxlint" 135 | }, 136 | "engines": { 137 | "node": "^20.19.0 || >=22.12.0" 138 | }, 139 | "funding": { 140 | "url": "https://github.com/sponsors/Boshen" 141 | }, 142 | "optionalDependencies": { 143 | "@oxlint/darwin-arm64": "1.34.0", 144 | "@oxlint/darwin-x64": "1.34.0", 145 | "@oxlint/linux-arm64-gnu": "1.34.0", 146 | "@oxlint/linux-arm64-musl": "1.34.0", 147 | "@oxlint/linux-x64-gnu": "1.34.0", 148 | "@oxlint/linux-x64-musl": "1.34.0", 149 | "@oxlint/win32-arm64": "1.34.0", 150 | "@oxlint/win32-x64": "1.34.0" 151 | }, 152 | "peerDependencies": { 153 | "oxlint-tsgolint": ">=0.9.2" 154 | }, 155 | "peerDependenciesMeta": { 156 | "oxlint-tsgolint": { 157 | "optional": true 158 | } 159 | } 160 | } 161 | } 162 | } 163 | -------------------------------------------------------------------------------- /src/test/testData/oxlint/highlighting/custom-config/package-lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "oxc-lint", 3 | "version": "1.0.0", 4 | "lockfileVersion": 3, 5 | "requires": true, 6 | "packages": { 7 | "": { 8 | "name": "oxc-lint", 9 | "version": "1.0.0", 10 | "devDependencies": { 11 | "oxlint": "1.34.0" 12 | } 13 | }, 14 | "node_modules/@oxlint/darwin-arm64": { 15 | "version": "1.34.0", 16 | "resolved": "https://registry.npmjs.org/@oxlint/darwin-arm64/-/darwin-arm64-1.34.0.tgz", 17 | "integrity": "sha512-euz3Dtp5/UE9axFkQnllOWp3gOwoqaxfZPUUwiW8HBelqhI9PRMVIfQ/akmwl+G5XixQZIgXkXQ5SJxnb1+Qww==", 18 | "cpu": [ 19 | "arm64" 20 | ], 21 | "dev": true, 22 | "license": "MIT", 23 | "optional": true, 24 | "os": [ 25 | "darwin" 26 | ] 27 | }, 28 | "node_modules/@oxlint/darwin-x64": { 29 | "version": "1.34.0", 30 | "resolved": "https://registry.npmjs.org/@oxlint/darwin-x64/-/darwin-x64-1.34.0.tgz", 31 | "integrity": "sha512-XpmNviE5KOnHkhmQPwJJIBs+mJkr0qItTZBN4dz+O3p9gWN+gCqi3CBP71RiVahZw4qAEQSgY4wro+z0kx+erg==", 32 | "cpu": [ 33 | "x64" 34 | ], 35 | "dev": true, 36 | "license": "MIT", 37 | "optional": true, 38 | "os": [ 39 | "darwin" 40 | ] 41 | }, 42 | "node_modules/@oxlint/linux-arm64-gnu": { 43 | "version": "1.34.0", 44 | "resolved": "https://registry.npmjs.org/@oxlint/linux-arm64-gnu/-/linux-arm64-gnu-1.34.0.tgz", 45 | "integrity": "sha512-aCPdoEUGsJGF9y88vDYoaugG4IEGwSBa+usyuAvEVl3vTfuTmE0RDQEC1Z+WnJ3J/cIEpbgKOzS12VwbzFicjg==", 46 | "cpu": [ 47 | "arm64" 48 | ], 49 | "dev": true, 50 | "license": "MIT", 51 | "optional": true, 52 | "os": [ 53 | "linux" 54 | ] 55 | }, 56 | "node_modules/@oxlint/linux-arm64-musl": { 57 | "version": "1.34.0", 58 | "resolved": "https://registry.npmjs.org/@oxlint/linux-arm64-musl/-/linux-arm64-musl-1.34.0.tgz", 59 | "integrity": "sha512-cMo72LQBFmdnVLRKLAHD94ZUBq5Z+aA9Y+RKzkjhCmJuef5ZAfKC24TJD/6c5LxGYzkwwmyySoQAHq5B69i3PQ==", 60 | "cpu": [ 61 | "arm64" 62 | ], 63 | "dev": true, 64 | "license": "MIT", 65 | "optional": true, 66 | "os": [ 67 | "linux" 68 | ] 69 | }, 70 | "node_modules/@oxlint/linux-x64-gnu": { 71 | "version": "1.34.0", 72 | "resolved": "https://registry.npmjs.org/@oxlint/linux-x64-gnu/-/linux-x64-gnu-1.34.0.tgz", 73 | "integrity": "sha512-+9xFhhkqgNIysEh+uHvcba8v4UtL1YzxuyDS2wTLdWrkGvIllCx5WjJItt3K/AhwatciksgNEXSo2Hh4fcQRog==", 74 | "cpu": [ 75 | "x64" 76 | ], 77 | "dev": true, 78 | "license": "MIT", 79 | "optional": true, 80 | "os": [ 81 | "linux" 82 | ] 83 | }, 84 | "node_modules/@oxlint/linux-x64-musl": { 85 | "version": "1.34.0", 86 | "resolved": "https://registry.npmjs.org/@oxlint/linux-x64-musl/-/linux-x64-musl-1.34.0.tgz", 87 | "integrity": "sha512-qa7TL2DfEDdMeSP5UiU5JMs6D2PW7ZJAQ0WZYTgqDV8BlZ6nMkIYVBVIk3QPxIfkyxvfJVbG1RB3PkSWDcfwpA==", 88 | "cpu": [ 89 | "x64" 90 | ], 91 | "dev": true, 92 | "license": "MIT", 93 | "optional": true, 94 | "os": [ 95 | "linux" 96 | ] 97 | }, 98 | "node_modules/@oxlint/win32-arm64": { 99 | "version": "1.34.0", 100 | "resolved": "https://registry.npmjs.org/@oxlint/win32-arm64/-/win32-arm64-1.34.0.tgz", 101 | "integrity": "sha512-mSJumUveg1S3DiOgvsrVNAGuvenBbbC/zsfT1qhltT+GLhJ7RPBK2I/jz0fTdE+I7M9/as8yc0XJ/eY23y2amA==", 102 | "cpu": [ 103 | "arm64" 104 | ], 105 | "dev": true, 106 | "license": "MIT", 107 | "optional": true, 108 | "os": [ 109 | "win32" 110 | ] 111 | }, 112 | "node_modules/@oxlint/win32-x64": { 113 | "version": "1.34.0", 114 | "resolved": "https://registry.npmjs.org/@oxlint/win32-x64/-/win32-x64-1.34.0.tgz", 115 | "integrity": "sha512-izsDDt5WY4FSISCkPRLUYQD1aRaaEJkPLtEZe3DlioSUdUVAdvVbE+BGllFqR16DWfTTwO/6K4jDeooxQzTMjw==", 116 | "cpu": [ 117 | "x64" 118 | ], 119 | "dev": true, 120 | "license": "MIT", 121 | "optional": true, 122 | "os": [ 123 | "win32" 124 | ] 125 | }, 126 | "node_modules/oxlint": { 127 | "version": "1.34.0", 128 | "resolved": "https://registry.npmjs.org/oxlint/-/oxlint-1.34.0.tgz", 129 | "integrity": "sha512-Ni0N8wAiKlgaYkI/Yz4VrutfVIZgd2shDtS+loQxyBTwO8YUAnk3+g7OQ1cyI/aatHiFwvFNfV/uvZyHUtyPpA==", 130 | "dev": true, 131 | "license": "MIT", 132 | "bin": { 133 | "oxc_language_server": "bin/oxc_language_server", 134 | "oxlint": "bin/oxlint" 135 | }, 136 | "engines": { 137 | "node": "^20.19.0 || >=22.12.0" 138 | }, 139 | "funding": { 140 | "url": "https://github.com/sponsors/Boshen" 141 | }, 142 | "optionalDependencies": { 143 | "@oxlint/darwin-arm64": "1.34.0", 144 | "@oxlint/darwin-x64": "1.34.0", 145 | "@oxlint/linux-arm64-gnu": "1.34.0", 146 | "@oxlint/linux-arm64-musl": "1.34.0", 147 | "@oxlint/linux-x64-gnu": "1.34.0", 148 | "@oxlint/linux-x64-musl": "1.34.0", 149 | "@oxlint/win32-arm64": "1.34.0", 150 | "@oxlint/win32-x64": "1.34.0" 151 | }, 152 | "peerDependencies": { 153 | "oxlint-tsgolint": ">=0.9.2" 154 | }, 155 | "peerDependenciesMeta": { 156 | "oxlint-tsgolint": { 157 | "optional": true 158 | } 159 | } 160 | } 161 | } 162 | } 163 | -------------------------------------------------------------------------------- /src/test/testData/oxlint/highlighting/nested-config/package-lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "oxc-lint", 3 | "version": "1.0.0", 4 | "lockfileVersion": 3, 5 | "requires": true, 6 | "packages": { 7 | "": { 8 | "name": "oxc-lint", 9 | "version": "1.0.0", 10 | "devDependencies": { 11 | "oxlint": "1.34.0" 12 | } 13 | }, 14 | "node_modules/@oxlint/darwin-arm64": { 15 | "version": "1.34.0", 16 | "resolved": "https://registry.npmjs.org/@oxlint/darwin-arm64/-/darwin-arm64-1.34.0.tgz", 17 | "integrity": "sha512-euz3Dtp5/UE9axFkQnllOWp3gOwoqaxfZPUUwiW8HBelqhI9PRMVIfQ/akmwl+G5XixQZIgXkXQ5SJxnb1+Qww==", 18 | "cpu": [ 19 | "arm64" 20 | ], 21 | "dev": true, 22 | "license": "MIT", 23 | "optional": true, 24 | "os": [ 25 | "darwin" 26 | ] 27 | }, 28 | "node_modules/@oxlint/darwin-x64": { 29 | "version": "1.34.0", 30 | "resolved": "https://registry.npmjs.org/@oxlint/darwin-x64/-/darwin-x64-1.34.0.tgz", 31 | "integrity": "sha512-XpmNviE5KOnHkhmQPwJJIBs+mJkr0qItTZBN4dz+O3p9gWN+gCqi3CBP71RiVahZw4qAEQSgY4wro+z0kx+erg==", 32 | "cpu": [ 33 | "x64" 34 | ], 35 | "dev": true, 36 | "license": "MIT", 37 | "optional": true, 38 | "os": [ 39 | "darwin" 40 | ] 41 | }, 42 | "node_modules/@oxlint/linux-arm64-gnu": { 43 | "version": "1.34.0", 44 | "resolved": "https://registry.npmjs.org/@oxlint/linux-arm64-gnu/-/linux-arm64-gnu-1.34.0.tgz", 45 | "integrity": "sha512-aCPdoEUGsJGF9y88vDYoaugG4IEGwSBa+usyuAvEVl3vTfuTmE0RDQEC1Z+WnJ3J/cIEpbgKOzS12VwbzFicjg==", 46 | "cpu": [ 47 | "arm64" 48 | ], 49 | "dev": true, 50 | "license": "MIT", 51 | "optional": true, 52 | "os": [ 53 | "linux" 54 | ] 55 | }, 56 | "node_modules/@oxlint/linux-arm64-musl": { 57 | "version": "1.34.0", 58 | "resolved": "https://registry.npmjs.org/@oxlint/linux-arm64-musl/-/linux-arm64-musl-1.34.0.tgz", 59 | "integrity": "sha512-cMo72LQBFmdnVLRKLAHD94ZUBq5Z+aA9Y+RKzkjhCmJuef5ZAfKC24TJD/6c5LxGYzkwwmyySoQAHq5B69i3PQ==", 60 | "cpu": [ 61 | "arm64" 62 | ], 63 | "dev": true, 64 | "license": "MIT", 65 | "optional": true, 66 | "os": [ 67 | "linux" 68 | ] 69 | }, 70 | "node_modules/@oxlint/linux-x64-gnu": { 71 | "version": "1.34.0", 72 | "resolved": "https://registry.npmjs.org/@oxlint/linux-x64-gnu/-/linux-x64-gnu-1.34.0.tgz", 73 | "integrity": "sha512-+9xFhhkqgNIysEh+uHvcba8v4UtL1YzxuyDS2wTLdWrkGvIllCx5WjJItt3K/AhwatciksgNEXSo2Hh4fcQRog==", 74 | "cpu": [ 75 | "x64" 76 | ], 77 | "dev": true, 78 | "license": "MIT", 79 | "optional": true, 80 | "os": [ 81 | "linux" 82 | ] 83 | }, 84 | "node_modules/@oxlint/linux-x64-musl": { 85 | "version": "1.34.0", 86 | "resolved": "https://registry.npmjs.org/@oxlint/linux-x64-musl/-/linux-x64-musl-1.34.0.tgz", 87 | "integrity": "sha512-qa7TL2DfEDdMeSP5UiU5JMs6D2PW7ZJAQ0WZYTgqDV8BlZ6nMkIYVBVIk3QPxIfkyxvfJVbG1RB3PkSWDcfwpA==", 88 | "cpu": [ 89 | "x64" 90 | ], 91 | "dev": true, 92 | "license": "MIT", 93 | "optional": true, 94 | "os": [ 95 | "linux" 96 | ] 97 | }, 98 | "node_modules/@oxlint/win32-arm64": { 99 | "version": "1.34.0", 100 | "resolved": "https://registry.npmjs.org/@oxlint/win32-arm64/-/win32-arm64-1.34.0.tgz", 101 | "integrity": "sha512-mSJumUveg1S3DiOgvsrVNAGuvenBbbC/zsfT1qhltT+GLhJ7RPBK2I/jz0fTdE+I7M9/as8yc0XJ/eY23y2amA==", 102 | "cpu": [ 103 | "arm64" 104 | ], 105 | "dev": true, 106 | "license": "MIT", 107 | "optional": true, 108 | "os": [ 109 | "win32" 110 | ] 111 | }, 112 | "node_modules/@oxlint/win32-x64": { 113 | "version": "1.34.0", 114 | "resolved": "https://registry.npmjs.org/@oxlint/win32-x64/-/win32-x64-1.34.0.tgz", 115 | "integrity": "sha512-izsDDt5WY4FSISCkPRLUYQD1aRaaEJkPLtEZe3DlioSUdUVAdvVbE+BGllFqR16DWfTTwO/6K4jDeooxQzTMjw==", 116 | "cpu": [ 117 | "x64" 118 | ], 119 | "dev": true, 120 | "license": "MIT", 121 | "optional": true, 122 | "os": [ 123 | "win32" 124 | ] 125 | }, 126 | "node_modules/oxlint": { 127 | "version": "1.34.0", 128 | "resolved": "https://registry.npmjs.org/oxlint/-/oxlint-1.34.0.tgz", 129 | "integrity": "sha512-Ni0N8wAiKlgaYkI/Yz4VrutfVIZgd2shDtS+loQxyBTwO8YUAnk3+g7OQ1cyI/aatHiFwvFNfV/uvZyHUtyPpA==", 130 | "dev": true, 131 | "license": "MIT", 132 | "bin": { 133 | "oxc_language_server": "bin/oxc_language_server", 134 | "oxlint": "bin/oxlint" 135 | }, 136 | "engines": { 137 | "node": "^20.19.0 || >=22.12.0" 138 | }, 139 | "funding": { 140 | "url": "https://github.com/sponsors/Boshen" 141 | }, 142 | "optionalDependencies": { 143 | "@oxlint/darwin-arm64": "1.34.0", 144 | "@oxlint/darwin-x64": "1.34.0", 145 | "@oxlint/linux-arm64-gnu": "1.34.0", 146 | "@oxlint/linux-arm64-musl": "1.34.0", 147 | "@oxlint/linux-x64-gnu": "1.34.0", 148 | "@oxlint/linux-x64-musl": "1.34.0", 149 | "@oxlint/win32-arm64": "1.34.0", 150 | "@oxlint/win32-x64": "1.34.0" 151 | }, 152 | "peerDependencies": { 153 | "oxlint-tsgolint": ">=0.9.2" 154 | }, 155 | "peerDependenciesMeta": { 156 | "oxlint-tsgolint": { 157 | "optional": true 158 | } 159 | } 160 | } 161 | } 162 | } 163 | -------------------------------------------------------------------------------- /sandbox/oxfmt/nested-configs/package-lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "oxfmt-nested-configs", 3 | "version": "1.0.0", 4 | "lockfileVersion": 3, 5 | "requires": true, 6 | "packages": { 7 | "": { 8 | "name": "oxfmt-nested-configs", 9 | "version": "1.0.0", 10 | "devDependencies": { 11 | "oxfmt": "0.19.0" 12 | } 13 | }, 14 | "node_modules/@oxfmt/darwin-arm64": { 15 | "version": "0.19.0", 16 | "resolved": "https://registry.npmjs.org/@oxfmt/darwin-arm64/-/darwin-arm64-0.19.0.tgz", 17 | "integrity": "sha512-FfNpn3ximwbBZCaS8WL2vAXcFuQFQgvv/brO6D1WdmL4pnFOgfBIpFfkeFnKfdmdpxtg9O0wF8NTcdw5Iyl2Bg==", 18 | "cpu": [ 19 | "arm64" 20 | ], 21 | "dev": true, 22 | "license": "MIT", 23 | "optional": true, 24 | "os": [ 25 | "darwin" 26 | ] 27 | }, 28 | "node_modules/@oxfmt/darwin-x64": { 29 | "version": "0.19.0", 30 | "resolved": "https://registry.npmjs.org/@oxfmt/darwin-x64/-/darwin-x64-0.19.0.tgz", 31 | "integrity": "sha512-31FWUHAgTdTzOslz0zoA60UDEdeZZpeM6wTzCdffetwLbHkK8ZuCuzd+DauHZPNlSU8G72iw4lF7Zve9pSkK9w==", 32 | "cpu": [ 33 | "x64" 34 | ], 35 | "dev": true, 36 | "license": "MIT", 37 | "optional": true, 38 | "os": [ 39 | "darwin" 40 | ] 41 | }, 42 | "node_modules/@oxfmt/linux-arm64-gnu": { 43 | "version": "0.19.0", 44 | "resolved": "https://registry.npmjs.org/@oxfmt/linux-arm64-gnu/-/linux-arm64-gnu-0.19.0.tgz", 45 | "integrity": "sha512-BkU9h39xKj/8uko82uFDJUharM8VxZO+uXKglpBnEC8XxWRzXocVCX4wpLT/Tfk4NyBy6fRM9DeISdZvQKZCjA==", 46 | "cpu": [ 47 | "arm64" 48 | ], 49 | "dev": true, 50 | "license": "MIT", 51 | "optional": true, 52 | "os": [ 53 | "linux" 54 | ] 55 | }, 56 | "node_modules/@oxfmt/linux-arm64-musl": { 57 | "version": "0.19.0", 58 | "resolved": "https://registry.npmjs.org/@oxfmt/linux-arm64-musl/-/linux-arm64-musl-0.19.0.tgz", 59 | "integrity": "sha512-wWYk6Z/3iC+0zZAUkVCcEHui/IsUqsl+GEm9o6H7oARPLisXajbwCQcmqYslUD7eK6OXdYoWriBkEvSX/5dU4A==", 60 | "cpu": [ 61 | "arm64" 62 | ], 63 | "dev": true, 64 | "license": "MIT", 65 | "optional": true, 66 | "os": [ 67 | "linux" 68 | ] 69 | }, 70 | "node_modules/@oxfmt/linux-x64-gnu": { 71 | "version": "0.19.0", 72 | "resolved": "https://registry.npmjs.org/@oxfmt/linux-x64-gnu/-/linux-x64-gnu-0.19.0.tgz", 73 | "integrity": "sha512-EB/b3or437E3uDie8QxeU3eA502JcmR1koyIBcH9rFidY0cMik58xvw54tXCY3WpMRxEXf37aHZzUSDP3qJnZg==", 74 | "cpu": [ 75 | "x64" 76 | ], 77 | "dev": true, 78 | "license": "MIT", 79 | "optional": true, 80 | "os": [ 81 | "linux" 82 | ] 83 | }, 84 | "node_modules/@oxfmt/linux-x64-musl": { 85 | "version": "0.19.0", 86 | "resolved": "https://registry.npmjs.org/@oxfmt/linux-x64-musl/-/linux-x64-musl-0.19.0.tgz", 87 | "integrity": "sha512-htMB45orYoa1oFSjSmoGgcBDsD47/0joDfqpa8TrTDI5qsW5kAedpFR5wSce8Is9oj7SJ07omhOj96P/QiekWA==", 88 | "cpu": [ 89 | "x64" 90 | ], 91 | "dev": true, 92 | "license": "MIT", 93 | "optional": true, 94 | "os": [ 95 | "linux" 96 | ] 97 | }, 98 | "node_modules/@oxfmt/win32-arm64": { 99 | "version": "0.19.0", 100 | "resolved": "https://registry.npmjs.org/@oxfmt/win32-arm64/-/win32-arm64-0.19.0.tgz", 101 | "integrity": "sha512-x7+3Eh/VWdXEda+BUmAKYlhGrRJVera7RfWw47Yx8PJUGtNqBfeYGDbf0W59ceK8Z3bY3OinrmOO3d1jOuXzMQ==", 102 | "cpu": [ 103 | "arm64" 104 | ], 105 | "dev": true, 106 | "license": "MIT", 107 | "optional": true, 108 | "os": [ 109 | "win32" 110 | ] 111 | }, 112 | "node_modules/@oxfmt/win32-x64": { 113 | "version": "0.19.0", 114 | "resolved": "https://registry.npmjs.org/@oxfmt/win32-x64/-/win32-x64-0.19.0.tgz", 115 | "integrity": "sha512-X+FKXBg2jx4CxF5SJs3xpx1msMw5JfxaGD5qBZYqlHGdryQsy6zUY+bQwDDcuy3Ic/WNGD8ZNEuggeYNE7jx/Q==", 116 | "cpu": [ 117 | "x64" 118 | ], 119 | "dev": true, 120 | "license": "MIT", 121 | "optional": true, 122 | "os": [ 123 | "win32" 124 | ] 125 | }, 126 | "node_modules/oxfmt": { 127 | "version": "0.19.0", 128 | "resolved": "https://registry.npmjs.org/oxfmt/-/oxfmt-0.19.0.tgz", 129 | "integrity": "sha512-tPTa3j4kXdJBzBRlK9wR0/Lnd4J21rzg29cRr/VVqqfvdhZs6M+Q6TkL+rxI/IQpq8ZY8L3c+KZvga/RgeuMsg==", 130 | "dev": true, 131 | "license": "MIT", 132 | "dependencies": { 133 | "tinypool": "2.0.0" 134 | }, 135 | "bin": { 136 | "oxfmt": "bin/oxfmt" 137 | }, 138 | "engines": { 139 | "node": "^20.19.0 || >=22.12.0" 140 | }, 141 | "funding": { 142 | "url": "https://github.com/sponsors/Boshen" 143 | }, 144 | "optionalDependencies": { 145 | "@oxfmt/darwin-arm64": "0.19.0", 146 | "@oxfmt/darwin-x64": "0.19.0", 147 | "@oxfmt/linux-arm64-gnu": "0.19.0", 148 | "@oxfmt/linux-arm64-musl": "0.19.0", 149 | "@oxfmt/linux-x64-gnu": "0.19.0", 150 | "@oxfmt/linux-x64-musl": "0.19.0", 151 | "@oxfmt/win32-arm64": "0.19.0", 152 | "@oxfmt/win32-x64": "0.19.0" 153 | } 154 | }, 155 | "node_modules/tinypool": { 156 | "version": "2.0.0", 157 | "resolved": "https://registry.npmjs.org/tinypool/-/tinypool-2.0.0.tgz", 158 | "integrity": "sha512-/RX9RzeH2xU5ADE7n2Ykvmi9ED3FBGPAjw9u3zucrNNaEBIO0HPSYgL0NT7+3p147ojeSdaVu08F6hjpv31HJg==", 159 | "dev": true, 160 | "license": "MIT", 161 | "engines": { 162 | "node": "^20.0.0 || >=22.0.0" 163 | } 164 | } 165 | } 166 | } 167 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | # oxc-intellij-plugin Changelog 4 | 5 | ## [Unreleased] 6 | 7 | ## [0.0.21] - 2025-12-09 8 | 9 | ### Added 10 | 11 | - Add support for Oxfmt. 12 | - Format on save 13 | - Reformat code action 14 | - Right click and apply quick fixes 15 | - New settings for Oxfmt separate from the Oxlint settings 16 | 17 | ### Fixed 18 | 19 | - Fixed WSL with PNPM usage. 20 | 21 | ## [0.0.20] - 2025-11-19 22 | 23 | ### Added 24 | 25 | - Added support for the upcoming 1.29.0 version of Oxlint which allows executing the language server through 26 | `oxlint --lsp`. When using automatic configuration, this will be accommodated without change when updating to 27 | a newer version of Oxlint. When using a manual configuration with a custom path to the language server, there is 28 | a new checkbox that will need to be checked after updating. 29 | 30 | ## [0.0.19] - 2025-11-02 31 | 32 | ### Changed 33 | 34 | - Replace flags configuration with dedicated UI components. The Oxc language server is shifting away from a generic 35 | flags approach and this matches it. This also provides a better UI to users by exposing these options directly. 36 | 37 | ## [0.0.18] - 2025-10-19 38 | 39 | ### Changed 40 | 41 | - `pluginUntilBuild` version is not specified anymore. Plugin will be marked as compatible with all future versions of 42 | an IntelliJ IDE. Individual versions will be marked as not compatible within the JetBrains Marketplace as needed. 43 | 44 | ## [0.0.17] - 2025-09-18 45 | 46 | ### Added 47 | 48 | - Update pluginUntilBuild version for compatibility with 2025.3 versions of IntelliJ. 49 | 50 | ## [0.0.16] - 2025-08-30 51 | 52 | ### Fixed 53 | 54 | - Fix working directory lookup when specifying a language server path that is not part of a node_modules installation. 55 | 56 | ## [0.0.15] - 2025-08-28 57 | 58 | ### Added 59 | 60 | - Add support for type aware rules. 61 | - Cleanup the configuration UI. 62 | 63 | ## [0.0.14] - 2025-08-28 64 | 65 | ### Added 66 | 67 | - Add support for configuring the severity of unused disable directives. 68 | - Add support for passing flags to the language server. 69 | 70 | ### Fixed 71 | 72 | - Fix `fixAllOnSave` property to save to the correct element instead of conflicting with `configPath`. 73 | 74 | ## [0.0.13] - 2025-07-04 75 | 76 | ### Fixed 77 | 78 | - Resolve multiple language servers being started when only one should be running. 79 | This resulted in duplicate diagnostics in the IDE. 80 | - Resolve language server not stopping when closing all relevant files. 81 | - Resolve language server starting when opening irrelevant files. 82 | 83 | ### Removed 84 | 85 | - Removed support for IDE versions 2024.3. 86 | 87 | ## [0.0.12] - 2025-06-24 88 | 89 | ### Added 90 | 91 | - Add support for 2025.2.* versions of IntelliJ. 92 | 93 | ## [0.0.11] - 2025-06-11 94 | 95 | ### Fixed 96 | 97 | - Use project root directory as the root when an Oxlint config file is not available. 98 | 99 | ## [0.0.10] - 2025-06-09 100 | 101 | ### Added 102 | 103 | - Add run trigger to config settings. Allow triggering Oxlint either on save or on type. 104 | 105 | ### Changed 106 | 107 | - Use the new Oxc Language Server config format. This requires Oxlint 0.16.11 or newer. 108 | 109 | ## [0.0.8] - 2025-05-19 110 | 111 | ### Added 112 | 113 | - Support specifying a manual Oxc language server path to the binary instead of the Node.js wrapper. 114 | 115 | ### Fixed 116 | 117 | - Only show "Apply Oxc Quick Fixes" when the plugin is enabled and the selected file is a supported file extension. 118 | 119 | ## [0.0.7] - 2025-05-12 120 | 121 | ### Added 122 | 123 | - Add quick fixes back to the file explorer. 124 | 125 | ## [0.0.6] - 2025-05-05 126 | 127 | ### Added 128 | 129 | - If the RUST_LOG env variable is undefined, pass either DEBUG or TRACE to the process when starting the language server 130 | for additional logging when the plugin is configured to output debug or trace logs. 131 | 132 | ### Fixed 133 | 134 | - Additional fixes to support custom Oxlint config files. 135 | - Make the language server config path optional when using the manual config. 136 | 137 | ## [0.0.5] - 2025-05-01 138 | 139 | ### Fixed 140 | 141 | - Fix support for custom Oxlint config files. 142 | - Remove quick fix from file explorer context menu until the action supports it. 143 | 144 | ## [0.0.4] - 2025-04-25 145 | 146 | ### Fixed 147 | 148 | - Fix diagnostic may not have a code causing a NullPointerException to be thrown. 149 | ...Same as before, missed one spot for this. 150 | 151 | ## [0.0.3] - 2025-04-22 152 | 153 | ### Changed 154 | 155 | - Improve the settings UI. 156 | 157 | ### Fixed 158 | 159 | - Fix diagnostic may not have a code causing a NullPointerException to be thrown. 160 | - Stop showing the server restart message when enabling/disabling the plugin setting. 161 | 162 | ## [0.0.2] - 2025-04-15 163 | 164 | ### Fixed 165 | 166 | - Fix Oxc icon size. 167 | 168 | ## [0.0.1] - 2025-04-12 169 | 170 | ### Added 171 | 172 | - Initial scaffold created from [IntelliJ Platform Plugin Template](https://github.com/JetBrains/intellij-platform-plugin-template) 173 | - Integration with Oxc language server for lint abilities (oxlint) 174 | - Highlighting for warnings or errors identified by Oxlint. 175 | - Quick fixes to fix a warning or error when possible. 176 | - Command to fix all auto-fixable content within the current text editor. 177 | - Automatically apply fixes on save. 178 | - Custom icons for Oxlint config files 179 | - Schema validation for `.oxlintrc.json` configuration files. (Note: Comments within the .oxlintrc.json 180 | file are supported, however they show as an error within the IDE due to jsonc not being supported by the IDE.) 181 | 182 | [Unreleased]: https://github.com/oxc-project/oxc-intellij-plugin/compare/v0.0.21...HEAD 183 | [0.0.21]: https://github.com/oxc-project/oxc-intellij-plugin/compare/v0.0.20...v0.0.21 184 | [0.0.20]: https://github.com/oxc-project/oxc-intellij-plugin/compare/v0.0.19...v0.0.20 185 | [0.0.19]: https://github.com/oxc-project/oxc-intellij-plugin/compare/v0.0.18...v0.0.19 186 | [0.0.18]: https://github.com/oxc-project/oxc-intellij-plugin/compare/v0.0.17...v0.0.18 187 | [0.0.17]: https://github.com/oxc-project/oxc-intellij-plugin/compare/v0.0.16...v0.0.17 188 | [0.0.16]: https://github.com/oxc-project/oxc-intellij-plugin/compare/v0.0.15...v0.0.16 189 | [0.0.15]: https://github.com/oxc-project/oxc-intellij-plugin/compare/v0.0.14...v0.0.15 190 | [0.0.14]: https://github.com/oxc-project/oxc-intellij-plugin/compare/v0.0.13...v0.0.14 191 | [0.0.13]: https://github.com/oxc-project/oxc-intellij-plugin/compare/v0.0.12...v0.0.13 192 | [0.0.12]: https://github.com/oxc-project/oxc-intellij-plugin/compare/v0.0.11...v0.0.12 193 | [0.0.11]: https://github.com/oxc-project/oxc-intellij-plugin/compare/v0.0.10...v0.0.11 194 | [0.0.10]: https://github.com/oxc-project/oxc-intellij-plugin/compare/v0.0.8...v0.0.10 195 | [0.0.8]: https://github.com/oxc-project/oxc-intellij-plugin/compare/v0.0.7...v0.0.8 196 | [0.0.7]: https://github.com/oxc-project/oxc-intellij-plugin/compare/v0.0.6...v0.0.7 197 | [0.0.6]: https://github.com/oxc-project/oxc-intellij-plugin/compare/v0.0.5...v0.0.6 198 | [0.0.5]: https://github.com/oxc-project/oxc-intellij-plugin/compare/v0.0.4...v0.0.5 199 | [0.0.4]: https://github.com/oxc-project/oxc-intellij-plugin/compare/v0.0.3...v0.0.4 200 | [0.0.3]: https://github.com/oxc-project/oxc-intellij-plugin/compare/v0.0.2...v0.0.3 201 | [0.0.2]: https://github.com/oxc-project/oxc-intellij-plugin/compare/v0.0.1...v0.0.2 202 | [0.0.1]: https://github.com/oxc-project/oxc-intellij-plugin/commits/v0.0.1 203 | -------------------------------------------------------------------------------- /src/main/kotlin/com/github/oxc/project/oxcintellijplugin/oxfmt/settings/OxfmtConfigurable.kt: -------------------------------------------------------------------------------- 1 | package com.github.oxc.project.oxcintellijplugin.oxfmt.settings 2 | 3 | import com.github.oxc.project.oxcintellijplugin.ConfigurationMode 4 | import com.github.oxc.project.oxcintellijplugin.oxfmt.OxfmtBundle 5 | import com.github.oxc.project.oxcintellijplugin.oxfmt.OxfmtPackage 6 | import com.github.oxc.project.oxcintellijplugin.oxfmt.services.OxfmtServerService 7 | import com.intellij.ide.actionsOnSave.ActionsOnSaveConfigurable 8 | import com.intellij.lang.javascript.JavaScriptBundle 9 | import com.intellij.openapi.application.ApplicationManager 10 | import com.intellij.openapi.application.ApplicationNamesInfo 11 | import com.intellij.openapi.options.BoundSearchableConfigurable 12 | import com.intellij.openapi.project.Project 13 | import com.intellij.openapi.ui.DialogPanel 14 | import com.intellij.openapi.ui.ValidationInfo 15 | import com.intellij.ui.ContextHelpLabel 16 | import com.intellij.ui.components.JBTextField 17 | import com.intellij.ui.dsl.builder.AlignX 18 | import com.intellij.ui.dsl.builder.BottomGap 19 | import com.intellij.ui.dsl.builder.MutableProperty 20 | import com.intellij.ui.dsl.builder.bindSelected 21 | import com.intellij.ui.dsl.builder.bindText 22 | import com.intellij.ui.dsl.builder.panel 23 | import com.intellij.ui.layout.not 24 | import com.intellij.ui.layout.selected 25 | import com.intellij.util.ui.JBUI 26 | import com.intellij.util.ui.UIUtil 27 | import javax.swing.JCheckBox 28 | import javax.swing.JRadioButton 29 | import javax.swing.event.HyperlinkEvent 30 | 31 | private const val HELP_TOPIC = "reference.settings.oxfmt" 32 | 33 | class OxfmtConfigurable(private val project: Project) : 34 | BoundSearchableConfigurable(OxfmtBundle.message("oxfmt.name"), HELP_TOPIC, CONFIGURABLE_ID) { 35 | 36 | lateinit var fixAllOnSaveCheckBox: JCheckBox 37 | lateinit var disabledConfiguration: JRadioButton 38 | private lateinit var automaticConfiguration: JRadioButton 39 | private lateinit var manualConfiguration: JRadioButton 40 | private lateinit var extensionsField: JBTextField 41 | 42 | override fun createPanel(): DialogPanel { 43 | val settings = OxfmtSettings.getInstance(project) 44 | val server = OxfmtServerService.getInstance(project) 45 | 46 | return panel { 47 | // ********************* 48 | // Configuration mode row 49 | // ********************* 50 | buttonsGroup { 51 | row { 52 | disabledConfiguration = radioButton( 53 | JavaScriptBundle.message("settings.javascript.linters.autodetect.disabled", 54 | displayName)).bindSelected( 55 | ConfigurationModeProperty(settings, ConfigurationMode.DISABLED)).component 56 | } 57 | row { 58 | automaticConfiguration = radioButton(JavaScriptBundle.message( 59 | "settings.javascript.linters.autodetect.configure.automatically", 60 | displayName)).bindSelected( 61 | ConfigurationModeProperty(settings, ConfigurationMode.AUTOMATIC)).component 62 | 63 | val detectAutomaticallyHelpText = JavaScriptBundle.message( 64 | "settings.javascript.linters.autodetect.configure.automatically.help.text", 65 | ApplicationNamesInfo.getInstance().fullProductName, displayName, 66 | "${OxfmtPackage.CONFIG_NAME}.json") 67 | 68 | val helpLabel = ContextHelpLabel.create(detectAutomaticallyHelpText) 69 | helpLabel.border = JBUI.Borders.emptyLeft(UIUtil.DEFAULT_HGAP) 70 | cell(helpLabel) 71 | } 72 | row { 73 | manualConfiguration = radioButton(JavaScriptBundle.message( 74 | "settings.javascript.linters.autodetect.configure.manually", 75 | displayName)).bindSelected( 76 | ConfigurationModeProperty(settings, ConfigurationMode.MANUAL)).component 77 | } 78 | } 79 | 80 | // ********************* 81 | // Manual configuration row 82 | // ********************* 83 | indent { 84 | panel { 85 | row(OxfmtBundle.message("oxfmt.settings.languageServerPath")) { 86 | @Suppress("UnstableApiUsage") textFieldWithBrowseButton( 87 | OxfmtBundle.message("oxfmt.settings.languageServerPath")) { 88 | it.path 89 | }.align(AlignX.FILL).bindText(settings::binaryPath) 90 | }.visibleIf(manualConfiguration.selected) 91 | 92 | row(OxfmtBundle.message("oxfmt.config.path.label")) { 93 | @Suppress("UnstableApiUsage") textFieldWithBrowseButton( 94 | OxfmtBundle.message("oxfmt.config.path.label"), 95 | project, 96 | ) { it.path }.align(AlignX.FILL).bindText(settings::configPath) 97 | }.visibleIf(manualConfiguration.selected) 98 | } 99 | } 100 | 101 | // ********************* 102 | // Supported file extensions row 103 | // ********************* 104 | row(OxfmtBundle.message("oxfmt.supported.extensions.label")) { 105 | val parse = { it: String -> 106 | it.split(",").map { it.trim() }.filter { it.isNotBlank() }.toMutableList() 107 | } 108 | val join = { it: List -> 109 | it.joinToString(",") 110 | } 111 | 112 | val extensionsFieldCell = expandableTextField(parse, join).align(AlignX.FILL) 113 | .bindText({ join(settings.supportedExtensions) }, { value -> 114 | settings.supportedExtensions = parse(value) 115 | }).validationOnInput { 116 | validateExtensions(it) 117 | } 118 | 119 | extensionsField = extensionsFieldCell.component 120 | 121 | // Add help text with a "Reset" link below the field 122 | extensionsFieldCell.comment(OxfmtBundle.message( 123 | "oxfmt.supported.extensions.comment") + " Reset to Defaults") 124 | extensionsFieldCell.comment!!.addHyperlinkListener { event -> 125 | if (event.eventType == HyperlinkEvent.EventType.ACTIVATED && event.description == "reset") { 126 | extensionsField.text = join(OxfmtSettingsState.DEFAULT_EXTENSION_LIST) 127 | } 128 | } 129 | }.bottomGap(BottomGap.MEDIUM).enabledIf(!disabledConfiguration.selected) 130 | 131 | // ********************* 132 | // Apply fixes on save row 133 | // ********************* 134 | row { 135 | fixAllOnSaveCheckBox = checkBox( 136 | OxfmtBundle.message("oxfmt.run.fix.all.on.save.label")).bindSelected( 137 | { settings.configurationMode != ConfigurationMode.DISABLED && settings.fixAllOnSave }, 138 | { settings.fixAllOnSave = it }, 139 | ).component 140 | 141 | val link = ActionsOnSaveConfigurable.createGoToActionsOnSavePageLink() 142 | cell(link) 143 | }.enabledIf(!disabledConfiguration.selected) 144 | 145 | onApply { 146 | if (project.isDefault) { 147 | return@onApply 148 | } 149 | ApplicationManager.getApplication().invokeLater { 150 | server.restartServer() 151 | } 152 | } 153 | } 154 | } 155 | 156 | private fun validateExtensions(field: JBTextField): ValidationInfo? { 157 | val input = field.text 158 | val extensions = input.split(",").map { it.trim() } 159 | 160 | val invalidExtension = extensions.find { !it.matches(Regex("^\\.[a-zA-Z0-9]+$")) } 161 | return if (invalidExtension != null) { 162 | ValidationInfo( 163 | "Invalid extension: $invalidExtension. Must start with '.' and contain only alphanumeric characters.", 164 | field) 165 | } else { 166 | null 167 | } 168 | } 169 | 170 | private class ConfigurationModeProperty( 171 | private val settings: OxfmtSettings, 172 | private val mode: ConfigurationMode, 173 | ) : MutableProperty { 174 | 175 | override fun get(): Boolean = settings.configurationMode == mode 176 | 177 | override fun set(value: Boolean) { 178 | if (value) { 179 | settings.configurationMode = mode 180 | } 181 | } 182 | } 183 | 184 | companion object { 185 | 186 | const val CONFIGURABLE_ID = "OxfmtConfigurable" 187 | } 188 | } 189 | --------------------------------------------------------------------------------