@mnaoumov/create-obsidian-plugin 0.0.0-stage → 1.0.1

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.
Files changed (470) hide show
  1. package/LICENSE +19 -0
  2. package/README.md +341 -2
  3. package/dist/main.js +4184 -0
  4. package/package.json +83 -4
  5. package/src/main.ts +650 -0
  6. package/templates/default/.depcheckrc.json.ejs +19 -0
  7. package/templates/default/.editorconfig.ejs +11 -0
  8. package/templates/default/.env.ejs +1 -0
  9. package/templates/default/.env@vault_has-vault-false.ejs +2 -0
  10. package/templates/default/.env@vault_has-vault-true.ejs +1 -0
  11. package/templates/default/.gitattributes.ejs +7 -0
  12. package/templates/default/.github/FUNDING.yml.ejs +3 -0
  13. package/templates/default/.github/ISSUE_TEMPLATE/bug_report.yml.ejs +71 -0
  14. package/templates/default/.github/ISSUE_TEMPLATE/config.yml.ejs +1 -0
  15. package/templates/default/.github/ISSUE_TEMPLATE/feature_request.yml.ejs +61 -0
  16. package/templates/default/.github/workflows/attest-release-assets.yml.ejs +24 -0
  17. package/templates/default/.github/workflows/ci.yml.ejs +20 -0
  18. package/templates/default/.github/workflows/ci.yml@steps_biome-linter.ejs +1 -0
  19. package/templates/default/.github/workflows/ci.yml@steps_cspell.ejs +1 -0
  20. package/templates/default/.github/workflows/ci.yml@steps_dprint.ejs +1 -0
  21. package/templates/default/.github/workflows/ci.yml@steps_eslint.ejs +1 -0
  22. package/templates/default/.github/workflows/ci.yml@steps_jest.ejs +1 -0
  23. package/templates/default/.github/workflows/ci.yml@steps_markdownlint.ejs +1 -0
  24. package/templates/default/.github/workflows/ci.yml@steps_prettier.ejs +1 -0
  25. package/templates/default/.github/workflows/ci.yml@steps_vitest.ejs +1 -0
  26. package/templates/default/.github/workflows/release.yml.ejs +27 -0
  27. package/templates/default/.github/workflows/update-license-year.yml.ejs +38 -0
  28. package/templates/default/.gitignore_common.ejs +27 -0
  29. package/templates/default/.gitignore_scss.ejs +1 -0
  30. package/templates/default/.gitignore_tailwind.ejs +3 -0
  31. package/templates/default/.husky/commit-msg.ejs +1 -0
  32. package/templates/default/.husky/pre-commit.ejs +1 -0
  33. package/templates/default/.markdownlint-cli2.mjs.ejs +4 -0
  34. package/templates/default/.nano-staged.mjs.ejs +5 -0
  35. package/templates/default/.npmrc.ejs +1 -0
  36. package/templates/default/.parcelrc.ejs +9 -0
  37. package/templates/default/.parcelrc@transformers_svelte.ejs +1 -0
  38. package/templates/default/.prettierignore.ejs +10 -0
  39. package/templates/default/.typesafe-i18n.json.ejs +5 -0
  40. package/templates/default/CONTRIBUTING.md.ejs +49 -0
  41. package/templates/default/CONTRIBUTING.md@demo-vault_dev-utils.ejs +15 -0
  42. package/templates/default/CONTRIBUTING.md@scripts_biome-formatter.ejs +1 -0
  43. package/templates/default/CONTRIBUTING.md@scripts_biome-linter.ejs +1 -0
  44. package/templates/default/CONTRIBUTING.md@scripts_conventional-commits.ejs +1 -0
  45. package/templates/default/CONTRIBUTING.md@scripts_cspell.ejs +1 -0
  46. package/templates/default/CONTRIBUTING.md@scripts_dev-utils.ejs +1 -0
  47. package/templates/default/CONTRIBUTING.md@scripts_dprint.ejs +1 -0
  48. package/templates/default/CONTRIBUTING.md@scripts_eslint.ejs +1 -0
  49. package/templates/default/CONTRIBUTING.md@scripts_jest.ejs +1 -0
  50. package/templates/default/CONTRIBUTING.md@scripts_markdownlint.ejs +1 -0
  51. package/templates/default/CONTRIBUTING.md@scripts_prettier.ejs +1 -0
  52. package/templates/default/CONTRIBUTING.md@scripts_vitest.ejs +1 -0
  53. package/templates/default/LICENSE.ejs +21 -0
  54. package/templates/default/README.md.ejs +28 -0
  55. package/templates/default/README.md@demo-vault_dev-utils.ejs +12 -0
  56. package/templates/default/README.md@preset_dev-utils.ejs +12 -0
  57. package/templates/default/README.md@support_has-funding.ejs +8 -0
  58. package/templates/default/babel.config.json.ejs +8 -0
  59. package/templates/default/babel.config.mjs.ejs +5 -0
  60. package/templates/default/biome.jsonc.ejs +47 -0
  61. package/templates/default/commitlint.config.ts.ejs +1 -0
  62. package/templates/default/cspell.json.ejs +67 -0
  63. package/templates/default/demo-vault/.gitignore.ejs +15 -0
  64. package/templates/default/demo-vault/.obsidian/app.json.ejs +4 -0
  65. package/templates/default/demo-vault/.obsidian/community-plugins.json.ejs +4 -0
  66. package/templates/default/demo-vault/00 Start.md.ejs +32 -0
  67. package/templates/default/demo-vault/00 Start.md@features_wasm.ejs +1 -0
  68. package/templates/default/demo-vault/01 Sample commands.md.ejs +38 -0
  69. package/templates/default/demo-vault/01 Sample commands.md@commands_wasm.ejs +4 -0
  70. package/templates/default/demo-vault/02 Settings.md.ejs +40 -0
  71. package/templates/default/demo-vault/03 WebAssembly.md.ejs +34 -0
  72. package/templates/default/demo-vault/Materials/01 Sample commands/Scratch note.md.ejs +5 -0
  73. package/templates/default/demo-vault/README.md.ejs +10 -0
  74. package/templates/default/demo-vault/_assets/CodeScriptToolkit/demoSetup.ts.ejs +88 -0
  75. package/templates/default/demo-vault/_assets/CodeScriptToolkit/demoSetup.ts@commands_wasm.ejs +10 -0
  76. package/templates/default/demo-vault/_assets/CodeScriptToolkit/startup.ts.ejs +17 -0
  77. package/templates/default/dprint.json_dev-utils.ejs +10 -0
  78. package/templates/default/dprint.json_standalone.ejs +26 -0
  79. package/templates/default/e2e/sample.spec.ts_obsidian-test.ejs +16 -0
  80. package/templates/default/e2e/sample.spec.ts_obsidian-test@lint-no-void-disable_dev-utils.ejs +3 -0
  81. package/templates/default/e2e/sample.spec.ts_obsidian-test@lint-no-void-enable_dev-utils.ejs +1 -0
  82. package/templates/default/e2e/sample.spec.ts_obsidian-test@unofficial_with-unofficial.ejs +13 -0
  83. package/templates/default/e2e/sample.spec.ts_wdio-obsidian.ejs +7 -0
  84. package/templates/default/e2e/sample.spec.ts_wdio-obsidian@unofficial_with-unofficial.ejs +15 -0
  85. package/templates/default/eslint.config.mts_dev-utils.ejs +1 -0
  86. package/templates/default/eslint.config.mts_standalone.ejs +119 -0
  87. package/templates/default/eslint.config.mts_standalone@extra-files_has-e2e.ejs +2 -0
  88. package/templates/default/jest.config.ts.ejs +57 -0
  89. package/templates/default/jest.config.ts@lint-root-dir_dev-utils.ejs +1 -0
  90. package/templates/default/jest.config.ts@ts-jest-tsconfig_solid.ejs +8 -0
  91. package/templates/default/linkinator.config.json.ejs +11 -0
  92. package/templates/default/manifest.json.ejs +11 -0
  93. package/templates/default/manifest.json@funding_has-funding.ejs +1 -0
  94. package/templates/default/manifest.json@platform_desktop-and-mobile.ejs +1 -0
  95. package/templates/default/manifest.json@platform_desktop-only.ejs +1 -0
  96. package/templates/default/package.json.ejs +32 -0
  97. package/templates/default/package.json@targets_parcel.ejs +36 -0
  98. package/templates/default/parcel-namer-obsidian.cjs.ejs +26 -0
  99. package/templates/default/parcel-resolver-obsidian.cjs.ejs +37 -0
  100. package/templates/default/parcel-transformer-inline-imports.cjs.ejs +39 -0
  101. package/templates/default/parcel-transformer-svelte.cjs.ejs +67 -0
  102. package/templates/default/pinned-versions.json.ejs +1 -0
  103. package/templates/default/pnpm-workspace.yaml.ejs +19 -0
  104. package/templates/default/postcss.config.cjs.ejs +12 -0
  105. package/templates/default/prettier.config.mjs.ejs +5 -0
  106. package/templates/default/rollup.config.mjs.ejs +7 -0
  107. package/templates/default/scripts/babel.config.ts_rollup.ejs +16 -0
  108. package/templates/default/scripts/babel.config.ts_rollup@babel-plugin_preact.ejs +1 -0
  109. package/templates/default/scripts/babel.config.ts_rollup@babel-preset_react.ejs +1 -0
  110. package/templates/default/scripts/babel.config.ts_rollup@babel-preset_solid.ejs +1 -0
  111. package/templates/default/scripts/build-clean.ts.ejs +4 -0
  112. package/templates/default/scripts/build-compile.ts.ejs +4 -0
  113. package/templates/default/scripts/build-styles.ts.ejs +12 -0
  114. package/templates/default/scripts/build.ts.ejs +6 -0
  115. package/templates/default/scripts/build.ts@bundler_cli-bundler.ejs +63 -0
  116. package/templates/default/scripts/build.ts@bundler_cli-bundler@compile_svelte.ejs +4 -0
  117. package/templates/default/scripts/build.ts@bundler_cli-bundler@fs-import_hot-reload-plugin.ejs +2 -0
  118. package/templates/default/scripts/build.ts@bundler_cli-bundler@hot-reload_hot-reload-plugin.ejs +3 -0
  119. package/templates/default/scripts/build.ts@bundler_cli-bundler@import_tailwind.ejs +2 -0
  120. package/templates/default/scripts/build.ts@bundler_cli-bundler@lint-build-styles_dev-utils.ejs +1 -0
  121. package/templates/default/scripts/build.ts@bundler_cli-bundler@reload_obsidian-cli.ejs +5 -0
  122. package/templates/default/scripts/build.ts@bundler_cli-bundler@run_parcel.ejs +1 -0
  123. package/templates/default/scripts/build.ts@bundler_cli-bundler@run_rollup.ejs +1 -0
  124. package/templates/default/scripts/build.ts@bundler_cli-bundler@run_vite.ejs +1 -0
  125. package/templates/default/scripts/build.ts@bundler_cli-bundler@run_webpack.ejs +1 -0
  126. package/templates/default/scripts/build.ts@bundler_esbuild.ejs +3 -0
  127. package/templates/default/scripts/build.ts@bundler_esbuild@compile_svelte.ejs +4 -0
  128. package/templates/default/scripts/build.ts@bundler_esbuild@dev-utils-pre-build_tailwind.ejs +2 -0
  129. package/templates/default/scripts/build.ts@bundler_esbuild@fs-import_hot-reload-plugin.ejs +2 -0
  130. package/templates/default/scripts/build.ts@bundler_esbuild@hot-reload_hot-reload-plugin.ejs +4 -0
  131. package/templates/default/scripts/build.ts@bundler_esbuild@import_postcss.ejs +1 -0
  132. package/templates/default/scripts/build.ts@bundler_esbuild@import_scss.ejs +1 -0
  133. package/templates/default/scripts/build.ts@bundler_esbuild@import_solid.ejs +1 -0
  134. package/templates/default/scripts/build.ts@bundler_esbuild@import_svelte.ejs +10 -0
  135. package/templates/default/scripts/build.ts@bundler_esbuild@import_tailwind.ejs +1 -0
  136. package/templates/default/scripts/build.ts@bundler_esbuild@import_vue.ejs +1 -0
  137. package/templates/default/scripts/build.ts@bundler_esbuild@lint-build-styles_dev-utils.ejs +1 -0
  138. package/templates/default/scripts/build.ts@bundler_esbuild@options_preact.ejs +2 -0
  139. package/templates/default/scripts/build.ts@bundler_esbuild@options_react.ejs +1 -0
  140. package/templates/default/scripts/build.ts@bundler_esbuild@options_wasm.ejs +5 -0
  141. package/templates/default/scripts/build.ts@bundler_esbuild@plugin_postcss.ejs +1 -0
  142. package/templates/default/scripts/build.ts@bundler_esbuild@plugin_scss.ejs +1 -0
  143. package/templates/default/scripts/build.ts@bundler_esbuild@plugin_solid.ejs +1 -0
  144. package/templates/default/scripts/build.ts@bundler_esbuild@plugin_svelte.ejs +5 -0
  145. package/templates/default/scripts/build.ts@bundler_esbuild@plugin_vue.ejs +1 -0
  146. package/templates/default/scripts/build.ts@bundler_esbuild@preset_dev-utils.ejs +7 -0
  147. package/templates/default/scripts/build.ts@bundler_esbuild@preset_standalone.ejs +138 -0
  148. package/templates/default/scripts/build.ts@bundler_esbuild@reload_obsidian-cli.ejs +6 -0
  149. package/templates/default/scripts/commit.ts_dev-utils.ejs +3 -0
  150. package/templates/default/scripts/commit.ts_standalone.ejs +3 -0
  151. package/templates/default/scripts/commitlint-config.ts.ejs +5 -0
  152. package/templates/default/scripts/demo-vault-global-setup.ts.ejs +43 -0
  153. package/templates/default/scripts/dev.ts.ejs +6 -0
  154. package/templates/default/scripts/dev.ts@bundler_cli-bundler.ejs +8 -0
  155. package/templates/default/scripts/dev.ts@bundler_esbuild.ejs +3 -0
  156. package/templates/default/scripts/dev.ts@bundler_esbuild@dev-utils-pre-build_tailwind.ejs +2 -0
  157. package/templates/default/scripts/dev.ts@bundler_esbuild@lint-build-styles_dev-utils.ejs +1 -0
  158. package/templates/default/scripts/dev.ts@bundler_esbuild@preset_dev-utils.ejs +7 -0
  159. package/templates/default/scripts/dev.ts@bundler_esbuild@preset_standalone.ejs +3 -0
  160. package/templates/default/scripts/esbuild-plugins.ts.ejs +20 -0
  161. package/templates/default/scripts/esbuild-plugins.ts@dev-utils-import_vue.ejs +2 -0
  162. package/templates/default/scripts/esbuild-plugins.ts@dev-utils-plugin_vue.ejs +1 -0
  163. package/templates/default/scripts/esbuild-plugins.ts@dev-utils-plugin_wasm.ejs +13 -0
  164. package/templates/default/scripts/eslint-config.ts.ejs +113 -0
  165. package/templates/default/scripts/eslint-config.ts@extra-files_has-e2e.ejs +7 -0
  166. package/templates/default/scripts/find-overexposed-fix.ts.ejs +26 -0
  167. package/templates/default/scripts/find-overexposed.ts.ejs +15 -0
  168. package/templates/default/scripts/format-check.ts.ejs +4 -0
  169. package/templates/default/scripts/format-check.ts@tool_biome-formatter.ejs +8 -0
  170. package/templates/default/scripts/format-check.ts@tool_dprint.ejs +3 -0
  171. package/templates/default/scripts/format-check.ts@tool_dprint@preset_dev-utils.ejs +7 -0
  172. package/templates/default/scripts/format-check.ts@tool_dprint@preset_standalone.ejs +8 -0
  173. package/templates/default/scripts/format-check.ts@tool_prettier.ejs +8 -0
  174. package/templates/default/scripts/format.ts.ejs +4 -0
  175. package/templates/default/scripts/format.ts@tool_biome-formatter.ejs +8 -0
  176. package/templates/default/scripts/format.ts@tool_dprint.ejs +3 -0
  177. package/templates/default/scripts/format.ts@tool_dprint@preset_dev-utils.ejs +7 -0
  178. package/templates/default/scripts/format.ts@tool_dprint@preset_standalone.ejs +8 -0
  179. package/templates/default/scripts/format.ts@tool_prettier.ejs +8 -0
  180. package/templates/default/scripts/framework-component-stub.ts.ejs +46 -0
  181. package/templates/default/scripts/gate.ts.ejs +10 -0
  182. package/templates/default/scripts/lint-fix.ts.ejs +6 -0
  183. package/templates/default/scripts/lint-fix.ts@tool_biome-linter.ejs +8 -0
  184. package/templates/default/scripts/lint-fix.ts@tool_eslint.ejs +3 -0
  185. package/templates/default/scripts/lint-fix.ts@tool_eslint@preset_dev-utils.ejs +7 -0
  186. package/templates/default/scripts/lint-fix.ts@tool_eslint@preset_standalone.ejs +8 -0
  187. package/templates/default/scripts/lint-md-fix.ts_dev-utils.ejs +7 -0
  188. package/templates/default/scripts/lint-md-fix.ts_standalone.ejs +37 -0
  189. package/templates/default/scripts/lint-md.ts_dev-utils.ejs +7 -0
  190. package/templates/default/scripts/lint-md.ts_standalone.ejs +37 -0
  191. package/templates/default/scripts/lint.ts.ejs +6 -0
  192. package/templates/default/scripts/lint.ts@tool_biome-linter.ejs +8 -0
  193. package/templates/default/scripts/lint.ts@tool_eslint.ejs +3 -0
  194. package/templates/default/scripts/lint.ts@tool_eslint@preset_dev-utils.ejs +7 -0
  195. package/templates/default/scripts/lint.ts@tool_eslint@preset_standalone.ejs +8 -0
  196. package/templates/default/scripts/markdownlint-cli2-config.ts_dev-utils.ejs +14 -0
  197. package/templates/default/scripts/markdownlint-cli2-config.ts_standalone.ejs +36 -0
  198. package/templates/default/scripts/nano-staged-config.ts.ejs +9 -0
  199. package/templates/default/scripts/postcss.config.ts.ejs +1 -0
  200. package/templates/default/scripts/postcss.config.ts@plugins_postcss.ejs +7 -0
  201. package/templates/default/scripts/prepare.ts_dev-utils.ejs +3 -0
  202. package/templates/default/scripts/prepare.ts_standalone.ejs +3 -0
  203. package/templates/default/scripts/prettier.config.ts.ejs +12 -0
  204. package/templates/default/scripts/rollup.config.ts.ejs +129 -0
  205. package/templates/default/scripts/rollup.config.ts@import_rollup-babel.ejs +5 -0
  206. package/templates/default/scripts/rollup.config.ts@import_rollup-json.ejs +1 -0
  207. package/templates/default/scripts/rollup.config.ts@import_rollup-postcss.ejs +4 -0
  208. package/templates/default/scripts/rollup.config.ts@import_scss.ejs +1 -0
  209. package/templates/default/scripts/rollup.config.ts@import_svelte.ejs +2 -0
  210. package/templates/default/scripts/rollup.config.ts@import_vue.ejs +1 -0
  211. package/templates/default/scripts/rollup.config.ts@import_wasm.ejs +1 -0
  212. package/templates/default/scripts/rollup.config.ts@lint-out-dir_dev-utils.ejs +1 -0
  213. package/templates/default/scripts/rollup.config.ts@post-plugin_preact.ejs +4 -0
  214. package/templates/default/scripts/rollup.config.ts@post-plugin_react.ejs +5 -0
  215. package/templates/default/scripts/rollup.config.ts@post-plugin_rollup-json.ejs +1 -0
  216. package/templates/default/scripts/rollup.config.ts@post-plugin_rollup-postcss.ejs +4 -0
  217. package/templates/default/scripts/rollup.config.ts@post-plugin_scss.ejs +4 -0
  218. package/templates/default/scripts/rollup.config.ts@post-plugin_solid.ejs +4 -0
  219. package/templates/default/scripts/rollup.config.ts@post-plugin_wasm.ejs +4 -0
  220. package/templates/default/scripts/rollup.config.ts@pre-plugin_svelte.ejs +4 -0
  221. package/templates/default/scripts/rollup.config.ts@pre-plugin_vue.ejs +13 -0
  222. package/templates/default/scripts/rollup.config.ts@unwrap_rollup-babel.ejs +1 -0
  223. package/templates/default/scripts/rollup.config.ts@unwrap_rollup-json.ejs +1 -0
  224. package/templates/default/scripts/rollup.config.ts@unwrap_rollup-postcss.ejs +1 -0
  225. package/templates/default/scripts/rollup.config.ts@unwrap_scss.ejs +1 -0
  226. package/templates/default/scripts/rollup.config.ts@unwrap_svelte.ejs +1 -0
  227. package/templates/default/scripts/rollup.config.ts@unwrap_vue.ejs +1 -0
  228. package/templates/default/scripts/rollup.config.ts@unwrap_wasm.ejs +1 -0
  229. package/templates/default/scripts/spellcheck.ts_dev-utils.ejs +7 -0
  230. package/templates/default/scripts/spellcheck.ts_standalone.ejs +8 -0
  231. package/templates/default/scripts/stylesheet-stub.ts.ejs +17 -0
  232. package/templates/default/scripts/test-coverage.ts.ejs +8 -0
  233. package/templates/default/scripts/test-e2e.ts.ejs +1 -0
  234. package/templates/default/scripts/test-e2e.ts@tool_obsidian-test.ejs +8 -0
  235. package/templates/default/scripts/test-e2e.ts@tool_wdio-obsidian.ejs +8 -0
  236. package/templates/default/scripts/test-integration-android.ts.ejs +8 -0
  237. package/templates/default/scripts/test-integration-demo-vault.ts.ejs +8 -0
  238. package/templates/default/scripts/test-integration-desktop-performance.ts.ejs +8 -0
  239. package/templates/default/scripts/test-integration-desktop.ts.ejs +8 -0
  240. package/templates/default/scripts/test-integration-no-app.ts.ejs +8 -0
  241. package/templates/default/scripts/test-integration.ts.ejs +22 -0
  242. package/templates/default/scripts/test-watch.ts_dev-utils.ejs +1 -0
  243. package/templates/default/scripts/test-watch.ts_dev-utils@tool_jest.ejs +13 -0
  244. package/templates/default/scripts/test-watch.ts_dev-utils@tool_vitest.ejs +8 -0
  245. package/templates/default/scripts/test-watch.ts_standalone.ejs +1 -0
  246. package/templates/default/scripts/test-watch.ts_standalone@tool_jest.ejs +13 -0
  247. package/templates/default/scripts/test-watch.ts_standalone@tool_vitest.ejs +8 -0
  248. package/templates/default/scripts/test.ts_dev-utils.ejs +1 -0
  249. package/templates/default/scripts/test.ts_dev-utils@tool_jest.ejs +13 -0
  250. package/templates/default/scripts/test.ts_dev-utils@tool_vitest.ejs +8 -0
  251. package/templates/default/scripts/test.ts_standalone.ejs +1 -0
  252. package/templates/default/scripts/test.ts_standalone@tool_jest.ejs +13 -0
  253. package/templates/default/scripts/test.ts_standalone@tool_vitest.ejs +8 -0
  254. package/templates/default/scripts/version.ts_dev-utils.ejs +10 -0
  255. package/templates/default/scripts/version.ts_standalone.ejs +317 -0
  256. package/templates/default/scripts/vite.config.ts.ejs +76 -0
  257. package/templates/default/scripts/vite.config.ts@import_preact.ejs +1 -0
  258. package/templates/default/scripts/vite.config.ts@import_react.ejs +1 -0
  259. package/templates/default/scripts/vite.config.ts@import_solid.ejs +1 -0
  260. package/templates/default/scripts/vite.config.ts@import_svelte.ejs +1 -0
  261. package/templates/default/scripts/vite.config.ts@import_vue.ejs +1 -0
  262. package/templates/default/scripts/vite.config.ts@lint-empty-out-dir_dev-utils.ejs +1 -0
  263. package/templates/default/scripts/vite.config.ts@lint-out-dir_dev-utils.ejs +1 -0
  264. package/templates/default/scripts/vite.config.ts@plugin_preact.ejs +1 -0
  265. package/templates/default/scripts/vite.config.ts@plugin_react.ejs +1 -0
  266. package/templates/default/scripts/vite.config.ts@plugin_solid.ejs +1 -0
  267. package/templates/default/scripts/vite.config.ts@plugin_svelte.ejs +1 -0
  268. package/templates/default/scripts/vite.config.ts@plugin_vue.ejs +1 -0
  269. package/templates/default/scripts/vitest-config.ts.ejs +75 -0
  270. package/templates/default/scripts/vitest-config.ts@post-config_solid.ejs +67 -0
  271. package/templates/default/scripts/wasm-module-stub.ts.ejs +21 -0
  272. package/templates/default/scripts/webpack.config.ts.ejs +124 -0
  273. package/templates/default/scripts/webpack.config.ts@import_svelte.ejs +1 -0
  274. package/templates/default/scripts/webpack.config.ts@import_vue.ejs +1 -0
  275. package/templates/default/scripts/webpack.config.ts@import_webpack-css-extract.ejs +4 -0
  276. package/templates/default/scripts/webpack.config.ts@lint-webpack-default_dev-utils.ejs +1 -0
  277. package/templates/default/scripts/webpack.config.ts@plugin_vue.ejs +1 -0
  278. package/templates/default/scripts/webpack.config.ts@plugin_webpack-css-extract.ejs +4 -0
  279. package/templates/default/scripts/webpack.config.ts@rule_css-modules.ejs +7 -0
  280. package/templates/default/scripts/webpack.config.ts@rule_css.ejs +4 -0
  281. package/templates/default/scripts/webpack.config.ts@rule_postcss.ejs +4 -0
  282. package/templates/default/scripts/webpack.config.ts@rule_scss.ejs +4 -0
  283. package/templates/default/scripts/webpack.config.ts@rule_solid.ejs +17 -0
  284. package/templates/default/scripts/webpack.config.ts@rule_svelte.ejs +7 -0
  285. package/templates/default/scripts/webpack.config.ts@rule_tailwind.ejs +4 -0
  286. package/templates/default/scripts/webpack.config.ts@rule_vue.ejs +4 -0
  287. package/templates/default/scripts/webpack.config.ts@rule_wasm.ejs +8 -0
  288. package/templates/default/scripts/webpack.config.ts@ts-loader-compiler-options_vue.ejs +5 -0
  289. package/templates/default/scripts/webpack.config.ts@ts-loader-options_vue.ejs +5 -0
  290. package/templates/default/src/demo-vault-buttons.demo-vault.integration.test.ts.ejs +12 -0
  291. package/templates/default/src/demo-vault.no-app.integration.test.ts.ejs +18 -0
  292. package/templates/default/src/editor-extensions/sample-state-field.ts_dev-utils.ejs +56 -0
  293. package/templates/default/src/editor-extensions/sample-state-field.ts_standalone.ejs +60 -0
  294. package/templates/default/src/editor-extensions/sample-view-plugin.ts.ejs +66 -0
  295. package/templates/default/src/editor-extensions/sample-widget.ts.ejs +22 -0
  296. package/templates/default/src/editor-suggests/sample-editor-suggest.ts.ejs +42 -0
  297. package/templates/default/src/i18n/en/index.ts.ejs +10 -0
  298. package/templates/default/src/i18n/index.ts_i18next.ejs +21 -0
  299. package/templates/default/src/i18n/index.ts_typesafe-i18n.ejs +14 -0
  300. package/templates/default/src/i18n/locales/en.json.ejs +3 -0
  301. package/templates/default/src/lit-elements/sample-lit-element.ts.ejs +54 -0
  302. package/templates/default/src/main.test.ts.ejs +11 -0
  303. package/templates/default/src/main.test.ts@test-imports_vitest.ejs +6 -0
  304. package/templates/default/src/main.ts.ejs +4 -0
  305. package/templates/default/src/main.ts@import_css-modules.ejs +1 -0
  306. package/templates/default/src/main.ts@import_css.ejs +1 -0
  307. package/templates/default/src/main.ts@import_postcss.ejs +1 -0
  308. package/templates/default/src/main.ts@import_scss.ejs +1 -0
  309. package/templates/default/src/main.ts@import_tailwind.ejs +1 -0
  310. package/templates/default/src/modals/sample-form-modal.ts.ejs +35 -0
  311. package/templates/default/src/modals/sample-suggest-modal.ts.ejs +37 -0
  312. package/templates/default/src/plugin-settings-component.test.ts_demo.ejs +49 -0
  313. package/templates/default/src/plugin-settings-component.test.ts_demo@test-imports_vitest.ejs +5 -0
  314. package/templates/default/src/plugin-settings-component.test.ts_enhanced.ejs +41 -0
  315. package/templates/default/src/plugin-settings-component.test.ts_enhanced@test-imports_vitest.ejs +5 -0
  316. package/templates/default/src/plugin-settings-component.ts_demo.ejs +87 -0
  317. package/templates/default/src/plugin-settings-component.ts_enhanced.ejs +27 -0
  318. package/templates/default/src/plugin-settings-tab.ts_demo.ejs +286 -0
  319. package/templates/default/src/plugin-settings-tab.ts_enhanced.ejs +26 -0
  320. package/templates/default/src/plugin-settings.test.ts_demo.ejs +14 -0
  321. package/templates/default/src/plugin-settings.test.ts_demo@test-imports_vitest.ejs +6 -0
  322. package/templates/default/src/plugin-settings.ts_demo.ejs +64 -0
  323. package/templates/default/src/plugin-settings.ts_enhanced.ejs +3 -0
  324. package/templates/default/src/plugin.test.ts_jest.ejs +9 -0
  325. package/templates/default/src/plugin.test.ts_vitest.ejs +14 -0
  326. package/templates/default/src/plugin.ts_demo.ejs +316 -0
  327. package/templates/default/src/plugin.ts_demo@import_i18next.ejs +1 -0
  328. package/templates/default/src/plugin.ts_demo@import_lit.ejs +4 -0
  329. package/templates/default/src/plugin.ts_demo@import_preact.ejs +4 -0
  330. package/templates/default/src/plugin.ts_demo@import_react.ejs +4 -0
  331. package/templates/default/src/plugin.ts_demo@import_solid.ejs +4 -0
  332. package/templates/default/src/plugin.ts_demo@import_svelte.ejs +4 -0
  333. package/templates/default/src/plugin.ts_demo@import_typesafe-i18n.ejs +1 -0
  334. package/templates/default/src/plugin.ts_demo@import_vue.ejs +4 -0
  335. package/templates/default/src/plugin.ts_demo@import_wasm.ejs +1 -0
  336. package/templates/default/src/plugin.ts_demo@method_ui-view.ejs +7 -0
  337. package/templates/default/src/plugin.ts_demo@onload_i18next.ejs +2 -0
  338. package/templates/default/src/plugin.ts_demo@onload_lit.ejs +4 -0
  339. package/templates/default/src/plugin.ts_demo@onload_preact.ejs +4 -0
  340. package/templates/default/src/plugin.ts_demo@onload_react.ejs +4 -0
  341. package/templates/default/src/plugin.ts_demo@onload_solid.ejs +4 -0
  342. package/templates/default/src/plugin.ts_demo@onload_svelte.ejs +4 -0
  343. package/templates/default/src/plugin.ts_demo@onload_typesafe-i18n.ejs +8 -0
  344. package/templates/default/src/plugin.ts_demo@onload_vue.ejs +4 -0
  345. package/templates/default/src/plugin.ts_demo@onload_wasm.ejs +2 -0
  346. package/templates/default/src/plugin.ts_enhanced.ejs +72 -0
  347. package/templates/default/src/plugin.ts_enhanced@import2_codemirror.ejs +3 -0
  348. package/templates/default/src/plugin.ts_enhanced@import2_i18next.ejs +2 -0
  349. package/templates/default/src/plugin.ts_enhanced@import2_lit.ejs +4 -0
  350. package/templates/default/src/plugin.ts_enhanced@import2_preact.ejs +4 -0
  351. package/templates/default/src/plugin.ts_enhanced@import2_react.ejs +4 -0
  352. package/templates/default/src/plugin.ts_enhanced@import2_solid.ejs +4 -0
  353. package/templates/default/src/plugin.ts_enhanced@import2_svelte.ejs +4 -0
  354. package/templates/default/src/plugin.ts_enhanced@import2_typesafe-i18n.ejs +2 -0
  355. package/templates/default/src/plugin.ts_enhanced@import2_vue.ejs +4 -0
  356. package/templates/default/src/plugin.ts_enhanced@import2_wasm.ejs +1 -0
  357. package/templates/default/src/plugin.ts_enhanced@method_ui-view.ejs +7 -0
  358. package/templates/default/src/plugin.ts_enhanced@onload_codemirror.ejs +4 -0
  359. package/templates/default/src/plugin.ts_enhanced@onload_i18next.ejs +2 -0
  360. package/templates/default/src/plugin.ts_enhanced@onload_lit.ejs +4 -0
  361. package/templates/default/src/plugin.ts_enhanced@onload_preact.ejs +4 -0
  362. package/templates/default/src/plugin.ts_enhanced@onload_react.ejs +4 -0
  363. package/templates/default/src/plugin.ts_enhanced@onload_solid.ejs +4 -0
  364. package/templates/default/src/plugin.ts_enhanced@onload_svelte.ejs +4 -0
  365. package/templates/default/src/plugin.ts_enhanced@onload_typesafe-i18n.ejs +8 -0
  366. package/templates/default/src/plugin.ts_enhanced@onload_vue.ejs +4 -0
  367. package/templates/default/src/plugin.ts_enhanced@onload_wasm.ejs +2 -0
  368. package/templates/default/src/plugin.ts_standalone.ejs +56 -0
  369. package/templates/default/src/plugin.ts_standalone@import_codemirror.ejs +3 -0
  370. package/templates/default/src/plugin.ts_standalone@import_i18next.ejs +1 -0
  371. package/templates/default/src/plugin.ts_standalone@import_lit.ejs +4 -0
  372. package/templates/default/src/plugin.ts_standalone@import_preact.ejs +4 -0
  373. package/templates/default/src/plugin.ts_standalone@import_react.ejs +4 -0
  374. package/templates/default/src/plugin.ts_standalone@import_solid.ejs +4 -0
  375. package/templates/default/src/plugin.ts_standalone@import_svelte.ejs +4 -0
  376. package/templates/default/src/plugin.ts_standalone@import_typesafe-i18n.ejs +1 -0
  377. package/templates/default/src/plugin.ts_standalone@import_vue.ejs +4 -0
  378. package/templates/default/src/plugin.ts_standalone@import_wasm.ejs +1 -0
  379. package/templates/default/src/plugin.ts_standalone@method_ui-view.ejs +7 -0
  380. package/templates/default/src/plugin.ts_standalone@onload_codemirror.ejs +4 -0
  381. package/templates/default/src/plugin.ts_standalone@onload_i18next.ejs +2 -0
  382. package/templates/default/src/plugin.ts_standalone@onload_lit.ejs +4 -0
  383. package/templates/default/src/plugin.ts_standalone@onload_preact.ejs +4 -0
  384. package/templates/default/src/plugin.ts_standalone@onload_react.ejs +4 -0
  385. package/templates/default/src/plugin.ts_standalone@onload_solid.ejs +4 -0
  386. package/templates/default/src/plugin.ts_standalone@onload_svelte.ejs +4 -0
  387. package/templates/default/src/plugin.ts_standalone@onload_typesafe-i18n.ejs +8 -0
  388. package/templates/default/src/plugin.ts_standalone@onload_vue.ejs +4 -0
  389. package/templates/default/src/plugin.ts_standalone@onload_wasm.ejs +2 -0
  390. package/templates/default/src/preact-components/sample-preact-component.tsx_dev-utils.ejs +47 -0
  391. package/templates/default/src/preact-components/sample-preact-component.tsx_standalone.ejs +47 -0
  392. package/templates/default/src/react-components/sample-react-component.tsx_dev-utils.ejs +35 -0
  393. package/templates/default/src/react-components/sample-react-component.tsx_standalone.ejs +47 -0
  394. package/templates/default/src/settings.ts.ejs +32 -0
  395. package/templates/default/src/solid-components/sample-solid-component.tsx_dev-utils.ejs +47 -0
  396. package/templates/default/src/solid-components/sample-solid-component.tsx_standalone.ejs +47 -0
  397. package/templates/default/src/styles/css-modules.d.ts.ejs +4 -0
  398. package/templates/default/src/styles/main.css_dev-utils.ejs +17 -0
  399. package/templates/default/src/styles/main.css_dev-utils@content_tailwind.ejs +1 -0
  400. package/templates/default/src/styles/main.css_standalone.ejs +11 -0
  401. package/templates/default/src/styles/main.css_standalone@content_tailwind.ejs +1 -0
  402. package/templates/default/src/styles/main.module.css.ejs +3 -0
  403. package/templates/default/src/styles/main.scss_dev-utils.ejs +16 -0
  404. package/templates/default/src/styles/main.scss_standalone.ejs +10 -0
  405. package/templates/default/src/styles/styles.d.ts.ejs +4 -0
  406. package/templates/default/src/svelte-components/sample-svelte-component.d.ts.ejs +7 -0
  407. package/templates/default/src/svelte-components/sample-svelte-component.svelte.ejs +24 -0
  408. package/templates/default/src/views/sample-lit-view.ts.ejs +30 -0
  409. package/templates/default/src/views/sample-lit-view.ts@lint-register-element_dev-utils.ejs +1 -0
  410. package/templates/default/src/views/sample-preact-view.tsx_dev-utils.ejs +45 -0
  411. package/templates/default/src/views/sample-preact-view.tsx_standalone.ejs +42 -0
  412. package/templates/default/src/views/sample-react-view.tsx_dev-utils.ejs +47 -0
  413. package/templates/default/src/views/sample-react-view.tsx_standalone.ejs +49 -0
  414. package/templates/default/src/views/sample-solid-view.tsx_dev-utils.ejs +46 -0
  415. package/templates/default/src/views/sample-solid-view.tsx_standalone.ejs +46 -0
  416. package/templates/default/src/views/sample-svelte-view.ts.ejs +47 -0
  417. package/templates/default/src/views/sample-view.ts.ejs +37 -0
  418. package/templates/default/src/views/sample-vue-view.ts.ejs +34 -0
  419. package/templates/default/src/vue-components/sample-vue-component.d.ts.ejs +6 -0
  420. package/templates/default/src/vue-components/sample-vue-component.vue.ejs +25 -0
  421. package/templates/default/src/wasm/README.md.ejs +29 -0
  422. package/templates/default/src/wasm/answer.ts_esbuild.ejs +23 -0
  423. package/templates/default/src/wasm/answer.ts_parcel.ejs +29 -0
  424. package/templates/default/src/wasm/answer.ts_rollup.ejs +19 -0
  425. package/templates/default/src/wasm/answer.ts_vite.ejs +29 -0
  426. package/templates/default/src/wasm/answer.ts_webpack.ejs +29 -0
  427. package/templates/default/src/wasm/module.wasm +0 -0
  428. package/templates/default/src/wasm/module.wat.ejs +12 -0
  429. package/templates/default/src/wasm/sample-command.ts.ejs +33 -0
  430. package/templates/default/src/wasm.d.ts_esbuild.ejs +12 -0
  431. package/templates/default/src/wasm.d.ts_parcel.ejs +7 -0
  432. package/templates/default/src/wasm.d.ts_rollup.ejs +8 -0
  433. package/templates/default/src/wasm.d.ts_vite.ejs +8 -0
  434. package/templates/default/src/wasm.d.ts_webpack.ejs +7 -0
  435. package/templates/default/tsconfig.json_dev-utils.ejs +34 -0
  436. package/templates/default/tsconfig.json_dev-utils@compiler-options_i18next.ejs +1 -0
  437. package/templates/default/tsconfig.json_dev-utils@compiler-options_lit.ejs +2 -0
  438. package/templates/default/tsconfig.json_dev-utils@compiler-options_preact.ejs +2 -0
  439. package/templates/default/tsconfig.json_dev-utils@compiler-options_react.ejs +1 -0
  440. package/templates/default/tsconfig.json_dev-utils@compiler-options_solid.ejs +2 -0
  441. package/templates/default/tsconfig.json_dev-utils@include_has-e2e.ejs +1 -0
  442. package/templates/default/tsconfig.json_dev-utils@include_svelte.ejs +1 -0
  443. package/templates/default/tsconfig.json_dev-utils@include_vue.ejs +1 -0
  444. package/templates/default/tsconfig.json_dev-utils@types_jest.ejs +2 -0
  445. package/templates/default/tsconfig.json_dev-utils@types_svelte.ejs +2 -0
  446. package/templates/default/tsconfig.json_dev-utils@types_wdio-obsidian.ejs +5 -0
  447. package/templates/default/tsconfig.json_dev-utils@types_with-unofficial.ejs +2 -0
  448. package/templates/default/tsconfig.json_standalone.ejs +47 -0
  449. package/templates/default/tsconfig.json_standalone@compiler-options_i18next.ejs +1 -0
  450. package/templates/default/tsconfig.json_standalone@compiler-options_lit.ejs +2 -0
  451. package/templates/default/tsconfig.json_standalone@compiler-options_preact.ejs +2 -0
  452. package/templates/default/tsconfig.json_standalone@compiler-options_react.ejs +1 -0
  453. package/templates/default/tsconfig.json_standalone@compiler-options_solid.ejs +2 -0
  454. package/templates/default/tsconfig.json_standalone@include-extra_preact.ejs +2 -0
  455. package/templates/default/tsconfig.json_standalone@include-extra_react.ejs +2 -0
  456. package/templates/default/tsconfig.json_standalone@include-extra_solid.ejs +2 -0
  457. package/templates/default/tsconfig.json_standalone@include_has-e2e.ejs +1 -0
  458. package/templates/default/tsconfig.json_standalone@include_svelte.ejs +1 -0
  459. package/templates/default/tsconfig.json_standalone@include_vue.ejs +1 -0
  460. package/templates/default/tsconfig.json_standalone@types_jest.ejs +2 -0
  461. package/templates/default/tsconfig.json_standalone@types_svelte.ejs +2 -0
  462. package/templates/default/tsconfig.json_standalone@types_wdio-obsidian.ejs +5 -0
  463. package/templates/default/tsconfig.json_standalone@types_with-unofficial.ejs +2 -0
  464. package/templates/default/versions.json.ejs +3 -0
  465. package/templates/default/vite.config.ts.ejs +3 -0
  466. package/templates/default/vitest.config.ts_dev-utils.ejs +1 -0
  467. package/templates/default/vitest.config.ts_standalone.ejs +55 -0
  468. package/templates/default/vitest.config.ts_standalone@jsx_solid.ejs +11 -0
  469. package/templates/default/wdio.conf.ts.ejs +21 -0
  470. package/templates/default/webpack.config.ts.ejs +3 -0
package/src/main.ts ADDED
@@ -0,0 +1,650 @@
1
+ import {
2
+ cancel,
3
+ confirm,
4
+ intro,
5
+ log,
6
+ note,
7
+ outro,
8
+ select,
9
+ spinner,
10
+ text
11
+ } from '@clack/prompts';
12
+ import { spawn } from 'node:child_process';
13
+ import {
14
+ chmodSync,
15
+ existsSync,
16
+ readFileSync,
17
+ writeFileSync
18
+ } from 'node:fs';
19
+ import {
20
+ join,
21
+ resolve as resolvePath
22
+ } from 'node:path';
23
+ import { compare } from 'semver';
24
+
25
+ import type {
26
+ Answers,
27
+ GeneratorConfig,
28
+ PackageJson
29
+ } from './answers.ts';
30
+ import type { CliArgs } from './cli-args.ts';
31
+ import type { Overlay } from './overlay.ts';
32
+
33
+ import {
34
+ formatAnswersJson,
35
+ formatCreateCommand,
36
+ formatCreateScript,
37
+ getScriptExtension,
38
+ getShellForPlatform
39
+ } from './answers-export.ts';
40
+ import {
41
+ CONFIG_FILE_NAME,
42
+ Mode
43
+ } from './answers.ts';
44
+ import { assertNotCancelled } from './clack-utils.ts';
45
+ import {
46
+ getHelpText,
47
+ parseCliArgs
48
+ } from './cli-args.ts';
49
+ import { findUnlistableManifestAnswers } from './directory-constraints.ts';
50
+ import {
51
+ fetchCommunityPluginIds,
52
+ makeUnlistedPluginIdValidator,
53
+ validatePluginIdIsUnlisted
54
+ } from './directory-registry.ts';
55
+ import { needsInitialFormat } from './features/formatter/index.ts';
56
+ import {
57
+ getInstallCommand,
58
+ getRunCommand
59
+ } from './features/package-manager/index.ts';
60
+ import {
61
+ loadOverlay,
62
+ toRecordedOverlayPath
63
+ } from './overlay.ts';
64
+ import { createProjectFormatter } from './project-formatter.ts';
65
+ import {
66
+ getDefaultAnswers,
67
+ makePluginName,
68
+ promptAnswers
69
+ } from './prompts.ts';
70
+ import {
71
+ buildTemplate,
72
+ copyTemplates,
73
+ getScriptDir,
74
+ loadConfig,
75
+ recordHashesOnDisk
76
+ } from './templates.ts';
77
+ import {
78
+ fetchLatestObsidianVersion,
79
+ fetchLatestVersion,
80
+ resolveVersions
81
+ } from './versions.ts';
82
+
83
+ interface ExecError extends Error {
84
+ stderr: string;
85
+ }
86
+
87
+ interface ExecResult {
88
+ stderr: string;
89
+ stdout: string;
90
+ }
91
+
92
+ interface ResolvedExternalVersions {
93
+ minAppVersion: string;
94
+ resolvedVersions: ReadonlyMap<string, string>;
95
+ }
96
+
97
+ interface SavedConfig {
98
+ answers?: Answers;
99
+ }
100
+
101
+ /** Where this process's own arguments start: `node` and the script path come first. */
102
+ const ARGV_OFFSET = 2;
103
+
104
+ const JSON_INDENT_SPACES = 2;
105
+
106
+ /** Owner-writable, everyone-executable: a script the user must `chmod` before running is a poor hand-off. */
107
+ const SCRIPT_MODE = 0o755;
108
+
109
+ /**
110
+ * Refuses to scaffold a plugin the Community directory would not list, naming the flag that fixes it.
111
+ *
112
+ * Only on the create path: `runUpdate` reads an existing project's saved answers, which may predate any
113
+ * of this and are not ours to refuse. The same stderr-then-exit shape as a rejected flag, because that
114
+ * is what this is -- a usage error found one step later than the parser can find it.
115
+ */
116
+ function assertManifestAnswersAreListable(answers: Answers): Answers {
117
+ const problems = findUnlistableManifestAnswers(answers);
118
+ if (problems.length === 0) {
119
+ return answers;
120
+ }
121
+
122
+ const lines = problems.map((problem) => `"${problem.value}" is not a valid ${problem.key}. ${problem.message}.\nPass --${problem.key}=<value> to set it yourself.`);
123
+ exitWithUsageError(lines.join('\n\n'));
124
+ }
125
+
126
+ /**
127
+ * The config to write beside the generated files: the file hashes, the answers and, when one was applied,
128
+ * where the overlay lives -- which is what makes the next update apply it again.
129
+ */
130
+ function buildSavedConfig(newConfig: GeneratorConfig, answers: Answers, overlay: null | Overlay, targetDir: string): GeneratorConfig {
131
+ return overlay
132
+ ? { ...newConfig, answers, customTemplate: toRecordedOverlayPath(overlay.dir, targetDir) }
133
+ : { ...newConfig, answers };
134
+ }
135
+
136
+ async function checkForUpdates(currentVersion: string): Promise<void> {
137
+ const latestVer = await fetchLatestVersion('@mnaoumov/create-obsidian-plugin');
138
+ if (latestVer !== null && compare(currentVersion, latestVer) < 0) {
139
+ log.warn(`Your version is outdated. Latest: ${latestVer}. Update with:\n npm install -g @mnaoumov/create-obsidian-plugin`);
140
+ }
141
+ }
142
+
143
+ async function detectMode(cliArgs: CliArgs): Promise<Mode> {
144
+ if (cliArgs.mode) {
145
+ return cliArgs.mode;
146
+ }
147
+
148
+ const configPath = join(process.cwd(), CONFIG_FILE_NAME);
149
+ if (existsSync(configPath)) {
150
+ // Refused rather than defaulted: updating is not safe to assume about a project the caller may have
151
+ // Meant to leave alone, and creating would ignore the one thing detected here.
152
+ if (cliArgs.useDefaults) {
153
+ exitWithUsageError(`Existing project detected (${CONFIG_FILE_NAME}), and --yes cannot ask whether to update it.
154
+ Pass --mode=update to update it, or --mode=create to scaffold a new plugin beside it.`);
155
+ }
156
+
157
+ const shouldUpdate = await confirm({
158
+ message: 'Existing project detected. Would you like to update it?'
159
+ });
160
+ assertNotCancelled(shouldUpdate);
161
+ return shouldUpdate ? Mode.Update : Mode.Create;
162
+ }
163
+ return Mode.Create;
164
+ }
165
+
166
+ /**
167
+ * Checks the plugin id against `community-plugins.json`, the one directory constraint no synchronous prompt
168
+ * validator can check, and returns the answers with the id the run should use.
169
+ *
170
+ * - The list could not be fetched: a warning naming what went unchecked, and the answers unchanged --
171
+ * generating offline must still work.
172
+ * - The id is taken, under `--yes`: a usage error naming `--pluginId`, since there is nobody to ask.
173
+ * - The id is taken, interactively: ask again for the id alone rather than abort, which would throw away
174
+ * every other answer. Keeping it is offered too, for the author of that listed plugin re-scaffolding it.
175
+ */
176
+ async function ensurePluginIdIsUnlisted(answers: Answers, useDefaults: boolean): Promise<Answers> {
177
+ const s = spinner();
178
+ s.start('Checking the plugin id against the Community directory...');
179
+ const listedIds = await fetchCommunityPluginIds();
180
+
181
+ if (listedIds === null) {
182
+ s.stop('Could not check the plugin id against the Community directory.');
183
+ log.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.`);
184
+ return answers;
185
+ }
186
+
187
+ const collision = validatePluginIdIsUnlisted(answers.pluginId, listedIds);
188
+ if (collision === undefined) {
189
+ s.stop('The plugin id is free in the Community directory.');
190
+ return answers;
191
+ }
192
+
193
+ s.stop('The plugin id is already taken in the Community directory.');
194
+
195
+ if (useDefaults) {
196
+ exitWithUsageError(`${collision}.
197
+ Pass --pluginId=<value> to choose another.`);
198
+ }
199
+
200
+ log.warn(collision);
201
+ const choice = await select({
202
+ initialValue: 'reenter',
203
+ message: `What should happen to "${answers.pluginId}"?`,
204
+ options: [
205
+ { hint: 'Every other answer is kept', label: 'Choose a different id', value: 'reenter' },
206
+ { hint: 'Only if that listed plugin is your own', label: 'Keep it anyway', value: 'keep' },
207
+ { label: 'Cancel', value: 'cancel' }
208
+ ]
209
+ });
210
+ assertNotCancelled(choice);
211
+
212
+ if (choice === 'cancel') {
213
+ cancel('Aborted.');
214
+ process.exit(0);
215
+ }
216
+
217
+ if (choice === 'keep') {
218
+ return answers;
219
+ }
220
+
221
+ const pluginId = await text({
222
+ message: 'Plugin id (lowercase, hyphens allowed)',
223
+ validate: makeUnlistedPluginIdValidator(listedIds)
224
+ });
225
+ assertNotCancelled(pluginId);
226
+
227
+ // A name that is still the one derived from the old id was never a separate choice, so it follows the id.
228
+ const pluginName = answers.pluginName === makePluginName(answers.pluginId) ? makePluginName(pluginId) : answers.pluginName;
229
+ return { ...answers, pluginId, pluginName };
230
+ }
231
+
232
+ function execAsync(command: string, cwd: string): Promise<ExecResult> {
233
+ return new Promise((resolve, reject) => {
234
+ const child = spawn(command, [], {
235
+ cwd,
236
+ shell: true,
237
+ stdio: 'pipe'
238
+ });
239
+
240
+ let stdout = '';
241
+ let stderr = '';
242
+
243
+ child.stdout.on('data', (data: Buffer) => {
244
+ stdout += data.toString('utf-8');
245
+ });
246
+
247
+ child.stderr.on('data', (data: Buffer) => {
248
+ stderr += data.toString('utf-8');
249
+ });
250
+
251
+ child.on('close', (exitCode) => {
252
+ if (exitCode !== 0) {
253
+ const error = new Error(`Command failed with exit code ${String(exitCode)}`);
254
+ (error as ExecError).stderr = stderr;
255
+ reject(error);
256
+ return;
257
+ }
258
+ resolve({ stderr, stdout });
259
+ });
260
+
261
+ child.on('error', reject);
262
+ });
263
+ }
264
+
265
+ /**
266
+ * Stops with a usage error on stderr: a rejected answer, an overlay that does not load, or a question `--yes`
267
+ * would otherwise have had to ask. Under `--yes` there may be nobody to ask, and a prompt with no TTY behind
268
+ * it waits forever -- so the run names the flag that answers it and exits instead.
269
+ */
270
+ function exitWithUsageError(message: string): never {
271
+ process.stderr.write(`${message}\n`);
272
+ process.exit(1);
273
+ }
274
+
275
+ /**
276
+ * Loads the overlay or stops with the reason, the same stderr-then-exit shape as a rejected flag: an
277
+ * overlay that does not load is a usage error, and it is found before any question is asked.
278
+ */
279
+ function loadOverlayOrExit(dir: string, hint = ''): Overlay {
280
+ try {
281
+ return loadOverlay(dir);
282
+ } catch (error: unknown) {
283
+ exitWithUsageError(`${error instanceof Error ? error.message : String(error)}${hint}`);
284
+ }
285
+ }
286
+
287
+ async function main(): Promise<void> {
288
+ const packageJsonPath = join(getScriptDir(), '..', 'package.json');
289
+ const packageJson = JSON.parse(readFileSync(packageJsonPath, 'utf-8')) as PackageJson;
290
+ const currentVersion = packageJson.version ?? '0.0.0';
291
+
292
+ const m = '\x1b[38;5;135m';
293
+ const r = '\x1b[0m';
294
+ const banner = `
295
+ ${m}◆${r}
296
+ ${m}◆ ◆${r}
297
+ ${m}◆ ◆ ◆${r} ${m}create-obsidian-plugin${r}
298
+ ${m}◆ ◆${r} ${m}v${currentVersion}${r}
299
+ ${m}◆${r} ${m}@mnaoumov${r}
300
+
301
+ `;
302
+ let cliArgs;
303
+ try {
304
+ cliArgs = parseCliArgs(process.argv.slice(ARGV_OFFSET));
305
+ } catch (error: unknown) {
306
+ // Straight to stderr, before the banner: a rejected flag is a usage error, and burying it under the
307
+ // Intro would make it look like the run had started.
308
+ process.stderr.write(`${error instanceof Error ? error.message : String(error)}\n`);
309
+ process.exit(1);
310
+ }
311
+
312
+ if (cliArgs.showHelp) {
313
+ process.stdout.write(`${getHelpText()}\n`);
314
+ return;
315
+ }
316
+
317
+ process.stdout.write(banner);
318
+ intro('Let\'s build an Obsidian plugin!');
319
+
320
+ await checkForUpdates(currentVersion);
321
+
322
+ const mode = await detectMode(cliArgs);
323
+
324
+ if (mode === Mode.Create) {
325
+ await runCreate(currentVersion, cliArgs);
326
+ } else {
327
+ await runUpdate(currentVersion, cliArgs);
328
+ }
329
+ }
330
+
331
+ /**
332
+ * Offers to save the answers just given as a runnable script or as an answers file, before scaffolding.
333
+ *
334
+ * The menu loops rather than being a one-shot choice, so both forms can be captured in one session -- a
335
+ * save is never at the expense of the generation the user came for.
336
+ *
337
+ * The executable-bit on the `sh` form matters: a script the user has to `chmod` before running is a
338
+ * worse hand-off than the command they could already have copied off the screen.
339
+ */
340
+ async function offerAnswersExport(answers: Answers, customTemplate: string | undefined): Promise<void> {
341
+ const shell = getShellForPlatform(process.platform);
342
+ const scriptName = `obsidian-${answers.pluginId}-create${getScriptExtension(shell)}`;
343
+ const answersFileName = `obsidian-${answers.pluginId}-answers.json`;
344
+
345
+ async function ask(): Promise<string> {
346
+ const choice = await select({
347
+ initialValue: 'generate',
348
+ message: 'Your answers are collected.',
349
+ options: [
350
+ { hint: 'Scaffold the plugin now', label: 'Start generation', value: 'generate' },
351
+ { hint: scriptName, label: 'Save a script for non-interactive generation', value: 'script' },
352
+ { hint: answersFileName, label: 'Save an answers file for non-interactive generation', value: 'answers' }
353
+ ]
354
+ });
355
+ assertNotCancelled(choice);
356
+ return choice;
357
+ }
358
+
359
+ let choice = await ask();
360
+
361
+ while (choice !== 'generate') {
362
+ if (choice === 'script') {
363
+ const scriptPath = join(process.cwd(), scriptName);
364
+ writeFileSync(scriptPath, formatCreateScript(answers, shell, customTemplate));
365
+ if (shell === 'sh') {
366
+ chmodSync(scriptPath, SCRIPT_MODE);
367
+ }
368
+ log.success(`Wrote ${scriptName}`);
369
+ note(formatCreateCommand(answers, shell, customTemplate), 'Non-interactive command');
370
+ } else {
371
+ writeFileSync(join(process.cwd(), answersFileName), formatAnswersJson(answers));
372
+ log.success(`Wrote ${answersFileName}`);
373
+ const overlayFlag = customTemplate ? ` --customTemplate=${customTemplate}` : '';
374
+ note(`npm create @mnaoumov/obsidian-plugin -- --yes --mode=create --answersFile=${answersFileName}${overlayFlag}`, 'Non-interactive command');
375
+ }
376
+
377
+ choice = await ask();
378
+ }
379
+ }
380
+
381
+ // The `minAppVersion` is looked up here, next to the dependency versions, for the same reason they are:
382
+ // `copyTemplates` is synchronous and must not touch the network, so everything fetched is resolved before
383
+ // It runs and handed in.
384
+ async function resolveExternalVersions(answers: Answers, overlay: null | Overlay): Promise<ResolvedExternalVersions> {
385
+ const s = spinner();
386
+ s.start('Resolving versions...');
387
+ const [resolvedVersions, minAppVersion] = await Promise.all([
388
+ resolveVersions(buildTemplate(answers, overlay).dependencies),
389
+ fetchLatestObsidianVersion()
390
+ ]);
391
+ s.stop('Versions resolved.');
392
+ return { minAppVersion, resolvedVersions };
393
+ }
394
+
395
+ async function runCreate(currentVersion: string, cliArgs: CliArgs): Promise<void> {
396
+ const { answers: suppliedAnswers, customTemplate, useDefaults } = cliArgs;
397
+ const overlay = customTemplate ? loadOverlayOrExit(resolvePath(customTemplate)) : null;
398
+ const answers = await ensurePluginIdIsUnlisted(
399
+ assertManifestAnswersAreListable(useDefaults ? getDefaultAnswers(suppliedAnswers) : await promptAnswers(suppliedAnswers)),
400
+ useDefaults
401
+ );
402
+
403
+ // Not under `--yes`: that path is what an exported script itself runs, so offering to export from
404
+ // Inside it would be asking a question of a run that exists to ask none.
405
+ if (!useDefaults) {
406
+ await offerAnswersExport(answers, customTemplate);
407
+ }
408
+
409
+ const targetDir = join(process.cwd(), `obsidian-${answers.pluginId}`);
410
+
411
+ if (existsSync(targetDir) && !cliArgs.force) {
412
+ // The prompt's own default is no, but taking it silently would exit 0 having generated nothing.
413
+ if (useDefaults) {
414
+ exitWithUsageError(`Directory obsidian-${answers.pluginId} already exists, and --yes cannot ask whether to scaffold into it.
415
+ Pass --force to scaffold into it anyway.`);
416
+ }
417
+
418
+ const shouldContinue = await confirm({
419
+ initialValue: false,
420
+ message: `Directory obsidian-${answers.pluginId} already exists. Continue anyway?`
421
+ });
422
+ assertNotCancelled(shouldContinue);
423
+ if (!shouldContinue) {
424
+ cancel('Aborted.');
425
+ process.exit(0);
426
+ }
427
+ }
428
+
429
+ const { minAppVersion, resolvedVersions } = await resolveExternalVersions(answers, overlay);
430
+
431
+ const s = spinner();
432
+ s.start('Scaffolding plugin...');
433
+ const newConfig = copyTemplates(answers, targetDir, currentVersion, null, resolvedVersions, minAppVersion, overlay);
434
+ const configPath = join(targetDir, CONFIG_FILE_NAME);
435
+ const configWithAnswers = buildSavedConfig(newConfig, answers, overlay, targetDir);
436
+ writeFileSync(configPath, `${JSON.stringify(configWithAnswers, null, JSON_INDENT_SPACES)}\n`);
437
+ s.stop('Plugin scaffolded.');
438
+
439
+ // `--yes` installs nothing and so formats nothing, which leaves a prettier or biome project failing its own
440
+ // `format:check` until `format` runs once. It stays out of the install on purpose -- a script may want to
441
+ // Own that -- so the `format` it skipped is named in the steps instead.
442
+ const isFormatted = useDefaults ? false : await runPostScaffold(targetDir, answers);
443
+
444
+ const pm = answers.packageManager;
445
+ const dirName = `obsidian-${answers.pluginId}`;
446
+ const needsInstall = !existsSync(join(targetDir, 'node_modules'));
447
+ const needsFormat = needsInitialFormat(answers.formatter) && !isFormatted;
448
+ const steps = [
449
+ ...(needsInstall ? [getInstallCommand(pm)] : []),
450
+ ...(needsFormat ? [getRunCommand(pm, 'format')] : []),
451
+ getRunCommand(pm, 'dev')
452
+ ];
453
+
454
+ note(`cd ${dirName} && ${steps.join(' && ')}`, 'Next steps');
455
+ outro('Happy coding!');
456
+ }
457
+
458
+ // The templates are authored in one style, the one the compared plugins use. dprint is configured to
459
+ // Match it, but prettier and biome cannot be configured to reproduce it byte for byte -- biome
460
+ // Collapses an empty object to
461
+ // `{}` whatever the settings say -- so a project that picked either of those would be committed
462
+ // Already failing its own `format:check`. Formatting once here settles that, in the tool's own style,
463
+ // Before the initial commit is taken.
464
+ async function runInitialFormat(targetDir: string, answers: Answers, isInstalled: boolean): Promise<boolean> {
465
+ if (!isInstalled || answers.formatter === 'none') {
466
+ return false;
467
+ }
468
+
469
+ const s = spinner();
470
+ s.start('Formatting...');
471
+ try {
472
+ await execAsync(getRunCommand(answers.packageManager, 'format'), targetDir);
473
+ // Recorded before the initial commit, so the files the format just rewrote count as the generator's on
474
+ // The next update instead of as hand edits.
475
+ const configPath = join(targetDir, CONFIG_FILE_NAME);
476
+ const config = JSON.parse(readFileSync(configPath, 'utf-8')) as GeneratorConfig;
477
+ writeFileSync(configPath, `${JSON.stringify(recordHashesOnDisk(targetDir, config), null, JSON_INDENT_SPACES)}\n`);
478
+ s.stop('Formatted.');
479
+ return true;
480
+ } catch {
481
+ s.stop('Failed to format. Run `format` manually.');
482
+ return false;
483
+ }
484
+ }
485
+
486
+ async function runPostScaffold(targetDir: string, answers: Answers): Promise<boolean> {
487
+ const pm = answers.packageManager;
488
+ const installCmd = getInstallCommand(pm);
489
+
490
+ const shouldInstall = await confirm({
491
+ initialValue: true,
492
+ message: `Install dependencies with ${pm}?`
493
+ });
494
+ assertNotCancelled(shouldInstall);
495
+
496
+ let isInstalled = false;
497
+
498
+ if (shouldInstall) {
499
+ const s = spinner();
500
+ s.start('Installing dependencies...');
501
+ try {
502
+ await execAsync(installCmd, targetDir);
503
+ isInstalled = true;
504
+ s.stop('Dependencies installed.');
505
+ } catch (error: unknown) {
506
+ s.stop(`Failed to install dependencies. Run \`${installCmd}\` manually.`);
507
+ if (error instanceof Error && 'stderr' in error) {
508
+ log.error(String(error.stderr));
509
+ }
510
+ }
511
+ }
512
+
513
+ const isFormatted = await runInitialFormat(targetDir, answers, isInstalled);
514
+
515
+ const shouldGitInit = await confirm({
516
+ initialValue: true,
517
+ message: 'Initialize a git repository?'
518
+ });
519
+ assertNotCancelled(shouldGitInit);
520
+
521
+ if (shouldGitInit) {
522
+ const s = spinner();
523
+ s.start('Initializing git repository...');
524
+ try {
525
+ // `-b` rather than a bare `git init`: without it the branch is whatever the user's
526
+ // `init.defaultBranch` says, which need not be the branch the generated CI workflow triggers on --
527
+ // A mismatch produces a repo whose CI never fires. Both come from the same answer.
528
+ await execAsync(`git init -b ${answers.defaultBranch}`, targetDir);
529
+ await execAsync('git add -A', targetDir);
530
+ await execAsync('git commit -m "Initial commit from create-obsidian-plugin"', targetDir);
531
+ s.stop('Git repository initialized with initial commit.');
532
+ } catch {
533
+ s.stop('Failed to initialize git. Run `git init` manually.');
534
+ }
535
+ }
536
+
537
+ const shouldCreateGitHubRepo = await confirm({
538
+ initialValue: false,
539
+ message: 'Create a GitHub repository?'
540
+ });
541
+ assertNotCancelled(shouldCreateGitHubRepo);
542
+
543
+ if (shouldCreateGitHubRepo) {
544
+ const s = spinner();
545
+ s.start('Creating GitHub repository...');
546
+ try {
547
+ await execAsync(`gh repo create obsidian-${answers.pluginId} --public --source=. --push`, targetDir);
548
+ s.stop('GitHub repository created and pushed.');
549
+ } catch {
550
+ s.stop('Failed to create GitHub repo. Make sure `gh` CLI is installed and authenticated.');
551
+ }
552
+ }
553
+
554
+ return isFormatted;
555
+ }
556
+
557
+ async function runUpdate(currentVersion: string, cliArgs: CliArgs): Promise<void> {
558
+ const { answers: suppliedAnswers, customTemplate, useDefaults } = cliArgs;
559
+ const targetDir = process.cwd();
560
+ const existingConfig = loadConfig(targetDir);
561
+
562
+ if (!existingConfig) {
563
+ log.error('No config file found. Cannot update.');
564
+ process.exit(1);
565
+ }
566
+
567
+ log.info(`Current project was generated with v${existingConfig.generatorVersion}`);
568
+
569
+ // The recorded overlay is re-applied unless a flag names another one or, given empty, drops it. A recorded
570
+ // One that is gone is refused rather than skipped: skipping would regenerate every file it overrode from
571
+ // The built-ins, and each of those still matches its recorded hash, so all of them would be overwritten.
572
+ let overlay: null | Overlay = null;
573
+ if (customTemplate !== undefined) {
574
+ overlay = customTemplate ? loadOverlayOrExit(resolvePath(customTemplate)) : null;
575
+ } else if (existingConfig.customTemplate) {
576
+ overlay = loadOverlayOrExit(
577
+ resolvePath(targetDir, existingConfig.customTemplate),
578
+ `
579
+ This project records it in ${CONFIG_FILE_NAME}. Pass --customTemplate=<dir> to point at where it is now, or --customTemplate= to stop using it.`
580
+ );
581
+ log.info(`Applying the custom template recorded for this project: ${existingConfig.customTemplate}`);
582
+ }
583
+
584
+ const configPath = join(targetDir, CONFIG_FILE_NAME);
585
+ const savedConfig = JSON.parse(readFileSync(configPath, 'utf-8')) as SavedConfig;
586
+
587
+ let answers: Answers;
588
+
589
+ if (savedConfig.answers) {
590
+ // A flag beats the saved answer, which is what makes `--<key>=<value>` a way to CHANGE one setting on
591
+ // An existing project without walking the whole wizard again.
592
+ const saved: Answers = { ...savedConfig.answers, ...suppliedAnswers };
593
+ log.info('Using saved answers from previous generation.');
594
+ // Under `--yes` the flags ARE the changes, so there is nothing left to ask.
595
+ const shouldRePrompt = useDefaults
596
+ ? false
597
+ : await confirm({
598
+ initialValue: false,
599
+ message: 'Would you like to change any settings?'
600
+ });
601
+ assertNotCancelled(shouldRePrompt);
602
+
603
+ if (shouldRePrompt) {
604
+ answers = await promptAnswers(saved);
605
+ } else {
606
+ answers = saved;
607
+ }
608
+ } else {
609
+ // Not the defaults: each of them, the plugin id first, would be a guess about a project that exists.
610
+ if (useDefaults) {
611
+ exitWithUsageError(`${CONFIG_FILE_NAME} records no answers, so --yes has none to reuse.
612
+ Run without --yes to answer them again.`);
613
+ }
614
+ log.warn('No saved answers found. Please provide the settings again.');
615
+ answers = await promptAnswers(suppliedAnswers);
616
+ }
617
+
618
+ // Only an id this run CHANGED is checked: a plugin already listed in the directory would otherwise match
619
+ // Itself on every update. With no saved answers there is no id to compare against, and the likeliest
620
+ // Answer is the project's own id, so that case is left unchecked too.
621
+ const savedPluginId = savedConfig.answers?.pluginId;
622
+ if (savedPluginId !== undefined && answers.pluginId !== savedPluginId) {
623
+ answers = await ensurePluginIdIsUnlisted(answers, useDefaults);
624
+ }
625
+
626
+ // A project that has already released carries a `manifest.json` its own `npm run version` rewrote, so its
627
+ // Hash no longer matches the recorded one and the updater skips it -- the freshly looked-up
628
+ // `minAppVersion` only ever reaches a manifest nobody has touched.
629
+ const { minAppVersion, resolvedVersions } = await resolveExternalVersions(answers, overlay);
630
+
631
+ const projectFormatter = createProjectFormatter(targetDir, answers.formatter);
632
+ if (projectFormatter === null && needsInitialFormat(answers.formatter)) {
633
+ log.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.`);
634
+ }
635
+
636
+ const s = spinner();
637
+ s.start('Updating project files...');
638
+ copyTemplates(answers, targetDir, currentVersion, existingConfig, resolvedVersions, minAppVersion, overlay, projectFormatter);
639
+ s.stop('Update complete.');
640
+
641
+ const newConfig = loadConfig(targetDir);
642
+ if (newConfig) {
643
+ const configWithAnswers = buildSavedConfig(newConfig, answers, overlay, targetDir);
644
+ writeFileSync(configPath, `${JSON.stringify(configWithAnswers, null, JSON_INDENT_SPACES)}\n`);
645
+ }
646
+
647
+ outro('Project updated successfully!');
648
+ }
649
+
650
+ await main();
@@ -0,0 +1,19 @@
1
+ {
2
+ "comment": [
3
+ "Read automatically by depcheck (`npx depcheck`), which reports declared dependencies nothing appears to use.",
4
+ "depcheck cannot see a package that is only invoked as a CLI from a script, named as a string in a config, or",
5
+ "reached by a compiler option, so each name below is a verified false positive with the reference that proves it.",
6
+ "Anything depcheck still reports is genuinely unused: remove it, or add it here together with its reason.",
7
+ "The key is `ignores`, depcheck's CLI-style rc name; `ignoreMatches` is the API-only spelling and is silently",
8
+ "ignored in this file.",
9
+ "",
10
+ <% _depcheckIgnores.forEach((entry, i) => { -%>
11
+ <%- JSON.stringify(`${entry.packageName} - ${entry.reason}`) %><%= i < _depcheckIgnores.length - 1 ? ',' : '' %>
12
+ <% }); -%>
13
+ ],
14
+ "ignores": [
15
+ <% _depcheckIgnores.forEach((entry, i) => { -%>
16
+ <%- JSON.stringify(entry.packageName) %><%= i < _depcheckIgnores.length - 1 ? ',' : '' %>
17
+ <% }); -%>
18
+ ]
19
+ }
@@ -0,0 +1,11 @@
1
+ # top-most EditorConfig file
2
+ root = true
3
+
4
+ [*]
5
+ charset = utf-8
6
+ end_of_line = lf
7
+ insert_final_newline = true
8
+ indent_style = space
9
+ indent_size = 2
10
+ tab_width = 2
11
+ trim_trailing_whitespace = true
@@ -0,0 +1 @@
1
+ <%- render('vault') -%>
@@ -0,0 +1,2 @@
1
+ # Set this to your vault's .obsidian folder to auto-copy the plugin on build
2
+ # OBSIDIAN_CONFIG_FOLDER=path/to/my/vault/.obsidian
@@ -0,0 +1 @@
1
+ OBSIDIAN_CONFIG_FOLDER=<%- obsidianConfigFolder %>