diff --git a/.github/workflows/shopify-cli.yml b/.github/workflows/shopify-cli.yml index 1c8d1108c2..61a4540d4e 100644 --- a/.github/workflows/shopify-cli.yml +++ b/.github/workflows/shopify-cli.yml @@ -113,7 +113,7 @@ jobs: matrix: os: ['ubuntu-latest'] node: ['18.20.3'] - target: ['build', 'type-check', 'lint'] + target: ['build', 'type-check'] steps: - uses: actions/checkout@v3 with: @@ -127,6 +127,34 @@ jobs: - name: ${{ matrix.target }} run: pnpm nx run-many --all --skip-nx-cache --target=${{ matrix.target }} --output-style=stream + pr-platform-agnostic-lint: + name: '[PR] Run lint with Node ${{ matrix.node }} in ${{ matrix.os }}' + runs-on: ${{ matrix.os }} + if: ${{ github.event_name == 'pull_request' || github.event_name == 'merge_group' }} + timeout-minutes: 30 + strategy: + matrix: + os: ['ubuntu-latest'] + node: ['18.20.3'] + steps: + - uses: actions/checkout@v3 + with: + repository: ${{ github.event.pull_request.head.repo.full_name || github.event.repository.full_name }} + ref: ${{ github.event.pull_request.head.ref || github.event.merge_group.head_ref }} + fetch-depth: 1 + - name: Setup deps + uses: ./.github/actions/setup-cli-deps + with: + node-version: ${{ matrix.node }} + - name: Lint + run: pnpm nx run-many --all --skip-nx-cache --target=lint --output-style=stream --format=json -o eslint-report.json + continue-on-error: true + - name: Annotate Code Linting Results + uses: ataylorme/eslint-annotate-action@21a1ba0738d8b732639999029c4ff40b6e121bb4 # pin@v3 + with: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + report-json: "packages/*/eslint-report.json" + pr-platform-agnostic-bundle: name: '[PR] Run bundle with Node ${{ matrix.node }} in ${{ matrix.os }}' runs-on: ${{ matrix.os }} diff --git a/.gitignore b/.gitignore index 854f0c12e8..13df16cc1c 100644 --- a/.gitignore +++ b/.gitignore @@ -166,3 +166,6 @@ testing.mjs # NX .nx + +# ESLint reports +eslint-report.json diff --git a/packages/app/src/cli/models/app/app.ts b/packages/app/src/cli/models/app/app.ts index b5b01d907f..00ed24ad3b 100644 --- a/packages/app/src/cli/models/app/app.ts +++ b/packages/app/src/cli/models/app/app.ts @@ -112,7 +112,7 @@ export function getAppVersionedSchema( allowDynamicallySpecifiedConfigs = false, ): ZodObjectOf> { // eslint-disable-next-line @typescript-eslint/no-explicit-any - const schema = specs.reduce((schema, spec) => spec.contributeToAppConfigurationSchema(schema), AppSchema as any) + const schema = specs.reduce((schema, spec) => spec.contributeToAppConfigurationSchema(schema), AppSchema) if (allowDynamicallySpecifiedConfigs) { return schema.passthrough() diff --git a/packages/app/src/cli/models/app/loader.ts b/packages/app/src/cli/models/app/loader.ts index d5e9e226ce..a5b2d736cf 100644 --- a/packages/app/src/cli/models/app/loader.ts +++ b/packages/app/src/cli/models/app/loader.ts @@ -654,7 +654,7 @@ class AppLoader [`${name}.js`, `${name}.jsx`, `${name}.ts`, `${name}.tsx`]) - .flatMap((fileName) => [`src/${fileName}`, `${fileName}`]) + .flatMap((fileName) => [`src/${fileName}`, fileName]) .map((relativePath) => joinPath(directory, relativePath)) .map(async (sourcePath) => ((await fileExists(sourcePath)) ? sourcePath : undefined)), ) diff --git a/packages/app/src/cli/models/extensions/extension-instance.ts b/packages/app/src/cli/models/extensions/extension-instance.ts index f1b56c4192..52a11c98db 100644 --- a/packages/app/src/cli/models/extensions/extension-instance.ts +++ b/packages/app/src/cli/models/extensions/extension-instance.ts @@ -152,7 +152,7 @@ export class ExtensionInstance, ) { - return subscriptions.reduce((accumulator, subscription) => { + return subscriptions.reduce((accumulator, subscription) => { const existingSubscription = findSubscription(accumulator, subscription) if (existingSubscription) { if (property && subscription?.[property]?.length) { @@ -97,5 +97,5 @@ export function reduceWebhooks( accumulator.push(subscription) } return accumulator - }, [] as WebhookSubscription[]) + }, []) } diff --git a/packages/app/src/cli/models/extensions/specifications/transform/extension_uuid_to_handle.ts b/packages/app/src/cli/models/extensions/specifications/transform/extension_uuid_to_handle.ts index 1db3bbbd24..9e60fa0ae6 100644 --- a/packages/app/src/cli/models/extensions/specifications/transform/extension_uuid_to_handle.ts +++ b/packages/app/src/cli/models/extensions/specifications/transform/extension_uuid_to_handle.ts @@ -13,7 +13,7 @@ type Config = export function extensionUuidToHandle(config: Config, allExtensions: ExtensionRegistration[]) { const uiExtensionHandle = config.ui_extension_handle - if (uiExtensionHandle || 'ui_extension_registration_uuid' in config === false) { + if (uiExtensionHandle || !('ui_extension_registration_uuid' in config)) { return uiExtensionHandle } diff --git a/packages/app/src/cli/models/extensions/specifications/ui_extension.ts b/packages/app/src/cli/models/extensions/specifications/ui_extension.ts index eafa6bc333..384c0ecf7c 100644 --- a/packages/app/src/cli/models/extensions/specifications/ui_extension.ts +++ b/packages/app/src/cli/models/extensions/specifications/ui_extension.ts @@ -100,7 +100,7 @@ Please check the module path for ${target}`.value, ) } - if (uniqueTargets.indexOf(target) === -1) { + if (!uniqueTargets.includes(target)) { uniqueTargets.push(target) } else { duplicateTargets.push(target) diff --git a/packages/app/src/cli/prompts/init/init.ts b/packages/app/src/cli/prompts/init/init.ts index 45a5ebd0e8..f93fda5059 100644 --- a/packages/app/src/cli/prompts/init/init.ts +++ b/packages/app/src/cli/prompts/init/init.ts @@ -118,7 +118,7 @@ const init = async (options: InitOptions): Promise => { } if (branch) { - selectedUrl += `#${branch}` + selectedUrl = `${selectedUrl}#${branch}` } answers.template = selectedUrl || answers.template || defaults.template diff --git a/packages/app/src/cli/services/app-logs/logs-command/poll-app-logs.ts b/packages/app/src/cli/services/app-logs/logs-command/poll-app-logs.ts index f35f0de99b..4d7d8a8332 100644 --- a/packages/app/src/cli/services/app-logs/logs-command/poll-app-logs.ts +++ b/packages/app/src/cli/services/app-logs/logs-command/poll-app-logs.ts @@ -12,7 +12,7 @@ export const pollAppLogs = async ({jwtToken, cursor, filters}: PollOptions): Pro } if (response.status === 401 || response.status === 429 || response.status >= 500) { return { - errors: [{status: response.status, message: `${errorResponse.errors.join(', ')}`}], + errors: [{status: response.status, message: errorResponse.errors.join(', ')}], } } else { throw new AbortError(`${errorResponse.errors.join(', ')} while fetching app logs`) diff --git a/packages/app/src/cli/services/app-logs/logs-command/ui/components/Logs.tsx b/packages/app/src/cli/services/app-logs/logs-command/ui/components/Logs.tsx index 9c505f6930..cdb927f7b5 100644 --- a/packages/app/src/cli/services/app-logs/logs-command/ui/components/Logs.tsx +++ b/packages/app/src/cli/services/app-logs/logs-command/ui/components/Logs.tsx @@ -54,7 +54,7 @@ const Logs: FunctionComponent = ({pollOptions: {jwtToken, filters}, r {prefix.logTimestamp} {`${prefix.storeName.split('.')[0]}`} - {`${prefix.source}`} + {prefix.source} {prefix.status} {prefix.description} diff --git a/packages/app/src/cli/services/build/theme-check.ts b/packages/app/src/cli/services/build/theme-check.ts index 53988a6927..3851078d73 100644 --- a/packages/app/src/cli/services/build/theme-check.ts +++ b/packages/app/src/cli/services/build/theme-check.ts @@ -50,15 +50,9 @@ function formatOffenses(offenses: Offense[]): TokenItem { const codeSnippet = getSnippet(absolutePath, start.line, end.line) // Ensure enough padding between offenses - const offensePadding = `${index === offenses.length - 1 ? '' : '\n\n'}` + const offensePadding = index === offenses.length - 1 ? '' : '\n\n' - return [ - severityToToken(severity), - {bold: `${check}`}, - {subdued: `\n${message}`}, - `\n\n${codeSnippet}`, - offensePadding, - ] + return [severityToToken(severity), {bold: check}, {subdued: `\n${message}`}, `\n\n${codeSnippet}`, offensePadding] }) return offenseBodies.flat() diff --git a/packages/app/src/cli/services/deploy/upload.ts b/packages/app/src/cli/services/deploy/upload.ts index 0a4475583c..cbbf0c4c42 100644 --- a/packages/app/src/cli/services/deploy/upload.ts +++ b/packages/app/src/cli/services/deploy/upload.ts @@ -272,7 +272,7 @@ function generalErrorsSection( } function cliErrorsSections(errors: AppDeploySchema['appDeploy']['userErrors'], identifiers: IdentifiersExtensions) { - return errors.reduce((sections, error) => { + return errors.reduce((sections, error) => { const field = (error.field ?? ['unknown']).join('.').replace('extension_points', 'extensions.targeting') const errorMessage = field === 'base' ? error.message : `${field}: ${error.message}` @@ -343,12 +343,12 @@ function cliErrorsSections(errors: AppDeploySchema['appDeploy']['userErrors'], i }) return sections - }, [] as ErrorCustomSection[]) + }, []) } function partnersErrorsSections(errors: AppDeploySchema['appDeploy']['userErrors']) { return errors - .reduce((sections, error) => { + .reduce<{title: string | undefined; errorCount: number}[]>((sections, error) => { const extensionIdentifier = error.details.find( (detail) => typeof detail.extension_title !== 'undefined', )?.extension_title @@ -365,7 +365,7 @@ function partnersErrorsSections(errors: AppDeploySchema['appDeploy']['userErrors } return sections - }, [] as {title: string | undefined; errorCount: number}[]) + }, []) .map((section) => ({ title: section.title, body: `\n${section.errorCount} error${ diff --git a/packages/app/src/cli/services/dev/extension/payload/store.ts b/packages/app/src/cli/services/dev/extension/payload/store.ts index 44b461502a..251859f191 100644 --- a/packages/app/src/cli/services/dev/extension/payload/store.ts +++ b/packages/app/src/cli/services/dev/extension/payload/store.ts @@ -90,9 +90,11 @@ export class ExtensionsPayloadStore extends EventEmitter { isNewExtensionPointsSchema(foundExtension.extensionPoints) && isNewExtensionPointsSchema(rawPayloadExtension.extensionPoints) ) { - const foundExtensionPointsPayloadMap = foundExtension.extensionPoints.reduce((acc, ex) => { + const foundExtensionPointsPayloadMap = foundExtension.extensionPoints.reduce<{ + [key: string]: DevNewExtensionPointSchema + }>((acc, ex) => { return {...acc, [ex.target]: ex} - }, {} as {[key: string]: DevNewExtensionPointSchema}) + }, {}) rawPayloadExtension.extensionPoints = deepMergeObjects( rawPayloadExtension.extensionPoints, diff --git a/packages/app/src/cli/services/dev/extension/server/middlewares.ts b/packages/app/src/cli/services/dev/extension/server/middlewares.ts index 2eaaadafbd..6eee1d5f8f 100644 --- a/packages/app/src/cli/services/dev/extension/server/middlewares.ts +++ b/packages/app/src/cli/services/dev/extension/server/middlewares.ts @@ -86,7 +86,7 @@ export function getExtensionAssetMiddleware({devOptions}: GetExtensionsMiddlewar }) } - const buildDirectory = extension.outputPath.replace(`${extension.outputFileName}`, '') + const buildDirectory = extension.outputPath.replace(extension.outputFileName, '') return fileServerMiddleware(request, response, next, { filePath: joinPath(buildDirectory, assetPath), diff --git a/packages/app/src/cli/services/dev/processes/app-logs-polling.test.ts b/packages/app/src/cli/services/dev/processes/app-logs-polling.test.ts index 3b712aaad5..11cee7b813 100644 --- a/packages/app/src/cli/services/dev/processes/app-logs-polling.test.ts +++ b/packages/app/src/cli/services/dev/processes/app-logs-polling.test.ts @@ -54,7 +54,7 @@ describe('app-logs-polling', () => { token: TOKEN, } - let subscribeToAppLogs: Mock + let subscribeToAppLogs: Mock let developerPlatformClient: DeveloperPlatformClient let stdout: any let stderr: any diff --git a/packages/app/src/cli/services/function/build.ts b/packages/app/src/cli/services/function/build.ts index 32644218cd..5cd2e9dfa8 100644 --- a/packages/app/src/cli/services/function/build.ts +++ b/packages/app/src/cli/services/function/build.ts @@ -8,7 +8,7 @@ import {hyphenate, camelize} from '@shopify/cli-kit/common/string' import {outputDebug} from '@shopify/cli-kit/node/output' import {exec} from '@shopify/cli-kit/node/system' import {joinPath} from '@shopify/cli-kit/node/path' -import {build as esBuild, BuildResult, BuildOptions} from 'esbuild' +import {build as esBuild, BuildResult} from 'esbuild' import {findPathUp, inTemporaryDirectory, writeFile} from '@shopify/cli-kit/node/fs' import {AbortSignal} from '@shopify/cli-kit/node/abort' import {renderTasks} from '@shopify/cli-kit/node/ui' @@ -192,10 +192,7 @@ export async function installJavy(app: AppInterface) { } export interface JavyBuilder { - bundle( - fun: ExtensionInstance, - options: JSFunctionBuildOptions, - ): Promise> + bundle(fun: ExtensionInstance, options: JSFunctionBuildOptions): Promise compile(fun: ExtensionInstance, options: JSFunctionBuildOptions): Promise } diff --git a/packages/app/src/cli/services/function/ui/components/Replay/hooks/useFunctionWatcher.ts b/packages/app/src/cli/services/function/ui/components/Replay/hooks/useFunctionWatcher.ts index 058084d55c..5c1d940927 100644 --- a/packages/app/src/cli/services/function/ui/components/Replay/hooks/useFunctionWatcher.ts +++ b/packages/app/src/cli/services/function/ui/components/Replay/hooks/useFunctionWatcher.ts @@ -125,7 +125,7 @@ async function runFunctionRunnerWithLogInput( let functionRunnerOutput = '' const customStdout = new Writable({ write(chunk, _encoding, next) { - functionRunnerOutput += chunk + functionRunnerOutput += chunk as string next() }, }) diff --git a/packages/app/src/cli/services/generate.ts b/packages/app/src/cli/services/generate.ts index 1afdcb1186..360765ee94 100644 --- a/packages/app/src/cli/services/generate.ts +++ b/packages/app/src/cli/services/generate.ts @@ -156,7 +156,7 @@ function formatSuccessfulRunMessage( // eslint-disable-next-line @typescript-eslint/no-non-null-assertion options.nextSteps!.push([ 'To preview this extension along with the rest of the project, run', - {command: `${formatPackageManagerCommand(depndencyManager, 'shopify app dev')}`}, + {command: formatPackageManagerCommand(depndencyManager, 'shopify app dev')}, ]) } diff --git a/packages/app/src/cli/services/info.ts b/packages/app/src/cli/services/info.ts index 3f645e7708..657279b701 100644 --- a/packages/app/src/cli/services/info.ts +++ b/packages/app/src/cli/services/info.ts @@ -181,7 +181,7 @@ class AppInfo { if (relevantExtensions[0]) { body += `\n\n${outputContent`${outputToken.subheading(relevantExtensions[0].externalType)}`.value}` relevantExtensions.forEach((extension: TExtension) => { - body += `${outputFormatter(extension)}` + body += outputFormatter(extension) }) } }) @@ -193,7 +193,7 @@ class AppInfo { if (this.app.errors?.isEmpty() === false) { body += `\n\n${outputContent`${outputToken.subheading('Extensions with errors')}`.value}` supportedExtensions.forEach((extension) => { - body += `${this.invalidExtensionSubSection(extension)}` + body += this.invalidExtensionSubSection(extension) }) } return [title, body] @@ -268,6 +268,6 @@ class AppInfo { ['Shell', process.env.SHELL || 'unknown'], ['Node version', process.version], ] - return [title, `${linesToColumns(lines)}`] + return [title, linesToColumns(lines)] } } diff --git a/packages/app/src/cli/services/init/init.ts b/packages/app/src/cli/services/init/init.ts index 7425125ac0..2e91a8491c 100644 --- a/packages/app/src/cli/services/init/init.ts +++ b/packages/app/src/cli/services/init/init.ts @@ -77,7 +77,7 @@ async function init(options: InitOptions) { const repoUrl = githubRepo.branch ? `${githubRepo.baseURL}#${githubRepo.branch}` : githubRepo.baseURL await mkdir(templateDownloadDir) - const tasks: Task[] = [ + const tasks: Task[] = [ { title: `Downloading template from ${repoUrl}`, task: async () => { @@ -229,7 +229,7 @@ async function init(options: InitOptions) { {link: {label: 'Shopify docs', url: 'https://shopify.dev'}}, [ 'For an overview of commands, run', - {command: `${formatPackageManagerCommand(packageManager, 'shopify app', '--help')}`}, + {command: formatPackageManagerCommand(packageManager, 'shopify app', '--help')}, ], ], }) diff --git a/packages/app/src/cli/services/logs.ts b/packages/app/src/cli/services/logs.ts index f2a3183d4b..5d83380586 100644 --- a/packages/app/src/cli/services/logs.ts +++ b/packages/app/src/cli/services/logs.ts @@ -137,7 +137,7 @@ function renderAppLogsConfigInfo( const fileName = configFile ? getAppConfigurationFileName(configFile) : undefined renderInfo({ - headline: `${configFile ? `Using ${fileName} for default values:` : 'Using these settings:'}`, + headline: configFile ? `Using ${fileName} for default values:` : 'Using these settings:', body, }) } diff --git a/packages/app/src/cli/services/webhook/trigger-flags.ts b/packages/app/src/cli/services/webhook/trigger-flags.ts index c06a6cdaa3..3561f73d6f 100644 --- a/packages/app/src/cli/services/webhook/trigger-flags.ts +++ b/packages/app/src/cli/services/webhook/trigger-flags.ts @@ -142,11 +142,11 @@ function equivalentTopic(passedTopic: string, availableTopics: string[]): string export function deliveryMethodForAddress(address: string | undefined): string | undefined { if (!address) return undefined - if (PROTOCOL.PUBSUB.test(address)) { + if (address.startsWith('pubsub:')) { return DELIVERY_METHOD.PUBSUB } - if (PROTOCOL.EVENTBRIDGE.test(address)) { + if (address.startsWith('arn:aws:events:')) { return DELIVERY_METHOD.EVENTBRIDGE } diff --git a/packages/app/src/cli/utilities/app/http-reverse-proxy.ts b/packages/app/src/cli/utilities/app/http-reverse-proxy.ts index c9345d0a1b..52d5616346 100644 --- a/packages/app/src/cli/utilities/app/http-reverse-proxy.ts +++ b/packages/app/src/cli/utilities/app/http-reverse-proxy.ts @@ -120,7 +120,7 @@ function getProxyServerWebsocketUpgradeListener( function getProxyServerRequestListener( rules: {[key: string]: string}, proxy: Server, -): http.RequestListener | undefined { +): http.RequestListener | undefined { return function (req, res) { const target = match(rules, req) if (target) { diff --git a/packages/app/src/cli/utilities/developer-platform-client/partners-client.ts b/packages/app/src/cli/utilities/developer-platform-client/partners-client.ts index 0c2e257ef0..f544b33d88 100644 --- a/packages/app/src/cli/utilities/developer-platform-client/partners-client.ts +++ b/packages/app/src/cli/utilities/developer-platform-client/partners-client.ts @@ -174,7 +174,7 @@ function getAppVars( if (isLaunchable) { return { org: parseInt(org.id, 10), - title: `${name}`, + title: name, appUrl: 'https://example.com', redir: ['https://example.com/api/auth'], requestedAccessScopes: scopesArray ?? [], @@ -183,7 +183,7 @@ function getAppVars( } else { return { org: parseInt(org.id, 10), - title: `${name}`, + title: name, appUrl: MAGIC_URL, redir: [MAGIC_REDIRECT_URL], requestedAccessScopes: scopesArray ?? [], diff --git a/packages/cli-kit/package.json b/packages/cli-kit/package.json index f45b1d10cb..d488bfa045 100644 --- a/packages/cli-kit/package.json +++ b/packages/cli-kit/package.json @@ -50,9 +50,7 @@ "docs:open": "nx docs:open", "lint": "nx lint", "lint:ruby": "nx lint:ruby", - "lint:js": "nx lint:js", "lint:fix": "nx lint:fix", - "lint:js:fix": "nx lint:js:fix", "lint:ruby:fix": "nx lint:ruby:fix", "prepack": "cross-env NODE_ENV=production pnpm nx build && cp ../../README.md README.md", "test": "nx test", diff --git a/packages/cli-kit/project.json b/packages/cli-kit/project.json index 8fcf122e49..aa54c0637b 100644 --- a/packages/cli-kit/project.json +++ b/packages/cli-kit/project.json @@ -49,12 +49,9 @@ }, "lint": { "executor": "nx:run-commands", - "dependsOn": [ - "lint:js" - ], "options": { - "cwd": "packages/cli-kit", - "commands": [] + "command": "pnpm eslint \"src/**/*.{ts,tsx}\"", + "cwd": "packages/cli-kit" } }, "lint:ruby": { @@ -64,24 +61,7 @@ "cwd": "packages/cli-kit/assets/cli-ruby" } }, - "lint:js": { - "executor": "nx:run-commands", - "options": { - "command": "pnpm eslint \"src/**/*.{ts,tsx}\"", - "cwd": "packages/cli-kit" - } - }, "lint:fix": { - "executor": "nx:run-commands", - "dependsOn": [ - "lint:js:fix" - ], - "options": { - "cwd": "packages/cli-kit", - "commands": [] - } - }, - "lint:js:fix": { "executor": "nx:run-commands", "options": { "command": "pnpm eslint 'src/**/*.{ts,tsx}' --fix", diff --git a/packages/cli-kit/src/private/node/api.ts b/packages/cli-kit/src/private/node/api.ts index f4c20f8d19..d4ab5d91cd 100644 --- a/packages/cli-kit/src/private/node/api.ts +++ b/packages/cli-kit/src/private/node/api.ts @@ -63,14 +63,14 @@ async function makeVerboseRequest( duration = Math.round(t1 - t0) if (err instanceof ClientError) { - if (err.response?.headers) { - for (const [key, value] of err.response?.headers as Iterable<[string, string]>) { + if (err.response.headers) { + for (const [key, value] of err.response.headers as Iterable<[string, string]>) { if (responseHeaderIsInteresting(key)) responseHeaders[key] = value } } const sanitizedHeaders = sanitizedHeadersOutput(responseHeaders) - if (err.response.errors?.some((error) => error.extensions?.code === '429') || err.response.status === 429) { + if (err.response.errors?.some((error) => error.extensions.code === '429') || err.response.status === 429) { let delayMs: number | undefined try { @@ -215,10 +215,10 @@ ${result.sanitizedHeaders} outputDebug(`Scheduling retry request #${retriesUsed} to ${result.sanitizedUrl} in ${retryDelayMs} ms`) // eslint-disable-next-line no-await-in-loop - result = await new Promise>((resolve) => + result = await new Promise>((resolve) => { retryOptions.scheduleDelay(() => { resolve(makeVerboseRequest({request, url})) - }, retryDelayMs), - ) + }, retryDelayMs) + }) } } diff --git a/packages/cli-kit/src/private/node/api/headers.ts b/packages/cli-kit/src/private/node/api/headers.ts index e4c9fb9cc2..17781fdefb 100644 --- a/packages/cli-kit/src/private/node/api/headers.ts +++ b/packages/cli-kit/src/private/node/api/headers.ts @@ -57,8 +57,8 @@ export function buildHeaders(token?: string): {[key: string]: string} { } if (token) { const authString = token.match(/^shp(at|ua|ca)/) ? token : `Bearer ${token}` - // eslint-disable-next-line dot-notation - headers['authorization'] = authString + + headers.authorization = authString headers['X-Shopify-Access-Token'] = authString } diff --git a/packages/cli-kit/src/private/node/api/rest.ts b/packages/cli-kit/src/private/node/api/rest.ts index a7e608921f..a2312dae7b 100644 --- a/packages/cli-kit/src/private/node/api/rest.ts +++ b/packages/cli-kit/src/private/node/api/rest.ts @@ -22,7 +22,9 @@ export function restRequestUrl( ? `https://${themeKitAccessDomain}/cli/admin/api/${apiVersion}${path}.json` : `https://${session.storeFqdn}/admin/api/${apiVersion}${path}.json`, ) - Object.entries(searchParams).forEach(([name, value]) => url.searchParams.set(name, value)) + Object.entries(searchParams).forEach(([name, value]) => { + url.searchParams.set(name, value) + }) return url.toString() } diff --git a/packages/cli-kit/src/private/node/session/device-authorization.ts b/packages/cli-kit/src/private/node/session/device-authorization.ts index 77b98fe520..647902f854 100644 --- a/packages/cli-kit/src/private/node/session/device-authorization.ts +++ b/packages/cli-kit/src/private/node/session/device-authorization.ts @@ -96,21 +96,30 @@ export async function pollForDeviceAuthorization(code: string, interval = 5): Pr return new Promise((resolve, reject) => { const onPoll = async () => { const result = await exchangeDeviceCodeForAccessToken(code) - if (!result.isErr()) return resolve(result.value) + if (!result.isErr()) { + resolve(result.value) + return + } const error = result.error ?? 'unknown_failure' outputDebug(outputContent`Polling for device authorization... status: ${error}`) switch (error) { - case 'authorization_pending': - return startPolling() + case 'authorization_pending': { + startPolling() + return + } case 'slow_down': currentIntervalInSeconds += 5 - return startPolling() + { + startPolling() + return + } case 'access_denied': case 'expired_token': - case 'unknown_failure': - return reject(result) + case 'unknown_failure': { + reject(result) + } } } diff --git a/packages/cli-kit/src/private/node/ui.tsx b/packages/cli-kit/src/private/node/ui.tsx index 72254340e4..d4d0441d7b 100644 --- a/packages/cli-kit/src/private/node/ui.tsx +++ b/packages/cli-kit/src/private/node/ui.tsx @@ -79,7 +79,13 @@ const renderString = (element: ReactElement, renderOptions?: RenderOptions): Ins } } -export function handleCtrlC(input: string, key: Key, exit = () => treeKill(process.pid, 'SIGINT')) { +export function handleCtrlC( + input: string, + key: Key, + exit = () => { + treeKill(process.pid, 'SIGINT') + }, +) { if (input === 'c' && key.ctrl) { // Exceptions thrown in hooks aren't caught by our errorHandler. exit() diff --git a/packages/cli-kit/src/private/node/ui/components/ConcurrentOutput.test.tsx b/packages/cli-kit/src/private/node/ui/components/ConcurrentOutput.test.tsx index 3bc9c3bca7..719c2820dc 100644 --- a/packages/cli-kit/src/private/node/ui/components/ConcurrentOutput.test.tsx +++ b/packages/cli-kit/src/private/node/ui/components/ConcurrentOutput.test.tsx @@ -91,7 +91,7 @@ describe('ConcurrentOutput', () => { // Then const logColumns = renderInstance.lastFrame()!.split('│') - expect(logColumns?.length).toBe(3) + expect(logColumns.length).toBe(3) expect(logColumns[2]?.trim()).toEqual(output) }) @@ -119,7 +119,7 @@ describe('ConcurrentOutput', () => { // Then const logColumns = renderInstance.lastFrame()!.split('│') - expect(logColumns?.length).toBe(3) + expect(logColumns.length).toBe(3) expect(logColumns[2]?.trim()).toEqual(output) }) @@ -154,7 +154,7 @@ describe('ConcurrentOutput', () => { // Then const logColumns = unstyled(renderInstance.lastFrame()!).split('│') - expect(logColumns?.length).toBe(3) + expect(logColumns.length).toBe(3) expect(logColumns[1]?.trim()).toEqual(extensionName) }) @@ -193,10 +193,10 @@ describe('ConcurrentOutput', () => { // Then const logLines = unstyled(renderInstance.lastFrame()!).split('\n').filter(Boolean) - expect(logLines?.length).toBe(2) + expect(logLines.length).toBe(2) logLines.forEach((line) => { const logColumns = line.split('│') - expect(logColumns?.length).toBe(3) + expect(logColumns.length).toBe(3) // Including spacing expect(logColumns[1]?.length).toBe(columnSize + 2) }) @@ -224,7 +224,7 @@ describe('ConcurrentOutput', () => { // Then const logColumns = unstyled(renderInstance.lastFrame()!).split('│') - expect(logColumns?.length).toBe(3) + expect(logColumns.length).toBe(3) // 4 is largest prefix, plus spacing expect(logColumns[1]?.length).toBe(4 + 2) }) @@ -249,7 +249,7 @@ describe('ConcurrentOutput', () => { // Then const logColumns = unstyled(renderInstance.lastFrame()!).split('│') - expect(logColumns?.length).toBe(3) + expect(logColumns.length).toBe(3) // 25 is largest column allowed, plus spacing expect(logColumns[1]?.length).toBe(25 + 2) }) diff --git a/packages/cli-kit/src/private/node/ui/components/List.tsx b/packages/cli-kit/src/private/node/ui/components/List.tsx index 632b54644c..2fa9931e10 100644 --- a/packages/cli-kit/src/private/node/ui/components/List.tsx +++ b/packages/cli-kit/src/private/node/ui/components/List.tsx @@ -65,7 +65,7 @@ const List: FunctionComponent = ({ const ListItem: FunctionComponent = ({item, color, bullet, index, ordered}) => { return ( - {`${ordered ? `${index + 1}.` : bullet}`} + {ordered ? `${index + 1}.` : bullet} diff --git a/packages/cli-kit/src/private/node/ui/components/SelectInput.tsx b/packages/cli-kit/src/private/node/ui/components/SelectInput.tsx index 8484f9b75c..0698df0aea 100644 --- a/packages/cli-kit/src/private/node/ui/components/SelectInput.tsx +++ b/packages/cli-kit/src/private/node/ui/components/SelectInput.tsx @@ -226,7 +226,7 @@ function SelectInputInner( } // check that no special modifier (shift, control, etc.) is being pressed - if (enableShortcuts && input.length > 0 && Object.values(key).every((value) => value === false)) { + if (enableShortcuts && input.length > 0 && Object.values(key).every((value) => !value)) { handleShortcuts(input) } else { handleArrows(key) diff --git a/packages/cli-kit/src/private/node/ui/components/Table/Row.tsx b/packages/cli-kit/src/private/node/ui/components/Table/Row.tsx index 1c8dc9ec1d..7e64b1cd25 100644 --- a/packages/cli-kit/src/private/node/ui/components/Table/Row.tsx +++ b/packages/cli-kit/src/private/node/ui/components/Table/Row.tsx @@ -13,12 +13,12 @@ interface RowProps { } function join(elements: T[], separator: (index: number) => TI): (T | TI)[] { - return elements.reduce((elements, element, index) => { + return elements.reduce<(T | TI)[]>((elements, element, index) => { if (elements.length === 0) { return [element] } return [...elements, separator(index), element] - }, [] as (T | TI)[]) + }, []) } const Row = ({rowKey, columns, data, fillerChar, ignoreColumnColor}: RowProps) => { diff --git a/packages/cli-kit/src/private/node/ui/components/Tasks.tsx b/packages/cli-kit/src/private/node/ui/components/Tasks.tsx index ac39831e07..5b71a6f2e0 100644 --- a/packages/cli-kit/src/private/node/ui/components/Tasks.tsx +++ b/packages/cli-kit/src/private/node/ui/components/Tasks.tsx @@ -36,7 +36,7 @@ enum TasksState { async function runTask(task: Task, ctx: TContext) { task.retryCount = 0 task.errors = [] - const retry = task?.retry && task?.retry > 0 ? task.retry + 1 : 1 + const retry = task.retry && task.retry > 0 ? task.retry + 1 : 1 for (let retries = 1; retries <= retry; retries++) { try { diff --git a/packages/cli-kit/src/private/node/ui/hooks/use-abort-signal.ts b/packages/cli-kit/src/private/node/ui/hooks/use-abort-signal.ts index d20070e315..64e908ef34 100644 --- a/packages/cli-kit/src/private/node/ui/hooks/use-abort-signal.ts +++ b/packages/cli-kit/src/private/node/ui/hooks/use-abort-signal.ts @@ -10,7 +10,7 @@ export default function useAbortSignal(abortSignal?: AbortSignal, onAbort: (erro useLayoutEffect(() => { abortSignal?.addEventListener('abort', () => { - const abortWithError = abortSignal?.reason.message === 'AbortError' ? undefined : abortSignal?.reason + const abortWithError = abortSignal.reason.message === 'AbortError' ? undefined : abortSignal.reason onAbort(abortWithError) .then(() => { setIsAborted(true) diff --git a/packages/cli-kit/src/public/node/api/rest-api-throttler.ts b/packages/cli-kit/src/public/node/api/rest-api-throttler.ts index c27cac4b85..e0eecb2fce 100644 --- a/packages/cli-kit/src/public/node/api/rest-api-throttler.ts +++ b/packages/cli-kit/src/public/node/api/rest-api-throttler.ts @@ -33,7 +33,9 @@ export async function throttle(request: () => T): Promise { */ const throttleByHeader = () => { if (isReachingApiLimit()) { - setTimeout(() => throttleByParallelCounter(performRequest), DELAY_FOR_TOO_CLOSE_TO_API_LIMIT) + setTimeout(() => { + throttleByParallelCounter(performRequest) + }, DELAY_FOR_TOO_CLOSE_TO_API_LIMIT) } else { throttleByParallelCounter(performRequest) } @@ -50,7 +52,9 @@ export async function throttle(request: () => T): Promise { */ const throttleByParallelCounter = (command: () => void) => { if (hasTooManyRequests()) { - setTimeout(() => throttleByParallelCounter(throttleByHeader), DELAY_FOR_TOO_MANY_PARALLEL_REQUESTS) + setTimeout(() => { + throttleByParallelCounter(throttleByHeader) + }, DELAY_FOR_TOO_MANY_PARALLEL_REQUESTS) } else { command() } @@ -166,7 +170,9 @@ export async function delayAwareRetry( ): Promise { const retryDelay = extractRetryDelayMsFromResponse(response) return new Promise((resolve, _reject) => { - setTimeout(() => resolve(operation()), retryDelay) + setTimeout(() => { + resolve(operation()) + }, retryDelay) }) } diff --git a/packages/cli-kit/src/public/node/archiver.ts b/packages/cli-kit/src/public/node/archiver.ts index b3fdf927e8..7c760247d4 100644 --- a/packages/cli-kit/src/public/node/archiver.ts +++ b/packages/cli-kit/src/public/node/archiver.ts @@ -42,8 +42,12 @@ export async function zip(options: ZipOptions): Promise { const archive = archiver('zip') const output = createWriteStream(outputZipPath) - output.on('close', () => resolve()) - archive.on('error', (error) => reject(error)) + output.on('close', () => { + resolve() + }) + archive.on('error', (error) => { + reject(error) + }) archive.pipe(output) for (const filePath of pathsToZip) { diff --git a/packages/cli-kit/src/public/node/base-command.ts b/packages/cli-kit/src/public/node/base-command.ts index 42f1712046..c8f06d195b 100644 --- a/packages/cli-kit/src/public/node/base-command.ts +++ b/packages/cli-kit/src/public/node/base-command.ts @@ -265,7 +265,7 @@ function argsFromEnvironment { ) { const portPath = spinVersion === '2' ? joinPath(tmpDir, 'ports2', service, 'custom') : joinPath(tmpDir, 'ports', service, 'proc') - const content = spinVersion === '2' ? `[{"internal":${port}}]` : `${port}` + const content = spinVersion === '2' ? `[{"internal":${port}}]` : port return mkdir(portPath).then(() => writeFile(joinPath(portPath, portName), content)) } }) diff --git a/packages/cli-kit/src/public/node/context/spin.ts b/packages/cli-kit/src/public/node/context/spin.ts index 09c4f29e5d..517e9cfd35 100644 --- a/packages/cli-kit/src/public/node/context/spin.ts +++ b/packages/cli-kit/src/public/node/context/spin.ts @@ -159,7 +159,7 @@ export async function fetchSpinPort( } const match = config.contentPattern.exec(fileContent) - if (match && match[1] && !isNaN(parseInt(match[1], 10))) { + if (match?.[1] && !isNaN(parseInt(match[1], 10))) { return parseInt(match[1], 10) } } diff --git a/packages/cli-kit/src/public/node/custom-oclif-loader.ts b/packages/cli-kit/src/public/node/custom-oclif-loader.ts index 7b8f024629..122ebbe8d2 100644 --- a/packages/cli-kit/src/public/node/custom-oclif-loader.ts +++ b/packages/cli-kit/src/public/node/custom-oclif-loader.ts @@ -15,7 +15,7 @@ export class ShopifyConfig extends Config { if (currentPathMightBeHydrogenMonorepo && !ignoreHydrogenMonorepo) { path = execaSync('npm', ['prefix']).stdout.trim() } - if (fileExistsSync(joinPath(`${path}`, 'package.json'))) { + if (fileExistsSync(joinPath(path, 'package.json'))) { // Hydrogen is bundled, but we still want to support loading it as an external plugin for two reasons: // 1. To allow users to use an older version of Hydrogen. (to not force upgrades) // 2. To allow the Hydrogen team to load a local version for testing. @@ -26,12 +26,13 @@ export class ShopifyConfig extends Config { } super(options) - // eslint-disable-next-line dot-notation, @typescript-eslint/unbound-method - this['determinePriority'] = this.customPriority + // @ts-expect-error: This is a private method that we are overriding. OCLIF doesn't provide a way to extend it. + // eslint-disable-next-line @typescript-eslint/unbound-method + this.determinePriority = this.customPriority } customPriority(commands: Command.Loadable[]): Command.Loadable | undefined { - const oclifPlugins = this.pjson.oclif?.plugins ?? [] + const oclifPlugins = this.pjson.oclif.plugins ?? [] const commandPlugins = commands.sort((aCommand, bCommand) => { // eslint-disable-next-line no-restricted-syntax const pluginAliasA = aCommand.pluginAlias ?? 'A-Cannot-Find-This' diff --git a/packages/cli-kit/src/public/node/dot-env.ts b/packages/cli-kit/src/public/node/dot-env.ts index 891279c281..9f22b17904 100644 --- a/packages/cli-kit/src/public/node/dot-env.ts +++ b/packages/cli-kit/src/public/node/dot-env.ts @@ -134,7 +134,7 @@ export function createDotEnvFileLine(key: string, value?: string, quote?: string if (quote) { return `${key}=${quote}${value}${quote}` } - if (value && value.includes('\n')) { + if (value?.includes('\n')) { const quoteCharacter = ['"', "'", '`'].find((char) => !value.includes(char)) if (!quoteCharacter) { diff --git a/packages/cli-kit/src/public/node/environments.ts b/packages/cli-kit/src/public/node/environments.ts index fdf502049c..34d82da402 100644 --- a/packages/cli-kit/src/public/node/environments.ts +++ b/packages/cli-kit/src/public/node/environments.ts @@ -22,7 +22,7 @@ export async function loadEnvironment( fileName: string, options?: LoadEnvironmentOptions, ): Promise { - const basePath = options?.from && options?.from !== '.' ? options.from : cwd() + const basePath = options?.from && options.from !== '.' ? options.from : cwd() const filePath = await findPathUp(fileName, { cwd: basePath, type: 'file', diff --git a/packages/cli-kit/src/public/node/error-handler.ts b/packages/cli-kit/src/public/node/error-handler.ts index 104d73ac23..1c7c92b93a 100644 --- a/packages/cli-kit/src/public/node/error-handler.ts +++ b/packages/cli-kit/src/public/node/error-handler.ts @@ -161,7 +161,7 @@ export function cleanStackFrameFilePath({ ? currentFilePath : path.joinPath(projectRoot, currentFilePath) - const matchingPluginPath = pluginLocations.filter(({pluginPath}) => fullLocation.indexOf(pluginPath) === 0)[0] + const matchingPluginPath = pluginLocations.filter(({pluginPath}) => fullLocation.startsWith(pluginPath))[0] if (matchingPluginPath !== undefined) { // the plugin name (e.g. @shopify/cli-kit), plus the relative path of the error line from within the plugin's code (e.g. dist/something.js ) diff --git a/packages/cli-kit/src/public/node/framework.ts b/packages/cli-kit/src/public/node/framework.ts index fefe94381f..b5e6f64381 100644 --- a/packages/cli-kit/src/public/node/framework.ts +++ b/packages/cli-kit/src/public/node/framework.ts @@ -153,14 +153,14 @@ export async function resolveFramework(rootDirectory: string): Promise { const matchedFramework = frameworks.find( (framework) => - (!framework.detectors?.some || - framework.detectors?.some?.reduce( + (!framework.detectors.some || + framework.detectors.some.reduce( (_previousDetectorsMatch: boolean, detector) => matchDetector(detector, loadFwConfigFile(rootDirectory, detector.path, fwConfigFiles)), false, )) && - (!framework.detectors?.every || - framework.detectors?.every?.reduce( + (!framework.detectors.every || + framework.detectors.every.reduce( (previousDetectorsMatch: boolean, detector) => previousDetectorsMatch ? matchDetector(detector, loadFwConfigFile(rootDirectory, detector.path, fwConfigFiles)) diff --git a/packages/cli-kit/src/public/node/fs.ts b/packages/cli-kit/src/public/node/fs.ts index 43d5b4a0ba..44799515c9 100644 --- a/packages/cli-kit/src/public/node/fs.ts +++ b/packages/cli-kit/src/public/node/fs.ts @@ -334,7 +334,7 @@ export function fileSizeSync(path: string): number { * @returns A promise that resolves when the file is unlinked. */ export function unlinkFileSync(path: string): void { - return fsUnlinkSync(path) + fsUnlinkSync(path) } /** diff --git a/packages/cli-kit/src/public/node/hooks/deprecations.ts b/packages/cli-kit/src/public/node/hooks/deprecations.ts index fd27ccdf02..5398b2fb2d 100644 --- a/packages/cli-kit/src/public/node/hooks/deprecations.ts +++ b/packages/cli-kit/src/public/node/hooks/deprecations.ts @@ -10,7 +10,7 @@ import {Command} from '@oclif/core' export const postrun = (Command: Command.Class): void => { const nextDeprecationDate = getNextDeprecationDate() if (nextDeprecationDate) { - const forThemes = Command?.id?.includes('theme') + const forThemes = Command.id.includes('theme') renderUpgradeWarning(nextDeprecationDate, forThemes) } } diff --git a/packages/cli-kit/src/public/node/hooks/postrun.ts b/packages/cli-kit/src/public/node/hooks/postrun.ts index 8eb9a0d446..5a95ecd59f 100644 --- a/packages/cli-kit/src/public/node/hooks/postrun.ts +++ b/packages/cli-kit/src/public/node/hooks/postrun.ts @@ -11,7 +11,7 @@ export const hook: Hook.Postrun = async ({config, Command}) => { await reportAnalyticsEvent({config, exitMode: 'ok'}) deprecationsHook(Command) - const command = Command?.id?.replace(/:/g, ' ') + const command = Command.id.replace(/:/g, ' ') outputDebug(`Completed command ${command}`) } diff --git a/packages/cli-kit/src/public/node/hooks/prerun.ts b/packages/cli-kit/src/public/node/hooks/prerun.ts index f321cc2926..367fab4e05 100644 --- a/packages/cli-kit/src/public/node/hooks/prerun.ts +++ b/packages/cli-kit/src/public/node/hooks/prerun.ts @@ -51,7 +51,7 @@ function parseNormalCommand(id: string, aliases: string[]): CommandContent { * @returns Command content with the name of the command or undefined otherwise */ function parseCreateCommand(pluginAlias?: string): CommandContent | undefined { - if (!pluginAlias || !pluginAlias.startsWith('@shopify/create-')) { + if (!pluginAlias?.startsWith('@shopify/create-')) { return undefined } diff --git a/packages/cli-kit/src/public/node/http.ts b/packages/cli-kit/src/public/node/http.ts index 0d47bec63a..16d912d391 100644 --- a/packages/cli-kit/src/public/node/http.ts +++ b/packages/cli-kit/src/public/node/http.ts @@ -59,7 +59,7 @@ export async function shopifyFetch(url: RequestInfo, init?: RequestInit): Promis outputDebug(outputContent`Sending ${options.method ?? 'GET'} request to URL ${sanitizedUrl} With request headers: -${sanitizedHeadersOutput((options?.headers ?? {}) as {[header: string]: string})} +${sanitizedHeadersOutput((options.headers ?? {}) as {[header: string]: string})} `) return runWithTimer('cmd_all_timing_network_ms')(async () => { return simpleRequestWithDebugLog({ diff --git a/packages/cli-kit/src/public/node/json-schema.ts b/packages/cli-kit/src/public/node/json-schema.ts index d58bdb3913..bc3621a94e 100644 --- a/packages/cli-kit/src/public/node/json-schema.ts +++ b/packages/cli-kit/src/public/node/json-schema.ts @@ -5,7 +5,7 @@ import {capitalize} from '../common/string.js' import {Ajv, ErrorObject, SchemaObject} from 'ajv' import $RefParser from '@apidevtools/json-schema-ref-parser' -type AjvError = ErrorObject +type AjvError = ErrorObject /** * Normalises a JSON Schema by standardising it's internal implementation. diff --git a/packages/cli-kit/src/public/node/metadata.ts b/packages/cli-kit/src/public/node/metadata.ts index 0f71390ab6..078a3fa224 100644 --- a/packages/cli-kit/src/public/node/metadata.ts +++ b/packages/cli-kit/src/public/node/metadata.ts @@ -142,7 +142,8 @@ export function createRuntimeMetadataContainer< // The top of the stack is the total time for all nested timers const wallClockDuration = Math.max(end - start, 0) - const childDurations = durationStack.pop() as number + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + const childDurations = durationStack.pop()! const duration = Math.max(wallClockDuration - childDurations, 0) // If this is the topmost timer, the stack will be empty. diff --git a/packages/cli-kit/src/public/node/output.ts b/packages/cli-kit/src/public/node/output.ts index 878a870b38..cf2f0a1f82 100644 --- a/packages/cli-kit/src/public/node/output.ts +++ b/packages/cli-kit/src/public/node/output.ts @@ -417,7 +417,7 @@ export function outputWhereAppropriate( } else { logger(message, logLevel) } - if (!options?.skipUIEvent) recordUIEvent({type: 'output', properties: {content: message}}) + if (!options.skipUIEvent) recordUIEvent({type: 'output', properties: {content: message}}) } } diff --git a/packages/cli-kit/src/public/node/ruby.ts b/packages/cli-kit/src/public/node/ruby.ts index 284b66de8d..4fbb97d7d4 100644 --- a/packages/cli-kit/src/public/node/ruby.ts +++ b/packages/cli-kit/src/public/node/ruby.ts @@ -258,10 +258,11 @@ async function bundleInstallShopifyCLI() { * @returns The absolute path to the directory. */ async function shopifyCLIDirectory(embedded = false): Promise { + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion const embeddedDirectory = (await file.findPathUp('assets/cli-ruby', { type: 'directory', cwd: dirname(fileURLToPath(import.meta.url)), - })) as string + }))! const bundledDirectory = joinPath(pathConstants.directories.cache.vendor.path(), 'ruby-cli', RubyCLIVersion) return embedded ? embeddedDirectory : getEnvironmentVariables().SHOPIFY_CLI_2_0_DIRECTORY ?? bundledDirectory diff --git a/packages/cli-kit/src/public/node/system.ts b/packages/cli-kit/src/public/node/system.ts index f6a14d3608..7b56b05b65 100644 --- a/packages/cli-kit/src/public/node/system.ts +++ b/packages/cli-kit/src/public/node/system.ts @@ -75,7 +75,7 @@ export async function exec(command: string, args: string[], options?: ExecOption // The aborted flag tell use that we killed it, so we can ignore the error. if (aborted) return if (options?.externalErrorHandler) { - await options?.externalErrorHandler(processError) + await options.externalErrorHandler(processError) } else { const abortError = new ExternalError(processError.message, command, args) abortError.stack = processError.stack @@ -92,7 +92,7 @@ export async function exec(command: string, args: string[], options?: ExecOption * @param options - Optional settings for how to run the command. * @returns A promise for a result with stdout and stderr properties. */ -function buildExec(command: string, args: string[], options?: ExecOptions): ExecaChildProcess { +function buildExec(command: string, args: string[], options?: ExecOptions): ExecaChildProcess { const env = options?.env ?? process.env if (shouldDisplayColors()) { env.FORCE_COLOR = '1' diff --git a/packages/cli-kit/src/public/node/themes/api.ts b/packages/cli-kit/src/public/node/themes/api.ts index 8cf3e4f846..3c2ae9af65 100644 --- a/packages/cli-kit/src/public/node/themes/api.ts +++ b/packages/cli-kit/src/public/node/themes/api.ts @@ -43,6 +43,7 @@ export async function fetchThemeAsset(id: number, key: Key, session: AdminSessio const response = await request('GET', `/themes/${id}/assets`, session, undefined, { 'asset[key]': key, }) + // eslint-disable-next-line @typescript-eslint/no-confusing-void-expression return buildThemeAsset(response.json.asset) } diff --git a/packages/cli-kit/src/public/node/tree-kill.ts b/packages/cli-kit/src/public/node/tree-kill.ts index 6ba1b05f85..595879c0ea 100644 --- a/packages/cli-kit/src/public/node/tree-kill.ts +++ b/packages/cli-kit/src/public/node/tree-kill.ts @@ -59,7 +59,8 @@ function adaptedTreeKill( if (Number.isNaN(rootPid)) { if (callback) { - return callback(new Error('pid must be a number')) + callback(new Error('pid must be a number')) + return } else { throw new Error('pid must be a number') } @@ -141,13 +142,14 @@ function killAll( } catch (err: unknown) { if (callback) { // @ts-ignore - return callback(err) + callback(err) + return } else { throw err } } if (callback) { - return callback() + callback() } } @@ -195,7 +197,8 @@ function buildProcessTree( if (code !== 0) { // no more parent processes if (pidsToProcess.size === 0) { - return cb() + cb() + return } return } diff --git a/packages/cli-kit/src/public/node/ui.tsx b/packages/cli-kit/src/public/node/ui.tsx index 11d93eff4c..e0dfcc4a90 100644 --- a/packages/cli-kit/src/public/node/ui.tsx +++ b/packages/cli-kit/src/public/node/ui.tsx @@ -448,9 +448,7 @@ export async function renderAutocompletePrompt( const lowerTerm = term.toLowerCase() return Promise.resolve({ data: props.choices.filter((item) => { - return ( - item.label.toLowerCase().includes(lowerTerm) || (item.group && item.group.toLowerCase().includes(lowerTerm)) - ) + return item.label.toLowerCase().includes(lowerTerm) || item.group?.toLowerCase().includes(lowerTerm) }), }) }, @@ -528,7 +526,9 @@ export async function renderTasks(tasks: Task[], {renderOpti ...renderOptions, exitOnCtrlC: false, }) - .then(() => resetRecordedSleep()) + .then(() => { + resetRecordedSleep() + }) .catch(reject) }) } diff --git a/packages/cli/package.json b/packages/cli/package.json index 4b2464a6e3..3281f785d3 100644 --- a/packages/cli/package.json +++ b/packages/cli/package.json @@ -60,6 +60,9 @@ "**/bin/*.js" ], "parser": "espree", + "extends": [ + "plugin:@typescript-eslint/disable-type-checked" + ], "rules": { "@typescript-eslint/naming-convention": "off", "@typescript-eslint/no-floating-promises": "off", @@ -77,21 +80,7 @@ ] } ], - "@typescript-eslint/consistent-type-assertions": "off", - "@typescript-eslint/no-base-to-string": "off", - "@typescript-eslint/no-duplicate-type-constituents": "off", - "@typescript-eslint/no-implied-eval": "off", - "@typescript-eslint/no-redundant-type-constituents": "off", - "@typescript-eslint/restrict-plus-operands": "off", - "@typescript-eslint/no-unsafe-argument": "off", - "@typescript-eslint/no-unsafe-assignment": "off", - "@typescript-eslint/no-unsafe-call": "off", - "@typescript-eslint/no-unsafe-member-access": "off", - "@typescript-eslint/no-unsafe-return": "off", - "@typescript-eslint/no-unsafe-enum-comparison": "off", - "@typescript-eslint/no-unsafe-nullish-coalescing": "off", - "@typescript-eslint/no-unsafe-optional-chain": "off", - "@typescript-eslint/unbound-method": "off" + "@typescript-eslint/consistent-type-assertions": "off" } }, { @@ -126,6 +115,7 @@ "@shopify/theme": "3.68.0", "@shopify/cli-hydrogen": "8.4.6", "@types/node": "18.19.3", + "@typescript-eslint/eslint-plugin": "7.13.1", "@vitest/coverage-istanbul": "^1.6.0", "esbuild-plugin-copy": "^2.1.1", "espree": "9.6.1", diff --git a/packages/cli/src/cli/services/kitchen-sink/prompts.ts b/packages/cli/src/cli/services/kitchen-sink/prompts.ts index be8e623573..e40f5f2d84 100644 --- a/packages/cli/src/cli/services/kitchen-sink/prompts.ts +++ b/packages/cli/src/cli/services/kitchen-sink/prompts.ts @@ -112,13 +112,13 @@ export async function prompts() { [ 'first theme', { - subdued: `(#${1})`, + subdued: `(#1)`, }, ], [ 'second theme', { - subdued: `(#${2})`, + subdued: `(#2)`, }, ], ] diff --git a/packages/eslint-plugin-cli/config.js b/packages/eslint-plugin-cli/config.js index 9bfbe40b0b..a90f972898 100644 --- a/packages/eslint-plugin-cli/config.js +++ b/packages/eslint-plugin-cli/config.js @@ -6,9 +6,8 @@ module.exports = { 'plugin:@shopify/prettier', 'plugin:@shopify/node', 'prettier', - 'plugin:@typescript-eslint/recommended-type-checked', - 'plugin:@typescript-eslint/strict', - 'plugin:@typescript-eslint/stylistic', + 'plugin:@typescript-eslint/strict-type-checked', + 'plugin:@typescript-eslint/stylistic-type-checked', ], rules: { 'prettier/prettier': ['error'], @@ -168,6 +167,17 @@ module.exports = { '@typescript-eslint/no-unsafe-return': 'off', '@typescript-eslint/restrict-template-expressions': 'off', '@typescript-eslint/no-unsafe-enum-comparison': 'off', + '@typescript-eslint/prefer-nullish-coalescing': 'warn', + '@typescript-eslint/no-unnecessary-condition': 'warn', + '@typescript-eslint/prefer-optional-chain': 'warn', + '@typescript-eslint/no-confusing-void-expression': 'warn', + '@typescript-eslint/non-nullable-type-assertion-style': 'warn', + 'no-negated-condition': 'warn', + 'node/callback-return': 'warn', + '@typescript-eslint/prefer-promise-reject-errors': 'warn', + 'no-lone-blocks': 'warn', + '@typescript-eslint/use-unknown-in-catch-callback-variable': 'warn', + '@typescript-eslint/only-throw-error': 'warn', }, overrides: [ { @@ -183,6 +193,12 @@ module.exports = { '@typescript-eslint/no-dynamic-delete': 'off', '@typescript-eslint/unbound-method': 'off', '@typescript-eslint/no-redundant-type-constituents': 'off', + '@typescript-eslint/no-confusing-void-expression': 'off', + '@typescript-eslint/no-unnecessary-template-expression': 'warn', + '@typescript-eslint/no-unnecessary-condition': 'warn', + '@typescript-eslint/restrict-plus-operands': 'off', + '@typescript-eslint/non-nullable-type-assertion-style': 'off', + '@typescript-eslint/prefer-reduce-type-parameter': 'warn', }, }, ], diff --git a/packages/features/lib/system.ts b/packages/features/lib/system.ts index 5620d5c680..e7f92b2b6e 100644 --- a/packages/features/lib/system.ts +++ b/packages/features/lib/system.ts @@ -15,7 +15,7 @@ export interface ExecOptions { * @param command - The command to be executed. * @returns A promise that resolves or rejects when the command execution finishes. */ -export function exec(command: string, args: string[] = [], options?: ExecOptions): ExecaChildProcess { +export function exec(command: string, args: string[] = [], options?: ExecOptions): ExecaChildProcess { if (isDebug) { console.log(colors.gray(`Running: ${command} ${args.join(' ')}`)) } diff --git a/packages/theme/src/cli/services/check.ts b/packages/theme/src/cli/services/check.ts index ca1743d30f..77ef72a903 100644 --- a/packages/theme/src/cli/services/check.ts +++ b/packages/theme/src/cli/services/check.ts @@ -115,15 +115,9 @@ export function formatOffenses(offenses: Offense[]) { const codeSnippet = getSnippet(absolutePath, start.line, end.line) // Ensure enough padding between offenses - const offensePadding = `${index === offenses.length - 1 ? '' : '\n\n'}` - - return [ - severityToToken(severity), - {bold: `${check}`}, - {subdued: `\n${message}`}, - `\n\n${codeSnippet}`, - offensePadding, - ] + const offensePadding = index === offenses.length - 1 ? '' : '\n\n' + + return [severityToToken(severity), {bold: check}, {subdued: `\n${message}`}, `\n\n${codeSnippet}`, offensePadding] }) return offenseBodies.flat() diff --git a/packages/theme/src/cli/services/info.ts b/packages/theme/src/cli/services/info.ts index e13bef2716..9689519c5b 100644 --- a/packages/theme/src/cli/services/info.ts +++ b/packages/theme/src/cli/services/info.ts @@ -74,7 +74,7 @@ function devConfigSection(): [string, string] { ['Store', store], ['Development Theme ID', developmentTheme], ] - return [title, `${linesToColumns(lines)}`] + return [title, linesToColumns(lines)] } async function systemInfoSection(config: {cliVersion: string}): Promise<[string, string]> { @@ -88,7 +88,7 @@ async function systemInfoSection(config: {cliVersion: string}): Promise<[string, ['Node version', process.version], ['Ruby version', ruby], ] - return [title, `${linesToColumns(lines)}`] + return [title, linesToColumns(lines)] } async function cliVersionInfo(config: {cliVersion: string}): Promise { diff --git a/packages/theme/src/cli/utilities/repl/evaluator.ts b/packages/theme/src/cli/utilities/repl/evaluator.ts index 9620fd51e5..5635eb3474 100644 --- a/packages/theme/src/cli/utilities/repl/evaluator.ts +++ b/packages/theme/src/cli/utilities/repl/evaluator.ts @@ -153,7 +153,7 @@ function stripHTMLContent(result: string): undefined | string { } function hasLiquidError(body: string): boolean { - return /Liquid syntax error/.test(body) + return body.includes('Liquid syntax error') } function isStandardAssignment(input: string): boolean { diff --git a/packages/theme/src/cli/utilities/theme-command.ts b/packages/theme/src/cli/utilities/theme-command.ts index 24318b2e5e..14bbd6a59c 100644 --- a/packages/theme/src/cli/utilities/theme-command.ts +++ b/packages/theme/src/cli/utilities/theme-command.ts @@ -18,7 +18,7 @@ export default abstract class ThemeCommand extends Command { } else if (typeof value === 'boolean') { if (value) passThroughFlags.push(`--${label}`) } else if (Array.isArray(value)) { - value.forEach((element) => passThroughFlags.push(`--${label}`, `${element}`)) + value.forEach((element) => passThroughFlags.push(`--${label}`, element)) } else { passThroughFlags.push(`--${label}`, `${value}`) } diff --git a/packages/theme/src/cli/utilities/theme-environment/local-assets.ts b/packages/theme/src/cli/utilities/theme-environment/local-assets.ts index abf3d66ddb..644e724dd4 100644 --- a/packages/theme/src/cli/utilities/theme-environment/local-assets.ts +++ b/packages/theme/src/cli/utilities/theme-environment/local-assets.ts @@ -1,14 +1,6 @@ import {injectCdnProxy} from './proxy.js' import {lookupMimeType} from '@shopify/cli-kit/node/mimes' -import { - defineEventHandler, - EventHandlerRequest, - H3Event, - serveStatic, - setResponseHeader, - sendError, - createError, -} from 'h3' +import {defineEventHandler, H3Event, serveStatic, setResponseHeader, sendError, createError} from 'h3' import {joinPath} from '@shopify/cli-kit/node/path' import type {Theme, VirtualFileSystem} from '@shopify/cli-kit/node/themes/types' import type {DevServerContext} from './types.js' @@ -63,7 +55,7 @@ export function getAssetsHandler(_theme: Theme, ctx: DevServerContext) { }) } -function findLocalFile(event: H3Event, ctx: DevServerContext) { +function findLocalFile(event: H3Event, ctx: DevServerContext) { const tryGetFile = (pattern: RegExp, fileSystem: VirtualFileSystem) => { const matchedFileName = event.path.match(pattern)?.[1] diff --git a/packages/theme/src/cli/utilities/theme-environment/theme-environment.test.ts b/packages/theme/src/cli/utilities/theme-environment/theme-environment.test.ts index c07ef0c624..355665ccf1 100644 --- a/packages/theme/src/cli/utilities/theme-environment/theme-environment.test.ts +++ b/packages/theme/src/cli/utilities/theme-environment/theme-environment.test.ts @@ -184,7 +184,7 @@ describe('setupDevServer', () => { const dispatchEvent = async ( url: string, headers?: {[key: string]: string}, - ): Promise<{res: ServerResponse; status: number; body: string | Buffer}> => { + ): Promise<{res: ServerResponse; status: number; body: string | Buffer}> => { const event = createH3Event({url, headers}) const {res} = event.node let body: string diff --git a/packages/theme/src/cli/utilities/theme-fs.ts b/packages/theme/src/cli/utilities/theme-fs.ts index dd64a5ac30..e214773dc3 100644 --- a/packages/theme/src/cli/utilities/theme-fs.ts +++ b/packages/theme/src/cli/utilities/theme-fs.ts @@ -320,7 +320,7 @@ export function partitionThemeFiles(files: T[]) { files.forEach((file) => { const fileKey = file.key - if (THEME_PARTITION_REGEX.liquidRegex.test(fileKey)) { + if (fileKey.endsWith('.liquid')) { if (THEME_PARTITION_REGEX.sectionLiquidRegex.test(fileKey)) { sectionLiquidFiles.push(file) } else { diff --git a/packages/theme/src/cli/utilities/theme-uploader.ts b/packages/theme/src/cli/utilities/theme-uploader.ts index f224dee2ce..d720bd6f13 100644 --- a/packages/theme/src/cli/utilities/theme-uploader.ts +++ b/packages/theme/src/cli/utilities/theme-uploader.ts @@ -379,7 +379,7 @@ async function handleBulkUpload( .join('\n')}`, ) - const failedUploadResults = results.filter((result) => result.success === false) + const failedUploadResults = results.filter((result) => !result.success) if (failedUploadResults.length > 0) { outputDebug( `The following files failed to upload:\n${failedUploadResults.map((param) => `-${param.key}`).join('\n')}`, diff --git a/packages/ui-extensions-dev-console/src/components/Modal/Modal.tsx b/packages/ui-extensions-dev-console/src/components/Modal/Modal.tsx index 87b42a4c6a..4ddc961bff 100644 --- a/packages/ui-extensions-dev-console/src/components/Modal/Modal.tsx +++ b/packages/ui-extensions-dev-console/src/components/Modal/Modal.tsx @@ -2,7 +2,7 @@ import {Dialog, Header, Backdrop} from './components' import styles from './Modal.module.scss' import {TransitionGroup} from 'react-transition-group' -import React, {JSXElementConstructor, useState} from 'react' +import React, {useState} from 'react' import {createPortal} from 'react-dom' import {ModalContainerId} from '@/foundation/ModalContainer' @@ -10,7 +10,7 @@ export interface ModalProps { /** Whether the modal is open or not */ open: boolean /** The content for the title of the modal */ - title: React.ReactElement> | string + title: React.ReactElement | string /** The content to display inside modal */ children: React.ReactNode /** Callback when the modal is closed */ diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 24a180775f..55f912eef1 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -295,6 +295,9 @@ importers: '@types/node': specifier: 18.19.3 version: 18.19.3 + '@typescript-eslint/eslint-plugin': + specifier: 7.13.1 + version: 7.13.1(@typescript-eslint/parser@7.13.1)(eslint@8.57.0)(typescript@5.2.2) '@vitest/coverage-istanbul': specifier: ^1.6.0 version: 1.6.0(vitest@1.6.0)