diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 00000000..20870f29 --- /dev/null +++ b/.gitattributes @@ -0,0 +1,42 @@ +# Set default behavior to automatically normalize line endings. +* text=auto + +# Ensure these files always have LF line endings on checkout +*.ts text eol=lf +*.js text eol=lf +*.mjs text eol=lf +*.cjs text eol=lf +*.jsx text eol=lf +*.tsx text eol=lf +*.json text eol=lf +*.md text eol=lf +*.yml text eol=lf +*.yaml text eol=lf +*.toml text eol=lf +*.go text eol=lf + +# Shell scripts should always have LF +*.sh text eol=lf + +# Windows batch files should have CRLF +*.bat text eol=crlf +*.cmd text eol=crlf + +# Binary files should not be touched +*.png binary +*.jpg binary +*.jpeg binary +*.gif binary +*.ico binary +*.svg binary +*.woff binary +*.woff2 binary +*.ttf binary +*.eot binary +*.zip binary +*.tar.gz binary +*.tgz binary +*.exe binary +*.dll binary +*.so binary +*.dylib binary \ No newline at end of file diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 28c1a25d..668ff94b 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -19,9 +19,15 @@ env: jobs: test-go: name: Test Go - runs-on: rspack-ubuntu-22.04-large + runs-on: ${{ matrix.os }} strategy: matrix: + os: + [ + rspack-ubuntu-22.04-large, + rspack-darwin-14-medium, + rspack-windows-2022-large, + ] go-version: ['1.24.1'] steps: - name: Checkout code @@ -39,13 +45,16 @@ jobs: go-version: ${{ matrix.go-version }} cache-name: test-go - name: golangci-lint + if: ${{ runner.os == 'Linux' && runner.environment == 'self-hosted' }} uses: golangci/golangci-lint-action@v8 with: version: v2.3.0 args: --timeout=5m ./cmd/... ./internal/... - name: go vet + if: ${{ runner.os == 'Linux' && runner.environment == 'self-hosted' }} run: npm run lint:go - name: go fmt + if: ${{ runner.os == 'Linux' && runner.environment == 'self-hosted' }} run: npm run format:go - name: Unit Test run: | @@ -53,9 +62,15 @@ 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, + rspack-darwin-14-medium, + rspack-windows-2022-large, + ] go-version: ['1.24.1'] steps: - name: Checkout code diff --git a/internal/config/config.go b/internal/config/config.go index dbf3eb74..86ce4d77 100644 --- a/internal/config/config.go +++ b/internal/config/config.go @@ -331,6 +331,10 @@ func getAllTypeScriptEslintPluginRules() []rule.Rule { return rules } +func pathMatch(pattern, name string) (bool, error) { + return doublestar.PathMatch(filepath.ToSlash(pattern), filepath.ToSlash(name)) +} + // isFileIgnored checks if a file should be ignored based on ignore patterns func isFileIgnored(filePath string, ignorePatterns []string) bool { // Get current working directory for relative path resolution @@ -345,13 +349,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 := pathMatch(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 := pathMatch(pattern, filePath); err == nil && matched { return true } } @@ -359,7 +363,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 := pathMatch(pattern, unixPath); err == nil && matched { return true } } @@ -387,7 +391,7 @@ func normalizePath(filePath, cwd string) string { // 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 := pathMatch(pattern, filePath); err == nil && matched { return true } } diff --git a/internal/config/cwd_test.go b/internal/config/cwd_test.go deleted file mode 100644 index 59b586b1..00000000 --- a/internal/config/cwd_test.go +++ /dev/null @@ -1,157 +0,0 @@ -package config - -import ( - "os" - "path/filepath" - "testing" - - "github.com/bmatcuk/doublestar/v4" -) - -func TestCwdHandling(t *testing.T) { - // Save the original working directory - originalCwd, err := os.Getwd() - if err != nil { - t.Fatalf("Unable to get current working directory: %v", err) - } - defer t.Chdir(originalCwd) // Restore after test completes - - tests := []struct { - name string - filePath string - patterns []string - shouldIgnore bool - description string - }{ - { - name: "Relative path matching", - filePath: "src/component.ts", - patterns: []string{"src/**"}, - shouldIgnore: true, - description: "Relative paths should match relative patterns", - }, - { - name: "Absolute path to relative path matching", - filePath: filepath.Join(originalCwd, "src/component.ts"), - patterns: []string{"src/**"}, - shouldIgnore: true, - description: "Absolute paths should be converted to relative paths before matching", - }, - { - name: "node_modules recursive matching", - filePath: "node_modules/package/deep/file.js", - patterns: []string{"node_modules/**"}, - shouldIgnore: true, - description: "Recursive patterns should match deeply nested files", - }, - { - name: "Test file pattern matching", - filePath: "src/utils/helper.test.ts", - patterns: []string{"**/*.test.ts"}, - shouldIgnore: true, - description: "Global recursive patterns should match test files in any location", - }, - { - name: "Non-matching file", - filePath: "src/component.ts", - patterns: []string{"dist/**", "*.log"}, - shouldIgnore: false, - description: "Files not matching any pattern should not be ignored", - }, - { - name: "Cross-platform path handling", - filePath: "src\\windows\\style\\path.ts", - patterns: []string{"src/**"}, - shouldIgnore: true, - description: "Windows style paths should be handled correctly", - }, - } - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - result := isFileIgnored(tt.filePath, tt.patterns) - if result != tt.shouldIgnore { - t.Errorf("%s: isFileIgnored(%q, %v) = %v, expected %v", - tt.description, tt.filePath, tt.patterns, result, tt.shouldIgnore) - } - }) - } -} - -func TestNormalizePath(t *testing.T) { - cwd, err := os.Getwd() - if err != nil { - t.Fatalf("Unable to get working directory: %v", err) - } - - tests := []struct { - name string - filePath string - expected string - }{ - { - name: "Relative path remains unchanged", - filePath: "src/component.ts", - expected: "src/component.ts", - }, - { - name: "Absolute path converts to relative path", - filePath: filepath.Join(cwd, "src/component.ts"), - expected: "src/component.ts", - }, - { - name: "Current directory marker", - filePath: "./src/component.ts", - expected: "src/component.ts", - }, - { - name: "Complex relative path", - filePath: "src/../src/component.ts", - expected: "src/component.ts", - }, - } - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - result := normalizePath(tt.filePath, cwd) - if result != tt.expected { - t.Errorf("normalizePath(%q, %q) = %q, expected %q", - tt.filePath, cwd, result, tt.expected) - } - }) - } -} - -func TestDoublestarBehavior(t *testing.T) { - // Test specific behavior of the doublestar library - tests := []struct { - pattern string - path string - expected bool - name string - }{ - {"**/*.ts", "src/file.ts", true, "Recursive matching of TypeScript files"}, - {"**/*.ts", "src/deep/nested/file.ts", true, "Deep recursive matching"}, - {"src/**", "src/file.ts", true, "Directory recursive matching"}, - {"src/**", "src/deep/nested/file.ts", true, "Deep directory recursive matching"}, - {"*.ts", "file.ts", true, "Single-level wildcard"}, - {"*.ts", "src/file.ts", false, "Single-level wildcard doesn't match nested files"}, - {"node_modules/**", "node_modules/package/file.js", true, "node_modules recursive matching"}, - {"**/test/**", "src/test/file.js", true, "Middle recursive matching"}, - {"**/test/**", "test/file.js", true, "Beginning recursive matching"}, - } - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - matched, err := doublestar.PathMatch(tt.pattern, tt.path) - if err != nil { - t.Errorf("doublestar.PathMatch error: %v", err) - return - } - if matched != tt.expected { - t.Errorf("doublestar.PathMatch(%q, %q) = %v, expected %v", - tt.pattern, tt.path, matched, tt.expected) - } - }) - } -} diff --git a/npm/darwin-arm64/package.json b/npm/darwin-arm64/package.json index e1127dd2..dd1c0e02 100644 --- a/npm/darwin-arm64/package.json +++ b/npm/darwin-arm64/package.json @@ -2,6 +2,9 @@ "name": "@rslint/darwin-arm64", "version": "0.1.7", "license": "MIT", + "exports": { + "./bin": "./rslint" + }, "description": "binary for rslint", "homepage": "https://rslint.rs", "bugs": "https://github.com/web-infra-dev/rslint/issues", diff --git a/npm/darwin-x64/package.json b/npm/darwin-x64/package.json index e58b9363..e39ef34f 100644 --- a/npm/darwin-x64/package.json +++ b/npm/darwin-x64/package.json @@ -2,6 +2,9 @@ "name": "@rslint/darwin-x64", "version": "0.1.7", "license": "MIT", + "exports": { + "./bin": "./rslint" + }, "description": "binary for rslint", "bugs": "https://github.com/web-infra-dev/rslint/issues", "repository": { diff --git a/npm/linux-arm64/package.json b/npm/linux-arm64/package.json index 758b2ec0..7ea8fa96 100644 --- a/npm/linux-arm64/package.json +++ b/npm/linux-arm64/package.json @@ -2,6 +2,9 @@ "name": "@rslint/linux-arm64", "version": "0.1.7", "license": "MIT", + "exports": { + "./bin": "./rslint" + }, "description": "binary for rslint", "bugs": "https://github.com/web-infra-dev/rslint/issues", "repository": { diff --git a/npm/linux-x64/package.json b/npm/linux-x64/package.json index be82416d..f4a2df2b 100644 --- a/npm/linux-x64/package.json +++ b/npm/linux-x64/package.json @@ -2,6 +2,9 @@ "name": "@rslint/linux-x64", "version": "0.1.7", "license": "MIT", + "exports": { + "./bin": "./rslint" + }, "description": "binary for rslint", "bugs": "https://github.com/web-infra-dev/rslint/issues", "repository": { diff --git a/npm/win32-arm64/package.json b/npm/win32-arm64/package.json index 80dd31ef..c497d596 100644 --- a/npm/win32-arm64/package.json +++ b/npm/win32-arm64/package.json @@ -2,6 +2,9 @@ "name": "@rslint/win32-arm64", "version": "0.1.7", "license": "MIT", + "exports": { + "./bin": "./rslint.exe" + }, "description": "binary for rslint", "bugs": "https://github.com/web-infra-dev/rslint/issues", "repository": { diff --git a/npm/win32-x64/package.json b/npm/win32-x64/package.json index 6c8c2c16..a9515308 100644 --- a/npm/win32-x64/package.json +++ b/npm/win32-x64/package.json @@ -2,6 +2,9 @@ "name": "@rslint/win32-x64", "version": "0.1.7", "license": "MIT", + "exports": { + "./bin": "./rslint.exe" + }, "description": "binary for rslint", "bugs": "https://github.com/web-infra-dev/rslint/issues", "repository": { diff --git a/package.json b/package.json index 9ccae5a8..0f81d39a 100644 --- a/package.json +++ b/package.json @@ -14,8 +14,9 @@ "license": "MIT", "scripts": { "build": "pnpm -r build", - "build:npm": "zx scripts/build-npm.mjs", "check-spell": "pnpx cspell", + "build:npm:all": "zx scripts/build-npm.mjs all", + "build:npm:current": "zx scripts/build-npm.mjs current", "version": "zx scripts/version.mjs", "release": "pnpm publish -r --no-git-checks", "publish:vsce": "zx scripts/publish-marketplace.mjs", diff --git a/packages/rslint/.gitignore b/packages/rslint/.gitignore deleted file mode 100644 index 0ade55af..00000000 --- a/packages/rslint/.gitignore +++ /dev/null @@ -1,2 +0,0 @@ -bin/rslint -bin/rslint-* diff --git a/packages/rslint/bin/rslint.cjs b/packages/rslint/bin/rslint.cjs index b160bb50..f31fcb26 100755 --- a/packages/rslint/bin/rslint.cjs +++ b/packages/rslint/bin/rslint.cjs @@ -1,22 +1,9 @@ #!/usr/bin/env node -const path = require('node:path'); const os = require('node:os'); -const fs = require('node:fs'); -function getBinPath() { - if (fs.existsSync(path.resolve(__dirname, './rslint'))) { - return path.resolve(__dirname, './rslint'); - } - if (fs.existsSync(path.resolve(__dirname, './rslint.exe'))) { - return path.resolve(__dirname, './rslint.exe'); - } - let platformKey = `${process.platform}-${os.arch()}`; - return require.resolve( - `@rslint/${platformKey}/rslint${process.platform === 'win32' ? '.exe' : ''}`, - ); -} function main() { - const binPath = getBinPath(); + const binPath = require.resolve(`@rslint/${os.platform()}-${os.arch()}/bin`); + try { require('child_process').execFileSync(binPath, process.argv.slice(2), { stdio: 'inherit', @@ -31,4 +18,5 @@ function main() { } } } + main(); diff --git a/packages/rslint/package.json b/packages/rslint/package.json index c8ee5c9e..c33a851f 100644 --- a/packages/rslint/package.json +++ b/packages/rslint/package.json @@ -12,7 +12,7 @@ "url": "https://github.com/web-infra-dev/rslint" }, "scripts": { - "build:bin": "go build -v -o bin/rslint ../../cmd/rslint", + "build:bin": "zx ../../scripts/build-npm.mjs current", "build:debug": "GOEXPERIMENT=noregabi go build -v -gcflags='all=-N -l' -o bin/rslint ../../cmd/rslint ", "build:js": "tsc -b tsconfig.build.json --force", "dev": "tsc -b tsconfig.build.json --force --watch", diff --git a/packages/rslint/src/service.ts b/packages/rslint/src/service.ts index bd9c846a..bbd9399e 100644 --- a/packages/rslint/src/service.ts +++ b/packages/rslint/src/service.ts @@ -1,5 +1,5 @@ import { spawn, ChildProcess } from 'child_process'; -import path from 'path'; +import { platform, arch } from 'os'; /** * Types for rslint IPC protocol @@ -63,10 +63,11 @@ export class RSLintService { private expectedSize: number | null; constructor(options: RSlintOptions = {}) { + const defaultPath = `@rslint/${platform()}-${arch()}/bin`; + this.nextMessageId = 1; this.pendingMessages = new Map(); - this.rslintPath = - options.rslintPath || path.join(import.meta.dirname, '../bin/rslint'); + this.rslintPath = options.rslintPath || require.resolve(defaultPath); this.process = spawn(this.rslintPath, ['--api'], { stdio: ['pipe', 'pipe', 'inherit'], diff --git a/packages/rslint/tests/api.test.mjs b/packages/rslint/tests/api.test.mjs index 4cdcb48f..e58c897f 100644 --- a/packages/rslint/tests/api.test.mjs +++ b/packages/rslint/tests/api.test.mjs @@ -3,13 +3,14 @@ import { describe, test, expect } from '@rstest/core'; import path from 'node:path'; describe('lint api', async t => { - let cwd = path.resolve(import.meta.dirname, '../fixtures'); + const cwd = path.resolve(import.meta.dirname, '../fixtures'); + test('virtual file support', async t => { - let config = path.resolve( + const config = path.resolve( import.meta.dirname, '../fixtures/rslint.virtual.json', ); - let virtual_entry = path.resolve(cwd, 'src/virtual.ts'); + const virtual_entry = path.resolve(cwd, 'src/virtual.ts'); // Use virtual file contents instead of reading from disk const diags = await lint({ config, @@ -28,7 +29,7 @@ describe('lint api', async t => { expect(diags).toMatchSnapshot(); }); test('diag snapshot', async t => { - let config = path.resolve(import.meta.dirname, '../fixtures/rslint.json'); + const config = path.resolve(import.meta.dirname, '../fixtures/rslint.json'); const diags = await lint({ config, workingDirectory: cwd, diff --git a/packages/vscode-extension/package.json b/packages/vscode-extension/package.json index c19db7a8..ca86ff5c 100644 --- a/packages/vscode-extension/package.json +++ b/packages/vscode-extension/package.json @@ -83,6 +83,12 @@ }, "devDependencies": { "@rslint/core": "workspace:*", + "@rslint/darwin-arm64": "workspace:*", + "@rslint/darwin-x64": "workspace:*", + "@rslint/linux-arm64": "workspace:*", + "@rslint/linux-x64": "workspace:*", + "@rslint/win32-arm64": "workspace:*", + "@rslint/win32-x64": "workspace:*", "@types/mocha": "10.0.9", "@types/node": "24.0.14", "@types/vscode": "^1.74.0", diff --git a/packages/vscode-extension/scripts/build.js b/packages/vscode-extension/scripts/build.js index 4ab26c09..b3be1db5 100644 --- a/packages/vscode-extension/scripts/build.js +++ b/packages/vscode-extension/scripts/build.js @@ -1,6 +1,7 @@ const esbuild = require('esbuild'); -const path = require('path'); -const fs = require('fs'); +const path = require('node:path'); +const os = require('node:os'); +const fs = require('node:fs'); const isWatchMode = process.argv.includes('--watch'); const config = { @@ -20,17 +21,17 @@ const config = { name: 'copy-files', setup(build) { build.onStart(() => { - console.info('start rebuild'); + console.log('📁 Copy file start'); }); build.onEnd(() => { - const binDir = path.resolve( - require.resolve('@rslint/core/package.json'), - '../bin', + const binPath = require.resolve( + `@rslint/${os.platform()}-${os.arch()}/bin`, ); - fs.cpSync(binDir, path.join(__dirname, '../dist'), { + const binaryName = path.basename(binPath); + fs.cpSync(binPath, path.join(__dirname, `../dist/${binaryName}`), { recursive: true, }); - console.log('rebuild done'); + console.log('✅ Copy file done'); }); }, }, diff --git a/packages/vscode-extension/src/Rslint.ts b/packages/vscode-extension/src/Rslint.ts index 7400e84b..39688d06 100644 --- a/packages/vscode-extension/src/Rslint.ts +++ b/packages/vscode-extension/src/Rslint.ts @@ -15,7 +15,7 @@ import { Trace, } from 'vscode-languageclient/node'; import { Logger } from './logger'; -import type { Extension } from './Extension'; +import type { Extension } from './extension'; import { fileExists, PLATFORM_BIN_REQUEST, RslintBinPath } from './utils'; import { dirname } from 'node:path'; diff --git a/packages/vscode-extension/src/Extension.ts b/packages/vscode-extension/src/extension.ts similarity index 99% rename from packages/vscode-extension/src/Extension.ts rename to packages/vscode-extension/src/extension.ts index 009ebefc..ff26598d 100644 --- a/packages/vscode-extension/src/Extension.ts +++ b/packages/vscode-extension/src/extension.ts @@ -7,7 +7,7 @@ import { } from 'vscode'; import { State } from 'vscode-languageclient/node'; import { LogLevel, Logger } from './logger'; -import { Rslint } from './Rslint'; +import { Rslint } from './rslint'; export class Extension implements Disposable { private rslintInstances: Map = new Map(); @@ -135,4 +135,4 @@ export class Extension implements Disposable { this.disposables.push(stateChangeDisposable); this.context.subscriptions.push(stateChangeDisposable); } -} +} \ No newline at end of file diff --git a/packages/vscode-extension/src/main.ts b/packages/vscode-extension/src/main.ts index 2d91bd56..7b3b0e9d 100644 --- a/packages/vscode-extension/src/main.ts +++ b/packages/vscode-extension/src/main.ts @@ -1,5 +1,5 @@ import { ExtensionContext } from 'vscode'; -import { Extension } from './Extension'; +import { Extension } from './extension'; let extension: Extension; diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 72ca1d7a..cb5dc7ec 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -115,6 +115,24 @@ importers: '@rslint/core': specifier: workspace:* version: link:../rslint + '@rslint/darwin-arm64': + specifier: workspace:* + version: link:../../npm/darwin-arm64 + '@rslint/darwin-x64': + specifier: workspace:* + version: link:../../npm/darwin-x64 + '@rslint/linux-arm64': + specifier: workspace:* + version: link:../../npm/linux-arm64 + '@rslint/linux-x64': + specifier: workspace:* + version: link:../../npm/linux-x64 + '@rslint/win32-arm64': + specifier: workspace:* + version: link:../../npm/win32-arm64 + '@rslint/win32-x64': + specifier: workspace:* + version: link:../../npm/win32-x64 '@types/mocha': specifier: 10.0.9 version: 10.0.9 @@ -538,24 +556,28 @@ packages: engines: {node: '>= 10'} cpu: [arm64] os: [linux] + libc: [glibc] '@node-rs/crc32-linux-arm64-musl@1.10.6': resolution: {integrity: sha512-k8ra/bmg0hwRrIEE8JL1p32WfaN9gDlUUpQRWsbxd1WhjqvXea7kKO6K4DwVxyxlPhBS9Gkb5Urq7Y4mXANzaw==} engines: {node: '>= 10'} cpu: [arm64] os: [linux] + libc: [musl] '@node-rs/crc32-linux-x64-gnu@1.10.6': resolution: {integrity: sha512-IfjtqcuFK7JrSZ9mlAFhb83xgium30PguvRjIMI45C3FJwu18bnLk1oR619IYb/zetQT82MObgmqfKOtgemEKw==} engines: {node: '>= 10'} cpu: [x64] os: [linux] + libc: [glibc] '@node-rs/crc32-linux-x64-musl@1.10.6': resolution: {integrity: sha512-LbFYsA5M9pNunOweSt6uhxenYQF94v3bHDAQRPTQ3rnjn+mK6IC7YTAYoBjvoJP8lVzcvk9hRj8wp4Jyh6Y80g==} engines: {node: '>= 10'} cpu: [x64] os: [linux] + libc: [musl] '@node-rs/crc32-wasm32-wasi@1.10.6': resolution: {integrity: sha512-KaejdLgHMPsRaxnM+OG9L9XdWL2TabNx80HLdsCOoX9BVhEkfh39OeahBo8lBmidylKbLGMQoGfIKDjq0YMStw==} @@ -619,21 +641,25 @@ packages: resolution: {integrity: sha512-ms6uwECUIcu+6e82C5HJhRMHnfsI+l33v7XQezntzRPN0+sG3EpikEoT7SGbgt4vDwaWLR7wS20suN4qd5r3GA==} cpu: [arm64] os: [linux] + libc: [glibc] '@rspack/binding-linux-arm64-musl@1.4.11': resolution: {integrity: sha512-9evq0DOdxMN/H8VM8ZmyY9NSuBgILNVV6ydBfVPMHPx4r1E7JZGpWeKDegZcS5Erw3sS9kVSIxyX78L5PDzzKw==} cpu: [arm64] os: [linux] + libc: [musl] '@rspack/binding-linux-x64-gnu@1.4.11': resolution: {integrity: sha512-bHYFLxPPYBOSaHdQbEoCYGMQ1gOrEWj7Mro/DLfSHZi1a0okcQ2Q1y0i1DczReim3ZhLGNrK7k1IpFXCRbAobQ==} cpu: [x64] os: [linux] + libc: [glibc] '@rspack/binding-linux-x64-musl@1.4.11': resolution: {integrity: sha512-wrm4E7q2k4+cwT6Uhp6hIQ3eUe/YoaUttj6j5TqHYZX6YeLrNPtD9+ne6lQQ17BV8wmm6NZsmoFIJ5xIptpRhQ==} cpu: [x64] os: [linux] + libc: [musl] '@rspack/binding-wasm32-wasi@1.4.11': resolution: {integrity: sha512-hiYxHZjaZ17wQtXyLCK0IdtOvMWreGVTiGsaHCxyeT+SldDG+r16bXNjmlqfZsjlfl1mkAqKz1dg+mMX28OTqw==} diff --git a/scripts/build-npm.mjs b/scripts/build-npm.mjs index da2c10be..b1427695 100644 --- a/scripts/build-npm.mjs +++ b/scripts/build-npm.mjs @@ -1,28 +1,117 @@ #!/usr/bin/env zx -import fs from 'fs'; +import 'zx/globals'; +import { join } from 'node:path'; +import { platform, arch } from 'node:os'; + $.verbose = true; +$.cwd = join(__dirname, '..'); + +/** + * @typedef {Object} PlatformConfig + * @property {string} GOOS - Go operating system + * @property {string} GOARCH - Go architecture + * @property {string} ext - File extension for the binary + */ + +/** + * @typedef {Object} PlatformConfigWithBinPath + * @property {string} GOOS - Go operating system + * @property {string} GOARCH - Go architecture + * @property {string} ext - File extension for the binary + * @property {string} binPath - Full binary path with platform-specific naming + */ + +/** + * @type {Record>} + */ +const platformMap = { + darwin: { + x64: { GOOS: 'darwin', GOARCH: 'amd64', ext: '' }, + arm64: { GOOS: 'darwin', GOARCH: 'arm64', ext: '' }, + }, + linux: { + x64: { GOOS: 'linux', GOARCH: 'amd64', ext: '' }, + arm64: { GOOS: 'linux', GOARCH: 'arm64', ext: '' }, + }, + win32: { + x64: { GOOS: 'windows', GOARCH: 'amd64', ext: '.exe' }, + arm64: { GOOS: 'windows', GOARCH: 'arm64', ext: '.exe' }, + }, +}; -// build binary for following platforms -// # darwin/amd64 -// # darwin/arm64 -// # linux/amd64 -// # linux/arm64 -// # windows/amd64 -async function build_all() { - const platforms = [ - { os: 'darwin', arch: 'amd64', 'node-arch': 'x64' }, - { os: 'darwin', arch: 'arm64', 'node-arch': 'arm64' }, - { os: 'linux', arch: 'amd64', 'node-arch': 'x64' }, - { os: 'linux', arch: 'arm64', 'node-arch': 'arm64' }, - { os: 'windows', arch: 'amd64', 'node-arch': 'x64', 'node-os': 'win32' }, - { os: 'windows', arch: 'arm64', 'node-arch': 'arm64', 'node-os': 'win32' }, - ]; - for (const platform of platforms) { - await $`GOOS=${platform.os} GOARCH=${platform.arch} go build -o npm/${platform['node-os'] || platform.os}-${platform['node-arch']}/rslint ./cmd/rslint`; +/** + * Get platform configuration for building Go binaries + * @param {string} [platform=process.platform()] - The target platform + * @param {string} [arch=os.arch()] - The target architecture + * @returns {PlatformConfigWithBinPath} Platform configuration with binary path + * @throws {Error} When platform/architecture combination is not supported + */ +function getPlatformConfig(platform = platform(), arch = arch()) { + const config = platformMap[platform]?.[arch]; + if (!config) { + throw new Error(`Unsupported platform: ${platform}-${arch}`); } + + const { GOOS, GOARCH, ext } = config; + + return { + GOOS, + GOARCH, + ext, + }; } -async function main() { - await build_all(); + +async function buildForTarget(targetPlatform, targetArch) { + const { GOOS, GOARCH, ext } = getPlatformConfig(targetPlatform, targetArch); + + console.log(`Building for ${GOOS}/${GOARCH}...`); + await $`GOOS=${GOOS} GOARCH=${GOARCH} go build -o npm/${targetPlatform}-${targetArch}/rslint${ext} ./cmd/rslint`; } -main(); +const args = process.argv.slice(2); +const command = args[1]; + +switch (command) { + case 'current': + buildForTarget(platform(), arch()); + break; + + case 'all': + console.log('Building for all supported platforms...'); + await Promise.all( + Object.entries(platformMap).flatMap(([platform, architectures]) => + Object.keys(architectures).map(async arch => { + return buildForTarget(platform, arch).catch(error => { + console.error( + `Failed to build ${platform}-${arch}:`, + error.message, + ); + }); + }), + ), + ); + break; + + case 'darwin-x64': + case 'darwin-arm64': + case 'linux-x64': + case 'linux-arm64': + case 'win32-x64': + case 'win32-arm64': + const [targetPlatform, targetArch] = command.split('-'); + buildForTarget(targetPlatform, targetArch); + break; + + default: + console.log('Usage: node build-platform.js '); + console.log('Commands:'); + console.log(' current - Build for current platform'); + console.log(' all - Build for all platforms'); + console.log(' darwin-x64 - Build for macOS Intel'); + console.log(' darwin-arm64 - Build for macOS Apple Silicon'); + console.log(' linux-x64 - Build for Linux x64'); + console.log(' linux-arm64 - Build for Linux ARM64'); + console.log(' win32-x64 - Build for Windows x64'); + console.log(' win32-arm64 - Build for Windows ARM64'); + process.exit(1); +}