Repository: tailwindlabs/tailwindcss-jit (js)

Issue:
JIT does not work when prefix callback is specified
### What version of @tailwindcss/jit are you using?

0.1.10

### What version of Node.js are you using?

15.9.0

### What browser are you using?

all of them

### What operating system are you using?

macOS

### Reproduction repository

https://github.com/signor-pedro/tailwind-repro

Commenting this out makes JIT work
```
prefix(selector) {
    if (['.align-bottom', '.ml-1'].includes(selector)) {
      return 'tw-';
    }

    return '';
  },
```


Pull request description:
Add support for prefix as a function
Closes #159

Due to the implementation of the JIT we must support utility class prefixes in addition to the selectors themselves.

For example for .tw-ml-1 we'd pass .ml and .ml-1 to the prefix function. This is a byproduct of how we handle matching of classes before we generate the rules.

Because of this we'll want to encourage people to use a `startsWith` check instead of equality or includes:
```js
prefix(selector) {
  if (['.align-bottom', '.ml'].some(prefix => selector.startsWith(prefix))) {
    return 'tw-';
  }

  return '';
},
```

You can also do this using an anchored regular expression which can be shorter:
```js
prefix(selector) {
  if (/^\.(align-bottom|ml)/.test(selector)) {
    return 'tw-';
  }

  return '';
},
```

Patch:
diff --git a/src/lib/setupContext.js b/src/lib/setupContext.js
index 49a354d..65538d0 100644
--- a/src/lib/setupContext.js
+++ b/src/lib/setupContext.js
@@ -401,7 +401,15 @@ function buildPluginApi(tailwindConfig, context, { variantList, variantMap, offs
       return '*'
     }
 
-    return options.respectPrefix ? context.tailwindConfig.prefix + identifier : identifier
+    if (! options.respectPrefix) {
+      return identifier
+    }
+
+    if (typeof context.tailwindConfig.prefix === 'function') {
+      return prefixSelector(context.tailwindConfig.prefix, `.${identifier}`).substr(1)
+    }
+
+    return context.tailwindConfig.prefix + identifier
   }
 
   return {


Test patch:
diff --git a/tests/prefix.fn.test.css b/tests/prefix.fn.test.css
new file mode 100644
index 0000000..e1e60a0
--- /dev/null
+++ b/tests/prefix.fn.test.css
@@ -0,0 +1,9 @@
+.tw-ml-1 {
+  margin-left: 0.25rem;
+}
+.flex {
+  display: flex;
+}
+.tw-align-bottom {
+  vertical-align: bottom
+}
diff --git a/tests/prefix.fn.test.html b/tests/prefix.fn.test.html
new file mode 100644
index 0000000..9647a5d
--- /dev/null
+++ b/tests/prefix.fn.test.html
@@ -0,0 +1,3 @@
+<div class="flex"></div>
+<div class="tw-align-bottom"></div>
+<div class="tw-ml-1"></div>
diff --git a/tests/prefix.fn.test.js b/tests/prefix.fn.test.js
new file mode 100644
index 0000000..8e5d80e
--- /dev/null
+++ b/tests/prefix.fn.test.js
@@ -0,0 +1,34 @@
+const postcss = require('postcss')
+const tailwind = require('../src/index.js')
+const fs = require('fs')
+const path = require('path')
+
+function run(input, config = {}) {
+  return postcss([tailwind(config)]).process(input, { from: path.resolve(__filename) })
+}
+
+test('prefix fn', () => {
+  let config = {
+    prefix(selector) {
+      if (['.align-bottom', '.ml'].some(prefix => selector.startsWith(prefix))) {
+        return 'tw-';
+      }
+
+      return '';
+    },
+    purge: [path.resolve(__dirname, './prefix.fn.test.html')],
+    corePlugins: { preflight: false },
+    theme: {},
+  }
+
+  let css = `
+    @tailwind utilities;
+  `
+
+  return run(css, config).then((result) => {
+    let expectedPath = path.resolve(__dirname, './prefix.fn.test.css')
+    let expected = fs.readFileSync(expectedPath, 'utf8')
+
+    expect(result.css).toMatchCss(expected)
+  })
+})