Ir al contenido

Scaffolding

Para cualquier cosa más grande que un par de archivos, no escribas un create() por archivo — mantén una carpeta de archivos de plantilla dentro del paquete de tu schematic y replícala con scaffold. Es el caballo de batalla de los generadores multi-archivo.

schematics/service/
├── schema.json
├── schema.generated.ts
├── factory.ts
└── files/ # tu carpeta de plantillas
├── __name@dasherize__.service.ts.template
├── __name@dasherize__.spec.ts.template
└── config/
└── settings.json
schematics/service/factory.ts
import { scaffold } from "@pbuilder/sdk/commons";
import type { Input } from "./schema.generated.ts";
export default (input: Input) => {
scaffold({
from: "files",
to: "src/services/__name@dasherize__",
options: { name: input.name },
});
};

Con name: "userProfile" esto emite src/services/user-profile/user-profile.service.ts, …/user-profile.spec.ts y …/config/settings.json — con los contenidos renderizados contra el mismo options, usando el lenguaje de plantillas.

from es local al paquete — se resuelve relativo a la carpeta de tu schematic, como copyIn y create({ templateFile }). El CLI pasa la ubicación del paquete automáticamente; en tests pasas packageDir tú mismo (típicamente import.meta.dir).

Los nombres de archivos y carpetas transportan valores dinámicos mediante tokens de nombre de archivo:

  • __name__ se convierte en {= .name =} — la opción name, renderizada en la ruta.
  • __name@pipe__ se convierte en {= .name | pipe =} — los mismos 7 pipes que en las plantillas, así que __name@dasherize__.service.ts con name: "userProfile" termina como user-profile.service.ts.
  • El destino to se traduce de la misma manera — así es como una sola llamada a scaffold se expande en un directorio por opción (to: "src/services/__name@dasherize__" arriba).

scaffold solo reescribe la sintaxis de los marcadores; el renderizado en sí ocurre en el engine, exactamente igual que para las plantillas de create.

Tres controles más dan forma al destino de cada entrada, aplicados en un orden fijo: rename primero, la traducción de tokens segundo, la eliminación de .template al final.

Un .template final se elimina del nombre de destino. Úsalo para evitar que los archivos de plantilla parezcan código fuente real ante tu editor y tu tooling: user.service.ts.templateuser.service.ts.

rename es una tabla de remapeo estática que se compara contra la ruta original relativa a la fuente — para el archivo suelto cuyo destino no sigue el patrón. Como corre primero en el orden fijo, el nombre remapeado igualmente pasa después por la traducción de tokens y la eliminación de .template.

include y exclude son filtros de glob sobre las rutas originales: * coincide dentro de un segmento de ruta, ** coincide a través de segmentos, y exclude gana cuando ambos coinciden con un archivo.

Cada archivo superviviente se clasifica automáticamente: el texto válido y dentro del presupuesto se renderiza como plantilla; los archivos binarios o que exceden el presupuesto viajan tal cual (como copyIn). No decides a mano qué archivos se renderizan y cuáles no.

Como todo verbo que escribe en una ruta nueva, scaffold es fail-closed ante colisiones: si un archivo de destino ya existe, el run rechaza y no se escribe nada. Pasa force: true dentro del objeto de argumentos para una sobrescritura deliberada.

  • Los directorios symlinkeados anidados dentro de from se omiten silenciosamente; una raíz from symlinkeada rechaza de plano.
  • Una sola llamada a scaffold tiene un tope de 10,000 entradas.

Previsualiza lo que una llamada a scaffold está por emitir con dry-run, y consulta verbos de mutación para la semántica completa de bordes y errores de scaffold y sus hermanos.