Repository: tailwindlabs/tailwindcss
Pull request #5588 (2021-09-24 15:35:50)

Handle unknown typehints
This PR improves unnecessary typehints. If we know that only a single plugin is handling `w` in this case, then any typehints can be ignored because the contents of the arbitrary values can accept anything (due to the (default) `any` typehint). 

**input:**
```html
<div class="w-[length:12px]"></div>
```

**output:**
```css
.w-\[length\:12px\] {
    width: 12px
}
```

However, this will also provide you with a warning in the terminal output:

```
warn - Unnecessary typehint `length` in `w-[length:12px]`.
warn - You can safely update it to `w-[12px]`.
```

We can remove warning if it is too much noise, but it is nice that we can detect this behaviour.
The current heuristic is to check whether only a single plugin exists that handles a certain prefix (`w-`).


Patch:
diff --git a/src/lib/generateRules.js b/src/lib/generateRules.js
index 53107b96..265e2482 100644
--- a/src/lib/generateRules.js
+++ b/src/lib/generateRules.js
@@ -226,10 +226,11 @@ function* resolveMatches(candidate, context) {
   for (let matchedPlugins of resolveMatchedPlugins(classCandidate, context)) {
     let matches = []
     let [plugins, modifier] = matchedPlugins
+    let isOnlyPlugin = plugins.length === 1
 
     for (let [sort, plugin] of plugins) {
       if (typeof plugin === 'function') {
-        for (let ruleSet of [].concat(plugin(modifier))) {
+        for (let ruleSet of [].concat(plugin(modifier, { isOnlyPlugin }))) {
           let [rules, options] = parseRules(ruleSet, context.postCssNodeCache)
           for (let rule of rules) {
             matches.push([{ ...sort, options: { ...sort.options, ...options } }, rule])
diff --git a/src/lib/setupContextUtils.js b/src/lib/setupContextUtils.js
index ed7c974d..ba643fce 100644
--- a/src/lib/setupContextUtils.js
+++ b/src/lib/setupContextUtils.js
@@ -300,15 +300,29 @@ function buildPluginApi(tailwindConfig, context, { variantList, variantMap, offs
 
         classList.add([prefixedIdentifier, options])
 
-        function wrapped(modifier) {
+        function wrapped(modifier, { isOnlyPlugin }) {
           let { type = 'any' } = options
           type = [].concat(type)
           let [value, coercedType] = coerceValue(type, modifier, options.values, tailwindConfig)
 
-          if (!type.includes(coercedType) || value === undefined) {
+          if (value === undefined) {
             return []
           }
 
+          if (!type.includes(coercedType)) {
+            if (isOnlyPlugin) {
+              log.warn([
+                `Unnecessary typehint \`${coercedType}\` in \`${identifier}-${modifier}\`.`,
+                `You can safely update it to \`${identifier}-${modifier.replace(
+                  coercedType + ':',
+                  ''
+                )}\`.`,
+              ])
+            } else {
+              return []
+            }
+          }
+
           if (!isValidArbitraryValue(value)) {
             return []
           }


Test patch:
diff --git a/tests/arbitrary-values.test.js b/tests/arbitrary-values.test.js
index e223ddeb..17252be9 100644
--- a/tests/arbitrary-values.test.js
+++ b/tests/arbitrary-values.test.js
@@ -30,6 +30,18 @@ it('should not generate any css if an unknown typehint is used', () => {
   })
 })
 
+it('should handle unknown typehints', () => {
+  let config = { content: [{ raw: html`<div class="w-[length:12px]"></div>` }] }
+
+  return run('@tailwind utilities', config).then((result) => {
+    return expect(result.css).toMatchFormattedCss(`
+      .w-\\[length\\:12px\\] {
+        width: 12px;
+      }
+    `)
+  })
+})
+
 it('should convert _ to spaces', () => {
   let config = {
     content: [