duto 0.261011.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "duto",
3
3
  "description": "All-in-one serverless framework for web artisans",
4
- "version": "0.261011.0",
4
+ "version": "0.261012.0",
5
5
  "type": "module",
6
6
  "main": "./src/index.ts",
7
7
  "types": "./src/index.d.ts",
@@ -25,46 +25,42 @@
25
25
  "./length": "./src/server/utils/length.ts"
26
26
  },
27
27
  "dependencies": {
28
- "@inquirer/confirm": "^5.1.8",
29
- "@inquirer/input": "^4.1.8",
30
- "@inquirer/select": "^4.1.0",
28
+ "@inquirer/confirm": "^5.1.21",
29
+ "@inquirer/input": "^4.3.1",
30
+ "@inquirer/select": "^4.4.2",
31
31
  "nanospinner": "^1.2.2",
32
- "@hono/node-server": "^1.19.11",
33
- "@hono/standard-validator": "^0.2.2",
32
+ "@hono/node-server": "^1.19.17",
33
+ "@hono/standard-validator": "^0.2.3",
34
34
  "@hono/zod-validator": "^0.7.6",
35
- "@scalar/hono-api-reference": "^0.12.0",
35
+ "@scalar/hono-api-reference": "^0.12.11",
36
36
  "@minify-html/node": "^0.18.1",
37
- "@preact/signals": "^2.9.2",
38
- "@swc/core": "^1.15.21",
39
- "chokidar": "^3.5.2",
37
+ "@preact/signals": "^2.11.3",
38
+ "bun-plugin-tailwind": "^0.1.2",
39
+ "chokidar": "^3.6.0",
40
40
  "citty": "^0.2.2",
41
41
  "consola": "^3.4.2",
42
42
  "cripta": "0.x",
43
- "dotenv": "^16.5.0",
44
- "esbuild": "^0.25.2",
43
+ "dotenv": "^16.6.1",
44
+ "esbuild": "^0.25.12",
45
45
  "forj": "0.x",
46
- "hono": "^4.13.7",
47
- "hono-openapi": "^1.3.0",
46
+ "hono": "^4.13.13",
47
+ "hono-openapi": "^1.3.5",
48
48
  "localflare-api": "^0.5.0",
49
49
  "localflare-core": "^0.5.0",
50
50
  "miniflare": "^4.20260730.0",
51
- "pathe": "^2.0",
52
- "preact": "^10.29.7",
53
- "preact-render-to-string": "^6.6.6",
51
+ "pathe": "^2.0.3",
52
+ "preact": "^11.0.1",
53
+ "preact-render-to-string": "^6.8.0",
54
54
  "quansync": "^0.2.11",
55
55
  "t0n": "0.x",
56
- "vite": "^8.0.0",
57
- "wouter-preact": "^3.10.0",
58
- "wrangler": "^4.128.0",
59
- "zod": "^4.3.6",
56
+ "wouter-preact": "^3.13.0",
57
+ "wrangler": "^4.149.0",
58
+ "zod": "^4.6.6",
60
59
  "zod-openapi": "^5.4.6"
61
60
  },
62
61
  "devDependencies": {
63
- "@cloudflare/workers-types": "^4.20260113.0",
64
- "@minify-html/node": "^0.18.1",
65
- "@preact/preset-vite": "^2.10.5",
66
- "@tailwindcss/vite": "^4.3.1",
67
- "bun-types": "^1.4.0",
62
+ "@cloudflare/workers-types": "^4.20260702.1",
63
+ "bun-types": "^1.4.3",
68
64
  "typescript": "^5.9.3"
69
65
  },
70
66
  "resolutions": {
@@ -1,9 +1,8 @@
1
- import { build } from 'vite'
2
1
  import { setEnv } from 't0n'
3
2
  import { command } from 't0n/cli'
4
3
  import { dim, underline } from 't0n/color'
5
4
  import { wait, error, rn, event } from 't0n/log'
6
- import { meta } from '#/compiler/vite-plugin'
5
+ import { meta, build } from '#/compiler/bun-plugin'
7
6
  import type { SummaryMetadata } from '#/compiler/types'
8
7
  import { getConfig } from '../utils'
9
8
  import { filesize } from '@/utils'
@@ -1,17 +1,16 @@
1
1
  import { IMPORT, Datte } from 't0n'
2
2
  import { join } from 'pathe'
3
3
  import { gray, green, yellow, red, cyan } from 't0n/color'
4
- import type { UserConfig } from 'vite'
5
4
 
6
5
  import { resolveExtension } from '#/compiler/file'
7
- import config from '#/compiler/vite-plugin'
6
+ import config, { type DutoOptions } from '#/compiler/bun-plugin'
8
7
  import { _root } from '@/utils'
9
8
  import { match, match404 } from '#/compiler/routes'
10
9
  import type { Metadata, PluginOptions } from '#/compiler/types'
11
10
  import { EXTENSIONS_LIST, NF_NAMES } from '#/compiler/constants'
12
11
 
13
- export async function getConfig(name = 'config'): Promise<UserConfig & PluginOptions> {
14
- let opts = {} as UserConfig
12
+ export async function getConfig(name = 'config'): Promise<DutoOptions> {
13
+ let opts = {} as DutoOptions
15
14
  const file = resolveExtension(name)
16
15
  if (file) opts = await IMPORT(join(_root, file), 'default')
17
16
  return config(opts)
@@ -106,7 +106,7 @@ async function resolveRoute(
106
106
  sProps = null
107
107
 
108
108
  if (typeof Component === 'string') {
109
- const mod = await import(/* @vite-ignore */ Component)
109
+ const mod = await import((Component[0] == '/' ? '' : '/')+ Component)
110
110
  Component = mod.default
111
111
  getProps = getProps === 1 ? mod.getProps : 0
112
112
  sProps = mod.props ?? null
@@ -1,6 +1,19 @@
1
1
  import { resolve } from 'node:path'
2
2
  import { resolveAlias } from './aliases'
3
3
 
4
+ const IMPORT_RE = /(?:\/\/[^\n]*|\/\*[\s\S]*?\*\/)|import\s*(?:type\s*)?(?:[^'"]*?from\s*)?['"]([^'"]+)['"]/g
5
+ export function getImports(str: string) {
6
+ const found: string[] = []
7
+ let match: RegExpExecArray | null
8
+ while ((match = IMPORT_RE.exec(str))) {
9
+ if (match[0].includes('import type') || !match[1]) continue
10
+
11
+ found.push(match[1])
12
+ }
13
+
14
+ return found
15
+ }
16
+
4
17
  const resolveImportCache = new Map<string, string>()
5
18
  export const resolveImport = (lib: string, importer: string, aliases: Record<string, string>) => {
6
19
  if (resolveImportCache.has(lib))
@@ -0,0 +1,245 @@
1
+ import { existsSync, rmSync } from 'node:fs'
2
+ import { cp, mkdir } from 'node:fs/promises'
3
+ import { dirname, extname, join, relative } from 'node:path'
4
+
5
+ import tailwind from 'bun-plugin-tailwind'
6
+ import { JSJSON } from 't0n'
7
+
8
+ import { EXT_RE } from './constants'
9
+ import { getChunk, setAsset, setChunk, setEntry } from './assets'
10
+ import { renderPage } from '.'
11
+ import { getImports, removeExports } from './ast'
12
+ import { getMetadata } from './meta'
13
+ import { _pages, _root } from '@/utils'
14
+
15
+ import type { BunPlugin, BuildArtifact } from 'bun'
16
+ import type { Metadata } from './types'
17
+
18
+ const VIRTUAL_ID = ':duto'
19
+ const NAMESPACE = 'duto'
20
+ const APP_ID = ':duto/client/app.tsx'
21
+
22
+ export type DutoOptions = {
23
+ root: string
24
+ pagesDir: string
25
+ outDir: string
26
+ plugins: BunPlugin[]
27
+ }
28
+
29
+ export const config = (opts: DutoOptions): DutoOptions => {
30
+ opts.root ||= _root
31
+ opts.pagesDir ||= 'pages'
32
+ opts.outDir ||= 'dist'
33
+ opts.plugins = [...(opts.plugins || [])]
34
+ return opts
35
+ }
36
+
37
+ let metadata: Metadata
38
+ export function meta() {
39
+ return metadata
40
+ }
41
+
42
+ const CSS_CANDIDATES = [
43
+ 'style.css',
44
+ 'styles.css',
45
+ 'styles/index.css',
46
+ 'style/index.css',
47
+ 'tailwind.css',
48
+ 'tailwindcss.css',
49
+ 'styles/tailwind.css',
50
+ 'styles/tailwindcss.css',
51
+ ]
52
+
53
+ const REACT_ALIAS: Record<string, string> = {
54
+ 'react': 'preact/compat',
55
+ 'react-dom': 'preact/compat',
56
+ 'react-dom/client': 'preact/compat/client',
57
+ 'react-dom/test-utils': 'preact/test-utils',
58
+ 'react/jsx-runtime': 'preact/jsx-runtime',
59
+ 'react/jsx-dev-runtime': 'preact/jsx-dev-runtime',
60
+ }
61
+
62
+ const stripExt = (p: string) => p.slice(0, p.length - extname(p).length)
63
+
64
+ export async function build(opts: DutoOptions) {
65
+ const conf = config(opts)
66
+ const outPath = join(conf.root, conf.outDir)
67
+
68
+ const PAGES_RE = new RegExp(`/${conf.pagesDir}/.*\\.(tsx?|jsx?)$`, 'i')
69
+ const stripTarget = new Set(['getStaticProps'])
70
+
71
+ metadata = await getMetadata(conf.root, conf.pagesDir)
72
+
73
+ const appEntry = join(_pages, 'client', 'app.tsx')
74
+ setEntry(APP_ID, relative(conf.root, appEntry))
75
+
76
+ const routes = metadata.routes.filter(r => !r.is404)
77
+ const entrypoints = [...routes.map(r => r.fullFilePath), appEntry]
78
+
79
+ const sources = new Map(entrypoints.map(e => [relative(conf.root, stripExt(e)), e]))
80
+
81
+ const prefix = metadata.minify ? '' : '[name].'
82
+
83
+ rmSync(outPath, { recursive: true, force: true })
84
+ await mkdir(outPath, { recursive: true })
85
+
86
+ const result = await Bun.build({
87
+ entrypoints,
88
+ root: conf.root,
89
+ target: 'browser',
90
+ format: 'esm',
91
+ splitting: true,
92
+ minify: metadata.minify,
93
+ sourcemap: 'none',
94
+ naming: {
95
+ entry: '[dir]/[name]-[hash6].[ext]', // used to map output to source
96
+ chunk: prefix + '[hash6].[ext]',
97
+ asset: prefix + '[hash6].[ext]',
98
+ },
99
+ define: {
100
+ 'process.env.NODE_ENV': '"production"',
101
+ },
102
+ plugins: [
103
+ ...(conf.plugins || []),
104
+ tailwind,
105
+ {
106
+ name: 'duto',
107
+ setup(build) {
108
+ build.onResolve({ filter: /^react(-dom)?(\/|$)/ }, args => {
109
+ const target = REACT_ALIAS[args.path]
110
+ if (!target) return
111
+ return { path: Bun.resolveSync(target, conf.root) }
112
+ })
113
+
114
+ build.onResolve({ filter: /^:root/ }, args => {
115
+ for (const f of CSS_CANDIDATES) {
116
+ const path = join(conf.root, f)
117
+ if (existsSync(path)) return { path }
118
+ }
119
+ })
120
+
121
+ build.onResolve({ filter: /^:duto/ }, args => {
122
+ const id = args.path
123
+
124
+ if (EXT_RE.test(id) && id.includes('/')) {
125
+ const path = join(_pages, id.replace(VIRTUAL_ID, ''))
126
+ return { path }
127
+ }
128
+
129
+ if (id.endsWith('/hmr') && metadata.hmr)
130
+ return { path: join(_pages, 'client', id.replace(VIRTUAL_ID, '') + '.ts') }
131
+
132
+ return { path: id, namespace: NAMESPACE }
133
+ })
134
+
135
+ build.onLoad({ filter: /.*/, namespace: NAMESPACE }, args => ({
136
+ contents: args.path.endsWith('/i18n') ? metadata.i18n.string : metadata.string,
137
+ loader: 'ts',
138
+ }))
139
+
140
+ build.onLoad({ filter: PAGES_RE }, async args => {
141
+ if (args.path.includes('node_modules')) return
142
+
143
+ const code = await Bun.file(args.path).text()
144
+ const stripped = removeExports(args.path, code, stripTarget)
145
+ if (!stripped || stripped === code) return
146
+
147
+ const ext = extname(args.path).slice(1) as 'ts' | 'tsx' | 'js' | 'jsx'
148
+ return { contents: stripped, loader: ext }
149
+ })
150
+ },
151
+ },
152
+ ],
153
+ })
154
+
155
+ if (!result.success)
156
+ throw new AggregateError(result.logs, 'duto: build failed')
157
+
158
+ const keyOf = (a: BuildArtifact, fileName: string) => {
159
+ let noExt = stripExt(fileName).replaceAll('_.._', '..')
160
+ if (a.hash) noExt = noExt.replace(new RegExp(`-${a.hash}$`), '')
161
+ return noExt
162
+ }
163
+
164
+ const dirs = new Set<string>()
165
+ for (const out of result.outputs) {
166
+ const fileName = out.path.replace(/^\.\//, '').replaceAll('_.._', '..')
167
+ const dest = metadata.minify ? fileName.split('/').at(-1)!.split('-').at(-1)! : fileName.replace('../../packages/duto/src/pages', '')
168
+ const filePath = join(outPath, dest)
169
+ const key = keyOf(out, fileName)
170
+ const owner = sources.get(key)
171
+
172
+ const fileDir = dirname(filePath)
173
+ if (fileDir !== outPath && !dirs.has(fileDir)) {
174
+ dirs.add(fileDir)
175
+ await mkdir(fileDir, { recursive: true })
176
+ }
177
+
178
+ let content = await out.text()
179
+ if (fileName.endsWith('.css')) {
180
+ if (owner === appEntry) content += 'i-i{display:contents}'
181
+ content = content.replace(/\/\*!\s*tailwindcss[^*]*\*\/\s*/g, '')
182
+
183
+ setAsset(relative(conf.root, owner ?? fileName), '/'+ dest)
184
+ } else if (out.kind === 'entry-point' && owner) {
185
+ setChunk(relative(conf.root, owner),'/'+ dest)
186
+ // o lookup original usa o caminho relativo ao root
187
+ // if (owner !== appEntry) setChunk(owner.replace(conf.root, ''), name)
188
+ }
189
+
190
+ const imports = getImports(content)
191
+ for (const path of imports) {
192
+ content = content.replaceAll(path, '/'+ path.split('/').at(-1))
193
+ }
194
+
195
+ await Bun.write(filePath, content)
196
+ }
197
+
198
+ for (const [_filePath, page] of metadata.pages)
199
+ await renderPage(metadata, conf, page)
200
+
201
+ rmSync(join(conf.root, 'tmp'), { recursive: true, force: true })
202
+
203
+ if (existsSync(join(conf.root, 'public')))
204
+ await cp(join(conf.root, 'public'), outPath, { recursive: true, force: true })
205
+
206
+ const entryPath = join(outPath, getChunk(APP_ID))
207
+ const entry = Bun.file(entryPath)
208
+
209
+ if (await entry.exists()) {
210
+ let entryContent = await entry.text()
211
+
212
+ for (const r of routes) {
213
+ const rPath = getChunk(r.fullFilePath.replace(conf.root, ''))
214
+
215
+ if (r.props) {
216
+ const rFilePath = join(outPath, rPath)
217
+ const rText = await Bun.file(rFilePath).text()
218
+ await Bun.write(rFilePath, `export const props=${JSJSON(r.props)};${rText}`)
219
+ }
220
+
221
+ entryContent = entryContent.replace(r.fullFilePath, rPath)
222
+ }
223
+
224
+ await Bun.write(entryPath, entryContent)
225
+ }
226
+
227
+ // i18n
228
+ for (const locale of metadata.i18n.locales) {
229
+ const intlPath = join(conf.root, 'i18n', locale + '.ts')
230
+ if (!existsSync(intlPath)) continue
231
+
232
+ const intlMod = await import(intlPath)
233
+ await Bun.write(
234
+ join(outPath, locale + '.js'),
235
+ `var a=${JSJSON({
236
+ routes: intlMod.routes ?? intlMod.default?.routes,
237
+ messages: intlMod.messages ?? intlMod.default?.messages,
238
+ })};export{a as default}`,
239
+ )
240
+ }
241
+
242
+ return result
243
+ }
244
+
245
+ export default config
@@ -12,15 +12,15 @@ import { getAsset, getChunk } from './assets'
12
12
  import { resolveStaticOutputPath } from './meta'
13
13
  import { setLocale } from '../i18n/client'
14
14
 
15
- import type { ResolvedConfig } from 'vite'
16
15
  import type { IRoute, Metadata, RouteParams, StaticProps, SummaryMetadata } from './types'
16
+ import type { DutoOptions } from './bun-plugin'
17
17
 
18
18
 
19
19
  const brCompress = promisify(brotliCompress)
20
20
 
21
21
  export async function renderPage(
22
22
  meta: Metadata,
23
- config: ResolvedConfig,
23
+ config: DutoOptions,
24
24
  page: IRoute
25
25
  ) {
26
26
  const {
@@ -117,7 +117,7 @@ export async function renderPage(
117
117
 
118
118
  html = buildHtml(page, html, Object.keys(allProps).length ? allProps : undefined)
119
119
 
120
- const outFile = join(config.root, config.build.outDir, pathname +'.html')
120
+ const outFile = join(config.root, config.outDir, pathname +'.html')
121
121
  const outHTML = meta.minify ? minify.minify(Buffer.from(html), {}) : html
122
122
  await mkdir(dirname(outFile), {recursive: true})
123
123
  await Bun.write(outFile, outHTML)
@@ -3,7 +3,6 @@ import type { Platform } from '$/cli/types'
3
3
 
4
4
  export type PluginOptions = {
5
5
  root?: string,
6
- // Directory to scan for pages, relative to Vite root. Default: "./pages"
7
6
  pagesDir?: string,
8
7
  }
9
8
 
@@ -1,336 +0,0 @@
1
- // REF: https://github.com/tailwindlabs/tailwindcss/blob/main/packages/%40tailwindcss-vite/src/index.ts
2
-
3
- import { existsSync, rmSync } from 'node:fs'
4
- import { join } from 'node:path'
5
- import { defineConfig } from 'vite'
6
-
7
- import preact from '@preact/preset-vite'
8
- import tailwind from '@tailwindcss/vite'
9
- import { JSJSON } from 't0n'
10
-
11
- import { EXT_RE } from './constants'
12
- import { getChunk, setAsset, setChunk, setEntry } from './assets'
13
- import { renderPage } from '.'
14
- import { removeExports, resolveImport } from './ast'
15
- import { getMetadata } from './meta'
16
-
17
- import type {
18
- UserConfig, ConfigEnv, ResolvedConfig,
19
- // PreviewServer,
20
- // ViteDevServer,
21
- EnvironmentOptions,
22
- PluginOption,
23
- } from 'vite'
24
- import type { Metadata, PluginOptions } from './types'
25
- import { _pages, _root } from '@/utils'
26
- import { cp } from 'node:fs/promises'
27
-
28
- const VIRTUAL_ID = ':duto'
29
- const RESOLVED_ID = '\0'+ VIRTUAL_ID
30
-
31
- export const config = (opts: UserConfig & PluginOptions) => { // @ts-ignore
32
- if (!opts.root) opts.root = _root
33
- if (!opts.pagesDir) opts.pagesDir = 'pages',
34
-
35
- opts.logLevel = 'silent'
36
- opts.plugins = [
37
- ...(opts.plugins || []),
38
- preact(),
39
- tailwind(),
40
- plugin(opts),
41
- ]
42
-
43
- return defineConfig(opts)
44
- }
45
-
46
- // TODO: remover..
47
- export default config
48
-
49
- let metadata: Metadata
50
- export function meta() {
51
- return metadata
52
- }
53
-
54
- export function plugin(options: PluginOptions = {}): PluginOption[] {
55
- let config: ResolvedConfig
56
- const {
57
- root = _root,
58
- pagesDir = 'pages',
59
- } = options
60
-
61
- const PAGES_RE = new RegExp(`\/${pagesDir}\/.*\.(tsx?|jsx?)$`, 'i')
62
- const stripTarget = new Set(['getStaticProps'])
63
-
64
- return [
65
- {
66
- name: 'duto:preact-environment',
67
-
68
- configEnvironment(name, options) {
69
- const opts: EnvironmentOptions = {optimizeDeps: {}, resolve: {}}
70
-
71
- if (name === 'client') {
72
- opts.optimizeDeps!.include = [
73
- 'preact',
74
- 'preact/jsx-runtime',
75
- ]
76
- }
77
-
78
- opts.resolve = {dedupe: ['preact/compat', 'preact']}
79
-
80
- if (name === 'client')
81
- opts.optimizeDeps!.include!.push(
82
- 'preact/compat',
83
- // 'preact/test-utils',
84
- 'preact/compat/jsx-runtime',
85
- )
86
-
87
- if (
88
- !options.resolve?.noExternal &&
89
- (name === 'ssr' || name === 'prerender')
90
- ) {
91
- // noExternal React entrypoints to be bundled, resolved, and aliased by Vite
92
- opts.resolve!.noExternal = [
93
- 'react',
94
- 'react-dom',
95
- 'react-dom/test-utils',
96
- 'react/jsx-runtime',
97
- ]
98
- }
99
-
100
- return opts
101
- },
102
- },
103
-
104
- {
105
- name: 'duto:scan',
106
- enforce: 'pre',
107
-
108
- async config(conf: UserConfig, env: ConfigEnv) {
109
- metadata = await getMetadata(root as string, pagesDir)
110
-
111
- if (conf.build) {
112
- // conf.build.emptyOutDir = false
113
- conf.build.copyPublicDir = false
114
- conf.build.manifest = false
115
- conf.build.modulePreload = { polyfill: false }
116
-
117
- conf.build.minify = metadata.minify
118
- conf.build.cssMinify = metadata.minify
119
-
120
- conf.build.rolldownOptions = conf.build.rolldownOptions || {}
121
- conf.build.rolldownOptions.preserveEntrySignatures = 'exports-only' ///
122
- conf.build.rolldownOptions.input = [
123
- ...metadata.routes.map(r => r.is404 ? '' : r.fullFilePath).filter(Boolean),
124
- ':duto/client/app.tsx',
125
- // ':duto/not-found.tsx',
126
- // ...Object.keys(metadata.pages),
127
- // ...metadata.assets,
128
- ]
129
-
130
- const filePrefix = metadata.minify ? '' : '[name].'
131
- // conf.build.rolldownOptions.input = ':duto/client/app.tsx'
132
- conf.build.rolldownOptions.output = {
133
- hoistTransitiveImports: false,
134
- format: 'esm',
135
- minifyInternalExports: metadata.minify,
136
- preserveModulesRoot: '/',
137
- assetFileNames: filePrefix +'[hash][extname]',
138
- entryFileNames: filePrefix +'[hash].js',
139
- chunkFileNames: filePrefix +'[hash].js',
140
-
141
- // chunkFileNames: '[name].[hash].js',
142
- // entryFileNames: 'assets/[name].js',
143
- // chunkFileNames: 'assets/chunks/[name]-[hash].js',
144
- // assetFileNames: 'assets/[name][extname]',
145
-
146
- // manualChunks: {
147
- // vendor: ['preact', '@preact/signals', 'wouter-preact'],
148
- // },
149
- // preserveModules: true,
150
- }
151
- }
152
-
153
- return conf
154
- },
155
-
156
- async configResolved(c) {
157
- config = c
158
- },
159
-
160
- resolveId(id) {
161
- if (id === VIRTUAL_ID) return RESOLVED_ID
162
-
163
- if (id.startsWith(':root')) {
164
- for (const f of [
165
- 'style.css',
166
- 'styles.css',
167
- 'styles/index.css',
168
- 'style/index.css',
169
- 'tailwind.css',
170
- 'tailwindcss.css',
171
- 'styles/tailwind.css',
172
- 'styles/tailwindcss.css',
173
- ]) {
174
- const path = join(config.root, f)
175
- if (existsSync(path)) {
176
- setEntry(id, path)
177
- return path
178
- }
179
- }
180
-
181
- return null
182
- }
183
-
184
- if (!id.startsWith(VIRTUAL_ID)) return null
185
-
186
- if (EXT_RE.test(id) && id.includes('/')) {
187
- const path = join(_pages, id.replace(VIRTUAL_ID, ''))
188
- setEntry(id, path)
189
- return path
190
- }
191
-
192
- if (id.endsWith('/hmr') && metadata.hmr)
193
- return join(_pages, 'client', id.replace(VIRTUAL_ID, '') + '.ts')
194
-
195
- return id.replace(VIRTUAL_ID, RESOLVED_ID)
196
- },
197
-
198
- async load(id) {
199
- if (!id.startsWith(RESOLVED_ID)) return
200
-
201
- if (id.endsWith('/i18n'))
202
- return metadata.i18n.string
203
-
204
- return metadata.string
205
- },
206
-
207
- generateBundle(_, bundle) {
208
- // const moduleIds = this.getModuleIds()
209
- // for (const id of moduleIds)
210
- // console.error(id)
211
-
212
- // TODO: rever isso...
213
- for (const key in bundle) {
214
- const file = bundle[key]
215
-
216
- if (file.type === 'chunk' && file.isEntry) {
217
- setChunk(file.facadeModuleId, '/'+ file.fileName)
218
- continue
219
- }
220
-
221
- if (file.type === 'asset' && file?.originalFileName?.endsWith('duto/pages/client/app.tsx'))
222
- file.source += 'i-i{display:contents}'
223
-
224
- if (file.type === 'asset' && file.fileName.endsWith('.css')) {
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
- continue
233
- }
234
-
235
- // console.error(file.fileName)
236
- // console.error(file.type)
237
- // console.error(file.viteMetadata)
238
- // if (file.type !== 'chunk' || !file.isEntry) continue
239
- }
240
- },
241
-
242
- },
243
-
244
- {
245
- name: 'duto:build',
246
- enforce: 'pre',
247
- apply: 'build',
248
-
249
- async buildStart() {
250
- // if (!server) {
251
- // const { createServer } = await import('vite')
252
- // server = await createServer({ server: {middlewareMode: true} })
253
- // }
254
-
255
-
256
- // teste de import de assets
257
- // for (const file of metadata.assets) {
258
- // const id = await this.resolve(file)
259
- // if (!id) continue
260
-
261
- // console.error(id)
262
- // console.error(relative(metadata.root, file).replace(/\.(ts|tsx)$/, '.js'),)
263
-
264
- // this.emitFile({
265
- // type: 'chunk',
266
- // id: id.id,
267
- // implicitlyLoadedAfterOneOf: [
268
- // ':duto/app.tsx',
269
- // ]
270
- // // fileName: 'assets/[name]-[hash][extname]',
271
- // // fileName: relative(metadata.root, file).replace(/\.(ts|tsx)$/, '.js'),
272
- // })
273
- // }
274
-
275
- },
276
-
277
- async closeBundle() {
278
- for (const [_filePath, page] of metadata.pages)
279
- await renderPage(metadata, config, page)
280
- // const mod = await server.ssrLoadModule(_filePath)
281
-
282
- rmSync(join(metadata.root, 'tmp'), {recursive: true, force: true})
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
-
322
- },
323
-
324
- // TODO: precisa ser aqui mesmo pois se a pagina for importada no entry, pode incluir codigo nao usado no chunck/bundle
325
- async transform(code, id) {
326
- if (!PAGES_RE.test(id) || id.includes('node_modules')) return null
327
- // console.log(this.getModuleInfo(id))
328
-
329
- const stripped = removeExports(id, code, stripTarget)
330
-
331
- return (!stripped || stripped === code) ? null : { code: stripped, map: null }
332
- },
333
-
334
- },
335
- ]
336
- }