@jujulego/jill 2.4.0 → 2.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.
@@ -1,1017 +0,0 @@
1
- import { withTimestamp, LogLevel, quick, qlevelColor, toStderr, Logger, logger$, withLabel } from '@jujulego/logger';
2
- import { _ } from '@swc/helpers/_/_ts_decorate';
3
- import { _ as _$1 } from '@swc/helpers/_/_ts_param';
4
- import { Container, decorate, injectable, inject, ContainerModule } from 'inversify';
5
- import { AsyncLocalStorage } from 'node:async_hooks';
6
- import { SpawnTask, GroupTask, TaskSet, TaskManager } from '@jujulego/tasks';
7
- import os from 'node:os';
8
- import path from 'node:path';
9
- import Ajv from 'ajv';
10
- import wt, { BroadcastChannel, setEnvironmentData, getEnvironmentData } from 'node:worker_threads';
11
- import yargs from 'yargs';
12
- import { hideBin } from 'yargs/helpers';
13
- import { cosmiconfig, defaultLoaders } from 'cosmiconfig';
14
- import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
15
- import { Box, Text, useStderr, render } from 'ink';
16
- import Spinner from 'ink-spinner';
17
- import symbols from 'log-symbols';
18
- import ms from 'pretty-ms';
19
- import { useState, useLayoutEffect } from 'react';
20
- import { source$, once$, waitFor$ } from '@jujulego/event-tree';
21
- import getDecorators from 'inversify-inject-decorators';
22
- import 'reflect-metadata';
23
- import { flow$, filter$, var$ } from '@jujulego/aegis';
24
- import { qprop } from '@jujulego/quick-tag';
25
- import { chalkTemplateStderr } from 'chalk-template';
26
-
27
- // Utils
28
- async function dynamicImport(filepath) {
29
- return import(/* webpackIgnore: true */ process.platform === "win32" ? `file://${filepath}` : filepath);
30
- }
31
- function fixDefaultExport(mod) {
32
- return "default" in mod ? mod.default : mod;
33
- }
34
-
35
- // Container
36
- const container = new Container();
37
- // Utilities
38
- const { lazyInject, lazyInjectNamed } = fixDefaultExport(getDecorators)(container);
39
-
40
- // Decorators
41
- /**
42
- * Register class as a service
43
- */ function Service() {
44
- return (cls)=>{
45
- decorate(injectable(), cls);
46
- container.bind(cls).toSelf().inSingletonScope().onActivation((ctx, service)=>{
47
- if ("onServiceActivate" in service) {
48
- service.onServiceActivate(ctx);
49
- }
50
- return service;
51
- });
52
- return cls;
53
- };
54
- }
55
-
56
- // Constants
57
- const LOG_BROADCAST_CHANNEL = Symbol.for("jujulego:jill:log-broadcast-channel");
58
- // Parameters
59
- container.bind(LOG_BROADCAST_CHANNEL).toConstantValue("jujulego:jill:logger");
60
-
61
- class ThreadGateway {
62
- // Constructor
63
- constructor(channel){
64
- this._source = source$();
65
- this.subscribe = this._source.subscribe;
66
- this.unsubscribe = this._source.unsubscribe;
67
- this.clear = this._source.clear;
68
- this.channel = new BroadcastChannel(channel);
69
- this.channel.unref();
70
- this.channel.onmessage = (log)=>{
71
- this._source.next(log.data);
72
- };
73
- this.channel.onmessageerror = (data)=>{
74
- this._source.next(withTimestamp()({
75
- level: LogLevel.error,
76
- message: quick.string`Unable to read message: #!json:${data.data}`
77
- }));
78
- };
79
- }
80
- // Methods
81
- next(data) {
82
- this.channel.postMessage(data);
83
- }
84
- }
85
- ThreadGateway = _([
86
- Service(),
87
- _$1(0, inject(LOG_BROADCAST_CHANNEL))
88
- ], ThreadGateway);
89
-
90
- // Utils
91
- const jillLogFormat = qlevelColor(quick.wrap(chalkTemplateStderr).function`#?:${qprop("label")}{grey [#$]} ?#${qprop("message")}#?:${qprop("error")}\n#!error$?#`);
92
- class LogGateway {
93
- // Lifecycle
94
- onServiceActivate({ container }) {
95
- const threadGtw = container.get(ThreadGateway);
96
- if (wt.isMainThread) {
97
- // Redirect logs to stderr
98
- flow$(this._source, toStderr(jillLogFormat));
99
- // Add thread gateway as input
100
- this.connect(threadGtw);
101
- } else {
102
- // Redirect logs to thread gateway
103
- flow$(this._source, threadGtw);
104
- }
105
- }
106
- connect(origin) {
107
- return flow$(origin, filter$((log)=>log.level >= this.level), this._source);
108
- }
109
- // Properties
110
- get listeners() {
111
- return Array.from(this._source.listeners);
112
- }
113
- get level() {
114
- return this.level$.read();
115
- }
116
- set level(level) {
117
- this.level$.mutate(level);
118
- }
119
- constructor(){
120
- // Attributes
121
- this.level$ = var$(LogLevel.info);
122
- this._source = source$();
123
- // Methods
124
- this.subscribe = this._source.subscribe;
125
- this.unsubscribe = this._source.unsubscribe;
126
- this.clear = this._source.clear;
127
- }
128
- }
129
- LogGateway = _([
130
- Service()
131
- ], LogGateway);
132
-
133
- // Service
134
- container.bind(Logger).toDynamicValue(()=>logger$(withTimestamp())).inSingletonScope().onActivation(({ container }, logger)=>{
135
- const gateway = container.get(LogGateway);
136
- gateway.connect(logger);
137
- return logger;
138
- });
139
-
140
- class ContextService {
141
- // Constructor
142
- constructor(logger){
143
- this._storage = new AsyncLocalStorage();
144
- this._logger = logger.child(withLabel("context"));
145
- this.reset();
146
- }
147
- // Methods
148
- reset(context = {}) {
149
- this._storage.enterWith(context);
150
- }
151
- _getContext() {
152
- const ctx = this._storage.getStore();
153
- if (!ctx) {
154
- this._logger.warning("Trying to access uninitialized context");
155
- return {};
156
- }
157
- return ctx;
158
- }
159
- _updateContext(update) {
160
- Object.assign(this._getContext(), update);
161
- }
162
- // Properties
163
- get application() {
164
- return this._getContext().application;
165
- }
166
- set application(application) {
167
- this._updateContext({
168
- application
169
- });
170
- }
171
- get project() {
172
- return this._getContext().project;
173
- }
174
- set project(project) {
175
- this._updateContext({
176
- project
177
- });
178
- }
179
- get workspace() {
180
- return this._getContext().workspace;
181
- }
182
- set workspace(workspace) {
183
- this._updateContext({
184
- workspace
185
- });
186
- }
187
- }
188
- ContextService = _([
189
- Service(),
190
- _$1(0, inject(Logger))
191
- ], ContextService);
192
-
193
- // Symbols
194
- const CONFIG_OPTIONS = Symbol("jujulego:jill:config-options");
195
- // Constants
196
- const VERBOSITY_LEVEL = {
197
- 1: "verbose",
198
- 2: "debug"
199
- };
200
- // Options
201
- function applyConfigOptions(parser) {
202
- return parser.option("verbose", {
203
- alias: "v",
204
- type: "count",
205
- description: "Set verbosity level",
206
- coerce: (cnt)=>VERBOSITY_LEVEL[Math.min(cnt, 2)]
207
- }).option("jobs", {
208
- alias: "j",
209
- type: "number",
210
- description: "Set maximum parallel job number"
211
- }).option("hooks", {
212
- type: "boolean",
213
- description: "Run hook scripts"
214
- });
215
- }
216
- container.bind(CONFIG_OPTIONS).toDynamicValue(()=>{
217
- const parser = yargs(hideBin(process.argv)).help(false).version(false);
218
- applyConfigOptions(parser);
219
- return parser.parse();
220
- }).inSingletonScope();
221
-
222
- // Constants
223
- const CURRENT = "current";
224
-
225
- // Symbols
226
- const MODULE = Symbol("jujulego:jill:module");
227
- const REGISTRY = Symbol("jujulego:jill:registry");
228
- // Utils
229
- function getRegistry(target) {
230
- const registry = Reflect.getMetadata(REGISTRY, target);
231
- if (typeof registry !== "function") {
232
- throw new Error(`No registry found in ${target.name}`);
233
- }
234
- return registry;
235
- }
236
- function setRegistry(target, registry) {
237
- Reflect.defineMetadata(REGISTRY, registry, target);
238
- }
239
- function getModule(target, assert = false) {
240
- let module = Reflect.getMetadata(MODULE, target);
241
- if (!module || !(module instanceof ContainerModule)) {
242
- const registry = Reflect.getMetadata(REGISTRY, target);
243
- if (typeof registry !== "function") {
244
- if (assert) {
245
- throw new Error(`No module found in ${target.name}`);
246
- }
247
- return null;
248
- }
249
- module = new ContainerModule(registry);
250
- setModule(target, module);
251
- }
252
- return module;
253
- }
254
- function setModule(target, module) {
255
- Reflect.defineMetadata(MODULE, module, target);
256
- }
257
-
258
- // Class
259
- class ExitException extends Error {
260
- // Constructor
261
- constructor(code, message){
262
- super(message);
263
- this.code = code;
264
- }
265
- }
266
-
267
- // Decorator
268
- function Middleware() {
269
- return (target)=>{
270
- decorate(injectable(), target);
271
- container.bind(target).toSelf().inSingletonScope();
272
- };
273
- }
274
- // Utils
275
- function applyMiddlewares(parser, middlewares) {
276
- let tmp = parser;
277
- for (const cls of middlewares){
278
- const middleware = container.get(cls);
279
- if (middleware.builder) {
280
- tmp = middleware.builder(tmp);
281
- }
282
- tmp.middleware((args)=>middleware.handler(args));
283
- }
284
- return tmp;
285
- }
286
-
287
- // Symbols
288
- const COMMAND_OPTS = Symbol("jujulego:jill:command-opts");
289
- const COMMAND = Symbol("jujulego:jill:command");
290
- const COMMAND_MODULE = Symbol("jujulego:jill:command-module");
291
- // Utils
292
- function getCommandOpts(target) {
293
- const opts = Reflect.getMetadata(COMMAND_OPTS, target);
294
- if (typeof opts !== "object") {
295
- throw new Error(`No command options found in ${target.name}`);
296
- }
297
- return opts;
298
- }
299
- function buildCommandModule(cmd, opts) {
300
- return {
301
- command: opts.command,
302
- aliases: opts.aliases,
303
- describe: opts.describe,
304
- deprecated: opts.deprecated,
305
- builder (parser) {
306
- if (opts.middlewares) {
307
- parser = applyMiddlewares(parser, opts.middlewares);
308
- }
309
- if (cmd.builder) {
310
- parser = cmd.builder(parser);
311
- }
312
- return parser;
313
- },
314
- handler: async (args)=>{
315
- try {
316
- await cmd.handler(args);
317
- } catch (err) {
318
- if (err.message) {
319
- const logger = container.get(Logger);
320
- logger.error("Error while running command:", err);
321
- }
322
- throw new ExitException(1);
323
- }
324
- }
325
- };
326
- }
327
- // Decorator
328
- function Command(opts) {
329
- return (target)=>{
330
- decorate(injectable(), target);
331
- Reflect.defineMetadata(COMMAND_OPTS, opts, target);
332
- const cmd = opts.command.split(" ")[0];
333
- setRegistry(target, (bind)=>{
334
- bind(target).toSelf();
335
- bind(COMMAND).toDynamicValue(({ container })=>container.getAsync(target)).whenTargetNamed(cmd);
336
- bind(COMMAND_MODULE).toDynamicValue(async ({ container })=>{
337
- const cmd = await container.getAsync(target);
338
- return buildCommandModule(cmd, opts);
339
- }).whenTargetNamed(cmd);
340
- });
341
- };
342
- }
343
-
344
- // Symbols
345
- const AJV = Symbol("jujulego:jill:Ajv");
346
- // Setup
347
- container.bind(AJV).toDynamicValue(({ container })=>{
348
- const logger = container.get(Logger);
349
- return new Ajv.default({
350
- allErrors: true,
351
- logger: logger.child(withLabel("ajv")),
352
- strict: process.env.NODE_ENV === "development" ? "log" : true
353
- });
354
- }).inSingletonScope();
355
-
356
- function isCacheUpdate(msg) {
357
- return typeof msg === "object" && msg !== null && "key" in msg && "value" in msg;
358
- }
359
- // Chanel
360
- const channel = new BroadcastChannel("jujulego:jill:worker-cache");
361
- channel.unref();
362
- channel.onmessage = (arg)=>{
363
- const msg = arg;
364
- if (isCacheUpdate(msg.data)) {
365
- setEnvironmentData(msg.data.key, msg.data.value);
366
- }
367
- };
368
- // Utils
369
- async function workerCache(key, compute) {
370
- const cache = getEnvironmentData(key);
371
- if (cache !== undefined) return cache;
372
- // Compute it
373
- const result = await compute();
374
- setEnvironmentData(key, result);
375
- channel.postMessage({
376
- key,
377
- value: result
378
- });
379
- return result;
380
- }
381
-
382
- var $schema = "http://json-schema.org/draft-07/schema";
383
- var type = "object";
384
- var additionalProperties = false;
385
- var properties = {
386
- jobs: {
387
- type: "number"
388
- },
389
- hooks: {
390
- type: "boolean",
391
- "default": true,
392
- description: "Run hook scripts"
393
- },
394
- plugins: {
395
- type: "array",
396
- items: {
397
- type: "string"
398
- }
399
- },
400
- verbose: {
401
- type: "string",
402
- "enum": [
403
- "info",
404
- "verbose",
405
- "debug"
406
- ]
407
- }
408
- };
409
- var schema = {
410
- $schema: $schema,
411
- type: type,
412
- additionalProperties: additionalProperties,
413
- properties: properties
414
- };
415
-
416
- // Symbols
417
- const CONFIG_EXPLORER = Symbol("jujulego:jill:config-explorer");
418
- const CONFIG_VALIDATOR = Symbol("jujulego:jill:config-validator");
419
- // Setup
420
- container.bind(CONFIG_VALIDATOR).toDynamicValue(({ container })=>{
421
- const ajv = container.get(AJV);
422
- return ajv.compile(schema);
423
- }).inSingletonScope();
424
- container.bind(CONFIG_EXPLORER).toDynamicValue(()=>{
425
- return cosmiconfig("jill", {
426
- loaders: {
427
- ".cjs": (filepath)=>dynamicImport(filepath).then((mod)=>mod.default),
428
- ".js": (filepath)=>dynamicImport(filepath).then((mod)=>mod.default),
429
- ".json": defaultLoaders[".json"],
430
- ".yaml": defaultLoaders[".yaml"],
431
- ".yml": defaultLoaders[".yml"],
432
- noExt: defaultLoaders.noExt
433
- }
434
- });
435
- }).inSingletonScope();
436
-
437
- // Symbols
438
- const CONFIG = Symbol("jujulego:jill:config");
439
- // Loader
440
- async function configLoader() {
441
- const logger = container.get(Logger).child(withLabel("config"));
442
- const options = container.get(CONFIG_OPTIONS);
443
- const explorer = container.get(CONFIG_EXPLORER);
444
- const validator = container.get(CONFIG_VALIDATOR);
445
- // Load file
446
- const loaded = await explorer.search();
447
- const config = loaded?.config ?? {};
448
- // Apply options from cli
449
- if (options.jobs) config.jobs = options.jobs;
450
- if (options.verbose) config.verbose = options.verbose;
451
- if (options.hooks !== undefined) config.hooks = options.hooks;
452
- // Apply defaults
453
- config.jobs ??= os.cpus().length - 1;
454
- config.hooks ??= true;
455
- // Validate
456
- if (!validator(config)) {
457
- const ajv = container.get(AJV);
458
- const errors = ajv.errorsText(validator.errors, {
459
- separator: "\n- ",
460
- dataVar: "config"
461
- });
462
- logger.error(`Errors in config file:\n- ${errors}`);
463
- throw new ExitException(1);
464
- }
465
- // Apply on logger
466
- if (config.verbose) {
467
- container.get(LogGateway).level = LogLevel[config.verbose];
468
- }
469
- if (loaded) {
470
- // Resolve paths relative to config file
471
- const base = path.dirname(loaded.filepath);
472
- config.plugins = config.plugins?.map((plugin)=>path.resolve(base, plugin));
473
- logger.verbose(`Loaded ${loaded.filepath} config file`);
474
- }
475
- logger.debug`Loaded config:\n#!json:${config}`;
476
- return config;
477
- }
478
- container.bind(CONFIG).toDynamicValue(async ()=>await workerCache("jujulego:jill:config", configLoader)).inSingletonScope();
479
-
480
- class PluginLoaderService {
481
- // Constructor
482
- constructor(_config, logger){
483
- this._config = _config;
484
- this._logger = logger.child(withLabel("plugin"));
485
- }
486
- // Methods
487
- async _importPlugin(filepath) {
488
- this._logger.verbose`Loading plugin ${filepath}`;
489
- // Load plugin
490
- let plugin = await dynamicImport(filepath);
491
- while(plugin && typeof plugin === "object" && "default" in plugin){
492
- plugin = plugin.default;
493
- }
494
- if (!plugin) {
495
- throw new Error(`Invalid plugin ${filepath}: no plugin class found`);
496
- }
497
- // Load module from plugin
498
- const module = getModule(plugin);
499
- if (!module) {
500
- throw new Error(`Invalid plugin ${filepath}: invalid plugin class`);
501
- }
502
- return module;
503
- }
504
- async loadPlugins(ctn = container) {
505
- if (!this._config.plugins) return;
506
- for (const path of this._config.plugins){
507
- const plugin = await this._importPlugin(path);
508
- ctn.load(plugin);
509
- }
510
- }
511
- }
512
- PluginLoaderService = _([
513
- Service(),
514
- _$1(0, inject(CONFIG)),
515
- _$1(1, inject(Logger))
516
- ], PluginLoaderService);
517
-
518
- // Utils
519
- function linesFrom(task, stream) {
520
- const inner = source$();
521
- if (task.completed) {
522
- return inner;
523
- }
524
- // Listen to stream
525
- let current = "";
526
- const stop = task.on(`stream.${stream}`, (chunk)=>{
527
- const data = current + chunk.data.toString("utf-8");
528
- const lines = data.split(/\r?\n/);
529
- current = lines.pop() ?? "";
530
- for (const line of lines){
531
- inner.next(line);
532
- }
533
- });
534
- // Listen to end of task
535
- once$(task, "completed", ()=>{
536
- stop();
537
- if (current) {
538
- inner.next(current);
539
- }
540
- });
541
- return inner;
542
- }
543
-
544
- // Utils
545
- function isCommandCtx(ctx) {
546
- return "workspace" in ctx && "command" in ctx;
547
- }
548
- // Class
549
- class CommandTask extends SpawnTask {
550
- // Constructor
551
- constructor(workspace, command, args, opts = {}){
552
- let cmd = command;
553
- if (opts.superCommand) {
554
- if (typeof opts.superCommand === "string") {
555
- opts.superCommand = [
556
- opts.superCommand
557
- ];
558
- }
559
- if (opts.superCommand.length > 0) {
560
- cmd = opts.superCommand[0];
561
- args = [
562
- ...opts.superCommand.slice(1),
563
- command,
564
- ...args
565
- ];
566
- }
567
- }
568
- super(cmd, args, {
569
- workspace,
570
- command
571
- }, {
572
- ...opts,
573
- cwd: workspace.cwd,
574
- env: {
575
- FORCE_COLOR: "1",
576
- ...opts.env
577
- }
578
- });
579
- this.workspace = workspace;
580
- this._logStreams();
581
- }
582
- // Methods
583
- _logStreams() {
584
- // TODO: clean up this subscriptions
585
- linesFrom(this, "stdout").subscribe((line)=>this._logger.info(line));
586
- linesFrom(this, "stderr").subscribe((line)=>this._logger.info(line));
587
- }
588
- }
589
-
590
- // Utils
591
- function capitalize(txt) {
592
- return txt.charAt(0).toUpperCase() + txt.substring(1).toLowerCase();
593
- }
594
- function splitCommandLine(line) {
595
- line = line.trim();
596
- const parts = [];
597
- let current_cote = "";
598
- let last = 0;
599
- for(let i = 1; i < line.length; ++i){
600
- const c = line[i];
601
- if (current_cote) {
602
- if (c === current_cote) {
603
- current_cote = "";
604
- }
605
- } else {
606
- if ([
607
- '"',
608
- "'"
609
- ].includes(c)) {
610
- current_cote = c;
611
- } else if (c === " ") {
612
- parts.push(line.slice(last, i));
613
- last = i + 1;
614
- }
615
- }
616
- }
617
- parts.push(line.slice(last));
618
- return parts;
619
- }
620
-
621
- // Utils
622
- function isScriptCtx(ctx) {
623
- return "workspace" in ctx && "script" in ctx;
624
- }
625
- // Class
626
- class ScriptTask extends GroupTask {
627
- // Constructor
628
- constructor(workspace, script, args, opts){
629
- super(script, {
630
- workspace,
631
- script
632
- }, opts);
633
- this.workspace = workspace;
634
- this.script = script;
635
- this.args = args;
636
- this._preHookTasks = null;
637
- this._postHookTasks = null;
638
- this._scriptTasks = null;
639
- this._runHooks = opts?.runHooks ?? true;
640
- }
641
- // Methods
642
- async _runScript(script, args) {
643
- const line = this.workspace.getScript(script);
644
- if (!line) {
645
- return null;
646
- }
647
- // Create command task for script
648
- const [command, ...commandArgs] = splitCommandLine(line);
649
- if (command === "jill") {
650
- this._logger.debug(`Interpreting ${line}`);
651
- const argv = commandArgs.map((arg)=>arg.replace(/^["'](.+)["']$/, "$1"));
652
- const { JillApplication } = await import('./jill.application-8LJ2a7MD.js').then(function (n) { return n.j; });
653
- const app = container.get(JillApplication);
654
- const tasks = await app.tasksOf(argv, {
655
- project: this.project,
656
- workspace: this.workspace
657
- });
658
- if (tasks.length) {
659
- const set = new TaskSet();
660
- for (const tsk of tasks){
661
- set.add(tsk);
662
- }
663
- return set;
664
- }
665
- }
666
- const pm = await this.workspace.project.packageManager();
667
- const set = new TaskSet();
668
- set.add(new CommandTask(this.workspace, command, [
669
- ...commandArgs,
670
- ...args
671
- ], {
672
- logger: this._logger,
673
- superCommand: pm === "yarn" ? [
674
- "yarn",
675
- "exec"
676
- ] : undefined
677
- }));
678
- return set;
679
- }
680
- async prepare() {
681
- // Prepare script run
682
- this._scriptTasks = await this._runScript(this.script, this.args);
683
- if (!this._scriptTasks) {
684
- throw new Error(`No script ${this.script} in ${this.workspace.name}`);
685
- }
686
- // Prepare hooks run
687
- if (this._runHooks) {
688
- this._preHookTasks = await this._runScript(`pre${this.script}`, []);
689
- this._postHookTasks = await this._runScript(`post${this.script}`, []);
690
- }
691
- // Add tasks to group
692
- if (this._preHookTasks) {
693
- this._logger.verbose(`Found pre-hook script "pre${this.script}"`);
694
- for (const tsk of this._preHookTasks){
695
- this.add(tsk);
696
- }
697
- }
698
- for (const tsk of this._scriptTasks){
699
- this.add(tsk);
700
- }
701
- if (this._postHookTasks) {
702
- this._logger.verbose(`Found post-hook script "post${this.script}"`);
703
- for (const tsk of this._postHookTasks){
704
- this.add(tsk);
705
- }
706
- }
707
- }
708
- async *_orchestrate() {
709
- if (!this._scriptTasks) {
710
- throw new Error("ScriptTask needs to be prepared. Call prepare before starting it");
711
- }
712
- // Run pre-hook
713
- if (this._preHookTasks) {
714
- yield* this._preHookTasks;
715
- if (await this._hasFailed(this._preHookTasks)) {
716
- return this.setStatus("failed");
717
- }
718
- }
719
- // Run script
720
- yield* this._scriptTasks;
721
- if (await this._hasFailed(this._scriptTasks)) {
722
- return this.setStatus("failed");
723
- }
724
- // Run post-hook
725
- if (this._postHookTasks) {
726
- yield* this._postHookTasks;
727
- if (await this._hasFailed(this._postHookTasks)) {
728
- return this.setStatus("failed");
729
- }
730
- }
731
- this.setStatus("done");
732
- }
733
- async _hasFailed(set) {
734
- const results = await waitFor$(set, "finished");
735
- return results.failed > 0;
736
- }
737
- _stop() {
738
- if (!this._scriptTasks) return;
739
- for (const tsk of this._scriptTasks){
740
- tsk.stop();
741
- }
742
- }
743
- complexity(cache = new Map()) {
744
- let complexity = super.complexity(cache);
745
- if (this._scriptTasks) {
746
- complexity += this._scriptTasks.tasks.reduce((cpl, tsk)=>cpl + tsk.complexity(cache), 0);
747
- }
748
- cache.set(this.id, complexity);
749
- return complexity;
750
- }
751
- // Properties
752
- get project() {
753
- return this.workspace.project;
754
- }
755
- }
756
-
757
- // Symbols
758
- const TASK_MANAGER = Symbol("jujulego:jill:TaskManager");
759
- // Service
760
- container.bind(TASK_MANAGER).toDynamicValue(({ container })=>{
761
- const config = container.get(CONFIG);
762
- const logger = container.get(Logger);
763
- return new TaskManager({
764
- jobs: config.jobs,
765
- logger
766
- });
767
- }).inSingletonScope();
768
-
769
- // Component
770
- function List({ items, headers }) {
771
- if (items.length === 0) {
772
- return null;
773
- }
774
- return /*#__PURE__*/ jsx(Box, {
775
- children: Object.keys(items[0]).map((key)=>/*#__PURE__*/ jsxs(Box, {
776
- flexDirection: "column",
777
- marginRight: 2,
778
- children: [
779
- headers && /*#__PURE__*/ jsx(Text, {
780
- bold: true,
781
- children: capitalize(key)
782
- }),
783
- items.map((item, idx)=>/*#__PURE__*/ jsx(Text, {
784
- children: item[key] || " "
785
- }, idx))
786
- ]
787
- }, key))
788
- });
789
- }
790
-
791
- // Components
792
- function TaskName({ task }) {
793
- if (isScriptCtx(task.context)) {
794
- return /*#__PURE__*/ jsxs(Text, {
795
- children: [
796
- "Run ",
797
- /*#__PURE__*/ jsx(Text, {
798
- bold: true,
799
- children: task.context.script
800
- }),
801
- " in ",
802
- task.context.workspace.name
803
- ]
804
- });
805
- } else {
806
- return /*#__PURE__*/ jsx(Text, {
807
- children: task.name
808
- });
809
- }
810
- }
811
-
812
- // Components
813
- function TaskSpinner({ task }) {
814
- // State
815
- const [status, setStatus] = useState(task.status);
816
- const [time, setTime] = useState(task.duration);
817
- // Effects
818
- useLayoutEffect(()=>{
819
- return task.on("status", (event)=>{
820
- setStatus(event.status);
821
- });
822
- }, [
823
- task
824
- ]);
825
- useLayoutEffect(()=>{
826
- return task.on("completed", ({ duration })=>{
827
- setTime(duration);
828
- });
829
- }, [
830
- task
831
- ]);
832
- // Render
833
- const isScriptChild = isCommandCtx(task.context) && task.group && isScriptCtx(task.group.context);
834
- switch(status){
835
- case "blocked":
836
- case "ready":
837
- case "starting":
838
- return /*#__PURE__*/ jsxs(Box, {
839
- children: [
840
- /*#__PURE__*/ jsx(Text, {
841
- color: "grey",
842
- children: "\xb7"
843
- }),
844
- /*#__PURE__*/ jsx(Box, {
845
- paddingLeft: 1,
846
- children: /*#__PURE__*/ jsx(Text, {
847
- color: "grey",
848
- wrap: "truncate",
849
- children: /*#__PURE__*/ jsx(TaskName, {
850
- task: task
851
- })
852
- })
853
- })
854
- ]
855
- });
856
- case "running":
857
- return /*#__PURE__*/ jsxs(Box, {
858
- children: [
859
- /*#__PURE__*/ jsx(Text, {
860
- color: isScriptChild ? "dim" : undefined,
861
- children: /*#__PURE__*/ jsx(Spinner, {})
862
- }),
863
- /*#__PURE__*/ jsx(Box, {
864
- paddingLeft: 1,
865
- children: /*#__PURE__*/ jsx(Text, {
866
- color: isScriptChild ? "dim" : undefined,
867
- wrap: "truncate",
868
- children: /*#__PURE__*/ jsx(TaskName, {
869
- task: task
870
- })
871
- })
872
- })
873
- ]
874
- });
875
- case "done":
876
- return /*#__PURE__*/ jsxs(Box, {
877
- children: [
878
- /*#__PURE__*/ jsx(Text, {
879
- color: "green",
880
- children: symbols.success
881
- }),
882
- /*#__PURE__*/ jsx(Box, {
883
- paddingLeft: 1,
884
- children: /*#__PURE__*/ jsx(Text, {
885
- color: isScriptChild ? "dim" : undefined,
886
- wrap: "truncate",
887
- children: /*#__PURE__*/ jsx(TaskName, {
888
- task: task
889
- })
890
- })
891
- }),
892
- /*#__PURE__*/ jsx(Box, {
893
- paddingLeft: 1,
894
- flexShrink: 0,
895
- children: /*#__PURE__*/ jsxs(Text, {
896
- color: isScriptChild ? "grey" : "dim",
897
- children: [
898
- "(took ",
899
- ms(time),
900
- ")"
901
- ]
902
- })
903
- })
904
- ]
905
- });
906
- case "failed":
907
- return /*#__PURE__*/ jsxs(Box, {
908
- children: [
909
- /*#__PURE__*/ jsx(Text, {
910
- color: "red",
911
- children: symbols.error
912
- }),
913
- /*#__PURE__*/ jsx(Box, {
914
- paddingLeft: 1,
915
- children: /*#__PURE__*/ jsx(Text, {
916
- color: isScriptChild ? "dim" : undefined,
917
- wrap: "truncate",
918
- children: /*#__PURE__*/ jsx(TaskName, {
919
- task: task
920
- })
921
- })
922
- }),
923
- /*#__PURE__*/ jsx(Box, {
924
- paddingLeft: 1,
925
- flexShrink: 0,
926
- children: /*#__PURE__*/ jsxs(Text, {
927
- color: isScriptChild ? "grey" : "dim",
928
- children: [
929
- "(took ",
930
- ms(time),
931
- ")"
932
- ]
933
- })
934
- })
935
- ]
936
- });
937
- }
938
- }
939
-
940
- // Utils
941
- function printJson(data, stream = process.stdout) {
942
- if (stream.isTTY) {
943
- stream.write(JSON.stringify(data, null, 2));
944
- } else {
945
- stream.write(JSON.stringify(data));
946
- }
947
- }
948
-
949
- // Component
950
- function StaticLogs() {
951
- // State
952
- const { write } = useStderr();
953
- // Effect
954
- useLayoutEffect(()=>{
955
- const gateway = container.get(LogGateway);
956
- // Remove Console transport
957
- const listeners = gateway.listeners;
958
- gateway.clear();
959
- // Add custom transport
960
- const off = gateway.subscribe((log)=>{
961
- write(jillLogFormat(log) + "\n");
962
- });
963
- return ()=>{
964
- off();
965
- // Restore previous listeners
966
- for (const lst of listeners){
967
- gateway.subscribe(lst);
968
- }
969
- };
970
- }, [
971
- write
972
- ]);
973
- return null;
974
- }
975
-
976
- // Component
977
- function Layout({ children }) {
978
- return /*#__PURE__*/ jsxs(Fragment, {
979
- children: [
980
- /*#__PURE__*/ jsx(StaticLogs, {}),
981
- children
982
- ]
983
- });
984
- }
985
-
986
- // Constants
987
- const INK_APP = Symbol.for("jujulego:jill:ink-app");
988
- // Setup
989
- container.bind(INK_APP).toDynamicValue(()=>{
990
- if (!wt.isMainThread) {
991
- throw new Error("Ink should only be used in main thread");
992
- }
993
- return render(/*#__PURE__*/ jsx(Layout, {}));
994
- }).inSingletonScope();
995
-
996
- class InkCommand {
997
- builder(parser) {
998
- return parser;
999
- }
1000
- async handler(args) {
1001
- for await (const children of this.render(args)){
1002
- this.app.rerender(/*#__PURE__*/ jsx(Layout, {
1003
- children: children
1004
- }));
1005
- }
1006
- await this.app.waitUntilExit();
1007
- }
1008
- }
1009
- _([
1010
- lazyInject(INK_APP)
1011
- ], InkCommand.prototype, "app", void 0);
1012
- InkCommand = _([
1013
- injectable()
1014
- ], InkCommand);
1015
-
1016
- export { splitCommandLine as A, workerCache as B, CONFIG as C, ContextService as D, ExitException as E, applyConfigOptions as F, InkCommand as I, Layout as L, Middleware as M, PluginLoaderService as P, Service as S, TaskName as T, List as a, TaskSpinner as b, COMMAND as c, COMMAND_MODULE as d, buildCommandModule as e, Command as f, getCommandOpts as g, applyMiddlewares as h, getRegistry as i, getModule as j, setModule as k, CURRENT as l, container as m, lazyInject as n, lazyInjectNamed as o, isCommandCtx as p, CommandTask as q, isScriptCtx as r, setRegistry as s, ScriptTask as t, TASK_MANAGER as u, linesFrom as v, dynamicImport as w, fixDefaultExport as x, printJson as y, capitalize as z };
1017
- //# sourceMappingURL=ink-command-8BDSCbqp.js.map