@inglorious/create-app 1.1.2 → 1.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +15 -7
- package/bin/index.js +34 -39
- package/bin/lib/app-jsx.js +280 -0
- package/bin/lib/app-lit.js +275 -0
- package/bin/lib/app-vue.js +239 -0
- package/bin/lib/app.js +402 -0
- package/bin/lib/cli.js +31 -0
- package/bin/lib/constants.js +3 -3
- package/bin/lib/features.js +142 -0
- package/bin/lib/imports.js +64 -0
- package/bin/lib/mobile.js +19 -1
- package/bin/lib/pwa.js +50 -4
- package/bin/lib/questions.js +184 -0
- package/bin/lib/source.js +58 -0
- package/package.json +3 -3
- package/templates/js/package.json +4 -4
- package/templates/js/vite.config.js +2 -1
- package/templates/js-jsx/package.json +4 -4
- package/templates/js-jsx/vite.config.js +2 -1
- package/templates/js-vue/package.json +4 -4
- package/templates/js-vue/vite.config.js +2 -1
- package/templates/ssx-js/package.json +8 -7
- package/templates/ssx-js/src/site.config.js +2 -1
- package/templates/ssx-ts/package.json +9 -7
- package/templates/ssx-ts/src/site.config.ts +1 -0
- package/templates/ts/package.json +7 -6
- package/templates/ts/src/app.ts +4 -2
- package/templates/ts/tsconfig.app.json +2 -0
- package/templates/ts/tsconfig.node.json +2 -0
- package/templates/ts/types/index.d.ts +4 -1
- package/templates/ts/vite.config.ts +2 -1
- package/templates/ts-jsx/package.json +7 -6
- package/templates/ts-jsx/src/app.tsx +2 -2
- package/templates/ts-jsx/tsconfig.app.json +2 -0
- package/templates/ts-jsx/tsconfig.node.json +2 -0
- package/templates/ts-jsx/types/index.d.ts +3 -1
- package/templates/ts-jsx/types/vite-end.d.ts +1 -0
- package/templates/ts-jsx/vite.config.ts +2 -1
- package/templates/ts-vue/eslint.config.ts +1 -1
- package/templates/ts-vue/package.json +7 -6
- package/templates/ts-vue/src/store/middlewares.ts +5 -2
- package/templates/ts-vue/tsconfig.app.json +2 -0
- package/templates/ts-vue/tsconfig.node.json +2 -0
- package/templates/ts-vue/types/index.d.ts +3 -1
- package/templates/ts-vue/vite.config.ts +2 -1
|
@@ -0,0 +1,275 @@
|
|
|
1
|
+
// The app's components in lit-html: tagged templates in .js or .ts files.
|
|
2
|
+
|
|
3
|
+
import { importBlock } from "./imports.js"
|
|
4
|
+
|
|
5
|
+
export const extension = (app) => (app.isTypeScript ? "ts" : "js")
|
|
6
|
+
|
|
7
|
+
export function appSource(app, { pages, navIcons }) {
|
|
8
|
+
const imports = ['import { html } from "@inglorious/web"']
|
|
9
|
+
if (navIcons) {
|
|
10
|
+
for (const icon of Object.values(navIcons)) {
|
|
11
|
+
imports.push(`import ${icon.name} from "${icon.from}"`)
|
|
12
|
+
}
|
|
13
|
+
imports.push('import { Icon } from "@inglorious/ui/icon"')
|
|
14
|
+
}
|
|
15
|
+
if (app.ui) {
|
|
16
|
+
imports.push(
|
|
17
|
+
'import { AppBar } from "@inglorious/ui/app-bar"',
|
|
18
|
+
'import { Container } from "@inglorious/ui/container"',
|
|
19
|
+
)
|
|
20
|
+
if (app.tabs) {
|
|
21
|
+
imports.push(
|
|
22
|
+
'import { BottomNavigation } from "@inglorious/ui/bottom-navigation"',
|
|
23
|
+
)
|
|
24
|
+
}
|
|
25
|
+
if (app.links) imports.push('import { Link } from "@inglorious/ui/link"')
|
|
26
|
+
if (app.i18n) imports.push('import { Select } from "@inglorious/ui/select"')
|
|
27
|
+
}
|
|
28
|
+
if (app.isTypeScript) imports.push('import type { AppApi } from "../types"')
|
|
29
|
+
if (app.i18n) imports.push('import { t } from "./i18n"')
|
|
30
|
+
|
|
31
|
+
const label = (key, text) => (app.i18n ? `t("${key}")` : `"${text}"`)
|
|
32
|
+
const signature = app.isTypeScript
|
|
33
|
+
? "render(props: Record<string, unknown> | null, api: AppApi)"
|
|
34
|
+
: "render(props, api)"
|
|
35
|
+
|
|
36
|
+
const content = app.routing
|
|
37
|
+
? "api.render(router.route)"
|
|
38
|
+
: 'api.render("home")'
|
|
39
|
+
|
|
40
|
+
const template = app.ui
|
|
41
|
+
? uiShell(app, { label, pages, content })
|
|
42
|
+
: plainShell(app, { label, pages, content })
|
|
43
|
+
|
|
44
|
+
return `${importBlock(imports)}
|
|
45
|
+
|
|
46
|
+
export const App = {
|
|
47
|
+
${signature} {
|
|
48
|
+
${renderLocals(app)} return html\`
|
|
49
|
+
${template}
|
|
50
|
+
\`
|
|
51
|
+
},
|
|
52
|
+
}
|
|
53
|
+
`
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
// The app's own entities are always there; TypeScript cannot know that.
|
|
57
|
+
export function renderLocals(app) {
|
|
58
|
+
const always = app.isTypeScript ? "!" : ""
|
|
59
|
+
const locals = []
|
|
60
|
+
if (app.routing) {
|
|
61
|
+
locals.push(` const router = api.getEntity("router")${always}`)
|
|
62
|
+
}
|
|
63
|
+
if (app.i18n) locals.push(` const i18n = api.getEntity("i18n")${always}`)
|
|
64
|
+
return locals.length ? `${locals.join("\n")}\n\n` : ""
|
|
65
|
+
}
|
|
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
|
+
// The native language picker's change handler, laid out as Prettier would at
|
|
132
|
+
// `indent`. TypeScript has to be told what the event's target is.
|
|
133
|
+
export function selectChange(app, indent) {
|
|
134
|
+
if (!app.isTypeScript) {
|
|
135
|
+
return `(event) =>
|
|
136
|
+
${indent} api.notify("#i18n:localeChange", event.target.value)`
|
|
137
|
+
}
|
|
138
|
+
return `(event: Event) =>
|
|
139
|
+
${indent} api.notify(
|
|
140
|
+
${indent} "#i18n:localeChange",
|
|
141
|
+
${indent} (event.target as HTMLSelectElement).value,
|
|
142
|
+
${indent} )`
|
|
143
|
+
}
|
|
144
|
+
|
|
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
|
+
export function homeSource(app, { showcase }) {
|
|
182
|
+
const imports = ['import { html } from "@inglorious/web"']
|
|
183
|
+
for (const icon of showcase) {
|
|
184
|
+
imports.push(`import ${icon.name} from "${icon.from}"`)
|
|
185
|
+
}
|
|
186
|
+
if (showcase.length) {
|
|
187
|
+
imports.push('import { Icon } from "@inglorious/ui/icon"')
|
|
188
|
+
}
|
|
189
|
+
if (app.isTypeScript) {
|
|
190
|
+
imports.push('import type { AppApi, HomeEntity } from "../../types"')
|
|
191
|
+
}
|
|
192
|
+
if (app.i18n) imports.push('import { t } from "../i18n"')
|
|
193
|
+
|
|
194
|
+
const text = (key, fallback) => (app.i18n ? `\${t("${key}")}` : fallback)
|
|
195
|
+
const icons = showcase.length
|
|
196
|
+
? `
|
|
197
|
+
<p class="app-icons">
|
|
198
|
+
${showcase
|
|
199
|
+
.map(
|
|
200
|
+
(icon) => ` \${Icon.render({
|
|
201
|
+
icon: ${icon.name},
|
|
202
|
+
size: "lg",
|
|
203
|
+
label: "${icon.label}",
|
|
204
|
+
})}`,
|
|
205
|
+
)
|
|
206
|
+
.join("\n")}
|
|
207
|
+
</p>`
|
|
208
|
+
: ""
|
|
209
|
+
|
|
210
|
+
const signature = app.isTypeScript
|
|
211
|
+
? "render(entity: HomeEntity, api: AppApi)"
|
|
212
|
+
: "render(entity, api)"
|
|
213
|
+
|
|
214
|
+
return `${importBlock(imports)}
|
|
215
|
+
|
|
216
|
+
export const Home = {
|
|
217
|
+
${signature} {
|
|
218
|
+
return html\`
|
|
219
|
+
<h1>${text("home.title", "Welcome")}</h1>
|
|
220
|
+
<p>
|
|
221
|
+
\${api.render("message1")}, \${api.render("message2")},
|
|
222
|
+
\${api.render("message3")}!
|
|
223
|
+
</p>
|
|
224
|
+
<p>${text("home.hint", "Click a greeting to shout it.")}</p>${icons}
|
|
225
|
+
\`
|
|
226
|
+
},
|
|
227
|
+
}
|
|
228
|
+
`
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
export function aboutSource(app) {
|
|
232
|
+
const imports = ['import { html } from "@inglorious/web"']
|
|
233
|
+
if (app.i18n) imports.push('import { t } from "../i18n"')
|
|
234
|
+
const text = (key, fallback) => (app.i18n ? `\${t("${key}")}` : fallback)
|
|
235
|
+
|
|
236
|
+
return `${importBlock(imports)}
|
|
237
|
+
|
|
238
|
+
export const About = {
|
|
239
|
+
render() {
|
|
240
|
+
return html\`
|
|
241
|
+
<h1>${text("about.title", "About")}</h1>
|
|
242
|
+
<p>${text("about.body", "A page of its own, at its own address.")}</p>
|
|
243
|
+
\`
|
|
244
|
+
},
|
|
245
|
+
}
|
|
246
|
+
`
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
export function messageSource(app) {
|
|
250
|
+
const imports = ['import { html } from "@inglorious/web"']
|
|
251
|
+
if (app.isTypeScript) {
|
|
252
|
+
imports.push('import type { MessageType } from "../../types"')
|
|
253
|
+
}
|
|
254
|
+
if (app.i18n) imports.push('import { t } from "../i18n"')
|
|
255
|
+
const greeting = app.i18n ? '${t("message.hello", { who })}' : "Hello ${who}"
|
|
256
|
+
const declaration = app.isTypeScript
|
|
257
|
+
? "export const Message: MessageType = {"
|
|
258
|
+
: "export const Message = {"
|
|
259
|
+
|
|
260
|
+
return `${importBlock(imports)}
|
|
261
|
+
|
|
262
|
+
${declaration}
|
|
263
|
+
click(entity) {
|
|
264
|
+
entity.isUpperCase = !entity.isUpperCase
|
|
265
|
+
},
|
|
266
|
+
|
|
267
|
+
render(entity, api) {
|
|
268
|
+
const who = entity.isUpperCase ? entity.who.toUpperCase() : entity.who
|
|
269
|
+
return html\`<span @click=\${() => api.notify(\`#\${entity.id}:click\`)}
|
|
270
|
+
>${greeting}</span
|
|
271
|
+
>\`
|
|
272
|
+
},
|
|
273
|
+
}
|
|
274
|
+
`
|
|
275
|
+
}
|
|
@@ -0,0 +1,239 @@
|
|
|
1
|
+
// The app's components as Vue single-file components, compiled to lit-html by
|
|
2
|
+
// @inglorious/vite-plugin-vue. Imported Inglorious UI components are tags with
|
|
3
|
+
// props; imported .vue components with `v-bind="id"` render store entities;
|
|
4
|
+
// script values that read `api` are computed on every render.
|
|
5
|
+
|
|
6
|
+
import { importBlock } from "./imports.js"
|
|
7
|
+
import { LINE_WIDTH } from "./source.js"
|
|
8
|
+
|
|
9
|
+
export const extension = () => "vue"
|
|
10
|
+
|
|
11
|
+
export function appSource(app, { pages, navIcons }) {
|
|
12
|
+
const imports = []
|
|
13
|
+
if (navIcons) {
|
|
14
|
+
for (const icon of Object.values(navIcons)) {
|
|
15
|
+
imports.push(`import ${icon.name} from "${icon.from}"`)
|
|
16
|
+
}
|
|
17
|
+
imports.push('import { Icon } from "@inglorious/ui/icon"')
|
|
18
|
+
}
|
|
19
|
+
if (app.ui) {
|
|
20
|
+
imports.push(
|
|
21
|
+
'import { AppBar } from "@inglorious/ui/app-bar"',
|
|
22
|
+
'import { Container } from "@inglorious/ui/container"',
|
|
23
|
+
)
|
|
24
|
+
if (app.tabs) {
|
|
25
|
+
imports.push(
|
|
26
|
+
'import { BottomNavigation } from "@inglorious/ui/bottom-navigation"',
|
|
27
|
+
)
|
|
28
|
+
}
|
|
29
|
+
if (app.links) imports.push('import { Link } from "@inglorious/ui/link"')
|
|
30
|
+
if (app.i18n) imports.push('import { Select } from "@inglorious/ui/select"')
|
|
31
|
+
}
|
|
32
|
+
if (app.i18n) imports.push('import { t } from "./i18n"')
|
|
33
|
+
|
|
34
|
+
const locals = []
|
|
35
|
+
if (app.routing) locals.push('const router = api.getEntity("router")')
|
|
36
|
+
if (app.i18n) locals.push('const i18n = api.getEntity("i18n")')
|
|
37
|
+
|
|
38
|
+
const content = app.routing
|
|
39
|
+
? "api.render(router.route)"
|
|
40
|
+
: 'api.render("home")'
|
|
41
|
+
const label = (key, text) => (app.i18n ? `{{ t("${key}") }}` : text)
|
|
42
|
+
const quotedLabel = (key, text) => (app.i18n ? `t('${key}')` : `'${text}'`)
|
|
43
|
+
|
|
44
|
+
const template = app.ui
|
|
45
|
+
? uiShell(app, { label, quotedLabel, pages, content })
|
|
46
|
+
: plainShell(app, { label, pages, content })
|
|
47
|
+
|
|
48
|
+
return `${script(imports, locals)}<template>
|
|
49
|
+
${template}
|
|
50
|
+
</template>
|
|
51
|
+
`
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
function script(imports, locals) {
|
|
55
|
+
if (!imports.length && !locals.length) return ""
|
|
56
|
+
const parts = []
|
|
57
|
+
if (imports.length) parts.push(importBlock(imports))
|
|
58
|
+
if (locals.length) parts.push(locals.join("\n"))
|
|
59
|
+
return `<script>
|
|
60
|
+
${parts.join("\n\n")}
|
|
61
|
+
</script>
|
|
62
|
+
|
|
63
|
+
`
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
function uiShell(app, { label, quotedLabel, pages, content }) {
|
|
67
|
+
const nav = []
|
|
68
|
+
if (app.links) {
|
|
69
|
+
nav.push(
|
|
70
|
+
...pages.map(
|
|
71
|
+
(page) =>
|
|
72
|
+
` <Link href="${page.path}">${label(page.key, page.text)}</Link>`,
|
|
73
|
+
),
|
|
74
|
+
)
|
|
75
|
+
}
|
|
76
|
+
if (app.i18n) {
|
|
77
|
+
nav.push(` <Select
|
|
78
|
+
:value="i18n.locale"
|
|
79
|
+
:options="i18n.languages"
|
|
80
|
+
size="sm"
|
|
81
|
+
:ariaLabel="t('language')"
|
|
82
|
+
:onChange="(locale) => api.notify('#i18n:localeChange', locale)"
|
|
83
|
+
/>`)
|
|
84
|
+
}
|
|
85
|
+
const navBlock = nav.length
|
|
86
|
+
? `
|
|
87
|
+
<nav class="app-nav">
|
|
88
|
+
${nav.join("\n")}
|
|
89
|
+
</nav>
|
|
90
|
+
`
|
|
91
|
+
: ""
|
|
92
|
+
const appBar = nav.length
|
|
93
|
+
? `<AppBar title="${app.projectName}">${navBlock}</AppBar>`
|
|
94
|
+
: `<AppBar title="${app.projectName}" />`
|
|
95
|
+
|
|
96
|
+
const header = ` <header${app.mobile ? ' class="pt-safe"' : ""}>
|
|
97
|
+
${appBar}
|
|
98
|
+
</header>
|
|
99
|
+
<main>
|
|
100
|
+
<Container maxWidth="md">{{ ${content} }}</Container>
|
|
101
|
+
</main>`
|
|
102
|
+
|
|
103
|
+
if (!app.tabs) return header
|
|
104
|
+
|
|
105
|
+
// Each action on one line when it fits, one property per line otherwise.
|
|
106
|
+
const actions = pages.map((page) => {
|
|
107
|
+
const fields = [
|
|
108
|
+
`value: '${page.path}'`,
|
|
109
|
+
`label: ${quotedLabel(page.key, page.text)}`,
|
|
110
|
+
]
|
|
111
|
+
if (page.icon) {
|
|
112
|
+
fields.push(`icon: Icon.render({ icon: ${page.icon.name}, size: 'lg' })`)
|
|
113
|
+
}
|
|
114
|
+
const line = ` { ${fields.join(", ")} },`
|
|
115
|
+
if (line.length <= LINE_WIDTH) return line
|
|
116
|
+
return ` {
|
|
117
|
+
${fields.map((field) => ` ${field},`).join("\n")}
|
|
118
|
+
},`
|
|
119
|
+
})
|
|
120
|
+
|
|
121
|
+
return `${header}
|
|
122
|
+
<footer class="pb-safe">
|
|
123
|
+
<BottomNavigation
|
|
124
|
+
:value="router.path"
|
|
125
|
+
:actions="[
|
|
126
|
+
${actions.join("\n")}
|
|
127
|
+
]"
|
|
128
|
+
:onChange="(path) => api.notify('#router:navigate', path)"
|
|
129
|
+
/>
|
|
130
|
+
</footer>`
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
function plainShell(app, { label, pages, content }) {
|
|
134
|
+
const nav = app.routing
|
|
135
|
+
? pages.map(
|
|
136
|
+
(page) =>
|
|
137
|
+
` <a href="${page.path}">${label(page.key, page.text)}</a>`,
|
|
138
|
+
)
|
|
139
|
+
: []
|
|
140
|
+
if (app.i18n) {
|
|
141
|
+
nav.push(` <select
|
|
142
|
+
:aria-label="t('language')"
|
|
143
|
+
@change="
|
|
144
|
+
(event) => api.notify('#i18n:localeChange', event.target.value)
|
|
145
|
+
"
|
|
146
|
+
>
|
|
147
|
+
<option
|
|
148
|
+
v-for="language in i18n.languages"
|
|
149
|
+
:key="language.value"
|
|
150
|
+
:value="language.value"
|
|
151
|
+
:selected="language.value === i18n.locale"
|
|
152
|
+
>
|
|
153
|
+
{{ language.label }}
|
|
154
|
+
</option>
|
|
155
|
+
</select>`)
|
|
156
|
+
}
|
|
157
|
+
const navBlock = nav.length
|
|
158
|
+
? `
|
|
159
|
+
<nav class="app-nav">
|
|
160
|
+
${nav.join("\n")}
|
|
161
|
+
</nav>`
|
|
162
|
+
: ""
|
|
163
|
+
|
|
164
|
+
return ` <header>
|
|
165
|
+
<strong>${app.projectName}</strong>${navBlock}
|
|
166
|
+
</header>
|
|
167
|
+
<main>{{ ${content} }}</main>`
|
|
168
|
+
}
|
|
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
|
+
}
|