Authoritative guide for AI/code agents contributing to this repository.
- What this is: A small, dependency-free HTML templating/DOM rendering library for AEM Edge Delivery Services blocks (and other HTML fragments).
- Public API:
renderBlock(element, context?),renderElement(element, context?)exported fromsrc/index.jsand bundled todist/faintly.js. - Security: Built-in XSS protection via
src/faintly.security.js, bundled todist/faintly.security.js(dynamically loaded by default).
- Node: 20 (CI uses Node 20).
- Package manager: npm.
- ES modules: Use ESM imports/exports. Always include the
.jsextension in local imports (lint enforces this).
- Install:
npm ci - Lint:
npm run lintandnpm run lint:fix(autofix) - Unit tests + coverage:
npm test - Run individual test file(s):
npm run test:file 'path/to/test.js'(supports glob patterns like'test/security/*.test.js') - Build bundle:
npm run build→ outputsdist/faintly.jsand prints gzipped size (warns if over limit) - Build (strict):
npm run build:strict→ fails if gzipped size exceeds 5120 bytes - Clean:
npm run clean - Verify all:
npm run verify→ runs clean, build:strict, lint, and test in sequence (comprehensive check)
- Test runner:
@web/test-runnerwith Mocha. - Groups:
unit:test/**/*.test.{html,js}excludingtest/performance/**(default group used bynpm test).perf:test/performance/**/*.test.{html,js}(opt-in).
- Coverage thresholds (enforced in CI): 100% for statements, branches, functions, and lines.
- Coverage reports: written to
coverage/. Excludestest/fixtures/**,test/snapshots/**,test/test-utils.js, andnode_modules/**. - When adding features, add or update tests to maintain 100% coverage in the
unitgroup. - Running individual test files:
- Use
npm run test:file 'path/to/test.js'to run a specific test file. - Supports glob patterns:
npm run test:file 'test/security/*.test.js' - Note: This uses a separate config (
wtr-single.config.mjs) because the main config's group-based file patterns take precedence over the--filesflag. - Do NOT use
npm test -- --files 'path/to/test.js'as it will not work correctly with the group-based configuration.
- Use
- ESLint config:
airbnb-basevia.eslintrc.jswith@babel/eslint-parser. - Key rules:
import/extensions: require.jsin imports.linebreak-style:unix.no-param-reassign: allowed for parameter properties (props: false).
- Style expectations for agents:
- Prefer clear, descriptive variable and function names over abbreviations.
- Use guard clauses and shallow control flow.
- Only add comments where non-obvious rationale or edge cases require it.
- Keep modules small and readable; avoid deep nesting; avoid unnecessary try/catch.
- Bundling uses
esbuildto produce two ESM bundles for browser usage:dist/faintly.js(core library, gzipped limit: 4KB / 4096 bytes)dist/faintly.security.js(security module, separate to allow tree-shaking)
- CI enforces a combined gzipped size limit of 6KB (6144 bytes) for both files.
- Keep additions small; avoid adding heavy dependencies.
- If you change source under
src/, runnpm run buildsodist/artifacts are up to date.
- Workflow:
.github/workflows/main.yamlruns on pull requests (open/sync/reopen). - Steps: checkout → Node 20 →
npm ci→npm run lint→npm test→npm run build:strict. - The workflow will attempt to commit updated
dist/artifacts back to the PR branch if they changed.
src/: library source- Core:
index.js,render.js,directives.js,expressions.js,templates.js - Security:
faintly.security.js
- Core:
dist/: built artifacts (faintly.js,faintly.security.js)test/: unit/perf tests, fixtures, snapshots, and utilitiestest/security/: tests for security module
coverage/: coverage output when tests are run with coverageweb-test-runner.config.mjs: main test runner config with group-based patternswtr-single.config.mjs: minimal config for running individual test files (bypasses groups)
- Install deps with
npm ci. - Make focused edits under
src/and relevant tests undertest/. - Run
npm run lint:fixthennpm run lintand resolve any remaining issues. - Run
npm testand ensure coverage stays at 100%. - Run
npm run build:strictand verifydist/artifacts update (if source changed). - Ensure combined gzipped size remains <= 6144 bytes (CI will enforce).
- Update
README.mdif you change public behavior or usage. - Commit changes; open a PR. CI will validate and may commit updated
dist/to the PR branch.
- Consumers copy
dist/faintly.jsanddist/faintly.security.jsinto their AEM project and use:renderBlock(block, context?)renderElement(element, context?)
- Security is enabled by default and dynamically loads
faintly.security.json first use. - See
README.mdfor examples, directives, expression syntax, and security configuration.
- Keep the bundle tiny; avoid adding runtime deps.
- Maintain 100% test coverage; do not reduce thresholds or exclude more files.
- Respect ESM and
.jsextension import rule. - Do not introduce Node-only APIs into browser code paths.
- Provides default XSS protection: attribute sanitization, URL scheme validation, same-origin enforcement.
- Exported as a separate bundle (
dist/faintly.security.js) for tree-shaking in opt-out scenarios. - Dynamically imported by
directives.jswhencontext.securityis undefined. - Users can disable (
security: false), provide custom hooks, or override default configuration. - When modifying security:
- Test thoroughly - security bugs have serious consequences.
- Use TDD approach with comprehensive test coverage.
- Document changes in
README.mdsecurity section. - Consider backwards compatibility for existing users.
- Be conservative about what is allowed by default.