duto 0.260906.0 → 0.261001.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 +34 -12
- package/package.json +9 -3
- package/src/cli.ts +10 -7
- package/src/commands/new.ts +172 -0
- package/src/commands/templates/pages/.editorconfig +21 -0
- package/src/commands/templates/pages/README.md +21 -0
- package/src/commands/templates/pages/_gitignore +24 -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/i18n/en.ts +17 -0
- package/src/commands/templates/pages/i18n/index.ts +19 -0
- package/src/commands/templates/pages/i18n/pt-br.ts +17 -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/index.tsx +16 -0
- package/src/commands/templates/pages/pages/layout.tsx +24 -0
- package/src/commands/templates/pages/style.css +24 -0
- package/src/commands/templates/pages/tsconfig.json +54 -0
- package/src/commands/templates/pages/types.ts +8 -0
- package/src/commands/templates/server/base/.editorconfig +21 -0
- package/src/commands/templates/server/base/.env.dev.example +2 -0
- package/src/commands/templates/server/base/.env.prod.example +2 -0
- package/src/commands/templates/server/base/README.md +29 -0
- package/src/commands/templates/server/base/_gitignore +100 -0
- package/src/commands/templates/server/base/actions/blog/index.get.ts +9 -0
- package/src/commands/templates/server/base/actions/blog/post/[id].get.ts +9 -0
- package/src/commands/templates/server/base/actions/index.get.ts +5 -0
- package/src/commands/templates/server/base/configs/duto.ts +11 -0
- package/src/commands/templates/server/base/configs/roles.ts +5 -0
- package/src/commands/templates/server/base/package.json +24 -0
- package/src/commands/templates/server/base/tsconfig.json +42 -0
- package/src/commands/templates/server/workerd/wrangler.toml +47 -0
- package/src/commands/upgrade.ts +30 -0
- package/src/index.ts +7 -2
- package/src/pages/{commands → cli/commands}/build.ts +13 -7
- package/src/pages/{commands → cli/commands}/dev.ts +19 -6
- package/src/pages/{commands → cli/commands}/preview.ts +6 -4
- package/src/pages/cli/index.ts +10 -0
- package/src/pages/cli/utils.ts +126 -0
- package/src/pages/client/app.tsx +27 -74
- package/src/pages/client/router.ts +15 -281
- package/src/pages/compiler/aliases.ts +2 -8
- package/src/pages/compiler/ast.ts +1 -538
- package/src/pages/compiler/constants.ts +95 -13
- package/src/pages/compiler/file.ts +1 -1
- package/src/pages/compiler/index.ts +20 -128
- package/src/pages/compiler/meta.ts +39 -228
- package/src/pages/compiler/routes.ts +4 -5
- package/src/pages/compiler/types.ts +11 -101
- package/src/pages/compiler/vite-plugin.ts +90 -69
- package/src/pages/i18n/client.ts +19 -7
- package/src/pages/i18n/element.ts +1 -1
- package/src/pages/i18n/server.ts +22 -7
- package/src/pages/index.ts +1 -2
- package/src/pages/preact/server.ts +1 -1
- package/src/server/app.ts +5 -10
- package/src/server/auth/ability.ts +1 -1
- package/src/server/auth/token.ts +5 -5
- package/src/server/cli/commands/build.ts +2 -2
- package/src/server/cli/commands/deploy.ts +1 -2
- package/src/server/cli/commands/dev.ts +6 -4
- package/src/server/cli/commands/routes.ts +0 -1
- package/src/server/cli/utils.ts +2 -8
- package/src/server/config.ts +18 -3
- package/src/server/entry.ts +3 -3
- package/src/server/open-api/register.ts +3 -3
- package/src/server/open-api/spec.ts +11 -4
- package/src/server/request.ts +46 -14
- package/src/server/routes.ts +8 -31
- package/src/server/utils.ts +25 -0
- package/src/utils.ts +43 -2
- package/src/pages/commands/dev-old.ts +0 -213
- package/src/pages/compiler/island.ts +0 -108
- package/src/pages/compiler/utils.ts +0 -5
- package/src/server/http.ts +0 -23
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
// import type { ComponentType, FunctionComponent, VNode } from 'preact'
|
|
2
|
+
import type { Platform } from '$/cli/types'
|
|
2
3
|
import type { FunctionComponent, VNode } from 'preact'
|
|
3
4
|
|
|
4
5
|
export type PluginOptions = {
|
|
@@ -7,17 +8,6 @@ export type PluginOptions = {
|
|
|
7
8
|
pagesDir?: string,
|
|
8
9
|
}
|
|
9
10
|
|
|
10
|
-
export type DepMetadata = {
|
|
11
|
-
path: string,
|
|
12
|
-
ref: string,
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
export type FileMetadata = {
|
|
16
|
-
path: string,
|
|
17
|
-
content: string,
|
|
18
|
-
deps: Map<string, DepMetadata>,
|
|
19
|
-
}
|
|
20
|
-
|
|
21
11
|
export type I18nMetadata = {
|
|
22
12
|
fallbackLocale: string,
|
|
23
13
|
locales: string[],
|
|
@@ -41,16 +31,22 @@ export type SummaryMetadata = {
|
|
|
41
31
|
export type Metadata = {
|
|
42
32
|
root: string,
|
|
43
33
|
dir: string,
|
|
34
|
+
type: 'pages' | 'server',
|
|
35
|
+
platform: Platform,
|
|
36
|
+
dev: {
|
|
37
|
+
host: string,
|
|
38
|
+
port: number,
|
|
39
|
+
},
|
|
40
|
+
hmr: boolean,
|
|
41
|
+
minify: boolean,
|
|
42
|
+
|
|
44
43
|
aliases: Record<string, string>,
|
|
45
|
-
map: Map<string, FileMetadata>,
|
|
46
|
-
violations: Map<string, DepMetadata[]>,
|
|
47
44
|
layouts: Map<string, IRoute>, // all layouts
|
|
48
45
|
nf: Map<string, IRoute>, // all 404
|
|
49
46
|
pages: Map<string, IRoute>, // all
|
|
50
47
|
spages: Set<string>, // ssg pages
|
|
51
48
|
routes: Routes, // client-side
|
|
52
49
|
// routes: Map<string, IRoute>, // client-side
|
|
53
|
-
assets: string[],
|
|
54
50
|
i18n: I18nMetadata,
|
|
55
51
|
string: string,
|
|
56
52
|
summary: Map<string, SummaryMetadata>,
|
|
@@ -59,8 +55,7 @@ export type Metadata = {
|
|
|
59
55
|
export type Page<P = {}> = FunctionComponent<VNode<P>>
|
|
60
56
|
// A page module as imported by Bun/Vite
|
|
61
57
|
export type PageModule<P = {}> = {
|
|
62
|
-
default: Page<P>,
|
|
63
|
-
// default: ComponentType<VNode<any>>,
|
|
58
|
+
default: Page<P>, // ComponentType<VNode<any>>,
|
|
64
59
|
getStaticProps?: GetStaticProps,
|
|
65
60
|
getProps?: GetProps,
|
|
66
61
|
}
|
|
@@ -76,12 +71,6 @@ export type GetStaticProps =
|
|
|
76
71
|
| (() => StaticProps[] | Promise<StaticProps[]>)
|
|
77
72
|
export type StaticProps = Record<string, unknown>
|
|
78
73
|
|
|
79
|
-
/** browser-only */
|
|
80
|
-
// export type $duto = {
|
|
81
|
-
// sroutes: Set<string>,
|
|
82
|
-
// navigate: {},
|
|
83
|
-
// route: { pattern: string, params: RouteParams, path: string },
|
|
84
|
-
// }
|
|
85
74
|
export type GetProps = (params: RouteParams) => StaticProps | Promise<StaticProps>
|
|
86
75
|
export type RouteParams = Record<string, string | string[] | undefined>
|
|
87
76
|
|
|
@@ -89,14 +78,9 @@ export type Routes = IRoute[]
|
|
|
89
78
|
export type IRoute = {
|
|
90
79
|
content: string,
|
|
91
80
|
mod: PageModule,
|
|
92
|
-
/** True if the page exports getStaticProps */
|
|
93
81
|
hasGetStaticProps: boolean,
|
|
94
|
-
/** True if the page exports getProps */
|
|
95
82
|
hasGetProps: boolean,
|
|
96
83
|
isClientOnly: boolean,
|
|
97
|
-
islands: ImportUsage[], // TODO mover para cá
|
|
98
|
-
// islands: IslandMeta[],
|
|
99
|
-
// safeFilePath: string,
|
|
100
84
|
layouts: IRoute[],
|
|
101
85
|
importName: string,
|
|
102
86
|
|
|
@@ -133,77 +117,3 @@ export type RouteParam = {
|
|
|
133
117
|
catchAll: boolean,
|
|
134
118
|
value?: string | string[],
|
|
135
119
|
}
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
export interface Usage {
|
|
140
|
-
type: 'jsx' | 'call' | 'reference'
|
|
141
|
-
source: string,
|
|
142
|
-
imported: string,
|
|
143
|
-
local: string,
|
|
144
|
-
start: number,
|
|
145
|
-
end: number,
|
|
146
|
-
text: string,
|
|
147
|
-
node: unknown,
|
|
148
|
-
parent: unknown,
|
|
149
|
-
}
|
|
150
|
-
|
|
151
|
-
export interface ImportUsage {
|
|
152
|
-
source: string,
|
|
153
|
-
imported: string,
|
|
154
|
-
local: string,
|
|
155
|
-
importStart: number,
|
|
156
|
-
importEnd: number,
|
|
157
|
-
importText: string,
|
|
158
|
-
usages: Usage[],
|
|
159
|
-
}
|
|
160
|
-
|
|
161
|
-
export interface IslandMeta {
|
|
162
|
-
/** Identificador curto e único (base64url de 7 chars) */
|
|
163
|
-
id: string
|
|
164
|
-
/** URL pública do asset JS do componente */
|
|
165
|
-
url: string
|
|
166
|
-
/** Nome do export usado */
|
|
167
|
-
export: string
|
|
168
|
-
/** Props serializadas como JSON (HTML-encoded) */
|
|
169
|
-
props: string
|
|
170
|
-
/** HTML do SSR do componente */
|
|
171
|
-
ssrHtml: string
|
|
172
|
-
}
|
|
173
|
-
|
|
174
|
-
// export interface ReplaceIslandsOptions {
|
|
175
|
-
// /**
|
|
176
|
-
// * Função que resolve o nome do componente para a URL pública do asset JS.
|
|
177
|
-
// * Ex: 'Counter' → '/assets/Counter.BQxoyJbC.js'
|
|
178
|
-
// */
|
|
179
|
-
// resolveUrl: (exportName: string) => string
|
|
180
|
-
// /**
|
|
181
|
-
// * Função que carrega o módulo do componente para fazer SSR.
|
|
182
|
-
// * Recebe o source da importação (ex: '@/components/counter2') e o nome do export.
|
|
183
|
-
// */
|
|
184
|
-
// loadComponent: (source: string, exportName: string) => Promise<unknown>
|
|
185
|
-
// }
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
// export type Island = {
|
|
189
|
-
// /** Unique id — written as data-island on the placeholder div */
|
|
190
|
-
// id: string,
|
|
191
|
-
// /** Component display name — used by the client to look up the component */
|
|
192
|
-
// name: string,
|
|
193
|
-
// /** Props serialised into the placeholder for client hydration */
|
|
194
|
-
// props: Record<string, unknown>,
|
|
195
|
-
// }
|
|
196
|
-
|
|
197
|
-
// export type RenderResult = {
|
|
198
|
-
// html: string,
|
|
199
|
-
// islands: Island[],
|
|
200
|
-
// /** true if any islands were found (= client JS needed) */
|
|
201
|
-
// hasIslands: boolean,
|
|
202
|
-
// }
|
|
203
|
-
|
|
204
|
-
// export type IslandMeta = {
|
|
205
|
-
// id: string,
|
|
206
|
-
// name: string,
|
|
207
|
-
// props: Record<string, unknown>,
|
|
208
|
-
// }
|
|
209
|
-
|
|
@@ -1,34 +1,33 @@
|
|
|
1
1
|
// REF: https://github.com/tailwindlabs/tailwindcss/blob/main/packages/%40tailwindcss-vite/src/index.ts
|
|
2
2
|
|
|
3
|
-
import { existsSync,
|
|
3
|
+
import { existsSync, rmSync } from 'node:fs'
|
|
4
4
|
import { join } from 'node:path'
|
|
5
5
|
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
|
-
import type { IncomingMessage, ServerResponse } from 'node:http'
|
|
17
17
|
import type {
|
|
18
|
-
// Plugin,
|
|
19
18
|
UserConfig, ConfigEnv, ResolvedConfig,
|
|
20
|
-
PreviewServer,
|
|
19
|
+
// PreviewServer,
|
|
21
20
|
// ViteDevServer,
|
|
22
21
|
EnvironmentOptions,
|
|
23
22
|
PluginOption,
|
|
24
23
|
} from 'vite'
|
|
25
24
|
import type { Metadata, PluginOptions } from './types'
|
|
26
|
-
import {
|
|
25
|
+
import { _pages, _root } from '@/utils'
|
|
27
26
|
|
|
28
27
|
const VIRTUAL_ID = ':duto'
|
|
29
28
|
const RESOLVED_ID = '\0'+ VIRTUAL_ID
|
|
30
29
|
|
|
31
|
-
export const config = (opts: UserConfig & PluginOptions) => {
|
|
30
|
+
export const config = (opts: UserConfig & PluginOptions) => { // @ts-ignore
|
|
32
31
|
if (!opts.root) opts.root = _root
|
|
33
32
|
if (!opts.pagesDir) opts.pagesDir = 'pages',
|
|
34
33
|
|
|
@@ -43,6 +42,7 @@ export const config = (opts: UserConfig & PluginOptions) => {
|
|
|
43
42
|
return defineConfig(opts)
|
|
44
43
|
}
|
|
45
44
|
|
|
45
|
+
// TODO: remover..
|
|
46
46
|
export default config
|
|
47
47
|
|
|
48
48
|
let metadata: Metadata
|
|
@@ -51,15 +51,12 @@ export function meta() {
|
|
|
51
51
|
}
|
|
52
52
|
|
|
53
53
|
export function plugin(options: PluginOptions = {}): PluginOption[] {
|
|
54
|
-
// let server: ViteDevServer
|
|
55
54
|
let config: ResolvedConfig
|
|
56
55
|
const {
|
|
57
|
-
root = _root,
|
|
56
|
+
root = _root,
|
|
58
57
|
pagesDir = 'pages',
|
|
59
58
|
} = options
|
|
60
59
|
|
|
61
|
-
// let metadata: Metadata
|
|
62
|
-
|
|
63
60
|
const PAGES_RE = new RegExp(`\/${pagesDir}\/.*\.(tsx?|jsx?)$`, 'i')
|
|
64
61
|
const stripTarget = new Set(['getStaticProps'])
|
|
65
62
|
|
|
@@ -101,44 +98,14 @@ export function plugin(options: PluginOptions = {}): PluginOption[] {
|
|
|
101
98
|
|
|
102
99
|
return opts
|
|
103
100
|
},
|
|
104
|
-
|
|
105
|
-
// configurePreviewServer(server: PreviewServer) {
|
|
106
|
-
// const distDir = join(server.config.root, 'dist')
|
|
107
|
-
|
|
108
|
-
// server.middlewares.use((req: IncomingMessage, res: ServerResponse, next: (err?: any) => void) => {
|
|
109
|
-
// if (req.url && !existsSync(join(distDir, req.url.split('?')[0]))) {
|
|
110
|
-
// const notFound = join(distDir, '404.html')
|
|
111
|
-
|
|
112
|
-
// if (existsSync(notFound)) {
|
|
113
|
-
// res.statusCode = 404
|
|
114
|
-
// res.setHeader('Content-Type', 'text/html')
|
|
115
|
-
// .end(readFileSync(notFound, 'utf-8'))
|
|
116
|
-
// return
|
|
117
|
-
// }
|
|
118
|
-
// }
|
|
119
|
-
|
|
120
|
-
// next()
|
|
121
|
-
// })
|
|
122
|
-
// },
|
|
123
|
-
|
|
124
101
|
},
|
|
125
102
|
|
|
126
103
|
{
|
|
127
104
|
name: 'duto:scan',
|
|
128
105
|
enforce: 'pre',
|
|
129
106
|
|
|
130
|
-
// configureServer(_server) {
|
|
131
|
-
// server = _server
|
|
132
|
-
// // servers.push(server)
|
|
133
|
-
// },
|
|
134
|
-
|
|
135
107
|
async config(conf: UserConfig, env: ConfigEnv) {
|
|
136
|
-
|
|
137
|
-
metadata = await getMetadata(root, pagesDir)
|
|
138
|
-
|
|
139
|
-
// console.log(metadata.map)
|
|
140
|
-
// console.log(metadata.violations)
|
|
141
|
-
// console.log(Array.from(metadata.violations).filter(file => EXT_RE.test(file)))
|
|
108
|
+
metadata = await getMetadata(root as string, pagesDir)
|
|
142
109
|
|
|
143
110
|
if (conf.build) {
|
|
144
111
|
// conf.build.emptyOutDir = false
|
|
@@ -146,44 +113,47 @@ export function plugin(options: PluginOptions = {}): PluginOption[] {
|
|
|
146
113
|
conf.build.manifest = false
|
|
147
114
|
conf.build.modulePreload = { polyfill: false }
|
|
148
115
|
|
|
149
|
-
conf.build.minify =
|
|
150
|
-
conf.build.cssMinify =
|
|
116
|
+
conf.build.minify = metadata.minify
|
|
117
|
+
conf.build.cssMinify = metadata.minify
|
|
151
118
|
|
|
152
119
|
conf.build.rolldownOptions = conf.build.rolldownOptions || {}
|
|
153
120
|
conf.build.rolldownOptions.preserveEntrySignatures = 'exports-only' ///
|
|
154
121
|
conf.build.rolldownOptions.input = [
|
|
122
|
+
...metadata.routes.map(r => r.is404 ? '' : r.fullFilePath).filter(Boolean),
|
|
155
123
|
':duto/client/app.tsx',
|
|
156
124
|
// ':duto/not-found.tsx',
|
|
157
|
-
...Object.keys(metadata.pages),
|
|
158
|
-
...metadata.assets,
|
|
125
|
+
// ...Object.keys(metadata.pages),
|
|
126
|
+
// ...metadata.assets,
|
|
159
127
|
]
|
|
128
|
+
|
|
129
|
+
const filePrefix = metadata.minify ? '' : '[name].'
|
|
160
130
|
// conf.build.rolldownOptions.input = ':duto/client/app.tsx'
|
|
161
131
|
conf.build.rolldownOptions.output = {
|
|
162
132
|
hoistTransitiveImports: false,
|
|
163
133
|
format: 'esm',
|
|
164
|
-
minifyInternalExports:
|
|
165
|
-
|
|
166
|
-
// manualChunks: {
|
|
167
|
-
// vendor: ['preact', '@preact/signals', 'wouter-preact'],
|
|
168
|
-
// },
|
|
169
|
-
// preserveModules: true,
|
|
134
|
+
minifyInternalExports: metadata.minify,
|
|
170
135
|
preserveModulesRoot: '/',
|
|
171
|
-
assetFileNames: '[hash][extname]',
|
|
172
|
-
entryFileNames: '[hash].js',
|
|
173
|
-
chunkFileNames: '[hash].js',
|
|
136
|
+
assetFileNames: filePrefix +'[hash][extname]',
|
|
137
|
+
entryFileNames: filePrefix +'[hash].js',
|
|
138
|
+
chunkFileNames: filePrefix +'[hash].js',
|
|
174
139
|
|
|
175
140
|
// chunkFileNames: '[name].[hash].js',
|
|
176
141
|
// entryFileNames: 'assets/[name].js',
|
|
177
142
|
// chunkFileNames: 'assets/chunks/[name]-[hash].js',
|
|
178
143
|
// assetFileNames: 'assets/[name][extname]',
|
|
144
|
+
|
|
145
|
+
// manualChunks: {
|
|
146
|
+
// vendor: ['preact', '@preact/signals', 'wouter-preact'],
|
|
147
|
+
// },
|
|
148
|
+
// preserveModules: true,
|
|
179
149
|
}
|
|
180
150
|
}
|
|
181
151
|
|
|
182
152
|
return conf
|
|
183
153
|
},
|
|
184
154
|
|
|
185
|
-
async configResolved(
|
|
186
|
-
config =
|
|
155
|
+
async configResolved(c) {
|
|
156
|
+
config = c
|
|
187
157
|
},
|
|
188
158
|
|
|
189
159
|
resolveId(id) {
|
|
@@ -213,13 +183,13 @@ export function plugin(options: PluginOptions = {}): PluginOption[] {
|
|
|
213
183
|
if (!id.startsWith(VIRTUAL_ID)) return null
|
|
214
184
|
|
|
215
185
|
if (EXT_RE.test(id) && id.includes('/')) {
|
|
216
|
-
const path = join(
|
|
186
|
+
const path = join(_pages, id.replace(VIRTUAL_ID, ''))
|
|
217
187
|
setEntry(id, path)
|
|
218
188
|
return path
|
|
219
189
|
}
|
|
220
190
|
|
|
221
|
-
if (id.endsWith('/hmr') &&
|
|
222
|
-
return join(
|
|
191
|
+
if (id.endsWith('/hmr') && metadata.hmr)
|
|
192
|
+
return join(_pages, 'client', id.replace(VIRTUAL_ID, '') + '.ts')
|
|
223
193
|
|
|
224
194
|
return id.replace(VIRTUAL_ID, RESOLVED_ID)
|
|
225
195
|
},
|
|
@@ -238,15 +208,18 @@ export function plugin(options: PluginOptions = {}): PluginOption[] {
|
|
|
238
208
|
// for (const id of moduleIds)
|
|
239
209
|
// console.error(id)
|
|
240
210
|
|
|
211
|
+
// TODO: rever isso...
|
|
241
212
|
for (const key in bundle) {
|
|
242
213
|
const file = bundle[key]
|
|
214
|
+
|
|
243
215
|
if (file.type === 'chunk' && file.isEntry) {
|
|
244
216
|
setChunk(file.facadeModuleId, '/'+ file.fileName)
|
|
245
217
|
continue
|
|
246
218
|
}
|
|
247
219
|
|
|
248
|
-
if (file.type === 'asset' && file?.originalFileName?.endsWith('duto/client/app.tsx'))
|
|
249
|
-
file.source += '
|
|
220
|
+
if (file.type === 'asset' && file?.originalFileName?.endsWith('duto/pages/client/app.tsx'))
|
|
221
|
+
file.source += 'i-i{display:contents}'
|
|
222
|
+
// file.source += 'd-b,d-p,i-d,i-s,i-i{display:contents}'
|
|
250
223
|
|
|
251
224
|
if (file.type === 'asset' && file.fileName.endsWith('.css')) {
|
|
252
225
|
setAsset(resolveImport(file.originalFileName as string, metadata.root, metadata.aliases), '/'+ file.fileName)
|
|
@@ -262,6 +235,35 @@ export function plugin(options: PluginOptions = {}): PluginOption[] {
|
|
|
262
235
|
|
|
263
236
|
},
|
|
264
237
|
|
|
238
|
+
// {
|
|
239
|
+
// name: 'duto:routes-inject',
|
|
240
|
+
// enforce: 'post',
|
|
241
|
+
// generateBundle(_, bundle) {
|
|
242
|
+
// // monta o mapa a partir dos inputs de metadata.routes
|
|
243
|
+
// const routeMap: Record<string, string> = {}
|
|
244
|
+
// for (const r of metadata.routes) {
|
|
245
|
+
// if (r.is404) continue
|
|
246
|
+
// // encontra o chunk cujo facadeModuleId bate com r.fullFilePath
|
|
247
|
+
// for (const [fileName, chunk] of Object.entries(bundle)) {
|
|
248
|
+
// if (chunk.type === 'chunk' && chunk.facadeModuleId === r.fullFilePath) {
|
|
249
|
+
// routeMap[r.path] = fileName
|
|
250
|
+
// break
|
|
251
|
+
// }
|
|
252
|
+
// }
|
|
253
|
+
// }
|
|
254
|
+
|
|
255
|
+
// // injeta no chunk do app
|
|
256
|
+
// for (const chunk of Object.values(bundle)) {
|
|
257
|
+
// if (chunk.type !== 'chunk') continue
|
|
258
|
+
// if (chunk.facadeModuleId?.includes('app.tsx') || chunk.code.includes('__DUTO_ROUTES__')) {
|
|
259
|
+
// chunk.code = chunk.code.replace(
|
|
260
|
+
// '__DUTO_ROUTES__',
|
|
261
|
+
// JSON.stringify(routeMap)
|
|
262
|
+
// )
|
|
263
|
+
// }
|
|
264
|
+
// }
|
|
265
|
+
// }
|
|
266
|
+
// },
|
|
265
267
|
{
|
|
266
268
|
name: 'duto:build',
|
|
267
269
|
enforce: 'pre',
|
|
@@ -296,13 +298,36 @@ export function plugin(options: PluginOptions = {}): PluginOption[] {
|
|
|
296
298
|
},
|
|
297
299
|
|
|
298
300
|
async closeBundle() {
|
|
299
|
-
for (const [_filePath, page] of metadata.pages)
|
|
301
|
+
for (const [_filePath, page] of metadata.pages)
|
|
300
302
|
await renderPage(metadata, config, page)
|
|
301
303
|
// const mod = await server.ssrLoadModule(_filePath)
|
|
302
|
-
}
|
|
303
304
|
|
|
304
305
|
rmSync(join(metadata.root, 'tmp'), {recursive: true, force: true})
|
|
305
306
|
// await server.close()
|
|
307
|
+
|
|
308
|
+
const outPath = join(config.root, config.build.outDir)
|
|
309
|
+
const entry = Bun.file(join(outPath, getChunk(':duto/client/app.tsx')))
|
|
310
|
+
let entryContent = await entry.text()
|
|
311
|
+
|
|
312
|
+
for (const r of metadata.routes.filter(r => !r.is404))
|
|
313
|
+
entryContent = entryContent.replace(r.fullFilePath, getChunk(r.fullFilePath.replace(config.root, '')))
|
|
314
|
+
|
|
315
|
+
await entry.write(entryContent)
|
|
316
|
+
|
|
317
|
+
// i18n
|
|
318
|
+
for (const locale of metadata.i18n.locales) {
|
|
319
|
+
const intlPath = join(metadata.root, 'i18n', locale + '.ts')
|
|
320
|
+
if (existsSync(intlPath)) {
|
|
321
|
+
const intlMod = await import(intlPath)
|
|
322
|
+
await Bun.write(
|
|
323
|
+
join(outPath, locale +'.js'),
|
|
324
|
+
`var a=${JSJSON({
|
|
325
|
+
routes: intlMod.routes ?? intlMod.default?.routes,
|
|
326
|
+
messages: intlMod.messages ?? intlMod.default?.messages,
|
|
327
|
+
})};export { a as default }`
|
|
328
|
+
)
|
|
329
|
+
}
|
|
330
|
+
}
|
|
306
331
|
},
|
|
307
332
|
|
|
308
333
|
// TODO: precisa ser aqui mesmo pois se a pagina for importada no entry, pode incluir codigo nao usado no chunck/bundle
|
|
@@ -311,12 +336,8 @@ export function plugin(options: PluginOptions = {}): PluginOption[] {
|
|
|
311
336
|
// console.log(this.getModuleInfo(id))
|
|
312
337
|
|
|
313
338
|
const stripped = removeExports(id, code, stripTarget)
|
|
314
|
-
if (!stripped || stripped === code) return null
|
|
315
339
|
|
|
316
|
-
return {
|
|
317
|
-
code: stripped,
|
|
318
|
-
map: null, // source maps omitted for brevity, add if needed
|
|
319
|
-
}
|
|
340
|
+
return (!stripped || stripped === code) ? null : { code: stripped, map: null }
|
|
320
341
|
},
|
|
321
342
|
|
|
322
343
|
},
|
package/src/pages/i18n/client.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { h, type JSX } from 'preact'
|
|
2
|
-
import { local } from '#/storage'
|
|
2
|
+
import { local } from '#/client/storage'
|
|
3
3
|
import { navigate } from '..'
|
|
4
4
|
|
|
5
5
|
const accepted = (lang: string | undefined, locales: string[] = [], fallback: string) => lang && locales.includes(lang) ? lang : fallback
|
|
@@ -84,12 +84,22 @@ export const getLocale = () => {
|
|
|
84
84
|
}
|
|
85
85
|
}
|
|
86
86
|
|
|
87
|
-
export const setLocale = (lang: string) => {
|
|
87
|
+
export const setLocale = async (lang: string) => {
|
|
88
|
+
await loadLang(lang)
|
|
88
89
|
cache(lang)
|
|
89
90
|
_locale = accepted(lang, _locales, _detected)
|
|
90
91
|
}
|
|
91
92
|
|
|
92
|
-
|
|
93
|
+
async function loadLang(lang: string) {
|
|
94
|
+
if (!_routes[lang] || !_messages[lang]) {
|
|
95
|
+
const intlMod = await import('/'+ lang +'.js')
|
|
96
|
+
if (!intlMod) return
|
|
97
|
+
_routes[lang] = intlMod.routes ?? intlMod.default?.routes
|
|
98
|
+
_messages[lang] = intlMod.messages ?? intlMod.default?.messages
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
export default async function i18n(
|
|
93
103
|
fallbackLocale: string,
|
|
94
104
|
locales: string[],
|
|
95
105
|
messages: Record<string, Record<string, string>>,
|
|
@@ -103,6 +113,8 @@ export default function i18n(
|
|
|
103
113
|
_detected = detect(locales, fallbackLocale)
|
|
104
114
|
_locale = typeof window === 'undefined' ? fallbackLocale : getLocale()
|
|
105
115
|
|
|
116
|
+
await loadLang(_locale)
|
|
117
|
+
|
|
106
118
|
// console.error(routes)
|
|
107
119
|
// console.error(routeMap)
|
|
108
120
|
}
|
|
@@ -173,10 +185,10 @@ type TProps = {
|
|
|
173
185
|
}
|
|
174
186
|
|
|
175
187
|
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
|
|
188
|
+
if (!key) return ''
|
|
189
|
+
if (key.startsWith('_')) return key.replace(/^_+/, '')
|
|
190
|
+
const msg = _messages[lang] ?? _messages[_locale]
|
|
191
|
+
return (msg && key in msg ? msg[key] : key) as string
|
|
180
192
|
}
|
|
181
193
|
|
|
182
194
|
const interpolate = (template: string, params: Record<string, Primitives> | Primitives[]): string => {
|
package/src/pages/i18n/server.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { join } from 'node:path'
|
|
2
2
|
import { IMPORT, JSJSON } from 't0n'
|
|
3
3
|
|
|
4
|
-
import type { Metadata, I18nMetadata } from '
|
|
4
|
+
import type { Metadata, I18nMetadata } from '#/compiler/types'
|
|
5
5
|
import { existsSync } from 'node:fs'
|
|
6
6
|
|
|
7
7
|
export default async function getI18n(meta: Metadata) {
|
|
@@ -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
|
@@ -8,7 +8,7 @@ import { getContext, incrementIslandId } from './context'
|
|
|
8
8
|
import { restoreSignalsOnProps, serializeSignals } from './signals'
|
|
9
9
|
import StaticHtml from './static-html'
|
|
10
10
|
import type { IslandAttrs } from './types'
|
|
11
|
-
import type { IslandMeta } from '
|
|
11
|
+
import type { IslandMeta } from '#/compiler/types'
|
|
12
12
|
// import type { AstroPreactAttrs, RendererContext } from './types.js'
|
|
13
13
|
// import { createFilter } from '@astrojs/internal-helpers/create-filter'
|
|
14
14
|
|
package/src/server/app.ts
CHANGED
|
@@ -1,18 +1,16 @@
|
|
|
1
1
|
import { Hono } from 'hono'
|
|
2
|
-
import { Envir } from 't0n'
|
|
3
2
|
import type {
|
|
4
3
|
Env, Context, Next,
|
|
5
4
|
HTTPResponseError,
|
|
6
5
|
ServerOptions, Route
|
|
7
6
|
} from './types'
|
|
8
|
-
import request
|
|
7
|
+
import request from './request'
|
|
9
8
|
import response from './response'
|
|
10
9
|
import { isDev } from './utils/environment'
|
|
11
|
-
import { getVerb } from './
|
|
10
|
+
import { getVerb } from './utils'
|
|
12
11
|
import { registerMiddleware, resolveHandle, resolveMw } from './handle'
|
|
13
12
|
|
|
14
|
-
|
|
15
|
-
const EHandler = async (e: Error | HTTPResponseError) => {
|
|
13
|
+
async function EHandler(e: Error | HTTPResponseError) {
|
|
16
14
|
console.error(e)
|
|
17
15
|
|
|
18
16
|
switch (true) {
|
|
@@ -59,7 +57,7 @@ const EHandler = async (e: Error | HTTPResponseError) => {
|
|
|
59
57
|
// stack: isDev (? e.stack : undefined
|
|
60
58
|
}
|
|
61
59
|
|
|
62
|
-
export
|
|
60
|
+
export default function createApp<E extends Env>(options?: ServerOptions<E>) {
|
|
63
61
|
// const root = options?.root ?? '/'
|
|
64
62
|
const app = options?.app ?? new Hono<E>()
|
|
65
63
|
|
|
@@ -76,8 +74,7 @@ export const createApp = <E extends Env>(options?: ServerOptions<E>) => {
|
|
|
76
74
|
|
|
77
75
|
// @ts-ignore
|
|
78
76
|
app.onError(options?.onError || EHandler)
|
|
79
|
-
|
|
80
|
-
app.notFound(options?.notFound || NFHandler)
|
|
77
|
+
app.notFound(options?.notFound || response.notFound)
|
|
81
78
|
|
|
82
79
|
if (options?.init) options.init(app)
|
|
83
80
|
|
|
@@ -92,5 +89,3 @@ export const createApp = <E extends Env>(options?: ServerOptions<E>) => {
|
|
|
92
89
|
|
|
93
90
|
return app
|
|
94
91
|
}
|
|
95
|
-
|
|
96
|
-
export default createApp
|