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

Return list of dependencies/files used in Build API call with bundle: true #673

Closed
rdmurphy opened this issue Jan 14, 2021 · 5 comments
Closed

Comments

@rdmurphy
Copy link

rdmurphy commented Jan 14, 2021

Hello! 👋

Apologies if this is something that is already possible that I missed or if has been previously addressed. Would it be possible to make the Build API in bundle mode (I'm interested in the JavaScript interface, but imagine this would make sense in the Go API too) return a list of all the absolute paths to files it loaded/parsed to prepare a bundle?

My use case here is creating a watch mode — I'm thrilled that esbuild made the choice to not be a tool with yet another file watcher, but one thing I've not been able to figure out how to do is determine what files esbuild ends up parsing.

In my ideal scenario, a call to build({ bundle: true, incremental: true }) would return a list in the return object of all the absolute paths to files it used, and I could then take that list to hook it up to a file watcher of my choosing and selectively call result.rebuild() when any of them change.

{
  warnings: [],
  dependencies: [ // or includedFiles:
    "/absolute/path/to/entrypoint.js",
    "/absolute/path/to/other/file.js", 
    "/absolute/path/in/node_modules/library.js"
  ]
}

I could see this being useful with code that interfaces with the Serve API too — by watching for changes to any files it reports back as dependencies, I could smartly force a refresh of a page (and trigger the rebuild) only when necessary. (The only catch here is there is no direct way to get updated dependency lists like a rebuild() could, so perhaps it'd need an event emitter interface to do so? Not sure.)

Most guides to setting up file watchers with esbuild I've seen assume that every file in a glob should be the trigger (src/**/*.js), but it'd be great to have the information in hand to make smarter calls. One of my favorite examples of this in another space is with dart-sass and node-sass, which returns a handy list of used files under the includedFiles key, which is enough to create ways to selectively re-compile CSS as needed. My hope is esbuild could do something similar.

Thank you!

@evanw
Copy link
Owner

evanw commented Jan 14, 2021

Look at the metafile API option: https://esbuild.github.io/api/#metafile. If you specify that option, I think the contents of that JSON file will have the information you are looking for.

Keep in mind that the approach you described can lead to incorrect behavior. If you build twice, it's possible for none of the original input files to change and for the second build to still be different than the first. This is possible because new input files may be added that take precedence over the original input files in path resolution order.

For example, if the list of implicit extensions to resolve against is .a,.b and an import of ./file resolves to ./file.b in the first build, it may resolve to ./file.a in the second build if the file file.a is created in between the two builds. If you just watch the original input files, you will only watch file.b and you will miss this change that invalidated the build. This can also happen with node_modules folders if a package is installed in between builds.

Just a friendly warning that this approach is very hard to get right! It's safer to just watch the whole project directory and explicitly exclude files you know don't invalidate the build instead of trying to only include files that you know invalidate the build.

@rdmurphy
Copy link
Author

Hah! I was just coming back here because I saw I completely missed metafile in the docs. I do think this gives me everything I was looking for, but do wish I could make it optionally return that object with something like metafile: true instead of having it always write to disk by default. 🤔 Like:

{
  warnings: [],
  metafile: ... 
}

Not end of the world though!

And you're right! I think my assumption is that a feature like this would always return an up-to-date list of what is currently being depended on with each build/rebuild.

What I usually do in scenarios like this is err on the side of watching too much vs. keeping it exactly in sync. So using your example — if build({ incremental: true }) returns an initial metafile JSON that shows ./file.b is used, I'll start watching for changes to it. Then, when result.rebuild() is called it too would ideally return an updated metafile JSON, and it'd include that new ./file.a import in the list because it's now a dependency, and I would add that as an additional file that needs to be watched. Same with the node_modules example — if a new package gets used, it shows up in the updated metafile and can be added.

@evanw
Copy link
Owner

evanw commented Jan 14, 2021

I'm going to close this then since it seems like the original issue is resolved.

wish I could make it optionally return that object with something like metafile: true instead of having it always write to disk by default.

I agree. This is already tracked by #633.

@evanw evanw closed this as completed Jan 14, 2021
@lukeed
Copy link
Contributor

lukeed commented Jan 14, 2021

Not exactly what you're looking for, but if you use write: false then you'll get a outputFiles array and the metafile will be in there, identifiable by the path you gave it. (But then you gotta write the files to disk manually)

@rdmurphy
Copy link
Author

Sounds good — thanks for talking through it with me @evanw! (And for raising those concerns!)

@lukeed Ooh. Didn't know it did that. I'll check that out. 🙃 I'm cool with writing my own files.

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

No branches or pull requests

3 participants