diff --git a/.github/actions/setup-go/action.yml b/.github/actions/setup-go/action.yml index 854815dc..74e0b3e8 100644 --- a/.github/actions/setup-go/action.yml +++ b/.github/actions/setup-go/action.yml @@ -21,6 +21,7 @@ runs: - name: Clean Go module cache dir shell: bash + if: runner.os == 'linux' run: sudo rm -rf ~/go/pkg/mod/golang.org/toolchain* - name: Go cache diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index b0b1156e..118c5a3c 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -19,9 +19,10 @@ env: jobs: test-go: name: Test Go - runs-on: rspack-ubuntu-22.04-large + runs-on: ${{ matrix.runner }} strategy: matrix: + runner: [rspack-ubuntu-22.04-large, windows-latest] go-version: ['1.24.1'] steps: - name: Checkout code @@ -39,13 +40,16 @@ jobs: go-version: ${{ matrix.go-version }} cache-name: test-go - name: golangci-lint + if: runner.os == 'Linux' uses: golangci/golangci-lint-action@v8 with: version: v2.3.0 args: --timeout=5m ./cmd/... ./internal/... - name: go vet + if: runner.os == 'Linux' run: npm run lint:go - name: go fmt + if: runner.os == 'Linux' run: npm run format:go - name: Unit Test run: | @@ -53,9 +57,10 @@ jobs: test-node: name: Test npm packages - runs-on: rspack-ubuntu-22.04-large + runs-on: ${{ matrix.os }} strategy: matrix: + os: [rspack-ubuntu-22.04-large, windows-latest] go-version: ['1.24.1'] steps: - name: Checkout code @@ -73,16 +78,18 @@ jobs: uses: ./.github/actions/setup-node - name: Format + if: runner.os == 'Linux' run: pnpm format:check - name: Build run: pnpm build - name: TypeCheck + if: runner.os == 'Linux' run: pnpm typecheck - - name: Install xvfb and dependencies - if: ${{ runner.os == 'Linux' && runner.environment == 'self-hosted' }} + - name: Install xvfb and dependencies (Linux only) + if: runner.os == 'Linux' run: | sudo apt update sudo apt install -y libasound2 libgbm1 libgtk-3-0 libnss3 xvfb @@ -91,9 +98,9 @@ jobs: uses: lynx-infra/cache@5c6160a6a4c7fca80a2f3057bb9dfc9513fcb732 with: path: packages/vscode-extension/.vscode-test - key: 'vscode-test' + key: vscode-test-${{ matrix.os }} restore-keys: | - - 'vscode-test-' + vscode-test-${{ matrix.os }}- - name: Test on Linux if: runner.os == 'Linux' @@ -104,9 +111,11 @@ jobs: run: pnpm -r test - name: Check Spell + if: runner.os == 'Linux' run: pnpm check-spell - name: Lint + if: runner.os == 'Linux' run: pnpm run lint done: @@ -114,7 +123,7 @@ jobs: - test-go - test-node if: always() - runs-on: rspack-ubuntu-22.04-large + runs-on: ubuntu-latest name: CI Done steps: - run: exit 1 diff --git a/cmd/rslint/api.go b/cmd/rslint/api.go index 4958975d..c93eaccd 100644 --- a/cmd/rslint/api.go +++ b/cmd/rslint/api.go @@ -48,11 +48,14 @@ func (h *IPCHandler) HandleLint(req api.LintRequest) (*api.LintResponse, error) allowedFiles := []string{} // Apply file contents if provided if len(req.FileContents) > 0 { - fs = utils.NewOverlayVFS(fs, req.FileContents) - for file := range req.FileContents { - - allowedFiles = append(allowedFiles, file) // Collect allowed files from request + fileContents := make(map[string]string, len(req.FileContents)) + for k, v := range req.FileContents { + normalizePath := tspath.NormalizePath(k) + fileContents[normalizePath] = v + allowedFiles = append(allowedFiles, normalizePath) } + fs = utils.NewOverlayVFS(fs, fileContents) + } // Initialize rule registry with all available rules diff --git a/cmd/rslint/lsp.go b/cmd/rslint/lsp.go index e2bf310b..21e1d3f7 100644 --- a/cmd/rslint/lsp.go +++ b/cmd/rslint/lsp.go @@ -8,6 +8,7 @@ import ( "io" "log" "os" + "path/filepath" "strings" "sync" @@ -394,7 +395,7 @@ func findRslintConfig(fs vfs.FS, workingDir string) (string, bool) { // Strategy 1: Try in the working directory for _, configName := range defaultConfigs { - configPath := workingDir + "/" + configName + configPath := filepath.Join(workingDir, configName) if fs.FileExists(configPath) { return configPath, true } diff --git a/internal/config/config.go b/internal/config/config.go index 5ebd6f45..3744d71d 100644 --- a/internal/config/config.go +++ b/internal/config/config.go @@ -2,10 +2,10 @@ package config import ( "os" - "path/filepath" "strings" "github.com/bmatcuk/doublestar/v4" + "github.com/microsoft/typescript-go/shim/tspath" importPlugin "github.com/web-infra-dev/rslint/internal/plugins/import" "github.com/web-infra-dev/rslint/internal/rule" "github.com/web-infra-dev/rslint/internal/rules/adjacent_overload_signatures" @@ -362,13 +362,13 @@ func isFileIgnored(filePath string, ignorePatterns []string) bool { for _, pattern := range ignorePatterns { // Try matching against normalized path - if matched, err := doublestar.PathMatch(pattern, normalizedPath); err == nil && matched { + if matched, err := doublestar.Match(pattern, normalizedPath); err == nil && matched { return true } // Also try matching against original path for absolute patterns if normalizedPath != filePath { - if matched, err := doublestar.PathMatch(pattern, filePath); err == nil && matched { + if matched, err := doublestar.Match(pattern, filePath); err == nil && matched { return true } } @@ -376,7 +376,7 @@ func isFileIgnored(filePath string, ignorePatterns []string) bool { // Try Unix-style path for cross-platform compatibility unixPath := strings.ReplaceAll(normalizedPath, "\\", "/") if unixPath != normalizedPath { - if matched, err := doublestar.PathMatch(pattern, unixPath); err == nil && matched { + if matched, err := doublestar.Match(pattern, unixPath); err == nil && matched { return true } } @@ -386,25 +386,16 @@ func isFileIgnored(filePath string, ignorePatterns []string) bool { // normalizePath converts file path to be relative to cwd for consistent matching func normalizePath(filePath, cwd string) string { - cleanPath := filepath.Clean(filePath) - - // If absolute path, try to make it relative to working directory - if filepath.IsAbs(cleanPath) { - if relPath, err := filepath.Rel(cwd, cleanPath); err == nil { - // Only use relative path if it doesn't go outside the working directory - if !strings.HasPrefix(relPath, "..") { - return relPath - } - } - } - - return cleanPath + return tspath.NormalizePath(tspath.ConvertToRelativePath(filePath, tspath.ComparePathsOptions{ + UseCaseSensitiveFileNames: true, + CurrentDirectory: cwd, + })) } // isFileIgnoredSimple provides fallback matching when cwd is unavailable func isFileIgnoredSimple(filePath string, ignorePatterns []string) bool { for _, pattern := range ignorePatterns { - if matched, err := doublestar.PathMatch(pattern, filePath); err == nil && matched { + if matched, err := doublestar.Match(pattern, filePath); err == nil && matched { return true } } diff --git a/internal/config/cwd_test.go b/internal/config/cwd_test.go index 59b586b1..f6f9c3d4 100644 --- a/internal/config/cwd_test.go +++ b/internal/config/cwd_test.go @@ -143,7 +143,7 @@ func TestDoublestarBehavior(t *testing.T) { for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - matched, err := doublestar.PathMatch(tt.pattern, tt.path) + matched, err := doublestar.Match(tt.pattern, tt.path) if err != nil { t.Errorf("doublestar.PathMatch error: %v", err) return