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 🙏

© 2025 – Pkg Stats / Ryan Hefner

@deepracticex/template

v1.1.0

Published

Template package demonstrating Deepractice package development standards

Downloads

19

Readme

@deepracticex/template

Deepractice Package Development Standards Template

This package serves as the standard template for all Deepractice packages. It demonstrates best practices for package structure, code organization, testing, and configuration.

Table of Contents


Quick Start

Creating a New Package

# 1. Copy this template
cp -r packages/template packages/your-package

# 2. Update package.json
cd packages/your-package
# Edit: name, description, keywords

# 3. Install dependencies (from monorepo root)
pnpm install

# 4. Start development
pnpm dev

# 5. Run tests
pnpm test

Architecture Standards

Layered Architecture

All packages MUST follow the three-layer architecture:

src/
├── api/          # Public API - what users import
├── types/        # Type definitions - exported to users
└── core/         # Internal implementation - NOT exported

Layer Responsibilities

api/ - Public API Layer

  • Contains classes, functions, and utilities users directly call
  • Stable interface - changes here affect users
  • Example: Logger, createLogger(), convenience functions

types/ - Type Definition Layer

  • All TypeScript interfaces and types users need
  • Configuration interfaces
  • Public type exports
  • Example: LoggerConfig, LogLevel

core/ - Internal Implementation Layer

  • Implementation details completely hidden from users
  • Can be freely refactored without breaking changes
  • Third-party library adapters
  • Internal utilities
  • Example: Pino adapter, caller tracking logic

Benefits

Clear API Boundary - Users only see what they need ✅ Safe Refactoring - Change core/ without breaking users ✅ Better Testing - E2E test api/, unit test core/Type Safety - Types separated from implementation


Directory Structure

packages/your-package/
├── src/
│   ├── api/                  # Public API
│   │   ├── *.ts              # Implementation files
│   │   └── index.ts          # Unified exports
│   ├── types/                # Type definitions
│   │   ├── *.ts              # Type files
│   │   └── index.ts          # Unified exports
│   ├── core/                 # Internal implementation
│   │   ├── *.ts              # Internal files
│   │   └── index.ts          # Internal exports
│   └── index.ts              # Package entry point
│
├── features/                 # BDD scenarios (Gherkin)
│   └── *.feature             # Feature files
│
├── tests/
│   ├── e2e/
│   │   ├── steps/            # Cucumber step definitions
│   │   │   └── *.steps.ts
│   │   └── support/          # Test support files
│   │       ├── world.ts      # Shared test context
│   │       └── hooks.ts      # Setup/teardown
│   └── unit/                 # Unit tests (optional)
│
├── dist/                     # Build output (gitignored)
├── tsconfig.json             # TypeScript configuration
├── tsup.config.ts            # Build configuration
├── cucumber.cjs              # Cucumber configuration
├── package.json              # Package manifest
└── README.md                 # Package documentation

Coding Standards

1. Path Aliases

Two aliases for different contexts:

// ✅ src/ internal - use ~ alias
// src/api/example.ts
import type { ExampleConfig } from "~/types/config";
import { Processor } from "~/core/processor";

// ✅ tests/ access src/ - use @ alias
// tests/e2e/steps/example.steps.ts
import { createExample } from "@/index";

// tests/unit/core/processor.test.ts
import { Processor } from "@/core/processor";

// ❌ Wrong - relative paths
import { Example } from "../../../src/api/example";

Alias Convention:

  • ~/* - Internal use within src/ (~ means "home/internal")
  • @/* - External access from tests/ (@ means "external reference")

Configuration:

  • tsconfig.json: baseUrl: "." + paths for both aliases
  • tsup.config.ts: esbuildOptions.alias for build (only ~ needed)
  • tsx: Native support for tsconfig paths
  • Uses moduleResolution: "Bundler" - no .js extension needed

2. Naming Conventions

Interface-First Naming (NOT Hungarian notation):

// ✅ Correct - interface gets the clean name
export interface Logger {}
export class DefaultLogger implements Logger {}
export class PinoLogger implements Logger {}

// ❌ Wrong - Hungarian notation
export interface ILogger {}
export class Logger implements ILogger {}

Principles:

  • Interface = simple, clean name (e.g., Logger)
  • Implementation = descriptive name (e.g., DefaultLogger, PinoLogger)
  • Avoid prefixes like I, T, E

3. File Organization

One file, one type:

// example.ts - one class per file
export class Example {}

// config.ts - related types can group
export interface Config {}
export type ConfigOption = "a" | "b";

Index files for exports:

// api/index.ts - unified public API
export { Example, createExample } from "~/api/example";
export { Helper } from "~/api/helper";

// types/index.ts - unified types
export type { Config } from "~/types/config";
export type { Result } from "~/types/result";

4. Export Strategy

Main src/index.ts:

// Export public API
export * from "~/api/index";

// Export types separately to avoid duplication
export type { Config, Result } from "~/types/index";

// Default export (optional)
import { createExample } from "~/api/example";
export default createExample();

DO NOT export core/ from package:

// ✅ Correct - only api and types
export * from "~/api/index";
export type * from "~/types/index";

// ❌ Wrong - exposing internal implementation
export * from "~/core/index"; // NEVER do this

Testing Standards

BDD with Cucumber

Feature files (features/*.feature):

Feature: Example Functionality
  As a developer
  I want to use the Example API
  So that I can process data

  Rule: Example should process correctly

    Scenario: Process simple input
      Given I have created an Example instance
      When I execute with input "hello"
      Then the result should be "Processed: hello"

Step definitions (tests/e2e/steps/*.steps.ts):

import { Given, When, Then } from "@cucumber/cucumber";
import { expect } from "chai";
import { createExample } from "../../../src/index";

Given("I have created an Example instance", function () {
  this.example = createExample();
});

When("I execute with input {string}", async function (input: string) {
  this.result = await this.example.execute(input);
});

Then("the result should be {string}", function (expected: string) {
  expect(this.result).to.equal(expected);
});

Test scripts:

{
  "scripts": {
    "test": "NODE_OPTIONS='--import tsx' cucumber-js",
    "test:dev": "NODE_OPTIONS='--import tsx' cucumber-js --profile dev",
    "test:ci": "NODE_OPTIONS='--import tsx' cucumber-js --profile ci"
  }
}

Key points:

  • Use tsx instead of ts-node (native tsconfig paths support)
  • Load via NODE_OPTIONS='--import tsx'
  • BDD describes behavior in business language
  • E2E tests verify the public API works

Configuration Standards

1. TypeScript Configuration

tsconfig.json:

{
  "extends": "../typescript-config/base.json",
  "compilerOptions": {
    "outDir": "./dist",
    "rootDir": "./src",
    "types": [],
    "baseUrl": "./src",
    "paths": {
      "~/*": ["./*"]
    }
  },
  "include": ["src/**/*.ts"],
  "exclude": ["node_modules", "dist"]
}

Key settings:

  • moduleResolution: "Bundler" (from base.json) - no .js extension
  • baseUrl + paths for ~ alias
  • Strict mode enabled

2. Build Configuration

tsup.config.ts:

import { defineConfig } from "tsup";
import path from "path";

export default defineConfig({
  entry: ["src/index.ts"],
  format: ["cjs", "esm"], // Dual format
  dts: true, // Generate .d.ts
  splitting: false,
  sourcemap: true,
  clean: true,
  esbuildOptions(options) {
    options.alias = {
      "~": path.resolve(__dirname, "./src"),
    };
  },
});

Output:

  • dist/index.js - ESM
  • dist/index.cjs - CommonJS
  • dist/index.d.ts - TypeScript definitions

3. Package Configuration

package.json essentials:

{
  "type": "module",
  "main": "./dist/index.js",
  "module": "./dist/index.js",
  "types": "./dist/index.d.ts",
  "exports": {
    ".": {
      "types": "./dist/index.d.ts",
      "import": "./dist/index.js",
      "require": "./dist/index.cjs"
    }
  },
  "files": ["dist", "package.json", "README.md"]
}

Dependency Management:

  • All tooling dependencies are managed in the root package.json
  • Sub-packages do NOT declare devDependencies
  • Dependencies are automatically available via pnpm workspace hoisting
  • This ensures absolute version consistency across all packages

Available tools (auto-hoisted from root):

  • TypeScript, tsup, tsx, rimraf
  • Cucumber, Vitest, Chai
  • Prettier, Lefthook
  • All @deepracticex/* config packages

When creating a new package:

  1. Copy the template
  2. Update name, description, keywords
  3. Run pnpm install from root
  4. Start coding - all tools are ready!

Publishing Standards

Before Publishing

  1. Build the package:

    pnpm build
  2. Run tests:

    pnpm test
  3. Type check:

    pnpm typecheck
  4. Verify exports:

    # Check dist/ contains expected files
    ls -la dist/

Version Management

Use Changesets for version management:

# 1. Create changeset
pnpm changeset

# 2. Version packages (CI will do this)
pnpm changeset version

# 3. Publish (CI will do this)
pnpm changeset publish

Best Practices

DO ✅

  • Use layered architecture (api/types/core)
  • Use ~ path alias for internal imports
  • Use interface-first naming
  • Write BDD scenarios before implementation
  • Export only public API and types
  • Keep core/ completely internal
  • Use tsx for testing
  • Generate proper TypeScript definitions

DON'T ❌

  • Export from core/ layer
  • Use Hungarian notation (ILogger)
  • Use relative paths for internal imports
  • Skip BDD scenarios
  • Include .js extensions in imports (with Bundler resolution)
  • Use ts-node (use tsx instead)
  • Forget to configure path aliases in tsup

Common Tasks

Add a new public function

  1. Implement in core/ if complex logic
  2. Create wrapper in api/
  3. Export from api/index.ts
  4. Export from src/index.ts
  5. Add BDD scenario in features/
  6. Implement step definition in tests/e2e/steps/

Add a new type

  1. Define in types/*.ts
  2. Export from types/index.ts
  3. Export from src/index.ts as type-only

Refactor internal code

  1. Change anything in core/ freely
  2. Keep api/ interface stable
  3. Run tests to ensure no breakage

Questions?

This template embodies Deepractice package development standards. If you have questions or suggestions for improvements, please discuss with the team.

Key Principle: Make it easy to do the right thing.


Last updated: 2025-10-08