@tothalex/nulljs 0.0.53 → 0.0.54
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 +8 -5
- package/src/cli.ts +0 -24
- package/src/commands/config.ts +0 -130
- package/src/commands/deploy.ts +0 -219
- package/src/commands/dev.ts +0 -10
- package/src/commands/host.ts +0 -330
- package/src/commands/index.ts +0 -6
- package/src/commands/secret.ts +0 -387
- package/src/commands/status.ts +0 -41
- package/src/components/DeployAnimation.tsx +0 -92
- package/src/components/DeploymentLogsPane.tsx +0 -79
- package/src/components/Header.tsx +0 -57
- package/src/components/HelpModal.tsx +0 -64
- package/src/components/SystemLogsPane.tsx +0 -78
- package/src/config/index.ts +0 -181
- package/src/lib/bundle/external.ts +0 -23
- package/src/lib/bundle/function.ts +0 -125
- package/src/lib/bundle/index.ts +0 -5
- package/src/lib/bundle/react.ts +0 -149
- package/src/lib/bundle/types.ts +0 -4
- package/src/lib/deploy.ts +0 -103
- package/src/lib/server.ts +0 -160
- package/src/lib/vite.ts +0 -120
- package/src/lib/watcher.ts +0 -274
- package/src/ui.tsx +0 -363
- package/tsconfig.json +0 -30
|
@@ -1,64 +0,0 @@
|
|
|
1
|
-
type HelpModalProps = {
|
|
2
|
-
visible: boolean
|
|
3
|
-
}
|
|
4
|
-
|
|
5
|
-
const KeyBinding = ({ keys, description }: { keys: string; description: string }) => (
|
|
6
|
-
<text>
|
|
7
|
-
{' '}
|
|
8
|
-
<span fg="green">{keys}</span>
|
|
9
|
-
<span fg="gray"> - </span>
|
|
10
|
-
{description}
|
|
11
|
-
</text>
|
|
12
|
-
)
|
|
13
|
-
|
|
14
|
-
const Section = ({ title }: { title: string }) => (
|
|
15
|
-
<text>
|
|
16
|
-
<span fg="gray">{title}</span>
|
|
17
|
-
</text>
|
|
18
|
-
)
|
|
19
|
-
|
|
20
|
-
export const HelpModal = ({ visible }: HelpModalProps) => {
|
|
21
|
-
if (!visible) return null
|
|
22
|
-
|
|
23
|
-
return (
|
|
24
|
-
<box
|
|
25
|
-
position="absolute"
|
|
26
|
-
top={0}
|
|
27
|
-
left={0}
|
|
28
|
-
width="100%"
|
|
29
|
-
height="100%"
|
|
30
|
-
backgroundColor="black"
|
|
31
|
-
opacity={0.9}
|
|
32
|
-
justifyContent="center"
|
|
33
|
-
alignItems="center">
|
|
34
|
-
<box
|
|
35
|
-
width={50}
|
|
36
|
-
height={18}
|
|
37
|
-
border
|
|
38
|
-
borderStyle="rounded"
|
|
39
|
-
flexDirection="column"
|
|
40
|
-
padding={1}
|
|
41
|
-
backgroundColor="black">
|
|
42
|
-
<text>
|
|
43
|
-
<span fg="cyan">
|
|
44
|
-
<strong>Keyboard Shortcuts</strong>
|
|
45
|
-
</span>
|
|
46
|
-
</text>
|
|
47
|
-
<text />
|
|
48
|
-
<Section title="Navigation" />
|
|
49
|
-
<KeyBinding keys="1/2" description="Switch tabs" />
|
|
50
|
-
<KeyBinding keys="h/l" description="Previous/next tab" />
|
|
51
|
-
<KeyBinding keys="Tab" description="Cycle tabs" />
|
|
52
|
-
<text />
|
|
53
|
-
<Section title="Scrolling" />
|
|
54
|
-
<KeyBinding keys="j/k" description="Scroll down/up" />
|
|
55
|
-
<KeyBinding keys="g/G" description="Top/bottom" />
|
|
56
|
-
<text />
|
|
57
|
-
<Section title="Actions" />
|
|
58
|
-
<KeyBinding keys="d" description="Deploy all" />
|
|
59
|
-
<KeyBinding keys="?" description="Toggle help" />
|
|
60
|
-
<KeyBinding keys="Esc" description="Close help" />
|
|
61
|
-
</box>
|
|
62
|
-
</box>
|
|
63
|
-
)
|
|
64
|
-
}
|
|
@@ -1,78 +0,0 @@
|
|
|
1
|
-
import type { SystemLogEntry } from '@nulljs/api'
|
|
2
|
-
|
|
3
|
-
interface SystemLogsPaneProps {
|
|
4
|
-
logs: SystemLogEntry[]
|
|
5
|
-
loading: boolean
|
|
6
|
-
autoScroll: boolean
|
|
7
|
-
jumpTrigger: number
|
|
8
|
-
}
|
|
9
|
-
|
|
10
|
-
const getLevelColor = (level: string): string => {
|
|
11
|
-
switch (level.toLowerCase()) {
|
|
12
|
-
case 'error':
|
|
13
|
-
return 'red'
|
|
14
|
-
case 'warn':
|
|
15
|
-
case 'warning':
|
|
16
|
-
return 'yellow'
|
|
17
|
-
case 'info':
|
|
18
|
-
return 'green'
|
|
19
|
-
case 'debug':
|
|
20
|
-
case 'trace':
|
|
21
|
-
return 'gray'
|
|
22
|
-
default:
|
|
23
|
-
return 'white'
|
|
24
|
-
}
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
const sanitizeMessage = (message: string): string => {
|
|
28
|
-
return message
|
|
29
|
-
.replace(/\x1b\[[0-9;]*m/g, '')
|
|
30
|
-
.replace(/[\r\n]+/g, ' ')
|
|
31
|
-
.replace(/\s+/g, ' ')
|
|
32
|
-
.trim()
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
export const SystemLogsPane = (props: SystemLogsPaneProps) => {
|
|
36
|
-
if (props.loading && props.logs.length === 0) {
|
|
37
|
-
return (
|
|
38
|
-
<box flexDirection="column" padding={1}>
|
|
39
|
-
<text>Loading system logs...</text>
|
|
40
|
-
</box>
|
|
41
|
-
)
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
if (!props.loading && props.logs.length === 0) {
|
|
45
|
-
return (
|
|
46
|
-
<box flexDirection="column" padding={1}>
|
|
47
|
-
<text>No system logs found</text>
|
|
48
|
-
</box>
|
|
49
|
-
)
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
// Reverse logs so oldest are at top, newest at bottom
|
|
53
|
-
const logsInOrder = [...props.logs].reverse()
|
|
54
|
-
|
|
55
|
-
return (
|
|
56
|
-
<scrollbox
|
|
57
|
-
focused
|
|
58
|
-
stickyStart={props.autoScroll ? 'bottom' : 'top'}
|
|
59
|
-
stickyScroll={props.autoScroll}
|
|
60
|
-
key={`${props.autoScroll}-${props.jumpTrigger}`}>
|
|
61
|
-
{logsInOrder.map((log) => {
|
|
62
|
-
const time = new Date(log.timestamp).toLocaleTimeString()
|
|
63
|
-
const timestamp = `[${time}]`
|
|
64
|
-
const level = log.level.toUpperCase()
|
|
65
|
-
|
|
66
|
-
return (
|
|
67
|
-
<box key={log.id} flexDirection="row">
|
|
68
|
-
<text>
|
|
69
|
-
<span fg="gray">{timestamp}</span>
|
|
70
|
-
<span fg={getLevelColor(log.level)}>{` ${level} `}</span>
|
|
71
|
-
<span>{sanitizeMessage(log.message)}</span>
|
|
72
|
-
</text>
|
|
73
|
-
</box>
|
|
74
|
-
)
|
|
75
|
-
})}
|
|
76
|
-
</scrollbox>
|
|
77
|
-
)
|
|
78
|
-
}
|
package/src/config/index.ts
DELETED
|
@@ -1,181 +0,0 @@
|
|
|
1
|
-
import { readFileSync, writeFileSync, existsSync, mkdirSync } from 'fs'
|
|
2
|
-
import { join } from 'path'
|
|
3
|
-
import chalk from 'chalk'
|
|
4
|
-
|
|
5
|
-
export type Config = {
|
|
6
|
-
name: string
|
|
7
|
-
key: {
|
|
8
|
-
public: string
|
|
9
|
-
private: string
|
|
10
|
-
}
|
|
11
|
-
api: string
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
type ConfigStore = {
|
|
15
|
-
current: string
|
|
16
|
-
configs: Record<string, Config>
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
// Local config (project-level)
|
|
20
|
-
const getLocalConfigDir = () => join(process.cwd(), '.nulljs')
|
|
21
|
-
const getLocalConfigFile = () => join(getLocalConfigDir(), 'config.json')
|
|
22
|
-
|
|
23
|
-
const ensureLocalConfigDir = () => {
|
|
24
|
-
const localDir = getLocalConfigDir()
|
|
25
|
-
if (!existsSync(localDir)) {
|
|
26
|
-
mkdirSync(localDir, { recursive: true })
|
|
27
|
-
}
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
const readConfigStore = (): ConfigStore | null => {
|
|
31
|
-
try {
|
|
32
|
-
const localFile = getLocalConfigFile()
|
|
33
|
-
if (!existsSync(localFile)) {
|
|
34
|
-
return null
|
|
35
|
-
}
|
|
36
|
-
const data = readFileSync(localFile, 'utf-8')
|
|
37
|
-
return JSON.parse(data)
|
|
38
|
-
} catch {
|
|
39
|
-
return null
|
|
40
|
-
}
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
const writeConfigStore = (store: ConfigStore) => {
|
|
44
|
-
ensureLocalConfigDir()
|
|
45
|
-
writeFileSync(getLocalConfigFile(), JSON.stringify(store, null, 2))
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
export const readLocalConfig = (): Config | null => {
|
|
49
|
-
const store = readConfigStore()
|
|
50
|
-
if (!store) return null
|
|
51
|
-
return store.configs[store.current] ?? null
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
export const getConfig = (name: string): Config | null => {
|
|
55
|
-
const store = readConfigStore()
|
|
56
|
-
if (!store) return null
|
|
57
|
-
return store.configs[name] ?? null
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
export const listConfigs = (): { configs: Config[]; current: string } | null => {
|
|
61
|
-
const store = readConfigStore()
|
|
62
|
-
if (!store) return null
|
|
63
|
-
return {
|
|
64
|
-
configs: Object.values(store.configs),
|
|
65
|
-
current: store.current
|
|
66
|
-
}
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
export const useConfig = (name: string): boolean => {
|
|
70
|
-
const store = readConfigStore()
|
|
71
|
-
if (!store) {
|
|
72
|
-
console.error(chalk.red('✗ No configurations found. Run "nulljs dev" first.'))
|
|
73
|
-
return false
|
|
74
|
-
}
|
|
75
|
-
if (!store.configs[name]) {
|
|
76
|
-
console.error(chalk.red(`✗ Config "${name}" not found.`))
|
|
77
|
-
console.log(chalk.gray('Available configs:'))
|
|
78
|
-
Object.keys(store.configs).forEach((n) => {
|
|
79
|
-
console.log(chalk.gray(` - ${n}`))
|
|
80
|
-
})
|
|
81
|
-
return false
|
|
82
|
-
}
|
|
83
|
-
store.current = name
|
|
84
|
-
writeConfigStore(store)
|
|
85
|
-
console.log(chalk.green(`✓ Now using config "${name}"`))
|
|
86
|
-
return true
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
export const createConfig = async (name: string, api: string): Promise<Config> => {
|
|
90
|
-
const store = readConfigStore() ?? { current: 'dev', configs: {} }
|
|
91
|
-
|
|
92
|
-
if (store.configs[name]) {
|
|
93
|
-
console.error(chalk.red(`✗ Config "${name}" already exists.`))
|
|
94
|
-
process.exit(1)
|
|
95
|
-
}
|
|
96
|
-
|
|
97
|
-
const keyPair = await crypto.subtle.generateKey(
|
|
98
|
-
{
|
|
99
|
-
name: 'Ed25519',
|
|
100
|
-
namedCurve: 'Ed25519'
|
|
101
|
-
},
|
|
102
|
-
true,
|
|
103
|
-
['sign', 'verify']
|
|
104
|
-
)
|
|
105
|
-
|
|
106
|
-
const privateKeyBuffer = await crypto.subtle.exportKey('pkcs8', keyPair.privateKey)
|
|
107
|
-
const publicKeyBuffer = await crypto.subtle.exportKey('spki', keyPair.publicKey)
|
|
108
|
-
|
|
109
|
-
const privateKey = btoa(String.fromCharCode(...new Uint8Array(privateKeyBuffer)))
|
|
110
|
-
const publicKey = btoa(String.fromCharCode(...new Uint8Array(publicKeyBuffer)))
|
|
111
|
-
|
|
112
|
-
const newConfig: Config = {
|
|
113
|
-
name,
|
|
114
|
-
key: {
|
|
115
|
-
public: publicKey,
|
|
116
|
-
private: privateKey
|
|
117
|
-
},
|
|
118
|
-
api
|
|
119
|
-
}
|
|
120
|
-
|
|
121
|
-
store.configs[name] = newConfig
|
|
122
|
-
writeConfigStore(store)
|
|
123
|
-
|
|
124
|
-
console.log(chalk.green(`✓ Config "${name}" created`))
|
|
125
|
-
console.log(chalk.blue(' API:') + ` ${api}`)
|
|
126
|
-
console.log(chalk.blue(' Public Key:') + ` ${publicKey.substring(0, 30)}...`)
|
|
127
|
-
|
|
128
|
-
return newConfig
|
|
129
|
-
}
|
|
130
|
-
|
|
131
|
-
export const getOrCreateLocalDevConfig = async (api: string = 'http://localhost:3000'): Promise<Config> => {
|
|
132
|
-
const store = readConfigStore()
|
|
133
|
-
|
|
134
|
-
if (store?.configs['dev']) {
|
|
135
|
-
return store.configs['dev']
|
|
136
|
-
}
|
|
137
|
-
|
|
138
|
-
// Generate new key pair for dev
|
|
139
|
-
const keyPair = await crypto.subtle.generateKey(
|
|
140
|
-
{
|
|
141
|
-
name: 'Ed25519',
|
|
142
|
-
namedCurve: 'Ed25519'
|
|
143
|
-
},
|
|
144
|
-
true,
|
|
145
|
-
['sign', 'verify']
|
|
146
|
-
)
|
|
147
|
-
|
|
148
|
-
const privateKeyBuffer = await crypto.subtle.exportKey('pkcs8', keyPair.privateKey)
|
|
149
|
-
const publicKeyBuffer = await crypto.subtle.exportKey('spki', keyPair.publicKey)
|
|
150
|
-
|
|
151
|
-
const privateKey = btoa(String.fromCharCode(...new Uint8Array(privateKeyBuffer)))
|
|
152
|
-
const publicKey = btoa(String.fromCharCode(...new Uint8Array(publicKeyBuffer)))
|
|
153
|
-
|
|
154
|
-
const devConfig: Config = {
|
|
155
|
-
name: 'dev',
|
|
156
|
-
key: {
|
|
157
|
-
public: publicKey,
|
|
158
|
-
private: privateKey
|
|
159
|
-
},
|
|
160
|
-
api
|
|
161
|
-
}
|
|
162
|
-
|
|
163
|
-
const newStore: ConfigStore = store ?? { current: 'dev', configs: {} }
|
|
164
|
-
newStore.configs['dev'] = devConfig
|
|
165
|
-
newStore.current = 'dev'
|
|
166
|
-
writeConfigStore(newStore)
|
|
167
|
-
|
|
168
|
-
return devConfig
|
|
169
|
-
}
|
|
170
|
-
|
|
171
|
-
export const loadPrivateKey = async (config: Config): Promise<CryptoKey> => {
|
|
172
|
-
const privateKeyBytes = Uint8Array.from(atob(config.key.private), (c) => c.charCodeAt(0))
|
|
173
|
-
|
|
174
|
-
return crypto.subtle.importKey(
|
|
175
|
-
'pkcs8',
|
|
176
|
-
privateKeyBytes,
|
|
177
|
-
{ name: 'Ed25519', namedCurve: 'Ed25519' },
|
|
178
|
-
false,
|
|
179
|
-
['sign']
|
|
180
|
-
)
|
|
181
|
-
}
|
|
@@ -1,23 +0,0 @@
|
|
|
1
|
-
export const external = [
|
|
2
|
-
'cloud/cache',
|
|
3
|
-
'cloud/event',
|
|
4
|
-
'cloud',
|
|
5
|
-
'cloud/postgres',
|
|
6
|
-
'cloud/secret',
|
|
7
|
-
'cloud/uuid',
|
|
8
|
-
'assert',
|
|
9
|
-
'buffer',
|
|
10
|
-
'crypto',
|
|
11
|
-
'dns',
|
|
12
|
-
'events',
|
|
13
|
-
'net',
|
|
14
|
-
'os',
|
|
15
|
-
'process',
|
|
16
|
-
'stream/web',
|
|
17
|
-
'string_decoder',
|
|
18
|
-
'timers',
|
|
19
|
-
'tty',
|
|
20
|
-
'url',
|
|
21
|
-
'util',
|
|
22
|
-
'zlib'
|
|
23
|
-
]
|
|
@@ -1,125 +0,0 @@
|
|
|
1
|
-
import type { InlineConfig, Plugin, UserConfig } from 'vite'
|
|
2
|
-
import { external } from './external'
|
|
3
|
-
import type { PluginOptions } from './types'
|
|
4
|
-
|
|
5
|
-
const jsFunction = ({ filePath }: PluginOptions): Plugin => {
|
|
6
|
-
return {
|
|
7
|
-
name: 'nulljs-function-plugin',
|
|
8
|
-
apply: 'build',
|
|
9
|
-
config: async (config: UserConfig, { command }) => {
|
|
10
|
-
if (command !== 'build') {
|
|
11
|
-
return config
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
return {
|
|
15
|
-
build: {
|
|
16
|
-
rollupOptions: {
|
|
17
|
-
input: {
|
|
18
|
-
handler: filePath
|
|
19
|
-
},
|
|
20
|
-
external,
|
|
21
|
-
output: {
|
|
22
|
-
// preserveModules: false,
|
|
23
|
-
entryFileNames: '[name].js'
|
|
24
|
-
},
|
|
25
|
-
preserveEntrySignatures: 'strict'
|
|
26
|
-
}
|
|
27
|
-
}
|
|
28
|
-
}
|
|
29
|
-
}
|
|
30
|
-
}
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
export const functionConfig = (filePath: string): InlineConfig => {
|
|
34
|
-
return {
|
|
35
|
-
logLevel: 'error',
|
|
36
|
-
plugins: [
|
|
37
|
-
jsFunction({
|
|
38
|
-
filePath
|
|
39
|
-
})
|
|
40
|
-
],
|
|
41
|
-
build: {
|
|
42
|
-
outDir: '/tmp',
|
|
43
|
-
minify: false
|
|
44
|
-
}
|
|
45
|
-
}
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
export type FunctionEntry = {
|
|
49
|
-
name: string
|
|
50
|
-
path: string
|
|
51
|
-
type: 'api' | 'cron' | 'event'
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
export type BuildResult = {
|
|
55
|
-
entry: FunctionEntry
|
|
56
|
-
code: string
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
export type WatchCallbacks = {
|
|
60
|
-
onBuildComplete: (results: BuildResult[]) => Promise<void>
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
const jsFunctionWatch = (entries: FunctionEntry[], callbacks: WatchCallbacks): Plugin => {
|
|
64
|
-
// Map output filename back to entry
|
|
65
|
-
const outputToEntry = new Map<string, FunctionEntry>()
|
|
66
|
-
|
|
67
|
-
return {
|
|
68
|
-
name: 'nulljs-function-watch-plugin',
|
|
69
|
-
apply: 'build',
|
|
70
|
-
config: async (config: UserConfig, { command }) => {
|
|
71
|
-
if (command !== 'build') {
|
|
72
|
-
return config
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
// Build input object from entries
|
|
76
|
-
const input: Record<string, string> = {}
|
|
77
|
-
for (const entry of entries) {
|
|
78
|
-
input[entry.name] = entry.path
|
|
79
|
-
outputToEntry.set(`${entry.name}.js`, entry)
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
return {
|
|
83
|
-
build: {
|
|
84
|
-
rollupOptions: {
|
|
85
|
-
input,
|
|
86
|
-
external,
|
|
87
|
-
output: {
|
|
88
|
-
entryFileNames: '[name].js'
|
|
89
|
-
},
|
|
90
|
-
preserveEntrySignatures: 'strict'
|
|
91
|
-
}
|
|
92
|
-
}
|
|
93
|
-
}
|
|
94
|
-
},
|
|
95
|
-
async writeBundle(_, bundle) {
|
|
96
|
-
const results: BuildResult[] = []
|
|
97
|
-
|
|
98
|
-
for (const [fileName, chunk] of Object.entries(bundle)) {
|
|
99
|
-
if (chunk.type !== 'chunk' || !chunk.isEntry) continue
|
|
100
|
-
|
|
101
|
-
const entry = outputToEntry.get(fileName)
|
|
102
|
-
if (!entry) continue
|
|
103
|
-
|
|
104
|
-
results.push({ entry, code: chunk.code })
|
|
105
|
-
}
|
|
106
|
-
|
|
107
|
-
await callbacks.onBuildComplete(results)
|
|
108
|
-
}
|
|
109
|
-
}
|
|
110
|
-
}
|
|
111
|
-
|
|
112
|
-
export const functionWatchConfig = (
|
|
113
|
-
entries: FunctionEntry[],
|
|
114
|
-
callbacks: WatchCallbacks
|
|
115
|
-
): InlineConfig => {
|
|
116
|
-
return {
|
|
117
|
-
logLevel: 'error',
|
|
118
|
-
plugins: [jsFunctionWatch(entries, callbacks)],
|
|
119
|
-
build: {
|
|
120
|
-
outDir: '/tmp/nulljs-dev',
|
|
121
|
-
minify: false,
|
|
122
|
-
watch: {}
|
|
123
|
-
}
|
|
124
|
-
}
|
|
125
|
-
}
|
package/src/lib/bundle/index.ts
DELETED
package/src/lib/bundle/react.ts
DELETED
|
@@ -1,149 +0,0 @@
|
|
|
1
|
-
import { basename, extname } from 'path'
|
|
2
|
-
import type { InlineConfig, Plugin, UserConfig } from 'vite'
|
|
3
|
-
import react from '@vitejs/plugin-react'
|
|
4
|
-
import tailwindcss from '@tailwindcss/vite'
|
|
5
|
-
|
|
6
|
-
import type { PluginOptions } from './types'
|
|
7
|
-
|
|
8
|
-
const jsConfig = ({ filePath }: PluginOptions): Plugin => {
|
|
9
|
-
const entry = basename(filePath, extname(filePath))
|
|
10
|
-
const virtualPrefix = `virtual:config/${entry}.tsx`
|
|
11
|
-
|
|
12
|
-
return {
|
|
13
|
-
name: 'nulljs-config-plugin',
|
|
14
|
-
apply: 'build',
|
|
15
|
-
config: async (config: UserConfig, { command }) => {
|
|
16
|
-
if (command !== 'build') {
|
|
17
|
-
return config
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
return {
|
|
21
|
-
build: {
|
|
22
|
-
ssr: true,
|
|
23
|
-
rollupOptions: {
|
|
24
|
-
input: {
|
|
25
|
-
[entry]: virtualPrefix
|
|
26
|
-
},
|
|
27
|
-
external: ['react', 'react/jsx-runtime'],
|
|
28
|
-
output: {
|
|
29
|
-
entryFileNames: '[name].js'
|
|
30
|
-
},
|
|
31
|
-
preserveEntrySignatures: 'strict'
|
|
32
|
-
}
|
|
33
|
-
}
|
|
34
|
-
}
|
|
35
|
-
},
|
|
36
|
-
|
|
37
|
-
resolveId: (id: string) => {
|
|
38
|
-
if (id === virtualPrefix) {
|
|
39
|
-
return id
|
|
40
|
-
}
|
|
41
|
-
return null
|
|
42
|
-
},
|
|
43
|
-
|
|
44
|
-
load: (id: string) => {
|
|
45
|
-
if (id === virtualPrefix) {
|
|
46
|
-
const script = `
|
|
47
|
-
import { config } from "${filePath.replace(/\\/g, '\\\\')}";
|
|
48
|
-
|
|
49
|
-
export default config;
|
|
50
|
-
`
|
|
51
|
-
|
|
52
|
-
return script
|
|
53
|
-
}
|
|
54
|
-
return null
|
|
55
|
-
},
|
|
56
|
-
|
|
57
|
-
transform: (code, id) => {
|
|
58
|
-
if (id === filePath) {
|
|
59
|
-
const configMatch = code.match(/export const config = \{[^}]*\};/)
|
|
60
|
-
return configMatch ? { code: configMatch[0], map: null } : null
|
|
61
|
-
}
|
|
62
|
-
}
|
|
63
|
-
}
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
export const spaConfigConfig = (filePath: string): InlineConfig => {
|
|
67
|
-
return {
|
|
68
|
-
logLevel: 'error',
|
|
69
|
-
plugins: [
|
|
70
|
-
jsConfig({
|
|
71
|
-
filePath
|
|
72
|
-
})
|
|
73
|
-
],
|
|
74
|
-
build: {
|
|
75
|
-
outDir: '/tmp'
|
|
76
|
-
}
|
|
77
|
-
}
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
const jsSpa = ({ filePath }: PluginOptions): Plugin => {
|
|
81
|
-
const entry = basename(filePath, extname(filePath))
|
|
82
|
-
const virtualPrefix = `virtual:spa/${entry}.tsx`
|
|
83
|
-
|
|
84
|
-
return {
|
|
85
|
-
name: 'nulljs-spa-client-plugin',
|
|
86
|
-
apply: 'build',
|
|
87
|
-
config: async (config: UserConfig, { command }) => {
|
|
88
|
-
if (command !== 'build') {
|
|
89
|
-
return config
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
return {
|
|
93
|
-
build: {
|
|
94
|
-
rollupOptions: {
|
|
95
|
-
input: {
|
|
96
|
-
[entry]: virtualPrefix
|
|
97
|
-
},
|
|
98
|
-
external: ['cloud'],
|
|
99
|
-
output: {
|
|
100
|
-
entryFileNames: '[name].js'
|
|
101
|
-
}
|
|
102
|
-
}
|
|
103
|
-
},
|
|
104
|
-
plugins: [react()]
|
|
105
|
-
}
|
|
106
|
-
},
|
|
107
|
-
|
|
108
|
-
resolveId: (id: string) => {
|
|
109
|
-
if (id === virtualPrefix) {
|
|
110
|
-
return id
|
|
111
|
-
}
|
|
112
|
-
return null
|
|
113
|
-
},
|
|
114
|
-
|
|
115
|
-
load: (id: string) => {
|
|
116
|
-
if (id === virtualPrefix) {
|
|
117
|
-
const script = `
|
|
118
|
-
import { StrictMode } from 'react'
|
|
119
|
-
import { createRoot } from 'react-dom/client'
|
|
120
|
-
|
|
121
|
-
import { Page } from "${filePath.replace(/\\/g, '\\\\')}";
|
|
122
|
-
|
|
123
|
-
createRoot(document.getElementById('root')!).render(
|
|
124
|
-
<StrictMode>
|
|
125
|
-
<Page />
|
|
126
|
-
</StrictMode>
|
|
127
|
-
)`
|
|
128
|
-
|
|
129
|
-
return script
|
|
130
|
-
}
|
|
131
|
-
return null
|
|
132
|
-
}
|
|
133
|
-
}
|
|
134
|
-
}
|
|
135
|
-
|
|
136
|
-
export const spaClientConfig = (filePath: string): InlineConfig => {
|
|
137
|
-
return {
|
|
138
|
-
logLevel: 'error',
|
|
139
|
-
plugins: [
|
|
140
|
-
jsSpa({
|
|
141
|
-
filePath
|
|
142
|
-
}),
|
|
143
|
-
tailwindcss()
|
|
144
|
-
],
|
|
145
|
-
build: {
|
|
146
|
-
outDir: '/tmp'
|
|
147
|
-
}
|
|
148
|
-
}
|
|
149
|
-
}
|
package/src/lib/bundle/types.ts
DELETED