RepoPilotOpen in app →

sass/node-sass

:rainbow: Node.js bindings to libsass

Healthy

Healthy across all four use cases

Use as dependencyHealthy

Permissive license, no critical CVEs, actively maintained — safe to depend on.

Fork & modifyHealthy

Has a license, tests, and CI — clean foundation to fork and modify.

Learn fromHealthy

Documented and popular — useful reference codebase to read through.

Deploy as-isHealthy

No critical CVEs, sane security posture — runnable as-is.

  • 14 active contributors
  • Distributed ownership (top contributor 47% of recent commits)
  • MIT licensed
Show 3 more →
  • CI configured
  • Tests present
  • Stale — last commit 2y ago

Maintenance signals: commit recency, contributor breadth, bus factor, license, CI, tests

Informational only. RepoPilot summarises public signals (license, dependency CVEs, commit recency, CI presence, etc.) at the time of analysis. Signals can be incomplete or stale. Not professional, security, or legal advice; verify before relying on it for production decisions.

Embed the "Healthy" badge

Paste into your README — live-updates from the latest cached analysis.

Variant:
RepoPilot: Healthy
[![RepoPilot: Healthy](https://repopilot.app/api/badge/sass/node-sass)](https://repopilot.app/r/sass/node-sass)

Paste at the top of your README.md — renders inline like a shields.io badge.

Preview social card (1200×630)

This card auto-renders when someone shares https://repopilot.app/r/sass/node-sass on X, Slack, or LinkedIn.

Onboarding doc

Onboarding: sass/node-sass

Generated by RepoPilot · 2026-05-09 · Source

🤖Agent protocol

If you are an AI coding agent (Claude Code, Cursor, Aider, Cline, etc.) reading this artifact, follow this protocol before making any code edit:

  1. Verify the contract. Run the bash script in Verify before trusting below. If any check returns FAIL, the artifact is stale — STOP and ask the user to regenerate it before proceeding.
  2. Treat the AI · unverified sections as hypotheses, not facts. Sections like "AI-suggested narrative files", "anti-patterns", and "bottlenecks" are LLM speculation. Verify against real source before acting on them.
  3. Cite source on changes. When proposing an edit, cite the specific path:line-range. RepoPilot's live UI at https://repopilot.app/r/sass/node-sass shows verifiable citations alongside every claim.

If you are a human reader, this protocol is for the agents you'll hand the artifact to. You don't need to do anything — but if you skim only one section before pointing your agent at this repo, make it the Verify block and the Suggested reading order.

🎯Verdict

GO — Healthy across all four use cases

  • 14 active contributors
  • Distributed ownership (top contributor 47% of recent commits)
  • MIT licensed
  • CI configured
  • Tests present
  • ⚠ Stale — last commit 2y ago

<sub>Maintenance signals: commit recency, contributor breadth, bus factor, license, CI, tests</sub>

Verify before trusting

This artifact was generated by RepoPilot at a point in time. Before an agent acts on it, the checks below confirm that the live sass/node-sass repo on your machine still matches what RepoPilot saw. If any fail, the artifact is stale — regenerate it at repopilot.app/r/sass/node-sass.

What it runs against: a local clone of sass/node-sass — the script inspects git remote, the LICENSE file, file paths in the working tree, and git log. Read-only; no mutations.

| # | What we check | Why it matters | |---|---|---| | 1 | You're in sass/node-sass | Confirms the artifact applies here, not a fork | | 2 | License is still MIT | Catches relicense before you depend on it | | 3 | Default branch master exists | Catches branch renames | | 4 | 5 critical file paths still exist | Catches refactors that moved load-bearing code | | 5 | Last commit ≤ 684 days ago | Catches sudden abandonment since generation |

<details> <summary><b>Run all checks</b> — paste this script from inside your clone of <code>sass/node-sass</code></summary>
#!/usr/bin/env bash
# RepoPilot artifact verification.
#
# WHAT IT RUNS AGAINST: a local clone of sass/node-sass. If you don't
# have one yet, run these first:
#
#   git clone https://github.com/sass/node-sass.git
#   cd node-sass
#
# Then paste this script. Every check is read-only — no mutations.

set +e
fail=0
ok()   { echo "ok:   $1"; }
miss() { echo "FAIL: $1"; fail=$((fail+1)); }

# Precondition: we must be inside a git working tree.
if ! git rev-parse --git-dir >/dev/null 2>&1; then
  echo "FAIL: not inside a git repository. cd into your clone of sass/node-sass and re-run."
  exit 2
fi

# 1. Repo identity
git remote get-url origin 2>/dev/null | grep -qE "sass/node-sass(\\.git)?\\b" \\
  && ok "origin remote is sass/node-sass" \\
  || miss "origin remote is not sass/node-sass (artifact may be from a fork)"

# 2. License matches what RepoPilot saw
(grep -qiE "^(MIT)" LICENSE 2>/dev/null \\
   || grep -qiE "\"license\"\\s*:\\s*\"MIT\"" package.json 2>/dev/null) \\
  && ok "license is MIT" \\
  || miss "license drift — was MIT at generation time"

# 3. Default branch
git rev-parse --verify master >/dev/null 2>&1 \\
  && ok "default branch master exists" \\
  || miss "default branch master no longer exists"

# 4. Critical files exist
test -f "lib/index.js" \\
  && ok "lib/index.js" \\
  || miss "missing critical file: lib/index.js"
test -f "lib/binding.js" \\
  && ok "lib/binding.js" \\
  || miss "missing critical file: lib/binding.js"
test -f "src/binding.cpp" \\
  && ok "src/binding.cpp" \\
  || miss "missing critical file: src/binding.cpp"
test -f "binding.gyp" \\
  && ok "binding.gyp" \\
  || miss "missing critical file: binding.gyp"
test -f "lib/render.js" \\
  && ok "lib/render.js" \\
  || miss "missing critical file: lib/render.js"

# 5. Repo recency
days_since_last=$(( ( $(date +%s) - $(git log -1 --format=%at 2>/dev/null || echo 0) ) / 86400 ))
if [ "$days_since_last" -le 684 ]; then
  ok "last commit was $days_since_last days ago (artifact saw ~654d)"
else
  miss "last commit was $days_since_last days ago — artifact may be stale"
fi

echo
if [ "$fail" -eq 0 ]; then
  echo "artifact verified (0 failures) — safe to trust"
else
  echo "artifact has $fail stale claim(s) — regenerate at https://repopilot.app/r/sass/node-sass"
  exit 1
fi

Each check prints ok: or FAIL:. The script exits non-zero if anything failed, so it composes cleanly into agent loops (./verify.sh || regenerate-and-retry).

</details>

TL;DR

node-sass is a Node.js binding to libsass (a C++ implementation of Sass), allowing JavaScript developers to compile SCSS/SASS stylesheets to CSS directly within Node without relying on Ruby or external tools. It provides both a CLI (bin/node-sass) and a programmatic API (lib/index.js) for file watching and real-time style compilation. Hybrid C++/JavaScript structure: lib/ contains the JavaScript API (lib/index.js entry point, lib/binding.js for native bridge, lib/render.js for compilation, lib/watcher.js for file watching), while src/ contains C++ bindings (src/binding.cpp, src/callback_bridge.h, src/create_string.cpp) that wrap libsass 3.5.5. bin/node-sass is the CLI wrapper. Scripts handle build (scripts/build.js, scripts/install.js) and installation logic.

👥Who it's for

Frontend developers and build tool maintainers who need fast Sass compilation in Node.js projects, particularly those building custom build pipelines or build tools that require native Sass compilation as a dependency. Teams migrating away from Ruby-based Sass tooling.

🌱Maturity & risk

node-sass is end-of-life and no longer receiving releases or security fixes (as explicitly stated in the README). While the codebase is stable and production-used in many legacy projects, the project is not actively maintained. It has comprehensive CI/CD across Alpine/Linux/macOS/Windows (via GitHub Actions and AppVeyor), extensive test coverage (.nycrc.json present), and version 9.0.0 supports Node 16+, but no new development occurs.

Critical risk: This is an EOL project with no maintenance. Users are strongly directed to migrate to Dart Sass. Node-version coupling is tight (see binding.gyp and node-gyp dependency for native compilation)—each Node major version release requires CI/tooling updates. Dependency security updates ceased; pinned versions in package.json may contain unfixed vulnerabilities. Single-maintainer risk is extreme given EOL status.

Active areas of work

No active development. The project is in maintenance-only mode (accepting no new features). Last meaningful work was EOL announcement. GitHub Actions workflows (.github/workflows/) exist for CI but are not triggering new releases.

🚀Get running

git clone https://github.com/sass/node-sass.git
cd node-sass
npm install
npm test

Note: This requires Node 16+ and a C++ compiler (build-essential on Linux, Xcode on macOS, Visual Studio on Windows) because scripts/install.js triggers node-gyp compilation of C++ bindings.

Daily commands:

npm run build              # Force rebuild native bindings
npm test                  # Run Mocha test suite
npm run lint              # Run ESLint on bin/lib/scripts/test
npm run coverage          # Generate coverage with nyc
node bin/node-sass input.scss output.css  # CLI usage

🗺️Map of the codebase

  • lib/index.js — Main entry point exposing the render and renderSync APIs; all consumer code starts here.
  • lib/binding.js — Loads native C++ bindings compiled from binding.gyp; critical for bridging JavaScript to libsass.
  • src/binding.cpp — Core C++ glue code that exposes libsass functionality to Node.js; implements render callbacks and value conversion.
  • binding.gyp — Build configuration for node-gyp; defines how native extension is compiled across platforms.
  • lib/render.js — Normalizes render options and orchestrates sync/async rendering with error handling and output formatting.
  • scripts/install.js — Post-install script downloads or builds native binaries; failure here breaks the entire package installation.
  • package.json — Declares libsass version, Node engine requirements, and critical build/install scripts.

🧩Components & responsibilities

  • lib/index.js (Main API) (JavaScript (ES6+)) — Exports render() and renderSync() functions; handles argument validation and delegates to lib/render.js.
    • Failure mode: Invalid options or missing required parameters cause immediate Promise rejection or thrown Error.
  • lib/render.js (Options Orchestration) (JavaScript, async/await, event emitters) — Normalizes render options, manages callback queues, formats output (CSS + source map), invokes error parser.
    • Failure mode: Malformed options or unhandled exceptions in callbacks leak into user promises; async render timeouts not enforced.
  • src/binding.cpp (Native Bridge) (C++11, Node.js N-API, libsass C API) — Converts JS objects to sass_context, invokes libsass render, marshals results and errors back to JavaScript.
    • Failure mode: Segmentation faults if V8 object lifecycle mismanaged; memory leaks if libsass context not properly freed; ABI incompatibility crashes on Node version mismatch.
  • libsass (Core Compiler) (C++98, POSIX file I/O) — Parses SCSS/SASS, resolves imports, executes mixins/functions, generates CSS output; vendored in src/libsass/.
    • Failure mode: Compilation syntax errors reported via error struct; I/O failures (missing imports) propagated to lib/errors.js for formatting.
  • scripts/install.js (Binary Management) (Node.js HTTP, child_process, platform detection) — Post-install hook downloads prebuilt binaries from GitHub releases or triggers local node-gyp build if binary unavailable.
    • Failure mode: Network failures,

🛠️How to make changes

Add a new Sass render option

  1. Define option schema and default in lib/render.js normalizeOptions() (lib/render.js)
  2. Pass option through to C++ binding via binding->Set() in src/binding.cpp (src/binding.cpp)
  3. Consume in libsass C API call (sass_context_set_options or equivalent) (src/libsass/include/sass.h)

Add support for a custom Sass function

  1. Register callback handler in src/custom_function_bridge.cpp invoke() (src/custom_function_bridge.cpp)
  2. Convert JS callback arguments to libsass SassValue types via create_string.cpp (src/create_string.cpp)
  3. Expose registration in lib/render.js via options.functions map (lib/render.js)

Debug a native compilation failure

  1. Reproduce with: npm run install -- --verbose (scripts/install.js)
  2. Check platform-specific compiler flags and includes in binding.gyp (binding.gyp)
  3. Review error logs from GitHub Actions workflow for your OS (.github/workflows/linux.yml)

🔧Why these technologies

  • C++ bindings via node-gyp — libsass is a high-performance C library; native bindings enable 10–100x faster compilation than pure JavaScript Sass implementations.
  • Precompiled binary downloads — Avoids requiring users to have C++ compiler toolchain installed; dramatically improves npm install experience.
  • node-gyp build system — Standard Node.js native module toolchain; supports Windows, macOS, Linux with unified configuration.

⚖️Trade-offs already made

  • End-of-life status; recommend migration to Dart Sass

    • Why: node-sass maintenance burden (platform-specific binaries, compiler versions, Node.js ABI breaks) exceeded benefit as Dart Sass matured.
    • Consequence: Projects using node-sass receive no security updates or new Sass language features; Dart Sass (pure JavaScript, faster startup) is now recommended.
  • Synchronous renderSync API

    • Why: Enables simpler integration in tooling and allows blocking compilation in some build systems.
    • Consequence: Blocks event loop; not suitable for high-concurrency servers. Async render() is preferred but more complex for callback handling.
  • Monolithic libsass compilation into node-sass

    • Why: Simplifies distribution (single binary per platform); users don't install libsass separately.
    • Consequence: Larger node_modules footprint; C++ rebuild on incompatible Node version requires re-download or local compilation.

🚫Non-goals (don't propose these)

  • Real-time watch-mode with live browser injection (watch mode exists but does not handle browser refresh).
  • Support for Sass 4.0+ language features (locked to libsass 3.5.5 which predates Dart Sass feature parity).
  • Concurrent/parallel rendering of multiple files (renderSync blocks; async render uses single libsass context).
  • Cross-platform source map generation in all scenarios (some edge cases on Windows remain unsupported).

🪤Traps & gotchas

Native module compilation required: npm install triggers scripts/install.js → scripts/build.js → node-gyp compile. Needs C++ compiler (not just Node). Platform-specific binaries: If prebuilt binary not available for your Node version + OS, compilation will be attempted locally and can fail if toolchain is missing. Node version lock: The native module uses node-gyp which hardcodes the ABI version; upgrading Node may require rebuild. libsass 3.5.5 is old: Only supports Sass 3 syntax; no support for newer Sass 4+ features like modules (@use/@forward). EOL status impacts security: Dependencies like lodash 4.17.15 may have known vulnerabilities that won't be patched.

🏗️Architecture

💡Concepts to learn

  • Node Native Modules (N-API / node-gyp) — node-sass's core functionality depends on native C++ bindings compiled via node-gyp; understanding how node-gyp compiles binding.gyp and loads the .node module is critical to debugging build failures or modifying bindings
  • libsass (C++ Sass compiler) — node-sass wraps libsass 3.5.5, a C++ port of the original Sass compiler; the entire rendering engine lives in libsass, not in node-sass itself—you are a wrapper, not the implementation
  • Callback Bridges (src/callback_bridge.h) — Enables custom Sass functions registered in JavaScript to be called from the C++ libsass context; complex marshaling between JS and C++ data types occurs here, crucial for understanding custom function support
  • Async Worker Threads — render() is async via libuv work queue (separate thread) to avoid blocking Node's event loop during heavy C++ compilation; renderSync() blocks the main thread—understand this distinction for performance
  • Binary Pre-compilation & Platform Distribution — scripts/install.js downloads precompiled .node binaries from GitHub Releases for your Node version + OS; if not available, it falls back to local compilation—failure at this step blocks the entire install
  • File Watch Graph Dependency Resolution (sass-graph) — lib/watcher.js uses sass-graph to track @import dependencies so that changes to partial files trigger recompilation of files that import them; without it, incremental watch would miss transitive changes
  • ABI (Application Binary Interface) Versioning — Node's native module ABI changes with each major version; node-sass binaries are version-specific (see Node Module column in README); upgrading Node may require rebuilding or redownloading the .node file
  • sass/dart-sass — Official replacement for node-sass; pure Dart implementation with better maintainability and full Sass language support
  • sass-lang/migrator — Tool for migrating from node-sass to Dart Sass; helps users update codebases after end-of-life
  • webpack-contrib/sass-loader — Webpack loader that integrates Sass compilation (uses node-sass or Dart Sass as backend); shows how node-sass is consumed in bundlers
  • postcss/postcss — Complementary CSS transformation tool often chained with node-sass output for additional post-processing
  • nodejs/node-gyp — Direct dependency and build system powering node-sass's native C++ compilation; understanding node-gyp is essential for modifying bindings

🪄PR ideas

To work on one of these in Claude Code or Cursor, paste: Implement the "<title>" PR idea from CLAUDE.md, working through the checklist as the task list.

Add comprehensive test suite for lib/binding.js and src/binding.cpp native bindings

The binding layer between Node.js and libsass is critical but appears to have minimal test coverage. The test/ directory structure is not shown in the file listing, but lib/binding.js and src/binding.cpp handle core functionality (rendering, importer/function callbacks). Adding unit tests for native binding edge cases, error handling, and memory management would catch regressions early and help new contributors understand the Node-to-C++ bridge.

  • [ ] Create test/binding.js with tests for successful render operations via binding.js
  • [ ] Add tests for error propagation from src/binding.cpp to JavaScript (lib/errors.js)
  • [ ] Test custom importer callback invocations (src/custom_importer_bridge.cpp integration)
  • [ ] Test custom function bridge callbacks (src/custom_function_bridge.cpp integration)
  • [ ] Add tests for memory leak scenarios with large files or many render calls
  • [ ] Document findings in TROUBLESHOOTING.md for common binding failures

Add GitHub Actions workflow for Node.js LTS version parity testing

The repo has platform-specific workflows (alpine.yml, linux.yml, macos.yml, windows.yml) but no dedicated workflow that tests against all actively-supported Node.js LTS versions simultaneously. Given the package.json engines constraint (node >= 16) and the stated policy of dropping support at each release per README, a matrix workflow would ensure binary compatibility and catch Node.js API breaking changes early.

  • [ ] Create .github/workflows/node-lts-matrix.yml with Node versions: 16.x, 18.x, 20.x, 22.x
  • [ ] Configure matrix to run npm test, npm run lint, and npm run build across all versions
  • [ ] Add step to verify binary pre-built downloads for each Node version
  • [ ] Configure workflow to fail if any LTS version breaks (given end-of-life policy)
  • [ ] Add workflow dispatch trigger for manual testing when Node.js versions are released

Refactor lib/render.js to separate CLI rendering from programmatic rendering logic

The lib/render.js file likely handles both the command-line interface (bin/node-sass) and the programmatic API. This mixing of concerns makes testing and maintenance harder. Splitting into lib/render-cli.js and lib/render-api.js would clarify the boundary and allow independent testing of each code path, especially important for users who only use the programmatic API.

  • [ ] Analyze lib/render.js to identify CLI-specific logic (file watching, globbing, output formatting)
  • [ ] Create lib/render-api.js with core renderFile, render, and renderSync functions
  • [ ] Create lib/render-cli.js with CLI argument parsing and file I/O orchestration (currently in bin/node-sass)
  • [ ] Update lib/index.js to export both paths appropriately
  • [ ] Move watcher logic from lib/watcher.js integration into lib/render-cli.js
  • [ ] Add tests in test/render-api.js and test/render-cli.js validating each path independently
  • [ ] Update bin/node-sass to require lib/render-cli.js instead of duplicating logic

🌿Good first issues

  • Add unit tests for lib/extensions.js (currently has no dedicated test file visible in test/); write tests covering custom extension registration and resolution
  • Document the memory-tests/ directory with a README explaining what memory-tests/{boolean,function-bridge,map,string}.js are measuring and how to run them; currently undocumented
  • Add integration tests in test/ that verify behavior with Node 16/18/20 binary downloads from scripts/install.js (scripts/util/downloadoptions.js exists but has no tests validating the download logic)

Top contributors

Click to expand

📝Recent commits

Click to expand
  • 107ded9 — Mark end-of-life (nex3)
  • 6081731 — build(deps): bump actions/setup-node from 3 to 4 (dependabot[bot])
  • 62c0f46 — build(deps): bump actions/checkout from 3 to 4 (dependabot[bot])
  • 884547b — build(deps): bump node-gyp from 8.4.1 to 10.0.1 (dependabot[bot])
  • 87f3899 — feat: Node 20 support (#3355) (nschonni)
  • 06ae4c7 — build(deps): bump coverallsapp/github-action from 2.0.0 to 2.1.0 (#3350) (dependabot[bot])
  • e069f73 — build(deps): bump coverallsapp/github-action from 1.2.0 to 2.0.0 (dependabot[bot])
  • c34837d — build(deps): bump coverallsapp/github-action from 1.1.3 to 1.2.0 (dependabot[bot])
  • ee13eb9 — 8.0.0 (xzyfer)
  • 98e75b3 — feat: Node 18 and 19 support and drop Node 17 (#3257) (nschonni)

🔒Security observations

  • Critical · End of Life Project - No Security Updates — README.md, Package.json (project-wide). Node Sass has reached end of life and will receive no more releases, even for security fixes. This means any newly discovered vulnerabilities in node-sass or its dependencies will not be patched. The project is officially deprecated in favor of Dart Sass. Fix: Migrate to Dart Sass (https://sass-lang.com/dart-sass) as soon as possible. Do not use node-sass in new projects or maintain it in existing ones without a migration plan.
  • High · Outdated libsass Version — package.json (libsass: 3.5.5), src/libsass/. The project uses libsass 3.5.5, which is significantly outdated. This C++ library may contain unpatched security vulnerabilities that could be exploited through SCSS/SASS processing. Fix: Update to the latest stable version of libsass. However, given the EOL status, this may not be feasible. Migration to Dart Sass is strongly recommended.
  • High · Binary Download During Installation — scripts/install.js, scripts/build.js, nodeSassConfig.binarySite. The scripts/install.js and scripts/build.js perform downloads of precompiled binaries from GitHub releases during installation. This could be vulnerable to MITM attacks if not properly secured, and the binaries themselves cannot be verified for integrity given the EOL status. Fix: Ensure HTTPS is enforced for binary downloads. Implement checksum verification for downloaded binaries. Consider compiling from source instead. Migrate away from node-sass to reduce this attack surface.
  • Medium · Native Module Compilation Risk — binding.gyp, scripts/build.js, src/binding.cpp. The package uses node-gyp to compile native C++ modules. This introduces complexity and potential security risks from the compilation process itself, especially with outdated libsass. Fix: Keep node-gyp and build tools updated. Run builds in isolated environments. Consider moving to pure JavaScript implementations like Dart Sass.
  • Medium · Lodash Dependency — package.json (dependencies: lodash). The project depends on lodash ^4.17.15. While this version is generally safe, lodash versions below 4.17.21 contain prototype pollution vulnerabilities (CVE-2021-23337). Fix: Update lodash to version ^4.17.21 or higher to address CVE-2021-23337. However, verify that this update is compatible with the rest of the codebase.
  • Medium · Glob Dependency - Potential ReDoS — package.json (dependencies: glob). The project uses glob ^7.0.3, which is outdated. Older versions of glob have known denial of service vulnerabilities through certain pattern matching. Fix: Update glob to the latest stable version (currently ^9.0.0 or ^10.0.0). Review CHANGELOG for security fixes since version 7.
  • Medium · Deprecated get-stdin Dependency — package.json (dependencies: get-stdin). The package depends on get-stdin ^4.0.1, which is an older, less maintained module. Newer alternatives or built-in Node.js features should be preferred. Fix: Replace with process.stdin or readline module. If a third-party package is needed, evaluate more actively maintained alternatives.
  • Low · Missing Prepublish Script — package.json (scripts.prepublishOnly ). The prepublishOnly script in package.json has a trailing space in the key name ('prepublishOnly '), which may cause it not to execute as intended during npm publish operations. Fix: Remove the trailing space from the script key: 'prepublishOnly' instead of 'prepublishOnly '. This ensures the script runs correctly before publishing.
  • Low · Cross-spawn Usage — scripts/, package.json (. The package uses cross-spawn for spawning child processes. While this library is maintained, ensure all spawned processes are properly sanitized and do not accept untrusted input that could lead to command injection. Fix: undefined

LLM-derived; treat as a starting point, not a security audit.


Generated by RepoPilot. Verdict based on maintenance signals — see the live page for receipts. Re-run on a new commit to refresh.