content-mapper
Webda's compile-time code generation, as a TypeScript 7.1 content mapper.
TypeScript 7 removed custom emit transformers, ts-patch and language-service plugins. This package replaces them
with the supported mechanism: it rewrites model and service sources (*.model.ts, *.service.ts) into ordinary
TypeScript before the compiler sees them, with span maps so diagnostics, hover and rename still land on the authored
text.
You rarely use it directly: webdac build (from @webda/compiler) runs it for you.
What it generates
| Generator | Output |
|---|---|
accessors | Typed accessors that coerce on assignment (user.createdAt = "2020-01-01" stores a Date) and relations |
behaviors | Members of the Behaviors attached to a model |
loadParameters | loadParameters() for services extending Service<T>, from their parameters class |
qlValidator | Compile-time validation of WebdaQLString<T> queries, reported as TypeScript diagnostics; a sealed field in a query, in a getPermissionQuery or under an @Index / @Unique tag is a warning (WQL9004), logged by the build |
It also discovers the Webda objects of a project and produces the JSON schemas of models, services and operations
used in webda.module.json.
Editor support
Starting in typescript@7.1.0-dev.20261102.1, tsgo rejects content mapper extensions that end in .ts (TS18066).
You can choose between two strategies depending on whether you prefer standard .ts file suffixes or an unmodified tsc binary:
Strategy A: Keep .model.ts / .service.ts with patchTsc (Default)
Patch the installed native tsc binary so compound .model.ts and .service.ts extensions remain allowed in tsc and tsc --lsp:
node node_modules/@webda/content-mapper/lib/patch-tsc.js
Then register .model.ts and .service.ts in tsconfig.json:
{
"contentMappers": [{ "package": "@webda/content-mapper", "extensions": [".model.ts", ".service.ts"] }]
}
Strategy B: Use .model.wts / .service.wts with outputExtensions (Unmodified tsc)
If you prefer not to patch the tsc binary, use .model.wts and .service.wts source suffixes and configure outputExtensions in @webda/content-mapper's manifest (or your mapper wrapper) so emitted files and ESM imports remain ./user.model.js and ./query.service.js:
// tsconfig.json
{
"contentMappers": [{ "package": "@webda/content-mapper", "extensions": [".model.wts", ".service.wts"] }]
}
// package.json
{
"typescript": {
"contentMapper": {
"exec": ["node", "lib/server.js"],
"compilerOptions": ["module", "target"],
"outputExtensions": {
".model.wts": ".model.js",
".service.wts": ".service.js",
".flow.wts": ".flow.js"
}
}
}
}
Note: With
.wtsfiles, configure your editor and tools (e.g. VS Code"files.associations": { "*.wts": "typescript" }, ESLint, Prettier) to treat*.wtsas TypeScript.
Content mappers run external code, so TypeScript asks you to trust them: tsc --runExternalCode on the command line,
initializationOptions.runExternalCode for the language server.
A model or service declared in a file without a mapped suffix (.model.ts / .service.ts or .model.wts / .service.wts) is never transformed: webdac build fails and names the file to rename (WEBDA_STRICT_FILE_NAMING=0 downgrades it to a warning while migrating).
Tests with Vitest
The Vite plugin applies the same generators, so specs exercise the code webdac build emits:
import { webdaContentMapper } from "@webda/content-mapper/vite";
import { defineConfig } from "vitest/config";
export default defineConfig({
plugins: [webdaContentMapper()]
});
Entry points
| Export | Use |
|---|---|
@webda/content-mapper | Generators, WarmSession, runTwoPass, schema generation |
@webda/content-mapper/server | The content mapper process started by TypeScript |
@webda/content-mapper/vite | The webdaContentMapper() Vite/Vitest plugin |
@webda/content-mapper/coercions | The default coercion rules |
@webda/content-mapper/schema-worker | Schema generation in a worker thread |
Requires TypeScript >=7.1.