summaryrefslogtreecommitdiff
path: root/cli/tools/vendor/import_map.rs
diff options
context:
space:
mode:
Diffstat (limited to 'cli/tools/vendor/import_map.rs')
-rw-r--r--cli/tools/vendor/import_map.rs508
1 files changed, 0 insertions, 508 deletions
diff --git a/cli/tools/vendor/import_map.rs b/cli/tools/vendor/import_map.rs
deleted file mode 100644
index 644e84a7b..000000000
--- a/cli/tools/vendor/import_map.rs
+++ /dev/null
@@ -1,508 +0,0 @@
-// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license.
-
-use deno_ast::LineAndColumnIndex;
-use deno_ast::ModuleSpecifier;
-use deno_ast::SourceTextInfo;
-use deno_core::error::AnyError;
-use deno_graph::source::ResolutionMode;
-use deno_graph::Module;
-use deno_graph::ModuleGraph;
-use deno_graph::Position;
-use deno_graph::Range;
-use deno_graph::Resolution;
-use import_map::ImportMap;
-use import_map::SpecifierMap;
-use indexmap::IndexMap;
-use log::warn;
-
-use crate::args::JsxImportSourceConfig;
-use crate::cache::ParsedSourceCache;
-
-use super::mappings::Mappings;
-use super::specifiers::is_remote_specifier;
-use super::specifiers::is_remote_specifier_text;
-
-struct ImportMapBuilder<'a> {
- base_dir: &'a ModuleSpecifier,
- mappings: &'a Mappings,
- imports: ImportsBuilder<'a>,
- scopes: IndexMap<String, ImportsBuilder<'a>>,
-}
-
-impl<'a> ImportMapBuilder<'a> {
- pub fn new(base_dir: &'a ModuleSpecifier, mappings: &'a Mappings) -> Self {
- ImportMapBuilder {
- base_dir,
- mappings,
- imports: ImportsBuilder::new(base_dir, mappings),
- scopes: Default::default(),
- }
- }
-
- pub fn base_dir(&self) -> &ModuleSpecifier {
- self.base_dir
- }
-
- pub fn scope(
- &mut self,
- base_specifier: &ModuleSpecifier,
- ) -> &mut ImportsBuilder<'a> {
- self
- .scopes
- .entry(
- self
- .mappings
- .relative_specifier_text(self.base_dir, base_specifier),
- )
- .or_insert_with(|| ImportsBuilder::new(self.base_dir, self.mappings))
- }
-
- pub fn into_import_map(
- self,
- maybe_original_import_map: Option<&ImportMap>,
- ) -> ImportMap {
- fn get_local_imports(
- new_relative_path: &str,
- original_imports: &SpecifierMap,
- ) -> Vec<(String, String)> {
- let mut result = Vec::new();
- for entry in original_imports.entries() {
- if let Some(raw_value) = entry.raw_value {
- if raw_value.starts_with("./") || raw_value.starts_with("../") {
- let sub_index = raw_value.find('/').unwrap() + 1;
- result.push((
- entry.raw_key.to_string(),
- format!("{}{}", new_relative_path, &raw_value[sub_index..]),
- ));
- }
- }
- }
- result
- }
-
- fn add_local_imports<'a>(
- new_relative_path: &str,
- original_imports: &SpecifierMap,
- get_new_imports: impl FnOnce() -> &'a mut SpecifierMap,
- ) {
- let local_imports =
- get_local_imports(new_relative_path, original_imports);
- if !local_imports.is_empty() {
- let new_imports = get_new_imports();
- for (key, value) in local_imports {
- if let Err(warning) = new_imports.append(key, value) {
- warn!("{}", warning);
- }
- }
- }
- }
-
- let mut import_map = ImportMap::new(self.base_dir.clone());
-
- if let Some(original_im) = maybe_original_import_map {
- let original_base_dir = ModuleSpecifier::from_directory_path(
- original_im
- .base_url()
- .to_file_path()
- .unwrap()
- .parent()
- .unwrap(),
- )
- .unwrap();
- let new_relative_path = self
- .mappings
- .relative_specifier_text(self.base_dir, &original_base_dir);
- // add the imports
- add_local_imports(&new_relative_path, original_im.imports(), || {
- import_map.imports_mut()
- });
-
- for scope in original_im.scopes() {
- if scope.raw_key.starts_with("./") || scope.raw_key.starts_with("../") {
- let sub_index = scope.raw_key.find('/').unwrap() + 1;
- let new_key =
- format!("{}{}", new_relative_path, &scope.raw_key[sub_index..]);
- add_local_imports(&new_relative_path, scope.imports, || {
- import_map.get_or_append_scope_mut(&new_key).unwrap()
- });
- }
- }
- }
-
- let imports = import_map.imports_mut();
- for (key, value) in self.imports.imports {
- if !imports.contains(&key) {
- imports.append(key, value).unwrap();
- }
- }
-
- for (scope_key, scope_value) in self.scopes {
- if !scope_value.imports.is_empty() {
- let imports = import_map.get_or_append_scope_mut(&scope_key).unwrap();
- for (key, value) in scope_value.imports {
- if !imports.contains(&key) {
- imports.append(key, value).unwrap();
- }
- }
- }
- }
-
- import_map
- }
-}
-
-struct ImportsBuilder<'a> {
- base_dir: &'a ModuleSpecifier,
- mappings: &'a Mappings,
- imports: IndexMap<String, String>,
-}
-
-impl<'a> ImportsBuilder<'a> {
- pub fn new(base_dir: &'a ModuleSpecifier, mappings: &'a Mappings) -> Self {
- Self {
- base_dir,
- mappings,
- imports: Default::default(),
- }
- }
-
- pub fn add(&mut self, key: String, specifier: &ModuleSpecifier) {
- let value = self
- .mappings
- .relative_specifier_text(self.base_dir, specifier);
-
- // skip creating identity entries
- if key != value {
- self.imports.insert(key, value);
- }
- }
-}
-
-pub struct BuildImportMapInput<'a> {
- pub base_dir: &'a ModuleSpecifier,
- pub modules: &'a [&'a Module],
- pub graph: &'a ModuleGraph,
- pub mappings: &'a Mappings,
- pub maybe_original_import_map: Option<&'a ImportMap>,
- pub maybe_jsx_import_source: Option<&'a JsxImportSourceConfig>,
- pub resolver: &'a dyn deno_graph::source::Resolver,
- pub parsed_source_cache: &'a ParsedSourceCache,
-}
-
-pub fn build_import_map(
- input: BuildImportMapInput<'_>,
-) -> Result<String, AnyError> {
- let BuildImportMapInput {
- base_dir,
- modules,
- graph,
- mappings,
- maybe_original_import_map,
- maybe_jsx_import_source,
- resolver,
- parsed_source_cache,
- } = input;
- let mut builder = ImportMapBuilder::new(base_dir, mappings);
- visit_modules(graph, modules, mappings, &mut builder, parsed_source_cache)?;
-
- for base_specifier in mappings.base_specifiers() {
- builder
- .imports
- .add(base_specifier.to_string(), base_specifier);
- }
-
- // add the jsx import source to the destination import map, if mapped in the original import map
- if let Some(jsx_import_source) = maybe_jsx_import_source {
- if let Some(specifier_text) = jsx_import_source.maybe_specifier_text() {
- if let Ok(resolved_url) = resolver.resolve(
- &specifier_text,
- &deno_graph::Range {
- specifier: jsx_import_source.base_url.clone(),
- start: deno_graph::Position::zeroed(),
- end: deno_graph::Position::zeroed(),
- },
- ResolutionMode::Execution,
- ) {
- builder.imports.add(specifier_text, &resolved_url);
- }
- }
- }
-
- Ok(builder.into_import_map(maybe_original_import_map).to_json())
-}
-
-fn visit_modules(
- graph: &ModuleGraph,
- modules: &[&Module],
- mappings: &Mappings,
- import_map: &mut ImportMapBuilder,
- parsed_source_cache: &ParsedSourceCache,
-) -> Result<(), AnyError> {
- for module in modules {
- let module = match module {
- Module::Js(module) => module,
- // skip visiting Json modules as they are leaves
- Module::Json(_)
- | Module::Npm(_)
- | Module::Node(_)
- | Module::External(_) => continue,
- };
-
- let parsed_source =
- parsed_source_cache.get_parsed_source_from_js_module(module)?;
- let text_info = parsed_source.text_info_lazy().clone();
-
- for dep in module.dependencies.values() {
- visit_resolution(
- &dep.maybe_code,
- graph,
- import_map,
- &module.specifier,
- mappings,
- &text_info,
- &module.source,
- );
- visit_resolution(
- &dep.maybe_type,
- graph,
- import_map,
- &module.specifier,
- mappings,
- &text_info,
- &module.source,
- );
- }
-
- if let Some(types_dep) = &module.maybe_types_dependency {
- visit_resolution(
- &types_dep.dependency,
- graph,
- import_map,
- &module.specifier,
- mappings,
- &text_info,
- &module.source,
- );
- }
- }
-
- Ok(())
-}
-
-fn visit_resolution(
- resolution: &Resolution,
- graph: &ModuleGraph,
- import_map: &mut ImportMapBuilder,
- referrer: &ModuleSpecifier,
- mappings: &Mappings,
- text_info: &SourceTextInfo,
- source_text: &str,
-) {
- if let Some(resolved) = resolution.ok() {
- let text = text_from_range(text_info, source_text, &resolved.range);
- // if the text is empty then it's probably an x-TypeScript-types
- if !text.is_empty() {
- handle_dep_specifier(
- text,
- &resolved.specifier,
- graph,
- import_map,
- referrer,
- mappings,
- );
- }
- }
-}
-
-fn handle_dep_specifier(
- text: &str,
- unresolved_specifier: &ModuleSpecifier,
- graph: &ModuleGraph,
- import_map: &mut ImportMapBuilder,
- referrer: &ModuleSpecifier,
- mappings: &Mappings,
-) {
- let specifier = match graph.get(unresolved_specifier) {
- Some(module) => module.specifier().clone(),
- // Ignore when None. The graph was previous validated so this is a
- // dynamic import that was missing and is ignored for vendoring
- None => return,
- };
- // check if it's referencing a remote module
- if is_remote_specifier(&specifier) {
- handle_remote_dep_specifier(
- text,
- unresolved_specifier,
- &specifier,
- import_map,
- referrer,
- mappings,
- )
- } else if specifier.scheme() == "file" {
- handle_local_dep_specifier(
- text,
- unresolved_specifier,
- &specifier,
- import_map,
- referrer,
- mappings,
- );
- }
-}
-
-fn handle_remote_dep_specifier(
- text: &str,
- unresolved_specifier: &ModuleSpecifier,
- specifier: &ModuleSpecifier,
- import_map: &mut ImportMapBuilder,
- referrer: &ModuleSpecifier,
- mappings: &Mappings,
-) {
- if is_remote_specifier_text(text) {
- let base_specifier = mappings.base_specifier(specifier);
- if text.starts_with(base_specifier.as_str()) {
- let sub_path = &text[base_specifier.as_str().len()..];
- let relative_text =
- mappings.relative_specifier_text(base_specifier, specifier);
- let expected_sub_path = relative_text.trim_start_matches("./");
- if expected_sub_path != sub_path {
- import_map.imports.add(text.to_string(), specifier);
- }
- } else {
- // it's probably a redirect. Add it explicitly to the import map
- import_map.imports.add(text.to_string(), specifier);
- }
- } else {
- let expected_relative_specifier_text =
- mappings.relative_specifier_text(referrer, specifier);
- if expected_relative_specifier_text == text {
- return;
- }
-
- if !is_remote_specifier(referrer) {
- // local module referencing a remote module using
- // non-remote specifier text means it was something in
- // the original import map, so add a mapping to it
- import_map.imports.add(text.to_string(), specifier);
- return;
- }
-
- let base_referrer = mappings.base_specifier(referrer);
- let base_dir = import_map.base_dir().clone();
- let imports = import_map.scope(base_referrer);
- if text.starts_with("./") || text.starts_with("../") {
- // resolve relative specifier key
- let mut local_base_specifier = mappings.local_uri(base_referrer);
- local_base_specifier = local_base_specifier
- // path includes "/" so make it relative
- .join(&format!(".{}", unresolved_specifier.path()))
- .unwrap_or_else(|_| {
- panic!(
- "Error joining {} to {}",
- unresolved_specifier.path(),
- local_base_specifier
- )
- });
- local_base_specifier.set_query(unresolved_specifier.query());
-
- imports.add(
- mappings.relative_specifier_text(&base_dir, &local_base_specifier),
- specifier,
- );
-
- // add a mapping that uses the local directory name and the remote
- // filename in order to support files importing this relatively
- imports.add(
- {
- let local_path = mappings.local_path(specifier);
- let mut value =
- ModuleSpecifier::from_directory_path(local_path.parent().unwrap())
- .unwrap();
- value.set_query(specifier.query());
- value.set_path(&format!(
- "{}{}",
- value.path(),
- specifier.path_segments().unwrap().last().unwrap(),
- ));
- mappings.relative_specifier_text(&base_dir, &value)
- },
- specifier,
- );
- } else {
- // absolute (`/`) or bare specifier should be left as-is
- imports.add(text.to_string(), specifier);
- }
- }
-}
-
-fn handle_local_dep_specifier(
- text: &str,
- unresolved_specifier: &ModuleSpecifier,
- specifier: &ModuleSpecifier,
- import_map: &mut ImportMapBuilder,
- referrer: &ModuleSpecifier,
- mappings: &Mappings,
-) {
- if !is_remote_specifier(referrer) {
- // do not handle local modules referencing local modules
- return;
- }
-
- // The remote module is referencing a local file. This could occur via an
- // existing import map. In this case, we'll have to add an import map
- // entry in order to map the path back to the local path once vendored.
- let base_dir = import_map.base_dir().clone();
- let base_specifier = mappings.base_specifier(referrer);
- let imports = import_map.scope(base_specifier);
-
- if text.starts_with("./") || text.starts_with("../") {
- let referrer_local_uri = mappings.local_uri(referrer);
- let mut specifier_local_uri =
- referrer_local_uri.join(text).unwrap_or_else(|_| {
- panic!(
- "Error joining {} to {}",
- unresolved_specifier.path(),
- referrer_local_uri
- )
- });
- specifier_local_uri.set_query(unresolved_specifier.query());
-
- imports.add(
- mappings.relative_specifier_text(&base_dir, &specifier_local_uri),
- specifier,
- );
- } else {
- imports.add(text.to_string(), specifier);
- }
-}
-
-fn text_from_range<'a>(
- text_info: &SourceTextInfo,
- text: &'a str,
- range: &Range,
-) -> &'a str {
- let result = &text[byte_range(text_info, range)];
- if result.starts_with('"') || result.starts_with('\'') {
- // remove the quotes
- &result[1..result.len() - 1]
- } else {
- result
- }
-}
-
-fn byte_range(
- text_info: &SourceTextInfo,
- range: &Range,
-) -> std::ops::Range<usize> {
- let start = byte_index(text_info, &range.start);
- let end = byte_index(text_info, &range.end);
- start..end
-}
-
-fn byte_index(text_info: &SourceTextInfo, pos: &Position) -> usize {
- // todo(https://github.com/denoland/deno_graph/issues/79): use byte indexes all the way down
- text_info.loc_to_source_pos(LineAndColumnIndex {
- line_index: pos.line,
- column_index: pos.character,
- }) - text_info.range().start
-}