@youcan/cli-kit 2.1.3 → 2.2.0

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 (51) hide show
  1. package/dist/common/string.d.ts +2 -2
  2. package/dist/common/string.js +11 -11
  3. package/dist/index.d.ts +18 -17
  4. package/dist/index.js +2 -0
  5. package/dist/internal/node/constants.d.ts +5 -5
  6. package/dist/internal/node/constants.js +10 -10
  7. package/dist/internal/node/ui.d.ts +11 -11
  8. package/dist/internal/node/ui.js +40 -40
  9. package/dist/node/callback.d.ts +5 -5
  10. package/dist/node/callback.js +53 -53
  11. package/dist/node/cli.d.ts +21 -21
  12. package/dist/node/cli.js +61 -61
  13. package/dist/node/config.d.ts +20 -20
  14. package/dist/node/config.js +20 -22
  15. package/dist/node/context/helpers.d.ts +1 -1
  16. package/dist/node/context/helpers.js +5 -5
  17. package/dist/node/context/local.d.ts +2 -2
  18. package/dist/node/context/local.js +2 -2
  19. package/dist/node/crypto.d.ts +12 -9
  20. package/dist/node/crypto.js +23 -23
  21. package/dist/node/env.d.ts +5 -6
  22. package/dist/node/env.js +36 -47
  23. package/dist/node/filesystem.d.ts +30 -29
  24. package/dist/node/filesystem.js +81 -78
  25. package/dist/node/form.d.ts +9 -9
  26. package/dist/node/form.js +39 -39
  27. package/dist/node/git.d.ts +10 -10
  28. package/dist/node/git.js +46 -46
  29. package/dist/node/github.d.ts +6 -6
  30. package/dist/node/github.js +8 -8
  31. package/dist/node/http.d.ts +4 -4
  32. package/dist/node/http.js +35 -32
  33. package/dist/node/path.d.ts +5 -5
  34. package/dist/node/path.js +14 -14
  35. package/dist/node/session.d.ts +8 -8
  36. package/dist/node/session.js +92 -78
  37. package/dist/node/system.d.ts +23 -21
  38. package/dist/node/system.js +74 -58
  39. package/dist/node/tasks.d.ts +8 -7
  40. package/dist/node/tasks.js +33 -25
  41. package/dist/node/worker.d.ts +16 -19
  42. package/dist/node/worker.js +43 -30
  43. package/dist/ui/components/DevOutput.d.ts +27 -0
  44. package/dist/ui/components/DevOutput.js +60 -0
  45. package/dist/ui/components/HotKeys.d.ts +12 -0
  46. package/dist/ui/components/HotKeys.js +25 -0
  47. package/dist/ui/components/utils/symbols.d.ts +3 -0
  48. package/dist/ui/components/utils/symbols.js +7 -0
  49. package/dist/ui/index.d.ts +2 -0
  50. package/dist/ui/index.js +2 -0
  51. package/package.json +7 -3
@@ -1,11 +1,11 @@
1
- function parseRepositoryReference(reference) {
2
- const url = new URL(reference);
3
- const [, user, repo, ...repoPath] = url.pathname.split('/');
4
- return {
5
- baseUrl: `${url.origin}/${user}/${repo}`,
6
- branch: url.hash ? url.hash.slice(1) : undefined,
7
- path: repoPath.length > 0 ? repoPath.join('/') : undefined,
8
- };
1
+ function parseRepositoryReference(reference) {
2
+ const url = new URL(reference);
3
+ const [, user, repo, ...repoPath] = url.pathname.split('/');
4
+ return {
5
+ baseUrl: `${url.origin}/${user}/${repo}`,
6
+ branch: url.hash ? url.hash.slice(1) : undefined,
7
+ path: repoPath.length > 0 ? repoPath.join('/') : undefined,
8
+ };
9
9
  }
10
10
 
11
11
  export { parseRepositoryReference };
@@ -1,4 +1,4 @@
1
- import type { RequestInit } from 'node-fetch';
2
- export declare function scheme(): 'http' | 'https';
3
- export declare function get<T>(endpoint: string, options?: RequestInit): Promise<T>;
4
- export declare function post<T>(endpoint: string, options?: RequestInit): Promise<T>;
1
+ import type { RequestInit } from 'node-fetch';
2
+ export declare function scheme(): 'http' | 'https';
3
+ export declare function get<T>(endpoint: string, options?: RequestInit): Promise<T>;
4
+ export declare function post<T>(endpoint: string, options?: RequestInit): Promise<T>;
package/dist/node/http.js CHANGED
@@ -13,40 +13,43 @@ import 'conf';
13
13
  import 'dayjs';
14
14
  import { get as get$2 } from './session.js';
15
15
  import './filesystem.js';
16
+ import '../ui/components/DevOutput.js';
17
+ import 'react';
18
+ import 'ink';
16
19
  import { isJson } from '../common/string.js';
17
20
 
18
- function scheme() {
19
- return get$1('HOST_ENV') === 'dev' ? 'http' : 'https';
20
- }
21
- async function agent() {
22
- const { Agent } = await import(scheme());
23
- return new Agent({ keepAlive: true, keepAliveMsecs: 5 * 60 * 1000 });
24
- }
25
- async function defaults() {
26
- const session$1 = await get$2();
27
- return {
28
- agent: await agent(),
29
- headers: {
30
- Accept: 'application/json',
31
- Authorization: session$1 ? `Bearer ${session$1.access_token}` : undefined,
32
- },
33
- };
34
- }
35
- async function request(endpoint, options = {}) {
36
- if (is(String)(options.body) && isJson(options.body)) {
37
- options = mergeDeepLeft(options, { headers: { 'Content-Type': 'application/json' } });
38
- }
39
- const response = await fetch(endpoint, mergeDeepLeft(options, await defaults()));
40
- if (!response.ok) {
41
- throw new Error(await response.text(), { cause: response });
42
- }
43
- return response.json();
44
- }
45
- async function get(endpoint, options = {}) {
46
- return request(`${scheme()}://${endpoint}`, { ...options, method: 'GET' });
47
- }
48
- async function post(endpoint, options = {}) {
49
- return request(`${scheme()}://${endpoint}`, { ...options, method: 'POST' });
21
+ function scheme() {
22
+ return get$1('HOST_ENV') === 'dev' ? 'http' : 'https';
23
+ }
24
+ async function agent() {
25
+ const { Agent } = await import(scheme());
26
+ return new Agent({ keepAlive: true, keepAliveMsecs: 5 * 60 * 1000 });
27
+ }
28
+ async function defaults() {
29
+ const session$1 = await get$2();
30
+ return {
31
+ agent: await agent(),
32
+ headers: {
33
+ Accept: 'application/json',
34
+ Authorization: session$1 ? `Bearer ${session$1.access_token}` : undefined,
35
+ },
36
+ };
37
+ }
38
+ async function request(endpoint, options = {}) {
39
+ if (is(String)(options.body) && isJson(options.body)) {
40
+ options = mergeDeepLeft(options, { headers: { 'Content-Type': 'application/json' } });
41
+ }
42
+ const response = await fetch(endpoint, mergeDeepLeft(options, await defaults()));
43
+ if (!response.ok) {
44
+ throw new Error(await response.text(), { cause: response });
45
+ }
46
+ return response.json();
47
+ }
48
+ async function get(endpoint, options = {}) {
49
+ return request(`${scheme()}://${endpoint}`, { ...options, method: 'GET' });
50
+ }
51
+ async function post(endpoint, options = {}) {
52
+ return request(`${scheme()}://${endpoint}`, { ...options, method: 'POST' });
50
53
  }
51
54
 
52
55
  export { get, post, scheme };
@@ -1,5 +1,5 @@
1
- export declare function resolve(...paths: string[]): string;
2
- export declare function cwd(): string;
3
- export declare function join(...paths: string[]): string;
4
- export declare function dirname(filepath: string): string;
5
- export declare function basename(filepath: string): string;
1
+ export declare function resolve(...paths: string[]): string;
2
+ export declare function cwd(): string;
3
+ export declare function join(...paths: string[]): string;
4
+ export declare function dirname(filepath: string): string;
5
+ export declare function basename(filepath: string): string;
package/dist/node/path.js CHANGED
@@ -1,19 +1,19 @@
1
1
  import path from 'node:path';
2
2
 
3
- function resolve(...paths) {
4
- return path.resolve(...paths);
5
- }
6
- function cwd() {
7
- return path.normalize(process.env.INIT_CWD ? process.env.INIT_CWD : process.cwd());
8
- }
9
- function join(...paths) {
10
- return path.join(...paths);
11
- }
12
- function dirname(filepath) {
13
- return path.dirname(filepath);
14
- }
15
- function basename(filepath) {
16
- return path.basename(filepath);
3
+ function resolve(...paths) {
4
+ return path.resolve(...paths);
5
+ }
6
+ function cwd() {
7
+ return path.normalize(process.env.INIT_CWD ? process.env.INIT_CWD : process.cwd());
8
+ }
9
+ function join(...paths) {
10
+ return path.join(...paths);
11
+ }
12
+ function dirname(filepath) {
13
+ return path.dirname(filepath);
14
+ }
15
+ function basename(filepath) {
16
+ return path.basename(filepath);
17
17
  }
18
18
 
19
19
  export { basename, cwd, dirname, join, resolve };
@@ -1,8 +1,8 @@
1
- import { type Cli } from '..';
2
- export interface StoreSession {
3
- id: string;
4
- slug: string;
5
- access_token: string;
6
- }
7
- export declare function get(): Promise<StoreSession | null>;
8
- export declare function authenticate(command: Cli.Command): Promise<StoreSession>;
1
+ import { type Cli } from '..';
2
+ export interface StoreSession {
3
+ id: string;
4
+ slug: string;
5
+ access_token: string;
6
+ }
7
+ export declare function get(): Promise<StoreSession | null>;
8
+ export declare function authenticate(command: Cli.Command): Promise<StoreSession>;
@@ -1,94 +1,108 @@
1
1
  import './cli.js';
2
2
  import 'simple-git';
3
3
  import { isPortAvailable, getPortProcessName, killPortProcess, open } from './system.js';
4
- import { apiHostname, oauthClientId, oauthClientSecret, sellerAreaHostname } from './env.js';
4
+ import { apiHostname, oauthClientId, sellerAreaHostname } from './env.js';
5
5
  import { get as get$1, post } from './http.js';
6
6
  import 'formdata-node';
7
7
  import 'formdata-node/file-from-path';
8
8
  import 'kleur';
9
9
  import { manager } from './config.js';
10
- import { randomHex } from './crypto.js';
10
+ import { randomHex, sha256, base64URLEncode } from './crypto.js';
11
11
  import 'dayjs';
12
12
  import { listen } from './callback.js';
13
13
  import './filesystem.js';
14
+ import '../ui/components/DevOutput.js';
15
+ import 'react';
16
+ import 'ink';
14
17
  import 'change-case';
15
18
 
16
- const LS_PORT = 3000;
17
- const LS_HOST = 'localhost';
18
- async function isSessionValid(session) {
19
- try {
20
- const store = await get$1(`${apiHostname()}/me`, { headers: { Authorization: `Bearer ${session.access_token}` } });
21
- return store.status === 1;
22
- }
23
- catch (err) {
24
- return false;
25
- }
26
- }
27
- async function exchange(code) {
28
- const params = {
29
- code,
30
- client_id: oauthClientId(),
31
- grant_type: 'authorization_code',
32
- client_secret: oauthClientSecret(),
33
- redirect_uri: `http://${LS_HOST}:${LS_PORT}/`,
34
- };
35
- const result = await post(`${apiHostname()}/oauth/token`, {
36
- body: new URLSearchParams(Object.entries(params)),
37
- headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
38
- });
39
- return result.access_token;
40
- }
41
- async function authorize(command, state = randomHex(30)) {
42
- const AUTHORIZATION_URL = sellerAreaHostname();
43
- if (!await isPortAvailable(LS_PORT)) {
44
- command.output.warn(`Port ${LS_PORT} is unavailable, but it is required for authentication.`);
45
- const { confirmed } = await command.prompt({
46
- type: 'confirm',
47
- name: 'confirmed',
48
- initial: true,
49
- message: `Would you like to terminate ${await getPortProcessName(LS_PORT)}?`,
50
- });
51
- if (!confirmed) {
52
- throw new Error('Exiting..');
53
- }
54
- await killPortProcess(LS_PORT);
55
- }
56
- const params = {
57
- state,
58
- response_type: 'code',
59
- scope: '*',
60
- client_id: oauthClientId(),
61
- redirect_uri: `http://${LS_HOST}:${LS_PORT}/`,
62
- };
63
- await command.output.anykey('Press any key to open the login page on your browser..');
64
- const url = `http://${AUTHORIZATION_URL}/admin/oauth/authorize?${new URLSearchParams(params).toString()}`;
65
- open(url);
66
- const result = await listen(command, LS_HOST, LS_PORT, url);
67
- if (result.state !== state) {
68
- throw new Error('Authorization state mismatch..');
69
- }
70
- return result.code;
71
- }
72
- async function get() {
73
- return manager({ projectName: 'youcan-cli' })
74
- .get('store_session') ?? null;
75
- }
76
- async function authenticate(command) {
77
- const existingSession = manager({ projectName: 'youcan-cli' })
78
- .get('store_session');
79
- if (existingSession && await isSessionValid(existingSession)) {
80
- return existingSession;
81
- }
82
- const accessToken = await exchange(await authorize(command));
83
- const store = await get$1(`${apiHostname()}/me`, { headers: { Authorization: `Bearer ${accessToken}` } });
84
- const session = {
85
- slug: store.slug,
86
- id: store.id,
87
- access_token: accessToken,
88
- };
89
- manager({ projectName: 'youcan-cli' })
90
- .set('store_session', session);
91
- return session;
19
+ const LS_PORT = 3000;
20
+ const LS_HOST = 'localhost';
21
+ function generatePkcePair(length) {
22
+ const verifier = randomHex(length);
23
+ const encoder = new TextEncoder();
24
+ const data = encoder.encode(verifier);
25
+ const hash = sha256(data);
26
+ return [verifier, base64URLEncode(hash)];
27
+ }
28
+ async function isSessionValid(session) {
29
+ try {
30
+ const store = await get$1(`${apiHostname()}/me`, { headers: { Authorization: `Bearer ${session.access_token}` } });
31
+ return store.status === 1;
32
+ }
33
+ catch (err) {
34
+ return false;
35
+ }
36
+ }
37
+ async function exchange(code, verifier) {
38
+ const params = {
39
+ code,
40
+ client_id: oauthClientId(),
41
+ grant_type: 'authorization_code',
42
+ redirect_uri: `http://${LS_HOST}:${LS_PORT}/`,
43
+ code_verifier: verifier,
44
+ };
45
+ const result = await post(`${apiHostname()}/oauth/token`, {
46
+ body: new URLSearchParams(Object.entries(params)),
47
+ headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
48
+ });
49
+ return result.access_token;
50
+ }
51
+ async function authorize(command, state = randomHex(30)) {
52
+ const AUTHORIZATION_URL = sellerAreaHostname();
53
+ if (!await isPortAvailable(LS_PORT)) {
54
+ command.output.warn(`Port ${LS_PORT} is unavailable, but it is required for authentication.`);
55
+ const { confirmed } = await command.prompt({
56
+ type: 'confirm',
57
+ name: 'confirmed',
58
+ initial: true,
59
+ message: `Would you like to terminate ${await getPortProcessName(LS_PORT)}?`,
60
+ });
61
+ if (!confirmed) {
62
+ throw new Error('Exiting..');
63
+ }
64
+ await killPortProcess(LS_PORT);
65
+ }
66
+ const [verifier, challenge] = await generatePkcePair(64);
67
+ const params = {
68
+ state,
69
+ response_type: 'code',
70
+ scope: '*',
71
+ client_id: oauthClientId(),
72
+ redirect_uri: `http://${LS_HOST}:${LS_PORT}/`,
73
+ code_challenge: challenge,
74
+ code_challenge_method: 'S256',
75
+ };
76
+ await command.output.anykey('Press any key to open the login page on your browser..');
77
+ const url = `http://${AUTHORIZATION_URL}/admin/oauth/authorize?${new URLSearchParams(params).toString()}`;
78
+ open(url);
79
+ const result = await listen(command, LS_HOST, LS_PORT, url);
80
+ if (result.state !== state) {
81
+ throw new Error('Authorization state mismatch..');
82
+ }
83
+ return { code: result.code, verifier };
84
+ }
85
+ async function get() {
86
+ return manager({ projectName: 'youcan-cli' })
87
+ .get('store_session') ?? null;
88
+ }
89
+ async function authenticate(command) {
90
+ const existingSession = manager({ projectName: 'youcan-cli' })
91
+ .get('store_session');
92
+ if (existingSession && await isSessionValid(existingSession)) {
93
+ return existingSession;
94
+ }
95
+ const { code, verifier } = await authorize(command);
96
+ const accessToken = await exchange(code, verifier);
97
+ const store = await get$1(`${apiHostname()}/me`, { headers: { Authorization: `Bearer ${accessToken}` } });
98
+ const session = {
99
+ slug: store.slug,
100
+ id: store.id,
101
+ access_token: accessToken,
102
+ };
103
+ manager({ projectName: 'youcan-cli' })
104
+ .set('store_session', session);
105
+ return session;
92
106
  }
93
107
 
94
108
  export { authenticate, get };
@@ -1,21 +1,23 @@
1
- /// <reference types="node" />
2
- /// <reference types="node" />
3
- import type { Readable, Writable } from 'stream';
4
- export interface ExecOptions {
5
- cwd?: string;
6
- env?: {
7
- [key: string]: string | undefined;
8
- };
9
- stdin?: Readable | 'inherit';
10
- stdout?: Writable | 'inherit';
11
- stderr?: Writable | 'inherit';
12
- stdio?: 'inherit';
13
- input?: string;
14
- signal?: AbortSignal;
15
- errorHandler?: (error: unknown) => Promise<void>;
16
- }
17
- export declare function exec(command: string, args: string[], options?: ExecOptions): Promise<void>;
18
- export declare function isPortAvailable(port: number): Promise<boolean>;
19
- export declare function getPortProcessName(port: number): Promise<string>;
20
- export declare function killPortProcess(port: number): Promise<void>;
21
- export declare function open(url: string): Promise<void>;
1
+ /// <reference types="node" />
2
+ /// <reference types="node" />
3
+ import type { Readable, Writable } from 'stream';
4
+ export interface ExecOptions {
5
+ cwd?: string;
6
+ env?: {
7
+ [key: string]: string | undefined;
8
+ };
9
+ stdin?: Readable | 'inherit';
10
+ stdout?: Writable | 'inherit';
11
+ stderr?: Writable | 'inherit';
12
+ stdio?: 'inherit';
13
+ input?: string;
14
+ signal?: AbortSignal;
15
+ errorHandler?: (error: unknown) => Promise<void>;
16
+ }
17
+ export declare function exec(command: string, args: string[], options?: ExecOptions): Promise<void>;
18
+ export declare function isPortAvailable(port: number): Promise<boolean>;
19
+ export declare function getPortProcessName(port: number): Promise<string>;
20
+ export declare function killPortProcess(port: number): Promise<void>;
21
+ export declare function open(url: string): Promise<void>;
22
+ export type PackageManagerType = 'pnpm' | 'npm' | 'yarn';
23
+ export declare function inferUserPackageManager(): PackageManagerType;
@@ -2,63 +2,79 @@ import { execa } from 'execa';
2
2
  import tpu from 'tcp-port-used';
3
3
  import findProcess from 'find-process';
4
4
 
5
- function buildExec(command, args, options) {
6
- const env = options?.env ?? process.env;
7
- const commandProcess = execa(command, args, {
8
- env,
9
- cwd: options?.cwd,
10
- input: options?.input,
11
- stdio: options?.stdio,
12
- stdin: options?.stdin,
13
- stdout: options?.stdout === 'inherit' ? 'inherit' : undefined,
14
- stderr: options?.stderr === 'inherit' ? 'inherit' : undefined,
15
- windowsHide: false,
16
- });
17
- return commandProcess;
18
- }
19
- async function exec(command, args, options) {
20
- const commandProcess = buildExec(command, args, options);
21
- if (options?.stderr && options.stderr !== 'inherit') {
22
- commandProcess.stderr?.pipe(options.stderr, { end: false });
23
- }
24
- if (options?.stdout && options.stdout !== 'inherit') {
25
- commandProcess.stdout?.pipe(options.stdout, { end: false });
26
- }
27
- let aborted = false;
28
- options?.signal?.addEventListener('abort', () => {
29
- const pid = commandProcess.pid;
30
- if (pid) {
31
- aborted = true;
32
- }
33
- });
34
- try {
35
- await commandProcess;
36
- }
37
- catch (err) {
38
- if (aborted) {
39
- return;
40
- }
41
- if (options?.errorHandler) {
42
- await options?.errorHandler(err);
43
- return;
44
- }
45
- throw err;
46
- }
47
- }
48
- async function isPortAvailable(port) {
49
- return !await tpu.check(port);
50
- }
51
- async function getPortProcessName(port) {
52
- const info = await findProcess('port', port);
53
- return (info && info.length > 0) ? `(${info[0]?.name})` : '';
54
- }
55
- async function killPortProcess(port) {
56
- const { killPortProcess: kill } = await import('kill-port-process');
57
- await kill(port);
58
- }
59
- async function open(url) {
60
- const _open = await import('open');
61
- await _open.default(url);
5
+ function buildExec(command, args, options) {
6
+ const env = options?.env ?? process.env;
7
+ const commandProcess = execa(command, args, {
8
+ env,
9
+ cwd: options?.cwd,
10
+ input: options?.input,
11
+ stdio: options?.stdio,
12
+ stdin: options?.stdin,
13
+ signal: options?.signal,
14
+ stdout: options?.stdout === 'inherit' ? 'inherit' : undefined,
15
+ stderr: options?.stderr === 'inherit' ? 'inherit' : undefined,
16
+ windowsHide: false,
17
+ });
18
+ return commandProcess;
19
+ }
20
+ async function exec(command, args, options) {
21
+ const commandProcess = buildExec(command, args, options);
22
+ if (options?.stderr && options.stderr !== 'inherit') {
23
+ commandProcess.stderr?.pipe(options.stderr, { end: false });
24
+ }
25
+ if (options?.stdout && options.stdout !== 'inherit') {
26
+ commandProcess.stdout?.pipe(options.stdout, { end: false });
27
+ }
28
+ let aborted = false;
29
+ options?.signal?.addEventListener('abort', () => {
30
+ const pid = commandProcess.pid;
31
+ if (pid) {
32
+ aborted = true;
33
+ }
34
+ });
35
+ try {
36
+ await commandProcess;
37
+ }
38
+ catch (err) {
39
+ if (aborted) {
40
+ return;
41
+ }
42
+ if (options?.errorHandler) {
43
+ await options?.errorHandler(err);
44
+ return;
45
+ }
46
+ throw err;
47
+ }
48
+ }
49
+ async function isPortAvailable(port) {
50
+ return !await tpu.check(port);
51
+ }
52
+ async function getPortProcessName(port) {
53
+ const info = await findProcess('port', port);
54
+ return (info && info.length > 0) ? `(${info[0]?.name})` : '';
55
+ }
56
+ async function killPortProcess(port) {
57
+ const { killPortProcess: kill } = await import('kill-port-process');
58
+ await kill(port);
59
+ }
60
+ async function open(url) {
61
+ const _open = await import('open');
62
+ await _open.default(url);
63
+ }
64
+ function inferUserPackageManager() {
65
+ const defaultPackageManager = 'npm';
66
+ const packageManagersMap = {
67
+ '^npm/.*': 'npm',
68
+ '^pnpm/.*': 'pnpm',
69
+ '^yarn/.*': 'yarn',
70
+ };
71
+ const packageManagerUserAgent = process.env.npm_config_user_agent;
72
+ for (const key in packageManagersMap) {
73
+ if (new RegExp(key).test(packageManagerUserAgent)) {
74
+ return packageManagersMap[key];
75
+ }
76
+ }
77
+ return defaultPackageManager;
62
78
  }
63
79
 
64
- export { exec, getPortProcessName, isPortAvailable, killPortProcess, open };
80
+ export { exec, getPortProcessName, inferUserPackageManager, isPortAvailable, killPortProcess, open };
@@ -1,7 +1,8 @@
1
- export interface Task<T = unknown> {
2
- title: string;
3
- errors?: Error[];
4
- skip?: (ctx: T) => boolean;
5
- task: (context: T, task: Task<T>) => Promise<void | Task<T>[]>;
6
- }
7
- export declare function run<T = unknown>(ctx: T, tasks: Task<T>[]): Promise<T>;
1
+ export interface Task<T = unknown> {
2
+ title: string;
3
+ errors?: Error[];
4
+ skip?: (ctx: T) => boolean;
5
+ task: (context: T, task: Task<T>) => Promise<void | Task<T>[]>;
6
+ loadable?: false;
7
+ }
8
+ export declare function run<T = unknown>(ctx: T, tasks: Task<T>[]): Promise<T>;
@@ -1,31 +1,39 @@
1
1
  import { exit } from 'node:process';
2
2
  import { Loader } from '../internal/node/ui.js';
3
3
 
4
- async function runTask(task, ctx) {
5
- if (task.skip?.(ctx)) {
6
- return;
7
- }
8
- return await task.task(ctx, task);
9
- }
10
- async function run(ctx, tasks) {
11
- for await (const task of tasks) {
12
- await Loader.exec(task.title, async (loader) => {
13
- try {
14
- const subtasks = await runTask(task, ctx);
15
- if (Array.isArray(subtasks) && subtasks.length > 0 && subtasks.every(t => 'task' in t)) {
16
- for await (const subtask of subtasks) {
17
- await runTask(subtask, ctx);
18
- }
19
- }
20
- loader.stop();
21
- }
22
- catch (err) {
23
- loader.error(String(err));
24
- exit(1);
25
- }
26
- });
27
- }
28
- return ctx;
4
+ async function runTask(task, ctx) {
5
+ if (task.skip?.(ctx)) {
6
+ return;
7
+ }
8
+ return await task.task(ctx, task);
9
+ }
10
+ async function run(ctx, tasks) {
11
+ for await (const task of tasks) {
12
+ const runner = async () => {
13
+ const subtasks = await runTask(task, ctx);
14
+ if (Array.isArray(subtasks) && subtasks.length > 0 && subtasks.every(t => 'task' in t)) {
15
+ for await (const subtask of subtasks) {
16
+ await runTask(subtask, ctx);
17
+ }
18
+ }
19
+ };
20
+ if (task.loadable === false) {
21
+ process.stdout.write(`${task.title}\n`);
22
+ await runner();
23
+ return ctx;
24
+ }
25
+ await Loader.exec(task.title, async (loader) => {
26
+ try {
27
+ await runner();
28
+ loader.stop();
29
+ }
30
+ catch (err) {
31
+ loader.error(String(err));
32
+ exit(1);
33
+ }
34
+ });
35
+ }
36
+ return ctx;
29
37
  }
30
38
 
31
39
  export { run };