@tothalex/nulljs 0.0.48 → 0.0.53
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 +22 -32
- package/src/cli.ts +24 -0
- package/src/commands/config.ts +130 -0
- package/src/commands/deploy.ts +182 -123
- package/src/commands/dev.ts +10 -0
- package/src/commands/host.ts +130 -139
- package/src/commands/index.ts +6 -8
- package/src/commands/secret.ts +364 -56
- package/src/commands/status.ts +41 -0
- package/src/components/DeployAnimation.tsx +92 -0
- package/src/components/DeploymentLogsPane.tsx +79 -0
- package/src/components/Header.tsx +57 -0
- package/src/components/HelpModal.tsx +64 -0
- package/src/components/SystemLogsPane.tsx +78 -0
- package/src/config/index.ts +181 -0
- package/src/lib/bundle/function.ts +125 -0
- package/src/lib/bundle/index.ts +3 -0
- package/src/lib/bundle/react.ts +149 -0
- package/src/lib/deploy.ts +103 -0
- package/src/lib/server.ts +160 -0
- package/src/lib/vite.ts +120 -0
- package/src/lib/watcher.ts +274 -0
- package/src/ui.tsx +363 -0
- package/tsconfig.json +30 -0
- package/scripts/install-server.js +0 -199
- package/src/commands/api.ts +0 -16
- package/src/commands/auth.ts +0 -54
- package/src/commands/create.ts +0 -43
- package/src/commands/dev/function/index.ts +0 -221
- package/src/commands/dev/function/utils.ts +0 -99
- package/src/commands/dev/index.tsx +0 -126
- package/src/commands/dev/logging-manager.ts +0 -87
- package/src/commands/dev/server/index.ts +0 -48
- package/src/commands/dev/server/utils.ts +0 -37
- package/src/commands/dev/ui/components/scroll-area.tsx +0 -141
- package/src/commands/dev/ui/components/tab-bar.tsx +0 -67
- package/src/commands/dev/ui/index.tsx +0 -71
- package/src/commands/dev/ui/logging-context.tsx +0 -76
- package/src/commands/dev/ui/tabs/functions-tab.tsx +0 -35
- package/src/commands/dev/ui/tabs/server-tab.tsx +0 -36
- package/src/commands/dev/ui/tabs/vite-tab.tsx +0 -35
- package/src/commands/dev/ui/use-logging.tsx +0 -34
- package/src/commands/dev/vite/index.ts +0 -54
- package/src/commands/dev/vite/utils.ts +0 -71
- package/src/commands/profile.ts +0 -189
- package/src/index.ts +0 -346
- package/src/lib/api.ts +0 -189
- package/src/lib/bundle/function/index.ts +0 -46
- package/src/lib/bundle/react/index.ts +0 -2
- package/src/lib/bundle/react/spa.ts +0 -77
- package/src/lib/bundle/react/ssr/client.ts +0 -93
- package/src/lib/bundle/react/ssr/config.ts +0 -77
- package/src/lib/bundle/react/ssr/index.ts +0 -4
- package/src/lib/bundle/react/ssr/props.ts +0 -71
- package/src/lib/bundle/react/ssr/server.ts +0 -83
- package/src/lib/config.ts +0 -347
- package/src/lib/deployment.ts +0 -244
- package/src/lib/update-server.ts +0 -262
|
@@ -1,141 +0,0 @@
|
|
|
1
|
-
import { Box, type DOMElement, measureElement, useInput } from 'ink'
|
|
2
|
-
import { useEffect, useReducer, useRef } from 'react'
|
|
3
|
-
|
|
4
|
-
interface ScrollAreaState {
|
|
5
|
-
innerHeight: number
|
|
6
|
-
height: number
|
|
7
|
-
scrollTop: number
|
|
8
|
-
}
|
|
9
|
-
|
|
10
|
-
type ScrollAreaAction =
|
|
11
|
-
| { type: 'SET_INNER_HEIGHT'; innerHeight: number }
|
|
12
|
-
| { type: 'SET_HEIGHT'; height: number }
|
|
13
|
-
| { type: 'SCROLL_DOWN' }
|
|
14
|
-
| { type: 'SCROLL_UP' }
|
|
15
|
-
| { type: 'SCROLL_TO_BOTTOM' }
|
|
16
|
-
| { type: 'SCROLL_TO_TOP' }
|
|
17
|
-
|
|
18
|
-
const reducer = (state: ScrollAreaState, action: ScrollAreaAction) => {
|
|
19
|
-
switch (action.type) {
|
|
20
|
-
case 'SET_INNER_HEIGHT':
|
|
21
|
-
return {
|
|
22
|
-
...state,
|
|
23
|
-
innerHeight: action.innerHeight
|
|
24
|
-
}
|
|
25
|
-
case 'SET_HEIGHT':
|
|
26
|
-
return {
|
|
27
|
-
...state,
|
|
28
|
-
height: action.height
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
case 'SCROLL_DOWN':
|
|
32
|
-
return {
|
|
33
|
-
...state,
|
|
34
|
-
scrollTop: Math.min(
|
|
35
|
-
state.innerHeight <= state.height ? 0 : state.innerHeight - state.height,
|
|
36
|
-
state.scrollTop + 1
|
|
37
|
-
)
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
case 'SCROLL_UP':
|
|
41
|
-
return {
|
|
42
|
-
...state,
|
|
43
|
-
scrollTop: Math.max(0, state.scrollTop - 1)
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
case 'SCROLL_TO_BOTTOM':
|
|
47
|
-
return {
|
|
48
|
-
...state,
|
|
49
|
-
scrollTop: Math.max(0, state.innerHeight - state.height)
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
case 'SCROLL_TO_TOP':
|
|
53
|
-
return {
|
|
54
|
-
...state,
|
|
55
|
-
scrollTop: 0
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
default:
|
|
59
|
-
return state
|
|
60
|
-
}
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
export interface ScrollAreaProps extends React.PropsWithChildren {
|
|
64
|
-
height: number
|
|
65
|
-
autoScroll?: boolean
|
|
66
|
-
onRef?: (scrollToBottom: () => void) => void
|
|
67
|
-
onShutdown?: () => Promise<void>
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
export function ScrollArea({ height, children, autoScroll = false, onRef }: ScrollAreaProps) {
|
|
71
|
-
const [state, dispatch] = useReducer(reducer, {
|
|
72
|
-
height: height,
|
|
73
|
-
scrollTop: 0,
|
|
74
|
-
innerHeight: 0
|
|
75
|
-
})
|
|
76
|
-
|
|
77
|
-
const innerRef = useRef<DOMElement>(null)
|
|
78
|
-
|
|
79
|
-
const scrollToBottom = () => {
|
|
80
|
-
dispatch({ type: 'SCROLL_TO_BOTTOM' })
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
useEffect(() => {
|
|
84
|
-
if (onRef) {
|
|
85
|
-
onRef(scrollToBottom)
|
|
86
|
-
}
|
|
87
|
-
}, [onRef])
|
|
88
|
-
|
|
89
|
-
useEffect(() => {
|
|
90
|
-
dispatch({ type: 'SET_HEIGHT', height })
|
|
91
|
-
}, [height])
|
|
92
|
-
|
|
93
|
-
useEffect(() => {
|
|
94
|
-
if (!innerRef.current) return
|
|
95
|
-
|
|
96
|
-
const dimensions = measureElement(innerRef.current)
|
|
97
|
-
|
|
98
|
-
dispatch({
|
|
99
|
-
type: 'SET_INNER_HEIGHT',
|
|
100
|
-
innerHeight: dimensions.height
|
|
101
|
-
})
|
|
102
|
-
|
|
103
|
-
if (autoScroll) {
|
|
104
|
-
scrollToBottom()
|
|
105
|
-
}
|
|
106
|
-
}, [children, autoScroll])
|
|
107
|
-
|
|
108
|
-
useInput((input) => {
|
|
109
|
-
if (input === 'j') {
|
|
110
|
-
dispatch({
|
|
111
|
-
type: 'SCROLL_DOWN'
|
|
112
|
-
})
|
|
113
|
-
}
|
|
114
|
-
|
|
115
|
-
if (input === 'k') {
|
|
116
|
-
dispatch({
|
|
117
|
-
type: 'SCROLL_UP'
|
|
118
|
-
})
|
|
119
|
-
}
|
|
120
|
-
|
|
121
|
-
if (input === 'G') {
|
|
122
|
-
dispatch({
|
|
123
|
-
type: 'SCROLL_TO_BOTTOM'
|
|
124
|
-
})
|
|
125
|
-
}
|
|
126
|
-
|
|
127
|
-
if (input === 'g') {
|
|
128
|
-
dispatch({
|
|
129
|
-
type: 'SCROLL_TO_TOP'
|
|
130
|
-
})
|
|
131
|
-
}
|
|
132
|
-
})
|
|
133
|
-
|
|
134
|
-
return (
|
|
135
|
-
<Box height={height} flexDirection="column" flexGrow={1} overflow="hidden">
|
|
136
|
-
<Box ref={innerRef} flexShrink={0} flexDirection="column" marginTop={-state.scrollTop}>
|
|
137
|
-
{children}
|
|
138
|
-
</Box>
|
|
139
|
-
</Box>
|
|
140
|
-
)
|
|
141
|
-
}
|
|
@@ -1,67 +0,0 @@
|
|
|
1
|
-
import React from 'react'
|
|
2
|
-
import { Box, Text } from 'ink'
|
|
3
|
-
|
|
4
|
-
export type Tab = {
|
|
5
|
-
name: string
|
|
6
|
-
status?: 'running' | 'stopped' | 'error' | 'loading'
|
|
7
|
-
}
|
|
8
|
-
|
|
9
|
-
type TabBarProps = {
|
|
10
|
-
tabs: Tab[]
|
|
11
|
-
activeTabIndex: number
|
|
12
|
-
showKeyBindings?: boolean
|
|
13
|
-
isAutoScrolling?: boolean
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
const getStatusSymbol = (status?: Tab['status']): string => {
|
|
17
|
-
switch (status) {
|
|
18
|
-
case 'running':
|
|
19
|
-
return '●'
|
|
20
|
-
case 'stopped':
|
|
21
|
-
return '○'
|
|
22
|
-
case 'error':
|
|
23
|
-
return '✗'
|
|
24
|
-
case 'loading':
|
|
25
|
-
return '⋯'
|
|
26
|
-
default:
|
|
27
|
-
return '○'
|
|
28
|
-
}
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
export const TabBar: React.FC<TabBarProps> = ({ tabs, activeTabIndex, showKeyBindings = true }) => {
|
|
32
|
-
const tabsText =
|
|
33
|
-
tabs.length === 0
|
|
34
|
-
? 'No services'
|
|
35
|
-
: tabs
|
|
36
|
-
.map((tab, index) => {
|
|
37
|
-
const statusSymbol = getStatusSymbol(tab.status)
|
|
38
|
-
const isActive = index === activeTabIndex
|
|
39
|
-
const activeIndicator = isActive ? '►' : ' '
|
|
40
|
-
|
|
41
|
-
return `${activeIndicator} ${index + 1}. ${statusSymbol} ${tab.name} `
|
|
42
|
-
})
|
|
43
|
-
.join('')
|
|
44
|
-
|
|
45
|
-
const currentTab = tabs[activeTabIndex]
|
|
46
|
-
const functionKeybinding =
|
|
47
|
-
currentTab?.name === 'Functions' ? ' | d: deploy all functions | s: deploy secrets' : ''
|
|
48
|
-
const keyBindings = showKeyBindings
|
|
49
|
-
? `h/l: switch panels | j/k: scroll | g/G: top/bottom | q: quit${functionKeybinding}`
|
|
50
|
-
: ''
|
|
51
|
-
|
|
52
|
-
return (
|
|
53
|
-
<Box
|
|
54
|
-
borderStyle="single"
|
|
55
|
-
borderColor="white"
|
|
56
|
-
paddingX={1}
|
|
57
|
-
justifyContent="space-between"
|
|
58
|
-
flexDirection="row">
|
|
59
|
-
<Text>{tabsText}</Text>
|
|
60
|
-
{showKeyBindings && (
|
|
61
|
-
<Text color="gray" dimColor>
|
|
62
|
-
{keyBindings}
|
|
63
|
-
</Text>
|
|
64
|
-
)}
|
|
65
|
-
</Box>
|
|
66
|
-
)
|
|
67
|
-
}
|
|
@@ -1,71 +0,0 @@
|
|
|
1
|
-
import { useCallback, useState } from 'react'
|
|
2
|
-
import { Box, useStdout, useInput } from 'ink'
|
|
3
|
-
|
|
4
|
-
import { TabBar, type Tab } from './components/tab-bar'
|
|
5
|
-
import { ServerTab } from './tabs/server-tab'
|
|
6
|
-
import { FunctionsTab } from './tabs/functions-tab'
|
|
7
|
-
import { ViteTab } from './tabs/vite-tab'
|
|
8
|
-
import { LoggingManager } from '../logging-manager'
|
|
9
|
-
import { deployAllFunctions, deploySecrets } from '../function/utils'
|
|
10
|
-
|
|
11
|
-
type UIProps = {
|
|
12
|
-
loggingManager: LoggingManager
|
|
13
|
-
onShutdown: () => Promise<void>
|
|
14
|
-
tabs: Tab[]
|
|
15
|
-
srcDir: string
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
export const UI: React.FC<UIProps> = ({ loggingManager, onShutdown, tabs, srcDir }) => {
|
|
19
|
-
const { stdout } = useStdout()
|
|
20
|
-
const [activeTabIndex, setActiveTabIndex] = useState(0)
|
|
21
|
-
|
|
22
|
-
const switchToNextService = useCallback(() => {
|
|
23
|
-
setActiveTabIndex((prev) => (prev + 1) % tabs.length)
|
|
24
|
-
}, [tabs.length])
|
|
25
|
-
|
|
26
|
-
const switchToPrevService = useCallback(() => {
|
|
27
|
-
setActiveTabIndex((prev) => (prev - 1 + tabs.length) % tabs.length)
|
|
28
|
-
}, [tabs.length])
|
|
29
|
-
|
|
30
|
-
useInput(async (input, key) => {
|
|
31
|
-
if (input === 'l') {
|
|
32
|
-
switchToNextService()
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
if (input === 'h') {
|
|
36
|
-
switchToPrevService()
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
if (input === 'd') {
|
|
40
|
-
await deployAllFunctions(srcDir, loggingManager.getLogFunction('functions'))
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
if (input === 's') {
|
|
44
|
-
await deploySecrets(srcDir, loggingManager.getLogFunction('functions'))
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
if (input === 'q' || (key.ctrl && input === 'c')) {
|
|
48
|
-
await onShutdown()
|
|
49
|
-
}
|
|
50
|
-
})
|
|
51
|
-
|
|
52
|
-
const renderActiveTab = () => {
|
|
53
|
-
switch (activeTabIndex) {
|
|
54
|
-
case 0:
|
|
55
|
-
return <ServerTab loggingManager={loggingManager} />
|
|
56
|
-
case 1:
|
|
57
|
-
return <FunctionsTab loggingManager={loggingManager} />
|
|
58
|
-
case 2:
|
|
59
|
-
return <ViteTab loggingManager={loggingManager} />
|
|
60
|
-
default:
|
|
61
|
-
return <ServerTab loggingManager={loggingManager} />
|
|
62
|
-
}
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
return (
|
|
66
|
-
<Box flexDirection="column" width="100%" height={stdout?.rows - 1 || 24}>
|
|
67
|
-
<TabBar tabs={tabs} activeTabIndex={activeTabIndex} />
|
|
68
|
-
{renderActiveTab()}
|
|
69
|
-
</Box>
|
|
70
|
-
)
|
|
71
|
-
}
|
|
@@ -1,76 +0,0 @@
|
|
|
1
|
-
import React, { createContext, useContext, useState, useCallback, type ReactNode } from 'react'
|
|
2
|
-
|
|
3
|
-
export type LogEntry = {
|
|
4
|
-
id: string
|
|
5
|
-
timestamp: Date
|
|
6
|
-
service: 'server' | 'functions' | 'vite'
|
|
7
|
-
level: 'info' | 'warn' | 'error'
|
|
8
|
-
message: string
|
|
9
|
-
}
|
|
10
|
-
|
|
11
|
-
type LoggingContextType = {
|
|
12
|
-
logs: Record<string, LogEntry[]>
|
|
13
|
-
addLog: (service: LogEntry['service'], level: LogEntry['level'], message: string) => void
|
|
14
|
-
clearLogs: (service?: LogEntry['service']) => void
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
const LoggingContext = createContext<LoggingContextType | undefined>(undefined)
|
|
18
|
-
|
|
19
|
-
export const useLogging = () => {
|
|
20
|
-
const context = useContext(LoggingContext)
|
|
21
|
-
if (!context) {
|
|
22
|
-
throw new Error('useLogging must be used within a LoggingProvider')
|
|
23
|
-
}
|
|
24
|
-
return context
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
type LoggingProviderProps = {
|
|
28
|
-
children: ReactNode
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
export const LoggingProvider: React.FC<LoggingProviderProps> = ({ children }) => {
|
|
32
|
-
const [logs, setLogs] = useState<Record<string, LogEntry[]>>({
|
|
33
|
-
server: [],
|
|
34
|
-
functions: [],
|
|
35
|
-
vite: []
|
|
36
|
-
})
|
|
37
|
-
|
|
38
|
-
const addLog = useCallback(
|
|
39
|
-
(service: LogEntry['service'], level: LogEntry['level'], message: string) => {
|
|
40
|
-
const newLog: LogEntry = {
|
|
41
|
-
id: `${service}-${Date.now()}-${Math.random()}`,
|
|
42
|
-
timestamp: new Date(),
|
|
43
|
-
service,
|
|
44
|
-
level,
|
|
45
|
-
message
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
setLogs((prev) => ({
|
|
49
|
-
...prev,
|
|
50
|
-
[service]: [...prev[service], newLog]
|
|
51
|
-
}))
|
|
52
|
-
},
|
|
53
|
-
[]
|
|
54
|
-
)
|
|
55
|
-
|
|
56
|
-
const clearLogs = useCallback((service?: LogEntry['service']) => {
|
|
57
|
-
if (service) {
|
|
58
|
-
setLogs((prev) => ({
|
|
59
|
-
...prev,
|
|
60
|
-
[service]: []
|
|
61
|
-
}))
|
|
62
|
-
} else {
|
|
63
|
-
setLogs({
|
|
64
|
-
server: [],
|
|
65
|
-
functions: [],
|
|
66
|
-
vite: []
|
|
67
|
-
})
|
|
68
|
-
}
|
|
69
|
-
}, [])
|
|
70
|
-
|
|
71
|
-
return (
|
|
72
|
-
<LoggingContext.Provider value={{ logs, addLog, clearLogs }}>
|
|
73
|
-
{children}
|
|
74
|
-
</LoggingContext.Provider>
|
|
75
|
-
)
|
|
76
|
-
}
|
|
@@ -1,35 +0,0 @@
|
|
|
1
|
-
import React from 'react'
|
|
2
|
-
import { Box, Text, useStdout } from 'ink'
|
|
3
|
-
import { ScrollArea } from '../components/scroll-area'
|
|
4
|
-
import { LoggingManager } from '../../logging-manager'
|
|
5
|
-
import { useLogging } from '../use-logging'
|
|
6
|
-
|
|
7
|
-
type FunctionsTabProps = {
|
|
8
|
-
loggingManager: LoggingManager
|
|
9
|
-
}
|
|
10
|
-
|
|
11
|
-
export const FunctionsTab: React.FC<FunctionsTabProps> = ({ loggingManager }) => {
|
|
12
|
-
const { stdout } = useStdout()
|
|
13
|
-
const { logs } = useLogging(loggingManager, 'functions')
|
|
14
|
-
|
|
15
|
-
return (
|
|
16
|
-
<ScrollArea height={stdout.rows - 5} autoScroll={true}>
|
|
17
|
-
<Box flexDirection="column" padding={1}>
|
|
18
|
-
<Text color="green">Functions</Text>
|
|
19
|
-
{logs.length === 0 ? (
|
|
20
|
-
<Text dimColor>No function logs yet...</Text>
|
|
21
|
-
) : (
|
|
22
|
-
<Box flexDirection="column">
|
|
23
|
-
{logs.map((log) => (
|
|
24
|
-
<Text
|
|
25
|
-
key={`function-tab-${log.id}`}
|
|
26
|
-
color={log.level === 'error' ? 'red' : log.level === 'warn' ? 'yellow' : 'white'}>
|
|
27
|
-
[{log.timestamp.toLocaleTimeString()}] {log.message}
|
|
28
|
-
</Text>
|
|
29
|
-
))}
|
|
30
|
-
</Box>
|
|
31
|
-
)}
|
|
32
|
-
</Box>
|
|
33
|
-
</ScrollArea>
|
|
34
|
-
)
|
|
35
|
-
}
|
|
@@ -1,36 +0,0 @@
|
|
|
1
|
-
import React from 'react'
|
|
2
|
-
import { Box, Text, useStdout } from 'ink'
|
|
3
|
-
|
|
4
|
-
import { ScrollArea } from '../components/scroll-area'
|
|
5
|
-
import { LoggingManager } from '../../logging-manager'
|
|
6
|
-
import { useLogging } from '../use-logging'
|
|
7
|
-
|
|
8
|
-
type ServerTabProps = {
|
|
9
|
-
loggingManager: LoggingManager
|
|
10
|
-
}
|
|
11
|
-
|
|
12
|
-
export const ServerTab: React.FC<ServerTabProps> = ({ loggingManager }) => {
|
|
13
|
-
const { stdout } = useStdout()
|
|
14
|
-
const { logs } = useLogging(loggingManager, 'server')
|
|
15
|
-
|
|
16
|
-
return (
|
|
17
|
-
<ScrollArea height={stdout.rows - 5} autoScroll={true}>
|
|
18
|
-
<Box flexDirection="column" padding={1}>
|
|
19
|
-
<Text color="cyan">Server</Text>
|
|
20
|
-
{logs.length === 0 ? (
|
|
21
|
-
<Text dimColor>No server logs yet...</Text>
|
|
22
|
-
) : (
|
|
23
|
-
<Box flexDirection="column">
|
|
24
|
-
{logs.map((log) => (
|
|
25
|
-
<Text
|
|
26
|
-
key={log.id}
|
|
27
|
-
color={log.level === 'error' ? 'red' : log.level === 'warn' ? 'yellow' : 'white'}>
|
|
28
|
-
[{log.timestamp.toLocaleTimeString()}] {log.message}
|
|
29
|
-
</Text>
|
|
30
|
-
))}
|
|
31
|
-
</Box>
|
|
32
|
-
)}
|
|
33
|
-
</Box>
|
|
34
|
-
</ScrollArea>
|
|
35
|
-
)
|
|
36
|
-
}
|
|
@@ -1,35 +0,0 @@
|
|
|
1
|
-
import React from 'react'
|
|
2
|
-
import { Box, Text, useStdout } from 'ink'
|
|
3
|
-
import { ScrollArea } from '../components/scroll-area'
|
|
4
|
-
import { LoggingManager } from '../../logging-manager'
|
|
5
|
-
import { useLogging } from '../use-logging'
|
|
6
|
-
|
|
7
|
-
type ViteTabProps = {
|
|
8
|
-
loggingManager: LoggingManager
|
|
9
|
-
}
|
|
10
|
-
|
|
11
|
-
export const ViteTab: React.FC<ViteTabProps> = ({ loggingManager }) => {
|
|
12
|
-
const { stdout } = useStdout()
|
|
13
|
-
const { logs } = useLogging(loggingManager, 'vite')
|
|
14
|
-
|
|
15
|
-
return (
|
|
16
|
-
<ScrollArea height={stdout.rows - 5} autoScroll={true}>
|
|
17
|
-
<Box flexDirection="column" padding={1}>
|
|
18
|
-
<Text color="yellow">Vite</Text>
|
|
19
|
-
{logs.length === 0 ? (
|
|
20
|
-
<Text dimColor>No Vite logs yet...</Text>
|
|
21
|
-
) : (
|
|
22
|
-
<Box flexDirection="column">
|
|
23
|
-
{logs.map((log) => (
|
|
24
|
-
<Text
|
|
25
|
-
key={log.id}
|
|
26
|
-
color={log.level === 'error' ? 'red' : log.level === 'warn' ? 'yellow' : 'white'}>
|
|
27
|
-
[{log.timestamp.toLocaleTimeString()}] {log.message}
|
|
28
|
-
</Text>
|
|
29
|
-
))}
|
|
30
|
-
</Box>
|
|
31
|
-
)}
|
|
32
|
-
</Box>
|
|
33
|
-
</ScrollArea>
|
|
34
|
-
)
|
|
35
|
-
}
|
|
@@ -1,34 +0,0 @@
|
|
|
1
|
-
import { useState, useEffect } from 'react'
|
|
2
|
-
import { LoggingManager, type LogEntry } from '../logging-manager'
|
|
3
|
-
|
|
4
|
-
export const useLogging = (loggingManager: LoggingManager, service?: LogEntry['service']) => {
|
|
5
|
-
const [logs, setLogs] = useState<LogEntry[]>(() => {
|
|
6
|
-
return service ? loggingManager.getLogs(service) : loggingManager.getLogs()
|
|
7
|
-
})
|
|
8
|
-
|
|
9
|
-
const [allLogs, setAllLogs] = useState<Record<string, LogEntry[]>>(() => {
|
|
10
|
-
return loggingManager.getAllLogs()
|
|
11
|
-
})
|
|
12
|
-
|
|
13
|
-
useEffect(() => {
|
|
14
|
-
const unsubscribe = loggingManager.subscribe(() => {
|
|
15
|
-
if (service) {
|
|
16
|
-
setLogs(loggingManager.getLogs(service))
|
|
17
|
-
} else {
|
|
18
|
-
setLogs(loggingManager.getLogs())
|
|
19
|
-
}
|
|
20
|
-
setAllLogs(loggingManager.getAllLogs())
|
|
21
|
-
})
|
|
22
|
-
|
|
23
|
-
return () => {
|
|
24
|
-
unsubscribe()
|
|
25
|
-
}
|
|
26
|
-
}, [loggingManager, service])
|
|
27
|
-
|
|
28
|
-
return {
|
|
29
|
-
logs,
|
|
30
|
-
allLogs,
|
|
31
|
-
addLog: loggingManager.addLog.bind(loggingManager),
|
|
32
|
-
clearLogs: loggingManager.clearLogs.bind(loggingManager)
|
|
33
|
-
}
|
|
34
|
-
}
|
|
@@ -1,54 +0,0 @@
|
|
|
1
|
-
import { createServer, type LogOptions } from 'vite'
|
|
2
|
-
import { dirname, resolve } from 'path'
|
|
3
|
-
import react from '@vitejs/plugin-react'
|
|
4
|
-
import tailwindcss from '@tailwindcss/vite'
|
|
5
|
-
import { writeFile } from 'node:fs/promises'
|
|
6
|
-
|
|
7
|
-
import { createTempIndexHtml, createViteLogger } from './utils'
|
|
8
|
-
import { getDevConfig } from '../../../lib/config'
|
|
9
|
-
|
|
10
|
-
const PORT = 5173
|
|
11
|
-
|
|
12
|
-
export const createVite = async (props: {
|
|
13
|
-
srcDir: string
|
|
14
|
-
indexTsxPath: string
|
|
15
|
-
log: (msg: string, level?: string, options?: LogOptions) => void
|
|
16
|
-
}) => {
|
|
17
|
-
const devConfig = getDevConfig()
|
|
18
|
-
const projectDir = dirname(props.srcDir)
|
|
19
|
-
const tempIndexPath = resolve(projectDir, 'index.html')
|
|
20
|
-
|
|
21
|
-
try {
|
|
22
|
-
const indexHtml = createTempIndexHtml(props.indexTsxPath, projectDir)
|
|
23
|
-
await writeFile(tempIndexPath, indexHtml)
|
|
24
|
-
|
|
25
|
-
const server = await createServer({
|
|
26
|
-
root: projectDir,
|
|
27
|
-
plugins: [react(), tailwindcss()],
|
|
28
|
-
customLogger: createViteLogger(props.log),
|
|
29
|
-
server: {
|
|
30
|
-
port: PORT,
|
|
31
|
-
host: true,
|
|
32
|
-
proxy: {
|
|
33
|
-
'/api': {
|
|
34
|
-
target: `http://localhost:${devConfig.gatewayPort}`,
|
|
35
|
-
changeOrigin: true,
|
|
36
|
-
secure: false
|
|
37
|
-
},
|
|
38
|
-
'/assets': {
|
|
39
|
-
target: `http://localhost:${devConfig.gatewayPort}`,
|
|
40
|
-
changeOrigin: true,
|
|
41
|
-
secure: false
|
|
42
|
-
}
|
|
43
|
-
}
|
|
44
|
-
}
|
|
45
|
-
})
|
|
46
|
-
|
|
47
|
-
await server.listen()
|
|
48
|
-
props.log(`Vite server started on http://localhost:${PORT}`)
|
|
49
|
-
|
|
50
|
-
return server
|
|
51
|
-
} catch (error) {
|
|
52
|
-
console.error('Error setting up Vite:', error)
|
|
53
|
-
}
|
|
54
|
-
}
|
|
@@ -1,71 +0,0 @@
|
|
|
1
|
-
import { resolve, basename } from 'path'
|
|
2
|
-
import type { Logger, LogOptions } from 'vite'
|
|
3
|
-
|
|
4
|
-
export const createViteLogger = (
|
|
5
|
-
log: (msg: string, level?: string, options?: LogOptions) => void
|
|
6
|
-
): Logger => {
|
|
7
|
-
return {
|
|
8
|
-
info: (msg: string, options) => {
|
|
9
|
-
log(msg, 'info', options)
|
|
10
|
-
},
|
|
11
|
-
warn: (msg: string, options) => {
|
|
12
|
-
log(msg, 'warn', options)
|
|
13
|
-
},
|
|
14
|
-
warnOnce: (msg: string, options) => {
|
|
15
|
-
log(msg, 'warn', options)
|
|
16
|
-
},
|
|
17
|
-
error: (msg: string, options) => {
|
|
18
|
-
log(msg, 'error', options)
|
|
19
|
-
},
|
|
20
|
-
clearScreen: () => {
|
|
21
|
-
// Not implemented - we don't want to clear screens in our UI
|
|
22
|
-
},
|
|
23
|
-
hasErrorLogged: () => {
|
|
24
|
-
return false // Simple implementation
|
|
25
|
-
},
|
|
26
|
-
hasWarned: false
|
|
27
|
-
}
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
export const createTempIndexHtml = (componentPath: string, projectRoot: string): string => {
|
|
31
|
-
const relativePath = resolve(componentPath).replace(projectRoot + '/', '')
|
|
32
|
-
const componentName = basename(componentPath, '.tsx')
|
|
33
|
-
|
|
34
|
-
return `<!DOCTYPE html>
|
|
35
|
-
<html lang="en">
|
|
36
|
-
<head>
|
|
37
|
-
<meta charset="UTF-8" />
|
|
38
|
-
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
39
|
-
<title>${componentName} - Dev Mode</title>
|
|
40
|
-
<style>
|
|
41
|
-
* {
|
|
42
|
-
box-sizing: border-box;
|
|
43
|
-
}
|
|
44
|
-
body {
|
|
45
|
-
margin: 0;
|
|
46
|
-
padding: 0;
|
|
47
|
-
font-family: system-ui, -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;
|
|
48
|
-
}
|
|
49
|
-
#root {
|
|
50
|
-
min-height: 100vh;
|
|
51
|
-
}
|
|
52
|
-
</style>
|
|
53
|
-
</head>
|
|
54
|
-
<body>
|
|
55
|
-
<div id="root"></div>
|
|
56
|
-
<script type="module">
|
|
57
|
-
import React from 'react'
|
|
58
|
-
import ReactDOM from 'react-dom/client'
|
|
59
|
-
import { Page } from '/${relativePath}'
|
|
60
|
-
|
|
61
|
-
const root = ReactDOM.createRoot(document.getElementById('root'))
|
|
62
|
-
|
|
63
|
-
root.render(
|
|
64
|
-
React.createElement(React.StrictMode, null,
|
|
65
|
-
React.createElement(Page)
|
|
66
|
-
)
|
|
67
|
-
)
|
|
68
|
-
</script>
|
|
69
|
-
</body>
|
|
70
|
-
</html>`
|
|
71
|
-
}
|