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

Handle file: dependencies #18

Open
wants to merge 6 commits into
base: master
Choose a base branch
from
Open

Handle file: dependencies #18

wants to merge 6 commits into from

Conversation

balsoft
Copy link
Member

@balsoft balsoft commented Apr 17, 2020

Ligo's website uses a file: dependency syntax in package.json. We need to handle that. It turned out to be pretty simple really.

@zhenyavinogradov
Copy link
Contributor

Why does the test fail?

@balsoft
Copy link
Member Author

balsoft commented May 15, 2020

Because I'm using builtins.filterSource, and I probably shouldn't. I'll try to figure it out on Monday.

@balsoft
Copy link
Member Author

balsoft commented May 19, 2020

@zhenyavinogradov please review again, tests work now

@balsoft balsoft requested a review from a team May 19, 2020 13:58
default.nix Outdated
echo 'building npm cache'
chmod u+w ./package-lock.json
NODE_PATH=${npmModules} node ${./mknpmcache.js} ${cacheInput "npm-cache-input.json" lock}

echo 'building node_modules'
npm $npmFlags ci
patchShebangs ./node_modules/
${concatMapStringsSep "\n" (dep: "cp -r ${dep}/node_modules/* node_modules") peerDependencies}
Copy link
Contributor

Choose a reason for hiding this comment

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

I'm not sure I understand the logic for doing this. AFAIU this adds dependencies of local packages to node_modules, but I think that we should respect the behavior of npm install, and if it does not include these dependencies, we should not include them either

Copy link
Member Author

Choose a reason for hiding this comment

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

But if we don't include those dependencies, the resulting package won't work.

This is a good question: should we mimic the behaviour of npm even if it means more work needs to be done manually, or do we go for "least effort" approach (as I have done here)?

Copy link
Contributor

Choose a reason for hiding this comment

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

Does that mean the package won't work if it's built with pure npm? I think we don't have to support a case like this then

Copy link
Member Author

Choose a reason for hiding this comment

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

Yes, it will require additional commands if built with pure npm (npm install complains about peer dependencies and tells you how to install them manually). I don't know what's the reasoning for this. If there is a compelling reason not to automatically resolve peer dependencies this way, I'd be happy to remove this logic. However, if this npm behaviour is for historic reasons only, I think we should keep it the way it currently is becuase this is simply more user-friendly and simplifies the nix expressions needed to build the package.

Copy link
Contributor

Choose a reason for hiding this comment

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

That’s not for historic reasons, it’s actually the opposite, npm < 3 used to install peer dependencies automatically, now it doesn’t. See npm/npm#6565.

What I’m somewhat confused about is that peer dependencies seem to be some special weird thing meant for plugins or something like this (I have no idea). These are not the same as local (file:) dependencies, are they?

Copy link
Member Author

Choose a reason for hiding this comment

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

Peer dependencies are transitive dependencies of local dependencies IIUC.

Copy link
Contributor

Choose a reason for hiding this comment

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

Reference on peer dependencies that gets me confused: https://nodejs.org/en/blog/npm/peer-dependencies/

Copy link
Member Author

Choose a reason for hiding this comment

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

Oh, ok, so I need to RTFM before writing code as it turns out.

So the peer dependency change doesn't have to do anything with the main premise of this PR -- handling file: dependencies. It just so happens that one of the local dependencies of LIGO's website has peer dependencies that aren't a dependency of the website itself.

Whether my logic of automatically installing peer deps is correct or not is a complex question, because it appears that the issues you've mentioned is to do with npm link and other development-time commands that don't interest us much because nix-npm-buildpackage is targeting CIs.

Copy link
Contributor

Choose a reason for hiding this comment

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

So, IIUC, in this specific case the best solution would be to revert the peer dependency logic you added, and then... um... fix the build by either fixing the dependency that needs a peer for some reason, or adding the peer dependency to package.yaml of the repo?

Copy link
Contributor

Choose a reason for hiding this comment

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

(I’m really with Zhenya on not doing what npm doesn’t do. I don’t really understand peer dependencies, but it kinda sounds like one’s project should declare peer dependencies of its dependencies explicitly?)

@@ -117,44 +124,53 @@ with stdenv.lib; let
in rec {
mkNodeModules = { src, extraEnvVars ? {} }:
let
localDeps = map dirOfLocal (builtins.filter isLocal (builtins.attrValues (lock.dependencies)));
Copy link
Contributor

Choose a reason for hiding this comment

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

That’s not very... functional? Instead of matching, filtering, matching, mapping, you could just mapMaybe once (drop isLocal, make dirOfLocal return a “maybe”).

(I’m not sure if there is mapMaybe or catMaybes in the nixpkgs library, but it should be there... in the meantime you can just return a list of 0/1-length and concatMap.)

@@ -90,6 +95,8 @@ with stdenv.lib; let

commonBuildInputs = [ _nodejs makeWrapper ]; # TODO: git?

normalizeName = builtins.replaceStrings [ "@" "." "/" ] [ "_" "_" "_" ];
Copy link
Contributor

Choose a reason for hiding this comment

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

Would be nice to have some documentation (or comment) for this. Or maybe a library function.

Anyway, this set of symbols looks somewhat arbitrary to me. According to this you might need to escape way more chars than this.

Copy link
Member Author

Choose a reason for hiding this comment

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

Those are the characters that can appear in javascript package name, but can't appear in nix path name.

Copy link
Contributor

@kirelagin kirelagin May 20, 2020

Choose a reason for hiding this comment

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

Still would be nice to have some documentation.

I found this and it says that the package name can only contain “url-safe” characters. I’m not sure what this means, but I suppose, they refer to the “unreserved characters” from the URL RFC, namely:

unreserved  = ALPHA / DIGIT / "-" / "." / "_" / "~"

which means that the only problematic one is ~ (which you did not include in your list). (Note that both Nix and npm allow ., but not as the first char.) @ and / in your list, I suppose, come from the scoped packages that have the form @<scope>/<package> and, according to the doc the restrictions for scope names are the same.

tldr; basically, it looks like you need to replace your list with just [~, /] and remove leading @.

Copy link
Contributor

@kirelagin kirelagin May 20, 2020

Choose a reason for hiding this comment

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

(Also, tbh, I would probably replace both ~ and / with - rather than _ for readability reasons.)

default.nix Outdated

mkdir $out
mv ./node_modules $out/
cp -r ./node_modules $out/
Copy link
Contributor

Choose a reason for hiding this comment

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

Why?

Copy link
Member Author

Choose a reason for hiding this comment

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

Because node_modules can contain read-only peer dependencies now, and in that case it can't be moved.

@zhenyavinogradov
Copy link
Contributor

Can you please also add a simple test case for this, so we can be sure that we don't break it in the future?

default.nix Outdated
packageLockJson = filteredSrc + "/package-lock.json";

packageJson = src + "/package.json";
packageLockJson = src + "/package-lock.json";
Copy link
Contributor

Choose a reason for hiding this comment

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

This path will change every time anything in src changes, causing everything to rebuild. As I understand the problem is that now filteredSrc depends on the contents of package-lock.json, causing infinite recursion. In that case we can create another filter expression which contains only package.json and package-lock.json, read package-lock.json from there, and then use it to construct a filter which will include local dependencies

Copy link
Member Author

Choose a reason for hiding this comment

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

Oh, good point.

Copy link
Contributor

Choose a reason for hiding this comment

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

@balsoft Do you plan to address this in this PR or would you like to create a separate issue?

Copy link
Member Author

Choose a reason for hiding this comment

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

I think it's a separate issue.

Copy link
Contributor

Choose a reason for hiding this comment

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

Why not do it in this PR?

Copy link
Member Author

Choose a reason for hiding this comment

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

Maybe fixed it here, please take a look

Copy link
Contributor

Choose a reason for hiding this comment

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

Why not do it in this PR?

Because it has the potential for postponing the merging of this PR forever.

Copy link
Contributor

Choose a reason for hiding this comment

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

In my opinion caching node_modules is more important than file: dependency support. @balsoft I can help you with finishing this PR if you want

Copy link
Contributor

Choose a reason for hiding this comment

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

There're only minor fixes left as I see

default.nix Outdated
metaSrc = cleanSourceWith {
src = src;
filter = path: type: elem (getRelativePath path) [ "package.json" "package-lock.json" ];
};
Copy link
Contributor

Choose a reason for hiding this comment

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

filteredSrc also has a check for canCleanSource to support the situation when src is a nix store path not produced by cleanSourceWith, it should be done here as well. Also, to avoid repetition, you can make filteredSrc a function which accepts localDeps as a parameter and use filteredSrc [] for metaSrc

cleanedSource = cleanSourceWith {
src = src;
filter = path: type: elem (getRelativePath path) usedPaths;
filter = path: type: any (x: elem x usedPathsRec) (dirOfRec (getRelativePath path));
Copy link
Contributor

Choose a reason for hiding this comment

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

I just noticed this, but I think any should not be used here, because it will include all parent directories of a dependency, while we only want the exact directory of the dependency to be included. For example, if we have dependency file:./deps/foo, and there also exists ./deps/extra-stuff directory which is not used as a dependency, it will still be included in the filtered source

@angerman
Copy link

Just ran into this issue; are we still planning on merging this feature?

@angerman angerman mentioned this pull request Jun 24, 2022
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.

None yet

4 participants