create-vite-hyperapp 1.3.0 → 2.0.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.
Files changed (222) hide show
  1. package/README.md +110 -18
  2. package/create-vite-hyperapp.mjs +193 -0
  3. package/package.json +18 -11
  4. package/templates/{template → common}/_gitignore +1 -1
  5. package/templates/{template-ts/src → common/src/app}/style.css +0 -4
  6. package/templates/{template → js-html-cli}/index.html +3 -3
  7. package/templates/js-html-cli/jsconfig.json +13 -0
  8. package/templates/js-html-cli/package.json +20 -0
  9. package/templates/js-html-cli/src/app/counter.js +5 -0
  10. package/templates/js-html-cli/src/app/logos.js +24 -0
  11. package/templates/js-html-cli/src/app/main.js +24 -0
  12. package/templates/{template → js-html-cli}/src/run.js +2 -2
  13. package/templates/js-html-cli/vite.config.js +12 -0
  14. package/templates/{template-ssr → js-html-ssr}/index.html +2 -2
  15. package/templates/js-html-ssr/jsconfig.json +13 -0
  16. package/templates/js-html-ssr/package.json +25 -0
  17. package/templates/{template-ssr → js-html-ssr}/server.js +10 -8
  18. package/templates/js-html-ssr/src/app/counter.js +5 -0
  19. package/templates/js-html-ssr/src/app/logos.js +24 -0
  20. package/templates/js-html-ssr/src/app/main.js +24 -0
  21. package/templates/{template-ssr → js-html-ssr}/src/entry-client.js +2 -2
  22. package/templates/js-html-ssr/src/entry-server.js +5 -0
  23. package/templates/js-html-ssr/vite.config.js +13 -0
  24. package/templates/js-jsx-cli/index.html +13 -0
  25. package/templates/js-jsx-cli/jsconfig.json +15 -0
  26. package/templates/{template → js-jsx-cli}/package.json +3 -3
  27. package/templates/js-jsx-cli/src/app/logos.jsx +15 -0
  28. package/templates/{template/src → js-jsx-cli/src/app}/main.jsx +1 -13
  29. package/templates/js-jsx-cli/src/run.js +5 -0
  30. package/templates/js-jsx-cli/vite.config.js +12 -0
  31. package/templates/js-jsx-ssr/index.html +13 -0
  32. package/templates/js-jsx-ssr/jsconfig.json +15 -0
  33. package/templates/{template-ssr → js-jsx-ssr}/package.json +7 -7
  34. package/templates/{template-ts-ssr → js-jsx-ssr}/server.js +11 -9
  35. package/templates/js-jsx-ssr/src/app/logos.jsx +15 -0
  36. package/templates/{template-ssr/src → js-jsx-ssr/src/app}/main.jsx +1 -13
  37. package/templates/js-jsx-ssr/src/entry-client.js +5 -0
  38. package/templates/js-jsx-ssr/src/entry-server.js +5 -0
  39. package/templates/js-jsx-ssr/vite.config.js +13 -0
  40. package/templates/js-plain-cli/index.html +13 -0
  41. package/templates/js-plain-cli/jsconfig.json +13 -0
  42. package/templates/{template-ts → js-plain-cli}/package.json +4 -5
  43. package/templates/js-plain-cli/src/app/counter.js +8 -0
  44. package/templates/js-plain-cli/src/app/logos.js +18 -0
  45. package/templates/js-plain-cli/src/app/main.js +25 -0
  46. package/templates/js-plain-cli/src/run.js +5 -0
  47. package/templates/js-plain-cli/vite.config.js +12 -0
  48. package/templates/js-plain-ssr/index.html +13 -0
  49. package/templates/js-plain-ssr/jsconfig.json +13 -0
  50. package/templates/{template-ts-ssr → js-plain-ssr}/package.json +8 -9
  51. package/templates/js-plain-ssr/server.js +62 -0
  52. package/templates/js-plain-ssr/src/app/counter.js +8 -0
  53. package/templates/js-plain-ssr/src/app/logos.js +18 -0
  54. package/templates/js-plain-ssr/src/app/main.js +25 -0
  55. package/templates/js-plain-ssr/src/entry-client.js +5 -0
  56. package/templates/js-plain-ssr/src/entry-server.js +5 -0
  57. package/templates/js-plain-ssr/vite.config.js +13 -0
  58. package/templates/tj-html-cli/index.html +13 -0
  59. package/templates/tj-html-cli/jsconfig.json +29 -0
  60. package/templates/tj-html-cli/package.json +21 -0
  61. package/templates/tj-html-cli/src/app/counter.js +16 -0
  62. package/templates/tj-html-cli/src/app/logos.js +24 -0
  63. package/templates/tj-html-cli/src/app/main.js +27 -0
  64. package/templates/tj-html-cli/src/run.js +5 -0
  65. package/templates/tj-html-cli/src/types/global.d.ts +13 -0
  66. package/templates/tj-html-cli/src/types/hyperapp-html.d.ts +101 -0
  67. package/templates/tj-html-cli/src/types/hyperapp-svg.d.ts +73 -0
  68. package/templates/tj-html-cli/src/types/hyperapp-tags.d.ts +34 -0
  69. package/templates/tj-html-cli/vite.config.js +12 -0
  70. package/templates/tj-html-ssr/index.html +13 -0
  71. package/templates/tj-html-ssr/jsconfig.json +31 -0
  72. package/templates/tj-html-ssr/package.json +29 -0
  73. package/templates/tj-html-ssr/server.js +63 -0
  74. package/templates/tj-html-ssr/src/app/counter.js +16 -0
  75. package/templates/tj-html-ssr/src/app/logos.js +24 -0
  76. package/templates/tj-html-ssr/src/app/main.js +27 -0
  77. package/templates/tj-html-ssr/src/entry-client.js +5 -0
  78. package/templates/tj-html-ssr/src/entry-server.js +5 -0
  79. package/templates/tj-html-ssr/src/types/global.d.ts +13 -0
  80. package/templates/tj-html-ssr/src/types/hyperapp-html.d.ts +101 -0
  81. package/templates/tj-html-ssr/src/types/hyperapp-svg.d.ts +73 -0
  82. package/templates/tj-html-ssr/src/types/hyperapp-tags.d.ts +34 -0
  83. package/templates/tj-html-ssr/vite.config.js +13 -0
  84. package/templates/tj-jsx-cli/index.html +13 -0
  85. package/templates/tj-jsx-cli/jsconfig.json +31 -0
  86. package/templates/tj-jsx-cli/package.json +19 -0
  87. package/templates/tj-jsx-cli/src/app/counter.jsx +18 -0
  88. package/templates/tj-jsx-cli/src/app/logos.jsx +15 -0
  89. package/templates/tj-jsx-cli/src/app/main.jsx +26 -0
  90. package/templates/tj-jsx-cli/src/run.js +5 -0
  91. package/templates/tj-jsx-cli/src/types/global.d.ts +13 -0
  92. package/templates/tj-jsx-cli/vite.config.js +12 -0
  93. package/templates/tj-jsx-ssr/index.html +13 -0
  94. package/templates/tj-jsx-ssr/jsconfig.json +33 -0
  95. package/templates/tj-jsx-ssr/package.json +27 -0
  96. package/templates/tj-jsx-ssr/server.js +63 -0
  97. package/templates/tj-jsx-ssr/src/app/counter.jsx +18 -0
  98. package/templates/tj-jsx-ssr/src/app/logos.jsx +15 -0
  99. package/templates/tj-jsx-ssr/src/app/main.jsx +26 -0
  100. package/templates/tj-jsx-ssr/src/entry-client.js +5 -0
  101. package/templates/tj-jsx-ssr/src/entry-server.js +5 -0
  102. package/templates/tj-jsx-ssr/src/types/global.d.ts +13 -0
  103. package/templates/tj-jsx-ssr/vite.config.js +13 -0
  104. package/templates/tj-plain-cli/index.html +13 -0
  105. package/templates/tj-plain-cli/jsconfig.json +29 -0
  106. package/templates/tj-plain-cli/package.json +19 -0
  107. package/templates/tj-plain-cli/src/app/counter.js +19 -0
  108. package/templates/tj-plain-cli/src/app/logos.js +18 -0
  109. package/templates/tj-plain-cli/src/app/main.js +28 -0
  110. package/templates/tj-plain-cli/src/run.js +5 -0
  111. package/templates/tj-plain-cli/src/types/global.d.ts +13 -0
  112. package/templates/tj-plain-cli/src/types/hyperapp-h.d.ts +37 -0
  113. package/templates/tj-plain-cli/vite.config.js +12 -0
  114. package/templates/tj-plain-ssr/index.html +13 -0
  115. package/templates/tj-plain-ssr/jsconfig.json +31 -0
  116. package/templates/tj-plain-ssr/package.json +27 -0
  117. package/templates/tj-plain-ssr/server.js +63 -0
  118. package/templates/tj-plain-ssr/src/app/counter.js +19 -0
  119. package/templates/tj-plain-ssr/src/app/logos.js +18 -0
  120. package/templates/tj-plain-ssr/src/app/main.js +28 -0
  121. package/templates/tj-plain-ssr/src/entry-client.js +5 -0
  122. package/templates/tj-plain-ssr/src/entry-server.js +5 -0
  123. package/templates/tj-plain-ssr/src/types/global.d.ts +13 -0
  124. package/templates/tj-plain-ssr/src/types/hyperapp-h.d.ts +37 -0
  125. package/templates/tj-plain-ssr/vite.config.js +13 -0
  126. package/templates/{template-ts → ts-html-cli}/index.html +3 -3
  127. package/templates/ts-html-cli/package.json +21 -0
  128. package/templates/ts-html-cli/src/app/counter.ts +12 -0
  129. package/templates/ts-html-cli/src/app/logos.ts +33 -0
  130. package/templates/ts-html-cli/src/app/main.ts +27 -0
  131. package/templates/{template-ts → ts-html-cli}/src/run.ts +2 -2
  132. package/templates/ts-html-cli/src/types/hyperapp-html.d.ts +101 -0
  133. package/templates/ts-html-cli/src/types/hyperapp-svg.d.ts +73 -0
  134. package/templates/ts-html-cli/src/types/hyperapp-tags.d.ts +34 -0
  135. package/templates/ts-html-cli/tsconfig.json +27 -0
  136. package/templates/ts-html-cli/vite.config.ts +12 -0
  137. package/templates/{template-ts-ssr → ts-html-ssr}/index.html +2 -2
  138. package/templates/ts-html-ssr/package.json +32 -0
  139. package/templates/ts-html-ssr/server.ts +63 -0
  140. package/templates/ts-html-ssr/src/app/counter.ts +12 -0
  141. package/templates/ts-html-ssr/src/app/logos.ts +33 -0
  142. package/templates/ts-html-ssr/src/app/main.ts +27 -0
  143. package/templates/{template-ts-ssr → ts-html-ssr}/src/entry-client.ts +2 -2
  144. package/templates/ts-html-ssr/src/entry-server.ts +5 -0
  145. package/templates/ts-html-ssr/src/types/hyperapp-html.d.ts +101 -0
  146. package/templates/ts-html-ssr/src/types/hyperapp-svg.d.ts +73 -0
  147. package/templates/ts-html-ssr/src/types/hyperapp-tags.d.ts +34 -0
  148. package/templates/ts-html-ssr/tsconfig.json +27 -0
  149. package/templates/ts-html-ssr/vite.config.ts +13 -0
  150. package/templates/ts-jsx-cli/index.html +13 -0
  151. package/templates/ts-jsx-cli/package.json +21 -0
  152. package/templates/ts-jsx-cli/src/app/counter.tsx +14 -0
  153. package/templates/ts-jsx-cli/src/app/logos.tsx +19 -0
  154. package/templates/ts-jsx-cli/src/app/main.tsx +26 -0
  155. package/templates/ts-jsx-cli/src/assets/typescript.svg +1 -0
  156. package/templates/ts-jsx-cli/src/run.ts +5 -0
  157. package/templates/{template-ts → ts-jsx-cli}/tsconfig.json +6 -3
  158. package/templates/ts-jsx-cli/vite.config.ts +12 -0
  159. package/templates/ts-jsx-ssr/index.html +13 -0
  160. package/templates/ts-jsx-ssr/package.json +30 -0
  161. package/templates/ts-jsx-ssr/server.ts +63 -0
  162. package/templates/ts-jsx-ssr/src/app/counter.tsx +14 -0
  163. package/templates/ts-jsx-ssr/src/app/logos.tsx +19 -0
  164. package/templates/ts-jsx-ssr/src/app/main.tsx +26 -0
  165. package/templates/ts-jsx-ssr/src/assets/typescript.svg +1 -0
  166. package/templates/ts-jsx-ssr/src/entry-client.ts +5 -0
  167. package/templates/ts-jsx-ssr/src/entry-server.ts +5 -0
  168. package/templates/{template-ts-ssr → ts-jsx-ssr}/tsconfig.json +7 -4
  169. package/templates/ts-jsx-ssr/vite.config.ts +13 -0
  170. package/templates/ts-plain-cli/index.html +13 -0
  171. package/templates/ts-plain-cli/package.json +21 -0
  172. package/templates/ts-plain-cli/src/app/counter.ts +13 -0
  173. package/templates/ts-plain-cli/src/app/logos.ts +30 -0
  174. package/templates/ts-plain-cli/src/app/main.ts +27 -0
  175. package/templates/ts-plain-cli/src/assets/typescript.svg +1 -0
  176. package/templates/ts-plain-cli/src/run.ts +5 -0
  177. package/templates/ts-plain-cli/src/types/hyperapp-h.d.ts +37 -0
  178. package/templates/ts-plain-cli/tsconfig.json +27 -0
  179. package/templates/ts-plain-cli/vite.config.ts +12 -0
  180. package/templates/ts-plain-ssr/index.html +13 -0
  181. package/templates/ts-plain-ssr/package.json +30 -0
  182. package/templates/ts-plain-ssr/server.ts +63 -0
  183. package/templates/ts-plain-ssr/src/app/counter.ts +13 -0
  184. package/templates/ts-plain-ssr/src/app/logos.ts +30 -0
  185. package/templates/ts-plain-ssr/src/app/main.ts +27 -0
  186. package/templates/ts-plain-ssr/src/assets/typescript.svg +1 -0
  187. package/templates/ts-plain-ssr/src/entry-client.ts +5 -0
  188. package/templates/ts-plain-ssr/src/entry-server.ts +5 -0
  189. package/templates/ts-plain-ssr/src/types/hyperapp-h.d.ts +37 -0
  190. package/templates/ts-plain-ssr/tsconfig.json +27 -0
  191. package/templates/ts-plain-ssr/vite.config.ts +13 -0
  192. package/index.js +0 -154
  193. package/templates/template/public/vite.svg +0 -1
  194. package/templates/template/src/hyperapp.svg +0 -4
  195. package/templates/template/src/style.css +0 -97
  196. package/templates/template/vite.config.js +0 -6
  197. package/templates/template-ssr/_gitignore +0 -24
  198. package/templates/template-ssr/src/entry-server.js +0 -5
  199. package/templates/template-ssr/src/style.css +0 -97
  200. package/templates/template-ssr/vite.config.js +0 -6
  201. package/templates/template-ts/_gitignore +0 -24
  202. package/templates/template-ts/public/vite.svg +0 -1
  203. package/templates/template-ts/src/counter.tsx +0 -14
  204. package/templates/template-ts/src/hyperapp.svg +0 -4
  205. package/templates/template-ts/src/main.tsx +0 -43
  206. package/templates/template-ts/src/vite-env.d.ts +0 -2
  207. package/templates/template-ts/vite.config.ts +0 -6
  208. package/templates/template-ts-ssr/_gitignore +0 -24
  209. package/templates/template-ts-ssr/public/vite.svg +0 -1
  210. package/templates/template-ts-ssr/src/counter.tsx +0 -14
  211. package/templates/template-ts-ssr/src/entry-server.ts +0 -5
  212. package/templates/template-ts-ssr/src/main.tsx +0 -43
  213. package/templates/template-ts-ssr/src/style.css +0 -97
  214. package/templates/template-ts-ssr/src/vite-env.d.ts +0 -2
  215. package/templates/template-ts-ssr/vite.config.ts +0 -6
  216. /package/templates/{template-ssr/src/hyperapp.svg → common/public/favicon.svg} +0 -0
  217. /package/templates/{template-ts-ssr/src → common/src/assets}/hyperapp.svg +0 -0
  218. /package/templates/{template-ssr/public → common/src/assets}/vite.svg +0 -0
  219. /package/templates/{template-ssr/src → js-jsx-cli/src/app}/counter.jsx +0 -0
  220. /package/templates/{template/src → js-jsx-ssr/src/app}/counter.jsx +0 -0
  221. /package/templates/{template-ts-ssr/src → ts-html-cli/src/assets}/typescript.svg +0 -0
  222. /package/templates/{template-ts/src → ts-html-ssr/src/assets}/typescript.svg +0 -0
package/README.md CHANGED
@@ -1,34 +1,126 @@
1
1
  # create-vite-hyperapp
2
2
 
3
- Quickly scaffold new web-frontend projects using the [Hyperapp](https://hyperapp.dev) microframework, using the [Vite](https://vitejs.dev) bundler.
3
+ Create a project using [Hyperapp](https://github.com/jorgebucaran/hyperapp) and [Vite](https://vite.dev/). The generated projects use [vite-plugin-hyperapp](https://github.com/zaceno/vite-plugin-hyperapp#readme) for development, builds, and optional server-side rendering; see its README for more about the plugin.
4
4
 
5
5
  ## Usage
6
6
 
7
- With your shell in the folder _under which_ you would like to create your new project folder, run `npm create vite-hyperapp` and answer the questions.
7
+ ```text
8
+ Usage: npm create vite-hyperapp -- [name] [options]
8
9
 
9
- To skip questions and do the entire setup directly in the command line, use the options:
10
+ Create a Vite + Hyperapp project in an empty directory.
11
+ Run without arguments to choose options interactively.
10
12
 
11
- ```
12
- Usage: npm create vite-hyperapp -- [TARGET_DIR] [OPTIONS]
13
+ Arguments:
14
+ name Project name and directory (default: hyperapp-app)
15
+ Use . or ./ for the current empty directory.
13
16
 
14
17
  Options:
15
- -t, --typescript Use typescript
16
- -s, --ssr Use server-side rendering
17
- -n, --no-prompt Just do it, no questions asked
18
- -h, --help Show this message
19
-
20
- If no target dir is specified, the project name will be used.
21
- If --no-prompt option is used, project name will be target dir
22
- Default project-name/target-dir is 'hyperapp-project'
18
+ --types=none|jsdoc|typescript Type checking (default: typescript)
19
+ --views=plain|html|jsx View style (default: jsx)
20
+ --ssr=no|yes Server-side rendering (default: no)
21
+ -h, --help Show this message and exit
22
+
23
+ Examples:
24
+ npm create vite-hyperapp -- my-app --types=jsdoc --views=html --ssr=yes
25
+ npm create vite-hyperapp -- .
26
+ ```
27
+
28
+ ## Writing views
29
+
30
+ Choose how you want to write Hyperapp's view functions. These examples show the same counter component in plain JavaScript; your choice of view style is independent of your choice of typing system.
31
+
32
+ ### Plain Hyperapp (`--views=plain`)
33
+
34
+ Use Hyperapp's built-in `h` and `text` functions:
35
+
36
+ ```js
37
+ import { h, text } from "hyperapp"
38
+
39
+ const Counter = ({ clicks, onclick }) =>
40
+ h("div", { class: "card" }, [
41
+ h("button", { onclick }, text("Clicks: " + clicks)),
42
+ ])
43
+ ```
44
+
45
+ ### HTML functions (`--views=html`)
46
+
47
+ Use tags from `@hyperapp/html` as functions:
48
+
49
+ ```js
50
+ import { div, button, text } from "@hyperapp/html"
51
+
52
+ const Counter = ({ clicks, onclick }) =>
53
+ div({ class: "card" }, button({ onclick }, text("Clicks: " + clicks)))
54
+ ```
55
+
56
+ ### JSX (`--views=jsx`)
57
+
58
+ Write markup-like components in `.jsx` files:
59
+
60
+ ```jsx
61
+ const Counter = ({ clicks, onclick }) => (
62
+ <div class="card">
63
+ <button onclick={onclick}>Clicks: {clicks}</button>
64
+ </div>
65
+ )
23
66
  ```
24
67
 
25
- ## About the scaffolds
68
+ ## Typing
69
+
70
+ Choose separately whether—and how—to type-check your project. The following examples all use the HTML-function view style so you can compare the same component.
71
+
72
+ ### JavaScript without type-checking (`--types=none`)
73
+
74
+ Write JavaScript without type annotations. `npm run build` does not run a type checker:
75
+
76
+ ```js
77
+ import { div, button, text } from "@hyperapp/html"
78
+
79
+ const Counter = ({ clicks, onclick }) =>
80
+ div({ class: "card" }, button({ onclick }, text("Clicks: " + clicks)))
81
+ ```
82
+
83
+ ### JavaScript with JSDoc type-checking (`--types=jsdoc`)
84
+
85
+ Keep `.js`/`.jsx` files and add types in [JSDoc comments](https://www.typescriptlang.org/docs/handbook/jsdoc-supported-types.html). The build runs TypeScript's checker on your JavaScript:
26
86
 
27
- There are four basic templates that can be scaffolded up, depending on wether you want to use Typescript or not, and if you want to scaffold your project for SSR or not.
87
+ ```js
88
+ import { div, button, text } from "@hyperapp/html"
28
89
 
29
- The vite-hyperapp connection is provided by [vite-plugin-hyperapp](https://github.com/zaceno/vite-plugin-hyperapp).
90
+ /**
91
+ * @template S
92
+ * @typedef CounterProps
93
+ * @property {number} clicks
94
+ * @property {Action<S>} onclick
95
+ */
30
96
 
31
- The SSR scaffolds are based directly on Vite's SSR docs for setting up a server with SSR support.
97
+ /**
98
+ * @template S
99
+ * @param {CounterProps<S>} props
100
+ */
101
+ const Counter = ({ clicks, onclick }) =>
102
+ div({ class: "card" }, button({ onclick }, text("Clicks: " + clicks)))
103
+ ```
104
+
105
+ Here `Action<S>` is available from the generated project's `src/types/global.d.ts`.
106
+
107
+ ### TypeScript (`--types=typescript`)
108
+
109
+ Use [TypeScript syntax](https://www.typescriptlang.org/docs/handbook/2/everyday-types.html) in `.ts`/`.tsx` files. The build type-checks the project before bundling:
110
+
111
+ ```ts
112
+ import { div, button, text } from "@hyperapp/html"
113
+ import type { Action } from "hyperapp"
114
+
115
+ type CounterProps<S> = {
116
+ clicks: number
117
+ onclick: Action<S>
118
+ }
119
+
120
+ const Counter = <S>({ clicks, onclick }: CounterProps<S>) =>
121
+ div({ class: "card" }, button({ onclick }, text("Clicks: " + clicks)))
122
+ ```
32
123
 
33
- All templates have their views written in JSX (or TSX, for the TS-templates), but you can just as easily install a view-system of your preference (or use hyperapp's builtin `h` and `text`). Just name your files with `.js` or `.ts` extensions instead of `.jsx`.
124
+ ## Server-side rendering
34
125
 
126
+ With `--ssr=yes`, the server sends the initial view as HTML, so visitors can see it before the client app finishes loading. This can improve perceived loading speed. When the client starts, Hyperapp recycles the server-provided DOM rather than replacing it. The generated project includes separate client and server entries and a server for development and production. See the [vite-plugin-hyperapp SSR documentation](https://github.com/zaceno/vite-plugin-hyperapp#server-side-rendering) for more detail.
@@ -0,0 +1,193 @@
1
+ #!/usr/bin/env node
2
+ import { cpSync, existsSync, lstatSync, mkdirSync, readFileSync, readdirSync, renameSync, writeFileSync } from "node:fs"
3
+ import { basename, dirname, join, resolve } from "node:path"
4
+ import { fileURLToPath } from "node:url"
5
+ import { confirm, input, select } from "@inquirer/prompts"
6
+ import { bold, cyan, isColorSupported, red, white, yellow } from "colorette"
7
+
8
+ const templates = join(dirname(fileURLToPath(import.meta.url)), "templates")
9
+ const usage = `Usage: npm create vite-hyperapp -- [name] [options]
10
+
11
+ Create a Vite + Hyperapp project in an empty directory.
12
+ Run without arguments to choose options interactively.
13
+
14
+ Arguments:
15
+ name Project name and directory (default: hyperapp-app)
16
+ Use . or ./ for the current empty directory.
17
+
18
+ Options:
19
+ --types=none|jsdoc|typescript Type checking (default: typescript)
20
+ --views=plain|html|jsx View style (default: jsx)
21
+ --ssr=no|yes Server-side rendering (default: no)
22
+ -h, --help Show this message and exit
23
+
24
+ Examples:
25
+ npm create vite-hyperapp -- my-app --types=jsdoc --views=html --ssr=yes
26
+ npm create vite-hyperapp -- .
27
+ `
28
+
29
+ function parseOptions(args) {
30
+ const options = { name: "hyperapp-app", types: "typescript", views: "jsx", ssr: "no" }
31
+ const choices = {
32
+ types: ["none", "jsdoc", "typescript"],
33
+ views: ["plain", "html", "jsx"],
34
+ ssr: ["no", "yes"],
35
+ }
36
+ const seen = new Set()
37
+ let hasName = false
38
+
39
+ for (let i = 0; i < args.length; i++) {
40
+ const arg = args[i]
41
+ if (!arg.startsWith("-")) {
42
+ if (hasName) throw new Error("Provide only one project name. See --help.")
43
+ options.name = arg
44
+ hasName = true
45
+ continue
46
+ }
47
+ if (!arg.startsWith("--")) throw new Error(`Unknown argument: ${arg}. See --help.`)
48
+ const equal = arg.indexOf("=")
49
+ const key = arg.slice(2, equal === -1 ? undefined : equal)
50
+ if (!(key in choices)) throw new Error(`Unknown option: --${key}. See --help.`)
51
+ if (seen.has(key)) throw new Error(`--${key} was provided more than once.`)
52
+ seen.add(key)
53
+
54
+ const value = equal === -1 ? args[++i] : arg.slice(equal + 1)
55
+ if (!value || value.startsWith("--")) throw new Error(`--${key} requires a value. See --help.`)
56
+ if (!choices[key].includes(value)) {
57
+ throw new Error(`Invalid --${key} value: ${value}. Choose ${choices[key].join(", ")}.`)
58
+ }
59
+ options[key] = value
60
+ }
61
+ return options
62
+ }
63
+
64
+ function getTarget(options) {
65
+ const here = options.name === "." || options.name === "./"
66
+ const name = here ? basename(process.cwd()) : options.name
67
+ if (!/^[a-z0-9][a-z0-9._-]*$/.test(name)) {
68
+ throw new Error(`Invalid project name: ${name}. Use lowercase letters, digits, dots, hyphens, or underscores (starting with a letter or digit).`)
69
+ }
70
+
71
+ const destination = here ? process.cwd() : resolve(process.cwd(), name)
72
+ if (existsSync(destination)) {
73
+ if (!lstatSync(destination).isDirectory()) throw new Error(`Target is not a directory: ${destination}`)
74
+ if (readdirSync(destination).length) throw new Error(`Target directory is not empty: ${destination}`)
75
+ }
76
+ return { here, name, destination }
77
+ }
78
+
79
+ function createProject(options) {
80
+ const { here, name, destination } = getTarget(options)
81
+ const variant = `${{ none: "js", jsdoc: "tj", typescript: "ts" }[options.types]}-${options.views}-${options.ssr === "yes" ? "ssr" : "cli"}`
82
+
83
+ if (!existsSync(destination)) {
84
+ try {
85
+ mkdirSync(destination)
86
+ } catch (error) {
87
+ throw new Error(`Could not create target directory ${destination}: ${error.message}`)
88
+ }
89
+ }
90
+
91
+ cpSync(join(templates, "common"), destination, { recursive: true, force: false, errorOnExist: true })
92
+ cpSync(join(templates, variant), destination, { recursive: true, force: false, errorOnExist: true })
93
+ renameSync(join(destination, "_gitignore"), join(destination, ".gitignore"))
94
+
95
+ for (const filename of ["package.json", "index.html"]) {
96
+ const path = join(destination, filename)
97
+ writeFileSync(path, readFileSync(path, "utf8").replaceAll("__PROJECTNAME__", name))
98
+ }
99
+
100
+ console.log(`Created ${name} in ${destination}.\n\nNext steps:`)
101
+ if (!here) console.log(` cd ${name}`)
102
+ console.log(" npm install\n npm run dev")
103
+ }
104
+
105
+ async function wizard() {
106
+ if (!process.stdin.isTTY || !process.stdout.isTTY) {
107
+ throw new Error("The wizard needs an interactive terminal. Pass a name or options to run without it.")
108
+ }
109
+
110
+ // Colorette handles bold and white; truecolor supplies the requested RGB accent.
111
+ const logo = isColorSupported
112
+ ? `\x1b[48;2;107;154;238m ${bold(white("+\\"))} \x1b[49m`
113
+ : "+\\"
114
+ const heading = isColorSupported
115
+ ? `\x1b[38;2;107;154;238m${bold("Create a project with Vite & Hyperapp")}\x1b[39m`
116
+ : "Create a project with Vite & Hyperapp"
117
+ console.log(`\n${heading} ${logo}\n`)
118
+
119
+ try {
120
+ const name = (await input({
121
+ message: "Project name (. or ./ for current directory)",
122
+ default: "hyperapp-app",
123
+ validate(value) {
124
+ try {
125
+ getTarget({ name: value.trim() || "hyperapp-app" })
126
+ return true
127
+ } catch (error) {
128
+ return error.message
129
+ }
130
+ },
131
+ })).trim() || "hyperapp-app"
132
+
133
+ const views = await select({
134
+ message: "Views",
135
+ choices: [
136
+ { name: "Plain Hyperapp (h/text)", value: "plain" },
137
+ { name: "@hyperapp/html", value: "html" },
138
+ { name: "JSX", value: "jsx" },
139
+ ],
140
+ default: "jsx",
141
+ })
142
+ const types = await select({
143
+ message: "Types",
144
+ choices: [
145
+ { name: "JavaScript (no type-checking)", value: "none" },
146
+ { name: "JavaScript with JSDoc type-checking", value: "jsdoc" },
147
+ { name: "TypeScript", value: "typescript" },
148
+ ],
149
+ default: "typescript",
150
+ })
151
+ const ssr = await select({
152
+ message: "Server-side rendering",
153
+ choices: [
154
+ { name: "No (client-only)", value: "no" },
155
+ { name: "Yes (SSR)", value: "yes" },
156
+ ],
157
+ default: "no",
158
+ })
159
+
160
+ console.log(`\n${bold("Your project")}`)
161
+ console.log(` Name: ${cyan(name)}`)
162
+ console.log(` Views: ${cyan(views)}`)
163
+ console.log(` Types: ${cyan(types)}`)
164
+ console.log(` SSR: ${cyan(ssr)}`)
165
+
166
+ if (!await confirm({ message: "Create this project?", default: false })) {
167
+ console.log(yellow("Cancelled. No project was created."))
168
+ return
169
+ }
170
+ createProject({ name, views, types, ssr })
171
+ } catch (error) {
172
+ if (error.name !== "ExitPromptError") throw error
173
+ console.log(yellow("\nCancelled. No project was created."))
174
+ }
175
+ }
176
+
177
+ async function main() {
178
+ const args = process.argv.slice(2)
179
+ if (args.some(arg => arg === "--help" || arg === "-h")) {
180
+ console.log(usage)
181
+ } else if (args.length === 0) {
182
+ await wizard()
183
+ } else {
184
+ createProject(parseOptions(args))
185
+ }
186
+ }
187
+
188
+ try {
189
+ await main()
190
+ } catch (error) {
191
+ console.error(red(`Error: ${error.message}`))
192
+ process.exitCode = 1
193
+ }
package/package.json CHANGED
@@ -1,19 +1,26 @@
1
1
  {
2
2
  "name": "create-vite-hyperapp",
3
- "type": "module",
4
- "version": "1.3.0",
5
- "description": "\"Scaffold a project quickly with Hyperapp and Vite\"",
6
- "main": "index.js",
7
- "scripts": {
8
- "test": "echo \"Error: no test specified\" && exit 1"
3
+ "version": "2.0.0",
4
+ "description": "Create a Vite + Hyperapp project",
5
+ "repository": {
6
+ "type": "git",
7
+ "url": "git+https://github.com/zaceno/create-vite-hyperapp.git"
9
8
  },
10
9
  "author": "Zacharias Enochsson",
11
10
  "license": "MIT",
12
- "dependencies": {
13
- "minimist": "^1.2.8",
14
- "prompts": "^2.4.2"
15
- },
11
+ "type": "module",
16
12
  "bin": {
17
- "create-vite-hyperapp": "index.js"
13
+ "create-vite-hyperapp": "./create-vite-hyperapp.mjs"
14
+ },
15
+ "files": [
16
+ "create-vite-hyperapp.mjs",
17
+ "templates"
18
+ ],
19
+ "engines": {
20
+ "node": ">=22.18.0"
21
+ },
22
+ "dependencies": {
23
+ "@inquirer/prompts": "^8.7.3",
24
+ "colorette": "^2.0.20"
18
25
  }
19
26
  }
@@ -21,4 +21,4 @@ dist-ssr
21
21
  *.ntvs*
22
22
  *.njsproj
23
23
  *.sln
24
- *.sw?
24
+ *.sw?
@@ -52,10 +52,6 @@ h1 {
52
52
  .logo:hover {
53
53
  filter: drop-shadow(0 0 2em #646cffaa);
54
54
  }
55
- .logo.vanilla:hover {
56
- filter: drop-shadow(0 0 2em #3178c6aa);
57
- }
58
-
59
55
  .card {
60
56
  padding: 2em;
61
57
  }
@@ -2,12 +2,12 @@
2
2
  <html lang="en">
3
3
  <head>
4
4
  <meta charset="UTF-8" />
5
- <link rel="icon" type="image/svg+xml" href="/vite.svg" />
5
+ <link rel="icon" type="image/svg+xml" href="/favicon.svg" />
6
6
  <meta name="viewport" content="width=device-width, initial-scale=1.0" />
7
- <title>Vite + Hyperapp</title>
7
+ <title>__PROJECTNAME__</title>
8
8
  </head>
9
9
  <body>
10
- <div id="app"></div>
10
+ <main id="app"></main>
11
11
  <script type="module" src="/src/run.js"></script>
12
12
  </body>
13
13
  </html>
@@ -0,0 +1,13 @@
1
+ {
2
+ "compilerOptions": {
3
+ "baseUrl": ".",
4
+ "paths": {
5
+ "@/*": [
6
+ "src/*"
7
+ ]
8
+ }
9
+ },
10
+ "include": [
11
+ "src"
12
+ ]
13
+ }
@@ -0,0 +1,20 @@
1
+ {
2
+ "name": "__PROJECTNAME__",
3
+ "private": true,
4
+ "version": "0.0.0",
5
+ "type": "module",
6
+ "scripts": {
7
+ "dev": "vite",
8
+ "build": "vite build",
9
+ "preview": "vite preview"
10
+ },
11
+ "dependencies": {
12
+ "@hyperapp/html": "^2.0.0",
13
+ "@hyperapp/svg": "^2.0.0",
14
+ "hyperapp": "^2.0.22"
15
+ },
16
+ "devDependencies": {
17
+ "vite": "^8.3.4",
18
+ "vite-plugin-hyperapp": "^2.0.1"
19
+ }
20
+ }
@@ -0,0 +1,5 @@
1
+ import { div, button, text } from "@hyperapp/html"
2
+ const Counter = ({ clicks, onclick }) =>
3
+ div({ class: "card" }, button({ onclick }, text("Clicks: " + clicks)))
4
+
5
+ export default Counter
@@ -0,0 +1,24 @@
1
+ import { a, img } from "@hyperapp/html"
2
+ import viteLogo from "@/assets/vite.svg"
3
+ import hyperappLogo from "@/assets/hyperapp.svg"
4
+
5
+ const Logos = () => [
6
+ a(
7
+ { href: "https://vitejs.dev" },
8
+ img({
9
+ src: viteLogo,
10
+ class: "logo",
11
+ alt: "Vite logo",
12
+ }),
13
+ ),
14
+ a(
15
+ { href: "https://github.com/jorgebucaran/hyperapp" },
16
+ img({
17
+ src: hyperappLogo,
18
+ class: "logo",
19
+ alt: "Hyperapp logo",
20
+ }),
21
+ ),
22
+ ]
23
+
24
+ export default Logos
@@ -0,0 +1,24 @@
1
+ import "./style.css"
2
+ import { main, h1, p, text } from "@hyperapp/html"
3
+ import Counter from "./counter"
4
+ import Logos from "./logos"
5
+
6
+ export const init = () => ({
7
+ counter: 0,
8
+ })
9
+
10
+ const HandleClick = state => ({
11
+ ...state,
12
+ counter: state.counter + 1,
13
+ })
14
+
15
+ export const view = state =>
16
+ main({ id: "app" }, [
17
+ h1(text("Vite + Hyperapp")),
18
+ ...Logos(),
19
+ Counter({ clicks: state.counter, onclick: HandleClick }),
20
+ p(
21
+ { class: "read-the-docs" },
22
+ text("Click on the Vite & Hyperapp logos to learn more"),
23
+ ),
24
+ ])
@@ -1,5 +1,5 @@
1
1
  import { app } from "hyperapp"
2
- import { init, view } from "./main"
2
+ import { init, view } from "./app/main"
3
3
  const node = document.querySelector("#app")
4
4
  if (!node) throw new Error("mountpoint not found")
5
- export const dispatch = app({ init, view, node })
5
+ app({ init, view, node })
@@ -0,0 +1,12 @@
1
+ import { defineConfig } from "vite"
2
+ import hyperapp from "vite-plugin-hyperapp"
3
+ import { fileURLToPath } from "node:url"
4
+
5
+ export default defineConfig({
6
+ plugins: [hyperapp()],
7
+ resolve: {
8
+ alias: {
9
+ "@": fileURLToPath(new URL("./src", import.meta.url)),
10
+ },
11
+ },
12
+ })
@@ -2,9 +2,9 @@
2
2
  <html lang="en">
3
3
  <head>
4
4
  <meta charset="UTF-8" />
5
- <link rel="icon" type="image/svg+xml" href="/vite.svg" />
5
+ <link rel="icon" type="image/svg+xml" href="/favicon.svg" />
6
6
  <meta name="viewport" content="width=device-width, initial-scale=1.0" />
7
- <title>Vite (SSR) + Hyperapp</title>
7
+ <title>__PROJECTNAME__</title>
8
8
  </head>
9
9
  <body>
10
10
  <!--ssr-outlet-->
@@ -0,0 +1,13 @@
1
+ {
2
+ "compilerOptions": {
3
+ "baseUrl": ".",
4
+ "paths": {
5
+ "@/*": [
6
+ "src/*"
7
+ ]
8
+ }
9
+ },
10
+ "include": [
11
+ "src"
12
+ ]
13
+ }
@@ -0,0 +1,25 @@
1
+ {
2
+ "name": "__PROJECTNAME__",
3
+ "private": true,
4
+ "version": "0.0.0",
5
+ "type": "module",
6
+ "scripts": {
7
+ "dev": "node server.js",
8
+ "build:client": "vite build --outDir dist/client",
9
+ "build:server": "vite build --outDir dist/server --ssr src/entry-server.js",
10
+ "build": "npm run build:client && npm run build:server",
11
+ "preview": "NODE_ENV=production node server.js"
12
+ },
13
+ "devDependencies": {
14
+ "vite": "^8.3.4"
15
+ },
16
+ "dependencies": {
17
+ "@hyperapp/html": "^2.0.0",
18
+ "@hyperapp/svg": "^2.0.0",
19
+ "compression": "^1.7.4",
20
+ "express": "^4.19.2",
21
+ "hyperapp": "^2.0.22",
22
+ "sirv": "^2.0.4",
23
+ "vite-plugin-hyperapp": "^2.0.1"
24
+ }
25
+ }
@@ -19,7 +19,6 @@ async function createServer() {
19
19
  vite = await createServer({
20
20
  server: { middlewareMode: true },
21
21
  appType: "custom",
22
- base,
23
22
  })
24
23
  app.use(vite.middlewares)
25
24
  } else {
@@ -29,26 +28,29 @@ async function createServer() {
29
28
  app.use(base, sirv("./dist/client", { extensions: [] }))
30
29
  }
31
30
 
32
- app.use("*", async (req, res) => {
33
- const url = req.originalUrl.replace(base, "")
31
+ app.use("*", async (req, res, next) => {
32
+ const url = req.originalUrl
33
+ if (!url.startsWith(base)) return next()
34
34
  try {
35
35
  let template
36
36
  let render
37
37
  if (!isProduction) {
38
+ if (!vite) throw new Error("Vite dev server is not initialized")
38
39
  // Always read fresh template in development
39
40
  template = await fs.readFile("./index.html", "utf-8")
40
41
  template = await vite.transformIndexHtml(url, template)
41
42
  render = (await vite.ssrLoadModule("/src/entry-server.js")).render
42
43
  } else {
43
44
  template = await fs.readFile("./dist/client/index.html", "utf-8")
44
- render = (await import("./dist/server/entry-server.js")).render
45
+ const serverEntry = "./dist/server/entry-server.js"
46
+ render = (await import(serverEntry)).render
45
47
  }
46
- const html = template.replace(`<!--ssr-outlet-->`, await render())
48
+ const html = template.replace(`<!--ssr-outlet-->`, () => render())
47
49
  res.status(200).set({ "Content-Type": "text/html" }).end(html)
48
50
  } catch (e) {
49
- vite?.ssrFixStacktrace(e)
50
- console.log(e.stack)
51
- res.status(500).end(e.stack)
51
+ if (e instanceof Error) vite?.ssrFixStacktrace(e)
52
+ console.error(e)
53
+ res.status(500).end()
52
54
  }
53
55
  })
54
56
 
@@ -0,0 +1,5 @@
1
+ import { div, button, text } from "@hyperapp/html"
2
+ const Counter = ({ clicks, onclick }) =>
3
+ div({ class: "card" }, button({ onclick }, text("Clicks: " + clicks)))
4
+
5
+ export default Counter
@@ -0,0 +1,24 @@
1
+ import { a, img } from "@hyperapp/html"
2
+ import viteLogo from "@/assets/vite.svg"
3
+ import hyperappLogo from "@/assets/hyperapp.svg"
4
+
5
+ const Logos = () => [
6
+ a(
7
+ { href: "https://vitejs.dev" },
8
+ img({
9
+ src: viteLogo,
10
+ class: "logo",
11
+ alt: "Vite logo",
12
+ }),
13
+ ),
14
+ a(
15
+ { href: "https://github.com/jorgebucaran/hyperapp" },
16
+ img({
17
+ src: hyperappLogo,
18
+ class: "logo",
19
+ alt: "Hyperapp logo",
20
+ }),
21
+ ),
22
+ ]
23
+
24
+ export default Logos