Repository: tailwindlabs/tailwindcss

Problem statement:
JIT analysis the comments code to add that classes instead of omit it
<!-- Please provide all of the information requested below. We're a small team and without all of this information it's not possible for us to help and your bug report will be closed. -->

**What version of Tailwind CSS are you using?**

For example: v3.0.17

**What build tool (or framework if it abstracts the build tool) are you using?**

For example: postcss 8.4.5, Next.js 12.1.0

**What version of Node.js are you using?**

For example: v17.3.1

**What browser are you using?**

For example: N/A

**What operating system are you using?**

macOS

**Describe your issue**

Storybook can't start because JIT parses code from comments

<img width="563" alt="imagen" src="https://user-images.githubusercontent.com/13077343/157001625-06c4caa4-b6b8-4e9d-90f9-4dcfd1daa6ee.png">



Hints:
Hey, this is covered in the docs: https://tailwindcss.com/docs/content-configuration#dynamic-class-names. Tailwind CSS does not interpret your source code so dynamic class names like this won't work. Ideally, we would just _not_ generate the CSS here since it's generating values that appear to result in a syntax error. Will see what we can do about this.

Patch:
diff --git a/src/lib/generateRules.js b/src/lib/generateRules.js
--- a/src/lib/generateRules.js
+++ b/src/lib/generateRules.js
@@ -303,6 +303,19 @@ function looksLikeUri(declaration) {
   }
 }
 
+function isParsableNode(node) {
+  let isParsable = true
+
+  node.walkDecls((decl) => {
+    if (!isParsableCssValue(decl.name, decl.value)) {
+      isParsable = false
+      return false
+    }
+  })
+
+  return isParsable
+}
+
 function isParsableCssValue(property, value) {
   // We don't want to to treat [https://example.com] as a custom property
   // Even though, according to the CSS grammar, it's a totally valid CSS declaration
@@ -456,60 +469,66 @@ function* resolveMatches(candidate, context) {
       }
     }
 
-    // Only keep the result of the very first plugin if we are dealing with
-    // arbitrary values, to protect against ambiguity.
-    if (isArbitraryValue(modifier) && matches.length > 1) {
-      let typesPerPlugin = matches.map((match) => new Set([...(typesByMatches.get(match) ?? [])]))
+    if (isArbitraryValue(modifier)) {
+      // When generated arbitrary values are ambiguous, we can't know
+      // which to pick so don't generate any utilities for them
+      if (matches.length > 1) {
+        let typesPerPlugin = matches.map((match) => new Set([...(typesByMatches.get(match) ?? [])]))
 
-      // Remove duplicates, so that we can detect proper unique types for each plugin.
-      for (let pluginTypes of typesPerPlugin) {
-        for (let type of pluginTypes) {
-          let removeFromOwnGroup = false
+        // Remove duplicates, so that we can detect proper unique types for each plugin.
+        for (let pluginTypes of typesPerPlugin) {
+          for (let type of pluginTypes) {
+            let removeFromOwnGroup = false
 
-          for (let otherGroup of typesPerPlugin) {
-            if (pluginTypes === otherGroup) continue
+            for (let otherGroup of typesPerPlugin) {
+              if (pluginTypes === otherGroup) continue
 
-            if (otherGroup.has(type)) {
-              otherGroup.delete(type)
-              removeFromOwnGroup = true
+              if (otherGroup.has(type)) {
+                otherGroup.delete(type)
+                removeFromOwnGroup = true
+              }
             }
-          }
 
-          if (removeFromOwnGroup) pluginTypes.delete(type)
+            if (removeFromOwnGroup) pluginTypes.delete(type)
+          }
         }
-      }
 
-      let messages = []
-
-      for (let [idx, group] of typesPerPlugin.entries()) {
-        for (let type of group) {
-          let rules = matches[idx]
-            .map(([, rule]) => rule)
-            .flat()
-            .map((rule) =>
-              rule
-                .toString()
-                .split('\n')
-                .slice(1, -1) // Remove selector and closing '}'
-                .map((line) => line.trim())
-                .map((x) => `      ${x}`) // Re-indent
-                .join('\n')
+        let messages = []
+
+        for (let [idx, group] of typesPerPlugin.entries()) {
+          for (let type of group) {
+            let rules = matches[idx]
+              .map(([, rule]) => rule)
+              .flat()
+              .map((rule) =>
+                rule
+                  .toString()
+                  .split('\n')
+                  .slice(1, -1) // Remove selector and closing '}'
+                  .map((line) => line.trim())
+                  .map((x) => `      ${x}`) // Re-indent
+                  .join('\n')
+              )
+              .join('\n\n')
+
+            messages.push(
+              `  Use \`${candidate.replace('[', `[${type}:`)}\` for \`${rules.trim()}\``
             )
-            .join('\n\n')
-
-          messages.push(`  Use \`${candidate.replace('[', `[${type}:`)}\` for \`${rules.trim()}\``)
-          break
+            break
+          }
         }
+
+        log.warn([
+          `The class \`${candidate}\` is ambiguous and matches multiple utilities.`,
+          ...messages,
+          `If this is content and not a class, replace it with \`${candidate
+            .replace('[', '&lsqb;')
+            .replace(']', '&rsqb;')}\` to silence this warning.`,
+        ])
+        continue
       }
 
-      log.warn([
-        `The class \`${candidate}\` is ambiguous and matches multiple utilities.`,
-        ...messages,
-        `If this is content and not a class, replace it with \`${candidate
-          .replace('[', '&lsqb;')
-          .replace(']', '&rsqb;')}\` to silence this warning.`,
-      ])
-      continue
+      matches = matches.map((list) => list.filter((match) => isParsableNode(match[1])))
     }
 
     matches = matches.flat()


Test patch:
diff --git a/tests/arbitrary-values.test.js b/tests/arbitrary-values.test.js
--- a/tests/arbitrary-values.test.js
+++ b/tests/arbitrary-values.test.js
@@ -370,3 +370,17 @@ it('should be possible to read theme values in arbitrary values (with quotes) wh
     `)
   })
 })
+
+it('should not output unparsable arbitrary CSS values', () => {
+  let config = {
+    content: [
+      {
+        raw: 'let classes = `w-[${sizes.width}]`',
+      },
+    ],
+  }
+
+  return run('@tailwind utilities', config).then((result) => {
+    return expect(result.css).toMatchFormattedCss(``)
+  })
+})