diff --git a/.github/workflows/release_cli.yaml b/.github/workflows/release_cli.yaml index 24f59adf4d818..5f547ee122103 100644 --- a/.github/workflows/release_cli.yaml +++ b/.github/workflows/release_cli.yaml @@ -127,17 +127,42 @@ jobs: steps: - uses: actions/checkout@v3 + - name: Install Node.js + uses: actions/setup-node@v3 + with: + node-version: 16 + registry-url: 'https://registry.npmjs.org' + - name: Download Artifacts uses: actions/download-artifact@v3 with: name: binaries - path: binaries + + - name: Unzip + uses: montudor/action-zip@v1 + with: + args: unzip -qq *.zip -d . + + - name: Untar + run: ls *.gz | xargs -i tar xf {} + + - name: Generate npm packages + run: | + node npm/oxc/scripts/generate-packages.mjs + cat npm/oxc/package.json + for package in npm/*; do cat $package/package.json ; echo ; done + + - name: Publish npm packages as latest + # NOTE: The trailing slash on $package/ changes it to publishing the directory + run: for package in npm/*; do npm publish $package/ --tag latest --access public; done + env: + NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }} - name: Create GitHub Release uses: softprops/action-gh-release@v1 with: name: CLI v${{ needs.build.outputs.version }} draft: true - files: binaries/* + files: oxc-* fail_on_unmatched_files: true generate_release_notes: true diff --git a/.gitignore b/.gitignore index d2e859a8e488b..8f5060308facc 100644 --- a/.gitignore +++ b/.gitignore @@ -6,6 +6,7 @@ **/*.rs.bk node_modules/ +npm/cli-* *.js *.jsx diff --git a/npm/oxc/bin/oxc b/npm/oxc/bin/oxc new file mode 100755 index 0000000000000..e6d3e4a6a4f81 --- /dev/null +++ b/npm/oxc/bin/oxc @@ -0,0 +1,67 @@ +#!/usr/bin/env node +const { platform, arch, env, version, release } = process; + +const PLATFORMS = { + win32: { + x64: "@oxidation-compiler/win32-x64/oxc.exe", + arm64: "@oxidation-compiler/win32-arm64/oxc.exe", + }, + darwin: { + x64: "@oxidation-compiler/darwin-x64/oxc", + arm64: "@oxidation-compiler/darwin-arm64/oxc", + }, + linux: { + x64: "@oxidation-compiler/linux-x64/oxc", + arm64: "@oxidation-compiler/linux-arm64/oxc", + }, +}; + +const binPath = PLATFORMS?.[platform]?.[arch]; +if (binPath) { + const result = require("child_process").spawnSync( + require.resolve(binPath), + process.argv.slice(2), + { + shell: false, + stdio: "inherit", + env: { + ...env, + JS_RUNTIME_VERSION: version, + JS_RUNTIME_NAME: release.name, + NODE_PACKAGE_MANAGER: detectPackageManager(), + }, + } + ); + + if (result.error) { + throw result.error; + } + + process.exitCode = result.status; +} else { + console.error( + "The Oxc CLI package doesn't ship with prebuilt binaries for your platform yet. " + + "You can create an issue at https://github.com/Boshen/oxc/issues for support." + ); + process.exitCode = 1; +} + +/** + * NPM, Yarn, and other package manager set the `npm_config_user_agent`. It has the following format: + * + * ``` + * "npm/8.3.0 node/v16.13.2 win32 x64 workspaces/false + * ``` + * + * @returns The package manager string (`npm/8.3.0`) or null if the user agent string isn't set. + */ +function detectPackageManager() { + const userAgent = env.npm_config_user_agent; + + if (userAgent == null) { + return null; + } + + return userAgent.split(" ")[0]; +} + diff --git a/npm/oxc/package.json b/npm/oxc/package.json index dc6f0c3963f2e..e9b6fa6cb3f61 100644 --- a/npm/oxc/package.json +++ b/npm/oxc/package.json @@ -1,9 +1,22 @@ { - "name": "oxc", - "version": "0.0.0-alpha.4", + "name": "oxidation-compiler", + "version": "0.0.0-alpha.5", "description": "The JavaScript Oxidation Compiler", - "main": "index.js", "keywords": [], "author": "Boshen and oxc contributors", - "license": "MIT" + "license": "MIT", + "homepage": "https://github.com/Boshen/oxc", + "bugs": "https://github.com/Boshen/oxc/issues", + "repository": { + "type": "git", + "url": "https://github.com/Boshen/oxc", + "directory": "npm/oxc" + }, + "bin": "bin/oxc", + "engines": { + "node": ">=14.*" + }, + "files": [ + "bin/oxc" + ] } diff --git a/npm/oxc/scripts/generate-packages.mjs b/npm/oxc/scripts/generate-packages.mjs new file mode 100644 index 0000000000000..2bc16fdef04a4 --- /dev/null +++ b/npm/oxc/scripts/generate-packages.mjs @@ -0,0 +1,103 @@ +// Code copied from [Rome](https://github.com/rome/tools/blob/main/npm/rome/scripts/generate-packages.mjs) + +import { resolve } from "node:path"; +import { fileURLToPath } from "node:url"; +import * as fs from "node:fs"; + +const OXCCLI_ROOT = resolve(fileURLToPath(import.meta.url), "../.."); +const PACKAGES_ROOT = resolve(OXCCLI_ROOT, ".."); +const REPO_ROOT = resolve(PACKAGES_ROOT, ".."); +const MANIFEST_PATH = resolve(OXCCLI_ROOT, "package.json"); + +const rootManifest = JSON.parse( + fs.readFileSync(MANIFEST_PATH).toString("utf-8") +); + +function generateNativePackage(platform, arch) { + const packageName = `@oxidation-compiler/${platform}-${arch}`; + const packageRoot = resolve(PACKAGES_ROOT, `${platform}-${arch}`); + + // Remove the directory just in case it already exists (it's autogenerated + // so there shouldn't be anything important there anyway) + fs.rmSync(packageRoot, { recursive: true, force: true }); + + // Create the package directory + console.log(`Create directory ${packageRoot}`); + fs.mkdirSync(packageRoot); + + // Generate the package.json manifest + const { version } = rootManifest; + + const manifest = JSON.stringify({ + name: packageName, + version, + os: [platform], + cpu: [arch], + }); + + const manifestPath = resolve(packageRoot, "package.json"); + console.log(`Create manifest ${manifestPath}`); + fs.writeFileSync(manifestPath, manifest); + + // Copy the CLI binary + const ext = platform === "win32" ? ".exe" : ""; + const binarySource = resolve(REPO_ROOT, `oxc-${platform}-${arch}${ext}`); + const binaryTarget = resolve(packageRoot, `oxc${ext}`); + + console.log(`Copy binary ${binaryTarget}`); + fs.copyFileSync(binarySource, binaryTarget); + fs.chmodSync(binaryTarget, 0o755); +} + +function updateWasmPackage(target) { + const packageName = `@oxidation-compiler/wasm-${target}`; + const packageRoot = resolve(PACKAGES_ROOT, `wasm-${target}`); + + const manifestPath = resolve(packageRoot, "package.json"); + const manifest = JSON.parse(fs.readFileSync(manifestPath).toString("utf-8")); + + const { version } = rootManifest; + manifest["name"] = packageName; + manifest["version"] = version; + + console.log(`Update manifest ${manifestPath}`); + fs.writeFileSync(manifestPath, JSON.stringify(manifest)); +} + +function writeManifest(packagePath) { + const manifestPath = resolve(PACKAGES_ROOT, packagePath, "package.json"); + + const manifestData = JSON.parse( + fs.readFileSync(manifestPath).toString("utf-8") + ); + + const nativePackages = PLATFORMS.flatMap((platform) => + ARCHITECTURES.map((arch) => [ + `@oxidation-compiler/${platform}-${arch}`, + rootManifest.version, + ]) + ); + + manifestData["version"] = rootManifest.version; + manifestData["optionalDependencies"] = Object.fromEntries(nativePackages); + + console.log(`Update manifest ${manifestPath}`); + const content = JSON.stringify(manifestData); + fs.writeFileSync(manifestPath, content); +} + +const PLATFORMS = ["win32", "darwin", "linux"]; +const ARCHITECTURES = ["x64", "arm64"]; +const WASM_TARGETS = []; + +for (const target of WASM_TARGETS) { + updateWasmPackage(target); +} + +for (const platform of PLATFORMS) { + for (const arch of ARCHITECTURES) { + generateNativePackage(platform, arch); + } +} + +writeManifest("oxc");