diff --git a/.changeset/sixty-years-stare.md b/.changeset/sixty-years-stare.md new file mode 100644 index 00000000000..ed503065bff --- /dev/null +++ b/.changeset/sixty-years-stare.md @@ -0,0 +1,5 @@ +--- +'@shopify/cli-kit': patch +--- + +Skip notifications when using --json, -j or SHOPIFY_FLAG_JSON diff --git a/docs-shopify.dev/commands/interfaces/app-function-replay.interface.ts b/docs-shopify.dev/commands/interfaces/app-function-replay.interface.ts index eb67cce1cdc..f7ed1544468 100644 --- a/docs-shopify.dev/commands/interfaces/app-function-replay.interface.ts +++ b/docs-shopify.dev/commands/interfaces/app-function-replay.interface.ts @@ -13,7 +13,7 @@ export interface appfunctionreplay { '-c, --config '?: string /** - * Output the function run result as a JSON object. + * Output the result as JSON. * @environment SHOPIFY_FLAG_JSON */ '-j, --json'?: '' diff --git a/docs-shopify.dev/commands/interfaces/app-function-run.interface.ts b/docs-shopify.dev/commands/interfaces/app-function-run.interface.ts index 9d1173fc23c..c61acf6c36e 100644 --- a/docs-shopify.dev/commands/interfaces/app-function-run.interface.ts +++ b/docs-shopify.dev/commands/interfaces/app-function-run.interface.ts @@ -19,7 +19,7 @@ export interface appfunctionrun { '-i, --input '?: string /** - * Log the run result as a JSON object. + * Output the result as JSON. * @environment SHOPIFY_FLAG_JSON */ '-j, --json'?: '' diff --git a/docs-shopify.dev/commands/interfaces/app-info.interface.ts b/docs-shopify.dev/commands/interfaces/app-info.interface.ts index 09989ff2e89..9c0de90ee96 100644 --- a/docs-shopify.dev/commands/interfaces/app-info.interface.ts +++ b/docs-shopify.dev/commands/interfaces/app-info.interface.ts @@ -7,10 +7,10 @@ export interface appinfo { '-c, --config '?: string /** - * format output as JSON + * Output the result as JSON. * @environment SHOPIFY_FLAG_JSON */ - '--json'?: '' + '-j, --json'?: '' /** * Disable color output. diff --git a/docs-shopify.dev/commands/interfaces/app-logs.interface.ts b/docs-shopify.dev/commands/interfaces/app-logs.interface.ts index 954067bdf5e..3ba6d882ff2 100644 --- a/docs-shopify.dev/commands/interfaces/app-logs.interface.ts +++ b/docs-shopify.dev/commands/interfaces/app-logs.interface.ts @@ -13,7 +13,7 @@ export interface applogs { '-c, --config '?: string /** - * Log the run result as a JSON object. + * Output the result as JSON. * @environment SHOPIFY_FLAG_JSON */ '-j, --json'?: '' diff --git a/docs-shopify.dev/commands/interfaces/app-versions-list.interface.ts b/docs-shopify.dev/commands/interfaces/app-versions-list.interface.ts index 0b1efb13b83..0638ef3d92b 100644 --- a/docs-shopify.dev/commands/interfaces/app-versions-list.interface.ts +++ b/docs-shopify.dev/commands/interfaces/app-versions-list.interface.ts @@ -13,10 +13,10 @@ export interface appversionslist { '-c, --config '?: string /** - * Output the versions list as JSON. + * Output the result as JSON. * @environment SHOPIFY_FLAG_JSON */ - '--json'?: '' + '-j, --json'?: '' /** * Disable color output. diff --git a/docs-shopify.dev/commands/interfaces/theme-info.interface.ts b/docs-shopify.dev/commands/interfaces/theme-info.interface.ts index 5a41b25b932..3c6db433d56 100644 --- a/docs-shopify.dev/commands/interfaces/theme-info.interface.ts +++ b/docs-shopify.dev/commands/interfaces/theme-info.interface.ts @@ -13,10 +13,10 @@ export interface themeinfo { '-e, --environment '?: string /** - * Output the theme info as JSON. + * Output the result as JSON. * @environment SHOPIFY_FLAG_JSON */ - '--json'?: '' + '-j, --json'?: '' /** * Disable color output. diff --git a/docs-shopify.dev/commands/interfaces/theme-list.interface.ts b/docs-shopify.dev/commands/interfaces/theme-list.interface.ts index f8c97259aff..9a50fdf5845 100644 --- a/docs-shopify.dev/commands/interfaces/theme-list.interface.ts +++ b/docs-shopify.dev/commands/interfaces/theme-list.interface.ts @@ -13,10 +13,10 @@ export interface themelist { '--id '?: string /** - * Output the theme list as JSON. + * Output the result as JSON. * @environment SHOPIFY_FLAG_JSON */ - '--json'?: '' + '-j, --json'?: '' /** * Only list themes that contain the given name. diff --git a/docs-shopify.dev/commands/interfaces/theme-push.interface.ts b/docs-shopify.dev/commands/interfaces/theme-push.interface.ts index 8d026193669..07e48fb9076 100644 --- a/docs-shopify.dev/commands/interfaces/theme-push.interface.ts +++ b/docs-shopify.dev/commands/interfaces/theme-push.interface.ts @@ -25,7 +25,7 @@ export interface themepush { '-x, --ignore '?: string /** - * Output JSON instead of a UI. + * Output the result as JSON. * @environment SHOPIFY_FLAG_JSON */ '-j, --json'?: '' diff --git a/docs-shopify.dev/generated/generated_docs_data.json b/docs-shopify.dev/generated/generated_docs_data.json index bac2f1c5399..afc8958649e 100644 --- a/docs-shopify.dev/generated/generated_docs_data.json +++ b/docs-shopify.dev/generated/generated_docs_data.json @@ -868,7 +868,7 @@ "syntaxKind": "PropertySignature", "name": "-j, --json", "value": "\"\"", - "description": "Output the function run result as a JSON object.", + "description": "Output the result as JSON.", "isOptional": true, "environmentValue": "SHOPIFY_FLAG_JSON" }, @@ -891,7 +891,7 @@ "environmentValue": "SHOPIFY_FLAG_WATCH" } ], - "value": "export interface appfunctionreplay {\n /**\n * Application's Client ID\n * @environment SHOPIFY_FLAG_CLIENT_ID\n */\n '--client-id '?: string\n\n /**\n * The name of the app configuration.\n * @environment SHOPIFY_FLAG_APP_CONFIG\n */\n '-c, --config '?: string\n\n /**\n * Output the function run result as a JSON object.\n * @environment SHOPIFY_FLAG_JSON\n */\n '-j, --json'?: ''\n\n /**\n * Specifies a log identifier to replay instead of selecting from a list. The identifier is provided in the output of `shopify app dev` and is the suffix of the log file name.\n * @environment SHOPIFY_FLAG_LOG\n */\n '-l, --log '?: string\n\n /**\n * Disable color output.\n * @environment SHOPIFY_FLAG_NO_COLOR\n */\n '--no-color'?: ''\n\n /**\n * The path to your function directory.\n * @environment SHOPIFY_FLAG_PATH\n */\n '--path '?: string\n\n /**\n * Increase the verbosity of the output.\n * @environment SHOPIFY_FLAG_VERBOSE\n */\n '--verbose'?: ''\n\n /**\n * Re-run the function when the source code changes.\n * @environment SHOPIFY_FLAG_WATCH\n */\n '-w, --watch'?: ''\n}" + "value": "export interface appfunctionreplay {\n /**\n * Application's Client ID\n * @environment SHOPIFY_FLAG_CLIENT_ID\n */\n '--client-id '?: string\n\n /**\n * The name of the app configuration.\n * @environment SHOPIFY_FLAG_APP_CONFIG\n */\n '-c, --config '?: string\n\n /**\n * Output the result as JSON.\n * @environment SHOPIFY_FLAG_JSON\n */\n '-j, --json'?: ''\n\n /**\n * Specifies a log identifier to replay instead of selecting from a list. The identifier is provided in the output of `shopify app dev` and is the suffix of the log file name.\n * @environment SHOPIFY_FLAG_LOG\n */\n '-l, --log '?: string\n\n /**\n * Disable color output.\n * @environment SHOPIFY_FLAG_NO_COLOR\n */\n '--no-color'?: ''\n\n /**\n * The path to your function directory.\n * @environment SHOPIFY_FLAG_PATH\n */\n '--path '?: string\n\n /**\n * Increase the verbosity of the output.\n * @environment SHOPIFY_FLAG_VERBOSE\n */\n '--verbose'?: ''\n\n /**\n * Re-run the function when the source code changes.\n * @environment SHOPIFY_FLAG_WATCH\n */\n '-w, --watch'?: ''\n}" } } } @@ -987,12 +987,12 @@ "syntaxKind": "PropertySignature", "name": "-j, --json", "value": "\"\"", - "description": "Log the run result as a JSON object.", + "description": "Output the result as JSON.", "isOptional": true, "environmentValue": "SHOPIFY_FLAG_JSON" } ], - "value": "export interface appfunctionrun {\n /**\n * The name of the app configuration.\n * @environment SHOPIFY_FLAG_APP_CONFIG\n */\n '-c, --config '?: string\n\n /**\n * Name of the WebAssembly export to invoke.\n * @environment SHOPIFY_FLAG_EXPORT\n */\n '-e, --export '?: string\n\n /**\n * The input JSON to pass to the function. If omitted, standard input is used.\n * @environment SHOPIFY_FLAG_INPUT\n */\n '-i, --input '?: string\n\n /**\n * Log the run result as a JSON object.\n * @environment SHOPIFY_FLAG_JSON\n */\n '-j, --json'?: ''\n\n /**\n * Disable color output.\n * @environment SHOPIFY_FLAG_NO_COLOR\n */\n '--no-color'?: ''\n\n /**\n * The path to your function directory.\n * @environment SHOPIFY_FLAG_PATH\n */\n '--path '?: string\n\n /**\n * Increase the verbosity of the output.\n * @environment SHOPIFY_FLAG_VERBOSE\n */\n '--verbose'?: ''\n}" + "value": "export interface appfunctionrun {\n /**\n * The name of the app configuration.\n * @environment SHOPIFY_FLAG_APP_CONFIG\n */\n '-c, --config '?: string\n\n /**\n * Name of the WebAssembly export to invoke.\n * @environment SHOPIFY_FLAG_EXPORT\n */\n '-e, --export '?: string\n\n /**\n * The input JSON to pass to the function. If omitted, standard input is used.\n * @environment SHOPIFY_FLAG_INPUT\n */\n '-i, --input '?: string\n\n /**\n * Output the result as JSON.\n * @environment SHOPIFY_FLAG_JSON\n */\n '-j, --json'?: ''\n\n /**\n * Disable color output.\n * @environment SHOPIFY_FLAG_NO_COLOR\n */\n '--no-color'?: ''\n\n /**\n * The path to your function directory.\n * @environment SHOPIFY_FLAG_PATH\n */\n '--path '?: string\n\n /**\n * Increase the verbosity of the output.\n * @environment SHOPIFY_FLAG_VERBOSE\n */\n '--verbose'?: ''\n}" } } } @@ -1406,15 +1406,6 @@ "name": "appinfo", "description": "", "members": [ - { - "filePath": "docs-shopify.dev/commands/interfaces/app-info.interface.ts", - "syntaxKind": "PropertySignature", - "name": "--json", - "value": "\"\"", - "description": "format output as JSON", - "isOptional": true, - "environmentValue": "SHOPIFY_FLAG_JSON" - }, { "filePath": "docs-shopify.dev/commands/interfaces/app-info.interface.ts", "syntaxKind": "PropertySignature", @@ -1459,9 +1450,18 @@ "description": "The name of the app configuration.", "isOptional": true, "environmentValue": "SHOPIFY_FLAG_APP_CONFIG" + }, + { + "filePath": "docs-shopify.dev/commands/interfaces/app-info.interface.ts", + "syntaxKind": "PropertySignature", + "name": "-j, --json", + "value": "\"\"", + "description": "Output the result as JSON.", + "isOptional": true, + "environmentValue": "SHOPIFY_FLAG_JSON" } ], - "value": "export interface appinfo {\n /**\n * The name of the app configuration.\n * @environment SHOPIFY_FLAG_APP_CONFIG\n */\n '-c, --config '?: string\n\n /**\n * format output as JSON\n * @environment SHOPIFY_FLAG_JSON\n */\n '--json'?: ''\n\n /**\n * Disable color output.\n * @environment SHOPIFY_FLAG_NO_COLOR\n */\n '--no-color'?: ''\n\n /**\n * The path to your app directory.\n * @environment SHOPIFY_FLAG_PATH\n */\n '--path '?: string\n\n /**\n * Increase the verbosity of the output.\n * @environment SHOPIFY_FLAG_VERBOSE\n */\n '--verbose'?: ''\n\n /**\n * Outputs environment variables necessary for running and deploying web/.\n * @environment SHOPIFY_FLAG_OUTPUT_WEB_ENV\n */\n '--web-env'?: ''\n}" + "value": "export interface appinfo {\n /**\n * The name of the app configuration.\n * @environment SHOPIFY_FLAG_APP_CONFIG\n */\n '-c, --config '?: string\n\n /**\n * Output the result as JSON.\n * @environment SHOPIFY_FLAG_JSON\n */\n '-j, --json'?: ''\n\n /**\n * Disable color output.\n * @environment SHOPIFY_FLAG_NO_COLOR\n */\n '--no-color'?: ''\n\n /**\n * The path to your app directory.\n * @environment SHOPIFY_FLAG_PATH\n */\n '--path '?: string\n\n /**\n * Increase the verbosity of the output.\n * @environment SHOPIFY_FLAG_VERBOSE\n */\n '--verbose'?: ''\n\n /**\n * Outputs environment variables necessary for running and deploying web/.\n * @environment SHOPIFY_FLAG_OUTPUT_WEB_ENV\n */\n '--web-env'?: ''\n}" } } } @@ -1759,7 +1759,7 @@ "syntaxKind": "PropertySignature", "name": "-j, --json", "value": "\"\"", - "description": "Log the run result as a JSON object.", + "description": "Output the result as JSON.", "isOptional": true, "environmentValue": "SHOPIFY_FLAG_JSON" }, @@ -1773,7 +1773,7 @@ "environmentValue": "SHOPIFY_FLAG_STORE" } ], - "value": "export interface applogs {\n /**\n * The Client ID of your app.\n * @environment SHOPIFY_FLAG_CLIENT_ID\n */\n '--client-id '?: string\n\n /**\n * The name of the app configuration.\n * @environment SHOPIFY_FLAG_APP_CONFIG\n */\n '-c, --config '?: string\n\n /**\n * Log the run result as a JSON object.\n * @environment SHOPIFY_FLAG_JSON\n */\n '-j, --json'?: ''\n\n /**\n * Disable color output.\n * @environment SHOPIFY_FLAG_NO_COLOR\n */\n '--no-color'?: ''\n\n /**\n * The path to your app directory.\n * @environment SHOPIFY_FLAG_PATH\n */\n '--path '?: string\n\n /**\n * Reset all your settings.\n * @environment SHOPIFY_FLAG_RESET\n */\n '--reset'?: ''\n\n /**\n * Filters output to the specified log source.\n * @environment SHOPIFY_FLAG_SOURCE\n */\n '--source '?: string\n\n /**\n * Filters output to the specified status (success or failure).\n * @environment SHOPIFY_FLAG_STATUS\n */\n '--status '?: string\n\n /**\n * Store URL. Must be an existing development or Shopify Plus sandbox store.\n * @environment SHOPIFY_FLAG_STORE\n */\n '-s, --store '?: string\n\n /**\n * Increase the verbosity of the output.\n * @environment SHOPIFY_FLAG_VERBOSE\n */\n '--verbose'?: ''\n}" + "value": "export interface applogs {\n /**\n * The Client ID of your app.\n * @environment SHOPIFY_FLAG_CLIENT_ID\n */\n '--client-id '?: string\n\n /**\n * The name of the app configuration.\n * @environment SHOPIFY_FLAG_APP_CONFIG\n */\n '-c, --config '?: string\n\n /**\n * Output the result as JSON.\n * @environment SHOPIFY_FLAG_JSON\n */\n '-j, --json'?: ''\n\n /**\n * Disable color output.\n * @environment SHOPIFY_FLAG_NO_COLOR\n */\n '--no-color'?: ''\n\n /**\n * The path to your app directory.\n * @environment SHOPIFY_FLAG_PATH\n */\n '--path '?: string\n\n /**\n * Reset all your settings.\n * @environment SHOPIFY_FLAG_RESET\n */\n '--reset'?: ''\n\n /**\n * Filters output to the specified log source.\n * @environment SHOPIFY_FLAG_SOURCE\n */\n '--source '?: string\n\n /**\n * Filters output to the specified status (success or failure).\n * @environment SHOPIFY_FLAG_STATUS\n */\n '--status '?: string\n\n /**\n * Store URL. Must be an existing development or Shopify Plus sandbox store.\n * @environment SHOPIFY_FLAG_STORE\n */\n '-s, --store '?: string\n\n /**\n * Increase the verbosity of the output.\n * @environment SHOPIFY_FLAG_VERBOSE\n */\n '--verbose'?: ''\n}" } } } @@ -1928,15 +1928,6 @@ "isOptional": true, "environmentValue": "SHOPIFY_FLAG_CLIENT_ID" }, - { - "filePath": "docs-shopify.dev/commands/interfaces/app-versions-list.interface.ts", - "syntaxKind": "PropertySignature", - "name": "--json", - "value": "\"\"", - "description": "Output the versions list as JSON.", - "isOptional": true, - "environmentValue": "SHOPIFY_FLAG_JSON" - }, { "filePath": "docs-shopify.dev/commands/interfaces/app-versions-list.interface.ts", "syntaxKind": "PropertySignature", @@ -1972,9 +1963,18 @@ "description": "The name of the app configuration.", "isOptional": true, "environmentValue": "SHOPIFY_FLAG_APP_CONFIG" + }, + { + "filePath": "docs-shopify.dev/commands/interfaces/app-versions-list.interface.ts", + "syntaxKind": "PropertySignature", + "name": "-j, --json", + "value": "\"\"", + "description": "Output the result as JSON.", + "isOptional": true, + "environmentValue": "SHOPIFY_FLAG_JSON" } ], - "value": "export interface appversionslist {\n /**\n * The Client ID to fetch versions for.\n * @environment SHOPIFY_FLAG_CLIENT_ID\n */\n '--client-id '?: string\n\n /**\n * The name of the app configuration.\n * @environment SHOPIFY_FLAG_APP_CONFIG\n */\n '-c, --config '?: string\n\n /**\n * Output the versions list as JSON.\n * @environment SHOPIFY_FLAG_JSON\n */\n '--json'?: ''\n\n /**\n * Disable color output.\n * @environment SHOPIFY_FLAG_NO_COLOR\n */\n '--no-color'?: ''\n\n /**\n * The path to your app directory.\n * @environment SHOPIFY_FLAG_PATH\n */\n '--path '?: string\n\n /**\n * Increase the verbosity of the output.\n * @environment SHOPIFY_FLAG_VERBOSE\n */\n '--verbose'?: ''\n}" + "value": "export interface appversionslist {\n /**\n * The Client ID to fetch versions for.\n * @environment SHOPIFY_FLAG_CLIENT_ID\n */\n '--client-id '?: string\n\n /**\n * The name of the app configuration.\n * @environment SHOPIFY_FLAG_APP_CONFIG\n */\n '-c, --config '?: string\n\n /**\n * Output the result as JSON.\n * @environment SHOPIFY_FLAG_JSON\n */\n '-j, --json'?: ''\n\n /**\n * Disable color output.\n * @environment SHOPIFY_FLAG_NO_COLOR\n */\n '--no-color'?: ''\n\n /**\n * The path to your app directory.\n * @environment SHOPIFY_FLAG_PATH\n */\n '--path '?: string\n\n /**\n * Increase the verbosity of the output.\n * @environment SHOPIFY_FLAG_VERBOSE\n */\n '--verbose'?: ''\n}" } } } @@ -4993,15 +4993,6 @@ "name": "themeinfo", "description": "", "members": [ - { - "filePath": "docs-shopify.dev/commands/interfaces/theme-info.interface.ts", - "syntaxKind": "PropertySignature", - "name": "--json", - "value": "\"\"", - "description": "Output the theme info as JSON.", - "isOptional": true, - "environmentValue": "SHOPIFY_FLAG_JSON" - }, { "filePath": "docs-shopify.dev/commands/interfaces/theme-info.interface.ts", "syntaxKind": "PropertySignature", @@ -5047,6 +5038,15 @@ "isOptional": true, "environmentValue": "SHOPIFY_FLAG_ENVIRONMENT" }, + { + "filePath": "docs-shopify.dev/commands/interfaces/theme-info.interface.ts", + "syntaxKind": "PropertySignature", + "name": "-j, --json", + "value": "\"\"", + "description": "Output the result as JSON.", + "isOptional": true, + "environmentValue": "SHOPIFY_FLAG_JSON" + }, { "filePath": "docs-shopify.dev/commands/interfaces/theme-info.interface.ts", "syntaxKind": "PropertySignature", @@ -5066,7 +5066,7 @@ "environmentValue": "SHOPIFY_FLAG_THEME_ID" } ], - "value": "export interface themeinfo {\n /**\n * Retrieve info from your development theme.\n * @environment SHOPIFY_FLAG_DEVELOPMENT\n */\n '-d, --development'?: ''\n\n /**\n * The environment to apply to the current command.\n * @environment SHOPIFY_FLAG_ENVIRONMENT\n */\n '-e, --environment '?: string\n\n /**\n * Output the theme info as JSON.\n * @environment SHOPIFY_FLAG_JSON\n */\n '--json'?: ''\n\n /**\n * Disable color output.\n * @environment SHOPIFY_FLAG_NO_COLOR\n */\n '--no-color'?: ''\n\n /**\n * Password generated from the Theme Access app.\n * @environment SHOPIFY_CLI_THEME_TOKEN\n */\n '--password '?: string\n\n /**\n * Store URL. It can be the store prefix (example) or the full myshopify.com URL (example.myshopify.com, https://example.myshopify.com).\n * @environment SHOPIFY_FLAG_STORE\n */\n '-s, --store '?: string\n\n /**\n * Theme ID or name of the remote theme.\n * @environment SHOPIFY_FLAG_THEME_ID\n */\n '-t, --theme '?: string\n\n /**\n * Increase the verbosity of the output.\n * @environment SHOPIFY_FLAG_VERBOSE\n */\n '--verbose'?: ''\n}" + "value": "export interface themeinfo {\n /**\n * Retrieve info from your development theme.\n * @environment SHOPIFY_FLAG_DEVELOPMENT\n */\n '-d, --development'?: ''\n\n /**\n * The environment to apply to the current command.\n * @environment SHOPIFY_FLAG_ENVIRONMENT\n */\n '-e, --environment '?: string\n\n /**\n * Output the result as JSON.\n * @environment SHOPIFY_FLAG_JSON\n */\n '-j, --json'?: ''\n\n /**\n * Disable color output.\n * @environment SHOPIFY_FLAG_NO_COLOR\n */\n '--no-color'?: ''\n\n /**\n * Password generated from the Theme Access app.\n * @environment SHOPIFY_CLI_THEME_TOKEN\n */\n '--password '?: string\n\n /**\n * Store URL. It can be the store prefix (example) or the full myshopify.com URL (example.myshopify.com, https://example.myshopify.com).\n * @environment SHOPIFY_FLAG_STORE\n */\n '-s, --store '?: string\n\n /**\n * Theme ID or name of the remote theme.\n * @environment SHOPIFY_FLAG_THEME_ID\n */\n '-t, --theme '?: string\n\n /**\n * Increase the verbosity of the output.\n * @environment SHOPIFY_FLAG_VERBOSE\n */\n '--verbose'?: ''\n}" } } } @@ -5251,15 +5251,6 @@ "isOptional": true, "environmentValue": "SHOPIFY_FLAG_ID" }, - { - "filePath": "docs-shopify.dev/commands/interfaces/theme-list.interface.ts", - "syntaxKind": "PropertySignature", - "name": "--json", - "value": "\"\"", - "description": "Output the theme list as JSON.", - "isOptional": true, - "environmentValue": "SHOPIFY_FLAG_JSON" - }, { "filePath": "docs-shopify.dev/commands/interfaces/theme-list.interface.ts", "syntaxKind": "PropertySignature", @@ -5314,6 +5305,15 @@ "isOptional": true, "environmentValue": "SHOPIFY_FLAG_ENVIRONMENT" }, + { + "filePath": "docs-shopify.dev/commands/interfaces/theme-list.interface.ts", + "syntaxKind": "PropertySignature", + "name": "-j, --json", + "value": "\"\"", + "description": "Output the result as JSON.", + "isOptional": true, + "environmentValue": "SHOPIFY_FLAG_JSON" + }, { "filePath": "docs-shopify.dev/commands/interfaces/theme-list.interface.ts", "syntaxKind": "PropertySignature", @@ -5324,7 +5324,7 @@ "environmentValue": "SHOPIFY_FLAG_STORE" } ], - "value": "export interface themelist {\n /**\n * The environment to apply to the current command.\n * @environment SHOPIFY_FLAG_ENVIRONMENT\n */\n '-e, --environment '?: string\n\n /**\n * Only list theme with the given ID.\n * @environment SHOPIFY_FLAG_ID\n */\n '--id '?: string\n\n /**\n * Output the theme list as JSON.\n * @environment SHOPIFY_FLAG_JSON\n */\n '--json'?: ''\n\n /**\n * Only list themes that contain the given name.\n * @environment SHOPIFY_FLAG_NAME\n */\n '--name '?: string\n\n /**\n * Disable color output.\n * @environment SHOPIFY_FLAG_NO_COLOR\n */\n '--no-color'?: ''\n\n /**\n * Password generated from the Theme Access app.\n * @environment SHOPIFY_CLI_THEME_TOKEN\n */\n '--password '?: string\n\n /**\n * Only list themes with the given role.\n * @environment SHOPIFY_FLAG_ROLE\n */\n '--role '?: string\n\n /**\n * Store URL. It can be the store prefix (example) or the full myshopify.com URL (example.myshopify.com, https://example.myshopify.com).\n * @environment SHOPIFY_FLAG_STORE\n */\n '-s, --store '?: string\n\n /**\n * Increase the verbosity of the output.\n * @environment SHOPIFY_FLAG_VERBOSE\n */\n '--verbose'?: ''\n}" + "value": "export interface themelist {\n /**\n * The environment to apply to the current command.\n * @environment SHOPIFY_FLAG_ENVIRONMENT\n */\n '-e, --environment '?: string\n\n /**\n * Only list theme with the given ID.\n * @environment SHOPIFY_FLAG_ID\n */\n '--id '?: string\n\n /**\n * Output the result as JSON.\n * @environment SHOPIFY_FLAG_JSON\n */\n '-j, --json'?: ''\n\n /**\n * Only list themes that contain the given name.\n * @environment SHOPIFY_FLAG_NAME\n */\n '--name '?: string\n\n /**\n * Disable color output.\n * @environment SHOPIFY_FLAG_NO_COLOR\n */\n '--no-color'?: ''\n\n /**\n * Password generated from the Theme Access app.\n * @environment SHOPIFY_CLI_THEME_TOKEN\n */\n '--password '?: string\n\n /**\n * Only list themes with the given role.\n * @environment SHOPIFY_FLAG_ROLE\n */\n '--role '?: string\n\n /**\n * Store URL. It can be the store prefix (example) or the full myshopify.com URL (example.myshopify.com, https://example.myshopify.com).\n * @environment SHOPIFY_FLAG_STORE\n */\n '-s, --store '?: string\n\n /**\n * Increase the verbosity of the output.\n * @environment SHOPIFY_FLAG_VERBOSE\n */\n '--verbose'?: ''\n}" } } } @@ -5860,7 +5860,7 @@ "syntaxKind": "PropertySignature", "name": "-j, --json", "value": "\"\"", - "description": "Output JSON instead of a UI.", + "description": "Output the result as JSON.", "isOptional": true, "environmentValue": "SHOPIFY_FLAG_JSON" }, @@ -5937,7 +5937,7 @@ "environmentValue": "SHOPIFY_FLAG_IGNORE" } ], - "value": "export interface themepush {\n /**\n * Allow push to a live theme.\n * @environment SHOPIFY_FLAG_ALLOW_LIVE\n */\n '-a, --allow-live'?: ''\n\n /**\n * Push theme files from your remote development theme.\n * @environment SHOPIFY_FLAG_DEVELOPMENT\n */\n '-d, --development'?: ''\n\n /**\n * The environment to apply to the current command.\n * @environment SHOPIFY_FLAG_ENVIRONMENT\n */\n '-e, --environment '?: string\n\n /**\n * Skip downloading the specified files (Multiple flags allowed).\n * @environment SHOPIFY_FLAG_IGNORE\n */\n '-x, --ignore '?: string\n\n /**\n * Output JSON instead of a UI.\n * @environment SHOPIFY_FLAG_JSON\n */\n '-j, --json'?: ''\n\n /**\n * Push theme files from your remote live theme.\n * @environment SHOPIFY_FLAG_LIVE\n */\n '-l, --live'?: ''\n\n /**\n * Disable color output.\n * @environment SHOPIFY_FLAG_NO_COLOR\n */\n '--no-color'?: ''\n\n /**\n * Prevent deleting remote files that don't exist locally.\n * @environment SHOPIFY_FLAG_NODELETE\n */\n '-n, --nodelete'?: ''\n\n /**\n * Download only the specified files (Multiple flags allowed).\n * @environment SHOPIFY_FLAG_ONLY\n */\n '-o, --only '?: string\n\n /**\n * Password generated from the Theme Access app.\n * @environment SHOPIFY_CLI_THEME_TOKEN\n */\n '--password '?: string\n\n /**\n * The path to your theme directory.\n * @environment SHOPIFY_FLAG_PATH\n */\n '--path '?: string\n\n /**\n * Publish as the live theme after uploading.\n * @environment SHOPIFY_FLAG_PUBLISH\n */\n '-p, --publish'?: ''\n\n /**\n * Store URL. It can be the store prefix (example) or the full myshopify.com URL (example.myshopify.com, https://example.myshopify.com).\n * @environment SHOPIFY_FLAG_STORE\n */\n '-s, --store '?: string\n\n /**\n * Theme ID or name of the remote theme.\n * @environment SHOPIFY_FLAG_THEME_ID\n */\n '-t, --theme '?: string\n\n /**\n * Create a new unpublished theme and push to it.\n * @environment SHOPIFY_FLAG_UNPUBLISHED\n */\n '-u, --unpublished'?: ''\n\n /**\n * Increase the verbosity of the output.\n * @environment SHOPIFY_FLAG_VERBOSE\n */\n '--verbose'?: ''\n}" + "value": "export interface themepush {\n /**\n * Allow push to a live theme.\n * @environment SHOPIFY_FLAG_ALLOW_LIVE\n */\n '-a, --allow-live'?: ''\n\n /**\n * Push theme files from your remote development theme.\n * @environment SHOPIFY_FLAG_DEVELOPMENT\n */\n '-d, --development'?: ''\n\n /**\n * The environment to apply to the current command.\n * @environment SHOPIFY_FLAG_ENVIRONMENT\n */\n '-e, --environment '?: string\n\n /**\n * Skip downloading the specified files (Multiple flags allowed).\n * @environment SHOPIFY_FLAG_IGNORE\n */\n '-x, --ignore '?: string\n\n /**\n * Output the result as JSON.\n * @environment SHOPIFY_FLAG_JSON\n */\n '-j, --json'?: ''\n\n /**\n * Push theme files from your remote live theme.\n * @environment SHOPIFY_FLAG_LIVE\n */\n '-l, --live'?: ''\n\n /**\n * Disable color output.\n * @environment SHOPIFY_FLAG_NO_COLOR\n */\n '--no-color'?: ''\n\n /**\n * Prevent deleting remote files that don't exist locally.\n * @environment SHOPIFY_FLAG_NODELETE\n */\n '-n, --nodelete'?: ''\n\n /**\n * Download only the specified files (Multiple flags allowed).\n * @environment SHOPIFY_FLAG_ONLY\n */\n '-o, --only '?: string\n\n /**\n * Password generated from the Theme Access app.\n * @environment SHOPIFY_CLI_THEME_TOKEN\n */\n '--password '?: string\n\n /**\n * The path to your theme directory.\n * @environment SHOPIFY_FLAG_PATH\n */\n '--path '?: string\n\n /**\n * Publish as the live theme after uploading.\n * @environment SHOPIFY_FLAG_PUBLISH\n */\n '-p, --publish'?: ''\n\n /**\n * Store URL. It can be the store prefix (example) or the full myshopify.com URL (example.myshopify.com, https://example.myshopify.com).\n * @environment SHOPIFY_FLAG_STORE\n */\n '-s, --store '?: string\n\n /**\n * Theme ID or name of the remote theme.\n * @environment SHOPIFY_FLAG_THEME_ID\n */\n '-t, --theme '?: string\n\n /**\n * Create a new unpublished theme and push to it.\n * @environment SHOPIFY_FLAG_UNPUBLISHED\n */\n '-u, --unpublished'?: ''\n\n /**\n * Increase the verbosity of the output.\n * @environment SHOPIFY_FLAG_VERBOSE\n */\n '--verbose'?: ''\n}" } } } diff --git a/packages/app/package.json b/packages/app/package.json index b1d9c54e505..867222571ae 100644 --- a/packages/app/package.json +++ b/packages/app/package.json @@ -67,6 +67,7 @@ "graphql-request": "5.2.0", "h3": "0.7.21", "http-proxy": "1.18.1", + "ignore": "6.0.2", "proper-lockfile": "4.1.2", "react": "^18.2.0", "react-dom": "18.2.0", diff --git a/packages/app/src/cli/commands/app/function/replay.ts b/packages/app/src/cli/commands/app/function/replay.ts index 8352d9a71a1..ae86903fab2 100644 --- a/packages/app/src/cli/commands/app/function/replay.ts +++ b/packages/app/src/cli/commands/app/function/replay.ts @@ -3,7 +3,7 @@ import {replay} from '../../../services/function/replay.js' import {appFlags} from '../../../flags.js' import {showApiKeyDeprecationWarning} from '../../../prompts/deprecation-warnings.js' import AppCommand, {AppCommandOutput} from '../../../utilities/app-command.js' -import {globalFlags} from '@shopify/cli-kit/node/cli' +import {globalFlags, jsonFlag} from '@shopify/cli-kit/node/cli' import {Flags} from '@oclif/core' export default class FunctionReplay extends AppCommand { @@ -17,6 +17,7 @@ export default class FunctionReplay extends AppCommand { ...globalFlags, ...appFlags, ...functionFlags, + ...jsonFlag, 'api-key': Flags.string({ hidden: true, description: "Application's API key", @@ -35,12 +36,6 @@ export default class FunctionReplay extends AppCommand { 'Specifies a log identifier to replay instead of selecting from a list. The identifier is provided in the output of `shopify app dev` and is the suffix of the log file name.', env: 'SHOPIFY_FLAG_LOG', }), - json: Flags.boolean({ - char: 'j', - hidden: false, - description: 'Output the function run result as a JSON object.', - env: 'SHOPIFY_FLAG_JSON', - }), watch: Flags.boolean({ char: 'w', hidden: false, diff --git a/packages/app/src/cli/commands/app/function/run.ts b/packages/app/src/cli/commands/app/function/run.ts index 2c8d352f4d6..c655b0b4002 100644 --- a/packages/app/src/cli/commands/app/function/run.ts +++ b/packages/app/src/cli/commands/app/function/run.ts @@ -2,7 +2,7 @@ import {functionFlags, inFunctionContext, getOrGenerateSchemaPath} from '../../. import {runFunction} from '../../../services/function/runner.js' import {appFlags} from '../../../flags.js' import AppCommand, {AppCommandOutput} from '../../../utilities/app-command.js' -import {globalFlags} from '@shopify/cli-kit/node/cli' +import {globalFlags, jsonFlag} from '@shopify/cli-kit/node/cli' import {Flags} from '@oclif/core' import {renderAutocompletePrompt, isTTY} from '@shopify/cli-kit/node/ui' import {outputDebug} from '@shopify/cli-kit/node/output' @@ -20,6 +20,7 @@ export default class FunctionRun extends AppCommand { ...globalFlags, ...appFlags, ...functionFlags, + ...jsonFlag, input: Flags.string({ char: 'i', description: 'The input JSON to pass to the function. If omitted, standard input is used.', @@ -31,12 +32,6 @@ export default class FunctionRun extends AppCommand { description: 'Name of the WebAssembly export to invoke.', env: 'SHOPIFY_FLAG_EXPORT', }), - json: Flags.boolean({ - char: 'j', - hidden: false, - description: 'Log the run result as a JSON object.', - env: 'SHOPIFY_FLAG_JSON', - }), } public async run(): Promise { diff --git a/packages/app/src/cli/commands/app/import-extensions.ts b/packages/app/src/cli/commands/app/import-extensions.ts index e2b05631cf1..e0ab6e4babc 100644 --- a/packages/app/src/cli/commands/app/import-extensions.ts +++ b/packages/app/src/cli/commands/app/import-extensions.ts @@ -8,6 +8,7 @@ import {appFlags} from '../../flags.js' import {importExtensions} from '../../services/import-extensions.js' import AppCommand, {AppCommandOutput} from '../../utilities/app-command.js' import {linkedAppContext} from '../../services/app-context.js' +import {CurrentAppConfiguration} from '../../models/app/app.js' import {renderSelectPrompt, renderFatalError} from '@shopify/cli-kit/node/ui' import {Flags} from '@oclif/core' import {globalFlags} from '@shopify/cli-kit/node/cli' @@ -17,7 +18,11 @@ interface MigrationChoice { label: string value: string extensionTypes: string[] - buildTomlObject: (ext: ExtensionRegistration, allExtensions: ExtensionRegistration[]) => string + buildTomlObject: ( + ext: ExtensionRegistration, + allExtensions: ExtensionRegistration[], + appConfiguration: CurrentAppConfiguration, + ) => string } const getMigrationChoices = (): MigrationChoice[] => [ diff --git a/packages/app/src/cli/commands/app/info.ts b/packages/app/src/cli/commands/app/info.ts index 36ee9a08871..ca21952f589 100644 --- a/packages/app/src/cli/commands/app/info.ts +++ b/packages/app/src/cli/commands/app/info.ts @@ -3,7 +3,7 @@ import {Format, info} from '../../services/info.js' import AppCommand, {AppCommandOutput} from '../../utilities/app-command.js' import {linkedAppContext} from '../../services/app-context.js' import {Flags} from '@oclif/core' -import {globalFlags} from '@shopify/cli-kit/node/cli' +import {globalFlags, jsonFlag} from '@shopify/cli-kit/node/cli' import {outputInfo} from '@shopify/cli-kit/node/output' export default class AppInfo extends AppCommand { @@ -21,11 +21,7 @@ export default class AppInfo extends AppCommand { static flags = { ...globalFlags, ...appFlags, - json: Flags.boolean({ - hidden: false, - description: 'format output as JSON', - env: 'SHOPIFY_FLAG_JSON', - }), + ...jsonFlag, 'web-env': Flags.boolean({ hidden: false, description: 'Outputs environment variables necessary for running and deploying web/.', diff --git a/packages/app/src/cli/commands/app/logs.ts b/packages/app/src/cli/commands/app/logs.ts index 0e089f6d028..716a12607ad 100644 --- a/packages/app/src/cli/commands/app/logs.ts +++ b/packages/app/src/cli/commands/app/logs.ts @@ -7,7 +7,7 @@ import {linkedAppContext} from '../../services/app-context.js' import {storeContext} from '../../services/store-context.js' import {Flags} from '@oclif/core' import {normalizeStoreFqdn} from '@shopify/cli-kit/node/context/fqdn' -import {globalFlags} from '@shopify/cli-kit/node/cli' +import {globalFlags, jsonFlag} from '@shopify/cli-kit/node/cli' export default class Logs extends AppCommand { static summary = 'Stream detailed logs for your Shopify app.' @@ -26,6 +26,7 @@ export default class Logs extends AppCommand { static flags = { ...globalFlags, ...appFlags, + ...jsonFlag, 'api-key': Dev.flags['api-key'], 'client-id': Dev.flags['client-id'], store: Flags.string({ @@ -46,11 +47,6 @@ export default class Logs extends AppCommand { options: ['success', 'failure'], env: 'SHOPIFY_FLAG_STATUS', }), - json: Flags.boolean({ - char: 'j', - description: 'Log the run result as a JSON object.', - env: 'SHOPIFY_FLAG_JSON', - }), } public async run(): Promise { diff --git a/packages/app/src/cli/commands/app/versions/list.ts b/packages/app/src/cli/commands/app/versions/list.ts index 700673819a8..8b9d2c8f73f 100644 --- a/packages/app/src/cli/commands/app/versions/list.ts +++ b/packages/app/src/cli/commands/app/versions/list.ts @@ -3,7 +3,7 @@ import versionList from '../../../services/versions-list.js' import {showApiKeyDeprecationWarning} from '../../../prompts/deprecation-warnings.js' import AppCommand, {AppCommandOutput} from '../../../utilities/app-command.js' import {linkedAppContext} from '../../../services/app-context.js' -import {globalFlags} from '@shopify/cli-kit/node/cli' +import {globalFlags, jsonFlag} from '@shopify/cli-kit/node/cli' import {Args, Flags} from '@oclif/core' export default class VersionsList extends AppCommand { @@ -18,6 +18,7 @@ export default class VersionsList extends AppCommand { static flags = { ...globalFlags, ...appFlags, + ...jsonFlag, 'api-key': Flags.string({ hidden: true, description: "Application's API key to fetch versions for.", @@ -30,11 +31,6 @@ export default class VersionsList extends AppCommand { env: 'SHOPIFY_FLAG_CLIENT_ID', exclusive: ['config'], }), - json: Flags.boolean({ - description: 'Output the versions list as JSON.', - default: false, - env: 'SHOPIFY_FLAG_JSON', - }), } static args = { diff --git a/packages/app/src/cli/models/app/app.test.ts b/packages/app/src/cli/models/app/app.test.ts index 7c4534a09a1..2d9280efa9f 100644 --- a/packages/app/src/cli/models/app/app.test.ts +++ b/packages/app/src/cli/models/app/app.test.ts @@ -39,6 +39,7 @@ const CORRECT_CURRENT_APP_SCHEMA: CurrentAppConfiguration = { }, }, application_url: 'http://example.com', + embedded: false, auth: { redirect_urls: ['https://google.com'], }, diff --git a/packages/app/src/cli/models/app/loader.test.ts b/packages/app/src/cli/models/app/loader.test.ts index 8525ad56684..b8546629b2a 100644 --- a/packages/app/src/cli/models/app/loader.test.ts +++ b/packages/app/src/cli/models/app/loader.test.ts @@ -2353,6 +2353,7 @@ wrong = "property" devDependencies: {}, }) await writeFile(joinPath(webDirectory, 'package.json'), JSON.stringify({})) + await writeFile(joinPath(tmpDir, '.gitignore'), '') await loadTestingApp() @@ -2364,7 +2365,7 @@ wrong = "property" cmd_app_all_configs_any: true, cmd_app_all_configs_clients: JSON.stringify({'shopify.app.toml': '1234567890'}), cmd_app_linked_config_name: 'shopify.app.toml', - cmd_app_linked_config_git_tracked: false, + cmd_app_linked_config_git_tracked: true, cmd_app_linked_config_source: 'cached', cmd_app_warning_api_key_deprecation_displayed: false, app_extensions_any: false, @@ -2388,6 +2389,43 @@ wrong = "property" app_web_frontend_count: 0, }) }) + + test.skipIf(runningOnWindows)(`git_tracked metadata is false when ignored by the gitignore`, async () => { + const {webDirectory} = await writeConfig(linkedAppConfiguration, { + workspaces: ['packages/*'], + name: 'my_app', + dependencies: {}, + devDependencies: {}, + }) + await writeFile(joinPath(webDirectory, 'package.json'), JSON.stringify({})) + await writeFile(joinPath(tmpDir, '.gitignore'), 'shopify.app.toml') + + await loadTestingApp() + + expect(metadata.getAllPublicMetadata()).toEqual( + expect.objectContaining({ + cmd_app_linked_config_git_tracked: false, + }), + ) + }) + + test.skipIf(runningOnWindows)(`git_tracked metadata is true when there is no gitignore`, async () => { + const {webDirectory} = await writeConfig(linkedAppConfiguration, { + workspaces: ['packages/*'], + name: 'my_app', + dependencies: {}, + devDependencies: {}, + }) + await writeFile(joinPath(webDirectory, 'package.json'), JSON.stringify({})) + + await loadTestingApp() + + expect(metadata.getAllPublicMetadata()).toEqual( + expect.objectContaining({ + cmd_app_linked_config_git_tracked: true, + }), + ) + }) }) describe('getAppConfigurationFileName', () => { diff --git a/packages/app/src/cli/models/app/loader.ts b/packages/app/src/cli/models/app/loader.ts index e8d04662e97..52170020453 100644 --- a/packages/app/src/cli/models/app/loader.ts +++ b/packages/app/src/cli/models/app/loader.ts @@ -16,7 +16,9 @@ import { BasicAppConfigurationWithoutModules, SchemaForConfig, AppCreationDefaultOptions, + AppLinkedInterface, } from './app.js' +import {showMultipleCLIWarningIfNeeded} from './validation/multi-cli-warning.js' import {configurationFileNames, dotEnvFileNames} from '../../constants.js' import metadata from '../../metadata.js' import {ExtensionInstance} from '../extensions/extension-instance.js' @@ -42,16 +44,13 @@ import { import {resolveFramework} from '@shopify/cli-kit/node/framework' import {hashString} from '@shopify/cli-kit/node/crypto' import {JsonMapType, decodeToml} from '@shopify/cli-kit/node/toml' -import {joinPath, dirname, basename, relativePath, relativizePath, sniffForJson} from '@shopify/cli-kit/node/path' +import {joinPath, dirname, basename, relativePath, relativizePath} from '@shopify/cli-kit/node/path' import {AbortError} from '@shopify/cli-kit/node/error' import {outputContent, outputDebug, OutputMessage, outputToken} from '@shopify/cli-kit/node/output' import {joinWithAnd, slugify} from '@shopify/cli-kit/common/string' import {getArrayRejectingUndefined} from '@shopify/cli-kit/common/array' -import {checkIfIgnoredInGitRepository} from '@shopify/cli-kit/node/git' -import {renderInfo} from '@shopify/cli-kit/node/ui' -import {currentProcessIsGlobal} from '@shopify/cli-kit/node/is-global' import {showNotificationsIfNeeded} from '@shopify/cli-kit/node/notifications-system' -import {globalCLIVersion, localCLIVersion} from '@shopify/cli-kit/node/version' +import ignore from 'ignore' const defaultExtensionDirectory = 'extensions/*' @@ -202,6 +201,8 @@ export class AppErrors { interface AppLoaderConstructorArgs { mode?: AppLoaderMode loadedConfiguration: ConfigurationLoaderResult + // Used when reloading an app, to avoid some expensive steps during loading. + previousApp?: AppLinkedInterface } export async function checkFolderIsValidApp(directory: string) { @@ -251,6 +252,21 @@ export async function loadApp { + const state = await getAppConfigurationState(app.directory, basename(app.configuration.path)) + if (state.state !== 'connected-app') { + throw new AbortError('Error loading the app, please check your app configuration.') + } + const loadedConfiguration = await loadAppConfigurationFromState(state, app.specifications, app.remoteFlags ?? []) + + const loader = new AppLoader({ + loadedConfiguration, + previousApp: app, + }) + + return loader.loaded() +} + export async function loadAppUsingConfigurationState( configState: TConfig, { @@ -293,20 +309,20 @@ export async function loadDotEnv(appDirectory: string, configurationPath: string return dotEnvFile } -let alreadyShownCLIWarning = false - class AppLoader { private readonly mode: AppLoaderMode private readonly errors: AppErrors = new AppErrors() private readonly specifications: TModuleSpec[] private readonly remoteFlags: Flag[] private readonly loadedConfiguration: ConfigurationLoaderResult + private readonly previousApp: AppLinkedInterface | undefined - constructor({mode, loadedConfiguration}: AppLoaderConstructorArgs) { + constructor({mode, loadedConfiguration, previousApp}: AppLoaderConstructorArgs) { this.mode = mode ?? 'strict' this.specifications = loadedConfiguration.specifications this.remoteFlags = loadedConfiguration.remoteFlags this.loadedConfiguration = loadedConfiguration + this.previousApp = previousApp } async loaded() { @@ -319,15 +335,21 @@ class AppLoader { const websOfType = webs.filter((web) => web.configuration.roles.includes(webType)) @@ -647,6 +640,7 @@ We recommend removing the @shopify/cli and @shopify/app dependencies from your p ) // get all the keys from appConfiguration that aren't used by any of the results + const unusedKeys = Object.keys(appConfiguration) .filter((key) => !extensionInstancesWithKeys.some(([_, keys]) => keys.includes(key))) .filter((key) => { @@ -945,9 +939,7 @@ async function loadAppConfigurationFromState< case 'connected-app': { let gitTracked = false try { - gitTracked = !( - await checkIfIgnoredInGitRepository(configState.appDirectory, [configState.configurationPath]) - )[0] + gitTracked = await checkIfGitTracked(configState.appDirectory, configState.configurationPath) // eslint-disable-next-line no-catch-all/no-catch-all } catch { // leave as false @@ -975,6 +967,16 @@ async function loadAppConfigurationFromState< } } +async function checkIfGitTracked(appDirectory: string, configurationPath: string) { + const gitIgnorePath = joinPath(appDirectory, '.gitignore') + if (!fileExistsSync(gitIgnorePath)) return true + const gitIgnoreContent = await readFile(gitIgnorePath) + const ignored = ignore.default().add(gitIgnoreContent) + const relative = relativePath(appDirectory, configurationPath) + const isTracked = !ignored.ignores(relative) + return isTracked +} + async function getConfigurationPath(appDirectory: string, configName: string | undefined) { const configurationFileName = getAppConfigurationFileName(configName) const configurationPath = joinPath(appDirectory, configurationFileName) diff --git a/packages/app/src/cli/models/app/validation/multi-cli-warning.ts b/packages/app/src/cli/models/app/validation/multi-cli-warning.ts new file mode 100644 index 00000000000..3516617b583 --- /dev/null +++ b/packages/app/src/cli/models/app/validation/multi-cli-warning.ts @@ -0,0 +1,31 @@ +import {renderInfo} from '@shopify/cli-kit/node/ui' +import {currentProcessIsGlobal} from '@shopify/cli-kit/node/is-global' +import {globalCLIVersion, localCLIVersion} from '@shopify/cli-kit/node/version' +import {jsonOutputEnabled} from '@shopify/cli-kit/node/environment' + +export async function showMultipleCLIWarningIfNeeded(directory: string, dependencies: {[key: string]: string}) { + // Show the warning if: + // - There is a global installation + // - The project has a local CLI dependency + // - The user didn't include the --json flag (to avoid showing the warning in scripts or CI/CD pipelines) + + const localVersion = dependencies['@shopify/cli'] && (await localCLIVersion(directory)) + const globalVersion = await globalCLIVersion() + + if (localVersion && globalVersion && !jsonOutputEnabled()) { + const currentInstallation = currentProcessIsGlobal() ? 'global installation' : 'local dependency' + + const warningContent = { + headline: `Two Shopify CLI installations found – using ${currentInstallation}`, + body: [ + `A global installation (v${globalVersion}) and a local dependency (v${localVersion}) were detected. +We recommend removing the @shopify/cli and @shopify/app dependencies from your package.json, unless you want to use different versions across multiple apps.`, + ], + link: { + label: 'See Shopify CLI documentation.', + url: 'https://shopify.dev/docs/apps/build/cli-for-apps#switch-to-a-global-executable-or-local-dependency', + }, + } + renderInfo(warningContent) + } +} diff --git a/packages/app/src/cli/models/extensions/specifications/types/app_config.ts b/packages/app/src/cli/models/extensions/specifications/types/app_config.ts index 8ef18e65bff..a0b154f18df 100644 --- a/packages/app/src/cli/models/extensions/specifications/types/app_config.ts +++ b/packages/app/src/cli/models/extensions/specifications/types/app_config.ts @@ -13,6 +13,7 @@ import {WebhooksConfig} from './app_config_webhook.js' export interface AppConfigurationUsedByCli { name: string application_url: string + embedded: boolean app_proxy?: { url: string prefix: string diff --git a/packages/app/src/cli/services/admin-link/extension-to-toml.test.ts b/packages/app/src/cli/services/admin-link/extension-to-toml.test.ts index eed9408d858..69d627de4e2 100644 --- a/packages/app/src/cli/services/admin-link/extension-to-toml.test.ts +++ b/packages/app/src/cli/services/admin-link/extension-to-toml.test.ts @@ -3,8 +3,16 @@ import {ExtensionRegistration} from '../../api/graphql/all_app_extension_registr import {describe, expect, test} from 'vitest' describe('extension-to-toml', () => { - test('correctly builds a toml string for a app_link', () => { + test('correctly builds a toml string for a app_link extension on a non embedded app', () => { // Given + const appConfig = { + path: '', + name: 'app 1', + client_id: '12345', + application_url: 'http://example.com', + embedded: false, + } + const extension1: ExtensionRegistration = { id: '26237698049', uuid: 'ad9947a9-bc0b-4855-82da-008aefbc1c71', @@ -17,7 +25,7 @@ describe('extension-to-toml', () => { } // When - const got = buildTomlObject(extension1) + const got = buildTomlObject(extension1, [], appConfig) // Then expect(got).toEqual(`[[extensions]] @@ -32,8 +40,15 @@ handle = "admin-link-title" `) }) - test('correctly builds a toml string for a bulk_action', () => { + test('correctly builds a toml string for bulk_action extension with path in an embedded app', () => { // Given + const appConfig = { + path: '', + name: 'app 1', + client_id: '12345', + application_url: 'http://example.com', + embedded: true, + } const extension1: ExtensionRegistration = { id: '26237698049', uuid: 'ad9947a9-bc0b-4855-82da-008aefbc1c71', @@ -41,12 +56,12 @@ handle = "admin-link-title" type: 'bulk_action', draftVersion: { context: 'PRODUCTS#ACTION', - config: '{"text":"bulk action label","url":"https://google.es"}', + config: '{"text":"bulk action label","url":"https://google.es/action/product?product_id=123#hash"}', }, } // When - const got = buildTomlObject(extension1) + const got = buildTomlObject(extension1, [], appConfig) // Then expect(got).toEqual(`[[extensions]] @@ -56,7 +71,77 @@ handle = "bulk-action-title" [[extensions.targeting]] text = "bulk action label" - url = "https://google.es" + url = "app://action/product?product_id=123#hash" + target = "admin.product.selection.link" +`) + }) + test('correctly builds a toml string for bulk_action extension with no path in an embedded app', () => { + // Given + const appConfig = { + path: '', + name: 'app 1', + client_id: '12345', + application_url: 'http://example.com', + embedded: true, + } + const extension1: ExtensionRegistration = { + id: '26237698049', + uuid: 'ad9947a9-bc0b-4855-82da-008aefbc1c71', + title: 'Bulk action title', + type: 'bulk_action', + draftVersion: { + context: 'PRODUCTS#ACTION', + config: '{"text":"bulk action label","url":"https://google.es/"}', + }, + } + + // When + const got = buildTomlObject(extension1, [], appConfig) + + // Then + expect(got).toEqual(`[[extensions]] +type = "admin_link" +name = "Bulk action title" +handle = "bulk-action-title" + + [[extensions.targeting]] + text = "bulk action label" + url = "app://" + target = "admin.product.selection.link" +`) + }) + test('correctly builds a toml string for bulk_action extension with no path but search query in an embedded app', () => { + // Given + const appConfig = { + path: '', + name: 'app 1', + client_id: '12345', + application_url: 'http://example.com', + embedded: true, + } + const extension1: ExtensionRegistration = { + id: '26237698049', + uuid: 'ad9947a9-bc0b-4855-82da-008aefbc1c71', + title: 'Bulk action title', + type: 'bulk_action', + draftVersion: { + context: 'PRODUCTS#ACTION', + config: '{"text":"bulk action label","url":"https://google.es?foo=bar"}', + }, + } + + // When + const got = buildTomlObject(extension1, [], appConfig) + + // Then + expect(got).toEqual(`[[extensions]] +type = "admin_link" +name = "Bulk action title" +handle = "bulk-action-title" + + [[extensions.targeting]] + text = "bulk action label" + url = "app://?foo=bar" target = "admin.product.selection.link" `) }) diff --git a/packages/app/src/cli/services/admin-link/extension-to-toml.ts b/packages/app/src/cli/services/admin-link/extension-to-toml.ts index 319fc00d8ff..28667ccb496 100644 --- a/packages/app/src/cli/services/admin-link/extension-to-toml.ts +++ b/packages/app/src/cli/services/admin-link/extension-to-toml.ts @@ -1,6 +1,7 @@ import {contextToTarget} from './utils.js' import {ExtensionRegistration} from '../../api/graphql/all_app_extension_registrations.js' import {MAX_EXTENSION_HANDLE_LENGTH} from '../../models/extensions/schemas.js' +import {CurrentAppConfiguration} from '../../models/app/app.js' import {encodeToml} from '@shopify/cli-kit/node/toml' import {slugify} from '@shopify/cli-kit/common/string' @@ -12,7 +13,11 @@ interface AdminLinkConfig { /** * Given an app_link or bulk_action extension config file, convert it to toml */ -export function buildTomlObject(extension: ExtensionRegistration): string { +export function buildTomlObject( + extension: ExtensionRegistration, + _: ExtensionRegistration[], + appConfiguration: CurrentAppConfiguration, +): string { const versionConfig = extension.activeVersion?.config ?? extension.draftVersion?.config if (!versionConfig) throw new Error('No config found for extension') @@ -21,6 +26,20 @@ export function buildTomlObject(extension: ExtensionRegistration): string { const config: AdminLinkConfig = JSON.parse(versionConfig) + if (appConfiguration.embedded) { + try { + const linkUrl = new URL(config.url) + const linkPath = linkUrl.pathname.startsWith('/') ? linkUrl.pathname.substring(1) : linkUrl.pathname + const fullUrl = new URL(`app://${linkPath}`) + fullUrl.search = linkUrl.search + fullUrl.hash = linkUrl.hash + config.url = fullUrl.toString() + // eslint-disable-next-line no-catch-all/no-catch-all + } catch (error) { + // Keep original URL if parsing fails + } + } + const localExtensionRepresentation = { extensions: [ { diff --git a/packages/app/src/cli/services/app-context.test.ts b/packages/app/src/cli/services/app-context.test.ts index 91ef8dfae20..be38e6ae25c 100644 --- a/packages/app/src/cli/services/app-context.test.ts +++ b/packages/app/src/cli/services/app-context.test.ts @@ -92,6 +92,7 @@ describe('linkedAppContext', () => { name: 'test-app', application_url: 'https://test-app.com', path: joinPath(tmp, 'shopify.app.toml'), + embedded: false, }, }) @@ -195,6 +196,7 @@ describe('linkedAppContext', () => { name: 'test-app', application_url: 'https://test-app.com', path: joinPath(tmp, 'shopify.app.toml'), + embedded: false, }, }) diff --git a/packages/app/src/cli/services/app/config/link-service.test.ts b/packages/app/src/cli/services/app/config/link-service.test.ts index e1f400e47e5..417a72648c9 100644 --- a/packages/app/src/cli/services/app/config/link-service.test.ts +++ b/packages/app/src/cli/services/app/config/link-service.test.ts @@ -13,7 +13,7 @@ vi.mock('../../local-storage') vi.mock('@shopify/cli-kit/node/ui') vi.mock('../../dev/fetch.js') vi.mock('../../../utilities/developer-platform-client.js') - +vi.mock('../../../models/app/validation/multi-cli-warning.js') beforeEach(async () => {}) function buildDeveloperPlatformClient(): DeveloperPlatformClient { diff --git a/packages/app/src/cli/services/app/config/link.test.ts b/packages/app/src/cli/services/app/config/link.test.ts index 0c560693bfa..7f82e9cf5e0 100644 --- a/packages/app/src/cli/services/app/config/link.test.ts +++ b/packages/app/src/cli/services/app/config/link.test.ts @@ -582,11 +582,12 @@ test('the local configuration is discarded if the client_id is different from th scopes: 'write_products', webhooks: {api_version: '2023-04'}, application_url: 'https://myapp.com', + embedded: false, build: { automatically_update_urls_on_dev: true, dev_store_url: 'my-store.myshopify.com', }, - } as CurrentAppConfiguration, + }, } vi.mocked(loadApp).mockResolvedValue(await mockApp(tmp, localApp, [], 'current')) vi.mocked(fetchOrCreateOrganizationApp).mockResolvedValue( @@ -1358,7 +1359,8 @@ test('the api client configuration is deep merged with the remote app_config ext api_version: '2023-04', }, application_url: 'https://myapp.com', - } as CurrentAppConfiguration, + embedded: true, + }, } vi.mocked(loadApp).mockResolvedValue(await mockApp(tmp, localApp, [], 'current')) vi.mocked(fetchOrCreateOrganizationApp).mockResolvedValue( diff --git a/packages/app/src/cli/services/context/breakdown-extensions.test.ts b/packages/app/src/cli/services/context/breakdown-extensions.test.ts index a022da29dcb..09fc0ceb7ba 100644 --- a/packages/app/src/cli/services/context/breakdown-extensions.test.ts +++ b/packages/app/src/cli/services/context/breakdown-extensions.test.ts @@ -250,6 +250,7 @@ const APP_CONFIGURATION: CurrentAppConfiguration = { api_version: '2023-04', }, application_url: 'https://myapp.com', + embedded: false, build: { include_config_on_deploy: true, }, diff --git a/packages/app/src/cli/services/context/identifiers-extensions.ts b/packages/app/src/cli/services/context/identifiers-extensions.ts index c8b04cf7e75..8141b87e135 100644 --- a/packages/app/src/cli/services/context/identifiers-extensions.ts +++ b/packages/app/src/cli/services/context/identifiers-extensions.ts @@ -124,6 +124,7 @@ export async function ensureExtensionsIds( options.developerPlatformClient, ) remoteExtensions = remoteExtensions.concat(newRemoteExtensions) + didMigrateDashboardExtensions = true } if (adminLinkExtensionsToMigrate.length > 0) { @@ -137,6 +138,7 @@ export async function ensureExtensionsIds( options.developerPlatformClient, ) remoteExtensions = remoteExtensions.concat(newRemoteExtensions) + didMigrateDashboardExtensions = true } const matchExtensions = await automaticMatchmaking( diff --git a/packages/app/src/cli/services/dev/app-events/app-event-watcher-handler.ts b/packages/app/src/cli/services/dev/app-events/app-event-watcher-handler.ts index 00770aa1521..a85be4a1384 100644 --- a/packages/app/src/cli/services/dev/app-events/app-event-watcher-handler.ts +++ b/packages/app/src/cli/services/dev/app-events/app-event-watcher-handler.ts @@ -3,11 +3,10 @@ import {AppEvent, EventType, ExtensionEvent} from './app-event-watcher.js' import {appDiff} from './app-diffing.js' import {AppLinkedInterface} from '../../../models/app/app.js' import {ExtensionInstance} from '../../../models/extensions/extension-instance.js' -import {loadApp} from '../../../models/app/loader.js' -import {outputDebug} from '@shopify/cli-kit/node/output' +import {reloadApp} from '../../../models/app/loader.js' import {AbortError} from '@shopify/cli-kit/node/error' import {endHRTimeInMs, startHRTime} from '@shopify/cli-kit/node/hrtime' -import {basename} from '@shopify/cli-kit/node/path' +import {outputDebug} from '@shopify/cli-kit/node/output' /** * Transforms an array of WatcherEvents from the file system into a processed AppEvent. @@ -114,7 +113,7 @@ function AppConfigDeletedHandler(_input: HandlerInput): AppEvent { * - When an extension toml is updated */ async function ReloadAppHandler({event, app}: HandlerInput): Promise { - const newApp = await reloadApp(app) + const newApp = await reload(app) const diff = appDiff(app, newApp, true) const createdEvents = diff.created.map((ext) => ({type: EventType.Created, extension: ext})) const deletedEvents = diff.deleted.map((ext) => ({type: EventType.Deleted, extension: ext})) @@ -127,17 +126,12 @@ async function ReloadAppHandler({event, app}: HandlerInput): Promise { * Reload the app and returns it * Prints the time to reload the app to stdout */ -export async function reloadApp(app: AppLinkedInterface): Promise { +async function reload(app: AppLinkedInterface): Promise { const start = startHRTime() try { - const newApp = await loadApp({ - specifications: app.specifications, - directory: app.directory, - userProvidedConfigName: basename(app.configuration.path), - remoteFlags: app.remoteFlags, - }) + const newApp = await reloadApp(app) outputDebug(`App reloaded [${endHRTimeInMs(start)}ms]`) - return newApp as AppLinkedInterface + return newApp // eslint-disable-next-line @typescript-eslint/no-explicit-any } catch (error: any) { throw new Error(`Error reloading app: ${error.message}`) diff --git a/packages/app/src/cli/services/dev/app-events/app-event-watcher.test.ts b/packages/app/src/cli/services/dev/app-events/app-event-watcher.test.ts index eb1fbeb4865..0232a6f1129 100644 --- a/packages/app/src/cli/services/dev/app-events/app-event-watcher.test.ts +++ b/packages/app/src/cli/services/dev/app-events/app-event-watcher.test.ts @@ -2,7 +2,6 @@ import {AppEvent, AppEventWatcher, EventType, ExtensionEvent} from './app-event- import {OutputContextOptions, WatcherEvent, startFileWatcher} from './file-watcher.js' import {ESBuildContextManager} from './app-watcher-esbuild.js' import { - testApp, testAppAccessConfigExtension, testAppConfigExtensions, testAppLinked, @@ -11,7 +10,7 @@ import { testUIExtension, } from '../../../models/app/app.test-data.js' import {ExtensionInstance} from '../../../models/extensions/extension-instance.js' -import {loadApp} from '../../../models/app/loader.js' +import {loadApp, reloadApp} from '../../../models/app/loader.js' import {describe, expect, test, vi} from 'vitest' import {AbortSignal} from '@shopify/cli-kit/node/abort' import {flushPromises} from '@shopify/cli-kit/node/promises' @@ -233,7 +232,9 @@ describe('app-event-watcher when receiving a file event', () => { async ({fileWatchEvent, initialExtensions, finalExtensions, extensionEvents, needsAppReload}) => { // Given await inTemporaryDirectory(async (tmpDir) => { - vi.mocked(loadApp).mockResolvedValue(testApp({allExtensions: finalExtensions})) + const mockedApp = testAppLinked({allExtensions: finalExtensions}) + vi.mocked(loadApp).mockResolvedValue(mockedApp) + vi.mocked(reloadApp).mockResolvedValue(mockedApp) vi.mocked(startFileWatcher).mockImplementation(async (app, options, onChange) => onChange([fileWatchEvent])) const buildOutputPath = joinPath(tmpDir, '.shopify', 'bundle') @@ -286,15 +287,9 @@ describe('app-event-watcher when receiving a file event', () => { }) if (needsAppReload) { - expect(loadApp).toHaveBeenCalledWith({ - specifications: expect.anything(), - directory: expect.anything(), - // The app is loaded with the same configuration file - userProvidedConfigName: 'shopify.app.custom.toml', - remoteFlags: expect.anything(), - }) + expect(reloadApp).toHaveBeenCalled() } else { - expect(loadApp).not.toHaveBeenCalled() + expect(reloadApp).not.toHaveBeenCalled() } }) }, diff --git a/packages/app/src/cli/services/dev/app-events/file-watcher.ts b/packages/app/src/cli/services/dev/app-events/file-watcher.ts index 44123c86b65..d3971c3cf4d 100644 --- a/packages/app/src/cli/services/dev/app-events/file-watcher.ts +++ b/packages/app/src/cli/services/dev/app-events/file-watcher.ts @@ -69,9 +69,9 @@ export async function startFileWatcher( /** * Debounced function to emit the accumulated events. - * This function will be called at most once every 500ms to avoid emitting too many events in a short period. + * This function will be called at most once every 300ms to avoid emitting too many events in a short period. */ - const debouncedEmit = debounce(emitEvents, 500) + const debouncedEmit = debounce(emitEvents, 300, {leading: true, trailing: true}) /** * Emits the accumulated events and resets the current events list. diff --git a/packages/app/src/cli/services/dev/processes/setup-dev-processes.test.ts b/packages/app/src/cli/services/dev/processes/setup-dev-processes.test.ts index b462235f363..f02a8e6462f 100644 --- a/packages/app/src/cli/services/dev/processes/setup-dev-processes.test.ts +++ b/packages/app/src/cli/services/dev/processes/setup-dev-processes.test.ts @@ -28,7 +28,7 @@ import {WebType} from '../../../models/app/app.js' import {ensureDeploymentIdsPresence} from '../../context/identifiers.js' import {DeveloperPlatformClient} from '../../../utilities/developer-platform-client.js' import {AppEventWatcher} from '../app-events/app-event-watcher.js' -import {reloadApp} from '../app-events/app-event-watcher-handler.js' +import * as loader from '../../../models/app/loader.js' import {describe, test, expect, beforeEach, vi} from 'vitest' import {ensureAuthenticatedAdmin, ensureAuthenticatedStorefront} from '@shopify/cli-kit/node/session' import {Config} from '@oclif/core' @@ -42,7 +42,6 @@ vi.mock('../fetch.js') vi.mock('@shopify/cli-kit/node/environment') vi.mock('@shopify/theme') vi.mock('@shopify/cli-kit/node/themes/api') -vi.mock('../app-events/app-event-watcher-handler.js') beforeEach(() => { // mocked for draft extensions @@ -68,7 +67,6 @@ beforeEach(() => { role: 'theme', processing: false, }) - vi.mocked(reloadApp).mockResolvedValue(testAppLinked()) }) const appContextResult = { @@ -130,7 +128,7 @@ describe('setup-dev-processes', () => { allExtensions: [previewable, draftable, theme], }, }) - vi.mocked(reloadApp).mockResolvedValue(localApp) + vi.spyOn(loader, 'reloadApp').mockResolvedValue(localApp) const remoteApp: DevConfig['remoteApp'] = { apiKey: 'api-key', @@ -313,6 +311,7 @@ describe('setup-dev-processes', () => { }, } const localApp = testAppWithConfig() + vi.spyOn(loader, 'reloadApp').mockResolvedValue(localApp) const remoteApp: DevConfig['remoteApp'] = { apiKey: 'api-key', @@ -406,7 +405,7 @@ describe('setup-dev-processes', () => { allExtensions: [previewable, draftable, theme, functionExtension], }, }) - vi.mocked(reloadApp).mockResolvedValue(localApp) + vi.spyOn(loader, 'reloadApp').mockResolvedValue(localApp) const remoteApp: DevConfig['remoteApp'] = { apiKey: 'api-key', @@ -502,6 +501,8 @@ describe('setup-dev-processes', () => { }, }) + vi.spyOn(loader, 'reloadApp').mockResolvedValue(localApp) + const remoteApp: DevConfig['remoteApp'] = { apiKey: 'api-key', apiSecretKeys: [{secret: 'api-secret'}], @@ -594,7 +595,7 @@ describe('setup-dev-processes', () => { ], }, }) - vi.mocked(reloadApp).mockResolvedValue(localApp) + vi.spyOn(loader, 'reloadApp').mockResolvedValue(localApp) const remoteApp: DevConfig['remoteApp'] = { apiKey: 'api-key', diff --git a/packages/app/src/cli/services/dev/processes/setup-dev-processes.ts b/packages/app/src/cli/services/dev/processes/setup-dev-processes.ts index 27c528eef38..86ee932b5a0 100644 --- a/packages/app/src/cli/services/dev/processes/setup-dev-processes.ts +++ b/packages/app/src/cli/services/dev/processes/setup-dev-processes.ts @@ -17,8 +17,8 @@ import {getProxyingWebServer} from '../../../utilities/app/http-reverse-proxy.js import {buildAppURLForWeb} from '../../../utilities/app/app-url.js' import {PartnersURLs} from '../urls.js' import {DeveloperPlatformClient} from '../../../utilities/developer-platform-client.js' -import {reloadApp} from '../app-events/app-event-watcher-handler.js' import {AppEventWatcher} from '../app-events/app-event-watcher.js' +import {reloadApp} from '../../../models/app/loader.js' import {getAvailableTCPPort} from '@shopify/cli-kit/node/tcp' import {isTruthy} from '@shopify/cli-kit/node/context/utilities' import {getEnvironmentVariables} from '@shopify/cli-kit/node/environment' diff --git a/packages/app/src/cli/services/generate/extension.test.ts b/packages/app/src/cli/services/generate/extension.test.ts index 29b8728d864..4c4cbfc16a3 100644 --- a/packages/app/src/cli/services/generate/extension.test.ts +++ b/packages/app/src/cli/services/generate/extension.test.ts @@ -29,6 +29,7 @@ import * as git from '@shopify/cli-kit/node/git' import {joinPath, dirname} from '@shopify/cli-kit/node/path' import {slugify} from '@shopify/cli-kit/common/string' +vi.mock('../../models/app/validation/multi-cli-warning.js') vi.mock('@shopify/cli-kit/node/node-package-manager', async () => { const actual: any = await vi.importActual('@shopify/cli-kit/node/node-package-manager') return { diff --git a/packages/app/src/cli/services/import-extensions.ts b/packages/app/src/cli/services/import-extensions.ts index 7f94c0d6311..a702c202a2b 100644 --- a/packages/app/src/cli/services/import-extensions.ts +++ b/packages/app/src/cli/services/import-extensions.ts @@ -1,6 +1,6 @@ import {ensureExtensionDirectoryExists} from './extensions/common.js' import {getExtensions} from './fetch-extensions.js' -import {AppLinkedInterface} from '../models/app/app.js' +import {AppLinkedInterface, CurrentAppConfiguration} from '../models/app/app.js' import {updateAppIdentifiers, IdentifiersExtensions} from '../models/app/identifiers.js' import {ExtensionRegistration} from '../api/graphql/all_app_extension_registrations.js' import {DeveloperPlatformClient} from '../utilities/developer-platform-client.js' @@ -17,7 +17,11 @@ interface ImportOptions { remoteApp: OrganizationApp developerPlatformClient: DeveloperPlatformClient extensionTypes: string[] - buildTomlObject: (ext: ExtensionRegistration, allExtensions: ExtensionRegistration[]) => string + buildTomlObject: ( + ext: ExtensionRegistration, + allExtensions: ExtensionRegistration[], + appConfig: CurrentAppConfiguration, + ) => string } export async function importExtensions(options: ImportOptions) { @@ -44,7 +48,10 @@ export async function importExtensions(options: ImportOptions) { const choices = extensions.map((ext) => { return {label: ext.title, value: ext.uuid} }) - choices.push({label: 'All', value: 'All'}) + + if (extensions.length > 1) { + choices.push({label: 'All', value: 'All'}) + } const promptAnswer = await renderSelectPrompt({message: 'Extensions to migrate', choices}) const extensionsToMigrate = @@ -54,7 +61,7 @@ export async function importExtensions(options: ImportOptions) { const extensionUuids: IdentifiersExtensions = {} const importPromises = extensionsToMigrate.map(async (ext) => { const directory = await ensureExtensionDirectoryExists({app: options.app, name: ext.title}) - const tomlObject = options.buildTomlObject(ext, extensionRegistrations) + const tomlObject = options.buildTomlObject(ext, extensionRegistrations, options.app.configuration) const path = joinPath(directory, 'shopify.extension.toml') await writeFile(path, tomlObject) const handle = slugify(ext.title.substring(0, MAX_EXTENSION_HANDLE_LENGTH)) diff --git a/packages/app/src/cli/utilities/json-schema.ts b/packages/app/src/cli/utilities/json-schema.ts index 20cbc09e40a..33f2b3bf589 100644 --- a/packages/app/src/cli/utilities/json-schema.ts +++ b/packages/app/src/cli/utilities/json-schema.ts @@ -19,6 +19,7 @@ export async function unifiedConfigurationParserFactory( return merged.parseConfigurationObject } const contract = await normaliseJsonSchema(contractJsonSchema) + const extensionIdentifier = merged.identifier const parseConfigurationObject = (config: object): ParseConfigurationResult => { // First we parse with zod. This may also change the format of the data. @@ -29,7 +30,7 @@ export async function unifiedConfigurationParserFactory( const subjectForAjv = zodValidatedData ?? (config as JsonMapType) const subjectForAjvWithoutFirstClassFields = configWithoutFirstClassFields(subjectForAjv) - const jsonSchemaParse = jsonSchemaValidate(subjectForAjvWithoutFirstClassFields, contract) + const jsonSchemaParse = jsonSchemaValidate(subjectForAjvWithoutFirstClassFields, contract, extensionIdentifier) // Finally, we de-duplicate the error set from both validations -- identical messages for identical paths are removed let errors = zodParse.errors || [] diff --git a/packages/cli-kit/package.json b/packages/cli-kit/package.json index 720d2d14ba5..33f0d9366b2 100644 --- a/packages/cli-kit/package.json +++ b/packages/cli-kit/package.json @@ -82,7 +82,6 @@ "./context/local.js", "./custom-oclif-loader.js", "@oclif/core", - "../../private/node/constants.js", "./path.js", "./system.js", "./ui.js" @@ -91,6 +90,7 @@ "@oclif/core", "./context/utilities.js", "../../private/node/conf-store.js", + "../../private/node/constants.js", "url" ] } diff --git a/packages/cli-kit/src/private/node/constants.ts b/packages/cli-kit/src/private/node/constants.ts index 6337664367c..faee18c688e 100644 --- a/packages/cli-kit/src/private/node/constants.ts +++ b/packages/cli-kit/src/private/node/constants.ts @@ -44,6 +44,7 @@ export const environmentVariables = { refreshToken: 'SHOPIFY_CLI_REFRESH_TOKEN', otelURL: 'SHOPIFY_CLI_OTEL_EXPORTER_OTLP_ENDPOINT', themeKitAccessDomain: 'SHOPIFY_CLI_THEME_KIT_ACCESS_DOMAIN', + json: 'SHOPIFY_FLAG_JSON', } export const defaultThemeKitAccessDomain = 'theme-kit-access.shopifyapps.com' diff --git a/packages/cli-kit/src/public/node/cli.ts b/packages/cli-kit/src/public/node/cli.ts index 999cdae5184..6e07c9b5459 100644 --- a/packages/cli-kit/src/public/node/cli.ts +++ b/packages/cli-kit/src/public/node/cli.ts @@ -1,5 +1,6 @@ import {isTruthy} from './context/utilities.js' import {cacheClear} from '../../private/node/conf-store.js' +import {environmentVariables} from '../../private/node/constants.js' import {Flags} from '@oclif/core' /** @@ -135,6 +136,16 @@ export const globalFlags = { }), } +export const jsonFlag = { + json: Flags.boolean({ + char: 'j', + description: 'Output the result as JSON.', + hidden: false, + default: false, + env: environmentVariables.json, + }), +} + /** * Clear the CLI cache, used to store some API responses and handle notifications status */ diff --git a/packages/cli-kit/src/public/node/environment.ts b/packages/cli-kit/src/public/node/environment.ts index 549338baf3d..43dd9d465f8 100644 --- a/packages/cli-kit/src/public/node/environment.ts +++ b/packages/cli-kit/src/public/node/environment.ts @@ -1,4 +1,6 @@ import {nonRandomUUID} from './crypto.js' +import {isTruthy} from './context/utilities.js' +import {sniffForJson} from './path.js' import {environmentVariables, systemEnvironmentVariables} from '../../private/node/constants.js' /** @@ -70,3 +72,13 @@ export function getIdentityTokenInformation(): {accessToken: string; refreshToke userId: nonRandomUUID(identityToken), } } + +/** + * Checks if the JSON output is enabled via flag (--json or -j) or environment variable (SHOPIFY_FLAG_JSON). + * + * @param environment - Process environment variables. + * @returns True if the JSON output is enabled, false otherwise. + */ +export function jsonOutputEnabled(environment = getEnvironmentVariables()): boolean { + return sniffForJson() || isTruthy(environment[environmentVariables.json]) +} diff --git a/packages/cli-kit/src/public/node/json-schema.test.ts b/packages/cli-kit/src/public/node/json-schema.test.ts index fe83c8424d6..6b67c081ad4 100644 --- a/packages/cli-kit/src/public/node/json-schema.test.ts +++ b/packages/cli-kit/src/public/node/json-schema.test.ts @@ -52,7 +52,7 @@ describe('jsonSchemaValidate', () => { zod.object({foo: zod.number().max(99)}), {foo: 100}, ], - ])('matches the zod behaviour for %s', (_name, contract, zodVersion, subject) => { + ])('matches the zod behaviour for %s', (name, contract, zodVersion, subject) => { const zodParsed = zodVersion.safeParse(subject) expect(zodParsed.success).toBe(false) if (zodParsed.success) { @@ -61,7 +61,7 @@ describe('jsonSchemaValidate', () => { const zodErrors = zodParsed.error.errors.map((error) => ({path: error.path, message: error.message})) - const schemaParsed = jsonSchemaValidate(subject, contract) + const schemaParsed = jsonSchemaValidate(subject, contract, `test-${name}`) expect(schemaParsed.state).toBe('error') expect(schemaParsed.errors, `Converting ${JSON.stringify(schemaParsed.rawErrors)}`).toEqual(zodErrors) }) @@ -77,7 +77,7 @@ describe('jsonSchemaValidate', () => { }, 'x-taplo': {foo: 'bar'}, } - const schemaParsed = jsonSchemaValidate(subject, contract) + const schemaParsed = jsonSchemaValidate(subject, contract, 'test2') expect(schemaParsed.state).toBe('ok') }) @@ -121,7 +121,7 @@ describe('jsonSchemaValidate', () => { }, }, } - const schemaParsed = jsonSchemaValidate(subject, contract) + const schemaParsed = jsonSchemaValidate(subject, contract, 'test3') expect(schemaParsed.state).toBe('error') expect(schemaParsed.errors).toEqual([ { @@ -175,7 +175,7 @@ describe('jsonSchemaValidate', () => { }, }, } - const schemaParsed = jsonSchemaValidate(subject, contract) + const schemaParsed = jsonSchemaValidate(subject, contract, 'test4') expect(schemaParsed.state).toBe('error') expect(schemaParsed.errors).toEqual([ { diff --git a/packages/cli-kit/src/public/node/json-schema.ts b/packages/cli-kit/src/public/node/json-schema.ts index 2c740a3550f..4ffdea4d91f 100644 --- a/packages/cli-kit/src/public/node/json-schema.ts +++ b/packages/cli-kit/src/public/node/json-schema.ts @@ -2,7 +2,7 @@ import {ParseConfigurationResult} from './schema.js' import {getPathValue} from '../common/object.js' import {capitalize} from '../common/string.js' -import {Ajv, ErrorObject, SchemaObject} from 'ajv' +import {Ajv, ErrorObject, SchemaObject, ValidateFunction} from 'ajv' import $RefParser from '@apidevtools/json-schema-ref-parser' type AjvError = ErrorObject @@ -22,6 +22,8 @@ export async function normaliseJsonSchema(schema: string): Promise return parsedSchema } +const validatorsCache = new Map() + /** * Given a subject object and a JSON schema contract, validate the subject against the contract. * @@ -29,15 +31,21 @@ export async function normaliseJsonSchema(schema: string): Promise * * @param subject - The object to validate. * @param schema - The JSON schema to validate against. + * @param identifier - The identifier of the schema being validated, used to cache the validator. * @returns The result of the validation. If the state is 'error', the errors will be in a zod-like format. */ export function jsonSchemaValidate( subject: object, schema: SchemaObject, + identifier: string, ): ParseConfigurationResult & {rawErrors?: AjvError[]} { const ajv = new Ajv({allowUnionTypes: true}) + ajv.addKeyword('x-taplo') - const validator = ajv.compile(schema) + + const validator = validatorsCache.get(identifier) ?? ajv.compile(schema) + validatorsCache.set(identifier, validator) + validator(subject) // Errors from the contract are post-processed to be more zod-like and to deal with unions better diff --git a/packages/cli-kit/src/public/node/notifications-system.test.ts b/packages/cli-kit/src/public/node/notifications-system.test.ts index 974d522cc45..c7b44ec34ce 100644 --- a/packages/cli-kit/src/public/node/notifications-system.test.ts +++ b/packages/cli-kit/src/public/node/notifications-system.test.ts @@ -1,10 +1,12 @@ import {Notification, filterNotifications, showNotificationsIfNeeded} from './notifications-system.js' import {renderError, renderInfo, renderWarning} from './ui.js' +import {sniffForJson} from './path.js' import {cacheRetrieve, cacheRetrieveOrRepopulate} from '../../private/node/conf-store.js' import {afterEach, describe, expect, test, vi} from 'vitest' vi.mock('./ui.js') vi.mock('../../private/node/conf-store.js') +vi.mock('./path.js') const betweenVersins1and2: Notification = { id: 'betweenVersins1and2', @@ -258,7 +260,7 @@ afterEach(() => { vi.useRealTimers() }) -describe('notifications-system filter notifications', () => { +describe('filterNotifications', () => { test.each(testCases)('Filter for %name', ({input, commandId, version, date, surfaces, output}) => { // When const result = filterNotifications(input, commandId, surfaces, new Date(date), version) @@ -327,7 +329,7 @@ describe('notifications-system filter notifications', () => { }) }) -describe('notifications-system', () => { +describe('showNotificationsIfNeeded', () => { test('an info notification triggers a renderInfo call', async () => { // Given const notifications = [infoNotification] @@ -363,4 +365,53 @@ describe('notifications-system', () => { // Then expect(renderError).toHaveBeenCalled() }) + + test('notifications are skipped on CI', async () => { + // Given + const notifications = [infoNotification] + vi.mocked(cacheRetrieveOrRepopulate).mockResolvedValue(JSON.stringify({notifications})) + + // When + await showNotificationsIfNeeded(undefined, {SHOPIFY_UNIT_TEST: 'false', CI: 'true'}) + + // Then + expect(renderInfo).not.toHaveBeenCalled() + }) + + test('notifications are skipped on tests', async () => { + // Given + const notifications = [infoNotification] + vi.mocked(cacheRetrieveOrRepopulate).mockResolvedValue(JSON.stringify({notifications})) + + // When + await showNotificationsIfNeeded(undefined, {SHOPIFY_UNIT_TEST: 'true'}) + + // Then + expect(renderInfo).not.toHaveBeenCalled() + }) + + test('notifications are skipped when using --json flag', async () => { + // Given + const notifications = [infoNotification] + vi.mocked(cacheRetrieveOrRepopulate).mockResolvedValue(JSON.stringify({notifications})) + vi.mocked(sniffForJson).mockReturnValue(true) + + // When + await showNotificationsIfNeeded(undefined, {SHOPIFY_UNIT_TEST: 'false'}) + + // Then + expect(renderInfo).not.toHaveBeenCalled() + }) + + test('notifications are skipped when using SHOPIFY_FLAG_JSON', async () => { + // Given + const notifications = [infoNotification] + vi.mocked(cacheRetrieveOrRepopulate).mockResolvedValue(JSON.stringify({notifications})) + + // When + await showNotificationsIfNeeded(undefined, {SHOPIFY_UNIT_TEST: 'false', SHOPIFY_FLAG_JSON: 'true'}) + + // Then + expect(renderInfo).not.toHaveBeenCalled() + }) }) diff --git a/packages/cli-kit/src/public/node/notifications-system.ts b/packages/cli-kit/src/public/node/notifications-system.ts index b382dafb2a6..8ca472a5d82 100644 --- a/packages/cli-kit/src/public/node/notifications-system.ts +++ b/packages/cli-kit/src/public/node/notifications-system.ts @@ -5,6 +5,7 @@ import {outputDebug} from './output.js' import {zod} from './schema.js' import {AbortSilentError} from './error.js' import {isTruthy} from './context/utilities.js' +import {jsonOutputEnabled} from './environment.js' import {CLI_KIT_VERSION} from '../common/version.js' import { NotificationKey, @@ -54,9 +55,12 @@ export type Notifications = zod.infer * @param environment - Process environment variables. * @returns - A promise that resolves when the notifications have been shown. */ -export async function showNotificationsIfNeeded(currentSurfaces?: string[], environment = process.env): Promise { +export async function showNotificationsIfNeeded( + currentSurfaces?: string[], + environment: NodeJS.ProcessEnv = process.env, +): Promise { try { - if (isTruthy(environment.CI) || isTruthy(environment.SHOPIFY_UNIT_TEST)) return + if (skipNotifications(environment)) return const notifications = await getNotifications() const commandId = getCurrentCommandId() @@ -74,6 +78,10 @@ export async function showNotificationsIfNeeded(currentSurfaces?: string[], envi } } +function skipNotifications(environment: NodeJS.ProcessEnv): boolean { + return isTruthy(environment.CI) || isTruthy(environment.SHOPIFY_UNIT_TEST) || jsonOutputEnabled(environment) +} + /** * Renders the first 2 notifications to the user. * diff --git a/packages/cli-kit/src/public/node/path.ts b/packages/cli-kit/src/public/node/path.ts index 41fbcda42dd..1b064805c9c 100644 --- a/packages/cli-kit/src/public/node/path.ts +++ b/packages/cli-kit/src/public/node/path.ts @@ -172,12 +172,11 @@ export function sniffForPath(argv = process.argv): string | undefined { } /** - * Returns whether the `--json` flag is present in the arguments. + * Returns whether the `--json` or `-j` flags are present in the arguments. * - * @param argv - The arguments to search for the `--json` flag. - * @returns Whether the `--json` flag is present in the arguments. + * @param argv - The arguments to search for the `--json` and `-j` flags. + * @returns Whether the `--json` or `-j` flag is present in the arguments. */ export function sniffForJson(argv = process.argv): boolean { - const jsonFlagIndex = argv.indexOf('--json') - return jsonFlagIndex !== -1 + return argv.includes('--json') || argv.includes('-j') } diff --git a/packages/cli/README.md b/packages/cli/README.md index 9ec6d84f714..bcac5dbbde3 100644 --- a/packages/cli/README.md +++ b/packages/cli/README.md @@ -344,7 +344,7 @@ USAGE FLAGS -c, --config= The name of the app configuration. - -j, --json Output the function run result as a JSON object. + -j, --json Output the result as JSON. -l, --log= Specifies a log identifier to replay instead of selecting from a list. The identifier is provided in the output of `shopify app dev` and is the suffix of the log file name. -w, --[no-]watch Re-run the function when the source code changes. @@ -373,7 +373,7 @@ FLAGS -c, --config= The name of the app configuration. -e, --export= Name of the WebAssembly export to invoke. -i, --input= The input JSON to pass to the function. If omitted, standard input is used. - -j, --json Log the run result as a JSON object. + -j, --json Output the result as JSON. --no-color Disable color output. --path= The path to your function directory. --verbose Increase the verbosity of the output. @@ -496,11 +496,11 @@ Print basic information about your app and extensions. ``` USAGE - $ shopify app info [-c ] [--json] [--no-color] [--path ] [--verbose] [--web-env] + $ shopify app info [-c ] [-j] [--no-color] [--path ] [--verbose] [--web-env] FLAGS -c, --config= The name of the app configuration. - --json format output as JSON + -j, --json Output the result as JSON. --no-color Disable color output. --path= The path to your app directory. --verbose Increase the verbosity of the output. @@ -554,7 +554,7 @@ USAGE FLAGS -c, --config= The name of the app configuration. - -j, --json Log the run result as a JSON object. + -j, --json Output the result as JSON. -s, --store=... Store URL. Must be an existing development or Shopify Plus sandbox store. --client-id= The Client ID of your app. --no-color Disable color output. @@ -629,13 +629,12 @@ List deployed versions of your app. ``` USAGE - $ shopify app versions list [FILE] [--client-id | -c ] [--json] [--no-color] [--path ] - [--verbose] + $ shopify app versions list [FILE] [--client-id | -c ] [-j] [--no-color] [--path ] [--verbose] FLAGS -c, --config= The name of the app configuration. + -j, --json Output the result as JSON. --client-id= The Client ID to fetch versions for. - --json Output the versions list as JSON. --no-color Disable color output. --path= The path to your app directory. --verbose Increase the verbosity of the output. @@ -1871,16 +1870,16 @@ Displays information about your theme environment, including your current store. ``` USAGE - $ shopify theme info [-d] [-e ] [--json] [--no-color] [--password ] [-s ] [-t ] + $ shopify theme info [-d] [-e ] [-j] [--no-color] [--password ] [-s ] [-t ] [--verbose] FLAGS -d, --development Retrieve info from your development theme. -e, --environment= The environment to apply to the current command. + -j, --json Output the result as JSON. -s, --store= Store URL. It can be the store prefix (example) or the full myshopify.com URL (example.myshopify.com, https://example.myshopify.com). -t, --theme= Theme ID or name of the remote theme. - --json Output the theme info as JSON. --no-color Disable color output. --password= Password generated from the Theme Access app. --verbose Increase the verbosity of the output. @@ -1948,15 +1947,15 @@ Lists the themes in your store, along with their IDs and statuses. ``` USAGE - $ shopify theme list [-e ] [--id ] [--json] [--name ] [--no-color] [--password ] + $ shopify theme list [-e ] [--id ] [-j] [--name ] [--no-color] [--password ] [--role live|unpublished|development] [-s ] [--verbose] FLAGS -e, --environment= The environment to apply to the current command. + -j, --json Output the result as JSON. -s, --store= Store URL. It can be the store prefix (example) or the full myshopify.com URL (example.myshopify.com, https://example.myshopify.com). --id= Only list theme with the given ID. - --json Output the theme list as JSON. --name= Only list themes that contain the given name. --no-color Disable color output. --password= Password generated from the Theme Access app. @@ -2106,7 +2105,7 @@ FLAGS -a, --allow-live Allow push to a live theme. -d, --development Push theme files from your remote development theme. -e, --environment= The environment to apply to the current command. - -j, --json Output JSON instead of a UI. + -j, --json Output the result as JSON. -l, --live Push theme files from your remote live theme. -n, --nodelete Prevent deleting remote files that don't exist locally. -o, --only=... Download only the specified files (Multiple flags allowed). diff --git a/packages/cli/oclif.manifest.json b/packages/cli/oclif.manifest.json index f0485425515..cfdf8c26b40 100644 --- a/packages/cli/oclif.manifest.json +++ b/packages/cli/oclif.manifest.json @@ -767,7 +767,7 @@ "json": { "allowNo": false, "char": "j", - "description": "Output the function run result as a JSON object.", + "description": "Output the result as JSON.", "env": "SHOPIFY_FLAG_JSON", "hidden": false, "name": "json", @@ -869,7 +869,7 @@ "json": { "allowNo": false, "char": "j", - "description": "Log the run result as a JSON object.", + "description": "Output the result as JSON.", "env": "SHOPIFY_FLAG_JSON", "hidden": false, "name": "json", @@ -1383,7 +1383,8 @@ }, "json": { "allowNo": false, - "description": "format output as JSON", + "char": "j", + "description": "Output the result as JSON.", "env": "SHOPIFY_FLAG_JSON", "hidden": false, "name": "json", @@ -1583,8 +1584,9 @@ "json": { "allowNo": false, "char": "j", - "description": "Log the run result as a JSON object.", + "description": "Output the result as JSON.", "env": "SHOPIFY_FLAG_JSON", + "hidden": false, "name": "json", "type": "boolean" }, @@ -1877,8 +1879,10 @@ }, "json": { "allowNo": false, - "description": "Output the versions list as JSON.", + "char": "j", + "description": "Output the result as JSON.", "env": "SHOPIFY_FLAG_JSON", + "hidden": false, "name": "json", "type": "boolean" }, @@ -5047,8 +5051,10 @@ }, "json": { "allowNo": false, - "description": "Output the theme info as JSON.", + "char": "j", + "description": "Output the result as JSON.", "env": "SHOPIFY_FLAG_JSON", + "hidden": false, "name": "json", "type": "boolean" }, @@ -5236,8 +5242,10 @@ }, "json": { "allowNo": false, - "description": "Output the theme list as JSON.", + "char": "j", + "description": "Output the result as JSON.", "env": "SHOPIFY_FLAG_JSON", + "hidden": false, "name": "json", "type": "boolean" }, @@ -5710,8 +5718,9 @@ "json": { "allowNo": false, "char": "j", - "description": "Output JSON instead of a UI.", + "description": "Output the result as JSON.", "env": "SHOPIFY_FLAG_JSON", + "hidden": false, "name": "json", "type": "boolean" }, diff --git a/packages/theme/src/cli/commands/theme/info.ts b/packages/theme/src/cli/commands/theme/info.ts index 18525ac49df..009715f7342 100644 --- a/packages/theme/src/cli/commands/theme/info.ts +++ b/packages/theme/src/cli/commands/theme/info.ts @@ -5,7 +5,7 @@ import ThemeCommand from '../../utilities/theme-command.js' import {Flags} from '@oclif/core' import {ensureAuthenticatedThemes} from '@shopify/cli-kit/node/session' import {AbortError} from '@shopify/cli-kit/node/error' -import {globalFlags} from '@shopify/cli-kit/node/cli' +import {globalFlags, jsonFlag} from '@shopify/cli-kit/node/cli' import {formatSection, outputInfo} from '@shopify/cli-kit/node/output' export default class Info extends ThemeCommand { @@ -14,6 +14,7 @@ export default class Info extends ThemeCommand { static flags = { ...globalFlags, + ...jsonFlag, store: themeFlags.store, password: themeFlags.password, environment: themeFlags.environment, @@ -27,11 +28,6 @@ export default class Info extends ThemeCommand { description: 'Theme ID or name of the remote theme.', env: 'SHOPIFY_FLAG_THEME_ID', }), - json: Flags.boolean({ - description: 'Output the theme info as JSON.', - default: false, - env: 'SHOPIFY_FLAG_JSON', - }), } public async run(): Promise { diff --git a/packages/theme/src/cli/commands/theme/list.ts b/packages/theme/src/cli/commands/theme/list.ts index 24167e2e6b2..916fa8517eb 100644 --- a/packages/theme/src/cli/commands/theme/list.ts +++ b/packages/theme/src/cli/commands/theme/list.ts @@ -5,13 +5,14 @@ import {themeFlags} from '../../flags.js' import ThemeCommand from '../../utilities/theme-command.js' import {Flags} from '@oclif/core' import {ensureAuthenticatedThemes} from '@shopify/cli-kit/node/session' -import {globalFlags} from '@shopify/cli-kit/node/cli' +import {globalFlags, jsonFlag} from '@shopify/cli-kit/node/cli' export default class List extends ThemeCommand { static description = 'Lists the themes in your store, along with their IDs and statuses.' static flags = { ...globalFlags, + ...jsonFlag, password: themeFlags.password, store: themeFlags.store, role: Flags.custom({ @@ -27,11 +28,6 @@ export default class List extends ThemeCommand { description: 'Only list theme with the given ID.', env: 'SHOPIFY_FLAG_ID', }), - json: Flags.boolean({ - description: 'Output the theme list as JSON.', - default: false, - env: 'SHOPIFY_FLAG_JSON', - }), environment: themeFlags.environment, } diff --git a/packages/theme/src/cli/commands/theme/push.ts b/packages/theme/src/cli/commands/theme/push.ts index 99cbf83c3c1..e2925507a79 100644 --- a/packages/theme/src/cli/commands/theme/push.ts +++ b/packages/theme/src/cli/commands/theme/push.ts @@ -2,7 +2,7 @@ import {themeFlags} from '../../flags.js' import ThemeCommand from '../../utilities/theme-command.js' import {push, PushFlags} from '../../services/push.js' import {Flags} from '@oclif/core' -import {globalFlags} from '@shopify/cli-kit/node/cli' +import {globalFlags, jsonFlag} from '@shopify/cli-kit/node/cli' export default class Push extends ThemeCommand { static summary = 'Uploads your local theme files to the connected store, overwriting the remote version if specified.' @@ -43,6 +43,7 @@ export default class Push extends ThemeCommand { static flags = { ...globalFlags, ...themeFlags, + ...jsonFlag, theme: Flags.string({ char: 't', description: 'Theme ID or name of the remote theme.', @@ -80,11 +81,6 @@ export default class Push extends ThemeCommand { multiple: true, env: 'SHOPIFY_FLAG_IGNORE', }), - json: Flags.boolean({ - char: 'j', - description: 'Output JSON instead of a UI.', - env: 'SHOPIFY_FLAG_JSON', - }), 'allow-live': Flags.boolean({ char: 'a', description: 'Allow push to a live theme.', diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 900c18c9f55..62fc3f98567 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -200,6 +200,9 @@ importers: http-proxy: specifier: 1.18.1 version: 1.18.1 + ignore: + specifier: 6.0.2 + version: 6.0.2 proper-lockfile: specifier: 4.1.2 version: 4.1.2 @@ -15823,7 +15826,15 @@ snapshots: ignore@5.3.1: {} - immutable@3.7.6: {} + /ignore@6.0.2: + resolution: {integrity: sha512-InwqeHHN2XpumIkMvpl/DCJVrAHgCsG5+cn1XlnLWGwtZBm8QJfSusItfrwx81CTp5agNZqpKU2J/ccC5nGT4A==} + engines: {node: '>= 4'} + dev: false + + /immutable@3.7.6: + resolution: {integrity: sha512-AizQPcaofEtO11RZhPPHBOJRdo/20MKQF9mBLnVkBoyHi1/zXK8fzVdnEpSV9gxqtnh6Qomfp3F0xT5qP/vThw==} + engines: {node: '>=0.8.0'} + dev: true immutable@4.3.5: {}