@inglorious/create-app 1.3.0 → 1.4.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.
- package/bin/lib/app-jsx.js +99 -101
- package/bin/lib/app-lit.js +106 -110
- package/bin/lib/app-vue.js +71 -71
- package/bin/lib/app.js +22 -12
- package/bin/lib/mobile.js +15 -15
- package/bin/lib/pwa.js +3 -1
- package/package.json +2 -2
- package/templates/ssx-ts/src/pages/about.ts +5 -5
- package/templates/ssx-ts/types/index.d.ts +13 -7
- package/templates/ts/src/store/index.ts +2 -2
- package/templates/ts/types/index.d.ts +10 -6
- package/templates/ts-jsx/src/store/index.ts +2 -2
- package/templates/ts-jsx/types/index.d.ts +10 -6
- package/templates/ts-vue/src/store/index.ts +2 -2
- package/templates/ts-vue/types/index.d.ts +6 -8
package/bin/lib/app-jsx.js
CHANGED
|
@@ -12,14 +12,6 @@ export const extension = (app) => (app.isTypeScript ? "tsx" : "jsx")
|
|
|
12
12
|
const EXPECT_ERROR =
|
|
13
13
|
"@ts-expect-error - Inglorious components are objects, not functions"
|
|
14
14
|
|
|
15
|
-
// The comment line before a capitalized tag in .tsx: `{/* */}` among JSX
|
|
16
|
-
// children, `//` inside code.
|
|
17
|
-
function expectError(app, indent, isChild = true) {
|
|
18
|
-
if (!app.isTypeScript) return ""
|
|
19
|
-
const comment = isChild ? `{/* ${EXPECT_ERROR} */}` : `// ${EXPECT_ERROR}`
|
|
20
|
-
return `${indent}${comment}\n`
|
|
21
|
-
}
|
|
22
|
-
|
|
23
15
|
export function appSource(app, { pages, navIcons }) {
|
|
24
16
|
const imports = []
|
|
25
17
|
if (navIcons) {
|
|
@@ -69,6 +61,105 @@ ${shell}
|
|
|
69
61
|
`
|
|
70
62
|
}
|
|
71
63
|
|
|
64
|
+
export function homeSource(app, { showcase }) {
|
|
65
|
+
const imports = ['import { Message } from "../types/message"']
|
|
66
|
+
for (const icon of showcase) {
|
|
67
|
+
imports.push(`import ${icon.name} from "${icon.from}"`)
|
|
68
|
+
}
|
|
69
|
+
if (showcase.length) {
|
|
70
|
+
imports.push('import { Icon } from "@inglorious/ui/icon"')
|
|
71
|
+
}
|
|
72
|
+
if (app.i18n) imports.push('import { t } from "../i18n"')
|
|
73
|
+
|
|
74
|
+
const text = (key, fallback) => (app.i18n ? `{t("${key}")}` : fallback)
|
|
75
|
+
const icons = showcase.length
|
|
76
|
+
? `
|
|
77
|
+
<p className="app-icons">
|
|
78
|
+
${showcase
|
|
79
|
+
.map(
|
|
80
|
+
(icon) =>
|
|
81
|
+
`${expectError(app, " ")} <Icon icon={${icon.name}} size="lg" label="${icon.label}" />`,
|
|
82
|
+
)
|
|
83
|
+
.join("\n")}
|
|
84
|
+
</p>`
|
|
85
|
+
: ""
|
|
86
|
+
|
|
87
|
+
return `${importBlock(imports)}
|
|
88
|
+
|
|
89
|
+
export const Home = {
|
|
90
|
+
render() {
|
|
91
|
+
return (
|
|
92
|
+
<>
|
|
93
|
+
<h1>${text("home.title", "Welcome")}</h1>
|
|
94
|
+
<p>
|
|
95
|
+
${expectError(app, " ")} <Message entityId="message1" />, <Message entityId="message2" />,{" "}
|
|
96
|
+
${expectError(app, " ")} <Message entityId="message3" />!
|
|
97
|
+
</p>
|
|
98
|
+
<p>${text("home.hint", "Click a greeting to shout it.")}</p>${icons}
|
|
99
|
+
</>
|
|
100
|
+
)
|
|
101
|
+
},
|
|
102
|
+
}
|
|
103
|
+
`
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
export function aboutSource(app) {
|
|
107
|
+
const head = app.i18n ? 'import { t } from "../i18n"\n\n' : ""
|
|
108
|
+
const text = (key, fallback) => (app.i18n ? `{t("${key}")}` : fallback)
|
|
109
|
+
|
|
110
|
+
return `${head}export const About = {
|
|
111
|
+
render() {
|
|
112
|
+
return (
|
|
113
|
+
<>
|
|
114
|
+
<h1>${text("about.title", "About")}</h1>
|
|
115
|
+
<p>${text("about.body", "A page of its own, at its own address.")}</p>
|
|
116
|
+
</>
|
|
117
|
+
)
|
|
118
|
+
},
|
|
119
|
+
}
|
|
120
|
+
`
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
export function messageSource(app) {
|
|
124
|
+
const imports = []
|
|
125
|
+
if (app.isTypeScript) {
|
|
126
|
+
imports.push('import type { MessageType } from "../../types"')
|
|
127
|
+
}
|
|
128
|
+
if (app.i18n) imports.push('import { t } from "../i18n"')
|
|
129
|
+
const greeting = app.i18n ? '{t("message.hello", { who })}' : "Hello {who}"
|
|
130
|
+
const annotation = app.isTypeScript ? ": MessageType" : ""
|
|
131
|
+
|
|
132
|
+
const span = app.i18n
|
|
133
|
+
? ` <span onClick={() => api.notify(\`#\${entity.id}:click\`)}>
|
|
134
|
+
${greeting}
|
|
135
|
+
</span>`
|
|
136
|
+
: ` <span onClick={() => api.notify(\`#\${entity.id}:click\`)}>${greeting}</span>`
|
|
137
|
+
|
|
138
|
+
const head = imports.length ? `${importBlock(imports)}\n\n` : ""
|
|
139
|
+
|
|
140
|
+
return `${head}export const Message${annotation} = {
|
|
141
|
+
click(entity) {
|
|
142
|
+
entity.isUpperCase = !entity.isUpperCase
|
|
143
|
+
},
|
|
144
|
+
|
|
145
|
+
render(entity, api) {
|
|
146
|
+
const who = entity.isUpperCase ? entity.who.toUpperCase() : entity.who
|
|
147
|
+
return (
|
|
148
|
+
${span}
|
|
149
|
+
)
|
|
150
|
+
},
|
|
151
|
+
}
|
|
152
|
+
`
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
// The comment line before a capitalized tag in .tsx: `{/* */}` among JSX
|
|
156
|
+
// children, `//` inside code.
|
|
157
|
+
function expectError(app, indent, isChild = true) {
|
|
158
|
+
if (!app.isTypeScript) return ""
|
|
159
|
+
const comment = isChild ? `{/* ${EXPECT_ERROR} */}` : `// ${EXPECT_ERROR}`
|
|
160
|
+
return `${indent}${comment}\n`
|
|
161
|
+
}
|
|
162
|
+
|
|
72
163
|
// A callback's parameter. The components' props are not typed in .tsx, so
|
|
73
164
|
// TypeScript cannot infer it.
|
|
74
165
|
function typed(app, name, type) {
|
|
@@ -185,96 +276,3 @@ ${nav.join("\n")}
|
|
|
185
276
|
</header>
|
|
186
277
|
<main>{${content}}</main>`
|
|
187
278
|
}
|
|
188
|
-
|
|
189
|
-
export function homeSource(app, { showcase }) {
|
|
190
|
-
const imports = ['import { Message } from "../types/message"']
|
|
191
|
-
for (const icon of showcase) {
|
|
192
|
-
imports.push(`import ${icon.name} from "${icon.from}"`)
|
|
193
|
-
}
|
|
194
|
-
if (showcase.length) {
|
|
195
|
-
imports.push('import { Icon } from "@inglorious/ui/icon"')
|
|
196
|
-
}
|
|
197
|
-
if (app.i18n) imports.push('import { t } from "../i18n"')
|
|
198
|
-
|
|
199
|
-
const text = (key, fallback) => (app.i18n ? `{t("${key}")}` : fallback)
|
|
200
|
-
const icons = showcase.length
|
|
201
|
-
? `
|
|
202
|
-
<p className="app-icons">
|
|
203
|
-
${showcase
|
|
204
|
-
.map(
|
|
205
|
-
(icon) =>
|
|
206
|
-
`${expectError(app, " ")} <Icon icon={${icon.name}} size="lg" label="${icon.label}" />`,
|
|
207
|
-
)
|
|
208
|
-
.join("\n")}
|
|
209
|
-
</p>`
|
|
210
|
-
: ""
|
|
211
|
-
|
|
212
|
-
return `${importBlock(imports)}
|
|
213
|
-
|
|
214
|
-
export const Home = {
|
|
215
|
-
render() {
|
|
216
|
-
return (
|
|
217
|
-
<>
|
|
218
|
-
<h1>${text("home.title", "Welcome")}</h1>
|
|
219
|
-
<p>
|
|
220
|
-
${expectError(app, " ")} <Message entityId="message1" />, <Message entityId="message2" />,{" "}
|
|
221
|
-
${expectError(app, " ")} <Message entityId="message3" />!
|
|
222
|
-
</p>
|
|
223
|
-
<p>${text("home.hint", "Click a greeting to shout it.")}</p>${icons}
|
|
224
|
-
</>
|
|
225
|
-
)
|
|
226
|
-
},
|
|
227
|
-
}
|
|
228
|
-
`
|
|
229
|
-
}
|
|
230
|
-
|
|
231
|
-
export function aboutSource(app) {
|
|
232
|
-
const head = app.i18n ? 'import { t } from "../i18n"\n\n' : ""
|
|
233
|
-
const text = (key, fallback) => (app.i18n ? `{t("${key}")}` : fallback)
|
|
234
|
-
|
|
235
|
-
return `${head}export const About = {
|
|
236
|
-
render() {
|
|
237
|
-
return (
|
|
238
|
-
<>
|
|
239
|
-
<h1>${text("about.title", "About")}</h1>
|
|
240
|
-
<p>${text("about.body", "A page of its own, at its own address.")}</p>
|
|
241
|
-
</>
|
|
242
|
-
)
|
|
243
|
-
},
|
|
244
|
-
}
|
|
245
|
-
`
|
|
246
|
-
}
|
|
247
|
-
|
|
248
|
-
export function messageSource(app) {
|
|
249
|
-
const imports = []
|
|
250
|
-
if (app.isTypeScript) {
|
|
251
|
-
imports.push('import type { MessageType } from "../../types"')
|
|
252
|
-
}
|
|
253
|
-
if (app.i18n) imports.push('import { t } from "../i18n"')
|
|
254
|
-
const greeting = app.i18n ? '{t("message.hello", { who })}' : "Hello {who}"
|
|
255
|
-
const declaration = app.isTypeScript
|
|
256
|
-
? "export const Message: MessageType = {"
|
|
257
|
-
: "export const Message = {"
|
|
258
|
-
|
|
259
|
-
const span = app.i18n
|
|
260
|
-
? ` <span onClick={() => api.notify(\`#\${entity.id}:click\`)}>
|
|
261
|
-
${greeting}
|
|
262
|
-
</span>`
|
|
263
|
-
: ` <span onClick={() => api.notify(\`#\${entity.id}:click\`)}>${greeting}</span>`
|
|
264
|
-
|
|
265
|
-
const head = imports.length ? `${importBlock(imports)}\n\n` : ""
|
|
266
|
-
|
|
267
|
-
return `${head}${declaration}
|
|
268
|
-
click(entity) {
|
|
269
|
-
entity.isUpperCase = !entity.isUpperCase
|
|
270
|
-
},
|
|
271
|
-
|
|
272
|
-
render(entity, api) {
|
|
273
|
-
const who = entity.isUpperCase ? entity.who.toUpperCase() : entity.who
|
|
274
|
-
return (
|
|
275
|
-
${span}
|
|
276
|
-
)
|
|
277
|
-
},
|
|
278
|
-
}
|
|
279
|
-
`
|
|
280
|
-
}
|
package/bin/lib/app-lit.js
CHANGED
|
@@ -64,70 +64,6 @@ export function renderLocals(app) {
|
|
|
64
64
|
return locals.length ? `${locals.join("\n")}\n\n` : ""
|
|
65
65
|
}
|
|
66
66
|
|
|
67
|
-
function uiShell(app, { label, pages, content }) {
|
|
68
|
-
const nav = []
|
|
69
|
-
if (app.links) {
|
|
70
|
-
nav.push(
|
|
71
|
-
...pages.map(
|
|
72
|
-
(page) =>
|
|
73
|
-
`Link.render({ href: "${page.path}", children: ${label(page.key, page.text)} })`,
|
|
74
|
-
),
|
|
75
|
-
)
|
|
76
|
-
}
|
|
77
|
-
if (app.i18n) {
|
|
78
|
-
nav.push(`Select.render({
|
|
79
|
-
value: i18n.locale,
|
|
80
|
-
options: i18n.languages,
|
|
81
|
-
size: "sm",
|
|
82
|
-
ariaLabel: t("language"),
|
|
83
|
-
onChange: (locale) => api.notify("#i18n:localeChange", locale),
|
|
84
|
-
})`)
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
const trailing = nav.length
|
|
88
|
-
? `
|
|
89
|
-
trailing: html\`<nav class="app-nav">
|
|
90
|
-
\${[
|
|
91
|
-
${nav.join(",\n ")},
|
|
92
|
-
]}
|
|
93
|
-
</nav>\`,`
|
|
94
|
-
: ""
|
|
95
|
-
|
|
96
|
-
const header = ` <header${app.mobile ? ' class="pt-safe"' : ""}>
|
|
97
|
-
\${AppBar.render({
|
|
98
|
-
title: "${app.projectName}",${trailing}
|
|
99
|
-
})}
|
|
100
|
-
</header>
|
|
101
|
-
<main>
|
|
102
|
-
\${Container.render({ maxWidth: "md", children: ${content} })}
|
|
103
|
-
</main>`
|
|
104
|
-
|
|
105
|
-
if (!app.tabs) return header
|
|
106
|
-
|
|
107
|
-
const actions = pages.map(
|
|
108
|
-
(page) => ` {
|
|
109
|
-
value: "${page.path}",
|
|
110
|
-
label: ${label(page.key, page.text)},${
|
|
111
|
-
page.icon
|
|
112
|
-
? `
|
|
113
|
-
icon: Icon.render({ icon: ${page.icon.name}, size: "lg" }),`
|
|
114
|
-
: ""
|
|
115
|
-
}
|
|
116
|
-
},`,
|
|
117
|
-
)
|
|
118
|
-
|
|
119
|
-
return `${header}
|
|
120
|
-
<footer class="pb-safe">
|
|
121
|
-
\${BottomNavigation.render({
|
|
122
|
-
value: router.path,
|
|
123
|
-
actions: [
|
|
124
|
-
${actions.join("\n")}
|
|
125
|
-
],
|
|
126
|
-
onChange: (path) => api.notify("#router:navigate", path),
|
|
127
|
-
})}
|
|
128
|
-
</footer>`
|
|
129
|
-
}
|
|
130
|
-
|
|
131
67
|
// The native language picker's change handler, laid out as Prettier would at
|
|
132
68
|
// `indent`. TypeScript has to be told what the event's target is.
|
|
133
69
|
export function selectChange(app, indent) {
|
|
@@ -142,42 +78,6 @@ ${indent} (event.target as HTMLSelectElement).value,
|
|
|
142
78
|
${indent} )`
|
|
143
79
|
}
|
|
144
80
|
|
|
145
|
-
function plainShell(app, { label, pages, content }) {
|
|
146
|
-
const nav = app.routing
|
|
147
|
-
? pages.map(
|
|
148
|
-
(page) =>
|
|
149
|
-
` <a href="${page.path}">\${${label(page.key, page.text)}}</a>`,
|
|
150
|
-
)
|
|
151
|
-
: []
|
|
152
|
-
if (app.i18n) {
|
|
153
|
-
nav.push(` <select
|
|
154
|
-
aria-label=\${t("language")}
|
|
155
|
-
@change=\${${selectChange(app, " ")}}
|
|
156
|
-
>
|
|
157
|
-
\${i18n.languages${app.isTypeScript ? "?." : "."}map(
|
|
158
|
-
(language) =>
|
|
159
|
-
html\`<option
|
|
160
|
-
value=\${language.value}
|
|
161
|
-
?selected=\${language.value === i18n.locale}
|
|
162
|
-
>
|
|
163
|
-
\${language.label}
|
|
164
|
-
</option>\`,
|
|
165
|
-
)}
|
|
166
|
-
</select>`)
|
|
167
|
-
}
|
|
168
|
-
const navBlock = nav.length
|
|
169
|
-
? `
|
|
170
|
-
<nav class="app-nav">
|
|
171
|
-
${nav.join("\n")}
|
|
172
|
-
</nav>`
|
|
173
|
-
: ""
|
|
174
|
-
|
|
175
|
-
return ` <header>
|
|
176
|
-
<strong>${app.projectName}</strong>${navBlock}
|
|
177
|
-
</header>
|
|
178
|
-
<main>\${${content}}</main>`
|
|
179
|
-
}
|
|
180
|
-
|
|
181
81
|
export function homeSource(app, { showcase }) {
|
|
182
82
|
const imports = ['import { html } from "@inglorious/web"']
|
|
183
83
|
for (const icon of showcase) {
|
|
@@ -187,7 +87,7 @@ export function homeSource(app, { showcase }) {
|
|
|
187
87
|
imports.push('import { Icon } from "@inglorious/ui/icon"')
|
|
188
88
|
}
|
|
189
89
|
if (app.isTypeScript) {
|
|
190
|
-
imports.push('import type {
|
|
90
|
+
imports.push('import type { HomeType } from "../../types"')
|
|
191
91
|
}
|
|
192
92
|
if (app.i18n) imports.push('import { t } from "../i18n"')
|
|
193
93
|
|
|
@@ -207,14 +107,12 @@ ${showcase
|
|
|
207
107
|
</p>`
|
|
208
108
|
: ""
|
|
209
109
|
|
|
210
|
-
const
|
|
211
|
-
? "render(entity: HomeEntity, api: AppApi)"
|
|
212
|
-
: "render(entity, api)"
|
|
110
|
+
const annotation = app.isTypeScript ? ": HomeType" : ""
|
|
213
111
|
|
|
214
112
|
return `${importBlock(imports)}
|
|
215
113
|
|
|
216
|
-
export const Home = {
|
|
217
|
-
|
|
114
|
+
export const Home${annotation} = {
|
|
115
|
+
render(entity, api) {
|
|
218
116
|
return html\`
|
|
219
117
|
<h1>${text("home.title", "Welcome")}</h1>
|
|
220
118
|
<p>
|
|
@@ -253,13 +151,11 @@ export function messageSource(app) {
|
|
|
253
151
|
}
|
|
254
152
|
if (app.i18n) imports.push('import { t } from "../i18n"')
|
|
255
153
|
const greeting = app.i18n ? '${t("message.hello", { who })}' : "Hello ${who}"
|
|
256
|
-
const
|
|
257
|
-
? "export const Message: MessageType = {"
|
|
258
|
-
: "export const Message = {"
|
|
154
|
+
const annotation = app.isTypeScript ? ": MessageType" : ""
|
|
259
155
|
|
|
260
156
|
return `${importBlock(imports)}
|
|
261
157
|
|
|
262
|
-
${
|
|
158
|
+
export const Message${annotation} = {
|
|
263
159
|
click(entity) {
|
|
264
160
|
entity.isUpperCase = !entity.isUpperCase
|
|
265
161
|
},
|
|
@@ -273,3 +169,103 @@ ${declaration}
|
|
|
273
169
|
}
|
|
274
170
|
`
|
|
275
171
|
}
|
|
172
|
+
|
|
173
|
+
function uiShell(app, { label, pages, content }) {
|
|
174
|
+
const nav = []
|
|
175
|
+
if (app.links) {
|
|
176
|
+
nav.push(
|
|
177
|
+
...pages.map(
|
|
178
|
+
(page) =>
|
|
179
|
+
`Link.render({ href: "${page.path}", children: ${label(page.key, page.text)} })`,
|
|
180
|
+
),
|
|
181
|
+
)
|
|
182
|
+
}
|
|
183
|
+
if (app.i18n) {
|
|
184
|
+
nav.push(`Select.render({
|
|
185
|
+
value: i18n.locale,
|
|
186
|
+
options: i18n.languages,
|
|
187
|
+
size: "sm",
|
|
188
|
+
ariaLabel: t("language"),
|
|
189
|
+
onChange: (locale) => api.notify("#i18n:localeChange", locale),
|
|
190
|
+
})`)
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
const trailing = nav.length
|
|
194
|
+
? `
|
|
195
|
+
trailing: html\`<nav class="app-nav">
|
|
196
|
+
\${[
|
|
197
|
+
${nav.join(",\n ")},
|
|
198
|
+
]}
|
|
199
|
+
</nav>\`,`
|
|
200
|
+
: ""
|
|
201
|
+
|
|
202
|
+
const header = ` <header${app.mobile ? ' class="pt-safe"' : ""}>
|
|
203
|
+
\${AppBar.render({
|
|
204
|
+
title: "${app.projectName}",${trailing}
|
|
205
|
+
})}
|
|
206
|
+
</header>
|
|
207
|
+
<main>
|
|
208
|
+
\${Container.render({ maxWidth: "md", children: ${content} })}
|
|
209
|
+
</main>`
|
|
210
|
+
|
|
211
|
+
if (!app.tabs) return header
|
|
212
|
+
|
|
213
|
+
const actions = pages.map(
|
|
214
|
+
(page) => ` {
|
|
215
|
+
value: "${page.path}",
|
|
216
|
+
label: ${label(page.key, page.text)},${
|
|
217
|
+
page.icon
|
|
218
|
+
? `
|
|
219
|
+
icon: Icon.render({ icon: ${page.icon.name}, size: "lg" }),`
|
|
220
|
+
: ""
|
|
221
|
+
}
|
|
222
|
+
},`,
|
|
223
|
+
)
|
|
224
|
+
|
|
225
|
+
return `${header}
|
|
226
|
+
<footer class="pb-safe">
|
|
227
|
+
\${BottomNavigation.render({
|
|
228
|
+
value: router.path,
|
|
229
|
+
actions: [
|
|
230
|
+
${actions.join("\n")}
|
|
231
|
+
],
|
|
232
|
+
onChange: (path) => api.notify("#router:navigate", path),
|
|
233
|
+
})}
|
|
234
|
+
</footer>`
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
function plainShell(app, { label, pages, content }) {
|
|
238
|
+
const nav = app.routing
|
|
239
|
+
? pages.map(
|
|
240
|
+
(page) =>
|
|
241
|
+
` <a href="${page.path}">\${${label(page.key, page.text)}}</a>`,
|
|
242
|
+
)
|
|
243
|
+
: []
|
|
244
|
+
if (app.i18n) {
|
|
245
|
+
nav.push(` <select
|
|
246
|
+
aria-label=\${t("language")}
|
|
247
|
+
@change=\${${selectChange(app, " ")}}
|
|
248
|
+
>
|
|
249
|
+
\${i18n.languages${app.isTypeScript ? "?." : "."}map(
|
|
250
|
+
(language) =>
|
|
251
|
+
html\`<option
|
|
252
|
+
value=\${language.value}
|
|
253
|
+
?selected=\${language.value === i18n.locale}
|
|
254
|
+
>
|
|
255
|
+
\${language.label}
|
|
256
|
+
</option>\`,
|
|
257
|
+
)}
|
|
258
|
+
</select>`)
|
|
259
|
+
}
|
|
260
|
+
const navBlock = nav.length
|
|
261
|
+
? `
|
|
262
|
+
<nav class="app-nav">
|
|
263
|
+
${nav.join("\n")}
|
|
264
|
+
</nav>`
|
|
265
|
+
: ""
|
|
266
|
+
|
|
267
|
+
return ` <header>
|
|
268
|
+
<strong>${app.projectName}</strong>${navBlock}
|
|
269
|
+
</header>
|
|
270
|
+
<main>\${${content}}</main>`
|
|
271
|
+
}
|
package/bin/lib/app-vue.js
CHANGED
|
@@ -51,6 +51,77 @@ ${template}
|
|
|
51
51
|
`
|
|
52
52
|
}
|
|
53
53
|
|
|
54
|
+
export function homeSource(app, { showcase }) {
|
|
55
|
+
const imports = ['import { Message } from "../types/message.vue"']
|
|
56
|
+
for (const icon of showcase) {
|
|
57
|
+
imports.push(`import ${icon.name} from "${icon.from}"`)
|
|
58
|
+
}
|
|
59
|
+
if (showcase.length) {
|
|
60
|
+
imports.push('import { Icon } from "@inglorious/ui/icon"')
|
|
61
|
+
}
|
|
62
|
+
if (app.i18n) imports.push('import { t } from "../i18n"')
|
|
63
|
+
|
|
64
|
+
const text = (key, fallback) => (app.i18n ? `{{ t("${key}") }}` : fallback)
|
|
65
|
+
const icons = showcase.length
|
|
66
|
+
? `
|
|
67
|
+
<p class="app-icons">
|
|
68
|
+
${showcase
|
|
69
|
+
.map(
|
|
70
|
+
(icon) =>
|
|
71
|
+
` <Icon :icon="${icon.name}" size="lg" label="${icon.label}" />`,
|
|
72
|
+
)
|
|
73
|
+
.join("\n")}
|
|
74
|
+
</p>`
|
|
75
|
+
: ""
|
|
76
|
+
|
|
77
|
+
return `${script(imports, [])}<template>
|
|
78
|
+
<h1>${text("home.title", "Welcome")}</h1>
|
|
79
|
+
<p>
|
|
80
|
+
<Message v-bind="message1" />, <Message v-bind="message2" />,
|
|
81
|
+
<Message v-bind="message3" />!
|
|
82
|
+
</p>
|
|
83
|
+
<p>${text("home.hint", "Click a greeting to shout it.")}</p>${icons}
|
|
84
|
+
</template>
|
|
85
|
+
`
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
export function aboutSource(app) {
|
|
89
|
+
const imports = app.i18n ? ['import { t } from "../i18n"'] : []
|
|
90
|
+
const text = (key, fallback) => (app.i18n ? `{{ t("${key}") }}` : fallback)
|
|
91
|
+
return `${script(imports, [])}<template>
|
|
92
|
+
<h1>${text("about.title", "About")}</h1>
|
|
93
|
+
<p>${text("about.body", "A page of its own, at its own address.")}</p>
|
|
94
|
+
</template>
|
|
95
|
+
`
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
export function messageSource(app) {
|
|
99
|
+
const imports = app.i18n ? ['import { t } from "../i18n"'] : []
|
|
100
|
+
const who = "entity.isUpperCase ? entity.who.toUpperCase() : entity.who"
|
|
101
|
+
const span = app.i18n
|
|
102
|
+
? ` <span @click="click">{{
|
|
103
|
+
t("message.hello", {
|
|
104
|
+
who: ${who},
|
|
105
|
+
})
|
|
106
|
+
}}</span>`
|
|
107
|
+
: ` <span @click="click"
|
|
108
|
+
>Hello
|
|
109
|
+
{{ ${who} }}</span
|
|
110
|
+
>`
|
|
111
|
+
const scriptBody = [
|
|
112
|
+
...(imports.length ? [importBlock(imports), ""] : []),
|
|
113
|
+
"const click = (entity) => (entity.isUpperCase = !entity.isUpperCase)",
|
|
114
|
+
]
|
|
115
|
+
return `<script>
|
|
116
|
+
${scriptBody.join("\n")}
|
|
117
|
+
</script>
|
|
118
|
+
|
|
119
|
+
<template>
|
|
120
|
+
${span}
|
|
121
|
+
</template>
|
|
122
|
+
`
|
|
123
|
+
}
|
|
124
|
+
|
|
54
125
|
function script(imports, locals) {
|
|
55
126
|
if (!imports.length && !locals.length) return ""
|
|
56
127
|
const parts = []
|
|
@@ -166,74 +237,3 @@ ${nav.join("\n")}
|
|
|
166
237
|
</header>
|
|
167
238
|
<main>{{ ${content} }}</main>`
|
|
168
239
|
}
|
|
169
|
-
|
|
170
|
-
export function homeSource(app, { showcase }) {
|
|
171
|
-
const imports = ['import { Message } from "../types/message.vue"']
|
|
172
|
-
for (const icon of showcase) {
|
|
173
|
-
imports.push(`import ${icon.name} from "${icon.from}"`)
|
|
174
|
-
}
|
|
175
|
-
if (showcase.length) {
|
|
176
|
-
imports.push('import { Icon } from "@inglorious/ui/icon"')
|
|
177
|
-
}
|
|
178
|
-
if (app.i18n) imports.push('import { t } from "../i18n"')
|
|
179
|
-
|
|
180
|
-
const text = (key, fallback) => (app.i18n ? `{{ t("${key}") }}` : fallback)
|
|
181
|
-
const icons = showcase.length
|
|
182
|
-
? `
|
|
183
|
-
<p class="app-icons">
|
|
184
|
-
${showcase
|
|
185
|
-
.map(
|
|
186
|
-
(icon) =>
|
|
187
|
-
` <Icon :icon="${icon.name}" size="lg" label="${icon.label}" />`,
|
|
188
|
-
)
|
|
189
|
-
.join("\n")}
|
|
190
|
-
</p>`
|
|
191
|
-
: ""
|
|
192
|
-
|
|
193
|
-
return `${script(imports, [])}<template>
|
|
194
|
-
<h1>${text("home.title", "Welcome")}</h1>
|
|
195
|
-
<p>
|
|
196
|
-
<Message v-bind="message1" />, <Message v-bind="message2" />,
|
|
197
|
-
<Message v-bind="message3" />!
|
|
198
|
-
</p>
|
|
199
|
-
<p>${text("home.hint", "Click a greeting to shout it.")}</p>${icons}
|
|
200
|
-
</template>
|
|
201
|
-
`
|
|
202
|
-
}
|
|
203
|
-
|
|
204
|
-
export function aboutSource(app) {
|
|
205
|
-
const imports = app.i18n ? ['import { t } from "../i18n"'] : []
|
|
206
|
-
const text = (key, fallback) => (app.i18n ? `{{ t("${key}") }}` : fallback)
|
|
207
|
-
return `${script(imports, [])}<template>
|
|
208
|
-
<h1>${text("about.title", "About")}</h1>
|
|
209
|
-
<p>${text("about.body", "A page of its own, at its own address.")}</p>
|
|
210
|
-
</template>
|
|
211
|
-
`
|
|
212
|
-
}
|
|
213
|
-
|
|
214
|
-
export function messageSource(app) {
|
|
215
|
-
const imports = app.i18n ? ['import { t } from "../i18n"'] : []
|
|
216
|
-
const who = "entity.isUpperCase ? entity.who.toUpperCase() : entity.who"
|
|
217
|
-
const span = app.i18n
|
|
218
|
-
? ` <span @click="click">{{
|
|
219
|
-
t("message.hello", {
|
|
220
|
-
who: ${who},
|
|
221
|
-
})
|
|
222
|
-
}}</span>`
|
|
223
|
-
: ` <span @click="click"
|
|
224
|
-
>Hello
|
|
225
|
-
{{ ${who} }}</span
|
|
226
|
-
>`
|
|
227
|
-
const scriptBody = [
|
|
228
|
-
...(imports.length ? [importBlock(imports), ""] : []),
|
|
229
|
-
"const click = (entity) => (entity.isUpperCase = !entity.isUpperCase)",
|
|
230
|
-
]
|
|
231
|
-
return `<script>
|
|
232
|
-
${scriptBody.join("\n")}
|
|
233
|
-
</script>
|
|
234
|
-
|
|
235
|
-
<template>
|
|
236
|
-
${span}
|
|
237
|
-
</template>
|
|
238
|
-
`
|
|
239
|
-
}
|
package/bin/lib/app.js
CHANGED
|
@@ -227,9 +227,7 @@ main {
|
|
|
227
227
|
|
|
228
228
|
function storeSource(app) {
|
|
229
229
|
const imports = [
|
|
230
|
-
|
|
231
|
-
? 'import { createStore, type TypesConfig } from "@inglorious/store"'
|
|
232
|
-
: 'import { createStore } from "@inglorious/store"',
|
|
230
|
+
'import { createStore } from "@inglorious/store"',
|
|
233
231
|
componentImport(app, "Home", "../pages/home"),
|
|
234
232
|
componentImport(app, "Message", "../types/message"),
|
|
235
233
|
'import { entities } from "./entities"',
|
|
@@ -250,14 +248,13 @@ function storeSource(app) {
|
|
|
250
248
|
const types = ["Message", "Home"]
|
|
251
249
|
if (app.routing) types.push("About", "Router")
|
|
252
250
|
if (app.i18n) types.push("I18n")
|
|
253
|
-
const
|
|
254
|
-
const oneLine = ` types: { ${types.join(", ")} }${cast},`
|
|
251
|
+
const oneLine = ` types: { ${types.join(", ")} },`
|
|
255
252
|
const typesValue =
|
|
256
253
|
oneLine.length <= LINE_WIDTH
|
|
257
254
|
? oneLine
|
|
258
255
|
: ` types: {
|
|
259
256
|
${types.map((type) => ` ${type},`).join("\n")}
|
|
260
|
-
}
|
|
257
|
+
},`
|
|
261
258
|
const generics = app.isTypeScript ? "<AppEntity, AppState>" : ""
|
|
262
259
|
|
|
263
260
|
return `${importBlock(imports)}
|
|
@@ -333,14 +330,13 @@ export const { I18n, t, ready } = createI18n({
|
|
|
333
330
|
function typesSource(app) {
|
|
334
331
|
const imports = [
|
|
335
332
|
'import "./vite-end"',
|
|
336
|
-
'import type {
|
|
333
|
+
'import type {\n BaseEntity,\n EntitiesState,\n EntityType,\n} from "@inglorious/store"',
|
|
337
334
|
'import type { Api, TemplateResult } from "@inglorious/web"',
|
|
338
335
|
]
|
|
339
336
|
const entities = ["MessageEntity", "HomeEntity"]
|
|
340
337
|
const declarations = [
|
|
341
338
|
`export interface MessageEntity extends BaseEntity {
|
|
342
339
|
type: "Message"
|
|
343
|
-
id?: string
|
|
344
340
|
who: string
|
|
345
341
|
isUpperCase?: boolean
|
|
346
342
|
}`,
|
|
@@ -362,7 +358,7 @@ function typesSource(app) {
|
|
|
362
358
|
`export interface AboutEntity extends BaseEntity {
|
|
363
359
|
type: "About"
|
|
364
360
|
}`,
|
|
365
|
-
`export interface AppRouterEntity extends
|
|
361
|
+
`export interface AppRouterEntity extends RouterEntity {
|
|
366
362
|
type: "Router"
|
|
367
363
|
}`,
|
|
368
364
|
)
|
|
@@ -373,7 +369,7 @@ function typesSource(app) {
|
|
|
373
369
|
imports.push('import type { I18nEntity } from "@inglorious/i18n"')
|
|
374
370
|
entities.push("AppI18nEntity")
|
|
375
371
|
declarations.push(
|
|
376
|
-
`export interface AppI18nEntity extends
|
|
372
|
+
`export interface AppI18nEntity extends I18nEntity {
|
|
377
373
|
type: "I18n"
|
|
378
374
|
}`,
|
|
379
375
|
)
|
|
@@ -394,9 +390,23 @@ ${state.join("\n")}
|
|
|
394
390
|
|
|
395
391
|
export type AppApi = Api<AppEntity, AppState>
|
|
396
392
|
|
|
397
|
-
|
|
393
|
+
// Each type's events and their payloads. Declare types with \`type\`, not
|
|
394
|
+
// \`interface\`: an interface does not fit the store's \`types\`.
|
|
395
|
+
export type MessageType = {
|
|
398
396
|
click: (entity: MessageEntity) => void
|
|
399
|
-
render: (entity: MessageEntity, api: AppApi) => TemplateResult
|
|
397
|
+
render: (entity: MessageEntity, api: AppApi) => TemplateResult
|
|
398
|
+
}
|
|
399
|
+
${
|
|
400
|
+
app.renderer === "lit-html"
|
|
401
|
+
? `
|
|
402
|
+
export type HomeType = {
|
|
403
|
+
render: (entity: HomeEntity, api: AppApi) => TemplateResult
|
|
404
|
+
}
|
|
405
|
+
`
|
|
406
|
+
: ""
|
|
400
407
|
}
|
|
408
|
+
// A shorthand for a type: \`{ ... } satisfies AppType<MessageEntity>\` infers
|
|
409
|
+
// its handlers' parameters, but neither lists its events nor types payloads.
|
|
410
|
+
export type AppType<TEntity extends AppEntity> = EntityType<TEntity, AppApi>
|
|
401
411
|
`
|
|
402
412
|
}
|
package/bin/lib/mobile.js
CHANGED
|
@@ -34,21 +34,6 @@ export function addMobileSupport(targetDir, pkg, projectName) {
|
|
|
34
34
|
appendMobileSetupToReadme(targetDir)
|
|
35
35
|
}
|
|
36
36
|
|
|
37
|
-
// A TypeScript template type-checks and lints its config files through
|
|
38
|
-
// tsconfig.node.json, so a new one has to be listed there. Edited as text, so
|
|
39
|
-
// the `include` line keeps the one-line layout Prettier gives it.
|
|
40
|
-
function includeInNodeProject(targetDir, file) {
|
|
41
|
-
const tsconfigPath = path.join(targetDir, "tsconfig.node.json")
|
|
42
|
-
if (!fs.existsSync(tsconfigPath)) return
|
|
43
|
-
|
|
44
|
-
const tsconfig = fs.readFileSync(tsconfigPath, "utf-8")
|
|
45
|
-
if (tsconfig.includes(`"${file}"`)) return
|
|
46
|
-
fs.writeFileSync(
|
|
47
|
-
tsconfigPath,
|
|
48
|
-
tsconfig.replace(/("include": \[[^\]]*)\]/, `$1, "${file}"]`),
|
|
49
|
-
)
|
|
50
|
-
}
|
|
51
|
-
|
|
52
37
|
export function appendMobileSetupToReadme(targetDir) {
|
|
53
38
|
const readmePath = path.join(targetDir, "README.md")
|
|
54
39
|
if (!fs.existsSync(readmePath)) {
|
|
@@ -138,3 +123,18 @@ const config: CapacitorConfig = {
|
|
|
138
123
|
export default config
|
|
139
124
|
`
|
|
140
125
|
}
|
|
126
|
+
|
|
127
|
+
// A TypeScript template type-checks and lints its config files through
|
|
128
|
+
// tsconfig.node.json, so a new one has to be listed there. Edited as text, so
|
|
129
|
+
// the `include` line keeps the one-line layout Prettier gives it.
|
|
130
|
+
function includeInNodeProject(targetDir, file) {
|
|
131
|
+
const tsconfigPath = path.join(targetDir, "tsconfig.node.json")
|
|
132
|
+
if (!fs.existsSync(tsconfigPath)) return
|
|
133
|
+
|
|
134
|
+
const tsconfig = fs.readFileSync(tsconfigPath, "utf-8")
|
|
135
|
+
if (tsconfig.includes(`"${file}"`)) return
|
|
136
|
+
fs.writeFileSync(
|
|
137
|
+
tsconfigPath,
|
|
138
|
+
tsconfig.replace(/("include": \[[^\]]*)\]/, `$1, "${file}"]`),
|
|
139
|
+
)
|
|
140
|
+
}
|
package/bin/lib/pwa.js
CHANGED
|
@@ -93,7 +93,9 @@ export function writePwaRegistration(targetDir) {
|
|
|
93
93
|
|
|
94
94
|
export function createPwaRegistrationSource(extension) {
|
|
95
95
|
if (extension === ".ts") {
|
|
96
|
-
return
|
|
96
|
+
return `// Only the framework-free helpers: "client" also loads React's and Preact's
|
|
97
|
+
// typings, whose global JSX would replace Inglorious's.
|
|
98
|
+
/// <reference types="vite-plugin-pwa/vanillajs" />
|
|
97
99
|
import { isNative } from "@inglorious/web/mobile"
|
|
98
100
|
import { registerSW } from "virtual:pwa-register"
|
|
99
101
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@inglorious/create-app",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.4.1",
|
|
4
4
|
"description": "A scaffolding tool to quickly create a new app with Inglorious Web.",
|
|
5
5
|
"author": "Matteo Antony Mistretta <antony.mistretta@gmail.com> (https://ingloriouscoderz.it)",
|
|
6
6
|
"license": "MIT",
|
|
@@ -37,7 +37,7 @@
|
|
|
37
37
|
"prompts": "^2.4.2"
|
|
38
38
|
},
|
|
39
39
|
"devDependencies": {
|
|
40
|
-
"@inglorious/eslint-config": "1.1.
|
|
40
|
+
"@inglorious/eslint-config": "1.1.4",
|
|
41
41
|
"prettier": "^3.9.6"
|
|
42
42
|
},
|
|
43
43
|
"scripts": {
|
|
@@ -1,13 +1,13 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { html } from "@inglorious/web"
|
|
2
2
|
|
|
3
|
-
import type {
|
|
3
|
+
import type { AboutType } from "../../types"
|
|
4
4
|
|
|
5
|
-
export const About = {
|
|
6
|
-
click(entity
|
|
5
|
+
export const About: AboutType = {
|
|
6
|
+
click(entity) {
|
|
7
7
|
entity.name += "!"
|
|
8
8
|
},
|
|
9
9
|
|
|
10
|
-
render(entity
|
|
10
|
+
render(entity, api) {
|
|
11
11
|
return html`
|
|
12
12
|
<div>
|
|
13
13
|
<h1>
|
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
import { BaseEntity, EntitiesState } from "@inglorious/store"
|
|
1
|
+
import type { BaseEntity, EntitiesState, EntityType } from "@inglorious/store"
|
|
2
|
+
import type { Api, TemplateResult } from "@inglorious/web"
|
|
2
3
|
|
|
3
4
|
// Define your entity types
|
|
4
5
|
export interface AboutEntity extends BaseEntity {
|
|
5
6
|
type: "About"
|
|
6
|
-
id?: string
|
|
7
7
|
name: string
|
|
8
8
|
}
|
|
9
9
|
|
|
@@ -15,9 +15,15 @@ export interface AppState extends EntitiesState<AppEntity> {
|
|
|
15
15
|
about: AboutEntity
|
|
16
16
|
}
|
|
17
17
|
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
18
|
+
export type AppApi = Api<AppEntity, AppState>
|
|
19
|
+
|
|
20
|
+
// Each type's events and their payloads. Declare types with `type`, not
|
|
21
|
+
// `interface`: an interface does not fit the store's `types`.
|
|
22
|
+
export type AboutType = {
|
|
23
|
+
click: (entity: AboutEntity) => void
|
|
24
|
+
render: (entity: AboutEntity, api: AppApi) => TemplateResult
|
|
23
25
|
}
|
|
26
|
+
|
|
27
|
+
// A shorthand for a type: `{ ... } satisfies AppType<AboutEntity>` infers
|
|
28
|
+
// its handlers' parameters, but neither lists its events nor types payloads.
|
|
29
|
+
export type AppType<TEntity extends AppEntity> = EntityType<TEntity, AppApi>
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { createStore
|
|
1
|
+
import { createStore } from "@inglorious/store"
|
|
2
2
|
|
|
3
3
|
import type { AppEntity, AppState } from "../../types"
|
|
4
4
|
import { Message } from "../types/message"
|
|
@@ -6,7 +6,7 @@ import { entities } from "./entities"
|
|
|
6
6
|
import { middlewares } from "./middlewares"
|
|
7
7
|
|
|
8
8
|
export const store = createStore<AppEntity, AppState>({
|
|
9
|
-
types: { Message }
|
|
9
|
+
types: { Message },
|
|
10
10
|
entities,
|
|
11
11
|
middlewares,
|
|
12
12
|
})
|
|
@@ -1,12 +1,11 @@
|
|
|
1
1
|
import "./vite-end"
|
|
2
2
|
|
|
3
|
-
import { BaseEntity, EntitiesState } from "@inglorious/store"
|
|
4
|
-
import { Api, TemplateResult } from "@inglorious/web"
|
|
3
|
+
import type { BaseEntity, EntitiesState, EntityType } from "@inglorious/store"
|
|
4
|
+
import type { Api, TemplateResult } from "@inglorious/web"
|
|
5
5
|
|
|
6
6
|
// Define your entity types
|
|
7
7
|
export interface MessageEntity extends BaseEntity {
|
|
8
8
|
type: "Message"
|
|
9
|
-
id?: string
|
|
10
9
|
who: string
|
|
11
10
|
isUpperCase?: boolean
|
|
12
11
|
}
|
|
@@ -23,8 +22,13 @@ export interface AppState extends EntitiesState<AppEntity> {
|
|
|
23
22
|
|
|
24
23
|
export type AppApi = Api<AppEntity, AppState>
|
|
25
24
|
|
|
26
|
-
//
|
|
27
|
-
|
|
25
|
+
// Each type's events and their payloads. Declare types with `type`, not
|
|
26
|
+
// `interface`: an interface does not fit the store's `types`.
|
|
27
|
+
export type MessageType = {
|
|
28
28
|
click: (entity: MessageEntity) => void
|
|
29
|
-
render: (entity: MessageEntity, api: AppApi) => TemplateResult
|
|
29
|
+
render: (entity: MessageEntity, api: AppApi) => TemplateResult
|
|
30
30
|
}
|
|
31
|
+
|
|
32
|
+
// A shorthand for a type: `{ ... } satisfies AppType<MessageEntity>` infers
|
|
33
|
+
// its handlers' parameters, but neither lists its events nor types payloads.
|
|
34
|
+
export type AppType<TEntity extends AppEntity> = EntityType<TEntity, AppApi>
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { createStore
|
|
1
|
+
import { createStore } from "@inglorious/store"
|
|
2
2
|
|
|
3
3
|
import type { AppEntity, AppState } from "../../types"
|
|
4
4
|
import { Message } from "../types/message"
|
|
@@ -6,7 +6,7 @@ import { entities } from "./entities"
|
|
|
6
6
|
import { middlewares } from "./middlewares"
|
|
7
7
|
|
|
8
8
|
export const store = createStore<AppEntity, AppState>({
|
|
9
|
-
types: { Message }
|
|
9
|
+
types: { Message },
|
|
10
10
|
entities,
|
|
11
11
|
middlewares,
|
|
12
12
|
})
|
|
@@ -1,12 +1,11 @@
|
|
|
1
1
|
import "./vite-end"
|
|
2
2
|
|
|
3
|
-
import { BaseEntity, EntitiesState } from "@inglorious/store"
|
|
4
|
-
import { Api, TemplateResult } from "@inglorious/web"
|
|
3
|
+
import type { BaseEntity, EntitiesState, EntityType } from "@inglorious/store"
|
|
4
|
+
import type { Api, TemplateResult } from "@inglorious/web"
|
|
5
5
|
|
|
6
6
|
// Define your entity types
|
|
7
7
|
export interface MessageEntity extends BaseEntity {
|
|
8
8
|
type: "Message"
|
|
9
|
-
id?: string
|
|
10
9
|
who: string
|
|
11
10
|
isUpperCase?: boolean
|
|
12
11
|
}
|
|
@@ -23,8 +22,13 @@ export interface AppState extends EntitiesState<AppEntity> {
|
|
|
23
22
|
|
|
24
23
|
export type AppApi = Api<AppEntity, AppState>
|
|
25
24
|
|
|
26
|
-
//
|
|
27
|
-
|
|
25
|
+
// Each type's events and their payloads. Declare types with `type`, not
|
|
26
|
+
// `interface`: an interface does not fit the store's `types`.
|
|
27
|
+
export type MessageType = {
|
|
28
28
|
click: (entity: MessageEntity) => void
|
|
29
|
-
render: (entity: MessageEntity, api: AppApi) => TemplateResult
|
|
29
|
+
render: (entity: MessageEntity, api: AppApi) => TemplateResult
|
|
30
30
|
}
|
|
31
|
+
|
|
32
|
+
// A shorthand for a type: `{ ... } satisfies AppType<MessageEntity>` infers
|
|
33
|
+
// its handlers' parameters, but neither lists its events nor types payloads.
|
|
34
|
+
export type AppType<TEntity extends AppEntity> = EntityType<TEntity, AppApi>
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { createStore
|
|
1
|
+
import { createStore } from "@inglorious/store"
|
|
2
2
|
|
|
3
3
|
import type { AppEntity, AppState } from "../../types"
|
|
4
4
|
// @ts-expect-error - Handled by @inglorious/vite-plugin-vue
|
|
@@ -7,7 +7,7 @@ import { entities } from "./entities"
|
|
|
7
7
|
import { middlewares } from "./middlewares"
|
|
8
8
|
|
|
9
9
|
export const store = createStore<AppEntity, AppState>({
|
|
10
|
-
types: { Message }
|
|
10
|
+
types: { Message },
|
|
11
11
|
entities,
|
|
12
12
|
middlewares,
|
|
13
13
|
})
|
|
@@ -1,12 +1,11 @@
|
|
|
1
1
|
import "./vite-end"
|
|
2
2
|
|
|
3
|
-
import { BaseEntity, EntitiesState } from "@inglorious/store"
|
|
4
|
-
import { Api
|
|
3
|
+
import type { BaseEntity, EntitiesState, EntityType } from "@inglorious/store"
|
|
4
|
+
import type { Api } from "@inglorious/web"
|
|
5
5
|
|
|
6
6
|
// Define your entity types
|
|
7
7
|
export interface MessageEntity extends BaseEntity {
|
|
8
8
|
type: "Message"
|
|
9
|
-
id?: string
|
|
10
9
|
who: string
|
|
11
10
|
isUpperCase?: boolean
|
|
12
11
|
}
|
|
@@ -23,8 +22,7 @@ export interface AppState extends EntitiesState<AppEntity> {
|
|
|
23
22
|
|
|
24
23
|
export type AppApi = Api<AppEntity, AppState>
|
|
25
24
|
|
|
26
|
-
//
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
}
|
|
25
|
+
// A type for one of the app's entities. For rigour, declare a type alias
|
|
26
|
+
// listing its events and payloads; this shorthand infers its handlers'
|
|
27
|
+
// parameters with `{ ... } satisfies AppType<MessageEntity>`.
|
|
28
|
+
export type AppType<TEntity extends AppEntity> = EntityType<TEntity, AppApi>
|