Skip to content

Commit faa8f38

Browse files
authored
fix: allow tailwindcss plugins to be added after initial execution (#456)
1 parent 7dcf6d0 commit faa8f38

File tree

2 files changed

+29
-24
lines changed

2 files changed

+29
-24
lines changed

.changeset/ninety-islands-cross.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
'sv': patch
3+
---
4+
5+
fix: properly add tailwind plugins on subsequent add-on executions

packages/addons/tailwindcss/index.ts

Lines changed: 24 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
import { defineAddon, defineAddonOptions } from '@sveltejs/cli-core';
2-
import { addAtRule, addImports } from '@sveltejs/cli-core/css';
32
import { array, functions, imports, object, exports } from '@sveltejs/cli-core/js';
43
import { parseCss, parseJson, parseScript, parseSvelte } from '@sveltejs/cli-core/parsers';
54
import { addSlot } from '@sveltejs/cli-core/html';
@@ -74,37 +73,38 @@ export default defineAddon({
7473
});
7574

7675
sv.file('src/app.css', (content) => {
77-
if (content.includes('tailwindcss')) {
78-
return content;
76+
let atRules = parseCss(content).ast.nodes.filter((node) => node.type === 'atrule');
77+
78+
const findAtRule = (name: string, params: string) =>
79+
atRules.find(
80+
(rule) =>
81+
rule.name === name &&
82+
// checks for both double and single quote variants
83+
rule.params.replace(/['"]/g, '') === params
84+
);
85+
86+
let code = content;
87+
const importsTailwind = findAtRule('import', 'tailwindcss');
88+
if (!importsTailwind) {
89+
code = "@import 'tailwindcss';\n" + code;
90+
// reparse to account for the newly added tailwindcss import
91+
atRules = parseCss(code).ast.nodes.filter((node) => node.type === 'atrule');
7992
}
8093

81-
const { ast, generateCode } = parseCss(content);
82-
const originalFirst = ast.first;
83-
84-
const nodes = addImports(ast, ["'tailwindcss'"]);
94+
const lastAtRule = atRules.findLast((rule) => ['plugin', 'import'].includes(rule.name));
95+
const pluginPos = lastAtRule!.source!.end!.offset;
8596

8697
for (const plugin of plugins) {
8798
if (!options.plugins.includes(plugin.id)) continue;
8899

89-
addAtRule(ast, 'plugin', `'${plugin.package}'`, true);
90-
}
91-
92-
if (
93-
originalFirst !== ast.first &&
94-
originalFirst?.type === 'atrule' &&
95-
originalFirst.name === 'import'
96-
) {
97-
originalFirst.raws.before = '\n';
100+
const pluginRule = findAtRule('plugin', plugin.package);
101+
if (!pluginRule) {
102+
const pluginImport = `\n@plugin '${plugin.package}';`;
103+
code = code.substring(0, pluginPos) + pluginImport + code.substring(pluginPos);
104+
}
98105
}
99106

100-
// We remove the first node to avoid adding a newline at the top of the stylesheet
101-
nodes.shift();
102-
103-
// Each node is prefixed with single newline, ensuring the imports will always be single spaced.
104-
// Without this, the CSS printer will vary the spacing depending on the current state of the stylesheet
105-
nodes.forEach((n) => (n.raws.before = '\n'));
106-
107-
return generateCode();
107+
return code;
108108
});
109109

110110
if (!kit) {

0 commit comments

Comments
 (0)