Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add theme profile command #5109

Open
wants to merge 41 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 32 commits
Commits
Show all changes
41 commits
Select commit Hold shift + click to select a range
bab02b0
Setup `theme profile` command
macournoyer Dec 11, 2024
037a0fa
Output profiling JSON data to stdout
macournoyer Dec 13, 2024
45bb9f4
Open theme profile in Speedscope by default
macournoyer Dec 16, 2024
1347120
Add token auth to theme profile command
macournoyer Dec 16, 2024
dbbdcf6
Refresh manifests
macournoyer Dec 16, 2024
7559132
pnpm update-docs
macournoyer Dec 16, 2024
0ab1b0f
Update lock file
macournoyer Dec 16, 2024
60fe55c
Add knip entry for speedscope
macournoyer Dec 16, 2024
2495c6b
Add new generated doc files I missed
macournoyer Dec 17, 2024
b65e554
Switch to using mime type for Liquid profiling
macournoyer Dec 17, 2024
e981668
Add tests for theme profile command
macournoyer Dec 17, 2024
34a2184
Add more doc to theme profile
macournoyer Dec 17, 2024
2141fc6
Add more validation around theme profile command
macournoyer Dec 18, 2024
b7cda8c
Add .changeset file
macournoyer Jan 6, 2025
91ff0c4
Fix error handling in theme profile command
macournoyer Jan 7, 2025
487ed2f
Adjust tests expectations
macournoyer Jan 7, 2025
a0955f7
Switch `theme profile` to use DevServer rendering
macournoyer Jan 8, 2025
a5ceb0b
Update theme profile tests
macournoyer Jan 8, 2025
de4b963
Ignore typescipt error for conditional in resolveSpeedscope
macournoyer Jan 8, 2025
71f2677
Lint
macournoyer Jan 8, 2025
2d7ef73
more descriptive changelogs
macournoyer Jan 8, 2025
a49cf3e
Swtich to minor version bump
macournoyer Jan 8, 2025
2241a17
Refresh OCLIF manifest
macournoyer Jan 8, 2025
1a56b88
Regen docs
macournoyer Jan 8, 2025
d33d13e
Add waiting rainbow to theme profile command
macournoyer Jan 9, 2025
7d21e67
Add imports for Task in theme profile
macournoyer Jan 9, 2025
6810b3b
Don't use native node os and fs modules
macournoyer Jan 9, 2025
e222c18
Restructure theme profile test following Given/When/Then
macournoyer Jan 9, 2025
5e9ef0b
Lint
macournoyer Jan 9, 2025
93d2018
Try removing speedscope from ignoreDependencies
macournoyer Jan 9, 2025
1e4e69c
Fix speedscope error on `shopify theme profile` (#5178)
karreiro Jan 10, 2025
dbfa8b9
Fix json flag polluting stdout in theme profile
macournoyer Jan 14, 2025
25d4dd2
Add use for theme profile w/o url flag
macournoyer Jan 15, 2025
6e9888c
Add --environment flag to theme profile
macournoyer Jan 15, 2025
f8bb4d6
Move speedscore from deps to assets
frandiox Jan 16, 2025
c1062a9
Fix path in unbundled unit tests
frandiox Jan 16, 2025
8eb3b92
Ignore cli dep in knip
frandiox Jan 16, 2025
c3d1d92
Regenerate types.d.ts
frandiox Jan 16, 2025
12b9c49
Merge pull request #5214 from Shopify/fd-fix-speedscope-bundling
macournoyer Jan 16, 2025
82e8981
Merge branch 'main' into theme-profile
frandiox Jan 16, 2025
88d83f7
Update usage of isStorefrontPasswordProtected
frandiox Jan 16, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/curvy-moons-judge.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@shopify/theme': minor
---

Add `theme profile` command which allows users to get a performance profile for Liquid rendering on a given page
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
theme profile --url /products/classic-leather-jacket
50 changes: 50 additions & 0 deletions docs-shopify.dev/commands/interfaces/theme-profile.interface.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
// This is an autogenerated file. Don't edit this file manually.
export interface themeprofile {
/**
* Output the result as JSON.
* @environment SHOPIFY_FLAG_JSON
*/
'-j, --json'?: ''

/**
* Disable color output.
* @environment SHOPIFY_FLAG_NO_COLOR
*/
'--no-color'?: ''

/**
* Password generated from the Theme Access app.
* @environment SHOPIFY_CLI_THEME_TOKEN
*/
'--password <value>'?: string

/**
* Store URL. It can be the store prefix (example) or the full myshopify.com URL (example.myshopify.com, https://example.myshopify.com).
* @environment SHOPIFY_FLAG_STORE
*/
'-s, --store <value>'?: string

/**
* The password for storefronts with password protection.
* @environment SHOPIFY_FLAG_STORE_PASSWORD
*/
'--store-password <value>'?: string

/**
* Theme ID or name of the remote theme.
* @environment SHOPIFY_FLAG_THEME_ID
*/
'-t, --theme <value>'?: string

/**
* The url to be used as context
* @environment SHOPIFY_FLAG_URL
*/
'--url <value>'?: string

/**
* Increase the verbosity of the output.
* @environment SHOPIFY_FLAG_VERBOSE
*/
'--verbose'?: ''
}
36 changes: 36 additions & 0 deletions docs-shopify.dev/commands/theme-profile.doc.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
// This is an autogenerated file. Don't edit this file manually.
import {ReferenceEntityTemplateSchema} from '@shopify/generate-docs'

const data: ReferenceEntityTemplateSchema = {
name: 'theme profile',
description: `Profile the Shopify Liquid on a given page.
This command will open a web page with the Speedscope profiler detailing the time spent executing Liquid on the given page.`,
overviewPreviewDescription: `Profile the Liquid rendering of a theme page.`,
type: 'command',
isVisualComponent: false,
defaultExample: {
codeblock: {
tabs: [
{
title: 'theme profile',
code: './examples/theme-profile.example.sh',
language: 'bash',
},
],
title: 'theme profile',
},
},
definitions: [
{
title: 'Flags',
description: 'The following flags are available for the `theme profile` command:',
type: 'themeprofile',
},
],
category: 'theme',
related: [
],
}

export default data
110 changes: 110 additions & 0 deletions docs-shopify.dev/generated/generated_docs_data.json
Original file line number Diff line number Diff line change
Expand Up @@ -5806,6 +5806,116 @@
"category": "theme",
"related": []
},
{
"name": "theme profile",
"description": "Profile the Shopify Liquid on a given page.\n\n This command will open a web page with the Speedscope profiler detailing the time spent executing Liquid on the given page.",
"overviewPreviewDescription": "Profile the Liquid rendering of a theme page.",
"type": "command",
"isVisualComponent": false,
"defaultExample": {
"codeblock": {
"tabs": [
{
"title": "theme profile",
"code": "theme profile --url /products/classic-leather-jacket",
"language": "bash"
}
],
"title": "theme profile"
}
},
"definitions": [
{
"title": "Flags",
"description": "The following flags are available for the `theme profile` command:",
"type": "themeprofile",
"typeDefinitions": {
"themeprofile": {
"filePath": "docs-shopify.dev/commands/interfaces/theme-profile.interface.ts",
"name": "themeprofile",
"description": "",
"members": [
{
"filePath": "docs-shopify.dev/commands/interfaces/theme-profile.interface.ts",
"syntaxKind": "PropertySignature",
"name": "--no-color",
"value": "\"\"",
"description": "Disable color output.",
"isOptional": true,
"environmentValue": "SHOPIFY_FLAG_NO_COLOR"
},
{
"filePath": "docs-shopify.dev/commands/interfaces/theme-profile.interface.ts",
"syntaxKind": "PropertySignature",
"name": "--password <value>",
"value": "string",
"description": "Password generated from the Theme Access app.",
"isOptional": true,
"environmentValue": "SHOPIFY_CLI_THEME_TOKEN"
},
{
"filePath": "docs-shopify.dev/commands/interfaces/theme-profile.interface.ts",
"syntaxKind": "PropertySignature",
"name": "--store-password <value>",
"value": "string",
"description": "The password for storefronts with password protection.",
"isOptional": true,
"environmentValue": "SHOPIFY_FLAG_STORE_PASSWORD"
},
{
"filePath": "docs-shopify.dev/commands/interfaces/theme-profile.interface.ts",
"syntaxKind": "PropertySignature",
"name": "--url <value>",
"value": "string",
"description": "The url to be used as context",
"isOptional": true,
"environmentValue": "SHOPIFY_FLAG_URL"
},
{
"filePath": "docs-shopify.dev/commands/interfaces/theme-profile.interface.ts",
"syntaxKind": "PropertySignature",
"name": "--verbose",
"value": "\"\"",
"description": "Increase the verbosity of the output.",
"isOptional": true,
"environmentValue": "SHOPIFY_FLAG_VERBOSE"
},
{
"filePath": "docs-shopify.dev/commands/interfaces/theme-profile.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-profile.interface.ts",
"syntaxKind": "PropertySignature",
"name": "-s, --store <value>",
"value": "string",
"description": "Store URL. It can be the store prefix (example) or the full myshopify.com URL (example.myshopify.com, https://example.myshopify.com).",
"isOptional": true,
"environmentValue": "SHOPIFY_FLAG_STORE"
},
{
"filePath": "docs-shopify.dev/commands/interfaces/theme-profile.interface.ts",
"syntaxKind": "PropertySignature",
"name": "-t, --theme <value>",
"value": "string",
"description": "Theme ID or name of the remote theme.",
"isOptional": true,
"environmentValue": "SHOPIFY_FLAG_THEME_ID"
}
],
"value": "export interface themeprofile {\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 <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 * The url to be used as context\n * @environment SHOPIFY_FLAG_URL\n */\n '--url <value>'?: string\n\n /**\n * Increase the verbosity of the output.\n * @environment SHOPIFY_FLAG_VERBOSE\n */\n '--verbose'?: ''\n}"
}
}
}
],
"category": "theme",
"related": []
},
{
"name": "theme publish",
"description": "Publishes an unpublished theme from your theme library.\n\nIf no theme ID is specified, then you're prompted to select the theme that you want to publish from the list of themes in your store.\n\nYou can run this command only in a directory that matches the [default Shopify theme folder structure](/docs/themes/tools/cli#directory-structure).\n\nIf you want to publish your local theme, then you need to run `shopify theme push` first. You're asked to confirm that you want to publish the specified theme. You can skip this confirmation using the `--force` flag.",
Expand Down
3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -193,7 +193,8 @@
],
"project": "**/*.ts!",
"ignoreDependencies": [
"@ast-grep/napi"
"@ast-grep/napi",
"speedscope"
],
"vite": {
"config": [
Expand Down
29 changes: 29 additions & 0 deletions packages/cli/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,7 @@
* [`shopify theme metafields pull`](#shopify-theme-metafields-pull)
* [`shopify theme open`](#shopify-theme-open)
* [`shopify theme package`](#shopify-theme-package)
* [`shopify theme profile --url /products/classic-leather-jacket`](#shopify-theme-profile---url-productsclassic-leather-jacket)
* [`shopify theme publish`](#shopify-theme-publish)
* [`shopify theme pull`](#shopify-theme-pull)
* [`shopify theme push`](#shopify-theme-push)
Expand Down Expand Up @@ -2085,6 +2086,34 @@ DESCRIPTION
(https://shopify.dev/docs/themes/architecture/config/settings-schema-json) file.
```

## `shopify theme profile --url /products/classic-leather-jacket`

Profile the Liquid rendering of a theme page.

```
USAGE
$ shopify theme profile --url /products/classic-leather-jacket

FLAGS
-j, --json Output the result as JSON.
-s, --store=<value> Store URL. It can be the store prefix (example) or the full myshopify.com URL
(example.myshopify.com, https://example.myshopify.com).
-t, --theme=<value> Theme ID or name of the remote theme.
--no-color Disable color output.
--password=<value> Password generated from the Theme Access app.
--store-password=<value> The password for storefronts with password protection.
--url=<value> [default: /] The url to be used as context
--verbose Increase the verbosity of the output.

DESCRIPTION
Profile the Liquid rendering of a theme page.

Profile the Shopify Liquid on a given page.

This command will open a web page with the Speedscope profiler detailing the time spent executing Liquid on the given
page.
```

## `shopify theme publish`

Set a remote theme as the live theme.
Expand Down
91 changes: 91 additions & 0 deletions packages/cli/oclif.manifest.json
Original file line number Diff line number Diff line change
Expand Up @@ -5826,6 +5826,97 @@
"strict": true,
"summary": "Package your theme into a .zip file, ready to upload to the Online Store."
},
"theme:profile": {
"aliases": [
],
"args": {
},
"customPluginName": "@shopify/theme",
"description": "Profile the Shopify Liquid on a given page.\n\n This command will open a web page with the Speedscope profiler detailing the time spent executing Liquid on the given page.",
"descriptionWithMarkdown": "Profile the Shopify Liquid on a given page.\n\n This command will open a web page with the Speedscope profiler detailing the time spent executing Liquid on the given page.",
"flags": {
"json": {
"allowNo": false,
"char": "j",
"description": "Output the result as JSON.",
"env": "SHOPIFY_FLAG_JSON",
"hidden": false,
"name": "json",
"type": "boolean"
},
"no-color": {
"allowNo": false,
"description": "Disable color output.",
"env": "SHOPIFY_FLAG_NO_COLOR",
"hidden": false,
"name": "no-color",
"type": "boolean"
},
"password": {
"description": "Password generated from the Theme Access app.",
"env": "SHOPIFY_CLI_THEME_TOKEN",
"hasDynamicHelp": false,
"multiple": false,
"name": "password",
"type": "option"
},
"store": {
"char": "s",
"description": "Store URL. It can be the store prefix (example) or the full myshopify.com URL (example.myshopify.com, https://example.myshopify.com).",
"env": "SHOPIFY_FLAG_STORE",
"hasDynamicHelp": false,
"multiple": false,
"name": "store",
"type": "option"
},
"store-password": {
"description": "The password for storefronts with password protection.",
"env": "SHOPIFY_FLAG_STORE_PASSWORD",
"hasDynamicHelp": false,
"multiple": false,
"name": "store-password",
"type": "option"
},
"theme": {
"char": "t",
"description": "Theme ID or name of the remote theme.",
"env": "SHOPIFY_FLAG_THEME_ID",
"hasDynamicHelp": false,
"multiple": false,
"name": "theme",
"type": "option"
},
"url": {
"default": "/",
"description": "The url to be used as context",
"env": "SHOPIFY_FLAG_URL",
"hasDynamicHelp": false,
"multiple": false,
"name": "url",
"type": "option"
},
"verbose": {
"allowNo": false,
"description": "Increase the verbosity of the output.",
"env": "SHOPIFY_FLAG_VERBOSE",
"hidden": false,
"name": "verbose",
"type": "boolean"
}
},
"hasDynamicHelp": false,
"hiddenAliases": [
],
"id": "theme:profile",
"pluginAlias": "@shopify/cli",
"pluginName": "@shopify/cli",
"pluginType": "core",
"strict": true,
"summary": "Profile the Liquid rendering of a theme page.",
"usage": [
"theme profile --url /products/classic-leather-jacket"
]
},
"theme:publish": {
"aliases": [
],
Expand Down
1 change: 1 addition & 0 deletions packages/cli/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,7 @@
},
"dependencies": {
"@ast-grep/napi": "0.33.0",
"speedscope": "1.21.0",
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why is this dependency in both cli and theme package.json's? It should be enough with the theme one

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good question... not sure @karreiro if you had to add this to fix the packaging?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I did a short exploration into this when I had some free time.

I am relatively green to TS / JS so I won't purport to be an expert on the module system ( or have much knowledge here at all yet) - info here

Would definitely prefer not to have to udpate the cli package.json

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@jamesmengo thanks for the exploration! I'm unclear what solution you're suggesting, out of those:

Only supported Node 20+ (could use import.meta.resolve)
Made @shopify/theme public (dependencies would be visible)
Didn't need to access speedscope's files directly

Copy link
Contributor

@isaacroldan isaacroldan Jan 16, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That's actually not the reason why you need it there for require.resolve to work.

We bundle dependencies, once bundled, you can't access specific files with require.resolve. By adding it as a dependency of CLI, you are basically telling the CLI not to bundle this package, which will affect the total size and speed of the CLI installation.

You need to solve this issue in the bundle script, manually copying the files you want to access with require.resolve. (cli/bin/bundle.js).

Sorry but adding dependencies to CLI directly is not allowed as it affects all users performance.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@isaacroldan thanks for the context!

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I should add some lint rule so that teams are warned about this earlier in the dev process and not in the final review, sorry I didn't see it earlier 🙏

"esbuild": "0.24.0"
},
"devDependencies": {
Expand Down
1 change: 1 addition & 0 deletions packages/theme/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@
"@shopify/theme-language-server-node": "2.3.3",
"chokidar": "3.6.0",
"h3": "1.13.0",
"speedscope": "1.21.0",
"yaml": "2.7.0"
},
"devDependencies": {
Expand Down
Loading
Loading