mirror of
https://github.com/denoland/deno.git
synced 2024-12-22 07:14:47 -05:00
fix(compile): be more deterministic when compiling the same code in different directories (#27395)
Additionaly, this no longer unnecessarily stores the source twice for file specifiers and fixes some sourcemap issues. Closes https://github.com/denoland/deno/issues/27284
This commit is contained in:
parent
350d9dce41
commit
074444ab6c
18 changed files with 563 additions and 269 deletions
|
@ -1363,9 +1363,9 @@ impl CliOptions {
|
||||||
|
|
||||||
Ok(DenoLintConfig {
|
Ok(DenoLintConfig {
|
||||||
default_jsx_factory: (!transpile_options.jsx_automatic)
|
default_jsx_factory: (!transpile_options.jsx_automatic)
|
||||||
.then(|| transpile_options.jsx_factory.clone()),
|
.then_some(transpile_options.jsx_factory),
|
||||||
default_jsx_fragment_factory: (!transpile_options.jsx_automatic)
|
default_jsx_fragment_factory: (!transpile_options.jsx_automatic)
|
||||||
.then(|| transpile_options.jsx_fragment_factory.clone()),
|
.then_some(transpile_options.jsx_fragment_factory),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
35
cli/emit.rs
35
cli/emit.rs
|
@ -5,6 +5,7 @@ use crate::cache::FastInsecureHasher;
|
||||||
use crate::cache::ParsedSourceCache;
|
use crate::cache::ParsedSourceCache;
|
||||||
use crate::resolver::CjsTracker;
|
use crate::resolver::CjsTracker;
|
||||||
|
|
||||||
|
use deno_ast::EmittedSourceText;
|
||||||
use deno_ast::ModuleKind;
|
use deno_ast::ModuleKind;
|
||||||
use deno_ast::SourceMapOption;
|
use deno_ast::SourceMapOption;
|
||||||
use deno_ast::SourceRange;
|
use deno_ast::SourceRange;
|
||||||
|
@ -132,6 +133,7 @@ impl Emitter {
|
||||||
&transpile_and_emit_options.0,
|
&transpile_and_emit_options.0,
|
||||||
&transpile_and_emit_options.1,
|
&transpile_and_emit_options.1,
|
||||||
)
|
)
|
||||||
|
.map(|r| r.text)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.await
|
.await
|
||||||
|
@ -166,7 +168,8 @@ impl Emitter {
|
||||||
source.clone(),
|
source.clone(),
|
||||||
&self.transpile_and_emit_options.0,
|
&self.transpile_and_emit_options.0,
|
||||||
&self.transpile_and_emit_options.1,
|
&self.transpile_and_emit_options.1,
|
||||||
)?;
|
)?
|
||||||
|
.text;
|
||||||
helper.post_emit_parsed_source(
|
helper.post_emit_parsed_source(
|
||||||
specifier,
|
specifier,
|
||||||
&transpiled_source,
|
&transpiled_source,
|
||||||
|
@ -177,6 +180,31 @@ impl Emitter {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn emit_parsed_source_for_deno_compile(
|
||||||
|
&self,
|
||||||
|
specifier: &ModuleSpecifier,
|
||||||
|
media_type: MediaType,
|
||||||
|
module_kind: deno_ast::ModuleKind,
|
||||||
|
source: &Arc<str>,
|
||||||
|
) -> Result<(String, String), AnyError> {
|
||||||
|
let mut emit_options = self.transpile_and_emit_options.1.clone();
|
||||||
|
emit_options.inline_sources = false;
|
||||||
|
emit_options.source_map = SourceMapOption::Separate;
|
||||||
|
// strip off the path to have more deterministic builds as we don't care
|
||||||
|
// about the source name because we manually provide the source map to v8
|
||||||
|
emit_options.source_map_base = Some(deno_path_util::url_parent(specifier));
|
||||||
|
let source = EmitParsedSourceHelper::transpile(
|
||||||
|
&self.parsed_source_cache,
|
||||||
|
specifier,
|
||||||
|
media_type,
|
||||||
|
module_kind,
|
||||||
|
source.clone(),
|
||||||
|
&self.transpile_and_emit_options.0,
|
||||||
|
&emit_options,
|
||||||
|
)?;
|
||||||
|
Ok((source.text, source.source_map.unwrap()))
|
||||||
|
}
|
||||||
|
|
||||||
/// Expects a file URL, panics otherwise.
|
/// Expects a file URL, panics otherwise.
|
||||||
pub async fn load_and_emit_for_hmr(
|
pub async fn load_and_emit_for_hmr(
|
||||||
&self,
|
&self,
|
||||||
|
@ -282,7 +310,7 @@ impl<'a> EmitParsedSourceHelper<'a> {
|
||||||
source: Arc<str>,
|
source: Arc<str>,
|
||||||
transpile_options: &deno_ast::TranspileOptions,
|
transpile_options: &deno_ast::TranspileOptions,
|
||||||
emit_options: &deno_ast::EmitOptions,
|
emit_options: &deno_ast::EmitOptions,
|
||||||
) -> Result<String, AnyError> {
|
) -> Result<EmittedSourceText, AnyError> {
|
||||||
// nothing else needs the parsed source at this point, so remove from
|
// nothing else needs the parsed source at this point, so remove from
|
||||||
// the cache in order to not transpile owned
|
// the cache in order to not transpile owned
|
||||||
let parsed_source = parsed_source_cache
|
let parsed_source = parsed_source_cache
|
||||||
|
@ -302,8 +330,7 @@ impl<'a> EmitParsedSourceHelper<'a> {
|
||||||
source
|
source
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
debug_assert!(transpiled_source.source_map.is_none());
|
Ok(transpiled_source)
|
||||||
Ok(transpiled_source.text)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn post_emit_parsed_source(
|
pub fn post_emit_parsed_source(
|
||||||
|
|
|
@ -91,6 +91,7 @@ use super::serialization::DenoCompileModuleData;
|
||||||
use super::serialization::DeserializedDataSection;
|
use super::serialization::DeserializedDataSection;
|
||||||
use super::serialization::RemoteModulesStore;
|
use super::serialization::RemoteModulesStore;
|
||||||
use super::serialization::RemoteModulesStoreBuilder;
|
use super::serialization::RemoteModulesStoreBuilder;
|
||||||
|
use super::serialization::SourceMapStore;
|
||||||
use super::virtual_fs::output_vfs;
|
use super::virtual_fs::output_vfs;
|
||||||
use super::virtual_fs::BuiltVfs;
|
use super::virtual_fs::BuiltVfs;
|
||||||
use super::virtual_fs::FileBackedVfs;
|
use super::virtual_fs::FileBackedVfs;
|
||||||
|
@ -98,6 +99,7 @@ use super::virtual_fs::VfsBuilder;
|
||||||
use super::virtual_fs::VfsFileSubDataKind;
|
use super::virtual_fs::VfsFileSubDataKind;
|
||||||
use super::virtual_fs::VfsRoot;
|
use super::virtual_fs::VfsRoot;
|
||||||
use super::virtual_fs::VirtualDirectory;
|
use super::virtual_fs::VirtualDirectory;
|
||||||
|
use super::virtual_fs::VirtualDirectoryEntries;
|
||||||
use super::virtual_fs::WindowsSystemRootablePath;
|
use super::virtual_fs::WindowsSystemRootablePath;
|
||||||
|
|
||||||
pub static DENO_COMPILE_GLOBAL_NODE_MODULES_DIR_NAME: &str =
|
pub static DENO_COMPILE_GLOBAL_NODE_MODULES_DIR_NAME: &str =
|
||||||
|
@ -203,18 +205,25 @@ pub struct Metadata {
|
||||||
pub otel_config: OtelConfig,
|
pub otel_config: OtelConfig,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[allow(clippy::too_many_arguments)]
|
||||||
fn write_binary_bytes(
|
fn write_binary_bytes(
|
||||||
mut file_writer: File,
|
mut file_writer: File,
|
||||||
original_bin: Vec<u8>,
|
original_bin: Vec<u8>,
|
||||||
metadata: &Metadata,
|
metadata: &Metadata,
|
||||||
npm_snapshot: Option<SerializedNpmResolutionSnapshot>,
|
npm_snapshot: Option<SerializedNpmResolutionSnapshot>,
|
||||||
remote_modules: &RemoteModulesStoreBuilder,
|
remote_modules: &RemoteModulesStoreBuilder,
|
||||||
|
source_map_store: &SourceMapStore,
|
||||||
vfs: &BuiltVfs,
|
vfs: &BuiltVfs,
|
||||||
compile_flags: &CompileFlags,
|
compile_flags: &CompileFlags,
|
||||||
) -> Result<(), AnyError> {
|
) -> Result<(), AnyError> {
|
||||||
let data_section_bytes =
|
let data_section_bytes = serialize_binary_data_section(
|
||||||
serialize_binary_data_section(metadata, npm_snapshot, remote_modules, vfs)
|
metadata,
|
||||||
.context("Serializing binary data section.")?;
|
npm_snapshot,
|
||||||
|
remote_modules,
|
||||||
|
source_map_store,
|
||||||
|
vfs,
|
||||||
|
)
|
||||||
|
.context("Serializing binary data section.")?;
|
||||||
|
|
||||||
let target = compile_flags.resolve_target();
|
let target = compile_flags.resolve_target();
|
||||||
if target.contains("linux") {
|
if target.contains("linux") {
|
||||||
|
@ -256,6 +265,7 @@ pub struct StandaloneData {
|
||||||
pub modules: StandaloneModules,
|
pub modules: StandaloneModules,
|
||||||
pub npm_snapshot: Option<ValidSerializedNpmResolutionSnapshot>,
|
pub npm_snapshot: Option<ValidSerializedNpmResolutionSnapshot>,
|
||||||
pub root_path: PathBuf,
|
pub root_path: PathBuf,
|
||||||
|
pub source_maps: SourceMapStore,
|
||||||
pub vfs: Arc<FileBackedVfs>,
|
pub vfs: Arc<FileBackedVfs>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -283,13 +293,12 @@ impl StandaloneModules {
|
||||||
pub fn read<'a>(
|
pub fn read<'a>(
|
||||||
&'a self,
|
&'a self,
|
||||||
specifier: &'a ModuleSpecifier,
|
specifier: &'a ModuleSpecifier,
|
||||||
|
kind: VfsFileSubDataKind,
|
||||||
) -> Result<Option<DenoCompileModuleData<'a>>, AnyError> {
|
) -> Result<Option<DenoCompileModuleData<'a>>, AnyError> {
|
||||||
if specifier.scheme() == "file" {
|
if specifier.scheme() == "file" {
|
||||||
let path = deno_path_util::url_to_file_path(specifier)?;
|
let path = deno_path_util::url_to_file_path(specifier)?;
|
||||||
let bytes = match self.vfs.file_entry(&path) {
|
let bytes = match self.vfs.file_entry(&path) {
|
||||||
Ok(entry) => self
|
Ok(entry) => self.vfs.read_file_all(entry, kind)?,
|
||||||
.vfs
|
|
||||||
.read_file_all(entry, VfsFileSubDataKind::ModuleGraph)?,
|
|
||||||
Err(err) if err.kind() == ErrorKind::NotFound => {
|
Err(err) if err.kind() == ErrorKind::NotFound => {
|
||||||
match RealFs.read_file_sync(&path, None) {
|
match RealFs.read_file_sync(&path, None) {
|
||||||
Ok(bytes) => bytes,
|
Ok(bytes) => bytes,
|
||||||
|
@ -307,7 +316,18 @@ impl StandaloneModules {
|
||||||
data: bytes,
|
data: bytes,
|
||||||
}))
|
}))
|
||||||
} else {
|
} else {
|
||||||
self.remote_modules.read(specifier)
|
self.remote_modules.read(specifier).map(|maybe_entry| {
|
||||||
|
maybe_entry.map(|entry| DenoCompileModuleData {
|
||||||
|
media_type: entry.media_type,
|
||||||
|
specifier: entry.specifier,
|
||||||
|
data: match kind {
|
||||||
|
VfsFileSubDataKind::Raw => entry.data,
|
||||||
|
VfsFileSubDataKind::ModuleGraph => {
|
||||||
|
entry.transpiled_data.unwrap_or(entry.data)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
})
|
||||||
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -328,7 +348,8 @@ pub fn extract_standalone(
|
||||||
mut metadata,
|
mut metadata,
|
||||||
npm_snapshot,
|
npm_snapshot,
|
||||||
remote_modules,
|
remote_modules,
|
||||||
mut vfs_dir,
|
source_maps,
|
||||||
|
vfs_root_entries,
|
||||||
vfs_files_data,
|
vfs_files_data,
|
||||||
} = match deserialize_binary_data_section(data)? {
|
} = match deserialize_binary_data_section(data)? {
|
||||||
Some(data_section) => data_section,
|
Some(data_section) => data_section,
|
||||||
|
@ -351,11 +372,12 @@ pub fn extract_standalone(
|
||||||
metadata.argv.push(arg.into_string().unwrap());
|
metadata.argv.push(arg.into_string().unwrap());
|
||||||
}
|
}
|
||||||
let vfs = {
|
let vfs = {
|
||||||
// align the name of the directory with the root dir
|
|
||||||
vfs_dir.name = root_path.file_name().unwrap().to_string_lossy().to_string();
|
|
||||||
|
|
||||||
let fs_root = VfsRoot {
|
let fs_root = VfsRoot {
|
||||||
dir: vfs_dir,
|
dir: VirtualDirectory {
|
||||||
|
// align the name of the directory with the root dir
|
||||||
|
name: root_path.file_name().unwrap().to_string_lossy().to_string(),
|
||||||
|
entries: vfs_root_entries,
|
||||||
|
},
|
||||||
root_path: root_path.clone(),
|
root_path: root_path.clone(),
|
||||||
start_file_offset: 0,
|
start_file_offset: 0,
|
||||||
};
|
};
|
||||||
|
@ -372,6 +394,7 @@ pub fn extract_standalone(
|
||||||
},
|
},
|
||||||
npm_snapshot,
|
npm_snapshot,
|
||||||
root_path,
|
root_path,
|
||||||
|
source_maps,
|
||||||
vfs,
|
vfs,
|
||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
|
@ -451,7 +474,7 @@ impl<'a> DenoCompileBinaryWriter<'a> {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
self.write_standalone_binary(options, original_binary).await
|
self.write_standalone_binary(options, original_binary)
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn get_base_binary(
|
async fn get_base_binary(
|
||||||
|
@ -554,7 +577,7 @@ impl<'a> DenoCompileBinaryWriter<'a> {
|
||||||
/// This functions creates a standalone deno binary by appending a bundle
|
/// This functions creates a standalone deno binary by appending a bundle
|
||||||
/// and magic trailer to the currently executing binary.
|
/// and magic trailer to the currently executing binary.
|
||||||
#[allow(clippy::too_many_arguments)]
|
#[allow(clippy::too_many_arguments)]
|
||||||
async fn write_standalone_binary(
|
fn write_standalone_binary(
|
||||||
&self,
|
&self,
|
||||||
options: WriteBinOptions<'_>,
|
options: WriteBinOptions<'_>,
|
||||||
original_bin: Vec<u8>,
|
original_bin: Vec<u8>,
|
||||||
|
@ -598,71 +621,81 @@ impl<'a> DenoCompileBinaryWriter<'a> {
|
||||||
.with_context(|| format!("Including {}", path.display()))?;
|
.with_context(|| format!("Including {}", path.display()))?;
|
||||||
}
|
}
|
||||||
let mut remote_modules_store = RemoteModulesStoreBuilder::default();
|
let mut remote_modules_store = RemoteModulesStoreBuilder::default();
|
||||||
let mut code_cache_key_hasher = if self.cli_options.code_cache_enabled() {
|
let mut source_maps = Vec::with_capacity(graph.specifiers_count());
|
||||||
Some(FastInsecureHasher::new_deno_versioned())
|
// todo(dsherret): transpile in parallel
|
||||||
} else {
|
|
||||||
None
|
|
||||||
};
|
|
||||||
for module in graph.modules() {
|
for module in graph.modules() {
|
||||||
if module.specifier().scheme() == "data" {
|
if module.specifier().scheme() == "data" {
|
||||||
continue; // don't store data urls as an entry as they're in the code
|
continue; // don't store data urls as an entry as they're in the code
|
||||||
}
|
}
|
||||||
if let Some(hasher) = &mut code_cache_key_hasher {
|
let (maybe_original_source, maybe_transpiled, media_type) = match module {
|
||||||
if let Some(source) = module.source() {
|
|
||||||
hasher.write(module.specifier().as_str().as_bytes());
|
|
||||||
hasher.write(source.as_bytes());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
let (maybe_source, media_type) = match module {
|
|
||||||
deno_graph::Module::Js(m) => {
|
deno_graph::Module::Js(m) => {
|
||||||
let source = if m.media_type.is_emittable() {
|
let original_bytes = m.source.as_bytes().to_vec();
|
||||||
|
let maybe_transpiled = if m.media_type.is_emittable() {
|
||||||
let is_cjs = self.cjs_tracker.is_cjs_with_known_is_script(
|
let is_cjs = self.cjs_tracker.is_cjs_with_known_is_script(
|
||||||
&m.specifier,
|
&m.specifier,
|
||||||
m.media_type,
|
m.media_type,
|
||||||
m.is_script,
|
m.is_script,
|
||||||
)?;
|
)?;
|
||||||
let module_kind = ModuleKind::from_is_cjs(is_cjs);
|
let module_kind = ModuleKind::from_is_cjs(is_cjs);
|
||||||
let source = self
|
let (source, source_map) =
|
||||||
.emitter
|
self.emitter.emit_parsed_source_for_deno_compile(
|
||||||
.emit_parsed_source(
|
|
||||||
&m.specifier,
|
&m.specifier,
|
||||||
m.media_type,
|
m.media_type,
|
||||||
module_kind,
|
module_kind,
|
||||||
&m.source,
|
&m.source,
|
||||||
)
|
)?;
|
||||||
.await?;
|
if source != m.source.as_ref() {
|
||||||
source.into_bytes()
|
source_maps.push((&m.specifier, source_map));
|
||||||
|
Some(source.into_bytes())
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
m.source.as_bytes().to_vec()
|
None
|
||||||
};
|
};
|
||||||
(Some(source), m.media_type)
|
(Some(original_bytes), maybe_transpiled, m.media_type)
|
||||||
}
|
}
|
||||||
deno_graph::Module::Json(m) => {
|
deno_graph::Module::Json(m) => {
|
||||||
(Some(m.source.as_bytes().to_vec()), m.media_type)
|
(Some(m.source.as_bytes().to_vec()), None, m.media_type)
|
||||||
}
|
}
|
||||||
deno_graph::Module::Wasm(m) => {
|
deno_graph::Module::Wasm(m) => {
|
||||||
(Some(m.source.to_vec()), MediaType::Wasm)
|
(Some(m.source.to_vec()), None, MediaType::Wasm)
|
||||||
}
|
}
|
||||||
deno_graph::Module::Npm(_)
|
deno_graph::Module::Npm(_)
|
||||||
| deno_graph::Module::Node(_)
|
| deno_graph::Module::Node(_)
|
||||||
| deno_graph::Module::External(_) => (None, MediaType::Unknown),
|
| deno_graph::Module::External(_) => (None, None, MediaType::Unknown),
|
||||||
};
|
};
|
||||||
if module.specifier().scheme() == "file" {
|
if let Some(original_source) = maybe_original_source {
|
||||||
let file_path = deno_path_util::url_to_file_path(module.specifier())?;
|
if module.specifier().scheme() == "file" {
|
||||||
vfs
|
let file_path = deno_path_util::url_to_file_path(module.specifier())?;
|
||||||
.add_file_with_data(
|
vfs
|
||||||
&file_path,
|
.add_file_with_data(
|
||||||
match maybe_source {
|
&file_path,
|
||||||
Some(source) => source,
|
original_source,
|
||||||
None => RealFs.read_file_sync(&file_path, None)?.into_owned(),
|
VfsFileSubDataKind::Raw,
|
||||||
},
|
)
|
||||||
VfsFileSubDataKind::ModuleGraph,
|
.with_context(|| {
|
||||||
)
|
format!("Failed adding '{}'", file_path.display())
|
||||||
.with_context(|| {
|
})?;
|
||||||
format!("Failed adding '{}'", file_path.display())
|
if let Some(transpiled_source) = maybe_transpiled {
|
||||||
})?;
|
vfs
|
||||||
} else if let Some(source) = maybe_source {
|
.add_file_with_data(
|
||||||
remote_modules_store.add(module.specifier(), media_type, source);
|
&file_path,
|
||||||
|
transpiled_source,
|
||||||
|
VfsFileSubDataKind::ModuleGraph,
|
||||||
|
)
|
||||||
|
.with_context(|| {
|
||||||
|
format!("Failed adding '{}'", file_path.display())
|
||||||
|
})?;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
remote_modules_store.add(
|
||||||
|
module.specifier(),
|
||||||
|
media_type,
|
||||||
|
original_source,
|
||||||
|
maybe_transpiled,
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
remote_modules_store.add_redirects(&graph.redirects);
|
remote_modules_store.add_redirects(&graph.redirects);
|
||||||
|
@ -695,6 +728,28 @@ impl<'a> DenoCompileBinaryWriter<'a> {
|
||||||
None => StandaloneRelativeFileBaseUrl::WindowsSystemRoot,
|
None => StandaloneRelativeFileBaseUrl::WindowsSystemRoot,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
let code_cache_key = if self.cli_options.code_cache_enabled() {
|
||||||
|
let mut hasher = FastInsecureHasher::new_deno_versioned();
|
||||||
|
for module in graph.modules() {
|
||||||
|
if let Some(source) = module.source() {
|
||||||
|
hasher
|
||||||
|
.write(root_dir_url.specifier_key(module.specifier()).as_bytes());
|
||||||
|
hasher.write(source.as_bytes());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Some(hasher.finish())
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
};
|
||||||
|
|
||||||
|
let mut source_map_store = SourceMapStore::with_capacity(source_maps.len());
|
||||||
|
for (specifier, source_map) in source_maps {
|
||||||
|
source_map_store.add(
|
||||||
|
Cow::Owned(root_dir_url.specifier_key(specifier).into_owned()),
|
||||||
|
Cow::Owned(source_map),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
let node_modules = match self.npm_resolver.as_inner() {
|
let node_modules = match self.npm_resolver.as_inner() {
|
||||||
InnerCliNpmResolverRef::Managed(_) => {
|
InnerCliNpmResolverRef::Managed(_) => {
|
||||||
npm_snapshot.as_ref().map(|_| NodeModules::Managed {
|
npm_snapshot.as_ref().map(|_| NodeModules::Managed {
|
||||||
|
@ -742,7 +797,7 @@ impl<'a> DenoCompileBinaryWriter<'a> {
|
||||||
let metadata = Metadata {
|
let metadata = Metadata {
|
||||||
argv: compile_flags.args.clone(),
|
argv: compile_flags.args.clone(),
|
||||||
seed: self.cli_options.seed(),
|
seed: self.cli_options.seed(),
|
||||||
code_cache_key: code_cache_key_hasher.map(|h| h.finish()),
|
code_cache_key,
|
||||||
location: self.cli_options.location_flag().clone(),
|
location: self.cli_options.location_flag().clone(),
|
||||||
permissions: self.cli_options.permission_flags().clone(),
|
permissions: self.cli_options.permission_flags().clone(),
|
||||||
v8_flags: self.cli_options.v8_flags().clone(),
|
v8_flags: self.cli_options.v8_flags().clone(),
|
||||||
|
@ -809,6 +864,7 @@ impl<'a> DenoCompileBinaryWriter<'a> {
|
||||||
&metadata,
|
&metadata,
|
||||||
npm_snapshot.map(|s| s.into_serialized()),
|
npm_snapshot.map(|s| s.into_serialized()),
|
||||||
&remote_modules_store,
|
&remote_modules_store,
|
||||||
|
&source_map_store,
|
||||||
&vfs,
|
&vfs,
|
||||||
compile_flags,
|
compile_flags,
|
||||||
)
|
)
|
||||||
|
@ -903,10 +959,10 @@ impl<'a> DenoCompileBinaryWriter<'a> {
|
||||||
root_dir.name = DENO_COMPILE_GLOBAL_NODE_MODULES_DIR_NAME.to_string();
|
root_dir.name = DENO_COMPILE_GLOBAL_NODE_MODULES_DIR_NAME.to_string();
|
||||||
let mut new_entries = Vec::with_capacity(root_dir.entries.len());
|
let mut new_entries = Vec::with_capacity(root_dir.entries.len());
|
||||||
let mut localhost_entries = IndexMap::new();
|
let mut localhost_entries = IndexMap::new();
|
||||||
for entry in std::mem::take(&mut root_dir.entries) {
|
for entry in root_dir.entries.take_inner() {
|
||||||
match entry {
|
match entry {
|
||||||
VfsEntry::Dir(dir) => {
|
VfsEntry::Dir(mut dir) => {
|
||||||
for entry in dir.entries {
|
for entry in dir.entries.take_inner() {
|
||||||
log::debug!("Flattening {} into node_modules", entry.name());
|
log::debug!("Flattening {} into node_modules", entry.name());
|
||||||
if let Some(existing) =
|
if let Some(existing) =
|
||||||
localhost_entries.insert(entry.name().to_string(), entry)
|
localhost_entries.insert(entry.name().to_string(), entry)
|
||||||
|
@ -925,11 +981,11 @@ impl<'a> DenoCompileBinaryWriter<'a> {
|
||||||
}
|
}
|
||||||
new_entries.push(VfsEntry::Dir(VirtualDirectory {
|
new_entries.push(VfsEntry::Dir(VirtualDirectory {
|
||||||
name: "localhost".to_string(),
|
name: "localhost".to_string(),
|
||||||
entries: localhost_entries.into_iter().map(|(_, v)| v).collect(),
|
entries: VirtualDirectoryEntries::new(
|
||||||
|
localhost_entries.into_iter().map(|(_, v)| v).collect(),
|
||||||
|
),
|
||||||
}));
|
}));
|
||||||
// needs to be sorted by name
|
root_dir.entries = VirtualDirectoryEntries::new(new_entries);
|
||||||
new_entries.sort_by(|a, b| a.name().cmp(b.name()));
|
|
||||||
root_dir.entries = new_entries;
|
|
||||||
|
|
||||||
// it's better to not expose the user's cache directory, so take it out
|
// it's better to not expose the user's cache directory, so take it out
|
||||||
// of there
|
// of there
|
||||||
|
@ -937,10 +993,7 @@ impl<'a> DenoCompileBinaryWriter<'a> {
|
||||||
let parent_dir = vfs.get_dir_mut(parent).unwrap();
|
let parent_dir = vfs.get_dir_mut(parent).unwrap();
|
||||||
let index = parent_dir
|
let index = parent_dir
|
||||||
.entries
|
.entries
|
||||||
.iter()
|
.binary_search(DENO_COMPILE_GLOBAL_NODE_MODULES_DIR_NAME)
|
||||||
.position(|entry| {
|
|
||||||
entry.name() == DENO_COMPILE_GLOBAL_NODE_MODULES_DIR_NAME
|
|
||||||
})
|
|
||||||
.unwrap();
|
.unwrap();
|
||||||
let npm_global_cache_dir_entry = parent_dir.entries.remove(index);
|
let npm_global_cache_dir_entry = parent_dir.entries.remove(index);
|
||||||
|
|
||||||
|
@ -950,11 +1003,7 @@ impl<'a> DenoCompileBinaryWriter<'a> {
|
||||||
Cow::Borrowed(DENO_COMPILE_GLOBAL_NODE_MODULES_DIR_NAME);
|
Cow::Borrowed(DENO_COMPILE_GLOBAL_NODE_MODULES_DIR_NAME);
|
||||||
for ancestor in parent.ancestors() {
|
for ancestor in parent.ancestors() {
|
||||||
let dir = vfs.get_dir_mut(ancestor).unwrap();
|
let dir = vfs.get_dir_mut(ancestor).unwrap();
|
||||||
if let Some(index) = dir
|
if let Ok(index) = dir.entries.binary_search(&last_name) {
|
||||||
.entries
|
|
||||||
.iter()
|
|
||||||
.position(|entry| entry.name() == last_name)
|
|
||||||
{
|
|
||||||
dir.entries.remove(index);
|
dir.entries.remove(index);
|
||||||
}
|
}
|
||||||
last_name = Cow::Owned(dir.name.clone());
|
last_name = Cow::Owned(dir.name.clone());
|
||||||
|
@ -965,7 +1014,7 @@ impl<'a> DenoCompileBinaryWriter<'a> {
|
||||||
|
|
||||||
// now build the vfs and add the global cache dir entry there
|
// now build the vfs and add the global cache dir entry there
|
||||||
let mut built_vfs = vfs.build();
|
let mut built_vfs = vfs.build();
|
||||||
built_vfs.root.insert_entry(npm_global_cache_dir_entry);
|
built_vfs.entries.insert(npm_global_cache_dir_entry);
|
||||||
built_vfs
|
built_vfs
|
||||||
}
|
}
|
||||||
InnerCliNpmResolverRef::Byonm(_) => vfs.build(),
|
InnerCliNpmResolverRef::Byonm(_) => vfs.build(),
|
||||||
|
|
|
@ -55,6 +55,7 @@ use node_resolver::errors::ClosestPkgJsonError;
|
||||||
use node_resolver::NodeResolutionKind;
|
use node_resolver::NodeResolutionKind;
|
||||||
use node_resolver::ResolutionMode;
|
use node_resolver::ResolutionMode;
|
||||||
use serialization::DenoCompileModuleSource;
|
use serialization::DenoCompileModuleSource;
|
||||||
|
use serialization::SourceMapStore;
|
||||||
use std::borrow::Cow;
|
use std::borrow::Cow;
|
||||||
use std::rc::Rc;
|
use std::rc::Rc;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
@ -122,6 +123,7 @@ struct SharedModuleLoaderState {
|
||||||
npm_module_loader: Arc<NpmModuleLoader>,
|
npm_module_loader: Arc<NpmModuleLoader>,
|
||||||
npm_req_resolver: Arc<CliNpmReqResolver>,
|
npm_req_resolver: Arc<CliNpmReqResolver>,
|
||||||
npm_resolver: Arc<dyn CliNpmResolver>,
|
npm_resolver: Arc<dyn CliNpmResolver>,
|
||||||
|
source_maps: SourceMapStore,
|
||||||
vfs: Arc<FileBackedVfs>,
|
vfs: Arc<FileBackedVfs>,
|
||||||
workspace_resolver: WorkspaceResolver,
|
workspace_resolver: WorkspaceResolver,
|
||||||
}
|
}
|
||||||
|
@ -396,7 +398,11 @@ impl ModuleLoader for EmbeddedModuleLoader {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
match self.shared.modules.read(original_specifier) {
|
match self
|
||||||
|
.shared
|
||||||
|
.modules
|
||||||
|
.read(original_specifier, VfsFileSubDataKind::ModuleGraph)
|
||||||
|
{
|
||||||
Ok(Some(module)) => {
|
Ok(Some(module)) => {
|
||||||
let media_type = module.media_type;
|
let media_type = module.media_type;
|
||||||
let (module_specifier, module_type, module_source) =
|
let (module_specifier, module_type, module_source) =
|
||||||
|
@ -495,6 +501,46 @@ impl ModuleLoader for EmbeddedModuleLoader {
|
||||||
}
|
}
|
||||||
std::future::ready(()).boxed_local()
|
std::future::ready(()).boxed_local()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn get_source_map(&self, file_name: &str) -> Option<Vec<u8>> {
|
||||||
|
if file_name.starts_with("file:///") {
|
||||||
|
let url =
|
||||||
|
deno_path_util::url_from_directory_path(self.shared.vfs.root()).ok()?;
|
||||||
|
let file_url = ModuleSpecifier::parse(file_name).ok()?;
|
||||||
|
let relative_path = url.make_relative(&file_url)?;
|
||||||
|
self.shared.source_maps.get(&relative_path)
|
||||||
|
} else {
|
||||||
|
self.shared.source_maps.get(file_name)
|
||||||
|
}
|
||||||
|
// todo(https://github.com/denoland/deno_core/pull/1007): don't clone
|
||||||
|
.map(|s| s.as_bytes().to_vec())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_source_mapped_source_line(
|
||||||
|
&self,
|
||||||
|
file_name: &str,
|
||||||
|
line_number: usize,
|
||||||
|
) -> Option<String> {
|
||||||
|
let specifier = ModuleSpecifier::parse(file_name).ok()?;
|
||||||
|
let data = self
|
||||||
|
.shared
|
||||||
|
.modules
|
||||||
|
.read(&specifier, VfsFileSubDataKind::Raw)
|
||||||
|
.ok()??;
|
||||||
|
|
||||||
|
let source = String::from_utf8_lossy(&data.data);
|
||||||
|
// Do NOT use .lines(): it skips the terminating empty line.
|
||||||
|
// (due to internally using_terminator() instead of .split())
|
||||||
|
let lines: Vec<&str> = source.split('\n').collect();
|
||||||
|
if line_number >= lines.len() {
|
||||||
|
Some(format!(
|
||||||
|
"{} Couldn't format source line: Line {} is out of bounds (source may have changed at runtime)",
|
||||||
|
crate::colors::yellow("Warning"), line_number + 1,
|
||||||
|
))
|
||||||
|
} else {
|
||||||
|
Some(lines[line_number].to_string())
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl NodeRequireLoader for EmbeddedModuleLoader {
|
impl NodeRequireLoader for EmbeddedModuleLoader {
|
||||||
|
@ -590,6 +636,7 @@ pub async fn run(data: StandaloneData) -> Result<i32, AnyError> {
|
||||||
modules,
|
modules,
|
||||||
npm_snapshot,
|
npm_snapshot,
|
||||||
root_path,
|
root_path,
|
||||||
|
source_maps,
|
||||||
vfs,
|
vfs,
|
||||||
} = data;
|
} = data;
|
||||||
let deno_dir_provider = Arc::new(DenoDirProvider::new(None));
|
let deno_dir_provider = Arc::new(DenoDirProvider::new(None));
|
||||||
|
@ -841,6 +888,7 @@ pub async fn run(data: StandaloneData) -> Result<i32, AnyError> {
|
||||||
)),
|
)),
|
||||||
npm_resolver: npm_resolver.clone(),
|
npm_resolver: npm_resolver.clone(),
|
||||||
npm_req_resolver,
|
npm_req_resolver,
|
||||||
|
source_maps,
|
||||||
vfs,
|
vfs,
|
||||||
workspace_resolver,
|
workspace_resolver,
|
||||||
}),
|
}),
|
||||||
|
|
|
@ -6,6 +6,7 @@ use std::collections::BTreeMap;
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
use std::io::Write;
|
use std::io::Write;
|
||||||
|
|
||||||
|
use deno_ast::swc::common::source_map;
|
||||||
use deno_ast::MediaType;
|
use deno_ast::MediaType;
|
||||||
use deno_core::anyhow::bail;
|
use deno_core::anyhow::bail;
|
||||||
use deno_core::anyhow::Context;
|
use deno_core::anyhow::Context;
|
||||||
|
@ -20,12 +21,14 @@ use deno_npm::resolution::SerializedNpmResolutionSnapshotPackage;
|
||||||
use deno_npm::resolution::ValidSerializedNpmResolutionSnapshot;
|
use deno_npm::resolution::ValidSerializedNpmResolutionSnapshot;
|
||||||
use deno_npm::NpmPackageId;
|
use deno_npm::NpmPackageId;
|
||||||
use deno_semver::package::PackageReq;
|
use deno_semver::package::PackageReq;
|
||||||
|
use indexmap::IndexMap;
|
||||||
|
|
||||||
use crate::standalone::virtual_fs::VirtualDirectory;
|
use crate::standalone::virtual_fs::VirtualDirectory;
|
||||||
|
|
||||||
use super::binary::Metadata;
|
use super::binary::Metadata;
|
||||||
use super::virtual_fs::BuiltVfs;
|
use super::virtual_fs::BuiltVfs;
|
||||||
use super::virtual_fs::VfsBuilder;
|
use super::virtual_fs::VfsBuilder;
|
||||||
|
use super::virtual_fs::VirtualDirectoryEntries;
|
||||||
|
|
||||||
const MAGIC_BYTES: &[u8; 8] = b"d3n0l4nd";
|
const MAGIC_BYTES: &[u8; 8] = b"d3n0l4nd";
|
||||||
|
|
||||||
|
@ -33,21 +36,22 @@ const MAGIC_BYTES: &[u8; 8] = b"d3n0l4nd";
|
||||||
/// * d3n0l4nd
|
/// * d3n0l4nd
|
||||||
/// * <metadata_len><metadata>
|
/// * <metadata_len><metadata>
|
||||||
/// * <npm_snapshot_len><npm_snapshot>
|
/// * <npm_snapshot_len><npm_snapshot>
|
||||||
/// * <remote_modules_len><remote_modules>
|
/// * <remote_modules>
|
||||||
/// * <vfs_headers_len><vfs_headers>
|
/// * <vfs_headers_len><vfs_headers>
|
||||||
/// * <vfs_file_data_len><vfs_file_data>
|
/// * <vfs_file_data_len><vfs_file_data>
|
||||||
|
/// * <source_map_data>
|
||||||
/// * d3n0l4nd
|
/// * d3n0l4nd
|
||||||
pub fn serialize_binary_data_section(
|
pub fn serialize_binary_data_section(
|
||||||
metadata: &Metadata,
|
metadata: &Metadata,
|
||||||
npm_snapshot: Option<SerializedNpmResolutionSnapshot>,
|
npm_snapshot: Option<SerializedNpmResolutionSnapshot>,
|
||||||
remote_modules: &RemoteModulesStoreBuilder,
|
remote_modules: &RemoteModulesStoreBuilder,
|
||||||
|
source_map_store: &SourceMapStore,
|
||||||
vfs: &BuiltVfs,
|
vfs: &BuiltVfs,
|
||||||
) -> Result<Vec<u8>, AnyError> {
|
) -> Result<Vec<u8>, AnyError> {
|
||||||
let metadata = serde_json::to_string(metadata)?;
|
let metadata = serde_json::to_string(metadata)?;
|
||||||
let npm_snapshot =
|
let npm_snapshot =
|
||||||
npm_snapshot.map(serialize_npm_snapshot).unwrap_or_default();
|
npm_snapshot.map(serialize_npm_snapshot).unwrap_or_default();
|
||||||
let remote_modules_len = Cell::new(0_u64);
|
let serialized_vfs = serde_json::to_string(&vfs.entries)?;
|
||||||
let serialized_vfs = serde_json::to_string(&vfs.root)?;
|
|
||||||
|
|
||||||
let bytes = capacity_builder::BytesBuilder::build(|builder| {
|
let bytes = capacity_builder::BytesBuilder::build(|builder| {
|
||||||
builder.append(MAGIC_BYTES);
|
builder.append(MAGIC_BYTES);
|
||||||
|
@ -63,10 +67,7 @@ pub fn serialize_binary_data_section(
|
||||||
}
|
}
|
||||||
// 3. Remote modules
|
// 3. Remote modules
|
||||||
{
|
{
|
||||||
builder.append_le(remote_modules_len.get()); // this will be properly initialized on the second pass
|
|
||||||
let start_index = builder.len();
|
|
||||||
remote_modules.write(builder);
|
remote_modules.write(builder);
|
||||||
remote_modules_len.set((builder.len() - start_index) as u64);
|
|
||||||
}
|
}
|
||||||
// 4. VFS
|
// 4. VFS
|
||||||
{
|
{
|
||||||
|
@ -78,6 +79,16 @@ pub fn serialize_binary_data_section(
|
||||||
builder.append(file);
|
builder.append(file);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// 5. Source maps
|
||||||
|
{
|
||||||
|
builder.append_le(source_map_store.data.len() as u32);
|
||||||
|
for (specifier, source_map) in &source_map_store.data {
|
||||||
|
builder.append_le(specifier.len() as u32);
|
||||||
|
builder.append(specifier);
|
||||||
|
builder.append_le(source_map.len() as u32);
|
||||||
|
builder.append(source_map);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// write the magic bytes at the end so we can use it
|
// write the magic bytes at the end so we can use it
|
||||||
// to make sure we've deserialized correctly
|
// to make sure we've deserialized correctly
|
||||||
|
@ -91,19 +102,14 @@ pub struct DeserializedDataSection {
|
||||||
pub metadata: Metadata,
|
pub metadata: Metadata,
|
||||||
pub npm_snapshot: Option<ValidSerializedNpmResolutionSnapshot>,
|
pub npm_snapshot: Option<ValidSerializedNpmResolutionSnapshot>,
|
||||||
pub remote_modules: RemoteModulesStore,
|
pub remote_modules: RemoteModulesStore,
|
||||||
pub vfs_dir: VirtualDirectory,
|
pub source_maps: SourceMapStore,
|
||||||
|
pub vfs_root_entries: VirtualDirectoryEntries,
|
||||||
pub vfs_files_data: &'static [u8],
|
pub vfs_files_data: &'static [u8],
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn deserialize_binary_data_section(
|
pub fn deserialize_binary_data_section(
|
||||||
data: &'static [u8],
|
data: &'static [u8],
|
||||||
) -> Result<Option<DeserializedDataSection>, AnyError> {
|
) -> Result<Option<DeserializedDataSection>, AnyError> {
|
||||||
fn read_bytes_with_len(input: &[u8]) -> Result<(&[u8], &[u8]), AnyError> {
|
|
||||||
let (input, len) = read_u64(input)?;
|
|
||||||
let (input, data) = read_bytes(input, len as usize)?;
|
|
||||||
Ok((input, data))
|
|
||||||
}
|
|
||||||
|
|
||||||
fn read_magic_bytes(input: &[u8]) -> Result<(&[u8], bool), AnyError> {
|
fn read_magic_bytes(input: &[u8]) -> Result<(&[u8], bool), AnyError> {
|
||||||
if input.len() < MAGIC_BYTES.len() {
|
if input.len() < MAGIC_BYTES.len() {
|
||||||
bail!("Unexpected end of data. Could not find magic bytes.");
|
bail!("Unexpected end of data. Could not find magic bytes.");
|
||||||
|
@ -115,34 +121,51 @@ pub fn deserialize_binary_data_section(
|
||||||
Ok((input, true))
|
Ok((input, true))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[allow(clippy::type_complexity)]
|
||||||
|
fn read_source_map_entry(
|
||||||
|
input: &[u8],
|
||||||
|
) -> Result<(&[u8], (Cow<str>, Cow<str>)), AnyError> {
|
||||||
|
let (input, specifier) = read_string_lossy(input)?;
|
||||||
|
let (input, source_map) = read_string_lossy(input)?;
|
||||||
|
Ok((input, (specifier, source_map)))
|
||||||
|
}
|
||||||
|
|
||||||
let (input, found) = read_magic_bytes(data)?;
|
let (input, found) = read_magic_bytes(data)?;
|
||||||
if !found {
|
if !found {
|
||||||
return Ok(None);
|
return Ok(None);
|
||||||
}
|
}
|
||||||
|
|
||||||
// 1. Metadata
|
// 1. Metadata
|
||||||
let (input, data) = read_bytes_with_len(input).context("reading metadata")?;
|
let (input, data) =
|
||||||
|
read_bytes_with_u64_len(input).context("reading metadata")?;
|
||||||
let metadata: Metadata =
|
let metadata: Metadata =
|
||||||
serde_json::from_slice(data).context("deserializing metadata")?;
|
serde_json::from_slice(data).context("deserializing metadata")?;
|
||||||
// 2. Npm snapshot
|
// 2. Npm snapshot
|
||||||
let (input, data) =
|
let (input, data) =
|
||||||
read_bytes_with_len(input).context("reading npm snapshot")?;
|
read_bytes_with_u64_len(input).context("reading npm snapshot")?;
|
||||||
let npm_snapshot = if data.is_empty() {
|
let npm_snapshot = if data.is_empty() {
|
||||||
None
|
None
|
||||||
} else {
|
} else {
|
||||||
Some(deserialize_npm_snapshot(data).context("deserializing npm snapshot")?)
|
Some(deserialize_npm_snapshot(data).context("deserializing npm snapshot")?)
|
||||||
};
|
};
|
||||||
// 3. Remote modules
|
// 3. Remote modules
|
||||||
let (input, data) =
|
let (input, remote_modules) =
|
||||||
read_bytes_with_len(input).context("reading remote modules data")?;
|
RemoteModulesStore::build(input).context("deserializing remote modules")?;
|
||||||
let remote_modules =
|
|
||||||
RemoteModulesStore::build(data).context("deserializing remote modules")?;
|
|
||||||
// 4. VFS
|
// 4. VFS
|
||||||
let (input, data) = read_bytes_with_len(input).context("vfs")?;
|
let (input, data) = read_bytes_with_u64_len(input).context("vfs")?;
|
||||||
let vfs_dir: VirtualDirectory =
|
let vfs_root_entries: VirtualDirectoryEntries =
|
||||||
serde_json::from_slice(data).context("deserializing vfs data")?;
|
serde_json::from_slice(data).context("deserializing vfs data")?;
|
||||||
let (input, vfs_files_data) =
|
let (input, vfs_files_data) =
|
||||||
read_bytes_with_len(input).context("reading vfs files data")?;
|
read_bytes_with_u64_len(input).context("reading vfs files data")?;
|
||||||
|
// 5. Source maps
|
||||||
|
let (mut input, source_map_data_len) = read_u32_as_usize(input)?;
|
||||||
|
let mut source_maps = SourceMapStore::with_capacity(source_map_data_len);
|
||||||
|
for _ in 0..source_map_data_len {
|
||||||
|
let (current_input, (specifier, source_map)) =
|
||||||
|
read_source_map_entry(input)?;
|
||||||
|
input = current_input;
|
||||||
|
source_maps.add(specifier, source_map);
|
||||||
|
}
|
||||||
|
|
||||||
// finally ensure we read the magic bytes at the end
|
// finally ensure we read the magic bytes at the end
|
||||||
let (_input, found) = read_magic_bytes(input)?;
|
let (_input, found) = read_magic_bytes(input)?;
|
||||||
|
@ -154,7 +177,8 @@ pub fn deserialize_binary_data_section(
|
||||||
metadata,
|
metadata,
|
||||||
npm_snapshot,
|
npm_snapshot,
|
||||||
remote_modules,
|
remote_modules,
|
||||||
vfs_dir,
|
source_maps,
|
||||||
|
vfs_root_entries,
|
||||||
vfs_files_data,
|
vfs_files_data,
|
||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
|
@ -162,19 +186,31 @@ pub fn deserialize_binary_data_section(
|
||||||
#[derive(Default)]
|
#[derive(Default)]
|
||||||
pub struct RemoteModulesStoreBuilder {
|
pub struct RemoteModulesStoreBuilder {
|
||||||
specifiers: Vec<(String, u64)>,
|
specifiers: Vec<(String, u64)>,
|
||||||
data: Vec<(MediaType, Vec<u8>)>,
|
data: Vec<(MediaType, Vec<u8>, Option<Vec<u8>>)>,
|
||||||
data_byte_len: u64,
|
data_byte_len: u64,
|
||||||
redirects: Vec<(String, String)>,
|
redirects: Vec<(String, String)>,
|
||||||
redirects_len: u64,
|
redirects_len: u64,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl RemoteModulesStoreBuilder {
|
impl RemoteModulesStoreBuilder {
|
||||||
pub fn add(&mut self, specifier: &Url, media_type: MediaType, data: Vec<u8>) {
|
pub fn add(
|
||||||
|
&mut self,
|
||||||
|
specifier: &Url,
|
||||||
|
media_type: MediaType,
|
||||||
|
data: Vec<u8>,
|
||||||
|
maybe_transpiled: Option<Vec<u8>>,
|
||||||
|
) {
|
||||||
log::debug!("Adding '{}' ({})", specifier, media_type);
|
log::debug!("Adding '{}' ({})", specifier, media_type);
|
||||||
let specifier = specifier.to_string();
|
let specifier = specifier.to_string();
|
||||||
self.specifiers.push((specifier, self.data_byte_len));
|
self.specifiers.push((specifier, self.data_byte_len));
|
||||||
self.data_byte_len += 1 + 8 + data.len() as u64; // media type (1 byte), data length (8 bytes), data
|
let maybe_transpiled_len = match &maybe_transpiled {
|
||||||
self.data.push((media_type, data));
|
// data length (4 bytes), data
|
||||||
|
Some(data) => 4 + data.len() as u64,
|
||||||
|
None => 0,
|
||||||
|
};
|
||||||
|
// media type (1 byte), data length (4 bytes), data, has transpiled (1 byte), transpiled length
|
||||||
|
self.data_byte_len += 1 + 4 + data.len() as u64 + 1 + maybe_transpiled_len;
|
||||||
|
self.data.push((media_type, data, maybe_transpiled));
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn add_redirects(&mut self, redirects: &BTreeMap<Url, Url>) {
|
pub fn add_redirects(&mut self, redirects: &BTreeMap<Url, Url>) {
|
||||||
|
@ -193,7 +229,7 @@ impl RemoteModulesStoreBuilder {
|
||||||
builder.append_le(self.redirects.len() as u32);
|
builder.append_le(self.redirects.len() as u32);
|
||||||
for (specifier, offset) in &self.specifiers {
|
for (specifier, offset) in &self.specifiers {
|
||||||
builder.append_le(specifier.len() as u32);
|
builder.append_le(specifier.len() as u32);
|
||||||
builder.append(specifier.as_bytes());
|
builder.append(specifier);
|
||||||
builder.append_le(*offset);
|
builder.append_le(*offset);
|
||||||
}
|
}
|
||||||
for (from, to) in &self.redirects {
|
for (from, to) in &self.redirects {
|
||||||
|
@ -202,10 +238,32 @@ impl RemoteModulesStoreBuilder {
|
||||||
builder.append_le(to.len() as u32);
|
builder.append_le(to.len() as u32);
|
||||||
builder.append(to);
|
builder.append(to);
|
||||||
}
|
}
|
||||||
for (media_type, data) in &self.data {
|
builder.append_le(
|
||||||
|
self
|
||||||
|
.data
|
||||||
|
.iter()
|
||||||
|
.map(|(_, data, maybe_transpiled)| {
|
||||||
|
1 + 4
|
||||||
|
+ (data.len() as u64)
|
||||||
|
+ 1
|
||||||
|
+ match maybe_transpiled {
|
||||||
|
Some(transpiled) => 4 + (transpiled.len() as u64),
|
||||||
|
None => 0,
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.sum::<u64>(),
|
||||||
|
);
|
||||||
|
for (media_type, data, maybe_transpiled) in &self.data {
|
||||||
builder.append(serialize_media_type(*media_type));
|
builder.append(serialize_media_type(*media_type));
|
||||||
builder.append_le(data.len() as u64);
|
builder.append_le(data.len() as u32);
|
||||||
builder.append(data);
|
builder.append(data);
|
||||||
|
if let Some(transpiled) = maybe_transpiled {
|
||||||
|
builder.append(1);
|
||||||
|
builder.append_le(transpiled.len() as u32);
|
||||||
|
builder.append(transpiled);
|
||||||
|
} else {
|
||||||
|
builder.append(0);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -234,6 +292,30 @@ impl DenoCompileModuleSource {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub struct SourceMapStore {
|
||||||
|
data: IndexMap<Cow<'static, str>, Cow<'static, str>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl SourceMapStore {
|
||||||
|
pub fn with_capacity(capacity: usize) -> Self {
|
||||||
|
Self {
|
||||||
|
data: IndexMap::with_capacity(capacity),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn add(
|
||||||
|
&mut self,
|
||||||
|
specifier: Cow<'static, str>,
|
||||||
|
source_map: Cow<'static, str>,
|
||||||
|
) {
|
||||||
|
self.data.insert(specifier, source_map);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get(&self, specifier: &str) -> Option<&Cow<'static, str>> {
|
||||||
|
self.data.get(specifier)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub struct DenoCompileModuleData<'a> {
|
pub struct DenoCompileModuleData<'a> {
|
||||||
pub specifier: &'a Url,
|
pub specifier: &'a Url,
|
||||||
pub media_type: MediaType,
|
pub media_type: MediaType,
|
||||||
|
@ -280,6 +362,13 @@ impl<'a> DenoCompileModuleData<'a> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub struct RemoteModuleEntry<'a> {
|
||||||
|
pub specifier: &'a Url,
|
||||||
|
pub media_type: MediaType,
|
||||||
|
pub data: Cow<'static, [u8]>,
|
||||||
|
pub transpiled_data: Option<Cow<'static, [u8]>>,
|
||||||
|
}
|
||||||
|
|
||||||
enum RemoteModulesStoreSpecifierValue {
|
enum RemoteModulesStoreSpecifierValue {
|
||||||
Data(usize),
|
Data(usize),
|
||||||
Redirect(Url),
|
Redirect(Url),
|
||||||
|
@ -291,7 +380,7 @@ pub struct RemoteModulesStore {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl RemoteModulesStore {
|
impl RemoteModulesStore {
|
||||||
fn build(data: &'static [u8]) -> Result<Self, AnyError> {
|
fn build(input: &'static [u8]) -> Result<(&'static [u8], Self), AnyError> {
|
||||||
fn read_specifier(input: &[u8]) -> Result<(&[u8], (Url, u64)), AnyError> {
|
fn read_specifier(input: &[u8]) -> Result<(&[u8], (Url, u64)), AnyError> {
|
||||||
let (input, specifier) = read_string_lossy(input)?;
|
let (input, specifier) = read_string_lossy(input)?;
|
||||||
let specifier = Url::parse(&specifier)?;
|
let specifier = Url::parse(&specifier)?;
|
||||||
|
@ -334,12 +423,16 @@ impl RemoteModulesStore {
|
||||||
Ok((input, specifiers))
|
Ok((input, specifiers))
|
||||||
}
|
}
|
||||||
|
|
||||||
let (files_data, specifiers) = read_headers(data)?;
|
let (input, specifiers) = read_headers(input)?;
|
||||||
|
let (input, files_data) = read_bytes_with_u64_len(input)?;
|
||||||
|
|
||||||
Ok(Self {
|
Ok((
|
||||||
specifiers,
|
input,
|
||||||
files_data,
|
Self {
|
||||||
})
|
specifiers,
|
||||||
|
files_data,
|
||||||
|
},
|
||||||
|
))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn resolve_specifier<'a>(
|
pub fn resolve_specifier<'a>(
|
||||||
|
@ -370,7 +463,7 @@ impl RemoteModulesStore {
|
||||||
pub fn read<'a>(
|
pub fn read<'a>(
|
||||||
&'a self,
|
&'a self,
|
||||||
original_specifier: &'a Url,
|
original_specifier: &'a Url,
|
||||||
) -> Result<Option<DenoCompileModuleData<'a>>, AnyError> {
|
) -> Result<Option<RemoteModuleEntry<'a>>, AnyError> {
|
||||||
let mut count = 0;
|
let mut count = 0;
|
||||||
let mut specifier = original_specifier;
|
let mut specifier = original_specifier;
|
||||||
loop {
|
loop {
|
||||||
|
@ -386,12 +479,25 @@ impl RemoteModulesStore {
|
||||||
let input = &self.files_data[*offset..];
|
let input = &self.files_data[*offset..];
|
||||||
let (input, media_type_byte) = read_bytes(input, 1)?;
|
let (input, media_type_byte) = read_bytes(input, 1)?;
|
||||||
let media_type = deserialize_media_type(media_type_byte[0])?;
|
let media_type = deserialize_media_type(media_type_byte[0])?;
|
||||||
let (input, len) = read_u64(input)?;
|
let (input, data) = read_bytes_with_u32_len(input)?;
|
||||||
let (_input, data) = read_bytes(input, len as usize)?;
|
check_has_len(input, 1)?;
|
||||||
return Ok(Some(DenoCompileModuleData {
|
let (input, has_transpiled) = (&input[1..], input[0]);
|
||||||
|
let (_, transpiled_data) = match has_transpiled {
|
||||||
|
0 => (input, None),
|
||||||
|
1 => {
|
||||||
|
let (input, data) = read_bytes_with_u32_len(input)?;
|
||||||
|
(input, Some(data))
|
||||||
|
}
|
||||||
|
value => bail!(
|
||||||
|
"Invalid transpiled data flag: {}. Compiled data is corrupt.",
|
||||||
|
value
|
||||||
|
),
|
||||||
|
};
|
||||||
|
return Ok(Some(RemoteModuleEntry {
|
||||||
specifier,
|
specifier,
|
||||||
media_type,
|
media_type,
|
||||||
data: Cow::Borrowed(data),
|
data: Cow::Borrowed(data),
|
||||||
|
transpiled_data: transpiled_data.map(Cow::Borrowed),
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
None => {
|
None => {
|
||||||
|
@ -630,14 +736,32 @@ fn parse_vec_n_times_with_index<TResult>(
|
||||||
Ok((input, results))
|
Ok((input, results))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn read_bytes_with_u64_len(input: &[u8]) -> Result<(&[u8], &[u8]), AnyError> {
|
||||||
|
let (input, len) = read_u64(input)?;
|
||||||
|
let (input, data) = read_bytes(input, len as usize)?;
|
||||||
|
Ok((input, data))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn read_bytes_with_u32_len(input: &[u8]) -> Result<(&[u8], &[u8]), AnyError> {
|
||||||
|
let (input, len) = read_u32_as_usize(input)?;
|
||||||
|
let (input, data) = read_bytes(input, len)?;
|
||||||
|
Ok((input, data))
|
||||||
|
}
|
||||||
|
|
||||||
fn read_bytes(input: &[u8], len: usize) -> Result<(&[u8], &[u8]), AnyError> {
|
fn read_bytes(input: &[u8], len: usize) -> Result<(&[u8], &[u8]), AnyError> {
|
||||||
if input.len() < len {
|
check_has_len(input, len)?;
|
||||||
bail!("Unexpected end of data.",);
|
|
||||||
}
|
|
||||||
let (len_bytes, input) = input.split_at(len);
|
let (len_bytes, input) = input.split_at(len);
|
||||||
Ok((input, len_bytes))
|
Ok((input, len_bytes))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
|
fn check_has_len(input: &[u8], len: usize) -> Result<(), AnyError> {
|
||||||
|
if input.len() < len {
|
||||||
|
bail!("Unexpected end of data.");
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
fn read_string_lossy(input: &[u8]) -> Result<(&[u8], Cow<str>), AnyError> {
|
fn read_string_lossy(input: &[u8]) -> Result<(&[u8], Cow<str>), AnyError> {
|
||||||
let (input, str_len) = read_u32_as_usize(input)?;
|
let (input, str_len) = read_u32_as_usize(input)?;
|
||||||
let (input, data_bytes) = read_bytes(input, str_len)?;
|
let (input, data_bytes) = read_bytes(input, str_len)?;
|
||||||
|
|
|
@ -67,7 +67,7 @@ impl WindowsSystemRootablePath {
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct BuiltVfs {
|
pub struct BuiltVfs {
|
||||||
pub root_path: WindowsSystemRootablePath,
|
pub root_path: WindowsSystemRootablePath,
|
||||||
pub root: VirtualDirectory,
|
pub entries: VirtualDirectoryEntries,
|
||||||
pub files: Vec<Vec<u8>>,
|
pub files: Vec<Vec<u8>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -95,7 +95,7 @@ impl VfsBuilder {
|
||||||
Self {
|
Self {
|
||||||
executable_root: VirtualDirectory {
|
executable_root: VirtualDirectory {
|
||||||
name: "/".to_string(),
|
name: "/".to_string(),
|
||||||
entries: Vec::new(),
|
entries: Default::default(),
|
||||||
},
|
},
|
||||||
files: Vec::new(),
|
files: Vec::new(),
|
||||||
current_offset: 0,
|
current_offset: 0,
|
||||||
|
@ -208,23 +208,20 @@ impl VfsBuilder {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
let name = component.as_os_str().to_string_lossy();
|
let name = component.as_os_str().to_string_lossy();
|
||||||
let index = match current_dir
|
let index = match current_dir.entries.binary_search(&name) {
|
||||||
.entries
|
|
||||||
.binary_search_by(|e| e.name().cmp(&name))
|
|
||||||
{
|
|
||||||
Ok(index) => index,
|
Ok(index) => index,
|
||||||
Err(insert_index) => {
|
Err(insert_index) => {
|
||||||
current_dir.entries.insert(
|
current_dir.entries.0.insert(
|
||||||
insert_index,
|
insert_index,
|
||||||
VfsEntry::Dir(VirtualDirectory {
|
VfsEntry::Dir(VirtualDirectory {
|
||||||
name: name.to_string(),
|
name: name.to_string(),
|
||||||
entries: Vec::new(),
|
entries: Default::default(),
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
insert_index
|
insert_index
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
match &mut current_dir.entries[index] {
|
match &mut current_dir.entries.0[index] {
|
||||||
VfsEntry::Dir(dir) => {
|
VfsEntry::Dir(dir) => {
|
||||||
current_dir = dir;
|
current_dir = dir;
|
||||||
}
|
}
|
||||||
|
@ -248,14 +245,8 @@ impl VfsBuilder {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
let name = component.as_os_str().to_string_lossy();
|
let name = component.as_os_str().to_string_lossy();
|
||||||
let index = match current_dir
|
let entry = current_dir.entries.get_mut_by_name(&name)?;
|
||||||
.entries
|
match entry {
|
||||||
.binary_search_by(|e| e.name().cmp(&name))
|
|
||||||
{
|
|
||||||
Ok(index) => index,
|
|
||||||
Err(_) => return None,
|
|
||||||
};
|
|
||||||
match &mut current_dir.entries[index] {
|
|
||||||
VfsEntry::Dir(dir) => {
|
VfsEntry::Dir(dir) => {
|
||||||
current_dir = dir;
|
current_dir = dir;
|
||||||
}
|
}
|
||||||
|
@ -320,9 +311,9 @@ impl VfsBuilder {
|
||||||
offset,
|
offset,
|
||||||
len: data.len() as u64,
|
len: data.len() as u64,
|
||||||
};
|
};
|
||||||
match dir.entries.binary_search_by(|e| e.name().cmp(&name)) {
|
match dir.entries.binary_search(&name) {
|
||||||
Ok(index) => {
|
Ok(index) => {
|
||||||
let entry = &mut dir.entries[index];
|
let entry = &mut dir.entries.0[index];
|
||||||
match entry {
|
match entry {
|
||||||
VfsEntry::File(virtual_file) => match sub_data_kind {
|
VfsEntry::File(virtual_file) => match sub_data_kind {
|
||||||
VfsFileSubDataKind::Raw => {
|
VfsFileSubDataKind::Raw => {
|
||||||
|
@ -336,7 +327,7 @@ impl VfsBuilder {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Err(insert_index) => {
|
Err(insert_index) => {
|
||||||
dir.entries.insert(
|
dir.entries.0.insert(
|
||||||
insert_index,
|
insert_index,
|
||||||
VfsEntry::File(VirtualFile {
|
VfsEntry::File(VirtualFile {
|
||||||
name: name.to_string(),
|
name: name.to_string(),
|
||||||
|
@ -384,10 +375,10 @@ impl VfsBuilder {
|
||||||
let target = normalize_path(path.parent().unwrap().join(&target));
|
let target = normalize_path(path.parent().unwrap().join(&target));
|
||||||
let dir = self.add_dir_raw(path.parent().unwrap());
|
let dir = self.add_dir_raw(path.parent().unwrap());
|
||||||
let name = path.file_name().unwrap().to_string_lossy();
|
let name = path.file_name().unwrap().to_string_lossy();
|
||||||
match dir.entries.binary_search_by(|e| e.name().cmp(&name)) {
|
match dir.entries.binary_search(&name) {
|
||||||
Ok(_) => {} // previously inserted
|
Ok(_) => {} // previously inserted
|
||||||
Err(insert_index) => {
|
Err(insert_index) => {
|
||||||
dir.entries.insert(
|
dir.entries.0.insert(
|
||||||
insert_index,
|
insert_index,
|
||||||
VfsEntry::Symlink(VirtualSymlink {
|
VfsEntry::Symlink(VirtualSymlink {
|
||||||
name: name.to_string(),
|
name: name.to_string(),
|
||||||
|
@ -426,7 +417,7 @@ impl VfsBuilder {
|
||||||
dir: &mut VirtualDirectory,
|
dir: &mut VirtualDirectory,
|
||||||
parts: &[String],
|
parts: &[String],
|
||||||
) {
|
) {
|
||||||
for entry in &mut dir.entries {
|
for entry in &mut dir.entries.0 {
|
||||||
match entry {
|
match entry {
|
||||||
VfsEntry::Dir(dir) => {
|
VfsEntry::Dir(dir) => {
|
||||||
strip_prefix_from_symlinks(dir, parts);
|
strip_prefix_from_symlinks(dir, parts);
|
||||||
|
@ -454,13 +445,13 @@ impl VfsBuilder {
|
||||||
if self.min_root_dir.as_ref() == Some(¤t_path) {
|
if self.min_root_dir.as_ref() == Some(¤t_path) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
match ¤t_dir.entries[0] {
|
match ¤t_dir.entries.0[0] {
|
||||||
VfsEntry::Dir(dir) => {
|
VfsEntry::Dir(dir) => {
|
||||||
if dir.name == DENO_COMPILE_GLOBAL_NODE_MODULES_DIR_NAME {
|
if dir.name == DENO_COMPILE_GLOBAL_NODE_MODULES_DIR_NAME {
|
||||||
// special directory we want to maintain
|
// special directory we want to maintain
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
match current_dir.entries.remove(0) {
|
match current_dir.entries.0.remove(0) {
|
||||||
VfsEntry::Dir(dir) => {
|
VfsEntry::Dir(dir) => {
|
||||||
current_path =
|
current_path =
|
||||||
WindowsSystemRootablePath::Path(current_path.join(&dir.name));
|
WindowsSystemRootablePath::Path(current_path.join(&dir.name));
|
||||||
|
@ -480,7 +471,7 @@ impl VfsBuilder {
|
||||||
}
|
}
|
||||||
BuiltVfs {
|
BuiltVfs {
|
||||||
root_path: current_path,
|
root_path: current_path,
|
||||||
root: current_dir,
|
entries: current_dir.entries,
|
||||||
files: self.files,
|
files: self.files,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -506,7 +497,7 @@ pub fn output_vfs(vfs: &BuiltVfs, executable_name: &str) {
|
||||||
return; // no need to compute if won't output
|
return; // no need to compute if won't output
|
||||||
}
|
}
|
||||||
|
|
||||||
if vfs.root.entries.is_empty() {
|
if vfs.entries.is_empty() {
|
||||||
return; // nothing to output
|
return; // nothing to output
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -696,7 +687,7 @@ fn vfs_as_display_tree(
|
||||||
|
|
||||||
fn dir_size(dir: &VirtualDirectory, seen_offsets: &mut HashSet<u64>) -> Size {
|
fn dir_size(dir: &VirtualDirectory, seen_offsets: &mut HashSet<u64>) -> Size {
|
||||||
let mut size = Size::default();
|
let mut size = Size::default();
|
||||||
for entry in &dir.entries {
|
for entry in dir.entries.iter() {
|
||||||
match entry {
|
match entry {
|
||||||
VfsEntry::Dir(virtual_directory) => {
|
VfsEntry::Dir(virtual_directory) => {
|
||||||
size = size + dir_size(virtual_directory, seen_offsets);
|
size = size + dir_size(virtual_directory, seen_offsets);
|
||||||
|
@ -760,15 +751,10 @@ fn vfs_as_display_tree(
|
||||||
|
|
||||||
fn include_all_entries<'a>(
|
fn include_all_entries<'a>(
|
||||||
dir_path: &WindowsSystemRootablePath,
|
dir_path: &WindowsSystemRootablePath,
|
||||||
vfs_dir: &'a VirtualDirectory,
|
entries: &'a VirtualDirectoryEntries,
|
||||||
seen_offsets: &mut HashSet<u64>,
|
seen_offsets: &mut HashSet<u64>,
|
||||||
) -> Vec<DirEntryOutput<'a>> {
|
) -> Vec<DirEntryOutput<'a>> {
|
||||||
if vfs_dir.name == DENO_COMPILE_GLOBAL_NODE_MODULES_DIR_NAME {
|
entries
|
||||||
return show_global_node_modules_dir(vfs_dir, seen_offsets);
|
|
||||||
}
|
|
||||||
|
|
||||||
vfs_dir
|
|
||||||
.entries
|
|
||||||
.iter()
|
.iter()
|
||||||
.map(|entry| DirEntryOutput {
|
.map(|entry| DirEntryOutput {
|
||||||
name: Cow::Borrowed(entry.name()),
|
name: Cow::Borrowed(entry.name()),
|
||||||
|
@ -826,10 +812,12 @@ fn vfs_as_display_tree(
|
||||||
} else {
|
} else {
|
||||||
EntryOutput::Subset(children)
|
EntryOutput::Subset(children)
|
||||||
}
|
}
|
||||||
|
} else if vfs_dir.name == DENO_COMPILE_GLOBAL_NODE_MODULES_DIR_NAME {
|
||||||
|
EntryOutput::Subset(show_global_node_modules_dir(vfs_dir, seen_offsets))
|
||||||
} else {
|
} else {
|
||||||
EntryOutput::Subset(include_all_entries(
|
EntryOutput::Subset(include_all_entries(
|
||||||
&WindowsSystemRootablePath::Path(dir),
|
&WindowsSystemRootablePath::Path(dir),
|
||||||
vfs_dir,
|
&vfs_dir.entries,
|
||||||
seen_offsets,
|
seen_offsets,
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
|
@ -839,7 +827,7 @@ fn vfs_as_display_tree(
|
||||||
// user might not have context about what's being shown
|
// user might not have context about what's being shown
|
||||||
let mut seen_offsets = HashSet::with_capacity(vfs.files.len());
|
let mut seen_offsets = HashSet::with_capacity(vfs.files.len());
|
||||||
let mut child_entries =
|
let mut child_entries =
|
||||||
include_all_entries(&vfs.root_path, &vfs.root, &mut seen_offsets);
|
include_all_entries(&vfs.root_path, &vfs.entries, &mut seen_offsets);
|
||||||
for child_entry in &mut child_entries {
|
for child_entry in &mut child_entries {
|
||||||
child_entry.collapse_leaf_nodes();
|
child_entry.collapse_leaf_nodes();
|
||||||
}
|
}
|
||||||
|
@ -961,27 +949,70 @@ impl VfsEntry {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Default, Serialize, Deserialize)]
|
||||||
|
pub struct VirtualDirectoryEntries(Vec<VfsEntry>);
|
||||||
|
|
||||||
|
impl VirtualDirectoryEntries {
|
||||||
|
pub fn new(mut entries: Vec<VfsEntry>) -> Self {
|
||||||
|
// needs to be sorted by name
|
||||||
|
entries.sort_by(|a, b| a.name().cmp(b.name()));
|
||||||
|
Self(entries)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn take_inner(&mut self) -> Vec<VfsEntry> {
|
||||||
|
std::mem::take(&mut self.0)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn is_empty(&self) -> bool {
|
||||||
|
self.0.is_empty()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn len(&self) -> usize {
|
||||||
|
self.0.len()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_by_name(&self, name: &str) -> Option<&VfsEntry> {
|
||||||
|
self.binary_search(name).ok().map(|index| &self.0[index])
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_mut_by_name(&mut self, name: &str) -> Option<&mut VfsEntry> {
|
||||||
|
self
|
||||||
|
.binary_search(name)
|
||||||
|
.ok()
|
||||||
|
.map(|index| &mut self.0[index])
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn binary_search(&self, name: &str) -> Result<usize, usize> {
|
||||||
|
self.0.binary_search_by(|e| e.name().cmp(name))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn insert(&mut self, entry: VfsEntry) {
|
||||||
|
match self.binary_search(entry.name()) {
|
||||||
|
Ok(index) => {
|
||||||
|
self.0[index] = entry;
|
||||||
|
}
|
||||||
|
Err(insert_index) => {
|
||||||
|
self.0.insert(insert_index, entry);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn remove(&mut self, index: usize) -> VfsEntry {
|
||||||
|
self.0.remove(index)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn iter(&self) -> std::slice::Iter<'_, VfsEntry> {
|
||||||
|
self.0.iter()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Debug, Serialize, Deserialize)]
|
#[derive(Debug, Serialize, Deserialize)]
|
||||||
pub struct VirtualDirectory {
|
pub struct VirtualDirectory {
|
||||||
#[serde(rename = "n")]
|
#[serde(rename = "n")]
|
||||||
pub name: String,
|
pub name: String,
|
||||||
// should be sorted by name
|
// should be sorted by name
|
||||||
#[serde(rename = "e")]
|
#[serde(rename = "e")]
|
||||||
pub entries: Vec<VfsEntry>,
|
pub entries: VirtualDirectoryEntries,
|
||||||
}
|
|
||||||
|
|
||||||
impl VirtualDirectory {
|
|
||||||
pub fn insert_entry(&mut self, entry: VfsEntry) {
|
|
||||||
let name = entry.name();
|
|
||||||
match self.entries.binary_search_by(|e| e.name().cmp(name)) {
|
|
||||||
Ok(index) => {
|
|
||||||
self.entries[index] = entry;
|
|
||||||
}
|
|
||||||
Err(insert_index) => {
|
|
||||||
self.entries.insert(insert_index, entry);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy, Serialize, Deserialize)]
|
#[derive(Debug, Clone, Copy, Serialize, Deserialize)]
|
||||||
|
@ -1136,20 +1167,13 @@ impl VfsRoot {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
let component = component.to_string_lossy();
|
let component = component.to_string_lossy();
|
||||||
match current_dir
|
current_entry = current_dir
|
||||||
.entries
|
.entries
|
||||||
.binary_search_by(|e| e.name().cmp(&component))
|
.get_by_name(&component)
|
||||||
{
|
.ok_or_else(|| {
|
||||||
Ok(index) => {
|
std::io::Error::new(std::io::ErrorKind::NotFound, "path not found")
|
||||||
current_entry = current_dir.entries[index].as_ref();
|
})?
|
||||||
}
|
.as_ref();
|
||||||
Err(_) => {
|
|
||||||
return Err(std::io::Error::new(
|
|
||||||
std::io::ErrorKind::NotFound,
|
|
||||||
"path not found",
|
|
||||||
));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok((final_path, current_entry))
|
Ok((final_path, current_entry))
|
||||||
|
@ -1706,7 +1730,10 @@ mod test {
|
||||||
FileBackedVfs::new(
|
FileBackedVfs::new(
|
||||||
Cow::Owned(data),
|
Cow::Owned(data),
|
||||||
VfsRoot {
|
VfsRoot {
|
||||||
dir: vfs.root,
|
dir: VirtualDirectory {
|
||||||
|
name: "".to_string(),
|
||||||
|
entries: vfs.entries,
|
||||||
|
},
|
||||||
root_path: dest_path.to_path_buf(),
|
root_path: dest_path.to_path_buf(),
|
||||||
start_file_offset: 0,
|
start_file_offset: 0,
|
||||||
},
|
},
|
||||||
|
|
|
@ -2,7 +2,6 @@
|
||||||
|
|
||||||
use deno_core::serde_json;
|
use deno_core::serde_json;
|
||||||
use test_util as util;
|
use test_util as util;
|
||||||
use util::assert_contains;
|
|
||||||
use util::assert_not_contains;
|
use util::assert_not_contains;
|
||||||
use util::testdata_path;
|
use util::testdata_path;
|
||||||
use util::TestContext;
|
use util::TestContext;
|
||||||
|
@ -90,78 +89,6 @@ fn standalone_args() {
|
||||||
.assert_exit_code(0);
|
.assert_exit_code(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn standalone_error() {
|
|
||||||
let context = TestContextBuilder::new().build();
|
|
||||||
let dir = context.temp_dir();
|
|
||||||
let exe = if cfg!(windows) {
|
|
||||||
dir.path().join("error.exe")
|
|
||||||
} else {
|
|
||||||
dir.path().join("error")
|
|
||||||
};
|
|
||||||
context
|
|
||||||
.new_command()
|
|
||||||
.args_vec([
|
|
||||||
"compile",
|
|
||||||
"--output",
|
|
||||||
&exe.to_string_lossy(),
|
|
||||||
"./compile/standalone_error.ts",
|
|
||||||
])
|
|
||||||
.run()
|
|
||||||
.skip_output_check()
|
|
||||||
.assert_exit_code(0);
|
|
||||||
|
|
||||||
let output = context.new_command().name(&exe).split_output().run();
|
|
||||||
output.assert_exit_code(1);
|
|
||||||
output.assert_stdout_matches_text("");
|
|
||||||
let stderr = output.stderr();
|
|
||||||
// On Windows, we cannot assert the file path (because '\').
|
|
||||||
// Instead we just check for relevant output.
|
|
||||||
assert_contains!(stderr, "error: Uncaught (in promise) Error: boom!");
|
|
||||||
assert_contains!(stderr, "\n at boom (file://");
|
|
||||||
assert_contains!(stderr, "standalone_error.ts:2:9");
|
|
||||||
assert_contains!(stderr, "at foo (file://");
|
|
||||||
assert_contains!(stderr, "standalone_error.ts:5:3");
|
|
||||||
assert_contains!(stderr, "standalone_error.ts:7:1");
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn standalone_error_module_with_imports() {
|
|
||||||
let context = TestContextBuilder::new().build();
|
|
||||||
let dir = context.temp_dir();
|
|
||||||
let exe = if cfg!(windows) {
|
|
||||||
dir.path().join("error.exe")
|
|
||||||
} else {
|
|
||||||
dir.path().join("error")
|
|
||||||
};
|
|
||||||
context
|
|
||||||
.new_command()
|
|
||||||
.args_vec([
|
|
||||||
"compile",
|
|
||||||
"--output",
|
|
||||||
&exe.to_string_lossy(),
|
|
||||||
"./compile/standalone_error_module_with_imports_1.ts",
|
|
||||||
])
|
|
||||||
.run()
|
|
||||||
.skip_output_check()
|
|
||||||
.assert_exit_code(0);
|
|
||||||
|
|
||||||
let output = context
|
|
||||||
.new_command()
|
|
||||||
.name(&exe)
|
|
||||||
.env("NO_COLOR", "1")
|
|
||||||
.split_output()
|
|
||||||
.run();
|
|
||||||
output.assert_stdout_matches_text("hello\n");
|
|
||||||
let stderr = output.stderr();
|
|
||||||
// On Windows, we cannot assert the file path (because '\').
|
|
||||||
// Instead we just check for relevant output.
|
|
||||||
assert_contains!(stderr, "error: Uncaught (in promise) Error: boom!");
|
|
||||||
assert_contains!(stderr, "\n at file://");
|
|
||||||
assert_contains!(stderr, "standalone_error_module_with_imports_2.ts:2:7");
|
|
||||||
output.assert_exit_code(1);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn standalone_load_datauri() {
|
fn standalone_load_datauri() {
|
||||||
let context = TestContextBuilder::new().build();
|
let context = TestContextBuilder::new().build();
|
||||||
|
|
|
@ -1,6 +1,9 @@
|
||||||
{
|
{
|
||||||
"tempDir": true,
|
"tempDir": true,
|
||||||
"steps": [{
|
"steps": [{
|
||||||
|
"args": "run -A cleanup.ts",
|
||||||
|
"output": "[WILDCARD]"
|
||||||
|
}, {
|
||||||
"if": "unix",
|
"if": "unix",
|
||||||
"args": "compile --output using_code_cache --log-level=debug main.ts",
|
"args": "compile --output using_code_cache --log-level=debug main.ts",
|
||||||
"output": "[WILDCARD]"
|
"output": "[WILDCARD]"
|
||||||
|
|
11
tests/specs/compile/code_cache/cleanup.ts
Normal file
11
tests/specs/compile/code_cache/cleanup.ts
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
import { tmpdir } from "node:os";
|
||||||
|
|
||||||
|
// cleanup the code cache file from a previous run
|
||||||
|
try {
|
||||||
|
if (Deno.build.os === "windows") {
|
||||||
|
Deno.removeSync(tmpdir() + "\\deno-compile-using_code_cache.exe.cache");
|
||||||
|
} else {
|
||||||
|
Deno.removeSync(tmpdir() + "\\deno-compile-using_code_cache.cache");
|
||||||
|
}
|
||||||
|
} catch {
|
||||||
|
}
|
|
@ -1,28 +1,31 @@
|
||||||
{
|
{
|
||||||
"tempDir": true,
|
"tempDir": true,
|
||||||
"steps": [{
|
"steps": [{
|
||||||
"if": "unix",
|
"args": "run -A setup.ts",
|
||||||
"args": "compile --output main1 main.ts",
|
|
||||||
"output": "[WILDCARD]"
|
"output": "[WILDCARD]"
|
||||||
}, {
|
}, {
|
||||||
"if": "unix",
|
"if": "unix",
|
||||||
"args": "compile --output main2 main.ts",
|
"args": "compile --no-config --output a/main a/main.ts",
|
||||||
"output": "[WILDCARD]"
|
"output": "[WILDCARD]"
|
||||||
}, {
|
}, {
|
||||||
"if": "unix",
|
"if": "unix",
|
||||||
"args": "run --allow-read=. assert_equal.ts main1 main2",
|
"args": "compile --no-config --output b/main b/main.ts",
|
||||||
|
"output": "[WILDCARD]"
|
||||||
|
}, {
|
||||||
|
"if": "unix",
|
||||||
|
"args": "run --allow-read=. assert_equal.ts a/main b/main",
|
||||||
"output": "Same\n"
|
"output": "Same\n"
|
||||||
}, {
|
}, {
|
||||||
"if": "windows",
|
"if": "windows",
|
||||||
"args": "compile --output main1.exe main.ts",
|
"args": "compile --no-config --output a/main.exe a/main.ts",
|
||||||
"output": "[WILDCARD]"
|
"output": "[WILDCARD]"
|
||||||
}, {
|
}, {
|
||||||
"if": "windows",
|
"if": "windows",
|
||||||
"args": "compile --output main2.exe main.ts",
|
"args": "compile --no-config --output b/main.exe b/main.ts",
|
||||||
"output": "[WILDCARD]"
|
"output": "[WILDCARD]"
|
||||||
}, {
|
}, {
|
||||||
"if": "windows",
|
"if": "windows",
|
||||||
"args": "run --allow-read=. assert_equal.ts main1.exe main2.exe",
|
"args": "run --allow-read=. assert_equal.ts a/main.exe b/main.exe",
|
||||||
"output": "Same\n"
|
"output": "Same\n"
|
||||||
}]
|
}]
|
||||||
}
|
}
|
||||||
|
|
10
tests/specs/compile/determinism/setup.ts
Normal file
10
tests/specs/compile/determinism/setup.ts
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
// for setup, we create two directories with the same file in each
|
||||||
|
// and then when compiling we ensure this directory name has no
|
||||||
|
// effect on the output
|
||||||
|
makeCopyDir("a");
|
||||||
|
makeCopyDir("b");
|
||||||
|
|
||||||
|
function makeCopyDir(dirName) {
|
||||||
|
Deno.mkdirSync(dirName);
|
||||||
|
Deno.copyFileSync("main.ts", `${dirName}/main.ts`);
|
||||||
|
}
|
24
tests/specs/compile/error/local/__test__.jsonc
Normal file
24
tests/specs/compile/error/local/__test__.jsonc
Normal file
|
@ -0,0 +1,24 @@
|
||||||
|
{
|
||||||
|
"tempDir": true,
|
||||||
|
"steps": [{
|
||||||
|
"if": "unix",
|
||||||
|
"args": "compile --output main standalone_error.ts",
|
||||||
|
"output": "[WILDCARD]"
|
||||||
|
}, {
|
||||||
|
"if": "unix",
|
||||||
|
"commandName": "./main",
|
||||||
|
"args": [],
|
||||||
|
"output": "output.out",
|
||||||
|
"exitCode": 1
|
||||||
|
}, {
|
||||||
|
"if": "windows",
|
||||||
|
"args": "compile --output main.exe standalone_error.ts",
|
||||||
|
"output": "[WILDCARD]"
|
||||||
|
}, {
|
||||||
|
"if": "windows",
|
||||||
|
"commandName": "./main.exe",
|
||||||
|
"args": [],
|
||||||
|
"output": "output.out",
|
||||||
|
"exitCode": 1
|
||||||
|
}]
|
||||||
|
}
|
6
tests/specs/compile/error/local/output.out
Normal file
6
tests/specs/compile/error/local/output.out
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
error: Uncaught (in promise) Error: boom!
|
||||||
|
throw new Error("boom!");
|
||||||
|
^
|
||||||
|
at boom (file:///[WILDLINE]standalone_error.ts:2:9)
|
||||||
|
at foo (file:///[WILDLINE]standalone_error.ts:6:3)
|
||||||
|
at file:///[WILDLINE]standalone_error.ts:9:1
|
24
tests/specs/compile/error/remote/__test__.jsonc
Normal file
24
tests/specs/compile/error/remote/__test__.jsonc
Normal file
|
@ -0,0 +1,24 @@
|
||||||
|
{
|
||||||
|
"tempDir": true,
|
||||||
|
"steps": [{
|
||||||
|
"if": "unix",
|
||||||
|
"args": "compile -A --output main main.ts",
|
||||||
|
"output": "[WILDCARD]"
|
||||||
|
}, {
|
||||||
|
"if": "unix",
|
||||||
|
"commandName": "./main",
|
||||||
|
"args": [],
|
||||||
|
"output": "output.out",
|
||||||
|
"exitCode": 1
|
||||||
|
}, {
|
||||||
|
"if": "windows",
|
||||||
|
"args": "compile -A --output main.exe main.ts",
|
||||||
|
"output": "[WILDCARD]"
|
||||||
|
}, {
|
||||||
|
"if": "windows",
|
||||||
|
"commandName": "./main.exe",
|
||||||
|
"args": [],
|
||||||
|
"output": "output.out",
|
||||||
|
"exitCode": 1
|
||||||
|
}]
|
||||||
|
}
|
1
tests/specs/compile/error/remote/main.ts
Normal file
1
tests/specs/compile/error/remote/main.ts
Normal file
|
@ -0,0 +1 @@
|
||||||
|
import "http://localhost:4545/compile/standalone_error_module_with_imports_1.ts";
|
5
tests/specs/compile/error/remote/output.out
Normal file
5
tests/specs/compile/error/remote/output.out
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
hello
|
||||||
|
error: Uncaught (in promise) Error: boom!
|
||||||
|
throw new Error(value);
|
||||||
|
^
|
||||||
|
at http://localhost:4545/compile/standalone_error_module_with_imports_2.ts:7:7
|
|
@ -1,2 +1,7 @@
|
||||||
|
// file has blank lines to make the input line
|
||||||
|
// different than the output
|
||||||
console.log("hello");
|
console.log("hello");
|
||||||
throw new Error("boom!");
|
|
||||||
|
const value: string = "boom!";
|
||||||
|
|
||||||
|
throw new Error(value);
|
||||||
|
|
Loading…
Reference in a new issue