Contributing
Internal guide for developing the evjs monorepo.
Project identity
- Name: evjs, package scope
@evjs/* - Repository: afx-team/evjs
- CLI:
evfrom@evjs/cli - Linter: Biome
- Modules: ESM-only
Setup
git clone https://github.com/afx-team/evjs.git
cd evjs
npm install
Commands
npm run build
npm run test
npm run test:e2e
npm run check-types
npm run lint
npx biome check --write
Coding rules
- Keep imports at the top and use
import typefor type-only imports. - Use Biome formatting and linting. Avoid
anyand broad namespace imports without a concrete reason. - New applications use one page-and-route model:
src/pages/**/page.*, optional build-timepage.config.ts, directory-derived URLs, androuting.mode. - Keep page-specific components, hooks, models, services, tests, and styles
inside that page directory. They do not need
_. - File-based client routes use
$param, terminal$...splat, and(group)directories. API routes usesrc/apis/**/api.*; their directories determine the URL. - New runnable examples use
page.*,page.config.ts, androuting.mode. Keep explicitapplication.routescases in focused config-route fixtures. - Server functions begin with
"use server";and export named callables. - Config/build imports stay on
@evjs/ev; app source uses@evjs/ev/api,/middleware,/route,/navigation,/query,/server-context, and/transport. Applications that use a runtime directly import@evjs/clientor@evjs/server. - Keep framework semantics in
@evjs/evbuild internals and normalized contracts in@evjs/shared/manifest. Bundler adapters consume BuildPlan and return facts. .ev,dist,.turbo,node_modules, and route-type declarations are generated output.- Use
middlewaresfor middleware collection fields and arguments. UseMiddlewareHandlerfor one function andMiddlewareChainfor an ordered chain. Use singular middleware names for capabilities, hooks, and modules:server.request.middleware,clientDevMiddleware, andmiddleware.*. Name concrete middleware factories by behavior, such asrequestLogger().
Common tasks
Add a page route
- Create
src/pages/<url-segments>/page.tsx. - Default-export the page component.
- Use
$param, terminal$...splat, or(group)directories when needed. - Put page-specific source in the same directory; no
_prefix is required. - Add
page.config.tswhen the page needs a static title, supported named metadata, core rendering fields, or page settings for an installed plugin. Runtime use of plugin settings requires explicit plugin projection.
Add a server function
- Create
[name].server.tsbeside its caller or domain code and import it from the application. - Add
"use server";at the top. - Export named async callables.
- Consume them through
@evjs/ev/query.
Add an API route
- Create the URL directory under
src/apisand add itsapi.tsfile. - Export uppercase HTTP handlers such as
GETorPOSTfrom that file. - Keep helpers in ordinary colocated non-
api.*modules. - Compose ordered global middleware in
src/middlewares/middleware.ts, default-exporting one function or a non-empty array. Import middleware types andrequestLoggerfrom@evjs/ev/middleware. - Compose each method's policies with
withMiddlewares(handler, middlewares)from@evjs/ev/api. Reuse shared chains through ordinary imports.
Add an example
- Add a private workspace package under
examples/. - Use
routing.modeandpage.*route directories. - Add
index.htmland the required workspace dependencies. - Add/update create-app mapping only when it is a supported user template.
- Add focused unit/e2e validation.
- Keep explicit route-tree cases in clearly named config-route fixtures, not user templates.
Change page or route conventions
- Update config resolution and graph normalization first.
- Update English and Chinese
project-structure,file-conventions, config, and relevant examples together. - Add graph, diagnostics, scaffold, and config-route coverage.
- Run the repository validation gates.
Release a version
- Create a GitHub Release with the
vX.Y.Ztag for the version being released. - Release automation synchronizes internal package versions and publishes.
- Do not bump workspace-internal
"*"dependencies locally.