This document covers development setup, testing, and contributing to CV As Code.
- Node.js 18.0.0+ - Required for native test runner and modern features
- Git - For version control and update features
- VS Code (recommended) - For best development experience
git clone https://github.com/your-username/cv-as-code.git
cd cv-as-codecd src
npm installThis installs:
puppeteer- Headless Chrome for PDF generationpdfjs-dist- PDF text extractionchokidar- Cross-platform file watching
mkdir -p resumes/MyResume/cssCreate resumes/MyResume/resume.html with your content.
# From project root
./RunLinux.sh # or RunWindows.bat
# Or directly
cd src
npm startOpen http://localhost:3000 in your browser.
src/
├── cli/ # Standalone command-line tools
│ ├── pdf.js # PDF generator (Puppeteer)
│ └── ats.js # ATS text extractor (pdf.js)
│
├── launcher/ # Application startup
│ ├── index.js # Main entry, restart loop
│ ├── checks.js # Environment checks
│ ├── cleanup.js # File cleanup
│ └── utils.js # Logging, browser opening
│
├── server/ # HTTP server
│ ├── index.js # Server creation
│ ├── routes.js # Request routing
│ ├── state.js # Unified state management (config + runtime state)
│ ├── resumes.js # Resume discovery and management
│ ├── live-reload.js # File watching + SSE
│ │
│ ├── handlers/ # Request handlers
│ │ ├── api.js # REST endpoints
│ │ ├── pdf.js # PDF generation
│ │ ├── ats.js # ATS testing
│ │ └── static.js # Static files
│ │
│ └── templates/ # HTML generation
│ ├── loading.js
│ ├── ats-results.js
│ ├── shared.js
│ └── preview/ # Main page
│ ├── page.js
│ ├── toolbar.css
│ ├── modal.css
│ └── scripts/
│
├── shared/ # Shared utilities
│ ├── resume.js # Resume discovery (pure functions)
│ └── errors.js # Centralized error handling utilities
│
└── tests/ # Test suite
├── helpers/ # Test utilities
├── unit/ # Unit tests
└── integration/ # Integration tests
| Script | Description |
|---|---|
npm start |
Start the preview server |
npm test |
Run all tests |
npm run pdf |
Generate PDF for default resume |
npm run pdf -- --resume=NAME |
Generate PDF for specific resume |
npm run pdf -- --watch |
Watch mode (regenerate on changes) |
npm run ats |
Run ATS test for default resume |
npm run ats -- --resume=NAME |
Run ATS test for specific resume |
npm run serve |
Alias for npm start |
cd src
npm testnode --test tests/unit/config.test.js# Use nodemon or similar
npx nodemon --exec "npm test" --watch tests --watch serverCurrently no built-in coverage. Use c8:
npx c8 npm testLocated in tests/unit/. Test individual functions in isolation.
Example:
const { describe, it } = require('node:test');
const assert = require('node:assert');
const state = require('../../server/state');
describe('State', () => {
it('should return default config', () => {
const config = state.getConfig();
assert.ok(config);
assert.ok('lastResume' in config);
});
});Located in tests/integration/. Test full request/response cycles.
Example:
const { describe, it, before, after } = require('node:test');
const http = require('http');
const { makeRequest } = require('../helpers/http');
describe('Routes', () => {
let server;
before(async () => {
// Start test server
});
after(async () => {
// Stop server
});
it('should return resumes list', async () => {
const res = await makeRequest('/api/resumes');
assert.strictEqual(res.statusCode, 200);
});
});// assertions.js
assertFileExists(path)
assertFileNotExists(path)
assertContains(string, substring)
assertValidPdf(buffer)
assertValidHtml(string)
// http.js
makeRequest(path, options)
postJson(path, data)
// fixtures.js
createTestResume(name)
cleanupTestResume(name)
getTestResumeDir()- Add handler in
handlers/api.js:
function handleMyEndpoint(res) {
res.writeHead(200, { 'Content-Type': 'application/json' });
res.end(JSON.stringify({ data: 'value' }));
}
module.exports = { ..., handleMyEndpoint };- Add route in
routes.js:
if (pathname === '/api/my-endpoint') {
return handleMyEndpoint(res);
}- Add test in
tests/integration/routes.test.js.
- Create template in
templates/:
function getMyPage(data) {
return `<!DOCTYPE html>
<html>
<head><title>${data.title}</title></head>
<body>${data.content}</body>
</html>`;
}
module.exports = { getMyPage };- Import and use in routes.
- Create script in
templates/preview/scripts/:
// my-feature.js
function initMyFeature() {
console.log('Feature initialized');
}
// Initialize when DOM ready
document.addEventListener('DOMContentLoaded', initMyFeature);- Add to
page.jsscript loading:
const scripts = [
...,
readScript('my-feature.js')
];- Create in
cli/:
#!/usr/bin/env node
const { parseResumeArg, getResumeDir } = require('../shared/resume');
async function main() {
const resumeName = parseResumeArg();
const resumeDir = getResumeDir(resumeName);
// Your logic here
}
main();- Add npm script in
package.json:
{
"scripts": {
"mytool": "node cli/mytool.js"
}
}- Use ES6+ features (const/let, arrow functions, async/await)
- Use JSDoc comments for functions
- Use descriptive variable names
- Handle errors appropriately
/**
* Generate a PDF for the specified resume
* @param {string} resumeName - Name of the resume
* @returns {Promise<boolean>} True if successful
*/
async function generatePdf(resumeName) {
try {
// Implementation
return true;
} catch (err) {
console.error('[PDF] Error:', err.message);
return false;
}
}- Use BEM naming convention
- Organize by component
- Use CSS variables for theming
.toolbar {
/* Block */
}
.toolbar__btn {
/* Element */
}
.toolbar__btn--primary {
/* Modifier */
}- Use kebab-case for files:
live-reload.js - Use PascalCase for classes:
ConfigManager.js - Use descriptive names that indicate purpose
The server logs all requests:
[SERVER] GET /
[SERVER] GET /resume
[SERVER] GET /api/resumes
[LIVE-RELOAD] Watching: MyResume
[LIVE-RELOAD] File changed: resume.html
[LIVE-RELOAD] Notified 1 client(s): reload
[PDF] Launching browser with high-quality settings...
[PDF] Loading resume.html...
[PDF] Waiting for fonts to load...
[PDF] Fonts loaded: Roboto, Open-Sans
[PDF] Generating high-quality PDF...
[SUCCESS] High-quality PDF generated successfully!
Add console.log statements or use Node.js inspector:
node --inspect src/server/index.jsThen open chrome://inspect in Chrome.
| Variable | Description | Default |
|---|---|---|
CV_PORT |
Server port | 3000 |
CV_UPDATE_AVAILABLE |
Set by launcher | 0 |
Usage:
CV_PORT=8080 npm startNo build step needed - changes take effect on server restart.
For live reload during development, the server auto-reloads resume files but not server code.
cd src
npm updatenpm audit
npm audit fixnpm run pdf -- --resume=MyResumenpm run ats -- --resume=MyResume- Fork the repository
- Create a feature branch:
git checkout -b feature/my-feature - Make your changes
- Add/update tests
- Run tests:
npm test - Commit with descriptive message
- Push and create PR
type(scope): description
[optional body]
[optional footer]
Types: feat, fix, docs, style, refactor, test, chore
Examples:
feat(live-reload): add SSE-based live reload
fix(pdf): increase quality settings
docs(readme): update installation instructions
- Code follows style guidelines
- Tests added/updated
- Documentation updated
- No console.log left (use proper logging)
- Error handling in place
- Works on Windows/macOS/Linux