@shopify/cli-kit 3.44.1 → 3.45.0-pre.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +15 -7
- package/assets/cli-ruby/lib/project_types/extension/messages/messages.rb +0 -2
- package/assets/cli-ruby/lib/project_types/theme/commands/pull.rb +6 -0
- package/assets/cli-ruby/lib/project_types/theme/commands/push.rb +6 -0
- package/assets/cli-ruby/lib/shopify_cli/constants.rb +1 -0
- package/assets/cli-ruby/lib/shopify_cli/environment.rb +4 -0
- package/assets/cli-ruby/lib/shopify_cli/theme/dev_server/hot_reload.rb +1 -1
- package/assets/cli-ruby/lib/shopify_cli/theme/dev_server/proxy.rb +3 -0
- package/assets/cli-ruby/lib/shopify_cli/theme/extension/dev_server.rb +3 -3
- package/assets/cli-ruby/lib/shopify_cli/theme/extension/host_theme.rb +4 -4
- package/assets/cli-ruby/lib/shopify_cli/theme/extension/syncer/extension_serve_job.rb +4 -6
- package/assets/cli-ruby/lib/shopify_cli/theme/extension/ui/host_theme_raw_progress_bar.rb +40 -0
- package/assets/cli-ruby/vendor/deps/cli-ui/lib/cli/ui/progress_plain.rb +50 -0
- package/assets/cli-ruby/vendor/deps/cli-ui/lib/cli/ui.rb +15 -14
- package/dist/private/node/api/graphql.js +12 -23
- package/dist/private/node/api/graphql.js.map +1 -1
- package/dist/private/node/api.d.ts +1 -1
- package/dist/private/node/api.js +28 -9
- package/dist/private/node/api.js.map +1 -1
- package/dist/private/node/constants.d.ts +0 -1
- package/dist/private/node/constants.js +0 -1
- package/dist/private/node/constants.js.map +1 -1
- package/dist/private/node/session/redirect-listener.js +2 -3
- package/dist/private/node/session/redirect-listener.js.map +1 -1
- package/dist/private/node/session/schema.d.ts +28 -28
- package/dist/private/node/session/schema.js +12 -12
- package/dist/private/node/session/schema.js.map +1 -1
- package/dist/private/node/testing/ui.d.ts +11 -0
- package/dist/private/node/testing/ui.js +15 -1
- package/dist/private/node/testing/ui.js.map +1 -1
- package/dist/private/node/ui/alert.d.ts +5 -1
- package/dist/private/node/ui/alert.js +2 -2
- package/dist/private/node/ui/alert.js.map +1 -1
- package/dist/private/node/ui/components/AutocompletePrompt.js +38 -12
- package/dist/private/node/ui/components/AutocompletePrompt.js.map +1 -1
- package/dist/private/node/ui/components/AutocompletePrompt.test.js +56 -36
- package/dist/private/node/ui/components/AutocompletePrompt.test.js.map +1 -1
- package/dist/private/node/ui/components/ConcurrentOutput.d.ts +5 -1
- package/dist/private/node/ui/components/ConcurrentOutput.js +15 -13
- package/dist/private/node/ui/components/ConcurrentOutput.js.map +1 -1
- package/dist/private/node/ui/components/ConcurrentOutput.test.js +20 -11
- package/dist/private/node/ui/components/ConcurrentOutput.test.js.map +1 -1
- package/dist/private/node/ui/components/FullScreen.js +1 -1
- package/dist/private/node/ui/components/FullScreen.js.map +1 -1
- package/dist/private/node/ui/components/SelectInput.d.ts +7 -2
- package/dist/private/node/ui/components/SelectInput.js +73 -60
- package/dist/private/node/ui/components/SelectInput.js.map +1 -1
- package/dist/private/node/ui/components/SelectInput.test.js +72 -2
- package/dist/private/node/ui/components/SelectInput.test.js.map +1 -1
- package/dist/private/node/ui/components/SelectPrompt.js +38 -12
- package/dist/private/node/ui/components/SelectPrompt.js.map +1 -1
- package/dist/private/node/ui/components/SelectPrompt.test.js +52 -1
- package/dist/private/node/ui/components/SelectPrompt.test.js.map +1 -1
- package/dist/private/node/ui/components/Tasks.js +9 -1
- package/dist/private/node/ui/components/Tasks.js.map +1 -1
- package/dist/private/node/ui/components/TextAnimation.js +4 -4
- package/dist/private/node/ui/components/TextAnimation.js.map +1 -1
- package/dist/private/node/ui/components/TextPrompt.js +4 -4
- package/dist/private/node/ui/components/TextPrompt.js.map +1 -1
- package/dist/private/node/ui/hooks/use-layout.d.ts +6 -0
- package/dist/private/node/ui/hooks/use-layout.js +31 -12
- package/dist/private/node/ui/hooks/use-layout.js.map +1 -1
- package/dist/private/node/ui.d.ts +11 -3
- package/dist/private/node/ui.js +14 -10
- package/dist/private/node/ui.js.map +1 -1
- package/dist/public/common/object.d.ts +1 -1
- package/dist/public/common/object.js +1 -1
- package/dist/public/common/object.js.map +1 -1
- package/dist/public/common/version.d.ts +1 -1
- package/dist/public/common/version.js +1 -1
- package/dist/public/common/version.js.map +1 -1
- package/dist/public/node/base-command.d.ts +1 -4
- package/dist/public/node/base-command.js +17 -19
- package/dist/public/node/base-command.js.map +1 -1
- package/dist/public/node/context/local.d.ts +0 -7
- package/dist/public/node/context/local.js +0 -9
- package/dist/public/node/context/local.js.map +1 -1
- package/dist/public/node/environments.d.ts +7 -8
- package/dist/public/node/environments.js +23 -25
- package/dist/public/node/environments.js.map +1 -1
- package/dist/public/node/http.d.ts +0 -1
- package/dist/public/node/http.js +0 -1
- package/dist/public/node/http.js.map +1 -1
- package/dist/public/node/node-package-manager.d.ts +1 -1
- package/dist/public/node/node-package-manager.js.map +1 -1
- package/dist/public/node/output.d.ts +1 -1
- package/dist/public/node/output.js.map +1 -1
- package/dist/public/node/path.js +1 -1
- package/dist/public/node/path.js.map +1 -1
- package/dist/public/node/ruby.d.ts +1 -0
- package/dist/public/node/ruby.js +35 -15
- package/dist/public/node/ruby.js.map +1 -1
- package/dist/public/node/schema.d.ts +1 -1
- package/dist/public/node/schema.js +1 -1
- package/dist/public/node/schema.js.map +1 -1
- package/dist/public/node/themes/theme-manager.d.ts +1 -1
- package/dist/public/node/themes/theme-manager.js.map +1 -1
- package/dist/public/node/ui.d.ts +182 -121
- package/dist/public/node/ui.js +172 -120
- package/dist/public/node/ui.js.map +1 -1
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/package.json +20 -19
- package/dist/private/node/ui/components/TextWithBackground.d.ts +0 -12
- package/dist/private/node/ui/components/TextWithBackground.js +0 -39
- package/dist/private/node/ui/components/TextWithBackground.js.map +0 -1
|
@@ -1,13 +1,13 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { zod } from '../../../public/node/schema.js';
|
|
2
2
|
/**
|
|
3
3
|
* The schema represents an Identity token.
|
|
4
4
|
*/
|
|
5
|
-
declare const IdentityTokenSchema:
|
|
6
|
-
accessToken:
|
|
7
|
-
refreshToken:
|
|
8
|
-
expiresAt:
|
|
9
|
-
scopes:
|
|
10
|
-
}, "strip",
|
|
5
|
+
declare const IdentityTokenSchema: zod.ZodObject<{
|
|
6
|
+
accessToken: zod.ZodString;
|
|
7
|
+
refreshToken: zod.ZodString;
|
|
8
|
+
expiresAt: zod.ZodEffects<zod.ZodDate, Date, Date>;
|
|
9
|
+
scopes: zod.ZodArray<zod.ZodString, "many">;
|
|
10
|
+
}, "strip", zod.ZodTypeAny, {
|
|
11
11
|
accessToken: string;
|
|
12
12
|
refreshToken: string;
|
|
13
13
|
expiresAt: Date;
|
|
@@ -21,11 +21,11 @@ declare const IdentityTokenSchema: schema.ZodObject<{
|
|
|
21
21
|
/**
|
|
22
22
|
* The schema represents an application token.
|
|
23
23
|
*/
|
|
24
|
-
declare const ApplicationTokenSchema:
|
|
25
|
-
accessToken:
|
|
26
|
-
expiresAt:
|
|
27
|
-
scopes:
|
|
28
|
-
}, "strip",
|
|
24
|
+
declare const ApplicationTokenSchema: zod.ZodObject<{
|
|
25
|
+
accessToken: zod.ZodString;
|
|
26
|
+
expiresAt: zod.ZodEffects<zod.ZodDate, Date, Date>;
|
|
27
|
+
scopes: zod.ZodArray<zod.ZodString, "many">;
|
|
28
|
+
}, "strip", zod.ZodTypeAny, {
|
|
29
29
|
accessToken: string;
|
|
30
30
|
expiresAt: Date;
|
|
31
31
|
scopes: string[];
|
|
@@ -57,18 +57,18 @@ declare const ApplicationTokenSchema: schema.ZodObject<{
|
|
|
57
57
|
* }
|
|
58
58
|
* ```
|
|
59
59
|
*/
|
|
60
|
-
export declare const SessionSchema:
|
|
60
|
+
export declare const SessionSchema: zod.ZodObject<{}, "strip", zod.ZodObject<{
|
|
61
61
|
/**
|
|
62
62
|
* It contains the identity token. Before usint it, we exchange it
|
|
63
63
|
* to get a token that we can use with different applications. The exchanged
|
|
64
64
|
* tokens for the applications are stored under applications.
|
|
65
65
|
*/
|
|
66
|
-
identity:
|
|
67
|
-
accessToken:
|
|
68
|
-
refreshToken:
|
|
69
|
-
expiresAt:
|
|
70
|
-
scopes:
|
|
71
|
-
}, "strip",
|
|
66
|
+
identity: zod.ZodObject<{
|
|
67
|
+
accessToken: zod.ZodString;
|
|
68
|
+
refreshToken: zod.ZodString;
|
|
69
|
+
expiresAt: zod.ZodEffects<zod.ZodDate, Date, Date>;
|
|
70
|
+
scopes: zod.ZodArray<zod.ZodString, "many">;
|
|
71
|
+
}, "strip", zod.ZodTypeAny, {
|
|
72
72
|
accessToken: string;
|
|
73
73
|
refreshToken: string;
|
|
74
74
|
expiresAt: Date;
|
|
@@ -83,11 +83,11 @@ export declare const SessionSchema: schema.ZodObject<{}, "strip", schema.ZodObje
|
|
|
83
83
|
* It contains exchanged tokens for the applications the CLI
|
|
84
84
|
* authenticates with. Tokens are scoped under the fqdn of the applications.
|
|
85
85
|
*/
|
|
86
|
-
applications:
|
|
87
|
-
accessToken:
|
|
88
|
-
expiresAt:
|
|
89
|
-
scopes:
|
|
90
|
-
}, "strip",
|
|
86
|
+
applications: zod.ZodObject<{}, "strip", zod.ZodObject<{
|
|
87
|
+
accessToken: zod.ZodString;
|
|
88
|
+
expiresAt: zod.ZodEffects<zod.ZodDate, Date, Date>;
|
|
89
|
+
scopes: zod.ZodArray<zod.ZodString, "many">;
|
|
90
|
+
}, "strip", zod.ZodTypeAny, {
|
|
91
91
|
accessToken: string;
|
|
92
92
|
expiresAt: Date;
|
|
93
93
|
scopes: string[];
|
|
@@ -108,7 +108,7 @@ export declare const SessionSchema: schema.ZodObject<{}, "strip", schema.ZodObje
|
|
|
108
108
|
scopes: string[];
|
|
109
109
|
};
|
|
110
110
|
}>;
|
|
111
|
-
}, "strip",
|
|
111
|
+
}, "strip", zod.ZodTypeAny, {
|
|
112
112
|
identity: {
|
|
113
113
|
accessToken: string;
|
|
114
114
|
refreshToken: string;
|
|
@@ -169,7 +169,7 @@ export declare const SessionSchema: schema.ZodObject<{}, "strip", schema.ZodObje
|
|
|
169
169
|
};
|
|
170
170
|
};
|
|
171
171
|
}>;
|
|
172
|
-
export type Session =
|
|
173
|
-
export type IdentityToken =
|
|
174
|
-
export type ApplicationToken =
|
|
172
|
+
export type Session = zod.infer<typeof SessionSchema>;
|
|
173
|
+
export type IdentityToken = zod.infer<typeof IdentityTokenSchema>;
|
|
174
|
+
export type ApplicationToken = zod.infer<typeof ApplicationTokenSchema>;
|
|
175
175
|
export {};
|
|
@@ -1,25 +1,25 @@
|
|
|
1
|
-
import {
|
|
2
|
-
const DateSchema =
|
|
1
|
+
import { zod } from '../../../public/node/schema.js';
|
|
2
|
+
const DateSchema = zod.preprocess((arg) => {
|
|
3
3
|
if (typeof arg === 'string' || arg instanceof Date)
|
|
4
4
|
return new Date(arg);
|
|
5
5
|
return null;
|
|
6
|
-
},
|
|
6
|
+
}, zod.date());
|
|
7
7
|
/**
|
|
8
8
|
* The schema represents an Identity token.
|
|
9
9
|
*/
|
|
10
|
-
const IdentityTokenSchema =
|
|
11
|
-
accessToken:
|
|
12
|
-
refreshToken:
|
|
10
|
+
const IdentityTokenSchema = zod.object({
|
|
11
|
+
accessToken: zod.string(),
|
|
12
|
+
refreshToken: zod.string(),
|
|
13
13
|
expiresAt: DateSchema,
|
|
14
|
-
scopes:
|
|
14
|
+
scopes: zod.array(zod.string()),
|
|
15
15
|
});
|
|
16
16
|
/**
|
|
17
17
|
* The schema represents an application token.
|
|
18
18
|
*/
|
|
19
|
-
const ApplicationTokenSchema =
|
|
20
|
-
accessToken:
|
|
19
|
+
const ApplicationTokenSchema = zod.object({
|
|
20
|
+
accessToken: zod.string(),
|
|
21
21
|
expiresAt: DateSchema,
|
|
22
|
-
scopes:
|
|
22
|
+
scopes: zod.array(zod.string()),
|
|
23
23
|
});
|
|
24
24
|
/**
|
|
25
25
|
* This schema represents the format of the session
|
|
@@ -44,7 +44,7 @@ const ApplicationTokenSchema = schema.object({
|
|
|
44
44
|
* }
|
|
45
45
|
* ```
|
|
46
46
|
*/
|
|
47
|
-
export const SessionSchema =
|
|
47
|
+
export const SessionSchema = zod.object({}).catchall(zod.object({
|
|
48
48
|
/**
|
|
49
49
|
* It contains the identity token. Before usint it, we exchange it
|
|
50
50
|
* to get a token that we can use with different applications. The exchanged
|
|
@@ -55,6 +55,6 @@ export const SessionSchema = schema.object({}).catchall(schema.object({
|
|
|
55
55
|
* It contains exchanged tokens for the applications the CLI
|
|
56
56
|
* authenticates with. Tokens are scoped under the fqdn of the applications.
|
|
57
57
|
*/
|
|
58
|
-
applications:
|
|
58
|
+
applications: zod.object({}).catchall(ApplicationTokenSchema),
|
|
59
59
|
}));
|
|
60
60
|
//# sourceMappingURL=schema.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"schema.js","sourceRoot":"","sources":["../../../../src/private/node/session/schema.ts"],"names":[],"mappings":"AAAA,OAAO,EAAC,
|
|
1
|
+
{"version":3,"file":"schema.js","sourceRoot":"","sources":["../../../../src/private/node/session/schema.ts"],"names":[],"mappings":"AAAA,OAAO,EAAC,GAAG,EAAC,MAAM,gCAAgC,CAAA;AAElD,MAAM,UAAU,GAAG,GAAG,CAAC,UAAU,CAAC,CAAC,GAAG,EAAE,EAAE;IACxC,IAAI,OAAO,GAAG,KAAK,QAAQ,IAAI,GAAG,YAAY,IAAI;QAAE,OAAO,IAAI,IAAI,CAAC,GAAG,CAAC,CAAA;IACxE,OAAO,IAAI,CAAA;AACb,CAAC,EAAE,GAAG,CAAC,IAAI,EAAE,CAAC,CAAA;AAEd;;GAEG;AACH,MAAM,mBAAmB,GAAG,GAAG,CAAC,MAAM,CAAC;IACrC,WAAW,EAAE,GAAG,CAAC,MAAM,EAAE;IACzB,YAAY,EAAE,GAAG,CAAC,MAAM,EAAE;IAC1B,SAAS,EAAE,UAAU;IACrB,MAAM,EAAE,GAAG,CAAC,KAAK,CAAC,GAAG,CAAC,MAAM,EAAE,CAAC;CAChC,CAAC,CAAA;AAEF;;GAEG;AACH,MAAM,sBAAsB,GAAG,GAAG,CAAC,MAAM,CAAC;IACxC,WAAW,EAAE,GAAG,CAAC,MAAM,EAAE;IACzB,SAAS,EAAE,UAAU;IACrB,MAAM,EAAE,GAAG,CAAC,KAAK,CAAC,GAAG,CAAC,MAAM,EAAE,CAAC;CAChC,CAAC,CAAA;AAEF;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,MAAM,CAAC,MAAM,aAAa,GAAG,GAAG,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC,QAAQ,CAClD,GAAG,CAAC,MAAM,CAAC;IACT;;;;OAIG;IACH,QAAQ,EAAE,mBAAmB;IAC7B;;;OAGG;IACH,YAAY,EAAE,GAAG,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC,QAAQ,CAAC,sBAAsB,CAAC;CAC9D,CAAC,CACH,CAAA","sourcesContent":["import {zod} from '../../../public/node/schema.js'\n\nconst DateSchema = zod.preprocess((arg) => {\n if (typeof arg === 'string' || arg instanceof Date) return new Date(arg)\n return null\n}, zod.date())\n\n/**\n * The schema represents an Identity token.\n */\nconst IdentityTokenSchema = zod.object({\n accessToken: zod.string(),\n refreshToken: zod.string(),\n expiresAt: DateSchema,\n scopes: zod.array(zod.string()),\n})\n\n/**\n * The schema represents an application token.\n */\nconst ApplicationTokenSchema = zod.object({\n accessToken: zod.string(),\n expiresAt: DateSchema,\n scopes: zod.array(zod.string()),\n})\n\n/**\n * This schema represents the format of the session\n * that we cache in the system to avoid unnecessary\n * token exchanges.\n *\n * @example\n * ```\n * {\n * \"accounts.shopify.com\": {\n * \"identity\": {...} // IdentityTokenSchema\n * \"applications\": {\n * \"${domain}-application-id\": { // Admin APIs includes domain in the key\n * \"accessToken\": \"...\",\n * },\n * \"$application-id\": { // ApplicationTokenSchema\n * \"accessToken\": \"...\",\n * },\n * }\n * },\n * \"identity.spin.com\": {...}\n * }\n * ```\n */\nexport const SessionSchema = zod.object({}).catchall(\n zod.object({\n /**\n * It contains the identity token. Before usint it, we exchange it\n * to get a token that we can use with different applications. The exchanged\n * tokens for the applications are stored under applications.\n */\n identity: IdentityTokenSchema,\n /**\n * It contains exchanged tokens for the applications the CLI\n * authenticates with. Tokens are scoped under the fqdn of the applications.\n */\n applications: zod.object({}).catchall(ApplicationTokenSchema),\n }),\n)\n\nexport type Session = zod.infer<typeof SessionSchema>\nexport type IdentityToken = zod.infer<typeof IdentityTokenSchema>\nexport type ApplicationToken = zod.infer<typeof ApplicationTokenSchema>\n"]}
|
|
@@ -1,4 +1,14 @@
|
|
|
1
|
+
/// <reference types="node" resolution-mode="require"/>
|
|
1
2
|
import { render } from 'ink-testing-library';
|
|
3
|
+
import { EventEmitter } from 'events';
|
|
4
|
+
export declare class Stdin extends EventEmitter {
|
|
5
|
+
isTTY: boolean;
|
|
6
|
+
write: (data: string) => void;
|
|
7
|
+
setEncoding(): void;
|
|
8
|
+
setRawMode(): void;
|
|
9
|
+
resume(): void;
|
|
10
|
+
pause(): void;
|
|
11
|
+
}
|
|
2
12
|
/**
|
|
3
13
|
* Wait for the component to be ready to accept input.
|
|
4
14
|
*/
|
|
@@ -7,6 +17,7 @@ export declare function waitForInputsToBeReady(): Promise<unknown>;
|
|
|
7
17
|
* Wait for the last frame to change to anything.
|
|
8
18
|
*/
|
|
9
19
|
export declare function waitForChange(func: () => void, getChangingValue: () => string | number | undefined): Promise<void>;
|
|
20
|
+
export declare function waitFor(func: () => void, condition: () => boolean): Promise<void>;
|
|
10
21
|
/**
|
|
11
22
|
* Wait for the last frame to contain specific text.
|
|
12
23
|
*/
|
|
@@ -1,4 +1,18 @@
|
|
|
1
1
|
import { isTruthy } from '../context/utilities.js';
|
|
2
|
+
import { EventEmitter } from 'events';
|
|
3
|
+
export class Stdin extends EventEmitter {
|
|
4
|
+
constructor() {
|
|
5
|
+
super(...arguments);
|
|
6
|
+
this.isTTY = true;
|
|
7
|
+
this.write = (data) => {
|
|
8
|
+
this.emit('data', data);
|
|
9
|
+
};
|
|
10
|
+
}
|
|
11
|
+
setEncoding() { }
|
|
12
|
+
setRawMode() { }
|
|
13
|
+
resume() { }
|
|
14
|
+
pause() { }
|
|
15
|
+
}
|
|
2
16
|
/**
|
|
3
17
|
* Wait for the component to be ready to accept input.
|
|
4
18
|
*/
|
|
@@ -20,7 +34,7 @@ export function waitForChange(func, getChangingValue) {
|
|
|
20
34
|
}, 10);
|
|
21
35
|
});
|
|
22
36
|
}
|
|
23
|
-
function waitFor(func, condition) {
|
|
37
|
+
export function waitFor(func, condition) {
|
|
24
38
|
return new Promise((resolve) => {
|
|
25
39
|
func();
|
|
26
40
|
const interval = setInterval(() => {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ui.js","sourceRoot":"","sources":["../../../../src/private/node/testing/ui.ts"],"names":[],"mappings":"AAAA,OAAO,EAAC,QAAQ,EAAC,MAAM,yBAAyB,CAAA;
|
|
1
|
+
{"version":3,"file":"ui.js","sourceRoot":"","sources":["../../../../src/private/node/testing/ui.ts"],"names":[],"mappings":"AAAA,OAAO,EAAC,QAAQ,EAAC,MAAM,yBAAyB,CAAA;AAEhD,OAAO,EAAC,YAAY,EAAC,MAAM,QAAQ,CAAA;AAEnC,MAAM,OAAO,KAAM,SAAQ,YAAY;IAAvC;;QACE,UAAK,GAAG,IAAI,CAAA;QAEZ,UAAK,GAAG,CAAC,IAAY,EAAE,EAAE;YACvB,IAAI,CAAC,IAAI,CAAC,MAAM,EAAE,IAAI,CAAC,CAAA;QACzB,CAAC,CAAA;IASH,CAAC;IAPC,WAAW,KAAI,CAAC;IAEhB,UAAU,KAAI,CAAC;IAEf,MAAM,KAAI,CAAC;IAEX,KAAK,KAAI,CAAC;CACX;AAED;;GAEG;AACH,MAAM,UAAU,sBAAsB;IACpC,OAAO,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,UAAU,CAAC,OAAO,EAAE,GAAG,CAAC,CAAC,CAAA;AAC3D,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,aAAa,CAAC,IAAgB,EAAE,gBAAmD;IACjG,OAAO,IAAI,OAAO,CAAO,CAAC,OAAO,EAAE,EAAE;QACnC,MAAM,YAAY,GAAG,gBAAgB,EAAE,CAAA;QAEvC,IAAI,EAAE,CAAA;QAEN,MAAM,QAAQ,GAAG,WAAW,CAAC,GAAG,EAAE;YAChC,IAAI,gBAAgB,EAAE,KAAK,YAAY,EAAE;gBACvC,aAAa,CAAC,QAAQ,CAAC,CAAA;gBACvB,OAAO,EAAE,CAAA;aACV;QACH,CAAC,EAAE,EAAE,CAAC,CAAA;IACR,CAAC,CAAC,CAAA;AACJ,CAAC;AAED,MAAM,UAAU,OAAO,CAAC,IAAgB,EAAE,SAAwB;IAChE,OAAO,IAAI,OAAO,CAAO,CAAC,OAAO,EAAE,EAAE;QACnC,IAAI,EAAE,CAAA;QAEN,MAAM,QAAQ,GAAG,WAAW,CAAC,GAAG,EAAE;YAChC,IAAI,SAAS,EAAE,EAAE;gBACf,aAAa,CAAC,QAAQ,CAAC,CAAA;gBACvB,OAAO,EAAE,CAAA;aACV;QACH,CAAC,EAAE,EAAE,CAAC,CAAA;IACR,CAAC,CAAC,CAAA;AACJ,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,cAAc,CAC5B,cAAyC,EACzC,OAAe,EACf,OAAmB,GAAG,EAAE,GAAE,CAAC;IAE3B,OAAO,OAAO,CACZ,GAAG,EAAE,CAAC,IAAI,EAAE,EACZ,GAAG,EAAE,CAAC,cAAc,CAAC,SAAS,EAAG,CAAC,QAAQ,CAAC,OAAO,CAAC,CACpD,CAAA;AACH,CAAC;AAED;;;;;GAKG;AACH,MAAM,CAAC,KAAK,UAAU,yBAAyB,CAAC,cAAyC,EAAE,GAAG,MAAgB;IAC5G,MAAM,aAAa,CAAC,GAAG,EAAE,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,cAAc,CAAC,KAAK,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,EAAE,cAAc,CAAC,SAAS,CAAC,CAAA;IACjH,kEAAkE;IAClE,MAAM,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,UAAU,CAAC,OAAO,EAAE,CAAC,CAAC,CAAC,CAAA;AACxD,CAAC;AAED;;;;;GAKG;AACH,MAAM,CAAC,KAAK,UAAU,gBAAgB,CACpC,cAAyC,EACzC,QAAgB,EAChB,GAAG,MAAgB;IAEnB,MAAM,CAAC,OAAO,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,cAAc,CAAC,KAAK,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,CAAA;IAC5D,MAAM,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,UAAU,CAAC,OAAO,EAAE,QAAQ,CAAC,CAAC,CAAA;AAC/D,CAAC;AAED;;;;;GAKG;AACH,MAAM,CAAC,KAAK,UAAU,0BAA0B,CAC9C,cAAyC,EACzC,OAAe,EACf,GAAG,MAAgB;IAEnB,MAAM,cAAc,CAAC,cAAc,EAAE,OAAO,EAAE,GAAG,EAAE,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,cAAc,CAAC,KAAK,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,CAAC,CAAA;AACnH,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,wBAAwB,CAAC,cAAyC;IAChF,OAAO,QAAQ,CAAC,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,cAAc,CAAC,MAAM,CAAC,cAAc,CAAC,MAAM,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,cAAc,CAAC,SAAS,EAAE,CAAA;AACxH,CAAC","sourcesContent":["import {isTruthy} from '../context/utilities.js'\nimport {render} from 'ink-testing-library'\nimport {EventEmitter} from 'events'\n\nexport class Stdin extends EventEmitter {\n isTTY = true\n\n write = (data: string) => {\n this.emit('data', data)\n }\n\n setEncoding() {}\n\n setRawMode() {}\n\n resume() {}\n\n pause() {}\n}\n\n/**\n * Wait for the component to be ready to accept input.\n */\nexport function waitForInputsToBeReady() {\n return new Promise((resolve) => setTimeout(resolve, 100))\n}\n\n/**\n * Wait for the last frame to change to anything.\n */\nexport function waitForChange(func: () => void, getChangingValue: () => string | number | undefined) {\n return new Promise<void>((resolve) => {\n const initialValue = getChangingValue()\n\n func()\n\n const interval = setInterval(() => {\n if (getChangingValue() !== initialValue) {\n clearInterval(interval)\n resolve()\n }\n }, 10)\n })\n}\n\nexport function waitFor(func: () => void, condition: () => boolean) {\n return new Promise<void>((resolve) => {\n func()\n\n const interval = setInterval(() => {\n if (condition()) {\n clearInterval(interval)\n resolve()\n }\n }, 10)\n })\n}\n\n/**\n * Wait for the last frame to contain specific text.\n */\nexport function waitForContent(\n renderInstance: ReturnType<typeof render>,\n content: string,\n func: () => void = () => {},\n) {\n return waitFor(\n () => func(),\n () => renderInstance.lastFrame()!.includes(content),\n )\n}\n\n/**\n * Send input and wait for the last frame to change.\n *\n * Useful when you want to send some input and wait for anything to change in the interface.\n * If you need to wait for a specific change instead, you can use sendInputAndWaitForContent.\n */\nexport async function sendInputAndWaitForChange(renderInstance: ReturnType<typeof render>, ...inputs: string[]) {\n await waitForChange(() => inputs.forEach((input) => renderInstance.stdin.write(input)), renderInstance.lastFrame)\n // wait for another tick so we give time to react to update caches\n await new Promise((resolve) => setTimeout(resolve, 0))\n}\n\n/** Send input and wait a number of milliseconds.\n *\n * Useful if you know some what will happen after input will take a certain amount of time\n * and it will not cause any visible change so you can't use sendInputAndWaitForChange.\n * This function can also be used if you want to test that nothing changes after some input has been sent.\n */\nexport async function sendInputAndWait(\n renderInstance: ReturnType<typeof render>,\n waitTime: number,\n ...inputs: string[]\n) {\n inputs.forEach((input) => renderInstance.stdin.write(input))\n await new Promise((resolve) => setTimeout(resolve, waitTime))\n}\n\n/**\n * Send input and wait for the last frame to contain specific text.\n *\n * Useful when you want to send some input and wait for a specific change to happen.\n * If you need to wait for any change instead, you can use sendInputAndWaitForChange.\n */\nexport async function sendInputAndWaitForContent(\n renderInstance: ReturnType<typeof render>,\n content: string,\n ...inputs: string[]\n) {\n await waitForContent(renderInstance, content, () => inputs.forEach((input) => renderInstance.stdin.write(input)))\n}\n\n/** Function that is useful when you want to check the last frame of a component that unmounted.\n *\n * The reason this function exists is that in CI Ink will clear the last frame on unmount.\n */\nexport function getLastFrameAfterUnmount(renderInstance: ReturnType<typeof render>) {\n return isTruthy(process.env.CI) ? renderInstance.frames[renderInstance.frames.length - 2] : renderInstance.lastFrame()\n}\n"]}
|
|
@@ -1,2 +1,6 @@
|
|
|
1
1
|
import { AlertProps } from './components/Alert.js';
|
|
2
|
-
|
|
2
|
+
import { RenderOptions } from 'ink';
|
|
3
|
+
export interface AlertOptions extends AlertProps {
|
|
4
|
+
renderOptions?: RenderOptions;
|
|
5
|
+
}
|
|
6
|
+
export declare function alert({ type, headline, body, nextSteps, reference, link, customSections, orderedNextSteps, renderOptions, }: AlertOptions): string | undefined;
|
|
@@ -12,7 +12,7 @@ const typeToLogger = {
|
|
|
12
12
|
warning: consoleWarn,
|
|
13
13
|
success: consoleLog,
|
|
14
14
|
};
|
|
15
|
-
export function alert({ type, headline, body, nextSteps, reference, link, customSections, orderedNextSteps = false, }) {
|
|
16
|
-
renderOnce(React.createElement(Alert, { type: type, headline: headline, body: body, nextSteps: nextSteps, reference: reference, link: link, orderedNextSteps: orderedNextSteps, customSections: customSections }), typeToLogLevel[type], typeToLogger[type]);
|
|
15
|
+
export function alert({ type, headline, body, nextSteps, reference, link, customSections, orderedNextSteps = false, renderOptions, }) {
|
|
16
|
+
return renderOnce(React.createElement(Alert, { type: type, headline: headline, body: body, nextSteps: nextSteps, reference: reference, link: link, orderedNextSteps: orderedNextSteps, customSections: customSections }), { logLevel: typeToLogLevel[type], logger: typeToLogger[type], renderOptions });
|
|
17
17
|
}
|
|
18
18
|
//# sourceMappingURL=alert.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"alert.js","sourceRoot":"","sources":["../../../../src/private/node/ui/alert.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAC,KAAK,EAAa,MAAM,uBAAuB,CAAA;AACvD,OAAO,EAAC,UAAU,EAAC,MAAM,UAAU,CAAA;AACnC,OAAO,EAAC,UAAU,EAAE,WAAW,EAAmB,MAAM,gCAAgC,CAAA;AACxF,OAAO,KAAK,MAAM,OAAO,CAAA;
|
|
1
|
+
{"version":3,"file":"alert.js","sourceRoot":"","sources":["../../../../src/private/node/ui/alert.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAC,KAAK,EAAa,MAAM,uBAAuB,CAAA;AACvD,OAAO,EAAC,UAAU,EAAC,MAAM,UAAU,CAAA;AACnC,OAAO,EAAC,UAAU,EAAE,WAAW,EAAmB,MAAM,gCAAgC,CAAA;AACxF,OAAO,KAAK,MAAM,OAAO,CAAA;AAGzB,MAAM,cAAc,GAA4C;IAC9D,IAAI,EAAE,MAAM;IACZ,OAAO,EAAE,MAAM;IACf,OAAO,EAAE,MAAM;CAChB,CAAA;AAED,MAAM,YAAY,GAA0C;IAC1D,IAAI,EAAE,UAAU;IAChB,OAAO,EAAE,WAAW;IACpB,OAAO,EAAE,UAAU;CACpB,CAAA;AAMD,MAAM,UAAU,KAAK,CAAC,EACpB,IAAI,EACJ,QAAQ,EACR,IAAI,EACJ,SAAS,EACT,SAAS,EACT,IAAI,EACJ,cAAc,EACd,gBAAgB,GAAG,KAAK,EACxB,aAAa,GACA;IACb,OAAO,UAAU,CACf,oBAAC,KAAK,IACJ,IAAI,EAAE,IAAI,EACV,QAAQ,EAAE,QAAQ,EAClB,IAAI,EAAE,IAAI,EACV,SAAS,EAAE,SAAS,EACpB,SAAS,EAAE,SAAS,EACpB,IAAI,EAAE,IAAI,EACV,gBAAgB,EAAE,gBAAgB,EAClC,cAAc,EAAE,cAAc,GAC9B,EACF,EAAC,QAAQ,EAAE,cAAc,CAAC,IAAI,CAAC,EAAE,MAAM,EAAE,YAAY,CAAC,IAAI,CAAC,EAAE,aAAa,EAAC,CAC5E,CAAA;AACH,CAAC","sourcesContent":["import {Alert, AlertProps} from './components/Alert.js'\nimport {renderOnce} from '../ui.js'\nimport {consoleLog, consoleWarn, Logger, LogLevel} from '../../../public/node/output.js'\nimport React from 'react'\nimport {RenderOptions} from 'ink'\n\nconst typeToLogLevel: {[key in AlertProps['type']]: LogLevel} = {\n info: 'info',\n warning: 'warn',\n success: 'info',\n}\n\nconst typeToLogger: {[key in AlertProps['type']]: Logger} = {\n info: consoleLog,\n warning: consoleWarn,\n success: consoleLog,\n}\n\nexport interface AlertOptions extends AlertProps {\n renderOptions?: RenderOptions\n}\n\nexport function alert({\n type,\n headline,\n body,\n nextSteps,\n reference,\n link,\n customSections,\n orderedNextSteps = false,\n renderOptions,\n}: AlertOptions) {\n return renderOnce(\n <Alert\n type={type}\n headline={headline}\n body={body}\n nextSteps={nextSteps}\n reference={reference}\n link={link}\n orderedNextSteps={orderedNextSteps}\n customSections={customSections}\n />,\n {logLevel: typeToLogLevel[type], logger: typeToLogger[type], renderOptions},\n )\n}\n"]}
|
|
@@ -4,10 +4,10 @@ import { TextInput } from './TextInput.js';
|
|
|
4
4
|
import { TokenizedText } from './TokenizedText.js';
|
|
5
5
|
import { handleCtrlC } from '../../ui.js';
|
|
6
6
|
import { messageWithPunctuation } from '../utilities.js';
|
|
7
|
-
import
|
|
7
|
+
import { debounce } from '../../../../public/common/function.js';
|
|
8
|
+
import React, { useCallback, useEffect, useRef, useState } from 'react';
|
|
8
9
|
import { Box, measureElement, Text, useApp, useInput, useStdout } from 'ink';
|
|
9
10
|
import figures from 'figures';
|
|
10
|
-
import { debounce } from '@shopify/cli-kit/common/function';
|
|
11
11
|
import ansiEscapes from 'ansi-escapes';
|
|
12
12
|
var PromptState;
|
|
13
13
|
(function (PromptState) {
|
|
@@ -26,25 +26,49 @@ function AutocompletePrompt({ message, choices: initialChoices, infoTable, onSub
|
|
|
26
26
|
const [searchTerm, setSearchTerm] = useState('');
|
|
27
27
|
const [searchResults, setSearchResults] = useState(paginatedInitialChoices.slice(0, PAGE_SIZE));
|
|
28
28
|
const { stdout } = useStdout();
|
|
29
|
-
const [height, setHeight] = useState(0);
|
|
30
29
|
const canSearch = initialChoices.length >= PAGE_SIZE;
|
|
31
30
|
const [hasMorePages, setHasMorePages] = useState(initialHasMorePages);
|
|
31
|
+
const [wrapperHeight, setWrapperHeight] = useState(0);
|
|
32
|
+
const [selectInputHeight, setSelectInputHeight] = useState(0);
|
|
33
|
+
const [limit, setLimit] = useState(searchResults.length);
|
|
32
34
|
const paginatedSearch = useCallback(async (term) => {
|
|
33
35
|
const results = await search(term);
|
|
34
36
|
results.data = results.data.slice(0, PAGE_SIZE);
|
|
35
37
|
return results;
|
|
38
|
+
}, [search]);
|
|
39
|
+
const wrapperRef = useCallback((node) => {
|
|
40
|
+
if (node !== null) {
|
|
41
|
+
const { height } = measureElement(node);
|
|
42
|
+
setWrapperHeight(height);
|
|
43
|
+
}
|
|
36
44
|
}, []);
|
|
37
|
-
const
|
|
45
|
+
const inputRef = useCallback((node) => {
|
|
38
46
|
if (node !== null) {
|
|
39
47
|
const { height } = measureElement(node);
|
|
40
|
-
|
|
48
|
+
setSelectInputHeight(height);
|
|
49
|
+
}
|
|
50
|
+
}, []);
|
|
51
|
+
useEffect(() => {
|
|
52
|
+
function onResize() {
|
|
53
|
+
const availableSpace = stdout.rows - (wrapperHeight - selectInputHeight);
|
|
54
|
+
// rough estimate of the limit needed based on the space available
|
|
55
|
+
const newLimit = Math.max(2, availableSpace - 6);
|
|
56
|
+
if (newLimit < limit) {
|
|
57
|
+
stdout.write(ansiEscapes.clearTerminal);
|
|
58
|
+
}
|
|
59
|
+
setLimit(Math.min(newLimit, searchResults.length));
|
|
41
60
|
}
|
|
42
|
-
|
|
43
|
-
|
|
61
|
+
onResize();
|
|
62
|
+
stdout.on('resize', onResize);
|
|
63
|
+
return () => {
|
|
64
|
+
stdout.off('resize', onResize);
|
|
65
|
+
};
|
|
66
|
+
}, [wrapperHeight, selectInputHeight, searchResults.length, stdout, limit]);
|
|
67
|
+
useInput((input, key) => {
|
|
44
68
|
handleCtrlC(input, key);
|
|
45
69
|
if (key.return && promptState === PromptState.Idle && answer) {
|
|
46
70
|
// -1 is for the last row with the terminal cursor
|
|
47
|
-
if (stdout &&
|
|
71
|
+
if (stdout && wrapperHeight >= stdout.rows - 1) {
|
|
48
72
|
stdout.write(ansiEscapes.clearTerminal);
|
|
49
73
|
}
|
|
50
74
|
setPromptState(PromptState.Submitted);
|
|
@@ -52,12 +76,14 @@ function AutocompletePrompt({ message, choices: initialChoices, infoTable, onSub
|
|
|
52
76
|
unmountInk();
|
|
53
77
|
onSubmit(answer.value);
|
|
54
78
|
}
|
|
55
|
-
}
|
|
79
|
+
});
|
|
56
80
|
const setLoadingWhenSlow = useRef();
|
|
57
81
|
// we want to set it each time so that searchTermRef always tracks searchTerm,
|
|
58
82
|
// this is NOT the same as writing useRef(searchTerm)
|
|
59
83
|
const searchTermRef = useRef('');
|
|
60
84
|
searchTermRef.current = searchTerm;
|
|
85
|
+
// disable exhaustive-deps because we want to memoize the debounce function itself
|
|
86
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
61
87
|
const debounceSearch = useCallback(debounce((term) => {
|
|
62
88
|
setLoadingWhenSlow.current = setTimeout(() => {
|
|
63
89
|
setPromptState(PromptState.Loading);
|
|
@@ -83,8 +109,8 @@ function AutocompletePrompt({ message, choices: initialChoices, infoTable, onSub
|
|
|
83
109
|
.finally(() => {
|
|
84
110
|
clearTimeout(setLoadingWhenSlow.current);
|
|
85
111
|
});
|
|
86
|
-
}, 300), []);
|
|
87
|
-
return (React.createElement(Box, { flexDirection: "column", marginBottom: 1, ref:
|
|
112
|
+
}, 300), [initialHasMorePages, paginatedInitialChoices, paginatedSearch]);
|
|
113
|
+
return (React.createElement(Box, { flexDirection: "column", marginBottom: 1, ref: wrapperRef },
|
|
88
114
|
React.createElement(Box, null,
|
|
89
115
|
React.createElement(Box, { marginRight: 2 },
|
|
90
116
|
React.createElement(Text, null, "?")),
|
|
@@ -111,7 +137,7 @@ function AutocompletePrompt({ message, choices: initialChoices, infoTable, onSub
|
|
|
111
137
|
setAnswer(item);
|
|
112
138
|
}, enableShortcuts: false, emptyMessage: "No results found.", highlightedTerm: searchTerm, loading: promptState === PromptState.Loading, errorMessage: promptState === PromptState.Error
|
|
113
139
|
? 'There has been an error while searching. Please try again later.'
|
|
114
|
-
: undefined, hasMorePages: hasMorePages, morePagesMessage: "Find what you're looking for by typing its name." })))));
|
|
140
|
+
: undefined, hasMorePages: hasMorePages, morePagesMessage: "Find what you're looking for by typing its name.", ref: inputRef, limit: limit })))));
|
|
115
141
|
}
|
|
116
142
|
export { AutocompletePrompt };
|
|
117
143
|
//# sourceMappingURL=AutocompletePrompt.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"AutocompletePrompt.js","sourceRoot":"","sources":["../../../../../src/private/node/ui/components/AutocompletePrompt.tsx"],"names":[],"mappings":"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","sourcesContent":["import {SelectInput, SelectInputProps, Item as SelectItem} from './SelectInput.js'\nimport {InfoTable, InfoTableProps} from './Prompts/InfoTable.js'\nimport {TextInput} from './TextInput.js'\nimport {TokenizedText} from './TokenizedText.js'\nimport {handleCtrlC} from '../../ui.js'\nimport {messageWithPunctuation} from '../utilities.js'\nimport React, {ReactElement, useCallback, useRef, useState} from 'react'\nimport {Box, measureElement, Text, useApp, useInput, useStdout} from 'ink'\nimport figures from 'figures'\nimport {debounce} from '@shopify/cli-kit/common/function'\nimport ansiEscapes from 'ansi-escapes'\n\nexport interface SearchResults<T> {\n data: SelectItem<T>[]\n meta?: {\n hasNextPage: boolean\n }\n}\n\nexport interface AutocompletePromptProps<T> {\n message: string\n choices: SelectInputProps<T>['items']\n onSubmit: (value: T) => void\n infoTable?: InfoTableProps['table']\n hasMorePages?: boolean\n search: (term: string) => Promise<SearchResults<T>>\n}\n\nenum PromptState {\n Idle = 'idle',\n Loading = 'loading',\n Submitted = 'submitted',\n Error = 'error',\n}\n\nconst PAGE_SIZE = 25\n\n// eslint-disable-next-line react/function-component-definition\nfunction AutocompletePrompt<T>({\n message,\n choices: initialChoices,\n infoTable,\n onSubmit,\n search,\n hasMorePages: initialHasMorePages = false,\n}: React.PropsWithChildren<AutocompletePromptProps<T>>): ReactElement | null {\n const paginatedInitialChoices = initialChoices.slice(0, PAGE_SIZE)\n const [answer, setAnswer] = useState<SelectItem<T> | undefined>(paginatedInitialChoices[0])\n const {exit: unmountInk} = useApp()\n const [promptState, setPromptState] = useState<PromptState>(PromptState.Idle)\n const [searchTerm, setSearchTerm] = useState('')\n const [searchResults, setSearchResults] = useState<SelectItem<T>[]>(paginatedInitialChoices.slice(0, PAGE_SIZE))\n const {stdout} = useStdout()\n const [height, setHeight] = useState(0)\n const canSearch = initialChoices.length >= PAGE_SIZE\n const [hasMorePages, setHasMorePages] = useState(initialHasMorePages)\n\n const paginatedSearch = useCallback(async (term: string) => {\n const results = await search(term)\n results.data = results.data.slice(0, PAGE_SIZE)\n return results\n }, [])\n\n const measuredRef = useCallback(\n (node) => {\n if (node !== null) {\n const {height} = measureElement(node)\n setHeight(height)\n }\n },\n [searchResults, promptState],\n )\n\n useInput(\n useCallback(\n (input, key) => {\n handleCtrlC(input, key)\n\n if (key.return && promptState === PromptState.Idle && answer) {\n // -1 is for the last row with the terminal cursor\n if (stdout && height >= stdout.rows - 1) {\n stdout.write(ansiEscapes.clearTerminal)\n }\n setPromptState(PromptState.Submitted)\n setSearchTerm('')\n unmountInk()\n onSubmit(answer.value)\n }\n },\n [answer, onSubmit, height, promptState],\n ),\n )\n\n const setLoadingWhenSlow = useRef<NodeJS.Timeout>()\n\n // we want to set it each time so that searchTermRef always tracks searchTerm,\n // this is NOT the same as writing useRef(searchTerm)\n const searchTermRef = useRef('')\n searchTermRef.current = searchTerm\n\n const debounceSearch = useCallback(\n debounce((term) => {\n setLoadingWhenSlow.current = setTimeout(() => {\n setPromptState(PromptState.Loading)\n }, 100)\n paginatedSearch(term)\n .then((result) => {\n // while we were waiting for the promise to resolve, the user\n // has emptied the search term, so we want to show the default\n // choices instead\n if (searchTermRef.current.length === 0) {\n setSearchResults(paginatedInitialChoices)\n setHasMorePages(initialHasMorePages)\n } else {\n setSearchResults(result.data)\n setHasMorePages(result.meta?.hasNextPage ?? false)\n }\n\n setPromptState(PromptState.Idle)\n })\n .catch(() => {\n setPromptState(PromptState.Error)\n })\n .finally(() => {\n clearTimeout(setLoadingWhenSlow.current)\n })\n }, 300),\n [],\n )\n\n return (\n <Box flexDirection=\"column\" marginBottom={1} ref={measuredRef}>\n <Box>\n <Box marginRight={2}>\n <Text>?</Text>\n </Box>\n <TokenizedText item={messageWithPunctuation(message)} />\n {promptState !== PromptState.Submitted && canSearch ? (\n <Box marginLeft={3}>\n <TextInput\n value={searchTerm}\n onChange={(term) => {\n setSearchTerm(term)\n\n if (term.length > 0) {\n debounceSearch(term)\n } else {\n debounceSearch.cancel()\n setPromptState(PromptState.Idle)\n setSearchResults(paginatedInitialChoices)\n }\n }}\n placeholder=\"Type to search...\"\n />\n </Box>\n ) : null}\n </Box>\n\n {infoTable && promptState !== PromptState.Submitted ? (\n <Box marginLeft={7} marginTop={1}>\n <InfoTable table={infoTable} />\n </Box>\n ) : null}\n\n {promptState === PromptState.Submitted ? (\n <Box>\n <Box marginRight={2}>\n <Text color=\"cyan\">{figures.tick}</Text>\n </Box>\n\n <Text color=\"cyan\">{answer!.label}</Text>\n </Box>\n ) : (\n <Box marginTop={1}>\n <SelectInput\n items={searchResults}\n onChange={({item}) => {\n setAnswer(item)\n }}\n enableShortcuts={false}\n emptyMessage=\"No results found.\"\n highlightedTerm={searchTerm}\n loading={promptState === PromptState.Loading}\n errorMessage={\n promptState === PromptState.Error\n ? 'There has been an error while searching. Please try again later.'\n : undefined\n }\n hasMorePages={hasMorePages}\n morePagesMessage=\"Find what you're looking for by typing its name.\"\n />\n </Box>\n )}\n </Box>\n )\n}\n\nexport {AutocompletePrompt}\n"]}
|
|
1
|
+
{"version":3,"file":"AutocompletePrompt.js","sourceRoot":"","sources":["../../../../../src/private/node/ui/components/AutocompletePrompt.tsx"],"names":[],"mappings":"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","sourcesContent":["import {SelectInput, SelectInputProps, Item as SelectItem} from './SelectInput.js'\nimport {InfoTable, InfoTableProps} from './Prompts/InfoTable.js'\nimport {TextInput} from './TextInput.js'\nimport {TokenizedText} from './TokenizedText.js'\nimport {handleCtrlC} from '../../ui.js'\nimport {messageWithPunctuation} from '../utilities.js'\nimport {debounce} from '../../../../public/common/function.js'\nimport React, {ReactElement, useCallback, useEffect, useRef, useState} from 'react'\nimport {Box, measureElement, Text, useApp, useInput, useStdout} from 'ink'\nimport figures from 'figures'\nimport ansiEscapes from 'ansi-escapes'\n\nexport interface SearchResults<T> {\n data: SelectItem<T>[]\n meta?: {\n hasNextPage: boolean\n }\n}\n\nexport interface AutocompletePromptProps<T> {\n message: string\n choices: SelectInputProps<T>['items']\n onSubmit: (value: T) => void\n infoTable?: InfoTableProps['table']\n hasMorePages?: boolean\n search: (term: string) => Promise<SearchResults<T>>\n}\n\nenum PromptState {\n Idle = 'idle',\n Loading = 'loading',\n Submitted = 'submitted',\n Error = 'error',\n}\n\nconst PAGE_SIZE = 25\n\n// eslint-disable-next-line react/function-component-definition\nfunction AutocompletePrompt<T>({\n message,\n choices: initialChoices,\n infoTable,\n onSubmit,\n search,\n hasMorePages: initialHasMorePages = false,\n}: React.PropsWithChildren<AutocompletePromptProps<T>>): ReactElement | null {\n const paginatedInitialChoices = initialChoices.slice(0, PAGE_SIZE)\n const [answer, setAnswer] = useState<SelectItem<T> | undefined>(paginatedInitialChoices[0])\n const {exit: unmountInk} = useApp()\n const [promptState, setPromptState] = useState<PromptState>(PromptState.Idle)\n const [searchTerm, setSearchTerm] = useState('')\n const [searchResults, setSearchResults] = useState<SelectItem<T>[]>(paginatedInitialChoices.slice(0, PAGE_SIZE))\n const {stdout} = useStdout()\n const canSearch = initialChoices.length >= PAGE_SIZE\n const [hasMorePages, setHasMorePages] = useState(initialHasMorePages)\n const [wrapperHeight, setWrapperHeight] = useState(0)\n const [selectInputHeight, setSelectInputHeight] = useState(0)\n const [limit, setLimit] = useState(searchResults.length)\n\n const paginatedSearch = useCallback(\n async (term: string) => {\n const results = await search(term)\n results.data = results.data.slice(0, PAGE_SIZE)\n return results\n },\n [search],\n )\n\n const wrapperRef = useCallback((node) => {\n if (node !== null) {\n const {height} = measureElement(node)\n setWrapperHeight(height)\n }\n }, [])\n\n const inputRef = useCallback((node) => {\n if (node !== null) {\n const {height} = measureElement(node)\n setSelectInputHeight(height)\n }\n }, [])\n\n useEffect(() => {\n function onResize() {\n const availableSpace = stdout!.rows - (wrapperHeight - selectInputHeight)\n // rough estimate of the limit needed based on the space available\n const newLimit = Math.max(2, availableSpace - 6)\n\n if (newLimit < limit) {\n stdout!.write(ansiEscapes.clearTerminal)\n }\n\n setLimit(Math.min(newLimit, searchResults.length))\n }\n\n onResize()\n\n stdout!.on('resize', onResize)\n return () => {\n stdout!.off('resize', onResize)\n }\n }, [wrapperHeight, selectInputHeight, searchResults.length, stdout, limit])\n\n useInput((input, key) => {\n handleCtrlC(input, key)\n\n if (key.return && promptState === PromptState.Idle && answer) {\n // -1 is for the last row with the terminal cursor\n if (stdout && wrapperHeight >= stdout.rows - 1) {\n stdout.write(ansiEscapes.clearTerminal)\n }\n setPromptState(PromptState.Submitted)\n setSearchTerm('')\n unmountInk()\n onSubmit(answer.value)\n }\n })\n\n const setLoadingWhenSlow = useRef<NodeJS.Timeout>()\n\n // we want to set it each time so that searchTermRef always tracks searchTerm,\n // this is NOT the same as writing useRef(searchTerm)\n const searchTermRef = useRef('')\n searchTermRef.current = searchTerm\n\n // disable exhaustive-deps because we want to memoize the debounce function itself\n // eslint-disable-next-line react-hooks/exhaustive-deps\n const debounceSearch = useCallback(\n debounce((term: string) => {\n setLoadingWhenSlow.current = setTimeout(() => {\n setPromptState(PromptState.Loading)\n }, 100)\n paginatedSearch(term)\n .then((result) => {\n // while we were waiting for the promise to resolve, the user\n // has emptied the search term, so we want to show the default\n // choices instead\n if (searchTermRef.current.length === 0) {\n setSearchResults(paginatedInitialChoices)\n setHasMorePages(initialHasMorePages)\n } else {\n setSearchResults(result.data)\n setHasMorePages(result.meta?.hasNextPage ?? false)\n }\n\n setPromptState(PromptState.Idle)\n })\n .catch(() => {\n setPromptState(PromptState.Error)\n })\n .finally(() => {\n clearTimeout(setLoadingWhenSlow.current)\n })\n }, 300),\n [initialHasMorePages, paginatedInitialChoices, paginatedSearch],\n )\n\n return (\n <Box flexDirection=\"column\" marginBottom={1} ref={wrapperRef}>\n <Box>\n <Box marginRight={2}>\n <Text>?</Text>\n </Box>\n <TokenizedText item={messageWithPunctuation(message)} />\n {promptState !== PromptState.Submitted && canSearch ? (\n <Box marginLeft={3}>\n <TextInput\n value={searchTerm}\n onChange={(term) => {\n setSearchTerm(term)\n\n if (term.length > 0) {\n debounceSearch(term)\n } else {\n debounceSearch.cancel()\n setPromptState(PromptState.Idle)\n setSearchResults(paginatedInitialChoices)\n }\n }}\n placeholder=\"Type to search...\"\n />\n </Box>\n ) : null}\n </Box>\n\n {infoTable && promptState !== PromptState.Submitted ? (\n <Box marginLeft={7} marginTop={1}>\n <InfoTable table={infoTable} />\n </Box>\n ) : null}\n\n {promptState === PromptState.Submitted ? (\n <Box>\n <Box marginRight={2}>\n <Text color=\"cyan\">{figures.tick}</Text>\n </Box>\n\n <Text color=\"cyan\">{answer!.label}</Text>\n </Box>\n ) : (\n <Box marginTop={1}>\n <SelectInput\n items={searchResults}\n onChange={({item}) => {\n setAnswer(item)\n }}\n enableShortcuts={false}\n emptyMessage=\"No results found.\"\n highlightedTerm={searchTerm}\n loading={promptState === PromptState.Loading}\n errorMessage={\n promptState === PromptState.Error\n ? 'There has been an error while searching. Please try again later.'\n : undefined\n }\n hasMorePages={hasMorePages}\n morePagesMessage=\"Find what you're looking for by typing its name.\"\n ref={inputRef}\n limit={limit}\n />\n </Box>\n )}\n </Box>\n )\n}\n\nexport {AutocompletePrompt}\n"]}
|
|
@@ -1,8 +1,18 @@
|
|
|
1
1
|
import { AutocompletePrompt } from './AutocompletePrompt.js';
|
|
2
|
-
import { getLastFrameAfterUnmount, sendInputAndWait, sendInputAndWaitForChange, sendInputAndWaitForContent,
|
|
3
|
-
import {
|
|
2
|
+
import { getLastFrameAfterUnmount, sendInputAndWait, sendInputAndWaitForChange, sendInputAndWaitForContent, waitForInputsToBeReady, } from '../../testing/ui.js';
|
|
3
|
+
import { OutputStream } from '../../ui.js';
|
|
4
|
+
import { beforeEach, describe, expect, test, vi } from 'vitest';
|
|
4
5
|
import React from 'react';
|
|
5
6
|
import { render } from 'ink-testing-library';
|
|
7
|
+
import { useStdout } from 'ink';
|
|
8
|
+
vi.mock('ink', async () => {
|
|
9
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
10
|
+
const original = await vi.importActual('ink');
|
|
11
|
+
return {
|
|
12
|
+
...original,
|
|
13
|
+
useStdout: vi.fn(),
|
|
14
|
+
};
|
|
15
|
+
});
|
|
6
16
|
const ARROW_DOWN = '\u001B[B';
|
|
7
17
|
const ENTER = '\r';
|
|
8
18
|
const DELETE = '\u007F';
|
|
@@ -58,6 +68,16 @@ const DATABASE = [
|
|
|
58
68
|
{ label: 'forty-ninth', value: 'forty-ninth' },
|
|
59
69
|
{ label: 'fiftieth', value: 'fiftieth' },
|
|
60
70
|
];
|
|
71
|
+
beforeEach(() => {
|
|
72
|
+
vi.mocked(useStdout).mockReturnValue({
|
|
73
|
+
stdout: new OutputStream({
|
|
74
|
+
columns: 80,
|
|
75
|
+
rows: 80,
|
|
76
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
77
|
+
}),
|
|
78
|
+
write: () => { },
|
|
79
|
+
});
|
|
80
|
+
});
|
|
61
81
|
describe('AutocompletePrompt', async () => {
|
|
62
82
|
test('choose an answer', async () => {
|
|
63
83
|
const onEnter = vi.fn();
|
|
@@ -160,8 +180,9 @@ describe('AutocompletePrompt', async () => {
|
|
|
160
180
|
await waitForInputsToBeReady();
|
|
161
181
|
await sendInputAndWaitForContent(renderInstance, 'No results found', 'a');
|
|
162
182
|
// prompt doesn't change when enter is pressed
|
|
183
|
+
await new Promise((resolve) => setTimeout(resolve, 100));
|
|
163
184
|
await sendInputAndWait(renderInstance, 100, ENTER);
|
|
164
|
-
expect(
|
|
185
|
+
expect(renderInstance.lastFrame()).toMatchInlineSnapshot(`
|
|
165
186
|
"? Associate your project with the org Castile Ventures? [36ma[7m [27m[39m
|
|
166
187
|
|
|
167
188
|
[2mNo results found.[22m
|
|
@@ -181,8 +202,9 @@ describe('AutocompletePrompt', async () => {
|
|
|
181
202
|
await waitForInputsToBeReady();
|
|
182
203
|
await sendInputAndWaitForContent(renderInstance, 'Loading...', 'a');
|
|
183
204
|
// prompt doesn't change when enter is pressed
|
|
205
|
+
await new Promise((resolve) => setTimeout(resolve, 100));
|
|
184
206
|
await sendInputAndWait(renderInstance, 100, ENTER);
|
|
185
|
-
expect(
|
|
207
|
+
expect(renderInstance.lastFrame()).toMatchInlineSnapshot(`
|
|
186
208
|
"? Associate your project with the org Castile Ventures? [36ma[7m [27m[39m
|
|
187
209
|
|
|
188
210
|
[2mLoading...[22m
|
|
@@ -340,46 +362,21 @@ describe('AutocompletePrompt', async () => {
|
|
|
340
362
|
`);
|
|
341
363
|
expect(onEnter).toHaveBeenCalledWith('fifth');
|
|
342
364
|
});
|
|
343
|
-
test('allows selecting the first item after searching
|
|
365
|
+
test('allows selecting the first item after searching', async () => {
|
|
344
366
|
const onEnter = vi.fn();
|
|
345
367
|
const search = async (term) => {
|
|
346
|
-
await new Promise((resolve) => setTimeout(resolve,
|
|
368
|
+
await new Promise((resolve) => setTimeout(resolve, 300));
|
|
347
369
|
return {
|
|
348
370
|
data: DATABASE.filter((item) => item.label.includes(term)),
|
|
349
371
|
};
|
|
350
372
|
};
|
|
351
373
|
const renderInstance = render(React.createElement(AutocompletePrompt, { message: "Associate your project with the org Castile Ventures?", choices: DATABASE, onSubmit: onEnter, search: search }));
|
|
352
374
|
await waitForInputsToBeReady();
|
|
353
|
-
await sendInputAndWaitForContent(renderInstance, '
|
|
354
|
-
await waitForContent(renderInstance, 'Press ↑↓ arrows to select, enter to confirm');
|
|
375
|
+
await sendInputAndWaitForContent(renderInstance, '[1mfiftieth[22m', 'fiftieth');
|
|
355
376
|
expect(renderInstance.lastFrame()).toMatchInlineSnapshot(`
|
|
356
|
-
"? Associate your project with the org Castile Ventures? [
|
|
377
|
+
"? Associate your project with the org Castile Ventures? [36mfiftieth[7m [27m[39m
|
|
357
378
|
|
|
358
|
-
[36m>[39m [
|
|
359
|
-
s[1me[22mventh
|
|
360
|
-
[1me[22mighth
|
|
361
|
-
t[1me[22mnth
|
|
362
|
-
[1me[22mleventh
|
|
363
|
-
tw[1me[22mlfth
|
|
364
|
-
thirt[1me[22menth
|
|
365
|
-
fourt[1me[22menth
|
|
366
|
-
fift[1me[22menth
|
|
367
|
-
sixt[1me[22menth
|
|
368
|
-
s[1me[22mventeenth
|
|
369
|
-
[1me[22mighteenth
|
|
370
|
-
nin[1me[22mteenth
|
|
371
|
-
tw[1me[22mntieth
|
|
372
|
-
tw[1me[22mnty-first
|
|
373
|
-
tw[1me[22mnty-second
|
|
374
|
-
tw[1me[22mnty-third
|
|
375
|
-
tw[1me[22mnty-fourth
|
|
376
|
-
tw[1me[22mnty-fifth
|
|
377
|
-
tw[1me[22mnty-sixth
|
|
378
|
-
tw[1me[22mnty-seventh
|
|
379
|
-
tw[1me[22mnty-eighth
|
|
380
|
-
tw[1me[22mnty-ninth
|
|
381
|
-
thirti[1me[22mth
|
|
382
|
-
thirty-s[1me[22mcond
|
|
379
|
+
[36m>[39m [36m[1mfiftieth[22m[39m
|
|
383
380
|
|
|
384
381
|
[2mPress ↑↓ arrows to select, enter to confirm[22m
|
|
385
382
|
"
|
|
@@ -387,10 +384,10 @@ describe('AutocompletePrompt', async () => {
|
|
|
387
384
|
await sendInputAndWaitForChange(renderInstance, ENTER);
|
|
388
385
|
expect(getLastFrameAfterUnmount(renderInstance)).toMatchInlineSnapshot(`
|
|
389
386
|
"? Associate your project with the org Castile Ventures?
|
|
390
|
-
[36m✔[39m [
|
|
387
|
+
[36m✔[39m [36mfiftieth[39m
|
|
391
388
|
"
|
|
392
389
|
`);
|
|
393
|
-
expect(onEnter).toHaveBeenCalledWith('
|
|
390
|
+
expect(onEnter).toHaveBeenCalledWith('fiftieth');
|
|
394
391
|
});
|
|
395
392
|
test('displays an error message if the search fails', async () => {
|
|
396
393
|
const search = (_term) => {
|
|
@@ -523,5 +520,28 @@ describe('AutocompletePrompt', async () => {
|
|
|
523
520
|
"
|
|
524
521
|
`);
|
|
525
522
|
});
|
|
523
|
+
test('adapts to the height of the container', async () => {
|
|
524
|
+
vi.mocked(useStdout).mockReturnValue({
|
|
525
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
526
|
+
stdout: new OutputStream({ rows: 10 }),
|
|
527
|
+
write: () => { },
|
|
528
|
+
});
|
|
529
|
+
const renderInstance = render(React.createElement(AutocompletePrompt, { message: "Associate your project with the org Castile Ventures?", choices: DATABASE, onSubmit: () => { }, hasMorePages: true, search: () => Promise.resolve({
|
|
530
|
+
data: DATABASE,
|
|
531
|
+
}) }));
|
|
532
|
+
expect(renderInstance.lastFrame()).toMatchInlineSnapshot(`
|
|
533
|
+
"? Associate your project with the org Castile Ventures? [36m[7mT[27m[2mype to search...[22m[39m
|
|
534
|
+
|
|
535
|
+
[36m>[39m [36mfirst[39m
|
|
536
|
+
second
|
|
537
|
+
third
|
|
538
|
+
fourth
|
|
539
|
+
|
|
540
|
+
[1m1-25 of many[22m Find what you're looking for by typing its name.
|
|
541
|
+
[2mShowing 4 of 25 items.[22m
|
|
542
|
+
[2mPress ↑↓ arrows to select, enter to confirm[22m
|
|
543
|
+
"
|
|
544
|
+
`);
|
|
545
|
+
});
|
|
526
546
|
});
|
|
527
547
|
//# sourceMappingURL=AutocompletePrompt.test.js.map
|