@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 CHANGED
@@ -32,44 +32,101 @@ export type LogEvent = {
32
32
 
33
33
  type LogEventData =
34
34
  | LogEventDatum
35
- | Array<LogEventDatum>
36
- | Set<LogEventDatum>
37
- | Record<string, LogEventDatum>
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 (!logEvent.data) {
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
- function toStringData(datum: LogEventData): string {
55
- if (datum instanceof Set) {
56
- datum = Array.from(datum)
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
- datum !== null &&
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(datum)) {
66
- if (datum.length === 0) {
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 `${datum}`
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('\n' + out)
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 { type ChildProcess, spawn } from 'node:child_process'
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 splitCmdAndArgs = s.command.split(/\s+/)
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.cwd, cmd, args, 32)
162
+ const stdoutLabel = logLabel(s, 32)
155
163
  spawned.stdout.on('data', chunk => printChunk(stdoutLabel, chunk))
156
164
 
157
- const stderrLabel = logLabel(s.cwd, cmd, args, 31)
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: string | undefined,
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${cmd}\u001b[22m ${args.join(' ')} \u001b[2;3m${cwd}\u001b[22;23m]\u001b[0m`
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
  }
@@ -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 splitCmdAndArgs = s.command.split(/\s+/);
115
- const cmd = splitCmdAndArgs[0];
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.cwd, cmd, args, 31);
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(cwd, cmd, args, ansiColor) {
185
- cwd = !cwd ? "./" : cwd.startsWith("/") ? `/.../${basename(cwd)}` : cwd.startsWith(".") ? cwd : `./${cwd}`;
186
- return `\x1B[${ansiColor}m[\x1B[1m${cmd}\x1B[22m ${args.join(" ")} \x1B[2;3m${cwd}\x1B[22;23m]\x1B[0m`;
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,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@eighty4/dank",
3
- "version": "0.0.5-0",
3
+ "version": "0.0.5-1",
4
4
  "type": "module",
5
5
  "license": "MIT",
6
6
  "author": "Adam McKee Bennett <adam.be.g84d@gmail.com>",