This document describes the conventions used to organise packages in the WE monorepo. Follow these when adding or restructuring packages.
All packages use the @we/ scope.
Multi-package directories use a -system suffix to signal they contain sub-packages:
packages/
├── schema-system/ ← contains @we/schema-shared, @we/schema-solid
├── block-system/ ← contains @we/block-shared, @we/block-solid
├── design-system/ ← contains @we/tokens, @we/primitives, @we/components, ...
Use the shortest unambiguous name within the @we/ scope:
| Package | Name | Rationale |
|---|---|---|
schema-system/shared |
@we/schema-shared |
Needs prefix — "shared" alone is ambiguous |
schema-system/solid |
@we/schema-solid |
Needs prefix — "solid" alone is ambiguous |
design-system/types |
@we/design-types |
Needs prefix — "types" alone is ambiguous |
design-system/1-tokens |
@we/tokens |
Standalone identity — unambiguous without prefix |
models |
@we/models |
Standalone package — WeNode base + entities |
design-system/3-primitives |
@we/primitives |
Standalone identity — unambiguous without prefix |
There are two patterns. The right choice depends on whether the shared code warrants its own package.
Used when the shared layer is substantial and independently useful — it has its own consumers, tests, and dependency graph.
schema-system/
├── shared/ ← @we/schema-shared (own package.json)
│ ├── package.json
│ ├── tsconfig.json
│ ├── tsup.config.ts
│ └── src/
│ ├── index.ts
│ ├── types.ts
│ ├── validators.ts
│ └── ...
└── solid/ ← @we/schema-solid (own package.json)
├── package.json ← depends on @we/schema-shared via workspace:*
├── tsconfig.json
├── tsup.config.ts
└── src/
├── index.ts
└── SchemaRenderer.tsx
Characteristics:
- Separate
package.json,tsconfig.json,tsup.config.tsper sub-package - Each sub-package has its own
src/directory - Framework packages depend on shared via
workspace:* - Each builds and versions independently
- Adding a new framework (e.g. React) means adding a new sibling directory
When to use: The shared code has real substance (types, validators, serialization logic, its own test suite) and could have multiple independent consumers.
Current examples: schema-system/, block-system/
Used when the shared layer is trivial or empty and everything ships as one package.
4-components/
├── package.json ← single @we/components package
├── tsconfig.json
├── tsup.config.ts
└── src/
├── shared/
│ └── types.ts ← minimal or empty
└── solid/
├── index.ts
└── components/
└── ...
Characteristics:
- One
package.jsonat the root - One
src/withshared/andsolid/subdirectories inside - Single build, single version
- Consumers use subpath exports:
@we/components/solid
When to use: The shared code is minimal — real shared types/utils already live in external packages (@we/design-types, @we/design-utils).
Current examples: design-system/4-components/, design-system/5-widgets/
The convention differs by pattern:
- Multi-package directories (Pattern A): framework code lives as a sibling —
schema-system/shared/,schema-system/solid/,schema-system/react/ - Single-package directories (Pattern B): framework code lives under
src/frameworks/—src/shared/,src/frameworks/solid/
The app-framework package is an example of Pattern B with the frameworks/ convention: src/shared/, src/frameworks/solid/.
- Decide which pattern fits (see above)
- For Pattern A: create a new directory under the appropriate
-system/parent, addpackage.json,tsconfig.json,tsup.config.ts - For Pattern B: add a new directory under
src/ - Follow the naming rules for the
@we/scope - Add the workspace glob to
pnpm-workspace.yamlif needed (Pattern A requirespackages/your-system/*) - Add a
README.mdwith package purpose, usage, and API overview