@eighty4/dank 0.0.5-0 → 0.0.5-1
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/lib/developer.ts +83 -26
- package/lib/services.ts +60 -34
- package/lib_js/services.js +46 -20
- package/package.json +1 -1
package/lib/developer.ts
CHANGED
|
@@ -32,44 +32,101 @@ export type LogEvent = {
|
|
|
32
32
|
|
|
33
33
|
type LogEventData =
|
|
34
34
|
| LogEventDatum
|
|
35
|
-
|
|
|
36
|
-
|
|
|
37
|
-
|
|
|
35
|
+
| LogEventDataArray
|
|
36
|
+
| LogEventDataRecord
|
|
37
|
+
| LogEventDataSet
|
|
38
38
|
|
|
39
39
|
type LogEventDatum = boolean | number | string | null | undefined
|
|
40
40
|
|
|
41
|
+
type LogEventDataArray = Array<LogEventDatum> | Array<LogEventDataRecord>
|
|
42
|
+
|
|
43
|
+
type LogEventDataRecord = Record<
|
|
44
|
+
string,
|
|
45
|
+
LogEventDatum | LogEventDataArray | LogEventDataSet
|
|
46
|
+
>
|
|
47
|
+
|
|
48
|
+
type LogEventDataSet = Set<LogEventDatum>
|
|
49
|
+
|
|
41
50
|
function toStringLogEvent(logEvent: LogEvent): string {
|
|
42
51
|
const when = new Date().toISOString()
|
|
43
52
|
const message = `[${logEvent.realm}] ${logEvent.message}\n${when}\n`
|
|
44
|
-
if (
|
|
53
|
+
if (logEvent.data) {
|
|
54
|
+
const data: string = Object.keys(logEvent.data)
|
|
55
|
+
.sort()
|
|
56
|
+
.map(key => toStringData(key, logEvent.data![key]))
|
|
57
|
+
.join('')
|
|
58
|
+
return `${message}\n${data}`
|
|
59
|
+
} else {
|
|
45
60
|
return message
|
|
46
61
|
}
|
|
47
|
-
let data = ''
|
|
48
|
-
for (const k of Object.keys(logEvent.data).sort()) {
|
|
49
|
-
data += `\n ${k} = ${toStringData(logEvent.data[k])}`
|
|
50
|
-
}
|
|
51
|
-
return `${message}${data}\n`
|
|
52
62
|
}
|
|
53
63
|
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
64
|
+
const PAD = ' '
|
|
65
|
+
const nextIndent = (pad: string) => pad + PAD
|
|
66
|
+
|
|
67
|
+
function toStringData(
|
|
68
|
+
key: string,
|
|
69
|
+
data: LogEventData,
|
|
70
|
+
pad: string = PAD,
|
|
71
|
+
): string {
|
|
72
|
+
const prepend = `${pad}${key} = `
|
|
73
|
+
if (isDataAbsentOrScalar(data)) {
|
|
74
|
+
return `${prepend}${toStringDatum(data)}`
|
|
57
75
|
}
|
|
58
|
-
if (
|
|
59
|
-
|
|
60
|
-
typeof datum === 'object' &&
|
|
61
|
-
datum.constructor.name === 'Object'
|
|
62
|
-
) {
|
|
63
|
-
datum = Object.entries(datum).map(([k, v]) => `${k} = ${v}`)
|
|
76
|
+
if (data instanceof Set) {
|
|
77
|
+
data = Array.from(data)
|
|
64
78
|
}
|
|
65
|
-
if (Array.isArray(
|
|
66
|
-
|
|
67
|
-
return '[]'
|
|
68
|
-
} else {
|
|
69
|
-
return `[\n ${datum.join('\n ')}\n ]`
|
|
70
|
-
}
|
|
79
|
+
if (Array.isArray(data)) {
|
|
80
|
+
return `${prepend}${toStringArray(data, pad)}`
|
|
71
81
|
} else {
|
|
72
|
-
return `${
|
|
82
|
+
return `${prepend}${toStringRecord(data, pad)}`
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
function toStringDatum(datum: LogEventDatum): string {
|
|
87
|
+
return `${datum}\n`
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
function toStringArray(array: LogEventDataArray, padEnding: string): string {
|
|
91
|
+
if (array.length === 0) {
|
|
92
|
+
return '[]'
|
|
93
|
+
}
|
|
94
|
+
const padIndent = nextIndent(padEnding)
|
|
95
|
+
const content = array
|
|
96
|
+
.map(datum => {
|
|
97
|
+
if (isDataAbsentOrScalar(datum)) {
|
|
98
|
+
return toStringDatum(datum)
|
|
99
|
+
} else {
|
|
100
|
+
return toStringRecord(datum, padIndent)
|
|
101
|
+
}
|
|
102
|
+
})
|
|
103
|
+
.join(padIndent)
|
|
104
|
+
return `[\n${padIndent}${content}${padEnding}]\n`
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
function toStringRecord(record: LogEventDataRecord, padEnding: string): string {
|
|
108
|
+
const keys = Object.keys(record)
|
|
109
|
+
if (keys.length === 0) {
|
|
110
|
+
return '{}'
|
|
111
|
+
}
|
|
112
|
+
const padIndent = nextIndent(padEnding)
|
|
113
|
+
const content = keys
|
|
114
|
+
.map(key => toStringData(key, record[key], padIndent))
|
|
115
|
+
.join('')
|
|
116
|
+
return `{\n${content}${padEnding}}\n`
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
function isDataAbsentOrScalar(
|
|
120
|
+
data: LogEventData,
|
|
121
|
+
): data is undefined | null | string | boolean | number {
|
|
122
|
+
switch (typeof data) {
|
|
123
|
+
case 'undefined':
|
|
124
|
+
case 'string':
|
|
125
|
+
case 'boolean':
|
|
126
|
+
case 'number':
|
|
127
|
+
return true
|
|
128
|
+
default:
|
|
129
|
+
return data === null
|
|
73
130
|
}
|
|
74
131
|
}
|
|
75
132
|
|
|
@@ -79,7 +136,7 @@ function logToConsoleAndFile(out: string) {
|
|
|
79
136
|
}
|
|
80
137
|
|
|
81
138
|
function logToConsole(out: string) {
|
|
82
|
-
console.log(
|
|
139
|
+
console.log(out)
|
|
83
140
|
}
|
|
84
141
|
|
|
85
142
|
function logToFile(out: string) {
|
package/lib/services.ts
CHANGED
|
@@ -1,4 +1,9 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import {
|
|
2
|
+
type ChildProcess,
|
|
3
|
+
type ChildProcessWithoutNullStreams,
|
|
4
|
+
execSync,
|
|
5
|
+
spawn,
|
|
6
|
+
} from 'node:child_process'
|
|
2
7
|
import { basename, isAbsolute, resolve } from 'node:path'
|
|
3
8
|
import type { DevService, ResolvedDankConfig } from './config.ts'
|
|
4
9
|
|
|
@@ -15,6 +20,18 @@ export type HttpService = NonNullable<DevService['http']>
|
|
|
15
20
|
// up to date representation of dank.config.ts services
|
|
16
21
|
const running: Array<{ s: DevService; process: ChildProcess | null }> = []
|
|
17
22
|
|
|
23
|
+
// on Windows process.kill() and AbortSignal will not kill the process spawned with cmd.exe
|
|
24
|
+
if (process.platform === 'win32') {
|
|
25
|
+
process.once('SIGINT', () => process.exit())
|
|
26
|
+
process.once('exit', () => {
|
|
27
|
+
for (const { process } of running) {
|
|
28
|
+
if (process) {
|
|
29
|
+
execSync(`taskkill /pid ${process.pid} /T /F`)
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
})
|
|
33
|
+
}
|
|
34
|
+
|
|
18
35
|
let signal: AbortSignal
|
|
19
36
|
|
|
20
37
|
// batch of services that must be stopped before starting new services
|
|
@@ -140,34 +157,23 @@ function matchingConfig(a: DevService, b: DevService): boolean {
|
|
|
140
157
|
|
|
141
158
|
function startService(s: DevService): ChildProcess {
|
|
142
159
|
opPrint(s, 'starting')
|
|
143
|
-
const
|
|
144
|
-
const cmd = splitCmdAndArgs[0]
|
|
145
|
-
const args = splitCmdAndArgs.length === 1 ? [] : splitCmdAndArgs.slice(1)
|
|
146
|
-
const spawned = spawn(cmd, args, {
|
|
147
|
-
cwd: resolveCwd(s.cwd),
|
|
148
|
-
env: s.env,
|
|
149
|
-
signal,
|
|
150
|
-
detached: false,
|
|
151
|
-
shell: false,
|
|
152
|
-
})
|
|
160
|
+
const spawned = spawnService(s)
|
|
153
161
|
|
|
154
|
-
const stdoutLabel = logLabel(s
|
|
162
|
+
const stdoutLabel = logLabel(s, 32)
|
|
155
163
|
spawned.stdout.on('data', chunk => printChunk(stdoutLabel, chunk))
|
|
156
164
|
|
|
157
|
-
const stderrLabel = logLabel(s
|
|
165
|
+
const stderrLabel = logLabel(s, 31)
|
|
158
166
|
spawned.stderr.on('data', chunk => printChunk(stderrLabel, chunk))
|
|
159
167
|
|
|
160
168
|
spawned.on('error', e => {
|
|
161
|
-
if (e.name !== 'AbortError') {
|
|
162
|
-
const cause =
|
|
163
|
-
'code' in e && e.code === 'ENOENT'
|
|
164
|
-
? 'program not found'
|
|
165
|
-
: e.message
|
|
166
|
-
opPrint(s, 'error: ' + cause)
|
|
167
|
-
}
|
|
168
169
|
removeFromRunning(s)
|
|
170
|
+
if (e.name === 'AbortError') {
|
|
171
|
+
return
|
|
172
|
+
}
|
|
173
|
+
const cause =
|
|
174
|
+
'code' in e && e.code === 'ENOENT' ? 'program not found' : e.message
|
|
175
|
+
opPrint(s, 'error: ' + cause)
|
|
169
176
|
})
|
|
170
|
-
|
|
171
177
|
spawned.on('exit', () => {
|
|
172
178
|
opPrint(s, 'exited')
|
|
173
179
|
removeFromRunning(s)
|
|
@@ -176,6 +182,31 @@ function startService(s: DevService): ChildProcess {
|
|
|
176
182
|
return spawned
|
|
177
183
|
}
|
|
178
184
|
|
|
185
|
+
function spawnService(s: DevService): ChildProcessWithoutNullStreams {
|
|
186
|
+
const splitCmdAndArgs = s.command.split(/\s+/)
|
|
187
|
+
const program = splitCmdAndArgs[0]
|
|
188
|
+
const args = splitCmdAndArgs.length === 1 ? [] : splitCmdAndArgs.slice(1)
|
|
189
|
+
const env = s.env ? { ...process.env, ...s.env } : undefined
|
|
190
|
+
const cwd = resolveCwd(s.cwd)
|
|
191
|
+
if (process.platform === 'win32') {
|
|
192
|
+
return spawn('cmd', ['/c', program, ...args], {
|
|
193
|
+
cwd,
|
|
194
|
+
env,
|
|
195
|
+
detached: false,
|
|
196
|
+
shell: false,
|
|
197
|
+
windowsHide: true,
|
|
198
|
+
})
|
|
199
|
+
} else {
|
|
200
|
+
return spawn(program, args, {
|
|
201
|
+
cwd,
|
|
202
|
+
env,
|
|
203
|
+
signal,
|
|
204
|
+
detached: false,
|
|
205
|
+
shell: false,
|
|
206
|
+
})
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
|
|
179
210
|
function removeFromRunning(s: DevService) {
|
|
180
211
|
for (let i = 0; i < running.length; i++) {
|
|
181
212
|
if (matchingConfig(running[i].s, s)) {
|
|
@@ -227,18 +258,13 @@ function opLabel(s: DevService) {
|
|
|
227
258
|
return `\`${s.cwd ? s.cwd + ' ' : ''}${s.command}\``
|
|
228
259
|
}
|
|
229
260
|
|
|
230
|
-
function logLabel(
|
|
231
|
-
cwd
|
|
232
|
-
cmd: string,
|
|
233
|
-
args: Array<string>,
|
|
234
|
-
ansiColor: number,
|
|
235
|
-
): string {
|
|
236
|
-
cwd = !cwd
|
|
261
|
+
function logLabel(s: DevService, ansiColor: number): string {
|
|
262
|
+
s.cwd = !s.cwd
|
|
237
263
|
? './'
|
|
238
|
-
: cwd.startsWith('/')
|
|
239
|
-
? `/.../${basename(cwd)}`
|
|
240
|
-
: cwd.startsWith('.')
|
|
241
|
-
? cwd
|
|
242
|
-
: `./${cwd}`
|
|
243
|
-
return `\u001b[${ansiColor}m[\u001b[1m${
|
|
264
|
+
: s.cwd.startsWith('/')
|
|
265
|
+
? `/.../${basename(s.cwd)}`
|
|
266
|
+
: s.cwd.startsWith('.')
|
|
267
|
+
? s.cwd
|
|
268
|
+
: `./${s.cwd}`
|
|
269
|
+
return `\u001b[${ansiColor}m[\u001b[1m${s.command}\u001b[22m \u001b[2;3m${s.cwd}\u001b[22;23m]\u001b[0m`
|
|
244
270
|
}
|
package/lib_js/services.js
CHANGED
|
@@ -1,6 +1,16 @@
|
|
|
1
|
-
import { spawn } from "node:child_process";
|
|
1
|
+
import { execSync, spawn } from "node:child_process";
|
|
2
2
|
import { basename, isAbsolute, resolve } from "node:path";
|
|
3
3
|
const running = [];
|
|
4
|
+
if (process.platform === "win32") {
|
|
5
|
+
process.once("SIGINT", () => process.exit());
|
|
6
|
+
process.once("exit", () => {
|
|
7
|
+
for (const { process: process2 } of running) {
|
|
8
|
+
if (process2) {
|
|
9
|
+
execSync(`taskkill /pid ${process2.pid} /T /F`);
|
|
10
|
+
}
|
|
11
|
+
}
|
|
12
|
+
});
|
|
13
|
+
}
|
|
4
14
|
let signal;
|
|
5
15
|
let updating = null;
|
|
6
16
|
function startDevServices(services, _signal) {
|
|
@@ -111,26 +121,18 @@ function matchingConfig(a, b) {
|
|
|
111
121
|
}
|
|
112
122
|
function startService(s) {
|
|
113
123
|
opPrint(s, "starting");
|
|
114
|
-
const
|
|
115
|
-
const
|
|
116
|
-
const args = splitCmdAndArgs.length === 1 ? [] : splitCmdAndArgs.slice(1);
|
|
117
|
-
const spawned = spawn(cmd, args, {
|
|
118
|
-
cwd: resolveCwd(s.cwd),
|
|
119
|
-
env: s.env,
|
|
120
|
-
signal,
|
|
121
|
-
detached: false,
|
|
122
|
-
shell: false
|
|
123
|
-
});
|
|
124
|
-
const stdoutLabel = logLabel(s.cwd, cmd, args, 32);
|
|
124
|
+
const spawned = spawnService(s);
|
|
125
|
+
const stdoutLabel = logLabel(s, 32);
|
|
125
126
|
spawned.stdout.on("data", (chunk) => printChunk(stdoutLabel, chunk));
|
|
126
|
-
const stderrLabel = logLabel(s
|
|
127
|
+
const stderrLabel = logLabel(s, 31);
|
|
127
128
|
spawned.stderr.on("data", (chunk) => printChunk(stderrLabel, chunk));
|
|
128
129
|
spawned.on("error", (e) => {
|
|
129
|
-
if (e.name !== "AbortError") {
|
|
130
|
-
const cause = "code" in e && e.code === "ENOENT" ? "program not found" : e.message;
|
|
131
|
-
opPrint(s, "error: " + cause);
|
|
132
|
-
}
|
|
133
130
|
removeFromRunning(s);
|
|
131
|
+
if (e.name === "AbortError") {
|
|
132
|
+
return;
|
|
133
|
+
}
|
|
134
|
+
const cause = "code" in e && e.code === "ENOENT" ? "program not found" : e.message;
|
|
135
|
+
opPrint(s, "error: " + cause);
|
|
134
136
|
});
|
|
135
137
|
spawned.on("exit", () => {
|
|
136
138
|
opPrint(s, "exited");
|
|
@@ -139,6 +141,30 @@ function startService(s) {
|
|
|
139
141
|
});
|
|
140
142
|
return spawned;
|
|
141
143
|
}
|
|
144
|
+
function spawnService(s) {
|
|
145
|
+
const splitCmdAndArgs = s.command.split(/\s+/);
|
|
146
|
+
const program = splitCmdAndArgs[0];
|
|
147
|
+
const args = splitCmdAndArgs.length === 1 ? [] : splitCmdAndArgs.slice(1);
|
|
148
|
+
const env = s.env ? { ...process.env, ...s.env } : void 0;
|
|
149
|
+
const cwd = resolveCwd(s.cwd);
|
|
150
|
+
if (process.platform === "win32") {
|
|
151
|
+
return spawn("cmd", ["/c", program, ...args], {
|
|
152
|
+
cwd,
|
|
153
|
+
env,
|
|
154
|
+
detached: false,
|
|
155
|
+
shell: false,
|
|
156
|
+
windowsHide: true
|
|
157
|
+
});
|
|
158
|
+
} else {
|
|
159
|
+
return spawn(program, args, {
|
|
160
|
+
cwd,
|
|
161
|
+
env,
|
|
162
|
+
signal,
|
|
163
|
+
detached: false,
|
|
164
|
+
shell: false
|
|
165
|
+
});
|
|
166
|
+
}
|
|
167
|
+
}
|
|
142
168
|
function removeFromRunning(s) {
|
|
143
169
|
for (let i = 0; i < running.length; i++) {
|
|
144
170
|
if (matchingConfig(running[i].s, s)) {
|
|
@@ -181,9 +207,9 @@ function opPrint(s, msg) {
|
|
|
181
207
|
function opLabel(s) {
|
|
182
208
|
return `\`${s.cwd ? s.cwd + " " : ""}${s.command}\``;
|
|
183
209
|
}
|
|
184
|
-
function logLabel(
|
|
185
|
-
cwd = !cwd ? "./" : cwd.startsWith("/") ? `/.../${basename(cwd)}` : cwd.startsWith(".") ? cwd : `./${cwd}`;
|
|
186
|
-
return `\x1B[${ansiColor}m[\x1B[1m${
|
|
210
|
+
function logLabel(s, ansiColor) {
|
|
211
|
+
s.cwd = !s.cwd ? "./" : s.cwd.startsWith("/") ? `/.../${basename(s.cwd)}` : s.cwd.startsWith(".") ? s.cwd : `./${s.cwd}`;
|
|
212
|
+
return `\x1B[${ansiColor}m[\x1B[1m${s.command}\x1B[22m \x1B[2;3m${s.cwd}\x1B[22;23m]\x1B[0m`;
|
|
187
213
|
}
|
|
188
214
|
export {
|
|
189
215
|
startDevServices,
|