@conformetry/configuration

中文文档
0.0.78发布于 2026/11/10MITTypeScriptES Module构建来源已验证近 30 天下载 1.2万

Configuration reader, schema validation, and template discovery for Conformetry

原始使用文档
文档目录13 节
  1. The configuration file
  2. Generator fields
  3. What is rejected
  4. Instances
  5. Directory globs versus file globs
  6. Where a template is laid down
  7. Thresholds
  8. Inputs
  9. Matching
  10. Exports
  11. Test
  12. License
  13. 👔 Conformetry

👔 Conformetry Configuration

npm

The configuration reference for Conformetry.

@conformetry/configuration reads a repository's conformetry.config.*, checks it, and turns it into the three things the rest of the toolchain works from: the generator registry, the instances validation walks, and the resolved inputs a generator renders with.

npm install --save-dev @conformetry/configuration

Most consumers never install this directly — @conformetry/cli and @conformetry/nx both depend on it. Install it when you are typing a configuration file, or embedding conformetry in a host of your own.

The configuration file

The file's extension may be any of .ts, .mts, .cts, .js, .mjs, .cjs, .json, or .jsonc. The path is always passed in explicitly — no filename is searched for. TypeScript and JavaScript files are loaded through jiti, so a .ts config needs no build step and may import from your workspace.

The path is resolved against the current directory first, then against the workspace root — located by walking upward for pnpm-workspace.yaml — so a config path written relative to the root still resolves from a nested directory.

The file default-exports an array of generator definitions. It is an array rather than a keyed record because a generator's name is already a field, and a record made the name true in two places at once.

import { type ConformetryConfiguration } from "@conformetry/configuration";

const conformetryConfiguration: ConformetryConfiguration = [
  {
    description: "Generate a NestJS service module",
    inputs: {
      name: { description: "Module name in kebab-case", type: "string" },
    },
    instances: [{ patterns: ["packages/*/src/modules/*"] }],
    name: "nestjs-service-module",
    templatePath: "templates/nestjs-service-module",
  },
];

export default conformetryConfiguration;

Generator fields

FieldRequiredPurpose
name✅How the generator is addressed, and the filename it is emitted under
templatePath✅The template folder, relative to the workspace root
inputsThe values it substitutes, as JSON Schema fragments
instancesWhere this generator's output already lives
descriptionShown when a host lists or prompts for generators
hookspreGenerate and postGenerate functions run around the render; see @conformetry/generation

Both inputs and instances may be omitted. A generator with neither renders a fixed template nobody validates, which is legal.

What is rejected

The schema fails loudly rather than validating nothing, and reports every problem in one pass:

  • Two generators answering to the same name. A host resolves the first match, so a collision does not error where it is used — it silently shadows, and the losing generator becomes unreachable while still appearing in the configuration.
  • Two generators sharing a templatePath. Validation then finds instances that fit both equally and reports them as matching nothing.
  • A name containing a path separator. A generator is addressed by its name and emitted to a file named after it.
  • A key the schema does not declare. The generator object is strict, so a misspelled field — or aliases, which was removed outright — is rejected rather than stripped and left reading as though it took effect.
  • A hook that is not a function, or an unknown key under hooks. A string or object where a function was meant is named by its hook, and hooks is strict for the same reason the generator is: a misspelt hook name would otherwise be stripped and the hook silently never run.

Instances

instances is what makes validation possible. It says where a generator's output already lives, so validation knows what to check without being told a second time.

instances: [
  { patterns: ["packages/*/src/modules/*"] },
  {
    patterns: ["applications/*/src/modules/*"],
    substitutions: { type: "applications" },
  },
];
FieldPurpose
excludeWorkspace-relative globs naming paths that are never instances, applied with or without tags
patternsGlobs locating this group's instances, workspace-relative
substitutionsValues the template's placeholders are rendered with for this group
tagsLabels selecting which hosts the group applies to, carried uninterpreted
thresholdLowest conformance score these instances may have, overriding the generator's

Groups exist so substitutions can differ per glob. type is packages for one set of paths and applications for another, and no generic rule can tell them apart.

A placeholder these substitutions leave out is read from the instance. Validation renders a random stand-in for it, captures the text the instance holds where the template's first use of it aligns, renders the template again with that text, and compares as usual — so a later use holding different text is an ordinary difference. When nothing aligns, the instance fails with Could not infer {{…}}. name and its variants always come from the instance path, and a value supplied here always wins. Generation has no instance to read, so there a placeholder nobody supplied still raises MissingSubstitutionError — see @conformetry/generation for why, and for the section syntax a template uses to make a placeholder optional.

exclude drops any instance whose own path — the directory a pattern found, or the files of a file instance — matches one of its globs, whether or not the group has tags. It is how vendored or generated code living beside real instances, such as shadcn's component output, is left out of validation: exclude: ["projects/lexico/lexico-web/src/components/ui/**"].

tags is carried through untouched by this package, which has no notion of a host to match labels against. @conformetry/nx reads them as Nx project tags and resolves each group's globs inside every matching project; another host is free to read them as something else. A group naming only tags is legal — it selects without locating, which is what a template with no instances yet wants.

Directory globs versus file globs

The two behave differently on purpose, and the difference is what lets a small template win against a large one.

  • A directory glob leaves the match scope open, and the largest fitting template wins.
  • A file glob narrows the scope to the matched files, so a template describing exactly those files fits better than one describing the whole directory.

That is how a two-file template such as nestjs-service-file is matchable at all inside a directory a five-file module template also claims:

instances: [
  { patterns: ["src/modules/*/*.service.ts", "src/modules/*/*.service.unit.test.ts"] },
];

Where a template is laid down

A template that produces a folder contains that folder, so its instance path is the folder's parent: nestjs-service-module holds {{nameKebabCase}}/…, and its instances are …/src/modules. A template that produces loose files holds them at its root, and its instance path is the directory those files sit in.

Thresholds

An instance passes when its conformance score reaches the threshold that applies to it. Three levels set it, and the narrowest wins:

instances[].threshold ?? generator.threshold ?? runThreshold ?? 1

A level left unset stays unset rather than being filled with the default — that is what lets a host's own --threshold reach an instance whose generator has no opinion. The default of 1 is applied only at the end of the chain, so nothing configured is quietly overridden, and a level nobody set is not quietly treated as if they had.

{
  name: "nestjs-service-module",
  templatePath: "templates/nestjs-service-module",
  threshold: 1,                               // strict everywhere by default
  instances: [
    { patterns: ["packages/*/src/modules/*"] },
    {
      patterns: ["applications/legacy/src/modules/*"],
      threshold: 0.75,                        // …except while migrating this one
    },
  ],
}

When two groups locate the same instance with different thresholds the strictest wins: nothing makes one group more specific than another, and letting order decide would mean a lenient group could silently relax a bar someone else set.

Inputs

Each entry in inputs is a JSON Schema fragment. Only properties and required are read — enough to know an input's name, whether it must be supplied, and what to say when prompting for it.

Authoring them by hand is verbose, so deriving them from a schema library is the usual approach:

import { z } from "zod";

function defineInputs(shape: z.ZodRawShape) {
  return z.toJSONSchema(z.object(shape)).properties ?? {};
}

inputs: defineInputs({
  name: z.string().describe("Module name in kebab-case"),
  project: z.string().describe("Parent project name in kebab-case"),
});

Values are resolved in order: flags passed on the command line first, then interactive prompts for anything still missing. Prompting is skipped entirely when stdin is not a TTY or CI is true, so a missing input fails the command rather than hanging it.

Matching

Validation does not record which template produced a file. It works it out: every instance is weighed against every template by the share of the template's files the instance already has, and the best fit wins.

Two outcomes are reported rather than skipped, because a glob is the author asserting that these paths are instances:

ReasonMeaning
no-matchNo template explains this instance well enough
ambiguousTwo or more templates tied, so they are indistinguishable here

Exports

ExportPurpose
ConfigurationServiceEverything this layer answers: loading a configuration file, reading templates, expanding globs into instances, matching the two, rendering placeholders, and resolving generator inputs
ConfigurationModuleThe NestJS module that provides it
ConfigurationPathRequiredErrorThrown by ConfigurationService.requireConfigurationPath for an absent or blank path, naming the flag or option the caller passed as the surface
ConformetryConfigurationThe loaded configuration type — author your config as this
ConformetryInstanceGroup, ConformetryGeneratorDefinitionField-level types for the above
GeneratorHook, GeneratorHookContext, GeneratorHooksThe hook types, re-exported from @conformetry/core so a config file can type its hooks from this package

One service and one module, and no other collaborator — the same pair @callidescope/configuration, @codependix/configuration and @codometer/configuration publish, so learning one of the four teaches you all of them.

Behind the facade, template discovery, instance discovery, rendering, instance groups, and input resolution stay six separate classes in six separate files, because they are six different jobs. What they are not is six entry points a host has to know the names of: a consumer imports ConfigurationModule and injects ConfigurationService.

Whether a configuration path is required, and what the refusal says, is decided here and nowhere else: ConfigurationService.requireConfigurationPath(path, surface) returns the trimmed path or throws ConfigurationPathRequiredError. The CLI passes the --config flag, and the Nx plugin passes its own option names; neither keeps a guard or a message of its own.

Loading a configuration file is the one job ConfigurationService holds itself; everything that touches paths is a one-line hand-off to the collaborator that owns it.

Test

nx run conformetry-configuration:vitest

License

MIT — see LICENSE.

👔 Conformetry

This project was generated from the nestjs-service-project conformetry template.

版本与发布记录

@conformetry/configuration 共发布 75 个版本,首次发布于 2026/10/05;过去 12 个月发布了 75 个版本。最新版本附带 npm provenance 构建来源证明,可追溯到公开的 CI 构建记录。

版本发布日期距今
0.0.78latest2026/11/10今天
0.0.772026/11/10今天
0.0.762026/11/091 天前
0.0.752026/11/091 天前
0.0.742026/11/091 天前
0.0.732026/11/082 天前

关于 @conformetry/configuration 的常见问题

@conformetry/configuration 是什么?

Configuration reader, schema validation, and template discovery for Conformetry

如何安装 @conformetry/configuration?

在项目目录运行 npm install @conformetry/configuration,或使用 pnpm add / yarn add / bun add。

@conformetry/configuration 支持 TypeScript 吗?

支持。包内自带 TypeScript 类型声明(package.json 声明了 types 入口),安装后即可获得类型提示,无需再安装 @types 包。

@conformetry/configuration 支持 ESM 还是 CommonJS?

以 ES Module 形式发布,推荐使用 import 引入。在 CommonJS 项目中,Node.js 20.19+ / 22.12+ 可以直接 require() 不含顶层 await 的 ES Module,更早的版本需要改用动态 import()。

@conformetry/configuration 可以商用吗?

@conformetry/configuration 使用 MIT 许可。宽松许可:允许商业使用、修改和再分发,通常只需在分发时保留原版权与许可声明。以上为概括,不构成法律意见,请以许可原文为准。

@conformetry/configuration 的下载量有多少?

根据 npm 官方下载统计,@conformetry/configuration 在 2026/10/11 – 2026/11/09 这 30 天内被下载 12,490 次,日均约 416 次。下载量会把 CI 构建、镜像同步和作为其他包依赖的安装一并计入,适合横向比较热度,不能直接等同于使用人数。

@conformetry/configuration 还在维护吗?

最新版本 0.0.78 发布于 2026/11/10(今天);至今共发布 75 个版本,其中过去 12 个月发布了 75 个;首次发布于 2026/10/05。发布频率只是参考信号:功能稳定的工具可能很少需要更新,建议结合源码仓库的 issue 和提交记录判断。