duto 0.261001.0 → 0.261012.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/package.json +22 -26
- package/src/commands/templates/pages/components/html.tsx +55 -0
- package/src/commands/templates/pages/components/theme-select.tsx +60 -0
- package/src/commands/templates/pages/i18n/en.ts +6 -0
- package/src/commands/templates/pages/i18n/pt-br.ts +6 -0
- package/src/commands/templates/pages/pages/404.tsx +1 -1
- package/src/commands/templates/pages/pages/blog/[slug].tsx +88 -0
- package/src/commands/templates/pages/pages/blog/index.tsx +74 -0
- package/src/commands/templates/pages/pages/index.tsx +17 -7
- package/src/commands/templates/pages/pages/layout.tsx +5 -3
- package/src/commands/templates/pages/public/favicon.ico +0 -0
- package/src/commands/templates/pages/public/favicon.svg +1 -0
- package/src/commands/templates/pages/style.css +110 -7
- package/src/commands/templates/pages/tsconfig.json +2 -0
- package/src/pages/cli/commands/build.ts +1 -2
- package/src/pages/cli/utils.ts +8 -6
- package/src/pages/client/app.tsx +177 -28
- package/src/pages/client/link.ts +15 -41
- package/src/pages/client/storage.ts +1 -1
- package/src/pages/client/theme.ts +49 -0
- package/src/pages/compiler/ast.ts +13 -0
- package/src/pages/compiler/bun-plugin.ts +245 -0
- package/src/pages/compiler/constants.ts +2 -0
- package/src/pages/compiler/index.ts +44 -23
- package/src/pages/compiler/meta.ts +7 -31
- package/src/pages/compiler/types.ts +15 -25
- package/src/pages/components/link.tsx +11 -19
- package/src/pages/i18n/client.ts +3 -4
- package/src/pages/index.ts +4 -21
- package/src/server/app.ts +2 -1
- package/src/pages/client/router.ts +0 -63
- package/src/pages/compiler/vite-plugin.ts +0 -345
|
@@ -7,32 +7,32 @@ import { h } from 'preact'
|
|
|
7
7
|
import { render } from 'preact-render-to-string'
|
|
8
8
|
import minify from '@minify-html/node'
|
|
9
9
|
|
|
10
|
-
import { JSJSON } from 't0n'
|
|
11
10
|
import { DOCTYPE_RE } from './constants'
|
|
12
11
|
import { getAsset, getChunk } from './assets'
|
|
13
|
-
import {
|
|
12
|
+
import { resolveStaticOutputPath } from './meta'
|
|
14
13
|
import { setLocale } from '../i18n/client'
|
|
15
14
|
|
|
16
|
-
import type { ResolvedConfig } from 'vite'
|
|
17
15
|
import type { IRoute, Metadata, RouteParams, StaticProps, SummaryMetadata } from './types'
|
|
16
|
+
import type { DutoOptions } from './bun-plugin'
|
|
18
17
|
|
|
19
18
|
|
|
20
19
|
const brCompress = promisify(brotliCompress)
|
|
21
20
|
|
|
22
21
|
export async function renderPage(
|
|
23
22
|
meta: Metadata,
|
|
24
|
-
config:
|
|
23
|
+
config: DutoOptions,
|
|
25
24
|
page: IRoute
|
|
26
25
|
) {
|
|
27
26
|
const {
|
|
28
27
|
mod,
|
|
28
|
+
layouts,
|
|
29
29
|
hasGetProps, hasGetStaticProps,
|
|
30
|
-
|
|
30
|
+
useClient,
|
|
31
31
|
} = page
|
|
32
32
|
if (!mod?.default)
|
|
33
33
|
return console.warn(`No default export in ${page.filePath} — skipping`)
|
|
34
34
|
|
|
35
|
-
if (page.isInternal || page.is404 || (page.isDynamic && !hasGetStaticProps))
|
|
35
|
+
if (page.isInternal || (!page.useRaw && !page.useHtml && page.is404) || (page.isDynamic && !hasGetStaticProps))
|
|
36
36
|
return meta.summary.set('/'+ page.fileName, { type: 2, path: '/'+ page.fileName, size: 0, sizeBr: 0, sizeJS: 0 })
|
|
37
37
|
|
|
38
38
|
const { filePath, pattern, params } = page
|
|
@@ -70,44 +70,54 @@ export async function renderPage(
|
|
|
70
70
|
props.push({ routeParams: {}, pageProps: {} })
|
|
71
71
|
}
|
|
72
72
|
|
|
73
|
-
const
|
|
74
|
-
|
|
73
|
+
const sProps = []
|
|
75
74
|
for (const prop of props) {
|
|
76
75
|
const allProps = { ...prop.pageProps, ...prop.routeParams }
|
|
76
|
+
Object.keys(allProps).length && sProps.push(allProps)
|
|
77
77
|
|
|
78
78
|
// @ts-ignore
|
|
79
|
-
allProps?.locale && setLocale(allProps?.locale || meta.i18n.fallbackLocale)
|
|
79
|
+
allProps?.locale && await setLocale(allProps?.locale || meta.i18n.fallbackLocale)
|
|
80
80
|
|
|
81
81
|
const pathname = resolveStaticOutputPath(page, prop.routeParams)
|
|
82
|
+
|
|
83
|
+
// Shims
|
|
82
84
|
// TODO: melhorar isso para impedir erros do wouter e afins
|
|
83
85
|
// @ts-ignore
|
|
84
86
|
global.location = { pathname: '/'+ (pathname === 'index' ? '' : pathname) }
|
|
87
|
+
global.location.href = global.location.pathname
|
|
85
88
|
// @ts-ignore
|
|
86
|
-
global.window = {
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
89
|
+
global.window = {
|
|
90
|
+
location: global.location,
|
|
91
|
+
$duto: {
|
|
92
|
+
navigate: {},
|
|
93
|
+
route: { pattern: page.pattern, params: prop.routeParams, path: global.location.pathname },
|
|
94
|
+
}
|
|
95
|
+
}
|
|
91
96
|
|
|
92
97
|
let html = ''
|
|
93
98
|
|
|
94
|
-
|
|
99
|
+
if (!useClient) {
|
|
95
100
|
try {
|
|
96
101
|
let el = h(mod.default, allProps as any)
|
|
97
102
|
for (const layout of layouts)
|
|
98
103
|
el = h(layout.mod.default, null, el)
|
|
99
104
|
|
|
105
|
+
// @ts-ignore
|
|
106
|
+
const shell = mod?.Html || mod?.html || mod?.HTML
|
|
107
|
+
// @ts-ignore
|
|
108
|
+
if (shell) el = h(shell, allProps, el)
|
|
109
|
+
|
|
100
110
|
html = render(el)
|
|
101
111
|
} catch (e) {
|
|
102
112
|
console.error(` SSR render failed for ${filePath}: ${e}`)
|
|
103
113
|
console.warn(e)
|
|
104
114
|
html = ''
|
|
105
115
|
}
|
|
106
|
-
|
|
116
|
+
}
|
|
107
117
|
|
|
108
|
-
html = buildHtml(html, Object.keys(allProps).length ? allProps : undefined)
|
|
118
|
+
html = buildHtml(page, html, Object.keys(allProps).length ? allProps : undefined)
|
|
109
119
|
|
|
110
|
-
const outFile = join(config.root, config.
|
|
120
|
+
const outFile = join(config.root, config.outDir, pathname +'.html')
|
|
111
121
|
const outHTML = meta.minify ? minify.minify(Buffer.from(html), {}) : html
|
|
112
122
|
await mkdir(dirname(outFile), {recursive: true})
|
|
113
123
|
await Bun.write(outFile, outHTML)
|
|
@@ -144,16 +154,22 @@ export async function renderPage(
|
|
|
144
154
|
sizeJS: 0,
|
|
145
155
|
})
|
|
146
156
|
}
|
|
147
|
-
|
|
148
157
|
}
|
|
158
|
+
|
|
159
|
+
if (sProps.length)
|
|
160
|
+
page.props = sProps.length < 2 ? sProps[0] : sProps
|
|
149
161
|
}
|
|
150
162
|
|
|
151
163
|
function buildHtml(
|
|
164
|
+
page: IRoute,
|
|
152
165
|
html: string,
|
|
153
|
-
props
|
|
166
|
+
props: StaticProps = {},
|
|
154
167
|
): string {
|
|
168
|
+
if (page.useRaw) return html
|
|
155
169
|
if (!(html.includes('<body') || html.includes('<html') || html.includes('<head')))
|
|
156
170
|
html = wrapHtml(html)
|
|
171
|
+
else
|
|
172
|
+
html = html.replace('<body', '<body hidden')
|
|
157
173
|
|
|
158
174
|
if (!DOCTYPE_RE.test(html))
|
|
159
175
|
html = '<!DOCTYPE html>'+ html
|
|
@@ -164,7 +180,8 @@ function buildHtml(
|
|
|
164
180
|
|
|
165
181
|
const prepend = assets.map(asset => {
|
|
166
182
|
const href = getAsset(asset)
|
|
167
|
-
return href ? `<link href="${href}"
|
|
183
|
+
// return href ? `<link rel="stylesheet" href="${href}">` : null
|
|
184
|
+
return href ? `<link rel="preload" href="${href}" as="style" onload="this.rel='stylesheet'">` : null
|
|
168
185
|
}).filter(Boolean).join('')
|
|
169
186
|
|
|
170
187
|
if (html.includes('</head>')) {
|
|
@@ -175,9 +192,13 @@ function buildHtml(
|
|
|
175
192
|
html = prepend + html
|
|
176
193
|
}
|
|
177
194
|
|
|
178
|
-
|
|
195
|
+
if (page.useHtml) props.__raw = true
|
|
196
|
+
|
|
197
|
+
// const append = (Object.keys(props).length ? `<script>(self.$duto=self.$duto||{}).props=${JSJSON(props)}</script>` : '') + assets.map(file => {
|
|
198
|
+
const append = assets.map(file => {
|
|
179
199
|
const src = getChunk(file)
|
|
180
|
-
return src ? `<script src="${src}"
|
|
200
|
+
return src ? `<script type=module src="${src}"></script>` : null
|
|
201
|
+
// return src ? `<script type=module src="${src}"${(Object.keys(props).length ? ` props='${JSON.stringify(props)}'` : '')}></script>` : null
|
|
181
202
|
}).filter(Boolean).join('')
|
|
182
203
|
|
|
183
204
|
if (html.includes('</body>'))
|
|
@@ -1,15 +1,9 @@
|
|
|
1
|
-
// https://github.com/cloudflare/vinext/blob/main/packages/vinext/src/routing/utils.ts
|
|
2
|
-
|
|
3
|
-
// https://github.com/cloudflare/vinext/blob/adfc2236cba5f7cde63f32f6fb35c0d892428376/packages/vinext/src/build/prerender.ts#L203
|
|
4
|
-
// https://github.com/cloudflare/vinext/blob/main/packages/vinext/src/entries/pages-client-entry.ts
|
|
5
|
-
// https://github.com/cloudflare/vinext/blob/main/packages/vinext/src/shims/ALS-ARCHITECTURE.md
|
|
6
|
-
|
|
7
1
|
import { dirname, extname } from 'node:path'
|
|
8
2
|
|
|
9
3
|
import { getEnv, JSJSON, newID } from 't0n'
|
|
10
4
|
|
|
11
5
|
import { getAliases } from './aliases'
|
|
12
|
-
import { DYNAMIC_RE, INTERNAL_RE, LAYOUT_RE, NF_NAMES, FILE_EXTENSIONS } from './constants'
|
|
6
|
+
import { DYNAMIC_RE, INTERNAL_RE, LAYOUT_RE, NF_NAMES, FILE_EXTENSIONS, USE_RAW_RE, USE_HTML_RE } from './constants'
|
|
13
7
|
import { scan } from '@/utils'
|
|
14
8
|
|
|
15
9
|
import { USE_CLIENT_RE } from './constants'
|
|
@@ -23,13 +17,10 @@ import type {
|
|
|
23
17
|
SummaryMetadata,
|
|
24
18
|
} from './types'
|
|
25
19
|
|
|
26
|
-
|
|
27
|
-
// TODO: https://github.com/cloudflare/vinext/blob/adfc2236cba5f7cde63f32f6fb35c0d892428376/packages/vinext/src/routing/file-matcher.ts#L90
|
|
28
20
|
export async function getPages(meta: Metadata) {
|
|
29
21
|
const nf = new Map<string, IRoute>()
|
|
30
22
|
const layouts = new Map<string, IRoute>()
|
|
31
23
|
const pages = new Map<string, IRoute>()
|
|
32
|
-
const spages = new Set<string>()
|
|
33
24
|
const routes: IRoute[] = []
|
|
34
25
|
|
|
35
26
|
for await (const [file, path, mod] of scan(meta.dir, FILE_EXTENSIONS)) {
|
|
@@ -59,7 +50,9 @@ export async function getPages(meta: Metadata) {
|
|
|
59
50
|
layouts: [],
|
|
60
51
|
hasGetStaticProps: typeof mod.getStaticProps === 'function',
|
|
61
52
|
hasGetProps: typeof mod.getProps === 'function',
|
|
62
|
-
|
|
53
|
+
useClient: USE_CLIENT_RE.test(content),
|
|
54
|
+
useRaw: USE_RAW_RE.test(content),
|
|
55
|
+
useHtml: USE_HTML_RE.test(content),
|
|
63
56
|
}
|
|
64
57
|
|
|
65
58
|
if (LAYOUT_RE.test(path)) {
|
|
@@ -77,31 +70,13 @@ export async function getPages(meta: Metadata) {
|
|
|
77
70
|
if (!page.isInternal)
|
|
78
71
|
pages.set(file, page)
|
|
79
72
|
|
|
80
|
-
if (!page.isInternal)
|
|
73
|
+
if (!page.isInternal && !page.useRaw && !page.useHtml)
|
|
81
74
|
routes.push(page)
|
|
82
|
-
|
|
83
|
-
if (!page.isInternal) {
|
|
84
|
-
if (page.isDynamic && page.hasGetStaticProps) {
|
|
85
|
-
try {
|
|
86
|
-
const _props = await page.mod.getStaticProps?.()
|
|
87
|
-
const props = Array.isArray(_props) ? _props : [_props]
|
|
88
|
-
for (const prop of props)
|
|
89
|
-
spages.add('/'+resolveStaticOutputPath(page, prop as any))
|
|
90
|
-
|
|
91
|
-
} catch(e) {
|
|
92
|
-
console.log('errro', e)
|
|
93
|
-
}
|
|
94
|
-
} else if (!page.isDynamic) {
|
|
95
|
-
spages.add(page.pattern)
|
|
96
|
-
}
|
|
97
|
-
}
|
|
98
|
-
|
|
99
75
|
}
|
|
100
76
|
|
|
101
77
|
meta.nf = nf
|
|
102
78
|
meta.layouts = layouts
|
|
103
79
|
meta.pages = pages
|
|
104
|
-
meta.spages = spages
|
|
105
80
|
meta.routes = await sortRoutes(meta, routes)
|
|
106
81
|
}
|
|
107
82
|
|
|
@@ -140,7 +115,6 @@ ${Array.from(meta.layouts.values()).map(l => `\nimport Layout${l.importName} fro
|
|
|
140
115
|
|
|
141
116
|
export const views = [${meta.routes.map(r => `['${r.pattern}',${r.is404 ? 'Page'+ r.importName : `'${r.fullFilePath}'`},${Object.keys(r.params).length ? JSJSON(Object.entries(r.params).map(([_,v]) => v)) : '0'}${r.hasGetProps ? (r.is404 ? `,getProps${r.importName}` : `,1`) : (r.layouts.length ? ',0': '')}${r.layouts.length ? ',[' + Object.entries(r.layouts).map(([_,l]) => 'Layout'+l.importName) +']' : ''}]`).join(',')}]
|
|
142
117
|
|
|
143
|
-
export const SRoutes = new Set(${JSJSON([...meta.spages])})
|
|
144
118
|
`
|
|
145
119
|
|
|
146
120
|
return meta
|
|
@@ -153,6 +127,8 @@ export const SRoutes = new Set(${JSJSON([...meta.spages])})
|
|
|
153
127
|
|
|
154
128
|
|
|
155
129
|
// //////////////////////////////////////////////////////////////////////////////////////////
|
|
130
|
+
// https://github.com/cloudflare/vinext/blob/main/packages/vinext/src/routing/utils.ts
|
|
131
|
+
|
|
156
132
|
type RouteMeta = {
|
|
157
133
|
staticSegments: number,
|
|
158
134
|
dynamicSegments: number,
|
|
@@ -1,10 +1,8 @@
|
|
|
1
|
-
// import type { ComponentType, FunctionComponent, VNode } from 'preact'
|
|
2
|
-
import type { Platform } from '$/cli/types'
|
|
3
1
|
import type { FunctionComponent, VNode } from 'preact'
|
|
2
|
+
import type { Platform } from '$/cli/types'
|
|
4
3
|
|
|
5
4
|
export type PluginOptions = {
|
|
6
5
|
root?: string,
|
|
7
|
-
// Directory to scan for pages, relative to Vite root. Default: "./pages"
|
|
8
6
|
pagesDir?: string,
|
|
9
7
|
}
|
|
10
8
|
|
|
@@ -43,8 +41,7 @@ export type Metadata = {
|
|
|
43
41
|
aliases: Record<string, string>,
|
|
44
42
|
layouts: Map<string, IRoute>, // all layouts
|
|
45
43
|
nf: Map<string, IRoute>, // all 404
|
|
46
|
-
pages: Map<string, IRoute>, // all
|
|
47
|
-
spages: Set<string>, // ssg pages
|
|
44
|
+
pages: Map<string, IRoute>, // all pages
|
|
48
45
|
routes: Routes, // client-side
|
|
49
46
|
// routes: Map<string, IRoute>, // client-side
|
|
50
47
|
i18n: I18nMetadata,
|
|
@@ -64,15 +61,17 @@ export type PageModule<P = {}> = {
|
|
|
64
61
|
* getStaticProps — build-only.
|
|
65
62
|
* For static pages (no dynamic segments): returns a single props object.
|
|
66
63
|
* For dynamic pages: returns an array of { [routeParam]: value, ...props }
|
|
67
|
-
*
|
|
68
64
|
*/
|
|
69
65
|
export type GetStaticProps =
|
|
70
66
|
| (() => StaticProps | Promise<StaticProps>)
|
|
71
67
|
| (() => StaticProps[] | Promise<StaticProps[]>)
|
|
72
68
|
export type StaticProps = Record<string, unknown>
|
|
73
69
|
|
|
74
|
-
export type GetProps = (params: RouteParams) => StaticProps | Promise<StaticProps>
|
|
75
70
|
export type RouteParams = Record<string, string | string[] | undefined>
|
|
71
|
+
export type GetProps = (params: RouteParams) => StaticProps | Promise<StaticProps>
|
|
72
|
+
export type Params = Record<string, RouteParams>
|
|
73
|
+
|
|
74
|
+
export type View = [string, Page, Params | 0, GetProps | 0 | 1 | null, Page[]]
|
|
76
75
|
|
|
77
76
|
export type Routes = IRoute[]
|
|
78
77
|
export type IRoute = {
|
|
@@ -80,34 +79,25 @@ export type IRoute = {
|
|
|
80
79
|
mod: PageModule,
|
|
81
80
|
hasGetStaticProps: boolean,
|
|
82
81
|
hasGetProps: boolean,
|
|
83
|
-
|
|
82
|
+
useClient: boolean,
|
|
83
|
+
useRaw: boolean,
|
|
84
|
+
useHtml: boolean,
|
|
84
85
|
layouts: IRoute[],
|
|
85
86
|
importName: string,
|
|
86
87
|
|
|
88
|
+
props?: StaticProps,
|
|
89
|
+
|
|
87
90
|
////////////////////////////
|
|
88
91
|
|
|
89
92
|
fileExt: string,
|
|
90
93
|
fileName: string,
|
|
91
|
-
|
|
92
|
-
filePath: string,
|
|
94
|
+
filePath: string, // Original file path relative to pages dir, e.g. `blog/[slug].tsx`
|
|
93
95
|
fullFilePath: string,
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
/** Params extracted from the path segments */
|
|
97
|
-
params: Record<string, RouteParam>,
|
|
98
|
-
// params: RouteParam[],
|
|
99
|
-
/**
|
|
100
|
-
* Similar to the "params" field, but with more associated metadata. For example, for `/site/[blog]/[...slug].tsx`, the segments are:
|
|
101
|
-
*
|
|
102
|
-
* 1. `{ content: 'site', dynamic: false, spread: false }`
|
|
103
|
-
* 2. `{ content: 'blog', dynamic: true, spread: false }`
|
|
104
|
-
* 3. `{ content: '...slug', dynamic: true, spread: true }`
|
|
105
|
-
*/
|
|
106
|
-
// segments: RoutePart[][]; // alternativa ao "params"
|
|
96
|
+
pattern: string, // Wouter-compatible pattern, e.g. `/blog/:slug`
|
|
97
|
+
params: Params,
|
|
107
98
|
is404: boolean,
|
|
108
|
-
|
|
99
|
+
|
|
109
100
|
isDynamic: boolean,
|
|
110
|
-
/** True if this route are internal */
|
|
111
101
|
isInternal: boolean,
|
|
112
102
|
}
|
|
113
103
|
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { useLocation } from 'wouter-preact'
|
|
2
2
|
import type { LinkProps } from 'wouter-preact'
|
|
3
3
|
|
|
4
4
|
const isExternalUrl = (href?: string) =>
|
|
@@ -12,27 +12,19 @@ export const Link = ({
|
|
|
12
12
|
href = to,
|
|
13
13
|
...rest
|
|
14
14
|
}: LinkProps) => {
|
|
15
|
-
|
|
16
|
-
const isSRoute = window?.$duto?.sroutes?.has(href)
|
|
17
|
-
|
|
18
|
-
if (isExternal) {
|
|
15
|
+
if (isExternalUrl(rest.href = href)) {
|
|
19
16
|
rest.target = '_blank'
|
|
20
17
|
rest.rel = 'noopener noreferrer'
|
|
18
|
+
} else {
|
|
19
|
+
rest.is = 'i-l'
|
|
21
20
|
}
|
|
22
21
|
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
rest.class = baseClass + (pathname === href ? ' active' : '')
|
|
29
|
-
|
|
30
|
-
!href && (delete rest.href)
|
|
31
|
-
return <a is='i-l' {...rest} />
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
if (typeof rest.class === 'string' || !rest?.class)
|
|
35
|
-
rest.className = a => (rest.class || '') + (a ? ' active' : '')
|
|
22
|
+
const [pathname] = useLocation()
|
|
23
|
+
const baseClass = typeof rest.class === 'string' || !rest?.class
|
|
24
|
+
? (rest.class || '') as string
|
|
25
|
+
: ''
|
|
26
|
+
rest.class = baseClass + (pathname === href ? ' active' : '')
|
|
36
27
|
|
|
37
|
-
|
|
28
|
+
!href && (delete rest.href)
|
|
29
|
+
return <a {...rest} />
|
|
38
30
|
}
|
package/src/pages/i18n/client.ts
CHANGED
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import { h, type JSX } from 'preact'
|
|
2
2
|
import { local } from '#/client/storage'
|
|
3
|
-
import { navigate } from '..'
|
|
4
3
|
|
|
5
4
|
const accepted = (lang: string | undefined, locales: string[] = [], fallback: string) => lang && locales.includes(lang) ? lang : fallback
|
|
6
5
|
|
|
@@ -41,7 +40,7 @@ const getAccepted = (lang: string, locales: string[] = [], defaultLocale = '') =
|
|
|
41
40
|
return defaultLocale
|
|
42
41
|
}
|
|
43
42
|
|
|
44
|
-
const detect = (locales: string[], defaultLocale: string) => {
|
|
43
|
+
export const detect = (locales: string[], defaultLocale: string) => {
|
|
45
44
|
if (!defaultLocale) defaultLocale = 'en'
|
|
46
45
|
if (typeof window === 'undefined') return defaultLocale
|
|
47
46
|
|
|
@@ -125,7 +124,7 @@ export const cached = () => local.get('lang') as string || ''
|
|
|
125
124
|
export const updateWidth = (el: HTMLSelectElement) => {
|
|
126
125
|
if (el) {
|
|
127
126
|
const option = el.querySelector(`option[value="${el.value}"]`) as HTMLOptionElement
|
|
128
|
-
const length = option
|
|
127
|
+
const length = option?.textContent.split(' ')[0]?.length || 0
|
|
129
128
|
el.style.width = (length * 15.7)+ 'px'
|
|
130
129
|
}
|
|
131
130
|
}
|
|
@@ -153,7 +152,7 @@ export const r = (key: string, lang?: string) => {
|
|
|
153
152
|
const path = '/'+ lang + (key ? '/'+ route(key, lang) : '')
|
|
154
153
|
|
|
155
154
|
if (!location.pathname.endsWith(path))
|
|
156
|
-
navigate(path)
|
|
155
|
+
self.$duto?.navigate(path)
|
|
157
156
|
|
|
158
157
|
// return '/' + lang + '/' + (key in _routes[lang] ? _routes[lang][key] : key)
|
|
159
158
|
}
|
package/src/pages/index.ts
CHANGED
|
@@ -1,27 +1,10 @@
|
|
|
1
1
|
export {
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
} from 'wouter-preact'
|
|
6
|
-
|
|
7
|
-
export {
|
|
8
|
-
t, r, d,
|
|
9
|
-
getLocale, setLocale,
|
|
10
|
-
isPrefixed,
|
|
2
|
+
route, r, redirect,
|
|
3
|
+
t, tl, d,
|
|
4
|
+
getLocale, setLocale, isPrefixed,
|
|
11
5
|
} from './i18n/client'
|
|
12
6
|
|
|
13
7
|
export { local } from './client/storage'
|
|
14
8
|
|
|
15
|
-
export
|
|
16
|
-
to: string | URL,
|
|
17
|
-
options?: { replace?: boolean; state?: S; transition?: boolean }
|
|
18
|
-
) => {
|
|
19
|
-
if (window?.$duto?.sroutes?.has(to)) {
|
|
20
|
-
location.pathname = to
|
|
21
|
-
return
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
window?.$duto?.navigate || useLocation()[1](to, options)
|
|
25
|
-
}
|
|
26
|
-
|
|
9
|
+
export * from './client/theme'
|
|
27
10
|
export { Link } from './components/link'
|
package/src/server/app.ts
CHANGED
|
@@ -10,6 +10,7 @@ import { isDev } from './utils/environment'
|
|
|
10
10
|
import { getVerb } from './utils'
|
|
11
11
|
import { registerMiddleware, resolveHandle, resolveMw } from './handle'
|
|
12
12
|
|
|
13
|
+
const NFHandler = () => response.notFound()
|
|
13
14
|
async function EHandler(e: Error | HTTPResponseError) {
|
|
14
15
|
console.error(e)
|
|
15
16
|
|
|
@@ -74,7 +75,7 @@ export default function createApp<E extends Env>(options?: ServerOptions<E>) {
|
|
|
74
75
|
|
|
75
76
|
// @ts-ignore
|
|
76
77
|
app.onError(options?.onError || EHandler)
|
|
77
|
-
app.notFound(options?.notFound ||
|
|
78
|
+
app.notFound(options?.notFound || NFHandler)
|
|
78
79
|
|
|
79
80
|
if (options?.init) options.init(app)
|
|
80
81
|
|
|
@@ -1,63 +0,0 @@
|
|
|
1
|
-
import { render, h } from 'preact'
|
|
2
|
-
import type { Page, GetProps, IRoute } from '#/compiler/types'
|
|
3
|
-
|
|
4
|
-
function normalizeParams(
|
|
5
|
-
raw: Record<string, string>,
|
|
6
|
-
paramDefs: IRoute['params']
|
|
7
|
-
): Record<string, string | string[] | undefined> {
|
|
8
|
-
const result: Record<string, string | string[] | undefined> = {};
|
|
9
|
-
for (const def of paramDefs) {
|
|
10
|
-
const val = raw[def.name];
|
|
11
|
-
if (def.catchAll) {
|
|
12
|
-
result[def.name] = val
|
|
13
|
-
? val.split('/').filter(Boolean)
|
|
14
|
-
: def.optional ? undefined : []
|
|
15
|
-
} else {
|
|
16
|
-
result[def.name] = val === undefined && def.optional ? undefined : val;
|
|
17
|
-
}
|
|
18
|
-
}
|
|
19
|
-
return result
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
export async function renderRoute(
|
|
23
|
-
container: HTMLElement,
|
|
24
|
-
Component: Page,
|
|
25
|
-
rawParams: Record<string, string>,
|
|
26
|
-
paramDefs?: IRoute['params'] | 0,
|
|
27
|
-
getProps?: GetProps | 0 | 1,
|
|
28
|
-
layouts?: Page[]
|
|
29
|
-
) {
|
|
30
|
-
while (document.body.firstChild)
|
|
31
|
-
document.body.removeChild(document.body.firstChild)
|
|
32
|
-
|
|
33
|
-
let props: Record<string, unknown> = paramDefs ? normalizeParams(rawParams, paramDefs) : rawParams
|
|
34
|
-
|
|
35
|
-
if (typeof Component === 'string') {
|
|
36
|
-
const mod = await import(Component)
|
|
37
|
-
Component = mod.default
|
|
38
|
-
getProps = getProps === 1 ? mod.getProps : 0
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
// @ts-ignore
|
|
42
|
-
const sProps = self.$duto?.props
|
|
43
|
-
if (typeof sProps === 'object' && sProps !== null) {
|
|
44
|
-
Object.assign(props, sProps)
|
|
45
|
-
} else if (typeof getProps === 'function') {
|
|
46
|
-
try { // TODO: melhorar tipagem do props
|
|
47
|
-
const fetched = await Promise.resolve(getProps(props as Record<string, string | string[] | undefined>))
|
|
48
|
-
props = { ...fetched, ...props }
|
|
49
|
-
} catch (e) {
|
|
50
|
-
console.error('[ssg] getProps failed:', e)
|
|
51
|
-
}
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
let el = h(Component, props as any)
|
|
56
|
-
|
|
57
|
-
const _layouts = layouts ? (Array.isArray(layouts) ? layouts : [layouts]) : []
|
|
58
|
-
for (const layout of _layouts)
|
|
59
|
-
el = h(layout, null, el)
|
|
60
|
-
|
|
61
|
-
render(el, container)
|
|
62
|
-
// render(h(Component, props as any), container)
|
|
63
|
-
}
|