update error messages when using addComponents or matchComponents

Nothing new here, but this improves the error messages a bit when using
`addComponents` or `matchComponents` because otherwise the error shows
`addUtilities` or `matchUtilities`.

These also add an additional note:

> Note: in Tailwind CSS v4, `matchComponents` is an alias for `matchUtilities`.
This commit is contained in:
Robin Malfait 2025-08-27 14:52:42 +02:00
parent e578238da5
commit 81c883d035
No known key found for this signature in database
2 changed files with 92 additions and 4 deletions

View file

@ -4444,6 +4444,43 @@ describe('addComponents()', () => {
}"
`)
})
test('throws on custom static utilities with an invalid name', async () => {
await expect(() => {
return compile(
css`
@plugin "my-plugin";
@layer utilities {
@tailwind utilities;
}
@theme reference {
--breakpoint-lg: 1024px;
}
`,
{
async loadModule(id, base) {
return {
path: '',
base,
module: ({ addComponents }: PluginAPI) => {
addComponents({
':hover > *': {
'text-box-trim': 'both',
'text-box-edge': 'cap alphabetic',
},
})
},
}
},
},
)
}).rejects.toThrowErrorMatchingInlineSnapshot(`
[Error: \`addComponents({ ':hover > *': … })\` defines an invalid utility selector. Components must be a single class name and start with a lowercase letter, eg. \`.scrollbar-none\`.
Note: in Tailwind CSS v4 \`addComponents\` is an alias for \`addUtilities\`.]
`)
})
})
describe('matchComponents()', () => {
@ -4490,6 +4527,37 @@ describe('matchComponents()', () => {
}"
`)
})
test('throws on custom utilities with an invalid name', async () => {
await expect(() => {
return compile(
css`
@plugin "my-plugin";
@tailwind utilities;
`,
{
async loadModule(id, base) {
return {
path: '',
base,
module: ({ matchComponents }: PluginAPI) => {
matchComponents({
'.text-trim > *': () => ({
'text-box-trim': 'both',
'text-box-edge': 'cap alphabetic',
}),
})
},
}
},
},
)
}).rejects.toThrowErrorMatchingInlineSnapshot(`
[Error: \`matchComponents({ '.text-trim > *': … })\` defines an invalid utility name. Components should be alphanumeric and start with a lowercase letter, eg. \`scrollbar\`.
Note: in Tailwind CSS v4 \`matchComponents\` is an alias for \`matchUtilities\`.]
`)
})
})
describe('prefix()', () => {

View file

@ -307,7 +307,7 @@ export function buildPluginApi({
if (!foundValidUtility) {
throw new Error(
`\`addUtilities({ '${name}' : … })\` defines an invalid utility selector. Utilities must be a single class name and start with a lowercase letter, eg. \`.scrollbar-none\`.`,
`\`addUtilities({ '${name}': … })\` defines an invalid utility selector. Utilities must be a single class name and start with a lowercase letter, eg. \`.scrollbar-none\`.`,
)
}
}
@ -347,7 +347,7 @@ export function buildPluginApi({
for (let [name, fn] of Object.entries(utilities)) {
if (!IS_VALID_UTILITY_NAME.test(name)) {
throw new Error(
`\`matchUtilities({ '${name}' : … })\` defines an invalid utility name. Utilities should be alphanumeric and start with a lowercase letter, eg. \`scrollbar\`.`,
`\`matchUtilities({ '${name}': … })\` defines an invalid utility name. Utilities should be alphanumeric and start with a lowercase letter, eg. \`scrollbar\`.`,
)
}
@ -493,11 +493,31 @@ export function buildPluginApi({
},
addComponents(components, options) {
this.addUtilities(components, options)
try {
this.addUtilities(components, options)
} catch (e) {
if (e instanceof Error) {
throw new Error(
`${e.message.replaceAll('addUtilities', 'addComponents').replaceAll('Utilities', 'Components')}\n\nNote: in Tailwind CSS v4 \`addComponents\` is an alias for \`addUtilities\`.`,
)
} else {
throw e
}
}
},
matchComponents(components, options) {
this.matchUtilities(components, options)
try {
this.matchUtilities(components, options)
} catch (e) {
if (e instanceof Error) {
throw new Error(
`${e.message.replaceAll('matchUtilities', 'matchComponents').replaceAll('Utilities', 'Components')}\n\nNote: in Tailwind CSS v4 \`matchComponents\` is an alias for \`matchUtilities\`.`,
)
} else {
throw e
}
}
},
theme: createThemeFn(