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

[Bug]: ESM-only stylelint plugins fail to import with Yarn PnP #464

Open
1 task done
kherock opened this issue Jul 29, 2023 · 8 comments
Open
1 task done

[Bug]: ESM-only stylelint plugins fail to import with Yarn PnP #464

kherock opened this issue Jul 29, 2023 · 8 comments
Labels
type: bug a problem with a feature or rule

Comments

@kherock
Copy link

kherock commented Jul 29, 2023

How did you encounter this bug?

The stylelint-prettier extension recently refactored its code to use Prettier's ESM entrypoint in prettier/stylelint-prettier#305. Yarn's PnP resolution does support ESM, but this comment from #272 is not completely true as it only pertains to CJS modules:

Just calling our setup function from within the script that will perform the require should be enough - it'll inject the runtime within the process and the rest (like the zip accesses via fs) is transparent.

The only way to hook into Node's ESM resolution is to pass --loader as a CLI arg or in NODE_OPTIONS.

Link to Minimal Reproducible Example

https://github.com/kherock/vscode-stylelint-yarn-esm-bug

Code Snippet

No response

Stylelint Configuration

extends:
  - stylelint-prettier/recommended

Extension Configuration

No response

Actual Behaviour

When Stylelint or a Stylelint plugin attempts to import ESM during server startup, an ERR_MODULE_NOT_FOUND error is thrown.

Expected Behaviour

The Stylint extension should check for a .pnp.loader.mjs file for Yarn projects and configure all spawned child processes to use Yarn's ESM resolution via the --loader option (which is supported on all non-EOL'd Node.js versions).

Logs

[Debug - 12:54:17 p.m.] [language-server] Resolved Stylelint using PnP | path: "/Users/herockk/Workspaces/example-project/.pnp.cjs"
[Debug - 12:54:17 p.m.] [language-server] Running Stylelint | options: {"ignoreDisables":false,"reportDescriptionlessDisables":false,"reportNeedlessDisables":false,"reportInvalidScopeDisables":false,"ignorePath":"/Users/herockk/Workspaces/example-project/.stylelintignore","code":"...","codeFilename":"/Users/herockk/Workspaces/example-project/packages/react/error-page/style.scss"}
node:internal/process/promises:279
            triggerUncaughtException(err, true /* fromPromise */);
            ^

Error [ERR_MODULE_NOT_FOUND]: Cannot find package 'prettier' imported from /Users/herockk/Workspaces/example-project/.yarn/__virtual__/stylelint-prettier-virtual-333ffca36b/0/cache/stylelint-prettier-npm-4.0.2-013484b286-b60112c10b.zip/node_modules/stylelint-prettier/stylelint-prettier.js
Did you mean to import prettier-npm-3.0.0-7ffbcce680-6a832876a1.zip/node_modules/prettier/index.cjs?
    at new NodeError (node:internal/errors:387:5)
    at packageResolve (node:internal/modules/esm/resolve:957:9)
    at moduleResolve (node:internal/modules/esm/resolve:1006:20)
    at defaultResolve (node:internal/modules/esm/resolve:1220:11)
    at nextResolve (node:internal/modules/esm/loader:165:28)
    at ESMLoader.resolve (node:internal/modules/esm/loader:844:30)
    at ESMLoader.getModuleJob (node:internal/modules/esm/loader:431:18)
    at ESMLoader.import (node:internal/modules/esm/loader:528:22)
    at importModuleDynamically (node:internal/modules/cjs/loader:1119:29)
    at importModuleDynamicallyWrapper (node:internal/vm/module:438:21) {
  code: 'ERR_MODULE_NOT_FOUND'
}

Stylelint Version

v15.10.1

vscode-stylelint Version

v1.2.4

Node.js Version

v16.20.1

Operating System

macOS Ventura 13.4.1

Windows Subsystem for Linux

No response

Code of Conduct

  • I agree to follow vscode-stylelint's Code of Conduct
@kherock kherock added the type: bug a problem with a feature or rule label Jul 29, 2023
@kherock kherock changed the title [Bug]: ESM-only stylelint plugins fail to import with PnP [Bug]: ESM-only stylelint plugins fail to import with Yarn PnP Jul 29, 2023
@bryanjtc
Copy link

@kherock Have you found a workaround or an alternative?

@guitartsword
Copy link

guitartsword commented Nov 3, 2023

@kherock Have you found a workaround or an alternative?

I have a workaround:

# inside your yarn PnP project folder
cd ..
yarn init
yarn config set nodeLinker node-modules
yarn add prettier

just make sure it created a node_modules folder and not using PnP

@psychobolt
Copy link

psychobolt commented Jan 7, 2024

I have same issue trying to use Stylelint 16. Seems like their API is now deprecating all common-js plugins and extensions. I don't think using Stylelint modules directly for Yarn ESM PnP is viable. We may need to spawn stylelint as a process. This most likely need a parser similar to SublineLinter-Stylelint's that convert each message to a warning object

@psychobolt
Copy link

psychobolt commented Mar 21, 2024

I managed to create a hybrid PnP + node_modules setup for my project so I can migrate to the latest stylelint version (v16). You can manually link to node_modules using a similar configuration:

First install stylelint and any configurations into a non-PnP workspace. Optionally install the same configurations into your main project:

yarn workspace third-party add -DE stylelint stylelint-config-standard-prettier stylelint-config-standard-scss # ... 
# yarn add -DE stylelint stylelint-config-standard-prettier stylelint-config-standard-scss # ... 

For the configuration, you can use Yarn's INIT_CWD environment variable to detect if stylelint is running with Yarn or VSCode's node to resolve module path:

Update (3/27/24): stylelint now requires /index.js to exists. See: stylelint/stylelint#7578

// stylelint.config.js
import { createRequire } from 'module';
import path from 'path';

const require = createRequire(
  process.env.INIT_CWD ?? // leave out if not mirroring setup for running stylelint CLI with Yarn node
    path.join(process.cwd(), 'packages/third-party/node_modules')
);

/** @type {import('stylelint').Config} */
export default {
  extends: [
    require.resolve("stylelint-config-standard-scss"),
    require.resolve("stylelint-config-prettier-scss"),
  ]
};

Set the library path in .vscode/settings.json

{
  "stylelint.stylelintPath": "packages/third-party/node_modules/stylelint",
}

The above setup should work in VSCode and running stylelint CLI.

@InvisibleGit
Copy link

First install stylelint and any configurations into a non-PnP workspace. Optionally install the same configurations into your main project:

That's basically the same hack @guitartsword suggested and isn't a solution.

This issue makes vscode-stylelint unusable for Yarn users for a long time, and stylelint and it's various plugins have moved very far in the past year. Unfortunately I don't know where to start looking for a solution, and is it an issue with this extension, vs-code or yarn itself... :(

@psychobolt
Copy link

Yeah that is indeed a workaround. Right now the only way for Yarn to resolve ESM modules is using their node loader. Unfortately, that would require to support runtime as its own process and forwarding , which is how vscode-eslint works.

@kherock
Copy link
Author

kherock commented May 10, 2024

With the Node 20.6 register utility and #439, it should be possible patch in PnP ESM resolution to vscode-stylelint's Node.js process. I'm trying to use this in my .yarn/sdks/stylelint:

#!/usr/bin/env node

const {existsSync} = require(`fs`);
const {createRequire, register} = require(`module`);
const {resolve} = require(`path`);
const { pathToFileURL } = require("url");

const relPnpApiPath = "../../../../.pnp.cjs";
const relPnpLoaderPath = "../../../../.pnp.loader.mjs";

const absPnpApiPath = resolve(__dirname, relPnpApiPath);
const absRequire = createRequire(absPnpApiPath);

if (existsSync(absPnpApiPath)) {
  if (!process.versions.pnp) {
    // Setup the environment to be able to require stylelint
    require(absPnpApiPath).setup();
    register(relPnpLoaderPath, pathToFileURL(__dirname))
  }
}

// Defer to the real stylelint your application uses
module.exports = absRequire(`stylelint`);

The extension tells me that register is not a function, which makes sense as Electron doesn't support these features yet.

@Mouvedia
Copy link
Member

@kherock Electron 29 uses Node.js 20.9.0.
i.e. the next version of vscode will support it

ref microsoft/vscode#209818

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
type: bug a problem with a feature or rule
Projects
None yet
Development

No branches or pull requests

6 participants