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

Symlinks cause parserOptions.project errors #2987

Open
3 tasks done
nikparo opened this issue Jan 31, 2021 · 25 comments
Open
3 tasks done

Symlinks cause parserOptions.project errors #2987

nikparo opened this issue Jan 31, 2021 · 25 comments
Labels
accepting prs Go ahead, send a pull request that resolves this issue bug Something isn't working help wanted Extra attention is needed package: typescript-estree Issues related to @typescript-eslint/typescript-estree

Comments

@nikparo
Copy link

nikparo commented Jan 31, 2021

  • I have tried restarting my IDE and the issue persists.
  • I have updated to the latest version of the packages.
  • I have read the FAQ and my problem is not listed.

Repro

https://github.com/nikparo/ts-eslint-symlinks

Expected Result

The targeted directories and files should be linted, whether or not there are symlinks elsewhere pointing at them.

Actual Result

If there is a symlink somewhere pointing at a directory, then linting it will throw Parsing error: "parserOptions.project" has been set for @typescript-eslint/parser. errors. At least if the symlink alphabetically comes before the directory.

> [email protected] lint /Users/nik/Projects/tmp/ts-eslint-symlinks
> eslint libs/symlinked-lib/**/*.ts

  typescript-eslint:typescript-estree:parser parserOptions.project (excluding ignored) matched projects: Set { '/users/nik/projects/tmp/ts-eslint-symlinks/tsconfig.base.json' } +0ms
  typescript-eslint:typescript-estree:createProjectProgram Creating project program for: /Users/nik/Projects/tmp/ts-eslint-symlinks/libs/symlinked-lib/src/index.ts +0ms
  typescript-eslint:typescript-estree:createWatchProgram File did not belong to any existing programs, moving to create/update. /users/nik/projects/tmp/ts-eslint-symlinks/libs/symlinked-lib/src/index.ts +0ms
  typescript-eslint:typescript-estree:createWatchProgram Creating watch program for /users/nik/projects/tmp/ts-eslint-symlinks/tsconfig.base.json. +0ms

/Users/nik/Projects/tmp/ts-eslint-symlinks/libs/symlinked-lib/src/index.ts
  0:0  error  Parsing error: "parserOptions.project" has been set for @typescript-eslint/parser.
The file does not match your project config: libs/symlinked-lib/src/index.ts.
The file must be included in at least one of the projects provided

By deleting the symlink apps/legacy-app/libs (which points at libs) the errors go away. Whether the symlink is ignored or not using ignorePatterns does not seem to matter.

Additional Info

I believe I have tracked this down to the use of updatedProgram.getRootFileNames() in createWatchProgram.js. That method call seems to only get the first instance of a symlinked file. I.e. apps/legacy-app/libs/symlinked-lib/src/index.ts will be in the returned fileList, but libs/symlinked-lib/src/index.ts will not.

Versions

package version
@typescript-eslint/typescript-estree 4.14.1
TypeScript 4.1.3
node v12.20.0
@nikparo nikparo added package: typescript-estree Issues related to @typescript-eslint/typescript-estree triage Waiting for team members to take a look labels Jan 31, 2021
@bradzacher
Copy link
Member

bradzacher commented Jan 31, 2021

We build on top of TypeScript and eslint, and we delegate all file reading to those packages.

I haven't looked into it, but I don't know it there's much we can do without reading from disk and attempting to pre-empt what TS is doing to normalise the file paths.

Attempting to encode knowledge about how TS/ESLint handle symlinks is also a recipe for disaster when TS releases a new version or ESLint releases a major.

Additionally disk reads aren't cheap so IDK if it's a great idea to just build into the regular flow.

Symlinks are also a pretty rare usecase in the community (hence this is the first time someone has reported this), so I'm not sure how much effort is worth putting into this.
If you want to look into this, I'm happy to find out the results of a deeper investigation! I can answer any questions you might have.

I'd be looking for a champion to work on this, as I don't have the time to look into this niche of an issue (similar to vue support - vuejs/eslint-plugin-vue#1296)

@bradzacher bradzacher added awaiting response Issues waiting for a reply from the OP or another party and removed triage Waiting for team members to take a look labels Jan 31, 2021
@nikparo
Copy link
Author

nikparo commented Feb 1, 2021

Unfortunately I don't really know any of the technologies very well, otherwise I may have dug into it a bit further. I'm currently more inclined to try and structure my project a bit differently instead to work around the issue.

I did notice though that TS seems to have some symlink functionality built in, as updatedProgram also has a getSymlinkCache function. It didn't provide anything of immediate value though.

@bradzacher bradzacher added bug Something isn't working help wanted Extra attention is needed and removed awaiting response Issues waiting for a reply from the OP or another party labels Feb 1, 2021
@armano2
Copy link
Member

armano2 commented Feb 12, 2021

i'm going to look into this

@armano2 armano2 self-assigned this Feb 12, 2021
@armano2
Copy link
Member

armano2 commented Feb 25, 2021

i have few news about this issue, and this seem to be caused by our normalization of paths <- that was a fix introduced to fix issues with editors

i can confirm that i can reproduce this issue on linux

@armano2 armano2 removed their assignment Mar 1, 2021
@larshp
Copy link

larshp commented Mar 2, 2021

any workarounds possible?

@nikparo
Copy link
Author

nikparo commented Mar 2, 2021

I discovered this because we have a monorepo (@nrwl/nx) containing a legacy app among other things. We decided to make our libraries "buildable" and import transpiled code instead. This got us around the issue even though we still use symlinks, since we have set up eslint to ignore the transpiled code.

@JoshuaKGoldberg JoshuaKGoldberg added the accepting prs Go ahead, send a pull request that resolves this issue label Oct 25, 2021
@typescript-eslint typescript-eslint deleted a comment Mar 14, 2022
@D-Pow
Copy link

D-Pow commented May 1, 2022

I was experiencing this as well, and finally came across a solution. Basically, we need to compare pwd (which preserves symlinks) with the repo's root directory (which doesn't preserve symlinks in most cases), strip out any nested paths from pwd if present, and then export the resulting pwd - nestedPath string as a constant for all other files/configs to use.

The gist is:

/***   ./config/utils/Files.js   ***/
const fs = require('fs');
const path = require('path');
const childProcess = require('child_process');


/*
 * Fix paths to use the path of the current process' directory if it's within a symlinked path.
 * Otherwise, IDEs may have trouble with automatic resolutions for ESLint, file paths, etc.
 * 
 * Short summary:
 * - process.cwd() == `npm prefix` - Doesn't preserve symlinks
 * - process.env.PWD == `pwd` - Preserves symlinks
 * - fs.realpathSync(process.env.PWD) == `realpath $(pwd)` - Doesn't preserve symlinks
 */

// Root directory of repository - absolute path without preserving symlinks
// You could use your own logic here, this was my choice so the root dir was always the same regardless
// of where a script/process was run
const realPath = path.resolve(
    childProcess
        .execSync('npm prefix')
        .toString()
        .replace(/\n/g, '')
);
// Root dir preserving symlinks + current process' path (e.g. if in a nested directory from the root)
const currentProcessPath = process.env.PWD;
// Only the nested directory, used for diffing any (non-)symlink-preserving path (nested or not) with
// the repo root in order to convert PWD to the root dir
const currentProcessNestedPath = fs.realpathSync(currentProcessPath).replace(realPath, '');
// Root dir preserving symlinks without the nested directory
const rootDirMaintainingSymlinks = currentProcessPath.replace(currentProcessNestedPath, '');


// Final constant for use in other files
export const rootDir = rootDirMaintainingSymlinks;

// Now to resolve the tsconfig.json
export const tsconfigPath = path.resolve(rootDir, 'tsconfig.json');



/***   ./.eslintrc.js   ***/
const {
    rootDir,
    tsconfigPath,
} = require('./config/utils/Files');


module.exports = {
    parser: '@typescript-eslint/parser',
    parserOptions: {
        // Set the root directory of the repo - now preserving symlinks
        tsconfigRootDir: rootDir,
        // Path of tsconfig.json - now preserving symlinks
        project: tsconfigPath,
        // ...
    },
    // ...
};

Tested on Linux Mint Uma 20.2.

@gutenye
Copy link

gutenye commented May 4, 2022

Following this discussion, I think this will fix the issue, Change

getProgramsForProjects(code, extra.filePath, extra),

to

const realPath = fs.realpathSync(extra.filePath)
getProgramsForProjects(code, realPath, extra), 

@ThayalanGR
Copy link

Isn't this fix (@gutenye) not yet the part of current release?

@mateo-m
Copy link

mateo-m commented Sep 7, 2022

Any news on this issue?
I need to use symbolic links for my projects but ESLint seems to break because of that.

@ThayalanGR
Copy link

ThayalanGR commented Sep 7, 2022

Not only Eslint, Many of the built-ins (eg: Git changes feature will not work as expected on symlinked dirs - try checking it out) and custom plugins of vscode fails to work along with symlinked dirs, so I suggest you to find a way to work without sym-linking, I wasted plethora of time looking for a fix, as mentioned in this thread, if I write fix for one plugin (like Eslint) the same issue will arise in other plugins as well.. since this is never ending loop and all the plugins were relatively referenced this causes error while resolving symbolic links, changing our dev flow is the only fix right now, since we can't write patch like this for every plugin which has this issue.

@mateo-m
Copy link

mateo-m commented Sep 8, 2022

Not only Eslint, Many of the built-ins (eg: Git changes feature will not work as expected on symlinked dirs - try checking it out) and custom plugins of vscode fails to work along with symlinked dirs, so I suggest you to find a way to work without sym-linking, I wasted plethora of time looking for a fix, as mentioned in this thread, if I write fix for one plugin (like Eslint) the same issue will arise in other plugins as well.. since this is never ending loop and all the plugins were relatively referenced this causes error while resolving symbolic links, changing our dev flow is the only fix right now, since we can't write patch like this for every plugin which has this issue.

Oh, so issue stems from VS Code? Not ESLint itself? @ThayalanGR

Edit:
Seems like a work-around "fix" would be to use bind mounts instead of symbolic links.
I'll test this potential band-aid when I have some time then tell you guys if it solved my issue.

@ThayalanGR
Copy link

Not only Eslint, Many of the built-ins (eg: Git changes feature will not work as expected on symlinked dirs - try checking it out) and custom plugins of vscode fails to work along with symlinked dirs, so I suggest you to find a way to work without sym-linking, I wasted plethora of time looking for a fix, as mentioned in this thread, if I write fix for one plugin (like Eslint) the same issue will arise in other plugins as well.. since this is never ending loop and all the plugins were relatively referenced this causes error while resolving symbolic links, changing our dev flow is the only fix right now, since we can't write patch like this for every plugin which has this issue.

Oh, so issue stems from VS Code? Not ESLint itself? @ThayalanGR

Edit:

Seems like a work-around "fix" would be to use bind mounts instead of symbolic links.

I'll test this potential band-aid when I have some time then tell you guys if it solved my issue.

No, it is not stems from vscode, both having this symlink issue in common.

tabcat added a commit to hldb/welo that referenced this issue Sep 15, 2022
issue had to do with opening the project from a symlink
Related: typescript-eslint/typescript-eslint#2987
alexfarrill added a commit to sofn-xyz/mailing that referenced this issue Dec 1, 2022
* await creating the default list

* for completeness: also assert organization

* lint for await

* ooops - put the eslint config for ts in the right place

* eslint should ignore .mailing directories

* add tsconfig's to parserOptions.project in @typescript-eslint/parser block

* await async functions

* don't ignore __test__ from tsconfig in core

* linter says that this expect must be awaited

* tsconfig covers all the .ts files in the root directory:

jest.config.ts			testSetup.integration.ts	testUtilIntegration.ts
jest.integration.config.ts	testSetup.ts

* tsconfig should include e2e/**/*

* src/cli/generator_templates/**/* is not a directory in packages/cli

* await some async functions

* we're using plain js in e2e/mailing_tests so don't need a tsconfig

* fix typo

* await analytics calls

* eslintignore these 2 files:
packages/cli/src/generator_templates
packages/cli/src/emails

because they are ignored in tsconfig.json

* update files attribute in package.json

* don't actually want to ignore those

* this is my workaround for typescript-eslint/typescript-eslint#2987

* noop?

* simplify tsconfigs?

* fix all the lints

* oops

* try void

* async await

* smaller change

* fix server build

* Revert "fix server build"

This reverts commit e24e0ed.

* await -> void in init

* remove trailing comma in json

* cypress does not actually need to reset the web db, so remove

* server: try cd into .mailing and then npx next build with the current directory

* pull posthog api key into its own file so it does not bloat the next build by pulling in posthog-node

* import from the righ tplaÿce
@alex-kinokon
Copy link

I’m also having this problem using ESLint from a symlink location resulting in the error project was set to `true` but couldn't find any tsconfig.json relative to .... The problem seems to be

let directory = path.dirname(parseSettings.filePath);

} while (
directory.length > 1 &&
directory.length >= parseSettings.tsconfigRootDir.length
);

from PR #6084. directory is the path before symlink resolution and parseSettings.tsconfigRootDir is the path after symlink resolution, causing the length comparison to fail. Perhaps we can wrap

filePath: ensureAbsolutePath(
typeof options.filePath === 'string' && options.filePath !== '<input>'
? options.filePath
: getFileName(options.jsx),
tsconfigRootDir,
),

with fs.realpathSync here?

@JoshuaKGoldberg JoshuaKGoldberg added the triage Waiting for team members to take a look label Nov 21, 2023
@JoshuaKGoldberg
Copy link
Member

@alex-kinokon feel free to send a PR, that'd be lovely! I'm not 100% sure that the project: true issue is the same as the original one in the issue, but either way if there's a demonstrable problem we'd happily take in a fix.

I'll extend that invitation to everybody seeing this. We the maintainers haven't had time to send a PR ourselves but I see at least 2-3 suggestions of how to fix the issue in the thread. Wonderful opportunity for someone to send a code contribution in. 😄

@JoshuaKGoldberg JoshuaKGoldberg removed the triage Waiting for team members to take a look label Nov 30, 2023
@ulfgebhardt
Copy link

ulfgebhardt commented Dec 29, 2023

@alex-kinokon Can you please provide your insight as PR ❤️

@birgersp
Copy link

birgersp commented Jan 19, 2024

(noob here)

I see that calling fs.realpathSync is a suggested solution, but can we use functions from the fs module in this project? Wouldn't that require an import of @types/node?

@JoshuaKGoldberg
Copy link
Member

Some code already calls to fs functions. Search for 'fs' imports and you'll come across files like useProvidedPrograms.ts.

@birgersp
Copy link

Some code already calls to fs functions. Search for 'fs' imports and you'll come across files like useProvidedPrograms.ts.

Thanks. Tried applying the propsed fixed but tests went bananas.

Hoping that someone who knows this project will pick this up 🤞🙏

@jakebailey
Copy link
Collaborator

Drive-by, but is this "fixed" by just using the project service? After all, surely this code works with tsserver in VS Code, so theoretically should behave no differently with the project service enabled.

@JoshuaKGoldberg
Copy link
Member

That's a workaround, yeah.

@BigSamu
Copy link

BigSamu commented Sep 12, 2024

Any final solution on this? To avoid using a work around.

@JoshuaKGoldberg
Copy link
Member

If there was an update, it'd be posted here. There is not. It's still status: accepting prs. So if this is impacting you, we'd welcome you sending a PR to fix it! 🙂

@bradzacher
Copy link
Member

With any issue opened in this project — it either has visible progress in the form of an attached PR, or it has no progress.

We are a community-run project. The volunteer maintainers spend most of their time triaging issues and reviewing PRs. This means that most issues will not progress unless a member of the community steps up and champions it.

If this issue is important to you — consider being that champion.
If not — please just subscribe to the issue and wait patiently.

@BigSamu
Copy link

BigSamu commented Sep 13, 2024

@JoshuaKGoldberg, @bradzacher,

Sorry. My previous message was not intended to be pushy. I will wait patiently for this.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
accepting prs Go ahead, send a pull request that resolves this issue bug Something isn't working help wanted Extra attention is needed package: typescript-estree Issues related to @typescript-eslint/typescript-estree
Projects
None yet
Development

No branches or pull requests