Force resolution (edit package.json)
May 12, 2026 · View on GitHub
Development Guide
Complete guide for setting up and contributing to templjs.
Quick Start
Get from clone to first commit in under 15 minutes:
# 1. Clone the repository
git clone https://github.com/yourusername/templjs.git
cd templjs
# 2. Activate the pinned package manager and install dependencies
corepack enable
corepack prepare pnpm@8.15.0 --activate
pnpm install
# 3. Run tests to verify setup
pnpm test
# 4. Build all packages
pnpm build
# 5. Make a change and commit
git checkout -b feature/my-feature
# ... make changes ...
pnpm test && pnpm lint
git add .
git commit -m "feat: my awesome feature"
Prerequisites
- Node.js: 22.12+ or 24.x
- pnpm: 8.15.0
- Git: 2.x or later
- VS Code (recommended): Latest stable version
Installing Prerequisites
Node.js
# Using nvm (recommended)
nvm install 24
nvm use 24
# Or download from https://nodejs.org/
pnpm
corepack enable
corepack prepare pnpm@8.15.0 --activate
Installation
First-Time Setup
# Activate the pinned package manager
corepack enable
corepack prepare pnpm@8.15.0 --activate
# Install all dependencies
pnpm install
# Set up Git hooks
pnpm prepare
# Verify installation
pnpm test
pnpm lint
pnpm build
This installs:
- Package dependencies across the monorepo
- Husky pre-commit hooks
- Nx build system
- Development tools (ESLint, Prettier, TypeScript)
Development Workflow
Running Tests
# Run all tests
pnpm test
# Run coverage for affected projects (CI equivalent)
pnpm run test:affected:ci
# Run package-level coverage
pnpm --filter @templjs/core test:coverage
pnpm --filter @templjs/cli test:coverage
pnpm --filter @templjs/volar test:coverage
# Run tests in watch mode (in a package directory)
cd src/packages/core
pnpm test --watch
# Run tests for affected packages only
pnpm nx affected -t test --base=main
Coverage Requirements
- Coverage thresholds are enforced per package via each
vitest.config.ts. - The CI path uses
pnpm run test:affected:ci, which runs affected tests with--coverageenabled. - To inspect local reports, run a package
test:coveragescript and open the generatedcoverage/output for that package. - If a legitimate threshold adjustment is required, propose it in a work item and include justification + impacted package scope.
Running Linters
# Lint all packages
pnpm lint
# Lint with auto-fix
pnpm lint:fix
# Lint root configuration only
pnpm lint:root
# Format all files
pnpm format
# Check formatting without changes
pnpm format:check
Building Packages
# Build all packages
pnpm build
# Build affected packages only
pnpm nx affected -t build
# Build specific package
pnpm nx build @templjs/core
# Build with dependencies
pnpm nx build @templjs/cli --with-deps
Visualizing Dependencies
# Open interactive dependency graph
pnpm graph
# Show affected projects
pnpm nx affected:graph
Pre-Commit Hooks
Husky runs these checks automatically before each commit:
- Lint-staged: Formats and lints changed files
- Commitlint: Validates commit message format (conventional commits)
- Repo hook runner: Executes the current repo-defined pre-commit flow from
scripts/ci/hook-runner.ts
Pre-Push Build Parity
Husky runs merge-gating parity checks before each push via scripts/ci/hook-runner.ts.
The pre-push flow includes:
ci:toolchain(fail-fast Node/pnpm version guard)lint:frontmatterlint:eslint:pre-pushtest:affected:pre-pushbuild:affected:pre-push(alias ofbuild:affected:local)type-check
This keeps local pre-push behavior aligned with CI by using the same affected-build strategy in both paths. Unsupported local runtimes are treated as merge-gating parity failures and must be corrected before push.
Commit Message Format
We use Conventional Commits:
<type>(<scope>): <subject>
<body>
<footer>
Types:
feat: New featurefix: Bug fixdocs: Documentation changeschore: Maintenance taskstest: Test changesrefactor: Code refactoringperf: Performance improvementsci: CI/CD changes
Examples:
feat(core): add support for custom delimiters
fix(parser): handle nested expressions correctly
docs: update README with installation steps
chore(cli): upgrade chevrotain to v11
test(lexer): add edge case for empty templates
Common Tasks
Adding a New Feature
-
Create a work item (if not exists):
# Work items live in backlog/ # Follow naming: NNN_description.md # Use frontmatter schema from schemas/frontmatter/by-type/work-item/latest.json -
Create a feature branch:
git checkout -b feature/005-new-feature -
Implement with tests:
# Write tests first (TDD) cd src/packages/core # Edit src/__tests__/feature.test.ts # Implement feature # Edit src/feature.ts # Run tests pnpm test -
Commit changes:
git add . git commit -m "feat(core): implement new feature - Add Feature class with X functionality - Add comprehensive tests (95% coverage) - Update documentation Work Item: [[005_new_feature.md]] Status: testing" -
Push and create PR:
git push origin feature/005-new-feature gh pr create --fill
Parser and Semantic-Binding Rule of Thumb
When implementing language features:
- Keep parser logic responsible for syntax only (structure, precedence, nesting, and ranges).
- Express binding and scope behavior as declarative mappings from AST node kinds.
- Use imperative binder code only when runtime data is required (for example schema-derived symbol sets, iterable coercion, or host offset translation).
- Keep local variable bindings distinct from schema contract references.
Adding a Built-In Function
Example: Adding a capitalize filter function
-
Update lexer (
src/packages/core/src/lexer/):// Add CAPITALIZE token if needed -
Update parser (
src/packages/core/src/parser/):// Add capitalize production rule -
Update renderer (
src/packages/core/src/renderer/):// Implement capitalize function export function capitalize(str: string): string { return str.charAt(0).toUpperCase() + str.slice(1); } -
Add tests (
src/packages/core/src/orsrc/packages/core/test/):describe('capitalize filter', () => { it('capitalizes first letter', () => { expect(capitalize('hello')).toBe('Hello'); }); }); -
Update documentation (docs/):
## capitalize Capitalizes the first letter of a string. ```templ {{ name | capitalize }} ```
Updating Documentation
- Format: All docs use Markdown with frontmatter
- Location:
- Architecture decisions:
docs/adr/ - Guides:
docs/ - Work items:
backlog/
- Architecture decisions:
- Schema: Validate frontmatter against schemas in
schemas/frontmatter/ - Linting: Run
pnpm lintto check markdown formatting
Creating a Pull Request
-
Ensure tests pass:
pnpm test pnpm lint pnpm build -
Update work item:
--- status: testing --- -
Create PR:
gh pr create --title "feat(core): implement feature X" \ --body "$(cat backlog/005_feature_x.md)" -
Link work item: Add
Work Item: [[005_feature_x.md]]to PR description
Releasing a New Version
See release-process.md for the complete release process.
Quick version:
# 1. Create changeset
pnpm changeset
# 2. Commit changeset
git add .changeset/
git commit -m "chore: add changeset for vX.Y.Z"
# 3. Merge to main triggers release workflow
Troubleshooting
Husky Blocks Commits
Problem: Pre-commit hook fails and prevents commit.
Recommended workflow:
# Fix the actual issue
pnpm lint:fix
pnpm test
Test Timeouts
Problem: Tests hang or timeout in CI.
Solutions:
# Increase timeout in vitest.config.ts
export default defineConfig({
test: {
testTimeout: 30000, // 30 seconds
},
});
# Run specific test file
pnpm test src/__tests__/specific.test.ts
# Check for infinite loops or unresolved promises
Coverage Drops
Problem: Codecov reports coverage decrease.
Solutions:
# Generate coverage report locally
pnpm test:coverage
# View HTML report
open coverage/index.html
# Add tests for uncovered lines
# Run specific package coverage
cd src/packages/core
pnpm test:coverage
Nx Cache Issues
Problem: Nx reports stale build outputs.
Solutions:
# Clear Nx cache
pnpm nx reset
# Clear all caches
rm -rf .nx/cache
# Clear node_modules and reinstall
pnpm clean
pnpm install
Dependency Conflicts
Problem: pnpm install fails with peer dependency errors.
Solutions:
# Update lockfile
pnpm install --no-frozen-lockfile
# Force resolution (edit package.json)
{
"pnpm": {
"overrides": {
"problematic-dep": "^1.0.0"
}
}
}
# Remove lockfile and reinstall
rm pnpm-lock.yaml
pnpm install
TypeScript Errors in VS Code
Problem: VS Code shows TypeScript errors that don't exist in CLI.
Solutions:
# Restart TypeScript server
# CMD+Shift+P -> "TypeScript: Restart TS Server"
# Rebuild packages
pnpm build
# Check from CLI
pnpm type-check
IDE Setup
VS Code (Recommended)
Required Extensions
Install from VS Code marketplace:
Recommended Extensions
- Nx Console - Nx integration
- GitLens - Git integration
- Error Lens - Inline diagnostics
- Todo Tree - TODO tracking
Workspace Settings
Already configured in .vscode/settings.json:
{
"editor.formatOnSave": true,
"editor.defaultFormatter": "esbenp.prettier-vscode",
"editor.codeActionsOnSave": {
"source.fixAll.eslint": true
},
"eslint.workingDirectories": [{ "mode": "auto" }]
}
Other IDEs
WebStorm
- Enable ESLint: Preferences → Languages & Frameworks → JavaScript → Code Quality Tools → ESLint
- Enable Prettier: Preferences → Languages & Frameworks → JavaScript → Prettier
- Set up Node interpreter: Preferences → Languages & Frameworks → Node.js
Development Tools
CLI Commands Reference
# Nx commands
pnpm nx <target> <project> # Run target for project
pnpm nx run-many -t <target> # Run target for all projects
pnpm nx affected -t <target> # Run target for affected projects
pnpm nx graph # Visualize dependency graph
pnpm nx reset # Clear Nx cache
# Testing
pnpm test # Run all tests
pnpm test:coverage # Run with coverage when available in package scope
pnpm --filter @templjs/core test # Test specific package
# Linting & Formatting
pnpm lint # Lint all packages
pnpm lint:fix # Lint and auto-fix
pnpm format # Format all files
pnpm format:check # Check formatting
# Building
pnpm build # Build all packages
pnpm clean # Clean all build outputs
Changesets & Version Management
Overview
This monorepo uses Changesets for automated version management. The four npm packages (@templjs/core, @templjs/cli, @templjs/volar, @templjs/context-graph) are configured with fixed versioning—they must always release with the same version number. The VS Code extension (vscode-templjs) is versioned independently.
Why Fixed Versioning?
- Coordinated releases: all packages ship v1.0.0, not v1.0.0, v0.9.5, v1.1.0
- Simplified user experience: one version to track
- Clearer dependency management across the monorepo
Configuration
{
"fixed": [["@templjs/core", "@templjs/cli", "@templjs/volar", "@templjs/context-graph"]],
"updateInternalDependencies": "patch"
}
Proper Workflow
For Contributors (All Changes)
# 1. Make your code changes
git checkout -b feature/my-feature
# ... edit files ...
# 2. When ready for PR, create a changeset
pnpm changeset
# You'll be prompted to:
# - Select changed packages (Ctrl+Space to toggle, Enter to submit)
# - Choose bump type: patch | minor | major
# - Write a brief changelog entry
Example:
$ pnpm changeset
? Which packages would like to bump?
◉ @templjs/core
◉ @templjs/cli
◉ @templjs/volar
◉ @templjs/context-graph
◉ vscode-templjs
? What kind of change is this for @templjs/core (Currently at 1.0.0)?
◯ patch (bugfix)
◯ minor (feature)
◉ major
? Write a summary for this change...
Update parser to support whitespace controls
This creates .changeset/<id>-<desc>.md with your change details.
3. Commit the changeset
git add .changeset/
git commit -m "chore: add changeset for feature description"
git push
4. Create PR normally
The changeset goes in the PR; CI validates it.
For Release (Maintainers Only)
When merging a feature branch with a changeset:
- Changesets bot detects the changeset and creates an automated "Version Packages" PR
- Review the Version PR: Check proposed versions align with semver intent
- Merge Version PR:
- Updates all root + workspace
package.jsonversions (synchronized) - Updates
CHANGELOG.mdentries - Pushes the release commit and tags for updated versions
- Updates all root + workspace
- Publish a GitHub Release from the appropriate tag (
v*/vscode-v*):- Triggers the stable release workflow in
release.yml
- Triggers the stable release workflow in
Common Pitfalls
❌ DON'T: Manually Edit package.json Versions
Problem: Breaks the automation; versions desynchronize
# WRONG - skips Changesets entirely
sed -i 's/"version": "1.0.0"/"version": "1.1.0"/g' package.json
git add package.json && git commit -m "bump version"
Result:
- ❌ No
CHANGELOG.mdentry - ❌ Release workflow fails
- ❌ Packages become misaligned
- ❌ No post-release Git tag
✅ DO: Use Changesets
# CORRECT
pnpm changeset
# ... follow prompts ...
git add .changeset/ && git commit
Result:
- ✅ Changelog auto-generated
- ✅ All versions sync perfectly
- ✅ Release workflow triggers correctly
- ✅ GitHub release created automatically
❌ DON'T: Skip Changesets for Bug Fixes
Even tiny changes need a changeset entry so users see them in release notes:
# WRONG
git commit -m "fix: resolve edge case in parser"
git push
Result:
- ❌ Change doesn't appear in release notes
- ❌ Users don't know about the fix
✅ DO: Include Changesets for All Changes
# CORRECT - even for patch fixes
pnpm changeset
# Select packages, choose "patch", describe the fix
git add .changeset/ && git commit
Emergency Manual Release
Only if automated release workflow fails:
# 1. Verify changes are committed and pushed
git status # should be clean
# 2. Version packages locally
pnpm changeset version
# 3. Review changes
git diff HEAD package.json
# 4. Commit and tag
git commit -am "chore(release): v1.1.0"
git tag v1.1.0
git push && git push --tags
# 5. Publish manually (credentials required)
pnpm publish -r --access public
Verification Checklist
Before merging a PR with changesets:
-
.changeset/*.mdfile exists and is committed - Changeset file lists all affected packages
- Semver bump type matches the change scope
- Changelog entry is clear and user-facing
- No manual
package.jsonversion edits in the PR - CI lint:frontmatter passes (validates changeset format)
Testing Locally
To test the full release flow without publishing:
# Simulate what the Version PR would do
pnpm changeset version
# Preview what would be committed
git diff
# Undo without committing
git reset --hard
Useful Commands
pnpm changeset # Create a changeset
pnpm changeset version # Version packages (for testing)
pnpm changeset publish # Publish packages (typically handled by CI)
pnpm changeset status # Show current changeset status
cat .changeset/config.json # Review version config
Environment Variables
Create .env.local for local overrides (ignored by Git):
# Debug mode
DEBUG=templjs:*
# Skip certain checks
SKIP_PREFLIGHT_CHECK=true
Getting Help
- Documentation: docs/
- Architecture Decisions: docs/adr/
- Work Items: backlog/
- Issues: GitHub Issues
- Discussions: GitHub Discussions
Contributing Guidelines
See CONTRIBUTING.md (when created) for:
- Code of Conduct
- PR submission guidelines
- Code review process
- Release process
- Governance model