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.
Files changed (60) hide show
  1. package/README.md +1 -1
  2. package/package.json +1 -1
  3. package/src/commands/new.ts +55 -30
  4. package/src/commands/templates/pages/README.md +21 -0
  5. package/src/commands/templates/pages/_gitignore +24 -0
  6. package/src/commands/templates/pages/components/html.tsx +55 -0
  7. package/src/commands/templates/pages/components/locale-select.tsx +59 -0
  8. package/src/commands/templates/pages/components/select.tsx +95 -0
  9. package/src/commands/templates/pages/components/theme-select.tsx +60 -0
  10. package/src/commands/templates/pages/i18n/en.ts +23 -0
  11. package/src/commands/templates/pages/i18n/index.ts +19 -0
  12. package/src/commands/templates/pages/i18n/pt-br.ts +23 -0
  13. package/src/commands/templates/pages/package.json +22 -0
  14. package/src/commands/templates/pages/pages/404.tsx +12 -0
  15. package/src/commands/templates/pages/pages/blog/[slug].tsx +88 -0
  16. package/src/commands/templates/pages/pages/blog/index.tsx +74 -0
  17. package/src/commands/templates/pages/pages/index.tsx +26 -0
  18. package/src/commands/templates/pages/pages/layout.tsx +26 -0
  19. package/src/commands/templates/pages/public/favicon.ico +0 -0
  20. package/src/commands/templates/pages/public/favicon.svg +1 -0
  21. package/src/commands/templates/pages/style.css +127 -0
  22. package/src/commands/templates/pages/tsconfig.json +56 -0
  23. package/src/commands/templates/pages/types.ts +8 -0
  24. package/src/commands/templates/server/base/.editorconfig +21 -0
  25. package/src/commands/templates/{base → server/base}/README.md +1 -1
  26. package/src/pages/cli/commands/dev.ts +10 -4
  27. package/src/pages/cli/utils.ts +7 -23
  28. package/src/pages/client/app.tsx +193 -91
  29. package/src/pages/client/link.ts +15 -41
  30. package/src/pages/client/storage.ts +1 -1
  31. package/src/pages/client/theme.ts +49 -0
  32. package/src/pages/compiler/aliases.ts +2 -8
  33. package/src/pages/compiler/ast.ts +1 -538
  34. package/src/pages/compiler/constants.ts +97 -13
  35. package/src/pages/compiler/index.ts +55 -140
  36. package/src/pages/compiler/meta.ts +40 -253
  37. package/src/pages/compiler/routes.ts +4 -5
  38. package/src/pages/compiler/types.ts +16 -125
  39. package/src/pages/compiler/vite-plugin.ts +64 -27
  40. package/src/pages/components/link.tsx +11 -19
  41. package/src/pages/i18n/client.ts +21 -10
  42. package/src/pages/i18n/server.ts +21 -6
  43. package/src/pages/index.ts +4 -22
  44. package/src/server/app.ts +2 -1
  45. package/src/server/routes.ts +4 -26
  46. package/src/utils.ts +34 -3
  47. package/src/pages/client/router.ts +0 -329
  48. package/src/pages/compiler/island.ts +0 -108
  49. /package/src/commands/templates/{base → pages}/.editorconfig +0 -0
  50. /package/src/commands/templates/{base → server/base}/.env.dev.example +0 -0
  51. /package/src/commands/templates/{base → server/base}/.env.prod.example +0 -0
  52. /package/src/commands/templates/{base → server/base}/_gitignore +0 -0
  53. /package/src/commands/templates/{base → server/base}/actions/blog/index.get.ts +0 -0
  54. /package/src/commands/templates/{base → server/base}/actions/blog/post/[id].get.ts +0 -0
  55. /package/src/commands/templates/{base → server/base}/actions/index.get.ts +0 -0
  56. /package/src/commands/templates/{base → server/base}/configs/duto.ts +0 -0
  57. /package/src/commands/templates/{base → server/base}/configs/roles.ts +0 -0
  58. /package/src/commands/templates/{base → server/base}/package.json +0 -0
  59. /package/src/commands/templates/{base → server/base}/tsconfig.json +0 -0
  60. /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, // process.env.npm_config_local_prefix || process.env.PWD || process.cwd() || __dirname,
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(conf) {
165
- config = conf
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 += 'd-b,d-p,i-d,i-s,i-i{display:contents}'
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 { Link as WouterLink, useLocation } from 'wouter-preact'
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
- const isExternal = isExternalUrl(rest.href = href)
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
- if (!href || isExternal || isSRoute) {
24
- const [pathname] = useLocation()
25
- const baseClass = typeof rest.class === 'string' || !rest?.class
26
- ? (rest.class || '') as string
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
- return <WouterLink is='i-l' {...rest} />
28
+ !href && (delete rest.href)
29
+ return <a {...rest} />
38
30
  }
@@ -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
- export default function i18n(
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.textContent.split(' ')[0]?.length || 0
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 => {
@@ -11,7 +11,7 @@ export default async function getI18n(meta: Metadata) {
11
11
  'i18n.ts',
12
12
  'i18n/index.ts',
13
13
  ]) {
14
- const path = join(meta.root, variant)
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', 'messages', 'routes', 'options'].some(i => !mod[i]))
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(mod.routes)
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(mod.routes)
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(mod.messages)}
48
- export const routes = ${JSJSON(mod.routes)}
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)}
@@ -1,28 +1,10 @@
1
1
  export {
2
- Redirect,
3
- useLocation, useParams, useSearch, useSearchParams,
4
- matchRoute,
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 const navigate = <S = any>(
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 || response.notFound)
78
+ app.notFound(options?.notFound || NFHandler)
78
79
 
79
80
  if (options?.init) options.init(app)
80
81
 
@@ -1,4 +1,4 @@
1
- import { Glob, file, write } from 'bun'
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 { getEnv } from 't0n'
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 = getEnv('npm_config_local_prefix') || getEnv('PWD') || join(_duto, '../../')
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
+ }