@hoardodile/create-plugin 0.1.5 → 0.1.6
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/dist/template/.github/workflows/release.yml +4 -4
- package/dist/template/CONTRIBUTING.md +2 -2
- package/dist/template/README.md +24 -20
- package/dist/template/package.json +1 -1
- package/dist/template/{intro/intro.en.md → readme/README.md} +2 -2
- package/dist/template/{intro/intro.zh.md → readme/README.zh.md} +2 -2
- package/dist/template/scripts/{check-intro.mjs → check-readme.mjs} +31 -32
- package/package.json +2 -2
|
@@ -43,15 +43,15 @@ jobs:
|
|
|
43
43
|
- name: Package the release artifacts
|
|
44
44
|
run: pnpm exec hoardodile plugin package --skip-build
|
|
45
45
|
|
|
46
|
-
- name: Gate the
|
|
47
|
-
run: pnpm
|
|
46
|
+
- name: Gate the readme (readme/ must be flat, images by bare filename)
|
|
47
|
+
run: pnpm readme:check
|
|
48
48
|
|
|
49
49
|
- name: Publish the GitHub release
|
|
50
50
|
env:
|
|
51
51
|
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
|
52
52
|
run: |
|
|
53
53
|
files=$(ls release/*)
|
|
54
|
-
if [ -d
|
|
55
|
-
files="$files
|
|
54
|
+
if [ -d readme ]; then
|
|
55
|
+
files="$files readme/*"
|
|
56
56
|
fi
|
|
57
57
|
gh release create "$GITHUB_REF_NAME" $files --generate-notes
|
|
@@ -51,8 +51,8 @@ Pushing the tag by hand still works as a fallback:
|
|
|
51
51
|
## Marketplace publishing
|
|
52
52
|
|
|
53
53
|
1. Add the repository address to your registry repo's `registry.json`.
|
|
54
|
-
2. Ship `
|
|
55
|
-
version-pinned
|
|
54
|
+
2. Ship a `README.md` fallback plus `README.<locale>.md` files (see the
|
|
55
|
+
README) so each release carries a version-pinned readme the app can show.
|
|
56
56
|
|
|
57
57
|
## Issues and pull requests
|
|
58
58
|
|
package/dist/template/README.md
CHANGED
|
@@ -50,7 +50,7 @@ server uses — the exact production execution path.
|
|
|
50
50
|
- hoardodile **≥ 0.1.1** — the built-in plugin marketplace
|
|
51
51
|
(**Settings → Marketplace**) and the batched asset-download API
|
|
52
52
|
(`download([…])`) when the manifest declares `"download": true`.
|
|
53
|
-
The marketplace's
|
|
53
|
+
The marketplace's Readme and Release notes tabs and release readme
|
|
54
54
|
assets are read by newer builds; older builds still list and install
|
|
55
55
|
the plugin normally.
|
|
56
56
|
- `"minAppVersion"` in `manifest.json` declares the lowest hoardodile
|
|
@@ -97,38 +97,42 @@ git tag v<version> && git push origin v<version>
|
|
|
97
97
|
Local installs (zip upload in **Settings → Plugins**) still work for
|
|
98
98
|
private packages.
|
|
99
99
|
|
|
100
|
-
## Publishing
|
|
101
|
-
|
|
102
|
-
The marketplace detail view shows a per-release **
|
|
103
|
-
markdown
|
|
104
|
-
`
|
|
105
|
-
`release.yml` uploads the whole
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
`
|
|
109
|
-
|
|
110
|
-
|
|
100
|
+
## Publishing a readme
|
|
101
|
+
|
|
102
|
+
The marketplace detail view shows a per-release **Readme** tab. Ship the
|
|
103
|
+
readme markdown in the **`readme/` folder** as a bare **`README.md`**
|
|
104
|
+
fallback, plus one `README.<locale>.md` file per extra language (e.g.
|
|
105
|
+
`readme/README.md`, `readme/README.zh.md`) — `release.yml` uploads the whole
|
|
106
|
+
folder alongside the zip, so **each release carries its own readme** and
|
|
107
|
+
every version shows independent notes.
|
|
108
|
+
`README.md` is the fallback the app shows for any language without a
|
|
109
|
+
specific file, so English normally lives there and you do **not** need a
|
|
110
|
+
`README.en.md`. Use the app's supported language codes for the extra files
|
|
111
|
+
(`zh`, `ja`, `de`, `es`) — a region-coded name like `README.zh-CN.md` only
|
|
112
|
+
matches a UI language resolved to that exact code, so `README.zh.md` is what
|
|
113
|
+
Chinese users see.
|
|
111
114
|
|
|
112
115
|
### Adding images
|
|
113
116
|
|
|
114
|
-
|
|
117
|
+
A readme may reference images. Place the image in `readme/` and
|
|
115
118
|
reference it by its **bare filename**:
|
|
116
119
|
|
|
117
120
|
```md
|
|
118
121
|

|
|
119
122
|
```
|
|
120
123
|
|
|
121
|
-
Every file in `
|
|
124
|
+
Every file in `readme/` is published as a release asset on each release, and
|
|
122
125
|
the app resolves a relative image reference against that release's download
|
|
123
|
-
URL. Because a GitHub release is a flat list of assets, the `
|
|
126
|
+
URL. Because a GitHub release is a flat list of assets, the `readme/` folder
|
|
124
127
|
must stay **flat** and references must be bare filenames — a nested path
|
|
125
128
|
like `` resolves to a URL the release does not serve and
|
|
126
129
|
the image breaks. Absolute `http(s)://` and `data:` image URIs are allowed.
|
|
127
130
|
|
|
128
|
-
`pnpm
|
|
129
|
-
fails the release if `
|
|
130
|
-
`
|
|
131
|
+
`pnpm readme:check` (run by `release.yml` before publishing) gates this: it
|
|
132
|
+
fails the release if `readme/` is absent-and-required, is not flat, ships no
|
|
133
|
+
`README.md` / `README.<locale>.md`, or references an image by a
|
|
134
|
+
nested/missing path.
|
|
131
135
|
|
|
132
|
-
The app resolves the
|
|
133
|
-
language → `
|
|
136
|
+
The app resolves the readme for the user's UI language (exact locale → base
|
|
137
|
+
language → the `README.md` fallback); the release body always shows
|
|
134
138
|
in the **Release notes** tab.
|
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
"detect:smoke": "hoardodile plugin run detect testdata --plugin-dir dist",
|
|
12
12
|
"lint": "tsc --noEmit",
|
|
13
13
|
"test": "vitest run",
|
|
14
|
-
"
|
|
14
|
+
"readme:check": "node scripts/check-readme.mjs",
|
|
15
15
|
"release": "node scripts/release.mjs"
|
|
16
16
|
},
|
|
17
17
|
"release-it": {
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
# My plugin
|
|
2
2
|
|
|
3
|
-
_Replace this
|
|
3
|
+
_Replace this readme with a real overview: what content the plugin
|
|
4
4
|
detects, how it renders it, and where users see it._
|
|
5
5
|
|
|
6
|
-
This
|
|
6
|
+
This readme ships with **every GitHub release** — edit it per release so
|
|
7
7
|
each version presents its own notes in the marketplace.
|
|
8
8
|
|
|
9
9
|
## Features
|
|
@@ -1,26 +1,26 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
/**
|
|
3
|
-
* Release gate for the plugin marketplace
|
|
3
|
+
* Release gate for the plugin marketplace readme files.
|
|
4
4
|
*
|
|
5
|
-
* The marketplace reads each release's `
|
|
6
|
-
* app resolves any image referenced
|
|
7
|
-
* download URL. Because a GitHub release
|
|
8
|
-
* referenced image must be:
|
|
5
|
+
* The marketplace reads each release's `README.md` fallback asset and its
|
|
6
|
+
* `README.<locale>.md` assets, and the app resolves any image referenced
|
|
7
|
+
* inside them against the release's download URL. Because a GitHub release
|
|
8
|
+
* is a flat list of assets, every referenced image must be:
|
|
9
9
|
*
|
|
10
|
-
* 1. Shipped inside the `
|
|
10
|
+
* 1. Shipped inside the `readme/` folder (the only folder the release
|
|
11
11
|
* workflow uploads), and
|
|
12
12
|
* 2. Referenced by its bare filename (``), never a
|
|
13
13
|
* nested path (`img/shot.png`) — a nested path resolves to a URL the
|
|
14
14
|
* release does not actually serve, so the image is silently broken.
|
|
15
15
|
*
|
|
16
|
-
* This gate fails a build/release when the `
|
|
17
|
-
* not flat, ships no `
|
|
18
|
-
* nested/missing path. External `http(s)://` and `data:` image
|
|
19
|
-
* allowed (they are not release assets).
|
|
16
|
+
* This gate fails a build/release when the `readme/` folder is absent, is
|
|
17
|
+
* not flat, ships no `README.md` / `README.<locale>.md`, or references an
|
|
18
|
+
* image by a nested/missing path. External `http(s)://` and `data:` image
|
|
19
|
+
* URIs are allowed (they are not release assets).
|
|
20
20
|
*
|
|
21
21
|
* Usage:
|
|
22
|
-
* node scripts/check-
|
|
23
|
-
* node scripts/check-
|
|
22
|
+
* node scripts/check-readme.mjs # checks ./readme
|
|
23
|
+
* node scripts/check-readme.mjs <dir> # checks <dir>/readme
|
|
24
24
|
*
|
|
25
25
|
* Dependency-free on purpose — it ships inside every scaffolded plugin.
|
|
26
26
|
*/
|
|
@@ -29,16 +29,18 @@ import { existsSync, readdirSync, readFileSync, statSync } from "node:fs"
|
|
|
29
29
|
import { join, resolve } from "node:path"
|
|
30
30
|
|
|
31
31
|
const ROOT = resolve(process.argv[2] ?? process.cwd())
|
|
32
|
-
const
|
|
32
|
+
const README_DIR = join(ROOT, "readme")
|
|
33
33
|
|
|
34
34
|
const RULE_SUMMARY =
|
|
35
|
-
"
|
|
36
|
-
"bare filename that exists in
|
|
35
|
+
"readme/ must be flat; each README image is referenced by a " +
|
|
36
|
+
"bare filename that exists in readme/ (absolute http(s)/data URIs are ok)"
|
|
37
37
|
|
|
38
38
|
// ``, ``, `<img src="x">` (`src` images only).
|
|
39
39
|
const MARKDOWN_IMG_RE = /!\[[^\]]*\]\(([^)\s]+)(?:\s+["'][^"']*["'])?\)/g
|
|
40
40
|
const HTML_IMG_RE = /<img\b[^>]*\bsrc\s*=\s*(["'])([^"']+)\1/gi
|
|
41
41
|
const EXT_URL_RE = /^(?:https?:\/\/|data:)/i
|
|
42
|
+
/** `README.md` or `README.<locale>.md` — the marketplace readme assets. */
|
|
43
|
+
const README_FILE_RE = /^(?:README\.md|README\.[A-Za-z0-9-]+\.md)$/
|
|
42
44
|
|
|
43
45
|
function isRelative(src) {
|
|
44
46
|
return !EXT_URL_RE.test(src)
|
|
@@ -46,7 +48,7 @@ function isRelative(src) {
|
|
|
46
48
|
|
|
47
49
|
/**
|
|
48
50
|
* A relative reference is valid only as a flat bare filename that exists
|
|
49
|
-
* inside `
|
|
51
|
+
* inside `readme/` — not a nested path and not a missing/unshipsed file.
|
|
50
52
|
*/
|
|
51
53
|
function resolveRelativeRef(src, file, issues) {
|
|
52
54
|
const trimmed = src.trim().replace(/^\.\//, "")
|
|
@@ -64,10 +66,10 @@ function resolveRelativeRef(src, file, issues) {
|
|
|
64
66
|
)
|
|
65
67
|
return
|
|
66
68
|
}
|
|
67
|
-
const target = join(
|
|
69
|
+
const target = join(README_DIR, trimmed)
|
|
68
70
|
if (!existsSync(target) || !statSync(target).isFile()) {
|
|
69
71
|
issues.push(
|
|
70
|
-
`${file}: image "${src}" is not in
|
|
72
|
+
`${file}: image "${src}" is not in readme/ — every referenced image must be committed there (it is published with the release)`,
|
|
71
73
|
)
|
|
72
74
|
}
|
|
73
75
|
}
|
|
@@ -80,55 +82,52 @@ function collectImageRefs(text) {
|
|
|
80
82
|
}
|
|
81
83
|
|
|
82
84
|
function main() {
|
|
83
|
-
if (!existsSync(
|
|
85
|
+
if (!existsSync(README_DIR)) {
|
|
84
86
|
console.log(
|
|
85
|
-
"[check-
|
|
87
|
+
"[check-readme] no readme/ folder — nothing to gate (a release without a readme is valid).",
|
|
86
88
|
)
|
|
87
89
|
return
|
|
88
90
|
}
|
|
89
91
|
|
|
90
|
-
const entries = readdirSync(
|
|
92
|
+
const entries = readdirSync(README_DIR, { withFileTypes: true })
|
|
91
93
|
const issues = []
|
|
92
94
|
|
|
93
95
|
// 1. Flat-only: any subdirectory makes the folder publish incorrectly.
|
|
94
96
|
for (const entry of entries) {
|
|
95
97
|
if (entry.isDirectory()) {
|
|
96
98
|
issues.push(
|
|
97
|
-
`
|
|
99
|
+
`readme/${entry.name}/ is a subdirectory — the readme folder must be flat (release assets are a flat list)`,
|
|
98
100
|
)
|
|
99
101
|
}
|
|
100
102
|
}
|
|
101
103
|
|
|
102
104
|
const mdFiles = entries
|
|
103
|
-
.filter(
|
|
104
|
-
(entry) =>
|
|
105
|
-
entry.isFile() && /^intro\.[A-Za-z0-9-]+\.md$/.test(entry.name),
|
|
106
|
-
)
|
|
105
|
+
.filter((entry) => entry.isFile() && README_FILE_RE.test(entry.name))
|
|
107
106
|
.map((entry) => entry.name)
|
|
108
107
|
|
|
109
108
|
if (mdFiles.length === 0) {
|
|
110
109
|
issues.push(
|
|
111
|
-
"
|
|
110
|
+
"readme/ has no README.md / README.<locale>.md — the release would ship images but the marketplace could not display a readme",
|
|
112
111
|
)
|
|
113
112
|
}
|
|
114
113
|
|
|
115
|
-
// 2. Flat, present image references inside each
|
|
114
|
+
// 2. Flat, present image references inside each readme markdown.
|
|
116
115
|
for (const name of mdFiles) {
|
|
117
|
-
const text = readFileSync(join(
|
|
116
|
+
const text = readFileSync(join(README_DIR, name), "utf-8")
|
|
118
117
|
for (const src of collectImageRefs(text)) {
|
|
119
|
-
if (isRelative(src)) resolveRelativeRef(src, `
|
|
118
|
+
if (isRelative(src)) resolveRelativeRef(src, `readme/${name}`, issues)
|
|
120
119
|
}
|
|
121
120
|
}
|
|
122
121
|
|
|
123
122
|
if (issues.length > 0) {
|
|
124
|
-
console.error("[check-
|
|
123
|
+
console.error("[check-readme] gate failed:")
|
|
125
124
|
for (const issue of issues) console.error(` - ${issue}`)
|
|
126
125
|
console.error(`\n${RULE_SUMMARY}`)
|
|
127
126
|
process.exit(1)
|
|
128
127
|
}
|
|
129
128
|
|
|
130
129
|
console.log(
|
|
131
|
-
`[check-
|
|
130
|
+
`[check-readme] readme/ ok — ${mdFiles.length} readme file(s), flat references only.`,
|
|
132
131
|
)
|
|
133
132
|
}
|
|
134
133
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@hoardodile/create-plugin",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.6",
|
|
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.6"
|
|
41
41
|
},
|
|
42
42
|
"devDependencies": {
|
|
43
43
|
"@types/node": "^26.2.0",
|