duto 0.260915.0 → 0.261011.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 +1 -1
- package/package.json +1 -1
- package/src/commands/new.ts +55 -30
- package/src/commands/templates/pages/README.md +21 -0
- package/src/commands/templates/pages/_gitignore +24 -0
- package/src/commands/templates/pages/components/html.tsx +55 -0
- package/src/commands/templates/pages/components/locale-select.tsx +59 -0
- package/src/commands/templates/pages/components/select.tsx +95 -0
- package/src/commands/templates/pages/components/theme-select.tsx +60 -0
- package/src/commands/templates/pages/i18n/en.ts +23 -0
- package/src/commands/templates/pages/i18n/index.ts +19 -0
- package/src/commands/templates/pages/i18n/pt-br.ts +23 -0
- package/src/commands/templates/pages/package.json +22 -0
- package/src/commands/templates/pages/pages/404.tsx +12 -0
- 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 +26 -0
- package/src/commands/templates/pages/pages/layout.tsx +26 -0
- 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 +127 -0
- package/src/commands/templates/pages/tsconfig.json +56 -0
- package/src/commands/templates/pages/types.ts +8 -0
- package/src/commands/templates/server/base/.editorconfig +21 -0
- package/src/commands/templates/{base → server/base}/README.md +1 -1
- package/src/pages/cli/commands/dev.ts +10 -4
- package/src/pages/cli/utils.ts +7 -23
- package/src/pages/client/app.tsx +193 -91
- 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/aliases.ts +2 -8
- package/src/pages/compiler/ast.ts +1 -538
- package/src/pages/compiler/constants.ts +97 -13
- package/src/pages/compiler/index.ts +55 -140
- package/src/pages/compiler/meta.ts +40 -253
- package/src/pages/compiler/routes.ts +4 -5
- package/src/pages/compiler/types.ts +16 -125
- package/src/pages/compiler/vite-plugin.ts +64 -27
- package/src/pages/components/link.tsx +11 -19
- package/src/pages/i18n/client.ts +21 -10
- package/src/pages/i18n/server.ts +21 -6
- package/src/pages/index.ts +4 -22
- package/src/server/app.ts +2 -1
- package/src/server/routes.ts +4 -26
- package/src/utils.ts +34 -3
- package/src/pages/client/router.ts +0 -329
- package/src/pages/compiler/island.ts +0 -108
- /package/src/commands/templates/{base → pages}/.editorconfig +0 -0
- /package/src/commands/templates/{base → server/base}/.env.dev.example +0 -0
- /package/src/commands/templates/{base → server/base}/.env.prod.example +0 -0
- /package/src/commands/templates/{base → server/base}/_gitignore +0 -0
- /package/src/commands/templates/{base → server/base}/actions/blog/index.get.ts +0 -0
- /package/src/commands/templates/{base → server/base}/actions/blog/post/[id].get.ts +0 -0
- /package/src/commands/templates/{base → server/base}/actions/index.get.ts +0 -0
- /package/src/commands/templates/{base → server/base}/configs/duto.ts +0 -0
- /package/src/commands/templates/{base → server/base}/configs/roles.ts +0 -0
- /package/src/commands/templates/{base → server/base}/package.json +0 -0
- /package/src/commands/templates/{base → server/base}/tsconfig.json +0 -0
- /package/src/commands/templates/{workerd → server/workerd}/wrangler.toml +0 -0
|
@@ -6,23 +6,24 @@ import { defineConfig } from 'vite'
|
|
|
6
6
|
|
|
7
7
|
import preact from '@preact/preset-vite'
|
|
8
8
|
import tailwind from '@tailwindcss/vite'
|
|
9
|
+
import { JSJSON } from 't0n'
|
|
9
10
|
|
|
10
11
|
import { EXT_RE } from './constants'
|
|
11
|
-
import { setAsset, setChunk, setEntry } from './assets'
|
|
12
|
+
import { getChunk, setAsset, setChunk, setEntry } from './assets'
|
|
12
13
|
import { renderPage } from '.'
|
|
13
14
|
import { removeExports, resolveImport } from './ast'
|
|
14
15
|
import { getMetadata } from './meta'
|
|
15
16
|
|
|
16
17
|
import type {
|
|
17
|
-
// Plugin,
|
|
18
18
|
UserConfig, ConfigEnv, ResolvedConfig,
|
|
19
|
-
PreviewServer,
|
|
19
|
+
// PreviewServer,
|
|
20
20
|
// ViteDevServer,
|
|
21
21
|
EnvironmentOptions,
|
|
22
22
|
PluginOption,
|
|
23
23
|
} from 'vite'
|
|
24
24
|
import type { Metadata, PluginOptions } from './types'
|
|
25
25
|
import { _pages, _root } from '@/utils'
|
|
26
|
+
import { cp } from 'node:fs/promises'
|
|
26
27
|
|
|
27
28
|
const VIRTUAL_ID = ':duto'
|
|
28
29
|
const RESOLVED_ID = '\0'+ VIRTUAL_ID
|
|
@@ -42,6 +43,7 @@ export const config = (opts: UserConfig & PluginOptions) => { // @ts-ignore
|
|
|
42
43
|
return defineConfig(opts)
|
|
43
44
|
}
|
|
44
45
|
|
|
46
|
+
// TODO: remover..
|
|
45
47
|
export default config
|
|
46
48
|
|
|
47
49
|
let metadata: Metadata
|
|
@@ -50,13 +52,11 @@ export function meta() {
|
|
|
50
52
|
}
|
|
51
53
|
|
|
52
54
|
export function plugin(options: PluginOptions = {}): PluginOption[] {
|
|
53
|
-
// let server: ViteDevServer
|
|
54
55
|
let config: ResolvedConfig
|
|
55
56
|
const {
|
|
56
|
-
root = _root,
|
|
57
|
+
root = _root,
|
|
57
58
|
pagesDir = 'pages',
|
|
58
59
|
} = options
|
|
59
|
-
// let metadata: Metadata
|
|
60
60
|
|
|
61
61
|
const PAGES_RE = new RegExp(`\/${pagesDir}\/.*\.(tsx?|jsx?)$`, 'i')
|
|
62
62
|
const stripTarget = new Set(['getStaticProps'])
|
|
@@ -105,18 +105,9 @@ export function plugin(options: PluginOptions = {}): PluginOption[] {
|
|
|
105
105
|
name: 'duto:scan',
|
|
106
106
|
enforce: 'pre',
|
|
107
107
|
|
|
108
|
-
// configureServer(_server) {
|
|
109
|
-
// server = _server
|
|
110
|
-
// // servers.push(server)
|
|
111
|
-
// },
|
|
112
|
-
|
|
113
108
|
async config(conf: UserConfig, env: ConfigEnv) {
|
|
114
109
|
metadata = await getMetadata(root as string, pagesDir)
|
|
115
110
|
|
|
116
|
-
// console.log(metadata.map)
|
|
117
|
-
// console.log(metadata.violations)
|
|
118
|
-
// console.log(Array.from(metadata.violations).filter(file => EXT_RE.test(file)))
|
|
119
|
-
|
|
120
111
|
if (conf.build) {
|
|
121
112
|
// conf.build.emptyOutDir = false
|
|
122
113
|
conf.build.copyPublicDir = false
|
|
@@ -129,10 +120,11 @@ export function plugin(options: PluginOptions = {}): PluginOption[] {
|
|
|
129
120
|
conf.build.rolldownOptions = conf.build.rolldownOptions || {}
|
|
130
121
|
conf.build.rolldownOptions.preserveEntrySignatures = 'exports-only' ///
|
|
131
122
|
conf.build.rolldownOptions.input = [
|
|
123
|
+
...metadata.routes.map(r => r.is404 ? '' : r.fullFilePath).filter(Boolean),
|
|
132
124
|
':duto/client/app.tsx',
|
|
133
125
|
// ':duto/not-found.tsx',
|
|
134
|
-
...Object.keys(metadata.pages),
|
|
135
|
-
...metadata.assets,
|
|
126
|
+
// ...Object.keys(metadata.pages),
|
|
127
|
+
// ...metadata.assets,
|
|
136
128
|
]
|
|
137
129
|
|
|
138
130
|
const filePrefix = metadata.minify ? '' : '[name].'
|
|
@@ -141,11 +133,6 @@ export function plugin(options: PluginOptions = {}): PluginOption[] {
|
|
|
141
133
|
hoistTransitiveImports: false,
|
|
142
134
|
format: 'esm',
|
|
143
135
|
minifyInternalExports: metadata.minify,
|
|
144
|
-
|
|
145
|
-
// manualChunks: {
|
|
146
|
-
// vendor: ['preact', '@preact/signals', 'wouter-preact'],
|
|
147
|
-
// },
|
|
148
|
-
// preserveModules: true,
|
|
149
136
|
preserveModulesRoot: '/',
|
|
150
137
|
assetFileNames: filePrefix +'[hash][extname]',
|
|
151
138
|
entryFileNames: filePrefix +'[hash].js',
|
|
@@ -155,14 +142,19 @@ export function plugin(options: PluginOptions = {}): PluginOption[] {
|
|
|
155
142
|
// entryFileNames: 'assets/[name].js',
|
|
156
143
|
// chunkFileNames: 'assets/chunks/[name]-[hash].js',
|
|
157
144
|
// assetFileNames: 'assets/[name][extname]',
|
|
145
|
+
|
|
146
|
+
// manualChunks: {
|
|
147
|
+
// vendor: ['preact', '@preact/signals', 'wouter-preact'],
|
|
148
|
+
// },
|
|
149
|
+
// preserveModules: true,
|
|
158
150
|
}
|
|
159
151
|
}
|
|
160
152
|
|
|
161
153
|
return conf
|
|
162
154
|
},
|
|
163
155
|
|
|
164
|
-
async configResolved(
|
|
165
|
-
config =
|
|
156
|
+
async configResolved(c) {
|
|
157
|
+
config = c
|
|
166
158
|
},
|
|
167
159
|
|
|
168
160
|
resolveId(id) {
|
|
@@ -217,18 +209,26 @@ export function plugin(options: PluginOptions = {}): PluginOption[] {
|
|
|
217
209
|
// for (const id of moduleIds)
|
|
218
210
|
// console.error(id)
|
|
219
211
|
|
|
212
|
+
// TODO: rever isso...
|
|
220
213
|
for (const key in bundle) {
|
|
221
214
|
const file = bundle[key]
|
|
215
|
+
|
|
222
216
|
if (file.type === 'chunk' && file.isEntry) {
|
|
223
217
|
setChunk(file.facadeModuleId, '/'+ file.fileName)
|
|
224
218
|
continue
|
|
225
219
|
}
|
|
226
220
|
|
|
227
221
|
if (file.type === 'asset' && file?.originalFileName?.endsWith('duto/pages/client/app.tsx'))
|
|
228
|
-
file.source += '
|
|
222
|
+
file.source += 'i-i{display:contents}'
|
|
229
223
|
|
|
230
224
|
if (file.type === 'asset' && file.fileName.endsWith('.css')) {
|
|
231
225
|
setAsset(resolveImport(file.originalFileName as string, metadata.root, metadata.aliases), '/'+ file.fileName)
|
|
226
|
+
|
|
227
|
+
const css = typeof file.source === 'string'
|
|
228
|
+
? file.source
|
|
229
|
+
: Buffer.from(file.source).toString('utf-8')
|
|
230
|
+
|
|
231
|
+
file.source = css.replace(/\/\*!\s*tailwindcss[^*]*\*\/\s*/g, '')
|
|
232
232
|
continue
|
|
233
233
|
}
|
|
234
234
|
|
|
@@ -275,13 +275,50 @@ export function plugin(options: PluginOptions = {}): PluginOption[] {
|
|
|
275
275
|
},
|
|
276
276
|
|
|
277
277
|
async closeBundle() {
|
|
278
|
-
for (const [_filePath, page] of metadata.pages)
|
|
278
|
+
for (const [_filePath, page] of metadata.pages)
|
|
279
279
|
await renderPage(metadata, config, page)
|
|
280
280
|
// const mod = await server.ssrLoadModule(_filePath)
|
|
281
|
-
// }
|
|
282
281
|
|
|
283
282
|
rmSync(join(metadata.root, 'tmp'), {recursive: true, force: true})
|
|
284
283
|
// await server.close()
|
|
284
|
+
|
|
285
|
+
const outPath = join(config.root, config.build.outDir)
|
|
286
|
+
|
|
287
|
+
await cp(join(config.root, 'public'), outPath, { recursive: true, force: true })
|
|
288
|
+
|
|
289
|
+
const entry = Bun.file(join(outPath, getChunk(':duto/client/app.tsx')))
|
|
290
|
+
if (await entry.exists()) {
|
|
291
|
+
let entryContent = await entry.text()
|
|
292
|
+
|
|
293
|
+
for (const r of metadata.routes.filter(r => !r.is404)) {
|
|
294
|
+
const rPath = getChunk(r.fullFilePath.replace(config.root, ''))
|
|
295
|
+
|
|
296
|
+
if (r.props) {
|
|
297
|
+
const rFile = Bun.file(join(outPath, rPath))
|
|
298
|
+
await rFile.write((await rFile.text()).replace('}export{', `}export const props=${JSJSON(r.props)};export{`))
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
entryContent = entryContent.replace(r.fullFilePath, rPath)
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
await entry.write(entryContent)
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
// i18n
|
|
308
|
+
for (const locale of metadata.i18n.locales) {
|
|
309
|
+
const intlPath = join(metadata.root, 'i18n', locale + '.ts')
|
|
310
|
+
if (existsSync(intlPath)) {
|
|
311
|
+
const intlMod = await import(intlPath)
|
|
312
|
+
await Bun.write(
|
|
313
|
+
join(outPath, locale +'.js'),
|
|
314
|
+
`var a=${JSJSON({
|
|
315
|
+
routes: intlMod.routes ?? intlMod.default?.routes,
|
|
316
|
+
messages: intlMod.messages ?? intlMod.default?.messages,
|
|
317
|
+
})};export { a as default }`
|
|
318
|
+
)
|
|
319
|
+
}
|
|
320
|
+
}
|
|
321
|
+
|
|
285
322
|
},
|
|
286
323
|
|
|
287
324
|
// TODO: precisa ser aqui mesmo pois se a pagina for importada no entry, pode incluir codigo nao usado no chunck/bundle
|
|
@@ -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
|
|
|
@@ -84,12 +83,22 @@ export const getLocale = () => {
|
|
|
84
83
|
}
|
|
85
84
|
}
|
|
86
85
|
|
|
87
|
-
export const setLocale = (lang: string) => {
|
|
86
|
+
export const setLocale = async (lang: string) => {
|
|
87
|
+
await loadLang(lang)
|
|
88
88
|
cache(lang)
|
|
89
89
|
_locale = accepted(lang, _locales, _detected)
|
|
90
90
|
}
|
|
91
91
|
|
|
92
|
-
|
|
92
|
+
async function loadLang(lang: string) {
|
|
93
|
+
if (!_routes[lang] || !_messages[lang]) {
|
|
94
|
+
const intlMod = await import('/'+ lang +'.js')
|
|
95
|
+
if (!intlMod) return
|
|
96
|
+
_routes[lang] = intlMod.routes ?? intlMod.default?.routes
|
|
97
|
+
_messages[lang] = intlMod.messages ?? intlMod.default?.messages
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
export default async function i18n(
|
|
93
102
|
fallbackLocale: string,
|
|
94
103
|
locales: string[],
|
|
95
104
|
messages: Record<string, Record<string, string>>,
|
|
@@ -103,6 +112,8 @@ export default function i18n(
|
|
|
103
112
|
_detected = detect(locales, fallbackLocale)
|
|
104
113
|
_locale = typeof window === 'undefined' ? fallbackLocale : getLocale()
|
|
105
114
|
|
|
115
|
+
await loadLang(_locale)
|
|
116
|
+
|
|
106
117
|
// console.error(routes)
|
|
107
118
|
// console.error(routeMap)
|
|
108
119
|
}
|
|
@@ -113,7 +124,7 @@ export const cached = () => local.get('lang') as string || ''
|
|
|
113
124
|
export const updateWidth = (el: HTMLSelectElement) => {
|
|
114
125
|
if (el) {
|
|
115
126
|
const option = el.querySelector(`option[value="${el.value}"]`) as HTMLOptionElement
|
|
116
|
-
const length = option
|
|
127
|
+
const length = option?.textContent.split(' ')[0]?.length || 0
|
|
117
128
|
el.style.width = (length * 15.7)+ 'px'
|
|
118
129
|
}
|
|
119
130
|
}
|
|
@@ -141,7 +152,7 @@ export const r = (key: string, lang?: string) => {
|
|
|
141
152
|
const path = '/'+ lang + (key ? '/'+ route(key, lang) : '')
|
|
142
153
|
|
|
143
154
|
if (!location.pathname.endsWith(path))
|
|
144
|
-
navigate(path)
|
|
155
|
+
self.$duto?.navigate(path)
|
|
145
156
|
|
|
146
157
|
// return '/' + lang + '/' + (key in _routes[lang] ? _routes[lang][key] : key)
|
|
147
158
|
}
|
|
@@ -173,10 +184,10 @@ type TProps = {
|
|
|
173
184
|
}
|
|
174
185
|
|
|
175
186
|
const lookup = (key: string, lang: string): string => {
|
|
176
|
-
if (!key) return ''
|
|
177
|
-
if (key.startsWith('_')) return key.replace(/^_+/, '')
|
|
178
|
-
const msg = _messages[lang] ?? _messages[_locale]
|
|
179
|
-
return (key in msg ? msg[key] : key) as string
|
|
187
|
+
if (!key) return ''
|
|
188
|
+
if (key.startsWith('_')) return key.replace(/^_+/, '')
|
|
189
|
+
const msg = _messages[lang] ?? _messages[_locale]
|
|
190
|
+
return (msg && key in msg ? msg[key] : key) as string
|
|
180
191
|
}
|
|
181
192
|
|
|
182
193
|
const interpolate = (template: string, params: Record<string, Primitives> | Primitives[]): string => {
|
package/src/pages/i18n/server.ts
CHANGED
|
@@ -11,7 +11,7 @@ export default async function getI18n(meta: Metadata) {
|
|
|
11
11
|
'i18n.ts',
|
|
12
12
|
'i18n/index.ts',
|
|
13
13
|
]) {
|
|
14
|
-
|
|
14
|
+
const path = join(meta.root, variant)
|
|
15
15
|
if (existsSync(path)) {
|
|
16
16
|
file = path
|
|
17
17
|
break
|
|
@@ -22,13 +22,28 @@ export default async function getI18n(meta: Metadata) {
|
|
|
22
22
|
const mod = await IMPORT<I18nMetadata>(file)
|
|
23
23
|
|
|
24
24
|
// @ts-ignore
|
|
25
|
-
if (['locales', 'fallbackLocale', '
|
|
25
|
+
if (['locales', 'fallbackLocale', 'options'].some(i => !mod[i]))
|
|
26
26
|
return {} as I18nMetadata
|
|
27
27
|
|
|
28
|
+
// const routes = Object.fromEntries(mod.locales.map(locale => [locale, ]))
|
|
29
|
+
|
|
30
|
+
const routes: Record<string, Record<string, string>> = {}
|
|
31
|
+
const messages: Record<string, Record<string, string>> = {}
|
|
32
|
+
for (const locale of mod.locales) {
|
|
33
|
+
const intlPath = join(meta.root, 'i18n', locale + '.ts')
|
|
34
|
+
if (existsSync(intlPath)) {
|
|
35
|
+
const intlMod = await IMPORT<{default:{routes: Record<string, string>, messages: Record<string, string>}}>(intlPath)
|
|
36
|
+
routes[locale] = intlMod.default.routes
|
|
37
|
+
messages[locale] = intlMod.default.messages
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
|
|
28
41
|
const i18n: I18nMetadata = {
|
|
29
42
|
...mod,
|
|
43
|
+
routes,
|
|
44
|
+
messages,
|
|
30
45
|
routeMap: Object.fromEntries(
|
|
31
|
-
Object.values(
|
|
46
|
+
Object.values(routes)
|
|
32
47
|
.map(paths => Object.entries(paths).map(([key, path]) => path == key ? 0 : [path, key]).filter(Boolean))
|
|
33
48
|
.flat() as Array<[string, string]>
|
|
34
49
|
),
|
|
@@ -36,7 +51,7 @@ export default async function getI18n(meta: Metadata) {
|
|
|
36
51
|
string: '',
|
|
37
52
|
}
|
|
38
53
|
|
|
39
|
-
Object.entries(
|
|
54
|
+
Object.entries(routes)
|
|
40
55
|
.forEach(([locale, paths]) => {
|
|
41
56
|
Object.entries(paths)
|
|
42
57
|
.forEach(([route, path]) => i18n.pageRoutes.push({ locale, path }))
|
|
@@ -44,8 +59,8 @@ export default async function getI18n(meta: Metadata) {
|
|
|
44
59
|
|
|
45
60
|
i18n.string = `export const fallbackLocale = ${JSJSON(mod.fallbackLocale)}
|
|
46
61
|
export const locales = ${JSJSON(mod.locales)}
|
|
47
|
-
export const messages = ${JSJSON(
|
|
48
|
-
export const routes = ${JSJSON(
|
|
62
|
+
export const messages = ${JSJSON(messages)}
|
|
63
|
+
export const routes = ${JSJSON(routes)}
|
|
49
64
|
export const routeMap = ${JSJSON(i18n.routeMap)}
|
|
50
65
|
export const pageRoutes = ${JSJSON(i18n.pageRoutes)}
|
|
51
66
|
export const options = ${JSJSON(mod.options)}
|
package/src/pages/index.ts
CHANGED
|
@@ -1,28 +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
|
-
const n = window?.$duto?.navigate || useLocation()[1]
|
|
25
|
-
n(to, options)
|
|
26
|
-
}
|
|
27
|
-
|
|
9
|
+
export * from './client/theme'
|
|
28
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
|
|
package/src/server/routes.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { file, write } from 'bun'
|
|
2
2
|
|
|
3
3
|
import { copyFileSync } from 'node:fs'
|
|
4
4
|
import { join, relative } from 'pathe'
|
|
@@ -12,7 +12,7 @@ import { STATUS_CODES } from 'node:http'
|
|
|
12
12
|
import $ from './app'
|
|
13
13
|
import _response from './response'
|
|
14
14
|
import _validator from './validator'
|
|
15
|
-
import { _root, _server } from '@/utils'
|
|
15
|
+
import { _root, _server, scan } from '@/utils'
|
|
16
16
|
import { getLastCommitHash, ensureDir, nextId, verbAlias } from './utils'
|
|
17
17
|
import { generateOpenAPI, generateOpenAPIClient, openAPIConfig } from './open-api/spec'
|
|
18
18
|
import { highlightedMethod, highlightedURI } from './cli/utils'
|
|
@@ -21,28 +21,6 @@ import type { Routes, Rule, StandardSchemaV1 } from './types'
|
|
|
21
21
|
import { autorizedPath } from './middlewares/auth'
|
|
22
22
|
import { isDev } from './utils/environment'
|
|
23
23
|
|
|
24
|
-
export async function* scan(
|
|
25
|
-
dir: string | string[],
|
|
26
|
-
ext: string | string[],
|
|
27
|
-
ignore: string[] = ['*.d.*'],
|
|
28
|
-
cwd?: string,
|
|
29
|
-
): AsyncGenerator<[string, string, any]> {
|
|
30
|
-
if (typeof dir !== 'string')
|
|
31
|
-
dir = ext.length === 1 ? dir[0] : `{${dir.join(',')}}`
|
|
32
|
-
|
|
33
|
-
if (typeof ext !== 'string')
|
|
34
|
-
ext = ext.length === 1 ? ext[0] : `{${ext.join(',')}}`
|
|
35
|
-
|
|
36
|
-
cwd ??= _root
|
|
37
|
-
for await (const file of new Glob(dir +'/**/*.'+ ext).scan({
|
|
38
|
-
cwd,
|
|
39
|
-
absolute: true,
|
|
40
|
-
onlyFiles: true, // @ts-ignore
|
|
41
|
-
ignore,
|
|
42
|
-
})) {
|
|
43
|
-
yield [file, file.split(cwd as string)[1], await IMPORT(file)]
|
|
44
|
-
}
|
|
45
|
-
}
|
|
46
24
|
|
|
47
25
|
const importName = (name: string | undefined, dirs: string[]) => (name || nextId()).replace(/\.ts$/, '')
|
|
48
26
|
|
|
@@ -407,10 +385,10 @@ export async function cacheRoutes() {
|
|
|
407
385
|
if (isDev()) {
|
|
408
386
|
const _file = join(_server, 'middlewares', 'logger.ts')
|
|
409
387
|
const _mod = await IMPORT(_file) // @ts-ignore
|
|
410
|
-
middlewares.unshift({
|
|
388
|
+
middlewares.unshift({ // @ts-ignore
|
|
411
389
|
path: _mod?.path || '*',
|
|
412
390
|
name: 'dev_logger',
|
|
413
|
-
file: _file,
|
|
391
|
+
file: _file, // @ts-ignore
|
|
414
392
|
handle: _mod.handle,
|
|
415
393
|
})
|
|
416
394
|
}
|
package/src/utils.ts
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { IMPORT } from 't0n'
|
|
2
2
|
import { dirname, join } from 'pathe'
|
|
3
3
|
|
|
4
4
|
export const _duto = dirname(new URL(import.meta.url).pathname)
|
|
5
5
|
export const _pages = join(_duto, 'pages')
|
|
6
|
-
export const _server = join(_duto, 'server')
|
|
7
|
-
export const _root =
|
|
6
|
+
export const _server = join(_duto, 'server') //@ts-ignore
|
|
7
|
+
export const _root = process.env?.npm_config_local_prefix || process.env?.PWD || process.stdin?.path || process.cwd() // || join(_duto, '../../')s
|
|
8
8
|
|
|
9
9
|
const UNITS = ['B', 'kB', 'MB', 'GB']
|
|
10
10
|
export function filesize(bytes: number, decimals = 2) {
|
|
@@ -13,3 +13,34 @@ export function filesize(bytes: number, decimals = 2) {
|
|
|
13
13
|
const i = Math.min(Math.floor(Math.log(bytes) / Math.log(1024)), UNITS.length - 1)
|
|
14
14
|
return `${parseFloat((bytes / 1024 ** i).toFixed(decimals))} ${UNITS[i]}`
|
|
15
15
|
}
|
|
16
|
+
|
|
17
|
+
export const formatTime = (ms: number) => {
|
|
18
|
+
if (ms < 0.001) return `${(ms * 1_000_000).toFixed(2)}ns`
|
|
19
|
+
if (ms < 1) return `${(ms * 1000).toFixed(2)}µs`
|
|
20
|
+
if (ms < 1000) return `${ms.toFixed(2)}ms`
|
|
21
|
+
if (ms < 60000) return `${(ms / 1000).toFixed(2)}s`
|
|
22
|
+
return `${Math.floor(ms / 60000)}m ${((ms % 60000) / 1000).toFixed(1)}s`
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export async function* scan(
|
|
26
|
+
dir: string | string[],
|
|
27
|
+
ext: string | string[],
|
|
28
|
+
ignore: string[] = ['*.d.*'],
|
|
29
|
+
cwd?: string,
|
|
30
|
+
): AsyncGenerator<[string, string, any]> {
|
|
31
|
+
if (typeof dir !== 'string')
|
|
32
|
+
dir = ext.length === 1 ? dir[0] : `{${dir.join(',')}}`
|
|
33
|
+
|
|
34
|
+
if (typeof ext !== 'string')
|
|
35
|
+
ext = ext.length === 1 ? ext[0] : `{${ext.join(',')}}`
|
|
36
|
+
|
|
37
|
+
cwd ??= _root
|
|
38
|
+
for await (const file of new Bun.Glob(dir +'/**/*.'+ ext).scan({
|
|
39
|
+
cwd,
|
|
40
|
+
absolute: true,
|
|
41
|
+
onlyFiles: true, // @ts-ignore
|
|
42
|
+
ignore,
|
|
43
|
+
})) {
|
|
44
|
+
yield [file, file.split(cwd as string)[1], await IMPORT(file)]
|
|
45
|
+
}
|
|
46
|
+
}
|