Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

PostCSS warning for custom utility used with before:/after: #13591

Open
gynekolog opened this issue Apr 26, 2024 · 1 comment
Open

PostCSS warning for custom utility used with before:/after: #13591

gynekolog opened this issue Apr 26, 2024 · 1 comment
Assignees

Comments

@gynekolog
Copy link

What version of Tailwind CSS are you using?

3.4.3

What build tool (or framework if it abstracts the build tool) are you using?

Vite

What version of Node.js are you using?

v21.7.3

What browser are you using?

Chrome

What operating system are you using?

macOS

Reproduction URL

https://stackblitz.com/edit/vitest-dev-vitest-cp1r2y

Describe your issue

There's a PostCSS warning in the console:

A PostCSS plugin did not pass the `from` option to `postcss.parse`. This may cause imported assets to be incorrectly transformed. If you've recently added a PostCSS plugin that raised this warning, please contact the package author to fix the issue.

It starts when you define your own CSS utility and use it with the before: or after:.
Example:

css file:

@tailwind base;
@tailwind components;
@tailwind utilities;

@layer utilities {
  .bg-own {
    background-color: red;
  }
}

Usage:

<div class="before:bg-own" />
@wongjn
Copy link
Contributor

wongjn commented Apr 30, 2024

This could be related to the before variant core plugin (this would happen with after too), it adds a new Declaration without a source property:

rule.prepend(postcss.decl({ prop: 'content', value: 'var(--tw-content)' }))

The vite-url-rewrite PostCSS plugin internally in Vite relies on this source property:

const importer = declaration.source?.input.file
if (!importer) {
  opts.logger.warnOnce(
    '\nA PostCSS plugin did not pass the `from` option to `postcss.parse`. ' +

As per the 2.4 of the PostCSS Plugin Guidelines:

Every node must have a relevant source so PostCSS can generate an accurate source map.

So if you add a new declaration based on some existing declaration, you should clone the existing declaration in order to save that original source.

if (needPrefix(decl.prop)) {
  decl.cloneBefore({ prop: '-webkit-' + decl.prop })
}

So perhaps we could clone a declaration inside the variant plugin, instead of creating a new one from scratch? But why only with your own declared utilities? The plot thickens!


It seems when we are generating the CSS:

cloneNodes(variantNodes, root.source, {
layer: 'variants',
})

We skip setting source recursively if preserveSource is true:

let shouldPreserveSource = node.raws.tailwind?.preserveSource === true && node.source
if (shouldPreserveSource) {
return false
}

And for @layer-defined class rules, this preserveSource is indeed set to true:

layerPlugins.push(function ({ addUtilities }) {
addUtilities(node, { respectPrefix: false, preserveSource: true })
})

Thus, for plugin-defined utilities, we don't have to worry about retaining source in the before/after plugin but we should do for @layer-defined class rules if seems.


So what would be the best way to solve this? Push the onus on to the plugins that use the PostCSS API to ensure they create appropriate source values? This would mean we could fix the core plugins to match this. Or try to fill in gaps in cloneNodes()? If we do want to fill in the gaps ourselves, should the source point to the original source of the @layer rule or the relevant @tailwind declaration?

Example test for this:
test('source maps for layer rules with injected nodes from plugins', async () => {
  let config = {
    content: [{ raw: `foo:bar` }],
    plugins: [
      function ({ addVariant }) {
        addVariant('foo', ({ container }) => {
          container.walkRules((rule) => {
            rule.prepend(postcss.decl({ prop: 'foo', value: 'baz' }))
          })
        })
      },
    ],
  }

  let input = postcss.parse(
    css`
      @tailwind utilities;

      @layer utilities {
        .bar {
          background-color: red;
        }
      }
    `,
    { from: 'input.css', map: { prev: map } }
  )

  let result = await run(input, config)

  let { sources, annotations } = parseSourceMaps(result)

  // All CSS generated by Tailwind CSS should be annotated with source maps
  // And always be able to point to the original source file
  expect(sources).not.toContain('<no source>')
})

@thecrypticace thecrypticace self-assigned this May 6, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants