@jujulego/jill 3.0.0-alpha.1 → 3.0.0-alpha.3

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.
Files changed (104) hide show
  1. package/README.md +1 -2
  2. package/bin/jill.js +1 -0
  3. package/dist/TaskName.js +33 -0
  4. package/dist/TaskName.js.map +1 -0
  5. package/dist/inked.js +66 -0
  6. package/dist/inked.js.map +1 -0
  7. package/dist/instrument.js +8 -0
  8. package/dist/instrument.js.map +1 -0
  9. package/dist/list.ink.js +50 -0
  10. package/dist/list.ink.js.map +1 -0
  11. package/dist/main.js +44 -35
  12. package/dist/main.js.map +1 -1
  13. package/dist/parser.js +2104 -0
  14. package/dist/parser.js.map +1 -0
  15. package/dist/planner.service.js +58 -0
  16. package/dist/planner.service.js.map +1 -0
  17. package/dist/task-exec.ink.js +539 -0
  18. package/dist/task-exec.ink.js.map +1 -0
  19. package/dist/task-plan.ink.js +114 -0
  20. package/dist/task-plan.ink.js.map +1 -0
  21. package/dist/tree.ink.js +185 -0
  22. package/dist/tree.ink.js.map +1 -0
  23. package/dist/tsconfig.build.tsbuildinfo +1 -1
  24. package/package.json +46 -45
  25. package/dist/ajv.config.d.ts +0 -3
  26. package/dist/commands/each.d.ts +0 -25
  27. package/dist/commands/exec.d.ts +0 -26
  28. package/dist/commands/group.d.ts +0 -16
  29. package/dist/commands/list.d.ts +0 -30
  30. package/dist/commands/run.d.ts +0 -24
  31. package/dist/commands/tree.d.ts +0 -6
  32. package/dist/commons/context.service.d.ts +0 -23
  33. package/dist/commons/git.service.d.ts +0 -62
  34. package/dist/commons/logger/log.gateway.d.ts +0 -18
  35. package/dist/commons/logger/parameters.d.ts +0 -2
  36. package/dist/commons/logger/thread.gateway.d.ts +0 -12
  37. package/dist/commons/logger/types.d.ts +0 -2
  38. package/dist/commons/logger.service.d.ts +0 -1
  39. package/dist/config/config-loader.d.ts +0 -4
  40. package/dist/config/config-options.d.ts +0 -5
  41. package/dist/config/types.d.ts +0 -8
  42. package/dist/config/utils.d.ts +0 -5
  43. package/dist/constants.d.ts +0 -1
  44. package/dist/core.plugin-CxgfxFUI.js +0 -642
  45. package/dist/core.plugin-CxgfxFUI.js.map +0 -1
  46. package/dist/core.plugin.d.ts +0 -2
  47. package/dist/filters/affected.filter.d.ts +0 -12
  48. package/dist/filters/pipeline.d.ts +0 -11
  49. package/dist/filters/private.filter.d.ts +0 -7
  50. package/dist/filters/scripts.filter.d.ts +0 -8
  51. package/dist/index.d.ts +0 -45
  52. package/dist/index.js +0 -35
  53. package/dist/index.js.map +0 -1
  54. package/dist/ink-command-CsbkuRbm.js +0 -2071
  55. package/dist/ink-command-CsbkuRbm.js.map +0 -1
  56. package/dist/ink.config.d.ts +0 -3
  57. package/dist/inversify.config.d.ts +0 -4
  58. package/dist/jill.application-DNJpmnCF.js +0 -637
  59. package/dist/jill.application-DNJpmnCF.js.map +0 -1
  60. package/dist/jill.application.d.ts +0 -19
  61. package/dist/main.d.ts +0 -1
  62. package/dist/middlewares/load-project.d.ts +0 -21
  63. package/dist/middlewares/load-workspace.d.ts +0 -20
  64. package/dist/modules/command.d.ts +0 -20
  65. package/dist/modules/ink-command.d.ts +0 -11
  66. package/dist/modules/middleware.d.ts +0 -8
  67. package/dist/modules/module.d.ts +0 -7
  68. package/dist/modules/plugin-loader.service.d.ts +0 -10
  69. package/dist/modules/plugin.d.ts +0 -14
  70. package/dist/modules/service.d.ts +0 -8
  71. package/dist/modules/task-command.d.ts +0 -14
  72. package/dist/project/project.d.ts +0 -27
  73. package/dist/project/project.repository.d.ts +0 -15
  74. package/dist/project/types.d.ts +0 -1
  75. package/dist/project/workspace.d.ts +0 -41
  76. package/dist/tasks/command-task.d.ts +0 -15
  77. package/dist/tasks/errors.d.ts +0 -4
  78. package/dist/tasks/script-task.d.ts +0 -27
  79. package/dist/tasks/task-expression.service.d.ts +0 -25
  80. package/dist/tasks/task-manager.config.d.ts +0 -3
  81. package/dist/types.d.ts +0 -11
  82. package/dist/ui/hooks/useFlatTaskTree.d.ts +0 -14
  83. package/dist/ui/hooks/useIsVerbose.d.ts +0 -1
  84. package/dist/ui/hooks/useStdoutDimensions.d.ts +0 -4
  85. package/dist/ui/layout.d.ts +0 -5
  86. package/dist/ui/list.d.ts +0 -5
  87. package/dist/ui/static-logs.d.ts +0 -1
  88. package/dist/ui/task-name.d.ts +0 -5
  89. package/dist/ui/task-spinner.d.ts +0 -5
  90. package/dist/ui/task-tree-completed.d.ts +0 -5
  91. package/dist/ui/task-tree-full-spinner.d.ts +0 -5
  92. package/dist/ui/task-tree-scrollable-spinner.d.ts +0 -5
  93. package/dist/ui/task-tree-spinner.d.ts +0 -5
  94. package/dist/ui/task-tree-stats.d.ts +0 -5
  95. package/dist/ui/workspace-tree.d.ts +0 -8
  96. package/dist/utils/events.d.ts +0 -3
  97. package/dist/utils/exit.d.ts +0 -4
  98. package/dist/utils/import.d.ts +0 -4
  99. package/dist/utils/json.d.ts +0 -1
  100. package/dist/utils/streams.d.ts +0 -3
  101. package/dist/utils/string.d.ts +0 -2
  102. package/dist/utils/worker-cache.d.ts +0 -3
  103. package/dist/workspace-tree-VWKE0B6b.js +0 -1120
  104. package/dist/workspace-tree-VWKE0B6b.js.map +0 -1
@@ -1,1120 +0,0 @@
1
- import { _ } from '@swc/helpers/_/_ts_decorate';
2
- import { Logger, withLabel } from '@jujulego/logger';
3
- import { S as Service, u as TASK_MANAGER, n as lazyInject, C as CONFIG, m as container, q as CommandTask, t as ScriptTask, M as Middleware, D as ContextService, l as CURRENT, o as lazyInjectNamed, E as ExitException, i as getRegistry, k as setModule } from './ink-command-CsbkuRbm.js';
4
- import { _ as _$1 } from '@swc/helpers/_/_ts_param';
5
- import { inject, injectable, ContainerModule, id } from 'inversify';
6
- import symbols from 'log-symbols';
7
- import path from 'node:path';
8
- import { satisfies } from 'semver';
9
- import { off$, once$, iterate$ } from '@jujulego/event-tree';
10
- import { SpawnTask, ParallelGroup, FallbackGroup, SequenceGroup } from '@jujulego/tasks';
11
- import { Lock } from '@jujulego/utils';
12
- import { Glob } from 'glob';
13
- import fs from 'node:fs';
14
- import normalize from 'normalize-package-data';
15
- import { PathScurry } from 'path-scurry';
16
- import fs$1 from 'node:fs/promises';
17
- import moo from 'moo';
18
- import { jsxs, jsx, Fragment } from 'react/jsx-runtime';
19
- import { Text, Newline } from 'ink';
20
- import { useState, useEffect } from 'react';
21
-
22
- // Utils
23
- async function* combine(...generators) {
24
- for (const gen of generators){
25
- yield* gen;
26
- }
27
- }
28
- async function* streamLines(task, stream) {
29
- // Abort
30
- const off = off$();
31
- once$(task, 'completed', off);
32
- // Stream
33
- let current = '';
34
- try {
35
- for await (const chunk of iterate$(task, `stream.${stream}`, {
36
- off
37
- })){
38
- const data = current + chunk.data.toString('utf-8');
39
- const lines = data.split(/\r?\n/);
40
- current = lines.pop() ?? '';
41
- for (const line of lines){
42
- yield line;
43
- }
44
- }
45
- } catch (err) {
46
- if (err.message !== 'Unsubscribed !') {
47
- throw err;
48
- }
49
- if (current) {
50
- yield current;
51
- }
52
- }
53
- }
54
-
55
- class GitService {
56
- // Constructor
57
- constructor(manager, logger){
58
- this.manager = manager;
59
- this.logger = logger;
60
- }
61
- // Methods
62
- /**
63
- * Runs a git command inside a SpawnTask
64
- *
65
- * @param cmd
66
- * @param args
67
- * @param options
68
- */ command(cmd, args, options = {}) {
69
- const opts = {
70
- logger: this.logger,
71
- ...options
72
- };
73
- // Create task
74
- const task = new SpawnTask('git', [
75
- cmd,
76
- ...args
77
- ], {
78
- command: cmd,
79
- hidden: true
80
- }, opts);
81
- task.on('stream', ({ data })=>opts.logger.debug(data.toString('utf-8')));
82
- this.manager.add(task);
83
- return task;
84
- }
85
- /**
86
- * Runs git branch
87
- *
88
- * @param args
89
- * @param options
90
- */ branch(args, options) {
91
- return this.command('branch', args, options);
92
- }
93
- /**
94
- * Runs git diff
95
- *
96
- * @param args
97
- * @param options
98
- */ diff(args, options) {
99
- return this.command('diff', args, options);
100
- }
101
- /**
102
- * Runs git tag
103
- *
104
- * @param args
105
- * @param options
106
- */ tag(args, options) {
107
- return this.command('tag', args, options);
108
- }
109
- /**
110
- * Uses git diff to detect if given files have been affected since given reference
111
- *
112
- * @param reference
113
- * @param files
114
- * @param opts
115
- */ isAffected(reference, files = [], opts) {
116
- return new Promise((resolve, reject)=>{
117
- const task = this.diff([
118
- '--quiet',
119
- reference,
120
- '--',
121
- ...files
122
- ], opts);
123
- once$(task, 'status.done', ()=>resolve(false));
124
- once$(task, 'status.failed', ()=>{
125
- if (task.exitCode) {
126
- resolve(true);
127
- } else {
128
- reject(new Error(`Task ${task.name} failed`));
129
- }
130
- });
131
- });
132
- }
133
- /**
134
- * List git branches
135
- *
136
- * @param args
137
- * @param opts
138
- */ async listBranches(args = [], opts) {
139
- const task = this.branch([
140
- '-l',
141
- ...args
142
- ], opts);
143
- const result = [];
144
- for await (const line of streamLines(task, 'stdout')){
145
- result.push(line.replace(/^[ *] /, ''));
146
- }
147
- return result;
148
- }
149
- /**
150
- * List git tags
151
- *
152
- * @param args
153
- * @param opts
154
- */ async listTags(args = [], opts) {
155
- const task = this.tag([
156
- '-l',
157
- ...args
158
- ], opts);
159
- const result = [];
160
- for await (const line of streamLines(task, 'stdout')){
161
- result.push(line);
162
- }
163
- return result;
164
- }
165
- }
166
- GitService = _([
167
- Service(),
168
- _$1(0, inject(TASK_MANAGER)),
169
- _$1(1, inject(Logger))
170
- ], GitService);
171
-
172
- // Class
173
- class AffectedFilter {
174
- // Constructor
175
- constructor(format, fallback, sort){
176
- this.format = format;
177
- this.fallback = fallback;
178
- this.sort = sort;
179
- }
180
- // Methods
181
- async _formatRevision(wks) {
182
- const logger = this._logger.child(withLabel(wks.name));
183
- // Format revision
184
- let result = this.format;
185
- result = result.replace(/(?<!\\)((?:\\\\)*)%name/g, `$1${wks.name}`);
186
- result = result.replace(/\\(.)/g, '$1');
187
- // Ask git to complete it
188
- const sortArgs = this.sort ? [
189
- '--sort',
190
- this.sort
191
- ] : [];
192
- // - search in branches
193
- if (result.includes('*')) {
194
- const branches = await this._git.listBranches([
195
- ...sortArgs,
196
- result
197
- ], {
198
- cwd: wks.cwd,
199
- logger: logger
200
- });
201
- if (branches.length > 0) {
202
- result = branches[branches.length - 1];
203
- }
204
- }
205
- // - search in tags
206
- if (result.includes('*')) {
207
- const tags = await this._git.listTags([
208
- ...sortArgs,
209
- result
210
- ], {
211
- cwd: wks.cwd,
212
- logger: logger
213
- });
214
- if (tags.length > 0) {
215
- result = tags[tags.length - 1];
216
- }
217
- }
218
- if (result !== this.format) {
219
- logger.verbose`Resolved ${this.format} into ${result}`;
220
- }
221
- if (result.includes('*')) {
222
- logger.warning(`No revision found matching ${result}, using fallback ${this.fallback}`);
223
- return this.fallback;
224
- }
225
- return result;
226
- }
227
- async test(workspace) {
228
- const rev = await this._formatRevision(workspace);
229
- return await workspace.isAffected(rev);
230
- }
231
- }
232
- _([
233
- lazyInject(Logger)
234
- ], AffectedFilter.prototype, "_logger", void 0);
235
- _([
236
- lazyInject(GitService)
237
- ], AffectedFilter.prototype, "_git", void 0);
238
-
239
- // Class
240
- class Pipeline {
241
- // Methods
242
- add(filter) {
243
- this._filters.push(filter);
244
- }
245
- async _test(workspace) {
246
- for (const filter of this._filters){
247
- const res = await filter.test(workspace);
248
- if (!res) {
249
- return false;
250
- }
251
- }
252
- return true;
253
- }
254
- async *filter(workspaces) {
255
- for await (const wks of workspaces){
256
- if (await this._test(wks)) {
257
- yield wks;
258
- }
259
- }
260
- }
261
- constructor(){
262
- // Attributes
263
- this._filters = [];
264
- }
265
- }
266
-
267
- // Filter
268
- class PrivateFilter {
269
- // Constructor
270
- constructor(value){
271
- this.value = value;
272
- }
273
- // Methods
274
- test(workspace) {
275
- return (workspace.manifest.private ?? false) === this.value;
276
- }
277
- }
278
-
279
- // Filter
280
- class ScriptsFilter {
281
- // Constructor
282
- constructor(scripts, all = false){
283
- this.scripts = scripts;
284
- this.all = all;
285
- }
286
- // Methods
287
- test(workspace) {
288
- const scripts = Object.keys(workspace.manifest.scripts || {});
289
- if (this.all) {
290
- return this.scripts.every((scr)=>scripts.includes(scr));
291
- } else {
292
- return this.scripts.some((scr)=>scripts.includes(scr));
293
- }
294
- }
295
- }
296
-
297
- class Workspace {
298
- // Constructor
299
- constructor(_cwd, manifest, project){
300
- this._cwd = _cwd;
301
- this.manifest = manifest;
302
- this.project = project;
303
- this._affectedCache = new Map();
304
- this._tasks = new Map();
305
- const logger = container.get(Logger);
306
- this._logger = logger.child(withLabel(this.manifest.name));
307
- }
308
- // Methods
309
- _satisfies(from, range) {
310
- if (range.startsWith('file:')) {
311
- return path.resolve(from.cwd, range.substring(5)) === this.cwd;
312
- }
313
- if (range.startsWith('workspace:')) {
314
- range = range.substring(10);
315
- }
316
- return !this.version || satisfies(this.version, range);
317
- }
318
- async _buildDependencies(task, opts) {
319
- // Generators
320
- const generators = [];
321
- switch(opts.buildDeps ?? 'all'){
322
- case 'all':
323
- generators.unshift(this.devDependencies());
324
- // eslint-disable-next no-fallthrough
325
- case 'prod':
326
- generators.unshift(this.dependencies());
327
- }
328
- // Build deps
329
- for await (const dep of combine(...generators)){
330
- const build = await dep.build(opts);
331
- if (build) {
332
- task.dependsOn(build);
333
- }
334
- }
335
- }
336
- async _isAffected(reference) {
337
- const isAffected = await this._git.isAffected(reference, [
338
- this.cwd
339
- ], {
340
- cwd: this.project.root,
341
- logger: this._logger
342
- });
343
- if (isAffected) {
344
- return true;
345
- }
346
- // Test dependencies
347
- const proms = [];
348
- for await (const dep of combine(this.dependencies(), this.devDependencies())){
349
- proms.push(dep.isAffected(reference));
350
- }
351
- const results = await Promise.all(proms);
352
- return results.some((r)=>r);
353
- }
354
- async isAffected(reference) {
355
- let isAffected = this._affectedCache.get(reference);
356
- if (!isAffected) {
357
- isAffected = this._isAffected(reference);
358
- this._affectedCache.set(reference, isAffected);
359
- }
360
- return await isAffected;
361
- }
362
- async *_loadDependencies(dependencies, kind) {
363
- for (const [dep, range] of Object.entries(dependencies)){
364
- const ws = await this.project.workspace(dep);
365
- if (ws) {
366
- if (ws._satisfies(this, range)) {
367
- yield ws;
368
- } else {
369
- this._logger.warning(`Ignoring ${kind} ${ws.reference} as it does not match requirement ${range}`);
370
- }
371
- }
372
- }
373
- }
374
- async *dependencies() {
375
- if (!this.manifest.dependencies) return;
376
- for await (const ws of this._loadDependencies(this.manifest.dependencies, 'dependency')){
377
- yield ws;
378
- }
379
- }
380
- async *devDependencies() {
381
- if (!this.manifest.devDependencies) return;
382
- for await (const ws of this._loadDependencies(this.manifest.devDependencies, 'devDependency')){
383
- yield ws;
384
- }
385
- }
386
- async exec(command, args = [], opts = {}) {
387
- const pm = await this.project.packageManager();
388
- const task = new CommandTask(this, command, args, {
389
- ...opts,
390
- logger: this._logger.child(withLabel(`${this.name}$${command}`)),
391
- superCommand: pm === 'yarn' ? [
392
- 'yarn',
393
- 'exec'
394
- ] : undefined
395
- });
396
- await this._buildDependencies(task, opts);
397
- return task;
398
- }
399
- async run(script, args = [], opts = {}) {
400
- // Script not found
401
- if (!this.getScript(script)) {
402
- return null;
403
- }
404
- // Create task if it doesn't exist yet
405
- let task = this._tasks.get(script);
406
- if (!task) {
407
- task = new ScriptTask(this, script, args, {
408
- ...opts,
409
- logger: this._logger.child(withLabel(`${this.name}#${script}`)),
410
- runHooks: this._config.hooks
411
- });
412
- await task.prepare();
413
- await this._buildDependencies(task, opts);
414
- this._tasks.set(script, task);
415
- }
416
- return task;
417
- }
418
- async build(opts = {}) {
419
- const task = await this.run(opts?.buildScript ?? 'build', [], opts);
420
- if (!task) {
421
- this._logger.warning('Will not be built (no build script)');
422
- }
423
- return task;
424
- }
425
- getScript(script) {
426
- const { scripts = {} } = this.manifest;
427
- return scripts[script] || null;
428
- }
429
- toJSON() {
430
- return {
431
- name: this.name,
432
- version: this.version,
433
- cwd: this.cwd
434
- };
435
- }
436
- // Properties
437
- get name() {
438
- return this.manifest.name;
439
- }
440
- get version() {
441
- return this.manifest.version;
442
- }
443
- get reference() {
444
- return this.version ? `${this.name}@${this.version}` : this.name;
445
- }
446
- get cwd() {
447
- return path.resolve(this.project.root, this._cwd);
448
- }
449
- }
450
- _([
451
- lazyInject(GitService)
452
- ], Workspace.prototype, "_git", void 0);
453
- _([
454
- lazyInject(CONFIG)
455
- ], Workspace.prototype, "_config", void 0);
456
- Workspace = _([
457
- injectable()
458
- ], Workspace);
459
-
460
- class Project {
461
- // Constructor
462
- constructor(_root, _logger, opts = {}){
463
- this._root = _root;
464
- this._logger = _logger;
465
- this._names = new Map();
466
- this._workspaces = new Map();
467
- this._isFullyLoaded = false;
468
- this._lock = new Lock();
469
- this._scurry = new PathScurry(this.root, {
470
- fs
471
- });
472
- if (opts.packageManager) {
473
- this._logger.debug`Forced use of ${opts.packageManager} in #!cwd:${this.root}`;
474
- this._packageManager = opts.packageManager;
475
- }
476
- }
477
- // Methods
478
- async _loadManifest(dir) {
479
- const file = path.resolve(this.root, dir, 'package.json');
480
- const relative = path.relative(this.root, path.dirname(file));
481
- const logger = this._logger.child(withLabel(relative ? `project@${relative}` : 'project'));
482
- logger.debug('Loading package.json ...');
483
- const data = await fs.promises.readFile(file, 'utf-8');
484
- const mnf = JSON.parse(data);
485
- normalize(mnf, (msg)=>logger.verbose(msg));
486
- return mnf;
487
- }
488
- async _loadWorkspace(dir) {
489
- return await this._lock.with(async ()=>{
490
- let wks = this._workspaces.get(dir);
491
- if (!wks) {
492
- const manifest = await this._loadManifest(dir);
493
- wks = new Workspace(dir, manifest, this);
494
- this._workspaces.set(dir, wks);
495
- this._names.set(wks.name, wks);
496
- }
497
- return wks;
498
- });
499
- }
500
- async packageManager() {
501
- if (!this._packageManager) {
502
- this._logger.debug`Searching lockfile from #!cwd:${this.root}`;
503
- const files = await this._scurry.readdir(this.root, {
504
- withFileTypes: false
505
- });
506
- if (files.includes('yarn.lock')) {
507
- this._logger.debug`Detected yarn in #!cwd:${this.root}`;
508
- this._packageManager = 'yarn';
509
- } else if (files.includes('package-lock.json')) {
510
- this._logger.debug`Detected npm in #!cwd:${this.root}`;
511
- this._packageManager = 'npm';
512
- } else {
513
- this._logger.debug`No package manager recognized in #!cwd:${this.root}, defaults to npm`;
514
- this._packageManager = 'npm';
515
- }
516
- }
517
- return this._packageManager;
518
- }
519
- async mainWorkspace() {
520
- if (!this._mainWorkspace) {
521
- const manifest = await this._loadManifest('.');
522
- this._mainWorkspace = new Workspace('.', manifest, this);
523
- this._names.set(this._mainWorkspace.name, this._mainWorkspace);
524
- }
525
- return this._mainWorkspace;
526
- }
527
- async currentWorkspace(cwd = process.cwd()) {
528
- let workspace = null;
529
- cwd = path.resolve(cwd);
530
- for await (const wks of this.workspaces()){
531
- if (cwd.startsWith(wks.cwd)) {
532
- workspace = wks;
533
- if (wks.cwd !== this.root) return wks;
534
- }
535
- }
536
- return workspace;
537
- }
538
- async *workspaces() {
539
- const main = await this.mainWorkspace();
540
- yield main;
541
- if (this._isFullyLoaded) {
542
- for (const wks of this._names.values()){
543
- if (wks.name !== main.name) yield wks;
544
- }
545
- } else {
546
- // Load child workspaces
547
- const { workspaces = [] } = main.manifest;
548
- this._workspaceGlob ??= new Glob(workspaces, {
549
- scurry: this._scurry,
550
- withFileTypes: true
551
- });
552
- for await (const dir of this._workspaceGlob){
553
- try {
554
- // Check if dir is a directory
555
- if (dir.isDirectory()) {
556
- yield await this._loadWorkspace(dir.fullpath());
557
- }
558
- } catch (error) {
559
- if (error.code === 'ENOENT') {
560
- continue;
561
- }
562
- throw error;
563
- }
564
- }
565
- this._isFullyLoaded = true;
566
- }
567
- }
568
- async workspace(name) {
569
- // With current directory
570
- if (!name) {
571
- const dir = path.relative(this.root, process.cwd());
572
- return this._loadWorkspace(dir);
573
- }
574
- // Try name index
575
- const wks = this._names.get(name);
576
- if (wks) {
577
- return wks;
578
- }
579
- // Load workspaces
580
- if (!this._isFullyLoaded) {
581
- for await (const ws of this.workspaces()){
582
- if (ws.name === name) {
583
- return ws;
584
- }
585
- }
586
- this._isFullyLoaded = true;
587
- }
588
- return null;
589
- }
590
- // Properties
591
- get root() {
592
- return path.resolve(this._root);
593
- }
594
- }
595
- Project = _([
596
- injectable()
597
- ], Project);
598
-
599
- // Constants
600
- const MANIFEST = 'package.json';
601
- const LOCK_FILES = [
602
- 'package-lock.json',
603
- 'yarn.lock'
604
- ];
605
- class ProjectRepository {
606
- // Constructor
607
- constructor(logger){
608
- this._cache = new Map();
609
- this._roots = new Map();
610
- this._logger = logger.child(withLabel('projects'));
611
- }
612
- // Methods
613
- async isProjectRoot(dir) {
614
- const files = await fs$1.readdir(dir);
615
- return {
616
- hasManifest: files.includes(MANIFEST),
617
- hasLockFile: LOCK_FILES.some((lock)=>files.includes(lock))
618
- };
619
- }
620
- async searchProjectRoot(directory) {
621
- directory = path.resolve(directory);
622
- // Test all ancestors
623
- const steps = [];
624
- let foundManifest = false;
625
- let projectRoot = directory;
626
- let dir = directory;
627
- let prev = dir;
628
- do {
629
- // Check cache
630
- const root = this._roots.get(dir);
631
- if (root) {
632
- projectRoot = root;
633
- foundManifest = true;
634
- break;
635
- }
636
- // Look for files
637
- const { hasManifest, hasLockFile } = await this.isProjectRoot(dir);
638
- steps.push(dir);
639
- if (hasManifest) {
640
- projectRoot = dir;
641
- foundManifest = true;
642
- }
643
- if (hasLockFile) {
644
- break;
645
- }
646
- prev = dir;
647
- dir = path.dirname(dir);
648
- }while (prev !== dir);
649
- // Cache result
650
- for (const dir of steps){
651
- if (dir.startsWith(projectRoot)) {
652
- this._roots.set(dir, projectRoot);
653
- }
654
- }
655
- // Log it
656
- if (foundManifest) {
657
- this._logger.debug`Project root found at #!cwd:${projectRoot}`;
658
- } else {
659
- this._logger.debug`Project root not found, keeping #!cwd:${projectRoot}`;
660
- }
661
- return projectRoot;
662
- }
663
- getProject(root, opts) {
664
- let project = this._cache.get(root);
665
- if (!project) {
666
- project = new Project(root, this._logger, opts);
667
- this._cache.set(root, project);
668
- }
669
- return project;
670
- }
671
- }
672
- ProjectRepository = _([
673
- Service(),
674
- _$1(0, inject(Logger))
675
- ], ProjectRepository);
676
-
677
- class LoadProject {
678
- // Constructor
679
- constructor(projects, context){
680
- this.projects = projects;
681
- this.context = context;
682
- }
683
- // Methods
684
- builder(parser) {
685
- return parser.option('project', {
686
- alias: 'p',
687
- type: 'string',
688
- description: 'Project root directory'
689
- }).option('package-manager', {
690
- choices: [
691
- 'yarn',
692
- 'npm'
693
- ],
694
- type: 'string',
695
- description: 'Force package manager'
696
- });
697
- }
698
- async handler(args) {
699
- if (!this.context.project || args.project) {
700
- args.project = await this.projects.searchProjectRoot(args.project ?? process.cwd());
701
- this.context.project = this.projects.getProject(args.project, {
702
- packageManager: args.packageManager
703
- });
704
- } else {
705
- args.project = this.context.project.root;
706
- }
707
- }
708
- }
709
- LoadProject = _([
710
- Middleware(),
711
- _$1(0, inject(ProjectRepository)),
712
- _$1(1, inject(ContextService))
713
- ], LoadProject);
714
- // Lazy injection
715
- function LazyCurrentProject() {
716
- return lazyInjectNamed(Project, CURRENT);
717
- }
718
- container.bind(Project).toDynamicValue(({ container })=>{
719
- const ctx = container.get(ContextService);
720
- const prj = ctx.project;
721
- if (!prj) {
722
- throw new Error('Cannot inject current project, it not yet defined');
723
- }
724
- return prj;
725
- }).whenTargetNamed(CURRENT);
726
-
727
- class LoadWorkspace {
728
- // Constructor
729
- constructor(context, logger){
730
- this.context = context;
731
- this.logger = logger;
732
- }
733
- // Methods
734
- builder(parser) {
735
- return parser.option('workspace', {
736
- alias: 'w',
737
- type: 'string',
738
- desc: 'Workspace to use'
739
- });
740
- }
741
- async handler(args) {
742
- let workspace = this.context.workspace ?? null;
743
- if (!workspace || args.workspace) {
744
- if (args.workspace) {
745
- workspace = await this.project.workspace(args.workspace);
746
- } else if (process.cwd().startsWith(this.project.root)) {
747
- workspace = await this.project.currentWorkspace();
748
- } else {
749
- workspace = await this.project.mainWorkspace();
750
- }
751
- }
752
- if (!workspace) {
753
- this.logger.error(`${symbols.error} Workspace "${args.workspace || '.'}" not found`);
754
- throw new ExitException(1, 'Workspace not found');
755
- } else {
756
- this.context.workspace = workspace;
757
- }
758
- }
759
- }
760
- _([
761
- LazyCurrentProject()
762
- ], LoadWorkspace.prototype, "project", void 0);
763
- LoadWorkspace = _([
764
- Middleware(),
765
- _$1(0, inject(ContextService)),
766
- _$1(1, inject(Logger))
767
- ], LoadWorkspace);
768
- // Decorators
769
- function LazyCurrentWorkspace() {
770
- return lazyInjectNamed(Workspace, CURRENT);
771
- }
772
- container.bind(Workspace).toDynamicValue(({ container })=>{
773
- const ctx = container.get(ContextService);
774
- const wks = ctx.workspace;
775
- if (!wks) {
776
- throw new Error('Cannot inject current workspace, it not yet defined');
777
- }
778
- return wks;
779
- }).whenTargetNamed(CURRENT);
780
-
781
- class PluginModule extends ContainerModule {
782
- // Constructor
783
- constructor(name, commands){
784
- super((...args)=>{
785
- for (const command of this.commands){
786
- const registry = getRegistry(command);
787
- registry(...args);
788
- }
789
- });
790
- this.name = name;
791
- this.commands = commands;
792
- this.id = id();
793
- }
794
- }
795
- // Decorator
796
- function Plugin(opts) {
797
- return (target)=>{
798
- const name = opts.name ?? target.name;
799
- const module = new PluginModule(name, opts.commands);
800
- setModule(target, module);
801
- };
802
- }
803
-
804
- class TaskExpressionError extends Error {
805
- }
806
- class TaskSyntaxError extends Error {
807
- }
808
-
809
- class TaskExpressionService {
810
- // Statics
811
- static isTaskNode(node) {
812
- return 'script' in node;
813
- }
814
- static{
815
- this._sequenceOperatorWarn = true;
816
- }
817
- // Constructor
818
- constructor(_logger){
819
- this._logger = _logger;
820
- }
821
- // Methods
822
- _lexer() {
823
- return moo.states({
824
- task: {
825
- lparen: '(',
826
- whitespace: /[ \t]+/,
827
- script: {
828
- match: /[-_:a-zA-Z0-9]+/,
829
- push: 'operatorOrArgument'
830
- },
831
- string: [
832
- {
833
- match: /'(?:\\['\\]|[^\r\n'\\])+'/,
834
- push: 'operator',
835
- value: (x)=>x.slice(1, -1).replace(/\\(['\\])/g, '$1')
836
- },
837
- {
838
- match: /"(?:\\["\\]|[^\r\n"\\])+"/,
839
- push: 'operator',
840
- value: (x)=>x.slice(1, -1).replace(/\\(["\\])/g, '$1')
841
- }
842
- ]
843
- },
844
- operator: {
845
- rparen: ')',
846
- whitespace: /[ \t]+/,
847
- operator: {
848
- match: [
849
- '->',
850
- '&&',
851
- '//',
852
- '||'
853
- ],
854
- pop: 1
855
- }
856
- },
857
- operatorOrArgument: {
858
- rparen: ')',
859
- whitespace: /[ \t]+/,
860
- operator: {
861
- match: [
862
- '->',
863
- '&&',
864
- '//',
865
- '||'
866
- ],
867
- pop: 1
868
- },
869
- argument: [
870
- {
871
- match: /[-_:a-zA-Z0-9]+/
872
- },
873
- {
874
- match: /'(?:\\['\\]|[^\r\n'\\])+'/,
875
- value: (x)=>x.slice(1, -1).replace(/\\(['\\])/g, '$1')
876
- },
877
- {
878
- match: /"(?:\\["\\]|[^\r\n"\\])+"/,
879
- value: (x)=>x.slice(1, -1).replace(/\\(["\\])/g, '$1')
880
- }
881
- ]
882
- }
883
- });
884
- }
885
- _nextNode(lexer, i = 0) {
886
- let node = null;
887
- for (const token of lexer){
888
- // Ignore whitespaces
889
- if (token.type === 'whitespace') {
890
- continue;
891
- }
892
- // rparen = end of group
893
- if (token.type === 'rparen') {
894
- break;
895
- }
896
- // Handle argument
897
- if (token.type === 'argument') {
898
- if (!node) {
899
- throw new TaskSyntaxError(lexer.formatError(token, 'Unexpected argument'));
900
- } else if (TaskExpressionService.isTaskNode(node)) {
901
- node.args.push(token.value);
902
- } else {
903
- const lastTask = node.tasks[node.tasks.length - 1];
904
- if (!lastTask || !TaskExpressionService.isTaskNode(lastTask)) {
905
- throw new TaskSyntaxError(lexer.formatError(token, 'Unexpected argument'));
906
- } else {
907
- lastTask.args.push(token.value);
908
- }
909
- }
910
- continue;
911
- }
912
- // Handle operator
913
- if (token.type === 'operator') {
914
- const operator = token.value;
915
- if (!node) {
916
- throw new TaskSyntaxError(lexer.formatError(token, 'Unexpected operator'));
917
- } else if (TaskExpressionService.isTaskNode(node)) {
918
- node = {
919
- operator,
920
- tasks: [
921
- node
922
- ]
923
- };
924
- continue;
925
- } else {
926
- if (node.operator !== operator) {
927
- node = {
928
- operator,
929
- tasks: [
930
- node
931
- ]
932
- };
933
- }
934
- continue;
935
- }
936
- }
937
- // Build "child"
938
- let child;
939
- if (token.type === 'script') {
940
- child = {
941
- script: token.value,
942
- args: []
943
- };
944
- } else if (token.type === 'string') {
945
- const [script, ...args] = token.value.split(/ +/);
946
- child = {
947
- script,
948
- args
949
- };
950
- } else if (token.type === 'lparen') {
951
- const res = this._nextNode(lexer, i + 1);
952
- if (!res) {
953
- throw new TaskSyntaxError(lexer.formatError(token, 'Empty group found'));
954
- }
955
- child = res;
956
- } else {
957
- throw new TaskSyntaxError(lexer.formatError(token, 'Unexpected token'));
958
- }
959
- if (!node) {
960
- node = child;
961
- } else if (TaskExpressionService.isTaskNode(node)) {
962
- throw new TaskSyntaxError(lexer.formatError(token, 'Unexpected token, expected an operator'));
963
- } else {
964
- node.tasks.push(child);
965
- }
966
- }
967
- return node;
968
- }
969
- parse(expr) {
970
- const lexer = this._lexer().reset(expr);
971
- const tree = {
972
- roots: []
973
- };
974
- // eslint-disable-next-line no-constant-condition
975
- while(true){
976
- const node = this._nextNode(lexer);
977
- if (node) {
978
- tree.roots.push(node);
979
- } else {
980
- break;
981
- }
982
- }
983
- return tree;
984
- }
985
- *extractScripts(node) {
986
- if ('roots' in node) {
987
- for (const child of node.roots){
988
- yield* this.extractScripts(child);
989
- }
990
- } else if (TaskExpressionService.isTaskNode(node)) {
991
- yield node.script;
992
- } else {
993
- for (const child of node.tasks){
994
- yield* this.extractScripts(child);
995
- }
996
- }
997
- }
998
- async buildTask(node, workspace, opts) {
999
- if (TaskExpressionService.isTaskNode(node)) {
1000
- const task = await workspace.run(node.script, node.args, opts);
1001
- if (!task) {
1002
- throw new TaskExpressionError(`Workspace ${workspace.name} have no ${node.script} script`);
1003
- }
1004
- return task;
1005
- } else {
1006
- let group;
1007
- if (node.operator === '//') {
1008
- group = new ParallelGroup('In parallel', {
1009
- workspace
1010
- }, {
1011
- logger: this._logger
1012
- });
1013
- } else if (node.operator === '||') {
1014
- group = new FallbackGroup('Fallbacks', {
1015
- workspace
1016
- }, {
1017
- logger: this._logger
1018
- });
1019
- } else {
1020
- if (node.operator === '->' && TaskExpressionService._sequenceOperatorWarn) {
1021
- this._logger.warn('Sequence operator -> is deprecated in favor of &&. It will be removed in a next major release.');
1022
- TaskExpressionService._sequenceOperatorWarn = true;
1023
- }
1024
- group = new SequenceGroup('In sequence', {
1025
- workspace
1026
- }, {
1027
- logger: this._logger
1028
- });
1029
- }
1030
- for (const child of node.tasks){
1031
- group.add(await this.buildTask(child, workspace, opts));
1032
- }
1033
- return group;
1034
- }
1035
- }
1036
- }
1037
- TaskExpressionService = _([
1038
- Service(),
1039
- _$1(0, inject(Logger))
1040
- ], TaskExpressionService);
1041
-
1042
- // Utils
1043
- const style = (dev)=>({
1044
- color: dev ? 'blue' : ''
1045
- });
1046
- // Component
1047
- function WorkspaceTree(props) {
1048
- const { workspace: wks, dev = false, level = '' } = props;
1049
- // State
1050
- const [deps, setDeps] = useState([]);
1051
- // Effects
1052
- useEffect(()=>void (async ()=>{
1053
- const deps = [];
1054
- for await (const dep of wks.dependencies()){
1055
- deps.push([
1056
- dep,
1057
- null
1058
- ]);
1059
- }
1060
- for await (const dep of wks.devDependencies()){
1061
- deps.push([
1062
- dep,
1063
- true
1064
- ]);
1065
- }
1066
- setDeps(deps);
1067
- })(), [
1068
- wks
1069
- ]);
1070
- // Render
1071
- return /*#__PURE__*/ jsxs(Text, {
1072
- children: [
1073
- /*#__PURE__*/ jsx(Text, {
1074
- ...style(dev),
1075
- children: wks.name
1076
- }),
1077
- wks.version && /*#__PURE__*/ jsxs(Text, {
1078
- color: "grey",
1079
- children: [
1080
- "@",
1081
- wks.version
1082
- ]
1083
- }),
1084
- deps.length > 0 && /*#__PURE__*/ jsx(Newline, {}),
1085
- deps.map(([dep, isDev], idx)=>/*#__PURE__*/ jsxs(Text, {
1086
- children: [
1087
- level,
1088
- /*#__PURE__*/ jsxs(Text, {
1089
- ...style(dev),
1090
- children: [
1091
- idx === deps.length - 1 ? '└' : '├',
1092
- "─",
1093
- ' '
1094
- ]
1095
- }),
1096
- /*#__PURE__*/ jsx(WorkspaceTree, {
1097
- workspace: dep,
1098
- dev: isDev ?? dev,
1099
- level: /*#__PURE__*/ jsxs(Fragment, {
1100
- children: [
1101
- level,
1102
- /*#__PURE__*/ jsxs(Text, {
1103
- ...style(dev),
1104
- children: [
1105
- idx === deps.length - 1 ? ' ' : '│',
1106
- ' '
1107
- ]
1108
- })
1109
- ]
1110
- })
1111
- }),
1112
- idx < deps.length - 1 && /*#__PURE__*/ jsx(Newline, {})
1113
- ]
1114
- }, dep.name))
1115
- ]
1116
- });
1117
- }
1118
-
1119
- export { AffectedFilter as A, GitService as G, LoadProject as L, Pipeline as P, ScriptsFilter as S, TaskExpressionService as T, WorkspaceTree as W, PrivateFilter as a, LazyCurrentProject as b, LoadWorkspace as c, LazyCurrentWorkspace as d, PluginModule as e, Plugin as f, Project as g, ProjectRepository as h, Workspace as i, combine as j, TaskExpressionError as k, TaskSyntaxError as l, streamLines as s };
1120
- //# sourceMappingURL=workspace-tree-VWKE0B6b.js.map