Skip to content

Commit 9c71afe

Browse files
authored
feat: log auto-discovered config file and support configFile: false (#507)
1 parent 9a1daa1 commit 9c71afe

12 files changed

Lines changed: 286 additions & 7 deletions

File tree

README.md

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -696,8 +696,14 @@ await build({
696696

697697
### deno.json Support
698698

699-
Starting in dnt 0.42, the deno.json is auto-discovered. A config file can be
700-
explicitly specified by the `configFile` key:
699+
Starting in dnt 0.42, the deno.json is auto-discovered by searching upwards from
700+
the entry points. dnt logs the config file it discovered:
701+
702+
```
703+
[dnt] Auto-discovered config file: /home/david/dev/my_project/deno.json
704+
```
705+
706+
A config file can be explicitly specified by the `configFile` key:
701707

702708
```ts
703709
await build({
@@ -706,6 +712,16 @@ await build({
706712
});
707713
```
708714

715+
Or set it to `false` to not use a config file at all, which also disables
716+
discovering a package.json and deno.lock:
717+
718+
```ts
719+
await build({
720+
// ...etc...
721+
configFile: false,
722+
});
723+
```
724+
709725
### Frozen Lock File
710726

711727
The deno.lock file that's beside the deno.json is used to resolve dependencies,

deno.jsonc

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@
4343
"wasm/target/",
4444
"lib/pkg/",
4545
"rs-lib/src/polyfills/scripts/",
46+
"tests/config_discovery_project/npm",
4647
"tests/declaration_import_project/npm",
4748
"tests/frozen_lockfile_project/npm",
4849
"tests/import_map_project/npm",

mod.ts

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -171,8 +171,15 @@ export interface BuildOptions {
171171
mappings?: SpecifierMappings;
172172
/** Package.json output. You may override dependencies and dev dependencies in here. */
173173
package: PackageJson;
174-
/** Path or url to a deno.json. */
175-
configFile?: string;
174+
/** Path or url to a deno.json.
175+
*
176+
* When not specified, a deno.json is auto-discovered by searching upwards
177+
* from the entry points.
178+
*
179+
* Specify `false` to disable the auto-discovery, which also disables
180+
* discovering a package.json and deno.lock.
181+
*/
182+
configFile?: string | false;
176183
/** Path or url to import map.
177184
*
178185
* @remarks Use `configFile` for a deno.json. Like `deno --import-map`, the
@@ -340,6 +347,13 @@ export async function build(options: BuildOptions): Promise<void> {
340347

341348
log("Transforming...");
342349
const transformOutput = await transformEntryPoints();
350+
if (transformOutput.discoveredConfigFile != null) {
351+
log(
352+
`Auto-discovered config file: ${
353+
standardizePath(transformOutput.discoveredConfigFile)
354+
}`,
355+
);
356+
}
343357
for (const warning of transformOutput.warnings) {
344358
warn(warning);
345359
}

rs-lib/src/lib.rs

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -100,6 +100,12 @@ pub struct TransformOutput {
100100
pub main: TransformOutputEnvironment,
101101
pub test: TransformOutputEnvironment,
102102
pub warnings: Vec<String>,
103+
/// Config file that was auto-discovered by searching upwards from the
104+
/// entry points (or the cwd when there are no local entry points).
105+
///
106+
/// This is `None` when no config file was found, when one was explicitly
107+
/// provided, or when auto-discovery is disabled.
108+
pub discovered_config_file: Option<PathBuf>,
103109
}
104110

105111
#[cfg_attr(feature = "serialization", derive(serde::Serialize))]
@@ -261,6 +267,11 @@ pub struct TransformOptions {
261267
/// over what `target` implies.
262268
pub polyfills: PolyfillOverrides,
263269
pub config_file: Option<ModuleSpecifier>,
270+
/// Disables auto-discovering a config file based on the entry points
271+
/// when no config file or import map is provided.
272+
///
273+
/// Note this also disables discovering a package.json and deno.lock.
274+
pub no_config: bool,
264275
pub import_map: Option<ModuleSpecifier>,
265276
/// Errors when the deno lockfile would need to be updated in order to
266277
/// transform (ex. a dependency is not in it).
@@ -329,6 +340,7 @@ pub async fn transform(
329340
};
330341
let config_discovery = match maybe_config_path.as_ref() {
331342
Some(config_path) => ConfigDiscoveryOption::Path(config_path.clone()),
343+
None if options.no_config => ConfigDiscoveryOption::Disabled,
332344
None => {
333345
if paths.is_empty() {
334346
ConfigDiscoveryOption::DiscoverCwd
@@ -337,6 +349,10 @@ pub async fn transform(
337349
}
338350
}
339351
};
352+
let is_auto_discovering = matches!(
353+
config_discovery,
354+
ConfigDiscoveryOption::Discover { .. } | ConfigDiscoveryOption::DiscoverCwd
355+
);
340356

341357
let factory = deno_resolver::factory::WorkspaceFactory::new(
342358
sys,
@@ -360,6 +376,14 @@ pub async fn transform(
360376
no_lock: false,
361377
},
362378
);
379+
let discovered_config_file = if is_auto_discovering {
380+
factory
381+
.workspace_directory()?
382+
.member_or_root_deno_json()
383+
.and_then(|c| deno_path_util::url_to_file_path(&c.specifier).ok())
384+
} else {
385+
None
386+
};
363387
let file_fetcher = PermissionedFileFetcher::new(
364388
NullBlobStore,
365389
Rc::new(factory.http_cache()?.clone()),
@@ -665,6 +689,7 @@ pub async fn transform(
665689
main: main_env_context.environment,
666690
test: test_env_context.environment,
667691
warnings,
692+
discovered_config_file,
668693
})
669694
}
670695

rs-lib/tests/integration/test_builder.rs

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ pub struct TestBuilder {
2929
target: ScriptTarget,
3030
polyfills: PolyfillOverrides,
3131
config_file: Option<ModuleSpecifier>,
32+
no_config: bool,
3233
import_map: Option<ModuleSpecifier>,
3334
frozen_lockfile: Option<bool>,
3435
}
@@ -50,6 +51,7 @@ impl TestBuilder {
5051
target: ScriptTarget::ES5,
5152
polyfills: Default::default(),
5253
config_file: None,
54+
no_config: false,
5355
import_map: None,
5456
frozen_lockfile: None,
5557
}
@@ -81,11 +83,16 @@ impl TestBuilder {
8183
}
8284

8385
pub fn set_config_file(&mut self, url: impl AsRef<str>) -> &mut Self {
84-
self.import_map =
86+
self.config_file =
8587
Some(ModuleSpecifier::parse(&normalize_urls(url.as_ref())).unwrap());
8688
self
8789
}
8890

91+
pub fn set_no_config(&mut self, value: bool) -> &mut Self {
92+
self.no_config = value;
93+
self
94+
}
95+
8996
pub fn set_import_map(&mut self, url: impl AsRef<str>) -> &mut Self {
9097
self.import_map =
9198
Some(ModuleSpecifier::parse(&normalize_urls(url.as_ref())).unwrap());
@@ -218,6 +225,7 @@ impl TestBuilder {
218225
target: self.target,
219226
polyfills: self.polyfills.clone(),
220227
config_file: self.config_file.clone(),
228+
no_config: self.no_config,
221229
import_map: self.import_map.clone(),
222230
frozen_lockfile: self.frozen_lockfile,
223231
cwd: self.loader.sys.env_current_dir().unwrap(),

rs-lib/tests/integration_test.rs

Lines changed: 113 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ use pretty_assertions::assert_eq;
1515
#[macro_use]
1616
mod integration;
1717

18+
use integration::InMemoryLoader;
1819
use integration::TestBuilder;
1920

2021
use crate::integration::assert_identity_transforms;
@@ -1244,7 +1245,7 @@ async fn transform_jsr_specifier_mappings() {
12441245
}
12451246

12461247
#[tokio::test]
1247-
async fn transform_jsr_specifier_mapping_via_import_map() {
1248+
async fn transform_jsr_specifier_mapping_via_config_file() {
12481249
let result = TestBuilder::new()
12491250
.with_loader(|loader| {
12501251
loader
@@ -1259,6 +1260,42 @@ async fn transform_jsr_specifier_mapping_via_import_map() {
12591260
);
12601261
})
12611262
.set_config_file("file:///deno.json")
1263+
// the version requirement comes from the config file
1264+
.add_package_specifier_mapping("jsr:@scope/name", "scope-name", None, None)
1265+
.transform()
1266+
.await
1267+
.unwrap();
1268+
1269+
assert_files!(
1270+
result.main.files,
1271+
&[("mod.ts", "import * as pkg from 'scope-name';")]
1272+
);
1273+
assert_eq!(
1274+
result.main.dependencies,
1275+
&[Dependency {
1276+
name: "scope-name".to_string(),
1277+
version: "^1.0.0".to_string(),
1278+
peer_dependency: false,
1279+
}]
1280+
);
1281+
}
1282+
1283+
#[tokio::test]
1284+
async fn transform_jsr_specifier_mapping_via_import_map() {
1285+
let result = TestBuilder::new()
1286+
.with_loader(|loader| {
1287+
loader
1288+
.add_local_file("/mod.ts", "import * as pkg from '@scope/name';")
1289+
.add_local_file(
1290+
"/import_map.json",
1291+
r#"{
1292+
"imports": {
1293+
"@scope/name": "jsr:@scope/name@^1.0.0"
1294+
}
1295+
}"#,
1296+
);
1297+
})
1298+
.set_import_map("file:///import_map.json")
12621299
// the version requirement comes from the import map
12631300
.add_package_specifier_mapping("jsr:@scope/name", "scope-name", None, None)
12641301
.transform()
@@ -1576,6 +1613,8 @@ async fn transform_import_map() {
15761613
.await
15771614
.unwrap();
15781615

1616+
// the import map is used as the config file, so nothing was auto-discovered
1617+
assert_eq!(result.discovered_config_file, None);
15791618
assert_files!(
15801619
result.main.files,
15811620
&[
@@ -1826,6 +1865,8 @@ async fn transform_config_file() {
18261865
.await
18271866
.unwrap();
18281867

1868+
// it was provided, so it wasn't auto-discovered
1869+
assert_eq!(result.discovered_config_file, None);
18291870
assert_files!(
18301871
result.main.files,
18311872
&[
@@ -1836,6 +1877,58 @@ async fn transform_config_file() {
18361877
);
18371878
}
18381879

1880+
#[tokio::test]
1881+
async fn transform_auto_discovered_config_file() {
1882+
let result = TestBuilder::new()
1883+
.with_loader(|loader| {
1884+
add_config_discovery_files(loader);
1885+
})
1886+
.entry_point("file:///sub_dir/mod.ts")
1887+
.transform()
1888+
.await
1889+
.unwrap();
1890+
1891+
assert_eq!(
1892+
result.discovered_config_file,
1893+
Some(PathBuf::from(if cfg!(windows) {
1894+
"C:\\deno.json"
1895+
} else {
1896+
"/deno.json"
1897+
}))
1898+
);
1899+
assert_files!(
1900+
result.main.files,
1901+
&[
1902+
("sub_dir/mod.ts", "import * as remote from '../other.js';",),
1903+
("other.ts", "export function test() {}",)
1904+
]
1905+
);
1906+
}
1907+
1908+
#[tokio::test]
1909+
async fn transform_no_config() {
1910+
let err_message = TestBuilder::new()
1911+
.with_loader(|loader| {
1912+
add_config_discovery_files(loader);
1913+
})
1914+
.entry_point("file:///sub_dir/mod.ts")
1915+
.set_no_config(true)
1916+
.transform()
1917+
.await
1918+
.err()
1919+
.unwrap();
1920+
1921+
// the config file should not have been discovered, so its
1922+
// import mapping should not have been applied
1923+
assert_eq!(
1924+
err_message.to_string(),
1925+
normalize_urls(
1926+
"Import \"localhost/mod.ts\" not a dependency
1927+
at file:///sub_dir/mod.ts:1:25"
1928+
)
1929+
);
1930+
}
1931+
18391932
#[tokio::test]
18401933
async fn transform_multiple_entry_points() {
18411934
let result = TestBuilder::new()
@@ -2851,3 +2944,22 @@ fn get_shim_file_text(mut text: String) -> String {
28512944
);
28522945
text
28532946
}
2947+
2948+
/// Adds a config file in a parent directory of the `/sub_dir/mod.ts`
2949+
/// entry point so that it's only found by searching upwards from it.
2950+
fn add_config_discovery_files(loader: &mut InMemoryLoader) {
2951+
loader
2952+
.add_local_file(
2953+
"/sub_dir/mod.ts",
2954+
"import * as remote from 'localhost/mod.ts';",
2955+
)
2956+
.add_local_file(
2957+
"/deno.json",
2958+
r#"{
2959+
"imports": {
2960+
"localhost/mod.ts": "./other.ts"
2961+
}
2962+
}"#,
2963+
)
2964+
.add_local_file("/other.ts", "export function test() {}");
2965+
}
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
{
2+
"imports": {
3+
"@dnt/marker": "./marker.ts"
4+
}
5+
}
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
// Copyright 2018-2024 the Deno authors. MIT license.
2+
3+
export const marker = "marker";
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
// Copyright 2018-2024 the Deno authors. MIT license.
2+
3+
import { marker } from "@dnt/marker";
4+
5+
export function getMarker() {
6+
return marker;
7+
}

0 commit comments

Comments
 (0)