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(compile): unstable npm and node specifier support #19005

Merged
merged 84 commits into from
May 11, 2023

Conversation

dsherret
Copy link
Member

@dsherret dsherret commented May 5, 2023

This is the initial support for npm and node specifiers in deno compile. The npm packages are included in the binary and read from it via a virtual file system. This also supports the --node-modules-dir flag, dependencies specified in a package.json, and npm binary commands (ex. deno compile --unstable npm:cowsay)

Closes #16632

}
}

// todo(dsherret): we should store this more efficiently in the binary
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 did zero optimizations for storing this efficiently in the binary. We could store this in a binary format or shorten the property names in the json.

Copy link
Member

Choose a reason for hiding this comment

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

I think this is perfectly reasonable for the first pass

}

#[derive(Clone)]
struct FileBackedVfsFile {
Copy link
Member Author

Choose a reason for hiding this comment

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

Something we may want to consider is storing the files compressed, then decompressing them on open.

Copy link
Member

Choose a reason for hiding this comment

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

Sounds like a good idea, but we should benchmark it - might not be worth added complexity and startup time 👍

@dsherret dsherret marked this pull request as ready for review May 8, 2023 17:54
Copy link
Member

@bartlomieju bartlomieju left a comment

Choose a reason for hiding this comment

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

I could be nitpicky about various things could land prior to this PR, but I'm not gonna do that :)

Tested this locally in a few projects and I was even able to create an eslint binary in seconds. This is gonna be a great feature.

I want to give it another pass and get more eyes on it before approving, but overall fantastic work. Weeks of refactors and cleanups are paying off.

Comment on lines +246 to +254
let _trailer_pos = bufreader
.seek(SeekFrom::End(-(TRAILER_SIZE as i64)))
.await?;
let mut trailer = [0; TRAILER_SIZE];
bufreader.read_exact(&mut trailer).await?;
let (magic_trailer, rest) = trailer.split_at(8);
if magic_trailer != MAGIC_TRAILER {
return Ok(None);
}

let (eszip_archive_pos, rest) = rest.split_at(8);
let metadata_pos = rest;
let eszip_archive_pos = u64_from_bytes(eszip_archive_pos)?;
let metadata_pos = u64_from_bytes(metadata_pos)?;
let metadata_len = trailer_pos - metadata_pos;
let trailer = match Trailer::parse(&trailer)? {
None => return Ok(None),
Some(trailer) => trailer,
};
Copy link
Member

Choose a reason for hiding this comment

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

Unrelated to this PR, but do you see any other faster way of checking if this is deno compile binary? Asking in the context of startup time - seems like quite a lot of work to do before we start executing code in deno run subcommand.

Copy link
Member Author

Choose a reason for hiding this comment

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

Probably we should refactor the code more to be able to have slim binaries like before.

cli/standalone/binary.rs Show resolved Hide resolved
Comment on lines +538 to +542
// DO NOT include the user's registry url as it may contain credentials,
// but also don't make this dependent on the registry url
let registry_url = self.npm_api.base_url();
let root_path = self.npm_cache.registry_folder(registry_url);
let mut builder = VfsBuilder::new(root_path);
Copy link
Member

Choose a reason for hiding this comment

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

I don't get what's happening here - if we're not depending on this info how do we know we use the correct one?

Copy link
Member Author

@dsherret dsherret May 9, 2023

Choose a reason for hiding this comment

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

This stores the information relative to a base like C:\Users\david\AppData\Local\deno\npm\registry.npmjs.org\ (so it doesn't include the registry url). Then when loading in cli/standalone/mod.rs, it uses the dummy registry https://localhost/ for the npm registry url (which should never be requested) and that gets mapped appropriately in the vfs to a folder that takes that into account.

Copy link
Member Author

Choose a reason for hiding this comment

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

This is going to get more challenging once we support multiple npm registries...

}

#[async_trait::async_trait(?Send)]
impl FileSystem for DenoCompileFileSystem {
Copy link
Member

Choose a reason for hiding this comment

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

This is great 👍

Comment on lines +420 to +424
maybe_binary_npm_command_name: NpmPackageReqReference::from_specifier(
main_module,
)
.ok()
.map(|req_ref| npm_pkg_req_ref_to_binary_command(&req_ref)),
Copy link
Member

Choose a reason for hiding this comment

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

Thanks for fixing this! Also: really strange formatting here 😓

}
}

// todo(dsherret): we should store this more efficiently in the binary
Copy link
Member

Choose a reason for hiding this comment

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

I think this is perfectly reasonable for the first pass

}

#[derive(Clone)]
struct FileBackedVfsFile {
Copy link
Member

Choose a reason for hiding this comment

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

Sounds like a good idea, but we should benchmark it - might not be worth added complexity and startup time 👍

// get the virtual fs
let (dest_path, virtual_fs) = into_virtual_fs(builder, &temp_dir);

assert_eq!(read_file(&virtual_fs, &dest_path.join("a.txt")), "data");
Copy link
Member

Choose a reason for hiding this comment

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

Great tests

Copy link
Member

@bartlomieju bartlomieju left a comment

Choose a reason for hiding this comment

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

LGTM, I wasn't able to break it so far.

@dsherret dsherret merged commit 28aa489 into denoland:main May 11, 2023
@dsherret dsherret deleted the deno_compile_virtual_fs branch May 11, 2023 00:07
dsherret added a commit that referenced this pull request May 11, 2023
This is the initial support for npm and node specifiers in `deno
compile`. The npm packages are included in the binary and read from it via
a virtual file system. This also supports the `--node-modules-dir` flag,
dependencies specified in a package.json, and npm binary commands (ex.
`deno compile --unstable npm:cowsay`)

Closes #16632
@MarkBennett
Copy link

Now that this is merged, does this mean that support for npm specifiers could be coming to Deno Deploy?

@bartlomieju
Copy link
Member

Now that this is merged, does this mean that support for npm specifiers could be coming to Deno Deploy?

Yes, we are working on it.

@MarkBennett
Copy link

Now that this is merged, does this mean that support for npm specifiers could be coming to Deno Deploy?

Yes, we are working on it.

Great news @bartlomieju. Really impressed with the teams persistent on ironing out these kind of issues and making Deno a great experience for both greenfield projects, and teams that need to live with npm to some degree for the forsable future. 🙂

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.

deno compile cannot resolve npm modules
3 participants