From faa8f382b82ac4fe8aa5af41fc97564c41bc92cd Mon Sep 17 00:00:00 2001 From: CokaKoala <31664583+AdrianGonz97@users.noreply.github.com> Date: Thu, 27 Feb 2025 11:33:31 -0500 Subject: [PATCH] fix: allow `tailwindcss` plugins to be added after initial execution (#456) --- .changeset/ninety-islands-cross.md | 5 +++ packages/addons/tailwindcss/index.ts | 48 ++++++++++++++-------------- 2 files changed, 29 insertions(+), 24 deletions(-) create mode 100644 .changeset/ninety-islands-cross.md diff --git a/.changeset/ninety-islands-cross.md b/.changeset/ninety-islands-cross.md new file mode 100644 index 00000000..b9b6e33a --- /dev/null +++ b/.changeset/ninety-islands-cross.md @@ -0,0 +1,5 @@ +--- +'sv': patch +--- + +fix: properly add tailwind plugins on subsequent add-on executions diff --git a/packages/addons/tailwindcss/index.ts b/packages/addons/tailwindcss/index.ts index a1a30130..18fe640b 100644 --- a/packages/addons/tailwindcss/index.ts +++ b/packages/addons/tailwindcss/index.ts @@ -1,5 +1,4 @@ import { defineAddon, defineAddonOptions } from '@sveltejs/cli-core'; -import { addAtRule, addImports } from '@sveltejs/cli-core/css'; import { array, functions, imports, object, exports } from '@sveltejs/cli-core/js'; import { parseCss, parseJson, parseScript, parseSvelte } from '@sveltejs/cli-core/parsers'; import { addSlot } from '@sveltejs/cli-core/html'; @@ -74,37 +73,38 @@ export default defineAddon({ }); sv.file('src/app.css', (content) => { - if (content.includes('tailwindcss')) { - return content; + let atRules = parseCss(content).ast.nodes.filter((node) => node.type === 'atrule'); + + const findAtRule = (name: string, params: string) => + atRules.find( + (rule) => + rule.name === name && + // checks for both double and single quote variants + rule.params.replace(/['"]/g, '') === params + ); + + let code = content; + const importsTailwind = findAtRule('import', 'tailwindcss'); + if (!importsTailwind) { + code = "@import 'tailwindcss';\n" + code; + // reparse to account for the newly added tailwindcss import + atRules = parseCss(code).ast.nodes.filter((node) => node.type === 'atrule'); } - const { ast, generateCode } = parseCss(content); - const originalFirst = ast.first; - - const nodes = addImports(ast, ["'tailwindcss'"]); + const lastAtRule = atRules.findLast((rule) => ['plugin', 'import'].includes(rule.name)); + const pluginPos = lastAtRule!.source!.end!.offset; for (const plugin of plugins) { if (!options.plugins.includes(plugin.id)) continue; - addAtRule(ast, 'plugin', `'${plugin.package}'`, true); - } - - if ( - originalFirst !== ast.first && - originalFirst?.type === 'atrule' && - originalFirst.name === 'import' - ) { - originalFirst.raws.before = '\n'; + const pluginRule = findAtRule('plugin', plugin.package); + if (!pluginRule) { + const pluginImport = `\n@plugin '${plugin.package}';`; + code = code.substring(0, pluginPos) + pluginImport + code.substring(pluginPos); + } } - // We remove the first node to avoid adding a newline at the top of the stylesheet - nodes.shift(); - - // Each node is prefixed with single newline, ensuring the imports will always be single spaced. - // Without this, the CSS printer will vary the spacing depending on the current state of the stylesheet - nodes.forEach((n) => (n.raws.before = '\n')); - - return generateCode(); + return code; }); if (!kit) {