@hoardodile/create-plugin 0.1.6 → 0.1.8
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +8 -5
- package/dist/index.js +101 -5
- package/dist/index.js.map +1 -1
- package/dist/template/AGENTS.md +36 -0
- package/dist/template/CONTRIBUTING.md +10 -1
- package/dist/template/README.md +16 -1
- package/dist/template/lefthook.yml +12 -0
- package/dist/template/package.json +16 -2
- package/dist/template/scripts/setup-hooks.mjs +32 -0
- package/dist/template/scripts/verify-commit.mjs +38 -0
- package/package.json +4 -5
package/README.md
CHANGED
|
@@ -1,10 +1,9 @@
|
|
|
1
1
|
# @hoardodile/create-plugin
|
|
2
2
|
|
|
3
3
|
Interactive scaffolder for hoardodile content plugins. Copies the
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
points.
|
|
4
|
+
template generated from `plugins/template` at build time, rewrites the
|
|
5
|
+
manifest with a fresh UUID, rewires dependency specs, installs,
|
|
6
|
+
validates the result, and prints the dev-loop entry points.
|
|
8
7
|
|
|
9
8
|
## Usage
|
|
10
9
|
|
|
@@ -22,7 +21,11 @@ A complete plugin directory: `manifest.json` (fresh UUID — never ship
|
|
|
22
21
|
the template's id), `src/main.ts` (`definePlugin` with `detect` +
|
|
23
22
|
`sourceMeta`), `src/render.tsx` (`createPluginRoot` + typed API pair),
|
|
24
23
|
`src/shared.ts` (the `PluginSchema` declared once), `testdata/` and
|
|
25
|
-
Vitest unit tests against `createResourceAPIFixture`.
|
|
24
|
+
Vitest unit tests against `createResourceAPIFixture`. It also ships the
|
|
25
|
+
development toolchain hoardodile uses — `biome.json` (format + lint),
|
|
26
|
+
`lefthook.yml` (git hooks: Conventional-Commits `commit-msg` + biome/tsc
|
|
27
|
+
`pre-commit`, installed by `postinstall` in a git repo), the release-it
|
|
28
|
+
changelog/release setup, and `AGENTS.md`.
|
|
26
29
|
|
|
27
30
|
```bash
|
|
28
31
|
cd my-plugin
|
package/dist/index.js
CHANGED
|
@@ -6,6 +6,86 @@ import { fileURLToPath } from 'url';
|
|
|
6
6
|
import { log, intro, text, isCancel, cancel, confirm, outro } from '@clack/prompts';
|
|
7
7
|
import { pluginManifest } from '@hoardodile/sdk-types/schema';
|
|
8
8
|
|
|
9
|
+
// src/biome-template.ts
|
|
10
|
+
var STANDALONE_BIOME_JSON = `{
|
|
11
|
+
"$schema": "https://biomejs.dev/schemas/2.5.10/schema.json",
|
|
12
|
+
"vcs": {
|
|
13
|
+
"enabled": true,
|
|
14
|
+
"clientKind": "git",
|
|
15
|
+
"useIgnoreFile": true
|
|
16
|
+
},
|
|
17
|
+
"css": {
|
|
18
|
+
"parser": {
|
|
19
|
+
"tailwindDirectives": true
|
|
20
|
+
}
|
|
21
|
+
},
|
|
22
|
+
"files": {
|
|
23
|
+
"ignoreUnknown": true,
|
|
24
|
+
"includes": [
|
|
25
|
+
"**",
|
|
26
|
+
"!**/vendor",
|
|
27
|
+
"!**/coverage",
|
|
28
|
+
"!**/dist",
|
|
29
|
+
"!**/out",
|
|
30
|
+
"!**/node_modules",
|
|
31
|
+
"!.turbo",
|
|
32
|
+
"!release"
|
|
33
|
+
]
|
|
34
|
+
},
|
|
35
|
+
"formatter": {
|
|
36
|
+
"enabled": true,
|
|
37
|
+
"indentStyle": "tab"
|
|
38
|
+
},
|
|
39
|
+
"linter": {
|
|
40
|
+
"enabled": true,
|
|
41
|
+
"rules": {
|
|
42
|
+
"preset": "recommended",
|
|
43
|
+
"a11y": {
|
|
44
|
+
"noSvgWithoutTitle": "off",
|
|
45
|
+
"noStaticElementInteractions": "off"
|
|
46
|
+
},
|
|
47
|
+
"style": {
|
|
48
|
+
"noNonNullAssertion": "off",
|
|
49
|
+
"useTemplate": "error"
|
|
50
|
+
},
|
|
51
|
+
"suspicious": {
|
|
52
|
+
"noArrayIndexKey": "off",
|
|
53
|
+
"noUnknownAtRules": "off",
|
|
54
|
+
"noExplicitAny": "off",
|
|
55
|
+
"noUndeclaredEnvVars": "off"
|
|
56
|
+
},
|
|
57
|
+
"security": {
|
|
58
|
+
"noDangerouslySetInnerHtml": "off"
|
|
59
|
+
},
|
|
60
|
+
"correctness": {
|
|
61
|
+
"noSelfAssign": "off",
|
|
62
|
+
"useExhaustiveDependencies": "off"
|
|
63
|
+
},
|
|
64
|
+
"performance": {
|
|
65
|
+
"noImgElement": "off"
|
|
66
|
+
},
|
|
67
|
+
"complexity": {
|
|
68
|
+
"noImportantStyles": "off"
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
},
|
|
72
|
+
"javascript": {
|
|
73
|
+
"formatter": {
|
|
74
|
+
"quoteStyle": "double",
|
|
75
|
+
"semicolons": "asNeeded"
|
|
76
|
+
}
|
|
77
|
+
},
|
|
78
|
+
"assist": {
|
|
79
|
+
"enabled": true,
|
|
80
|
+
"actions": {
|
|
81
|
+
"source": {
|
|
82
|
+
"organizeImports": "on"
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
`;
|
|
88
|
+
|
|
9
89
|
// src/sdk-deps.gen.ts
|
|
10
90
|
var SDK_DEP_NAMES = [
|
|
11
91
|
"@hoardodile/cli",
|
|
@@ -22,11 +102,14 @@ var SDK_DEP_NAMES = [
|
|
|
22
102
|
|
|
23
103
|
// src/rewrite.ts
|
|
24
104
|
var THIRD_PARTY_VERSIONS = {
|
|
105
|
+
"@biomejs/biome": "2.5.10",
|
|
25
106
|
"@release-it/conventional-changelog": "^12.0.0",
|
|
26
107
|
"@types/node": "^26.2.0",
|
|
27
108
|
"@types/react": "^19.2.18",
|
|
28
109
|
"@types/react-dom": "^19.2.4",
|
|
29
110
|
jsdom: "^30.0.1",
|
|
111
|
+
lefthook: "^2.1.10",
|
|
112
|
+
"lint-staged": "^17.3.0",
|
|
30
113
|
react: "^19.2.8",
|
|
31
114
|
"react-dom": "^19.2.8",
|
|
32
115
|
"release-it": "^21.0.2",
|
|
@@ -93,10 +176,21 @@ function rewritePackageJson(pkg, pluginName, opts) {
|
|
|
93
176
|
}
|
|
94
177
|
return pkg;
|
|
95
178
|
}
|
|
179
|
+
function allowBuildsYaml() {
|
|
180
|
+
const builds = ALLOWED_BUILDS.map((dep) => ` '${dep}': true`).join("\n");
|
|
181
|
+
return [
|
|
182
|
+
"# Approve the install scripts pnpm 11 blocks by default: host's optional",
|
|
183
|
+
"# binaries (ffmpeg/ffprobe/7-Zip) and lefthook's platform binary.",
|
|
184
|
+
"allowBuilds:",
|
|
185
|
+
builds,
|
|
186
|
+
""
|
|
187
|
+
].join("\n");
|
|
188
|
+
}
|
|
96
189
|
var ALLOWED_BUILDS = [
|
|
97
190
|
"@derhuerst/ffprobe-static",
|
|
98
191
|
"@hoardodile/7z-bin",
|
|
99
|
-
"ffmpeg-static"
|
|
192
|
+
"ffmpeg-static",
|
|
193
|
+
"lefthook"
|
|
100
194
|
];
|
|
101
195
|
function tarballOverridesYaml(tarballsDir, sdkVersion) {
|
|
102
196
|
const lines = SDK_DEP_NAMES.map(
|
|
@@ -106,7 +200,8 @@ function tarballOverridesYaml(tarballsDir, sdkVersion) {
|
|
|
106
200
|
return [
|
|
107
201
|
"# Redirect the cross-SDK 0.0.0 specs packed into the tarballs to the",
|
|
108
202
|
"# sibling tarballs in this workspace; approve the install scripts of",
|
|
109
|
-
"# host's optional binaries (ffmpeg/ffprobe/7-Zip) so pnpm
|
|
203
|
+
"# host's optional binaries (ffmpeg/ffprobe/7-Zip) and lefthook so pnpm",
|
|
204
|
+
"# runs them.",
|
|
110
205
|
"overrides:",
|
|
111
206
|
lines,
|
|
112
207
|
"allowBuilds:",
|
|
@@ -128,10 +223,10 @@ function writeJson(path, value) {
|
|
|
128
223
|
writeFileSync(path, `${JSON.stringify(value, null, " ")}
|
|
129
224
|
`);
|
|
130
225
|
}
|
|
131
|
-
function
|
|
226
|
+
function writeWorkspaceConfig(targetDir, tarballsDir) {
|
|
132
227
|
writeFileSync(
|
|
133
228
|
join(targetDir, "pnpm-workspace.yaml"),
|
|
134
|
-
tarballOverridesYaml(tarballsDir, SELF_VERSION)
|
|
229
|
+
tarballsDir !== void 0 ? tarballOverridesYaml(tarballsDir, SELF_VERSION) : allowBuildsYaml()
|
|
135
230
|
);
|
|
136
231
|
}
|
|
137
232
|
async function runInstall(targetDir) {
|
|
@@ -191,6 +286,7 @@ async function main() {
|
|
|
191
286
|
sdkVersion: SELF_VERSION
|
|
192
287
|
})
|
|
193
288
|
);
|
|
289
|
+
writeFileSync(join(targetDir, "biome.json"), STANDALONE_BIOME_JSON);
|
|
194
290
|
const parsed = pluginManifest.safeParse(
|
|
195
291
|
JSON.parse(readFileSync(manifestPath, "utf8"))
|
|
196
292
|
);
|
|
@@ -201,8 +297,8 @@ async function main() {
|
|
|
201
297
|
}
|
|
202
298
|
if (tarballsDir !== void 0) {
|
|
203
299
|
log.step(`Rewiring SDK deps to tarballs in ${tarballsDir}`);
|
|
204
|
-
writeTarballOverrides(targetDir, tarballsDir);
|
|
205
300
|
}
|
|
301
|
+
writeWorkspaceConfig(targetDir, tarballsDir);
|
|
206
302
|
try {
|
|
207
303
|
await runInstall(targetDir);
|
|
208
304
|
} catch (err) {
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/sdk-deps.gen.ts","../src/rewrite.ts","../src/index.ts"],"names":["join"],"mappings":"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","file":"index.js","sourcesContent":["// GENERATED by scripts/gen-sdk-deps.mjs — never hand-edit.\n// The published SDK closure a scaffolded plugin can depend on; kept in\n// lockstep with scripts/lib/sdk-closure.mjs (see sdks:pack).\nexport const SDK_DEP_NAMES: readonly string[] = [\n\t\"@hoardodile/cli\",\n\t\"@hoardodile/host\",\n\t\"@hoardodile/host-web\",\n\t\"@hoardodile/i18n\",\n\t\"@hoardodile/sdk-react\",\n\t\"@hoardodile/sdk-server\",\n\t\"@hoardodile/sdk-types\",\n\t\"@hoardodile/sdk-web\",\n\t\"@hoardodile/ui\",\n\t\"@hoardodile/workbench\",\n]\n","/**\n * Scaffold-time rewrites applied to the embedded template copy: fresh\n * manifest identity and concrete dependency specs (the template uses\n * `workspace:*` / `catalog:` which only resolve inside the monorepo).\n */\nimport { join } from \"node:path\"\nimport { SDK_DEP_NAMES } from \"./sdk-deps.gen.ts\"\n\n/** Concrete versions for the catalog: specs the template uses. */\nexport const THIRD_PARTY_VERSIONS: Readonly<Record<string, string>> = {\n\t\"@release-it/conventional-changelog\": \"^12.0.0\",\n\t\"@types/node\": \"^26.2.0\",\n\t\"@types/react\": \"^19.2.18\",\n\t\"@types/react-dom\": \"^19.2.4\",\n\tjsdom: \"^30.0.1\",\n\treact: \"^19.2.8\",\n\t\"react-dom\": \"^19.2.8\",\n\t\"release-it\": \"^21.0.2\",\n\ttypescript: \"^7.0.2\",\n\tvite: \"^8.2.2\",\n\tvitest: \"^4.1.11\",\n}\n\nexport type ManifestShape = {\n\tid: string\n\tname: string\n\tdescription: string\n\tversion: string\n\ti18n?: {\n\t\tname?: Record<string, string>\n\t\tdescription?: Record<string, string>\n\t}\n}\n\nexport function rewriteManifest(\n\tmanifest: ManifestShape,\n\tpluginName: string,\n): ManifestShape {\n\tmanifest.id = crypto.randomUUID()\n\tmanifest.name = pluginName\n\tmanifest.description = `Content plugin: ${pluginName}`\n\tif (manifest.i18n?.name !== undefined) {\n\t\tmanifest.i18n.name.en = pluginName\n\t}\n\tif (manifest.i18n?.description !== undefined) {\n\t\tmanifest.i18n.description.en = `Content plugin: ${pluginName}`\n\t}\n\tmanifest.version = \"0.0.1\"\n\treturn manifest\n}\n\nexport type PackageJson = {\n\tname: string\n\tversion?: string\n\tdependencies?: Record<string, string>\n\tdevDependencies?: Record<string, string>\n}\n\nexport function tarballSpec(\n\ttarballsDir: string,\n\tdep: string,\n\tversion: string,\n): string {\n\tconst fileName = dep.replace(\"@\", \"\").replace(\"/\", \"-\")\n\treturn `file:${join(tarballsDir, `${fileName}-${version}.tgz`).replaceAll(\"\\\\\", \"/\")}`\n}\n\nfunction rewriteDepBlock(\n\tdeps: Record<string, string> | undefined,\n\ttarballsDir: string | undefined,\n\tsdkVersion: string,\n): Record<string, string> {\n\tconst out = { ...deps }\n\tfor (const [dep, spec] of Object.entries(out)) {\n\t\tif (SDK_DEP_NAMES.includes(dep)) {\n\t\t\tout[dep] =\n\t\t\t\ttarballsDir !== undefined\n\t\t\t\t\t? tarballSpec(tarballsDir, dep, sdkVersion)\n\t\t\t\t\t: // `^0.2.0-alpha.3` would never match the 0.2.0 stable\n\t\t\t\t\t\t// release (semver keeps prereleases out of plain ranges),\n\t\t\t\t\t\t// so npm specs drop the prerelease suffix.\n\t\t\t\t\t\t`^${sdkVersion.split(\"-\")[0]}`\n\t\t} else if (spec === \"catalog:\") {\n\t\t\tconst version = THIRD_PARTY_VERSIONS[dep]\n\t\t\tif (version === undefined) {\n\t\t\t\tthrow new Error(\n\t\t\t\t\t`template dependency \"${dep}\" has no concrete version mapping`,\n\t\t\t\t)\n\t\t\t}\n\t\t\tout[dep] = version\n\t\t}\n\t}\n\treturn out\n}\n\nexport function rewritePackageJson(\n\tpkg: PackageJson,\n\tpluginName: string,\n\topts: { readonly tarballsDir?: string; readonly sdkVersion: string },\n): PackageJson {\n\tpkg.name = pluginName\n\tdelete pkg.version\n\tpkg.version = \"0.0.1\"\n\tif (pkg.dependencies !== undefined) {\n\t\tpkg.dependencies = rewriteDepBlock(\n\t\t\tpkg.dependencies,\n\t\t\topts.tarballsDir,\n\t\t\topts.sdkVersion,\n\t\t)\n\t}\n\tif (pkg.devDependencies !== undefined) {\n\t\tpkg.devDependencies = rewriteDepBlock(\n\t\t\tpkg.devDependencies,\n\t\t\topts.tarballsDir,\n\t\t\topts.sdkVersion,\n\t\t)\n\t}\n\treturn pkg\n}\n\n/**\n * Install-script dependencies that land in a scaffolded plugin through\n * host's optionalDependencies (the same names as the root workspace's\n * allowBuilds). pnpm refuses to run their build scripts without an\n * explicit approval, so the generated workspace approves them up front.\n */\nconst ALLOWED_BUILDS = [\n\t\"@derhuerst/ffprobe-static\",\n\t\"@hoardodile/7z-bin\",\n\t\"ffmpeg-static\",\n]\n\nexport function tarballOverridesYaml(\n\ttarballsDir: string,\n\tsdkVersion: string,\n): string {\n\tconst lines = SDK_DEP_NAMES.map(\n\t\t(dep) => ` '${dep}': '${tarballSpec(tarballsDir, dep, sdkVersion)}'`,\n\t).join(\"\\n\")\n\tconst builds = ALLOWED_BUILDS.map((dep) => ` '${dep}': true`).join(\"\\n\")\n\treturn [\n\t\t\"# Redirect the cross-SDK 0.0.0 specs packed into the tarballs to the\",\n\t\t\"# sibling tarballs in this workspace; approve the install scripts of\",\n\t\t\"# host's optional binaries (ffmpeg/ffprobe/7-Zip) so pnpm runs them.\",\n\t\t\"overrides:\",\n\t\tlines,\n\t\t\"allowBuilds:\",\n\t\tbuilds,\n\t\t\"\",\n\t].join(\"\\n\")\n}\n","#!/usr/bin/env node\n/**\n * `create-hoardodile-plugin` — scaffold a new content plugin from the\n * embedded template copy (`src/template/`, kept in sync with\n * `plugins/template` by scripts/sync-template.mjs).\n *\n * Flow: interactive prompts (name, id auto-generated) → copy template →\n * rewrite manifest + package.json → install dependencies → validate the\n * manifest → print the dev loop entry points.\n *\n * create-hoardodile-plugin my-plugin\n * create-hoardodile-plugin my-plugin --tarballs ../hoardodile/tmp/sdks\n *\n * `--tarballs <dir>` rewires the SDK deps to packed tarballs plus a\n * pnpm-workspace.yaml with the cross-package overrides — used by the\n * release smoke test and for offline installs.\n */\nimport { spawn } from \"node:child_process\"\nimport {\n\tcpSync,\n\texistsSync,\n\tmkdirSync,\n\treaddirSync,\n\treadFileSync,\n\twriteFileSync,\n} from \"node:fs\"\nimport { dirname, join, resolve } from \"node:path\"\nimport { fileURLToPath } from \"node:url\"\nimport {\n\tcancel,\n\tconfirm,\n\tintro,\n\tisCancel,\n\tlog,\n\toutro,\n\ttext,\n} from \"@clack/prompts\"\nimport { pluginManifest } from \"@hoardodile/sdk-types/schema\"\nimport {\n\trewriteManifest,\n\trewritePackageJson,\n\ttarballOverridesYaml,\n} from \"./rewrite.ts\"\n\nconst ROOT = resolve(dirname(fileURLToPath(import.meta.url)), \"..\")\n// The template ships inside dist (dist/template) — the bin always runs\n// from the built artifact, so resolve relative to this module.\nconst TEMPLATE_DIR = resolve(\n\tdirname(fileURLToPath(import.meta.url)),\n\t\"template\",\n)\nconst SELF_VERSION = JSON.parse(\n\treadFileSync(join(ROOT, \"package.json\"), \"utf8\"),\n).version\n\nfunction writeJson(path: string, value: unknown): void {\n\twriteFileSync(path, `${JSON.stringify(value, null, \"\\t\")}\\n`)\n}\n\nfunction writeTarballOverrides(targetDir: string, tarballsDir: string): void {\n\twriteFileSync(\n\t\tjoin(targetDir, \"pnpm-workspace.yaml\"),\n\t\ttarballOverridesYaml(tarballsDir, SELF_VERSION),\n\t)\n}\n\nasync function runInstall(targetDir: string): Promise<void> {\n\tlog.step(\"Installing dependencies (pnpm install)\")\n\treturn new Promise<void>((resolveDone, reject) => {\n\t\tconst child = spawn(\"pnpm\", [\"install\"], {\n\t\t\tcwd: targetDir,\n\t\t\tstdio: \"inherit\",\n\t\t\tshell: process.platform === \"win32\",\n\t\t})\n\t\tchild.on(\"error\", reject)\n\t\tchild.on(\"exit\", (code) =>\n\t\t\tcode === 0\n\t\t\t\t? resolveDone()\n\t\t\t\t: reject(new Error(`pnpm install exited ${code}`)),\n\t\t)\n\t})\n}\n\nexport async function main() {\n\tconst positional = process.argv.slice(2).filter((a) => !a.startsWith(\"--\"))\n\tconst tarballsFlag = process.argv.indexOf(\"--tarballs\")\n\tconst tarballsDir =\n\t\ttarballsFlag !== -1\n\t\t\t? resolve(process.argv[tarballsFlag + 1] ?? \"\")\n\t\t\t: undefined\n\n\tintro(\"hoardodile plugin scaffold\")\n\n\tconst providedName = positional[0]\n\tconst pluginName =\n\t\tprovidedName ??\n\t\t(await text({\n\t\t\tmessage: \"Plugin name (also the directory name, npm-style):\",\n\t\t\tvalidate: (v) =>\n\t\t\t\t/^[a-z0-9][a-z0-9-]*$/.test(v ?? \"\")\n\t\t\t\t\t? undefined\n\t\t\t\t\t: \"lowercase letters, digits and dashes only\",\n\t\t}))\n\tif (isCancel(pluginName)) {\n\t\tcancel(\"scaffold cancelled\")\n\t\tprocess.exit(1)\n\t}\n\n\tconst targetDir = resolve(pluginName)\n\tif (existsSync(targetDir) && readdirSync(targetDir).length > 0) {\n\t\tconst proceed = await confirm({\n\t\t\tmessage: `${targetDir} is not empty — scaffold into it anyway?`,\n\t\t\tinitialValue: false,\n\t\t})\n\t\tif (isCancel(proceed) || !proceed) {\n\t\t\tcancel(\"scaffold cancelled\")\n\t\t\tprocess.exit(1)\n\t\t}\n\t}\n\n\tlog.step(`Copying template into ${targetDir}`)\n\tmkdirSync(targetDir, { recursive: true })\n\t// dist/template is a build artifact (already filtered when copied in);\n\t// copy it wholesale so template source dirs inside it survive.\n\tcpSync(TEMPLATE_DIR, targetDir, { recursive: true })\n\n\tlog.step(\"Rewriting manifest and package.json\")\n\tconst manifestPath = join(targetDir, \"manifest.json\")\n\tconst pkgPath = join(targetDir, \"package.json\")\n\twriteJson(\n\t\tmanifestPath,\n\t\trewriteManifest(JSON.parse(readFileSync(manifestPath, \"utf8\")), pluginName),\n\t)\n\twriteJson(\n\t\tpkgPath,\n\t\trewritePackageJson(JSON.parse(readFileSync(pkgPath, \"utf8\")), pluginName, {\n\t\t\ttarballsDir,\n\t\t\tsdkVersion: SELF_VERSION,\n\t\t}),\n\t)\n\n\tconst parsed = pluginManifest.safeParse(\n\t\tJSON.parse(readFileSync(manifestPath, \"utf8\")),\n\t)\n\tif (!parsed.success) {\n\t\tprocess.exitCode = 1\n\t\tlog.error(`generated manifest is invalid: ${parsed.error.message}`)\n\t\treturn\n\t}\n\n\tif (tarballsDir !== undefined) {\n\t\tlog.step(`Rewiring SDK deps to tarballs in ${tarballsDir}`)\n\t\twriteTarballOverrides(targetDir, tarballsDir)\n\t}\n\n\ttry {\n\t\tawait runInstall(targetDir)\n\t} catch (err) {\n\t\tprocess.exitCode = 1\n\t\tlog.error(\n\t\t\t`install failed: ${err instanceof Error ? err.message : String(err)}`,\n\t\t)\n\t\treturn\n\t}\n\n\toutro(\"Plugin scaffolded. Next steps:\")\n\tconsole.log(` cd ${targetDir}`)\n\tconsole.log(\n\t\t\" pnpm build # build dist/ (client + server bundle + manifest)\",\n\t)\n\tconsole.log(\n\t\t\" pnpm dev # watch-build + serve the workbench (http://127.0.0.1:5199)\",\n\t)\n\tconsole.log(\" pnpm test # unit tests against the fixture API\")\n\tconsole.log(\" hoardodile plugin run detect testdata --plugin-dir dist\")\n\tconsole.log(\n\t\t\" # Replace the manifest id? No — it was generated fresh. Zip dist/ contents and upload in Settings → Plugins.\",\n\t)\n}\n\nif (\n\tprocess.argv[1] !== undefined &&\n\tresolve(process.argv[1]) === fileURLToPath(import.meta.url)\n) {\n\tmain().catch((err) => {\n\t\tlog.error(err instanceof Error ? err.message : String(err))\n\t\tprocess.exit(1)\n\t})\n}\n"]}
|
|
1
|
+
{"version":3,"sources":["../src/biome-template.ts","../src/sdk-deps.gen.ts","../src/rewrite.ts","../src/index.ts"],"names":["join"],"mappings":"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","file":"index.js","sourcesContent":["/**\n * `biome.json` for a scaffolded standalone plugin. It cannot live inside\n * `plugins/template`: biome 2.x rejects a nested root config when one\n * already exists, and the monorepo has a root `biome.json` — so the\n * scaffolder writes this config into each generated plugin instead. It\n * mirrors hoardodile's config (tab indent, double quotes, asNeeded\n * semicolons, organizeImports, the recommended linter with the same\n * rule offs, Tailwind directive parsing) minus monorepo-only excludes.\n */\nexport const STANDALONE_BIOME_JSON = `{\n\t\"$schema\": \"https://biomejs.dev/schemas/2.5.10/schema.json\",\n\t\"vcs\": {\n\t\t\"enabled\": true,\n\t\t\"clientKind\": \"git\",\n\t\t\"useIgnoreFile\": true\n\t},\n\t\"css\": {\n\t\t\"parser\": {\n\t\t\t\"tailwindDirectives\": true\n\t\t}\n\t},\n\t\"files\": {\n\t\t\"ignoreUnknown\": true,\n\t\t\"includes\": [\n\t\t\t\"**\",\n\t\t\t\"!**/vendor\",\n\t\t\t\"!**/coverage\",\n\t\t\t\"!**/dist\",\n\t\t\t\"!**/out\",\n\t\t\t\"!**/node_modules\",\n\t\t\t\"!.turbo\",\n\t\t\t\"!release\"\n\t\t]\n\t},\n\t\"formatter\": {\n\t\t\"enabled\": true,\n\t\t\"indentStyle\": \"tab\"\n\t},\n\t\"linter\": {\n\t\t\"enabled\": true,\n\t\t\"rules\": {\n\t\t\t\"preset\": \"recommended\",\n\t\t\t\"a11y\": {\n\t\t\t\t\"noSvgWithoutTitle\": \"off\",\n\t\t\t\t\"noStaticElementInteractions\": \"off\"\n\t\t\t},\n\t\t\t\"style\": {\n\t\t\t\t\"noNonNullAssertion\": \"off\",\n\t\t\t\t\"useTemplate\": \"error\"\n\t\t\t},\n\t\t\t\"suspicious\": {\n\t\t\t\t\"noArrayIndexKey\": \"off\",\n\t\t\t\t\"noUnknownAtRules\": \"off\",\n\t\t\t\t\"noExplicitAny\": \"off\",\n\t\t\t\t\"noUndeclaredEnvVars\": \"off\"\n\t\t\t},\n\t\t\t\"security\": {\n\t\t\t\t\"noDangerouslySetInnerHtml\": \"off\"\n\t\t\t},\n\t\t\t\"correctness\": {\n\t\t\t\t\"noSelfAssign\": \"off\",\n\t\t\t\t\"useExhaustiveDependencies\": \"off\"\n\t\t\t},\n\t\t\t\"performance\": {\n\t\t\t\t\"noImgElement\": \"off\"\n\t\t\t},\n\t\t\t\"complexity\": {\n\t\t\t\t\"noImportantStyles\": \"off\"\n\t\t\t}\n\t\t}\n\t},\n\t\"javascript\": {\n\t\t\"formatter\": {\n\t\t\t\"quoteStyle\": \"double\",\n\t\t\t\"semicolons\": \"asNeeded\"\n\t\t}\n\t},\n\t\"assist\": {\n\t\t\"enabled\": true,\n\t\t\"actions\": {\n\t\t\t\"source\": {\n\t\t\t\t\"organizeImports\": \"on\"\n\t\t\t}\n\t\t}\n\t}\n}\n`\n","// GENERATED by scripts/gen-sdk-deps.mjs — never hand-edit.\n// The published SDK closure a scaffolded plugin can depend on; kept in\n// lockstep with scripts/lib/sdk-closure.mjs (see sdks:pack).\nexport const SDK_DEP_NAMES: readonly string[] = [\n\t\"@hoardodile/cli\",\n\t\"@hoardodile/host\",\n\t\"@hoardodile/host-web\",\n\t\"@hoardodile/i18n\",\n\t\"@hoardodile/sdk-react\",\n\t\"@hoardodile/sdk-server\",\n\t\"@hoardodile/sdk-types\",\n\t\"@hoardodile/sdk-web\",\n\t\"@hoardodile/ui\",\n\t\"@hoardodile/workbench\",\n]\n","/**\n * Scaffold-time rewrites applied to the embedded template copy: fresh\n * manifest identity and concrete dependency specs (the template uses\n * `workspace:*` / `catalog:` which only resolve inside the monorepo).\n */\nimport { join } from \"node:path\"\nimport { SDK_DEP_NAMES } from \"./sdk-deps.gen.ts\"\n\n/** Concrete versions for the catalog: specs the template uses. */\nexport const THIRD_PARTY_VERSIONS: Readonly<Record<string, string>> = {\n\t\"@biomejs/biome\": \"2.5.10\",\n\t\"@release-it/conventional-changelog\": \"^12.0.0\",\n\t\"@types/node\": \"^26.2.0\",\n\t\"@types/react\": \"^19.2.18\",\n\t\"@types/react-dom\": \"^19.2.4\",\n\tjsdom: \"^30.0.1\",\n\tlefthook: \"^2.1.10\",\n\t\"lint-staged\": \"^17.3.0\",\n\treact: \"^19.2.8\",\n\t\"react-dom\": \"^19.2.8\",\n\t\"release-it\": \"^21.0.2\",\n\ttypescript: \"^7.0.2\",\n\tvite: \"^8.2.2\",\n\tvitest: \"^4.1.11\",\n}\n\nexport type ManifestShape = {\n\tid: string\n\tname: string\n\tdescription: string\n\tversion: string\n\ti18n?: {\n\t\tname?: Record<string, string>\n\t\tdescription?: Record<string, string>\n\t}\n}\n\nexport function rewriteManifest(\n\tmanifest: ManifestShape,\n\tpluginName: string,\n): ManifestShape {\n\tmanifest.id = crypto.randomUUID()\n\tmanifest.name = pluginName\n\tmanifest.description = `Content plugin: ${pluginName}`\n\tif (manifest.i18n?.name !== undefined) {\n\t\tmanifest.i18n.name.en = pluginName\n\t}\n\tif (manifest.i18n?.description !== undefined) {\n\t\tmanifest.i18n.description.en = `Content plugin: ${pluginName}`\n\t}\n\tmanifest.version = \"0.0.1\"\n\treturn manifest\n}\n\nexport type PackageJson = {\n\tname: string\n\tversion?: string\n\tdependencies?: Record<string, string>\n\tdevDependencies?: Record<string, string>\n\tpostinstall?: string\n}\n\nexport function tarballSpec(\n\ttarballsDir: string,\n\tdep: string,\n\tversion: string,\n): string {\n\tconst fileName = dep.replace(\"@\", \"\").replace(\"/\", \"-\")\n\treturn `file:${join(tarballsDir, `${fileName}-${version}.tgz`).replaceAll(\"\\\\\", \"/\")}`\n}\n\nfunction rewriteDepBlock(\n\tdeps: Record<string, string> | undefined,\n\ttarballsDir: string | undefined,\n\tsdkVersion: string,\n): Record<string, string> {\n\tconst out = { ...deps }\n\tfor (const [dep, spec] of Object.entries(out)) {\n\t\tif (SDK_DEP_NAMES.includes(dep)) {\n\t\t\tout[dep] =\n\t\t\t\ttarballsDir !== undefined\n\t\t\t\t\t? tarballSpec(tarballsDir, dep, sdkVersion)\n\t\t\t\t\t: // `^0.2.0-alpha.3` would never match the 0.2.0 stable\n\t\t\t\t\t\t// release (semver keeps prereleases out of plain ranges),\n\t\t\t\t\t\t// so npm specs drop the prerelease suffix.\n\t\t\t\t\t\t`^${sdkVersion.split(\"-\")[0]}`\n\t\t} else if (spec === \"catalog:\") {\n\t\t\tconst version = THIRD_PARTY_VERSIONS[dep]\n\t\t\tif (version === undefined) {\n\t\t\t\tthrow new Error(\n\t\t\t\t\t`template dependency \"${dep}\" has no concrete version mapping`,\n\t\t\t\t)\n\t\t\t}\n\t\t\tout[dep] = version\n\t\t}\n\t}\n\treturn out\n}\n\nexport function rewritePackageJson(\n\tpkg: PackageJson,\n\tpluginName: string,\n\topts: { readonly tarballsDir?: string; readonly sdkVersion: string },\n): PackageJson {\n\tpkg.name = pluginName\n\tdelete pkg.version\n\tpkg.version = \"0.0.1\"\n\tif (pkg.dependencies !== undefined) {\n\t\tpkg.dependencies = rewriteDepBlock(\n\t\t\tpkg.dependencies,\n\t\t\topts.tarballsDir,\n\t\t\topts.sdkVersion,\n\t\t)\n\t}\n\tif (pkg.devDependencies !== undefined) {\n\t\tpkg.devDependencies = rewriteDepBlock(\n\t\t\tpkg.devDependencies,\n\t\t\topts.tarballsDir,\n\t\t\topts.sdkVersion,\n\t\t)\n\t}\n\treturn pkg\n}\n\n/**\n * A standalone (non-workspace) plugin repo needs its build-script approvals in\n * a `pnpm-workspace.yaml`: pnpm 11 blocks dependency install scripts by\n * default and ignores the equivalent package.json `pnpm.onlyBuiltDependencies`\n * field. The scaffolder writes this file on the normal registry path (the\n * --tarballs path writes an equivalent `allowBuilds` alongside its overrides).\n */\nexport function allowBuildsYaml(): string {\n\tconst builds = ALLOWED_BUILDS.map((dep) => ` '${dep}': true`).join(\"\\n\")\n\treturn [\n\t\t\"# Approve the install scripts pnpm 11 blocks by default: host's optional\",\n\t\t\"# binaries (ffmpeg/ffprobe/7-Zip) and lefthook's platform binary.\",\n\t\t\"allowBuilds:\",\n\t\tbuilds,\n\t\t\"\",\n\t].join(\"\\n\")\n}\n\n/**\n * Install-script dependencies that land in a scaffolded plugin: host's\n * optional binaries plus lefthook (the git-hooks runner, whose postinstall\n * fetches the platform binary). pnpm refuses to run their build scripts\n * without an explicit approval, so the generated workspace approves them\n * all up front (mirroring the root workspace's allowBuilds).\n */\nconst ALLOWED_BUILDS = [\n\t\"@derhuerst/ffprobe-static\",\n\t\"@hoardodile/7z-bin\",\n\t\"ffmpeg-static\",\n\t\"lefthook\",\n]\n\nexport function tarballOverridesYaml(\n\ttarballsDir: string,\n\tsdkVersion: string,\n): string {\n\tconst lines = SDK_DEP_NAMES.map(\n\t\t(dep) => ` '${dep}': '${tarballSpec(tarballsDir, dep, sdkVersion)}'`,\n\t).join(\"\\n\")\n\tconst builds = ALLOWED_BUILDS.map((dep) => ` '${dep}': true`).join(\"\\n\")\n\treturn [\n\t\t\"# Redirect the cross-SDK 0.0.0 specs packed into the tarballs to the\",\n\t\t\"# sibling tarballs in this workspace; approve the install scripts of\",\n\t\t\"# host's optional binaries (ffmpeg/ffprobe/7-Zip) and lefthook so pnpm\",\n\t\t\"# runs them.\",\n\t\t\"overrides:\",\n\t\tlines,\n\t\t\"allowBuilds:\",\n\t\tbuilds,\n\t\t\"\",\n\t].join(\"\\n\")\n}\n","#!/usr/bin/env node\n/**\n * `create-hoardodile-plugin` — scaffold a new content plugin from the\n * template copy shipped in `dist/template`, generated from the canonical\n * `plugins/template` by scripts/copy-template.mjs at build time.\n *\n * Flow: interactive prompts (name, id auto-generated) → copy template →\n * rewrite manifest + package.json → install dependencies → validate the\n * manifest → print the dev loop entry points.\n *\n * create-hoardodile-plugin my-plugin\n * create-hoardodile-plugin my-plugin --tarballs ../hoardodile/tmp/sdks\n *\n * `--tarballs <dir>` rewires the SDK deps to packed tarballs plus a\n * pnpm-workspace.yaml with the cross-package overrides — used by the\n * release smoke test and for offline installs.\n */\nimport { spawn } from \"node:child_process\"\nimport {\n\tcpSync,\n\texistsSync,\n\tmkdirSync,\n\treaddirSync,\n\treadFileSync,\n\twriteFileSync,\n} from \"node:fs\"\nimport { dirname, join, resolve } from \"node:path\"\nimport { fileURLToPath } from \"node:url\"\nimport {\n\tcancel,\n\tconfirm,\n\tintro,\n\tisCancel,\n\tlog,\n\toutro,\n\ttext,\n} from \"@clack/prompts\"\nimport { pluginManifest } from \"@hoardodile/sdk-types/schema\"\nimport { STANDALONE_BIOME_JSON } from \"./biome-template.ts\"\nimport {\n\tallowBuildsYaml,\n\trewriteManifest,\n\trewritePackageJson,\n\ttarballOverridesYaml,\n} from \"./rewrite.ts\"\n\nconst ROOT = resolve(dirname(fileURLToPath(import.meta.url)), \"..\")\n// The template ships inside dist (dist/template) — the bin always runs\n// from the built artifact, so resolve relative to this module.\nconst TEMPLATE_DIR = resolve(\n\tdirname(fileURLToPath(import.meta.url)),\n\t\"template\",\n)\nconst SELF_VERSION = JSON.parse(\n\treadFileSync(join(ROOT, \"package.json\"), \"utf8\"),\n).version\n\nfunction writeJson(path: string, value: unknown): void {\n\twriteFileSync(path, `${JSON.stringify(value, null, \"\\t\")}\\n`)\n}\n\nfunction writeWorkspaceConfig(targetDir: string, tarballsDir?: string): void {\n\t// pnpm 11 blocks dependency install scripts and ignores the package.json\n\t// `pnpm.onlyBuiltDependencies` field, so approve them through an\n\t// allowBuilds workspace file. The --tarballs path also rewires the SDK\n\t// deps to the packed tarballs (same allowBuilds, plus file: overrides).\n\twriteFileSync(\n\t\tjoin(targetDir, \"pnpm-workspace.yaml\"),\n\t\ttarballsDir !== undefined\n\t\t\t? tarballOverridesYaml(tarballsDir, SELF_VERSION)\n\t\t\t: allowBuildsYaml(),\n\t)\n}\n\nasync function runInstall(targetDir: string): Promise<void> {\n\tlog.step(\"Installing dependencies (pnpm install)\")\n\treturn new Promise<void>((resolveDone, reject) => {\n\t\tconst child = spawn(\"pnpm\", [\"install\"], {\n\t\t\tcwd: targetDir,\n\t\t\tstdio: \"inherit\",\n\t\t\tshell: process.platform === \"win32\",\n\t\t})\n\t\tchild.on(\"error\", reject)\n\t\tchild.on(\"exit\", (code) =>\n\t\t\tcode === 0\n\t\t\t\t? resolveDone()\n\t\t\t\t: reject(new Error(`pnpm install exited ${code}`)),\n\t\t)\n\t})\n}\n\nexport async function main() {\n\tconst positional = process.argv.slice(2).filter((a) => !a.startsWith(\"--\"))\n\tconst tarballsFlag = process.argv.indexOf(\"--tarballs\")\n\tconst tarballsDir =\n\t\ttarballsFlag !== -1\n\t\t\t? resolve(process.argv[tarballsFlag + 1] ?? \"\")\n\t\t\t: undefined\n\n\tintro(\"hoardodile plugin scaffold\")\n\n\tconst providedName = positional[0]\n\tconst pluginName =\n\t\tprovidedName ??\n\t\t(await text({\n\t\t\tmessage: \"Plugin name (also the directory name, npm-style):\",\n\t\t\tvalidate: (v) =>\n\t\t\t\t/^[a-z0-9][a-z0-9-]*$/.test(v ?? \"\")\n\t\t\t\t\t? undefined\n\t\t\t\t\t: \"lowercase letters, digits and dashes only\",\n\t\t}))\n\tif (isCancel(pluginName)) {\n\t\tcancel(\"scaffold cancelled\")\n\t\tprocess.exit(1)\n\t}\n\n\tconst targetDir = resolve(pluginName)\n\tif (existsSync(targetDir) && readdirSync(targetDir).length > 0) {\n\t\tconst proceed = await confirm({\n\t\t\tmessage: `${targetDir} is not empty — scaffold into it anyway?`,\n\t\t\tinitialValue: false,\n\t\t})\n\t\tif (isCancel(proceed) || !proceed) {\n\t\t\tcancel(\"scaffold cancelled\")\n\t\t\tprocess.exit(1)\n\t\t}\n\t}\n\n\tlog.step(`Copying template into ${targetDir}`)\n\tmkdirSync(targetDir, { recursive: true })\n\t// dist/template is a build artifact (already filtered when copied in);\n\t// copy it wholesale so template source dirs inside it survive.\n\tcpSync(TEMPLATE_DIR, targetDir, { recursive: true })\n\n\tlog.step(\"Rewriting manifest and package.json\")\n\tconst manifestPath = join(targetDir, \"manifest.json\")\n\tconst pkgPath = join(targetDir, \"package.json\")\n\twriteJson(\n\t\tmanifestPath,\n\t\trewriteManifest(JSON.parse(readFileSync(manifestPath, \"utf8\")), pluginName),\n\t)\n\twriteJson(\n\t\tpkgPath,\n\t\trewritePackageJson(JSON.parse(readFileSync(pkgPath, \"utf8\")), pluginName, {\n\t\t\ttarballsDir,\n\t\t\tsdkVersion: SELF_VERSION,\n\t\t}),\n\t)\n\t// A standalone plugin repo has no root biome.json (biome rejects a nested\n\t// config when the monorepo's root one exists), so the scaffolder ships the\n\t// toolchain config directly into each generated plugin.\n\twriteFileSync(join(targetDir, \"biome.json\"), STANDALONE_BIOME_JSON)\n\n\tconst parsed = pluginManifest.safeParse(\n\t\tJSON.parse(readFileSync(manifestPath, \"utf8\")),\n\t)\n\tif (!parsed.success) {\n\t\tprocess.exitCode = 1\n\t\tlog.error(`generated manifest is invalid: ${parsed.error.message}`)\n\t\treturn\n\t}\n\n\tif (tarballsDir !== undefined) {\n\t\tlog.step(`Rewiring SDK deps to tarballs in ${tarballsDir}`)\n\t}\n\twriteWorkspaceConfig(targetDir, tarballsDir)\n\n\ttry {\n\t\tawait runInstall(targetDir)\n\t} catch (err) {\n\t\tprocess.exitCode = 1\n\t\tlog.error(\n\t\t\t`install failed: ${err instanceof Error ? err.message : String(err)}`,\n\t\t)\n\t\treturn\n\t}\n\n\toutro(\"Plugin scaffolded. Next steps:\")\n\tconsole.log(` cd ${targetDir}`)\n\tconsole.log(\n\t\t\" pnpm build # build dist/ (client + server bundle + manifest)\",\n\t)\n\tconsole.log(\n\t\t\" pnpm dev # watch-build + serve the workbench (http://127.0.0.1:5199)\",\n\t)\n\tconsole.log(\" pnpm test # unit tests against the fixture API\")\n\tconsole.log(\" hoardodile plugin run detect testdata --plugin-dir dist\")\n\tconsole.log(\n\t\t\" # Replace the manifest id? No — it was generated fresh. Zip dist/ contents and upload in Settings → Plugins.\",\n\t)\n}\n\nif (\n\tprocess.argv[1] !== undefined &&\n\tresolve(process.argv[1]) === fileURLToPath(import.meta.url)\n) {\n\tmain().catch((err) => {\n\t\tlog.error(err instanceof Error ? err.message : String(err))\n\t\tprocess.exit(1)\n\t})\n}\n"]}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
# hoardodile plugin template
|
|
2
|
+
|
|
3
|
+
Minimal end-to-end content plugin: `detect` → `sourceMeta` → iframe render. Copy this directory to start your own plugin — never edit it in place (the create-plugin scaffolder embeds a copy of it; CI compiles it).
|
|
4
|
+
|
|
5
|
+
## Commands
|
|
6
|
+
|
|
7
|
+
- `pnpm build` — build `dist/` (client + server bundle + manifest).
|
|
8
|
+
- `pnpm dev` — watch-build + serve the workbench at http://127.0.0.1:5199 (data from `testdata/`, one sandboxed `detect` on startup).
|
|
9
|
+
- `pnpm test` — Vitest against the in-memory fixture API; `pnpm run detect:smoke` — sandboxed `detect` against `testdata/` (needs a build first).
|
|
10
|
+
- `pnpm lint` — `biome check .` + `tsc --noEmit`; `pnpm format` — `biome check --write`; `pnpm lint-staged` — the pre-commit biome pass.
|
|
11
|
+
- `hoardodile plugin <run|package|dev>` — run hooks through the same worker sandbox the server uses (`run`), zip `dist/` into `release/<id>-<version>.zip` (`package`), or the offline dev workbench (`dev`).
|
|
12
|
+
- `pnpm readme:check` — gate the marketplace `readme/` folder (flat, ships one `README.md` fallback per locale). `pnpm release <version>` — release-it bumps version, writes `CHANGELOG.md`, tags `v<version>`, and the tag workflow builds/packages/uploads the GitHub release assets.
|
|
13
|
+
|
|
14
|
+
Git hooks (`lefthook.yml`, installed by `postinstall` when this is a git repo): `commit-msg` enforces the Conventional Commits format that feeds the changelog; `pre-commit` runs biome + `tsc` on staged files.
|
|
15
|
+
|
|
16
|
+
Scaffold a new plugin with `pnpm dlx create-hoardodile-plugin <name>`; it rewrites `manifest.json`/`package.json` and installs deps.
|
|
17
|
+
|
|
18
|
+
## Structure
|
|
19
|
+
|
|
20
|
+
```
|
|
21
|
+
src/main.ts server-side definition (definePlugin): detect + sourceMeta
|
|
22
|
+
src/shared.ts PluginSchema typed once, shared server ↔ client
|
|
23
|
+
src/hooks.ts typed plugin API (definePluginAPI) for the client
|
|
24
|
+
src/render.tsx iframe client (createPluginRoot @hoardodile/sdk-react)
|
|
25
|
+
testdata/ default data root for `hoardodile plugin dev`
|
|
26
|
+
__tests__/ unit tests
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
## Architecture
|
|
30
|
+
|
|
31
|
+
- **Contract:** `manifest.json` + server `main.js` (`definePlugin`) + sandboxed iframe client. `manifest.ui.card`/`.search`/`.message` declare host-rendered `{{...}}` templates; the CLI lints them at build time (`packages/cli/src/template-lint.ts`), so a template that renders in the workbench renders in the app.
|
|
32
|
+
- **SDK closure:** plugin code may import only `@hoardodile/{i18n,ui,sdk-*}`; terminal packages (`cli`, `host`, `host-web`, `workbench`) are never imported by a plugin. `plugins/host/src/hooks.ts` is the only path to invoke plugin hooks.
|
|
33
|
+
|
|
34
|
+
## Testing
|
|
35
|
+
|
|
36
|
+
Vitest unit tests use `createResourceAPIFixture` (in-memory); the sandboxed path is exercised via `hoardodile plugin run detect testdata --plugin-dir dist` — the exact production execution path.
|
|
@@ -20,13 +20,22 @@ detect hook through the same sandboxed worker the server uses.
|
|
|
20
20
|
|
|
21
21
|
## Code style
|
|
22
22
|
|
|
23
|
-
-
|
|
23
|
+
- Run `pnpm format` (biome) then `pnpm lint` (`biome check .` + `tsc --noEmit`,
|
|
24
|
+
TypeScript strict) before committing; no `any` without a comment.
|
|
24
25
|
- Prefer type guards and `satisfies` over `as`.
|
|
25
26
|
- Keep the plugin's own logic in `src/`; `manifest.json` at the repo root is
|
|
26
27
|
the single source of truth for the plugin's identity, permissions and UI
|
|
27
28
|
preferences.
|
|
28
29
|
- Add tests next to the code they cover; keep the fixture API usage minimal.
|
|
29
30
|
|
|
31
|
+
## Git hooks
|
|
32
|
+
|
|
33
|
+
`lefthook.yml` is installed by `pnpm postinstall` when the repo has a `.git`,
|
|
34
|
+
or on demand with `lefthook install`. `commit-msg` enforces Conventional
|
|
35
|
+
Commits (the format `pnpm release` uses to write `CHANGELOG.md`), and
|
|
36
|
+
`pre-commit` runs `pnpm lint-staged` (biome on staged files) + `pnpm lint`.
|
|
37
|
+
`AGENTS.md` follows the repo's casing convention (uppercase).
|
|
38
|
+
|
|
30
39
|
## Releases
|
|
31
40
|
|
|
32
41
|
Bump `version` in `manifest.json` on user-visible changes, then release with
|
package/dist/template/README.md
CHANGED
|
@@ -5,6 +5,17 @@ Copy this directory to start your own plugin.
|
|
|
5
5
|
|
|
6
6
|
## Quick start
|
|
7
7
|
|
|
8
|
+
Prefer the scaffolder — it regenerates the manifest id, rewrites
|
|
9
|
+
`manifest.json`/`package.json` and injects the toolchain config
|
|
10
|
+
(`biome.json`):
|
|
11
|
+
|
|
12
|
+
```bash
|
|
13
|
+
pnpm dlx create-hoardodile-plugin my-plugin # or: hoardodile plugin create my-plugin
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
To start from the repo copy instead (never edit it in place — CI compiles
|
|
17
|
+
this copy), add a `biome.json` and a fresh manifest UUID yourself:
|
|
18
|
+
|
|
8
19
|
```bash
|
|
9
20
|
# 1. Copy the template (never edit it in place — CI compiles this copy)
|
|
10
21
|
cp -r plugins/template my-plugin
|
|
@@ -61,7 +72,11 @@ server uses — the exact production execution path.
|
|
|
61
72
|
release.
|
|
62
73
|
- The plugin's version is independent of the hoardodile release
|
|
63
74
|
version; bump it on user-visible changes.
|
|
64
|
-
- Dev loop: Node ≥ 24, pnpm 11.
|
|
75
|
+
- Dev loop: Node ≥ 24, pnpm 11. Toolchain mirrors hoardodile: `biome`
|
|
76
|
+
(format + lint via `pnpm format` / `pnpm lint`), `lefthook` (git hooks —
|
|
77
|
+
`commit-msg` Conventional Commits + `pre-commit` biome/tsc; installed by
|
|
78
|
+
`pnpm install` in a git repo, or `lefthook install` by hand), `vitest`, and
|
|
79
|
+
`release-it` (writes `CHANGELOG.md`, tags `v<version>`).
|
|
65
80
|
|
|
66
81
|
## Deploying
|
|
67
82
|
|
|
@@ -4,16 +4,27 @@
|
|
|
4
4
|
"private": true,
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|
|
7
|
+
"engines": {
|
|
8
|
+
"node": ">=24"
|
|
9
|
+
},
|
|
7
10
|
"scripts": {
|
|
8
11
|
"dev": "hoardodile plugin dev",
|
|
9
12
|
"build": "hoardodile plugin build",
|
|
10
13
|
"watch": "hoardodile plugin build --watch",
|
|
11
14
|
"detect:smoke": "hoardodile plugin run detect testdata --plugin-dir dist",
|
|
12
|
-
"lint": "tsc --noEmit",
|
|
15
|
+
"lint": "biome check . && tsc --noEmit",
|
|
16
|
+
"format": "biome check --write",
|
|
17
|
+
"lint-staged": "lint-staged --max-arg-length=6000",
|
|
18
|
+
"postinstall": "node scripts/setup-hooks.mjs",
|
|
13
19
|
"test": "vitest run",
|
|
14
20
|
"readme:check": "node scripts/check-readme.mjs",
|
|
15
21
|
"release": "node scripts/release.mjs"
|
|
16
22
|
},
|
|
23
|
+
"lint-staged": {
|
|
24
|
+
"*.{js,ts,cjs,mjs,d.cts,d.mts,jsx,tsx,json,jsonc}": [
|
|
25
|
+
"biome check --write --no-errors-on-unmatched"
|
|
26
|
+
]
|
|
27
|
+
},
|
|
17
28
|
"release-it": {
|
|
18
29
|
"git": {
|
|
19
30
|
"commitMessage": "chore(release): v${version}",
|
|
@@ -57,6 +68,9 @@
|
|
|
57
68
|
"vite": "catalog:",
|
|
58
69
|
"vitest": "catalog:",
|
|
59
70
|
"release-it": "catalog:",
|
|
60
|
-
"@release-it/conventional-changelog": "catalog:"
|
|
71
|
+
"@release-it/conventional-changelog": "catalog:",
|
|
72
|
+
"@biomejs/biome": "catalog:",
|
|
73
|
+
"lefthook": "catalog:",
|
|
74
|
+
"lint-staged": "catalog:"
|
|
61
75
|
}
|
|
62
76
|
}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* Install git hooks via lefthook when this plugin is a git repository.
|
|
4
|
+
*
|
|
5
|
+
* Run from `postinstall` (see package.json). It is deliberately guarded:
|
|
6
|
+
* inside the hoardodile monorepo `plugins/template` has no `.git` of its
|
|
7
|
+
* own, and `create-hoardodile-plugin --tarballs` scaffolds into a temp
|
|
8
|
+
* directory with no git repo at all — in both cases installing hooks would
|
|
9
|
+
* either fail or write into the wrong repository, so we skip them.
|
|
10
|
+
*
|
|
11
|
+
* When a scaffolded/`cp`-ed plugin is in a real git repo, `lefthook install`
|
|
12
|
+
* wires up the hooks declared in lefthook.yml. Re-run `lefthook install` if
|
|
13
|
+
* you `git init` after dependency install.
|
|
14
|
+
*/
|
|
15
|
+
|
|
16
|
+
import { spawnSync } from "node:child_process"
|
|
17
|
+
import { existsSync } from "node:fs"
|
|
18
|
+
|
|
19
|
+
if (!existsSync(".git")) {
|
|
20
|
+
console.log("[plugin] no git repo found — skipping lefthook install")
|
|
21
|
+
process.exit(0)
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
const result = spawnSync("lefthook", ["install"], {
|
|
25
|
+
stdio: "inherit",
|
|
26
|
+
shell: process.platform === "win32",
|
|
27
|
+
})
|
|
28
|
+
if (result.error) {
|
|
29
|
+
console.error(`[plugin] lefthook install failed: ${result.error}`)
|
|
30
|
+
process.exit(1)
|
|
31
|
+
}
|
|
32
|
+
process.exit(result.status ?? 1)
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* Validates commit messages against the Conventional Commits format, which
|
|
4
|
+
* release-it relies on to generate the changelog and pick version bumps.
|
|
5
|
+
*
|
|
6
|
+
* Zero-dependency replacement for commitlint, adapted from hoardodile's
|
|
7
|
+
* scripts/verify-commit.mjs. Rules match the old @commitlint/config-conventional
|
|
8
|
+
* setup: a conventional header with one of the standard types, max 100 chars.
|
|
9
|
+
*
|
|
10
|
+
* Usage:
|
|
11
|
+
* node scripts/verify-commit.mjs <commit-msg-file>
|
|
12
|
+
*
|
|
13
|
+
* lefthook's commit-msg hook passes the message file path as {1}.
|
|
14
|
+
*/
|
|
15
|
+
|
|
16
|
+
import { readFileSync } from "node:fs"
|
|
17
|
+
import { styleText } from "node:util"
|
|
18
|
+
|
|
19
|
+
const msgPath = process.argv[2] || ".git/COMMIT_EDITMSG"
|
|
20
|
+
const header = readFileSync(msgPath, "utf-8").split("\n", 1)[0].trim()
|
|
21
|
+
|
|
22
|
+
const COMMIT_RE =
|
|
23
|
+
/^(build|chore|ci|docs|feat|fix|perf|refactor|revert|style|test)(\(.+\))?!?: .+/
|
|
24
|
+
|
|
25
|
+
if (!COMMIT_RE.test(header) || header.length > 100) {
|
|
26
|
+
console.error()
|
|
27
|
+
console.error(
|
|
28
|
+
` ${styleText(["bgRed", "white"], " ERROR ")} ${styleText("red", "invalid commit message format.")}\n\n` +
|
|
29
|
+
styleText(
|
|
30
|
+
"red",
|
|
31
|
+
" Proper commit message format is required for automated changelog generation. Examples:\n\n",
|
|
32
|
+
) +
|
|
33
|
+
` ${styleText("green", "feat(server): support out-of-tree plugin development")}\n` +
|
|
34
|
+
` ${styleText("green", "fix(web): abort stale query when switching libraries")}\n\n` +
|
|
35
|
+
styleText("red", " See CONTRIBUTING.md for more details.\n"),
|
|
36
|
+
)
|
|
37
|
+
process.exit(1)
|
|
38
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@hoardodile/create-plugin",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.8",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"description": "Scaffold a hoardodile content plugin.",
|
|
6
6
|
"keywords": [
|
|
@@ -37,7 +37,7 @@
|
|
|
37
37
|
"dependencies": {
|
|
38
38
|
"@clack/prompts": "^1.7.0",
|
|
39
39
|
"zod": "^4.4.3",
|
|
40
|
-
"@hoardodile/sdk-types": "0.1.
|
|
40
|
+
"@hoardodile/sdk-types": "0.1.8"
|
|
41
41
|
},
|
|
42
42
|
"devDependencies": {
|
|
43
43
|
"@types/node": "^26.2.0",
|
|
@@ -46,9 +46,8 @@
|
|
|
46
46
|
"vitest": "^4.1.11"
|
|
47
47
|
},
|
|
48
48
|
"scripts": {
|
|
49
|
-
"build": "
|
|
49
|
+
"build": "tsup && node scripts/copy-template.mjs",
|
|
50
50
|
"lint": "tsc --noEmit",
|
|
51
|
-
"test": "vitest run"
|
|
52
|
-
"template:check": "node scripts/sync-template.mjs --check"
|
|
51
|
+
"test": "vitest run"
|
|
53
52
|
}
|
|
54
53
|
}
|