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.
Files changed (75) hide show
  1. package/README.md +34 -12
  2. package/package.json +9 -3
  3. package/src/cli.ts +10 -7
  4. package/src/commands/new.ts +172 -0
  5. package/src/commands/templates/pages/.editorconfig +21 -0
  6. package/src/commands/templates/pages/README.md +21 -0
  7. package/src/commands/templates/pages/_gitignore +24 -0
  8. package/src/commands/templates/pages/components/locale-select.tsx +59 -0
  9. package/src/commands/templates/pages/components/select.tsx +95 -0
  10. package/src/commands/templates/pages/i18n/en.ts +17 -0
  11. package/src/commands/templates/pages/i18n/index.ts +19 -0
  12. package/src/commands/templates/pages/i18n/pt-br.ts +17 -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/index.tsx +16 -0
  16. package/src/commands/templates/pages/pages/layout.tsx +24 -0
  17. package/src/commands/templates/pages/style.css +24 -0
  18. package/src/commands/templates/pages/tsconfig.json +54 -0
  19. package/src/commands/templates/pages/types.ts +8 -0
  20. package/src/commands/templates/server/base/.editorconfig +21 -0
  21. package/src/commands/templates/server/base/.env.dev.example +2 -0
  22. package/src/commands/templates/server/base/.env.prod.example +2 -0
  23. package/src/commands/templates/server/base/README.md +29 -0
  24. package/src/commands/templates/server/base/_gitignore +100 -0
  25. package/src/commands/templates/server/base/actions/blog/index.get.ts +9 -0
  26. package/src/commands/templates/server/base/actions/blog/post/[id].get.ts +9 -0
  27. package/src/commands/templates/server/base/actions/index.get.ts +5 -0
  28. package/src/commands/templates/server/base/configs/duto.ts +11 -0
  29. package/src/commands/templates/server/base/configs/roles.ts +5 -0
  30. package/src/commands/templates/server/base/package.json +24 -0
  31. package/src/commands/templates/server/base/tsconfig.json +42 -0
  32. package/src/commands/templates/server/workerd/wrangler.toml +47 -0
  33. package/src/commands/upgrade.ts +30 -0
  34. package/src/index.ts +7 -2
  35. package/src/pages/{commands → cli/commands}/build.ts +13 -7
  36. package/src/pages/{commands → cli/commands}/dev.ts +19 -6
  37. package/src/pages/{commands → cli/commands}/preview.ts +6 -4
  38. package/src/pages/cli/index.ts +10 -0
  39. package/src/pages/cli/utils.ts +126 -0
  40. package/src/pages/client/app.tsx +27 -74
  41. package/src/pages/client/router.ts +15 -281
  42. package/src/pages/compiler/aliases.ts +2 -8
  43. package/src/pages/compiler/ast.ts +1 -538
  44. package/src/pages/compiler/constants.ts +95 -13
  45. package/src/pages/compiler/file.ts +1 -1
  46. package/src/pages/compiler/index.ts +20 -128
  47. package/src/pages/compiler/meta.ts +39 -228
  48. package/src/pages/compiler/routes.ts +4 -5
  49. package/src/pages/compiler/types.ts +11 -101
  50. package/src/pages/compiler/vite-plugin.ts +90 -69
  51. package/src/pages/i18n/client.ts +19 -7
  52. package/src/pages/i18n/element.ts +1 -1
  53. package/src/pages/i18n/server.ts +22 -7
  54. package/src/pages/index.ts +1 -2
  55. package/src/pages/preact/server.ts +1 -1
  56. package/src/server/app.ts +5 -10
  57. package/src/server/auth/ability.ts +1 -1
  58. package/src/server/auth/token.ts +5 -5
  59. package/src/server/cli/commands/build.ts +2 -2
  60. package/src/server/cli/commands/deploy.ts +1 -2
  61. package/src/server/cli/commands/dev.ts +6 -4
  62. package/src/server/cli/commands/routes.ts +0 -1
  63. package/src/server/cli/utils.ts +2 -8
  64. package/src/server/config.ts +18 -3
  65. package/src/server/entry.ts +3 -3
  66. package/src/server/open-api/register.ts +3 -3
  67. package/src/server/open-api/spec.ts +11 -4
  68. package/src/server/request.ts +46 -14
  69. package/src/server/routes.ts +8 -31
  70. package/src/server/utils.ts +25 -0
  71. package/src/utils.ts +43 -2
  72. package/src/pages/commands/dev-old.ts +0 -213
  73. package/src/pages/compiler/island.ts +0 -108
  74. package/src/pages/compiler/utils.ts +0 -5
  75. package/src/server/http.ts +0 -23
@@ -1,213 +0,0 @@
1
- import { defineCommand } from 'citty'
2
- import { build, preview } from 'vite'
3
- import { withPort } from 't0n/port'
4
- import { error, event, log, rn, wait } from 't0n/log'
5
- import { getConfig } from '~/utils'
6
- import { _root } from '@/utils'
7
- import { watch } from 't0n/cli'
8
- import { spawn, type ChildProcess } from 'node:child_process'
9
-
10
- export const args = {
11
- port: {
12
- alias: 'p',
13
- description: 'Port to listen on',
14
- type: 'number',
15
- default: 3000,
16
- },
17
- host: {
18
- description: 'Host to forward requests to, defaults to the zone of project',
19
- type: 'string',
20
- default: 'localhost',
21
- },
22
- }
23
-
24
- export default defineCommand({
25
- meta: {
26
- name: 'dev',
27
- description: '🧪 Start dev server\n',
28
- }, // @ts-ignore
29
- args,
30
- async run({ args }) {
31
- const config = await getConfig()
32
- const desiredPort = args.port ? Number(args.port) : (config?.preview?.port ? Number(config.preview.port) : 3000)
33
- const host = args.host ? String(args.host) : (config?.preview?.host ? config.preview.host : 'localhost')
34
-
35
- // const _build = async () => {
36
- // return await build(await getConfig())
37
- // }
38
-
39
- // let buildProcess: ChildProcess | null = null
40
- // const _build = async () => {
41
- // if (buildProcess) await killProcess(buildProcess)
42
-
43
- // return new Promise<void>((resolve, reject) => {
44
- // let timeoutId: NodeJS.Timeout | null = null
45
-
46
- // buildProcess = spawn('duto', ['build'], {
47
- // // stdio: 'ignore',
48
- // stdio: ['inherit', 'ignore', 'inherit', 'ipc'],
49
- // shell: process.platform === 'win32'
50
- // })
51
-
52
- // buildProcess.on('close', (code) => {
53
- // if (timeoutId) clearTimeout(timeoutId)
54
- // buildProcess = null
55
-
56
- // if (code === 0) {
57
- // resolve()
58
- // } else {
59
- // reject(new Error(`Build failed with code ${code}`))
60
- // }
61
- // })
62
-
63
- // buildProcess.on('error', (error) => {
64
- // if (timeoutId) clearTimeout(timeoutId)
65
- // buildProcess = null
66
- // reject(error)
67
- // })
68
- // })
69
- // }
70
-
71
- // const killProcess = async (app: ChildProcess | null) => {
72
- // if (!app) return null
73
- // // event('Stopping..')
74
- // try {
75
- // if (!app?.killed) {
76
- // app.kill('SIGTERM')
77
- // await wait(1000)
78
-
79
- // if (!app?.killed) { // force kill
80
- // app.kill('SIGKILL')
81
- // await wait(1000)
82
- // }
83
- // }
84
-
85
- // return null
86
- // } catch (e) {
87
- // error('Error stopping:', e)
88
- // }
89
-
90
- // return null
91
- // }
92
-
93
- let buildProcess: Bun.Subprocess | null = null
94
-
95
- const _build = async () => {
96
- if (buildProcess) await killProcess(buildProcess)
97
- buildProcess = Bun.spawn(['duto', 'build'], {stdin: 'inherit', stdout: 'ignore', stderr: 'inherit'})
98
-
99
- try {
100
- const code = await buildProcess.exited
101
-
102
- if (code !== 0)
103
- throw new Error(`Build failed with code ${code}`)
104
- } finally {
105
- buildProcess = null
106
- }
107
- }
108
-
109
- const killProcess = async (proc: Bun.Subprocess | null) => {
110
- if (!proc || proc.exitCode !== null) return
111
-
112
- try {
113
- proc.kill('SIGTERM')
114
-
115
- const exited = await Promise.race([
116
- proc.exited.then(() => true),
117
- new Promise(r => setTimeout(r, 1000)).then(() => false),
118
- ])
119
-
120
- if (!exited && proc.exitCode === null) {
121
- proc.kill('SIGKILL')
122
- await proc.exited
123
- }
124
- } catch (e) {
125
- error('Error stopping:', e)
126
- }
127
- }
128
-
129
- withPort(desiredPort, async (port) => {
130
- const ignore = [
131
- /(^|[\/\\])\.(duto|git|cache|vscode|idea|next|nuxt|nyc_output)([\/\\]|$)/,
132
- /(^|[\/\\])(node_modules|tmp|dist|build|out|coverage)([\/\\]|$)/,
133
-
134
- /\.(DS_Store|localized|tmp|temp|log|pid|swp|swo)$/,
135
- /(Thumbs\.db|desktop\.ini)$/,
136
- /~$/,
137
-
138
- /(^|[\/\\])\.(Spotlight-V100|Trashes)([\/\\]|$)/,
139
- /(^|[\/\\])__MACOSX([\/\\]|$)/,
140
- ]
141
-
142
- try {
143
- await _build()
144
-
145
- const clients = new Set<ReadableStreamDefaultController<string>>()
146
- const reload = () => {
147
- for (const client of clients)
148
- client.enqueue('data: reload\n\n')
149
- }
150
- Bun.serve({
151
- port: 1322,
152
-
153
- fetch(req) {
154
- const headers = {
155
- 'Access-Control-Allow-Origin': '*',
156
- 'Access-Control-Allow-Methods': 'GET',
157
- 'Access-Control-Allow-Headers': '*',
158
- }
159
-
160
- if (req.method === 'OPTIONS')
161
- return new Response(null, { status: 204, headers })
162
-
163
- const { pathname } = new URL(req.url)
164
-
165
- if (pathname !== '/events')
166
- return new Response(null, { status: 404, headers })
167
-
168
- const stream = new ReadableStream<string>({
169
- start(controller) {
170
- clients.add(controller)
171
-
172
- req.signal.addEventListener('abort', () => {
173
- clients.delete(controller)
174
- controller.close()
175
- })
176
- },
177
- })
178
-
179
- return new Response(stream, {
180
- headers: {
181
- ...headers,
182
- 'Content-Type': 'text/event-stream',
183
- 'Cache-Control': 'no-cache',
184
- 'Connection': 'keep-alive',
185
- },
186
- })
187
- },
188
- })
189
-
190
- await preview({ ...config, preview: { port, host } })
191
-
192
- log(`Running on http://${host}:${port}`)
193
-
194
- watch(async () => {
195
- await _build()
196
- reload()
197
- }, '.', _root, (path: string) => {
198
- for (const pattern of ignore)
199
- if (pattern.test(path)) return true
200
-
201
- return false
202
- })
203
-
204
- } catch (e: any) {
205
- error(e)
206
- process.exit(0)
207
- } finally {
208
- rn()
209
- }
210
- })
211
-
212
- },
213
- })
@@ -1,108 +0,0 @@
1
- import { h } from 'preact'
2
- import { renderToString } from 'preact-render-to-string'
3
- import { IMPORT, newID } from 't0n'
4
-
5
- import { getEntry } from './assets'
6
- import { makeByteToCharMap, extractPropsFromJSX } from './ast'
7
-
8
- import type { ImportUsage, IslandMeta } from './types'
9
-
10
- function serializeProps(props: Record<string, unknown>) {
11
- return JSON.stringify(props)
12
- }
13
-
14
- function htmlEncodeProps(serialized: string) {
15
- return serialized
16
- .replace(/&/g, '&amp;')
17
- .replace(/"/g, '&quot;')
18
- .replace(/</g, '&lt;')
19
- .replace(/>/g, '&gt;')
20
- }
21
-
22
- async function renderIslandSSR(
23
- component: unknown,
24
- props: Record<string, unknown>,
25
- children?: unknown,
26
- ): Promise<string> {
27
- try {
28
- return renderToString(h(component as never, props as never, children as never))
29
- } catch {
30
- return ''
31
- }
32
- }
33
-
34
- function buildIslandTag(meta: IslandMeta) {
35
- return `<i-s id="${meta.id}" url="${getEntry(meta.url)}"${meta.export === 'default' ? '' : ` export="${meta.export}"`}${meta.props === '{}' ? '' : ` props="${meta.props}"`}>${meta.ssrHtml}</i-s>`
36
- }
37
-
38
- /**
39
- * Replaces in "sourceCode" each occurrence of imported components
40
- * from "importSource" by hydrator-compatible <island> tags.
41
- *
42
- * @param sourceCode TSX source code
43
- * @param importSource The target module (e.g. '@/components/counter2')
44
- * @param opts Resolution callbacks
45
- * @returns Code with replacements applied
46
- */
47
- export async function replaceIslands(
48
- code: string,
49
- usages: ImportUsage[],
50
- ) {
51
- if (!usages.length) return code
52
-
53
- const b2c = makeByteToCharMap(code)
54
- const toChar = (byteOffset: number) => b2c[byteOffset - 1]
55
-
56
- const replacements: Array<{ start: number; end: number; replacement: string }> = []
57
-
58
- for (const importUsage of usages) {
59
- const component = await IMPORT(importUsage.source, importUsage.imported)
60
-
61
- // schedule removal of the import declaration (replacement with an empty string)
62
- replacements.push({
63
- start: toChar(importUsage.importStart),
64
- end: toChar(importUsage.importEnd),
65
- replacement: '',
66
- })
67
-
68
- // schedule the replacement of each JSX usage with `<island>`
69
- for (const usage of importUsage.usages) {
70
- if (usage.type !== 'jsx') continue
71
-
72
- const props = extractPropsFromJSX(usage.node)
73
- const ssrHtml = await renderIslandSSR(component, props)
74
-
75
- const island = buildIslandTag({
76
- id: newID(),
77
- url: importUsage.source,
78
- export: importUsage.imported,
79
- props: htmlEncodeProps(serializeProps(props)),
80
- ssrHtml,
81
- })
82
-
83
- replacements.push({
84
- start: toChar(usage.start),
85
- end: toChar(usage.end),
86
- replacement: island,
87
- })
88
- }
89
- }
90
-
91
- if (!replacements.length) return code
92
-
93
- replacements.sort((a, b) => a.start - b.start)
94
-
95
- const parts: string[] = []
96
- let cursor = 0
97
-
98
- for (const { start, end, replacement } of replacements) {
99
- if (start < cursor) continue // overlapped, discards
100
- if (start > cursor) parts.push(code.slice(cursor, start))
101
- parts.push(replacement)
102
- cursor = end
103
- }
104
-
105
- if (cursor < code.length) parts.push(code.slice(cursor))
106
-
107
- return parts.join('').replace(/\n{3,}/g, '\n\n')
108
- }
@@ -1,5 +0,0 @@
1
- import { Envir } from 't0n'
2
- import { dirname, join } from 'pathe'
3
-
4
- export const _duto = join(dirname(new URL(import.meta.url).pathname), '..')
5
- export const _root = Envir.get('npm_config_local_prefix') || Envir.get('PWD') || join(_duto, '../../../')
@@ -1,23 +0,0 @@
1
- export const verbAlias = {
2
- get: 0,
3
- post: 1,
4
- put: 2,
5
- patch: 3,
6
- delete: 4,
7
- head: 5,
8
- options: 6,
9
- connect: 7,
10
- trace: 8,
11
- } as Record<string, number>
12
-
13
- export const getVerb = [
14
- 'get',
15
- 'post',
16
- 'put',
17
- 'patch',
18
- 'delete',
19
- 'head',
20
- 'options',
21
- 'connect',
22
- 'trace',
23
- ]