Skip to content

oxc-project/sort-package-json

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

63 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

sort-package-json

Crates.io Docs.rs

MIT licensed Build Status Code Coverage CodSpeed Badge Sponsors Discord chat

A Rust implementation that sorts package.json files according to well-established npm conventions.

Note on Compatibility: This crate is not compatible with the original sort-package-json npm package. While both tools sort package.json files, this Rust implementation uses different sorting groupings that we believe are clearer and easier to navigate. The field order is inspired by both the original sort-package-json and Prettier's package.json sorting, but organized into more intuitive logical groups.

Features

  • Sorts top-level fields according to npm ecosystem conventions (138 predefined fields)
  • Preserves all data - only reorders fields, never modifies values
  • Fast and safe - pure Rust implementation with no unsafe code
  • Idempotent - sorting multiple times produces the same result
  • Handles edge cases - unknown fields sorted alphabetically, private fields (starting with _) sorted last

Usage

Add to your Cargo.toml:

[dependencies]
sort-package-json = "0.0.5"

Library API

use std::fs;

fn main() -> Result<(), Box<dyn std::error::Error>> {
    let contents = fs::read_to_string("package.json")?;
    let sorted = sort_package_json::sort_package_json(&contents)?;
    fs::write("package.json", sorted)?;
    Ok(())
}

With custom options:

use sort_package_json::{sort_package_json_with_options, SortOptions};

let options = SortOptions { pretty: false };
let sorted = sort_package_json_with_options(&contents, &options)?;

Running the Example

To test on a repository, run the included example which recursively finds and sorts all package.json files:

cargo run --example simple [PATH]

If no path is provided, it defaults to the current directory.

Example

Given an unsorted package.json:

{
  "version": "1.0.0",
  "dependencies": { "foo": "1.0.0" },
  "name": "my-package",
  "scripts": { "test": "vitest" }
}

After sorting:

{
  "name": "my-package",
  "version": "1.0.0",
  "scripts": { "test": "vitest" },
  "dependencies": { "foo": "1.0.0" }
}

Field Ordering

Fields are sorted into 12 logical groups, followed by unknown fields alphabetically, then private fields (starting with _) at the end. The complete field order is based on both the original sort-package-json and prettier's package.json sorting implementations.

{
  // 1. Core Package Metadata
  "$schema": "https://json.schemastore.org/package.json",
  "name": "my-package",
  "displayName": "My Package",
  "version": "1.0.0",
  "private": true,
  "description": "A sample package",
  "categories": ["linters", "formatters"],
  "keywords": ["sample", "test"],
  "homepage": "https://example.com",
  "bugs": { "url": "https://github.com/user/repo/issues", "email": "support@example.com" },

  // 2. License & People
  "license": "MIT",
  "author": { "name": "Author", "email": "author@example.com", "url": "https://example.com" },
  "maintainers": [{ "name": "Maintainer", "email": "maintainer@example.com" }],
  "contributors": [{ "name": "Contributor", "email": "contributor@example.com" }],

  // 3. Repository & Funding
  "repository": { "type": "git", "url": "https://github.com/user/repo.git" },
  "funding": { "type": "github", "url": "https://github.com/sponsors/user" },

  // 4. Package Content & Distribution
  "bin": { "my-cli": "./bin/cli.js" },
  "directories": { "lib": "lib", "bin": "bin", "man": "man", "doc": "doc" },
  "workspaces": ["packages/*"],
  "files": ["dist", "lib", "src/index.js"],
  "os": ["darwin", "linux"],
  "cpu": ["x64", "arm64"],

  // 5. Package Entry Points
  "type": "module",
  "sideEffects": false,
  "main": "./dist/index.cjs",
  "module": "./dist/index.mjs",
  "browser": "./dist/browser.js",
  "types": "./dist/index.d.ts",
  "exports": {
    ".": {
      "types": "./dist/index.d.ts",
      "import": "./dist/index.mjs",
      "require": "./dist/index.cjs",
      "default": "./dist/index.mjs",
    },
  },
  "publishConfig": { "access": "public", "registry": "https://registry.npmjs.org" },

  // 6. Scripts
  "scripts": {
    "build": "tsup",
    "test": "vitest",
    "lint": "eslint .",
  },

  // 7. Dependencies
  "dependencies": { "lodash": "^4.17.21" },
  "devDependencies": { "typescript": "^5.0.0", "vitest": "^1.0.0" },
  "peerDependencies": { "react": ">=18.0.0" },
  "peerDependenciesMeta": { "react": { "optional": true } },
  "optionalDependencies": { "fsevents": "^2.3.0" },
  "bundledDependencies": ["internal-lib"],
  "overrides": { "semver": "^7.5.4" },

  // 8. Git Hooks & Commit Tools
  "simple-git-hooks": { "pre-commit": "npx lint-staged" },
  "lint-staged": { "*.ts": ["eslint --fix", "prettier --write"] },
  "commitlint": { "extends": ["@commitlint/config-conventional"] },

  // 9. VSCode Extension Specific
  "contributes": { "commands": [] },
  "activationEvents": ["onLanguage:javascript"],
  "icon": "icon.png",

  // 10. Build & Tool Configuration
  "browserslist": ["> 1%", "last 2 versions"],
  "prettier": { "semi": false, "singleQuote": true },
  "eslintConfig": { "extends": ["eslint:recommended"] },

  // 11. Testing
  "jest": { "testEnvironment": "node" },
  "c8": { "include": ["src/**"] },

  // 12. Runtime & Package Manager
  "engines": { "node": ">=18.0.0" },
  "packageManager": "pnpm@8.0.0",
  "pnpm": { "overrides": {} },

  // Unknown fields (sorted alphabetically)
  "customField": "value",
  "myConfig": {},

  // Private fields (sorted alphabetically, always last)
  "_internal": "hidden",
  "_private": "data",
}

Why Not simd-json?

We use serde_json instead of simd-json because:

  • No preserve_order support - simd-json can't maintain custom field insertion order (required for our sorting)
  • Platform issues - simd-json doesn't work on big-endian architectures (#437)

Development

Building

cargo build --release

Running Tests

cargo test

Tests use snapshot testing via insta. To review and accept snapshot changes:

cargo insta review

Or to accept all changes:

cargo insta accept

Test Coverage

  • Field ordering test - verifies correct sorting of all field types
  • Idempotency test - ensures sorting is stable (sorting twice = sorting once)

License

MIT

References

My sponsors

About

No description, website, or topics provided.

Resources

License

Stars

Watchers

Forks

Contributors 6