development
Package Structure

Package Structure

This document describes the structure and organization of GridSheet packages.

Monorepo Overview

GridSheet is organized as a monorepo using pnpm workspaces:

gridsheet/
├── packages/
│   ├── core/                # Framework-agnostic core (Sheet, Registry, formula engine, etc.)
│   ├── react-core/          # React implementation (components, hooks, store)
│   ├── preact-core/         # Preact implementation (same codebase, different build target)
│   ├── react-dev/           # Development utilities for React
│   ├── functions/           # Extended formula functions (@gridsheet/functions)
│   ├── vue-core/            # Vue.js implementation (wrapper around preact-core)
│   ├── docs/                # Documentation site
│   └── storybook/           # Component examples and testing
├── e2e/                     # Playwright end-to-end tests
├── package.json             # Root package configuration
├── tsconfig.json            # Root TypeScript configuration
└── README.md                # Project overview

Dependency Graph

@gridsheet/core              ← Framework-agnostic (no React/Preact imports)
  ├── @gridsheet/react-core   ← React components, hooks, store (dep: core)
  │     └── @gridsheet/react-dev (peerDep: core)
  ├── @gridsheet/preact-core  ← Preact build via alias (dep: core)
  │     └── @gridsheet/vue-core (dep: preact-core)
  └── @gridsheet/functions    ← Formula definitions (peerDep: core)

Why @gridsheet/core exists

@gridsheet/functions uses classes like Sheet, Registry, and BaseFunction from the core. Previously, functions depended on @gridsheet/react-core, which forced Preact users to install react-core as well. This resulted in two separate copies of these classes in the bundle, causing instanceof checks to fail (e.g. SUM returning 0).

By extracting framework-agnostic code into @gridsheet/core, all packages share the same class instances regardless of the framework.

External and peerDependencies

When a package marks a dependency as external in its Vite config, the import statement (e.g. from "@gridsheet/core") is preserved in the built output rather than bundled. This means the dependency must exist in the user's node_modules at runtime.

peerDependencies declares this requirement to the package manager. Without it, npm/pnpm would not warn users when the external dependency is missing or has an incompatible version. Therefore, external and peerDependencies are always paired together.

Packageexternal (Vite)peerDependencies
@gridsheet/coredayjsdayjs
@gridsheet/react-corereact, react-dom, @gridsheet/core, dayjsreact, react-dom, dayjs (core is in dependencies)
@gridsheet/preact-corepreact, @gridsheet/core, dayjspreact, dayjs (core is in dependencies)
@gridsheet/functions@gridsheet/core, dayjs@gridsheet/core, dayjs
@gridsheet/react-devreact, react-dom, @gridsheet/corereact, react-dom, @gridsheet/core

Note: @gridsheet/core is listed in dependencies (not peerDependencies) for react-core and preact-core because they always require it. For functions and react-dev, it is a peerDependency because core is expected to be provided by the framework package (react-core or preact-core) that the user already has installed.

Versioning Strategy

Core Packages

  • @gridsheet/core: Framework-agnostic core (Sheet, Registry, formula engine, policy, coords, spatial, etc.)
  • @gridsheet/react-core: React components, hooks, and store
  • @gridsheet/preact-core: Preact build (same codebase via react→preact/compat alias)

These core packages always maintain identical versions following semantic versioning: MAJOR.MINOR.PATCH[-PRERELEASE]

Extended Packages

  • @gridsheet/functions: Extended formula functions library. Provides allFunctions and createSpellbook. Use useSpellbook from @gridsheet/react-core/spellbook for the React hook version.
  • @gridsheet/react-dev: Development utilities for React.

Wrapper Packages

The Vue implementation is a wrapper around the preact-core:

  • @gridsheet/vue-core: Vue.js wrapper around preact-core

When only the wrapper layer is updated, this package uses the format: MAJOR.MINOR.PATCH-X where X starts from 0 and increments as integers.

Version Examples

{
  "name": "@gridsheet/react-core",
  "version": "2.0.0-rc.2"
}
 
{
  "name": "@gridsheet/preact-core", 
  "version": "2.0.0-rc.2"
}
 
{
  "name": "@gridsheet/vue-core",
  "version": "2.0.0-rc.2-0"
}

Git Tagging Strategy

All packages in the monorepo use the following git tag format upon release:

{package-name}/{version}

For example:

  • @gridsheet/react-core/2.0.0-rc.2
  • @gridsheet/vue-core/2.0.0-rc.2-0

Version Bumping Rules

  • Core Updates: Both react-core and preact-core bump together
  • Wrapper Updates: Only vue-core increment the suffix (0, 1, 2, ...)
  • Breaking Changes: All packages bump major version
  • New Features: All packages bump minor version
  • Bug Fixes: All packages bump patch version