Skip to content

Error resolving preset within config file #1221

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

Closed
nwhittaker opened this issue Feb 19, 2025 · 19 comments · Fixed by #1225 or #1235
Closed

Error resolving preset within config file #1221

nwhittaker opened this issue Feb 19, 2025 · 19 comments · Fixed by #1225 or #1235
Labels
bug Something isn't working

Comments

@nwhittaker
Copy link

nwhittaker commented Feb 19, 2025

Possibly related to #1185. The plugin is throwing an error during initialization. It appears to no longer be able to follow one of the preset paths.

The project is in a monorepo using pnpm to manage dependencies. The preset file exists as a dependency of the parent project. The tailwind config exists in a child project. As a result, the child project's dependency is a symlink to the parent project's dependency.

The underscores in the stack trace are placeholders to privatize the name of the dependency while maintaining the line length.

[Global] Initializing projects...
[packages/child-project/config/tailwind.config.js] Initializing...
[packages/child-project/config/tailwind.config.js] supported features: ["layer:base","separator:root","content-list","jit","css-at-config-as-project","relative-content-paths","transpiled-configs"]
[packages/child-project/config/tailwind.config.js] Loaded tailwindcss v3.4.17: /parent-project/node_modules/.pnpm/[email protected][email protected]_@[email protected]_@[email protected]__@[email protected][email protected]_/node_modules/tailwindcss
[packages/child-project/config/tailwind.config.js] Loaded Tailwind CSS config file: /parent-project/packages/child-project/config/tailwind.config.js
[packages/child-project/config/tailwind.config.js] Loaded postcss v8.5.1: /parent-project/node_modules/.pnpm/[email protected]/node_modules/postcss
[packages/child-project/config/tailwind.config.js] Building...
[Error - 2:10:18 PM] Unable to load config file at: /parent-project/packages/child-project/config/tailwind.config.js
[Error - 2:10:18 PM] TypeError [ERR_INVALID_ARG_VALUE]: The argument 'path' must be a string, Uint8Array, or URL without null bytes. Received '/parent-project/node_modules/.pnpm/____________________@[email protected]+___________________________...
    at readFileSync (node:fs:448:42)
    at t.readFileSync (node:electron/js2c/node_init:2:10175)
    at require.readFileSync (eval at <anonymous> (/Users/username/.vscode/extensions/bradlc.vscode-tailwindcss-0.14.6/dist/tailwindServer.js:3548:800), <anonymous>:9:14)
    at jiti (/parent-project/node_modules/.pnpm/[email protected]/node_modules/jiti/dist/jiti.js:1:245281)
    at /parent-project/packages/child-project/config/tailwind.config.js:5:5
    at evalModule (/parent-project/node_modules/.pnpm/[email protected]/node_modules/jiti/dist/jiti.js:1:247313)
    at jiti (/parent-project/node_modules/.pnpm/[email protected]/node_modules/jiti/dist/jiti.js:1:245241)
    at /parent-project/node_modules/.pnpm/[email protected][email protected]_@[email protected]_@[email protected]__@[email protected][email protected]_/node_modules/tailwindcss/lib/lib/load-config.js:52:26
    at Object.loadConfig [as module] (/parent-project/node_modules/.pnpm/[email protected][email protected]_@[email protected]_@[email protected]__@[email protected][email protected]_/node_modules/tailwindcss/lib/lib/load-config.js:62:6)
    at D (/Users/username/.vscode/extensions/bradlc.vscode-tailwindcss-0.14.6/dist/tailwindServer.js:3542:11614) {
  code: 'ERR_INVALID_ARG_VALUE'
}
[Global] Initialized 1 projects
@thecrypticace
Copy link
Contributor

I don't think this isn't related to recursive symlinks. It wouldn't get that far to loading the config file. Any chance you could post the config file (even if the package name is redacted)?

@thecrypticace thecrypticace added the question Further information is requested label Feb 19, 2025
@thecrypticace
Copy link
Contributor

Ooh also do you have a typescript config file with path mappings in the project?

@nwhittaker
Copy link
Author

I don't think this isn't related to recursive symlinks. It wouldn't get that far to loading the config file.

To clarify, the config file itself is not accessed via a symlink. It's a path in the preset config that is a symlink. If I copy the shared dependency to the child project so the preset is accessed directly, I no longer see the error. Interestingly, I have another preset that also follows a symlink, but does work. The only difference is this symlink leads to another package in the monorepo (instead of to a dependency in the pnpm store).

Any chance you could post the config file (even if the package name is redacted)?

It looks something like this:

/** @type {import('tailwindcss/types').Config} */
module.exports = {
  presets: [
    require('___________/tailwindcss/presets/default.config.cjs'), // <- works
    require('____________________/config/tailwind.config.js'),     // <- fails with error
  ],

  content: […],
  theme: {…},
}

Ooh also do you have a typescript config file with path mappings in the project?

I do. I tried removing them and also adding an explicit mapping for the preset file, but didn't get a difference in behavior. Happy to try something else, if needed.

@thecrypticace
Copy link
Contributor

So I tracked the error in Node so I could understand what the exact requirements are for the validated path because it's obviously getting a string (or at least it prints as one) and it seems like the path itself can't include a null byte regardless of it being a string or UInt8Array.

It would be super weird that the path has one in it but I can't think of another reason it'd fail.

Any chance you're on on Windows? Also what version of the extension are you using?

@nwhittaker
Copy link
Author

Any chance you're on on Windows? Also what version of the extension are you using?

I'm on macOS 15.3.1 running version 0.14.6 of the extension and v22.12.0 of node. I see that extension version was recently published, but I know I've seen the error for a little while now.

I tried playing around with the path string. It's cut off in the error log, but the full path contains a "#" character. When I remove that character, I no longer get the error. At this point, this feels more like a bug with pnpm or node.

@nwhittaker
Copy link
Author

I suspect require() is transforming the path in a bad way. If I call readFileSync() directly with the path, it works. If I move the "#" character towards the front of the string so it doesn't get cut off in the log, I see it's escaped as "\x00#" which should explain why readFileSync() is throwing.

@thecrypticace
Copy link
Contributor

thecrypticace commented Feb 20, 2025

but the full path contains a "#" character.

OOOH! Yep. This might a problem with enhanced-resolve. It allows escaping # characters with \0 (no idea why) but afaik it's not supposed to return paths with a NUL byte. Lemme look into this.

https://github.com/webpack/enhanced-resolve?tab=readme-ov-file#escaping

@thecrypticace
Copy link
Contributor

Oh literally says it right there in the readme…

When a # is resolved as path it will be escaped in the result. Here: .../some\0#thing.js.

So I guess this is something we've got to handle on our side. Fun.

@thecrypticace thecrypticace added bug Something isn't working and removed question Further information is requested labels Feb 20, 2025
@thecrypticace
Copy link
Contributor

The fix will go out in the next release — probably Monday or Tuesday. 👍

@nwhittaker
Copy link
Author

nwhittaker commented Feb 25, 2025

@thecrypticace, can we reopen this issue? I'm getting the same stack trace after updating the plugin to 0.14.7:

[Error - 1:12:37 PM] TypeError [ERR_INVALID_ARG_VALUE]: The argument 'path' must be a string, Uint8Array, or URL without null bytes. Received '…/config/tail\x00#wind.config.js'
    at readFileSync (node:fs:448:42)
    at t.readFileSync (node:electron/js2c/node_init:2:10175)
    at require.readFileSync (eval at <anonymous> (…/.vscode/extensions/bradlc.vscode-tailwindcss-0.14.7/dist/tailwindServer.js:3548:800), <anonymous>:9:14)

Looking at the test in the PR, I think I see the difference. It's testing a direct file path while my use-case is going through the module resolver.

I have a patch with a failing test: test-resolver.patch.diff.zip. If you edit file paths to remove the "#", it passes -- so I believe it's exercising the right functionality.

Also including the test in plain text in case you don't want to open a zip file:

Test code
defineTest({
  name: 'v3: Presets with a `#` in the resolved name are loadable',
  fs: {
    'package.json': json`
      {
        "dependencies": {
          "tailwindcss": "3.4.17",
          "preset": "file:./a#b.js"
        }
      }
    `,
    'tailwind.config.js': js`
      module.exports = {
        presets: [require('preset').default]
      }
    `,
    'a#b.js': js`
      export default {
        plugins: [
          function ({ addUtilities }) {
            addUtilities({
              '.example': {
                color: 'red',
              },
            })
          }
        ]
      }
    `,
  },
  prepare: async ({ root }) => ({ client: await createClient({ root }) }),
  handle: async ({ client }) => {
    let document = await client.open({
      lang: 'html',
      text: '<div class="example">',
    })

    // <div class="example">
    //             ^
    let hover = await document.hover({ line: 0, character: 13 })

    expect(hover).toEqual({
      contents: {
        language: 'css',
        value: dedent`
          .example {
            color: red;
          }
        `,
      },
      range: {
        start: { line: 0, character: 12 },
        end: { line: 0, character: 19 },
      },
    })
  },
})

@thecrypticace thecrypticace reopened this Feb 25, 2025
@thecrypticace
Copy link
Contributor

@nwhittaker The above test fails for a different reason. That's an invalid package definition because apparently npm doesn't support # in file names in dependencies. NPM creates a symlink to ./a instead of ./a#b.js (its treating the # as a fragment identifier)

Looking at the test in the PR, I think I see the difference. It's testing a direct file path while my use-case is going through the module resolver.

I'm not sure what case your situation is hitting. The only uses of the resolver now replace \0 so if there's an extra \0 somewhere it seems like it's being introduced by something else. I'm not sure why that would be though.

Lemme think on this some…

@thecrypticace
Copy link
Contributor

Oh also @nwhittaker it looks like from the error message that the NUL byte is… in the name of the config file itself. That seems super weird.

@thecrypticace
Copy link
Contributor

Ah I think I might know what it is

@nwhittaker
Copy link
Author

Oh also @nwhittaker it looks like from the error message that the NUL byte is… in the name of the config file itself. That seems super weird.

Agreed, that's just me contriving the file path. In reality it looks more like node_modules/.pnpm/module@repo#commit/config/tailwind.config.js.

@nwhittaker
Copy link
Author

@nwhittaker The above test fails for a different reason. That's an invalid package definition because apparently npm doesn't support # in file names in dependencies. NPM creates a symlink to ./a instead of ./a#b.js (its treating the # as a fragment identifier)

In reality I have the "#" appearing in the dep's directory path, not its file name. Not sure if that makes a difference here, though…

@thecrypticace
Copy link
Contributor

Oh also @nwhittaker it looks like from the error message that the NUL byte is… in the name of the config file itself. That seems super weird.

Agreed, that's just me contriving the file path. In reality it looks more like node_modules/.pnpm/module@repo#commit/config/tailwind.config.js.

ooooooooh okay… 🤔

I think I know what the problem is (the resolveFrom function in packages/tailwindcss-language-server/src/util/resolveFrom.ts) but I am having one heck of a time writing a test to verify. Seems to pass no matter what I do 😭

@thecrypticace
Copy link
Contributor

Okay, I figured out a way to test it. Got a PR open. Will get it merged in the am.

@thecrypticace
Copy link
Contributor

That release should be out now. I really hope this works lol

@nwhittaker
Copy link
Author

Looks to be working great now! Thanks for the quick fixes.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Something isn't working
Projects
None yet
2 participants