@mono-labs/cli 0.0.39 → 0.0.41

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.
@@ -0,0 +1,15 @@
1
+ // Boot logic: load root + haste configuration
2
+ import {
3
+ getHasteConfig,
4
+ getRootDirectory,
5
+ getRootJson,
6
+ } from '../loadFromRoot.js';
7
+
8
+ export function boot() {
9
+ const rootDir = getRootDirectory();
10
+ const rootJson = getRootJson();
11
+ const { files, config } = getHasteConfig();
12
+ return { rootDir, rootJson, files, config };
13
+ }
14
+
15
+ export default boot;
@@ -0,0 +1,58 @@
1
+ import { program } from '../../app.js';
2
+ import runHasteCommand from './runHasteCommand.js';
3
+ import { verifyOptionValue } from './validators.js';
4
+ import { mergeData, setData } from './dataLayer.js';
5
+
6
+ /**
7
+ * Register commander commands for each haste file definition.
8
+ * Handles argument, options, validation, and action wiring.
9
+ */
10
+ export function buildCommands(files) {
11
+ Object.entries(files).forEach(([commandName, configObject]) => {
12
+ const optionsData = configObject.options || {};
13
+
14
+ let current = program
15
+ .command(commandName)
16
+ .description(configObject.description || 'Haste command');
17
+
18
+ // Argument
19
+ if (configObject.argument) {
20
+ const argInfo = configObject.argument;
21
+ const required = !!argInfo.required;
22
+ const type = argInfo.type || 'string';
23
+ const argSpec = required ? `<${type}>` : `[${type}]`;
24
+ current = current.argument(argSpec, argInfo.description || '');
25
+ }
26
+
27
+ // Options
28
+ Object.entries(optionsData).forEach(([optionKey, meta]) => {
29
+ const type = meta.type || 'boolean';
30
+ const shortcut = meta.shortcut ? `-${meta.shortcut}, ` : '';
31
+ if (type === 'string') {
32
+ current = current.option(
33
+ `${shortcut}--${optionKey} <${optionKey}>`,
34
+ meta.description || ''
35
+ );
36
+ if (meta.default !== undefined) setData(optionKey, meta.default);
37
+ } else {
38
+ current = current.option(
39
+ `${shortcut}--${optionKey}`,
40
+ meta.description || ''
41
+ );
42
+ if (meta.default !== undefined) setData(optionKey, meta.default);
43
+ }
44
+ });
45
+
46
+ current.action(async (arg, cmd) => {
47
+ const optionVals = cmd.opts ? cmd.opts() : cmd;
48
+ Object.keys(optionVals).forEach((k) => {
49
+ optionVals[k] = verifyOptionValue(k, optionVals[k], optionsData);
50
+ });
51
+ const argVal = arg || configObject.argument?.default;
52
+ mergeData({ ...optionVals, arg: argVal });
53
+ await runHasteCommand(configObject, optionVals);
54
+ });
55
+ });
56
+ }
57
+
58
+ export default buildCommands;
@@ -0,0 +1,30 @@
1
+ // Shared mutable key/value store for build-process commands.
2
+ // Extracts dynamic values from preactions and holds defaults from options.
3
+
4
+ const dataLayer = {};
5
+
6
+ export function setData(key, value) {
7
+ if (value !== undefined) dataLayer[key] = value;
8
+ }
9
+
10
+ export function mergeData(obj = {}) {
11
+ Object.entries(obj).forEach(([k, v]) => setData(k, v));
12
+ return dataLayer;
13
+ }
14
+
15
+ export function getData(key) {
16
+ return key ? dataLayer[key] : dataLayer;
17
+ }
18
+
19
+ export function hasData(key) {
20
+ return Object.prototype.hasOwnProperty.call(dataLayer, key);
21
+ }
22
+
23
+ export function replaceTokens(str) {
24
+ if (typeof str !== 'string') return str;
25
+ return str.replace(/\$\{([^}]+)\}/g, (m, k) =>
26
+ hasData(k) ? String(getData(k)) : m
27
+ );
28
+ }
29
+
30
+ export default dataLayer;
@@ -1,411 +1,13 @@
1
- import { spawn } from 'child_process'
2
- import fs from 'fs'
3
- import { program } from '../../app.js'
4
- import { generateEnvValues } from '../../app.js'
5
- import { STAGING_URL } from '../../config.js'
6
- import { getHasteConfig, getHasteFiles, getRootDirectory, getRootJson } from '../loadFromRoot.js'
1
+ // Orchestrator for modular build-process command system.
2
+ import { boot } from './boot.js';
3
+ import { buildCommands } from './cliFactory.js';
4
+ import { ensureSignalHandlers } from './runners/processManager.js';
7
5
 
8
- console.log('getRootDirectory', getRootDirectory())
9
- console.log('rootJson', getRootJson())
10
- const {files, config} = getHasteConfig()
6
+ const { files, config, rootDir } = boot();
7
+ console.log('[build-process] root:', rootDir);
8
+ console.log('[build-process] commands discovered:', Object.keys(files));
11
9
 
12
- console.log('Haste files', files)
10
+ ensureSignalHandlers();
11
+ buildCommands(files);
13
12
 
14
- const fileKeys = Object.keys(files)
15
- let dataLayer = {}
16
-
17
- fileKeys.forEach((key) => {
18
- const commandName = key
19
-
20
- console.log('Haste file key', commandName, files[key])
21
- const configObject = files[commandName] || {}
22
- const optionsData = configObject.options || {}
23
- const optionsList = Object.keys(optionsData)
24
-
25
- console.log('optionsList', optionsList, optionsData)
26
-
27
- let currentCommand = program.command(commandName).description('Execute eas build command')
28
-
29
- if (configObject.argument) {
30
- console.log('has argument', configObject.argument)
31
- // .argument('<input>', 'path to input file') // required
32
- // .argument('[output]', 'optional output file') // optional
33
- const argumentRequired = configObject.argument.required || false
34
- const argCommand = argumentRequired
35
- ? `<${configObject.argument.type || 'string'}>`
36
- : `[${configObject.argument.type || 'string'}]`
37
- currentCommand = currentCommand.argument(argCommand, configObject.argument.description || '')
38
- }
39
-
40
- optionsList.forEach((optionKey) => {
41
- const type = optionsData[optionKey].type || 'boolean'
42
-
43
- if (type === 'string') {
44
- if (optionsData[optionKey].shortcut) {
45
- currentCommand = currentCommand.option(
46
- `-${optionsData[optionKey].shortcut}, --${optionKey} <${optionKey}>`,
47
- optionsData[optionKey].description || ''
48
- )
49
- console.log('optionKey', optionKey, optionsData[optionKey])
50
- dataLayer[optionKey] = optionsData[optionKey].default || ''
51
- } else {
52
- currentCommand = currentCommand.option(
53
- `--${optionKey} <${optionKey}>`,
54
- optionsData[optionKey].description || ''
55
- )
56
- dataLayer[optionKey] = optionsData[optionKey].default || ''
57
- }
58
- } else {
59
- if (optionsData[optionKey].shortcut) {
60
- currentCommand = currentCommand.option(
61
- `-${optionsData[optionKey].shortcut}, --${optionKey}`,
62
- optionsData[optionKey].description || ''
63
- )
64
- console.log('optionKey', optionKey, optionsData[optionKey])
65
- dataLayer[optionKey] = optionsData[optionKey].default || false
66
- } else {
67
- currentCommand = currentCommand.option(
68
- `--${optionKey}`,
69
- optionsData[optionKey].description || ''
70
- )
71
- dataLayer[optionKey] = optionsData[optionKey].default || false
72
- }
73
- }
74
- })
75
-
76
- function verifyOptionValues(optionKey, value) {
77
- const optionInfo = optionsData[optionKey]
78
- if (optionInfo && optionInfo.options) {
79
- if (!optionInfo.options.includes(value)) {
80
- throw new Error(
81
- `Invalid value for --${optionKey}: ${value}. Valid options are: ${optionInfo.options.join(
82
- ', '
83
- )}`
84
- )
85
- }
86
- }
87
- return value
88
- }
89
-
90
- currentCommand.action(async (arg, cmd) => {
91
- console.log('test:')
92
- console.log('cmd', cmd)
93
- console.log('arg', arg)
94
- const optionVals = cmd.opts ? cmd.opts() : cmd
95
- console.log('arg', arg)
96
- console.log('optionVals')
97
-
98
- const opts = optionVals
99
- Object.keys(opts).forEach((k) => {
100
- opts[k] = verifyOptionValues(k, opts[k])
101
- })
102
- const argVal = arg ? arg : configObject.argument.default
103
- console.log('argVal', argVal)
104
- dataLayer = { ...dataLayer, ...opts, arg: argVal }
105
- console.log('opts', opts)
106
- console.log('firstDataLayer', dataLayer)
107
- await runHasteCommand(configObject, opts)
108
-
109
- // const devConfig = configObject.environments ? configObject.environments.dev : {}
110
- // const productionConfig = configObject.environments ? configObject.environments.production : {}
111
- // let envObj = {}
112
- // if (options.stage) {
113
- // envObj = { ...devConfig }
114
- // } else {
115
- // envObj = { ...productionConfig }
116
- // }
117
-
118
- // // You can decide what happens when each flag is passed.
119
- // // Example: run `eas build` with different profiles.
120
- // if (options.android) {
121
- // console.log('→ Building Android (preview profile)…')
122
- // //run('eas', ['build', '--platform', 'android', '--profile', 'preview'])
123
- // } else if (options.ios) {
124
- // console.log('→ Building iOS (production profile)…')
125
- // //run('eas', ['build', '--platform', 'ios', '--profile', 'production'])
126
- // } else {
127
- // // console.log('No target specified. Use --android or --ios.')
128
- // // program.help()
129
- // console.log('objHaste.actions', configObject)
130
- // const preactions = configObject['preactions'] || []
131
- // const actions = configObject.actions || []
132
- // console.log('preactions', preactions)
133
- // for (const item of preactions) {
134
- // console.log(`→ Running pre-action: ${item}`)
135
- // await run(item, [], envObj)
136
- // }
137
- // for (const item of actions) {
138
- // console.log(`→ Running action: ${item}`)
139
- // run(item, [], envObj)
140
- // }
141
- // }
142
- // })
143
- })
144
- })
145
-
146
- // const commandName = 'test'
147
- // program
148
- // .command(commandName)
149
- // .description('Execute eas build command')
150
- // .option('--android', 'Build to target preview profile')
151
- // .option('--ios', 'Build to target production profile')
152
- // .option('--stage', 'Set environment to staging')
153
- // .action((options) => {
154
- // const configObject = objHaste[commandName] || {}
155
-
156
- // const devConfig = configObject.environments ? configObject.environments.dev : {}
157
- // const productionConfig = configObject.environments ? configObject.environments.production : {}
158
- // let envObj = {}
159
- // if (options.stage) {
160
- // envObj = { ...devConfig }
161
- // } else {
162
- // envObj = { ...productionConfig }
163
- // }
164
-
165
- // // You can decide what happens when each flag is passed.
166
- // // Example: run `eas build` with different profiles.
167
- // if (options.android) {
168
- // console.log('→ Building Android (preview profile)…')
169
- // //run('eas', ['build', '--platform', 'android', '--profile', 'preview'])
170
- // } else if (options.ios) {
171
- // console.log('→ Building iOS (production profile)…')
172
- // //run('eas', ['build', '--platform', 'ios', '--profile', 'production'])
173
- // } else {
174
- // // console.log('No target specified. Use --android or --ios.')
175
- // // program.help()
176
- // console.log('objHaste.actions', objHaste)
177
- // for (const item of configObject.actions) {
178
- // console.log(`→ Running action: ${item}`)
179
- // run(item, [], envObj)
180
- // }
181
- // }
182
- // })
183
-
184
- const totalClosedActions = 0
185
- // Utility to spawn and pipe child output
186
- async function run(cmd, args, envObj = {}, count = 1) {
187
- const child = spawn(cmd, args, {
188
- stdio: 'inherit',
189
- shell: true,
190
- env: {
191
- ...process.env,
192
- ...envObj,
193
- },
194
- })
195
- const isLast = count === totalClosedActions + 1
196
-
197
- const exitAction = () => {
198
- totalClosedActions++
199
- process.exit(code)
200
- }
201
- if (!isLast) {
202
- child.on('exit', (code) => {
203
- exitAction()
204
- })
205
- child.on('sigint', () => {
206
- exitAction()
207
- })
208
- }
209
- if (isLast) {
210
- child.on('exit', (code) => {
211
- if (count < totalClosedActions) exitAction()
212
- })
213
- child.on('sigint', () => {
214
- if (count < totalClosedActions) {
215
- exitAction()
216
- process.exit(code)
217
- }
218
- })
219
- }
220
- }
221
-
222
- // Track background processes so we can kill them on exit
223
- const bgChildren = new Set()
224
-
225
- /** Run a command and resolve when it exits (attach stdio so you can see output). */
226
- function runForeground(cmd, envObj = {}, options = {}) {
227
- return new Promise((resolve, reject) => {
228
- let lastLine = ''
229
- let myTextData = ''
230
- const child = spawn(cmd, {
231
- shell: true,
232
- env: { ...process.env, ...envObj },
233
- stdio: ['inherit', 'pipe', 'pipe'], // stdin pass-through, capture out/err
234
- })
235
-
236
- const handleData = (chunk, isErr = false) => {
237
- const text = chunk.toString()
238
- console.log(text.toString().trim())
239
- console.log('text', text)
240
- myTextData = myTextData.concat(text)
241
- console.log('myTextData', myTextData)
242
-
243
- // Track last line
244
- const lines = text.trim().split(/\r?\n/)
245
- if (lines.length) {
246
- lastLine = lines[lines.length - 1]
247
- }
248
- }
249
-
250
- child.stdout.on('data', (chunk) => handleData(chunk, false))
251
- child.stderr.on('data', (chunk) => handleData(chunk, true))
252
-
253
- child.on('error', reject)
254
- child.on('exit', (code, signal) => {
255
- myTextData.concat('\n<<< CHILD PROCESS EXITED >>>\n')
256
- console.log('myTextData', myTextData)
257
- //const rx = /\{out\s*:\s*(.+?)\}/gs
258
- // ^\{out:field-name \$\{([^}]+)\}\}$
259
- const rx = /\{out:([^\s]+) (.*)\}\n/g
260
- //const rx = /Project ([^\r\n]*)/
261
- console.log('myTextData', myTextData)
262
- const match = rx.exec(myTextData)
263
-
264
- const rx3 = /\{out:(?<field>[^\s}]+)\s+(?<value>[^\s}]+)\}/g
265
- const results = [...myTextData.matchAll(rx3)].map((m) => {
266
- const layerIndex = m.groups.field
267
- const matchValue = m.groups.value
268
- console.log('matchValue', matchValue)
269
- dataLayer[layerIndex] = matchValue
270
-
271
- return {
272
- field: m.groups.field,
273
- value: m.groups.value,
274
- }
275
- })
276
- console.log(results)
277
-
278
- console.log('match', match)
279
-
280
- if (signal) return reject(new Error(`${cmd} exited via signal ${signal}`))
281
- if (code === 0) return resolve(lastLine) // resolve with last line
282
- reject(new Error(`${cmd} exited with code ${code}. Last line: ${lastLine}`))
283
- })
284
- })
285
- }
286
-
287
- function runBackground(cmd, envObj = {}, options = {}, attached = false) {
288
- const isWin = process.platform === 'win32'
289
- // Expand ${0}, ${1}, ... from dataLayer into env strings
290
- const newEnv = {}
291
- const commandClone = cmd
292
- for (const key of Object.keys(envObj)) {
293
- let value = envObj[key]
294
- if (typeof value === 'string') {
295
- console.log('dataLayer', dataLayer)
296
- Object.keys(dataLayer).map((k) => {
297
- value = value.replace(new RegExp(`\\$\\{${k}\\}`, 'g'), dataLayer[k])
298
- })
299
- }
300
- newEnv[key] = value
301
- }
302
-
303
- console.log('newEnv', newEnv)
304
-
305
- const out = commandClone.replace(/\$\{([^}]+)\}/g, (match, key) => {
306
- // keep 0/false, only treat null/undefined as missing
307
- const v = dataLayer[key]
308
- return v == null ? match : String(v)
309
- })
310
-
311
- return new Promise((resolve, reject) => {
312
- const child = spawn(out, {
313
- shell: true,
314
- stdio: attached ? 'inherit' : 'ignore', // no output in this terminal
315
- env: { ...process.env, ...newEnv },
316
- // On POSIX, detach so we can signal the whole group; on Windows, DON'T.
317
- detached: !attached && !isWin,
318
- windowsHide: !attached && !isWin, // prevent new console window on Windows
319
- })
320
-
321
- // Only unref on POSIX when detached; on Windows keep it referenced.
322
- if (!attached && !isWin) child.unref()
323
-
324
- child.once('error', (err) => {
325
- bgChildren.delete(child)
326
- reject(err)
327
- })
328
-
329
- child.once('exit', (code, signal) => {
330
- bgChildren.delete(child)
331
- if (signal) return reject(new Error(`${cmd} exited via signal ${signal}`))
332
- if (code === 0) return resolve()
333
- reject(new Error(`${cmd} exited with code ${code}`))
334
- })
335
- })
336
- }
337
-
338
- /** Kill all background children (called on SIGINT/SIGTERM or when foreground ends). */
339
- function killAllBackground() {
340
- for (const child of Array.from(bgChildren)) {
341
- try {
342
- if (process.platform === 'win32') {
343
- spawn('taskkill', ['/PID', String(child.pid), '/T', '/F'], { shell: true, stdio: 'ignore' })
344
- } else {
345
- process.kill(-child.pid, 'SIGTERM') // whole group
346
- }
347
- } catch {}
348
- }
349
- bgChildren.clear()
350
- }
351
-
352
- process.on('SIGINT', () => {
353
- console.log('\nSIGINT')
354
- killAllBackground()
355
- process.exit(130)
356
- })
357
- process.on('SIGTERM', () => {
358
- killAllBackground()
359
- process.exit(143)
360
- })
361
-
362
- /** In your commander .action handler */
363
- async function runHasteCommand(configObject, options = {}) {
364
- const environments = configObject.environments
365
- const devConfig = configObject.environments?.dev ?? {}
366
- const productionConfig = configObject.environments?.stage ?? {}
367
- console.log('options', options)
368
- const awsProfile = process.env.CDK_DEPLOY_PROFILE || 'default'
369
- console.log('prof', awsProfile)
370
- const envObj = options.stage ? productionConfig : devConfig
371
- envObj['AWS_PROFILE'] = awsProfile
372
-
373
- const preactions = configObject.preactions ?? []
374
- const actions = configObject.actions ?? []
375
-
376
- console.log('environments', environments)
377
-
378
- console.log('preactions', preactions)
379
-
380
- // 1) Run preactions SEQUENTIALLY (each waits for previous to finish)
381
- let num = 0
382
- console.log('envObj', envObj)
383
- for (const cmd of preactions) {
384
- console.log(`→ preaction: ${cmd}`)
385
- await runForeground(cmd, envObj, options)
386
- num++
387
- }
388
-
389
- // 2) Run actions: background all but the last; attach to the last
390
- if (actions.length === 0) return
391
-
392
- const bg = actions.slice(0, -1)
393
- const fg = actions[actions.length - 1]
394
-
395
- for (const cmd of bg) {
396
- console.log(`→ background action: ${cmd}`)
397
- runBackground(cmd, envObj, options)
398
- }
399
-
400
- console.log(`→ foreground action (attached): ${fg}`)
401
- try {
402
- console.log('envObj', envObj)
403
- await runBackground(fg, envObj, options, true)
404
- } finally {
405
- // When the foreground ends, clean up background processes too (optional)
406
- killAllBackground()
407
- }
408
- }
409
-
410
- //ACTION TYPES: preaction, action
411
- //COMMAND TYPES: sequence, parallel
13
+ // (No direct export; importing this file registers commands on the shared commander program.)
@@ -0,0 +1,46 @@
1
+ import { runForeground } from './runners/runForeground.js';
2
+ import { runBackground } from './runners/runBackground.js';
3
+ import { killAllBackground } from './runners/processManager.js';
4
+
5
+ /**
6
+ * Orchestrate execution of a single haste command definition.
7
+ * Phases:
8
+ * 1. Preactions (sequential, blocking) via runForeground
9
+ * 2. Actions (background except last; last attached) via runBackground
10
+ * Environment selection based on --stage flag and injection of AWS_PROFILE.
11
+ */
12
+ export async function runHasteCommand(configObject, options = {}) {
13
+ const devConfig = configObject.environments?.dev ?? {};
14
+ const stageConfig = configObject.environments?.stage ?? {};
15
+ const awsProfile = process.env.CDK_DEPLOY_PROFILE || 'default';
16
+ const envObj = options.stage ? { ...stageConfig } : { ...devConfig };
17
+ envObj.AWS_PROFILE = awsProfile;
18
+
19
+ const preactions = configObject.preactions ?? [];
20
+ const actions = configObject.actions ?? [];
21
+
22
+ // Run preactions sequentially
23
+ for (const cmd of preactions) {
24
+ console.log(`→ preaction: ${cmd}`);
25
+ await runForeground(cmd, envObj, options);
26
+ }
27
+
28
+ if (actions.length === 0) return;
29
+
30
+ const bg = actions.slice(0, -1);
31
+ const fg = actions[actions.length - 1];
32
+
33
+ for (const cmd of bg) {
34
+ console.log(`→ background action: ${cmd}`);
35
+ runBackground(cmd, envObj, options);
36
+ }
37
+
38
+ console.log(`→ foreground action (attached): ${fg}`);
39
+ try {
40
+ await runBackground(fg, envObj, options, true);
41
+ } finally {
42
+ killAllBackground();
43
+ }
44
+ }
45
+
46
+ export default runHasteCommand;
@@ -0,0 +1,39 @@
1
+ import { spawn } from 'child_process';
2
+
3
+ // Track background processes so we can kill them on exit
4
+ export const bgChildren = new Set();
5
+
6
+ export function registerBackground(child) {
7
+ bgChildren.add(child);
8
+ }
9
+
10
+ export function killAllBackground() {
11
+ for (const child of Array.from(bgChildren)) {
12
+ try {
13
+ if (process.platform === 'win32') {
14
+ spawn('taskkill', ['/PID', String(child.pid), '/T', '/F'], {
15
+ shell: true,
16
+ stdio: 'ignore',
17
+ });
18
+ } else {
19
+ process.kill(-child.pid, 'SIGTERM');
20
+ }
21
+ } catch {}
22
+ }
23
+ bgChildren.clear();
24
+ }
25
+
26
+ let signalsRegistered = false;
27
+ export function ensureSignalHandlers() {
28
+ if (signalsRegistered) return;
29
+ signalsRegistered = true;
30
+ process.on('SIGINT', () => {
31
+ console.log('\nSIGINT');
32
+ killAllBackground();
33
+ process.exit(130);
34
+ });
35
+ process.on('SIGTERM', () => {
36
+ killAllBackground();
37
+ process.exit(143);
38
+ });
39
+ }
@@ -0,0 +1,49 @@
1
+ import { spawn } from 'child_process';
2
+ import { getData, replaceTokens } from '../dataLayer.js';
3
+ import { registerBackground } from './processManager.js';
4
+
5
+ export function runBackground(
6
+ cmd,
7
+ envObj = {},
8
+ options = {},
9
+ attached = false
10
+ ) {
11
+ const isWin = process.platform === 'win32';
12
+
13
+ // Replace ${field} tokens in env values using dataLayer
14
+ const expandedEnv = {};
15
+ for (const k of Object.keys(envObj)) {
16
+ const v = envObj[k];
17
+ expandedEnv[k] = typeof v === 'string' ? replaceTokens(v) : v;
18
+ }
19
+
20
+ // Replace in command string
21
+ const outCmd = replaceTokens(cmd);
22
+
23
+ return new Promise((resolve, reject) => {
24
+ const child = spawn(outCmd, {
25
+ shell: true,
26
+ stdio: attached ? 'inherit' : 'ignore',
27
+ env: { ...process.env, ...expandedEnv },
28
+ detached: !attached && !isWin,
29
+ windowsHide: !attached && isWin,
30
+ });
31
+
32
+ if (!attached && !isWin) child.unref();
33
+
34
+ registerBackground(child);
35
+
36
+ child.once('error', (err) => {
37
+ reject(err);
38
+ });
39
+
40
+ child.once('exit', (code, signal) => {
41
+ if (signal)
42
+ return reject(new Error(`${cmd} exited via signal ${signal}`));
43
+ if (code === 0) return resolve();
44
+ reject(new Error(`${cmd} exited with code ${code}`));
45
+ });
46
+ });
47
+ }
48
+
49
+ export default runBackground;
@@ -0,0 +1,50 @@
1
+ import { spawn } from 'child_process';
2
+ import { setData } from '../dataLayer.js';
3
+
4
+ // Regex to capture tokens like: {out:field value}
5
+ const TOKEN_RX = /\{out:(?<field>[^\s}]+)\s+(?<value>[^\s}]+)\}/g;
6
+
7
+ /**
8
+ * Run a command in the foreground, capturing stdout/stderr. Extracts token patterns
9
+ * of the form {out:field value} and stores them in the shared dataLayer.
10
+ */
11
+ export function runForeground(cmd, envObj = {}, options = {}) {
12
+ return new Promise((resolve, reject) => {
13
+ let lastLine = '';
14
+ let buffer = '';
15
+
16
+ const child = spawn(cmd, {
17
+ shell: true,
18
+ env: { ...process.env, ...envObj },
19
+ stdio: ['inherit', 'pipe', 'pipe'],
20
+ });
21
+
22
+ const handleData = (chunk) => {
23
+ const text = chunk.toString();
24
+ buffer += text;
25
+ const lines = text.trim().split(/\r?\n/);
26
+ if (lines.length) lastLine = lines[lines.length - 1];
27
+ process.stdout.write(text); // echo output
28
+ };
29
+
30
+ child.stdout.on('data', handleData);
31
+ child.stderr.on('data', handleData);
32
+ child.on('error', reject);
33
+
34
+ child.on('exit', (code, signal) => {
35
+ // Extract tokens and populate dataLayer
36
+ for (const match of buffer.matchAll(TOKEN_RX)) {
37
+ setData(match.groups.field, match.groups.value);
38
+ }
39
+
40
+ if (signal)
41
+ return reject(new Error(`${cmd} exited via signal ${signal}`));
42
+ if (code === 0) return resolve(lastLine);
43
+ reject(
44
+ new Error(`${cmd} exited with code ${code}. Last line: ${lastLine}`)
45
+ );
46
+ });
47
+ });
48
+ }
49
+
50
+ export default runForeground;
@@ -0,0 +1,22 @@
1
+ import { execSync } from 'child_process';
2
+
3
+ //Run Action List before all script actions
4
+
5
+ export function executeCommandsIfWorkspaceAction(commands = [], action) {
6
+ console.log('we here');
7
+ const result = execSync('yarn workspaces list --json', { encoding: 'utf8' })
8
+ .trim()
9
+ .split('\n')
10
+ .map((line) => JSON.parse(line));
11
+
12
+ console.log(result.map((w) => w.location)); // list of workspace paths
13
+ // Check if the action is a workspace action
14
+ if (action && action.type === 'workspace') {
15
+ // Execute each command in the context of the workspace
16
+ commands.forEach((cmd) => {
17
+ console.log(`Executing command in workspace: ${cmd}`);
18
+ // Here you would add the logic to execute the command
19
+ execSync(cmd, { stdio: 'inherit', shell: true });
20
+ });
21
+ }
22
+ }
@@ -0,0 +1,12 @@
1
+ // Option/value validation utilities
2
+ export function verifyOptionValue(optionKey, value, optionsData) {
3
+ const optionInfo = optionsData[optionKey];
4
+ if (optionInfo && Array.isArray(optionInfo.options)) {
5
+ if (!optionInfo.options.includes(value)) {
6
+ throw new Error(
7
+ `Invalid value for --${optionKey}: ${value}. Valid options are: ${optionInfo.options.join(', ')}`
8
+ );
9
+ }
10
+ }
11
+ return value;
12
+ }
package/lib/index.js CHANGED
@@ -14,10 +14,11 @@ import './commands/submit/index.js';
14
14
  import './commands/update/index.js';
15
15
  import './commands/build-process/index.js';
16
16
  import { getHasteConfig } from './commands/loadFromRoot.js';
17
+ import { executeCommandsIfWorkspaceAction } from './commands/build-process/test.js';
17
18
 
18
19
  const { config } = getHasteConfig();
19
20
 
20
- const workspacemap = config.workspace || {};
21
+ const workspacemap = config.workspace?.packageMaps || {};
21
22
 
22
23
  program.on('command:*', (operands) => {
23
24
  const [cmd] = operands; // e.g. "destroy3"
@@ -31,26 +32,23 @@ program.on('command:*', (operands) => {
31
32
  console.log('Rest:', rest);
32
33
 
33
34
  // If the “rest” is empty or starts with flags, insert a default script
34
- if (rest.length === 0 || rest[0].startsWith('--')) {
35
- console.log('Rest is empty or starts with flags, inserting DEFAULT_SCRIPT');
36
- // Prefer an explicit script name; if you want to always use `run`, do:
37
- // rest = ['run', DEFAULT_SCRIPT, ...rest];
38
- rest = [...rest]; // yarn workspace <ws> dev --flags
39
- console.log('Rest after inserting DEFAULT_SCRIPT:', rest);
40
- }
41
35
 
42
36
  const args = ['workspace', workspace, ...rest];
43
37
  console.log('Final args for yarn:', args);
44
38
 
45
39
  console.error(`Unknown command. Falling back to: yarn ${args.join(' ')}`);
46
-
47
- const child = spawn('yarn', args, {
40
+ executeCommandsIfWorkspaceAction(['yarn', ...args], { type: 'workspace' });
41
+ console.log('yarn', args, {
48
42
  stdio: 'inherit',
49
43
  shell: process.platform === 'win32',
50
44
  });
51
- child.on('exit', (code) => {
52
- console.log('Child process exited with code:', code);
53
- process.exitCode = code ?? 1;
54
- });
45
+ // const child = spawn('yarn', args, {
46
+ // stdio: 'inherit',
47
+ // shell: process.platform === 'win32',
48
+ // });
49
+ // child.on('exit', (code) => {
50
+ // console.log('Child process exited with code:', code);
51
+ // process.exitCode = code ?? 1;
52
+ // });
55
53
  });
56
54
  program.parse();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mono-labs/cli",
3
- "version": "0.0.39",
3
+ "version": "0.0.41",
4
4
  "description": "A CLI tool for building and deploying projects",
5
5
  "type": "module",
6
6
  "main": "index.js",