Skip to content

Commit b7b784a

Browse files
authored
feat: support workspaces and use deno_resolver (#462)
1 parent 37c1055 commit b7b784a

20 files changed

+1263
-513
lines changed

Cargo.lock

+834-352
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

+10-1
Original file line numberDiff line numberDiff line change
@@ -12,5 +12,14 @@ members = [
1212
]
1313

1414
[workspace.dependencies]
15-
deno_error = "0.5.6"
15+
async-trait = "0.1.88"
16+
deno_config = "0.54.2"
17+
deno_error = { version = "0.5.6", features = ["serde", "serde_json", "url"] }
18+
deno_path_util = "0.3.2"
19+
deno_resolver = { version = "0.35.0", features = ["graph"] }
20+
serde_json = { version = "1.0.140", features = ["preserve_order"] }
1621
sys_traits = { version = "0.1.9", features = ["real"] }
22+
url = { version = "2.5.4", features =["serde"] }
23+
24+
[patch.crates-io]
25+
deno_resolver = { git = "https://github.com/denoland/deno", rev = "59ffc1987c67f570e2081457c898efe51c872945" }

README.md

+4-8
Original file line numberDiff line numberDiff line change
@@ -606,22 +606,18 @@ await build({
606606
});
607607
```
608608

609-
### Import Map / deno.json Support
609+
### deno.json Support
610610

611-
To use an import map or deno.json file with `"imports"` and/or `"scopes"`, add
612-
an `importMap` entry to your build object:
611+
Starting in dnt 0.42, the deno.json is auto-discovered. A config file can be
612+
explicitly specified by the `configFile` key:
613613

614614
```ts
615615
await build({
616616
// ...etc...
617-
importMap: "deno.json",
617+
configFile: import.meta.resolve("../deno.json"),
618618
});
619619
```
620620

621-
Note there is no support for the deno.json `importMap` key. Either embed that in
622-
your deno.json or specify the import map in this property directly. Also note
623-
that the deno.json is not auto-discovered—you must explicitly specify it.
624-
625621
### GitHub Actions - Npm Publish on Tag
626622

627623
1. Ensure your build script accepts a version as a CLI argument and sets that in

mod.ts

+5
Original file line numberDiff line numberDiff line change
@@ -122,6 +122,8 @@ export interface BuildOptions {
122122
mappings?: SpecifierMappings;
123123
/** Package.json output. You may override dependencies and dev dependencies in here. */
124124
package: PackageJson;
125+
/** Path or url to a deno.json. */
126+
configFile?: string;
125127
/** Path or url to import map. */
126128
importMap?: string;
127129
/** Package manager used to install dependencies and run npm scripts.
@@ -209,6 +211,7 @@ export async function build(options: BuildOptions): Promise<void> {
209211
? "inline"
210212
: options.declaration ?? "inline",
211213
};
214+
const cwd = Deno.cwd();
212215
const declarationMap = options.declarationMap ??
213216
(!!options.declaration && !options.skipSourceOutput);
214217
const packageManager = options.packageManager ?? "npm";
@@ -586,7 +589,9 @@ export async function build(options: BuildOptions): Promise<void> {
586589
mappings: options.mappings,
587590
target: scriptTarget,
588591
importMap: options.importMap,
592+
configFile: options.configFile,
589593
internalWasmUrl: options.internalWasmUrl,
594+
cwd: path.toFileUrl(cwd).toString(),
590595
});
591596
}
592597

rs-lib/Cargo.toml

+7-2
Original file line numberDiff line numberDiff line change
@@ -14,11 +14,14 @@ serialization = ["serde"]
1414

1515
[dependencies]
1616
anyhow = "1.0.70"
17+
async-trait.workspace = true
1718
base64 = "0.13.1"
1819
deno_ast = { version = "0.46.6", features = ["transforms", "view", "visit", "utils"] }
20+
deno_config.workspace = true
1921
deno_error.workspace = true
2022
deno_graph = { version = "0.90.0", features = [], default-features = false }
21-
deno_path_util = "0.3.2"
23+
deno_path_util.workspace = true
24+
deno_resolver.workspace = true
2225
deno_semver = "0.7.1"
2326
futures = "0.3.25"
2427
import_map = { version = "0.21.0", features = ["ext"] }
@@ -28,9 +31,11 @@ pathdiff = "0.2.1"
2831
regex = "1.7"
2932
reqwest = { version = "0.11", features = ["rustls"], optional = true }
3033
serde = { version = "1.0.159", features = ["derive"], optional = true }
31-
serde_json = "1.0.96"
34+
serde_json.workspace = true
3235
sys_traits.workspace = true
3336
tokio = { version = "1", features = ["full"], optional = true }
37+
url.workspace = true
3438

3539
[dev-dependencies]
3640
pretty_assertions = "1.3.0"
41+
sys_traits = { workspace = true, features = ["memory"] }

rs-lib/src/graph.rs

+42-94
Original file line numberDiff line numberDiff line change
@@ -13,29 +13,33 @@ use crate::specifiers::get_specifiers;
1313
use crate::specifiers::Specifiers;
1414
use crate::MappedSpecifier;
1515

16-
use anyhow::anyhow;
1716
use anyhow::bail;
18-
use anyhow::Context;
1917
use anyhow::Result;
2018
use deno_ast::ModuleSpecifier;
19+
use deno_ast::ParseDiagnostic;
2120
use deno_ast::ParsedSource;
22-
use deno_error::JsErrorBox;
23-
use deno_graph::source::CacheSetting;
24-
use deno_graph::source::ResolutionKind;
25-
use deno_graph::source::ResolveError;
21+
use deno_config::workspace::WorkspaceDirectory;
2622
use deno_graph::CapturingModuleAnalyzer;
23+
use deno_graph::EsParser;
24+
use deno_graph::JsModule;
2725
use deno_graph::Module;
26+
use deno_graph::ParseOptions;
2827
use deno_graph::ParsedSourceStore;
29-
use deno_graph::Range;
30-
use import_map::ImportMapOptions;
28+
use deno_resolver::factory::WorkspaceFactorySys;
29+
use deno_resolver::graph::DefaultDenoResolverRc;
30+
use deno_resolver::npm::DenoInNpmPackageChecker;
31+
use deno_resolver::workspace::ScopedJsxImportSourceConfig;
3132
use sys_traits::impls::RealSys;
3233

33-
pub struct ModuleGraphOptions<'a> {
34+
pub struct ModuleGraphOptions<'a, TSys: WorkspaceFactorySys> {
3435
pub entry_points: Vec<ModuleSpecifier>,
3536
pub test_entry_points: Vec<ModuleSpecifier>,
36-
pub loader: Option<Rc<dyn Loader>>,
37+
pub loader: Rc<dyn Loader>,
38+
pub resolver: DefaultDenoResolverRc<TSys>,
3739
pub specifier_mappings: &'a HashMap<ModuleSpecifier, MappedSpecifier>,
38-
pub import_map: Option<ModuleSpecifier>,
40+
pub cjs_tracker:
41+
Rc<deno_resolver::cjs::CjsTracker<DenoInNpmPackageChecker, TSys>>,
42+
pub workspace_dir: Rc<WorkspaceDirectory>,
3943
}
4044

4145
/// Wrapper around deno_graph::ModuleGraph.
@@ -45,32 +49,26 @@ pub struct ModuleGraph {
4549
}
4650

4751
impl ModuleGraph {
48-
pub async fn build_with_specifiers(
49-
options: ModuleGraphOptions<'_>,
52+
pub async fn build_with_specifiers<TSys: WorkspaceFactorySys>(
53+
options: ModuleGraphOptions<'_, TSys>,
5054
) -> Result<(Self, Specifiers)> {
51-
let loader = options.loader.unwrap_or_else(|| {
52-
#[cfg(feature = "tokio-loader")]
53-
return Rc::new(crate::loader::DefaultLoader::new());
54-
#[cfg(not(feature = "tokio-loader"))]
55-
panic!("You must provide a loader or use the 'tokio-loader' feature.")
56-
});
57-
let resolver = match options.import_map {
58-
Some(import_map_url) => Some(
59-
ImportMapResolver::load(&import_map_url, &*loader)
60-
.await
61-
.context("Error loading import map.")?,
62-
),
63-
None => None,
64-
};
55+
let resolver = options.resolver;
56+
let loader = options.loader;
6557
let loader = SourceLoader::new(
6658
loader,
6759
get_all_specifier_mappers(),
6860
options.specifier_mappings,
6961
);
62+
let scoped_jsx_import_source_config =
63+
ScopedJsxImportSourceConfig::from_workspace_dir(&options.workspace_dir)?;
7064
let source_parser = ScopeAnalysisParser;
7165
let capturing_analyzer =
7266
CapturingModuleAnalyzer::new(Some(Box::new(source_parser)), None);
7367
let mut graph = deno_graph::ModuleGraph::new(deno_graph::GraphKind::All);
68+
let graph_resolver = resolver.as_graph_resolver(
69+
&options.cjs_tracker,
70+
&scoped_jsx_import_source_config,
71+
);
7472
graph
7573
.build(
7674
options
@@ -84,7 +82,7 @@ impl ModuleGraph {
8482
is_dynamic: false,
8583
skip_dynamic_deps: false,
8684
imports: Default::default(),
87-
resolver: resolver.as_ref().map(|r| r.as_resolver()),
85+
resolver: Some(&graph_resolver),
8886
locker: None,
8987
module_analyzer: &capturing_analyzer,
9088
reporter: None,
@@ -181,17 +179,22 @@ impl ModuleGraph {
181179
})
182180
}
183181

184-
pub fn get_parsed_source(&self, specifier: &ModuleSpecifier) -> ParsedSource {
185-
let specifier = self.graph.resolve(specifier);
186-
self
182+
pub fn get_parsed_source(
183+
&self,
184+
js_module: &JsModule,
185+
) -> Result<ParsedSource, ParseDiagnostic> {
186+
match self
187187
.capturing_analyzer
188-
.get_parsed_source(&specifier)
189-
.unwrap_or_else(|| {
190-
panic!(
191-
"dnt bug - Did not find parsed source for specifier: {}",
192-
specifier
193-
);
194-
})
188+
.get_parsed_source(&js_module.specifier)
189+
{
190+
Some(parsed_source) => Ok(parsed_source),
191+
None => self.capturing_analyzer.parse_program(ParseOptions {
192+
specifier: &js_module.specifier,
193+
source: js_module.source.clone(),
194+
media_type: js_module.media_type,
195+
scope_analysis: false,
196+
}),
197+
}
195198
}
196199

197200
pub fn resolve_dependency(
@@ -202,7 +205,7 @@ impl ModuleGraph {
202205
self
203206
.graph
204207
.resolve_dependency(value, referrer, /* prefer_types */ false)
205-
.map(|url| url.clone())
208+
.cloned()
206209
.or_else(|| {
207210
let value_lower = value.to_lowercase();
208211
if value_lower.starts_with("https://")
@@ -236,58 +239,3 @@ fn format_specifiers_for_message(
236239
.collect::<Vec<_>>()
237240
.join("\n")
238241
}
239-
240-
#[derive(Debug)]
241-
struct ImportMapResolver(import_map::ImportMap);
242-
243-
impl ImportMapResolver {
244-
pub async fn load(
245-
import_map_url: &ModuleSpecifier,
246-
loader: &dyn Loader,
247-
) -> Result<Self> {
248-
let response = loader
249-
.load(import_map_url.clone(), CacheSetting::Use, None)
250-
.await?
251-
.ok_or_else(|| anyhow!("Could not find {}", import_map_url))?;
252-
let value = jsonc_parser::parse_to_serde_value(
253-
&String::from_utf8(response.content)?,
254-
&jsonc_parser::ParseOptions {
255-
allow_comments: true,
256-
allow_loose_object_property_names: true,
257-
allow_trailing_commas: true,
258-
},
259-
)?
260-
.unwrap_or_else(|| serde_json::Value::Object(Default::default()));
261-
let result = import_map::parse_from_value_with_options(
262-
import_map_url.clone(),
263-
value,
264-
ImportMapOptions {
265-
address_hook: None,
266-
expand_imports: true,
267-
},
268-
)?;
269-
// if !result.diagnostics.is_empty() {
270-
// todo: surface diagnostics maybe? It seems like this should not be hard error according to import map spec
271-
// bail!("Import map diagnostics:\n{}", result.diagnostics.into_iter().map(|d| format!(" - {}", d)).collect::<Vec<_>>().join("\n"));
272-
//}
273-
Ok(ImportMapResolver(result.import_map))
274-
}
275-
276-
pub fn as_resolver(&self) -> &dyn deno_graph::source::Resolver {
277-
self
278-
}
279-
}
280-
281-
impl deno_graph::source::Resolver for ImportMapResolver {
282-
fn resolve(
283-
&self,
284-
specifier: &str,
285-
referrer_range: &Range,
286-
_kind: ResolutionKind,
287-
) -> Result<ModuleSpecifier, ResolveError> {
288-
self
289-
.0
290-
.resolve(specifier, &referrer_range.specifier)
291-
.map_err(|err| ResolveError::Other(JsErrorBox::from_err(err)))
292-
}
293-
}

0 commit comments

Comments
 (0)