From e6c7950d693bde40d921de2dcd033d48f0f51495 Mon Sep 17 00:00:00 2001 From: gonzaloriestra <14979109+gonzaloriestra@users.noreply.github.com> Date: Fri, 12 Jun 2026 00:23:32 +0000 Subject: [PATCH] [Performance] Memoize isShopify The `isShopify` function performs a disk check to see if the `dev` executable is present. This function is called multiple times during the CLI lifecycle, including for every analytics event. Memoizing the result avoids redundant disk I/O, improving performance especially on systems with slower disks or under heavy load. Summary: - Memoizes the result of `isShopify` when called with the default `process.env`. - Uses a module-level promise to handle concurrent calls correctly. - Exposes `_resetIsShopify` for test isolation. - Updates unit tests to verify memoization behavior. --- .../src/public/node/context/local.test.ts | 19 +++++++++++++++++- .../cli-kit/src/public/node/context/local.ts | 20 +++++++++++++++++++ 2 files changed, 38 insertions(+), 1 deletion(-) diff --git a/packages/cli-kit/src/public/node/context/local.test.ts b/packages/cli-kit/src/public/node/context/local.test.ts index abe71bb52b7..133568f4280 100644 --- a/packages/cli-kit/src/public/node/context/local.test.ts +++ b/packages/cli-kit/src/public/node/context/local.test.ts @@ -10,11 +10,12 @@ import { macAddress, getThemeKitAccessDomain, opentelemetryDomain, + _resetIsShopify, } from './local.js' import {fileExists} from '../fs.js' import {exec} from '../system.js' -import {afterEach, expect, describe, vi, test} from 'vitest' +import {afterEach, beforeEach, expect, describe, vi, test} from 'vitest' vi.mock('../fs.js') vi.mock('../system.js') @@ -97,6 +98,10 @@ describe('isDevelopment', () => { }) describe('isShopify', () => { + beforeEach(() => { + _resetIsShopify() + }) + test('returns false when the SHOPIFY_RUN_AS_USER env. variable is truthy', async () => { // Given const env = {SHOPIFY_RUN_AS_USER: '1'} @@ -120,6 +125,18 @@ describe('isShopify', () => { // When await expect(isShopify()).resolves.toBe(true) }) + + test('memoizes the result', async () => { + // Given + vi.mocked(fileExists).mockResolvedValue(true) + + // When + await isShopify() + await isShopify() + + // Then + expect(fileExists).toHaveBeenCalledTimes(1) + }) }) describe('hasGit', () => { diff --git a/packages/cli-kit/src/public/node/context/local.ts b/packages/cli-kit/src/public/node/context/local.ts index ad816399db9..a6869e4b1bc 100644 --- a/packages/cli-kit/src/public/node/context/local.ts +++ b/packages/cli-kit/src/public/node/context/local.ts @@ -44,6 +44,11 @@ let memoizedIsVerbose: boolean | undefined */ let memoizedIsUnitTest: boolean | undefined +/** + * Memoized value for the shopify check. + */ +let memoizedIsShopify: Promise | undefined + /** * Returns true if the CLI is running in debug mode. * @@ -77,6 +82,14 @@ export function isVerbose(env = process.env): boolean { * @returns True if the CLI is used in a Shopify environment. */ export async function isShopify(env = process.env): Promise { + if (env === process.env) { + // Memoize the result to avoid repeated disk checks for the 'dev' executable. + return (memoizedIsShopify ??= checkIsShopify(env)) + } + return checkIsShopify(env) +} + +async function checkIsShopify(env: NodeJS.ProcessEnv): Promise { if (Object.prototype.hasOwnProperty.call(env, environmentVariables.runAsUser)) { return !isTruthy(env[environmentVariables.runAsUser]) } @@ -325,4 +338,11 @@ export function opentelemetryDomain(env = process.env): string { return isSet(domain) ? domain : 'https://otlp-http-production-cli.shopifysvc.com' } +/** + * Resets the memoized value for the shopify check. + */ +export function _resetIsShopify(): void { + memoizedIsShopify = undefined +} + export type CIMetadata = Metadata