Skip to content

Commit dd374c4

Browse files
authored
Merge pull request #5221 from Shopify/jm/render_error_page
[Feature] Render error page when `theme dev` encounters asset upload error
2 parents b6457f4 + 599cdd2 commit dd374c4

File tree

16 files changed

+221
-25
lines changed

16 files changed

+221
-25
lines changed

.changeset/bright-impalas-float.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
'@shopify/theme': minor
3+
---
4+
5+
Render error overlay when `theme dev` encounters asset upload errors. A 500 status code is returned when the error overlay is rendered.

docs-shopify.dev/commands/interfaces/theme-dev.interface.ts

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,15 @@ export interface themedev {
66
*/
77
'-e, --environment <value>'?: string
88

9+
/**
10+
* Controls the visibility of the error overlay when an theme asset upload fails:
11+
- silent Prevents the error overlay from appearing.
12+
- default Displays the error overlay.
13+
14+
* @environment SHOPIFY_FLAG_ERROR_OVERLAY
15+
*/
16+
'--error-overlay <value>'?: string
17+
918
/**
1019
* Set which network interface the web server listens on. The default value is 127.0.0.1.
1120
* @environment SHOPIFY_FLAG_HOST

docs-shopify.dev/generated/generated_docs_data.json

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4960,6 +4960,15 @@
49604960
"name": "themedev",
49614961
"description": "",
49624962
"members": [
4963+
{
4964+
"filePath": "docs-shopify.dev/commands/interfaces/theme-dev.interface.ts",
4965+
"syntaxKind": "PropertySignature",
4966+
"name": "--error-overlay <value>",
4967+
"value": "string",
4968+
"description": "Controls the visibility of the error overlay when an theme asset upload fails: - silent Prevents the error overlay from appearing. - default Displays the error overlay.",
4969+
"isOptional": true,
4970+
"environmentValue": "SHOPIFY_FLAG_ERROR_OVERLAY"
4971+
},
49634972
{
49644973
"filePath": "docs-shopify.dev/commands/interfaces/theme-dev.interface.ts",
49654974
"syntaxKind": "PropertySignature",
@@ -5114,7 +5123,7 @@
51145123
"environmentValue": "SHOPIFY_FLAG_IGNORE"
51155124
}
51165125
],
5117-
"value": "export interface themedev {\n /**\n * The environment to apply to the current command.\n * @environment SHOPIFY_FLAG_ENVIRONMENT\n */\n '-e, --environment <value>'?: string\n\n /**\n * Set which network interface the web server listens on. The default value is 127.0.0.1.\n * @environment SHOPIFY_FLAG_HOST\n */\n '--host <value>'?: string\n\n /**\n * Skip hot reloading any files that match the specified pattern.\n * @environment SHOPIFY_FLAG_IGNORE\n */\n '-x, --ignore <value>'?: string\n\n /**\n * The live reload mode switches the server behavior when a file is modified:\n- hot-reload Hot reloads local changes to CSS and sections (default)\n- full-page Always refreshes the entire page\n- off Deactivate live reload\n * @environment SHOPIFY_FLAG_LIVE_RELOAD\n */\n '--live-reload <value>'?: string\n\n /**\n * Disable color output.\n * @environment SHOPIFY_FLAG_NO_COLOR\n */\n '--no-color'?: ''\n\n /**\n * Prevents files from being deleted in the remote theme when a file has been deleted locally. This applies to files that are deleted while the command is running, and files that have been deleted locally before the command is run.\n * @environment SHOPIFY_FLAG_NODELETE\n */\n '-n, --nodelete'?: ''\n\n /**\n * The file path or URL. The file path is to a file that you want updated on idle. The URL path is where you want a webhook posted to report on file changes.\n * @environment SHOPIFY_FLAG_NOTIFY\n */\n '--notify <value>'?: string\n\n /**\n * Hot reload only files that match the specified pattern.\n * @environment SHOPIFY_FLAG_ONLY\n */\n '-o, --only <value>'?: string\n\n /**\n * Automatically launch the theme preview in your default web browser.\n * @environment SHOPIFY_FLAG_OPEN\n */\n '--open'?: ''\n\n /**\n * Password generated from the Theme Access app.\n * @environment SHOPIFY_CLI_THEME_TOKEN\n */\n '--password <value>'?: string\n\n /**\n * The path to your theme directory.\n * @environment SHOPIFY_FLAG_PATH\n */\n '--path <value>'?: string\n\n /**\n * Local port to serve theme preview from.\n * @environment SHOPIFY_FLAG_PORT\n */\n '--port <value>'?: 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 <value>'?: string\n\n /**\n * The password for storefronts with password protection.\n * @environment SHOPIFY_FLAG_STORE_PASSWORD\n */\n '--store-password <value>'?: string\n\n /**\n * Theme ID or name of the remote theme.\n * @environment SHOPIFY_FLAG_THEME_ID\n */\n '-t, --theme <value>'?: string\n\n /**\n * Synchronize Theme Editor updates in the local theme files.\n * @environment SHOPIFY_FLAG_THEME_EDITOR_SYNC\n */\n '--theme-editor-sync'?: ''\n\n /**\n * Increase the verbosity of the output.\n * @environment SHOPIFY_FLAG_VERBOSE\n */\n '--verbose'?: ''\n}"
5126+
"value": "export interface themedev {\n /**\n * The environment to apply to the current command.\n * @environment SHOPIFY_FLAG_ENVIRONMENT\n */\n '-e, --environment <value>'?: string\n\n /**\n * Controls the visibility of the error overlay when an theme asset upload fails:\n- silent Prevents the error overlay from appearing.\n- default Displays the error overlay.\n \n * @environment SHOPIFY_FLAG_ERROR_OVERLAY\n */\n '--error-overlay <value>'?: string\n\n /**\n * Set which network interface the web server listens on. The default value is 127.0.0.1.\n * @environment SHOPIFY_FLAG_HOST\n */\n '--host <value>'?: string\n\n /**\n * Skip hot reloading any files that match the specified pattern.\n * @environment SHOPIFY_FLAG_IGNORE\n */\n '-x, --ignore <value>'?: string\n\n /**\n * The live reload mode switches the server behavior when a file is modified:\n- hot-reload Hot reloads local changes to CSS and sections (default)\n- full-page Always refreshes the entire page\n- off Deactivate live reload\n * @environment SHOPIFY_FLAG_LIVE_RELOAD\n */\n '--live-reload <value>'?: string\n\n /**\n * Disable color output.\n * @environment SHOPIFY_FLAG_NO_COLOR\n */\n '--no-color'?: ''\n\n /**\n * Prevents files from being deleted in the remote theme when a file has been deleted locally. This applies to files that are deleted while the command is running, and files that have been deleted locally before the command is run.\n * @environment SHOPIFY_FLAG_NODELETE\n */\n '-n, --nodelete'?: ''\n\n /**\n * The file path or URL. The file path is to a file that you want updated on idle. The URL path is where you want a webhook posted to report on file changes.\n * @environment SHOPIFY_FLAG_NOTIFY\n */\n '--notify <value>'?: string\n\n /**\n * Hot reload only files that match the specified pattern.\n * @environment SHOPIFY_FLAG_ONLY\n */\n '-o, --only <value>'?: string\n\n /**\n * Automatically launch the theme preview in your default web browser.\n * @environment SHOPIFY_FLAG_OPEN\n */\n '--open'?: ''\n\n /**\n * Password generated from the Theme Access app.\n * @environment SHOPIFY_CLI_THEME_TOKEN\n */\n '--password <value>'?: string\n\n /**\n * The path to your theme directory.\n * @environment SHOPIFY_FLAG_PATH\n */\n '--path <value>'?: string\n\n /**\n * Local port to serve theme preview from.\n * @environment SHOPIFY_FLAG_PORT\n */\n '--port <value>'?: 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 <value>'?: string\n\n /**\n * The password for storefronts with password protection.\n * @environment SHOPIFY_FLAG_STORE_PASSWORD\n */\n '--store-password <value>'?: string\n\n /**\n * Theme ID or name of the remote theme.\n * @environment SHOPIFY_FLAG_THEME_ID\n */\n '-t, --theme <value>'?: string\n\n /**\n * Synchronize Theme Editor updates in the local theme files.\n * @environment SHOPIFY_FLAG_THEME_EDITOR_SYNC\n */\n '--theme-editor-sync'?: ''\n\n /**\n * Increase the verbosity of the output.\n * @environment SHOPIFY_FLAG_VERBOSE\n */\n '--verbose'?: ''\n}"
51185127
}
51195128
}
51205129
}

packages/cli/README.md

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1796,9 +1796,10 @@ Uploads the current theme as a development theme to the connected store, then pr
17961796

17971797
```
17981798
USAGE
1799-
$ shopify theme dev [-e <value>] [--host <value>] [-x <value>] [--live-reload hot-reload|full-page|off]
1800-
[--no-color] [-n] [--notify <value>] [-o <value>] [--open] [--password <value>] [--path <value>] [--port <value>]
1801-
[-s <value>] [--store-password <value>] [-t <value>] [--theme-editor-sync] [--verbose]
1799+
$ shopify theme dev [-e <value>] [--error-overlay silent|default] [--host <value>] [-x <value>]
1800+
[--live-reload hot-reload|full-page|off] [--no-color] [-n] [--notify <value>] [-o <value>] [--open] [--password
1801+
<value>] [--path <value>] [--port <value>] [-s <value>] [--store-password <value>] [-t <value>]
1802+
[--theme-editor-sync] [--verbose]
18021803
18031804
FLAGS
18041805
-e, --environment=<value>...
@@ -1821,6 +1822,13 @@ FLAGS
18211822
-x, --ignore=<value>...
18221823
Skip hot reloading any files that match the specified pattern.
18231824
1825+
--error-overlay=<option>
1826+
[default: default] Controls the visibility of the error overlay when an theme asset upload fails:
1827+
- silent Prevents the error overlay from appearing.
1828+
- default Displays the error overlay.
1829+
1830+
<options: silent|default>
1831+
18241832
--host=<value>
18251833
Set which network interface the web server listens on. The default value is 127.0.0.1.
18261834

packages/cli/oclif.manifest.json

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5123,6 +5123,19 @@
51235123
"name": "environment",
51245124
"type": "option"
51255125
},
5126+
"error-overlay": {
5127+
"default": "default",
5128+
"description": "Controls the visibility of the error overlay when an theme asset upload fails:\n- silent Prevents the error overlay from appearing.\n- default Displays the error overlay.\n ",
5129+
"env": "SHOPIFY_FLAG_ERROR_OVERLAY",
5130+
"hasDynamicHelp": false,
5131+
"multiple": false,
5132+
"name": "error-overlay",
5133+
"options": [
5134+
"silent",
5135+
"default"
5136+
],
5137+
"type": "option"
5138+
},
51265139
"force": {
51275140
"allowNo": false,
51285141
"char": "f",
@@ -6402,6 +6415,19 @@
64026415
"name": "environment",
64036416
"type": "option"
64046417
},
6418+
"error-overlay": {
6419+
"default": "default",
6420+
"description": "Controls the visibility of the error overlay when an theme asset upload fails:\n- silent Prevents the error overlay from appearing.\n- default Displays the error overlay.\n ",
6421+
"env": "SHOPIFY_FLAG_ERROR_OVERLAY",
6422+
"hasDynamicHelp": false,
6423+
"multiple": false,
6424+
"name": "error-overlay",
6425+
"options": [
6426+
"silent",
6427+
"default"
6428+
],
6429+
"type": "option"
6430+
},
64056431
"force": {
64066432
"allowNo": false,
64076433
"char": "f",

packages/theme/src/cli/commands/theme/dev.ts

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ import {Flags} from '@oclif/core'
99
import {globalFlags} from '@shopify/cli-kit/node/cli'
1010
import {Theme} from '@shopify/cli-kit/node/themes/types'
1111
import {ensureAuthenticatedThemes} from '@shopify/cli-kit/node/session'
12-
import type {LiveReload} from '../../utilities/theme-environment/types.js'
12+
import type {ErrorOverlayMode, LiveReload} from '../../utilities/theme-environment/types.js'
1313

1414
export default class Dev extends ThemeCommand {
1515
static summary =
@@ -54,6 +54,15 @@ You can run this command only in a directory that matches the [default Shopify t
5454
options: ['hot-reload', 'full-page', 'off'],
5555
env: 'SHOPIFY_FLAG_LIVE_RELOAD',
5656
}),
57+
'error-overlay': Flags.string({
58+
description: `Controls the visibility of the error overlay when an theme asset upload fails:
59+
- silent Prevents the error overlay from appearing.
60+
- default Displays the error overlay.
61+
`,
62+
options: ['silent', 'default'],
63+
default: 'default',
64+
env: 'SHOPIFY_FLAG_ERROR_OVERLAY',
65+
}),
5766
poll: Flags.boolean({
5867
hidden: true,
5968
description: 'Force polling to detect file changes.',
@@ -147,6 +156,7 @@ You can run this command only in a directory that matches the [default Shopify t
147156
host: flags.host,
148157
port: flags.port,
149158
'live-reload': flags['live-reload'] as LiveReload,
159+
'error-overlay': flags['error-overlay'] as ErrorOverlayMode,
150160
force: flags.force,
151161
open: flags.open,
152162
'theme-editor-sync': flags['theme-editor-sync'],

packages/theme/src/cli/services/dev.test.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,7 @@ describe('dev', () => {
5050
noDelete: false,
5151
ignore: [],
5252
only: [],
53+
'error-overlay': 'default',
5354
}
5455

5556
const session: DevServerSession = {
@@ -101,6 +102,7 @@ describe('dev', () => {
101102
ignore: [],
102103
noDelete: false,
103104
only: [],
105+
errorOverlay: 'default',
104106
},
105107
})
106108
})

packages/theme/src/cli/services/dev.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import {hasRequiredThemeDirectories, mountThemeFileSystem} from '../utilities/theme-fs.js'
22
import {ensureDirectoryConfirmed} from '../utilities/theme-ui.js'
33
import {setupDevServer} from '../utilities/theme-environment/theme-environment.js'
4-
import {DevServerContext, LiveReload} from '../utilities/theme-environment/types.js'
4+
import {DevServerContext, ErrorOverlayMode, LiveReload} from '../utilities/theme-environment/types.js'
55
import {isStorefrontPasswordProtected} from '../utilities/theme-environment/storefront-session.js'
66
import {ensureValidPassword} from '../utilities/theme-environment/storefront-password-prompt.js'
77
import {emptyThemeExtFileSystem} from '../utilities/theme-fs-empty.js'
@@ -31,6 +31,7 @@ export interface DevOptions {
3131
force: boolean
3232
'theme-editor-sync': boolean
3333
'live-reload': LiveReload
34+
'error-overlay': ErrorOverlayMode
3435
noDelete: boolean
3536
ignore: string[]
3637
only: string[]
@@ -90,6 +91,7 @@ export async function dev(options: DevOptions) {
9091
noDelete: options.noDelete,
9192
ignore: options.ignore,
9293
only: options.only,
94+
errorOverlay: options['error-overlay'],
9395
},
9496
}
9597

Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,92 @@
1+
interface Error {
2+
message: string
3+
code: string
4+
}
5+
6+
const POLARIS_STYLESHEET_URL = 'https://unpkg.com/@shopify/polaris@13.9.2/build/esm/styles.css'
7+
8+
function escapeHtml(unsafe: string) {
9+
return unsafe
10+
.replace(/&/g, '&amp;')
11+
.replace(/</g, '&lt;')
12+
.replace(/>/g, '&gt;')
13+
.replace(/"/g, '&quot;')
14+
.replace(/'/g, '&#039;')
15+
}
16+
17+
export function getErrorPage(options: {title: string; header: string; errors: Error[]}) {
18+
const html = String.raw
19+
return html`<!DOCTYPE html>
20+
<html>
21+
<head>
22+
<title>${options.title ?? 'Upload Errors'}</title>
23+
<link rel="stylesheet" href="${POLARIS_STYLESHEET_URL}" />
24+
</head>
25+
<body>
26+
<div style="display: flex; justify-content: center; padding-top: 2rem;">
27+
<div style="width: 80%;">
28+
<div class="Polaris-Banner Polaris-Banner--withinPage" tabindex="0" role="alert" aria-live="polite">
29+
<div class="Polaris-Box" style="--pc-box-width:100%">
30+
<div
31+
class="Polaris-BlockStack"
32+
style="--pc-block-stack-align:space-between;--pc-block-stack-order:column"
33+
>
34+
<div
35+
class="Polaris-Box"
36+
style="--pc-box-color: var(--p-color-text-critical-on-bg-fill); --pc-box-background: var(--p-color-bg-fill-critical); --pc-box-padding-block-start-xs: var(--p-space-300); --pc-box-padding-block-end-xs: var(--p-space-300); --pc-box-padding-inline-start-xs: var(--p-space-300); --pc-box-padding-inline-end-xs: var(--p-space-300); --pc-box-border-start-start-radius: var(--p-border-radius-300); --pc-box-border-start-end-radius: var(--p-border-radius-300);"
37+
>
38+
<div
39+
class="Polaris-InlineStack"
40+
style="--pc-inline-stack-align:space-between;--pc-inline-stack-block-align:center;--pc-inline-stack-wrap:nowrap;--pc-inline-stack-gap-xs:var(--p-space-200);--pc-inline-stack-flex-direction-xs:row"
41+
>
42+
<div
43+
class="Polaris-InlineStack"
44+
style="--pc-inline-stack-wrap:nowrap;--pc-inline-stack-gap-xs:var(--p-space-100);--pc-inline-stack-flex-direction-xs:row"
45+
>
46+
<span class="Polaris-Banner--textCriticalOnBgFill">
47+
<span class="Polaris-Icon">
48+
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20">
49+
<path d="M10 6a.75.75 0 0 1 .75.75v3.5a.75.75 0 0 1-1.5 0v-3.5a.75.75 0 0 1 .75-.75Z" />
50+
<path d="M11 13a1 1 0 1 1-2 0 1 1 0 0 1 2 0Z" />
51+
<path
52+
fill-rule="evenodd"
53+
d="M17 10a7 7 0 1 1-14 0 7 7 0 0 1 14 0Zm-1.5 0a5.5 5.5 0 1 1-11 0 5.5 5.5 0 0 1 11 0Z"
54+
/>
55+
</svg>
56+
</span>
57+
</span>
58+
<h2 class="Polaris-Text--root Polaris-Text--headingSm Polaris-Text--break">
59+
${options.header}
60+
</h2>
61+
</div>
62+
</div>
63+
</div>
64+
<div
65+
class="Polaris-Box"
66+
style="--pc-box-padding-block-start-xs:var(--p-space-300);--pc-box-padding-block-end-xs:var(--p-space-300);--pc-box-padding-block-end-md:var(--p-space-400);--pc-box-padding-inline-start-xs:var(--p-space-300);--pc-box-padding-inline-start-md:var(--p-space-400);--pc-box-padding-inline-end-xs:var(--p-space-300);--pc-box-padding-inline-end-md:var(--p-space-400)"
67+
>
68+
<div
69+
class="Polaris-BlockStack"
70+
style="--pc-block-stack-order:column;--pc-block-stack-gap-xs:var(--p-space-300)"
71+
>
72+
${options.errors
73+
.map(
74+
(error) => `
75+
<div>
76+
<span class="Polaris-Text--root Polaris-Text--headingSm">${escapeHtml(error.message)}</span>
77+
<ul class="Polaris-List">
78+
<li class="Polaris-List__Item">${escapeHtml(error.code)}</li>
79+
</ul>
80+
</div>`,
81+
)
82+
.join('')}
83+
</div>
84+
</div>
85+
</div>
86+
</div>
87+
</div>
88+
</div>
89+
</div>
90+
</body>
91+
</html>`
92+
}

packages/theme/src/cli/utilities/theme-environment/hot-reload/server.test.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -330,6 +330,7 @@ function createTestContext(options?: {files?: [string, string][]}) {
330330
liveReload: 'hot-reload',
331331
open: false,
332332
themeEditorSync: false,
333+
errorOverlay: 'default',
333334
},
334335
}
335336

0 commit comments

Comments
 (0)