duto 0.0.0 → 0.260808.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.
@@ -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,36 @@
1
+ import { join, resolve } from 'node:path'
2
+ import { readFileSync } from 'node:fs'
3
+ import { resolveExtension } from './file'
4
+
5
+ export function getTsConfig(configPath = './tsconfig.json') {
6
+ const content = readFileSync(configPath, 'utf8')
7
+ return JSON.parse(content.replace(/\/\/.*$/gm, '').replace(/\/\*[\s\S]*?\*\//g, ''))
8
+ }
9
+
10
+ export function getAliases(root: string) {
11
+ const tsconfig = getTsConfig(join(root, './tsconfig.json'))
12
+ const compilerOptions = tsconfig.compilerOptions || {}
13
+ const paths = Object.entries(compilerOptions.paths || {})
14
+ const baseUrl = compilerOptions.baseUrl || '.'
15
+
16
+ const aliases = {}
17
+
18
+ for (const [aliasPattern, targetPatterns] of paths) // @ts-ignore
19
+ aliases[aliasPattern.replace(/\/\*$/, '')] = resolve(process.cwd(), baseUrl, targetPatterns[0].replace(/\/\*$/, ''))
20
+
21
+ return aliases
22
+ }
23
+
24
+ export function resolveAlias(importPath: string, aliases: Record<string, string>) {
25
+ for (const [alias, realPath] of Object.entries(aliases)) {
26
+ if (importPath.startsWith(alias +'/') || importPath === alias) {
27
+ const subPath = importPath.substring(alias.length)
28
+ const cleanSubPath = subPath.startsWith('/') ? subPath.substring(1) : subPath
29
+ const resolved = join(realPath, cleanSubPath)
30
+
31
+ return resolveExtension(resolved) ?? resolved
32
+ }
33
+ }
34
+
35
+ return resolveExtension(importPath) ?? importPath
36
+ }
@@ -0,0 +1,43 @@
1
+ const distMap: Record<string, string> = {}
2
+
3
+ // export function getDist(){
4
+ // return distMap
5
+ // }
6
+ export function getAsset(id: string) {
7
+ const entry = getChunk(id, 1)
8
+ return distMap['\0'+ entry] || null
9
+ }
10
+ export function setAsset(id: string | null, val: string) {
11
+ distMap['\0'+ normalizeId(id)] = val
12
+ }
13
+
14
+ // corririr isso...
15
+ export function getChunk(id: string, deep: number = 3, loop: number = 1) {
16
+ if (!distMap[id]) return id
17
+
18
+ const currentValue = distMap[id]
19
+
20
+ if (loop < deep && distMap[currentValue] && currentValue !== id)
21
+ return getChunk(currentValue, deep, loop + 1)
22
+
23
+ return currentValue
24
+ }
25
+ export function setChunk(id: string | null, val: string) {
26
+ distMap[normalizeId(id)] = val
27
+ }
28
+
29
+ export function getEntry(id: string) {
30
+ if (id in distMap) return distMap[id]
31
+ const key = id in distMap ? id : normalizeId(id)
32
+ return distMap[key] || id
33
+ }
34
+ export function setEntry(id: string | null, val: string) {
35
+ if (!id) return
36
+ distMap[id] = normalizeId(val)
37
+ }
38
+
39
+ export function normalizeId(id?: string | null) {
40
+ id = id?.replace(process.cwd(), '') || ''
41
+ if (id && !id.startsWith('/')) id = '/'+ id
42
+ return id
43
+ }