@canonical/summon-core

中文文档
0.44.0发布于 2026/10/23GPL-3.0TypeScriptES Module构建来源已验证近 30 天下载 2691

代码生成框架:生成器定义,模板,邮票,发现. 建于 @ canonical/ task 上 .

中文使用文档
文档目录22 节
  1. 安装
  2. 使用发电机
  3. CLI 选项
  4. Shell 自动补全
  5. 位置参数
  6. 安装发电机包
  7. 创建生成器
  8. 软件包结构
  9. 地方发展
  10. 测试发电机
  11. 修道院模式
  12. 什么是摩纳德?
  13. 任务类型
  14. 编译任务
  15. 关键操作
  16. 为什么Monads为发电机?
  17. 调温引擎
  18. 默认 (EJS)
  19. 自定义引擎
  20. 接口
  21. 文档
  22. 许可证

调用

一个代码生成器框架,其中生成器是返回数据的纯函数,而不是副作用.

写发电机一次。 运行它的真实。 预览方式 --dry-run. 测试没有嘲笑。 同样的代码,不同的翻译.

generate: (answers) => writeFile(`src/${answers.name}.ts`, code)
// Returns a Task describing "write this file"
// Not a file write. Data.

安装

npm install -g @canonical/summon   # or: bun add -g @canonical/summon

CLI运行在平地节点下-不需要Bun. 它只用一个 示例生成器和 summon init (脚手架自己的发电机). 真实的 发电机在自己的软件包中生活; 将您需要的软件包安装在同一软件包中 位置和Summon自动发现它们:

npm install -g @canonical/summon-application   # summon application / domain / page / wrapper
npm install -g @canonical/summon-component      # summon component

使用发电机

提醒自动从已安装的软件包中发现生成器 . 见 安装发电机包 安装现有发电机包, 或者说 创建生成器 来创造一个新的.

# See what's available
summon

# Run a generator interactively
summon component react

# Pass answers as positional argument (when supported)
summon component react src/components/Button

# Or as a flag
summon component react --component-path=src/components/Button

# Preview first (nothing written to disk)
summon component react src/components/Button --dry-run

# Show debug output
summon component react src/components/Button --verbose

每个提示都变成CLI旗. 布尔提示 default: true 使用该 --no- 要禁用前缀。 发电机也可定义a 位置参数 允许更清洁的命令引用.

CLI 选项

旗帜说明
-d, --dry-run不写入文件预览
-y, --yes跳过确认提示并预览
-v, --verbose显示调试输出
--show-files在干线中显示文件内容
-l, --llmLLM 模式: 有下调输出的干运行, 无提示, 无邮票
--format <type>输出格式 : json (简单的干燥,没有提示,没有邮票)
--no-preview略过文件预览
--no-generated-stamp禁用生成的文件章注释

LLM 用法 : Summon将LLMs视为一等CLI用户. 使用 --llm 用于结构化的平分输出或 --format json 为机器解剖JSON。 两者都意味着 --dry-run --yes --show-files --no-generated-stamp数字 :

# Structured markdown (answers table, plan table, fenced code blocks)
summon component react src/components/Button --llm

# Machine-parseable JSON
summon component react src/components/Button --format json

# Session-level LLM mode via environment variable
export SUMMON_LLM=1
summon component react src/components/Button

# Structured help for LLMs
summon component react --help --llm

Shell 自动补全

Summon支持为Bash,Zsh和Fish壳完成TAB. 完成情况 动态 - 它们自动检测新安装的发电机包,而不需要重新运行设置.

# Automatic setup (recommended)
summon --setup-completion

# Or manual installation for each shell:

# Zsh
echo '. <(summon --completion)' >> ~/.zshrc

# Bash (may need: brew install bash-completion on macOS)
summon --completion >> ~/.summon-completion.sh
echo 'source ~/.summon-completion.sh' >> ~/.bash_profile

# Fish
echo 'summon --completion-fish | source' >> ~/.config/fish/config.fish

设置后, 重新启动您的 shell 或源代码配置文件 。 然后说:

summon <TAB>                    # Shows: component, application, init, ...
summon component <TAB>          # Shows: react, svelte, lit
summon component react <TAB>    # Shows: --component-path, --no-with-styles, ...
summon component react --comp<TAB>  # Completes to: --component-path

完成特性 :

  • 生成器名称 - 用 TAB 导航命令树
  • 发电机旗 - 所有提示成为可完成的旗帜
  • 确认提示 - 显示 --no-X 用于提示 default: true
  • 选择/ 多选择 - 完成现有选择
  • 路径提示 - 包含“路径”、“目录”、“文件”等提示的文件系统路径补全.

删除自动补全 :

summon --cleanup-completion

位置参数

生成器可以定义一个提示为 位置参数,允许用户提供主输入而不用旗:

# With positional argument support
summon component react src/components/Button

# Equivalent to
summon component react --component-path=src/components/Button

位置参数在使用 TAB 时也会获得文件系统路径补全 :

summon component react src/comp<TAB>  # Completes to: src/components/

安装发电机包

CLI 捆绑 application, (中文(简体) ), domain, (中文(简体) ), page,以及 wrapper 发电机. 其他发电机——包括公务发电机 @canonical/summon-component 船舶作为 后面的单独包 summon-* 或者说 @scope/summon-* 命名大会. 将一个安装到 同一地点 作为CLI(既包括全球项目,也包括两个项目) )和召唤自动发现:

# Component generators (react/svelte/lit)
npm install -g @canonical/summon-component   # or: bun add -g ...

# Now available (completions work immediately!)
summon component react
summon component svelte

创建生成器

生成器是一个纯函数,它接收答案并返回 a Task- 描述该怎么做的数据结构.

// src/module/types.ts
interface ModuleAnswers {
  name: string;
  withTests: boolean;
}

// src/module/generator.ts
import type { GeneratorDefinition } from "@canonical/summon";
import { debug, info, writeFile, mkdir, sequence_, when } from "@canonical/summon";
import type { ModuleAnswers } from "./types.js";

export const generator = {
  meta: {
    name: "module",
    description: "Creates a new module",
    version: "0.1.0",
  },

  prompts: [
    // positional: true allows `summon module src/utils` instead of `--name=src/utils`
    { name: "name", type: "text", message: "Module name:", positional: true },
    { name: "withTests", type: "confirm", message: "Include tests?", default: true },
  ],

  generate: (answers) => sequence_([
    info(`Creating module: ${answers.name}`),

    debug("Creating module directory"),
    mkdir(`src/${answers.name}`),

    debug("Creating index file"),
    writeFile(`src/${answers.name}/index.ts`, `export const ${answers.name} = {};\n`),

    when(answers.withTests, debug("Creating test file")),
    when(answers.withTests,
      writeFile(`src/${answers.name}/index.test.ts`, `test("works", () => {});\n`)
    ),

    info(`Created module at src/${answers.name}`),
  ]),
} as const satisfies GeneratorDefinition<ModuleAnswers>;

// src/module/index.ts (barrel)
export { generator } from "./generator.js";
export type { ModuleAnswers } from "./types.js";

// src/index.ts (package barrel)
import type { AnyGenerator } from "@canonical/summon";
import { generator as moduleGenerator } from "./module/index.js";

export const generators = {
  "module": moduleGenerator,
} as const satisfies Record<string, AnyGenerator>;

软件包结构

每个发电机应被分为三个文件,以便维持:

my-summon-package/
├── package.json
├── src/
│   ├── index.ts              # Package barrel - exports generators record
│   ├── module/
│   │   ├── index.ts          # Generator barrel
│   │   ├── types.ts          # Answer types
│   │   └── generator.ts      # Generator definition
│   └── templates/            # EJS templates (optional)
└── README.md
{
  "name": "@myorg/summon-module",
  "main": "src/index.ts",
  "peerDependencies": {
    "@canonical/summon": "workspace:*"
  }
}

地方发展

为在当地开发发电机,将发电机连接到全球:

# From your generator package directory
cd /path/to/my-summon-package
bun link        # for bun
npm link        # for npm

# Now available everywhere
summon module src/utils

项目-当地软件包 ./node_modules)优于全球链接的版本,因此可以使用特定项目版本来覆盖.

测试发电机

由于发电机返回数据,测试是直接的,不需要模拟:

import { dryRun, getAffectedFiles } from "@canonical/summon";
import { generators } from "./index";

const generator = generators["module"];

test("creates expected files", () => {
  const task = generator.generate({ name: "utils", withTests: true });
  const { effects } = dryRun(task);

  expect(getAffectedFiles(effects)).toEqual([
    "src/utils/index.ts",
    "src/utils/index.test.ts",
  ]);
});

test("skips test file when disabled", () => {
  const task = generator.generate({ name: "utils", withTests: false });
  const { effects } = dryRun(task);

  expect(getAffectedFiles(effects)).not.toContain("src/utils/index.test.ts");
});

干流解译器维持虚拟文件系统,所以基于 exists() 即使没有碰到磁盘,工作也是正确的.

修道院模式

调用 a 摩纳德 来写任务。 如果你用过"承诺",你已经理解了核心思想.

什么是摩纳德?

Monad是一些有上下文(如"可能失败"或"有效果")的连锁操作的设计模式. 把它当作一个管道,每个步骤都可以:

  1. 转换值 (单位:千美元)map——对结果应用函数
  2. 连锁业务 (单位:千美元)flatMap)——利用结果创造出下一步
  3. 故障后短路 —— 错误自动传播
// Promise (async context)
fetchUser(id)
  .then(user => fetchOrders(user.id))    // chain
  .then(orders => orders.length)          // transform
  .catch(handleError);                    // recover

// Task (effect context)
task(readFile("config.json"))
  .map(content => JSON.parse(content))    // transform
  .flatMap(config => writeFile(           // chain
    config.outputPath,
    generateCode(config)
  ))
  .recover(err => pure(defaultConfig));   // recover

任务类型

Task<A> 代表计算:

  • 描述效果( 文件 I/ O、 shell 命令等)
  • 最终产生类型值 A
  • 可能因错误而失败
// A Task is one of three things:
type Task<A> =
  | { _tag: "Pure"; value: A }                           // Done, here's the value
  | { _tag: "Fail"; error: TaskError }                   // Failed with error
  | { _tag: "Effect"; effect: Effect; cont: ... }        // Do this effect, then continue

编译任务

这个 TaskBuilder 为组成提供流利的API:

import { task, pure } from "@canonical/summon";

// Build a pipeline
const pipeline = task(readFile("input.txt"))
  .map(content => content.toUpperCase())           // Transform the value
  .flatMap(upper => writeFile("output.txt", upper)) // Chain another effect
  .andThen(info("Done!"))                          // Sequence (ignore previous value)
  .recover(err => pure(void 0));                   // Handle errors

// pipeline is still just data — nothing has executed yet

关键操作

运行说明示例
pure(value)在任务中环绕一个值pure(42)
map(fn)转换结果.map(x => x * 2)
flatMap(fn)与另一个任务连锁.flatMap(x => writeFile(...))
andThen(task)顺序, 前一个丢弃.andThen(info("next"))
recover(fn)处理错误.recover(e => pure(default))
tap(fn)边效果, 保留值.tap(x => debug(x))

为什么Monads为发电机?

  1. 可编曲 ——小任务结合成复杂的工作流程
  2. 可预见 ——错误在不明确处理的情况下在每一步骤上传播
  3. 可测试 ——输油管为数据;检查时无运行效果
  4. 声明 - 说明 什麽 做,没有 怎么样 要做到这一点
// Complex workflow, reads like a recipe
const scaffoldFeature = (name: string) =>
  task(mkdir(`src/features/${name}`))
    .andThen(template({
      source: "templates/feature.ts.ejs",
      dest: `src/features/${name}/index.ts`,
      vars: { name },
    }))
    .andThen(when(config.withTests,
      template({
        source: "templates/test.ts.ejs",
        dest: `src/features/${name}/index.test.ts`,
        vars: { name },
      })
    ))
    .andThen(info(`Created feature: ${name}`))
    .unwrap();

关于更深入地潜入“作为数据的效果”哲学,见: 解释。 。 。 .

调温引擎

Summon默认使用EJS进行模板渲染,但支持自定义调温引擎通过 TemplatingEngine 接口.

默认 (EJS)

import { template, templateDir } from "@canonical/summon";

// Uses EJS by default
template({
  source: "templates/component.tsx.ejs",
  dest: "src/components/<%= name %>.tsx",
  vars: { name: "Button" },
});

自定义引擎

执行 TemplatingEngine 用于使用 Handlebars、Mustache、Nunjucks 或任何其他引擎:

import type { TemplatingEngine } from "@canonical/summon";
import Handlebars from "handlebars";
import * as fs from "node:fs/promises";

const handlebarsEngine: TemplatingEngine = {
  render(template, vars) {
    return Handlebars.compile(template)(vars);
  },

  async renderAsync(template, vars) {
    return Handlebars.compile(template)(vars);
  },

  async renderFile(templatePath, vars) {
    const content = await fs.readFile(templatePath, "utf-8");
    return Handlebars.compile(content)(vars);
  },
};

// Use in templates
template({
  source: "templates/component.hbs",
  dest: "src/components/{{name}}.tsx",
  vars: { name: "Button" },
  engine: handlebarsEngine,
});

接口

interface TemplatingEngine {
  /** Render a template string synchronously */
  render(template: string, vars: Record<string, unknown>): string;

  /** Render a template string asynchronously */
  renderAsync(template: string, vars: Record<string, unknown>): Promise<string>;

  /** Render a template file asynchronously */
  renderFile(templatePath: string, vars: Record<string, unknown>): Promise<string>;
}

这个 engine 选项在 template(), (中文(简体) ), templateDir(), (中文(简体) ), renderString(), (中文(简体) ), renderStringAsync(),以及 renderFile()。 。 。 .

文档

许可证

GPL-3.0 (英语)

版本与发布记录

@canonical/summon-core 共发布 27 个版本,首次发布于 2026/03/26;过去 12 个月发布了 27 个版本。最新版本附带 npm provenance 构建来源证明,可追溯到公开的 CI 构建记录。

版本发布日期距今
0.44.0latest2026/10/2317 天前
0.43.02026/10/011 个月前
0.43.0-experimental.1experimental2026/09/291 个月前
0.42.02026/09/281 个月前
0.40.02026/09/211 个月前
0.38.02026/09/161 个月前

关于 @canonical/summon-core 的常见问题

@canonical/summon-core 是什么?

代码生成框架:生成器定义,模板,邮票,发现. 建于 @ canonical/ task 上 .

如何安装 @canonical/summon-core?

在项目目录运行 npm install @canonical/summon-core,或使用 pnpm add / yarn add / bun add。

@canonical/summon-core 支持 TypeScript 吗?

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

@canonical/summon-core 支持 ESM 还是 CommonJS?

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

@canonical/summon-core 需要什么版本的 Node.js?

package.json 的 engines 字段要求 Node.js >=22.18 <23 || >=23.6。版本不符时 npm 默认只给出警告,开启 engine-strict 后会拒绝安装。

@canonical/summon-core 可以商用吗?

@canonical/summon-core 使用 GPL-3.0 许可。强 Copyleft:分发包含该代码的软件时,整个作品需以 GPL-3.0 兼容许可开源,并附带专利与反锁定条款。仅内部使用不触发此义务。以上为概括,不构成法律意见,请以许可原文为准。

@canonical/summon-core 的下载量有多少?

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

@canonical/summon-core 还在维护吗?

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