# Contributing to html2canvas-pro ## Quick Start The project uses [pnpm](https://pnpm.io); the exact version is pinned in the `packageManager` field, so [Corepack](https://nodejs.org/api/corepack.html) picks it up automatically. **Unit tests require Node.js 24** (jsdom 30 does not run on older versions). ```bash corepack pnpm install corepack pnpm build # TypeScript compile + Rolldown bundle corepack pnpm unittest # Vitest unit tests corepack pnpm lint # ESLint (src/, zero warnings allowed) ``` ## Adding a New CSS Property ### Step 1: Create the property descriptor Create `src/css/property-descriptors/.ts`. Choose the parsing type: ```typescript // For keyword-only properties (e.g., display: block | inline | none) import { IPropertyIdentValueDescriptor, PropertyDescriptorParsingType } from '../property-descriptor'; import { Context } from '../../core/context'; export const enum MY_PROPERTY { VALUE_A = 0, VALUE_B = 1 } export const myProperty: IPropertyIdentValueDescriptor = { name: 'my-property', initialValue: 'value-a', prefix: false, // true for -webkit- prefixed properties type: PropertyDescriptorParsingType.IDENT_VALUE, parse: (_context: Context, value: string) => { switch (value) { case 'value-b': return MY_PROPERTY.VALUE_B; case 'value-a': default: return MY_PROPERTY.VALUE_A; } } }; ``` Refer to existing descriptors in `src/css/property-descriptors/` for examples of each type: - `VALUE` — `font-size.ts`, `opacity.ts` - `LIST` — `background-image.ts`, `margin.ts` - `IDENT_VALUE` — `display.ts`, `visibility.ts` - `TYPE_VALUE` — `background-color.ts` (color type) - `TOKEN_VALUE` — `content.ts` ### Step 2: Register in CSSParsedDeclaration In `src/css/index.ts`: 1. **Import the descriptor:** ```typescript import { myProperty } from './property-descriptors/my-property'; ``` 2. **Add a typed field** to `CSSParsedDeclaration` (alphabetically): ```typescript myProperty!: ReturnType; ``` 3. **Add a standardProps entry** (the third field is the camelCase CSS property name that CSSStyleDeclaration exposes): ```typescript ['myProperty', myProperty, 'myProperty'], ``` ### Step 3: Add rendering logic (if needed) If the property affects visual rendering, add a handler in the appropriate renderer: - **Background-related** → `src/render/canvas/background-renderer.ts` - **Border-related** → `src/render/canvas/border-renderer.ts` - **Content/layout-related** → `src/render/canvas/content-renderer.ts` - **Transform/effect-related** → `src/render/stacking-context.ts` (add to `ElementPaint.effects`) ### Step 4: Add a unit test Create or extend a test file in `src/css/property-descriptors/__tests__/`: ```typescript import { deepStrictEqual } from 'assert'; import { myProperty } from '../my-property'; import { Context } from '../../../core/context'; vi.mock('../../../core/context'); it('parses "value-a"', () => { const context = {} as Context; deepStrictEqual(myProperty.parse(context, 'value-a'), 0); }); ``` ### Step 5: Update docs Update `docs/features.md` to list the newly supported property. ## Testing ### Running Tests ```bash corepack pnpm unittest # Vitest unit tests (single run) corepack pnpm watch:unittest # Vitest in watch mode corepack pnpm unittest:coverage # Vitest with V8 coverage corepack pnpm test # lint + unit tests + browser reftests (Playwright) corepack pnpm reftests # Browser reftests only — `-- --browser=firefox|webkit` to switch (prereqs checked automatically) corepack pnpm reftests-diff # Screenshot reftest diffing (Vitest config) corepack pnpm bench:css-parse # CSS parse micro-benchmark ``` ### Test Structure Tests are colocated with source in `__tests__/` directories: - `src/css/property-descriptors/__tests__/` — CSS property parsing tests - `src/css/types/__tests__/` — CSS type tests (color, image, etc.) - `src/render/canvas/__tests__/` — Renderer tests - `src/dom/__tests__/` — DOM cloning tests - `src/core/__tests__/` — Core utility tests ## Code Style - TypeScript strict mode (`noUncheckedIndexedAccess` enabled — use the `at()` helpers from `src/core/util` for indexing that the grammar already guarantees) - No `any` in production code (use proper types or `unknown` with narrowing) - Prefer `const enum` for enum values that appear at runtime - Use `import type` for type-only imports to avoid circular dependencies - Match surrounding code style — the codebase uses semicolons, single quotes, 4-space indentation ## Build System ``` tsc (tsconfig.build.json) → typed dist Rolldown → CJS bundle (dist/html2canvas-pro.cjs) → ESM bundle (dist/html2canvas-pro.esm.js) → UMD bundle (dist/html2canvas-pro.js) → UMD minified bundle (dist/html2canvas-pro.min.js) → Test runner bundle (build/testrunner.js) + scripts/bundle-filter-surface.mjs (filter surface regression bundle) ``` - `corepack pnpm build` — full build (tsc + rolldown + reftest list) - Git hooks (Husky + lint-staged) run prettier + eslint on staged files ## Release Process ```bash corepack pnpm release # e.g., corepack pnpm release patch ``` The version argument is one of `major | minor | patch | premajor | preminor | prepatch | prerelease`. The script bumps the version, runs the build, and publishes to npm. ## Architecture Reference See [ARCHITECTURE.md](./ARCHITECTURE.md) for a detailed walkthrough of the rendering pipeline, key design patterns, and file size reference.