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
javascript: Test goal with package manager installed test runner support #18554
Conversation
I've added js projects to |
SourceFilesRequest( | ||
(tgt.get(SourcesField) for tgt in transitive_tgts.closure), | ||
enable_codegen=True, | ||
for_sources_types=[JSSourceField, AssetSourceField], |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The reason for hydrating AssetSourceField
s instead of only ResourceSourceField
is that I think the easiest way to support the many different configuration files that can play a part in tooling for a node package. I.e let this
files(name="configs", sources=["*.rc", "*.json", "*.config.js"])
package_json(
...
dependencies=[":configs"],
)
be the way to have them be part of the sandboxes.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If a test config is only to affect test caching, I think using __defaults__
to configure javascript_test
:s dependencies instead could work?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Supporting both files
and resources
makes sense I think, independent of the reason why. But:
The primary reason that those are separated into two separate types though is if an ecosystem should use separate APIs to load files which are "embedded" in a release binary/package (resources
) than they do to load files "next to" their binary/package (files
).
Does the JS ecosystem distinguish between those cases? If so, then the primary thing that you want to watch out for is alignment between pants test
time and pants package
time, so that if you've loaded a resource
one way during a test run, then you can also load it that way in production. The same does not necessarily apply to files
: depending on the deployment format, files
may in the cwd
... or there may be no such concept as a cwd
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hmm... I'm not really following the files
and cwd
bit fully, but:
I think in pants lingo, any file is considered a resource
to the package.json
: All files that will be included in a package .tar are expected to be at or below the directory containing the package.json
, when "packaging" (npm pack). But the pipeline is scriptable, and the scripts themselves have no limitations to where they can copy files from. I think it is not uncommon to e.g copy a repository README.md
from the repo root into a <package-dir>/docs
folder that is .gitignored and referenced in package.json
.
The package.json
has fields that references where files to include in packages are found, but either silently ignores ../
or errors, depending on package manager. The default is to implicitly include all files next to and below the package.json
, with a .gitignore
-esque blocklist.
In source code, files are either loaded via import "./path/to/file.json"
or via file reading api:s similar to pythons open
(of course...). Same thing here is that the import cannot "escape" the package.json
directory, whilst open
could.
So I guess if a package_json
happens to have a dependency on a resource
that isn't at or below its containing directory, pants could error or warn?
I think file
is the same, strictly speaking? But that is disregarding scriptability. Users could have scripts in package.json
to e.g copy files from outside into the package.json
dir that executes as part of npm pack
. Pants has facilities for this as well though in relocated_files
, so maybe encouraging using that is better? 🤔
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In source code, files are either loaded via
import "./path/to/file.json"
or via file reading api:s similar to pythonsopen
(of course...). Same thing here is that the import cannot "escape" thepackage.json
directory, whilstopen
could.
Are these cases always completely equivalent in JS?
The distinction in Python is: either "imported" (resources
) or loaded with open
(files
). The distinction matters there, because if your Python code is packaged in certain ways (as a zipapp, or using Pyoxidizer, for example), then resources
embedded in your binary must be loaded using the resource APIs, because they don't exist as loose files on disk.
So I guess if a
package_json
happens to have a dependency on aresource
that isn't at or below its containing directory, pants could error or warn?
That would probably be a good idea until support for source/workspace dependencies is available.
This relates to the other thread: if there is a CLASSPATH/PYTHONPATH concept in JS (probably NODE_PATH
, according to my chatgpt-ing), then that allows multiple source roots to be added to the module resolution at once. Otherwise, you'd have to strip source roots (which could cause file collisions if you have two resources at project1/src/something.json
and project2/src/something.json
).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That would probably be a good idea until support for source/workspace dependencies is available.
I think that already works, package manager workspaces deals with the module resolution, and the dep inference already picks up the source dependency. A workspace package dependee ends up as a symlink in the /node_modules
of the the dependent install time.
yield os.path.join( | ||
os.path.relpath(os.path.curdir, self.relative_workspace_directory()), "node_modules" | ||
) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
So this was very broken before. The NodeJSProjectEnvironment
assumes that
- CWD should always be at the project root dir, but
- that all output files and output directories will be referred to relative to the workspace
Point 2 Is true for "goal facing" stuff like lockfile generation, package goals and now tests. It is very convenient there. But I had borked it here, and made it so this method produce paths relative to 1 instead of 2.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hm. Yea, it's slightly odd to try and have this both ways.
Do JS runners have a concept like source roots (aka the PYTHONPATH, aka the CLASSPATH), where a prefix is stripped from a directory? If so, it might be good to make lockfile generation and test running consistently use strategy (2) using that facility.
That would also ease eventually allowing source dependencies between JS projects in a monorepo, IIUC.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Indeed, that is how NODE_PATH works, but I'm fairly sure NODE_PATH usage is discouraged in favor of the standard algorithm. Convention rules here.
Monorepos are directly supported via "workspaces", I'm not sure it is a worthwhile to re-invent that wheel?
So
That would also ease eventually allowing source dependencies between JS projects in a monorepo, IIUC.
You get this for free with the "workspaces" feature of the package managers already, I think. If a package.json
contains a package.json#dependencies
entry that points to a package whose name is a workspace in the current project, the package manager will symlink that package (the one in package.json#dependencies) to the node_modules
of the package.json
dependee, install time.
I suppose pants could attempt to support a completely separated package.json as well, but I'm afraid there's a lot of work there that I don't think will give a lot of value.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ok, it sounds like unlike in Python (and similar to the JVM), there is an install step for dependency projects that means that they can't collide with source dependencies.
fb8f8ec
to
0526a11
Compare
@@ -707,7 +707,7 @@ async def generate_node_package_targets( | |||
package_target = NodePackageTarget( | |||
{ | |||
**request.template, | |||
NodePackageNameField.alias: pkg_json.name.replace("@", "__"), | |||
NodePackageNameField.alias: pkg_json.name, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Another oopsie, this has to actually be the correct string w.r.t what is in package.json#name
field.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks good! Two questions related to the relativization of files in the sandbox.
SourceFilesRequest( | ||
(tgt.get(SourcesField) for tgt in transitive_tgts.closure), | ||
enable_codegen=True, | ||
for_sources_types=[JSSourceField, AssetSourceField], |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Supporting both files
and resources
makes sense I think, independent of the reason why. But:
The primary reason that those are separated into two separate types though is if an ecosystem should use separate APIs to load files which are "embedded" in a release binary/package (resources
) than they do to load files "next to" their binary/package (files
).
Does the JS ecosystem distinguish between those cases? If so, then the primary thing that you want to watch out for is alignment between pants test
time and pants package
time, so that if you've loaded a resource
one way during a test run, then you can also load it that way in production. The same does not necessarily apply to files
: depending on the deployment format, files
may in the cwd
... or there may be no such concept as a cwd
.
yield os.path.join( | ||
os.path.relpath(os.path.curdir, self.relative_workspace_directory()), "node_modules" | ||
) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hm. Yea, it's slightly odd to try and have this both ways.
Do JS runners have a concept like source roots (aka the PYTHONPATH, aka the CLASSPATH), where a prefix is stripped from a directory? If so, it might be good to make lockfile generation and test running consistently use strategy (2) using that facility.
That would also ease eventually allowing source dependencies between JS projects in a monorepo, IIUC.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It sounds like the only question to resolve is the resources
vs files
one: let me know if you plan to make further changes in that area, or whether you're ready to merge.
Thanks!
I intend to address all of the comments, I just haven't gotten around to it! |
@stuhood Done! |
It looks like your merge-base for this one has become too old? I'll rebase it in about 30 minutes unless you do first. |
The aliases were wrong, and comments are evil
Also remove redundant Sources from JSTestTargets.
./pants test testprojects/src/js/src/test/index.test.js \ --test-use-coverage \ --test-open-coverage Works!
Implement support for `nyc` style coverage reporting Also touch up the relationship between output dirs and output files. Also added testprojects
d651a4d
to
c021f22
Compare
Add support for running package manager installed test runners, and
javascript_test
targets.There's multiple flavors of how coverage reports are configured (extra args, separate script) depending on test runner.
To stay unopinionated I implemented support for both. If one has to go, extra args is the one.
This PR includes:
scripts
mapper build symbolnode_test_script
, where users can augment default behaviour, which is:(npm|pnpm|yarn) run test
, the "standard" way to execute test runners.npm run test <all-files-for-workspace>
is preferred to current behaviour of 1 run per file. See e.g https://jestjs.io/docs/cli#--maxworkersnumstring.Fixes #18524.