npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

react-three-nurbs

v0.4.5

Published

A React component library for NURBS (Non-Uniform Rational B-Spline) curves, surfaces, and solids in Three.js. Built with React Three Fiber, zero external NURBS dependencies — all math implemented from scratch. Boolean operations powered by OpenCASCADE WAS

Readme

react-three-nurbs

A React component library for NURBS (Non-Uniform Rational B-Spline) curves, surfaces, and solids in Three.js. Built with React Three Fiber, zero external NURBS dependencies — all math implemented from scratch. Boolean operations powered by OpenCASCADE WASM.

Buy Me A Coffee

Features

  • 20 components for curves, surfaces, solids, and geometric operations
  • 9 hooks for using NURBS math without rendering
  • Custom NURBS engine — no external math dependencies
  • Solid primitives (box, cylinder, sphere) with B-Rep data model
  • Boolean operations (union, difference, intersection) via OpenCASCADE WASM
  • Full TypeScript support with exported prop types
  • Interactive control point editing with drag handles
  • Point projection using NURBS Book Algorithm A6.1

Installation

npm install react-three-nurbs

Peer dependencies: react, react-dom, three, @react-three/fiber, @react-three/drei

Components

NurbsCurve

Renders a NURBS curve from control points. Knots are auto-generated if omitted.

import { NurbsCurve } from 'react-three-nurbs'

<NurbsCurve
  points={[[0, 0, 0], [1, 1, 0], [2, 0, 0], [3, 1, 0]]}
  degree={3}
  color="red"
  lineWidth={2}
/>

| Prop | Type | Default | Description | |------|------|---------|-------------| | points | number[][] | required | Control points | | degree | number | 3 | Curve degree | | knots | number[] | auto | Knot vector (auto-generated if omitted) | | weights | number[] | [1,...] | Control point weights | | resolution | number | 50 | Sampling resolution | | color | string | 'black' | Line color |

NurbsSurface

Renders a NURBS surface with customizable tessellation and materials.

import { NurbsSurface } from 'react-three-nurbs'
import { DoubleSide } from 'three'

<NurbsSurface
  controlPoints={[
    [[0, 0, 0], [1, 0, 0], [2, 0, 0]],
    [[0, 1, 0], [1, 1, 1], [2, 1, 0]],
    [[0, 2, 0], [1, 2, 0], [2, 2, 0]],
  ]}
  weights={[[1, 1, 1], [1, 1, 1], [1, 1, 1]]}
  degreeU={2}
  degreeV={2}
  resolutionU={30}
  resolutionV={30}
>
  <meshStandardMaterial color="#4488ff" side={DoubleSide} />
</NurbsSurface>

| Prop | Type | Default | Description | |------|------|---------|-------------| | controlPoints | number[][][] | required | 3D control point grid | | weights | number[][] | required | Weight grid | | degreeU / degreeV | number | required | Surface degree | | knotsU / knotsV | number[] | auto | Knot vectors | | resolutionU / resolutionV | number | 20 | Tessellation resolution | | fastNormals | boolean | false | Use fast vertex normals (skip analytical) |

InterpolatedCurve

Creates a smooth curve that passes through a set of points (unlike NurbsCurve which uses control points).

import { InterpolatedCurve } from 'react-three-nurbs'

<InterpolatedCurve
  throughPoints={[[0, 0, 0], [1, 1, 0], [2, 0, 0], [3, 1, 0]]}
  degree={3}
  color="blue"
  lineWidth={2}
/>

InterpolatedSurface

Creates a surface passing through a grid of 3D points.

import { InterpolatedSurface } from 'react-three-nurbs'

<InterpolatedSurface
  points={[
    [[0, 0, 0], [1, 0, 0], [2, 0, 0]],
    [[0, 1, 0], [1, 1, 0.5], [2, 1, 0]],
    [[0, 2, 0], [1, 2, 0], [2, 2, 0]],
  ]}
  degreeU={2}
  degreeV={2}
  resolutionU={30}
  resolutionV={30}
>
  <meshPhongMaterial color="#44ccff" side={DoubleSide} />
</InterpolatedSurface>

LoftedSurface

Creates a surface by lofting through multiple profile curves.

import { LoftedSurface, NurbsCurve } from 'react-three-nurbs'

<LoftedSurface degreeV={3} resolutionU={20} resolutionV={20}>
  <NurbsCurve points={[[0, 0, 0], [1, 0, 0], [2, 0, 0]]} degree={2} />
  <NurbsCurve points={[[0, 1, 1], [1, 1, 0], [2, 1, 1]]} degree={2} />
  <NurbsCurve points={[[0, 2, 0], [1, 2, 0], [2, 2, 0]]} degree={2} />
  <meshStandardMaterial color="#ff0000" side={DoubleSide} />
</LoftedSurface>

RevolvedSurface

Creates a surface of revolution by rotating a profile curve around an axis.

import { RevolvedSurface, NurbsCurve } from 'react-three-nurbs'

<RevolvedSurface
  center={[0, 0, 0]}
  axis={[0, 1, 0]}
  angle={Math.PI * 2}
  resolutionU={30}
  resolutionV={20}
>
  <NurbsCurve points={[[0, 0, 0], [0, 1, 0], [1, 1, 0], [1, 0, 0]]} degree={3} />
  <meshStandardMaterial color="#ff0000" side={DoubleSide} />
</RevolvedSurface>

ExtrudedSurface

Extrudes a profile curve along a direction vector.

import { ExtrudedSurface, NurbsCurve } from 'react-three-nurbs'

<ExtrudedSurface direction={[0, 0, 2]} resolutionU={20} resolutionV={20}>
  <NurbsCurve points={[[0, 0, 0], [0.5, 0.5, 0], [1, 0, 0]]} degree={2} />
  <meshStandardMaterial color="#4488ff" side={DoubleSide} />
</ExtrudedSurface>

SweptSurface

Sweeps a profile curve along a rail curve.

import { SweptSurface, NurbsCurve } from 'react-three-nurbs'

<SweptSurface resolutionU={30} resolutionV={30}>
  {/* First NurbsCurve = profile, Second = rail */}
  <NurbsCurve points={[[0, -0.2, 0], [0.2, 0, 0], [0, 0.2, 0], [-0.2, 0, 0]]} degree={2} />
  <NurbsCurve points={[[0, 0, 0], [1, 1, 0.5], [2, 0, 1], [3, 0, 2]]} degree={3} />
  <meshStandardMaterial color="#44ff88" side={DoubleSide} />
</SweptSurface>

TrimmedSurface

Creates a surface trimmed by one or more curves in UV space.

import { TrimmedSurface, NurbsSurface, NurbsCurve } from 'react-three-nurbs'

<TrimmedSurface trimCurveResolution={200} adaptiveMaxAngleDeg={5}>
  <NurbsSurface controlPoints={...} weights={...} degreeU={2} degreeV={2} />
  <NurbsCurve points={[[0.2, 0.2], [0.8, 0.2], [0.8, 0.8], [0.2, 0.8]]} degree={3} />
  <meshPhongMaterial color="#ff0000" side={DoubleSide} />
</TrimmedSurface>

Set world={true} to use 3D world-space trimming curves (automatically projected onto the surface).

CoonsPatch

Fills a region bounded by 4 boundary curves using bilinear Coons interpolation.

import { CoonsPatch, NurbsCurve } from 'react-three-nurbs'

<CoonsPatch resolutionU={30} resolutionV={30}>
  {/* Order: bottom, top, left, right */}
  <NurbsCurve points={[[0, 0, 0], [1, 0, 0.5], [2, 0, 0]]} degree={2} />
  <NurbsCurve points={[[0, 2, 0], [1, 2, 1], [2, 2, 0]]} degree={2} />
  <NurbsCurve points={[[0, 0, 0], [0, 1, 0.3], [0, 2, 0]]} degree={2} />
  <NurbsCurve points={[[2, 0, 0], [2, 1, 0.5], [2, 2, 0]]} degree={2} />
  <meshPhongMaterial color="#6688cc" side={DoubleSide} />
</CoonsPatch>

NurbsCircle / NurbsArc

Exact NURBS circle and arc primitives (not polygon approximations).

import { NurbsCircle, NurbsArc } from 'react-three-nurbs'

<NurbsCircle center={[0, 0, 0]} radius={1} color="blue" lineWidth={2} />
<NurbsArc center={[0, 0, 0]} radius={1} startAngle={0} endAngle={Math.PI} color="red" />

NurbsEllipse / NurbsEllipseArc

Exact NURBS ellipse and elliptical arc primitives. Semi-axes are defined by the length of the xaxis and yaxis vectors.

import { NurbsEllipse, NurbsEllipseArc } from 'react-three-nurbs'

<NurbsEllipse
  center={[0, 0, 0]}
  xaxis={[2, 0, 0]}
  yaxis={[0, 1, 0]}
  color="blue"
  lineWidth={2}
/>
<NurbsEllipseArc
  center={[0, 0, 0]}
  xaxis={[2, 0, 0]}
  yaxis={[0, 1, 0]}
  startAngle={0}
  endAngle={Math.PI}
  color="red"
/>

CylindricalSurface

Creates a cylindrical NURBS surface from a base circle extruded along an axis.

import { CylindricalSurface } from 'react-three-nurbs'

<CylindricalSurface
  axis={[0, 1, 0]}
  base={[0, 0, 0]}
  height={2}
  radius={0.5}
  resolutionU={30}
  resolutionV={10}
>
  <meshStandardMaterial color="#ff8800" side={DoubleSide} />
</CylindricalSurface>

IsoCurves

Renders iso-parametric curves on a surface for visualization.

import { IsoCurves } from 'react-three-nurbs'
import { useNurbsSurface } from 'react-three-nurbs'

function MyComponent() {
  const { surface } = useNurbsSurface({ controlPoints, weights, degreeU: 2, degreeV: 2 })
  return surface ? <IsoCurves surface={surface} countU={10} countV={10} color="#333" /> : null
}

OffsetCurve

Renders a curve offset by a distance in a plane.

import { OffsetCurve } from 'react-three-nurbs'

<OffsetCurve
  sourcePoints={[[0, 0, 0], [1, 1, 0], [2, 0, 0]]}
  sourceDegree={2}
  distance={0.2}
  color="red"
  lineWidth={2}
/>

SurfaceIntersection

Computes and renders intersection curves between two NurbsSurface children.

import { SurfaceIntersection, NurbsSurface } from 'react-three-nurbs'

<SurfaceIntersection lineColor="#ff0000" lineWidth={4} tolerance={0.001}>
  <NurbsSurface controlPoints={surface1CP} weights={w1} degreeU={2} degreeV={2}>
    <meshPhongMaterial color="#4488ff" transparent opacity={0.4} side={DoubleSide} />
  </NurbsSurface>
  <NurbsSurface controlPoints={surface2CP} weights={w2} degreeU={2} degreeV={2}>
    <meshPhongMaterial color="#44ff88" transparent opacity={0.4} side={DoubleSide} />
  </NurbsSurface>
</SurfaceIntersection>

NurbsSolidComponent

Renders all faces of a NURBS solid as a single merged mesh. Accepts a SolidData object (from NurbsSolid.asData() or useNurbsSolid).

import { NurbsSolidComponent, NurbsSolid } from 'react-three-nurbs'

const box = NurbsSolid.makeBox(2, 1, 1)

<NurbsSolidComponent
  solid={box.asData()}
  resolutionU={20}
  resolutionV={20}
  color="#4488ff"
/>

| Prop | Type | Default | Description | |------|------|---------|-------------| | solid | SolidData | required | Solid data (array of oriented faces) | | resolutionU / resolutionV | number | 20 | Per-face tessellation resolution | | color | string | '#4488ff' | Default material color | | wireframe | boolean | false | Render as wireframe |

BooleanResult

Performs a boolean operation (union, difference, intersection) between two shape descriptors using OpenCASCADE WASM and renders the resulting mesh. The WASM module (~5 MB) is lazy-loaded on first use.

import { BooleanResult } from 'react-three-nurbs'
import type { ShapeDescriptor } from 'react-three-nurbs'

const box: ShapeDescriptor = { type: 'box', dx: 2, dy: 2, dz: 2, origin: [-1, -1, -1] }
const cyl: ShapeDescriptor = { type: 'cylinder', radius: 0.8, height: 3, origin: [0, 0, -1.5] }

<BooleanResult
  shapeA={box}
  shapeB={cyl}
  operation="difference"
  meshDeflection={0.05}
  color="#ff4444"
/>

| Prop | Type | Default | Description | |------|------|---------|-------------| | shapeA | ShapeDescriptor \| null | required | First operand | | shapeB | ShapeDescriptor \| null | required | Second operand | | operation | BooleanOperation | required | 'union' | 'difference' | 'intersection' | | meshDeflection | number | 0.1 | Tessellation accuracy (lower = finer mesh) | | color | string | '#4488ff' | Default material color | | wireframe | boolean | false | Render as wireframe |

The ShapeDescriptor type supports three primitive shapes:

type ShapeDescriptor =
  | { type: 'box'; dx: number; dy: number; dz: number; origin?: [number, number, number] }
  | { type: 'cylinder'; radius: number; height: number; origin?: [number, number, number]; axis?: [number, number, number] }
  | { type: 'sphere'; radius: number; center?: [number, number, number] }

Note: Boolean operations require the OpenCASCADE WASM module (~5 MB), which is lazy-loaded via dynamic import() the first time a boolean operation is invoked. This keeps the base bundle small and avoids loading WASM for applications that only use curves and surfaces.

Hooks

useNurbsCurve

const { curve, points, point, tangent, length, closestParam } = useNurbsCurve({
  points: [[0, 0, 0], [1, 1, 0], [2, 0, 0]],
  degree: 2,
  resolution: 50,
})

// Evaluate at parameter t
const pt = point(0.5)        // Vector3
const tan = tangent(0.5)     // Vector3 (normalized)
const len = length()          // number
const t = closestParam(pt)   // number

useNurbsSurface

const { surface, geometry, point, normal, closestParam } = useNurbsSurface({
  controlPoints, weights, degreeU: 2, degreeV: 2,
  resolutionU: 30, resolutionV: 30,
})

// geometry contains: vertices, normals, uvs, indices (Float32Array/Uint32Array)
const pt = point(0.5, 0.5)         // Vector3
const n = normal(0.5, 0.5)          // Vector3
const [u, v] = closestParam(pt)    // [number, number]

useInterpolatedCurve

const { curve, points, point, tangent } = useInterpolatedCurve({
  throughPoints: [[0, 0, 0], [1, 1, 0], [2, 0, 0]],
  degree: 3,
})

useControlPointDrag

Interactive control point dragging for surfaces and curves.

const [controlPoints, setControlPoints] = useState(initialCP)

const { handles, isDragging, activeIndex, dragBind } = useControlPointDrag({
  controlPoints,
  onControlPointChange: (newPoints) => setControlPoints(newPoints),
  dragPlane: 'screen', // 'xy' | 'xz' | 'yz' | 'screen'
})

return (
  <>
    <NurbsSurface controlPoints={controlPoints} ... />
    {handles.map(handle => (
      <mesh key={handle.index.join('-')} position={handle.position} {...handle.bind}>
        <sphereGeometry args={[0.05]} />
        <meshBasicMaterial color={activeIndex?.join(',') === handle.index.join(',') ? 'red' : 'orange'} />
      </mesh>
    ))}
    {isDragging && (
      <mesh visible={false} {...dragBind}>
        <planeGeometry args={[100, 100]} />
      </mesh>
    )}
  </>
)

useSurfaceIntersection

const { curves } = useSurfaceIntersection({
  surface0: { controlPoints: cp1, weights: w1, degreeU: 2, degreeV: 2 },
  surface1: { controlPoints: cp2, weights: w2, degreeU: 2, degreeV: 2 },
  tolerance: 0.001,
})
// curves: Array<{ points: Vector3[] }>

useOffsetCurve

const { curve, points } = useOffsetCurve({
  curve: sourceNurbsCurve, // from useNurbsCurve
  distance: 0.2,
  planeNormal: [0, 0, 1],
})

useNurbsSolid

Creates a NURBS solid primitive (box, cylinder, sphere, or custom faces) and returns the NurbsSolid instance along with its plain data.

import { useNurbsSolid, NurbsSolidComponent } from 'react-three-nurbs'

function MyBox() {
  const { solid, data, faces } = useNurbsSolid({
    primitive: { type: 'box', dx: 2, dy: 1, dz: 1 },
  })

  return data ? <NurbsSolidComponent solid={data} color="#4488ff" /> : null
}

The primitive option accepts one of:

type SolidPrimitive =
  | { type: 'box'; dx: number; dy: number; dz: number; origin?: [number, number, number] }
  | { type: 'cylinder'; radius: number; height: number; axis?: [number, number, number]; origin?: [number, number, number] }
  | { type: 'sphere'; radius: number; center?: [number, number, number] }
  | { type: 'custom'; faces: FaceData[] }

useBooleanOperation

Performs an async boolean operation between two ShapeDescriptor objects using OpenCASCADE WASM. Returns the tessellated mesh result, a loading flag, and any error.

import { useBooleanOperation } from 'react-three-nurbs'
import type { ShapeDescriptor } from 'react-three-nurbs'

const box: ShapeDescriptor = { type: 'box', dx: 2, dy: 2, dz: 2 }
const sphere: ShapeDescriptor = { type: 'sphere', radius: 1.2 }

function MyBoolean() {
  const { mesh, isComputing, error } = useBooleanOperation({
    shapeA: box,
    shapeB: sphere,
    operation: 'intersection',
    meshDeflection: 0.05,
  })

  if (isComputing) return <mesh><sphereGeometry args={[0.1]} /><meshBasicMaterial color="#888" wireframe /></mesh>
  if (error || !mesh) return null

  // mesh.vertices, mesh.normals, mesh.indices are typed arrays
  // ready to be used with BufferGeometry
}

Utilities

import {
  generateUniformKnots,    // (numControlPoints, degree) => number[]
  computeNormal,            // (surface, u, v) => Vector3
  projectPointToSurfaceUV,  // (surface, point) => [u, v] | null
  projectCurveOntoSurface,  // (surface, curve, samples) => [u, v][]
  computeCoonsPatch,        // (bottom, top, left, right, resU, resV) => BufferGeometry
  curveKnotRefine,          // (curve, knotsToInsert) => NurbsCurve
  surfaceKnotRefine,        // (surface, knotsToInsert, useV) => NurbsSurface
  curveElevateDegree,       // (curve, finalDegree) => NurbsCurve
  unifyCurveKnots,          // (curves) => NurbsCurve[]
  validateControlPoints,    // (cp, degreeU, degreeV) => string | null
  validateKnots,            // (knots, numCP, degree) => string | null
} from 'react-three-nurbs'

Solids

NurbsSolid Primitives

Create solid volumes from NURBS faces:

import { NurbsSolid, NurbsSolidComponent } from 'react-three-nurbs'

const box = NurbsSolid.makeBox(2, 1.5, 1)
const cylinder = NurbsSolid.makeCylinder(0.5, 2)
const sphere = NurbsSolid.makeSphere(1)

<NurbsSolidComponent solid={box.asData()} resolutionU={10} resolutionV={10}>
  <meshPhongMaterial color="#4488ff" side={DoubleSide} />
</NurbsSolidComponent>

Solid Construction from Curves & Surfaces

Build solids from existing curves and surfaces:

import { NurbsSolid, NurbsCurve, createCircle } from 'react-three-nurbs'

// Revolve a profile curve around an axis (like a lathe)
const vaseProfile = NurbsCurve.byKnotsControlPointsWeights(3, knots, profilePoints, weights)
const vase = NurbsSolid.fromRevolution(vaseProfile.asData(), [0,0,0], [0,1,0], Math.PI * 2)

// Extrude a closed curve along a direction (like pushing Play-Doh)
const circle = createCircle([0,0,0], [1,0,0], [0,1,0], 0.8)
const tube = NurbsSolid.fromExtrusion(circle, [0, 0, 2], true) // capped = true

// Thicken a surface into a shell (adds wall thickness)
const shell = NurbsSolid.fromSurface(surfaceData, 0.2)

// Wrap any surface as a solid face for manual composition
const face = NurbsSolid.faceFromSurface(surfaceData, 'forward')
const custom = NurbsSolid.fromFaces([face1, face2, face3])

Both fromRevolution and fromExtrusion accept an optional capped parameter (default false) to close partial revolutions or extrusion ends.

Boolean Operations

Union, difference, and intersection between solids using OpenCASCADE WASM (~5 MB, lazy-loaded on first use):

import { BooleanResult } from 'react-three-nurbs'

<BooleanResult
  shapeA={{ type: "box", dx: 2, dy: 2, dz: 2, origin: [-1, -1, -1] }}
  shapeB={{ type: "cylinder", radius: 0.5, height: 3, origin: [0, 0, -1.5], axis: [0, 0, 1] }}
  operation="difference"
>
  <meshPhongMaterial color="#4488ff" side={DoubleSide} />
</BooleanResult>

Or at the hook level:

import { useBooleanOperation } from 'react-three-nurbs'

const { mesh, isComputing, error } = useBooleanOperation({
  shapeA: { type: "box", dx: 2, dy: 2, dz: 2 },
  shapeB: { type: "sphere", radius: 1 },
  operation: 'difference', // 'union' | 'difference' | 'intersection'
})

Shape descriptors: { type: "box", dx, dy, dz, origin? }, { type: "cylinder", radius, height, axis?, origin? }, { type: "sphere", radius, center? }.

Core NURBS Engine

The library includes a standalone NURBS math engine with no external dependencies:

import { NurbsCurve, NurbsSurface, NurbsSolid } from 'react-three-nurbs'

const curve = NurbsCurve.byKnotsControlPointsWeights(degree, knots, controlPoints, weights)
const point = curve.point(0.5)
const tangent = curve.tangent(0.5)

const surface = NurbsSurface.byKnotsControlPointsWeights(degreeU, degreeV, knotsU, knotsV, cp, w)
const pt = surface.point(0.5, 0.5)
const normal = surface.normal(0.5, 0.5)
const isoCurve = surface.isocurve(0.5, false)

const solid = NurbsSolid.makeBox(2, 1, 3)       // 6 planar faces
const revolved = NurbsSolid.fromRevolution(curveData, center, axis, angle)
const extruded = NurbsSolid.fromExtrusion(curveData, direction, capped)
const shell = NurbsSolid.fromSurface(surfaceData, thickness)
const faces = solid.faces()                      // FaceData[]

Boolean Operations (OpenCASCADE)

Boolean operations (union, difference, intersection) use a custom OpenCASCADE WASM build (~5 MB). The WASM is lazy-loaded via dynamic import() the first time a boolean operation runs, keeping the base bundle small.

import { booleanOperation } from 'react-three-nurbs'
import type { ShapeDescriptor, BooleanMeshResult } from 'react-three-nurbs'

const result: BooleanMeshResult = await booleanOperation(
  { type: 'box', dx: 2, dy: 2, dz: 2 },
  { type: 'cylinder', radius: 0.8, height: 3 },
  'difference',
  0.05 // meshDeflection
)
// result.vertices: Float32Array
// result.indices:  Uint32Array
// result.normals:  Float32Array

WASM loader utilities:

import { getOC, setOC, isOCLoaded } from 'react-three-nurbs'

await getOC()         // Load and return OCCT instance (cached after first call)
setOC(myOCInstance)   // Provide your own OCCT instance
isOCLoaded()          // Check if WASM is ready

Development

npm install       # Install dependencies
npm run dev       # Start Storybook (port 6006)
npm run build     # Build the library
npm run test      # Run tests (vitest)
npm run lint      # Run ESLint

License

MIT

Contributing

Contributions are welcome! Please feel free to submit a Pull Request.