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

Resolve doesn't resolve real path for basedir if it is relative path #177

Closed
malash opened this issue Nov 27, 2018 · 17 comments
Closed

Resolve doesn't resolve real path for basedir if it is relative path #177

malash opened this issue Nov 27, 2018 · 17 comments
Assignees

Comments

@malash
Copy link

malash commented Nov 27, 2018

resolve doesn't resolve real path for basedir if it is relative path.

I'v created a bad case example ( by lerna ) here https://github.com/malash/resolve-bad-case

The issue is related to #130 .
The issue effected peerigon/extract-loader#48 and may affect #163 .

@zkochan @ljharb

@ljharb
Copy link
Member

ljharb commented Nov 27, 2018

It seems like you're not setting the preserveSymlinks option to false, (it currently defaults to true in v1, altho master has the breaking change of defaulting it to false).

What happens if you set that option?

@malash
Copy link
Author

malash commented Nov 27, 2018

I update the test case and the bug still exists.

malash/resolve-issue-177@c5ea1c8

In fact preserveSymlinks isn't used at all in https://github.com/browserify/resolve/blob/master/lib/sync.js#L49-L52

@ljharb
Copy link
Member

ljharb commented Nov 28, 2018

hmm, i'm trying to reproduce this error, and i do get this failure:
Cannot find module '../../node_modules/jquery' from './node_modules/@my-scope/package-b'

However, after npm install and after npx lerna bootstrap, node_modules still lacks a @my-scope directory, so i'm not sure why you'd expect something to be resolved there.

@malash
Copy link
Author

malash commented Nov 29, 2018

@ljharb I will clarify it.

When running node packages/package-a

it equals to

cd packages/package-a && node ./index.js

so the basedir would be

the-repo/packages/package-a/node_modules/@my-scope/package-b

which is a symlink to the-repo/packages/package-b.

There is a real issue when I use webpack with extract-loader peerigon/extract-loader#48 (comment)

@ljharb
Copy link
Member

ljharb commented Nov 29, 2018

OK, but the error says "from node_modules/@my-scope/package-b" - and then "../../node_modules/jquery'" - so, the final path it's looking for is "node_modules/jquery" (inside package-a) - but package-a doesn't have jquery listed as a dependency, and it's not present on disk.

@ljharb
Copy link
Member

ljharb commented Nov 29, 2018

(and package-b doesn't have jquery either)

@malash
Copy link
Author

malash commented Nov 29, 2018

No. The final path is not 'node_modules/jquery' but 'packages/package-a/node_modules/node_modules/jquery' and it's wrong even if package-a had jquery.


First, I fixed the basedir in test case with abs path so the error trace is more clear.

/Users/malash/Projects/resolve-bad-case/node_modules/jquery/dist/jquery.js
/Users/malash/Projects/resolve-bad-case/node_modules/resolve/lib/sync.js:46
    throw err;
    ^

Error: Cannot find module '../../node_modules/jquery' from '/Users/malash/Projects/resolve-bad-case/packages/package-a/node_modules/@my-scope/package-b'
    at Function.module.exports [as sync] (/Users/malash/Projects/resolve-bad-case/node_modules/resolve/lib/sync.js:44:15)
    at Object.<anonymous> (/Users/malash/Projects/resolve-bad-case/packages/package-a/index.js:8:21)
    at Module._compile (internal/modules/cjs/loader.js:707:30)
    at Object.Module._extensions..js (internal/modules/cjs/loader.js:718:10)
    at Module.load (internal/modules/cjs/loader.js:605:32)
    at tryModuleLoad (internal/modules/cjs/loader.js:544:12)
    at Function.Module._load (internal/modules/cjs/loader.js:536:3)
    at Function.Module.runMain (internal/modules/cjs/loader.js:760:12)
    at startup (internal/bootstrap/node.js:303:19)
    at bootstrapNodeJSCore (internal/bootstrap/node.js:872:3)

The current logic equals to path.resolve because of this line :

'/path/to/repo/packages/package-a/node_modules/@my-scope/package-b'
+
'../../node_modules/jquery'
=>
'/path/to/repo/packages/package-a/node_modules/node_modules/jquery'

It's a non-exist path.

The correct way should be:

  1. Get the real path of basedir. E.g.

'/path/to/repo/packages/package-a/node_modules/@my-scope/package-b' =>
'/path/to/repo/packages/package-b/'

  1. Resolve the path.

'/path/to/repo/packages/package-b/' + '../../node_modules/jquery' =>
/path/to/repo/node_modules/jquery

That line could be refactor like this ( just for demo ):

// if not preserveSymlinks
path.resolve(fs.realpathSync(basedir), x)
// else
path.resolve(basedir, x)

fs.realpathSync will handle the basedir if it is a symlink ( even inside a symlink ).

@ljharb
Copy link
Member

ljharb commented Nov 29, 2018

Got it, thanks for explaining.

@ljharb
Copy link
Member

ljharb commented Nov 29, 2018

OK, so part of the problem is that the basedir you're passing in isn't resolved - it's relative to package-a/index, but that's not information that resolve can retain - so you first have to path.resolve(__dirname, './node_modules/@my-scope/package-b').

This makes the first assertion pass, but the second still fails for me.

@malash
Copy link
Author

malash commented Nov 30, 2018

Normally the basedir should be absolute path. It's my fault to pass a relative path for basedir in the first version test case so I fixed it. That could be another good question how relative basedir resolved.

For this issue the inconsistency is that user have to resolve real path of basedir for relative path but not NPM package.

// packages/package-a/index.js
console.log(resolve.sync("jquery", { basedir: basedir }));
console.log(resolve.sync("../../node_modules/jquery", { basedir: fs.realpathSync(basedir) }));

⬆️These lines worked for me.

@malash
Copy link
Author

malash commented Nov 30, 2018

Compression between preserveSymlinks === true and preserveSymlinks === false

// preserveSymlinks === false
// will search NPM package from
// * packages/package-b/node_modules
// * packages/node_modules
// * node_modules
console.log(resolve.sync('jquery', { basedir: basedir, preserveSymlinks: false }));
console.log(resolve.sync('../../node_modules/jquery', { basedir: fs.realpathSync(basedir), preserveSymlinks: false }));

// preserveSymlinks === true
// will search NPM package from
// * packages/package-a/node_modules/@my-scope/packages/package-b/node_modules
// * packages/package-a/node_modules/@my-scope/packages/node_modules
// * packages/package-a/node_modules/@my-scope/node_modules
// * packages/package-a/node_modules/node_modules
// * packages/package-a/node_modules
// * packages/node_modules
// * node_modules
console.log(resolve.sync('jquery', { basedir: basedir, preserveSymlinks: true }));
console.log(resolve.sync('../../../../../node_modules/jquery', { basedir: basedir, preserveSymlinks: true }));

@ljharb ljharb self-assigned this Dec 15, 2018
@ljharb ljharb closed this as completed in 2321cd4 Dec 16, 2018
@malash
Copy link
Author

malash commented Dec 17, 2018

@ljharb Thank you very much for your commit. This commit works for me.

@ljharb
Copy link
Member

ljharb commented Dec 17, 2018

Thank you for your excellent repro case!

ljharb added a commit that referenced this issue Dec 17, 2018
ljharb added a commit that referenced this issue Dec 17, 2018
 - [New] `async`/`sync`/`node-modules-paths`: Adds support for “paths” being a function (#172, #173)
 - [New] Implements a "normalize-options" pseudo-hook (#174)
 - [Fix] `sync`/`async`: fix `preserveSymlinks` option (#177)
 - [Fix] `sync`/`async`: when package.json `main` is not a string, throw an error (#178)
 - [Deps] update `path-parse`
 - [Dev Deps] update `eslint`, `@ljharb/eslint-config`, `object-keys`, `safe-publish-latest`, `tape`
 - [Tests] up to `node` `v11.4`, `v10.14`, `v8.14`, `v6.15`
 - [Tests] better failure messages
@john-kurkowski
Copy link

It seems like you're not setting the preserveSymlinks option to false, (it currently defaults to true in v1, altho master has the breaking change of defaulting it to false).

Did this breaking change get resolved before publishing v1.9.0?

master (04e6c79) has a different check

if (!opts || !opts.preserveSymlinks) {

vs. what I think was published at tag v1.9.0.

https://github.com/browserify/resolve/blob/v1.9.0/lib/sync.js#L36

@ljharb
Copy link
Member

ljharb commented Dec 19, 2018

@john-kurkowski v1.9.0 does not include any of the breaking changes. I cherry-picked commits onto the v1.8.1 tag, and released, and merged the v1.9.0 tag back to master.

@simonhaenisch
Copy link
Contributor

@ljharb what are the plans on releasing this fix? Having issues with this in ionic-team/stencil#1310.

[email protected] uses [email protected] and [email protected] uses [email protected].

Problem is that with path ./components/cmp-a/cmp-a.js and { basedir: '/src' } , [email protected] gives an error through the callback

`Error: Cannot find module './components/cmp-a/cmp-a.js' from '/src'

, but [email protected] does the fs.realpath check and then calls init(undefined) which throws

TypeError: Path must be a string. Received undefined

and makes the code fail.

When I replaced init(realStart) with init(realStart || absoluteStart), it was fixed... but I guess the problem could also be that the base dir is an absolute path /src even though that is meant as a relative path ./src in this case (adding if (basedir === '/src') basedir = './src'; also fixed it).

I'm not that familiar with the topic so what would you suggest? Is this more related to bad code that needs to be fixed, or should I wait for the fix?

@ljharb
Copy link
Member

ljharb commented Jan 11, 2019

@simonhaenisch it’s released in v1.9.0. If you’re still seeing a problem, please file a new issue and we can talk it out there.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Development

No branches or pull requests

4 participants