duto 0.0.0 โ 0.260906.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/LICENSE +21 -0
- package/README.md +21 -12
- package/package.json +91 -2
- package/src/cli.ts +42 -0
- package/src/enum.ts +2 -0
- package/src/index.d.ts +61 -0
- package/src/index.ts +19 -0
- package/src/middleware.ts +2 -0
- package/src/pages/client/app.tsx +123 -0
- package/src/pages/client/hmr.ts +13 -0
- package/src/pages/client/island.ts +136 -0
- package/src/pages/client/link.ts +70 -0
- package/src/pages/client/router.ts +329 -0
- package/src/pages/client/storage.ts +95 -0
- package/src/pages/commands/build.ts +74 -0
- package/src/pages/commands/dev-old.ts +213 -0
- package/src/pages/commands/dev.ts +87 -0
- package/src/pages/commands/preview.ts +33 -0
- package/src/pages/compiler/aliases.ts +36 -0
- package/src/pages/compiler/assets.ts +43 -0
- package/src/pages/compiler/ast.ts +764 -0
- package/src/pages/compiler/constants.ts +24 -0
- package/src/pages/compiler/file.ts +60 -0
- package/src/pages/compiler/index.ts +307 -0
- package/src/pages/compiler/island.ts +108 -0
- package/src/pages/compiler/meta.ts +601 -0
- package/src/pages/compiler/routes.ts +29 -0
- package/src/pages/compiler/types.ts +209 -0
- package/src/pages/compiler/utils.ts +5 -0
- package/src/pages/compiler/vite-plugin.ts +324 -0
- package/src/pages/components/link.tsx +38 -0
- package/src/pages/i18n/client.ts +403 -0
- package/src/pages/i18n/element.ts +44 -0
- package/src/pages/i18n/server.ts +56 -0
- package/src/pages/index.ts +28 -0
- package/src/pages/preact/client-dev.ts +5 -0
- package/src/pages/preact/client.ts +87 -0
- package/src/pages/preact/context.ts +41 -0
- package/src/pages/preact/server.ts +186 -0
- package/src/pages/preact/signals.ts +89 -0
- package/src/pages/preact/static-html.ts +45 -0
- package/src/pages/preact/types.ts +26 -0
- package/src/server/adapter/aws.ts +4 -0
- package/src/server/adapter/workerd.ts +4 -0
- package/src/server/app.ts +96 -0
- package/src/server/auth/ability.ts +72 -0
- package/src/server/auth/authnz.ts +86 -0
- package/src/server/auth/index.ts +5 -0
- package/src/server/auth/token.ts +147 -0
- package/src/server/auth/types.ts +2 -0
- package/src/server/cli/commands/build.ts +41 -0
- package/src/server/cli/commands/deploy.ts +82 -0
- package/src/server/cli/commands/dev.ts +277 -0
- package/src/server/cli/commands/make.ts +98 -0
- package/src/server/cli/commands/migrate.ts +93 -0
- package/src/server/cli/commands/routes.ts +79 -0
- package/src/server/cli/index.ts +27 -0
- package/src/server/cli/stubs.ts +39 -0
- package/src/server/cli/types.ts +3 -0
- package/src/server/cli/utils.ts +443 -0
- package/src/server/config.ts +4 -0
- package/src/server/db/d1.d.ts +1 -0
- package/src/server/db/d1.ts +1 -0
- package/src/server/db/dynamodb.d.ts +1 -0
- package/src/server/db/dynamodb.ts +1 -0
- package/src/server/db/index.ts +1 -0
- package/src/server/entry.ts +19 -0
- package/src/server/handle.ts +30 -0
- package/src/server/http.ts +23 -0
- package/src/server/middlewares/auth.ts +40 -0
- package/src/server/middlewares/logger.ts +30 -0
- package/src/server/open-api/register.ts +29 -0
- package/src/server/open-api/spec.ts +107 -0
- package/src/server/request.ts +233 -0
- package/src/server/response.ts +142 -0
- package/src/server/routes.ts +474 -0
- package/src/server/types.ts +142 -0
- package/src/server/utils/environment.ts +17 -0
- package/src/server/utils.ts +46 -0
- package/src/server/validator.ts +70 -0
- package/src/utils.ts +5 -0
|
@@ -0,0 +1,329 @@
|
|
|
1
|
+
import { render, h } from 'preact'
|
|
2
|
+
// import { render, hydrate, h } from 'preact'
|
|
3
|
+
// import { useLocation, Switch, Route, Router } from 'wouter-preact'
|
|
4
|
+
// import { matchRoute } from 'wouter-preact'
|
|
5
|
+
// import type { Routes, IRoute } from '../types'
|
|
6
|
+
import type { Page, GetProps, IRoute } from '@/types'
|
|
7
|
+
// import { updatePickers } from '../i18n/client'
|
|
8
|
+
|
|
9
|
+
// type Route = [string, Page, IRoute['params'], GetProps]
|
|
10
|
+
// type Route = [string, Page] | [string, Page, IRoute['params']] | [string, Page, IRoute['params'], GetProps]
|
|
11
|
+
// type Routes = Route[]
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
// type WRouter = {
|
|
15
|
+
// navigate: ((href: string) => void) | null,
|
|
16
|
+
// routes: Set<string>,
|
|
17
|
+
// load: (routes: Set<string>) => WRouter,
|
|
18
|
+
// has: (path: string) => boolean,
|
|
19
|
+
// // loaded: () => void,
|
|
20
|
+
// mount: (routes: Routes) => void,
|
|
21
|
+
// // App({ container }: {
|
|
22
|
+
// // container: HTMLElement;
|
|
23
|
+
// // }): VNode<Attributes & RouterOptions & {
|
|
24
|
+
// // children: ComponentChild;
|
|
25
|
+
// // }>,
|
|
26
|
+
// }
|
|
27
|
+
|
|
28
|
+
// export default {
|
|
29
|
+
// navigate: null,
|
|
30
|
+
// routes: new Set<string>(),
|
|
31
|
+
// load(routes: Set<string>) {
|
|
32
|
+
// this.routes = routes
|
|
33
|
+
// return this
|
|
34
|
+
// },
|
|
35
|
+
// has(path: string) {
|
|
36
|
+
// return this.routes.has(path)
|
|
37
|
+
// },
|
|
38
|
+
// // loaded() {
|
|
39
|
+
// // document.body.removeAttribute('hidden')
|
|
40
|
+
// // const [, navigate] = useLocation()
|
|
41
|
+
// // this.navigate = navigate
|
|
42
|
+
|
|
43
|
+
// // },
|
|
44
|
+
// mount(routes: Routes) {
|
|
45
|
+
// // ( // If body it was server-rendered โ hydrate instead of render
|
|
46
|
+
// // document.body.childElementCount > 0
|
|
47
|
+
// // ? hydrate
|
|
48
|
+
// // : render // @ts-ignore
|
|
49
|
+
// // )(this.App(routes), document.body)
|
|
50
|
+
|
|
51
|
+
// // @ts-ignore
|
|
52
|
+
// render(this.App(routes), document.body)
|
|
53
|
+
// },
|
|
54
|
+
|
|
55
|
+
// App(routes: Routes, container: HTMLElement = document.body) {
|
|
56
|
+
// return h(Router, null,
|
|
57
|
+
// h(() => {
|
|
58
|
+
// // const __ = useRoute(location.pathname)
|
|
59
|
+
// // console.log(__)
|
|
60
|
+
// document.body.removeAttribute('hidden')
|
|
61
|
+
// const [, navigate] = useLocation()
|
|
62
|
+
// this.navigate = navigate
|
|
63
|
+
|
|
64
|
+
// // TODO: talvez registrar todos os valores pelo id do elemento em uma fila e atualizar nesse tick...
|
|
65
|
+
// // input, select etc registra um update val e aqui popula..
|
|
66
|
+
// updatePickers()
|
|
67
|
+
// return null
|
|
68
|
+
// }, null),
|
|
69
|
+
// h(Switch,
|
|
70
|
+
// null,
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
// // ...[
|
|
74
|
+
// // ['/', 'index.tsx'],
|
|
75
|
+
// // ['/about', 'about.tsx'],
|
|
76
|
+
// // ['/blog/:slug', 'blog/[slug].tsx'],
|
|
77
|
+
// // ['/blog/:slug?', 'blog/[[slug]].tsx'],
|
|
78
|
+
// // ['/shop/*', 'shop/[...slug].tsx'],
|
|
79
|
+
// // ['/shop/*?', 'shop/[[...slug]].tsx'],
|
|
80
|
+
// // ['/:authorId/:postId', '[authorId]/[postId].tsx'],
|
|
81
|
+
// // ['/*', '404.tsx'],
|
|
82
|
+
// // ['/*', 'not-found.tsx'],
|
|
83
|
+
// // ['/users/*', 'users/not-found.tsx'],
|
|
84
|
+
// // ].map(([path, name]) =>
|
|
85
|
+
// // h(Route, {
|
|
86
|
+
// // key: path,
|
|
87
|
+
// // path,
|
|
88
|
+
// // component: ({ params }: { params: Record<string, string> }) => {
|
|
89
|
+
|
|
90
|
+
// // renderRoute(container, () => h("div", { class: "p-8" }, h("h1", { class: "text-white text-xl relative top-2", style: {zIndex:'999', position:'relative', padding: '12px'} }, name), dd(params)), params)
|
|
91
|
+
// // // renderRoute(container, () => h("pre", { class: "p-8" }, h("h1", null, JSON.stringify(params))), params)
|
|
92
|
+
// // return null
|
|
93
|
+
// // },
|
|
94
|
+
// // })
|
|
95
|
+
// // ),
|
|
96
|
+
|
|
97
|
+
|
|
98
|
+
// h(Route, {
|
|
99
|
+
// path: this.has(location.pathname) ? location.pathname : '/',
|
|
100
|
+
// component: () => null,
|
|
101
|
+
// }),
|
|
102
|
+
|
|
103
|
+
// ...routes.map(([path, Component, defs, getProps]) =>
|
|
104
|
+
// h(Route, {
|
|
105
|
+
// // key: path,
|
|
106
|
+
// path,
|
|
107
|
+
// component: ({ params }: { params: Record<string, string> }) => {
|
|
108
|
+
// // Trigger async render (updates container imperatively to avoid
|
|
109
|
+
// // React/Preact hook rules issues with async components)
|
|
110
|
+
|
|
111
|
+
// renderRoute(container, Component, params, defs, getProps)
|
|
112
|
+
// // Return null โ renderRoute will call render() directly on the container
|
|
113
|
+
// return null
|
|
114
|
+
// },
|
|
115
|
+
// })
|
|
116
|
+
// ),
|
|
117
|
+
|
|
118
|
+
// ) )
|
|
119
|
+
|
|
120
|
+
|
|
121
|
+
// }
|
|
122
|
+
// } as WRouter
|
|
123
|
+
|
|
124
|
+
/** Captures wouter's navigate() so the duto:navigate event can call it */
|
|
125
|
+
// let _navigate: ((href: string) => void) | null = null
|
|
126
|
+
|
|
127
|
+
// โโโ Page renderer โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
|
|
128
|
+
|
|
129
|
+
function normalizeParams(
|
|
130
|
+
raw: Record<string, string>,
|
|
131
|
+
paramDefs: IRoute['params']
|
|
132
|
+
): Record<string, string | string[] | undefined> {
|
|
133
|
+
const result: Record<string, string | string[] | undefined> = {};
|
|
134
|
+
for (const def of paramDefs) {
|
|
135
|
+
const val = raw[def.name];
|
|
136
|
+
if (def.catchAll) {
|
|
137
|
+
result[def.name] = val
|
|
138
|
+
? val.split('/').filter(Boolean)
|
|
139
|
+
: def.optional ? undefined : []
|
|
140
|
+
} else {
|
|
141
|
+
result[def.name] = val === undefined && def.optional ? undefined : val;
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
return result
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
export async function renderRoute(
|
|
148
|
+
container: HTMLElement,
|
|
149
|
+
Component: Page,
|
|
150
|
+
rawParams: Record<string, string>,
|
|
151
|
+
paramDefs?: IRoute['params'],
|
|
152
|
+
getProps?: GetProps,
|
|
153
|
+
layouts?: Page[]
|
|
154
|
+
) {
|
|
155
|
+
while (document.body.firstChild)
|
|
156
|
+
document.body.removeChild(document.body.firstChild)
|
|
157
|
+
|
|
158
|
+
// const params = normalizeParams(rawParams, paramDefs)
|
|
159
|
+
// const mod = await route._import()
|
|
160
|
+
|
|
161
|
+
// let props: Record<string, unknown> = { ...params }
|
|
162
|
+
|
|
163
|
+
|
|
164
|
+
let props: Record<string, unknown> = paramDefs ? normalizeParams(rawParams, paramDefs) : rawParams
|
|
165
|
+
|
|
166
|
+
if (typeof getProps === 'function') {
|
|
167
|
+
try { // TODO: melhorar tipagem do props
|
|
168
|
+
const fetched = await Promise.resolve(getProps(props as Record<string, string | string[] | undefined>))
|
|
169
|
+
props = { ...fetched, ...props }
|
|
170
|
+
} catch (e) {
|
|
171
|
+
console.error('[ssg] getProps failed:', e)
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
let el = h(Component, props as any)
|
|
176
|
+
|
|
177
|
+
const _layouts = layouts ? (Array.isArray(layouts) ? layouts : [layouts]) : []
|
|
178
|
+
for (const layout of _layouts)
|
|
179
|
+
el = h(layout, null, el)
|
|
180
|
+
|
|
181
|
+
render(el, container)
|
|
182
|
+
|
|
183
|
+
// TODO: melhorar tipagem do props
|
|
184
|
+
// render(h(Component, props as any), container)
|
|
185
|
+
// render(h(Component, props as any), container)
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
|
|
189
|
+
|
|
190
|
+
|
|
191
|
+
|
|
192
|
+
|
|
193
|
+
|
|
194
|
+
|
|
195
|
+
// TODO: remover..
|
|
196
|
+
// function dd(objeto) {
|
|
197
|
+
// // Transforma o objeto em string JSON formatada com 2 espaรงos de recuo
|
|
198
|
+
// const json = JSON.stringify(objeto, null, 2);
|
|
199
|
+
|
|
200
|
+
// const renderizarJsonColorido = (textoJson) => {
|
|
201
|
+
// // Regex para capturar propriedades, strings, nรบmeros, booleanos, nulls E quebras de linha/espaรงos explicitamente
|
|
202
|
+
// const regex = /(\n|\r\n|\s+|"(\\u[a-zA-Z0-9]{4}|\\[^u]|[^\\"])*"(\s*:)?|\b(true|false|null)\b|-?\d+(?:\.\d*)?(?:[eE][+-]?\d+)?)/g;
|
|
203
|
+
// const partes = [];
|
|
204
|
+
// let ultimoIndex = 0;
|
|
205
|
+
|
|
206
|
+
// textoJson.replace(regex, (match, p1, offset) => {
|
|
207
|
+
// // Adiciona qualquer texto residual intermediรกrio
|
|
208
|
+
// if (offset > ultimoIndex) {
|
|
209
|
+
// partes.push(textoJson.substring(ultimoIndex, offset));
|
|
210
|
+
// }
|
|
211
|
+
|
|
212
|
+
// // Se capturar uma quebra de linha, insere a tag <br /> correspondente
|
|
213
|
+
// if (match.trim() === '' && (match.includes('\n') || match.includes('\r'))) {
|
|
214
|
+
// partes.push(h('br', null));
|
|
215
|
+
|
|
216
|
+
// // Se apรณs a quebra de linha houver espaรงos de indentaรงรฃo, renderiza como spans de espaรงo
|
|
217
|
+
// const espacos = match.replace(/[\n\r]/g, '');
|
|
218
|
+
// if (espacos.length > 0) {
|
|
219
|
+
// partes.push(h('span', { style: { whiteSpace: 'pre' } }, espacos));
|
|
220
|
+
// }
|
|
221
|
+
// }
|
|
222
|
+
// // Se for apenas espaรงos de recuo (sem quebra de linha)
|
|
223
|
+
// else if (match.trim() === '') {
|
|
224
|
+
// partes.push(h('span', { style: { whiteSpace: 'pre' } }, match));
|
|
225
|
+
// }
|
|
226
|
+
// // Se for um token de dado, aplica a colorizaรงรฃo do Laravel
|
|
227
|
+
// else {
|
|
228
|
+
// let estilo = { color: '#bd93f9' }; // Roxo para nรบmeros
|
|
229
|
+
|
|
230
|
+
// if (/^"/.test(match)) {
|
|
231
|
+
// if (/:$/.test(match)) {
|
|
232
|
+
// estilo = { color: '#ff79c6', fontWeight: 'bold' }; // Rosa para chaves (keys)
|
|
233
|
+
// } else {
|
|
234
|
+
// estilo = { color: '#f1fa8c' }; // Amarelo para strings
|
|
235
|
+
// }
|
|
236
|
+
// } else if (/true|false/.test(match)) {
|
|
237
|
+
// estilo = { color: '#8be9fd' }; // Ciano para booleanos
|
|
238
|
+
// } else if (/null/.test(match)) {
|
|
239
|
+
// estilo = { color: '#6272a4', fontStyle: 'italic' }; // Cinza para nulos
|
|
240
|
+
// }
|
|
241
|
+
|
|
242
|
+
// partes.push(h('span', { style: estilo }, match));
|
|
243
|
+
// }
|
|
244
|
+
|
|
245
|
+
// ultimoIndex = offset + match.length;
|
|
246
|
+
// });
|
|
247
|
+
|
|
248
|
+
// if (ultimoIndex < textoJson.length) {
|
|
249
|
+
// partes.push(textoJson.substring(ultimoIndex));
|
|
250
|
+
// }
|
|
251
|
+
|
|
252
|
+
// return partes;
|
|
253
|
+
// };
|
|
254
|
+
|
|
255
|
+
// // Estilo do container escuro em tela cheia
|
|
256
|
+
// const estiloContainer = {
|
|
257
|
+
// background: '#1e1e24', // Grafite escuro elegante
|
|
258
|
+
// color: '#f8f8f2',
|
|
259
|
+
// padding: '30px',
|
|
260
|
+
// fontFamily: '"Fira Code", "Courier New", Courier, monospace',
|
|
261
|
+
// fontSize: '14px',
|
|
262
|
+
// lineHeight: '1.6',
|
|
263
|
+
// overflow: 'auto',
|
|
264
|
+
// position: 'fixed',
|
|
265
|
+
// top: 0,
|
|
266
|
+
// left: 0,
|
|
267
|
+
// width: '100vw',
|
|
268
|
+
// height: '100vh',
|
|
269
|
+
// zIndex: 99,
|
|
270
|
+
// textAlign: 'left',
|
|
271
|
+
// boxSizing: 'border-box'
|
|
272
|
+
// };
|
|
273
|
+
|
|
274
|
+
// // Retorna a estrutura h() contendo o bloco quebrado e indentado linha por linha
|
|
275
|
+
// return h('div', { style: estiloContainer }, renderizarJsonColorido(json));
|
|
276
|
+
// }
|
|
277
|
+
|
|
278
|
+
|
|
279
|
+
|
|
280
|
+
|
|
281
|
+
|
|
282
|
+
|
|
283
|
+
|
|
284
|
+
|
|
285
|
+
|
|
286
|
+
|
|
287
|
+
// function normalizeParams(
|
|
288
|
+
// raw: Record<string, string>,
|
|
289
|
+
// paramDefs: IRoute['params']
|
|
290
|
+
// ): Record<string, string | string[] | undefined> {
|
|
291
|
+
// const result: Record<string, string | string[] | undefined> = {};
|
|
292
|
+
// for (const def of paramDefs) {
|
|
293
|
+
// const val = raw[def.name];
|
|
294
|
+
// if (def.catchAll) {
|
|
295
|
+
// result[def.name] = val
|
|
296
|
+
// ? val.split('/').filter(Boolean)
|
|
297
|
+
// : def.optional ? undefined : []
|
|
298
|
+
// } else {
|
|
299
|
+
// result[def.name] = val === undefined && def.optional ? undefined : val;
|
|
300
|
+
// }
|
|
301
|
+
// }
|
|
302
|
+
// return result
|
|
303
|
+
// }
|
|
304
|
+
|
|
305
|
+
// async function renderRoute(
|
|
306
|
+
// route: IRoute,
|
|
307
|
+
// rawParams: Record<string, string>,
|
|
308
|
+
// container: HTMLElement
|
|
309
|
+
// ) {
|
|
310
|
+
// const params = normalizeParams(rawParams, route.params)
|
|
311
|
+
// const mod = await route._import()
|
|
312
|
+
|
|
313
|
+
// let props: Record<string, unknown> = { ...params }
|
|
314
|
+
|
|
315
|
+
// if (typeof mod.getProps === 'function') {
|
|
316
|
+
// try {
|
|
317
|
+
// const fetched = await Promise.resolve(mod.getProps(params))
|
|
318
|
+
// props = { ...fetched, ...params }
|
|
319
|
+
// } catch (e) {
|
|
320
|
+
// console.error('[ssg] getProps failed:', e)
|
|
321
|
+
// }
|
|
322
|
+
// } else {
|
|
323
|
+
// // No getProps โ re-use SSG props (already in the DOM, nothing to do on first load)
|
|
324
|
+
// // props = { ...(window.__SSG_PROPS__ ?? {}), ...params }
|
|
325
|
+
// }
|
|
326
|
+
|
|
327
|
+
// render(h(mod.default, props as any), container)
|
|
328
|
+
// }
|
|
329
|
+
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
import { emit, on } from 't0n'
|
|
2
|
+
|
|
3
|
+
interface IStoreChange<TValue> {
|
|
4
|
+
key: string,
|
|
5
|
+
val: TValue | null,
|
|
6
|
+
old: TValue | null,
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
type IStoreListener<TValue> = (change: IStoreChange<TValue>) => void
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Store reativo que encapsula o localStorage.
|
|
13
|
+
*
|
|
14
|
+
* - Nenhum componente toca em `window.localStorage` diretamente, sรณ nesta classe.
|
|
15
|
+
* - Mudanรงas feitas via `.set()`/`.remove()` disparam eventos na mesma aba.
|
|
16
|
+
* - Mudanรงas feitas em outras abas chegam via evento nativo `storage` e sรฃo
|
|
17
|
+
* normalizadas para o mesmo formato de evento.
|
|
18
|
+
* - Chaves e valores sรฃo tipados a partir de um schema (`TSchema`), evitando
|
|
19
|
+
* strings soltas e `JSON.parse`/`JSON.stringify` espalhados pelo cรณdigo.
|
|
20
|
+
*/
|
|
21
|
+
class LocalStore<TSchema extends Record<string, unknown>> extends EventTarget {
|
|
22
|
+
#prefix: string
|
|
23
|
+
|
|
24
|
+
constructor(prefix = '') {
|
|
25
|
+
super()
|
|
26
|
+
this.#prefix = prefix
|
|
27
|
+
|
|
28
|
+
if (typeof window !== 'undefined')
|
|
29
|
+
window.addEventListener('storage', this.#onCrossTabChange)
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
#buildKey<K extends keyof TSchema & string>(key: K): string {
|
|
33
|
+
return this.#prefix ? `${this.#prefix}:${key}` : key
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
#parse<TValue>(raw: string | null): TValue | null {
|
|
37
|
+
if (raw === null) return null
|
|
38
|
+
|
|
39
|
+
try {
|
|
40
|
+
return JSON.parse(raw) as TValue
|
|
41
|
+
} catch {
|
|
42
|
+
return raw as unknown as TValue
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
#onCrossTabChange = (e: StorageEvent): void => {
|
|
47
|
+
if (!e.key) return
|
|
48
|
+
|
|
49
|
+
const hasPrefix = this.#prefix.length > 0
|
|
50
|
+
const expectedPrefix = `${this.#prefix}:`
|
|
51
|
+
|
|
52
|
+
if (hasPrefix && !e.key.startsWith(expectedPrefix)) return
|
|
53
|
+
|
|
54
|
+
const key = hasPrefix ? e.key.slice(expectedPrefix.length) : e.key
|
|
55
|
+
const val = this.#parse<TSchema[keyof TSchema]>(e.newValue)
|
|
56
|
+
const old = this.#parse<TSchema[keyof TSchema]>(e.oldValue)
|
|
57
|
+
|
|
58
|
+
emit(key, { key, val, old })
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
get<K extends keyof TSchema & string>(key: K): TSchema[K] | null {
|
|
62
|
+
if (typeof window === 'undefined') return null
|
|
63
|
+
|
|
64
|
+
return this.#parse<TSchema[K]>(window?.localStorage?.getItem(this.#buildKey(key)))
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
set<K extends keyof TSchema & string>(key: K, val: TSchema[K]): void {
|
|
68
|
+
if (typeof window === 'undefined') return
|
|
69
|
+
|
|
70
|
+
const old = this.get(key)
|
|
71
|
+
window?.localStorage?.setItem(this.#buildKey(key), JSON.stringify(val))
|
|
72
|
+
|
|
73
|
+
emit(key, { key, val, old })
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
remove<K extends keyof TSchema & string>(key: K): void {
|
|
77
|
+
if (typeof window === 'undefined') return
|
|
78
|
+
|
|
79
|
+
const old = this.get(key)
|
|
80
|
+
window?.localStorage?.removeItem(this.#buildKey(key))
|
|
81
|
+
emit(key, { key, val: null, old })
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
subscribe<K extends keyof TSchema & string>(
|
|
85
|
+
key: K,
|
|
86
|
+
listener: IStoreListener<TSchema[K]>,
|
|
87
|
+
) {
|
|
88
|
+
return on(key, listener)
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
export const local = new LocalStore()
|
|
93
|
+
|
|
94
|
+
export { LocalStore }
|
|
95
|
+
export type { IStoreChange }
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
import { build } from 'vite'
|
|
2
|
+
import { filesize } from 'filesize'
|
|
3
|
+
import { command } from 't0n/cli'
|
|
4
|
+
import { dim, underline } from 't0n/color'
|
|
5
|
+
import { wait, error, rn, event } from 't0n/log'
|
|
6
|
+
import { meta } from '@/vite-plugin'
|
|
7
|
+
import { getConfig } from '~/utils'
|
|
8
|
+
import type { SummaryMetadata } from '@/types'
|
|
9
|
+
|
|
10
|
+
export default command({
|
|
11
|
+
meta: {
|
|
12
|
+
name: 'build',
|
|
13
|
+
description: '๐๏ธ Perform the build for production\n',
|
|
14
|
+
},
|
|
15
|
+
args: {
|
|
16
|
+
},
|
|
17
|
+
async run({ args }) {
|
|
18
|
+
global.__hmr = !!args?.hmr
|
|
19
|
+
wait('Building..')
|
|
20
|
+
|
|
21
|
+
try {
|
|
22
|
+
await build(await getConfig())
|
|
23
|
+
const st = global?.__st ? Math.ceil(performance.now() - global.__st) : 0
|
|
24
|
+
const metadata = meta()
|
|
25
|
+
|
|
26
|
+
const trunc = (str: string, length: number, width = 35) => length > width ? str.slice(0, width - 3) + '...' : str + (length < width ? ' '.repeat(width - length) : '')
|
|
27
|
+
|
|
28
|
+
const format = (row: SummaryMetadata, i: number, max: number, deep: boolean = false) => {
|
|
29
|
+
let str = (deep ? (i === max - 1 ? 'โ' : 'โ') : (i === 0 ? 'โ' : (i === max - 1 ? 'โ' : 'โ'))) + ' '
|
|
30
|
+
|
|
31
|
+
if (!deep) str += (['โ', 'โ', 'โพ'][row.type] || 'โ') + ' '
|
|
32
|
+
const size = filesize(row.sizeBr || 0)
|
|
33
|
+
return dim(str + [row.path, size + ' '.repeat(10 - size.length) + filesize(row.size || 0)].map(col => trunc(col, col.length, deep ? 33 : 35)).join(''))
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
rn()
|
|
37
|
+
console.log(' '+ ['Route', 'Size'].map(col => trunc(dim(underline(col)), col.length)).join(''))
|
|
38
|
+
|
|
39
|
+
let i = 0
|
|
40
|
+
const max = metadata.summary.size
|
|
41
|
+
for (const [, row] of metadata.summary) {
|
|
42
|
+
console.log(format(row, i, max))
|
|
43
|
+
|
|
44
|
+
if (row.children?.size) {
|
|
45
|
+
let i2 = 0
|
|
46
|
+
const max2 = row.children.size
|
|
47
|
+
for (const [, r] of row.children) {
|
|
48
|
+
console.log(dim('โ ')+ format(r, i2, max2, true))
|
|
49
|
+
i2++
|
|
50
|
+
|
|
51
|
+
if (i2 > 3 && max2 > 4) {
|
|
52
|
+
console.log(dim(` [+${max2 - i2} more paths]`))
|
|
53
|
+
break
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
i++
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
rn(2)
|
|
61
|
+
console.log(dim('โ [Static] prerendered as static HTML'))
|
|
62
|
+
console.log(dim('โ [SSG] prerendered as static HTML (uses getStaticProps)'))
|
|
63
|
+
console.log(dim('โพ [CSR] rendered on client side (uses getProps)'))
|
|
64
|
+
rn()
|
|
65
|
+
event(`Builded successfully${st ? dim(` in ${st} ms`) : ''}`)
|
|
66
|
+
} catch (e: any) {
|
|
67
|
+
error(e)
|
|
68
|
+
// error('Build failed:', e?.message || e)
|
|
69
|
+
process.exit(0)
|
|
70
|
+
} finally {
|
|
71
|
+
rn()
|
|
72
|
+
}
|
|
73
|
+
},
|
|
74
|
+
})
|