Skip to content

Creating Local Packages

Local Package Structure and Exports

Our monorepo uses the package.json exports field to control how each local package is accessed, ensuring a clean public API and protecting internal files. This is especially important for packages that are published or consumed by other parts of the workspace.

Workspace Organization

  • Workspaces are defined in the root package.json and include:
    • Every directory inside packages/ (e.g., @mmv3/core)
    • Every directory inside tools/ (for deployment and development tools)
    • The assets/ directory (for shared static assets)

Server vs. Client Code

  • Server-only code: Suffix files with .server.ts
  • Web/client-only code: Suffix files with .web.ts
  • Shared code: No suffix needed

When defining exports, make it clear which entry points are safe for all environments and which are server-only. For example:

json
"exports": {
  "./reports": "./src/lib/reports/index.ts",           // Safe for all environments
  "./reports/server": "./src/lib/reports/index.server.ts" // Server-only
}
  • Consumers should use @mmv3/core/reports for universal code and @mmv3/core/reports/server for server-only code.

Why Use the Exports Field?

  • Explicit API: Only files listed in exports are accessible to consumers.
  • Internal Protection: Internal files are not importable from outside the package.
  • Multi-environment Support: You can define different entry points for Node, browser, ESM, CJS, and TypeScript types.

Example: @mmv3/core/package.json

json
"exports": {
  "./utils": "./src/lib/utils/index.ts",
  "./cql": "./src/lib/cql/index.ts",
  "./api": "./src/lib/api/index.ts",
  "./log/server": "./src/lib/log/index.server.ts",
  "./context/server": "./src/lib/context/index.server.ts",
  "./reports": "./src/lib/reports/index.ts",
  "./reports/server": "./src/lib/reports/index.server.ts"
}
  • ./reports is safe for all environments.
  • ./reports/server is only for server-side usage.

When to Worry About Exports

  • If your package is not exporting anything (i.e., not meant to be consumed by others), you don't need to worry about the exports field.
  • If your package is consumed elsewhere, always define the exports field explicitly.