diff --git a/.changeset/witty-dodos-juggle.md b/.changeset/witty-dodos-juggle.md new file mode 100644 index 00000000000..1f74a9661a8 --- /dev/null +++ b/.changeset/witty-dodos-juggle.md @@ -0,0 +1,6 @@ +--- +'@shopify/theme': patch +'@shopify/app': patch +--- + +Bump @shopify/theme-check-node & @shopify/theme-language-server diff --git a/packages/app/package.json b/packages/app/package.json index e7c24740eed..8fb1d1d275f 100644 --- a/packages/app/package.json +++ b/packages/app/package.json @@ -57,7 +57,7 @@ "@shopify/polaris": "12.10.0", "@shopify/polaris-icons": "8.0.0", "@shopify/theme": "3.70.0", - "@shopify/theme-check-node": "2.9.2", + "@shopify/theme-check-node": "3.2.2", "body-parser": "1.20.2", "camelcase-keys": "9.1.3", "chokidar": "3.5.3", diff --git a/packages/app/src/cli/services/build/theme-check.ts b/packages/app/src/cli/services/build/theme-check.ts index 3851078d73f..53a00faebd0 100644 --- a/packages/app/src/cli/services/build/theme-check.ts +++ b/packages/app/src/cli/services/build/theme-check.ts @@ -1,12 +1,13 @@ import {readFileSync} from '@shopify/cli-kit/node/fs' import {itemToString} from '@shopify/cli-kit/node/output' import {TokenItem} from '@shopify/cli-kit/node/ui' -import {Severity, type Offense, check} from '@shopify/theme-check-node' +import {Severity, type Offense, check, path as pathUtils} from '@shopify/theme-check-node' /** * Returns a code snippet from a file. All line numbers given MUST be zero indexed */ -function getSnippet(absolutePath: string, startLine: number, endLine: number) { +function getSnippet(uri: string, startLine: number, endLine: number) { + const absolutePath = pathUtils.fsPath(uri) const fileContent = readFileSync(absolutePath).toString() const lines = fileContent.split('\n') const snippetLines = lines.slice(startLine, endLine + 1) @@ -45,9 +46,9 @@ function severityToToken(severity: Severity) { */ function formatOffenses(offenses: Offense[]): TokenItem { const offenseBodies = offenses.map((offense, index) => { - const {message, absolutePath, start, end, check, severity} = offense + const {message, uri, start, end, check, severity} = offense // Theme check line numbers are zero indexed, but intuitively 1-indexed - const codeSnippet = getSnippet(absolutePath, start.line, end.line) + const codeSnippet = getSnippet(uri, start.line, end.line) // Ensure enough padding between offenses const offensePadding = index === offenses.length - 1 ? '' : '\n\n' diff --git a/packages/theme/package.json b/packages/theme/package.json index 184502fb3f3..a5168f634cc 100644 --- a/packages/theme/package.json +++ b/packages/theme/package.json @@ -43,8 +43,8 @@ "dependencies": { "@oclif/core": "3.26.5", "@shopify/cli-kit": "3.70.0", - "@shopify/theme-check-node": "2.9.2", - "@shopify/theme-language-server-node": "1.14.0", + "@shopify/theme-check-node": "3.2.2", + "@shopify/theme-language-server-node": "2.2.2", "chokidar": "3.5.3", "h3": "1.12.0", "yaml": "2.3.2" diff --git a/packages/theme/src/cli/commands/theme/check.ts b/packages/theme/src/cli/commands/theme/check.ts index 32cbd5c30a2..f4a276f2eda 100644 --- a/packages/theme/src/cli/commands/theme/check.ts +++ b/packages/theme/src/cli/commands/theme/check.ts @@ -111,7 +111,7 @@ export default class Check extends ThemeCommand { let version = 'unknown' if (pkgJsonPath) { - version = (await getPackageVersion(pkgJsonPath)) || 'unknown' + version = (await getPackageVersion(pkgJsonPath)) ?? 'unknown' } outputInfo(version) diff --git a/packages/theme/src/cli/services/check.test.ts b/packages/theme/src/cli/services/check.test.ts index 63ffe4c4e6c..b5dad9cf7de 100644 --- a/packages/theme/src/cli/services/check.test.ts +++ b/packages/theme/src/cli/services/check.test.ts @@ -52,7 +52,7 @@ describe('formatOffenses', () => { type: SourceCodeType.LiquidHtml, check: 'LiquidHTMLSyntaxError', message: 'Attempting to close HtmlElement', - absolutePath: '/path/to/file', + uri: 'file:///path/to/file', severity: Severity.ERROR, start: {index: 0, line: 1, character: 0}, end: {index: 10, line: 1, character: 10}, @@ -80,7 +80,7 @@ describe('formatOffenses', () => { type: SourceCodeType.LiquidHtml, check: 'LiquidHTMLSyntaxError', message: 'Attempting to close HtmlElement', - absolutePath: '/path/to/file', + uri: 'file:///path/to/file', severity: Severity.ERROR, start: {index: 0, line: 1, character: 0}, end: {index: 10, line: 1, character: 10}, @@ -89,7 +89,7 @@ describe('formatOffenses', () => { type: SourceCodeType.LiquidHtml, check: 'LiquidHTMLSyntaxError', message: 'Attempting to close HtmlElement', - absolutePath: '/path/to/file', + uri: 'file:///path/to/file', severity: Severity.WARNING, start: {index: 0, line: 2, character: 0}, end: {index: 10, line: 2, character: 10}, @@ -120,7 +120,7 @@ describe('sortOffenses', () => { type: SourceCodeType.LiquidHtml, check: 'LiquidHTMLSyntaxError', message: 'Attempting to close HtmlElement', - absolutePath: '/path/to/file2', + uri: 'file:///path/to/file2', severity: Severity.ERROR, start: {index: 0, line: 1, character: 0}, end: {index: 10, line: 1, character: 10}, @@ -129,7 +129,7 @@ describe('sortOffenses', () => { type: SourceCodeType.LiquidHtml, check: 'LiquidHTMLSyntaxError', message: 'Attempting to close HtmlElement', - absolutePath: '/path/to/file1', + uri: 'file:///path/to/file1', severity: Severity.WARNING, start: {index: 0, line: 1, character: 0}, end: {index: 10, line: 1, character: 10}, @@ -150,7 +150,7 @@ describe('sortOffenses', () => { type: SourceCodeType.LiquidHtml, check: 'LiquidHTMLSyntaxError', message: 'Attempting to close HtmlElement', - absolutePath: '/path/to/file', + uri: 'file:///path/to/file', severity: Severity.WARNING, start: {index: 0, line: 1, character: 0}, end: {index: 10, line: 1, character: 10}, @@ -159,7 +159,7 @@ describe('sortOffenses', () => { type: SourceCodeType.LiquidHtml, check: 'LiquidHTMLSyntaxError', message: 'Attempting to close HtmlElement', - absolutePath: '/path/to/file', + uri: 'file:///path/to/file', severity: Severity.ERROR, start: {index: 0, line: 2, character: 0}, end: {index: 10, line: 2, character: 10}, @@ -190,7 +190,7 @@ describe('formatSummary', () => { type: SourceCodeType.LiquidHtml, check: 'LiquidHTMLSyntaxError', message: 'Attempting to close HtmlElement', - absolutePath: '/path/to/file', + uri: 'file:///path/to/file', severity: Severity.ERROR, start: {index: 0, line: 1, character: 0}, end: {index: 10, line: 1, character: 10}, @@ -199,7 +199,7 @@ describe('formatSummary', () => { type: SourceCodeType.LiquidHtml, check: 'LiquidHTMLSyntaxError', message: 'Attempting to close HtmlElement', - absolutePath: '/path/to/file', + uri: 'file:///path/to/file', severity: Severity.WARNING, start: {index: 0, line: 2, character: 0}, end: {index: 10, line: 2, character: 10}, @@ -235,7 +235,7 @@ describe('renderOffensesText', () => { type: SourceCodeType.LiquidHtml, check: 'LiquidHTMLSyntaxError', message: 'Attempting to close HtmlElement', - absolutePath: '/path/to/file', + uri: 'file:///path/to/file', severity: Severity.ERROR, start: {index: 0, line: 1, character: 0}, end: {index: 10, line: 1, character: 10}, @@ -258,7 +258,7 @@ describe('formatOffensesJson', () => { type: SourceCodeType.LiquidHtml, check: 'LiquidHTMLSyntaxError', message: 'Attempting to close HtmlElement', - absolutePath: '/path/to/file', + uri: 'file:///path/to/file', severity: Severity.ERROR, start: {index: 0, line: 1, character: 0}, end: {index: 10, line: 1, character: 10}, @@ -267,7 +267,7 @@ describe('formatOffensesJson', () => { type: SourceCodeType.LiquidHtml, check: 'LiquidHTMLSyntaxError', message: 'Attempting to close HtmlElement', - absolutePath: '/path/to/file', + uri: 'file:///path/to/file', severity: Severity.WARNING, start: {index: 0, line: 2, character: 0}, end: {index: 10, line: 2, character: 10}, @@ -333,7 +333,7 @@ describe('handleExit', () => { type: SourceCodeType.LiquidHtml, check: 'LiquidHTMLSyntaxError', message: 'Attempting to close HtmlElement', - absolutePath: '/path/to/file', + uri: 'file:///path/to/file', severity: Severity.ERROR, start: {index: 0, line: 1, character: 0}, end: {index: 10, line: 1, character: 10}, @@ -349,7 +349,7 @@ describe('handleExit', () => { type: SourceCodeType.LiquidHtml, check: 'LiquidHTMLSyntaxError', message: 'Attempting to close HtmlElement', - absolutePath: '/path/to/file', + uri: 'file:///path/to/file', severity: Severity.ERROR, start: {index: 0, line: 1, character: 0}, end: {index: 10, line: 1, character: 10}, @@ -365,7 +365,7 @@ describe('handleExit', () => { type: SourceCodeType.LiquidHtml, check: 'LiquidHTMLSyntaxError', message: 'Attempting to close HtmlElement', - absolutePath: '/path/to/file', + uri: 'file:///path/to/file', severity: Severity.INFO, start: {index: 0, line: 1, character: 0}, end: {index: 10, line: 1, character: 10}, diff --git a/packages/theme/src/cli/services/check.ts b/packages/theme/src/cli/services/check.ts index 77ef72a9031..e86722f9940 100644 --- a/packages/theme/src/cli/services/check.ts +++ b/packages/theme/src/cli/services/check.ts @@ -11,6 +11,7 @@ import { type FixApplicator, type Offense, type Theme, + path as pathUtils, } from '@shopify/theme-check-node' import YAML from 'yaml' @@ -37,7 +38,9 @@ interface TransformedOffenseMap { } type SeverityCounts = Partial<{ - [K in keyof typeof Severity]: number + [Severity.ERROR]: number + [Severity.WARNING]: number + [Severity.INFO]: number }> export type FailLevel = 'error' | 'suggestion' | 'style' | 'warning' | 'info' | 'crash' @@ -71,8 +74,9 @@ function severityToLabel(severity: Severity) { /** * Returns a code snippet from a file. All line numbers given MUST be zero indexed */ -function getSnippet(absolutePath: string, startLine: number, endLine: number) { - const fileContent = readFileSync(absolutePath).toString() +function getSnippet(uri: string, startLine: number, endLine: number) { + const fsPath = pathUtils.fsPath(uri) + const fileContent = readFileSync(fsPath).toString() const lines = fileContent.split('\n') const snippetLines = lines.slice(startLine, endLine + 1) const isSingleLine = snippetLines.length === 1 @@ -110,9 +114,9 @@ function severityToToken(severity: Severity) { */ export function formatOffenses(offenses: Offense[]) { const offenseBodies = offenses.map((offense, index) => { - const {message, absolutePath, start, end, check, severity} = offense + const {message, uri, start, end, check, severity} = offense // Theme check line numbers are zero indexed, but intuitively 1-indexed - const codeSnippet = getSnippet(absolutePath, start.line, end.line) + const codeSnippet = getSnippet(uri, start.line, end.line) // Ensure enough padding between offenses const offensePadding = index === offenses.length - 1 ? '' : '\n\n' @@ -132,12 +136,13 @@ const offenseSeverityAscending = (offenseA: Offense, offenseB: Offense) => offen export function sortOffenses(offenses: Offense[]): OffenseMap { // Bucket offenses by filename const offensesByFile = offenses.reduce((acc: OffenseMap, offense: Offense) => { - const {absolutePath} = offense - if (!acc[absolutePath]) { - acc[absolutePath] = [] + const {uri} = offense + const filePath = pathUtils.fsPath(uri) + if (!acc[filePath]) { + acc[filePath] = [] } - acc[absolutePath]!.push(offense) + acc[filePath]!.push(offense) return acc }, {}) @@ -222,9 +227,9 @@ export function formatOffensesJson(offensesByFile: OffenseMap): TransformedOffen return { path, offenses: transformedOffenses, - errorCount: counts[Severity.ERROR] || 0, - warningCount: counts[Severity.WARNING] || 0, - infoCount: counts[Severity.INFO] || 0, + errorCount: counts[Severity.ERROR] ?? 0, + warningCount: counts[Severity.WARNING] ?? 0, + infoCount: counts[Severity.INFO] ?? 0, } }) } @@ -274,7 +279,8 @@ export async function initConfig(root: string) { const saveToDiskFixApplicator: FixApplicator = async (sourceCode, fix) => { const updatedSource = applyFixToString(sourceCode.source, fix) - await writeFile(sourceCode.absolutePath, updatedSource) + const absolutePath = pathUtils.fsPath(sourceCode.uri) + await writeFile(absolutePath, updatedSource) } export async function performAutoFixes(sourceCodes: Theme, offenses: Offense[]) { @@ -282,7 +288,7 @@ export async function performAutoFixes(sourceCodes: Theme, offenses: Offense[]) } export async function outputActiveConfig(themeRoot: string, configPath?: string) { - const {ignore, settings, root} = await loadConfig(configPath, themeRoot) + const {ignore, settings, rootUri} = await loadConfig(configPath, themeRoot) const config = { // loadConfig flattens all configs, it doesn't extend anything @@ -292,7 +298,7 @@ export async function outputActiveConfig(themeRoot: string, configPath?: string) // duplicate patterns to ignore. We can clean them before outputting. ignore: [...new Set(ignore)], - root, + rootUri, // Dump out the active settings for all checks. ...settings, @@ -314,7 +320,7 @@ export async function outputActiveChecks(root: string, configPath?: string) { return acc } - const severityLabel = severityToLabel(severity === undefined ? Severity.INFO : severity) + const severityLabel = severityToLabel(severity ?? Severity.INFO) // Map metafields from the check into desired output format const meta = checks.find((check) => check.meta.code === checkCode) diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 5fc56655be9..38ab123bb5b 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -171,8 +171,8 @@ importers: specifier: 3.70.0 version: link:../theme '@shopify/theme-check-node': - specifier: 2.9.2 - version: 2.9.2 + specifier: 3.2.2 + version: 3.2.2 body-parser: specifier: 1.20.2 version: 1.20.2 @@ -700,11 +700,11 @@ importers: specifier: 3.70.0 version: link:../cli-kit '@shopify/theme-check-node': - specifier: 2.9.2 - version: 2.9.2 + specifier: 3.2.2 + version: 3.2.2 '@shopify/theme-language-server-node': - specifier: 1.14.0 - version: 1.14.0 + specifier: 2.2.2 + version: 2.2.2 chokidar: specifier: 3.5.3 version: 3.5.3 @@ -6156,8 +6156,8 @@ packages: engines: {node: '>=12.14.0'} dev: false - /@shopify/liquid-html-parser@2.0.5: - resolution: {integrity: sha512-MYp/fe3jSjzmRu6HlbaG/1IJpdB39iShdvnc5biDPjlBhLr0PH/2rHXVdthlAcYDhJvd7DTd7TV0kl3erpUNGg==} + /@shopify/liquid-html-parser@2.1.1: + resolution: {integrity: sha512-5shGZTB7tK28MzawqBb82n6fbn+77U6al5UrOcUruBUj3GNqHJWjzZVPtXq4fQHrFpXFu6l11x+z+ggdUHT0Ng==} dependencies: line-column: 1.0.2 ohm-js: 16.6.0 @@ -6275,10 +6275,10 @@ packages: react-reconciler: 0.26.2(react@17.0.2) dev: true - /@shopify/theme-check-common@2.9.2: - resolution: {integrity: sha512-GXlRRNFXKQdYTgLrA3eAamlpDtZVAYr12UeXyiHxmkk0mmoozkpbtw30yX67FgGVdplM0YSAa7MQ7yHeznPfig==} + /@shopify/theme-check-common@3.2.2: + resolution: {integrity: sha512-rEbZ2kDzpOY+D7FUFi7VWQforLB99WJg8rCcJlzaJIAV4hkQpkjPPjzzXitg3JByVKzfLxocmIlysAVbmort1A==} dependencies: - '@shopify/liquid-html-parser': 2.0.5 + '@shopify/liquid-html-parser': 2.1.1 cross-fetch: 4.0.0 json-to-ast: 2.1.0 jsonc-parser: 3.2.1 @@ -6286,37 +6286,39 @@ packages: lodash-es: 4.17.21 minimatch: 9.0.3 vscode-json-languageservice: 5.3.11 + vscode-uri: 3.0.8 transitivePeerDependencies: - encoding dev: false - /@shopify/theme-check-docs-updater@2.9.2: - resolution: {integrity: sha512-ZGewVePcYvyeOSAwQOf/K/gYsLENfK0guQB3DZ809btslw0FGQtFL5lhyawcof4UEOkPSl2O/swvA72DPbDYlg==} + /@shopify/theme-check-docs-updater@3.2.2: + resolution: {integrity: sha512-NXNviVnkGVEf0roDMvZptMODG97x/HiPDlWtHb4v4oyxIvg23jo1rcu8b3u9GnLmxTzWJWcW3tXJhQDqfTeErA==} hasBin: true dependencies: - '@shopify/theme-check-common': 2.9.2 + '@shopify/theme-check-common': 3.2.2 env-paths: 2.2.1 node-fetch: 2.7.0 transitivePeerDependencies: - encoding dev: false - /@shopify/theme-check-node@2.9.2: - resolution: {integrity: sha512-mS0gtSsjF9LxavruE3V4eNFLU26kcAuvNPExvFI7oxOpuHDRHOPqaeR74zcgmBBoQwz4XqoI4ZyhBPF6k3Oesw==} + /@shopify/theme-check-node@3.2.2: + resolution: {integrity: sha512-X1mzKbNq0YlxRKsEKz/3MmS4CyHkiPNdCVXe/TzqwJlzumMQYtYxd+xMYT8n5nTG2Reuv/UhVXWbqhtNPbwBOw==} dependencies: - '@shopify/theme-check-common': 2.9.2 - '@shopify/theme-check-docs-updater': 2.9.2 + '@shopify/theme-check-common': 3.2.2 + '@shopify/theme-check-docs-updater': 3.2.2 glob: 8.1.0 + vscode-uri: 3.0.8 yaml: 2.3.2 transitivePeerDependencies: - encoding dev: false - /@shopify/theme-language-server-common@1.14.0: - resolution: {integrity: sha512-7wC0Vh+mGIiHxK/eQ0Lef0QslNKXXvEMLh19UMAO/iMdE6m6nXwcyhUuEgEtxI3pcaEC+oeGiyEjrKT64vfQBA==} + /@shopify/theme-language-server-common@2.2.2: + resolution: {integrity: sha512-PPn6wfDnbvT++SGtET8DTb88kQWvkb9XKJ1t/b4r9cI0SOPtM+g2vcB336OdmddYeDfXCTnYwuEnBKfltEPTaQ==} dependencies: - '@shopify/liquid-html-parser': 2.0.5 - '@shopify/theme-check-common': 2.9.2 + '@shopify/liquid-html-parser': 2.1.1 + '@shopify/theme-check-common': 3.2.2 '@vscode/web-custom-data': 0.4.9 vscode-json-languageservice: 5.3.11 vscode-languageserver: 8.1.0 @@ -6326,12 +6328,12 @@ packages: - encoding dev: false - /@shopify/theme-language-server-node@1.14.0: - resolution: {integrity: sha512-V7XGrdMyoo5KuZekK+1z9/pPcWjj/Pj1sGAObscm5OqeDGJAxPORunBOhfUFntH149wNVyEhcu3GpLmDMgf2Ow==} + /@shopify/theme-language-server-node@2.2.2: + resolution: {integrity: sha512-e3QrLn0GIxQiBAI9g7/UKr56IZO8g1+kSHk2Mq3EIhW2Tcn6YXSMIDrw6jkL0MXtcCng6hHgOSgAdqD4roa7Zw==} dependencies: - '@shopify/theme-check-docs-updater': 2.9.2 - '@shopify/theme-check-node': 2.9.2 - '@shopify/theme-language-server-common': 1.14.0 + '@shopify/theme-check-docs-updater': 3.2.2 + '@shopify/theme-check-node': 3.2.2 + '@shopify/theme-language-server-common': 2.2.2 glob: 8.1.0 node-fetch: 2.7.0 vscode-languageserver: 8.1.0