From dc4cb0cd56685a821cb622dfffd490cadc25dea7 Mon Sep 17 00:00:00 2001 From: Firat Ciftci <2052855+firatciftci@users.noreply.github.com> Date: Sat, 23 May 2026 23:20:01 -0400 Subject: [PATCH 1/2] Fix Svelte 5 class sorting with modern AST --- package.json | 2 +- pnpm-lock.yaml | 19 +++++++------- src/index.ts | 61 +++++++++++++++++++++++++++++-------------- tests/plugins.test.ts | 5 ++++ 4 files changed, 57 insertions(+), 30 deletions(-) diff --git a/package.json b/package.json index 286b9c8..2754352 100644 --- a/package.json +++ b/package.json @@ -75,7 +75,7 @@ "prettier-plugin-organize-attributes": "^1.0.0", "prettier-plugin-organize-imports": "^4.2.0", "prettier-plugin-sort-imports": "^1.8.8", - "prettier-plugin-svelte": "^3.4.0", + "prettier-plugin-svelte": "^4.0.1", "pug-lexer": "^5.0.1", "svelte": "^5.38.2", "tailwindcss-v3": "npm:tailwindcss@^3.4.18", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 1fb6a2d..4204e2b 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -31,7 +31,7 @@ importers: version: 1.10.0(prettier@3.8.1) '@trivago/prettier-plugin-sort-imports': specifier: ^6.0.0 - version: 6.0.2(prettier-plugin-svelte@3.4.1(prettier@3.8.1)(svelte@5.49.1))(prettier@3.8.1)(svelte@5.49.1) + version: 6.0.2(prettier-plugin-svelte@4.0.1(prettier@3.8.1)(svelte@5.49.1))(prettier@3.8.1)(svelte@5.49.1) '@types/node': specifier: ^24.3.0 version: 24.10.9 @@ -111,8 +111,8 @@ importers: specifier: ^1.8.8 version: 1.8.9(typescript@5.8.2) prettier-plugin-svelte: - specifier: ^3.4.0 - version: 3.4.1(prettier@3.8.1)(svelte@5.49.1) + specifier: ^4.0.1 + version: 4.0.1(prettier@3.8.1)(svelte@5.49.1) pug-lexer: specifier: ^5.0.1 version: 5.0.1 @@ -2073,11 +2073,12 @@ packages: peerDependencies: typescript: '>4.0.0' - prettier-plugin-svelte@3.4.1: - resolution: {integrity: sha512-xL49LCloMoZRvSwa6IEdN2GV6cq2IqpYGstYtMT+5wmml1/dClEoI0MZR78MiVPpu6BdQFfN0/y73yO6+br5Pg==} + prettier-plugin-svelte@4.0.1: + resolution: {integrity: sha512-oDVmtKi+M8bJeUoMfPvulUqZYcuXrs5AmhhLYPKtBeg6hcpMdx7UYYisVCqEaLQuKtiPSYFpotfwp4cZK3D4xw==} + engines: {node: '>=20'} peerDependencies: prettier: ^3.0.0 - svelte: ^3.2.0 || ^4.0.0-next.0 || ^5.0.0-next.0 + svelte: ^5.0.0 prettier@3.8.1: resolution: {integrity: sha512-UOnG6LftzbdaHZcKoPFtOcCKztrQ57WkHDeRD9t/PTQtmT0NHSeWWepj6pS0z/N7+08BHFDQVUrfmfMRcZwbMg==} @@ -3241,7 +3242,7 @@ snapshots: dependencies: acorn: 8.15.0 - '@trivago/prettier-plugin-sort-imports@6.0.2(prettier-plugin-svelte@3.4.1(prettier@3.8.1)(svelte@5.49.1))(prettier@3.8.1)(svelte@5.49.1)': + '@trivago/prettier-plugin-sort-imports@6.0.2(prettier-plugin-svelte@4.0.1(prettier@3.8.1)(svelte@5.49.1))(prettier@3.8.1)(svelte@5.49.1)': dependencies: '@babel/generator': 7.29.0 '@babel/parser': 7.29.0 @@ -3253,7 +3254,7 @@ snapshots: parse-imports-exports: 0.2.4 prettier: 3.8.1 optionalDependencies: - prettier-plugin-svelte: 3.4.1(prettier@3.8.1)(svelte@5.49.1) + prettier-plugin-svelte: 4.0.1(prettier@3.8.1)(svelte@5.49.1) svelte: 5.49.1 transitivePeerDependencies: - supports-color @@ -4326,7 +4327,7 @@ snapshots: prettier: 3.8.1 typescript: 5.8.2 - prettier-plugin-svelte@3.4.1(prettier@3.8.1)(svelte@5.49.1): + prettier-plugin-svelte@4.0.1(prettier@3.8.1)(svelte@5.49.1): dependencies: prettier: 3.8.1 svelte: 5.49.1 diff --git a/src/index.ts b/src/index.ts index fc5e673..c5a42ae 100644 --- a/src/index.ts +++ b/src/index.ts @@ -945,14 +945,16 @@ function transformSvelte(ast: any, env: TransformerEnv) { continue } - for (let i = 0; i < attr.value.length; i++) { - let value = attr.value[i] + let values = getSvelteAttributeValues(attr) + + for (let i = 0; i < values.length; i++) { + let value = values[i] if (value.type === 'Text') { let same = value.raw === value.data value.raw = sortClasses(value.raw, { env, ignoreFirst: i > 0 && !/^\s/.test(value.raw), - ignoreLast: i < attr.value.length - 1 && !/\s$/.test(value.raw), + ignoreLast: i < values.length - 1 && !/\s$/.test(value.raw), removeDuplicates: true, collapseWhitespace: false, }) @@ -961,11 +963,11 @@ function transformSvelte(ast: any, env: TransformerEnv) { : sortClasses(value.data, { env, ignoreFirst: i > 0 && !/^\s/.test(value.data), - ignoreLast: i < attr.value.length - 1 && !/\s$/.test(value.data), + ignoreLast: i < values.length - 1 && !/\s$/.test(value.data), removeDuplicates: true, collapseWhitespace: false, }) - } else if (value.type === 'MustacheTag') { + } else if (value.type === 'MustacheTag' || value.type === 'ExpressionTag') { visit(value.expression, { Literal(node) { if (isStringLiteral(node)) { @@ -1010,27 +1012,46 @@ function transformSvelte(ast: any, env: TransformerEnv) { } } - for (let child of ast.children ?? []) { + for (let child of getSvelteChildNodes(ast)) { transformSvelte(child, env) } +} - if (ast.type === 'IfBlock') { - for (let child of ast.else?.children ?? []) { - transformSvelte(child, env) - } - } - - if (ast.type === 'AwaitBlock') { - let nodes = [ast.pending, ast.then, ast.catch] +function getSvelteAttributeValues(attr: any) { + if (Array.isArray(attr.value)) return attr.value + if (attr.value && typeof attr.value === 'object') return [attr.value] + return [] +} - for (let child of nodes) { - transformSvelte(child, env) - } +function getSvelteChildNodes(node: any) { + let children = [] + + for (let key of [ + 'children', + 'nodes', + 'fragment', + 'html', + 'else', + 'consequent', + 'alternate', + 'body', + 'fallback', + 'pending', + 'then', + 'catch', + ]) { + children.push(...getSvelteNodes(node[key])) } - if (ast.html) { - transformSvelte(ast.html, env) - } + return children +} + +function getSvelteNodes(value: any) { + if (Array.isArray(value)) return value.filter((node) => node?.type) + if (Array.isArray(value?.nodes)) return value.nodes + if (Array.isArray(value?.children)) return value.children + if (value?.type) return [value] + return [] } export { options } from './options.js' diff --git a/tests/plugins.test.ts b/tests/plugins.test.ts index 9c15329..eb940c2 100644 --- a/tests/plugins.test.ts +++ b/tests/plugins.test.ts @@ -396,6 +396,7 @@ import Custom from '../components/Custom.astro' }, { plugins: ['prettier-plugin-svelte'], + options: { tailwindFunctions: ['cn'] }, tests: { svelte: [ t`
`, @@ -409,6 +410,10 @@ import Custom from '../components/Custom.astro' t``, t``, t``, + [ + `