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

Feat: warn when the package from resolved alias is not available #160

Merged
merged 21 commits into from
Apr 18, 2017

Conversation

fatfisz
Copy link
Contributor

@fatfisz fatfisz commented Apr 9, 2017

This is the last feature branch for now. Thanks for bearing with me!

In #144 some users reported that the plugin stopped working for them and the reason was a fix for applying the plugin twice (fixed in #146).

This was not necessarily a problem on the plugin side, but rather bad configuration. Still, an expectation was that the plugin would somehow be working in that case. Here's an example of such configuration:

{
  "presets": ["es2015"],
  "plugins": [
    ["module-resolver", {
        "root": "./src",
        "alias": {
          "basic": "components/1-basic",
          "atoms": "components/1-basic/1-atoms",
          "molecules": "components/1-basic/2-molecules",
          "organisms": "components/2-organisms"
        }
      }]
    ]
}

The repo where this configuration was being used didn't have a package named "components", but rather this was the name of a directory in that repo. The expectation was that "components/1-basic" (bad) would be treated as "./src/components/1-basic" (good).

This change does that - after resolving using the alias, the path is resolved again using root.

I understand that this behavior may be confusing, but this should be alleviated by a "debug" functionality.

@codecov
Copy link

codecov bot commented Apr 9, 2017

Codecov Report

Merging #160 into beta will not change coverage.
The diff coverage is 100%.

Impacted Files Coverage Δ
src/normalizeOptions.js 100% <100%> (ø) ⬆️
src/getRealPath.js 100% <100%> (ø) ⬆️

Continue to review full report at Codecov.

Legend - Click here to learn more
Δ = absolute <relative> (impact), ø = not affected, ? = missing data
Powered by Codecov. Last update e9f9bf6...fda055b. Read the comment docs.

@codecov
Copy link

codecov bot commented Apr 9, 2017

Codecov Report

Merging #160 into beta will not change coverage.
The diff coverage is 100%.

Impacted Files Coverage Δ
src/utils.js 100% <100%> (ø) ⬆️
src/normalizeOptions.js 100% <100%> (ø) ⬆️
src/getRealPath.js 100% <100%> (ø) ⬆️

Continue to review full report at Codecov.

Legend - Click here to learn more
Δ = absolute <relative> (impact), ø = not affected, ? = missing data
Powered by Codecov. Last update 52600af...647ce41. Read the comment docs.

Copy link
Owner

@tleunen tleunen left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Interesting simplification to transform all alias into regexp and then use them for the path resolver.

I think in most cases, it would resolve the issue, but I wonder if some people specify relative alias path in their babel config without using ./. Probably not because it would have been taken as a node module by the plugin... (And thus the issue you found).

I believe it's ok. Not sure yet if it's a good thing to do, or warn that the alias doesn't exist in node_modules or in the project, but it exists in ${root}/${alias}, and then warn the user to update its config.

.map((key) => {
let value = alias[key];

if (value.startsWith('npm:') && !warnedAboutNpmPrefix) {
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Lets drop this for good :)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If you say so :) I'm just worried about breaking some more configs :D

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It will be a 3.0 so no problem removing the deprecated things :) Especially since there's a way to do it without using the prefix.

opts.regExps.push([new RegExp(key), substitute]);
const regExpAliases = aliasKeys
.filter(isRegExp)
.map(key => getAliasPair(key, alias[key]));
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would be best to do both case in the same map loop.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok, will join those cases.

@fatfisz
Copy link
Contributor Author

fatfisz commented Apr 9, 2017

So do you think I should roll back the double-resolve and there should be just a warning if the package is missing?

Sorry, maybe it's too late (almost midnight in Poland :D), but I didn't your intent from the second part of the comment.

Breaking change: support for the "npm:" prefix is removed
@tleunen
Copy link
Owner

tleunen commented Apr 10, 2017

Hehe.

What I was trying to say is if we should should really resolve with the root config after the alias, or only warn the user the alias config requires relative paths starting with ./ or a node_module dependency.
And if we decide to go for the warning, we could automatically detect that the user did something wrong in his config and suggest something.
Like in your example,
Instead of having "basic": "components/1-basic", we could suggest "basic": "./src/components/1-basic", because we found out the path exists (./src comes from the root config).

I'm not saying to do that. Just thinking out loud here.

Could you explain how the existing code in the PR will work for this config? I'm not sure I fully understand the alias myself.

@fatfisz
Copy link
Contributor Author

fatfisz commented Apr 10, 2017

Thanks for clearing that up!

In the config you've linked all the aliases point at packages or files that are in the packages, so in this case aliases should behave as they did up till now. I have to say though that this config is very confusing now that I've taken a clear look at it - @expo/vector-icons is pointing react-native-vector-icons and vice versa... but this is another issue :)

The conflict is only possible when after aliasing you end up with a path that is both a filename available in one of the roots and a package name. But! This is also possible for resolving with the root on its own.

At this point however I'm tempted to go with the warning instead. After aliasing a simple require.resolve would do to check if the package exists (ofc only in case the path is not starting with a dot). I don't feel strongly about the current solution, so I could go either way.

So what, should I give it a go?

@fatfisz
Copy link
Contributor Author

fatfisz commented Apr 10, 2017

Heads up: I'm adjusting the tests and going with the warning.

@tleunen
Copy link
Owner

tleunen commented Apr 10, 2017

Yeah, let's try how a warning will work. (Feel free to keep the code from this PR in case we need it later ;))

We can even simplify it at first by warning when normalizing the options, instead of trying to resolve it

@fatfisz
Copy link
Contributor Author

fatfisz commented Apr 10, 2017

I'm not sure if warning at the option processing time is a good idea, resolve results may vary depending on the file where the import is.

@tleunen
Copy link
Owner

tleunen commented Apr 10, 2017

Depends what/why we warn though. If we check that the node_module exists OR the path exists, it should be fine doing it in normalizeOptions, right?

@fatfisz
Copy link
Contributor Author

fatfisz commented Apr 10, 2017

What if there is node_modules nested in a directory?

package root
|- node_modules/
|- directory/
|  |- node_modules/
|  \- file.js
\- .baberc

In this case in file.js some other packages may be available. That may be a contrived example, but I think it's better to cover all bases here. I think that the overhead will be comparable, if not better: the plugin is initialized separately for each file, so all of the aliases will be processed in normalizeOptions compared to only the necessary ones in the file.

@tleunen
Copy link
Owner

tleunen commented Apr 10, 2017

Hmm, your exemple is a bit weird, since it should probably not really happen. At least for this plugin. Even if you can have that kind of structure with sub modules, I doubt you'll have a alias on something on another module ;)

Go for what you have in mind, that's fine :) We'll see how it works

@fatfisz
Copy link
Contributor Author

fatfisz commented Apr 10, 2017

Ok, let's make life more complicated (sorry, I just love coming up with those absurd scenarios):

mono-repo root
|- package1/
|  |- node_modules/
|  \- file.js
|- package2/
|  |- node_modules/
|  \- file.js
|- package3/
|  |- node_modules/
|  \- file.js
\- .baberc

Let's say that in the subpackages we want to alias package to package/lib using only one .babelrc config, but multiple node_modules. Not including the .babelrc is ok, because the packages are published in the built form anyway. Still contrived, but shows a different angle :)

@fatfisz
Copy link
Contributor Author

fatfisz commented Apr 10, 2017

@tleunen Finished!

@fatfisz fatfisz changed the title Feat: resolve alias again using root Feat: warn when the package from resolved alias is not available Apr 11, 2017
@tleunen
Copy link
Owner

tleunen commented Apr 12, 2017

Thanks @fatfisz, I'll check the PR a bit later today. Sorry for the delay again.

Copy link
Owner

@tleunen tleunen left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think that's pretty cool.
Just a nit comment, otherwise I'm ok with this update.

Do you have any other changes in mind or we're good to deploy a beta version? If so I'll do in the next few days, after testing it quickly in my project at work.

}
});

opts.alias = [...nonRegExpAliases, ...regExpAliases];
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What do you think of this?

opts.alias = aliasKeys.reduce((acc, key) => {
  acc.push(
    isRegExp(key) 
      ? getAliasPair(key, alias[key])
      : getAliasPair(`^${key}((?:/|).*)`, `${alias[key]}\\1`)
  );
  return acc;
}, []);

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I wanted to preserve the order: standard aliases before regexps. At this point I think it could go either way, as the order should be taken from the object.

Note: according to the spec, the order of enumerating the object keys is not guaranteed to be the same as in the literal, so in the future it may be needed to allow an array-type declaration. We're currently relying on V8.

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Indeed, one or the other, we'll have an issue if a user expects the order to be kept. With an object, it's not guaranteed as you say. We'll figure out a way later if really needed. For now, it doesn't seem like users need it.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok, I'll go with the single array then.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

(btw. map is even better than forEach here, will use that instead)

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Indeed.

try {
resolve.sync(modulePath, {
basedir: currentFile,
extensions: nodeExtensions,
Copy link
Owner

@tleunen tleunen Apr 13, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I missed that, but actually it should be the custom extensions the user sets, or the babel extension.

so extensions from opts.

And we should reuse this function in findPathInRoots to remove the duplicate.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure about this one - the packages are 3rd party and so they will probably have standard extensions.

OTOH it shouldn't hurt much to include more files in the lookup, as long as they are package-ish (what should be ensured by require.resolve). In the worst case there will be false positives, which would result in some warnings being silenced, but it doesn't sound very bad.

I'll go with your suggested change.

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmm.. Maybe I missed a detail, but it was used in the alias config to test the alias was actually pointing to an existing file (or node_modules). By 3rd party, you mean a node_module package, right?

If we really want to be safe, we could use the user/babel extensions for local files/modules and then the node extensions for the ones under node_modules (the ones who don't start with ./). But would it be really useful? Doesn't seem like it's really worth it to me. Again, we'll iterate if really needed, otherwise I'm ok with potential false positives. Anyway, this is only a helper for the user. We never checked the alias was correct in any previous versions so it's already a bit better now :)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, a node_modules package. And yes, let's just see if anyone complains at all :)

@fatfisz
Copy link
Contributor Author

fatfisz commented Apr 13, 2017

@tleunen After this I'd like to merge #149. This will be a nice addition for the beta version.

Copy link
Owner

@tleunen tleunen left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

lgtm, good to merge once you answered my small comment :)

'regexp-priority$': './miss',
'regexp-priority': './hit',
'regexp-priority': './miss',
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Coul you explain why this changed?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The test was invalidated because the order is now from the top to the bottom instead of aliases first, then reg exps :)

@tleunen tleunen merged commit 1ef8130 into beta Apr 18, 2017
@tleunen tleunen deleted the resolve-alias-using-root branch April 18, 2017 03:09
tleunen pushed a commit that referenced this pull request Apr 23, 2017
Breaking change: aliased paths are resolved again using root and cwd.
tleunen pushed a commit that referenced this pull request Apr 23, 2017
Breaking change: The "npm:" prefix has been removed.
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

Successfully merging this pull request may close these issues.

2 participants