@mnaoumov/create-obsidian-plugin 1.3.0 → 1.4.0
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/CHANGELOG.md +137 -0
- package/README.md +23 -6
- package/dist/main.js +42 -115
- package/package.json +1 -1
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
All notable changes to this project will be documented in this file.
|
|
4
|
+
|
|
5
|
+
## 1.4.0 (2026-11-01)
|
|
6
|
+
|
|
7
|
+
### Features
|
|
8
|
+
|
|
9
|
+
- The `official-sample` preset writes the `LICENSE` copyright line as `Copyright (C) <year> by <author>`, keeping upstream's 0BSD text.
|
|
10
|
+
- A See also section compares similar scaffolders, in the README and on the docs site.
|
|
11
|
+
- `CHANGELOG.md`, back-filled from the tags, linked from the README and the docs site.
|
|
12
|
+
- Tool names are marked as code in all Markdown prose, with a test that catches a bare one.
|
|
13
|
+
|
|
14
|
+
### Removed
|
|
15
|
+
|
|
16
|
+
- The `enhanced` preset alias: `--preset=enhanced` now fails validation like any unknown value.
|
|
17
|
+
- Every migration of an older `.create-obsidian-plugin.json` answer shape (`framework`, `cssMode`, `buildSystem`, `gitHubFunding`, a missing `defaultBranch` or `coverageBadge`, a stored `pluginShortName`).
|
|
18
|
+
|
|
19
|
+
[Full diff](https://github.com/mnaoumov/create-obsidian-plugin/compare/1.3.0...1.4.0)
|
|
20
|
+
## 1.3.0 (2026-11-01)
|
|
21
|
+
|
|
22
|
+
### Features
|
|
23
|
+
|
|
24
|
+
- **cli:** quote the -- separator in --help and the answers-file hint
|
|
25
|
+
|
|
26
|
+
[Full diff](https://github.com/mnaoumov/create-obsidian-plugin/compare/1.2.0...1.3.0)
|
|
27
|
+
|
|
28
|
+
## 1.2.0 (2026-11-01)
|
|
29
|
+
|
|
30
|
+
### Features
|
|
31
|
+
|
|
32
|
+
- **template:** add a Similar plugins section to the generated README
|
|
33
|
+
- **template:** let a parenthesized folder group templates without entering their path
|
|
34
|
+
- **issue-templates:** give the bug form field ids that bug-report links prefill
|
|
35
|
+
- tell AI agents how to file an issue when they skip the forms
|
|
36
|
+
- scaffold the Report a bug command whenever issue forms are chosen
|
|
37
|
+
|
|
38
|
+
### Fixes
|
|
39
|
+
|
|
40
|
+
- **deps:** float brace-expansion to 5.0.12 and fast-uri to 3.1.9 past their advisories
|
|
41
|
+
- **versions:** drop the smol-toml advisory override now that typescript-dev-toolkit 3.7.3 pins 1.9.1
|
|
42
|
+
- **templates:** select .obsidian-lib in the framework stylesheets
|
|
43
|
+
- **templates:** show the plugin name as code in the demo sample view
|
|
44
|
+
- **gate:** report a missing package manager as could not run, not a failed install
|
|
45
|
+
- **bundler:** leave esbuild to obsidian-dev-toolkit on the framework presets
|
|
46
|
+
- **lint:** discover root ESLint config for scripts
|
|
47
|
+
- **lint:** reconcile unused binding checks with ESLint 10
|
|
48
|
+
- **templates:** format Solid readonly configuration comment
|
|
49
|
+
- override vulnerable development KaTeX dependency
|
|
50
|
+
- **demo:** avoid keyword identifier in number setting callback
|
|
51
|
+
- **demo:** stop scaffolding the gitignored scratch note
|
|
52
|
+
- **versions:** override re2js to ^2.9.1 in scaffolded projects for GHSA-gg6h-894w-4m9c
|
|
53
|
+
- **spell-checker:** know "toolkits" in the emitted cspell.json
|
|
54
|
+
- **versions:** hold toolkit-declared packages in the toolkit's range
|
|
55
|
+
|
|
56
|
+
[Full diff](https://github.com/mnaoumov/create-obsidian-plugin/compare/1.1.0...1.2.0)
|
|
57
|
+
|
|
58
|
+
## 1.1.0 (2026-10-21)
|
|
59
|
+
|
|
60
|
+
### Features
|
|
61
|
+
|
|
62
|
+
- scaffold onto @mnaoumov/obsidian-lib and the @mnaoumov toolkits
|
|
63
|
+
- **templates:** take commitlint and nano-staged configs from typescript-dev-toolkit
|
|
64
|
+
- **preset:** keep preset "enhanced" working as a deprecated alias of all-in-one
|
|
65
|
+
- **preset:** label the preset All-in-one Framework and lead its hint with the helper library
|
|
66
|
+
- **preset:** add the Official sample plugin preset
|
|
67
|
+
- **docs-showcase:** extract, wrap and type-check the developer docs' code blocks
|
|
68
|
+
- **preset:** add the Developer docs showcase preset
|
|
69
|
+
- **upstream-sync:** watch every upstream and open one update PR per change
|
|
70
|
+
- **docs-showcase:** link each snippet's docs page from what its command shows
|
|
71
|
+
- **templates:** link Discussions from the issue chooser
|
|
72
|
+
- **formatter:** run prettier and biome through the toolkit runners
|
|
73
|
+
- **test-runner:** run jest through the toolkits on the framework presets
|
|
74
|
+
- **e2e:** run wdio-obsidian and obsidian-test through obsidian-dev-toolkit
|
|
75
|
+
- **github-actions:** call obsidian-dev-toolkit's reusable workflows
|
|
76
|
+
- **bundler:** build every bundler through obsidian-dev-toolkit's adapters
|
|
77
|
+
- **scaffold:** emit .nvmrc so CI runs the project's Node, not the latest LTS
|
|
78
|
+
- **docs-showcase:** recognize a code-examples-only LICENSE and refuse unknown ones
|
|
79
|
+
|
|
80
|
+
### Fixes
|
|
81
|
+
|
|
82
|
+
- **demo-vault:** import configureCommunityPlugin from @mnaoumov/obsidian-lib
|
|
83
|
+
- **dprint:** keep JSX attributes double-quoted
|
|
84
|
+
- **versions:** override smol-toml to clear GHSA-r4xh-jqrq-34v2
|
|
85
|
+
- **verify-tarball:** read npm 12's keyed npm pack --json output as well as the array
|
|
86
|
+
- **templates:** emit nano-staged config patterns in sorted order
|
|
87
|
+
- **templates:** require CodeScript Toolkit 13.8.0 for the demo-vault injection
|
|
88
|
+
- **templates:** drop the preact view's import-x/no-deprecated directives for preact 11
|
|
89
|
+
- **templates:** remove the three directives the previous commit left staged out
|
|
90
|
+
- **cli:** let render:case and gate:case read a deprecated answer value as the CLI does
|
|
91
|
+
- **update:** ask every question again when the user chooses to change settings
|
|
92
|
+
- **templates:** pass the framework scaffolds' own lint, format and audit gate
|
|
93
|
+
- **verify:** read the pins step's checks from under the pins key
|
|
94
|
+
- **versions:** pin @codemirror/state and view to obsidian 1.14.4's peers
|
|
95
|
+
- **prompts:** offer "Keep current tooling" on update and make "Use recommended defaults" apply them
|
|
96
|
+
- **cli-args:** look the mode up instead of asserting a string to the Mode enum
|
|
97
|
+
- **templates:** clear what the toolkit runners and verify:projects found
|
|
98
|
+
- **templates:** keep biome and dprint agreeing on the framework configs
|
|
99
|
+
- **standalone:** clear the gate failures the wdio install used to hide
|
|
100
|
+
- **templates:** reach the CSS module through its class names so vite keeps it
|
|
101
|
+
- **test:** declare sweepCases' void return so tsc accepts the generator
|
|
102
|
+
- **templates:** ignore coverage/ in git and cspell, spell-check staged dotfiles
|
|
103
|
+
- **scaffold:** install the git hooks after git init and make the first commit conventional
|
|
104
|
+
- **demo-vault:** stop linking to the gitignored scratch note
|
|
105
|
+
- **cspell:** know the word the template's word list adds for the demo vault .gitignore
|
|
106
|
+
- **plugin-drift:** leave generator-scaffolded plugins out of the real-plugin set
|
|
107
|
+
- **versions:** read the smol-toml pin off typescript-dev-toolkit's markdownlint-cli2
|
|
108
|
+
- **templates:** mark the plugin name as code in the README and demo-vault prose
|
|
109
|
+
|
|
110
|
+
[Full diff](https://github.com/mnaoumov/create-obsidian-plugin/compare/1.0.2...1.1.0)
|
|
111
|
+
|
|
112
|
+
## 1.0.2 (2026-10-10)
|
|
113
|
+
|
|
114
|
+
### Features
|
|
115
|
+
|
|
116
|
+
- verify obsidian-dev-utils source links against its checkout
|
|
117
|
+
|
|
118
|
+
### Fixes
|
|
119
|
+
|
|
120
|
+
- **verify-tarball:** pack with --ignore-scripts so HUSKY=0 output cannot break the JSON
|
|
121
|
+
- **templates:** point dead upstream links at their current homes
|
|
122
|
+
|
|
123
|
+
[Full diff](https://github.com/mnaoumov/create-obsidian-plugin/compare/1.0.1...1.0.2)
|
|
124
|
+
|
|
125
|
+
## 1.0.1 (2026-10-07)
|
|
126
|
+
|
|
127
|
+
### Other
|
|
128
|
+
|
|
129
|
+
- publish to npm from GitHub Actions with trusted publishing
|
|
130
|
+
|
|
131
|
+
[Full diff](https://github.com/mnaoumov/create-obsidian-plugin/compare/1.0.0...1.0.1)
|
|
132
|
+
|
|
133
|
+
## 1.0.0 (2026-10-07)
|
|
134
|
+
|
|
135
|
+
Initial release.
|
|
136
|
+
|
|
137
|
+
[Commits](https://github.com/mnaoumov/create-obsidian-plugin/commits/1.0.0)
|
package/README.md
CHANGED
|
@@ -12,7 +12,7 @@ A scaffolding CLI that generates [Obsidian](https://obsidian.md/) plugin project
|
|
|
12
12
|
npm create @mnaoumov/obsidian-plugin
|
|
13
13
|
```
|
|
14
14
|
|
|
15
|
-
This walks you through an interactive wizard, then scaffolds the project into a new `obsidian-<plugin id>` folder. pnpm
|
|
15
|
+
This walks you through an interactive wizard, then scaffolds the project into a new `obsidian-<plugin id>` folder. `pnpm`, `yarn` and `bun` work too: see [Getting started](https://mnaoumov.dev/create-obsidian-plugin/guides/getting-started/).
|
|
16
16
|
|
|
17
17
|
Run the same command inside a project it created to [update it](https://mnaoumov.dev/create-obsidian-plugin/guides/updating/) to the latest templates, keeping the files you changed.
|
|
18
18
|
|
|
@@ -24,10 +24,31 @@ Answer every question up front and skip the prompts:
|
|
|
24
24
|
npm create @mnaoumov/obsidian-plugin "--" --yes --pluginId=my-tool --packageManager=npm
|
|
25
25
|
```
|
|
26
26
|
|
|
27
|
-
Keep the quotes around `"--"`: in PowerShell a bare `--` is swallowed before npm sees it. See [The -- separator in PowerShell](https://mnaoumov.dev/create-obsidian-plugin/guides/powershell-separator/). Every option is listed in [Command-line options](https://mnaoumov.dev/create-obsidian-plugin/guides/command-line/).
|
|
27
|
+
Keep the quotes around `"--"`: in PowerShell a bare `--` is swallowed before `npm` sees it. See [The -- separator in PowerShell](https://mnaoumov.dev/create-obsidian-plugin/guides/powershell-separator/). Every option is listed in [Command-line options](https://mnaoumov.dev/create-obsidian-plugin/guides/command-line/).
|
|
28
28
|
|
|
29
29
|
[Sample Plugin Extended](https://github.com/mnaoumov/obsidian-sample-plugin-extended) is a plugin generated with this tool.
|
|
30
30
|
|
|
31
|
+
## See also
|
|
32
|
+
|
|
33
|
+
[My other Obsidian resources](https://github.com/mnaoumov/obsidian-resources)
|
|
34
|
+
|
|
35
|
+
| Tool | Features | How `create-obsidian-plugin` differs |
|
|
36
|
+
| --- | --- | --- |
|
|
37
|
+
| [`@aoout/create-obsidian-plugin`](https://www.npmjs.com/package/@aoout/create-obsidian-plugin) | `npm create` wizard, forked from zephraph's `create-obsidian-plugin`; `i18next`; a tag-triggered release workflow; a desktop-only option; `git init` and an optional GitHub repository | Same goal; adds: presets; a choice of bundler, UI framework, linter, formatter and test runner; an update in place that keeps the files you edited; `--yes` and answers export |
|
|
38
|
+
| [`cookiecutter-obsidian-plugin`](https://github.com/serafinovsky/cookiecutter-obsidian-plugin) | A Python `cookiecutter` template; TypeScript, `esbuild`, `eslint`, `prettier`; optional `vitest` and i18n; a release workflow | Same goal; runs on Node.js through `npm create`, with no Python; adds: presets; five bundlers; UI frameworks; an update in place |
|
|
39
|
+
| [`create-obsidian-plugin`](https://www.npmjs.com/package/create-obsidian-plugin) | The `npm init obsidian-plugin` wizard by zephraph; asks for the id and name; a TypeScript plugin whose `dev` and `build` scripts run `obsidian-plugin-cli` | Same goal; adds: presets; a tooling choice in each category; an update in place; `--yes` and answers export |
|
|
40
|
+
| [`create-obsidian-plugin-pnpm`](https://www.npmjs.com/package/create-obsidian-plugin-pnpm) | An `npm create` scaffolder that copies one fixed template; `pnpm`, strict TypeScript, `esbuild`, `eslint`; CI and draft-release workflows; a declarative settings example | Same goal; adds: `npm`, `yarn` and `bun`; presets; a choice of bundler, UI framework, linter, formatter and test runner; an update in place |
|
|
41
|
+
| [`dsebastien/obsidian-plugin-template`](https://github.com/dsebastien/obsidian-plugin-template) | A GitHub template repository; `bun`, strict TypeScript, `tailwindcss` 4, `eslint`, `prettier`, `husky` with `lint-staged`, `commitizen`; CI and release workflows | A generator rather than a template you copy and rename by hand; fills in your plugin's identity from prompts or flags; a choice in each tooling category; an update in place |
|
|
42
|
+
| [`generator-obsidian-plugin`](https://github.com/mnaoumov/generator-obsidian-plugin) (my tool) | A Yeoman generator (`yo obsidian-plugin`); `obsidian-typings`, `eslint`, `cspell`, `dprint`, `obsidian-dev-utils` scripts; `svelte` and `react` examples; `sass` | Supersedes it; needs no global `yo`; adds: presets; a choice in each tooling category; an update in place that keeps the files you edited; `--yes` |
|
|
43
|
+
| [`nolebase/obsidian-plugin-template`](https://github.com/nolebase/obsidian-plugin-template) | A GitHub template repository; TypeScript, `unbuild`, `eslint`, `cspell`, `pnpm`; publishing on release | A generator rather than a template you copy and rename; five bundlers, `unbuild` not among them; UI frameworks; an update in place |
|
|
44
|
+
| [`obsidianmd/obsidian-sample-plugin`](https://github.com/obsidianmd/obsidian-sample-plugin) | Obsidian's official template repository; TypeScript, `esbuild`, `eslint` with `eslint-plugin-obsidianmd`, `npm`; sample code | Extends it: the `official-sample` preset copies it with your id, name, author, description and license holder filled in, and can update it later; the other presets add a choice of tooling |
|
|
45
|
+
|
|
46
|
+
Last searched for similar tools on 2026-11-01.
|
|
47
|
+
|
|
48
|
+
## Changelog
|
|
49
|
+
|
|
50
|
+
All notable changes to this project will be documented in the [CHANGELOG](./CHANGELOG.md).
|
|
51
|
+
|
|
31
52
|
## Contributing
|
|
32
53
|
|
|
33
54
|
Contributions are welcome — see [CONTRIBUTING](./CONTRIBUTING.md) to get set up.
|
|
@@ -46,10 +67,6 @@ Open issues through the [issue forms](https://github.com/mnaoumov/create-obsidia
|
|
|
46
67
|
|
|
47
68
|
Filing from the CLI or the API (`gh issue create --body`, or an AI agent filing for you) skips the form, and `gh issue create --template` cannot load an issue form. In that case, open the matching form in [`.github/ISSUE_TEMPLATE/`](https://github.com/mnaoumov/create-obsidian-plugin/tree/HEAD/.github/ISSUE_TEMPLATE), copy every field as a `### <label>` heading in the form's order, and fill in each one. Always state the version you used and your operating system.
|
|
48
69
|
|
|
49
|
-
## My other Obsidian resources
|
|
50
|
-
|
|
51
|
-
[My other Obsidian resources](https://github.com/mnaoumov/obsidian-resources)
|
|
52
|
-
|
|
53
70
|
## License
|
|
54
71
|
|
|
55
72
|
© [Michael Naumov](https://github.com/mnaoumov/)
|
package/dist/main.js
CHANGED
|
@@ -5,7 +5,7 @@ import {
|
|
|
5
5
|
cancel as cancel3,
|
|
6
6
|
confirm,
|
|
7
7
|
intro,
|
|
8
|
-
log as
|
|
8
|
+
log as log4,
|
|
9
9
|
note,
|
|
10
10
|
outro,
|
|
11
11
|
select as select2,
|
|
@@ -179,7 +179,7 @@ var OFFICIAL_SAMPLE_ANSWER_KEYS = [
|
|
|
179
179
|
var OfficialSample = class extends FeatureOption {
|
|
180
180
|
constructor() {
|
|
181
181
|
super({
|
|
182
|
-
promptHint: "Exactly obsidianmd/obsidian-sample-plugin, with your plugin's id, name, author and
|
|
182
|
+
promptHint: "Exactly obsidianmd/obsidian-sample-plugin, with your plugin's id, name, author, description and license holder filled in",
|
|
183
183
|
promptLabel: "Official sample plugin",
|
|
184
184
|
settingValue: OFFICIAL_SAMPLE_PRESET
|
|
185
185
|
});
|
|
@@ -2555,26 +2555,8 @@ function getPluginShortName(pluginId) {
|
|
|
2555
2555
|
return pluginId.split("-").map((word) => (word[0] ?? "").toUpperCase() + word.slice(1)).join("");
|
|
2556
2556
|
}
|
|
2557
2557
|
|
|
2558
|
-
// src/answers/deprecated-answers.ts
|
|
2559
|
-
import { log as log2 } from "@clack/prompts";
|
|
2560
|
-
var DEPRECATED_ANSWER_VALUES = {
|
|
2561
|
-
preset: {
|
|
2562
|
-
enhanced: "all-in-one"
|
|
2563
|
-
}
|
|
2564
|
-
};
|
|
2565
|
-
function resolveDeprecatedAnswer(key, value, source) {
|
|
2566
|
-
const replacement = DEPRECATED_ANSWER_VALUES[key]?.[value];
|
|
2567
|
-
if (replacement === void 0) {
|
|
2568
|
-
return value;
|
|
2569
|
-
}
|
|
2570
|
-
log2.warn(`${key} "${value}" in ${source} is deprecated and was read as "${replacement}". Use "${replacement}" instead.`);
|
|
2571
|
-
return replacement;
|
|
2572
|
-
}
|
|
2573
|
-
|
|
2574
2558
|
// src/answers/cli-args.ts
|
|
2575
2559
|
var COMPUTED_ANSWER_KEYS = ["currentYear"];
|
|
2576
|
-
var DERIVED_TEMPLATE_KEYS = ["pluginShortName"];
|
|
2577
|
-
var REFUSED_KEYS = [...COMPUTED_ANSWER_KEYS, ...DERIVED_TEMPLATE_KEYS];
|
|
2578
2560
|
var ANSWERS_FILE_PREFIX = "--answersFile=";
|
|
2579
2561
|
var CUSTOM_TEMPLATE_PREFIX = "--customTemplate=";
|
|
2580
2562
|
var MODE_PREFIX = "--mode=";
|
|
@@ -2681,7 +2663,7 @@ function parseCliArgs(argv) {
|
|
|
2681
2663
|
}
|
|
2682
2664
|
const [rawKey, rawValue] = splitFlag(argument);
|
|
2683
2665
|
const key = toAnswerKey(rawKey);
|
|
2684
|
-
flagAnswers[key] = resolveAnswer(key, rawValue
|
|
2666
|
+
flagAnswers[key] = resolveAnswer(key, rawValue);
|
|
2685
2667
|
}
|
|
2686
2668
|
const fileAnswers = answersFilePath === null ? {} : readAnswersFile(answersFilePath);
|
|
2687
2669
|
const isOfficialSample = isOfficialSamplePreset(flagAnswers.preset ?? fileAnswers.preset);
|
|
@@ -2700,13 +2682,12 @@ function parseCliArgs(argv) {
|
|
|
2700
2682
|
useDefaults
|
|
2701
2683
|
};
|
|
2702
2684
|
}
|
|
2703
|
-
function resolveAnswer(key, value
|
|
2704
|
-
|
|
2705
|
-
|
|
2706
|
-
return resolved;
|
|
2685
|
+
function resolveAnswer(key, value) {
|
|
2686
|
+
assertValidAnswer(key, value);
|
|
2687
|
+
return value;
|
|
2707
2688
|
}
|
|
2708
2689
|
function toAnswerKey(key) {
|
|
2709
|
-
if (
|
|
2690
|
+
if (COMPUTED_ANSWER_KEYS.includes(key)) {
|
|
2710
2691
|
throw new Error(`${key} is computed by the generator, not answered, so it cannot be set.`);
|
|
2711
2692
|
}
|
|
2712
2693
|
const answerable = getAnswerableKeys();
|
|
@@ -2761,14 +2742,14 @@ function readAnswersFile(path) {
|
|
|
2761
2742
|
const source = typeof wrapped === "object" && wrapped !== null ? wrapped : record;
|
|
2762
2743
|
const answers = {};
|
|
2763
2744
|
for (const [rawKey, value] of Object.entries(source)) {
|
|
2764
|
-
if (
|
|
2745
|
+
if (COMPUTED_ANSWER_KEYS.includes(rawKey)) {
|
|
2765
2746
|
continue;
|
|
2766
2747
|
}
|
|
2767
2748
|
if (typeof value !== "string") {
|
|
2768
2749
|
throw new Error(`${rawKey} in ${path} should be a string, got ${typeof value}.`);
|
|
2769
2750
|
}
|
|
2770
2751
|
const key = toAnswerKey(rawKey);
|
|
2771
|
-
answers[key] = resolveAnswer(key, value
|
|
2752
|
+
answers[key] = resolveAnswer(key, value);
|
|
2772
2753
|
}
|
|
2773
2754
|
return answers;
|
|
2774
2755
|
}
|
|
@@ -2887,7 +2868,7 @@ function validatePluginIdIsUnlisted(pluginId, listedIds) {
|
|
|
2887
2868
|
import { join as join5 } from "node:path";
|
|
2888
2869
|
|
|
2889
2870
|
// src/templates.ts
|
|
2890
|
-
import { log as
|
|
2871
|
+
import { log as log3 } from "@clack/prompts";
|
|
2891
2872
|
import ejs from "ejs";
|
|
2892
2873
|
import { createHash } from "node:crypto";
|
|
2893
2874
|
import {
|
|
@@ -3169,7 +3150,7 @@ function walkFiles(root, relativeDir) {
|
|
|
3169
3150
|
}
|
|
3170
3151
|
|
|
3171
3152
|
// src/versions.ts
|
|
3172
|
-
import { log as
|
|
3153
|
+
import { log as log2 } from "@clack/prompts";
|
|
3173
3154
|
import {
|
|
3174
3155
|
maxSatisfying,
|
|
3175
3156
|
minVersion,
|
|
@@ -3472,7 +3453,7 @@ async function resolveVersions(dependencies) {
|
|
|
3472
3453
|
}
|
|
3473
3454
|
resolved.set(packageName, resolution.spec);
|
|
3474
3455
|
if (resolution.heldBelow !== null) {
|
|
3475
|
-
|
|
3456
|
+
log2.info(`Holding ${packageName} on ${resolution.spec}, the range ${resolution.heldBy ?? ""} declares, rather than the latest ${resolution.heldBelow}.`);
|
|
3476
3457
|
}
|
|
3477
3458
|
}
|
|
3478
3459
|
for (const constrainer of CONSTRAINING_PACKAGES) {
|
|
@@ -3497,7 +3478,7 @@ async function resolveVersions(dependencies) {
|
|
|
3497
3478
|
record(rest[index] ?? "", resolution);
|
|
3498
3479
|
}
|
|
3499
3480
|
if (failed.length > 0) {
|
|
3500
|
-
|
|
3481
|
+
log2.warn(`Could not look up the latest version of ${failed.join(", ")}. Falling back to "${FALLBACK_VERSION}".`);
|
|
3501
3482
|
}
|
|
3502
3483
|
return resolved;
|
|
3503
3484
|
}
|
|
@@ -3806,10 +3787,7 @@ function loadConfig(dir) {
|
|
|
3806
3787
|
if (!existsSync4(configPath)) {
|
|
3807
3788
|
return null;
|
|
3808
3789
|
}
|
|
3809
|
-
|
|
3810
|
-
const raw = parsed;
|
|
3811
|
-
migrateAnswers(raw);
|
|
3812
|
-
return parsed;
|
|
3790
|
+
return JSON.parse(readFileSync3(configPath, "utf-8"));
|
|
3813
3791
|
}
|
|
3814
3792
|
function pinPresetAnswers(answers) {
|
|
3815
3793
|
return answers.preset === "demo" ? { ...answers, ...DEMO_PINNED_ANSWERS } : answers;
|
|
@@ -3951,25 +3929,25 @@ function importedModule(block) {
|
|
|
3951
3929
|
}
|
|
3952
3930
|
function logUpdateSummary(updated, created, skipped) {
|
|
3953
3931
|
if (updated.length > 0) {
|
|
3954
|
-
|
|
3932
|
+
log3.success(`Updated ${String(updated.length)} file(s):`);
|
|
3955
3933
|
for (const f of updated) {
|
|
3956
|
-
|
|
3934
|
+
log3.info(` ${f}`);
|
|
3957
3935
|
}
|
|
3958
3936
|
}
|
|
3959
3937
|
if (created.length > 0) {
|
|
3960
|
-
|
|
3938
|
+
log3.success(`Created ${String(created.length)} new file(s):`);
|
|
3961
3939
|
for (const f of created) {
|
|
3962
|
-
|
|
3940
|
+
log3.info(` ${f}`);
|
|
3963
3941
|
}
|
|
3964
3942
|
}
|
|
3965
3943
|
if (skipped.length > 0) {
|
|
3966
|
-
|
|
3944
|
+
log3.warn(`Skipped ${String(skipped.length)} file(s) with local modifications:`);
|
|
3967
3945
|
for (const f of skipped) {
|
|
3968
|
-
|
|
3946
|
+
log3.info(` ${f}`);
|
|
3969
3947
|
}
|
|
3970
3948
|
}
|
|
3971
3949
|
if (updated.length === 0 && created.length === 0) {
|
|
3972
|
-
|
|
3950
|
+
log3.info("Everything is already up to date.");
|
|
3973
3951
|
}
|
|
3974
3952
|
}
|
|
3975
3953
|
function matchesRender(currentHash, newHash, projectFormatter, destinationPath, rendered) {
|
|
@@ -3982,64 +3960,6 @@ function matchesRender(currentHash, newHash, projectFormatter, destinationPath,
|
|
|
3982
3960
|
const formatted = projectFormatter.formatContent(destinationPath, rendered);
|
|
3983
3961
|
return formatted !== null && sha256(formatted) === currentHash;
|
|
3984
3962
|
}
|
|
3985
|
-
function migrateAnswers(raw) {
|
|
3986
|
-
const answers = raw["answers"];
|
|
3987
|
-
if (!answers) {
|
|
3988
|
-
return;
|
|
3989
|
-
}
|
|
3990
|
-
if (answers["framework"] && !answers["uiFramework"]) {
|
|
3991
|
-
answers["uiFramework"] = answers["framework"];
|
|
3992
|
-
delete answers["framework"];
|
|
3993
|
-
}
|
|
3994
|
-
if (answers["cssMode"] && !answers["styling"]) {
|
|
3995
|
-
answers["styling"] = answers["cssMode"];
|
|
3996
|
-
delete answers["cssMode"];
|
|
3997
|
-
}
|
|
3998
|
-
if (answers["buildSystem"] && !answers["bundler"]) {
|
|
3999
|
-
answers["bundler"] = answers["buildSystem"];
|
|
4000
|
-
delete answers["buildSystem"];
|
|
4001
|
-
}
|
|
4002
|
-
answers["defaultBranch"] ??= "master";
|
|
4003
|
-
migrateFundingAnswers(answers);
|
|
4004
|
-
answers["fundingUsername"] ??= answers["authorGitHubName"];
|
|
4005
|
-
answers["coverageBadge"] ??= "none";
|
|
4006
|
-
delete answers["pluginShortName"];
|
|
4007
|
-
for (const [key, value] of Object.entries(answers)) {
|
|
4008
|
-
if (typeof value === "string") {
|
|
4009
|
-
answers[key] = resolveDeprecatedAnswer(key, value, CONFIG_FILE_NAME);
|
|
4010
|
-
}
|
|
4011
|
-
}
|
|
4012
|
-
}
|
|
4013
|
-
function migrateFundingAnswers(answers) {
|
|
4014
|
-
if (answers["fundingPlatform"] !== void 0) {
|
|
4015
|
-
return;
|
|
4016
|
-
}
|
|
4017
|
-
delete answers["gitHubFunding"];
|
|
4018
|
-
const url = typeof answers["fundingUrl"] === "string" ? answers["fundingUrl"] : "";
|
|
4019
|
-
if (!url) {
|
|
4020
|
-
answers["fundingPlatform"] = "none";
|
|
4021
|
-
answers["fundingUrl"] = "";
|
|
4022
|
-
return;
|
|
4023
|
-
}
|
|
4024
|
-
function stripWww(value) {
|
|
4025
|
-
return value.replace("://www.", "://");
|
|
4026
|
-
}
|
|
4027
|
-
for (const option of FUNDING_PLATFORM_OPTIONS) {
|
|
4028
|
-
const prefix = stripWww(option.getUrl({ fundingUrl: "", fundingUsername: "" }));
|
|
4029
|
-
const normalizedUrl = stripWww(url);
|
|
4030
|
-
if (!prefix || !normalizedUrl.startsWith(prefix)) {
|
|
4031
|
-
continue;
|
|
4032
|
-
}
|
|
4033
|
-
const handle = normalizedUrl.slice(prefix.length).replace(/\/$/, "");
|
|
4034
|
-
if (!validateFundingUsername(handle)) {
|
|
4035
|
-
answers["fundingPlatform"] = option.settingValue;
|
|
4036
|
-
answers["fundingUsername"] = handle;
|
|
4037
|
-
answers["fundingUrl"] = "";
|
|
4038
|
-
return;
|
|
4039
|
-
}
|
|
4040
|
-
}
|
|
4041
|
-
answers["fundingPlatform"] = "custom";
|
|
4042
|
-
}
|
|
4043
3963
|
function sha256(content) {
|
|
4044
3964
|
return createHash("sha256").update(content).digest("hex");
|
|
4045
3965
|
}
|
|
@@ -4167,6 +4087,10 @@ function applyOfficialSampleIdentity(files, answers) {
|
|
|
4167
4087
|
if (readme) {
|
|
4168
4088
|
result.set("README.md", Buffer.from(readme.toString("utf-8").replace(/^# .*$/m, `# ${answers.pluginName}`), "utf-8"));
|
|
4169
4089
|
}
|
|
4090
|
+
const license = result.get("LICENSE");
|
|
4091
|
+
if (license) {
|
|
4092
|
+
result.set("LICENSE", Buffer.from(license.toString("utf-8").replace(/^Copyright \(C\) [^\r\n]*/m, getOfficialSampleCopyrightLine(answers)), "utf-8"));
|
|
4093
|
+
}
|
|
4170
4094
|
return result;
|
|
4171
4095
|
}
|
|
4172
4096
|
function copyOfficialSample(answers, targetDir, currentVersion, existingConfig, snapshot) {
|
|
@@ -4179,6 +4103,9 @@ function copyOfficialSample(answers, targetDir, currentVersion, existingConfig,
|
|
|
4179
4103
|
);
|
|
4180
4104
|
return { ...config, upstreamCommit: snapshot.commit };
|
|
4181
4105
|
}
|
|
4106
|
+
function getOfficialSampleCopyrightLine(answers) {
|
|
4107
|
+
return `Copyright (C) ${String(answers.currentYear)} by ${answers.authorName}`;
|
|
4108
|
+
}
|
|
4182
4109
|
function getPackageRoot() {
|
|
4183
4110
|
return join5(getScriptDir(), "..");
|
|
4184
4111
|
}
|
|
@@ -4583,7 +4510,7 @@ function buildSavedConfig(newConfig, answers, overlay, targetDir) {
|
|
|
4583
4510
|
async function checkForUpdates(currentVersion) {
|
|
4584
4511
|
const latestVer = await fetchLatestVersion("@mnaoumov/create-obsidian-plugin");
|
|
4585
4512
|
if (latestVer !== null && compare(currentVersion, latestVer) < 0) {
|
|
4586
|
-
|
|
4513
|
+
log4.warn(`Your version is outdated. Latest: ${latestVer}. Update with:
|
|
4587
4514
|
npm install -g @mnaoumov/create-obsidian-plugin`);
|
|
4588
4515
|
}
|
|
4589
4516
|
}
|
|
@@ -4611,7 +4538,7 @@ async function ensurePluginIdIsUnlisted(answers, useDefaults) {
|
|
|
4611
4538
|
const listedIds = await fetchCommunityPluginIds();
|
|
4612
4539
|
if (listedIds === null) {
|
|
4613
4540
|
s.stop("Could not check the plugin id against the Community directory.");
|
|
4614
|
-
|
|
4541
|
+
log4.warn(`Could not fetch community-plugins.json, so "${answers.pluginId}" was not checked for a collision with a published plugin. The directory rejects a duplicate id, and an id can never be changed once published: check it by hand before submitting.`);
|
|
4615
4542
|
return answers;
|
|
4616
4543
|
}
|
|
4617
4544
|
const collision = validatePluginIdIsUnlisted(answers.pluginId, listedIds);
|
|
@@ -4624,7 +4551,7 @@ async function ensurePluginIdIsUnlisted(answers, useDefaults) {
|
|
|
4624
4551
|
exitWithUsageError(`${collision}.
|
|
4625
4552
|
Pass --pluginId=<value> to choose another.`);
|
|
4626
4553
|
}
|
|
4627
|
-
|
|
4554
|
+
log4.warn(collision);
|
|
4628
4555
|
const choice = await select2({
|
|
4629
4556
|
initialValue: "reenter",
|
|
4630
4557
|
message: `What should happen to "${answers.pluginId}"?`,
|
|
@@ -4755,11 +4682,11 @@ async function offerAnswersExport(answers, customTemplate) {
|
|
|
4755
4682
|
if (shell === "sh") {
|
|
4756
4683
|
chmodSync(scriptPath, SCRIPT_MODE);
|
|
4757
4684
|
}
|
|
4758
|
-
|
|
4685
|
+
log4.success(`Wrote ${scriptName}`);
|
|
4759
4686
|
note(formatCreateCommand(answers, shell, customTemplate), "Non-interactive command");
|
|
4760
4687
|
} else {
|
|
4761
4688
|
writeFileSync2(join9(process.cwd(), answersFileName), formatAnswersJson(answers));
|
|
4762
|
-
|
|
4689
|
+
log4.success(`Wrote ${answersFileName}`);
|
|
4763
4690
|
const overlayFlag = customTemplate ? ` --customTemplate=${customTemplate}` : "";
|
|
4764
4691
|
note(`npm create @mnaoumov/obsidian-plugin "--" --yes --mode=create --answersFile=${answersFileName}${overlayFlag}`, "Non-interactive command");
|
|
4765
4692
|
}
|
|
@@ -4790,7 +4717,7 @@ async function resolveOfficialSampleSnapshot(latest) {
|
|
|
4790
4717
|
return snapshot;
|
|
4791
4718
|
} catch (error) {
|
|
4792
4719
|
s.stop(`Could not fetch ${upstream.repo}.`);
|
|
4793
|
-
|
|
4720
|
+
log4.warn(`${error instanceof Error ? error.message : String(error)}
|
|
4794
4721
|
Using the snapshot this release vendored, ${vendored.commit.slice(0, SHORT_SHA_LENGTH)}.`);
|
|
4795
4722
|
return vendored;
|
|
4796
4723
|
}
|
|
@@ -4894,7 +4821,7 @@ async function runPostScaffold(targetDir, answers) {
|
|
|
4894
4821
|
} catch (error) {
|
|
4895
4822
|
s.stop(`Failed to install dependencies. Run \`${installCmd}\` manually.`);
|
|
4896
4823
|
if (error instanceof Error && "stderr" in error) {
|
|
4897
|
-
|
|
4824
|
+
log4.error(String(error.stderr));
|
|
4898
4825
|
}
|
|
4899
4826
|
}
|
|
4900
4827
|
}
|
|
@@ -4918,7 +4845,7 @@ async function runPostScaffold(targetDir, answers) {
|
|
|
4918
4845
|
} catch (error) {
|
|
4919
4846
|
s.stop("Failed to initialize git. Run `git init` manually.");
|
|
4920
4847
|
if (error instanceof Error && "stderr" in error) {
|
|
4921
|
-
|
|
4848
|
+
log4.error(String(error.stderr));
|
|
4922
4849
|
}
|
|
4923
4850
|
}
|
|
4924
4851
|
}
|
|
@@ -4944,10 +4871,10 @@ async function runUpdate(currentVersion, cliArgs) {
|
|
|
4944
4871
|
const targetDir = process.cwd();
|
|
4945
4872
|
const existingConfig = loadConfig(targetDir);
|
|
4946
4873
|
if (!existingConfig) {
|
|
4947
|
-
|
|
4874
|
+
log4.error("No config file found. Cannot update.");
|
|
4948
4875
|
process.exit(1);
|
|
4949
4876
|
}
|
|
4950
|
-
|
|
4877
|
+
log4.info(`Current project was generated with v${existingConfig.generatorVersion}`);
|
|
4951
4878
|
let overlay = null;
|
|
4952
4879
|
if (customTemplate !== void 0) {
|
|
4953
4880
|
overlay = customTemplate ? loadOverlayOrExit(resolvePath(customTemplate)) : null;
|
|
@@ -4957,14 +4884,14 @@ async function runUpdate(currentVersion, cliArgs) {
|
|
|
4957
4884
|
`
|
|
4958
4885
|
This project records it in ${CONFIG_FILE_NAME}. Pass --customTemplate=<dir> to point at where it is now, or --customTemplate= to stop using it.`
|
|
4959
4886
|
);
|
|
4960
|
-
|
|
4887
|
+
log4.info(`Applying the custom template recorded for this project: ${existingConfig.customTemplate}`);
|
|
4961
4888
|
}
|
|
4962
4889
|
const configPath = join9(targetDir, CONFIG_FILE_NAME);
|
|
4963
4890
|
const savedConfig = JSON.parse(readFileSync8(configPath, "utf-8"));
|
|
4964
4891
|
let answers;
|
|
4965
4892
|
if (savedConfig.answers) {
|
|
4966
4893
|
const saved = { ...savedConfig.answers, ...suppliedAnswers };
|
|
4967
|
-
|
|
4894
|
+
log4.info("Using saved answers from previous generation.");
|
|
4968
4895
|
const shouldRePrompt = useDefaults ? false : await confirm({
|
|
4969
4896
|
initialValue: false,
|
|
4970
4897
|
message: "Would you like to change any settings?"
|
|
@@ -4980,7 +4907,7 @@ This project records it in ${CONFIG_FILE_NAME}. Pass --customTemplate=<dir> to p
|
|
|
4980
4907
|
exitWithUsageError(`${CONFIG_FILE_NAME} records no answers, so --yes has none to reuse.
|
|
4981
4908
|
Run without --yes to answer them again.`);
|
|
4982
4909
|
}
|
|
4983
|
-
|
|
4910
|
+
log4.warn("No saved answers found. Please provide the settings again.");
|
|
4984
4911
|
answers = await promptAnswers(suppliedAnswers);
|
|
4985
4912
|
}
|
|
4986
4913
|
const savedPluginId = savedConfig.answers?.pluginId;
|
|
@@ -5002,7 +4929,7 @@ Run without --yes to answer them again.`);
|
|
|
5002
4929
|
const { minAppVersion, resolvedVersions } = await resolveExternalVersions(answers, overlay);
|
|
5003
4930
|
const projectFormatter = createProjectFormatter(targetDir, answers.formatter);
|
|
5004
4931
|
if (projectFormatter === null && needsInitialFormat(answers.formatter)) {
|
|
5005
|
-
|
|
4932
|
+
log4.warn(`${answers.formatter} is not installed, so the update cannot tell the files it formatted from hand edits and skips them. Install the dependencies and update again to have them updated.`);
|
|
5006
4933
|
}
|
|
5007
4934
|
const s = spinner();
|
|
5008
4935
|
s.start("Updating project files...");
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@mnaoumov/create-obsidian-plugin",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.4.0",
|
|
4
4
|
"description": "Create Obsidian plugin from modern templates.",
|
|
5
5
|
"homepage": "https://mnaoumov.dev/create-obsidian-plugin/",
|
|
6
6
|
"bugs": "https://github.com/mnaoumov/create-obsidian-plugin/issues",
|