summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Cargo.lock411
-rw-r--r--cli/Cargo.toml4
-rw-r--r--cli/ast/bundle_hook.rs1
-rw-r--r--cli/cache.rs349
-rw-r--r--cli/compat.rs12
-rw-r--r--cli/config_file.rs14
-rw-r--r--cli/diagnostics.rs14
-rw-r--r--cli/emit.rs927
-rw-r--r--cli/errors.rs41
-rw-r--r--cli/file_fetcher.rs20
-rw-r--r--cli/info.rs513
-rw-r--r--cli/lockfile.rs42
-rw-r--r--cli/lsp/analysis.rs67
-rw-r--r--cli/lsp/cache.rs86
-rw-r--r--cli/lsp/language_server.rs55
-rw-r--r--cli/lsp/mod.rs1
-rw-r--r--cli/lsp/sources.rs29
-rw-r--r--cli/lsp/tsc.rs11
-rw-r--r--cli/main.rs365
-rw-r--r--cli/module_graph.rs2834
-rw-r--r--cli/module_loader.rs67
-rw-r--r--cli/ops/runtime_compiler.rs213
-rw-r--r--cli/proc_state.rs435
-rw-r--r--cli/resolver.rs35
-rw-r--r--cli/specifier_handler.rs776
-rw-r--r--cli/tests/integration/lsp_tests.rs1
-rw-r--r--cli/tests/integration/watcher_tests.rs3
-rw-r--r--cli/tests/testdata/041_dyn_import_eval.out1
-rw-r--r--cli/tests/testdata/055_info_file_json.out72
-rw-r--r--cli/tests/testdata/076_info_json_deps_order.out144
-rw-r--r--cli/tests/testdata/092_import_map_unmapped_bare_specifier.ts.out7
-rw-r--r--cli/tests/testdata/cafile_ts_fetch.ts.out1
-rw-r--r--cli/tests/testdata/cafile_ts_fetch_unsafe_ssl.ts.out1
-rw-r--r--cli/tests/testdata/compat/existing_import_map.out5
-rw-r--r--cli/tests/testdata/compiler_api_test.ts30
-rw-r--r--cli/tests/testdata/config.ts.out1
-rw-r--r--cli/tests/testdata/disallow_http_from_https_js.out3
-rw-r--r--cli/tests/testdata/disallow_http_from_https_ts.out3
-rw-r--r--cli/tests/testdata/dynamic_import/permissions_blob_local.ts.out4
-rw-r--r--cli/tests/testdata/dynamic_import/permissions_blob_remote.ts.out2
-rw-r--r--cli/tests/testdata/dynamic_import/permissions_data_local.ts.out2
-rw-r--r--cli/tests/testdata/dynamic_import/permissions_data_remote.ts.out2
-rw-r--r--cli/tests/testdata/dynamic_import/permissions_remote_remote.ts.out2
-rw-r--r--cli/tests/testdata/error_004_missing_module.ts.out4
-rw-r--r--cli/tests/testdata/error_005_missing_dynamic_import.ts.out2
-rw-r--r--cli/tests/testdata/error_006_import_ext_failure.ts.out4
-rw-r--r--cli/tests/testdata/error_011_bad_module_specifier.ts.out2
-rw-r--r--cli/tests/testdata/error_012_bad_dynamic_import_specifier.ts.out4
-rw-r--r--cli/tests/testdata/error_013_missing_script.out2
-rw-r--r--cli/tests/testdata/error_014_catch_dynamic_import_error.js.out14
-rw-r--r--cli/tests/testdata/error_015_dynamic_import_permissions.out3
-rw-r--r--cli/tests/testdata/error_016_dynamic_import_permissions2.out8
-rw-r--r--cli/tests/testdata/error_local_static_import_from_remote.js.out5
-rw-r--r--cli/tests/testdata/error_local_static_import_from_remote.ts.out5
-rw-r--r--cli/tests/testdata/error_missing_module_named_import.ts.out5
-rw-r--r--cli/tests/testdata/error_syntax.js.out2
-rw-r--r--cli/tests/testdata/error_syntax_empty_trailing_line.mjs.out2
-rw-r--r--cli/tests/testdata/import_blob_url_import_relative.ts.out2
-rw-r--r--cli/tests/testdata/import_data_url_import_relative.ts.out3
-rw-r--r--cli/tests/testdata/info_missing_module.out4
-rw-r--r--cli/tests/testdata/lint/expected_json.out2
-rw-r--r--cli/tests/testdata/localhost_unsafe_ssl.ts.out2
-rw-r--r--cli/tests/testdata/lock_check_err_with_bundle.out2
-rw-r--r--cli/tests/testdata/lock_dynamic_imports.out2
-rw-r--r--cli/tests/testdata/swc_syntax_error.ts.out2
-rw-r--r--cli/tests/testdata/ts_decorators_bundle.out2
-rw-r--r--cli/tests/testdata/ts_type_only_import.ts.out8
-rw-r--r--cli/tests/testdata/workers/nonexistent_worker.out2
-rw-r--r--cli/tests/testdata/workers/permissions_blob_local.ts.out2
-rw-r--r--cli/tests/testdata/workers/permissions_blob_remote.ts.out2
-rw-r--r--cli/tests/testdata/workers/permissions_data_local.ts.out2
-rw-r--r--cli/tests/testdata/workers/permissions_data_remote.ts.out2
-rw-r--r--cli/tests/testdata/workers/permissions_dynamic_remote.ts.out1
-rw-r--r--cli/tests/testdata/workers/permissions_remote_remote.ts.out2
-rw-r--r--cli/tools/coverage.rs12
-rw-r--r--cli/tools/doc.rs8
-rw-r--r--cli/tools/test.rs138
-rw-r--r--cli/tsc.rs222
78 files changed, 2972 insertions, 5118 deletions
diff --git a/Cargo.lock b/Cargo.lock
index 4f6b82d02..7b2076cee 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -85,9 +85,9 @@ dependencies = [
[[package]]
name = "anyhow"
-version = "1.0.43"
+version = "1.0.44"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "28ae2b3dec75a406790005a200b1bd89785afc02517a00ca99ecfe093ee9e6cf"
+checksum = "61604a8f862e1d5c3229fdd78f8b02c68dcf73a4c4b05fd636d12240aaa242c1"
[[package]]
name = "arrayvec"
@@ -106,9 +106,9 @@ dependencies = [
[[package]]
name = "ash"
-version = "0.33.2+1.2.186"
+version = "0.33.3+1.2.191"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "73e2b957a47afef62973ed5fa0a52afb3289c7a243bfbc3906090b8434971237"
+checksum = "cc4f1d82f164f838ae413296d1131aa6fa79b917d25bebaa7033d25620c09219"
dependencies = [
"libloading",
]
@@ -121,8 +121,8 @@ checksum = "f93f52ce8fac3d0e6720a92b0576d737c01b1b5db4dd786e962e5925f00bf755"
dependencies = [
"darling",
"pmutil",
- "proc-macro2 1.0.28",
- "quote 1.0.9",
+ "proc-macro2 1.0.29",
+ "quote 1.0.10",
"swc_macros_common",
"syn 1.0.65",
]
@@ -157,8 +157,8 @@ version = "0.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "648ed8c8d2ce5409ccd57453d9d1b214b342a0d69376a6feda1fd6cae3299308"
dependencies = [
- "proc-macro2 1.0.28",
- "quote 1.0.9",
+ "proc-macro2 1.0.29",
+ "quote 1.0.10",
"syn 1.0.65",
]
@@ -168,8 +168,8 @@ version = "0.1.51"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "44318e776df68115a881de9a8fd1b9e53368d7a4a5ce4cc48517da3393233a5e"
dependencies = [
- "proc-macro2 1.0.28",
- "quote 1.0.9",
+ "proc-macro2 1.0.29",
+ "quote 1.0.10",
"syn 1.0.65",
]
@@ -191,8 +191,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "42cbf586c80ada5e5ccdecae80d3ef0854f224e2dd74435f8d87e6831b8d0a38"
dependencies = [
"proc-macro-error",
- "proc-macro2 1.0.28",
- "quote 1.0.9",
+ "proc-macro2 1.0.29",
+ "quote 1.0.10",
"syn 1.0.65",
]
@@ -291,9 +291,9 @@ checksum = "b4ae4235e6dac0694637c763029ecea1a2ec9e4e06ec2729bd21ba4d9c863eb7"
[[package]]
name = "bumpalo"
-version = "3.7.0"
+version = "3.7.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "9c59e7af012c713f529e7a3ee57ce9b31ddd858d4b512923602f74608b009631"
+checksum = "d9df67f7bf9ef8498769f994239c45613ef0c5899415fb58e9add412d2c1a538"
[[package]]
name = "byteorder"
@@ -309,9 +309,9 @@ checksum = "c4872d67bab6358e59559027aa3b9157c53d9358c51423c17554809a8858e0f8"
[[package]]
name = "cc"
-version = "1.0.69"
+version = "1.0.71"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e70cc2f62c6ce1868963827bd677764c62d07c3d9a3e1fb1177ee1a9ab199eb2"
+checksum = "79c2681d6594606957bbb8631c4b90a7fcaaa72cdb714743a437b156d6a7eedd"
[[package]]
name = "cfg-if"
@@ -382,9 +382,9 @@ dependencies = [
[[package]]
name = "const-oid"
-version = "0.6.0"
+version = "0.6.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "44c32f031ea41b4291d695026c023b95d59db2d8a2c7640800ed56bc8f510f22"
+checksum = "fdab415d6744056100f40250a66bc430c1a46f7a02e20bc11c94c79a0f0464df"
[[package]]
name = "convert_case"
@@ -428,9 +428,9 @@ dependencies = [
[[package]]
name = "cpufeatures"
-version = "0.1.5"
+version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "66c99696f6c9dd7f35d486b9d04d7e6e202aa3e8c40d553f2fdf5e7e0c6a71ef"
+checksum = "95059428f66df56b63431fdb4e1947ed2190586af5c5a8a8b71122bdf5a7f469"
dependencies = [
"libc",
]
@@ -475,9 +475,9 @@ dependencies = [
[[package]]
name = "crypto-bigint"
-version = "0.2.4"
+version = "0.2.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "cc209804a22c34a98fe26a32d997ac64d4284816f65cf1a529c4e31a256218a0"
+checksum = "d12477e115c0d570c12a2dfd859f80b55b60ddb5075df210d3af06d133a69f45"
dependencies = [
"generic-array",
"rand_core 0.6.3",
@@ -501,7 +501,7 @@ version = "0.1.20"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5e98e2ad1a782e33928b96fc3948e7c355e5af34ba4de7670fe8bac2a3b2006d"
dependencies = [
- "quote 1.0.9",
+ "quote 1.0.10",
"syn 1.0.65",
]
@@ -534,8 +534,8 @@ checksum = "f0c960ae2da4de88a91b2d920c2a7233b400bc33cb28453a2987822d8392519b"
dependencies = [
"fnv",
"ident_case",
- "proc-macro2 1.0.28",
- "quote 1.0.9",
+ "proc-macro2 1.0.29",
+ "quote 1.0.10",
"strsim 0.9.3",
"syn 1.0.65",
]
@@ -547,7 +547,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d9b5a2f4ac4969822c62224815d069952656cadc7084fdca9751e6d959189b72"
dependencies = [
"darling_core",
- "quote 1.0.9",
+ "quote 1.0.10",
"syn 1.0.65",
]
@@ -753,9 +753,9 @@ dependencies = [
[[package]]
name = "deno_doc"
-version = "0.14.0"
+version = "0.15.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "3afa937c8bd6a5055a7688d4e9ea1de65947b399ae0f60504ad93277d238facf"
+checksum = "cac0571321c463d3cfa39d03084c9f823bcc812daa7202eb2e89b5f9abd6301f"
dependencies = [
"cfg-if 1.0.0",
"deno_ast",
@@ -798,9 +798,9 @@ dependencies = [
[[package]]
name = "deno_graph"
-version = "0.5.0"
+version = "0.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "be296aae1be28b1f7cea57ce4283abbb04c5013b146410e63b169a96e6b4e184"
+checksum = "1d856f43e3fd84399d9f90200708fdd5f507bb5ee43770a5a88b3dbdf667619e"
dependencies = [
"anyhow",
"cfg-if 1.0.0",
@@ -1001,9 +1001,9 @@ dependencies = [
[[package]]
name = "der"
-version = "0.4.1"
+version = "0.4.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "31e21d2d0f22cde6e88694108429775c0219760a07779bf96503b434a03d7412"
+checksum = "28e98c534e9c8a0483aa01d6f6913bc063de254311bd267c9cf535e9b70e15b2"
dependencies = [
"const-oid",
"crypto-bigint",
@@ -1016,8 +1016,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "40eebddd2156ce1bb37b20bbe5151340a31828b1f2d22ba4141f3531710e38df"
dependencies = [
"convert_case",
- "proc-macro2 1.0.28",
- "quote 1.0.9",
+ "proc-macro2 1.0.29",
+ "quote 1.0.10",
"rustc_version 0.3.3",
"syn 1.0.65",
]
@@ -1039,9 +1039,9 @@ dependencies = [
[[package]]
name = "dissimilar"
-version = "1.0.2"
+version = "1.0.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "fc4b29f4b9bb94bf267d57269fd0706d343a160937108e9619fe380645428abb"
+checksum = "31ad93652f40969dead8d4bf897a41e9462095152eb21c56e5830537e41179dd"
[[package]]
name = "dlopen"
@@ -1068,12 +1068,12 @@ dependencies = [
[[package]]
name = "dprint-core"
-version = "0.46.0"
+version = "0.46.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c5f145afa493cba0217d2c52e256e7626730b8ed97c9db4e20e0db8c242458ca"
+checksum = "df98b7d98583d9d57311b9df81f571f35a6ca3e8675d0128333b2e923dc85bcd"
dependencies = [
"bumpalo",
- "fnv",
+ "rustc-hash",
"serde",
]
@@ -1185,8 +1185,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7c5f0096a91d210159eceb2ff5e1c4da18388a170e1e3ce948aac9c8fdbbf595"
dependencies = [
"heck",
- "proc-macro2 1.0.28",
- "quote 1.0.9",
+ "proc-macro2 1.0.29",
+ "quote 1.0.10",
"syn 1.0.65",
]
@@ -1197,7 +1197,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "78b940da354ae81ef0926c5eaa428207b8f4f091d3956c891dfbd124162bed99"
dependencies = [
"pmutil",
- "proc-macro2 1.0.28",
+ "proc-macro2 1.0.29",
"swc_macros_common",
"syn 1.0.65",
]
@@ -1309,16 +1309,16 @@ version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "479cde5eb168cf5a056dd98f311cbfab7494c216394e4fb9eba0336827a8db93"
dependencies = [
- "proc-macro2 1.0.28",
- "quote 1.0.9",
+ "proc-macro2 1.0.29",
+ "quote 1.0.10",
"syn 1.0.65",
]
[[package]]
name = "flate2"
-version = "1.0.20"
+version = "1.0.22"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "cd3aec53de10fe96d7d8c565eb17f2c687bb5518a2ec453b5b1252964526abe0"
+checksum = "1e6988e897c1c9c485f43b47a529cef42fde0547f9d8d41a7062518f1d8fc53f"
dependencies = [
"cfg-if 1.0.0",
"crc32fast",
@@ -1364,7 +1364,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0951635027ca477be98f8774abd6f0345233439d63f307e47101acb40c7cc63d"
dependencies = [
"pmutil",
- "proc-macro2 1.0.28",
+ "proc-macro2 1.0.29",
"swc_macros_common",
"syn 1.0.65",
]
@@ -1391,9 +1391,9 @@ dependencies = [
[[package]]
name = "fslock"
-version = "0.1.7"
+version = "0.1.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "fbc585f4fe7227b37ef0216444c87ca8ab6051622e4e2bc75d4bed4ea5106148"
+checksum = "57eafdd0c16f57161105ae1b98a1238f97645f2f588438b2949c99a2af9616bf"
dependencies = [
"libc",
"winapi 0.3.9",
@@ -1455,8 +1455,8 @@ checksum = "18e4a4b95cea4b4ccbcf1c5675ca7c4ee4e9e75eb79944d07defde18068f79bb"
dependencies = [
"autocfg 1.0.1",
"proc-macro-hack",
- "proc-macro2 1.0.28",
- "quote 1.0.9",
+ "proc-macro2 1.0.29",
+ "quote 1.0.10",
"syn 1.0.65",
]
@@ -1558,9 +1558,9 @@ dependencies = [
[[package]]
name = "gpu-alloc"
-version = "0.5.0"
+version = "0.5.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c481459c44304a1dfed23bd650bb3912e12c9f77d7871f86d7ed7c9730a52e79"
+checksum = "0e64cbb8d36508d3e19da95e56e196a84f674fc190881f2cc010000798838aa6"
dependencies = [
"bitflags",
"gpu-alloc-types",
@@ -1608,9 +1608,9 @@ dependencies = [
[[package]]
name = "h2"
-version = "0.3.4"
+version = "0.3.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d7f3675cfef6a30c8031cf9e6493ebdc3bb3272a3fea3923c4210d1830e6a472"
+checksum = "6c06815895acec637cd6ed6e9662c935b866d20a106f8361892893a7d9234964"
dependencies = [
"bytes",
"fnv",
@@ -1693,9 +1693,9 @@ dependencies = [
[[package]]
name = "http"
-version = "0.2.4"
+version = "0.2.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "527e8c9ac747e28542699a951517aa9a6945af506cd1f2e1b53a576c17b6cc11"
+checksum = "1323096b05d41827dadeaee54c9981958c0f94e670bc94ed80037d1a7b8b186b"
dependencies = [
"bytes",
"fnv",
@@ -1733,9 +1733,9 @@ checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4"
[[package]]
name = "hyper"
-version = "0.14.12"
+version = "0.14.13"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "13f67199e765030fa08fe0bd581af683f0d5bc04ea09c2b1102012c5fb90e7fd"
+checksum = "15d1cfb9e4f68655fa04c01f59edb405b6074a0f7118ea881e5026e4a1cd8593"
dependencies = [
"bytes",
"futures-channel",
@@ -1748,7 +1748,7 @@ dependencies = [
"httpdate",
"itoa",
"pin-project-lite",
- "socket2 0.4.1",
+ "socket2 0.4.2",
"tokio",
"tower-service",
"tracing",
@@ -1819,9 +1819,9 @@ dependencies = [
[[package]]
name = "inotify"
-version = "0.9.3"
+version = "0.9.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b031475cb1b103ee221afb806a23d35e0570bf7271d7588762ceba8127ed43b3"
+checksum = "9e5fc8f41dbaa9c8492a96c8afffda4f76896ee041d6a57606e70581b80c901f"
dependencies = [
"bitflags",
"inotify-sys",
@@ -1854,9 +1854,9 @@ dependencies = [
[[package]]
name = "instant"
-version = "0.1.10"
+version = "0.1.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "bee0328b1209d157ef001c94dd85b4f8f64139adb0eac2659f4b08382b2f474d"
+checksum = "716d3d89f35ac6a34fd0eed635395f4c3b76fa889338a4632e5231a8684216bd"
dependencies = [
"cfg-if 1.0.0",
]
@@ -1887,8 +1887,8 @@ checksum = "a322dd16d960e322c3d92f541b4c1a4f0a2e81e1fdeee430d8cecc8b72e8015f"
dependencies = [
"Inflector",
"pmutil",
- "proc-macro2 1.0.28",
- "quote 1.0.9",
+ "proc-macro2 1.0.29",
+ "quote 1.0.10",
"syn 1.0.65",
]
@@ -1990,9 +1990,9 @@ dependencies = [
[[package]]
name = "libc"
-version = "0.2.101"
+version = "0.2.103"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "3cb00336871be5ed2c8ed44b60ae9959dc5b9f08539422ed43f09e34ecaeba21"
+checksum = "dd8f7255a17a627354f321ef0055d63b898c6fb27eff628af4d1b66b7331edf6"
[[package]]
name = "libloading"
@@ -2029,9 +2029,9 @@ checksum = "7fb9b38af92608140b86b693604b9ffcc5824240a484d1ecd4795bacb2fe88f3"
[[package]]
name = "lock_api"
-version = "0.4.4"
+version = "0.4.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "0382880606dff6d15c9476c416d18690b72742aa7b605bb6dd6ec9030fbf07eb"
+checksum = "712a4d093c9976e24e7dbca41db895dabcbac38eb5f4045393d17a95bdfb1109"
dependencies = [
"scopeguard",
]
@@ -2100,8 +2100,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ca1d48da0e4a6100b4afd52fae99f36d47964a209624021280ad9ffdd410e83d"
dependencies = [
"heck",
- "proc-macro2 1.0.28",
- "quote 1.0.9",
+ "proc-macro2 1.0.29",
+ "quote 1.0.10",
"syn 1.0.65",
]
@@ -2201,9 +2201,9 @@ dependencies = [
[[package]]
name = "naga"
-version = "0.6.1"
+version = "0.6.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "568344dfe26b6caee4003da5be57bf8c2ef16d9525a2a5561164bcc462687f92"
+checksum = "8c5859e55c51da10b98e7a73068e0a0c5da7bbcae4fc38f86043d0c6d1b917cf"
dependencies = [
"bit-set",
"bitflags",
@@ -2234,9 +2234,9 @@ dependencies = [
[[package]]
name = "nix"
-version = "0.22.1"
+version = "0.22.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e7555d6c7164cc913be1ce7f95cbecdabda61eb2ccd89008524af306fb7f5031"
+checksum = "d3bb9a13fa32bc5aeb64150cd3f32d6cf4c748f8f8a417cce5d2eb976a8370ba"
dependencies = [
"bitflags",
"cc",
@@ -2430,9 +2430,9 @@ dependencies = [
[[package]]
name = "parking_lot"
-version = "0.11.1"
+version = "0.11.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "6d7744ac029df22dca6284efe4e898991d28e3085c706c972bcd7da4a27a15eb"
+checksum = "7d17b78036a60663b797adeaee46f5c9dfebb86948d1255007a1d6be0271ff99"
dependencies = [
"instant",
"lock_api",
@@ -2441,9 +2441,9 @@ dependencies = [
[[package]]
name = "parking_lot_core"
-version = "0.8.3"
+version = "0.8.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "fa7a782938e745763fe6907fc6ba86946d72f49fe7e21de074e08128a99fb018"
+checksum = "d76e8e1493bcac0d2766c42737f34458f1c8c50c0d23bcb24ea953affb273216"
dependencies = [
"cfg-if 1.0.0",
"instant",
@@ -2518,8 +2518,8 @@ dependencies = [
"phf_generator",
"phf_shared",
"proc-macro-hack",
- "proc-macro2 1.0.28",
- "quote 1.0.9",
+ "proc-macro2 1.0.29",
+ "quote 1.0.10",
"syn 1.0.65",
]
@@ -2547,8 +2547,8 @@ version = "1.0.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6e8fe8163d14ce7f0cdac2e040116f22eac817edabff0be91e8aff7e9accf389"
dependencies = [
- "proc-macro2 1.0.28",
- "quote 1.0.9",
+ "proc-macro2 1.0.29",
+ "quote 1.0.10",
"syn 1.0.65",
]
@@ -2566,9 +2566,9 @@ checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184"
[[package]]
name = "pkcs1"
-version = "0.2.3"
+version = "0.2.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "359e7852310174a810f078124edb73c66e88a1a731b2fd586dba34ee32dbe416"
+checksum = "116bee8279d783c0cf370efa1a94632f2108e5ef0bb32df31f051647810a4e2c"
dependencies = [
"der",
"zeroize",
@@ -2576,9 +2576,9 @@ dependencies = [
[[package]]
name = "pkcs8"
-version = "0.7.5"
+version = "0.7.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "fbee84ed13e44dd82689fa18348a49934fa79cc774a344c42fc9b301c71b140a"
+checksum = "ee3ef9b64d26bad0536099c816c6734379e45bbd5f14798def6809e5cc350447"
dependencies = [
"der",
"pkcs1",
@@ -2588,9 +2588,9 @@ dependencies = [
[[package]]
name = "pkg-config"
-version = "0.3.19"
+version = "0.3.20"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "3831453b3449ceb48b6d9c7ad7c96d5ea673e9b470a1dc578c2ce6521230884c"
+checksum = "7c9b1041b4387893b91ee6746cddfc28516aff326a3519fb2adf820932c5e6cb"
[[package]]
name = "pmutil"
@@ -2598,8 +2598,8 @@ version = "0.5.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3894e5d549cccbe44afecf72922f277f603cd4bb0219c8342631ef18fffbe004"
dependencies = [
- "proc-macro2 1.0.28",
- "quote 1.0.9",
+ "proc-macro2 1.0.29",
+ "quote 1.0.10",
"syn 1.0.65",
]
@@ -2634,8 +2634,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c"
dependencies = [
"proc-macro-error-attr",
- "proc-macro2 1.0.28",
- "quote 1.0.9",
+ "proc-macro2 1.0.29",
+ "quote 1.0.10",
"syn 1.0.65",
"version_check",
]
@@ -2646,8 +2646,8 @@ version = "1.0.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869"
dependencies = [
- "proc-macro2 1.0.28",
- "quote 1.0.9",
+ "proc-macro2 1.0.29",
+ "quote 1.0.10",
"version_check",
]
@@ -2674,9 +2674,9 @@ dependencies = [
[[package]]
name = "proc-macro2"
-version = "1.0.28"
+version = "1.0.29"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5c7ed8b8c7b886ea3ed7dde405212185f423ab44682667c8c6dd14aa1d9f6612"
+checksum = "b9f5105d4fdaab20335ca9565e106a5d9b82b6219b5ba735731124ac6711d23d"
dependencies = [
"unicode-xid 0.2.2",
]
@@ -2725,11 +2725,11 @@ dependencies = [
[[package]]
name = "quote"
-version = "1.0.9"
+version = "1.0.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c3d0b9745dc2debf507c8422de05d7226cc1f0644216dfdfead988f9b1ab32a7"
+checksum = "38bc8cc6a5f2e3655e0899c1b848643b2562f853f114bfec7be120678e3ace05"
dependencies = [
- "proc-macro2 1.0.28",
+ "proc-macro2 1.0.29",
]
[[package]]
@@ -2903,9 +2903,9 @@ checksum = "f1382d1f0a252c4bf97dc20d979a2fdd05b024acd7c2ed0f7595d7817666a157"
[[package]]
name = "reqwest"
-version = "0.11.4"
+version = "0.11.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "246e9f61b9bb77df069a947682be06e31ac43ea37862e244a69f177694ea6d22"
+checksum = "51c732d463dd300362ffb44b7b125f299c23d2990411a4253824630ebc7467fb"
dependencies = [
"async-compression",
"base64 0.13.0",
@@ -2926,6 +2926,7 @@ dependencies = [
"pin-project-lite",
"rustls",
"serde",
+ "serde_json",
"serde_urlencoded",
"tokio",
"tokio-rustls",
@@ -2950,9 +2951,9 @@ dependencies = [
[[package]]
name = "retain_mut"
-version = "0.1.3"
+version = "0.1.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e9c17925a9027d298a4603d286befe3f9dc0e8ed02523141914eb628798d6e5b"
+checksum = "448296241d034b96c11173591deaa1302f2c17b56092106c1f92c1bc0183a8c9"
[[package]]
name = "ring"
@@ -2971,9 +2972,9 @@ dependencies = [
[[package]]
name = "ron"
-version = "0.6.4"
+version = "0.6.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "064ea8613fb712a19faf920022ec8ddf134984f100090764a4e1d768f3827f1f"
+checksum = "45005aa836116903a49cf3461474da697cfe66221762c6e95871092009ec86d6"
dependencies = [
"base64 0.13.0",
"bitflags",
@@ -3016,6 +3017,12 @@ dependencies = [
]
[[package]]
+name = "rustc-hash"
+version = "1.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2"
+
+[[package]]
name = "rustc_version"
version = "0.2.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -3100,7 +3107,7 @@ version = "0.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "688599bdab9f42105d0ae1494335a9ffafdeb7d36325e6b10fd4abc5829d6284"
dependencies = [
- "quote 1.0.9",
+ "quote 1.0.10",
"syn 1.0.65",
]
@@ -3166,9 +3173,9 @@ dependencies = [
[[package]]
name = "security-framework-sys"
-version = "2.4.0"
+version = "2.4.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "19133a286e494cc3311c165c4676ccb1fd47bed45b55f9d71fbd784ad4cea6f8"
+checksum = "a9dd14d83160b528b7bfd66439110573efcfbe281b17fc2ca9f39f550d619c7e"
dependencies = [
"core-foundation-sys",
"libc",
@@ -3222,16 +3229,16 @@ version = "1.0.130"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d7bc1a1ab1961464eae040d96713baa5a724a8152c1222492465b54322ec508b"
dependencies = [
- "proc-macro2 1.0.28",
- "quote 1.0.9",
+ "proc-macro2 1.0.29",
+ "quote 1.0.10",
"syn 1.0.65",
]
[[package]]
name = "serde_json"
-version = "1.0.67"
+version = "1.0.68"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a7f9e390c27c3c0ce8bc5d725f6e4d30a29d26659494aa4b17535f7522c5c950"
+checksum = "0f690853975602e1bfe1ccbf50504d67174e3bcf340f23b5ea9992e0587a52d8"
dependencies = [
"indexmap",
"itoa",
@@ -3245,8 +3252,8 @@ version = "0.1.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "98d0516900518c29efa217c298fa1f4e6c6ffc85ae29fd7f4ee48f176e1a9ed5"
dependencies = [
- "proc-macro2 1.0.28",
- "quote 1.0.9",
+ "proc-macro2 1.0.29",
+ "quote 1.0.10",
"syn 1.0.65",
]
@@ -3274,9 +3281,9 @@ dependencies = [
[[package]]
name = "sha-1"
-version = "0.9.7"
+version = "0.9.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1a0c8611594e2ab4ebbf06ec7cbbf0a99450b8570e96cbf5188b5d5f6ef18d81"
+checksum = "99cd6713db3cf16b6c84e06321e049a9b9f699826e16096d23bbcc44d15d51a6"
dependencies = [
"block-buffer",
"cfg-if 1.0.0",
@@ -3287,9 +3294,9 @@ dependencies = [
[[package]]
name = "sha2"
-version = "0.9.5"
+version = "0.9.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b362ae5752fd2137731f9fa25fd4d9058af34666ca1966fb969119cc35719f12"
+checksum = "b69f9a4c9740d74c5baa3fd2e547f9525fa8088a8a958e0ca2409a514e33f5fa"
dependencies = [
"block-buffer",
"cfg-if 1.0.0",
@@ -3325,9 +3332,9 @@ dependencies = [
[[package]]
name = "siphasher"
-version = "0.3.6"
+version = "0.3.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "729a25c17d72b06c68cb47955d44fda88ad2d3e7d77e025663fdd69b93dd71a1"
+checksum = "533494a8f9b724d33625ab53c6c4800f7cc445895924a8ef649222dcb76e938b"
[[package]]
name = "slab"
@@ -3337,18 +3344,18 @@ checksum = "c307a32c1c5c437f38c7fd45d753050587732ba8628319fbdf12a7e289ccc590"
[[package]]
name = "slotmap"
-version = "1.0.5"
+version = "1.0.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a952280edbecfb1d4bd3cf2dbc309dc6ab523e53487c438ae21a6df09fe84bc4"
+checksum = "e1e08e261d0e8f5c43123b7adf3e4ca1690d655377ac93a03b2c9d3e98de1342"
dependencies = [
"version_check",
]
[[package]]
name = "smallvec"
-version = "1.6.1"
+version = "1.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "fe0f37c9e8f3c5a4a66ad655a93c74daac4ad00c441533bf5c6e7990bb42604e"
+checksum = "1ecab6c735a6bb4139c0caafd0cc3635748bbb3acf4550e8138122099251f309"
[[package]]
name = "socket2"
@@ -3363,9 +3370,9 @@ dependencies = [
[[package]]
name = "socket2"
-version = "0.4.1"
+version = "0.4.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "765f090f0e423d2b55843402a07915add955e7d60657db13707a159727326cad"
+checksum = "5dc90fe6c7be1a323296982db1836d1ea9e47b6839496dde9a541bc496df3516"
dependencies = [
"libc",
"winapi 0.3.9",
@@ -3451,8 +3458,8 @@ checksum = "f24c8e5e19d22a726626f1a5e16fe15b132dcf21d10177fa5a45ce7962996b97"
dependencies = [
"phf_generator",
"phf_shared",
- "proc-macro2 1.0.28",
- "quote 1.0.9",
+ "proc-macro2 1.0.29",
+ "quote 1.0.10",
]
[[package]]
@@ -3462,8 +3469,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f584cc881e9e5f1fd6bf827b0444aa94c30d8fe6378cf241071b5f5700b2871f"
dependencies = [
"pmutil",
- "proc-macro2 1.0.28",
- "quote 1.0.9",
+ "proc-macro2 1.0.29",
+ "quote 1.0.10",
"swc_macros_common",
"syn 1.0.65",
]
@@ -3498,9 +3505,9 @@ dependencies = [
[[package]]
name = "swc_bundler"
-version = "0.58.0"
+version = "0.58.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "3d42b9e0dff902d05ea17813dc3537a29641a35634864d55ee91215c7cf5430f"
+checksum = "acfdeafdcfbfb0f4ca1838d775d8124da81168c4131a340c5b6682f078b34719"
dependencies = [
"ahash 0.7.4",
"anyhow",
@@ -3527,9 +3534,9 @@ dependencies = [
[[package]]
name = "swc_common"
-version = "0.12.0"
+version = "0.12.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7ca21695d45b5374d7eafedda065de3cab2337a4707642302f71caaa4c0d338a"
+checksum = "acc9ad667d8c220e1625e189933ffd9d29b643317aca7a211db2600ef622879e"
dependencies = [
"ahash 0.7.4",
"ast_node",
@@ -3567,9 +3574,9 @@ dependencies = [
[[package]]
name = "swc_ecma_codegen"
-version = "0.70.2"
+version = "0.70.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7940bff62e5caf62fe6732ce4f07e52c3c208cb58cd9299f7f7c92dddab2bf72"
+checksum = "e08e335585f54f85fa910ab771660f69d809f8f70160d4cf912c9a855970db81"
dependencies = [
"bitflags",
"num-bigint",
@@ -3588,8 +3595,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "51af418026cb4ea588e2b15fa206c44e09a3184b718e12a0919729c7c3ad20d3"
dependencies = [
"pmutil",
- "proc-macro2 1.0.28",
- "quote 1.0.9",
+ "proc-macro2 1.0.29",
+ "quote 1.0.10",
"swc_macros_common",
"syn 1.0.65",
]
@@ -3608,9 +3615,9 @@ dependencies = [
[[package]]
name = "swc_ecma_loader"
-version = "0.18.0"
+version = "0.18.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e9695aa0b1394a1954da965a00a6a9624aa1c9f49148f72f1c01f5bd9c39d74c"
+checksum = "c2f718f0335f9ab7437fecf9f3d73ae6a24c03a3d3f46910a68261703d407f03"
dependencies = [
"anyhow",
"fxhash",
@@ -3623,9 +3630,9 @@ dependencies = [
[[package]]
name = "swc_ecma_parser"
-version = "0.70.2"
+version = "0.70.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "042a901352b84cefbb64916a010ee33f621a7e341ced2b2fa60035858f3146a5"
+checksum = "04b2e3c0ac2132a7ab0679fe8bb89ad0a3311c6c5f44a22bffc559647e1f2e47"
dependencies = [
"either",
"enum_kind",
@@ -3664,9 +3671,9 @@ dependencies = [
[[package]]
name = "swc_ecma_transforms_base"
-version = "0.31.1"
+version = "0.31.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b26e191df68943565f22059d31b02967e60a62c4f76533b5b5106546785a8e2e"
+checksum = "fdcd1f3c1ca665605dcb2a7d6b536aeff1ed1da2eeebf32238d890e42b494312"
dependencies = [
"fxhash",
"once_cell",
@@ -3762,9 +3769,9 @@ dependencies = [
[[package]]
name = "swc_ecma_transforms_typescript"
-version = "0.40.2"
+version = "0.40.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "98099e3db58fb758715736ea9c8fa68d238e6527f0bfb4a3af0bf7ea063b9162"
+checksum = "96bb017f32fd1559f10245112eeac1cf10e3b76415495e159b10c82ee667ac9e"
dependencies = [
"fxhash",
"serde",
@@ -3827,8 +3834,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8c8f200a2eaed938e7c1a685faaa66e6d42fa9e17da5f62572d3cbc335898f5e"
dependencies = [
"pmutil",
- "proc-macro2 1.0.28",
- "quote 1.0.9",
+ "proc-macro2 1.0.29",
+ "quote 1.0.10",
"syn 1.0.65",
]
@@ -3839,8 +3846,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "08ed2e930f5a1a4071fe62c90fd3a296f6030e5d94bfe13993244423caf59a78"
dependencies = [
"pmutil",
- "proc-macro2 1.0.28",
- "quote 1.0.9",
+ "proc-macro2 1.0.29",
+ "quote 1.0.10",
"syn 1.0.65",
]
@@ -3862,8 +3869,8 @@ checksum = "e3b2825fee79f10d0166e8e650e79c7a862fb991db275743083f07555d7641f0"
dependencies = [
"Inflector",
"pmutil",
- "proc-macro2 1.0.28",
- "quote 1.0.9",
+ "proc-macro2 1.0.29",
+ "quote 1.0.10",
"swc_macros_common",
"syn 1.0.65",
]
@@ -3885,8 +3892,8 @@ version = "1.0.65"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f3a1d708c221c5a612956ef9f75b37e454e88d1f7b899fbd3a18d4252012d663"
dependencies = [
- "proc-macro2 1.0.28",
- "quote 1.0.9",
+ "proc-macro2 1.0.29",
+ "quote 1.0.10",
"unicode-xid 0.2.2",
]
@@ -3896,8 +3903,8 @@ version = "0.12.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "474aaa926faa1603c40b7885a9eaea29b444d1cb2850cb7c0e37bb1a4182f4fa"
dependencies = [
- "proc-macro2 1.0.28",
- "quote 1.0.9",
+ "proc-macro2 1.0.29",
+ "quote 1.0.10",
"syn 1.0.65",
"unicode-xid 0.2.2",
]
@@ -3991,21 +3998,21 @@ dependencies = [
[[package]]
name = "thiserror"
-version = "1.0.26"
+version = "1.0.30"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "93119e4feac1cbe6c798c34d3a53ea0026b0b1de6a120deef895137c0529bfe2"
+checksum = "854babe52e4df1653706b98fcfc05843010039b406875930a70e4d9644e5c417"
dependencies = [
"thiserror-impl",
]
[[package]]
name = "thiserror-impl"
-version = "1.0.26"
+version = "1.0.30"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "060d69a0afe7796bf42e9e2ff91f5ee691fb15c53d38b4b62a9a53eb23164745"
+checksum = "aa32fd3f627f367fe16f893e2597ae3c05020f8bba2666a4e6ea73d377e5714b"
dependencies = [
- "proc-macro2 1.0.28",
- "quote 1.0.9",
+ "proc-macro2 1.0.29",
+ "quote 1.0.10",
"syn 1.0.65",
]
@@ -4022,9 +4029,9 @@ dependencies = [
[[package]]
name = "tinyvec"
-version = "1.3.1"
+version = "1.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "848a1e1181b9f6753b5e96a092749e29b11d19ede67dfbbd6c7dc7e0f49b5338"
+checksum = "f83b2a3d4d9091d0abd7eba4dc2710b1718583bd4d8992e2190720ea38f391f7"
dependencies = [
"tinyvec_macros",
]
@@ -4037,9 +4044,9 @@ checksum = "cda74da7e1a664f795bb1f8a87ec406fb89a02522cf6e50620d016add6dbbf5c"
[[package]]
name = "tokio"
-version = "1.10.1"
+version = "1.12.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "92036be488bb6594459f2e03b60e42df6f937fe6ca5c5ffdcb539c6b84dc40f5"
+checksum = "c2c2416fdedca8443ae44b4527de1ea633af61d8f7169ffa6e72c5b53d24efcc"
dependencies = [
"autocfg 1.0.1",
"bytes",
@@ -4057,12 +4064,12 @@ dependencies = [
[[package]]
name = "tokio-macros"
-version = "1.3.0"
+version = "1.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "54473be61f4ebe4efd09cec9bd5d16fa51d70ea0192213d754d2d500457db110"
+checksum = "154794c8f499c2619acd19e839294703e9e32e7630ef5f46ea80d4ef0fbee5eb"
dependencies = [
- "proc-macro2 1.0.28",
- "quote 1.0.9",
+ "proc-macro2 1.0.29",
+ "quote 1.0.10",
"syn 1.0.65",
]
@@ -4107,9 +4114,9 @@ dependencies = [
[[package]]
name = "tokio-util"
-version = "0.6.7"
+version = "0.6.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1caa0b0c8d94a049db56b5acf8cba99dc0623aab1b26d5b5f5e2d945846b3592"
+checksum = "08d3725d3efa29485e87311c5b699de63cde14b00ed4d256b8318aa30ca452cd"
dependencies = [
"bytes",
"futures-core",
@@ -4136,9 +4143,9 @@ checksum = "360dfd1d6d30e05fda32ace2c8c70e9c0a9da713275777f5a4dbb8a1893930c6"
[[package]]
name = "tracing"
-version = "0.1.26"
+version = "0.1.29"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "09adeb8c97449311ccd28a427f96fb563e7fd31aabf994189879d9da2394b89d"
+checksum = "375a639232caf30edfc78e8d89b2d4c375515393e7af7e16f01cd96917fb2105"
dependencies = [
"cfg-if 1.0.0",
"pin-project-lite",
@@ -4147,9 +4154,9 @@ dependencies = [
[[package]]
name = "tracing-core"
-version = "0.1.19"
+version = "0.1.21"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "2ca517f43f0fb96e0c3072ed5c275fe5eece87e8cb52f4a77b69226d3b1c9df8"
+checksum = "1f4ed65637b8390770814083d20756f87bfa2c21bf2f110babdc5438351746e4"
dependencies = [
"lazy_static",
]
@@ -4285,9 +4292,9 @@ dependencies = [
[[package]]
name = "typenum"
-version = "1.13.0"
+version = "1.14.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "879f6906492a7cd215bfa4cf595b600146ccfac0c79bcbd1f3000162af5e8b06"
+checksum = "b63708a265f51345575b27fe43f9500ad611579e764c79edbc2037b1121959ec"
[[package]]
name = "ucd-trie"
@@ -4353,9 +4360,9 @@ dependencies = [
[[package]]
name = "unicode-bidi"
-version = "0.3.6"
+version = "0.3.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "246f4c42e67e7a4e3c6106ff716a5d067d4132a642840b242e357e468a2a0085"
+checksum = "1a01404663e3db436ed2746d9fefef640d868edae3cceb81c3b8d5732fda678f"
[[package]]
name = "unicode-normalization"
@@ -4374,9 +4381,9 @@ checksum = "8895849a949e7845e06bd6dc1aa51731a103c42707010a5b591c0038fb73385b"
[[package]]
name = "unicode-width"
-version = "0.1.8"
+version = "0.1.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "9337591893a19b88d8d87f2cec1e73fad5cdfd10e5a6f349f498ad6ea2ffb1e3"
+checksum = "3ed742d4ea2bd1176e236172c8429aaf54486e7ac098db29ffe6529e0ce50973"
[[package]]
name = "unicode-xid"
@@ -4502,8 +4509,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8fe8f61dba8e5d645a4d8132dc7a0a66861ed5e1045d2c0ed940fab33bac0fbe"
dependencies = [
"cfg-if 1.0.0",
- "serde",
- "serde_json",
"wasm-bindgen-macro",
]
@@ -4516,8 +4521,8 @@ dependencies = [
"bumpalo",
"lazy_static",
"log",
- "proc-macro2 1.0.28",
- "quote 1.0.9",
+ "proc-macro2 1.0.29",
+ "quote 1.0.10",
"syn 1.0.65",
"wasm-bindgen-shared",
]
@@ -4540,7 +4545,7 @@ version = "0.2.72"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0ef9aa01d36cda046f797c57959ff5f3c615c9cc63997a8d545831ec7976819b"
dependencies = [
- "quote 1.0.9",
+ "quote 1.0.10",
"wasm-bindgen-macro-support",
]
@@ -4550,8 +4555,8 @@ version = "0.2.72"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "96eb45c1b2ee33545a813a92dbb53856418bf7eb54ab34f7f7ff1448a5b3735d"
dependencies = [
- "proc-macro2 1.0.28",
- "quote 1.0.9",
+ "proc-macro2 1.0.29",
+ "quote 1.0.10",
"syn 1.0.65",
"wasm-bindgen-backend",
"wasm-bindgen-shared",
@@ -4594,9 +4599,9 @@ dependencies = [
[[package]]
name = "wgpu-core"
-version = "0.10.1"
+version = "0.10.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a81393a8d78194c7433d63afed2bf5f64feb126aef9ddd18eca3b16e9fac0cea"
+checksum = "f963c62473a36e3cef6c58181f2ed6d0d38d2043d970dbed46cb197190090c99"
dependencies = [
"arrayvec 0.7.1",
"bitflags",
@@ -4617,9 +4622,9 @@ dependencies = [
[[package]]
name = "wgpu-hal"
-version = "0.10.2"
+version = "0.10.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "31bc8def88a1416997bff66b1f6788a4bd497546ad985db2d442d099c48f4246"
+checksum = "27cd894b17bff1958ee93da1cc991fd64bf99667746d4bd2a7403855f4d37fe2"
dependencies = [
"arrayvec 0.7.1",
"ash",
@@ -4739,30 +4744,30 @@ dependencies = [
[[package]]
name = "winres"
-version = "0.1.11"
+version = "0.1.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ff4fb510bbfe5b8992ff15f77a2e6fe6cf062878f0eda00c0f44963a807ca5dc"
+checksum = "b68db261ef59e9e52806f688020631e987592bd83619edccda9c47d42cde4f6c"
dependencies = [
"toml",
]
[[package]]
name = "zeroize"
-version = "1.4.1"
+version = "1.4.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "377db0846015f7ae377174787dd452e1c5f5a9050bc6f954911d01f116daa0cd"
+checksum = "bf68b08513768deaa790264a7fac27a58cbf2705cfcdc9448362229217d7e970"
dependencies = [
"zeroize_derive",
]
[[package]]
name = "zeroize_derive"
-version = "1.1.0"
+version = "1.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a2c1e130bebaeab2f23886bf9acbaca14b092408c452543c857f66399cd6dab1"
+checksum = "bdff2024a851a322b08f179173ae2ba620445aef1e838f0c196820eade4ae0c7"
dependencies = [
- "proc-macro2 1.0.28",
- "quote 1.0.9",
+ "proc-macro2 1.0.29",
+ "quote 1.0.10",
"syn 1.0.65",
"synstructure",
]
diff --git a/cli/Cargo.toml b/cli/Cargo.toml
index e4efdf356..28a22b359 100644
--- a/cli/Cargo.toml
+++ b/cli/Cargo.toml
@@ -41,8 +41,8 @@ winres = "0.1.11"
[dependencies]
deno_ast = { version = "0.2.0", features = ["bundler", "codegen", "dep_graph", "module_specifier", "proposal", "react", "sourcemap", "transforms", "typescript", "view", "visit"] }
deno_core = { version = "0.102.0", path = "../core" }
-deno_doc = "0.14.0"
-deno_graph = "0.5.0"
+deno_doc = "0.15.0"
+deno_graph = "0.6.0"
deno_lint = { version = "0.16.0", features = ["docs"] }
deno_runtime = { version = "0.28.0", path = "../runtime" }
deno_tls = { version = "0.7.0", path = "../ext/tls" }
diff --git a/cli/ast/bundle_hook.rs b/cli/ast/bundle_hook.rs
index 6b29bd8dd..3f47946f7 100644
--- a/cli/ast/bundle_hook.rs
+++ b/cli/ast/bundle_hook.rs
@@ -4,6 +4,7 @@ use deno_ast::swc::bundler::ModuleRecord;
use deno_ast::swc::common::Span;
use deno_core::error::AnyError;
+/// This contains the logic for Deno to rewrite the `import.meta` when bundling.
pub struct BundleHook;
impl Hook for BundleHook {
diff --git a/cli/cache.rs b/cli/cache.rs
new file mode 100644
index 000000000..2c94172b8
--- /dev/null
+++ b/cli/cache.rs
@@ -0,0 +1,349 @@
+// Copyright 2018-2021 the Deno authors. All rights reserved. MIT license.
+
+use crate::disk_cache::DiskCache;
+use crate::file_fetcher::FileFetcher;
+
+use deno_core::error::AnyError;
+use deno_core::futures::future;
+use deno_core::futures::FutureExt;
+use deno_core::serde::Deserialize;
+use deno_core::serde::Serialize;
+use deno_core::serde_json;
+use deno_core::ModuleSpecifier;
+use deno_graph::source::CacheInfo;
+use deno_graph::source::LoadFuture;
+use deno_graph::source::LoadResponse;
+use deno_graph::source::Loader;
+use deno_runtime::permissions::Permissions;
+use std::collections::HashMap;
+use std::sync::Arc;
+
+#[derive(Debug, Deserialize, Serialize)]
+pub struct EmitMetadata {
+ pub version_hash: String,
+}
+
+pub(crate) enum CacheType {
+ Declaration,
+ Emit,
+ SourceMap,
+ TypeScriptBuildInfo,
+ Version,
+}
+
+/// A trait which provides a concise implementation to getting and setting
+/// values in a cache.
+pub(crate) trait Cacher {
+ /// Get a value from the cache.
+ fn get(
+ &self,
+ cache_type: CacheType,
+ specifier: &ModuleSpecifier,
+ ) -> Option<String>;
+ /// Set a value in the cache.
+ fn set(
+ &mut self,
+ cache_type: CacheType,
+ specifier: &ModuleSpecifier,
+ value: String,
+ ) -> Result<(), AnyError>;
+}
+
+/// Combines the cacher trait along with the deno_graph Loader trait to provide
+/// a single interface to be able to load and cache modules when building a
+/// graph.
+pub(crate) trait CacherLoader: Cacher + Loader {
+ fn as_cacher(&self) -> &dyn Cacher;
+ fn as_mut_loader(&mut self) -> &mut dyn Loader;
+ fn as_mut_cacher(&mut self) -> &mut dyn Cacher;
+}
+
+/// A "wrapper" for the FileFetcher and DiskCache for the Deno CLI that provides
+/// a concise interface to the DENO_DIR when building module graphs.
+pub(crate) struct FetchCacher {
+ disk_cache: DiskCache,
+ dynamic_permissions: Permissions,
+ file_fetcher: Arc<FileFetcher>,
+ root_permissions: Permissions,
+}
+
+impl FetchCacher {
+ pub fn new(
+ disk_cache: DiskCache,
+ file_fetcher: FileFetcher,
+ root_permissions: Permissions,
+ dynamic_permissions: Permissions,
+ ) -> Self {
+ let file_fetcher = Arc::new(file_fetcher);
+
+ Self {
+ disk_cache,
+ dynamic_permissions,
+ file_fetcher,
+ root_permissions,
+ }
+ }
+
+ fn get_emit_metadata(
+ &self,
+ specifier: &ModuleSpecifier,
+ ) -> Option<EmitMetadata> {
+ let filename = self
+ .disk_cache
+ .get_cache_filename_with_extension(specifier, "meta")?;
+ let bytes = self.disk_cache.get(&filename).ok()?;
+ serde_json::from_slice(&bytes).ok()
+ }
+
+ fn set_emit_metadata(
+ &self,
+ specifier: &ModuleSpecifier,
+ data: EmitMetadata,
+ ) -> Result<(), AnyError> {
+ let filename = self
+ .disk_cache
+ .get_cache_filename_with_extension(specifier, "meta")
+ .unwrap();
+ let bytes = serde_json::to_vec(&data)?;
+ self.disk_cache.set(&filename, &bytes).map_err(|e| e.into())
+ }
+}
+
+impl Loader for FetchCacher {
+ fn get_cache_info(&self, specifier: &ModuleSpecifier) -> Option<CacheInfo> {
+ let local = self.file_fetcher.get_local_path(specifier)?;
+ if local.is_file() {
+ let location = &self.disk_cache.location;
+ let emit = self
+ .disk_cache
+ .get_cache_filename_with_extension(specifier, "js")
+ .map(|p| location.join(p))
+ .filter(|p| p.is_file());
+ let map = self
+ .disk_cache
+ .get_cache_filename_with_extension(specifier, "js.map")
+ .map(|p| location.join(p))
+ .filter(|p| p.is_file());
+ Some(CacheInfo {
+ local: Some(local),
+ emit,
+ map,
+ })
+ } else {
+ None
+ }
+ }
+
+ fn load(
+ &mut self,
+ specifier: &ModuleSpecifier,
+ is_dynamic: bool,
+ ) -> LoadFuture {
+ let specifier = specifier.clone();
+ let mut permissions = if is_dynamic {
+ self.dynamic_permissions.clone()
+ } else {
+ self.root_permissions.clone()
+ };
+ let file_fetcher = self.file_fetcher.clone();
+
+ async move {
+ let load_result = file_fetcher
+ .fetch(&specifier, &mut permissions)
+ .await
+ .map_or_else(
+ |err| {
+ if let Some(err) = err.downcast_ref::<std::io::Error>() {
+ if err.kind() == std::io::ErrorKind::NotFound {
+ return Ok(None);
+ }
+ }
+ Err(err)
+ },
+ |file| {
+ Ok(Some(LoadResponse {
+ specifier: file.specifier,
+ maybe_headers: file.maybe_headers,
+ content: file.source,
+ }))
+ },
+ );
+
+ (specifier, load_result)
+ }
+ .boxed()
+ }
+}
+
+impl Cacher for FetchCacher {
+ fn get(
+ &self,
+ cache_type: CacheType,
+ specifier: &ModuleSpecifier,
+ ) -> Option<String> {
+ let extension = match cache_type {
+ CacheType::Declaration => "d.ts",
+ CacheType::Emit => "js",
+ CacheType::SourceMap => "js.map",
+ CacheType::TypeScriptBuildInfo => "buildinfo",
+ CacheType::Version => {
+ return self.get_emit_metadata(specifier).map(|d| d.version_hash)
+ }
+ };
+ let filename = self
+ .disk_cache
+ .get_cache_filename_with_extension(specifier, extension)?;
+ self
+ .disk_cache
+ .get(&filename)
+ .ok()
+ .map(|b| String::from_utf8(b).ok())
+ .flatten()
+ }
+
+ fn set(
+ &mut self,
+ cache_type: CacheType,
+ specifier: &ModuleSpecifier,
+ value: String,
+ ) -> Result<(), AnyError> {
+ let extension = match cache_type {
+ CacheType::Declaration => "d.ts",
+ CacheType::Emit => "js",
+ CacheType::SourceMap => "js.map",
+ CacheType::TypeScriptBuildInfo => "buildinfo",
+ CacheType::Version => {
+ let data = if let Some(mut data) = self.get_emit_metadata(specifier) {
+ data.version_hash = value;
+ data
+ } else {
+ EmitMetadata {
+ version_hash: value,
+ }
+ };
+ return self.set_emit_metadata(specifier, data);
+ }
+ };
+ let filename = self
+ .disk_cache
+ .get_cache_filename_with_extension(specifier, extension)
+ .unwrap();
+ self
+ .disk_cache
+ .set(&filename, value.as_bytes())
+ .map_err(|e| e.into())
+ }
+}
+
+impl CacherLoader for FetchCacher {
+ fn as_cacher(&self) -> &dyn Cacher {
+ self
+ }
+
+ fn as_mut_loader(&mut self) -> &mut dyn Loader {
+ self
+ }
+
+ fn as_mut_cacher(&mut self) -> &mut dyn Cacher {
+ self
+ }
+}
+
+/// An in memory cache that is used by the runtime `Deno.emit()` API to provide
+/// the same behavior as the disk cache when sources are provided.
+#[derive(Debug)]
+pub(crate) struct MemoryCacher {
+ sources: HashMap<String, Arc<String>>,
+ declarations: HashMap<ModuleSpecifier, String>,
+ emits: HashMap<ModuleSpecifier, String>,
+ maps: HashMap<ModuleSpecifier, String>,
+ build_infos: HashMap<ModuleSpecifier, String>,
+ versions: HashMap<ModuleSpecifier, String>,
+}
+
+impl MemoryCacher {
+ pub fn new(sources: HashMap<String, Arc<String>>) -> Self {
+ Self {
+ sources,
+ declarations: HashMap::default(),
+ emits: HashMap::default(),
+ maps: HashMap::default(),
+ build_infos: HashMap::default(),
+ versions: HashMap::default(),
+ }
+ }
+}
+
+impl Loader for MemoryCacher {
+ fn load(
+ &mut self,
+ specifier: &ModuleSpecifier,
+ _is_dynamic: bool,
+ ) -> LoadFuture {
+ let mut specifier_str = specifier.to_string();
+ if !self.sources.contains_key(&specifier_str) {
+ specifier_str = specifier_str.replace("file:///", "/");
+ if !self.sources.contains_key(&specifier_str) {
+ specifier_str = specifier_str[3..].to_string();
+ }
+ }
+ let response = self.sources.get(&specifier_str).map(|c| LoadResponse {
+ specifier: specifier.clone(),
+ maybe_headers: None,
+ content: c.to_owned(),
+ });
+ Box::pin(future::ready((specifier.clone(), Ok(response))))
+ }
+}
+
+impl Cacher for MemoryCacher {
+ fn get(
+ &self,
+ cache_type: CacheType,
+ specifier: &ModuleSpecifier,
+ ) -> Option<String> {
+ match cache_type {
+ CacheType::Declaration => self.declarations.get(specifier).cloned(),
+ CacheType::Emit => self.emits.get(specifier).cloned(),
+ CacheType::SourceMap => self.maps.get(specifier).cloned(),
+ CacheType::TypeScriptBuildInfo => {
+ self.build_infos.get(specifier).cloned()
+ }
+ CacheType::Version => self.versions.get(specifier).cloned(),
+ }
+ }
+
+ fn set(
+ &mut self,
+ cache_type: CacheType,
+ specifier: &ModuleSpecifier,
+ value: String,
+ ) -> Result<(), AnyError> {
+ match cache_type {
+ CacheType::Declaration => {
+ self.declarations.insert(specifier.clone(), value)
+ }
+ CacheType::Emit => self.emits.insert(specifier.clone(), value),
+ CacheType::SourceMap => self.maps.insert(specifier.clone(), value),
+ CacheType::TypeScriptBuildInfo => {
+ self.build_infos.insert(specifier.clone(), value)
+ }
+ CacheType::Version => self.versions.insert(specifier.clone(), value),
+ };
+ Ok(())
+ }
+}
+
+impl CacherLoader for MemoryCacher {
+ fn as_cacher(&self) -> &dyn Cacher {
+ self
+ }
+
+ fn as_mut_loader(&mut self) -> &mut dyn Loader {
+ self
+ }
+
+ fn as_mut_cacher(&mut self) -> &mut dyn Cacher {
+ self
+ }
+}
diff --git a/cli/compat.rs b/cli/compat.rs
index 4b64a501d..f120ccd04 100644
--- a/cli/compat.rs
+++ b/cli/compat.rs
@@ -4,6 +4,7 @@ use deno_core::url::Url;
use std::collections::HashMap;
static STD_NODE: &str = "https://deno.land/std/node/";
+static GLOBAL_MODULE: &str = "global.ts";
static SUPPORTED_MODULES: &[&str] = &[
"assert",
@@ -50,8 +51,15 @@ static SUPPORTED_MODULES: &[&str] = &[
"zlib",
];
-pub fn get_node_globals_url() -> Url {
- Url::parse(&format!("{}global.ts", STD_NODE)).unwrap()
+lazy_static::lazy_static! {
+ static ref GLOBAL_URL_STR: String = format!("{}{}", STD_NODE, GLOBAL_MODULE);
+ pub(crate) static ref GLOBAL_URL: Url = Url::parse(&GLOBAL_URL_STR).unwrap();
+ static ref COMPAT_IMPORT_URL: Url = Url::parse("flags:compat").unwrap();
+}
+
+/// Provide imports into a module graph when the compat flag is true.
+pub(crate) fn get_node_imports() -> Vec<(Url, Vec<String>)> {
+ vec![(COMPAT_IMPORT_URL.clone(), vec![GLOBAL_URL_STR.clone()])]
}
/// Create a map that can be used to update import map.
diff --git a/cli/config_file.rs b/cli/config_file.rs
index dd002ca97..3a71d41a9 100644
--- a/cli/config_file.rs
+++ b/cli/config_file.rs
@@ -10,6 +10,7 @@ use deno_core::serde::Serializer;
use deno_core::serde_json;
use deno_core::serde_json::json;
use deno_core::serde_json::Value;
+use deno_core::ModuleSpecifier;
use std::collections::BTreeMap;
use std::collections::HashMap;
use std::fmt;
@@ -401,6 +402,19 @@ impl ConfigFile {
}
}
+ /// If the configuration file contains "extra" modules (like TypeScript
+ /// `"types"`) options, return them as imports to be added to a module graph.
+ pub fn to_maybe_imports(
+ &self,
+ ) -> Option<Vec<(ModuleSpecifier, Vec<String>)>> {
+ let compiler_options_value = self.json.compiler_options.as_ref()?;
+ let compiler_options: CompilerOptions =
+ serde_json::from_value(compiler_options_value.clone()).ok()?;
+ let referrer = ModuleSpecifier::from_file_path(&self.path).ok()?;
+ let types = compiler_options.types?;
+ Some(vec![(referrer, types)])
+ }
+
pub fn to_fmt_config(&self) -> Result<Option<FmtConfig>, AnyError> {
if let Some(config) = self.json.fmt.clone() {
let fmt_config: FmtConfig = serde_json::from_value(config)
diff --git a/cli/diagnostics.rs b/cli/diagnostics.rs
index 459475117..50d7213f1 100644
--- a/cli/diagnostics.rs
+++ b/cli/diagnostics.rs
@@ -6,9 +6,8 @@ use deno_core::serde::Deserialize;
use deno_core::serde::Deserializer;
use deno_core::serde::Serialize;
use deno_core::serde::Serializer;
-use deno_core::ModuleSpecifier;
+use deno_graph::ModuleGraphError;
use regex::Regex;
-use std::collections::HashMap;
use std::error::Error;
use std::fmt;
@@ -353,20 +352,17 @@ impl Diagnostics {
Diagnostics(diagnostics)
}
- pub fn extend_graph_errors(
- &mut self,
- errors: HashMap<ModuleSpecifier, String>,
- ) {
- self.0.extend(errors.into_iter().map(|(s, e)| Diagnostic {
+ pub fn extend_graph_errors(&mut self, errors: Vec<ModuleGraphError>) {
+ self.0.extend(errors.into_iter().map(|err| Diagnostic {
category: DiagnosticCategory::Error,
code: 900001,
start: None,
end: None,
- message_text: Some(e),
+ message_text: Some(err.to_string()),
message_chain: None,
source: None,
source_line: None,
- file_name: Some(s.to_string()),
+ file_name: Some(err.specifier().to_string()),
related_information: None,
}));
}
diff --git a/cli/emit.rs b/cli/emit.rs
new file mode 100644
index 000000000..5a1cf61d1
--- /dev/null
+++ b/cli/emit.rs
@@ -0,0 +1,927 @@
+// Copyright 2018-2021 the Deno authors. All rights reserved. MIT license.
+
+//! The collection of APIs to be able to take `deno_graph` module graphs and
+//! populate a cache, emit files, and transform a graph into the structures for
+//! loading into an isolate.
+
+use crate::ast;
+use crate::cache::CacheType;
+use crate::cache::Cacher;
+use crate::colors;
+use crate::config_file::ConfigFile;
+use crate::config_file::IgnoredCompilerOptions;
+use crate::config_file::TsConfig;
+use crate::diagnostics::Diagnostics;
+use crate::tsc;
+use crate::version;
+
+use deno_ast::swc;
+use deno_core::error::anyhow;
+use deno_core::error::custom_error;
+use deno_core::error::AnyError;
+use deno_core::error::Context;
+use deno_core::serde::Deserialize;
+use deno_core::serde::Deserializer;
+use deno_core::serde::Serialize;
+use deno_core::serde::Serializer;
+use deno_core::serde_json::json;
+use deno_core::serde_json::Value;
+use deno_core::ModuleSource;
+use deno_core::ModuleSpecifier;
+use deno_graph::MediaType;
+use deno_graph::ModuleGraph;
+use deno_graph::ModuleGraphError;
+use deno_graph::ResolutionError;
+use std::collections::HashMap;
+use std::collections::HashSet;
+use std::fmt;
+use std::rc::Rc;
+use std::result;
+use std::sync::Arc;
+use std::time::Instant;
+
+/// Represents the "default" type library that should be used when type
+/// checking the code in the module graph. Note that a user provided config
+/// of `"lib"` would override this value.
+#[derive(Debug, Clone, Eq, PartialEq)]
+pub(crate) enum TypeLib {
+ DenoWindow,
+ DenoWorker,
+ UnstableDenoWindow,
+ UnstableDenoWorker,
+}
+
+impl Default for TypeLib {
+ fn default() -> Self {
+ Self::DenoWindow
+ }
+}
+
+impl Serialize for TypeLib {
+ fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
+ where
+ S: Serializer,
+ {
+ let value = match self {
+ Self::DenoWindow => vec!["deno.window".to_string()],
+ Self::DenoWorker => vec!["deno.worker".to_string()],
+ Self::UnstableDenoWindow => {
+ vec!["deno.window".to_string(), "deno.unstable".to_string()]
+ }
+ Self::UnstableDenoWorker => {
+ vec!["deno.worker".to_string(), "deno.unstable".to_string()]
+ }
+ };
+ Serialize::serialize(&value, serializer)
+ }
+}
+
+type Modules = HashMap<ModuleSpecifier, Result<ModuleSource, AnyError>>;
+
+/// A structure representing stats from an emit operation for a graph.
+#[derive(Clone, Debug, Default, Eq, PartialEq)]
+pub(crate) struct Stats(pub Vec<(String, u32)>);
+
+impl<'de> Deserialize<'de> for Stats {
+ fn deserialize<D>(deserializer: D) -> result::Result<Self, D::Error>
+ where
+ D: Deserializer<'de>,
+ {
+ let items: Vec<(String, u32)> = Deserialize::deserialize(deserializer)?;
+ Ok(Stats(items))
+ }
+}
+
+impl Serialize for Stats {
+ fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
+ where
+ S: Serializer,
+ {
+ Serialize::serialize(&self.0, serializer)
+ }
+}
+
+impl fmt::Display for Stats {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ writeln!(f, "Compilation statistics:")?;
+ for (key, value) in self.0.clone() {
+ writeln!(f, " {}: {}", key, value)?;
+ }
+
+ Ok(())
+ }
+}
+
+/// An enum that represents the base tsc configuration to return.
+pub(crate) enum ConfigType {
+ /// Return a configuration for bundling, using swc to emit the bundle. This is
+ /// independent of type checking.
+ Bundle,
+ /// Return a configuration to use tsc to type check and optionally emit. This
+ /// is independent of either bundling or just emitting via swc
+ Check { lib: TypeLib, tsc_emit: bool },
+ /// Return a configuration to use swc to emit single module files.
+ Emit,
+ /// Return a configuration as a base for the runtime `Deno.emit()` API.
+ RuntimeEmit { tsc_emit: bool },
+}
+
+/// For a given configuration type and optionally a configuration file, return a
+/// tuple of the resulting `TsConfig` struct and optionally any user
+/// configuration options that were ignored.
+pub(crate) fn get_ts_config(
+ config_type: ConfigType,
+ maybe_config_file: Option<&ConfigFile>,
+ maybe_user_config: Option<&HashMap<String, Value>>,
+) -> Result<(TsConfig, Option<IgnoredCompilerOptions>), AnyError> {
+ let mut ts_config = match config_type {
+ ConfigType::Bundle => TsConfig::new(json!({
+ "checkJs": false,
+ "emitDecoratorMetadata": false,
+ "importsNotUsedAsValues": "remove",
+ "inlineSourceMap": false,
+ "inlineSources": false,
+ "sourceMap": false,
+ "jsx": "react",
+ "jsxFactory": "React.createElement",
+ "jsxFragmentFactory": "React.Fragment",
+ })),
+ ConfigType::Check { tsc_emit, lib } => {
+ let mut ts_config = TsConfig::new(json!({
+ "allowJs": true,
+ "experimentalDecorators": true,
+ "incremental": true,
+ "jsx": "react",
+ "isolatedModules": true,
+ "lib": lib,
+ "module": "esnext",
+ "strict": true,
+ "target": "esnext",
+ "tsBuildInfoFile": "deno:///.tsbuildinfo",
+ "useDefineForClassFields": true,
+ // TODO(@kitsonk) remove for Deno 2.0
+ "useUnknownInCatchVariables": false,
+ }));
+ if tsc_emit {
+ ts_config.merge(&json!({
+ "emitDecoratorMetadata": false,
+ "importsNotUsedAsValues": "remove",
+ "inlineSourceMap": true,
+ "inlineSources": true,
+ "outDir": "deno://",
+ "removeComments": true,
+ }));
+ } else {
+ ts_config.merge(&json!({
+ "noEmit": true,
+ }));
+ }
+ ts_config
+ }
+ ConfigType::Emit => TsConfig::new(json!({
+ "checkJs": false,
+ "emitDecoratorMetadata": false,
+ "importsNotUsedAsValues": "remove",
+ "inlineSourceMap": true,
+ // TODO(@kitsonk) make this actually work when https://github.com/swc-project/swc/issues/2218 addressed.
+ "inlineSources": true,
+ "sourceMap": false,
+ "jsx": "react",
+ "jsxFactory": "React.createElement",
+ "jsxFragmentFactory": "React.Fragment",
+ })),
+ ConfigType::RuntimeEmit { tsc_emit } => {
+ let mut ts_config = TsConfig::new(json!({
+ "allowJs": true,
+ "checkJs": false,
+ "emitDecoratorMetadata": false,
+ "experimentalDecorators": true,
+ "importsNotUsedAsValues": "remove",
+ "incremental": true,
+ "isolatedModules": true,
+ "jsx": "react",
+ "jsxFactory": "React.createElement",
+ "jsxFragmentFactory": "React.Fragment",
+ "lib": TypeLib::DenoWindow,
+ "module": "esnext",
+ "removeComments": true,
+ "inlineSourceMap": false,
+ "inlineSources": false,
+ "sourceMap": true,
+ "strict": true,
+ "target": "esnext",
+ "tsBuildInfoFile": "deno:///.tsbuildinfo",
+ "useDefineForClassFields": true,
+ // TODO(@kitsonk) remove for Deno 2.0
+ "useUnknownInCatchVariables": false,
+ }));
+ if tsc_emit {
+ ts_config.merge(&json!({
+ "importsNotUsedAsValues": "remove",
+ "outDir": "deno://",
+ }));
+ } else {
+ ts_config.merge(&json!({
+ "noEmit": true,
+ }));
+ }
+ ts_config
+ }
+ };
+ let maybe_ignored_options = if let Some(user_options) = maybe_user_config {
+ ts_config.merge_user_config(user_options)?
+ } else {
+ ts_config.merge_tsconfig_from_config_file(maybe_config_file)?
+ };
+ Ok((ts_config, maybe_ignored_options))
+}
+
+/// Transform the graph into root specifiers that we can feed `tsc`. We have to
+/// provide the media type for root modules because `tsc` does not "resolve" the
+/// media type like other modules, as well as a root specifier needs any
+/// redirects resolved. If we aren't checking JavaScript, we need to include all
+/// the emittable files in the roots, so they get type checked and optionally
+/// emitted, otherwise they would be ignored if only imported into JavaScript.
+fn get_root_names(
+ graph: &ModuleGraph,
+ check_js: bool,
+) -> Vec<(ModuleSpecifier, MediaType)> {
+ if !check_js {
+ graph
+ .specifiers()
+ .into_iter()
+ .filter_map(|(_, r)| match r {
+ Ok((s, mt)) => match &mt {
+ MediaType::TypeScript | MediaType::Tsx | MediaType::Jsx => {
+ Some((s, mt))
+ }
+ _ => None,
+ },
+ _ => None,
+ })
+ .collect()
+ } else {
+ graph
+ .roots
+ .iter()
+ .filter_map(|s| graph.get(s).map(|m| (m.specifier.clone(), m.media_type)))
+ .collect()
+ }
+}
+
+/// A hashing function that takes the source code, version and optionally a
+/// user provided config and generates a string hash which can be stored to
+/// determine if the cached emit is valid or not.
+fn get_version(source_bytes: &[u8], config_bytes: &[u8]) -> String {
+ crate::checksum::gen(&[
+ source_bytes,
+ version::deno().as_bytes(),
+ config_bytes,
+ ])
+}
+
+/// Determine if a given media type is emittable or not.
+fn is_emittable(media_type: &MediaType, include_js: bool) -> bool {
+ match &media_type {
+ MediaType::TypeScript | MediaType::Tsx | MediaType::Jsx => true,
+ MediaType::JavaScript => include_js,
+ _ => false,
+ }
+}
+
+/// Options for performing a check of a module graph. Note that the decision to
+/// emit or not is determined by the `ts_config` settings.
+pub(crate) struct CheckOptions {
+ /// Set the debug flag on the TypeScript type checker.
+ pub debug: bool,
+ /// If true, any files emitted will be cached, even if there are diagnostics
+ /// produced. If false, if there are diagnostics, caching emitted files will
+ /// be skipped.
+ pub emit_with_diagnostics: bool,
+ /// The module specifier to the configuration file, passed to tsc so that
+ /// configuration related diagnostics are properly formed.
+ pub maybe_config_specifier: Option<ModuleSpecifier>,
+ /// The derived tsconfig that should be used when checking.
+ pub ts_config: TsConfig,
+}
+
+/// The result of a check or emit of a module graph. Note that the actual
+/// emitted sources are stored in the cache and are not returned in the result.
+#[derive(Debug, Default)]
+pub(crate) struct CheckEmitResult {
+ pub diagnostics: Diagnostics,
+ pub stats: Stats,
+}
+
+/// Given a module graph, type check the module graph and optionally emit
+/// modules, updating the cache as appropriate. Emitting is determined by the
+/// `ts_config` supplied in the options, and if emitting, the files are stored
+/// in the cache.
+///
+/// It is expected that it is determined if a check and/or emit is validated
+/// before the function is called.
+pub(crate) fn check_and_maybe_emit(
+ graph: Arc<ModuleGraph>,
+ cache: &mut dyn Cacher,
+ options: CheckOptions,
+) -> Result<CheckEmitResult, AnyError> {
+ let check_js = options.ts_config.get_check_js();
+ let root_names = get_root_names(&graph, check_js);
+ // while there might be multiple roots, we can't "merge" the build info, so we
+ // try to retrieve the build info for first root, which is the most common use
+ // case.
+ let maybe_tsbuildinfo =
+ cache.get(CacheType::TypeScriptBuildInfo, &graph.roots[0]);
+ // to make tsc build info work, we need to consistently hash modules, so that
+ // tsc can better determine if an emit is still valid or not, so we provide
+ // that data here.
+ let hash_data = vec![
+ options.ts_config.as_bytes(),
+ version::deno().as_bytes().to_owned(),
+ ];
+ let config_bytes = options.ts_config.as_bytes();
+
+ let response = tsc::exec(tsc::Request {
+ config: options.ts_config,
+ debug: options.debug,
+ graph: graph.clone(),
+ hash_data,
+ maybe_config_specifier: options.maybe_config_specifier,
+ maybe_tsbuildinfo,
+ root_names,
+ })?;
+
+ if let Some(info) = &response.maybe_tsbuildinfo {
+ // while we retrieve the build info for just the first module, it can be
+ // used for all the roots in the graph, so we will cache it for all roots
+ for root in &graph.roots {
+ cache.set(CacheType::TypeScriptBuildInfo, root, info.clone())?;
+ }
+ }
+ // sometimes we want to emit when there are diagnostics, and sometimes we
+ // don't. tsc will always return an emit if there are diagnostics
+ if (response.diagnostics.is_empty() || options.emit_with_diagnostics)
+ && !response.emitted_files.is_empty()
+ {
+ for emit in response.emitted_files.into_iter() {
+ if let Some(specifiers) = emit.maybe_specifiers {
+ assert!(specifiers.len() == 1);
+ // The emitted specifier might not be the file specifier we want, so we
+ // resolve it via the graph.
+ let specifier = graph.resolve(&specifiers[0]);
+ let (media_type, source) = if let Some(module) = graph.get(&specifier) {
+ (&module.media_type, module.source.clone())
+ } else {
+ log::debug!("module missing, skipping emit for {}", specifier);
+ continue;
+ };
+ // Sometimes if `tsc` sees a CommonJS file it will _helpfully_ output it
+ // to ESM, which we don't really want to do unless someone has enabled
+ // check_js.
+ if !check_js && *media_type == MediaType::JavaScript {
+ log::debug!("skipping emit for {}", specifier);
+ continue;
+ }
+ match emit.media_type {
+ MediaType::JavaScript => {
+ let version = get_version(source.as_bytes(), &config_bytes);
+ cache.set(CacheType::Version, &specifier, version)?;
+ cache.set(CacheType::Emit, &specifier, emit.data)?;
+ }
+ MediaType::SourceMap => {
+ cache.set(CacheType::SourceMap, &specifier, emit.data)?;
+ }
+ // this only occurs with the runtime emit, but we are using the same
+ // code paths, so we handle it here.
+ MediaType::Dts => {
+ cache.set(CacheType::Declaration, &specifier, emit.data)?;
+ }
+ _ => unreachable!(),
+ }
+ }
+ }
+ }
+
+ Ok(CheckEmitResult {
+ diagnostics: response.diagnostics,
+ stats: response.stats,
+ })
+}
+
+pub(crate) enum BundleType {
+ /// Return the emitted contents of the program as a single "flattened" ES
+ /// module.
+ Module,
+ /// Return the emitted contents of the program as a single script that
+ /// executes the program using an immediately invoked function execution
+ /// (IIFE).
+ Classic,
+}
+
+impl From<BundleType> for swc::bundler::ModuleType {
+ fn from(bundle_type: BundleType) -> Self {
+ match bundle_type {
+ BundleType::Classic => Self::Iife,
+ BundleType::Module => Self::Es,
+ }
+ }
+}
+
+pub(crate) struct BundleOptions {
+ pub bundle_type: BundleType,
+ pub ts_config: TsConfig,
+}
+
+/// A module loader for swc which does the appropriate retrieval and transpiling
+/// of modules from the graph.
+struct BundleLoader<'a> {
+ cm: Rc<swc::common::SourceMap>,
+ emit_options: &'a ast::EmitOptions,
+ globals: &'a deno_ast::swc::common::Globals,
+ graph: &'a ModuleGraph,
+}
+
+impl swc::bundler::Load for BundleLoader<'_> {
+ fn load(
+ &self,
+ file_name: &swc::common::FileName,
+ ) -> Result<swc::bundler::ModuleData, AnyError> {
+ match file_name {
+ swc::common::FileName::Url(specifier) => {
+ if let Some(m) = self.graph.get(specifier) {
+ let (fm, module) = ast::transpile_module(
+ specifier,
+ &m.source,
+ m.media_type,
+ self.emit_options,
+ self.globals,
+ self.cm.clone(),
+ )?;
+ Ok(swc::bundler::ModuleData {
+ fm,
+ module,
+ helpers: Default::default(),
+ })
+ } else {
+ Err(anyhow!(
+ "Module \"{}\" unexpectedly missing when bundling.",
+ specifier
+ ))
+ }
+ }
+ _ => unreachable!(
+ "Received a request for unsupported filename {:?}",
+ file_name
+ ),
+ }
+ }
+}
+
+/// A resolver implementation for swc that resolves specifiers from the graph.
+struct BundleResolver<'a>(&'a ModuleGraph);
+
+impl swc::bundler::Resolve for BundleResolver<'_> {
+ fn resolve(
+ &self,
+ referrer: &swc::common::FileName,
+ specifier: &str,
+ ) -> Result<swc::common::FileName, AnyError> {
+ let referrer = if let swc::common::FileName::Url(referrer) = referrer {
+ referrer
+ } else {
+ unreachable!(
+ "An unexpected referrer was passed when bundling: {:?}",
+ referrer
+ );
+ };
+ if let Some(specifier) =
+ self.0.resolve_dependency(specifier, referrer, false)
+ {
+ Ok(deno_ast::swc::common::FileName::Url(specifier.clone()))
+ } else {
+ Err(anyhow!(
+ "Cannot resolve \"{}\" from \"{}\".",
+ specifier,
+ referrer
+ ))
+ }
+ }
+}
+
+/// Given a module graph, generate and return a bundle of the graph and
+/// optionally its source map. Unlike emitting with `check_and_maybe_emit` and
+/// `emit`, which store the emitted modules in the cache, this function simply
+/// returns the output.
+pub(crate) fn bundle(
+ graph: &ModuleGraph,
+ options: BundleOptions,
+) -> Result<(String, Option<String>), AnyError> {
+ let emit_options: ast::EmitOptions = options.ts_config.into();
+
+ let cm = Rc::new(swc::common::SourceMap::new(
+ swc::common::FilePathMapping::empty(),
+ ));
+ let globals = swc::common::Globals::new();
+ let loader = BundleLoader {
+ graph,
+ emit_options: &emit_options,
+ globals: &globals,
+ cm: cm.clone(),
+ };
+ let resolver = BundleResolver(graph);
+ let config = swc::bundler::Config {
+ module: options.bundle_type.into(),
+ ..Default::default()
+ };
+ // This hook will rewrite the `import.meta` when bundling to give a consistent
+ // behavior between bundled and unbundled code.
+ let hook = Box::new(ast::BundleHook);
+ let bundler = swc::bundler::Bundler::new(
+ &globals,
+ cm.clone(),
+ loader,
+ resolver,
+ config,
+ hook,
+ );
+ let mut entries = HashMap::new();
+ entries.insert(
+ "bundle".to_string(),
+ swc::common::FileName::Url(graph.roots[0].clone()),
+ );
+ let output = bundler
+ .bundle(entries)
+ .context("Unable to output during bundling.")?;
+ let mut buf = Vec::new();
+ let mut srcmap = Vec::new();
+ {
+ let cfg = swc::codegen::Config { minify: false };
+ let wr = Box::new(swc::codegen::text_writer::JsWriter::new(
+ cm.clone(),
+ "\n",
+ &mut buf,
+ Some(&mut srcmap),
+ ));
+ let mut emitter = swc::codegen::Emitter {
+ cfg,
+ cm: cm.clone(),
+ comments: None,
+ wr,
+ };
+ emitter
+ .emit_module(&output[0].module)
+ .context("Unable to emit during bundling.")?;
+ }
+ let mut code =
+ String::from_utf8(buf).context("Emitted code is an invalid string.")?;
+ let mut maybe_map: Option<String> = None;
+ {
+ let mut buf = Vec::new();
+ cm.build_source_map_from(&mut srcmap, None)
+ .to_writer(&mut buf)?;
+ if emit_options.inline_source_map {
+ let encoded_map = format!(
+ "//# sourceMappingURL=data:application/json;base64,{}\n",
+ base64::encode(buf)
+ );
+ code.push_str(&encoded_map);
+ } else if emit_options.source_map {
+ maybe_map = Some(String::from_utf8(buf)?);
+ }
+ }
+
+ Ok((code, maybe_map))
+}
+
+pub(crate) struct EmitOptions {
+ pub ts_config: TsConfig,
+ pub reload_exclusions: HashSet<ModuleSpecifier>,
+ pub reload: bool,
+}
+
+/// Given a module graph, emit any appropriate modules and cache them.
+pub(crate) fn emit(
+ graph: &ModuleGraph,
+ cache: &mut dyn Cacher,
+ options: EmitOptions,
+) -> Result<CheckEmitResult, AnyError> {
+ let start = Instant::now();
+ let config_bytes = options.ts_config.as_bytes();
+ let include_js = options.ts_config.get_check_js();
+ let emit_options = options.ts_config.into();
+
+ let mut emit_count = 0_u32;
+ let mut file_count = 0_u32;
+ for module in graph.modules() {
+ file_count += 1;
+ if !is_emittable(&module.media_type, include_js) {
+ continue;
+ }
+ let needs_reload =
+ options.reload && !options.reload_exclusions.contains(&module.specifier);
+ let version = get_version(module.source.as_bytes(), &config_bytes);
+ let is_valid = cache
+ .get(CacheType::Version, &module.specifier)
+ .map_or(false, |v| {
+ v == get_version(module.source.as_bytes(), &config_bytes)
+ });
+ if is_valid && !needs_reload {
+ continue;
+ }
+ let (emit, maybe_map) =
+ ast::transpile(&module.parsed_source, &emit_options)?;
+ emit_count += 1;
+ cache.set(CacheType::Emit, &module.specifier, emit)?;
+ if let Some(map) = maybe_map {
+ cache.set(CacheType::SourceMap, &module.specifier, map)?;
+ }
+ if !is_valid {
+ cache.set(CacheType::Version, &module.specifier, version)?;
+ }
+ }
+
+ let stats = Stats(vec![
+ ("Files".to_string(), file_count),
+ ("Emitted".to_string(), emit_count),
+ ("Total time".to_string(), start.elapsed().as_millis() as u32),
+ ]);
+
+ Ok(CheckEmitResult {
+ diagnostics: Diagnostics::default(),
+ stats,
+ })
+}
+
+/// Check the sub-resource integrity of a module graph, exiting if the graph is
+/// not valid.
+pub(crate) fn lock(graph: &ModuleGraph) {
+ if let Err(err) = graph.lock() {
+ log::error!("{} {}", colors::red("error:"), err);
+ std::process::exit(10);
+ }
+}
+
+/// Check a module graph to determine if the graph contains anything that
+/// is required to be emitted to be valid. It determines what modules in the
+/// graph are emittable and for those that are emittable, if there is currently
+/// a valid emit in the cache.
+pub(crate) fn valid_emit(
+ graph: &ModuleGraph,
+ cache: &dyn Cacher,
+ ts_config: &TsConfig,
+ reload: bool,
+ reload_exclusions: &HashSet<ModuleSpecifier>,
+) -> bool {
+ let config_bytes = ts_config.as_bytes();
+ let emit_js = ts_config.get_check_js();
+ graph
+ .specifiers()
+ .iter()
+ .filter(|(_, r)| match r {
+ Ok((_, MediaType::TypeScript))
+ | Ok((_, MediaType::Tsx))
+ | Ok((_, MediaType::Jsx)) => true,
+ Ok((_, MediaType::JavaScript)) => emit_js,
+ _ => false,
+ })
+ .all(|(_, r)| {
+ if let Ok((s, _)) = r {
+ if reload && !reload_exclusions.contains(s) {
+ // we are reloading and the specifier isn't excluded from being
+ // reloaded
+ false
+ } else if let Some(version) = cache.get(CacheType::Version, s) {
+ if let Some(module) = graph.get(s) {
+ version == get_version(module.source.as_bytes(), &config_bytes)
+ } else {
+ // We have a source module in the graph we can't find, so the emit is
+ // clearly wrong
+ false
+ }
+ } else {
+ // A module that requires emitting doesn't have a version, so it doesn't
+ // have a valid emit
+ false
+ }
+ } else {
+ // Something in the module graph is missing, but that doesn't mean the
+ // emit is invalid
+ true
+ }
+ })
+}
+
+/// An adapter struct to make a deno_graph::ModuleGraphError display as expected
+/// in the Deno CLI.
+#[derive(Debug)]
+pub(crate) struct GraphError(pub ModuleGraphError);
+
+impl std::error::Error for GraphError {}
+
+impl From<ModuleGraphError> for GraphError {
+ fn from(err: ModuleGraphError) -> Self {
+ Self(err)
+ }
+}
+
+impl fmt::Display for GraphError {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ match &self.0 {
+ ModuleGraphError::ResolutionError(err) => {
+ if matches!(
+ err,
+ ResolutionError::InvalidDowngrade(_, _)
+ | ResolutionError::InvalidLocalImport(_, _)
+ ) {
+ write!(f, "{}", err.to_string_with_span())
+ } else {
+ self.0.fmt(f)
+ }
+ }
+ _ => self.0.fmt(f),
+ }
+ }
+}
+
+/// Convert a module graph to a map of "files", which are used by the runtime
+/// emit to be passed back to the caller.
+pub(crate) fn to_file_map(
+ graph: &ModuleGraph,
+ cache: &dyn Cacher,
+) -> HashMap<String, String> {
+ let mut files = HashMap::new();
+ for (_, result) in graph.specifiers().into_iter() {
+ if let Ok((specifier, media_type)) = result {
+ if let Some(emit) = cache.get(CacheType::Emit, &specifier) {
+ files.insert(format!("{}.js", specifier), emit);
+ if let Some(map) = cache.get(CacheType::SourceMap, &specifier) {
+ files.insert(format!("{}.js.map", specifier), map);
+ }
+ } else if media_type == MediaType::JavaScript
+ || media_type == MediaType::Unknown
+ {
+ if let Some(module) = graph.get(&specifier) {
+ files.insert(specifier.to_string(), module.source.to_string());
+ }
+ }
+ if let Some(declaration) = cache.get(CacheType::Declaration, &specifier) {
+ files.insert(format!("{}.d.ts", specifier), declaration);
+ }
+ }
+ }
+ files
+}
+
+/// Convert a module graph to a map of module sources, which are used by
+/// `deno_core` to load modules into V8.
+pub(crate) fn to_module_sources(
+ graph: &ModuleGraph,
+ cache: &dyn Cacher,
+) -> Modules {
+ graph
+ .specifiers()
+ .into_iter()
+ .map(|(requested_specifier, r)| match r {
+ Err(err) => (requested_specifier, Err(err.into())),
+ Ok((found_specifier, media_type)) => {
+ // First we check to see if there is an emitted file in the cache.
+ if let Some(code) = cache.get(CacheType::Emit, &found_specifier) {
+ (
+ requested_specifier.clone(),
+ Ok(ModuleSource {
+ code,
+ module_url_found: found_specifier.to_string(),
+ module_url_specified: requested_specifier.to_string(),
+ }),
+ )
+ // Then if the file is JavaScript (or unknown) and wasn't emitted, we
+ // will load the original source code in the module.
+ } else if media_type == MediaType::JavaScript
+ || media_type == MediaType::Unknown
+ {
+ if let Some(module) = graph.get(&found_specifier) {
+ (
+ requested_specifier.clone(),
+ Ok(ModuleSource {
+ code: module.source.as_str().to_string(),
+ module_url_found: module.specifier.to_string(),
+ module_url_specified: requested_specifier.to_string(),
+ }),
+ )
+ } else {
+ unreachable!(
+ "unexpected module missing from graph: {}",
+ found_specifier
+ )
+ }
+ // Otherwise we will add a not found error.
+ } else {
+ (
+ requested_specifier.clone(),
+ Err(custom_error(
+ "NotFound",
+ format!(
+ "Emitted code for \"{}\" not found.",
+ requested_specifier
+ ),
+ )),
+ )
+ }
+ }
+ })
+ .collect()
+}
+
+#[cfg(test)]
+mod tests {
+ use super::*;
+ use crate::cache::MemoryCacher;
+
+ #[test]
+ fn test_is_emittable() {
+ assert!(is_emittable(&MediaType::TypeScript, false));
+ assert!(!is_emittable(&MediaType::Dts, false));
+ assert!(is_emittable(&MediaType::Tsx, false));
+ assert!(!is_emittable(&MediaType::JavaScript, false));
+ assert!(is_emittable(&MediaType::JavaScript, true));
+ assert!(is_emittable(&MediaType::Jsx, false));
+ assert!(!is_emittable(&MediaType::Json, false));
+ }
+
+ async fn setup<S: AsRef<str>>(
+ root: S,
+ sources: Vec<(S, S)>,
+ ) -> (ModuleGraph, MemoryCacher) {
+ let roots = vec![ModuleSpecifier::parse(root.as_ref()).unwrap()];
+ let sources = sources
+ .into_iter()
+ .map(|(s, c)| (s.as_ref().to_string(), Arc::new(c.as_ref().to_string())))
+ .collect();
+ let mut cache = MemoryCacher::new(sources);
+ let graph = deno_graph::create_graph(
+ roots, false, None, &mut cache, None, None, None,
+ )
+ .await;
+ (graph, cache)
+ }
+
+ #[tokio::test]
+ async fn test_to_module_sources_emitted() {
+ let (graph, mut cache) = setup(
+ "https://example.com/a.ts",
+ vec![("https://example.com/a.ts", r#"console.log("hello deno");"#)],
+ )
+ .await;
+ let (ts_config, _) = get_ts_config(ConfigType::Emit, None, None).unwrap();
+ emit(
+ &graph,
+ &mut cache,
+ EmitOptions {
+ ts_config,
+ reload_exclusions: HashSet::default(),
+ reload: false,
+ },
+ )
+ .unwrap();
+ let modules = to_module_sources(&graph, &cache);
+ assert_eq!(modules.len(), 1);
+ let root = ModuleSpecifier::parse("https://example.com/a.ts").unwrap();
+ let maybe_result = modules.get(&root);
+ assert!(maybe_result.is_some());
+ let result = maybe_result.unwrap();
+ assert!(result.is_ok());
+ let module_source = result.as_ref().unwrap();
+ assert!(module_source
+ .code
+ .starts_with(r#"console.log("hello deno");"#));
+ }
+
+ #[tokio::test]
+ async fn test_to_module_sources_not_emitted() {
+ let (graph, mut cache) = setup(
+ "https://example.com/a.js",
+ vec![("https://example.com/a.js", r#"console.log("hello deno");"#)],
+ )
+ .await;
+ let (ts_config, _) = get_ts_config(ConfigType::Emit, None, None).unwrap();
+ emit(
+ &graph,
+ &mut cache,
+ EmitOptions {
+ ts_config,
+ reload_exclusions: HashSet::default(),
+ reload: false,
+ },
+ )
+ .unwrap();
+ let modules = to_module_sources(&graph, &cache);
+ assert_eq!(modules.len(), 1);
+ let root = ModuleSpecifier::parse("https://example.com/a.js").unwrap();
+ let maybe_result = modules.get(&root);
+ assert!(maybe_result.is_some());
+ let result = maybe_result.unwrap();
+ assert!(result.is_ok());
+ let module_source = result.as_ref().unwrap();
+ assert_eq!(module_source.code, r#"console.log("hello deno");"#);
+ }
+}
diff --git a/cli/errors.rs b/cli/errors.rs
index c26479b44..b09c3c01a 100644
--- a/cli/errors.rs
+++ b/cli/errors.rs
@@ -9,8 +9,12 @@
//! Diagnostics are compile-time type errors, whereas JsErrors are runtime
//! exceptions.
+use crate::emit::GraphError;
+
use deno_ast::Diagnostic;
use deno_core::error::AnyError;
+use deno_graph::ModuleGraphError;
+use deno_graph::ResolutionError;
use import_map::ImportMapError;
fn get_import_map_error_class(_: &ImportMapError) -> &'static str {
@@ -21,6 +25,34 @@ fn get_diagnostic_class(_: &Diagnostic) -> &'static str {
"SyntaxError"
}
+fn get_graph_error_class(err: &GraphError) -> &'static str {
+ get_module_graph_error_class(&err.0)
+}
+
+pub(crate) fn get_module_graph_error_class(
+ err: &ModuleGraphError,
+) -> &'static str {
+ match err {
+ ModuleGraphError::LoadingErr(_, err) => get_error_class_name(err.as_ref()),
+ ModuleGraphError::InvalidSource(_, _) => "SyntaxError",
+ ModuleGraphError::ParseErr(_, diagnostic) => {
+ get_diagnostic_class(diagnostic)
+ }
+ ModuleGraphError::ResolutionError(err) => get_resolution_error_class(err),
+ ModuleGraphError::UnsupportedMediaType(_, _) => "TypeError",
+ ModuleGraphError::Missing(_) => "NotFound",
+ }
+}
+
+fn get_resolution_error_class(err: &ResolutionError) -> &'static str {
+ match err {
+ ResolutionError::ResolverError(err, _, _) => {
+ get_error_class_name(err.as_ref())
+ }
+ _ => "TypeError",
+ }
+}
+
pub(crate) fn get_error_class_name(e: &AnyError) -> &'static str {
deno_runtime::errors::get_error_class_name(e)
.or_else(|| {
@@ -28,6 +60,15 @@ pub(crate) fn get_error_class_name(e: &AnyError) -> &'static str {
.map(get_import_map_error_class)
})
.or_else(|| e.downcast_ref::<Diagnostic>().map(get_diagnostic_class))
+ .or_else(|| e.downcast_ref::<GraphError>().map(get_graph_error_class))
+ .or_else(|| {
+ e.downcast_ref::<ModuleGraphError>()
+ .map(get_module_graph_error_class)
+ })
+ .or_else(|| {
+ e.downcast_ref::<ResolutionError>()
+ .map(get_resolution_error_class)
+ })
.unwrap_or_else(|| {
panic!(
"Error '{}' contains boxed error of unknown type:{}",
diff --git a/cli/file_fetcher.rs b/cli/file_fetcher.rs
index c4c89fec1..0527cbac1 100644
--- a/cli/file_fetcher.rs
+++ b/cli/file_fetcher.rs
@@ -371,7 +371,9 @@ impl FileFetcher {
})?;
let mut headers = HashMap::new();
headers.insert("content-type".to_string(), content_type);
- self.http_cache.set(specifier, headers, source.as_bytes())?;
+ self
+ .http_cache
+ .set(specifier, headers.clone(), source.as_bytes())?;
Ok(File {
local,
@@ -379,7 +381,7 @@ impl FileFetcher {
media_type,
source: Arc::new(source),
specifier: specifier.clone(),
- maybe_headers: None,
+ maybe_headers: Some(headers),
})
}
@@ -433,7 +435,9 @@ impl FileFetcher {
})?;
let mut headers = HashMap::new();
headers.insert("content-type".to_string(), content_type);
- self.http_cache.set(specifier, headers, source.as_bytes())?;
+ self
+ .http_cache
+ .set(specifier, headers.clone(), source.as_bytes())?;
Ok(File {
local,
@@ -441,7 +445,7 @@ impl FileFetcher {
media_type,
source: Arc::new(source),
specifier: specifier.clone(),
- maybe_headers: None,
+ maybe_headers: Some(headers),
})
}
/// Asynchronously fetch remote source file specified by the URL following
@@ -573,6 +577,14 @@ impl FileFetcher {
}
}
+ pub fn get_local_path(&self, specifier: &ModuleSpecifier) -> Option<PathBuf> {
+ if specifier.scheme() == "file" {
+ specifier.to_file_path().ok()
+ } else {
+ self.http_cache.get_cache_filename(specifier)
+ }
+ }
+
/// Get the location of the current HTTP cache associated with the fetcher.
pub fn get_http_cache_location(&self) -> PathBuf {
self.http_cache.location.clone()
diff --git a/cli/info.rs b/cli/info.rs
deleted file mode 100644
index 0f062028e..000000000
--- a/cli/info.rs
+++ /dev/null
@@ -1,513 +0,0 @@
-// Copyright 2018-2021 the Deno authors. All rights reserved. MIT license.
-
-use crate::colors;
-
-use deno_ast::MediaType;
-use deno_core::resolve_url;
-use deno_core::serde::Serialize;
-use deno_core::ModuleSpecifier;
-use std::collections::HashSet;
-use std::fmt;
-use std::iter::Iterator;
-use std::path::PathBuf;
-
-const SIBLING_CONNECTOR: char = '├';
-const LAST_SIBLING_CONNECTOR: char = '└';
-const CHILD_DEPS_CONNECTOR: char = '┬';
-const CHILD_NO_DEPS_CONNECTOR: char = '─';
-const VERTICAL_CONNECTOR: char = '│';
-const EMPTY_CONNECTOR: char = ' ';
-
-#[derive(Debug, Serialize, Ord, PartialOrd, Eq, PartialEq)]
-#[serde(rename_all = "camelCase")]
-pub struct ModuleGraphInfoDep {
- pub specifier: String,
- #[serde(skip_serializing_if = "is_false")]
- pub is_dynamic: bool,
- #[serde(rename = "code", skip_serializing_if = "Option::is_none")]
- pub maybe_code: Option<ModuleSpecifier>,
- #[serde(rename = "type", skip_serializing_if = "Option::is_none")]
- pub maybe_type: Option<ModuleSpecifier>,
-}
-
-fn is_false(b: &bool) -> bool {
- !b
-}
-
-impl ModuleGraphInfoDep {
- fn write_info<S: AsRef<str> + fmt::Display + Clone>(
- &self,
- f: &mut fmt::Formatter,
- prefix: S,
- last: bool,
- modules: &[ModuleGraphInfoMod],
- seen: &mut HashSet<ModuleSpecifier>,
- ) -> fmt::Result {
- let maybe_code = self
- .maybe_code
- .as_ref()
- .and_then(|s| modules.iter().find(|m| &m.specifier == s));
- let maybe_type = self
- .maybe_type
- .as_ref()
- .and_then(|s| modules.iter().find(|m| &m.specifier == s));
- match (maybe_code, maybe_type) {
- (Some(code), Some(types)) => {
- code.write_info(f, prefix.clone(), false, false, modules, seen)?;
- types.write_info(f, prefix, last, true, modules, seen)
- }
- (Some(code), None) => {
- code.write_info(f, prefix, last, false, modules, seen)
- }
- (None, Some(types)) => {
- types.write_info(f, prefix, last, true, modules, seen)
- }
- _ => Ok(()),
- }
- }
-}
-
-#[derive(Debug, Serialize, Ord, PartialOrd, Eq, PartialEq)]
-#[serde(rename_all = "camelCase")]
-pub struct ModuleGraphInfoMod {
- pub specifier: ModuleSpecifier,
- pub dependencies: Vec<ModuleGraphInfoDep>,
- #[serde(rename = "typeDependency", skip_serializing_if = "Option::is_none")]
- pub maybe_type_dependency: Option<ModuleGraphInfoDep>,
- #[serde(skip_serializing_if = "Option::is_none")]
- pub size: Option<usize>,
- #[serde(skip_serializing_if = "Option::is_none")]
- pub media_type: Option<MediaType>,
- #[serde(skip_serializing_if = "Option::is_none")]
- pub local: Option<PathBuf>,
- #[serde(skip_serializing_if = "Option::is_none")]
- pub checksum: Option<String>,
- #[serde(skip_serializing_if = "Option::is_none")]
- pub emit: Option<PathBuf>,
- #[serde(skip_serializing_if = "Option::is_none")]
- pub map: Option<PathBuf>,
- #[serde(skip_serializing_if = "Option::is_none")]
- pub error: Option<String>,
-}
-
-impl Default for ModuleGraphInfoMod {
- fn default() -> Self {
- ModuleGraphInfoMod {
- specifier: resolve_url("https://deno.land/x/mod.ts").unwrap(),
- dependencies: Vec::new(),
- maybe_type_dependency: None,
- size: None,
- media_type: None,
- local: None,
- checksum: None,
- emit: None,
- map: None,
- error: None,
- }
- }
-}
-
-impl ModuleGraphInfoMod {
- fn write_info<S: AsRef<str> + fmt::Display>(
- &self,
- f: &mut fmt::Formatter,
- prefix: S,
- last: bool,
- type_dep: bool,
- modules: &[ModuleGraphInfoMod],
- seen: &mut HashSet<ModuleSpecifier>,
- ) -> fmt::Result {
- let was_seen = seen.contains(&self.specifier);
- let sibling_connector = if last {
- LAST_SIBLING_CONNECTOR
- } else {
- SIBLING_CONNECTOR
- };
- let child_connector = if (self.dependencies.is_empty()
- && self.maybe_type_dependency.is_none())
- || was_seen
- {
- CHILD_NO_DEPS_CONNECTOR
- } else {
- CHILD_DEPS_CONNECTOR
- };
- let (size, specifier) = if self.error.is_some() {
- (
- colors::red_bold(" (error)").to_string(),
- colors::red(&self.specifier).to_string(),
- )
- } else if was_seen {
- let name = if type_dep {
- colors::italic_gray(&self.specifier).to_string()
- } else {
- colors::gray(&self.specifier).to_string()
- };
- (colors::gray(" *").to_string(), name)
- } else {
- let name = if type_dep {
- colors::italic(&self.specifier).to_string()
- } else {
- self.specifier.to_string()
- };
- (
- colors::gray(format!(
- " ({})",
- human_size(self.size.unwrap_or(0) as f64)
- ))
- .to_string(),
- name,
- )
- };
-
- seen.insert(self.specifier.clone());
-
- writeln!(
- f,
- "{} {}{}",
- colors::gray(format!(
- "{}{}─{}",
- prefix, sibling_connector, child_connector
- )),
- specifier,
- size
- )?;
-
- if !was_seen {
- let mut prefix = prefix.to_string();
- if last {
- prefix.push(EMPTY_CONNECTOR);
- } else {
- prefix.push(VERTICAL_CONNECTOR);
- }
- prefix.push(EMPTY_CONNECTOR);
- let dep_count = self.dependencies.len();
- for (idx, dep) in self.dependencies.iter().enumerate() {
- dep.write_info(
- f,
- &prefix,
- idx == dep_count - 1 && self.maybe_type_dependency.is_none(),
- modules,
- seen,
- )?;
- }
- if let Some(dep) = &self.maybe_type_dependency {
- dep.write_info(f, &prefix, true, modules, seen)?;
- }
- }
-
- Ok(())
- }
-}
-
-#[derive(Debug, Serialize)]
-#[serde(rename_all = "camelCase")]
-pub struct ModuleGraphInfo {
- pub root: ModuleSpecifier,
- pub modules: Vec<ModuleGraphInfoMod>,
- pub size: usize,
-}
-
-impl fmt::Display for ModuleGraphInfo {
- fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
- let root = self
- .modules
- .iter()
- .find(|m| m.specifier == self.root)
- .unwrap();
- if let Some(err) = &root.error {
- writeln!(f, "{} {}", colors::red("error:"), err)
- } else {
- if let Some(local) = &root.local {
- writeln!(f, "{} {}", colors::bold("local:"), local.to_string_lossy())?;
- }
- if let Some(media_type) = &root.media_type {
- writeln!(f, "{} {}", colors::bold("type:"), media_type)?;
- }
- if let Some(emit) = &root.emit {
- writeln!(f, "{} {}", colors::bold("emit:"), emit.to_string_lossy())?;
- }
- if let Some(map) = &root.map {
- writeln!(f, "{} {}", colors::bold("map:"), map.to_string_lossy())?;
- }
- let dep_count = self.modules.len() - 1;
- writeln!(
- f,
- "{} {} unique {}",
- colors::bold("dependencies:"),
- dep_count,
- colors::gray(format!("(total {})", human_size(self.size as f64)))
- )?;
- writeln!(
- f,
- "\n{} {}",
- self.root,
- colors::gray(format!(
- "({})",
- human_size(root.size.unwrap_or(0) as f64)
- ))
- )?;
- let mut seen = HashSet::new();
- let dep_len = root.dependencies.len();
- for (idx, dep) in root.dependencies.iter().enumerate() {
- dep.write_info(
- f,
- "",
- idx == dep_len - 1 && root.maybe_type_dependency.is_none(),
- &self.modules,
- &mut seen,
- )?;
- }
- if let Some(dep) = &root.maybe_type_dependency {
- dep.write_info(f, "", true, &self.modules, &mut seen)?;
- }
- Ok(())
- }
- }
-}
-
-/// An entry in the `ModuleInfoMap` the provides the size of the module and
-/// a vector of its dependencies, which should also be available as entries
-/// in the map.
-#[derive(Debug, Serialize)]
-#[serde(rename_all = "camelCase")]
-pub struct ModuleInfoMapItem {
- pub deps: Vec<ModuleSpecifier>,
- pub size: usize,
-}
-
-/// A function that converts a float to a string the represents a human
-/// readable version of that number.
-pub fn human_size(size: f64) -> String {
- let negative = if size.is_sign_positive() { "" } else { "-" };
- let size = size.abs();
- let units = ["B", "KB", "MB", "GB", "TB", "PB", "EB", "ZB", "YB"];
- if size < 1_f64 {
- return format!("{}{}{}", negative, size, "B");
- }
- let delimiter = 1024_f64;
- let exponent = std::cmp::min(
- (size.ln() / delimiter.ln()).floor() as i32,
- (units.len() - 1) as i32,
- );
- let pretty_bytes = format!("{:.2}", size / delimiter.powi(exponent))
- .parse::<f64>()
- .unwrap()
- * 1_f64;
- let unit = units[exponent as usize];
- format!("{}{}{}", negative, pretty_bytes, unit)
-}
-
-#[cfg(test)]
-mod test {
- use super::*;
- use deno_core::resolve_url;
- use deno_core::serde_json::json;
-
- #[test]
- fn human_size_test() {
- assert_eq!(human_size(16_f64), "16B");
- assert_eq!(human_size((16 * 1024) as f64), "16KB");
- assert_eq!(human_size((16 * 1024 * 1024) as f64), "16MB");
- assert_eq!(human_size(16_f64 * 1024_f64.powf(3.0)), "16GB");
- assert_eq!(human_size(16_f64 * 1024_f64.powf(4.0)), "16TB");
- assert_eq!(human_size(16_f64 * 1024_f64.powf(5.0)), "16PB");
- assert_eq!(human_size(16_f64 * 1024_f64.powf(6.0)), "16EB");
- assert_eq!(human_size(16_f64 * 1024_f64.powf(7.0)), "16ZB");
- assert_eq!(human_size(16_f64 * 1024_f64.powf(8.0)), "16YB");
- }
-
- fn get_fixture() -> ModuleGraphInfo {
- let specifier_a = resolve_url("https://deno.land/x/a.ts").unwrap();
- let specifier_b = resolve_url("https://deno.land/x/b.ts").unwrap();
- let specifier_c_js = resolve_url("https://deno.land/x/c.js").unwrap();
- let specifier_c_dts = resolve_url("https://deno.land/x/c.d.ts").unwrap();
- let specifier_d_js = resolve_url("https://deno.land/x/d.js").unwrap();
- let specifier_d_dts = resolve_url("https://deno.land/x/d.d.ts").unwrap();
- let modules = vec![
- ModuleGraphInfoMod {
- specifier: specifier_a.clone(),
- dependencies: vec![ModuleGraphInfoDep {
- specifier: "./b.ts".to_string(),
- is_dynamic: false,
- maybe_code: Some(specifier_b.clone()),
- maybe_type: None,
- }],
- size: Some(123),
- media_type: Some(MediaType::TypeScript),
- local: Some(PathBuf::from("/cache/deps/https/deno.land/x/a.ts")),
- checksum: Some("abcdef".to_string()),
- emit: Some(PathBuf::from("/cache/emit/https/deno.land/x/a.js")),
- ..Default::default()
- },
- ModuleGraphInfoMod {
- specifier: specifier_b,
- dependencies: vec![ModuleGraphInfoDep {
- specifier: "./c.js".to_string(),
- is_dynamic: false,
- maybe_code: Some(specifier_c_js.clone()),
- maybe_type: Some(specifier_c_dts.clone()),
- }],
- size: Some(456),
- media_type: Some(MediaType::TypeScript),
- local: Some(PathBuf::from("/cache/deps/https/deno.land/x/b.ts")),
- checksum: Some("def123".to_string()),
- emit: Some(PathBuf::from("/cache/emit/https/deno.land/x/b.js")),
- ..Default::default()
- },
- ModuleGraphInfoMod {
- specifier: specifier_c_js,
- dependencies: vec![ModuleGraphInfoDep {
- specifier: "./d.js".to_string(),
- is_dynamic: false,
- maybe_code: Some(specifier_d_js.clone()),
- maybe_type: None,
- }],
- size: Some(789),
- media_type: Some(MediaType::JavaScript),
- local: Some(PathBuf::from("/cache/deps/https/deno.land/x/c.js")),
- checksum: Some("9876abcef".to_string()),
- ..Default::default()
- },
- ModuleGraphInfoMod {
- specifier: specifier_c_dts,
- size: Some(999),
- media_type: Some(MediaType::Dts),
- local: Some(PathBuf::from("/cache/deps/https/deno.land/x/c.d.ts")),
- checksum: Some("a2b3c4d5".to_string()),
- ..Default::default()
- },
- ModuleGraphInfoMod {
- specifier: specifier_d_js,
- size: Some(987),
- maybe_type_dependency: Some(ModuleGraphInfoDep {
- specifier: "/x/d.d.ts".to_string(),
- is_dynamic: false,
- maybe_code: None,
- maybe_type: Some(specifier_d_dts.clone()),
- }),
- media_type: Some(MediaType::JavaScript),
- local: Some(PathBuf::from("/cache/deps/https/deno.land/x/d.js")),
- checksum: Some("5k6j7h8g".to_string()),
- ..Default::default()
- },
- ModuleGraphInfoMod {
- specifier: specifier_d_dts,
- size: Some(67),
- media_type: Some(MediaType::Dts),
- local: Some(PathBuf::from("/cache/deps/https/deno.land/x/d.d.ts")),
- checksum: Some("0h0h0h0h0h".to_string()),
- ..Default::default()
- },
- ];
- ModuleGraphInfo {
- root: specifier_a,
- modules,
- size: 99999,
- }
- }
-
- #[test]
- fn text_module_graph_info_display() {
- let fixture = get_fixture();
- let text = fixture.to_string();
- let actual = test_util::strip_ansi_codes(&text);
- let expected = r#"local: /cache/deps/https/deno.land/x/a.ts
-type: TypeScript
-emit: /cache/emit/https/deno.land/x/a.js
-dependencies: 5 unique (total 97.66KB)
-
-https://deno.land/x/a.ts (123B)
-└─┬ https://deno.land/x/b.ts (456B)
- ├─┬ https://deno.land/x/c.js (789B)
- │ └─┬ https://deno.land/x/d.js (987B)
- │ └── https://deno.land/x/d.d.ts (67B)
- └── https://deno.land/x/c.d.ts (999B)
-"#;
- assert_eq!(actual, expected);
- }
-
- #[test]
- fn test_module_graph_info_json() {
- let fixture = get_fixture();
- let actual = json!(fixture);
- assert_eq!(
- actual,
- json!({
- "root": "https://deno.land/x/a.ts",
- "modules": [
- {
- "specifier": "https://deno.land/x/a.ts",
- "dependencies": [
- {
- "specifier": "./b.ts",
- "code": "https://deno.land/x/b.ts"
- }
- ],
- "size": 123,
- "mediaType": "TypeScript",
- "local": "/cache/deps/https/deno.land/x/a.ts",
- "checksum": "abcdef",
- "emit": "/cache/emit/https/deno.land/x/a.js"
- },
- {
- "specifier": "https://deno.land/x/b.ts",
- "dependencies": [
- {
- "specifier": "./c.js",
- "code": "https://deno.land/x/c.js",
- "type": "https://deno.land/x/c.d.ts"
- }
- ],
- "size": 456,
- "mediaType": "TypeScript",
- "local": "/cache/deps/https/deno.land/x/b.ts",
- "checksum": "def123",
- "emit": "/cache/emit/https/deno.land/x/b.js"
- },
- {
- "specifier": "https://deno.land/x/c.js",
- "dependencies": [
- {
- "specifier": "./d.js",
- "code": "https://deno.land/x/d.js"
- }
- ],
- "size": 789,
- "mediaType": "JavaScript",
- "local": "/cache/deps/https/deno.land/x/c.js",
- "checksum": "9876abcef"
- },
- {
- "specifier": "https://deno.land/x/c.d.ts",
- "dependencies": [],
- "size": 999,
- "mediaType": "Dts",
- "local": "/cache/deps/https/deno.land/x/c.d.ts",
- "checksum": "a2b3c4d5"
- },
- {
- "specifier": "https://deno.land/x/d.js",
- "dependencies": [],
- "typeDependency": {
- "specifier": "/x/d.d.ts",
- "type": "https://deno.land/x/d.d.ts"
- },
- "size": 987,
- "mediaType": "JavaScript",
- "local": "/cache/deps/https/deno.land/x/d.js",
- "checksum": "5k6j7h8g"
- },
- {
- "specifier": "https://deno.land/x/d.d.ts",
- "dependencies": [],
- "size": 67,
- "mediaType": "Dts",
- "local": "/cache/deps/https/deno.land/x/d.d.ts",
- "checksum": "0h0h0h0h0h"
- }
- ],
- "size": 99999
- })
- );
- }
-}
diff --git a/cli/lockfile.rs b/cli/lockfile.rs
index 0b52f5ee7..6b553bd15 100644
--- a/cli/lockfile.rs
+++ b/cli/lockfile.rs
@@ -1,11 +1,16 @@
// Copyright 2018-2021 the Deno authors. All rights reserved. MIT license.
+use deno_core::parking_lot::Mutex;
use deno_core::serde_json;
use deno_core::serde_json::json;
+use deno_core::ModuleSpecifier;
use log::debug;
+use std::cell::RefCell;
use std::collections::BTreeMap;
use std::io::Result;
use std::path::PathBuf;
+use std::rc::Rc;
+use std::sync::Arc;
#[derive(Debug, Clone)]
pub struct Lockfile {
@@ -81,6 +86,43 @@ impl Lockfile {
}
}
+#[derive(Debug)]
+pub(crate) struct Locker(Option<Arc<Mutex<Lockfile>>>);
+
+impl deno_graph::source::Locker for Locker {
+ fn check_or_insert(
+ &mut self,
+ specifier: &ModuleSpecifier,
+ source: &str,
+ ) -> bool {
+ if let Some(lock_file) = &self.0 {
+ let mut lock_file = lock_file.lock();
+ lock_file.check_or_insert(specifier.as_str(), source)
+ } else {
+ true
+ }
+ }
+
+ fn get_checksum(&self, content: &str) -> String {
+ crate::checksum::gen(&[content.as_bytes()])
+ }
+
+ fn get_filename(&self) -> Option<String> {
+ let lock_file = self.0.as_ref()?.lock();
+ lock_file.filename.to_str().map(|s| s.to_string())
+ }
+}
+
+pub(crate) fn as_maybe_locker(
+ lockfile: Option<Arc<Mutex<Lockfile>>>,
+) -> Option<Rc<RefCell<Box<dyn deno_graph::source::Locker>>>> {
+ lockfile.as_ref().map(|lf| {
+ Rc::new(RefCell::new(
+ Box::new(Locker(Some(lf.clone()))) as Box<dyn deno_graph::source::Locker>
+ ))
+ })
+}
+
#[cfg(test)]
mod tests {
use super::*;
diff --git a/cli/lsp/analysis.rs b/cli/lsp/analysis.rs
index d1cf14500..a0718e8b3 100644
--- a/cli/lsp/analysis.rs
+++ b/cli/lsp/analysis.rs
@@ -6,12 +6,12 @@ use super::tsc;
use crate::ast;
use crate::ast::Location;
use crate::lsp::documents::DocumentData;
-use crate::module_graph::parse_deno_types;
-use crate::module_graph::parse_ts_reference;
-use crate::module_graph::TypeScriptReference;
use crate::tools::lint::create_linter;
use deno_ast::swc::ast as swc_ast;
+use deno_ast::swc::common::comments::Comment;
+use deno_ast::swc::common::BytePos;
+use deno_ast::swc::common::Span;
use deno_ast::swc::common::DUMMY_SP;
use deno_ast::swc::visit::Node;
use deno_ast::swc::visit::Visit;
@@ -68,10 +68,71 @@ lazy_static::lazy_static! {
.collect();
static ref IMPORT_SPECIFIER_RE: Regex = Regex::new(r#"\sfrom\s+["']([^"']*)["']"#).unwrap();
+
+ static ref DENO_TYPES_RE: Regex =
+ Regex::new(r#"(?i)^\s*@deno-types\s*=\s*(?:["']([^"']+)["']|(\S+))"#)
+ .unwrap();
+ static ref TRIPLE_SLASH_REFERENCE_RE: Regex =
+ Regex::new(r"(?i)^/\s*<reference\s.*?/>").unwrap();
+ static ref PATH_REFERENCE_RE: Regex =
+ Regex::new(r#"(?i)\spath\s*=\s*["']([^"']*)["']"#).unwrap();
+ static ref TYPES_REFERENCE_RE: Regex =
+ Regex::new(r#"(?i)\stypes\s*=\s*["']([^"']*)["']"#).unwrap();
+
}
const SUPPORTED_EXTENSIONS: &[&str] = &[".ts", ".tsx", ".js", ".jsx", ".mjs"];
+// TODO(@kitsonk) remove after deno_graph migration
+#[derive(Debug, Clone, Eq, PartialEq)]
+enum TypeScriptReference {
+ Path(String),
+ Types(String),
+}
+
+fn match_to_span(comment: &Comment, m: &regex::Match) -> Span {
+ Span {
+ lo: comment.span.lo + BytePos((m.start() + 1) as u32),
+ hi: comment.span.lo + BytePos((m.end() + 1) as u32),
+ ctxt: comment.span.ctxt,
+ }
+}
+
+// TODO(@kitsonk) remove after deno_graph migration
+fn parse_deno_types(comment: &Comment) -> Option<(String, Span)> {
+ let captures = DENO_TYPES_RE.captures(&comment.text)?;
+ if let Some(m) = captures.get(1) {
+ Some((m.as_str().to_string(), match_to_span(comment, &m)))
+ } else if let Some(m) = captures.get(2) {
+ Some((m.as_str().to_string(), match_to_span(comment, &m)))
+ } else {
+ unreachable!();
+ }
+}
+
+// TODO(@kitsonk) remove after deno_graph migration
+fn parse_ts_reference(
+ comment: &Comment,
+) -> Option<(TypeScriptReference, Span)> {
+ if !TRIPLE_SLASH_REFERENCE_RE.is_match(&comment.text) {
+ None
+ } else if let Some(captures) = PATH_REFERENCE_RE.captures(&comment.text) {
+ let m = captures.get(1).unwrap();
+ Some((
+ TypeScriptReference::Path(m.as_str().to_string()),
+ match_to_span(comment, &m),
+ ))
+ } else {
+ TYPES_REFERENCE_RE.captures(&comment.text).map(|captures| {
+ let m = captures.get(1).unwrap();
+ (
+ TypeScriptReference::Types(m.as_str().to_string()),
+ match_to_span(comment, &m),
+ )
+ })
+ }
+}
+
/// Category of self-generated diagnostic messages (those not coming from)
/// TypeScript.
#[derive(Debug, PartialEq, Eq)]
diff --git a/cli/lsp/cache.rs b/cli/lsp/cache.rs
new file mode 100644
index 000000000..39b9dca60
--- /dev/null
+++ b/cli/lsp/cache.rs
@@ -0,0 +1,86 @@
+// Copyright 2018-2021 the Deno authors. All rights reserved. MIT license.
+
+use crate::cache::CacherLoader;
+use crate::cache::FetchCacher;
+use crate::flags::Flags;
+use crate::proc_state::ProcState;
+use crate::resolver::ImportMapResolver;
+use crate::tokio_util::create_basic_runtime;
+
+use deno_core::error::anyhow;
+use deno_core::error::AnyError;
+use deno_core::ModuleSpecifier;
+use deno_runtime::permissions::Permissions;
+use import_map::ImportMap;
+use std::path::PathBuf;
+use std::thread;
+use tokio::sync::mpsc;
+use tokio::sync::oneshot;
+
+type Request = (Vec<ModuleSpecifier>, oneshot::Sender<Result<(), AnyError>>);
+
+/// A "server" that handles requests from the language server to cache modules
+/// in its own thread.
+#[derive(Debug)]
+pub(crate) struct CacheServer(mpsc::UnboundedSender<Request>);
+
+impl CacheServer {
+ pub async fn new(
+ maybe_cache_path: Option<PathBuf>,
+ maybe_import_map: Option<ImportMap>,
+ ) -> Self {
+ let (tx, mut rx) = mpsc::unbounded_channel::<Request>();
+ let _join_handle = thread::spawn(move || {
+ let runtime = create_basic_runtime();
+ runtime.block_on(async {
+ let ps = ProcState::build(Flags {
+ cache_path: maybe_cache_path,
+ ..Default::default()
+ })
+ .await
+ .unwrap();
+ let maybe_resolver =
+ maybe_import_map.as_ref().map(ImportMapResolver::new);
+ let mut cache = FetchCacher::new(
+ ps.dir.gen_cache.clone(),
+ ps.file_fetcher.clone(),
+ Permissions::allow_all(),
+ Permissions::allow_all(),
+ );
+
+ while let Some((roots, tx)) = rx.recv().await {
+ let graph = deno_graph::create_graph(
+ roots,
+ false,
+ None,
+ cache.as_mut_loader(),
+ maybe_resolver.as_ref().map(|r| r.as_resolver()),
+ None,
+ None,
+ )
+ .await;
+
+ if tx.send(graph.valid().map_err(|err| err.into())).is_err() {
+ log::warn!("cannot send to client");
+ }
+ }
+ })
+ });
+
+ Self(tx)
+ }
+
+ /// Attempt to cache the supplied module specifiers and their dependencies in
+ /// the current DENO_DIR, returning any errors, so they can be returned to the
+ /// client.
+ pub async fn cache(
+ &self,
+ roots: Vec<ModuleSpecifier>,
+ ) -> Result<(), AnyError> {
+ let (tx, rx) = oneshot::channel::<Result<(), AnyError>>();
+ if self.0.send((roots, tx)).is_err() {
+ return Err(anyhow!("failed to send request to cache thread"));
+ }
+ rx.await?
+ }
+}
diff --git a/cli/lsp/language_server.rs b/cli/lsp/language_server.rs
index ef659cd05..524a59191 100644
--- a/cli/lsp/language_server.rs
+++ b/cli/lsp/language_server.rs
@@ -29,6 +29,7 @@ use super::analysis::ts_changes_to_edit;
use super::analysis::CodeActionCollection;
use super::analysis::CodeActionData;
use super::analysis::ResolvedDependency;
+use super::cache::CacheServer;
use super::capabilities;
use super::code_lens;
use super::completions;
@@ -44,7 +45,6 @@ use super::parent_process_checker;
use super::performance::Performance;
use super::refactor;
use super::registries;
-use super::sources;
use super::sources::Sources;
use super::text;
use super::text::LineIndex;
@@ -99,6 +99,8 @@ pub(crate) struct Inner {
/// An optional path to the DENO_DIR which has been specified in the client
/// options.
maybe_cache_path: Option<PathBuf>,
+ /// A lazily created "server" for handling cache requests.
+ maybe_cache_server: Option<CacheServer>,
/// An optional configuration file which has been specified in the client
/// options.
maybe_config_file: Option<ConfigFile>,
@@ -149,6 +151,7 @@ impl Inner {
diagnostics_server,
documents: Default::default(),
maybe_cache_path: None,
+ maybe_cache_server: None,
maybe_config_file: None,
maybe_config_uri: None,
maybe_import_map: None,
@@ -424,6 +427,7 @@ impl Inner {
pub fn update_cache(&mut self) -> Result<(), AnyError> {
let mark = self.performance.mark("update_cache", None::<()>);
self.performance.measure(mark);
+ self.maybe_cache_server = None;
let (maybe_cache, maybe_root_uri) = {
let config = &self.config;
(
@@ -479,6 +483,7 @@ impl Inner {
pub async fn update_import_map(&mut self) -> Result<(), AnyError> {
let mark = self.performance.mark("update_import_map", None::<()>);
+ self.maybe_cache_server = None;
let (maybe_import_map, maybe_root_uri) = {
let config = &self.config;
(
@@ -2630,34 +2635,30 @@ impl Inner {
}
let mark = self.performance.mark("cache", Some(&params));
- if !params.uris.is_empty() {
- for identifier in &params.uris {
- let specifier = self.url_map.normalize_url(&identifier.uri);
- sources::cache(
- &specifier,
- &self.maybe_import_map,
- &self.maybe_config_file,
- &self.maybe_cache_path,
- )
- .await
- .map_err(|err| {
- error!("{}", err);
- LspError::internal_error()
- })?;
- }
+ let roots = if !params.uris.is_empty() {
+ params
+ .uris
+ .iter()
+ .map(|t| self.url_map.normalize_url(&t.uri))
+ .collect()
} else {
- sources::cache(
- &referrer,
- &self.maybe_import_map,
- &self.maybe_config_file,
- &self.maybe_cache_path,
- )
- .await
- .map_err(|err| {
- error!("{}", err);
- LspError::internal_error()
- })?;
+ vec![referrer.clone()]
+ };
+
+ if self.maybe_cache_server.is_none() {
+ self.maybe_cache_server = Some(
+ CacheServer::new(
+ self.maybe_cache_path.clone(),
+ self.maybe_import_map.clone(),
+ )
+ .await,
+ );
}
+ let cache_server = self.maybe_cache_server.as_ref().unwrap();
+ if let Err(err) = cache_server.cache(roots).await {
+ self.client.show_message(MessageType::Warning, err).await;
+ }
+
// now that we have dependencies loaded, we need to re-analyze them and
// invalidate some diagnostics
if self.documents.contains_key(&referrer) {
diff --git a/cli/lsp/mod.rs b/cli/lsp/mod.rs
index fda2ac82b..e6cc88208 100644
--- a/cli/lsp/mod.rs
+++ b/cli/lsp/mod.rs
@@ -5,6 +5,7 @@ use lspower::LspService;
use lspower::Server;
mod analysis;
+mod cache;
mod capabilities;
mod code_lens;
mod completions;
diff --git a/cli/lsp/sources.rs b/cli/lsp/sources.rs
index bd5835cf2..69de6d976 100644
--- a/cli/lsp/sources.rs
+++ b/cli/lsp/sources.rs
@@ -6,18 +6,12 @@ use super::text::LineIndex;
use super::tsc;
use super::urls::INVALID_SPECIFIER;
-use crate::config_file::ConfigFile;
use crate::file_fetcher::get_source_from_bytes;
use crate::file_fetcher::map_content_type;
use crate::file_fetcher::SUPPORTED_SCHEMES;
-use crate::flags::Flags;
use crate::http_cache;
use crate::http_cache::HttpCache;
-use crate::module_graph::GraphBuilder;
-use crate::proc_state::ProcState;
-use crate::specifier_handler::FetchHandler;
use crate::text_encoding;
-use import_map::ImportMap;
use deno_ast::MediaType;
use deno_core::error::anyhow;
@@ -25,7 +19,7 @@ use deno_core::error::AnyError;
use deno_core::parking_lot::Mutex;
use deno_core::serde_json;
use deno_core::ModuleSpecifier;
-use deno_runtime::permissions::Permissions;
+use import_map::ImportMap;
use std::collections::HashMap;
use std::fs;
use std::path::Path;
@@ -34,27 +28,6 @@ use std::sync::Arc;
use std::time::SystemTime;
use tsc::NavigationTree;
-pub async fn cache(
- specifier: &ModuleSpecifier,
- maybe_import_map: &Option<ImportMap>,
- maybe_config_file: &Option<ConfigFile>,
- maybe_cache_path: &Option<PathBuf>,
-) -> Result<(), AnyError> {
- let ps = ProcState::build(Flags {
- cache_path: maybe_cache_path.clone(),
- ..Default::default()
- })
- .await?;
- let handler = Arc::new(Mutex::new(FetchHandler::new(
- &ps,
- Permissions::allow_all(),
- Permissions::allow_all(),
- )?));
- let mut builder = GraphBuilder::new(handler, maybe_import_map.clone(), None);
- builder.analyze_config_file(maybe_config_file).await?;
- builder.add(specifier, false).await
-}
-
fn get_remote_headers(
cache_filename: &Path,
) -> Option<HashMap<String, String>> {
diff --git a/cli/lsp/tsc.rs b/cli/lsp/tsc.rs
index 05606cb79..9899fad72 100644
--- a/cli/lsp/tsc.rs
+++ b/cli/lsp/tsc.rs
@@ -44,12 +44,15 @@ use log::warn;
use lspower::lsp;
use regex::Captures;
use regex::Regex;
+use std::borrow::Cow;
+use std::cmp;
+use std::collections::HashMap;
use std::collections::HashSet;
+use std::path::Path;
use std::sync::Arc;
use std::thread;
-use std::{borrow::Cow, cmp};
-use std::{collections::HashMap, path::Path};
-use text_size::{TextRange, TextSize};
+use text_size::TextRange;
+use text_size::TextSize;
use tokio::sync::mpsc;
use tokio::sync::oneshot;
@@ -3375,7 +3378,7 @@ mod tests {
#[test]
fn test_modify_sources() {
let (mut runtime, state_snapshot, location) = setup(
- true,
+ false,
json!({
"target": "esnext",
"module": "esnext",
diff --git a/cli/main.rs b/cli/main.rs
index 4ffa8340c..8a85cacfb 100644
--- a/cli/main.rs
+++ b/cli/main.rs
@@ -2,6 +2,7 @@
mod ast;
mod auth_tokens;
+mod cache;
mod checksum;
mod compat;
mod config_file;
@@ -9,6 +10,7 @@ mod deno_dir;
mod diagnostics;
mod diff;
mod disk_cache;
+mod emit;
mod errors;
mod file_fetcher;
mod file_watcher;
@@ -18,16 +20,14 @@ mod fmt_errors;
mod fs_util;
mod http_cache;
mod http_util;
-mod info;
mod lockfile;
mod logger;
mod lsp;
-mod module_graph;
mod module_loader;
mod ops;
mod proc_state;
+mod resolver;
mod source_maps;
-mod specifier_handler;
mod standalone;
mod text_encoding;
mod tokio_util;
@@ -59,8 +59,8 @@ use crate::flags::UpgradeFlags;
use crate::fmt_errors::PrettyJsError;
use crate::module_loader::CliModuleLoader;
use crate::proc_state::ProcState;
+use crate::resolver::ImportMapResolver;
use crate::source_maps::apply_source_map;
-use crate::specifier_handler::FetchHandler;
use crate::tools::installer::infer_name_from_url;
use deno_ast::MediaType;
use deno_core::error::generic_error;
@@ -68,7 +68,6 @@ use deno_core::error::AnyError;
use deno_core::futures::future::FutureExt;
use deno_core::futures::Future;
use deno_core::located_script_name;
-use deno_core::parking_lot::Mutex;
use deno_core::resolve_url_or_path;
use deno_core::serde_json;
use deno_core::serde_json::json;
@@ -299,7 +298,7 @@ where
fn print_cache_info(
state: &ProcState,
json: bool,
- location: Option<deno_core::url::Url>,
+ location: Option<&deno_core::url::Url>,
) -> Result<(), AnyError> {
let deno_dir = &state.dir.root;
let modules_cache = &state.file_fetcher.get_http_cache_location();
@@ -402,19 +401,9 @@ async fn compile_command(
"An executable name was not provided. One could not be inferred from the URL. Aborting.",
))?;
- let module_graph = create_module_graph_and_maybe_check(
- module_specifier.clone(),
- ps.clone(),
- debug,
- )
- .await?;
-
- info!(
- "{} {}",
- colors::green("Bundle"),
- module_specifier.to_string()
- );
- let bundle_str = bundle_module_graph(module_graph, ps.clone(), flags, debug)?;
+ let graph =
+ create_graph_and_maybe_check(module_specifier.clone(), &ps, debug).await?;
+ let (bundle_str, _) = bundle_module_graph(graph.as_ref(), &ps, &flags)?;
info!(
"{} {}",
@@ -449,36 +438,38 @@ async fn info_command(
flags: Flags,
info_flags: InfoFlags,
) -> Result<(), AnyError> {
- let location = flags.location.clone();
let ps = ProcState::build(flags).await?;
if let Some(specifier) = info_flags.file {
let specifier = resolve_url_or_path(&specifier)?;
- let handler = Arc::new(Mutex::new(specifier_handler::FetchHandler::new(
- &ps,
- // info accesses dynamically imported modules just for their information
- // so we allow access to all of them.
+ let mut cache = cache::FetchCacher::new(
+ ps.dir.gen_cache.clone(),
+ ps.file_fetcher.clone(),
Permissions::allow_all(),
Permissions::allow_all(),
- )?));
- let mut builder = module_graph::GraphBuilder::new(
- handler,
- ps.maybe_import_map.clone(),
- ps.lockfile.clone(),
);
- builder.add(&specifier, false).await?;
- builder.analyze_config_file(&ps.maybe_config_file).await?;
- let graph = builder.get_graph();
- let info = graph.info()?;
+ let maybe_locker = lockfile::as_maybe_locker(ps.lockfile.clone());
+ let maybe_resolver =
+ ps.maybe_import_map.as_ref().map(ImportMapResolver::new);
+ let graph = deno_graph::create_graph(
+ vec![specifier],
+ false,
+ None,
+ &mut cache,
+ maybe_resolver.as_ref().map(|r| r.as_resolver()),
+ maybe_locker,
+ None,
+ )
+ .await;
if info_flags.json {
- write_json_to_stdout(&json!(info))
+ write_json_to_stdout(&json!(graph))
} else {
- write_to_stdout_ignore_sigpipe(info.to_string().as_bytes())
+ write_to_stdout_ignore_sigpipe(graph.to_string().as_bytes())
.map_err(|err| err.into())
}
} else {
// If it was just "deno info" print location of caches and exit
- print_cache_info(&ps, info_flags.json, location)
+ print_cache_info(&ps, info_flags.json, ps.flags.location.as_ref())
}
}
@@ -541,23 +532,25 @@ async fn cache_command(
cache_flags: CacheFlags,
) -> Result<(), AnyError> {
let lib = if flags.unstable {
- module_graph::TypeLib::UnstableDenoWindow
+ emit::TypeLib::UnstableDenoWindow
} else {
- module_graph::TypeLib::DenoWindow
+ emit::TypeLib::DenoWindow
};
let ps = ProcState::build(flags).await?;
for file in cache_flags.files {
let specifier = resolve_url_or_path(&file)?;
ps.prepare_module_load(
- specifier,
+ vec![specifier],
+ false,
lib.clone(),
Permissions::allow_all(),
Permissions::allow_all(),
- false,
- ps.maybe_import_map.clone(),
)
.await?;
+ if let Some(graph_error) = ps.maybe_graph_error.lock().take() {
+ return Err(graph_error.into());
+ }
}
Ok(())
@@ -567,8 +560,10 @@ async fn eval_command(
flags: Flags,
eval_flags: EvalFlags,
) -> Result<(), AnyError> {
- // Force TypeScript compile.
- let main_module = resolve_url_or_path("./$deno$eval.ts").unwrap();
+ // deno_graph works off of extensions for local files to determine the media
+ // type, and so our "fake" specifier needs to have the proper extension.
+ let main_module =
+ resolve_url_or_path(&format!("./$deno$eval.{}", eval_flags.ext)).unwrap();
let permissions = Permissions::from_options(&flags.clone().into());
let ps = ProcState::build(flags.clone()).await?;
let mut worker =
@@ -584,15 +579,7 @@ async fn eval_command(
let file = File {
local: main_module.clone().to_file_path().unwrap(),
maybe_types: None,
- media_type: if eval_flags.ext.as_str() == "ts" {
- MediaType::TypeScript
- } else if eval_flags.ext.as_str() == "tsx" {
- MediaType::Tsx
- } else if eval_flags.ext.as_str() == "js" {
- MediaType::JavaScript
- } else {
- MediaType::Jsx
- },
+ media_type: MediaType::Unknown,
source: Arc::new(String::from_utf8(source_code)?),
specifier: main_module.clone(),
maybe_headers: None,
@@ -603,9 +590,7 @@ async fn eval_command(
ps.file_fetcher.insert_cached(file);
debug!("main_module {}", &main_module);
if flags.compat {
- worker
- .execute_side_module(&compat::get_node_globals_url())
- .await?;
+ worker.execute_side_module(&compat::GLOBAL_URL).await?;
}
worker.execute_main_module(&main_module).await?;
worker.execute_script(
@@ -620,75 +605,133 @@ async fn eval_command(
Ok(())
}
-async fn create_module_graph_and_maybe_check(
- module_specifier: ModuleSpecifier,
- ps: ProcState,
+async fn create_graph_and_maybe_check(
+ root: ModuleSpecifier,
+ ps: &ProcState,
debug: bool,
-) -> Result<module_graph::Graph, AnyError> {
- let handler = Arc::new(Mutex::new(FetchHandler::new(
- &ps,
- // when bundling, dynamic imports are only access for their type safety,
- // therefore we will allow the graph to access any module.
+) -> Result<Arc<deno_graph::ModuleGraph>, AnyError> {
+ let mut cache = cache::FetchCacher::new(
+ ps.dir.gen_cache.clone(),
+ ps.file_fetcher.clone(),
Permissions::allow_all(),
Permissions::allow_all(),
- )?));
- let mut builder = module_graph::GraphBuilder::new(
- handler,
- ps.maybe_import_map.clone(),
- ps.lockfile.clone(),
);
- builder.add(&module_specifier, false).await?;
- builder.analyze_config_file(&ps.maybe_config_file).await?;
- let module_graph = builder.get_graph();
+ let maybe_locker = lockfile::as_maybe_locker(ps.lockfile.clone());
+ let maybe_imports = ps
+ .maybe_config_file
+ .as_ref()
+ .map(|cf| cf.to_maybe_imports())
+ .flatten();
+ let maybe_resolver = ps.maybe_import_map.as_ref().map(ImportMapResolver::new);
+ let graph = Arc::new(
+ deno_graph::create_graph(
+ vec![root],
+ false,
+ maybe_imports,
+ &mut cache,
+ maybe_resolver.as_ref().map(|r| r.as_resolver()),
+ maybe_locker,
+ None,
+ )
+ .await,
+ );
+
+ // Ensure that all non-dynamic, non-type only imports are properly loaded and
+ // if not, error with the first issue encountered.
+ graph.valid().map_err(emit::GraphError::from)?;
+ // If there was a locker, validate the integrity of all the modules in the
+ // locker.
+ emit::lock(graph.as_ref());
if !ps.flags.no_check {
- // TODO(@kitsonk) support bundling for workers
+ graph.valid_types_only().map_err(emit::GraphError::from)?;
let lib = if ps.flags.unstable {
- module_graph::TypeLib::UnstableDenoWindow
+ emit::TypeLib::UnstableDenoWindow
} else {
- module_graph::TypeLib::DenoWindow
+ emit::TypeLib::DenoWindow
};
- let result_info =
- module_graph.clone().check(module_graph::CheckOptions {
- debug,
- emit: false,
+ let (ts_config, maybe_ignored_options) = emit::get_ts_config(
+ emit::ConfigType::Check {
+ tsc_emit: false,
lib,
- maybe_config_file: ps.maybe_config_file.clone(),
- reload: ps.flags.reload,
- ..Default::default()
- })?;
-
- debug!("{}", result_info.stats);
- if let Some(ignored_options) = result_info.maybe_ignored_options {
+ },
+ ps.maybe_config_file.as_ref(),
+ None,
+ )?;
+ log::info!("{} {}", colors::green("Check"), graph.roots[0]);
+ if let Some(ignored_options) = maybe_ignored_options {
eprintln!("{}", ignored_options);
}
- if !result_info.diagnostics.is_empty() {
- return Err(generic_error(result_info.diagnostics.to_string()));
+ let maybe_config_specifier = ps
+ .maybe_config_file
+ .as_ref()
+ .map(|cf| ModuleSpecifier::from_file_path(&cf.path).unwrap());
+ let check_result = emit::check_and_maybe_emit(
+ graph.clone(),
+ &mut cache,
+ emit::CheckOptions {
+ debug,
+ emit_with_diagnostics: false,
+ maybe_config_specifier,
+ ts_config,
+ },
+ )?;
+ debug!("{}", check_result.stats);
+ if !check_result.diagnostics.is_empty() {
+ return Err(check_result.diagnostics.into());
}
}
- Ok(module_graph)
+ Ok(graph)
}
fn bundle_module_graph(
- module_graph: module_graph::Graph,
- ps: ProcState,
- flags: Flags,
- debug: bool,
-) -> Result<String, AnyError> {
- let (bundle, stats, maybe_ignored_options) =
- module_graph.bundle(module_graph::BundleOptions {
- debug,
- maybe_config_file: ps.maybe_config_file.clone(),
- })?;
- match maybe_ignored_options {
- Some(ignored_options) if flags.no_check => {
+ graph: &deno_graph::ModuleGraph,
+ ps: &ProcState,
+ flags: &Flags,
+) -> Result<(String, Option<String>), AnyError> {
+ info!("{} {}", colors::green("Bundle"), graph.roots[0]);
+
+ let (ts_config, maybe_ignored_options) = emit::get_ts_config(
+ emit::ConfigType::Bundle,
+ ps.maybe_config_file.as_ref(),
+ None,
+ )?;
+ if flags.no_check {
+ if let Some(ignored_options) = maybe_ignored_options {
eprintln!("{}", ignored_options);
}
- _ => {}
}
- debug!("{}", stats);
- Ok(bundle)
+
+ emit::bundle(
+ graph,
+ emit::BundleOptions {
+ bundle_type: emit::BundleType::Module,
+ ts_config,
+ },
+ )
+}
+
+/// A function that converts a float to a string the represents a human
+/// readable version of that number.
+fn human_size(size: f64) -> String {
+ let negative = if size.is_sign_positive() { "" } else { "-" };
+ let size = size.abs();
+ let units = ["B", "KB", "MB", "GB", "TB", "PB", "EB", "ZB", "YB"];
+ if size < 1_f64 {
+ return format!("{}{}{}", negative, size, "B");
+ }
+ let delimiter = 1024_f64;
+ let exponent = std::cmp::min(
+ (size.ln() / delimiter.ln()).floor() as i32,
+ (units.len() - 1) as i32,
+ );
+ let pretty_bytes = format!("{:.2}", size / delimiter.powi(exponent))
+ .parse::<f64>()
+ .unwrap()
+ * 1_f64;
+ let unit = units[exponent as usize];
+ format!("{}{}{}", negative, pretty_bytes, unit)
}
async fn bundle_command(
@@ -707,17 +750,18 @@ async fn bundle_command(
debug!(">>>>> bundle START");
let ps = ProcState::build(flags.clone()).await?;
- let module_graph = create_module_graph_and_maybe_check(
- module_specifier,
- ps.clone(),
- debug,
- )
- .await?;
+ let graph =
+ create_graph_and_maybe_check(module_specifier, &ps, debug).await?;
- let mut paths_to_watch: Vec<PathBuf> = module_graph
- .get_modules()
+ let mut paths_to_watch: Vec<PathBuf> = graph
+ .specifiers()
.iter()
- .filter_map(|specifier| specifier.to_file_path().ok())
+ .filter_map(|(_, r)| {
+ r.as_ref()
+ .ok()
+ .map(|(s, _)| s.to_file_path().ok())
+ .flatten()
+ })
.collect();
if let Some(import_map) = ps.flags.import_map_path.as_ref() {
@@ -725,12 +769,12 @@ async fn bundle_command(
.push(fs_util::resolve_from_cwd(std::path::Path::new(import_map))?);
}
- Ok((paths_to_watch, module_graph, ps))
+ Ok((paths_to_watch, graph, ps))
}
.map(move |result| match result {
- Ok((paths_to_watch, module_graph, ps)) => ResolutionResult::Restart {
+ Ok((paths_to_watch, graph, ps)) => ResolutionResult::Restart {
paths_to_watch,
- result: Ok((ps, module_graph)),
+ result: Ok((ps, graph)),
},
Err(e) => ResolutionResult::Restart {
paths_to_watch: vec![PathBuf::from(source_file2)],
@@ -739,28 +783,43 @@ async fn bundle_command(
})
};
- let operation = |(ps, module_graph): (ProcState, module_graph::Graph)| {
+ let operation = |(ps, graph): (ProcState, Arc<deno_graph::ModuleGraph>)| {
let flags = flags.clone();
let out_file = bundle_flags.out_file.clone();
async move {
- info!("{} {}", colors::green("Bundle"), module_graph.info()?.root);
-
- let output = bundle_module_graph(module_graph, ps, flags, debug)?;
-
+ let (bundle_emit, maybe_bundle_map) =
+ bundle_module_graph(graph.as_ref(), &ps, &flags)?;
debug!(">>>>> bundle END");
if let Some(out_file) = out_file.as_ref() {
- let output_bytes = output.as_bytes();
+ let output_bytes = bundle_emit.as_bytes();
let output_len = output_bytes.len();
fs_util::write_file(out_file, output_bytes, 0o644)?;
info!(
"{} {:?} ({})",
colors::green("Emit"),
out_file,
- colors::gray(&info::human_size(output_len as f64))
+ colors::gray(human_size(output_len as f64))
);
+ if let Some(bundle_map) = maybe_bundle_map {
+ let map_bytes = bundle_map.as_bytes();
+ let map_len = map_bytes.len();
+ let ext = if let Some(curr_ext) = out_file.extension() {
+ format!("{}.map", curr_ext.to_string_lossy())
+ } else {
+ "map".to_string()
+ };
+ let map_out_file = out_file.with_extension(ext);
+ fs_util::write_file(&map_out_file, map_bytes, 0o644)?;
+ info!(
+ "{} {:?} ({})",
+ colors::green("Emit"),
+ map_out_file,
+ colors::gray(human_size(map_len as f64))
+ );
+ }
} else {
- println!("{}", output);
+ println!("{}", bundle_emit);
}
Ok(())
@@ -825,9 +884,7 @@ async fn run_repl(flags: Flags, repl_flags: ReplFlags) -> Result<(), AnyError> {
let mut worker =
create_main_worker(&ps, main_module.clone(), permissions, None);
if flags.compat {
- worker
- .execute_side_module(&compat::get_node_globals_url())
- .await?;
+ worker.execute_side_module(&compat::GLOBAL_URL).await?;
}
worker.run_event_loop(false).await?;
@@ -858,9 +915,7 @@ async fn run_from_stdin(flags: Flags) -> Result<(), AnyError> {
debug!("main_module {}", main_module);
if flags.compat {
- worker
- .execute_side_module(&compat::get_node_globals_url())
- .await?;
+ worker.execute_side_module(&compat::GLOBAL_URL).await?;
}
worker.execute_main_module(&main_module).await?;
worker.execute_script(
@@ -883,25 +938,42 @@ async fn run_with_watch(flags: Flags, script: String) -> Result<(), AnyError> {
async move {
let main_module = resolve_url_or_path(&script1)?;
let ps = ProcState::build(flags).await?;
- let handler = Arc::new(Mutex::new(FetchHandler::new(
- &ps,
+ let mut cache = cache::FetchCacher::new(
+ ps.dir.gen_cache.clone(),
+ ps.file_fetcher.clone(),
Permissions::allow_all(),
Permissions::allow_all(),
- )?));
- let mut builder = module_graph::GraphBuilder::new(
- handler,
- ps.maybe_import_map.clone(),
- ps.lockfile.clone(),
);
- builder.add(&main_module, false).await?;
- builder.analyze_config_file(&ps.maybe_config_file).await?;
- let module_graph = builder.get_graph();
+ let maybe_locker = lockfile::as_maybe_locker(ps.lockfile.clone());
+ let maybe_imports = ps
+ .maybe_config_file
+ .as_ref()
+ .map(|cf| cf.to_maybe_imports())
+ .flatten();
+ let maybe_resolver =
+ ps.maybe_import_map.as_ref().map(ImportMapResolver::new);
+ let graph = deno_graph::create_graph(
+ vec![main_module.clone()],
+ false,
+ maybe_imports,
+ &mut cache,
+ maybe_resolver.as_ref().map(|r| r.as_resolver()),
+ maybe_locker,
+ None,
+ )
+ .await;
+ graph.valid()?;
// Find all local files in graph
- let mut paths_to_watch: Vec<PathBuf> = module_graph
- .get_modules()
+ let mut paths_to_watch: Vec<PathBuf> = graph
+ .specifiers()
.iter()
- .filter_map(|specifier| specifier.to_file_path().ok())
+ .filter_map(|(_, r)| {
+ r.as_ref()
+ .ok()
+ .map(|(s, _)| s.to_file_path().ok())
+ .flatten()
+ })
.collect();
if let Some(import_map) = ps.flags.import_map_path.as_ref() {
@@ -948,10 +1020,7 @@ async fn run_with_watch(flags: Flags, script: String) -> Result<(), AnyError> {
main_module: &ModuleSpecifier,
) -> Result<(), AnyError> {
if self.compat {
- self
- .worker
- .execute_side_module(&compat::get_node_globals_url())
- .await?;
+ self.worker.execute_side_module(&compat::GLOBAL_URL).await?;
}
self.worker.execute_main_module(main_module).await?;
self.worker.execute_script(
@@ -1046,9 +1115,7 @@ async fn run_command(
debug!("main_module {}", main_module);
if flags.compat {
- worker
- .execute_side_module(&compat::get_node_globals_url())
- .await?;
+ worker.execute_side_module(&compat::GLOBAL_URL).await?;
}
worker.execute_main_module(&main_module).await?;
worker.execute_script(
diff --git a/cli/module_graph.rs b/cli/module_graph.rs
deleted file mode 100644
index 7bea57e62..000000000
--- a/cli/module_graph.rs
+++ /dev/null
@@ -1,2834 +0,0 @@
-// Copyright 2018-2021 the Deno authors. All rights reserved. MIT license.
-use crate::ast;
-use crate::ast::transpile;
-use crate::ast::transpile_module;
-use crate::ast::BundleHook;
-use crate::ast::Location;
-use crate::checksum;
-use crate::colors;
-use crate::config_file::CompilerOptions;
-use crate::config_file::ConfigFile;
-use crate::config_file::IgnoredCompilerOptions;
-use crate::config_file::TsConfig;
-use crate::diagnostics::Diagnostics;
-use crate::info;
-use crate::lockfile::Lockfile;
-use crate::specifier_handler::CachedModule;
-use crate::specifier_handler::Dependency;
-use crate::specifier_handler::DependencyMap;
-use crate::specifier_handler::Emit;
-use crate::specifier_handler::FetchFuture;
-use crate::specifier_handler::SpecifierHandler;
-use crate::tsc;
-use crate::version;
-use deno_ast::swc::common::comments::Comment;
-use deno_ast::swc::common::BytePos;
-use deno_ast::swc::common::Span;
-use deno_ast::MediaType;
-use deno_ast::ParsedSource;
-use deno_ast::SourceTextInfo;
-use deno_core::error::anyhow;
-use deno_core::error::custom_error;
-use deno_core::error::get_custom_error_class;
-use deno_core::error::AnyError;
-use deno_core::error::Context;
-use deno_core::futures::stream::FuturesUnordered;
-use deno_core::futures::stream::StreamExt;
-use deno_core::parking_lot::Mutex;
-use deno_core::resolve_import;
-use deno_core::resolve_url_or_path;
-use deno_core::serde::Deserialize;
-use deno_core::serde::Deserializer;
-use deno_core::serde::Serialize;
-use deno_core::serde::Serializer;
-use deno_core::serde_json;
-use deno_core::serde_json::json;
-use deno_core::serde_json::Value;
-use deno_core::url::Url;
-use deno_core::ModuleResolutionError;
-use deno_core::ModuleSource;
-use deno_core::ModuleSpecifier;
-use deno_graph::analyze_dependencies;
-use import_map::ImportMap;
-use import_map::ImportMapError;
-use log::debug;
-use regex::Regex;
-use std::collections::HashMap;
-use std::collections::HashSet;
-use std::error::Error;
-use std::fmt;
-use std::path::PathBuf;
-use std::rc::Rc;
-use std::result;
-use std::sync::Arc;
-use std::time::Instant;
-
-lazy_static::lazy_static! {
- /// Matched the `@deno-types` pragma.
- static ref DENO_TYPES_RE: Regex =
- Regex::new(r#"(?i)^\s*@deno-types\s*=\s*(?:["']([^"']+)["']|(\S+))"#)
- .unwrap();
- /// Matches a `/// <reference ... />` comment reference.
- static ref TRIPLE_SLASH_REFERENCE_RE: Regex =
- Regex::new(r"(?i)^/\s*<reference\s.*?/>").unwrap();
- /// Matches a path reference, which adds a dependency to a module
- static ref PATH_REFERENCE_RE: Regex =
- Regex::new(r#"(?i)\spath\s*=\s*["']([^"']*)["']"#).unwrap();
- /// Matches a types reference, which for JavaScript files indicates the
- /// location of types to use when type checking a program that includes it as
- /// a dependency.
- static ref TYPES_REFERENCE_RE: Regex =
- Regex::new(r#"(?i)\stypes\s*=\s*["']([^"']*)["']"#).unwrap();
-}
-
-/// A group of errors that represent errors that can occur when interacting with
-/// a module graph.
-#[derive(Debug, Clone, Eq, PartialEq)]
-pub enum GraphError {
- /// A module using the HTTPS protocol is trying to import a module with an
- /// HTTP schema.
- InvalidDowngrade(ModuleSpecifier, Location),
- /// A remote module is trying to import a local module.
- InvalidLocalImport(ModuleSpecifier, Location),
- /// The source code is invalid, as it does not match the expected hash in the
- /// lockfile.
- InvalidSource(ModuleSpecifier, PathBuf),
- /// An unexpected dependency was requested for a module.
- MissingDependency(ModuleSpecifier, String),
- /// An unexpected specifier was requested.
- MissingSpecifier(ModuleSpecifier),
- /// The current feature is not supported.
- NotSupported(String),
- /// A unsupported media type was attempted to be imported as a module.
- UnsupportedImportType(ModuleSpecifier, MediaType),
-}
-
-impl fmt::Display for GraphError {
- fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
- match self {
- GraphError::InvalidDowngrade(ref specifier, ref location) => write!(f, "Modules imported via https are not allowed to import http modules.\n Importing: {}\n at {}", specifier, location),
- GraphError::InvalidLocalImport(ref specifier, ref location) => write!(f, "Remote modules are not allowed to import local modules. Consider using a dynamic import instead.\n Importing: {}\n at {}", specifier, location),
- GraphError::InvalidSource(ref specifier, ref lockfile) => write!(f, "The source code is invalid, as it does not match the expected hash in the lock file.\n Specifier: {}\n Lock file: {}", specifier, lockfile.to_str().unwrap()),
- GraphError::MissingDependency(ref referrer, specifier) => write!(
- f,
- "The graph is missing a dependency.\n Specifier: {} from {}",
- specifier, referrer
- ),
- GraphError::MissingSpecifier(ref specifier) => write!(
- f,
- "The graph is missing a specifier.\n Specifier: {}",
- specifier
- ),
- GraphError::NotSupported(ref msg) => write!(f, "{}", msg),
- GraphError::UnsupportedImportType(ref specifier, ref media_type) => write!(f, "An unsupported media type was attempted to be imported as a module.\n Specifier: {}\n MediaType: {}", specifier, media_type),
- }
- }
-}
-
-impl Error for GraphError {}
-
-/// A structure for handling bundle loading, which is implemented here, to
-/// avoid a circular dependency with `ast`.
-struct BundleLoader<'a> {
- cm: Rc<deno_ast::swc::common::SourceMap>,
- emit_options: &'a ast::EmitOptions,
- globals: &'a deno_ast::swc::common::Globals,
- graph: &'a Graph,
-}
-
-impl<'a> BundleLoader<'a> {
- pub fn new(
- graph: &'a Graph,
- emit_options: &'a ast::EmitOptions,
- globals: &'a deno_ast::swc::common::Globals,
- cm: Rc<deno_ast::swc::common::SourceMap>,
- ) -> Self {
- BundleLoader {
- cm,
- emit_options,
- globals,
- graph,
- }
- }
-}
-
-impl deno_ast::swc::bundler::Load for BundleLoader<'_> {
- fn load(
- &self,
- file: &deno_ast::swc::common::FileName,
- ) -> Result<deno_ast::swc::bundler::ModuleData, AnyError> {
- match file {
- deno_ast::swc::common::FileName::Url(specifier) => {
- if let Some(src) = self.graph.get_source(specifier) {
- let media_type = self
- .graph
- .get_media_type(specifier)
- .context("Looking up media type during bundling.")?;
- let (source_file, module) = transpile_module(
- specifier,
- &src,
- media_type,
- self.emit_options,
- self.globals,
- self.cm.clone(),
- )?;
- Ok(deno_ast::swc::bundler::ModuleData {
- fm: source_file,
- module,
- helpers: Default::default(),
- })
- } else {
- Err(
- GraphError::MissingDependency(
- specifier.clone(),
- "<bundle>".to_string(),
- )
- .into(),
- )
- }
- }
- _ => unreachable!("Received request for unsupported filename {:?}", file),
- }
- }
-}
-
-/// An enum which represents the parsed out values of references in source code.
-#[derive(Debug, Clone, Eq, PartialEq)]
-pub enum TypeScriptReference {
- Path(String),
- Types(String),
-}
-
-fn match_to_span(comment: &Comment, m: &regex::Match) -> Span {
- Span {
- lo: comment.span.lo + BytePos((m.start() + 1) as u32),
- hi: comment.span.lo + BytePos((m.end() + 1) as u32),
- ctxt: comment.span.ctxt,
- }
-}
-
-/// Determine if a comment contains a triple slash reference and optionally
-/// return its kind and value.
-pub fn parse_ts_reference(
- comment: &Comment,
-) -> Option<(TypeScriptReference, Span)> {
- if !TRIPLE_SLASH_REFERENCE_RE.is_match(&comment.text) {
- None
- } else if let Some(captures) = PATH_REFERENCE_RE.captures(&comment.text) {
- let m = captures.get(1).unwrap();
- Some((
- TypeScriptReference::Path(m.as_str().to_string()),
- match_to_span(comment, &m),
- ))
- } else {
- TYPES_REFERENCE_RE.captures(&comment.text).map(|captures| {
- let m = captures.get(1).unwrap();
- (
- TypeScriptReference::Types(m.as_str().to_string()),
- match_to_span(comment, &m),
- )
- })
- }
-}
-
-/// Determine if a comment contains a `@deno-types` pragma and optionally return
-/// its value.
-pub fn parse_deno_types(comment: &Comment) -> Option<(String, Span)> {
- let captures = DENO_TYPES_RE.captures(&comment.text)?;
- if let Some(m) = captures.get(1) {
- Some((m.as_str().to_string(), match_to_span(comment, &m)))
- } else if let Some(m) = captures.get(2) {
- Some((m.as_str().to_string(), match_to_span(comment, &m)))
- } else {
- unreachable!();
- }
-}
-
-/// A hashing function that takes the source code, version and optionally a
-/// user provided config and generates a string hash which can be stored to
-/// determine if the cached emit is valid or not.
-fn get_version(source: &str, version: &str, config: &[u8]) -> String {
- crate::checksum::gen(&[source.as_bytes(), version.as_bytes(), config])
-}
-
-/// A logical representation of a module within a graph.
-#[derive(Debug, Clone)]
-pub struct Module {
- pub dependencies: DependencyMap,
- is_dirty: bool,
- is_parsed: bool,
- maybe_emit: Option<Emit>,
- maybe_emit_path: Option<(PathBuf, Option<PathBuf>)>,
- maybe_import_map: Option<Arc<Mutex<ImportMap>>>,
- maybe_types: Option<(String, ModuleSpecifier)>,
- maybe_version: Option<String>,
- media_type: MediaType,
- specifier: ModuleSpecifier,
- text_info: SourceTextInfo,
- source_path: PathBuf,
-}
-
-impl Default for Module {
- fn default() -> Self {
- Module {
- dependencies: HashMap::new(),
- is_dirty: false,
- is_parsed: false,
- maybe_emit: None,
- maybe_emit_path: None,
- maybe_import_map: None,
- maybe_types: None,
- maybe_version: None,
- media_type: MediaType::Unknown,
- specifier: deno_core::resolve_url("file:///example.js").unwrap(),
- text_info: SourceTextInfo::from_string("".to_string()),
- source_path: PathBuf::new(),
- }
- }
-}
-
-impl Module {
- pub fn new(
- cached_module: CachedModule,
- is_root: bool,
- maybe_import_map: Option<Arc<Mutex<ImportMap>>>,
- ) -> Self {
- // If this is a local root file, and its media type is unknown, set the
- // media type to JavaScript. This allows easier ability to create "shell"
- // scripts with Deno.
- let media_type = if is_root
- && !cached_module.is_remote
- && cached_module.media_type == MediaType::Unknown
- {
- MediaType::JavaScript
- } else {
- cached_module.media_type
- };
- let mut module = Module {
- specifier: cached_module.specifier,
- maybe_import_map,
- media_type,
- text_info: SourceTextInfo::new(cached_module.source),
- source_path: cached_module.source_path,
- maybe_emit: cached_module.maybe_emit,
- maybe_emit_path: cached_module.maybe_emit_path,
- maybe_version: cached_module.maybe_version,
- is_dirty: false,
- ..Self::default()
- };
- if module.maybe_import_map.is_none() {
- if let Some(dependencies) = cached_module.maybe_dependencies {
- module.dependencies = dependencies;
- module.is_parsed = true;
- }
- }
- module.maybe_types = cached_module.maybe_types.map(|specifier| {
- (
- specifier.clone(),
- module
- .resolve_import(&specifier, None)
- .expect("could not resolve module"),
- )
- });
- module
- }
-
- /// Return `true` if the current hash of the module matches the stored
- /// version.
- pub fn is_emit_valid(&self, config: &[u8]) -> bool {
- if let Some(version) = self.maybe_version.clone() {
- version
- == get_version(self.text_info.text_str(), &version::deno(), config)
- } else {
- false
- }
- }
-
- /// Parse a module, populating the structure with data retrieved from the
- /// source of the module.
- pub fn parse(&mut self) -> Result<ParsedSource, AnyError> {
- let parsed_module = deno_ast::parse_module(deno_ast::ParseParams {
- specifier: self.specifier.as_str().to_string(),
- source: self.text_info.clone(),
- media_type: self.media_type,
- capture_tokens: false,
- maybe_syntax: None,
- })?;
-
- // parse out any triple slash references
- for comment in parsed_module.get_leading_comments().iter() {
- if let Some((ts_reference, _)) = parse_ts_reference(comment) {
- let location = Location::from_pos(&parsed_module, comment.span.lo);
- match ts_reference {
- TypeScriptReference::Path(import) => {
- let specifier =
- self.resolve_import(&import, Some(location.clone()))?;
- let dep = self
- .dependencies
- .entry(import)
- .or_insert_with(|| Dependency::new(location));
- dep.maybe_code = Some(specifier);
- }
- TypeScriptReference::Types(import) => {
- let specifier =
- self.resolve_import(&import, Some(location.clone()))?;
- if self.media_type == MediaType::JavaScript
- || self.media_type == MediaType::Jsx
- {
- // TODO(kitsonk) we need to specifically update the cache when
- // this value changes
- self.maybe_types = Some((import.clone(), specifier));
- } else {
- let dep = self
- .dependencies
- .entry(import)
- .or_insert_with(|| Dependency::new(location));
- dep.maybe_type = Some(specifier);
- }
- }
- }
- }
- }
-
- // Parse out all the syntactical dependencies for a module
- let dependencies = analyze_dependencies(&parsed_module);
- for desc in dependencies.iter().filter(|desc| {
- desc.kind != deno_ast::swc::dep_graph::DependencyKind::Require
- }) {
- let location = Location::from_pos(&parsed_module, desc.span.lo);
-
- // In situations where there is a potential issue with resolving the
- // import specifier, that ends up being a module resolution error for a
- // code dependency, we should not throw in the `ModuleGraph` but instead
- // wait until runtime and throw there, as with dynamic imports they need
- // to be catchable, which means they need to be resolved at runtime.
- let maybe_specifier =
- match self.resolve_import(&desc.specifier, Some(location.clone())) {
- Ok(specifier) => Some(specifier),
- Err(any_error) => {
- match any_error.downcast_ref::<ModuleResolutionError>() {
- Some(ModuleResolutionError::ImportPrefixMissing(..)) => {
- Some(Url::parse(&format!("bare:{}", &desc.specifier)).unwrap())
- }
- _ => match any_error.downcast_ref::<ImportMapError>() {
- Some(ImportMapError::UnmappedBareSpecifier(..)) => Some(
- Url::parse(&format!("bare:{}", &desc.specifier)).unwrap(),
- ),
- _ => {
- return Err(any_error);
- }
- },
- }
- }
- };
-
- // Parse out any `@deno-types` pragmas and modify dependency
- let maybe_type = if !desc.leading_comments.is_empty() {
- let comment = desc.leading_comments.last().unwrap();
- if let Some((deno_types, _)) = parse_deno_types(comment).as_ref() {
- Some(self.resolve_import(deno_types, Some(location.clone()))?)
- } else {
- None
- }
- } else {
- None
- };
-
- let dep = self
- .dependencies
- .entry(desc.specifier.to_string())
- .or_insert_with(|| Dependency::new(location));
- dep.is_dynamic = desc.is_dynamic;
- dep.maybe_code = maybe_specifier;
- // If there is a `@deno-types` pragma, we will add it to the dependency
- // if one doesn't already exist.
- if maybe_type.is_some() && dep.maybe_type.is_none() {
- dep.maybe_type = maybe_type;
- }
- }
- Ok(parsed_module)
- }
-
- fn resolve_import(
- &self,
- specifier: &str,
- maybe_location: Option<Location>,
- ) -> Result<ModuleSpecifier, AnyError> {
- let maybe_resolve = if let Some(import_map) = self.maybe_import_map.clone()
- {
- let import_map = import_map.lock();
- Some(import_map.resolve(specifier, self.specifier.as_str())?)
- } else {
- None
- };
- let mut remapped_import = false;
- let specifier = if let Some(module_specifier) = maybe_resolve {
- remapped_import = true;
- module_specifier
- } else {
- deno_core::resolve_import(specifier, self.specifier.as_str())?
- };
-
- let referrer_scheme = self.specifier.scheme();
- let specifier_scheme = specifier.scheme();
- let location = maybe_location.unwrap_or(Location {
- specifier: self.specifier.to_string(),
- line: 0,
- col: 0,
- });
-
- // Disallow downgrades from HTTPS to HTTP
- if referrer_scheme == "https" && specifier_scheme == "http" {
- return Err(
- GraphError::InvalidDowngrade(specifier.clone(), location).into(),
- );
- }
-
- // Disallow a remote URL from trying to import a local URL, unless it is a
- // remapped import via the import map
- if (referrer_scheme == "https" || referrer_scheme == "http")
- && !(specifier_scheme == "https" || specifier_scheme == "http")
- && !remapped_import
- {
- return Err(
- GraphError::InvalidLocalImport(specifier.clone(), location).into(),
- );
- }
-
- Ok(specifier)
- }
-
- pub fn set_emit(&mut self, code: String, maybe_map: Option<String>) {
- self.maybe_emit = Some(Emit::Cli((code, maybe_map)));
- }
-
- /// Calculate the hashed version of the module and update the `maybe_version`.
- pub fn set_version(&mut self, config: &[u8]) {
- self.maybe_version = Some(get_version(
- self.text_info.text_str(),
- &version::deno(),
- config,
- ))
- }
-
- pub fn size(&self) -> usize {
- self.text_info.text_str().len()
- }
-}
-
-#[derive(Clone, Debug, Default, Eq, PartialEq)]
-pub struct Stats(pub Vec<(String, u32)>);
-
-impl<'de> Deserialize<'de> for Stats {
- fn deserialize<D>(deserializer: D) -> result::Result<Self, D::Error>
- where
- D: Deserializer<'de>,
- {
- let items: Vec<(String, u32)> = Deserialize::deserialize(deserializer)?;
- Ok(Stats(items))
- }
-}
-
-impl Serialize for Stats {
- fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
- where
- S: Serializer,
- {
- Serialize::serialize(&self.0, serializer)
- }
-}
-
-impl fmt::Display for Stats {
- fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
- writeln!(f, "Compilation statistics:")?;
- for (key, value) in self.0.clone() {
- writeln!(f, " {}: {}", key, value)?;
- }
-
- Ok(())
- }
-}
-
-/// A structure that provides information about a module graph result.
-#[derive(Debug, Default)]
-pub struct ResultInfo {
- /// A structure which provides diagnostic information (usually from `tsc`)
- /// about the code in the module graph.
- pub diagnostics: Diagnostics,
- /// A map of specifiers to the result of their resolution in the module graph.
- pub loadable_modules:
- HashMap<ModuleSpecifier, Result<ModuleSource, AnyError>>,
- /// Optionally ignored compiler options that represent any options that were
- /// ignored if there was a user provided configuration.
- pub maybe_ignored_options: Option<IgnoredCompilerOptions>,
- /// A structure providing key metrics around the operation performed, in
- /// milliseconds.
- pub stats: Stats,
-}
-
-/// Represents the "default" type library that should be used when type
-/// checking the code in the module graph. Note that a user provided config
-/// of `"lib"` would override this value.
-#[derive(Debug, Clone, Eq, PartialEq)]
-pub enum TypeLib {
- DenoWindow,
- DenoWorker,
- UnstableDenoWindow,
- UnstableDenoWorker,
-}
-
-impl Default for TypeLib {
- fn default() -> Self {
- TypeLib::DenoWindow
- }
-}
-
-impl Serialize for TypeLib {
- fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
- where
- S: Serializer,
- {
- let value = match self {
- TypeLib::DenoWindow => vec!["deno.window".to_string()],
- TypeLib::DenoWorker => vec!["deno.worker".to_string()],
- TypeLib::UnstableDenoWindow => {
- vec!["deno.window".to_string(), "deno.unstable".to_string()]
- }
- TypeLib::UnstableDenoWorker => {
- vec!["deno.worker".to_string(), "deno.unstable".to_string()]
- }
- };
- Serialize::serialize(&value, serializer)
- }
-}
-
-#[derive(Debug, Default)]
-pub struct BundleOptions {
- /// If `true` then debug logging will be output from the isolate.
- pub debug: bool,
- /// An optional config file with user supplied TypeScript configuration
- /// that augments the the default configuration passed to the TypeScript
- /// compiler.
- pub maybe_config_file: Option<ConfigFile>,
-}
-
-#[derive(Debug, Default)]
-pub struct CheckOptions {
- /// If `true` then debug logging will be output from the isolate.
- pub debug: bool,
- /// Utilise the emit from `tsc` to update the emitted code for modules.
- pub emit: bool,
- /// The base type libraries that should be used when type checking.
- pub lib: TypeLib,
- /// An optional config file with user supplied TypeScript configuration
- /// that augments the the default configuration passed to the TypeScript
- /// compiler.
- pub maybe_config_file: Option<ConfigFile>,
- /// Ignore any previously emits and ensure that all files are emitted from
- /// source.
- pub reload: bool,
- /// A set of module specifiers to be excluded from the effect of
- /// `CheckOptions::reload` if it is `true`. Perhaps because they have already
- /// reloaded once in this process.
- pub reload_exclusions: HashSet<ModuleSpecifier>,
-}
-
-#[derive(Debug, Eq, PartialEq)]
-pub enum BundleType {
- /// Return the emitted contents of the program as a single "flattened" ES
- /// module.
- Module,
- /// Return the emitted contents of the program as a single script that
- /// executes the program using an immediately invoked function execution
- /// (IIFE).
- Classic,
- /// Do not bundle the emit, instead returning each of the modules that are
- /// part of the program as individual files.
- None,
-}
-
-impl Default for BundleType {
- fn default() -> Self {
- BundleType::None
- }
-}
-
-#[derive(Debug, Default)]
-pub struct EmitOptions {
- /// If true, then code will be type checked, otherwise type checking will be
- /// skipped. If false, then swc will be used for the emit, otherwise tsc will
- /// be used.
- pub check: bool,
- /// Indicate the form the result of the emit should take.
- pub bundle_type: BundleType,
- /// If `true` then debug logging will be output from the isolate.
- pub debug: bool,
- /// An optional map that contains user supplied TypeScript compiler
- /// configuration options that are passed to the TypeScript compiler.
- pub maybe_user_config: Option<HashMap<String, Value>>,
-}
-
-/// A structure which provides options when transpiling modules.
-#[derive(Debug, Default)]
-pub struct TranspileOptions {
- /// If `true` then debug logging will be output from the isolate.
- pub debug: bool,
- /// An optional config file with user supplied TypeScript configuration
- /// that augments the the default configuration passed to the TypeScript
- /// compiler.
- pub maybe_config_file: Option<ConfigFile>,
- /// Ignore any previously emits and ensure that all files are emitted from
- /// source.
- pub reload: bool,
- /// A set of module specifiers to be excluded from the effect of
- /// `CheckOptions::reload` if it is `true`. Perhaps because they have already
- /// reloaded once in this process.
- pub reload_exclusions: HashSet<ModuleSpecifier>,
-}
-
-#[derive(Debug, Clone)]
-enum ModuleSlot {
- /// The module fetch resulted in a non-recoverable error.
- Err(Arc<AnyError>),
- /// The the fetch resulted in a module.
- Module(Box<Module>),
- /// Used to denote a module that isn't part of the graph.
- None,
- /// The fetch of the module is pending.
- Pending,
-}
-
-/// A dependency graph of modules, were the modules that have been inserted via
-/// the builder will be loaded into the graph. Also provides an interface to
-/// be able to manipulate and handle the graph.
-#[derive(Debug, Clone)]
-pub struct Graph {
- /// A reference to the specifier handler that will retrieve and cache modules
- /// for the graph.
- handler: Arc<Mutex<dyn SpecifierHandler>>,
- /// Optional TypeScript build info that will be passed to `tsc` if `tsc` is
- /// invoked.
- maybe_tsbuildinfo: Option<String>,
- /// The modules that are part of the graph.
- modules: HashMap<ModuleSpecifier, ModuleSlot>,
- /// A map of redirects, where a module specifier is redirected to another
- /// module specifier by the handler. All modules references should be
- /// resolved internally via this, before attempting to access the module via
- /// the handler, to make sure the correct modules is being dealt with.
- redirects: HashMap<ModuleSpecifier, ModuleSpecifier>,
- /// The module specifiers that have been uniquely added to the graph, which
- /// does not include any transient dependencies.
- roots: Vec<ModuleSpecifier>,
- /// If all of the root modules are dynamically imported, then this is true.
- /// This is used to ensure correct `--reload` behavior, where subsequent
- /// calls to a module graph where the emit is already valid do not cause the
- /// graph to re-emit.
- roots_dynamic: bool,
- // A reference to lock file that will be used to check module integrity.
- maybe_lockfile: Option<Arc<Mutex<Lockfile>>>,
-}
-
-/// Convert a specifier and a module slot in a result to the module source which
-/// is needed by Deno core for loading the module.
-fn to_module_result(
- (specifier, module_slot): (&ModuleSpecifier, &ModuleSlot),
-) -> (ModuleSpecifier, Result<ModuleSource, AnyError>) {
- match module_slot {
- ModuleSlot::Err(err) => (specifier.clone(), Err(anyhow!(err.to_string()))),
- ModuleSlot::Module(module) => (
- specifier.clone(),
- if let Some(emit) = &module.maybe_emit {
- match emit {
- Emit::Cli((code, _)) => Ok(ModuleSource {
- code: code.clone(),
- module_url_found: module.specifier.to_string(),
- module_url_specified: specifier.to_string(),
- }),
- }
- } else {
- match module.media_type {
- MediaType::JavaScript | MediaType::Unknown => Ok(ModuleSource {
- code: module.text_info.text_str().to_string(),
- module_url_found: module.specifier.to_string(),
- module_url_specified: specifier.to_string(),
- }),
- _ => Err(custom_error(
- "NotFound",
- format!("Compiled module not found \"{}\"", specifier),
- )),
- }
- },
- ),
- _ => (
- specifier.clone(),
- Err(anyhow!("Module \"{}\" unavailable.", specifier)),
- ),
- }
-}
-
-impl Graph {
- /// Create a new instance of a graph, ready to have modules loaded it.
- ///
- /// The argument `handler` is an instance of a structure that implements the
- /// `SpecifierHandler` trait.
- ///
- pub fn new(
- handler: Arc<Mutex<dyn SpecifierHandler>>,
- maybe_lockfile: Option<Arc<Mutex<Lockfile>>>,
- ) -> Self {
- Graph {
- handler,
- maybe_tsbuildinfo: None,
- modules: HashMap::new(),
- redirects: HashMap::new(),
- roots: Vec::new(),
- roots_dynamic: true,
- maybe_lockfile,
- }
- }
-
- /// Transform the module graph into a single JavaScript module which is
- /// returned as a `String` in the result.
- pub fn bundle(
- &self,
- options: BundleOptions,
- ) -> Result<(String, Stats, Option<IgnoredCompilerOptions>), AnyError> {
- if self.roots.is_empty() || self.roots.len() > 1 {
- return Err(GraphError::NotSupported(format!("Bundling is only supported when there is a single root module in the graph. Found: {}", self.roots.len())).into());
- }
-
- let start = Instant::now();
- let root_specifier = self.roots[0].clone();
- let mut ts_config = TsConfig::new(json!({
- "checkJs": false,
- "emitDecoratorMetadata": false,
- "importsNotUsedAsValues": "remove",
- "inlineSourceMap": false,
- "inlineSources": false,
- "sourceMap": false,
- "jsx": "react",
- "jsxFactory": "React.createElement",
- "jsxFragmentFactory": "React.Fragment",
- }));
- let maybe_ignored_options = ts_config
- .merge_tsconfig_from_config_file(options.maybe_config_file.as_ref())?;
-
- let (src, _) = self.emit_bundle(
- &root_specifier,
- &ts_config.into(),
- &BundleType::Module,
- )?;
- let stats = Stats(vec![
- ("Files".to_string(), self.modules.len() as u32),
- ("Total time".to_string(), start.elapsed().as_millis() as u32),
- ]);
-
- Ok((src, stats, maybe_ignored_options))
- }
-
- /// Type check the module graph, corresponding to the options provided.
- pub fn check(self, options: CheckOptions) -> Result<ResultInfo, AnyError> {
- self.validate()?;
- let mut config = TsConfig::new(json!({
- "allowJs": true,
- // TODO(@kitsonk) is this really needed?
- "esModuleInterop": true,
- // Enabled by default to align to transpile/swc defaults
- "experimentalDecorators": true,
- "incremental": true,
- "jsx": "react",
- "isolatedModules": true,
- "lib": options.lib,
- "module": "esnext",
- "strict": true,
- "target": "esnext",
- "tsBuildInfoFile": "deno:///.tsbuildinfo",
- "useDefineForClassFields": true,
- // TODO(@kitsonk) remove for Deno 1.15
- "useUnknownInCatchVariables": false,
- }));
- if options.emit {
- config.merge(&json!({
- // TODO(@kitsonk) consider enabling this by default
- // see: https://github.com/denoland/deno/issues/7732
- "emitDecoratorMetadata": false,
- "importsNotUsedAsValues": "remove",
- "inlineSourceMap": true,
- "inlineSources": true,
- "outDir": "deno://",
- "removeComments": true,
- }));
- } else {
- config.merge(&json!({
- "noEmit": true,
- }));
- }
- let maybe_ignored_options = config
- .merge_tsconfig_from_config_file(options.maybe_config_file.as_ref())?;
-
- let needs_reload = options.reload
- && !self
- .roots
- .iter()
- .all(|u| options.reload_exclusions.contains(u));
- // Short circuit if none of the modules require an emit, or all of the
- // modules that require an emit have a valid emit.
- if !self.needs_emit(&config) || self.is_emit_valid(&config) && !needs_reload
- {
- debug!("graph does not need to be checked or emitted.");
- return Ok(ResultInfo {
- maybe_ignored_options,
- loadable_modules: self.get_loadable_modules(),
- ..Default::default()
- });
- }
-
- // TODO(@kitsonk) not totally happy with this here, but this is the first
- // point where we know we are actually going to check the program. If we
- // moved it out of here, we wouldn't know until after the check has already
- // happened, which isn't informative to the users.
- for specifier in &self.roots {
- log::info!("{} {}", colors::green("Check"), specifier);
- }
-
- let root_names = self.get_root_names(!config.get_check_js())?;
- let maybe_tsbuildinfo = self.maybe_tsbuildinfo.clone();
- let hash_data =
- vec![config.as_bytes(), version::deno().as_bytes().to_owned()];
- let graph = Arc::new(Mutex::new(self));
-
- let maybe_config_specifier =
- if let Some(config_file) = &options.maybe_config_file {
- ModuleSpecifier::from_file_path(&config_file.path).ok()
- } else {
- None
- };
- debug!("maybe_config_specifier: {:?}", maybe_config_specifier);
-
- let response = tsc::exec(tsc::Request {
- config: config.clone(),
- debug: options.debug,
- graph: graph.clone(),
- hash_data,
- maybe_config_specifier,
- maybe_tsbuildinfo,
- root_names,
- })?;
-
- let mut graph = graph.lock();
- graph.maybe_tsbuildinfo = response.maybe_tsbuildinfo;
- // Only process changes to the graph if there are no diagnostics and there
- // were files emitted.
- if response.diagnostics.is_empty() {
- if !response.emitted_files.is_empty() {
- let mut codes = HashMap::new();
- let mut maps = HashMap::new();
- let check_js = config.get_check_js();
- for emit in &response.emitted_files {
- if let Some(specifiers) = &emit.maybe_specifiers {
- assert!(specifiers.len() == 1, "Unexpected specifier length");
- // The specifier emitted might not be the redirected specifier, and
- // therefore we need to ensure it is the correct one.
- let specifier = graph.resolve_specifier(&specifiers[0]);
- // Sometimes if tsc sees a CommonJS file it will _helpfully_ output it
- // to ESM, which we don't really want unless someone has enabled the
- // check_js option.
- if !check_js
- && graph.get_media_type(specifier) == Some(MediaType::JavaScript)
- {
- debug!("skipping emit for {}", specifier);
- continue;
- }
- match emit.media_type {
- MediaType::JavaScript => {
- codes.insert(specifier.clone(), emit.data.clone());
- }
- MediaType::SourceMap => {
- maps.insert(specifier.clone(), emit.data.clone());
- }
- _ => unreachable!(),
- }
- }
- }
- let config = config.as_bytes();
- for (specifier, code) in codes.iter() {
- if let ModuleSlot::Module(module) =
- graph.get_module_mut(specifier).unwrap()
- {
- module.set_emit(code.clone(), maps.get(specifier).cloned());
- module.set_version(&config);
- module.is_dirty = true;
- } else {
- return Err(GraphError::MissingSpecifier(specifier.clone()).into());
- }
- }
- }
- graph.flush()?;
- }
-
- Ok(ResultInfo {
- diagnostics: response.diagnostics,
- loadable_modules: graph.get_loadable_modules(),
- maybe_ignored_options,
- stats: response.stats,
- })
- }
-
- /// Indicates if the module graph contains the supplied specifier or not.
- pub fn contains(&self, specifier: &ModuleSpecifier) -> bool {
- matches!(self.get_module(specifier), ModuleSlot::Module(_))
- }
-
- /// Emit the module graph in a specific format. This is specifically designed
- /// to be an "all-in-one" API for access by the runtime, allowing both
- /// emitting single modules as well as bundles, using Deno module resolution
- /// or supplied sources.
- pub fn emit(
- mut self,
- options: EmitOptions,
- ) -> Result<(HashMap<String, String>, ResultInfo), AnyError> {
- let mut config = TsConfig::new(json!({
- "allowJs": true,
- "checkJs": false,
- // TODO(@kitsonk) consider enabling this by default
- // see: https://github.com/denoland/deno/issues/7732
- "emitDecoratorMetadata": false,
- "esModuleInterop": true,
- "experimentalDecorators": true,
- "importsNotUsedAsValues": "remove",
- "inlineSourceMap": false,
- "inlineSources": false,
- "sourceMap": false,
- "isolatedModules": true,
- "jsx": "react",
- "jsxFactory": "React.createElement",
- "jsxFragmentFactory": "React.Fragment",
- "lib": TypeLib::DenoWindow,
- "module": "esnext",
- "strict": true,
- "target": "esnext",
- "useDefineForClassFields": true,
- // TODO(@kitsonk) remove for Deno 1.15
- "useUnknownInCatchVariables": false,
- }));
- let opts = match options.bundle_type {
- BundleType::Module | BundleType::Classic => json!({
- "noEmit": true,
- "removeComments": true,
- "sourceMap": true,
- }),
- BundleType::None => json!({
- "outDir": "deno://",
- "removeComments": true,
- "sourceMap": true,
- }),
- };
- config.merge(&opts);
- let maybe_ignored_options =
- if let Some(user_options) = &options.maybe_user_config {
- config.merge_user_config(user_options)?
- } else {
- None
- };
-
- if !options.check && config.get_declaration() {
- return Err(anyhow!("The option of `check` is false, but the compiler option of `declaration` is true which is not currently supported."));
- }
- if options.bundle_type != BundleType::None && config.get_declaration() {
- return Err(anyhow!("The bundle option is set, but the compiler option of `declaration` is true which is not currently supported."));
- }
-
- let mut emitted_files = HashMap::new();
- if options.check {
- let root_names = self.get_root_names(!config.get_check_js())?;
- let hash_data =
- vec![config.as_bytes(), version::deno().as_bytes().to_owned()];
- let graph = Arc::new(Mutex::new(self));
- let response = tsc::exec(tsc::Request {
- config: config.clone(),
- debug: options.debug,
- graph: graph.clone(),
- hash_data,
- maybe_config_specifier: None,
- maybe_tsbuildinfo: None,
- root_names,
- })?;
-
- let graph = graph.lock();
- match options.bundle_type {
- BundleType::Module | BundleType::Classic => {
- assert!(
- response.emitted_files.is_empty(),
- "No files should have been emitted from tsc."
- );
- assert_eq!(
- graph.roots.len(),
- 1,
- "Only a single root module supported."
- );
- let specifier = &graph.roots[0];
- let (src, maybe_src_map) = graph.emit_bundle(
- specifier,
- &config.into(),
- &options.bundle_type,
- )?;
- emitted_files.insert("deno:///bundle.js".to_string(), src);
- if let Some(src_map) = maybe_src_map {
- emitted_files.insert("deno:///bundle.js.map".to_string(), src_map);
- }
- }
- BundleType::None => {
- for emitted_file in &response.emitted_files {
- assert!(
- emitted_file.maybe_specifiers.is_some(),
- "Orphaned file emitted."
- );
- let specifiers = emitted_file.maybe_specifiers.clone().unwrap();
- assert_eq!(
- specifiers.len(),
- 1,
- "An unexpected number of specifiers associated with emitted file."
- );
- let specifier = specifiers[0].clone();
- let extension = match emitted_file.media_type {
- MediaType::JavaScript => ".js",
- MediaType::SourceMap => ".js.map",
- MediaType::Dts => ".d.ts",
- _ => unreachable!(),
- };
- let key = format!("{}{}", specifier, extension);
- emitted_files.insert(key, emitted_file.data.clone());
- }
- }
- };
-
- Ok((
- emitted_files,
- ResultInfo {
- diagnostics: response.diagnostics,
- loadable_modules: graph.get_loadable_modules(),
- maybe_ignored_options,
- stats: response.stats,
- },
- ))
- } else {
- let start = Instant::now();
- let mut emit_count = 0_u32;
- match options.bundle_type {
- BundleType::Module | BundleType::Classic => {
- assert_eq!(
- self.roots.len(),
- 1,
- "Only a single root module supported."
- );
- let specifier = &self.roots[0];
- let (src, maybe_src_map) = self.emit_bundle(
- specifier,
- &config.into(),
- &options.bundle_type,
- )?;
- emit_count += 1;
- emitted_files.insert("deno:///bundle.js".to_string(), src);
- if let Some(src_map) = maybe_src_map {
- emitted_files.insert("deno:///bundle.js.map".to_string(), src_map);
- }
- }
- BundleType::None => {
- let check_js = config.get_check_js();
- let emit_options: ast::EmitOptions = config.into();
- for (_, module_slot) in self.modules.iter_mut() {
- if let ModuleSlot::Module(module) = module_slot {
- if !(check_js
- || module.media_type == MediaType::Jsx
- || module.media_type == MediaType::Tsx
- || module.media_type == MediaType::TypeScript)
- {
- emitted_files.insert(
- module.specifier.to_string(),
- module.text_info.text_str().to_string(),
- );
- }
- let parsed_module = module.parse()?;
- let (code, maybe_map) = transpile(&parsed_module, &emit_options)?;
- emit_count += 1;
- emitted_files.insert(format!("{}.js", module.specifier), code);
- if let Some(map) = maybe_map {
- emitted_files
- .insert(format!("{}.js.map", module.specifier), map);
- }
- }
- }
- self.flush()?;
- }
- }
-
- let stats = Stats(vec![
- ("Files".to_string(), self.modules.len() as u32),
- ("Emitted".to_string(), emit_count),
- ("Total time".to_string(), start.elapsed().as_millis() as u32),
- ]);
-
- Ok((
- emitted_files,
- ResultInfo {
- diagnostics: Default::default(),
- loadable_modules: self.get_loadable_modules(),
- maybe_ignored_options,
- stats,
- },
- ))
- }
- }
-
- /// Shared between `bundle()` and `emit()`.
- fn emit_bundle(
- &self,
- specifier: &ModuleSpecifier,
- emit_options: &ast::EmitOptions,
- bundle_type: &BundleType,
- ) -> Result<(String, Option<String>), AnyError> {
- let cm = Rc::new(deno_ast::swc::common::SourceMap::new(
- deno_ast::swc::common::FilePathMapping::empty(),
- ));
- let globals = deno_ast::swc::common::Globals::new();
- let loader = BundleLoader::new(self, emit_options, &globals, cm.clone());
- let hook = Box::new(BundleHook);
- let module = match bundle_type {
- BundleType::Module => deno_ast::swc::bundler::ModuleType::Es,
- BundleType::Classic => deno_ast::swc::bundler::ModuleType::Iife,
- _ => unreachable!("invalid bundle type"),
- };
- let bundler = deno_ast::swc::bundler::Bundler::new(
- &globals,
- cm.clone(),
- loader,
- self,
- deno_ast::swc::bundler::Config {
- module,
- ..Default::default()
- },
- hook,
- );
- let mut entries = HashMap::new();
- entries.insert(
- "bundle".to_string(),
- deno_ast::swc::common::FileName::Url(specifier.clone()),
- );
- let output = bundler
- .bundle(entries)
- .context("Unable to output bundle during Graph::bundle().")?;
- let mut buf = Vec::new();
- let mut src_map_buf = Vec::new();
- {
- let mut emitter = deno_ast::swc::codegen::Emitter {
- cfg: deno_ast::swc::codegen::Config { minify: false },
- cm: cm.clone(),
- comments: None,
- wr: Box::new(deno_ast::swc::codegen::text_writer::JsWriter::new(
- cm.clone(),
- "\n",
- &mut buf,
- Some(&mut src_map_buf),
- )),
- };
-
- emitter
- .emit_module(&output[0].module)
- .context("Unable to emit bundle during Graph::bundle().")?;
- }
- let mut src = String::from_utf8(buf)
- .context("Emitted bundle is an invalid utf-8 string.")?;
- let mut map: Option<String> = None;
- {
- let mut buf = Vec::new();
- cm.build_source_map_from(&mut src_map_buf, None)
- .to_writer(&mut buf)?;
-
- if emit_options.inline_source_map {
- src.push_str("//# sourceMappingURL=data:application/json;base64,");
- let encoded_map = base64::encode(buf);
- src.push_str(&encoded_map);
- } else if emit_options.source_map {
- map = Some(String::from_utf8(buf)?);
- }
- }
-
- Ok((src, map))
- }
-
- /// Update the handler with any modules that are marked as _dirty_ and update
- /// any build info if present.
- fn flush(&mut self) -> Result<(), AnyError> {
- let mut handler = self.handler.lock();
- for (_, module_slot) in self.modules.iter_mut() {
- if let ModuleSlot::Module(module) = module_slot {
- if module.is_dirty {
- if let Some(emit) = &module.maybe_emit {
- handler.set_cache(&module.specifier, emit)?;
- }
- if let Some(version) = &module.maybe_version {
- handler.set_version(&module.specifier, version.clone())?;
- }
- module.is_dirty = false;
- }
- }
- }
- for root_specifier in self.roots.iter() {
- if let Some(tsbuildinfo) = &self.maybe_tsbuildinfo {
- handler.set_tsbuildinfo(root_specifier, tsbuildinfo.to_owned())?;
- }
- }
-
- Ok(())
- }
-
- /// Retrieve the first module loading error from the graph and return it.
- pub fn get_errors(&self) -> HashMap<ModuleSpecifier, String> {
- self
- .modules
- .iter()
- .filter_map(|(s, sl)| match sl {
- ModuleSlot::Err(err) => Some((s.clone(), err.to_string())),
- _ => None,
- })
- .collect()
- }
-
- /// Retrieve a map that contains a representation of each module in the graph
- /// which can be used to provide code to a module loader without holding all
- /// the state to be able to operate on the graph.
- pub fn get_loadable_modules(
- &self,
- ) -> HashMap<ModuleSpecifier, Result<ModuleSource, AnyError>> {
- let mut loadable_modules: HashMap<
- ModuleSpecifier,
- Result<ModuleSource, AnyError>,
- > = self.modules.iter().map(to_module_result).collect();
- for (specifier, _) in self.redirects.iter() {
- if let Some(module_slot) =
- self.modules.get(self.resolve_specifier(specifier))
- {
- let (_, result) = to_module_result((specifier, module_slot));
- loadable_modules.insert(specifier.clone(), result);
- }
- }
- loadable_modules
- }
-
- pub fn get_media_type(
- &self,
- specifier: &ModuleSpecifier,
- ) -> Option<MediaType> {
- if let ModuleSlot::Module(module) = self.get_module(specifier) {
- Some(module.media_type)
- } else {
- None
- }
- }
-
- fn get_module(&self, specifier: &ModuleSpecifier) -> &ModuleSlot {
- let s = self.resolve_specifier(specifier);
- if let Some(module_slot) = self.modules.get(s) {
- module_slot
- } else {
- &ModuleSlot::None
- }
- }
-
- fn get_module_mut(
- &mut self,
- specifier: &ModuleSpecifier,
- ) -> Option<&mut ModuleSlot> {
- // this is duplicated code because `.resolve_specifier` requires an
- // immutable borrow, but if `.resolve_specifier` is mut, then everything
- // that calls it is is mut
- let mut s = specifier;
- while let Some(redirect) = self.redirects.get(s) {
- s = redirect;
- }
- self.modules.get_mut(s)
- }
-
- pub fn get_specifier(
- &self,
- specifier: &ModuleSpecifier,
- ) -> Result<&Module, AnyError> {
- let s = self.resolve_specifier(specifier);
- match self.get_module(s) {
- ModuleSlot::Module(m) => Ok(m.as_ref()),
- ModuleSlot::Err(e) => Err(anyhow!(e.to_string())),
- _ => Err(GraphError::MissingSpecifier(specifier.clone()).into()),
- }
- }
-
- /// Consume graph and return list of all module specifiers contained in the
- /// graph.
- pub fn get_modules(&self) -> Vec<ModuleSpecifier> {
- self.modules.keys().map(|s| s.to_owned()).collect()
- }
-
- /// Transform `self.roots` into something that works for `tsc`, because `tsc`
- /// doesn't like root names without extensions that match its expectations,
- /// nor does it have any concept of redirection, so we have to resolve all
- /// that upfront before feeding it to `tsc`. In addition, if checkJs is not
- /// true, we should pass all emittable files in as the roots, so that `tsc`
- /// type checks them and potentially emits them.
- fn get_root_names(
- &self,
- include_emittable: bool,
- ) -> Result<Vec<(ModuleSpecifier, MediaType)>, AnyError> {
- let root_names: Vec<ModuleSpecifier> = if include_emittable {
- // in situations where there is `allowJs` with tsc, but not `checkJs`,
- // then tsc will not parse the whole module graph, meaning that any
- // JavaScript importing TypeScript will get ignored, meaning that those
- // files will not get emitted. To counter act that behavior, we will
- // include all modules that are emittable.
- let mut specifiers = HashSet::<&ModuleSpecifier>::new();
- for (_, module_slot) in self.modules.iter() {
- if let ModuleSlot::Module(module) = module_slot {
- if module.media_type == MediaType::Jsx
- || module.media_type == MediaType::TypeScript
- || module.media_type == MediaType::Tsx
- {
- specifiers.insert(&module.specifier);
- }
- }
- }
- // We should include all the original roots as well.
- for specifier in self.roots.iter() {
- specifiers.insert(specifier);
- }
- specifiers.into_iter().cloned().collect()
- } else {
- self.roots.clone()
- };
- let mut root_types = vec![];
- for ms in root_names {
- // if the root module has a types specifier, we should be sending that
- // to tsc instead of the original specifier
- let specifier = self.resolve_specifier(&ms);
- let module = match self.get_module(specifier) {
- ModuleSlot::Module(module) => module,
- ModuleSlot::Err(error) => {
- // It would be great if we could just clone the error here...
- if let Some(class) = get_custom_error_class(error) {
- return Err(custom_error(class, error.to_string()));
- } else {
- panic!("unsupported ModuleSlot error");
- }
- }
- _ => {
- panic!("missing module");
- }
- };
- let specifier = if let Some((_, types_specifier)) = &module.maybe_types {
- self.resolve_specifier(types_specifier)
- } else {
- specifier
- };
- root_types.push((
- // root modules can be redirects, so before we pass it to tsc we need
- // to resolve the redirect
- specifier.clone(),
- self.get_media_type(specifier).unwrap(),
- ));
- }
- Ok(root_types)
- }
-
- /// Get the source for a given module specifier. If the module is not part
- /// of the graph, the result will be `None`.
- pub fn get_source(&self, specifier: &ModuleSpecifier) -> Option<Arc<String>> {
- if let ModuleSlot::Module(module) = self.get_module(specifier) {
- Some(module.text_info.text())
- } else {
- None
- }
- }
-
- /// Return a structure which provides information about the module graph and
- /// the relationship of the modules in the graph. This structure is used to
- /// provide information for the `info` subcommand.
- pub fn info(&self) -> Result<info::ModuleGraphInfo, AnyError> {
- if self.roots.is_empty() || self.roots.len() > 1 {
- return Err(GraphError::NotSupported(format!("Info is only supported when there is a single root module in the graph. Found: {}", self.roots.len())).into());
- }
-
- let root = self.resolve_specifier(&self.roots[0]).clone();
- let mut modules: Vec<info::ModuleGraphInfoMod> = self
- .modules
- .iter()
- .filter_map(|(sp, sl)| match sl {
- ModuleSlot::Module(module) => {
- let mut dependencies: Vec<info::ModuleGraphInfoDep> = module
- .dependencies
- .iter()
- .map(|(k, v)| info::ModuleGraphInfoDep {
- specifier: k.clone(),
- is_dynamic: v.is_dynamic,
- maybe_code: v
- .maybe_code
- .clone()
- .map(|s| self.resolve_specifier(&s).clone()),
- maybe_type: v
- .maybe_type
- .clone()
- .map(|s| self.resolve_specifier(&s).clone()),
- })
- .collect();
- dependencies.sort();
- let (emit, map) =
- if let Some((emit, maybe_map)) = &module.maybe_emit_path {
- (Some(emit.clone()), maybe_map.clone())
- } else {
- (None, None)
- };
- let maybe_type_dependency =
- module.maybe_types.clone().map(|(specifier, _type)| {
- info::ModuleGraphInfoDep {
- specifier,
- is_dynamic: false,
- maybe_code: None,
- maybe_type: Some(_type),
- }
- });
- Some(info::ModuleGraphInfoMod {
- specifier: sp.clone(),
- dependencies,
- maybe_type_dependency,
- size: Some(module.size()),
- media_type: Some(module.media_type),
- local: Some(module.source_path.clone()),
- checksum: Some(checksum::gen(&[module
- .text_info
- .text_str()
- .as_bytes()])),
- emit,
- map,
- ..Default::default()
- })
- }
- ModuleSlot::Err(err) => Some(info::ModuleGraphInfoMod {
- specifier: sp.clone(),
- error: Some(err.to_string()),
- ..Default::default()
- }),
- _ => None,
- })
- .collect();
-
- modules.sort();
-
- let size = modules.iter().fold(0_usize, |acc, m| {
- if let Some(size) = &m.size {
- acc + size
- } else {
- acc
- }
- });
-
- Ok(info::ModuleGraphInfo {
- root,
- modules,
- size,
- })
- }
-
- /// Determines if all of the modules in the graph that require an emit have
- /// a valid emit. Returns `true` if all the modules have a valid emit,
- /// otherwise false.
- fn is_emit_valid(&self, config: &TsConfig) -> bool {
- let check_js = config.get_check_js();
- let config = config.as_bytes();
- self.modules.iter().all(|(_, m)| {
- if let ModuleSlot::Module(m) = m {
- let needs_emit = match m.media_type {
- MediaType::TypeScript | MediaType::Tsx | MediaType::Jsx => true,
- MediaType::JavaScript => check_js,
- _ => false,
- };
- if needs_emit {
- m.is_emit_valid(&config)
- } else {
- true
- }
- } else {
- true
- }
- })
- }
-
- /// Verify the subresource integrity of the graph based upon the optional
- /// lockfile, updating the lockfile with any missing resources. This will
- /// error if any of the resources do not match their lock status.
- pub fn lock(&self) {
- if let Some(lf) = self.maybe_lockfile.as_ref() {
- let mut lockfile = lf.lock();
- for (ms, module_slot) in self.modules.iter() {
- if let ModuleSlot::Module(module) = module_slot {
- let specifier = module.specifier.to_string();
- let valid =
- lockfile.check_or_insert(&specifier, module.text_info.text_str());
- if !valid {
- eprintln!(
- "{}",
- GraphError::InvalidSource(ms.clone(), lockfile.filename.clone())
- );
- std::process::exit(10);
- }
- }
- }
- }
- }
-
- /// Determines if any of the modules in the graph are required to be emitted.
- /// This is similar to `emit_valid()` except that the actual emit isn't
- /// checked to determine if it is valid.
- fn needs_emit(&self, config: &TsConfig) -> bool {
- let check_js = config.get_check_js();
- self.modules.iter().any(|(_, m)| match m {
- ModuleSlot::Module(m) => match m.media_type {
- MediaType::TypeScript | MediaType::Tsx | MediaType::Jsx => true,
- MediaType::JavaScript => check_js,
- _ => false,
- },
- _ => false,
- })
- }
-
- /// Given a string specifier and a referring module specifier, provide the
- /// resulting module specifier and media type for the module that is part of
- /// the graph.
- ///
- /// # Arguments
- ///
- /// * `specifier` - The string form of the module specifier that needs to be
- /// resolved.
- /// * `referrer` - The referring `ModuleSpecifier`.
- /// * `prefer_types` - When resolving to a module specifier, determine if a
- /// type dependency is preferred over a code dependency. This is set to
- /// `true` when resolving module names for `tsc` as it needs the type
- /// dependency over the code, while other consumers do not handle type only
- /// dependencies.
- pub fn resolve(
- &self,
- specifier: &str,
- referrer: &ModuleSpecifier,
- prefer_types: bool,
- ) -> Result<ModuleSpecifier, AnyError> {
- let module = if let ModuleSlot::Module(module) = self.get_module(referrer) {
- module
- } else {
- return Err(GraphError::MissingSpecifier(referrer.clone()).into());
- };
- if !module.dependencies.contains_key(specifier) {
- return Err(
- GraphError::MissingDependency(
- referrer.to_owned(),
- specifier.to_owned(),
- )
- .into(),
- );
- }
- let dependency = module.dependencies.get(specifier).unwrap();
- // If there is a @deno-types pragma that impacts the dependency, then the
- // maybe_type property will be set with that specifier, otherwise we use the
- // specifier that point to the runtime code.
- let resolved_specifier = if prefer_types && dependency.maybe_type.is_some()
- {
- dependency.maybe_type.clone().unwrap()
- } else if let Some(code_specifier) = dependency.maybe_code.clone() {
- code_specifier
- } else {
- return Err(
- GraphError::MissingDependency(
- referrer.to_owned(),
- specifier.to_owned(),
- )
- .into(),
- );
- };
- let dep_module = if let ModuleSlot::Module(dep_module) =
- self.get_module(&resolved_specifier)
- {
- dep_module
- } else {
- return Err(
- GraphError::MissingDependency(
- referrer.to_owned(),
- resolved_specifier.to_string(),
- )
- .into(),
- );
- };
- // In the case that there is a X-TypeScript-Types or a triple-slash types,
- // then the `maybe_types` specifier will be populated and we should use that
- // instead.
- let result = if prefer_types && dep_module.maybe_types.is_some() {
- let (_, types) = dep_module.maybe_types.clone().unwrap();
- // It is possible that `types` points to a redirected specifier, so we
- // need to ensure it resolves to the final specifier in the graph.
- self.resolve_specifier(&types).clone()
- } else {
- dep_module.specifier.clone()
- };
-
- Ok(result)
- }
-
- /// Takes a module specifier and returns the "final" specifier, accounting for
- /// any redirects that may have occurred.
- fn resolve_specifier<'a>(
- &'a self,
- specifier: &'a ModuleSpecifier,
- ) -> &'a ModuleSpecifier {
- let mut s = specifier;
- let mut seen = HashSet::new();
- seen.insert(s.clone());
- while let Some(redirect) = self.redirects.get(s) {
- if !seen.insert(redirect.clone()) {
- eprintln!("An infinite loop of module redirections detected.\n Original specifier: {}", specifier);
- break;
- }
- s = redirect;
- if seen.len() > 5 {
- eprintln!("An excessive number of module redirections detected.\n Original specifier: {}", specifier);
- break;
- }
- }
- s
- }
-
- /// Transpile (only transform) the graph, updating any emitted modules
- /// with the specifier handler. The result contains any performance stats
- /// from the compiler and optionally any user provided configuration compiler
- /// options that were ignored.
- ///
- /// # Arguments
- ///
- /// * `options` - A structure of options which impact how the code is
- /// transpiled.
- ///
- pub fn transpile(
- &mut self,
- options: TranspileOptions,
- ) -> Result<ResultInfo, AnyError> {
- let start = Instant::now();
-
- let mut ts_config = TsConfig::new(json!({
- "checkJs": false,
- "emitDecoratorMetadata": false,
- "importsNotUsedAsValues": "remove",
- "inlineSourceMap": true,
- // TODO(@kitsonk) make this actually work when https://github.com/swc-project/swc/issues/2218 addressed.
- "inlineSources": true,
- "sourceMap": false,
- "jsx": "react",
- "jsxFactory": "React.createElement",
- "jsxFragmentFactory": "React.Fragment",
- }));
-
- let maybe_ignored_options = ts_config
- .merge_tsconfig_from_config_file(options.maybe_config_file.as_ref())?;
-
- let config = ts_config.as_bytes();
- let check_js = ts_config.get_check_js();
- let emit_options: ast::EmitOptions = ts_config.into();
- let mut emit_count = 0_u32;
- for (specifier, module_slot) in self.modules.iter_mut() {
- if let ModuleSlot::Module(module) = module_slot {
- // TODO(kitsonk) a lot of this logic should be refactored into `Module` as
- // we start to support other methods on the graph. Especially managing
- // the dirty state is something the module itself should "own".
-
- // if the module is a Dts file we should skip it
- if module.media_type == MediaType::Dts {
- continue;
- }
- // if we don't have check_js enabled, we won't touch non TypeScript or JSX
- // modules
- if !(check_js
- || module.media_type == MediaType::Jsx
- || module.media_type == MediaType::Tsx
- || module.media_type == MediaType::TypeScript)
- {
- continue;
- }
-
- let needs_reload =
- options.reload && !options.reload_exclusions.contains(specifier);
- // skip modules that already have a valid emit
- if module.is_emit_valid(&config) && !needs_reload {
- continue;
- }
- let parsed_module = module.parse()?;
- let emit = transpile(&parsed_module, &emit_options)?;
- emit_count += 1;
- module.maybe_emit = Some(Emit::Cli(emit));
- module.set_version(&config);
- module.is_dirty = true;
- }
- }
- self.flush()?;
-
- let stats = Stats(vec![
- ("Files".to_string(), self.modules.len() as u32),
- ("Emitted".to_string(), emit_count),
- ("Total time".to_string(), start.elapsed().as_millis() as u32),
- ]);
-
- Ok(ResultInfo {
- diagnostics: Default::default(),
- loadable_modules: self.get_loadable_modules(),
- maybe_ignored_options,
- stats,
- })
- }
-
- /// Validate that the module graph is "valid" in that there are not module
- /// slots that have errorred that should be available to be able to statically
- /// analyze. In certain situations, we can spin up tsc with an "invalid"
- /// graph.
- fn validate(&self) -> Result<(), AnyError> {
- fn validate_module<F>(
- specifier: &ModuleSpecifier,
- seen: &mut HashSet<ModuleSpecifier>,
- get_module: &F,
- ) -> Result<(), AnyError>
- where
- F: Fn(&ModuleSpecifier) -> ModuleSlot,
- {
- if seen.contains(specifier) {
- return Ok(());
- }
- seen.insert(specifier.clone());
- match get_module(specifier) {
- ModuleSlot::Err(err) => Err(anyhow!(err.to_string())),
- ModuleSlot::Module(module) => {
- for (_, dep) in module.dependencies.iter() {
- // a dynamic import should be skipped, because while it might not
- // be available to statically analyze, it might be available at
- // runtime.
- if !dep.is_dynamic {
- if let Some(code_specifier) = &dep.maybe_code {
- validate_module(code_specifier, seen, get_module)?;
- }
- if let Some(type_specifier) = &dep.maybe_type {
- validate_module(type_specifier, seen, get_module)?;
- }
- }
- }
- Ok(())
- },
- ModuleSlot::None => Err(custom_error("NotFound", format!("The specifier \"{}\" is unexpectedly not in the module graph.", specifier))),
- ModuleSlot::Pending => Err(custom_error("InvalidState", format!("The specifier \"{}\" is in an unexpected state in the module graph.", specifier))),
- }
- }
-
- let mut seen = HashSet::new();
- for specifier in &self.roots {
- validate_module(specifier, &mut seen, &|s| self.get_module(s).clone())?;
- }
- Ok(())
- }
-}
-
-impl deno_ast::swc::bundler::Resolve for Graph {
- fn resolve(
- &self,
- referrer: &deno_ast::swc::common::FileName,
- specifier: &str,
- ) -> Result<deno_ast::swc::common::FileName, AnyError> {
- let referrer =
- if let deno_ast::swc::common::FileName::Url(referrer) = referrer {
- referrer
- } else {
- unreachable!(
- "An unexpected referrer was passed when bundling: {:?}",
- referrer
- )
- };
- let specifier = self.resolve(specifier, referrer, false)?;
-
- Ok(deno_ast::swc::common::FileName::Url(specifier))
- }
-}
-
-/// A structure for building a dependency graph of modules.
-pub struct GraphBuilder {
- graph: Graph,
- maybe_import_map: Option<Arc<Mutex<ImportMap>>>,
- pending: FuturesUnordered<FetchFuture>,
-}
-
-impl GraphBuilder {
- pub fn new(
- handler: Arc<Mutex<dyn SpecifierHandler>>,
- maybe_import_map: Option<ImportMap>,
- maybe_lockfile: Option<Arc<Mutex<Lockfile>>>,
- ) -> Self {
- let internal_import_map =
- maybe_import_map.map(|import_map| Arc::new(Mutex::new(import_map)));
- GraphBuilder {
- graph: Graph::new(handler, maybe_lockfile),
- maybe_import_map: internal_import_map,
- pending: FuturesUnordered::new(),
- }
- }
-
- /// Add a module into the graph based on a module specifier. The module
- /// and any dependencies will be fetched from the handler. The module will
- /// also be treated as a _root_ module in the graph.
- pub async fn add(
- &mut self,
- specifier: &ModuleSpecifier,
- is_dynamic: bool,
- ) -> Result<(), AnyError> {
- self.insert(specifier, is_dynamic).await?;
-
- if !self.graph.roots.contains(specifier) {
- self.graph.roots.push(specifier.clone());
- self.graph.roots_dynamic = self.graph.roots_dynamic && is_dynamic;
- if self.graph.maybe_tsbuildinfo.is_none() {
- let handler = self.graph.handler.lock();
- self.graph.maybe_tsbuildinfo = handler.get_tsbuildinfo(specifier)?;
- }
- }
-
- Ok(())
- }
-
- /// Analyze compiler options, identifying any specifiers that need to be
- /// resolved and added to the graph.
- pub async fn analyze_compiler_options(
- &mut self,
- maybe_compiler_options: &Option<HashMap<String, Value>>,
- ) -> Result<(), AnyError> {
- if let Some(user_config) = maybe_compiler_options {
- if let Some(value) = user_config.get("types") {
- let types: Vec<String> = serde_json::from_value(value.clone())?;
- for specifier in types {
- if let Ok(specifier) = resolve_url_or_path(&specifier) {
- self.insert(&specifier, false).await?;
- }
- }
- }
- }
- Ok(())
- }
-
- /// Analyze a config file, identifying any specifiers that need to be resolved
- /// and added to the graph.
- pub async fn analyze_config_file(
- &mut self,
- maybe_config_file: &Option<ConfigFile>,
- ) -> Result<(), AnyError> {
- if let Some(config_file) = maybe_config_file {
- let referrer = ModuleSpecifier::from_file_path(&config_file.path)
- .map_err(|_| {
- anyhow!("Could not convert file path: \"{:?}\"", config_file.path)
- })?;
- if let Some(compiler_options) = &config_file.json.compiler_options {
- let compiler_options: CompilerOptions =
- serde_json::from_value(compiler_options.clone())?;
- if let Some(types) = compiler_options.types {
- for specifier in types {
- if let Ok(specifier) =
- resolve_import(&specifier, &referrer.to_string())
- {
- self.insert(&specifier, false).await?;
- }
- }
- }
- }
- }
- Ok(())
- }
-
- /// Request a module to be fetched from the handler and queue up its future
- /// to be awaited to be resolved.
- fn fetch(
- &mut self,
- specifier: &ModuleSpecifier,
- maybe_referrer: &Option<Location>,
- is_dynamic: bool,
- ) {
- if !self.graph.modules.contains_key(specifier) {
- self
- .graph
- .modules
- .insert(specifier.clone(), ModuleSlot::Pending);
- let mut handler = self.graph.handler.lock();
- let future =
- handler.fetch(specifier.clone(), maybe_referrer.clone(), is_dynamic);
- self.pending.push(future);
- }
- }
-
- /// An internal method that fetches the specifier and recursively fetches any
- /// of the dependencies, adding them to the graph.
- async fn insert(
- &mut self,
- specifier: &ModuleSpecifier,
- is_dynamic: bool,
- ) -> Result<(), AnyError> {
- self.fetch(specifier, &None, is_dynamic);
-
- loop {
- match self.pending.next().await {
- Some(Err((specifier, err))) => {
- self
- .graph
- .modules
- .insert(specifier, ModuleSlot::Err(Arc::new(err)));
- }
- Some(Ok(cached_module)) => {
- let is_root = &cached_module.specifier == specifier;
- self.visit(cached_module, is_root, is_dynamic)?;
- }
- _ => {}
- }
- if self.pending.is_empty() {
- break;
- }
- }
-
- Ok(())
- }
-
- /// Visit a module that has been fetched, hydrating the module, analyzing its
- /// dependencies if required, fetching those dependencies, and inserting the
- /// module into the graph.
- fn visit(
- &mut self,
- cached_module: CachedModule,
- is_root: bool,
- is_root_dynamic: bool,
- ) -> Result<(), AnyError> {
- let specifier = cached_module.specifier.clone();
- let requested_specifier = cached_module.requested_specifier.clone();
- let mut module =
- Module::new(cached_module, is_root, self.maybe_import_map.clone());
- match module.media_type {
- MediaType::Json
- | MediaType::SourceMap
- | MediaType::TsBuildInfo
- | MediaType::Unknown => {
- return Err(
- GraphError::UnsupportedImportType(
- module.specifier,
- module.media_type,
- )
- .into(),
- );
- }
- _ => (),
- }
- if !module.is_parsed {
- let has_types = module.maybe_types.is_some();
- module.parse()?;
- if self.maybe_import_map.is_none() {
- let mut handler = self.graph.handler.lock();
- handler.set_deps(&specifier, module.dependencies.clone())?;
- if !has_types {
- if let Some((types, _)) = module.maybe_types.clone() {
- handler.set_types(&specifier, types)?;
- }
- }
- }
- }
- for (_, dep) in module.dependencies.iter() {
- let maybe_referrer = Some(dep.location.clone());
- for maybe_specifier in &[dep.maybe_code.as_ref(), dep.maybe_type.as_ref()]
- {
- if let Some(&dep_specifier) = maybe_specifier.as_ref() {
- if dep_specifier.scheme() == "bare" {
- self.graph.modules.insert(
- dep_specifier.clone(),
- ModuleSlot::Err(Arc::new(
- ModuleResolutionError::ImportPrefixMissing(
- dep_specifier.path().to_string(),
- Some(specifier.to_string()),
- )
- .into(),
- )),
- );
- } else {
- self.fetch(
- dep_specifier,
- &maybe_referrer,
- is_root_dynamic || dep.is_dynamic,
- );
- }
- }
- }
- }
- if let Some((_, specifier)) = module.maybe_types.as_ref() {
- self.fetch(specifier, &None, is_root_dynamic);
- }
- if specifier != requested_specifier {
- self
- .graph
- .redirects
- .insert(requested_specifier, specifier.clone());
- }
- self
- .graph
- .modules
- .insert(specifier, ModuleSlot::Module(Box::new(module)));
-
- Ok(())
- }
-
- /// Move out the graph from the builder to be utilized further. An optional
- /// lockfile can be provided, where if the sources in the graph do not match
- /// the expected lockfile, an error will be logged and the process will exit.
- pub fn get_graph(self) -> Graph {
- self.graph.lock();
- self.graph
- }
-}
-
-#[cfg(test)]
-pub mod tests {
- use super::*;
-
- use crate::specifier_handler::MemoryHandler;
- use deno_core::futures::future;
- use deno_core::parking_lot::Mutex;
- use std::fs;
- use std::path::PathBuf;
-
- macro_rules! map (
- { $($key:expr => $value:expr),+ } => {
- {
- let mut m = ::std::collections::HashMap::new();
- $(
- m.insert($key, $value);
- )+
- m
- }
- };
- );
-
- /// This is a testing mock for `SpecifierHandler` that uses a special file
- /// system renaming to mock local and remote modules as well as provides
- /// "spies" for the critical methods for testing purposes.
- #[derive(Debug, Default)]
- pub struct MockSpecifierHandler {
- pub fixtures: PathBuf,
- pub maybe_tsbuildinfo: Option<String>,
- pub tsbuildinfo_calls: Vec<(ModuleSpecifier, String)>,
- pub cache_calls: Vec<(ModuleSpecifier, Emit)>,
- pub deps_calls: Vec<(ModuleSpecifier, DependencyMap)>,
- pub types_calls: Vec<(ModuleSpecifier, String)>,
- pub version_calls: Vec<(ModuleSpecifier, String)>,
- }
-
- impl MockSpecifierHandler {
- fn get_cache(
- &self,
- specifier: ModuleSpecifier,
- ) -> Result<CachedModule, (ModuleSpecifier, AnyError)> {
- let specifier_text = specifier
- .to_string()
- .replace(":///", "_")
- .replace("://", "_")
- .replace("/", "-");
- let source_path = self.fixtures.join(specifier_text);
- let media_type = MediaType::from(&source_path);
- let source = Arc::new(
- fs::read_to_string(&source_path)
- .map_err(|err| (specifier.clone(), err.into()))?,
- );
- let is_remote = specifier.scheme() != "file";
-
- Ok(CachedModule {
- source,
- requested_specifier: specifier.clone(),
- source_path,
- specifier,
- media_type,
- is_remote,
- ..CachedModule::default()
- })
- }
- }
-
- impl SpecifierHandler for MockSpecifierHandler {
- fn fetch(
- &mut self,
- specifier: ModuleSpecifier,
- _maybe_referrer: Option<Location>,
- _is_dynamic: bool,
- ) -> FetchFuture {
- Box::pin(future::ready(self.get_cache(specifier)))
- }
- fn get_tsbuildinfo(
- &self,
- _specifier: &ModuleSpecifier,
- ) -> Result<Option<String>, AnyError> {
- Ok(self.maybe_tsbuildinfo.clone())
- }
- fn set_cache(
- &mut self,
- specifier: &ModuleSpecifier,
- emit: &Emit,
- ) -> Result<(), AnyError> {
- self.cache_calls.push((specifier.clone(), emit.clone()));
- Ok(())
- }
- fn set_types(
- &mut self,
- specifier: &ModuleSpecifier,
- types: String,
- ) -> Result<(), AnyError> {
- self.types_calls.push((specifier.clone(), types));
- Ok(())
- }
- fn set_tsbuildinfo(
- &mut self,
- specifier: &ModuleSpecifier,
- tsbuildinfo: String,
- ) -> Result<(), AnyError> {
- self.maybe_tsbuildinfo = Some(tsbuildinfo.clone());
- self
- .tsbuildinfo_calls
- .push((specifier.clone(), tsbuildinfo));
- Ok(())
- }
- fn set_deps(
- &mut self,
- specifier: &ModuleSpecifier,
- dependencies: DependencyMap,
- ) -> Result<(), AnyError> {
- self.deps_calls.push((specifier.clone(), dependencies));
- Ok(())
- }
- fn set_version(
- &mut self,
- specifier: &ModuleSpecifier,
- version: String,
- ) -> Result<(), AnyError> {
- self.version_calls.push((specifier.clone(), version));
- Ok(())
- }
- }
-
- async fn setup(
- specifier: ModuleSpecifier,
- ) -> (Graph, Arc<Mutex<MockSpecifierHandler>>) {
- let fixtures = test_util::testdata_path().join("module_graph");
- let handler = Arc::new(Mutex::new(MockSpecifierHandler {
- fixtures,
- ..MockSpecifierHandler::default()
- }));
- let mut builder = GraphBuilder::new(handler.clone(), None, None);
- builder
- .add(&specifier, false)
- .await
- .expect("module not inserted");
-
- (builder.get_graph(), handler)
- }
-
- async fn setup_memory(
- specifier: ModuleSpecifier,
- sources: HashMap<&str, &str>,
- ) -> Graph {
- let sources: HashMap<String, Arc<String>> = sources
- .iter()
- .map(|(k, v)| (k.to_string(), Arc::new(v.to_string())))
- .collect();
- let handler = Arc::new(Mutex::new(MemoryHandler::new(sources)));
- let mut builder = GraphBuilder::new(handler.clone(), None, None);
- builder
- .add(&specifier, false)
- .await
- .expect("module not inserted");
-
- builder.get_graph()
- }
-
- #[test]
- fn test_get_version() {
- let doc_a = "console.log(42);";
- let version_a = get_version(doc_a, "1.2.3", b"");
- let doc_b = "console.log(42);";
- let version_b = get_version(doc_b, "1.2.3", b"");
- assert_eq!(version_a, version_b);
-
- let version_c = get_version(doc_a, "1.2.3", b"options");
- assert_ne!(version_a, version_c);
-
- let version_d = get_version(doc_b, "1.2.3", b"options");
- assert_eq!(version_c, version_d);
-
- let version_e = get_version(doc_a, "1.2.4", b"");
- assert_ne!(version_a, version_e);
-
- let version_f = get_version(doc_b, "1.2.4", b"");
- assert_eq!(version_e, version_f);
- }
-
- #[test]
- fn test_module_emit_valid() {
- let source = "console.log(42);";
- let maybe_version = Some(get_version(source, &version::deno(), b""));
- let module = Module {
- maybe_version,
- text_info: SourceTextInfo::from_string(source.to_string()),
- ..Module::default()
- };
- assert!(module.is_emit_valid(b""));
-
- let source = "console.log(42);";
- let old_source = "console.log(43);";
- let maybe_version = Some(get_version(old_source, &version::deno(), b""));
- let module = Module {
- maybe_version,
- text_info: SourceTextInfo::from_string(source.to_string()),
- ..Module::default()
- };
- assert!(!module.is_emit_valid(b""));
-
- let source = "console.log(42);";
- let maybe_version = Some(get_version(source, "0.0.0", b""));
- let module = Module {
- maybe_version,
- text_info: SourceTextInfo::from_string(source.to_string()),
- ..Module::default()
- };
- assert!(!module.is_emit_valid(b""));
-
- let source = "console.log(42);";
- let module = Module {
- text_info: SourceTextInfo::from_string(source.to_string()),
- ..Module::default()
- };
- assert!(!module.is_emit_valid(b""));
- }
-
- #[test]
- fn test_module_set_version() {
- let source = "console.log(42);";
- let expected = Some(get_version(source, &version::deno(), b""));
- let mut module = Module {
- text_info: SourceTextInfo::from_string(source.to_string()),
- ..Module::default()
- };
- assert!(module.maybe_version.is_none());
- module.set_version(b"");
- assert_eq!(module.maybe_version, expected);
- }
-
- #[tokio::test]
- async fn test_graph_bundle() {
- let tests = vec![
- ("file:///tests/fixture01.ts", "fixture01.out"),
- ("file:///tests/fixture02.ts", "fixture02.out"),
- ("file:///tests/fixture03.ts", "fixture03.out"),
- ("file:///tests/fixture04.ts", "fixture04.out"),
- ("file:///tests/fixture05.ts", "fixture05.out"),
- ("file:///tests/fixture06.ts", "fixture06.out"),
- ("file:///tests/fixture07.ts", "fixture07.out"),
- ("file:///tests/fixture08.ts", "fixture08.out"),
- ("file:///tests/fixture09.ts", "fixture09.out"),
- ("file:///tests/fixture10.ts", "fixture10.out"),
- ("file:///tests/fixture11.ts", "fixture11.out"),
- ("file:///tests/fixture12.ts", "fixture12.out"),
- ("file:///tests/fixture13.ts", "fixture13.out"),
- ("file:///tests/fixture14.ts", "fixture14.out"),
- ("file:///tests/fixture15.ts", "fixture15.out"),
- ];
- let fixtures = test_util::testdata_path().join("bundle");
-
- for (specifier, expected_str) in tests {
- let specifier = resolve_url_or_path(specifier).unwrap();
- let handler = Arc::new(Mutex::new(MockSpecifierHandler {
- fixtures: fixtures.clone(),
- ..MockSpecifierHandler::default()
- }));
- let mut builder = GraphBuilder::new(handler.clone(), None, None);
- builder
- .add(&specifier, false)
- .await
- .expect("module not inserted");
- let graph = builder.get_graph();
- let (actual, stats, maybe_ignored_options) = graph
- .bundle(BundleOptions::default())
- .expect("could not bundle");
- assert_eq!(stats.0.len(), 2);
- assert_eq!(maybe_ignored_options, None);
- let expected_path = fixtures.join(expected_str);
- let expected = fs::read_to_string(expected_path).unwrap();
- assert_eq!(actual, expected, "fixture: {}", specifier);
- }
- }
-
- #[tokio::test]
- async fn test_graph_check_emit() {
- let specifier = resolve_url_or_path("file:///tests/main.ts")
- .expect("could not resolve module");
- let (graph, handler) = setup(specifier).await;
- let result_info = graph
- .check(CheckOptions {
- debug: false,
- emit: true,
- lib: TypeLib::DenoWindow,
- maybe_config_file: None,
- reload: false,
- ..Default::default()
- })
- .expect("should have checked");
- assert!(result_info.maybe_ignored_options.is_none());
- assert_eq!(result_info.stats.0.len(), 12);
- assert!(result_info.diagnostics.is_empty());
- let h = handler.lock();
- assert_eq!(h.cache_calls.len(), 2);
- assert_eq!(h.tsbuildinfo_calls.len(), 1);
- }
-
- #[tokio::test]
- async fn test_graph_check_ignores_dynamic_import_errors() {
- let specifier = resolve_url_or_path("file:///tests/dynamicimport.ts")
- .expect("could not resolve module");
- let (graph, _) = setup(specifier).await;
- let result_info = graph
- .check(CheckOptions {
- debug: false,
- emit: false,
- lib: TypeLib::DenoWindow,
- maybe_config_file: None,
- reload: false,
- ..Default::default()
- })
- .expect("should have checked");
- assert!(result_info.diagnostics.is_empty());
- }
-
- #[tokio::test]
- async fn fix_graph_check_emit_diagnostics() {
- let specifier = resolve_url_or_path("file:///tests/diag.ts")
- .expect("could not resolve module");
- let (graph, handler) = setup(specifier).await;
- let result_info = graph
- .check(CheckOptions {
- debug: false,
- emit: true,
- lib: TypeLib::DenoWindow,
- maybe_config_file: None,
- reload: false,
- ..Default::default()
- })
- .expect("should have checked");
- assert!(result_info.maybe_ignored_options.is_none());
- assert_eq!(result_info.stats.0.len(), 12);
- assert!(!result_info.diagnostics.is_empty());
- let h = handler.lock();
- // we shouldn't cache any files or write out tsbuildinfo if there are
- // diagnostic errors
- assert_eq!(h.cache_calls.len(), 0);
- assert_eq!(h.tsbuildinfo_calls.len(), 0);
- }
-
- #[tokio::test]
- async fn test_graph_check_no_emit() {
- let specifier = resolve_url_or_path("file:///tests/main.ts")
- .expect("could not resolve module");
- let (graph, handler) = setup(specifier).await;
- let result_info = graph
- .check(CheckOptions {
- debug: false,
- emit: false,
- lib: TypeLib::DenoWindow,
- maybe_config_file: None,
- reload: false,
- ..Default::default()
- })
- .expect("should have checked");
- assert!(result_info.maybe_ignored_options.is_none());
- assert_eq!(result_info.stats.0.len(), 12);
- assert!(result_info.diagnostics.is_empty());
- let h = handler.lock();
- assert_eq!(h.cache_calls.len(), 0);
- assert_eq!(h.tsbuildinfo_calls.len(), 1);
- }
-
- #[tokio::test]
- async fn fix_graph_check_mjs_root() {
- let specifier = resolve_url_or_path("file:///tests/a.mjs")
- .expect("could not resolve module");
- let (graph, handler) = setup(specifier).await;
- let result_info = graph
- .check(CheckOptions {
- debug: false,
- emit: true,
- lib: TypeLib::DenoWindow,
- maybe_config_file: None,
- reload: false,
- ..Default::default()
- })
- .expect("should have checked");
- assert!(result_info.maybe_ignored_options.is_none());
- assert!(result_info.diagnostics.is_empty());
- let h = handler.lock();
- assert_eq!(h.cache_calls.len(), 1);
- assert_eq!(h.tsbuildinfo_calls.len(), 1);
- }
-
- #[tokio::test]
- async fn fix_graph_check_types_root() {
- let specifier = resolve_url_or_path("file:///typesref.js")
- .expect("could not resolve module");
- let (graph, _) = setup(specifier).await;
- let result_info = graph
- .check(CheckOptions {
- debug: false,
- emit: false,
- lib: TypeLib::DenoWindow,
- maybe_config_file: None,
- reload: false,
- ..Default::default()
- })
- .expect("should have checked");
- assert!(result_info.diagnostics.is_empty());
- }
-
- #[tokio::test]
- async fn test_graph_check_user_config() {
- let specifier = resolve_url_or_path("file:///tests/checkwithconfig.ts")
- .expect("could not resolve module");
- let (graph, handler) = setup(specifier.clone()).await;
- let config_file = ConfigFile::read(
- test_util::testdata_path().join("module_graph/tsconfig_01.json"),
- )
- .unwrap();
- let result_info = graph
- .check(CheckOptions {
- debug: false,
- emit: true,
- lib: TypeLib::DenoWindow,
- maybe_config_file: Some(config_file),
- reload: true,
- ..Default::default()
- })
- .expect("should have checked");
- assert!(result_info.maybe_ignored_options.is_none());
- assert!(result_info.diagnostics.is_empty());
- let (ver0, ver1) = {
- let h = handler.lock();
- assert_eq!(h.version_calls.len(), 2);
- (h.version_calls[0].1.clone(), h.version_calls[1].1.clone())
- };
-
- // let's do it all over again to ensure that the versions are determinstic
- let (graph, handler) = setup(specifier).await;
- let config_file = ConfigFile::read(
- test_util::testdata_path().join("module_graph/tsconfig_01.json"),
- )
- .unwrap();
- let result_info = graph
- .check(CheckOptions {
- debug: false,
- emit: true,
- lib: TypeLib::DenoWindow,
- maybe_config_file: Some(config_file),
- reload: true,
- ..Default::default()
- })
- .expect("should have checked");
- assert!(result_info.maybe_ignored_options.is_none());
- assert!(result_info.diagnostics.is_empty());
- let h = handler.lock();
- assert_eq!(h.version_calls.len(), 2);
- assert!(h.version_calls[0].1 == ver0 || h.version_calls[0].1 == ver1);
- assert!(h.version_calls[1].1 == ver0 || h.version_calls[1].1 == ver1);
- }
-
- #[tokio::test]
- async fn test_graph_emit() {
- let specifier = resolve_url_or_path("file:///a.ts").unwrap();
- let graph = setup_memory(
- specifier,
- map!(
- "/a.ts" => r#"
- import * as b from "./b.ts";
-
- console.log(b);
- "#,
- "/b.ts" => r#"
- export const b = "b";
- "#
- ),
- )
- .await;
- let (emitted_files, result_info) = graph
- .emit(EmitOptions {
- check: true,
- bundle_type: BundleType::None,
- debug: false,
- maybe_user_config: None,
- })
- .expect("should have emitted");
- assert!(result_info.diagnostics.is_empty());
- assert!(result_info.maybe_ignored_options.is_none());
- assert_eq!(emitted_files.len(), 4);
- let out_a = emitted_files.get("file:///a.ts.js");
- assert!(out_a.is_some());
- let out_a = out_a.unwrap();
- assert!(out_a.starts_with("import * as b from"));
- assert!(emitted_files.contains_key("file:///a.ts.js.map"));
- let out_b = emitted_files.get("file:///b.ts.js");
- assert!(out_b.is_some());
- let out_b = out_b.unwrap();
- assert!(out_b.starts_with("export const b = \"b\";"));
- assert!(emitted_files.contains_key("file:///b.ts.js.map"));
- }
-
- #[tokio::test]
- async fn test_graph_emit_bundle() {
- let specifier = resolve_url_or_path("file:///a.ts").unwrap();
- let graph = setup_memory(
- specifier,
- map!(
- "/a.ts" => r#"
- import * as b from "./b.ts";
-
- console.log(b);
- "#,
- "/b.ts" => r#"
- export const b = "b";
- "#
- ),
- )
- .await;
- let (emitted_files, result_info) = graph
- .emit(EmitOptions {
- check: true,
- bundle_type: BundleType::Module,
- debug: false,
- maybe_user_config: None,
- })
- .expect("should have emitted");
- assert!(result_info.diagnostics.is_empty());
- assert!(result_info.maybe_ignored_options.is_none());
- assert_eq!(emitted_files.len(), 2);
- let actual = emitted_files.get("deno:///bundle.js");
- assert!(actual.is_some());
- assert!(emitted_files.contains_key("deno:///bundle.js.map"));
- let actual = actual.unwrap();
- assert!(actual.contains("const b = \"b\";"));
- assert!(actual.contains("console.log(mod);"));
- }
-
- #[tokio::test]
- async fn fix_graph_emit_declaration() {
- let specifier = resolve_url_or_path("file:///a.ts").unwrap();
- let graph = setup_memory(
- specifier,
- map!(
- "/a.ts" => r#"
- import * as b from "./b.ts";
-
- console.log(b);
- "#,
- "/b.ts" => r#"
- export const b = "b";
- "#
- ),
- )
- .await;
- let mut user_config = HashMap::<String, Value>::new();
- user_config.insert("declaration".to_string(), json!(true));
- let (emitted_files, result_info) = graph
- .emit(EmitOptions {
- check: true,
- bundle_type: BundleType::None,
- debug: false,
- maybe_user_config: Some(user_config),
- })
- .expect("should have emitted");
- assert!(result_info.diagnostics.is_empty());
- assert!(result_info.maybe_ignored_options.is_none());
- assert_eq!(emitted_files.len(), 6);
- let out_a = emitted_files.get("file:///a.ts.js");
- assert!(out_a.is_some());
- let out_a = out_a.unwrap();
- assert!(out_a.starts_with("import * as b from"));
- assert!(emitted_files.contains_key("file:///a.ts.js.map"));
- assert!(emitted_files.contains_key("file:///a.ts.d.ts"));
- let out_b = emitted_files.get("file:///b.ts.js");
- assert!(out_b.is_some());
- let out_b = out_b.unwrap();
- assert!(out_b.starts_with("export const b = \"b\";"));
- assert!(emitted_files.contains_key("file:///b.ts.js.map"));
- assert!(emitted_files.contains_key("file:///b.ts.d.ts"));
- }
-
- #[tokio::test]
- async fn test_graph_info() {
- let specifier = resolve_url_or_path("file:///tests/main.ts")
- .expect("could not resolve module");
- let (graph, _) = setup(specifier.clone()).await;
- let info = graph.info().expect("could not get info");
- assert_eq!(info.root, specifier);
- assert_eq!(info.modules.len(), 7);
- assert_eq!(info.size, 518);
- }
-
- #[tokio::test]
- async fn test_graph_import_json() {
- let specifier = resolve_url_or_path("file:///tests/importjson.ts")
- .expect("could not resolve module");
- let fixtures = test_util::testdata_path().join("module_graph");
- let handler = Arc::new(Mutex::new(MockSpecifierHandler {
- fixtures,
- ..MockSpecifierHandler::default()
- }));
- let mut builder = GraphBuilder::new(handler.clone(), None, None);
- builder
- .add(&specifier, false)
- .await
- .expect_err("should have errored");
- }
-
- #[tokio::test]
- async fn test_graph_transpile() {
- // This is a complex scenario of transpiling, where we have TypeScript
- // importing a JavaScript file (with type definitions) which imports
- // TypeScript, JavaScript, and JavaScript with type definitions.
- // For scenarios where we transpile, we only want the TypeScript files
- // to be actually emitted.
- //
- // This also exercises "@deno-types" and type references.
- let specifier = resolve_url_or_path("file:///tests/main.ts")
- .expect("could not resolve module");
- let (mut graph, handler) = setup(specifier).await;
- let result_info = graph.transpile(TranspileOptions::default()).unwrap();
- assert_eq!(result_info.stats.0.len(), 3);
- assert_eq!(result_info.maybe_ignored_options, None);
- let h = handler.lock();
- assert_eq!(h.cache_calls.len(), 2);
- match &h.cache_calls[0].1 {
- Emit::Cli((code, maybe_map)) => {
- assert!(
- code.contains("# sourceMappingURL=data:application/json;base64,")
- );
- assert!(maybe_map.is_none());
- }
- };
- match &h.cache_calls[1].1 {
- Emit::Cli((code, maybe_map)) => {
- assert!(
- code.contains("# sourceMappingURL=data:application/json;base64,")
- );
- assert!(maybe_map.is_none());
- }
- };
- assert_eq!(h.deps_calls.len(), 7);
- assert_eq!(
- h.deps_calls[0].0,
- resolve_url_or_path("file:///tests/main.ts").unwrap()
- );
- assert_eq!(h.deps_calls[0].1.len(), 1);
- assert_eq!(
- h.deps_calls[1].0,
- resolve_url_or_path("https://deno.land/x/lib/mod.js").unwrap()
- );
- assert_eq!(h.deps_calls[1].1.len(), 3);
- assert_eq!(
- h.deps_calls[2].0,
- resolve_url_or_path("https://deno.land/x/lib/mod.d.ts").unwrap()
- );
- assert_eq!(h.deps_calls[2].1.len(), 3, "should have 3 dependencies");
- // sometimes the calls are not deterministic, and so checking the contents
- // can cause some failures
- assert_eq!(h.deps_calls[3].1.len(), 0, "should have no dependencies");
- assert_eq!(h.deps_calls[4].1.len(), 0, "should have no dependencies");
- assert_eq!(h.deps_calls[5].1.len(), 0, "should have no dependencies");
- assert_eq!(h.deps_calls[6].1.len(), 0, "should have no dependencies");
- }
-
- #[tokio::test]
- async fn test_graph_transpile_user_config() {
- let specifier = resolve_url_or_path("https://deno.land/x/transpile.tsx")
- .expect("could not resolve module");
- let (mut graph, handler) = setup(specifier).await;
- let config_file = ConfigFile::read(
- test_util::testdata_path().join("module_graph/tsconfig.json"),
- )
- .unwrap();
- let result_info = graph
- .transpile(TranspileOptions {
- debug: false,
- maybe_config_file: Some(config_file),
- reload: false,
- ..Default::default()
- })
- .unwrap();
- assert_eq!(
- result_info.maybe_ignored_options.unwrap().items,
- vec!["target".to_string()],
- "the 'target' options should have been ignored"
- );
- let h = handler.lock();
- assert_eq!(h.cache_calls.len(), 1, "only one file should be emitted");
- // FIXME(bartlomieju): had to add space in `<div>`, probably a quirk in swc_ecma_codegen
- match &h.cache_calls[0].1 {
- Emit::Cli((code, _)) => {
- assert!(
- code.contains("<div >Hello world!</div>"),
- "jsx should have been preserved"
- );
- }
- }
- }
-
- #[tokio::test]
- async fn test_graph_import_map_remote_to_local() {
- let fixtures = test_util::testdata_path().join("module_graph");
- let maybe_import_map = Some(
- ImportMap::from_json(
- "file:///tests/importmap.json",
- r#"{
- "imports": {
- "https://deno.land/x/b/mod.js": "./b/mod.js"
- }
- }
- "#,
- )
- .expect("could not parse import map"),
- );
- let handler = Arc::new(Mutex::new(MockSpecifierHandler {
- fixtures,
- ..Default::default()
- }));
- let mut builder = GraphBuilder::new(handler, maybe_import_map, None);
- let specifier = resolve_url_or_path("file:///tests/importremap.ts")
- .expect("could not resolve module");
- builder.add(&specifier, false).await.expect("could not add");
- builder.get_graph();
- }
-
- #[tokio::test]
- async fn test_graph_with_lockfile() {
- let fixtures = test_util::testdata_path().join("module_graph");
- let lockfile_path = fixtures.join("lockfile.json");
- let lockfile =
- Lockfile::new(lockfile_path, false).expect("could not load lockfile");
- let maybe_lockfile = Some(Arc::new(Mutex::new(lockfile)));
- let handler = Arc::new(Mutex::new(MockSpecifierHandler {
- fixtures,
- ..MockSpecifierHandler::default()
- }));
- let mut builder = GraphBuilder::new(handler.clone(), None, maybe_lockfile);
- let specifier = resolve_url_or_path("file:///tests/main.ts")
- .expect("could not resolve module");
- builder
- .add(&specifier, false)
- .await
- .expect("module not inserted");
- builder.get_graph();
- }
-}
diff --git a/cli/module_loader.rs b/cli/module_loader.rs
index c14c953b2..e66e4e4fc 100644
--- a/cli/module_loader.rs
+++ b/cli/module_loader.rs
@@ -1,7 +1,8 @@
// Copyright 2018-2021 the Deno authors. All rights reserved. MIT license.
-use crate::module_graph::TypeLib;
+use crate::emit::TypeLib;
use crate::proc_state::ProcState;
+
use deno_core::error::AnyError;
use deno_core::futures::future::FutureExt;
use deno_core::futures::Future;
@@ -10,16 +11,12 @@ use deno_core::ModuleLoader;
use deno_core::ModuleSpecifier;
use deno_core::OpState;
use deno_runtime::permissions::Permissions;
-use import_map::ImportMap;
use std::cell::RefCell;
use std::pin::Pin;
use std::rc::Rc;
use std::str;
-pub struct CliModuleLoader {
- /// When flags contains a `.import_map_path` option, the content of the
- /// import map file will be resolved and set.
- pub import_map: Option<ImportMap>,
+pub(crate) struct CliModuleLoader {
pub lib: TypeLib,
/// The initial set of permissions used to resolve the static imports in the
/// worker. They are decoupled from the worker (dynamic) permissions since
@@ -36,10 +33,7 @@ impl CliModuleLoader {
TypeLib::DenoWindow
};
- let import_map = ps.maybe_import_map.clone();
-
Rc::new(CliModuleLoader {
- import_map,
lib,
root_permissions: Permissions::allow_all(),
ps,
@@ -54,7 +48,6 @@ impl CliModuleLoader {
};
Rc::new(CliModuleLoader {
- import_map: None,
lib,
root_permissions: permissions,
ps,
@@ -67,44 +60,25 @@ impl ModuleLoader for CliModuleLoader {
&self,
specifier: &str,
referrer: &str,
- is_main: bool,
+ _is_main: bool,
) -> Result<ModuleSpecifier, AnyError> {
- // FIXME(bartlomieju): hacky way to provide compatibility with repl
- let referrer = if referrer.is_empty() && self.ps.flags.repl {
- deno_core::DUMMY_SPECIFIER
- } else {
- referrer
- };
-
- // TODO(ry) I think we can remove this conditional. At the time of writing
- // we don't have any tests that fail if it was removed.
- // https://github.com/WICG/import-maps/issues/157
- if !is_main {
- if let Some(import_map) = &self.import_map {
- return import_map
- .resolve(specifier, referrer)
- .map_err(AnyError::from);
- }
- }
-
- let module_specifier = deno_core::resolve_import(specifier, referrer)?;
-
- Ok(module_specifier)
+ self.ps.resolve(specifier, referrer)
}
fn load(
&self,
module_specifier: &ModuleSpecifier,
maybe_referrer: Option<ModuleSpecifier>,
- _is_dynamic: bool,
+ is_dynamic: bool,
) -> Pin<Box<deno_core::ModuleSourceFuture>> {
let module_specifier = module_specifier.clone();
let ps = self.ps.clone();
- // NOTE: this block is async only because of `deno_core`
- // interface requirements; module was already loaded
- // when constructing module graph during call to `prepare_load`.
- async move { ps.load(module_specifier, maybe_referrer) }.boxed_local()
+ // NOTE: this block is async only because of `deno_core` interface
+ // requirements; module was already loaded when constructing module graph
+ // during call to `prepare_load`.
+ async move { ps.load(module_specifier, maybe_referrer, is_dynamic) }
+ .boxed_local()
}
fn prepare_load(
@@ -117,24 +91,31 @@ impl ModuleLoader for CliModuleLoader {
) -> Pin<Box<dyn Future<Output = Result<(), AnyError>>>> {
let specifier = specifier.clone();
let ps = self.ps.clone();
- let maybe_import_map = self.import_map.clone();
let state = op_state.borrow();
- let root_permissions = self.root_permissions.clone();
let dynamic_permissions = state.borrow::<Permissions>().clone();
+ let root_permissions = if is_dynamic {
+ dynamic_permissions.clone()
+ } else {
+ self.root_permissions.clone()
+ };
- let lib = self.lib.clone();
+ let lib = match self.lib {
+ TypeLib::DenoWindow => crate::emit::TypeLib::DenoWindow,
+ TypeLib::DenoWorker => crate::emit::TypeLib::DenoWorker,
+ TypeLib::UnstableDenoWindow => crate::emit::TypeLib::UnstableDenoWindow,
+ TypeLib::UnstableDenoWorker => crate::emit::TypeLib::UnstableDenoWorker,
+ };
drop(state);
// TODO(bartlomieju): `prepare_module_load` should take `load_id` param
async move {
ps.prepare_module_load(
- specifier,
+ vec![specifier],
+ is_dynamic,
lib,
root_permissions,
dynamic_permissions,
- is_dynamic,
- maybe_import_map,
)
.await
}
diff --git a/cli/ops/runtime_compiler.rs b/cli/ops/runtime_compiler.rs
index d63d97f58..ab6992d19 100644
--- a/cli/ops/runtime_compiler.rs
+++ b/cli/ops/runtime_compiler.rs
@@ -1,27 +1,30 @@
// Copyright 2018-2021 the Deno authors. All rights reserved. MIT license.
-use crate::module_graph::BundleType;
-use crate::module_graph::EmitOptions;
-use crate::module_graph::GraphBuilder;
+use crate::cache;
+use crate::config_file::IgnoredCompilerOptions;
+use crate::diagnostics::Diagnostics;
+use crate::emit;
+use crate::errors::get_error_class_name;
use crate::proc_state::ProcState;
-use crate::specifier_handler::FetchHandler;
-use crate::specifier_handler::MemoryHandler;
-use crate::specifier_handler::SpecifierHandler;
+use crate::resolver::ImportMapResolver;
+use deno_core::error::custom_error;
use deno_core::error::generic_error;
-use deno_core::error::type_error;
use deno_core::error::AnyError;
use deno_core::error::Context;
-use deno_core::parking_lot::Mutex;
use deno_core::resolve_url_or_path;
+use deno_core::serde_json;
use deno_core::serde_json::Value;
+use deno_core::ModuleSpecifier;
use deno_core::OpState;
+use deno_graph;
use deno_runtime::permissions::Permissions;
use import_map::ImportMap;
use serde::Deserialize;
use serde::Serialize;
use std::cell::RefCell;
use std::collections::HashMap;
+use std::collections::HashSet;
use std::rc::Rc;
use std::sync::Arc;
@@ -37,6 +40,15 @@ enum RuntimeBundleType {
Classic,
}
+impl<'a> From<&'a RuntimeBundleType> for emit::BundleType {
+ fn from(bundle_type: &'a RuntimeBundleType) -> Self {
+ match bundle_type {
+ RuntimeBundleType::Classic => Self::Classic,
+ RuntimeBundleType::Module => Self::Module,
+ }
+ }
+}
+
#[derive(Debug, Deserialize)]
#[serde(rename_all = "camelCase")]
struct EmitArgs {
@@ -52,10 +64,21 @@ struct EmitArgs {
#[derive(Serialize)]
#[serde(rename_all = "camelCase")]
struct EmitResult {
- diagnostics: crate::diagnostics::Diagnostics,
+ diagnostics: Diagnostics,
files: HashMap<String, String>,
- ignored_options: Option<crate::config_file::IgnoredCompilerOptions>,
- stats: crate::module_graph::Stats,
+ #[serde(rename = "ignoredOptions")]
+ maybe_ignored_options: Option<IgnoredCompilerOptions>,
+ stats: emit::Stats,
+}
+
+fn to_maybe_imports(
+ referrer: &ModuleSpecifier,
+ maybe_options: Option<&HashMap<String, Value>>,
+) -> Option<Vec<(ModuleSpecifier, Vec<String>)>> {
+ let options = maybe_options.as_ref()?;
+ let types_value = options.get("types")?;
+ let types: Vec<String> = serde_json::from_value(types_value.clone()).ok()?;
+ Some(vec![(referrer.clone(), types)])
}
async fn op_emit(
@@ -65,23 +88,19 @@ async fn op_emit(
) -> Result<EmitResult, AnyError> {
deno_runtime::ops::check_unstable2(&state, "Deno.emit");
let root_specifier = args.root_specifier;
- let ps = state.borrow().borrow::<ProcState>().clone();
- let mut runtime_permissions = {
- let state = state.borrow();
- state.borrow::<Permissions>().clone()
- };
- // when we are actually resolving modules without provided sources, we should
- // treat the root module as a dynamic import so that runtime permissions are
- // applied.
- let handler: Arc<Mutex<dyn SpecifierHandler>> =
- if let Some(sources) = args.sources {
- Arc::new(Mutex::new(MemoryHandler::new(sources)))
+ let state = state.borrow();
+ let ps = state.borrow::<ProcState>();
+ let mut runtime_permissions = { state.borrow::<Permissions>().clone() };
+ let mut cache: Box<dyn cache::CacherLoader> =
+ if let Some(sources) = &args.sources {
+ Box::new(cache::MemoryCacher::new(sources.clone()))
} else {
- Arc::new(Mutex::new(FetchHandler::new(
- &ps,
+ Box::new(cache::FetchCacher::new(
+ ps.dir.gen_cache.clone(),
+ ps.file_fetcher.clone(),
runtime_permissions.clone(),
runtime_permissions.clone(),
- )?))
+ ))
};
let maybe_import_map = if let Some(import_map_str) = args.import_map_path {
let import_map_specifier = resolve_url_or_path(&import_map_str)
@@ -107,37 +126,125 @@ async fn op_emit(
} else {
None
};
- let mut builder = GraphBuilder::new(handler, maybe_import_map, None);
- let root_specifier = resolve_url_or_path(&root_specifier)?;
- builder.add(&root_specifier, false).await.map_err(|_| {
- type_error(format!(
- "Unable to handle the given specifier: {}",
- &root_specifier
- ))
- })?;
- builder
- .analyze_compiler_options(&args.compiler_options)
- .await?;
- let bundle_type = match args.bundle {
- Some(RuntimeBundleType::Module) => BundleType::Module,
- Some(RuntimeBundleType::Classic) => BundleType::Classic,
- None => BundleType::None,
- };
- let graph = builder.get_graph();
+ let roots = vec![resolve_url_or_path(&root_specifier)?];
+ let maybe_imports =
+ to_maybe_imports(&roots[0], args.compiler_options.as_ref());
+ let maybe_resolver = maybe_import_map.as_ref().map(ImportMapResolver::new);
+ let graph = Arc::new(
+ deno_graph::create_graph(
+ roots,
+ true,
+ maybe_imports,
+ cache.as_mut_loader(),
+ maybe_resolver.as_ref().map(|r| r.as_resolver()),
+ None,
+ None,
+ )
+ .await,
+ );
+ // There are certain graph errors that we want to return as an error of an op,
+ // versus something that gets returned as a diagnostic of the op, this is
+ // handled here.
+ if let Err(err) = graph.valid() {
+ let err: AnyError = err.into();
+ if get_error_class_name(&err) == "PermissionDenied" {
+ return Err(err);
+ }
+ }
+ let check = args.check.unwrap_or(true);
let debug = ps.flags.log_level == Some(log::Level::Debug);
- let graph_errors = graph.get_errors();
- let (files, mut result_info) = graph.emit(EmitOptions {
- bundle_type,
- check: args.check.unwrap_or(true),
- debug,
- maybe_user_config: args.compiler_options,
- })?;
- result_info.diagnostics.extend_graph_errors(graph_errors);
+ let tsc_emit = check && args.bundle.is_none();
+ let (ts_config, maybe_ignored_options) = emit::get_ts_config(
+ emit::ConfigType::RuntimeEmit { tsc_emit },
+ None,
+ args.compiler_options.as_ref(),
+ )?;
+ let (files, mut diagnostics, stats) = if check && args.bundle.is_none() {
+ let (diagnostics, stats) = if args.sources.is_none()
+ && emit::valid_emit(
+ graph.as_ref(),
+ cache.as_cacher(),
+ &ts_config,
+ ps.flags.reload,
+ &HashSet::default(),
+ ) {
+ log::debug!(
+ "cache is valid for \"{}\", skipping check/emit",
+ root_specifier
+ );
+ (Diagnostics::default(), emit::Stats::default())
+ } else {
+ let emit_result = emit::check_and_maybe_emit(
+ graph.clone(),
+ cache.as_mut_cacher(),
+ emit::CheckOptions {
+ debug,
+ emit_with_diagnostics: true,
+ maybe_config_specifier: None,
+ ts_config,
+ },
+ )?;
+ (emit_result.diagnostics, emit_result.stats)
+ };
+ let files = emit::to_file_map(graph.as_ref(), cache.as_mut_cacher());
+ (files, diagnostics, stats)
+ } else if let Some(bundle) = &args.bundle {
+ let (diagnostics, stats) = if check {
+ if ts_config.get_declaration() {
+ return Err(custom_error("TypeError", "The bundle option is set, but the compiler option of `declaration` is true which is not currently supported."));
+ }
+ let emit_result = emit::check_and_maybe_emit(
+ graph.clone(),
+ cache.as_mut_cacher(),
+ emit::CheckOptions {
+ debug,
+ emit_with_diagnostics: true,
+ maybe_config_specifier: None,
+ ts_config: ts_config.clone(),
+ },
+ )?;
+ (emit_result.diagnostics, emit_result.stats)
+ } else {
+ (Diagnostics::default(), Default::default())
+ };
+ let (emit, maybe_map) = emit::bundle(
+ graph.as_ref(),
+ emit::BundleOptions {
+ bundle_type: bundle.into(),
+ ts_config,
+ },
+ )?;
+ let mut files = HashMap::new();
+ files.insert("deno:///bundle.js".to_string(), emit);
+ if let Some(map) = maybe_map {
+ files.insert("deno:///bundle.js.map".to_string(), map);
+ }
+ (files, diagnostics, stats)
+ } else {
+ if ts_config.get_declaration() {
+ return Err(custom_error("TypeError", "The option of `check` is false, but the compiler option of `declaration` is true which is not currently supported."));
+ }
+ let emit_result = emit::emit(
+ graph.as_ref(),
+ cache.as_mut_cacher(),
+ emit::EmitOptions {
+ reload: ps.flags.reload,
+ ts_config,
+ reload_exclusions: HashSet::default(),
+ },
+ )?;
+ let files = emit::to_file_map(graph.as_ref(), cache.as_mut_cacher());
+ (files, emit_result.diagnostics, emit_result.stats)
+ };
+
+ // we want to add any errors that were returned as an `Err` earlier by adding
+ // them to the diagnostics.
+ diagnostics.extend_graph_errors(graph.errors());
Ok(EmitResult {
- diagnostics: result_info.diagnostics,
+ diagnostics,
files,
- ignored_options: result_info.maybe_ignored_options,
- stats: result_info.stats,
+ maybe_ignored_options,
+ stats,
})
}
diff --git a/cli/proc_state.rs b/cli/proc_state.rs
index 2e1fb0e31..fe707754f 100644
--- a/cli/proc_state.rs
+++ b/cli/proc_state.rs
@@ -1,23 +1,24 @@
// Copyright 2018-2021 the Deno authors. All rights reserved. MIT license.
+use crate::cache;
use crate::colors;
use crate::compat;
use crate::config_file::ConfigFile;
use crate::deno_dir;
+use crate::emit;
+use crate::errors::get_module_graph_error_class;
use crate::file_fetcher::CacheSetting;
use crate::file_fetcher::FileFetcher;
use crate::flags;
use crate::http_cache;
+use crate::lockfile::as_maybe_locker;
use crate::lockfile::Lockfile;
-use crate::module_graph::CheckOptions;
-use crate::module_graph::GraphBuilder;
-use crate::module_graph::TranspileOptions;
-use crate::module_graph::TypeLib;
+use crate::resolver::ImportMapResolver;
use crate::source_maps::SourceMapGetter;
-use crate::specifier_handler::FetchHandler;
use crate::version;
use deno_core::error::anyhow;
+use deno_core::error::custom_error;
use deno_core::error::get_custom_error_class;
use deno_core::error::AnyError;
use deno_core::error::Context;
@@ -36,9 +37,6 @@ use deno_tls::rustls::RootCertStore;
use deno_tls::rustls_native_certs::load_native_certs;
use deno_tls::webpki_roots::TLS_SERVER_ROOTS;
use import_map::ImportMap;
-use log::debug;
-use log::info;
-use log::warn;
use std::collections::HashMap;
use std::collections::HashSet;
use std::env;
@@ -59,12 +57,25 @@ pub struct Inner {
pub dir: deno_dir::DenoDir,
pub coverage_dir: Option<String>,
pub file_fetcher: FileFetcher,
- pub modules:
- Arc<Mutex<HashMap<ModuleSpecifier, Result<ModuleSource, AnyError>>>>,
+ modules: Arc<Mutex<HashMap<ModuleSpecifier, Result<ModuleSource, AnyError>>>>,
pub lockfile: Option<Arc<Mutex<Lockfile>>>,
pub maybe_config_file: Option<ConfigFile>,
pub maybe_import_map: Option<ImportMap>,
pub maybe_inspector_server: Option<Arc<InspectorServer>>,
+ // deno_graph detects all sorts of issues at build time (prepare_module_load)
+ // but if they are errors at that stage, the don't cause the correct behaviors
+ // so we cache the error and then surface it when appropriate (e.g. load)
+ pub(crate) maybe_graph_error:
+ Arc<Mutex<Option<deno_graph::ModuleGraphError>>>,
+ // because the graph detects resolution issues early, but is build and dropped
+ // during the `prepare_module_load` method, we need to extract out the module
+ // resolution map so that those errors can be surfaced at the appropriate time
+ resolution_map:
+ Arc<Mutex<HashMap<ModuleSpecifier, HashMap<String, deno_graph::Resolved>>>>,
+ // in some cases we want to provide the span where the resolution error
+ // occurred but need to surface it on load, but on load we don't know who the
+ // referrer and span was, so we need to cache those
+ resolved_map: Arc<Mutex<HashMap<ModuleSpecifier, deno_graph::Span>>>,
pub root_cert_store: Option<RootCertStore>,
pub blob_store: BlobStore,
pub broadcast_channel: InMemoryBroadcastChannel,
@@ -222,11 +233,11 @@ impl ProcState {
let diagnostics = import_map.update_imports(node_builtins)?;
if !diagnostics.is_empty() {
- info!("Some Node built-ins were not added to the import map:");
+ log::info!("Some Node built-ins were not added to the import map:");
for diagnostic in diagnostics {
- info!(" - {}", diagnostic);
+ log::info!(" - {}", diagnostic);
}
- info!("If you want to use Node built-ins provided by Deno remove listed specifiers from \"imports\" mapping in the import map file.");
+ log::info!("If you want to use Node built-ins provided by Deno remove listed specifiers from \"imports\" mapping in the import map file.");
}
maybe_import_map = Some(import_map);
@@ -252,6 +263,9 @@ impl ProcState {
maybe_config_file,
maybe_import_map,
maybe_inspector_server,
+ maybe_graph_error: Default::default(),
+ resolution_map: Default::default(),
+ resolved_map: Default::default(),
root_cert_store: Some(root_cert_store.clone()),
blob_store,
broadcast_channel,
@@ -260,72 +274,174 @@ impl ProcState {
})))
}
- /// Prepares a set of module specifiers for loading in one shot.
- pub async fn prepare_module_graph(
+ /// Return any imports that should be brought into the scope of the module
+ /// graph.
+ fn get_maybe_imports(&self) -> Option<Vec<(ModuleSpecifier, Vec<String>)>> {
+ let mut imports = Vec::new();
+ if let Some(config_file) = &self.maybe_config_file {
+ if let Some(config_imports) = config_file.to_maybe_imports() {
+ imports.extend(config_imports);
+ }
+ }
+ if self.flags.compat {
+ imports.extend(compat::get_node_imports());
+ }
+ if imports.is_empty() {
+ None
+ } else {
+ Some(imports)
+ }
+ }
+
+ /// This method is called when a module requested by the `JsRuntime` is not
+ /// available, or in other sub-commands that need to "load" a module graph.
+ /// The method will collect all the dependencies of the provided specifier,
+ /// optionally checks their integrity, optionally type checks them, and
+ /// ensures that any modules that needs to be transpiled is transpiled.
+ ///
+ /// It then populates the `loadable_modules` with what can be loaded into v8.
+ pub(crate) async fn prepare_module_load(
&self,
- specifiers: Vec<ModuleSpecifier>,
- lib: TypeLib,
+ roots: Vec<ModuleSpecifier>,
+ is_dynamic: bool,
+ lib: emit::TypeLib,
root_permissions: Permissions,
dynamic_permissions: Permissions,
- maybe_import_map: Option<ImportMap>,
) -> Result<(), AnyError> {
- let handler = Arc::new(Mutex::new(FetchHandler::new(
- self,
- root_permissions,
- dynamic_permissions,
- )?));
-
- let mut builder =
- GraphBuilder::new(handler, maybe_import_map, self.lockfile.clone());
-
- for specifier in specifiers {
- builder.add(&specifier, false).await?;
- }
- builder.analyze_config_file(&self.maybe_config_file).await?;
-
- let mut graph = builder.get_graph();
- let debug = self.flags.log_level == Some(log::Level::Debug);
- let maybe_config_file = self.maybe_config_file.clone();
- let reload_exclusions = {
+ let mut cache = cache::FetchCacher::new(
+ self.dir.gen_cache.clone(),
+ self.file_fetcher.clone(),
+ root_permissions.clone(),
+ dynamic_permissions.clone(),
+ );
+ let maybe_locker = as_maybe_locker(self.lockfile.clone());
+ let maybe_imports = self.get_maybe_imports();
+ let maybe_resolver =
+ self.maybe_import_map.as_ref().map(ImportMapResolver::new);
+ let graph = deno_graph::create_graph(
+ roots,
+ is_dynamic,
+ maybe_imports,
+ &mut cache,
+ maybe_resolver.as_ref().map(|im| im.as_resolver()),
+ maybe_locker,
+ None,
+ )
+ .await;
+ // If there was a locker, validate the integrity of all the modules in the
+ // locker.
+ emit::lock(&graph);
+
+ // Determine any modules that have already been emitted this session and
+ // should be skipped.
+ let reload_exclusions: HashSet<ModuleSpecifier> = {
let modules = self.modules.lock();
- modules.keys().cloned().collect::<HashSet<_>>()
+ modules.keys().cloned().collect()
};
- let result_modules = if self.flags.no_check {
- let result_info = graph.transpile(TranspileOptions {
- debug,
- maybe_config_file,
- reload: self.flags.reload,
- reload_exclusions,
- })?;
- debug!("{}", result_info.stats);
- if let Some(ignored_options) = result_info.maybe_ignored_options {
- warn!("{}", ignored_options);
- }
- result_info.loadable_modules
+ let config_type = if self.flags.no_check {
+ emit::ConfigType::Emit
} else {
- let result_info = graph.check(CheckOptions {
- debug,
- emit: true,
+ emit::ConfigType::Check {
+ tsc_emit: true,
lib,
- maybe_config_file,
- reload: self.flags.reload,
- reload_exclusions,
- })?;
-
- debug!("{}", result_info.stats);
- if let Some(ignored_options) = result_info.maybe_ignored_options {
- eprintln!("{}", ignored_options);
}
- if !result_info.diagnostics.is_empty() {
- return Err(anyhow!(result_info.diagnostics));
- }
- result_info.loadable_modules
};
- let mut loadable_modules = self.modules.lock();
- loadable_modules.extend(result_modules);
+ let (ts_config, maybe_ignored_options) =
+ emit::get_ts_config(config_type, self.maybe_config_file.as_ref(), None)?;
+ let graph = Arc::new(graph);
+
+ // we will store this in proc state later, as if we were to return it from
+ // prepare_load, some dynamic errors would not be catchable
+ let maybe_graph_error = graph.valid().err();
+
+ if emit::valid_emit(
+ graph.as_ref(),
+ &cache,
+ &ts_config,
+ self.flags.reload,
+ &reload_exclusions,
+ ) {
+ if let Some(root) = graph.roots.get(0) {
+ log::debug!("specifier \"{}\" and dependencies have valid emit, skipping checking and emitting", root);
+ } else {
+ log::debug!("rootless graph, skipping checking and emitting");
+ }
+ } else {
+ if let Some(ignored_options) = maybe_ignored_options {
+ log::warn!("{}", ignored_options);
+ }
+ let emit_result = if self.flags.no_check {
+ let options = emit::EmitOptions {
+ ts_config,
+ reload_exclusions,
+ reload: self.flags.reload,
+ };
+ emit::emit(graph.as_ref(), &mut cache, options)?
+ } else {
+ // here, we are type checking, so we want to error here if any of the
+ // type only dependencies are missing or we have other errors with them
+ // where as if we are not type checking, we shouldn't care about these
+ // errors, and they don't get returned in `graph.valid()` above.
+ graph.valid_types_only()?;
+
+ let maybe_config_specifier = self
+ .maybe_config_file
+ .as_ref()
+ .map(|cf| ModuleSpecifier::from_file_path(&cf.path).unwrap());
+ let options = emit::CheckOptions {
+ debug: self.flags.log_level == Some(log::Level::Debug),
+ emit_with_diagnostics: true,
+ maybe_config_specifier,
+ ts_config,
+ };
+ for root in &graph.roots {
+ let root_str = root.to_string();
+ // `$deno$` specifiers are internal specifiers, printing out that
+ // they are being checked is confusing to a user, since they don't
+ // actually exist, so we will simply indicate that a generated module
+ // is being checked instead of the cryptic internal module
+ if !root_str.contains("$deno$") {
+ log::info!("{} {}", colors::green("Check"), root);
+ } else {
+ log::info!("{} a generated module", colors::green("Check"))
+ }
+ }
+ emit::check_and_maybe_emit(graph.clone(), &mut cache, options)?
+ };
+ log::debug!("{}", emit_result.stats);
+ // if the graph is not valid then the diagnostics returned are bogus and
+ // should just be ignored so that module loading can proceed to allow the
+ // "real" error to be surfaced
+ if !emit_result.diagnostics.is_empty() && maybe_graph_error.is_none() {
+ return Err(anyhow!(emit_result.diagnostics));
+ }
+ }
+
+ // we iterate over the graph, looking for any modules that were emitted, or
+ // should be loaded as their un-emitted source and add them to the in memory
+ // cache of modules for loading by deno_core.
+ {
+ let mut modules = self.modules.lock();
+ modules.extend(emit::to_module_sources(graph.as_ref(), &cache));
+ }
+ // since we can't store the graph in proc state, because proc state needs to
+ // be thread safe because of the need to provide source map resolution and
+ // the graph needs to not be thread safe (due to wasmbind_gen constraints),
+ // we have no choice but to extract out other meta data from the graph to
+ // provide the correct loading behaviors for CLI
+ {
+ let mut resolution_map = self.resolution_map.lock();
+ resolution_map.extend(graph.resolution_map());
+ }
+ {
+ let mut self_maybe_graph_error = self.maybe_graph_error.lock();
+ *self_maybe_graph_error = maybe_graph_error;
+ }
+
+ // any updates to the lockfile should be updated now
if let Some(ref lockfile) = self.lockfile {
let g = lockfile.lock();
g.write()?;
@@ -334,127 +450,116 @@ impl ProcState {
Ok(())
}
- /// This function is called when new module load is initialized by the JsRuntime. Its
- /// resposibility is to collect all dependencies and if it is required then also perform TS
- /// typecheck and traspilation.
- pub async fn prepare_module_load(
+ pub(crate) fn resolve(
&self,
- specifier: ModuleSpecifier,
- lib: TypeLib,
- root_permissions: Permissions,
- dynamic_permissions: Permissions,
- is_dynamic: bool,
- maybe_import_map: Option<ImportMap>,
- ) -> Result<(), AnyError> {
- let specifier = specifier.clone();
- let handler = Arc::new(Mutex::new(FetchHandler::new(
- self,
- root_permissions,
- dynamic_permissions,
- )?));
- let mut builder =
- GraphBuilder::new(handler, maybe_import_map, self.lockfile.clone());
- if self.flags.compat {
- builder.add(&compat::get_node_globals_url(), false).await?;
- }
- builder.add(&specifier, is_dynamic).await?;
- builder.analyze_config_file(&self.maybe_config_file).await?;
- let mut graph = builder.get_graph();
- let debug = self.flags.log_level == Some(log::Level::Debug);
- let maybe_config_file = self.maybe_config_file.clone();
- let reload_exclusions = {
- let modules = self.modules.lock();
- modules.keys().cloned().collect::<HashSet<_>>()
- };
-
- let result_modules = if self.flags.no_check {
- let result_info = graph.transpile(TranspileOptions {
- debug,
- maybe_config_file,
- reload: self.flags.reload,
- reload_exclusions,
- })?;
- debug!("{}", result_info.stats);
- if let Some(ignored_options) = result_info.maybe_ignored_options {
- warn!("{}", ignored_options);
+ specifier: &str,
+ referrer: &str,
+ ) -> Result<ModuleSpecifier, AnyError> {
+ let resolution_map = self.resolution_map.lock();
+ if let Some((_, Some(map))) = deno_core::resolve_url_or_path(referrer)
+ .ok()
+ .map(|s| (s.clone(), resolution_map.get(&s)))
+ {
+ if let Some(resolved) = map.get(specifier) {
+ match resolved {
+ Some(Ok((specifier, span))) => {
+ let mut resolved_map = self.resolved_map.lock();
+ resolved_map.insert(specifier.clone(), span.clone());
+ return Ok(specifier.clone());
+ }
+ Some(Err(err)) => {
+ return Err(custom_error(
+ "TypeError",
+ format!("{}\n", err.to_string_with_span()),
+ ))
+ }
+ _ => (),
+ }
}
- result_info.loadable_modules
+ }
+ // FIXME(bartlomieju): hacky way to provide compatibility with repl
+ let referrer = if referrer.is_empty() && self.flags.repl {
+ deno_core::DUMMY_SPECIFIER
} else {
- let result_info = graph.check(CheckOptions {
- debug,
- emit: true,
- lib,
- maybe_config_file,
- reload: self.flags.reload,
- reload_exclusions,
- })?;
-
- debug!("{}", result_info.stats);
- if let Some(ignored_options) = result_info.maybe_ignored_options {
- eprintln!("{}", ignored_options);
- }
- if !result_info.diagnostics.is_empty() {
- return Err(anyhow!(result_info.diagnostics));
- }
- result_info.loadable_modules
+ referrer
};
-
- let mut loadable_modules = self.modules.lock();
- loadable_modules.extend(result_modules);
-
- if let Some(ref lockfile) = self.lockfile {
- let g = lockfile.lock();
- g.write()?;
+ if let Some(import_map) = &self.maybe_import_map {
+ import_map
+ .resolve(specifier, referrer)
+ .map_err(|err| err.into())
+ } else {
+ deno_core::resolve_import(specifier, referrer).map_err(|err| err.into())
}
-
- Ok(())
}
pub fn load(
&self,
specifier: ModuleSpecifier,
maybe_referrer: Option<ModuleSpecifier>,
+ is_dynamic: bool,
) -> Result<ModuleSource, AnyError> {
+ log::debug!(
+ "specifier: {} maybe_referrer: {} is_dynamic: {}",
+ specifier,
+ maybe_referrer
+ .as_ref()
+ .map(|s| s.to_string())
+ .unwrap_or_else(|| "<none>".to_string()),
+ is_dynamic
+ );
let modules = self.modules.lock();
modules
.get(&specifier)
.map(|r| match r {
Ok(module_source) => Ok(module_source.clone()),
Err(err) => {
- // TODO(@kitsonk) this feels a bit hacky but it works, without
- // introducing another enum to have to try to deal with.
- if get_custom_error_class(err) == Some("NotFound") {
- let message = if let Some(referrer) = &maybe_referrer {
- format!("{}\n From: {}\n If the source module contains only types, use `import type` and `export type` to import it instead.", err, referrer)
- } else {
- format!("{}\n If the source module contains only types, use `import type` and `export type` to import it instead.", err)
- };
- warn!("{}: {}", crate::colors::yellow("warning"), message);
- Ok(ModuleSource {
- code: "".to_string(),
- module_url_found: specifier.to_string(),
- module_url_specified: specifier.to_string(),
- })
+ // this is the "pending" error we will return
+ let err = if let Some(error_class) = get_custom_error_class(err) {
+ if error_class == "NotFound" && maybe_referrer.is_some() && !is_dynamic {
+ let resolved_map = self.resolved_map.lock();
+ // in situations where we were to try to load a module that wasn't
+ // emitted and we can't run the original source code (it isn't)
+ // JavaScript, we will load a blank module instead. This is
+ // usually caused by people exporting type only exports and not
+ // type checking.
+ if let Some(span) = resolved_map.get(&specifier) {
+ log::warn!("{}: Cannot load module \"{}\".\n at {}\n If the source module contains only types, use `import type` and `export type` to import it instead.", colors::yellow("warning"), specifier, span);
+ return Ok(ModuleSource {
+ code: "".to_string(),
+ module_url_found: specifier.to_string(),
+ module_url_specified: specifier.to_string(),
+ });
+ }
+ }
+ custom_error(error_class, err.to_string())
+ } else {
+ anyhow!(err.to_string())
+ };
+ // if there is a pending graph error though we haven't returned, we
+ // will return that one
+ let mut maybe_graph_error = self.maybe_graph_error.lock();
+ if let Some(graph_error) = maybe_graph_error.take() {
+ log::debug!("returning cached graph error");
+ let resolved_map = self.resolved_map.lock();
+ if let Some(span) = resolved_map.get(&specifier) {
+ if !span.specifier.as_str().contains("$deno") {
+ return Err(custom_error(get_module_graph_error_class(&graph_error), format!("{}\n at {}", graph_error, span)));
+ }
+ }
+ Err(graph_error.into())
} else {
- // anyhow errors don't support cloning, so we have to manage this
- // ourselves
- Err(anyhow!(err.to_string()))
+ Err(err)
}
- },
+ }
})
.unwrap_or_else(|| {
- if let Some(referrer) = maybe_referrer {
- Err(anyhow!(
- "Module \"{}\" is missing from the graph.\n From: {}",
- specifier,
- referrer
- ))
- } else {
- Err(anyhow!(
- "Module \"{}\" is missing from the graph.",
- specifier
- ))
+ if maybe_referrer.is_some() && !is_dynamic {
+ let resolved_map = self.resolved_map.lock();
+ if let Some(span) = resolved_map.get(&specifier) {
+ return Err(custom_error("NotFound", format!("Cannot load module \"{}\".\n at {}", specifier, span)));
+ }
}
+ Err(custom_error("NotFound", format!("Cannot load module \"{}\".", specifier)))
})
}
@@ -497,7 +602,7 @@ impl SourceMapGetter for ProcState {
if let Some((code, maybe_map)) = self.get_emit(&specifier) {
let code = String::from_utf8(code).unwrap();
source_map_from_code(code).or(maybe_map)
- } else if let Ok(source) = self.load(specifier, None) {
+ } else if let Ok(source) = self.load(specifier, None, false) {
source_map_from_code(source.code)
} else {
None
diff --git a/cli/resolver.rs b/cli/resolver.rs
new file mode 100644
index 000000000..d3427c58b
--- /dev/null
+++ b/cli/resolver.rs
@@ -0,0 +1,35 @@
+// Copyright 2018-2021 the Deno authors. All rights reserved. MIT license.
+
+use deno_core::error::AnyError;
+use deno_core::ModuleSpecifier;
+use deno_graph::source::Resolver;
+use import_map::ImportMap;
+
+/// Wraps an import map to be used when building a deno_graph module graph.
+/// This is done to avoid having `import_map` be a direct dependency of
+/// `deno_graph`.
+#[derive(Debug)]
+pub(crate) struct ImportMapResolver<'a>(&'a ImportMap);
+
+impl<'a> ImportMapResolver<'a> {
+ pub fn new(import_map: &'a ImportMap) -> Self {
+ Self(import_map)
+ }
+
+ pub fn as_resolver(&'a self) -> &'a dyn Resolver {
+ self
+ }
+}
+
+impl Resolver for ImportMapResolver<'_> {
+ fn resolve(
+ &self,
+ specifier: &str,
+ referrer: &ModuleSpecifier,
+ ) -> Result<ModuleSpecifier, AnyError> {
+ self
+ .0
+ .resolve(specifier, referrer.as_str())
+ .map_err(|err| err.into())
+ }
+}
diff --git a/cli/specifier_handler.rs b/cli/specifier_handler.rs
deleted file mode 100644
index aec35a738..000000000
--- a/cli/specifier_handler.rs
+++ /dev/null
@@ -1,776 +0,0 @@
-// Copyright 2018-2021 the Deno authors. All rights reserved. MIT license.
-
-use crate::ast::Location;
-use crate::disk_cache::DiskCache;
-use crate::file_fetcher::FileFetcher;
-use crate::proc_state::ProcState;
-
-use deno_ast::MediaType;
-use deno_core::error::custom_error;
-use deno_core::error::AnyError;
-use deno_core::futures::future;
-use deno_core::futures::Future;
-use deno_core::futures::FutureExt;
-use deno_core::serde::Deserialize;
-use deno_core::serde::Serialize;
-use deno_core::serde_json;
-use deno_core::ModuleSpecifier;
-use deno_runtime::permissions::Permissions;
-use log::debug;
-use std::collections::HashMap;
-use std::fmt;
-use std::path::PathBuf;
-use std::pin::Pin;
-use std::sync::Arc;
-
-pub type DependencyMap = HashMap<String, Dependency>;
-type FetchFutureOutput = Result<CachedModule, (ModuleSpecifier, AnyError)>;
-pub type FetchFuture = Pin<Box<dyn Future<Output = FetchFutureOutput> + Send>>;
-
-/// A group of errors that represent errors that can occur with an
-/// an implementation of `SpecifierHandler`.
-#[derive(Debug, Clone, Eq, PartialEq)]
-pub enum HandlerError {
- /// A fetch error, where we have a location associated with it.
- FetchErrorWithLocation(String, Location),
-}
-
-impl fmt::Display for HandlerError {
- fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
- match self {
- HandlerError::FetchErrorWithLocation(ref err, ref location) => {
- write!(f, "{}\n at {}", err, location)
- }
- }
- }
-}
-
-impl std::error::Error for HandlerError {}
-
-#[derive(Debug, Clone)]
-pub struct CachedModule {
- pub is_remote: bool,
- pub maybe_dependencies: Option<DependencyMap>,
- pub maybe_emit: Option<Emit>,
- pub maybe_emit_path: Option<(PathBuf, Option<PathBuf>)>,
- pub maybe_types: Option<String>,
- pub maybe_version: Option<String>,
- pub media_type: MediaType,
- pub requested_specifier: ModuleSpecifier,
- pub source: Arc<String>,
- pub source_path: PathBuf,
- pub specifier: ModuleSpecifier,
-}
-
-impl Default for CachedModule {
- fn default() -> Self {
- let specifier = deno_core::resolve_url("file:///example.js").unwrap();
- CachedModule {
- is_remote: false,
- maybe_dependencies: None,
- maybe_emit: None,
- maybe_emit_path: None,
- maybe_types: None,
- maybe_version: None,
- media_type: MediaType::Unknown,
- requested_specifier: specifier.clone(),
- source: Arc::new(String::default()),
- source_path: PathBuf::new(),
- specifier,
- }
- }
-}
-
-/// An enum to own the a specific emit.
-///
-/// Currently there is only one type of emit that is cacheable, but this has
-/// been added to future proof the ability for the specifier handler
-/// implementations to be able to handle other types of emits, like form a
-/// runtime API which might have a different configuration.
-#[derive(Debug, Clone, Eq, PartialEq, Hash)]
-pub enum Emit {
- /// Code that was emitted for use by the CLI
- Cli((String, Option<String>)),
-}
-
-impl Default for Emit {
- fn default() -> Self {
- Emit::Cli(("".to_string(), None))
- }
-}
-
-#[derive(Debug, Clone)]
-pub struct Dependency {
- /// Flags if the dependency is a dynamic import or not.
- pub is_dynamic: bool,
- /// The location in the source code where the dependency statement occurred.
- pub location: Location,
- /// The module specifier that resolves to the runtime code dependency for the
- /// module.
- pub maybe_code: Option<ModuleSpecifier>,
- /// The module specifier that resolves to the type only dependency for the
- /// module.
- pub maybe_type: Option<ModuleSpecifier>,
-}
-
-impl Dependency {
- pub fn new(location: Location) -> Self {
- Dependency {
- is_dynamic: false,
- location,
- maybe_code: None,
- maybe_type: None,
- }
- }
-}
-
-pub trait SpecifierHandler: Sync + Send {
- /// Instructs the handler to fetch a specifier or retrieve its value from the
- /// cache.
- fn fetch(
- &mut self,
- specifier: ModuleSpecifier,
- maybe_location: Option<Location>,
- is_dynamic: bool,
- ) -> FetchFuture;
-
- /// Get the optional build info from the cache for a given module specifier.
- /// Because build infos are only associated with the "root" modules, they are
- /// not expected to be cached for each module, but are "lazily" checked when
- /// a root module is identified. The `emit_type` also indicates what form
- /// of the module the build info is valid for.
- fn get_tsbuildinfo(
- &self,
- specifier: &ModuleSpecifier,
- ) -> Result<Option<String>, AnyError>;
-
- /// Set the emit for the module specifier.
- fn set_cache(
- &mut self,
- specifier: &ModuleSpecifier,
- emit: &Emit,
- ) -> Result<(), AnyError>;
-
- /// When parsed out of a JavaScript module source, the triple slash reference
- /// to the types should be stored in the cache.
- fn set_types(
- &mut self,
- specifier: &ModuleSpecifier,
- types: String,
- ) -> Result<(), AnyError>;
-
- /// Set the build info for a module specifier, also providing the cache type.
- fn set_tsbuildinfo(
- &mut self,
- specifier: &ModuleSpecifier,
- tsbuildinfo: String,
- ) -> Result<(), AnyError>;
-
- /// Set the graph dependencies for a given module specifier.
- fn set_deps(
- &mut self,
- specifier: &ModuleSpecifier,
- dependencies: DependencyMap,
- ) -> Result<(), AnyError>;
-
- /// Set the version of the source for a given module, which is used to help
- /// determine if a module needs to be re-emitted.
- fn set_version(
- &mut self,
- specifier: &ModuleSpecifier,
- version: String,
- ) -> Result<(), AnyError>;
-}
-
-impl fmt::Debug for dyn SpecifierHandler {
- fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
- write!(f, "SpecifierHandler {{ }}")
- }
-}
-
-/// A representation of meta data for a compiled file.
-///
-/// *Note* this is currently just a copy of what is located in `tsc.rs` but will
-/// be refactored to be able to store dependencies and type information in the
-/// future.
-#[derive(Deserialize, Serialize)]
-pub struct CompiledFileMetadata {
- pub version_hash: String,
-}
-
-impl CompiledFileMetadata {
- pub fn from_bytes(bytes: &[u8]) -> Result<Self, AnyError> {
- let metadata_string = std::str::from_utf8(bytes)?;
- serde_json::from_str::<Self>(metadata_string).map_err(|e| e.into())
- }
-
- pub fn to_json_string(&self) -> Result<String, AnyError> {
- serde_json::to_string(self).map_err(|e| e.into())
- }
-}
-
-/// An implementation of the `SpecifierHandler` trait that integrates with the
-/// existing `file_fetcher` interface, which will eventually be refactored to
-/// align it more to the `SpecifierHandler` trait.
-pub struct FetchHandler {
- /// An instance of disk where generated (emitted) files are stored.
- disk_cache: DiskCache,
- /// The set permissions which are used for root modules (static imports).
- root_permissions: Permissions,
- /// The set of permissions which are used for dynamic imports.
- dynamic_permissions: Permissions,
- /// A clone of the `ps` file fetcher.
- file_fetcher: FileFetcher,
-}
-
-impl FetchHandler {
- pub fn new(
- ps: &ProcState,
- root_permissions: Permissions,
- dynamic_permissions: Permissions,
- ) -> Result<Self, AnyError> {
- let disk_cache = ps.dir.gen_cache.clone();
- let file_fetcher = ps.file_fetcher.clone();
-
- Ok(FetchHandler {
- disk_cache,
- root_permissions,
- dynamic_permissions,
- file_fetcher,
- })
- }
-}
-
-impl SpecifierHandler for FetchHandler {
- fn fetch(
- &mut self,
- requested_specifier: ModuleSpecifier,
- maybe_location: Option<Location>,
- is_dynamic: bool,
- ) -> FetchFuture {
- // When the module graph fetches dynamic modules, the set of dynamic
- // permissions need to be applied. Other static imports have all
- // permissions.
- let mut permissions = if is_dynamic {
- self.dynamic_permissions.clone()
- } else {
- self.root_permissions.clone()
- };
- let file_fetcher = self.file_fetcher.clone();
- let disk_cache = self.disk_cache.clone();
-
- async move {
- let source_file = file_fetcher
- .fetch(&requested_specifier, &mut permissions)
- .await
- .map_err(|err| {
- let err = if let Some(e) = err.downcast_ref::<std::io::Error>() {
- if e.kind() == std::io::ErrorKind::NotFound {
- let message = if let Some(location) = &maybe_location {
- format!(
- "Cannot resolve module \"{}\" from \"{}\".",
- requested_specifier, location.specifier
- )
- } else {
- format!("Cannot resolve module \"{}\".", requested_specifier)
- };
- custom_error("NotFound", message)
- } else {
- err
- }
- } else {
- err
- };
- if let Some(location) = maybe_location {
- // Injected modules (like test and eval) come with locations, but
- // they are confusing to the user to print out the location because
- // they cannot actually get to the source code that is quoted, as
- // it only exists in the runtime memory of Deno.
- if !location.specifier.contains("$deno$") {
- (
- requested_specifier.clone(),
- HandlerError::FetchErrorWithLocation(err.to_string(), location)
- .into(),
- )
- } else {
- (requested_specifier.clone(), err)
- }
- } else {
- (requested_specifier.clone(), err)
- }
- })?;
- let url = &source_file.specifier;
- let is_remote = !(url.scheme() == "file"
- || url.scheme() == "data"
- || url.scheme() == "blob");
- let filename = disk_cache.get_cache_filename_with_extension(url, "meta");
- let maybe_version = if let Some(filename) = filename {
- if let Ok(bytes) = disk_cache.get(&filename) {
- if let Ok(compiled_file_metadata) =
- CompiledFileMetadata::from_bytes(&bytes)
- {
- Some(compiled_file_metadata.version_hash)
- } else {
- None
- }
- } else {
- None
- }
- } else {
- None
- };
-
- let mut maybe_map_path = None;
- let map_path =
- disk_cache.get_cache_filename_with_extension(url, "js.map");
- let maybe_map = if let Some(map_path) = map_path {
- if let Ok(map) = disk_cache.get(&map_path) {
- maybe_map_path = Some(disk_cache.location.join(map_path));
- Some(String::from_utf8(map).unwrap())
- } else {
- None
- }
- } else {
- None
- };
- let mut maybe_emit = None;
- let mut maybe_emit_path = None;
- let emit_path = disk_cache.get_cache_filename_with_extension(url, "js");
- if let Some(emit_path) = emit_path {
- if let Ok(code) = disk_cache.get(&emit_path) {
- maybe_emit =
- Some(Emit::Cli((String::from_utf8(code).unwrap(), maybe_map)));
- maybe_emit_path =
- Some((disk_cache.location.join(emit_path), maybe_map_path));
- }
- };
-
- Ok(CachedModule {
- is_remote,
- maybe_dependencies: None,
- maybe_emit,
- maybe_emit_path,
- maybe_types: source_file.maybe_types,
- maybe_version,
- media_type: source_file.media_type,
- requested_specifier,
- source: source_file.source,
- source_path: source_file.local,
- specifier: source_file.specifier,
- })
- }
- .boxed()
- }
-
- fn get_tsbuildinfo(
- &self,
- specifier: &ModuleSpecifier,
- ) -> Result<Option<String>, AnyError> {
- let filename = self
- .disk_cache
- .get_cache_filename_with_extension(specifier, "buildinfo");
- if let Some(filename) = filename {
- if let Ok(tsbuildinfo) = self.disk_cache.get(&filename) {
- Ok(Some(String::from_utf8(tsbuildinfo)?))
- } else {
- Ok(None)
- }
- } else {
- Ok(None)
- }
- }
-
- fn set_tsbuildinfo(
- &mut self,
- specifier: &ModuleSpecifier,
- tsbuildinfo: String,
- ) -> Result<(), AnyError> {
- let filename = self
- .disk_cache
- .get_cache_filename_with_extension(specifier, "buildinfo")
- .unwrap();
- debug!("set_tsbuildinfo - filename {:?}", filename);
- self
- .disk_cache
- .set(&filename, tsbuildinfo.as_bytes())
- .map_err(|e| e.into())
- }
-
- fn set_cache(
- &mut self,
- specifier: &ModuleSpecifier,
- emit: &Emit,
- ) -> Result<(), AnyError> {
- match emit {
- Emit::Cli((code, maybe_map)) => {
- let filename = self
- .disk_cache
- .get_cache_filename_with_extension(specifier, "js")
- .unwrap();
- self.disk_cache.set(&filename, code.as_bytes())?;
-
- if let Some(map) = maybe_map {
- let filename = self
- .disk_cache
- .get_cache_filename_with_extension(specifier, "js.map")
- .unwrap();
- self.disk_cache.set(&filename, map.as_bytes())?;
- }
- }
- };
-
- Ok(())
- }
-
- fn set_deps(
- &mut self,
- _specifier: &ModuleSpecifier,
- _dependencies: DependencyMap,
- ) -> Result<(), AnyError> {
- // file_fetcher doesn't have the concept of caching dependencies
- Ok(())
- }
-
- fn set_types(
- &mut self,
- _specifier: &ModuleSpecifier,
- _types: String,
- ) -> Result<(), AnyError> {
- // file_fetcher doesn't have the concept of caching of the types
- Ok(())
- }
-
- fn set_version(
- &mut self,
- specifier: &ModuleSpecifier,
- version_hash: String,
- ) -> Result<(), AnyError> {
- let compiled_file_metadata = CompiledFileMetadata { version_hash };
- let filename = self
- .disk_cache
- .get_cache_filename_with_extension(specifier, "meta")
- .unwrap();
-
- self
- .disk_cache
- .set(
- &filename,
- compiled_file_metadata.to_json_string()?.as_bytes(),
- )
- .map_err(|e| e.into())
- }
-}
-
-pub struct MemoryHandler {
- sources: HashMap<String, Arc<String>>,
-}
-
-impl MemoryHandler {
- pub fn new(sources: HashMap<String, Arc<String>>) -> Self {
- Self { sources }
- }
-}
-
-impl SpecifierHandler for MemoryHandler {
- fn fetch(
- &mut self,
- specifier: ModuleSpecifier,
- _maybe_referrer: Option<Location>,
- _is_dynamic: bool,
- ) -> FetchFuture {
- let mut specifier_text = specifier.to_string();
- if !self.sources.contains_key(&specifier_text) {
- specifier_text = specifier_text.replace("file:///", "/");
- if !self.sources.contains_key(&specifier_text) {
- // Convert `C:/a/path/file.ts` to `/a/path/file.ts`
- specifier_text = specifier_text[3..].to_string()
- }
- }
- let result = if let Some(source) = self.sources.get(&specifier_text) {
- let media_type = MediaType::from(&specifier);
- let is_remote = specifier.scheme() != "file";
-
- Ok(CachedModule {
- source: source.clone(),
- requested_specifier: specifier.clone(),
- specifier,
- media_type,
- is_remote,
- ..Default::default()
- })
- } else {
- Err((
- specifier.clone(),
- custom_error(
- "NotFound",
- format!("Unable to find specifier in sources: {}", specifier),
- ),
- ))
- };
-
- Box::pin(future::ready(result))
- }
-
- fn get_tsbuildinfo(
- &self,
- _specifier: &ModuleSpecifier,
- ) -> Result<Option<String>, AnyError> {
- Ok(None)
- }
-
- fn set_cache(
- &mut self,
- _specifier: &ModuleSpecifier,
- _emit: &Emit,
- ) -> Result<(), AnyError> {
- Ok(())
- }
-
- fn set_types(
- &mut self,
- _specifier: &ModuleSpecifier,
- _types: String,
- ) -> Result<(), AnyError> {
- Ok(())
- }
-
- fn set_tsbuildinfo(
- &mut self,
- _specifier: &ModuleSpecifier,
- _tsbuildinfo: String,
- ) -> Result<(), AnyError> {
- Ok(())
- }
-
- fn set_deps(
- &mut self,
- _specifier: &ModuleSpecifier,
- _dependencies: DependencyMap,
- ) -> Result<(), AnyError> {
- Ok(())
- }
-
- fn set_version(
- &mut self,
- _specifier: &ModuleSpecifier,
- _version: String,
- ) -> Result<(), AnyError> {
- Ok(())
- }
-}
-
-#[cfg(test)]
-pub mod tests {
- use super::*;
- use crate::deno_dir::DenoDir;
- use crate::file_fetcher::CacheSetting;
- use crate::http_cache::HttpCache;
- use deno_core::resolve_url_or_path;
- use deno_runtime::deno_web::BlobStore;
- use tempfile::TempDir;
-
- macro_rules! map (
- { $($key:expr => $value:expr),+ } => {
- {
- let mut m = ::std::collections::HashMap::new();
- $(
- m.insert($key, $value);
- )+
- m
- }
- };
- );
-
- fn setup() -> (TempDir, FetchHandler) {
- let temp_dir = TempDir::new().expect("could not setup");
- let deno_dir = DenoDir::new(Some(temp_dir.path().to_path_buf()))
- .expect("could not setup");
-
- let file_fetcher = FileFetcher::new(
- HttpCache::new(&temp_dir.path().to_path_buf().join("deps")),
- CacheSetting::Use,
- true,
- None,
- BlobStore::default(),
- None,
- )
- .expect("could not setup");
- let disk_cache = deno_dir.gen_cache;
-
- let fetch_handler = FetchHandler {
- disk_cache,
- root_permissions: Permissions::allow_all(),
- dynamic_permissions: Permissions::default(),
- file_fetcher,
- };
-
- (temp_dir, fetch_handler)
- }
-
- #[tokio::test]
- async fn test_fetch_handler_fetch() {
- let _http_server_guard = test_util::http_server();
- let (_, mut file_fetcher) = setup();
- let specifier =
- resolve_url_or_path("http://localhost:4545/subdir/mod2.ts").unwrap();
- let cached_module: CachedModule = file_fetcher
- .fetch(specifier.clone(), None, false)
- .await
- .unwrap();
- assert!(cached_module.maybe_emit.is_none());
- assert!(cached_module.maybe_dependencies.is_none());
- assert_eq!(cached_module.media_type, MediaType::TypeScript);
- assert_eq!(
- cached_module.source.as_str(),
- "export { printHello } from \"./print_hello.ts\";\n"
- );
- assert_eq!(cached_module.specifier, specifier);
- }
-
- #[tokio::test]
- async fn test_fetch_handler_set_cache() {
- let _http_server_guard = test_util::http_server();
- let (_, mut file_fetcher) = setup();
- let specifier =
- resolve_url_or_path("http://localhost:4545/subdir/mod2.ts").unwrap();
- let cached_module: CachedModule = file_fetcher
- .fetch(specifier.clone(), None, false)
- .await
- .unwrap();
- assert!(cached_module.maybe_emit.is_none());
- let code = String::from("some code");
- file_fetcher
- .set_cache(&specifier, &Emit::Cli((code, None)))
- .expect("could not set cache");
- let cached_module: CachedModule = file_fetcher
- .fetch(specifier.clone(), None, false)
- .await
- .unwrap();
- assert_eq!(
- cached_module.maybe_emit,
- Some(Emit::Cli(("some code".to_string(), None)))
- );
- }
-
- #[tokio::test]
- async fn test_fetch_handler_is_remote() {
- let _http_server_guard = test_util::http_server();
- let (_, mut file_fetcher) = setup();
- let specifier =
- resolve_url_or_path("http://localhost:4545/subdir/mod2.ts").unwrap();
- let cached_module: CachedModule =
- file_fetcher.fetch(specifier, None, false).await.unwrap();
- assert!(cached_module.is_remote);
- let specifier = resolve_url_or_path(
- test_util::testdata_path()
- .join("subdir/mod1.ts")
- .as_os_str()
- .to_str()
- .unwrap(),
- )
- .unwrap();
- let cached_module: CachedModule =
- file_fetcher.fetch(specifier, None, false).await.unwrap();
- assert!(!cached_module.is_remote);
- }
-
- #[tokio::test]
- async fn test_memory_handler_fetch() {
- let a_src = r#"
- import * as b from "./b.ts";
- console.log(b);
- "#;
- let b_src = r#"
- export const b = "b";
- "#;
- let c_src = r#"
- export const c = "c";
- "#;
- let d_src = r#"
- export const d: string;
- "#;
- let sources = map!(
- "/a.ts" => a_src,
- "/b.ts" => b_src,
- "https://deno.land/x/c.js" => c_src,
- "https://deno.land/x/d.d.ts" => d_src
- );
- let sources: HashMap<String, Arc<String>> = sources
- .iter()
- .map(|(k, v)| (k.to_string(), Arc::new(v.to_string())))
- .collect();
- let mut handler = MemoryHandler::new(sources);
- let specifier = resolve_url_or_path("file:///a.ts").unwrap();
- let actual: CachedModule = handler
- .fetch(specifier.clone(), None, false)
- .await
- .expect("could not fetch module");
- assert_eq!(actual.source.as_str(), a_src);
- assert_eq!(actual.requested_specifier, specifier);
- assert_eq!(actual.specifier, specifier);
- assert_eq!(actual.media_type, MediaType::TypeScript);
- assert!(!actual.is_remote);
-
- let specifier = resolve_url_or_path("file:///b.ts").unwrap();
- let actual: CachedModule = handler
- .fetch(specifier.clone(), None, false)
- .await
- .expect("could not fetch module");
- assert_eq!(actual.source.as_str(), b_src);
- assert_eq!(actual.requested_specifier, specifier);
- assert_eq!(actual.specifier, specifier);
- assert_eq!(actual.media_type, MediaType::TypeScript);
- assert!(!actual.is_remote);
-
- let specifier = resolve_url_or_path("https://deno.land/x/c.js").unwrap();
- let actual: CachedModule = handler
- .fetch(specifier.clone(), None, false)
- .await
- .expect("could not fetch module");
- assert_eq!(actual.source.as_str(), c_src);
- assert_eq!(actual.requested_specifier, specifier);
- assert_eq!(actual.specifier, specifier);
- assert_eq!(actual.media_type, MediaType::JavaScript);
- assert!(actual.is_remote);
-
- let specifier = resolve_url_or_path("https://deno.land/x/d.d.ts").unwrap();
- let actual: CachedModule = handler
- .fetch(specifier.clone(), None, false)
- .await
- .expect("could not fetch module");
- assert_eq!(actual.source.as_str(), d_src);
- assert_eq!(actual.requested_specifier, specifier);
- assert_eq!(actual.specifier, specifier);
- assert_eq!(actual.media_type, MediaType::Dts);
- assert!(actual.is_remote);
-
- let specifier =
- resolve_url_or_path("https://deno.land/x/missing.ts").unwrap();
- handler
- .fetch(specifier.clone(), None, false)
- .await
- .expect_err("should have errored");
-
- let specifier = resolve_url_or_path("/a.ts").unwrap();
- let actual: CachedModule = handler
- .fetch(specifier.clone(), None, false)
- .await
- .expect("could not fetch module");
- assert_eq!(actual.source.as_str(), a_src);
- assert_eq!(actual.requested_specifier, specifier);
- assert_eq!(actual.specifier, specifier);
- assert_eq!(actual.media_type, MediaType::TypeScript);
- assert!(!actual.is_remote);
-
- let specifier = resolve_url_or_path("file:///C:/a.ts").unwrap();
- let actual: CachedModule = handler
- .fetch(specifier.clone(), None, false)
- .await
- .expect("could not fetch module");
- assert_eq!(actual.source.as_str(), a_src);
- assert_eq!(actual.requested_specifier, specifier);
- assert_eq!(actual.specifier, specifier);
- assert_eq!(actual.media_type, MediaType::TypeScript);
- assert!(!actual.is_remote);
- }
-}
diff --git a/cli/tests/integration/lsp_tests.rs b/cli/tests/integration/lsp_tests.rs
index 0b06beed4..215ba1a24 100644
--- a/cli/tests/integration/lsp_tests.rs
+++ b/cli/tests/integration/lsp_tests.rs
@@ -2761,7 +2761,6 @@ fn lsp_diagnostics_warn() {
.unwrap();
assert!(maybe_err.is_none());
assert!(maybe_res.is_some());
-
let (method, _) = client.read_notification::<Value>().unwrap();
assert_eq!(method, "textDocument/publishDiagnostics");
let (method, _) = client.read_notification::<Value>().unwrap();
diff --git a/cli/tests/integration/watcher_tests.rs b/cli/tests/integration/watcher_tests.rs
index 6148bcf2b..feda1bac7 100644
--- a/cli/tests/integration/watcher_tests.rs
+++ b/cli/tests/integration/watcher_tests.rs
@@ -206,6 +206,7 @@ fn bundle_js_watch() {
let (_stdout_lines, mut stderr_lines) = child_lines(&mut deno);
std::thread::sleep(std::time::Duration::from_secs(1));
+ assert_contains!(stderr_lines.next().unwrap(), "Check");
assert_contains!(stderr_lines.next().unwrap(), "file_to_watch.js");
assert_contains!(stderr_lines.next().unwrap(), "mod6.bundle.js");
let file = PathBuf::from(&bundle);
@@ -214,6 +215,7 @@ fn bundle_js_watch() {
write(&file_to_watch, "console.log('Hello world2');").unwrap();
std::thread::sleep(std::time::Duration::from_secs(1));
+ assert_contains!(stderr_lines.next().unwrap(), "Check");
assert_contains!(stderr_lines.next().unwrap(), "File change detected!");
assert_contains!(stderr_lines.next().unwrap(), "file_to_watch.js");
assert_contains!(stderr_lines.next().unwrap(), "mod6.bundle.js");
@@ -261,6 +263,7 @@ fn bundle_watch_not_exit() {
// Make sure the watcher actually restarts and works fine with the proper syntax
write(&file_to_watch, "console.log(42);").unwrap();
std::thread::sleep(std::time::Duration::from_secs(1));
+ assert_contains!(stderr_lines.next().unwrap(), "Check");
assert_contains!(stderr_lines.next().unwrap(), "File change detected!");
assert_contains!(stderr_lines.next().unwrap(), "file_to_watch.js");
assert_contains!(stderr_lines.next().unwrap(), "target.js");
diff --git a/cli/tests/testdata/041_dyn_import_eval.out b/cli/tests/testdata/041_dyn_import_eval.out
index 12a45b8da..a1d6c3687 100644
--- a/cli/tests/testdata/041_dyn_import_eval.out
+++ b/cli/tests/testdata/041_dyn_import_eval.out
@@ -1 +1,2 @@
+[WILDCARD]
Module { isMod4: true }
diff --git a/cli/tests/testdata/055_info_file_json.out b/cli/tests/testdata/055_info_file_json.out
index 4753ef0ef..a85bc83c4 100644
--- a/cli/tests/testdata/055_info_file_json.out
+++ b/cli/tests/testdata/055_info_file_json.out
@@ -1,53 +1,91 @@
{
- "root": "file://[WILDCARD]/005_more_imports.ts",
+ "roots": [
+ "file://[WILDCARD]/005_more_imports.ts"
+ ],
"modules": [
{
- "specifier": "file://[WILDCARD]/005_more_imports.ts",
"dependencies": [
{
"specifier": "./subdir/mod1.ts",
- "code": "file://[WILDCARD]/subdir/mod1.ts"
+ "code": {
+ "specifier": "file://[WILDCARD]/subdir/mod1.ts",
+ "span": {
+ "start": {
+ "line": 0,
+ "character": 52
+ },
+ "end": {
+ "line": 0,
+ "character": 70
+ }
+ }
+ }
}
],
- "size": 211,
- "mediaType": "TypeScript",
"local": "[WILDCARD]005_more_imports.ts",
[WILDCARD]
+ "mediaType": "TypeScript",
+ [WILDCARD]
+ "specifier": "file://[WILDCARD]/005_more_imports.ts"
},
{
- "specifier": "file://[WILDCARD]/subdir/mod1.ts",
"dependencies": [
{
"specifier": "./subdir2/mod2.ts",
- "code": "file://[WILDCARD]/subdir/subdir2/mod2.ts"
+ "code": {
+ "specifier": "file://[WILDCARD]/subdir/subdir2/mod2.ts",
+ "span": {
+ "start": {
+ "line": 0,
+ "character": 40
+ },
+ "end": {
+ "line": 0,
+ "character": 59
+ }
+ }
+ }
}
],
- "size": 308,
- "mediaType": "TypeScript",
"local": "[WILDCARD]mod1.ts",
[WILDCARD]
+ "mediaType": "TypeScript",
+ [WILDCARD]
+ "specifier": "file://[WILDCARD]/subdir/mod1.ts"
},
{
- "specifier": "file://[WILDCARD]/subdir/print_hello.ts",
"dependencies": [],
- "size": 57,
- "mediaType": "TypeScript",
"local": "[WILDCARD]print_hello.ts",
[WILDCARD]
+ "mediaType": "TypeScript",
+ [WILDCARD]
+ "specifier": "file://[WILDCARD]/subdir/print_hello.ts"
},
{
- "specifier": "file://[WILDCARD]/subdir/subdir2/mod2.ts",
"dependencies": [
{
"specifier": "../print_hello.ts",
- "code": "file://[WILDCARD]/subdir/print_hello.ts"
+ "code": {
+ "specifier": "file://[WILDCARD]/subdir/print_hello.ts",
+ "span": {
+ "start": {
+ "line": 0,
+ "character": 27
+ },
+ "end": {
+ "line": 0,
+ "character": 46
+ }
+ }
+ }
}
],
- "size": 157,
- "mediaType": "TypeScript",
"local": "[WILDCARD]mod2.ts",
[WILDCARD]
+ "mediaType": "TypeScript",
+ [WILDCARD]
+ "specifier": "file://[WILDCARD]/subdir/subdir2/mod2.ts"
}
],
- "size": 733
+ "redirects": {}
}
diff --git a/cli/tests/testdata/076_info_json_deps_order.out b/cli/tests/testdata/076_info_json_deps_order.out
index bcb2c70ed..69727e6e8 100644
--- a/cli/tests/testdata/076_info_json_deps_order.out
+++ b/cli/tests/testdata/076_info_json_deps_order.out
@@ -1,78 +1,164 @@
{
- "root": "file://[WILDCARD]/076_info_json_deps_order.ts",
+ "roots": [
+ "file://[WILDCARD]/076_info_json_deps_order.ts"
+ ],
"modules": [
{
- "specifier": "file://[WILDCARD]/076_info_json_deps_order.ts",
"dependencies": [
{
"specifier": "./recursive_imports/A.ts",
- "code": "file://[WILDCARD]/recursive_imports/A.ts"
+ "code": {
+ "specifier": "file://[WILDCARD]/recursive_imports/A.ts",
+ "span": {
+ "start": {
+ "line": 1,
+ "character": 18
+ },
+ "end": {
+ "line": 1,
+ "character": 44
+ }
+ }
+ }
}
],
- "size": 81,
- "mediaType": "TypeScript",
"local": "[WILDCARD]076_info_json_deps_order.ts",
- "checksum": "5dd40fe33e5924cca513489ce568e86c9b9fe318a87975403c8923629018680d"
+ [WILDCARD]
+ "mediaType": "TypeScript",
+ [WILDCARD]
+ "specifier": "file://[WILDCARD]/076_info_json_deps_order.ts"
},
{
- "specifier": "file://[WILDCARD]/recursive_imports/A.ts",
"dependencies": [
{
"specifier": "./B.ts",
- "code": "file://[WILDCARD]/recursive_imports/B.ts"
+ "code": {
+ "specifier": "file://[WILDCARD]/recursive_imports/B.ts",
+ "span": {
+ "start": {
+ "line": 0,
+ "character": 18
+ },
+ "end": {
+ "line": 0,
+ "character": 26
+ }
+ }
+ }
},
{
"specifier": "./common.ts",
- "code": "file://[WILDCARD]/recursive_imports/common.ts"
+ "code": {
+ "specifier": "file://[WILDCARD]/recursive_imports/common.ts",
+ "span": {
+ "start": {
+ "line": 1,
+ "character": 22
+ },
+ "end": {
+ "line": 1,
+ "character": 35
+ }
+ }
+ }
}
],
- "size": 108,
- "mediaType": "TypeScript",
"local": "[WILDCARD]A.ts",
- "checksum": "3b45a105d892584298490cb73372b2cac57118e1e42a677a1d5cacea704d8d3a"
+ [WILDCARD]
+ "mediaType": "TypeScript",
+ [WILDCARD]
+ "specifier": "file://[WILDCARD]/recursive_imports/A.ts"
},
{
- "specifier": "file://[WILDCARD]/recursive_imports/B.ts",
"dependencies": [
{
"specifier": "./C.ts",
- "code": "file://[WILDCARD]/recursive_imports/C.ts"
+ "code": {
+ "specifier": "file://[WILDCARD]/recursive_imports/C.ts",
+ "span": {
+ "start": {
+ "line": 0,
+ "character": 18
+ },
+ "end": {
+ "line": 0,
+ "character": 26
+ }
+ }
+ }
},
{
"specifier": "./common.ts",
- "code": "file://[WILDCARD]/recursive_imports/common.ts"
+ "code": {
+ "specifier": "file://[WILDCARD]/recursive_imports/common.ts",
+ "span": {
+ "start": {
+ "line": 1,
+ "character": 22
+ },
+ "end": {
+ "line": 1,
+ "character": 35
+ }
+ }
+ }
}
],
- "size": 108,
- "mediaType": "TypeScript",
"local": "[WILDCARD]B.ts",
- "checksum": "b12b0437ef9a91c4a4b1f66e8e4339f986b60bd8134031ccb296ce49df15b54e"
+ [WILDCARD]
+ "mediaType": "TypeScript",
+ [WILDCARD]
+ "specifier": "file://[WILDCARD]/recursive_imports/B.ts"
},
{
- "specifier": "file://[WILDCARD]/recursive_imports/C.ts",
"dependencies": [
{
"specifier": "./A.ts",
- "code": "file://[WILDCARD]/recursive_imports/A.ts"
+ "code": {
+ "specifier": "file://[WILDCARD]/recursive_imports/A.ts",
+ "span": {
+ "start": {
+ "line": 0,
+ "character": 18
+ },
+ "end": {
+ "line": 0,
+ "character": 26
+ }
+ }
+ }
},
{
"specifier": "./common.ts",
- "code": "file://[WILDCARD]/recursive_imports/common.ts"
+ "code": {
+ "specifier": "file://[WILDCARD]/recursive_imports/common.ts",
+ "span": {
+ "start": {
+ "line": 1,
+ "character": 22
+ },
+ "end": {
+ "line": 1,
+ "character": 35
+ }
+ }
+ }
}
],
- "size": 126,
- "mediaType": "TypeScript",
"local": "[WILDCARD]C.ts",
- "checksum": "605875a410741bfaeeade28cbccf45f219ad99d987ea695e35eda75d2c53a658"
+ [WILDCARD]
+ "mediaType": "TypeScript",
+ [WILDCARD]
+ "specifier": "file://[WILDCARD]/recursive_imports/C.ts"
},
{
- "specifier": "file://[WILDCARD]/recursive_imports/common.ts",
"dependencies": [],
- "size": 28,
- "mediaType": "TypeScript",
"local": "[WILDCARD]common.ts",
- "checksum": "c70025f0b936c02980c3be1fbd78f6f36b6241927c44ea67580821a6e664d8b3"
+ [WILDCARD]
+ "mediaType": "TypeScript",
+ [WILDCARD]
+ "specifier": "file://[WILDCARD]/recursive_imports/common.ts"
}
],
- "size": 451
+ "redirects": {}
}
diff --git a/cli/tests/testdata/092_import_map_unmapped_bare_specifier.ts.out b/cli/tests/testdata/092_import_map_unmapped_bare_specifier.ts.out
index 1a55e352b..6e82ad410 100644
--- a/cli/tests/testdata/092_import_map_unmapped_bare_specifier.ts.out
+++ b/cli/tests/testdata/092_import_map_unmapped_bare_specifier.ts.out
@@ -1,4 +1,7 @@
-[WILDCARD]error: Uncaught (in promise) TypeError: Relative import path "unmapped" not prefixed with / or ./ or ../ and not in import map from "[WILDCARD]"
+[WILDCARD]
+error: Uncaught (in promise) TypeError: Relative import path "unmapped" not prefixed with / or ./ or ../ and not in import map from "file://[WILDCARD]/092_import_map_unmapped_bare_specifier.ts"
+ at file://[WILDCARD]/092_import_map_unmapped_bare_specifier.ts:1:14
+
await import("unmapped");
^
- at [WILDCARD]
+ at async file://[WILDCARD]/092_import_map_unmapped_bare_specifier.ts:1:1
diff --git a/cli/tests/testdata/cafile_ts_fetch.ts.out b/cli/tests/testdata/cafile_ts_fetch.ts.out
index e965047ad..699b756ed 100644
--- a/cli/tests/testdata/cafile_ts_fetch.ts.out
+++ b/cli/tests/testdata/cafile_ts_fetch.ts.out
@@ -1 +1,2 @@
+[WILDCARD]
Hello
diff --git a/cli/tests/testdata/cafile_ts_fetch_unsafe_ssl.ts.out b/cli/tests/testdata/cafile_ts_fetch_unsafe_ssl.ts.out
index 1dc61c837..a0934e584 100644
--- a/cli/tests/testdata/cafile_ts_fetch_unsafe_ssl.ts.out
+++ b/cli/tests/testdata/cafile_ts_fetch_unsafe_ssl.ts.out
@@ -1,2 +1,3 @@
DANGER: TLS certificate validation is disabled for all hostnames
+[WILDCARD]
Hello
diff --git a/cli/tests/testdata/compat/existing_import_map.out b/cli/tests/testdata/compat/existing_import_map.out
index cbff0cc51..46125d411 100644
--- a/cli/tests/testdata/compat/existing_import_map.out
+++ b/cli/tests/testdata/compat/existing_import_map.out
@@ -1,6 +1,7 @@
[WILDCARD]
Some Node built-ins were not added to the import map:
- - "fs/promises" already exists and is mapped to "[WILDCARD]non_existent_file.js"
+ - "fs/promises" already exists and is mapped to "file://[WILDCARD]/non_existent_file.js"
If you want to use Node built-ins provided by Deno remove listed specifiers from "imports" mapping in the import map file.
[WILDCARD]
-error: Cannot resolve module [WILDCARD]
+error: Cannot load module "file://[WILDCARD]/non_existent_file.js".
+ at file://[WILDCARD]/fs_promises.js:1:16
diff --git a/cli/tests/testdata/compiler_api_test.ts b/cli/tests/testdata/compiler_api_test.ts
index 92eb4c519..b743a8612 100644
--- a/cli/tests/testdata/compiler_api_test.ts
+++ b/cli/tests/testdata/compiler_api_test.ts
@@ -313,10 +313,9 @@ Deno.test({
Deno.test({
name: "Deno.emit() - invalid syntax does not panic",
async fn() {
- await assertThrowsAsync(async () => {
- await Deno.emit("/main.js", {
- sources: {
- "/main.js": `
+ const { diagnostics } = await Deno.emit("/main.js", {
+ sources: {
+ "/main.js": `
export class Foo {
constructor() {
console.log("foo");
@@ -325,9 +324,14 @@ Deno.test({
console.log("bar");
}
}`,
- },
- });
+ },
});
+ assertEquals(diagnostics.length, 1);
+ assert(
+ diagnostics[0].messageText!.startsWith(
+ "The module's source code could not be parsed: Unexpected token `get`. Expected * for generator, private key, identifier or async at file:",
+ ),
+ );
},
});
@@ -356,12 +360,10 @@ Deno.test({
Deno.test({
name: "Deno.emit() - Unknown media type does not panic",
async fn() {
- await assertThrowsAsync(async () => {
- await Deno.emit("https://example.com/foo", {
- sources: {
- "https://example.com/foo": `let foo: string = "foo";`,
- },
- });
+ await Deno.emit("https://example.com/foo", {
+ sources: {
+ "https://example.com/foo": `let foo: string = "foo";`,
+ },
});
},
});
@@ -487,7 +489,7 @@ Deno.test({
code: 900001,
start: null,
end: null,
- messageText: "Unable to find specifier in sources: file:///b.ts",
+ messageText: 'Cannot load module "file:///b.ts".',
messageChain: null,
source: null,
sourceLine: null,
@@ -497,7 +499,7 @@ Deno.test({
]);
assert(
Deno.formatDiagnostics(diagnostics).includes(
- "Unable to find specifier in sources: file:///b.ts",
+ 'Cannot load module "file:///b.ts".',
),
);
},
diff --git a/cli/tests/testdata/config.ts.out b/cli/tests/testdata/config.ts.out
index 9f8a8ddd1..76e357a73 100644
--- a/cli/tests/testdata/config.ts.out
+++ b/cli/tests/testdata/config.ts.out
@@ -1,6 +1,7 @@
[WILDCARD]Unsupported compiler options in "[WILDCARD]config.tsconfig.json".
The following options were ignored:
module, target
+[WILDCARD]
error: TS1219 [ERROR]: Experimental support for decorators is a feature that is subject to change in a future release. Set the 'experimentalDecorators' option in your 'tsconfig' or 'jsconfig' to remove this warning.
a() {
^
diff --git a/cli/tests/testdata/disallow_http_from_https_js.out b/cli/tests/testdata/disallow_http_from_https_js.out
index 3219b7d35..428078399 100644
--- a/cli/tests/testdata/disallow_http_from_https_js.out
+++ b/cli/tests/testdata/disallow_http_from_https_js.out
@@ -1,3 +1,4 @@
error: Modules imported via https are not allowed to import http modules.
Importing: http://localhost:4545/001_hello.js
- at https://localhost:5545/disallow_http_from_https.js:2:0
+ at https://localhost:5545/disallow_http_from_https.js:2:8
+
diff --git a/cli/tests/testdata/disallow_http_from_https_ts.out b/cli/tests/testdata/disallow_http_from_https_ts.out
index d1ab64394..bd986cbce 100644
--- a/cli/tests/testdata/disallow_http_from_https_ts.out
+++ b/cli/tests/testdata/disallow_http_from_https_ts.out
@@ -1,3 +1,4 @@
error: Modules imported via https are not allowed to import http modules.
Importing: http://localhost:4545/001_hello.js
- at https://localhost:5545/disallow_http_from_https.ts:2:0
+ at https://localhost:5545/disallow_http_from_https.ts:2:8
+
diff --git a/cli/tests/testdata/dynamic_import/permissions_blob_local.ts.out b/cli/tests/testdata/dynamic_import/permissions_blob_local.ts.out
index 6dfa8e527..b7b246ba2 100644
--- a/cli/tests/testdata/dynamic_import/permissions_blob_local.ts.out
+++ b/cli/tests/testdata/dynamic_import/permissions_blob_local.ts.out
@@ -1,5 +1,5 @@
error: Uncaught (in promise) TypeError: Requires read access to "[WILDCARD]local_file.ts", run again with the --allow-read flag
- at blob:null/[WILDCARD]:1:0
+ at blob:null/[WILDCARD]:1:8
await import(URL.createObjectURL(blob));
^
- at async file:///[WILDCARD]/dynamic_import/permissions_blob_local.ts:6:1
+ at async file://[WILDCARD]/permissions_blob_local.ts:6:1
diff --git a/cli/tests/testdata/dynamic_import/permissions_blob_remote.ts.out b/cli/tests/testdata/dynamic_import/permissions_blob_remote.ts.out
index 60d71ed6d..a00c02d72 100644
--- a/cli/tests/testdata/dynamic_import/permissions_blob_remote.ts.out
+++ b/cli/tests/testdata/dynamic_import/permissions_blob_remote.ts.out
@@ -1,5 +1,5 @@
error: Uncaught (in promise) TypeError: Requires net access to "example.com", run again with the --allow-net flag
- at blob:null/[WILDCARD]:1:0
+ at blob:null/[WILDCARD]:1:8
await import(URL.createObjectURL(blob));
^
at async file:///[WILDCARD]/dynamic_import/permissions_blob_remote.ts:4:1
diff --git a/cli/tests/testdata/dynamic_import/permissions_data_local.ts.out b/cli/tests/testdata/dynamic_import/permissions_data_local.ts.out
index c9fcb0a17..98c8a7310 100644
--- a/cli/tests/testdata/dynamic_import/permissions_data_local.ts.out
+++ b/cli/tests/testdata/dynamic_import/permissions_data_local.ts.out
@@ -1,5 +1,5 @@
error: Uncaught (in promise) TypeError: Requires read access to "[WILDCARD]local_file.ts", run again with the --allow-read flag
- at data:application/javascript;base64,[WILDCARD]:1:0
+ at data:application/javascript;base64,[WILDCARD]:1:8
await import(`data:application/javascript;base64,${btoa(code)}`);
^
at async file:///[WILDCARD]/dynamic_import/permissions_data_local.ts:5:1
diff --git a/cli/tests/testdata/dynamic_import/permissions_data_remote.ts.out b/cli/tests/testdata/dynamic_import/permissions_data_remote.ts.out
index 772f0b51e..cb2a7ccf7 100644
--- a/cli/tests/testdata/dynamic_import/permissions_data_remote.ts.out
+++ b/cli/tests/testdata/dynamic_import/permissions_data_remote.ts.out
@@ -1,5 +1,5 @@
error: Uncaught (in promise) TypeError: Requires net access to "example.com", run again with the --allow-net flag
- at data:application/javascript;base64,aW1wb3J0ICJodHRwczovL2V4YW1wbGUuY29tL3NvbWUvZmlsZS50cyI7:1:0
+ at data:application/javascript;base64,aW1wb3J0ICJodHRwczovL2V4YW1wbGUuY29tL3NvbWUvZmlsZS50cyI7:1:8
await import(`data:application/javascript;base64,${btoa(code)}`);
^
at async file:///[WILDCARD]/dynamic_import/permissions_data_remote.ts:3:1
diff --git a/cli/tests/testdata/dynamic_import/permissions_remote_remote.ts.out b/cli/tests/testdata/dynamic_import/permissions_remote_remote.ts.out
index cd7f58bb9..bd88dd4d9 100644
--- a/cli/tests/testdata/dynamic_import/permissions_remote_remote.ts.out
+++ b/cli/tests/testdata/dynamic_import/permissions_remote_remote.ts.out
@@ -1,5 +1,5 @@
error: Uncaught (in promise) TypeError: Requires net access to "example.com", run again with the --allow-net flag
- at http://localhost:4545/dynamic_import/static_remote.ts:2:0
+ at http://localhost:4545/dynamic_import/static_remote.ts:2:8
await import(
^
at async file:///[WILDCARD]/dynamic_import/permissions_remote_remote.ts:1:1
diff --git a/cli/tests/testdata/error_004_missing_module.ts.out b/cli/tests/testdata/error_004_missing_module.ts.out
index 8521f4424..798ffc007 100644
--- a/cli/tests/testdata/error_004_missing_module.ts.out
+++ b/cli/tests/testdata/error_004_missing_module.ts.out
@@ -1,2 +1,2 @@
-[WILDCARD]error: Cannot resolve module "file:///[WILDCARD]/bad-module.ts" from "file:///[WILDCARD]/error_004_missing_module.ts".
- at file:///[WILDCARD]/error_004_missing_module.ts:1:0
+[WILDCARD]error: Cannot load module "file:///[WILDCARD]/bad-module.ts".
+ at file:///[WILDCARD]/error_004_missing_module.ts:1:28
diff --git a/cli/tests/testdata/error_005_missing_dynamic_import.ts.out b/cli/tests/testdata/error_005_missing_dynamic_import.ts.out
index e8647a44e..0ca35bd3b 100644
--- a/cli/tests/testdata/error_005_missing_dynamic_import.ts.out
+++ b/cli/tests/testdata/error_005_missing_dynamic_import.ts.out
@@ -1,4 +1,4 @@
-error: Uncaught (in promise) TypeError: Cannot resolve module "[WILDCARD]/bad-module.ts".
+error: Uncaught (in promise) TypeError: Cannot load module "[WILDCARD]/bad-module.ts".
const _badModule = await import("./bad-module.ts");
^
at async file://[WILDCARD]/error_005_missing_dynamic_import.ts:2:22
diff --git a/cli/tests/testdata/error_006_import_ext_failure.ts.out b/cli/tests/testdata/error_006_import_ext_failure.ts.out
index 24d819e5d..bffbafc7a 100644
--- a/cli/tests/testdata/error_006_import_ext_failure.ts.out
+++ b/cli/tests/testdata/error_006_import_ext_failure.ts.out
@@ -1,2 +1,2 @@
-[WILDCARD]error: Cannot resolve module "[WILDCARD]/non-existent" from "[WILDCARD]/error_006_import_ext_failure.ts".
- at file:///[WILDCARD]/error_006_import_ext_failure.ts:1:0
+[WILDCARD]error: Cannot load module "[WILDCARD]/non-existent".
+ at file:///[WILDCARD]/error_006_import_ext_failure.ts:1:8
diff --git a/cli/tests/testdata/error_011_bad_module_specifier.ts.out b/cli/tests/testdata/error_011_bad_module_specifier.ts.out
index 713072191..9ee9fd4d1 100644
--- a/cli/tests/testdata/error_011_bad_module_specifier.ts.out
+++ b/cli/tests/testdata/error_011_bad_module_specifier.ts.out
@@ -1 +1,3 @@
[WILDCARD]error: Relative import path "bad-module.ts" not prefixed with / or ./ or ../ from "[WILDCARD]/error_011_bad_module_specifier.ts"
+ at [WILDCARD]/error_011_bad_module_specifier.ts:1:28
+
diff --git a/cli/tests/testdata/error_012_bad_dynamic_import_specifier.ts.out b/cli/tests/testdata/error_012_bad_dynamic_import_specifier.ts.out
index 0d0b168a4..7ea32f682 100644
--- a/cli/tests/testdata/error_012_bad_dynamic_import_specifier.ts.out
+++ b/cli/tests/testdata/error_012_bad_dynamic_import_specifier.ts.out
@@ -1,5 +1,7 @@
Check [WILDCARD]error_012_bad_dynamic_import_specifier.ts
error: Uncaught (in promise) TypeError: Relative import path "bad-module.ts" not prefixed with / or ./ or ../ from "[WILDCARD]/error_012_bad_dynamic_import_specifier.ts"
+ at [WILDCARD]/error_012_bad_dynamic_import_specifier.ts:2:35
+
const _badModule = await import("bad-module.ts");
^
- at async file:///[WILDCARD]/error_012_bad_dynamic_import_specifier.ts:2:22
+ at async [WILDCARD]/error_012_bad_dynamic_import_specifier.ts:2:22
diff --git a/cli/tests/testdata/error_013_missing_script.out b/cli/tests/testdata/error_013_missing_script.out
index d1c257bdb..fdd7aa27e 100644
--- a/cli/tests/testdata/error_013_missing_script.out
+++ b/cli/tests/testdata/error_013_missing_script.out
@@ -1 +1 @@
-error: Cannot resolve module "[WILDCARD]missing_file_name".
+error: Cannot load module "[WILDCARD]missing_file_name".
diff --git a/cli/tests/testdata/error_014_catch_dynamic_import_error.js.out b/cli/tests/testdata/error_014_catch_dynamic_import_error.js.out
index 8f2d695f7..b19d87515 100644
--- a/cli/tests/testdata/error_014_catch_dynamic_import_error.js.out
+++ b/cli/tests/testdata/error_014_catch_dynamic_import_error.js.out
@@ -1,12 +1,16 @@
Caught direct dynamic import error.
TypeError: Relative import path "does not exist" not prefixed with / or ./ or ../ from "[WILDCARD]/error_014_catch_dynamic_import_error.js"
- at async file:///[WILDCARD]/error_014_catch_dynamic_import_error.js:3:5
+ at [WILDCARD]/error_014_catch_dynamic_import_error.js:3:18
+
+ at async [WILDCARD]/error_014_catch_dynamic_import_error.js:3:5
Caught indirect direct dynamic import error.
-TypeError: Relative import path "does not exist either" not prefixed with / or ./ or ../ from "[WILDCARD]/indirect_import_error.js"
- at async file:///[WILDCARD]/error_014_catch_dynamic_import_error.js:10:5
+TypeError: Relative import path "does not exist either" not prefixed with / or ./ or ../ from "[WILDCARD]/subdir/indirect_import_error.js"
+ at [WILDCARD]/subdir/indirect_import_error.js:1:15
+
+ at async [WILDCARD]/error_014_catch_dynamic_import_error.js:10:5
Caught error thrown by dynamically imported module.
Error: An error
- at file:///[WILDCARD]/subdir/throws.js:6:7
+ at [WILDCARD]/subdir/throws.js:6:7
Caught error thrown indirectly by dynamically imported module.
Error: An error
- at file:///[WILDCARD]/subdir/throws.js:6:7
+ at [WILDCARD]/subdir/throws.js:6:7
diff --git a/cli/tests/testdata/error_015_dynamic_import_permissions.out b/cli/tests/testdata/error_015_dynamic_import_permissions.out
index a509cfdab..42fb0a15d 100644
--- a/cli/tests/testdata/error_015_dynamic_import_permissions.out
+++ b/cli/tests/testdata/error_015_dynamic_import_permissions.out
@@ -1,4 +1,5 @@
error: Uncaught (in promise) TypeError: Requires net access to "localhost:4545", run again with the --allow-net flag
+ at file://[WILDCARD]/error_015_dynamic_import_permissions.js:2:16
await import("http://localhost:4545/subdir/mod4.js");
^
- at async file:///[WILDCARD]/error_015_dynamic_import_permissions.js:2:3
+ at async file://[WILDCARD]/error_015_dynamic_import_permissions.js:2:3
diff --git a/cli/tests/testdata/error_016_dynamic_import_permissions2.out b/cli/tests/testdata/error_016_dynamic_import_permissions2.out
index 0b43c5b91..bdfddb9ed 100644
--- a/cli/tests/testdata/error_016_dynamic_import_permissions2.out
+++ b/cli/tests/testdata/error_016_dynamic_import_permissions2.out
@@ -1,4 +1,8 @@
[WILDCARD]
-error: Remote modules are not allowed to import local modules. Consider using a dynamic import instead.
+error: Uncaught (in promise) TypeError: Remote modules are not allowed to import local modules. Consider using a dynamic import instead.
Importing: file:///c:/etc/passwd
- at http://localhost:4545/subdir/evil_remote_import.js:3:0
+ at http://localhost:4545/subdir/evil_remote_import.js:3:15
+
+ await import("http://localhost:4545/subdir/evil_remote_import.js");
+ ^
+ at async file://[WILDCARD]/error_016_dynamic_import_permissions2.js:4:3
diff --git a/cli/tests/testdata/error_local_static_import_from_remote.js.out b/cli/tests/testdata/error_local_static_import_from_remote.js.out
index 44b676532..8904bae29 100644
--- a/cli/tests/testdata/error_local_static_import_from_remote.js.out
+++ b/cli/tests/testdata/error_local_static_import_from_remote.js.out
@@ -1,4 +1,5 @@
[WILDCARD]
-error: Remote modules are not allowed to import local modules. Consider using a dynamic import instead.
+error: Remote modules are not allowed to import local modules. Consider using a dynamic import instead.
Importing: file:///some/dir/file.js
- at http://localhost:4545/error_local_static_import_from_remote.js:1:0
+ at http://localhost:4545/error_local_static_import_from_remote.js:1:8
+
diff --git a/cli/tests/testdata/error_local_static_import_from_remote.ts.out b/cli/tests/testdata/error_local_static_import_from_remote.ts.out
index f8f409de0..b06a12454 100644
--- a/cli/tests/testdata/error_local_static_import_from_remote.ts.out
+++ b/cli/tests/testdata/error_local_static_import_from_remote.ts.out
@@ -1,4 +1,5 @@
[WILDCARD]
-error: Remote modules are not allowed to import local modules. Consider using a dynamic import instead.
+error: Remote modules are not allowed to import local modules. Consider using a dynamic import instead.
Importing: file:///some/dir/file.ts
- at http://localhost:4545/error_local_static_import_from_remote.ts:1:0
+ at http://localhost:4545/error_local_static_import_from_remote.ts:1:8
+
diff --git a/cli/tests/testdata/error_missing_module_named_import.ts.out b/cli/tests/testdata/error_missing_module_named_import.ts.out
index 6f09f0c0d..9569dd9a9 100644
--- a/cli/tests/testdata/error_missing_module_named_import.ts.out
+++ b/cli/tests/testdata/error_missing_module_named_import.ts.out
@@ -1,2 +1,3 @@
-error: Cannot resolve module "[WILDCARD]/does_not_exist.js" from "[WILDCARD]/error_missing_module_named_import.ts".
- at [WILDCARD]/error_missing_module_named_import.ts:1:0
+[WILDCARD]
+error: Cannot load module "file://[WILDCARD]/does_not_exist.js".
+ at file://[WILDCARD]/error_missing_module_named_import.ts:1:19
diff --git a/cli/tests/testdata/error_syntax.js.out b/cli/tests/testdata/error_syntax.js.out
index 84f924f8f..6d3f05b8e 100644
--- a/cli/tests/testdata/error_syntax.js.out
+++ b/cli/tests/testdata/error_syntax.js.out
@@ -1 +1 @@
-error: Expected ,, got following at [WILDCARD]/error_syntax.js:3:6
+error: The module's source code could not be parsed: Expected ',', got 'following' at [WILDCARD]/error_syntax.js:3:6
diff --git a/cli/tests/testdata/error_syntax_empty_trailing_line.mjs.out b/cli/tests/testdata/error_syntax_empty_trailing_line.mjs.out
index 8b4feeb49..f4bbc6708 100644
--- a/cli/tests/testdata/error_syntax_empty_trailing_line.mjs.out
+++ b/cli/tests/testdata/error_syntax_empty_trailing_line.mjs.out
@@ -1 +1 @@
-error: Unexpected eof at [WILDCARD]/error_syntax_empty_trailing_line.mjs:2:22
+error: The module's source code could not be parsed: Unexpected eof at [WILDCARD]/error_syntax_empty_trailing_line.mjs:2:22
diff --git a/cli/tests/testdata/import_blob_url_import_relative.ts.out b/cli/tests/testdata/import_blob_url_import_relative.ts.out
index 77f399763..75e5fe811 100644
--- a/cli/tests/testdata/import_blob_url_import_relative.ts.out
+++ b/cli/tests/testdata/import_blob_url_import_relative.ts.out
@@ -1,4 +1,6 @@
error: Uncaught (in promise) TypeError: invalid URL: relative URL with a cannot-be-a-base base
+ at blob:null/[WILDCARD]:1:19
+
const a = await import(url);
^
at async file://[WILDCARD]/import_blob_url_import_relative.ts:6:11
diff --git a/cli/tests/testdata/import_data_url_import_relative.ts.out b/cli/tests/testdata/import_data_url_import_relative.ts.out
index 1f4f3e9ef..ed1a37208 100644
--- a/cli/tests/testdata/import_data_url_import_relative.ts.out
+++ b/cli/tests/testdata/import_data_url_import_relative.ts.out
@@ -1,4 +1,3 @@
error: invalid URL: relative URL with a cannot-be-a-base base
+ at data:application/javascript;base64,ZXhwb3J0IHsgYSB9IGZyb20gIi4vYS50cyI7Cg==:1:19
-Caused by:
- relative URL with a cannot-be-a-base base
diff --git a/cli/tests/testdata/info_missing_module.out b/cli/tests/testdata/info_missing_module.out
index 75b077407..07f893eec 100644
--- a/cli/tests/testdata/info_missing_module.out
+++ b/cli/tests/testdata/info_missing_module.out
@@ -1,6 +1,6 @@
local: [WILDCARD]error_009_missing_js_module.js
type: JavaScript
-dependencies: 1 unique (total 26B)
+dependencies: 0 unique (total 26B)
file://[WILDCARD]/error_009_missing_js_module.js (26B)
-└── file://[WILDCARD]/bad-module.js (error)
+└── file://[WILDCARD]/bad-module.js (missing)
diff --git a/cli/tests/testdata/lint/expected_json.out b/cli/tests/testdata/lint/expected_json.out
index 9af79ce3d..876aec9e0 100644
--- a/cli/tests/testdata/lint/expected_json.out
+++ b/cli/tests/testdata/lint/expected_json.out
@@ -58,7 +58,7 @@
"errors": [
{
"file_path": "[WILDCARD]malformed.js",
- "message": "Expected }, got <eof> at [WILDCARD]malformed.js:4:16"
+ "message": "Expected '}', got '<eof>' at [WILDCARD]malformed.js:4:16"
}
]
}
diff --git a/cli/tests/testdata/localhost_unsafe_ssl.ts.out b/cli/tests/testdata/localhost_unsafe_ssl.ts.out
index c482bd81c..b3f895d6e 100644
--- a/cli/tests/testdata/localhost_unsafe_ssl.ts.out
+++ b/cli/tests/testdata/localhost_unsafe_ssl.ts.out
@@ -1,3 +1,3 @@
DANGER: TLS certificate validation is disabled for: deno.land
error: error sending request for url (https://localhost:5545/subdir/mod2.ts): error trying to connect: invalid certificate: UnknownIssuer
- at [WILDCARD]/cafile_url_imports.ts:1:0
+ at file://[WILDCARD]/cafile_url_imports.ts:1:28
diff --git a/cli/tests/testdata/lock_check_err_with_bundle.out b/cli/tests/testdata/lock_check_err_with_bundle.out
index a47cbc606..a20e9b3af 100644
--- a/cli/tests/testdata/lock_check_err_with_bundle.out
+++ b/cli/tests/testdata/lock_check_err_with_bundle.out
@@ -1,4 +1,4 @@
[WILDCARD]
-The source code is invalid, as it does not match the expected hash in the lock file.
+error: The source code is invalid, as it does not match the expected hash in the lock file.
Specifier: http://127.0.0.1:4545/subdir/subdir2/mod2.ts
Lock file: lock_check_err_with_bundle.json
diff --git a/cli/tests/testdata/lock_dynamic_imports.out b/cli/tests/testdata/lock_dynamic_imports.out
index e5c76c8f0..601d2282b 100644
--- a/cli/tests/testdata/lock_dynamic_imports.out
+++ b/cli/tests/testdata/lock_dynamic_imports.out
@@ -1,4 +1,4 @@
[WILDCARD]
-The source code is invalid, as it does not match the expected hash in the lock file.
+error: The source code is invalid, as it does not match the expected hash in the lock file.
Specifier: http://127.0.0.1:4545/subdir/subdir2/mod2.ts
Lock file: lock_dynamic_imports.json
diff --git a/cli/tests/testdata/swc_syntax_error.ts.out b/cli/tests/testdata/swc_syntax_error.ts.out
index 98a753459..c968db5e9 100644
--- a/cli/tests/testdata/swc_syntax_error.ts.out
+++ b/cli/tests/testdata/swc_syntax_error.ts.out
@@ -1 +1 @@
-error: Unexpected token `}`. Expected an identifier, void, yield, null, await, break, a string literal, a numeric literal, true, false, `, -, import, this, typeof, {, [, ( at [WILDCARD]syntax_error.ts:4:1
+error: The module's source code could not be parsed: Unexpected token `}`. Expected an identifier, void, yield, null, await, break, a string literal, a numeric literal, true, false, `, -, import, this, typeof, {, [, ( at [WILDCARD]syntax_error.ts:4:1
diff --git a/cli/tests/testdata/ts_decorators_bundle.out b/cli/tests/testdata/ts_decorators_bundle.out
index a5b77b7bf..98f3733bf 100644
--- a/cli/tests/testdata/ts_decorators_bundle.out
+++ b/cli/tests/testdata/ts_decorators_bundle.out
@@ -1,5 +1,5 @@
[WILDCARD]
function _applyDecoratedDescriptor(target, property, decorators, descriptor, context) {
[WILDCARD]
-new SomeClass().test();
+new SomeClass[WILDCARD].test();
[WILDCARD] \ No newline at end of file
diff --git a/cli/tests/testdata/ts_type_only_import.ts.out b/cli/tests/testdata/ts_type_only_import.ts.out
index f808ed21a..9304a987e 100644
--- a/cli/tests/testdata/ts_type_only_import.ts.out
+++ b/cli/tests/testdata/ts_type_only_import.ts.out
@@ -1,4 +1,4 @@
-Check [WILDCARD]ts_type_only_import.ts
-warning: Compiled module not found "[WILDCARD]ts_type_only_import.d.ts"
- From: [WILDCARD]ts_type_only_import.ts
- If the source module contains only types, use `import type` and `export type` to import it instead.
+Check file://[WILDCARD]/ts_type_only_import.ts
+warning: Cannot load module "file://[WILDCARD]/ts_type_only_import.d.ts".
+ at file://[WILDCARD]/ts_type_only_import.ts:1:15
+ If the source module contains only types, use `import type` and `export type` to import it instead.
diff --git a/cli/tests/testdata/workers/nonexistent_worker.out b/cli/tests/testdata/workers/nonexistent_worker.out
index 99e94bfc9..5280e22d1 100644
--- a/cli/tests/testdata/workers/nonexistent_worker.out
+++ b/cli/tests/testdata/workers/nonexistent_worker.out
@@ -1,3 +1,3 @@
-[WILDCARD]error: Uncaught (in worker "") Cannot resolve module "file:///[WILDCARD]/workers/doesnt_exist.js".
+[WILDCARD]error: Uncaught (in worker "") Cannot load module "file:///[WILDCARD]/workers/doesnt_exist.js".
error: Uncaught (in promise) Error: Unhandled error event in child worker.
at Worker.#pollControl ([WILDCARD])
diff --git a/cli/tests/testdata/workers/permissions_blob_local.ts.out b/cli/tests/testdata/workers/permissions_blob_local.ts.out
index b35d1d4db..ee19c7ab5 100644
--- a/cli/tests/testdata/workers/permissions_blob_local.ts.out
+++ b/cli/tests/testdata/workers/permissions_blob_local.ts.out
@@ -1,4 +1,4 @@
error: Uncaught (in worker "") Requires read access to "[WILDCARD]local_file.ts", run again with the --allow-read flag
- at blob:null/[WILDCARD]:1:0
+ at blob:null/[WILDCARD]:1:8
error: Uncaught (in promise) Error: Unhandled error event in child worker.
at Worker.#pollControl ([WILDCARD])
diff --git a/cli/tests/testdata/workers/permissions_blob_remote.ts.out b/cli/tests/testdata/workers/permissions_blob_remote.ts.out
index 26936971a..597e5bf1e 100644
--- a/cli/tests/testdata/workers/permissions_blob_remote.ts.out
+++ b/cli/tests/testdata/workers/permissions_blob_remote.ts.out
@@ -1,4 +1,4 @@
error: Uncaught (in worker "") Requires net access to "example.com", run again with the --allow-net flag
- at blob:null/[WILDCARD]:1:0
+ at blob:null/[WILDCARD]:1:8
error: Uncaught (in promise) Error: Unhandled error event in child worker.
at Worker.#pollControl ([WILDCARD])
diff --git a/cli/tests/testdata/workers/permissions_data_local.ts.out b/cli/tests/testdata/workers/permissions_data_local.ts.out
index 1e8c5fffa..5c9bcf1c1 100644
--- a/cli/tests/testdata/workers/permissions_data_local.ts.out
+++ b/cli/tests/testdata/workers/permissions_data_local.ts.out
@@ -1,4 +1,4 @@
error: Uncaught (in worker "") Requires read access to "[WILDCARD]local_file.ts", run again with the --allow-read flag
- at data:application/javascript;base64,[WILDCARD]:1:0
+ at data:application/javascript;base64,[WILDCARD]:1:8
error: Uncaught (in promise) Error: Unhandled error event in child worker.
at Worker.#pollControl ([WILDCARD])
diff --git a/cli/tests/testdata/workers/permissions_data_remote.ts.out b/cli/tests/testdata/workers/permissions_data_remote.ts.out
index 92eadf284..0273664bd 100644
--- a/cli/tests/testdata/workers/permissions_data_remote.ts.out
+++ b/cli/tests/testdata/workers/permissions_data_remote.ts.out
@@ -1,4 +1,4 @@
error: Uncaught (in worker "") Requires net access to "example.com", run again with the --allow-net flag
- at data:application/javascript;base64,aW1wb3J0ICJodHRwczovL2V4YW1wbGUuY29tL3NvbWUvZmlsZS50cyI7:1:0
+ at data:application/javascript;base64,aW1wb3J0ICJodHRwczovL2V4YW1wbGUuY29tL3NvbWUvZmlsZS50cyI7:1:8
error: Uncaught (in promise) Error: Unhandled error event in child worker.
at Worker.#pollControl ([WILDCARD])
diff --git a/cli/tests/testdata/workers/permissions_dynamic_remote.ts.out b/cli/tests/testdata/workers/permissions_dynamic_remote.ts.out
index e5015abff..57a922391 100644
--- a/cli/tests/testdata/workers/permissions_dynamic_remote.ts.out
+++ b/cli/tests/testdata/workers/permissions_dynamic_remote.ts.out
@@ -1,4 +1,5 @@
error: Uncaught (in worker "") (in promise) TypeError: Requires net access to "example.com", run again with the --allow-net flag
+ at http://localhost:4545/workers/dynamic_remote.ts:2:14
await import("https://example.com/some/file.ts");
^
at async http://localhost:4545/workers/dynamic_remote.ts:2:1
diff --git a/cli/tests/testdata/workers/permissions_remote_remote.ts.out b/cli/tests/testdata/workers/permissions_remote_remote.ts.out
index 8998891a3..42602cf71 100644
--- a/cli/tests/testdata/workers/permissions_remote_remote.ts.out
+++ b/cli/tests/testdata/workers/permissions_remote_remote.ts.out
@@ -1,4 +1,4 @@
error: Uncaught (in worker "") Requires net access to "example.com", run again with the --allow-net flag
- at http://localhost:4545/workers/static_remote.ts:2:0
+ at http://localhost:4545/workers/static_remote.ts:2:8
error: Uncaught (in promise) Error: Unhandled error event in child worker.
at Worker.#pollControl ([WILDCARD])
diff --git a/cli/tools/coverage.rs b/cli/tools/coverage.rs
index 60efe789c..c34ba22cc 100644
--- a/cli/tools/coverage.rs
+++ b/cli/tools/coverage.rs
@@ -1,11 +1,12 @@
// Copyright 2018-2021 the Deno authors. All rights reserved. MIT license.
use crate::colors;
+use crate::emit;
use crate::flags::Flags;
use crate::fs_util::collect_files;
-use crate::module_graph::TypeLib;
use crate::proc_state::ProcState;
use crate::source_maps::SourceMapGetter;
+
use deno_ast::swc::common::Span;
use deno_ast::MediaType;
use deno_core::error::AnyError;
@@ -687,16 +688,15 @@ pub async fn cover_files(
let module_specifier =
deno_core::resolve_url_or_path(&script_coverage.url)?;
ps.prepare_module_load(
- module_specifier.clone(),
- TypeLib::UnstableDenoWindow,
+ vec![module_specifier.clone()],
+ false,
+ emit::TypeLib::UnstableDenoWindow,
Permissions::allow_all(),
Permissions::allow_all(),
- false,
- ps.maybe_import_map.clone(),
)
.await?;
- let module_source = ps.load(module_specifier.clone(), None)?;
+ let module_source = ps.load(module_specifier.clone(), None, false)?;
let script_source = &module_source.code;
let maybe_source_map = ps.get_source_map(&script_coverage.url);
diff --git a/cli/tools/doc.rs b/cli/tools/doc.rs
index 1711c5ccd..aa9d913a0 100644
--- a/cli/tools/doc.rs
+++ b/cli/tools/doc.rs
@@ -106,7 +106,9 @@ pub async fn print_docs(
let source_file_specifier =
ModuleSpecifier::parse("deno://lib.deno.d.ts").unwrap();
let graph = create_graph(
- source_file_specifier.clone(),
+ vec![source_file_specifier.clone()],
+ false,
+ None,
&mut loader,
None,
None,
@@ -142,7 +144,9 @@ pub async fn print_docs(
import_map: ps.maybe_import_map.clone(),
};
let graph = create_graph(
- root_specifier.clone(),
+ vec![root_specifier.clone()],
+ false,
+ None,
&mut loader,
Some(&resolver),
None,
diff --git a/cli/tools/test.rs b/cli/tools/test.rs
index 8d005b101..e14b5cc8b 100644
--- a/cli/tools/test.rs
+++ b/cli/tools/test.rs
@@ -1,8 +1,11 @@
// Copyright 2018-2021 the Deno authors. All rights reserved. MIT license.
use crate::ast::Location;
+use crate::cache;
+use crate::cache::CacherLoader;
use crate::colors;
use crate::create_main_worker;
+use crate::emit;
use crate::file_fetcher::File;
use crate::file_watcher;
use crate::file_watcher::ResolutionResult;
@@ -11,15 +14,13 @@ use crate::fs_util::collect_specifiers;
use crate::fs_util::is_supported_test_ext;
use crate::fs_util::is_supported_test_path;
use crate::located_script_name;
-use crate::module_graph;
-use crate::module_graph::GraphBuilder;
-use crate::module_graph::Module;
-use crate::module_graph::TypeLib;
+use crate::lockfile;
use crate::ops;
use crate::proc_state::ProcState;
+use crate::resolver::ImportMapResolver;
use crate::tokio_util;
use crate::tools::coverage::CoverageCollector;
-use crate::FetchHandler;
+
use deno_ast::swc::common::comments::CommentKind;
use deno_ast::MediaType;
use deno_core::error::generic_error;
@@ -28,7 +29,6 @@ use deno_core::futures::future;
use deno_core::futures::stream;
use deno_core::futures::FutureExt;
use deno_core::futures::StreamExt;
-use deno_core::parking_lot::Mutex;
use deno_core::serde_json::json;
use deno_core::JsRuntime;
use deno_core::ModuleSpecifier;
@@ -500,7 +500,7 @@ async fn check_specifiers(
ps: ProcState,
permissions: Permissions,
specifiers: Vec<(ModuleSpecifier, TestMode)>,
- lib: TypeLib,
+ lib: emit::TypeLib,
) -> Result<(), AnyError> {
let inline_files = fetch_inline_files(
ps.clone(),
@@ -527,12 +527,12 @@ async fn check_specifiers(
ps.file_fetcher.insert_cached(file);
}
- ps.prepare_module_graph(
+ ps.prepare_module_load(
specifiers,
+ false,
lib.clone(),
Permissions::allow_all(),
permissions.clone(),
- ps.maybe_import_map.clone(),
)
.await?;
}
@@ -548,12 +548,12 @@ async fn check_specifiers(
})
.collect();
- ps.prepare_module_graph(
+ ps.prepare_module_load(
module_specifiers,
+ false,
lib,
Permissions::allow_all(),
permissions,
- ps.maybe_import_map.clone(),
)
.await?;
@@ -743,11 +743,14 @@ fn collect_specifiers_with_test_mode(
Ok(specifiers_with_mode)
}
-/// Collects module and document specifiers with test modes via `collect_specifiers_with_test_mode`
-/// which are then pre-fetched and adjusted based on the media type.
+/// Collects module and document specifiers with test modes via
+/// `collect_specifiers_with_test_mode` which are then pre-fetched and adjusted
+/// based on the media type.
///
-/// Specifiers that do not have a known media type that can be executed as a module are marked as
-/// `TestMode::Documentation`.
+/// Specifiers that do not have a known media type that can be executed as a
+/// module are marked as `TestMode::Documentation`. Type definition files
+/// cannot be run, and therefore need to be marked as `TestMode::Documentation`
+/// as well.
async fn fetch_specifiers_with_test_mode(
ps: ProcState,
include: Vec<String>,
@@ -762,7 +765,9 @@ async fn fetch_specifiers_with_test_mode(
.fetch(specifier, &mut Permissions::allow_all())
.await?;
- if file.media_type == MediaType::Unknown {
+ if file.media_type == MediaType::Unknown
+ || file.media_type == MediaType::Dts
+ {
*mode = TestMode::Documentation
}
}
@@ -798,9 +803,9 @@ pub async fn run_tests(
}
let lib = if flags.unstable {
- TypeLib::UnstableDenoWindow
+ emit::TypeLib::UnstableDenoWindow
} else {
- TypeLib::DenoWindow
+ emit::TypeLib::DenoWindow
};
check_specifiers(
@@ -845,26 +850,33 @@ pub async fn run_tests_with_watch(
let permissions = Permissions::from_options(&flags.clone().into());
let lib = if flags.unstable {
- TypeLib::UnstableDenoWindow
+ emit::TypeLib::UnstableDenoWindow
} else {
- TypeLib::DenoWindow
+ emit::TypeLib::DenoWindow
};
- let handler = Arc::new(Mutex::new(FetchHandler::new(
- &ps,
- Permissions::allow_all(),
- Permissions::allow_all(),
- )?));
-
let include = include.unwrap_or_else(|| vec![".".to_string()]);
let paths_to_watch: Vec<_> = include.iter().map(PathBuf::from).collect();
let resolver = |changed: Option<Vec<PathBuf>>| {
+ let mut cache = cache::FetchCacher::new(
+ ps.dir.gen_cache.clone(),
+ ps.file_fetcher.clone(),
+ Permissions::allow_all(),
+ Permissions::allow_all(),
+ );
+
let paths_to_watch = paths_to_watch.clone();
let paths_to_watch_clone = paths_to_watch.clone();
- let handler = handler.clone();
- let ps = ps.clone();
+ let maybe_resolver =
+ ps.maybe_import_map.as_ref().map(ImportMapResolver::new);
+ let maybe_locker = lockfile::as_maybe_locker(ps.lockfile.clone());
+ let maybe_imports = ps
+ .maybe_config_file
+ .as_ref()
+ .map(|cf| cf.to_maybe_imports())
+ .flatten();
let files_changed = changed.is_some();
let include = include.clone();
let ignore = ignore.clone();
@@ -886,61 +898,51 @@ pub async fn run_tests_with_watch(
.collect()
};
- let mut builder = GraphBuilder::new(
- handler,
- ps.maybe_import_map.clone(),
- ps.lockfile.clone(),
- );
- for specifier in test_modules.iter() {
- builder.add(specifier, false).await?;
- }
- builder.analyze_config_file(&ps.maybe_config_file).await?;
- let graph = builder.get_graph();
+ let graph = deno_graph::create_graph(
+ test_modules.clone(),
+ false,
+ maybe_imports,
+ cache.as_mut_loader(),
+ maybe_resolver.as_ref().map(|r| r.as_resolver()),
+ maybe_locker,
+ None,
+ )
+ .await;
+ graph.valid()?;
+ // TODO(@kitsonk) - This should be totally derivable from the graph.
for specifier in test_modules {
fn get_dependencies<'a>(
- graph: &'a module_graph::Graph,
- module: &'a Module,
+ graph: &'a deno_graph::ModuleGraph,
+ maybe_module: Option<&'a deno_graph::Module>,
// This needs to be accessible to skip getting dependencies if they're already there,
// otherwise this will cause a stack overflow with circular dependencies
output: &mut HashSet<&'a ModuleSpecifier>,
- ) -> Result<(), AnyError> {
- for dep in module.dependencies.values() {
- if let Some(specifier) = &dep.maybe_code {
- if !output.contains(specifier) {
- output.insert(specifier);
-
- get_dependencies(
- graph,
- graph.get_specifier(specifier)?,
- output,
- )?;
+ ) {
+ if let Some(module) = maybe_module {
+ for dep in module.dependencies.values() {
+ if let Some(specifier) = &dep.get_code() {
+ if !output.contains(specifier) {
+ output.insert(specifier);
+
+ get_dependencies(graph, graph.get(specifier), output);
+ }
}
- }
- if let Some(specifier) = &dep.maybe_type {
- if !output.contains(specifier) {
- output.insert(specifier);
-
- get_dependencies(
- graph,
- graph.get_specifier(specifier)?,
- output,
- )?;
+ if let Some(specifier) = &dep.get_type() {
+ if !output.contains(specifier) {
+ output.insert(specifier);
+
+ get_dependencies(graph, graph.get(specifier), output);
+ }
}
}
}
-
- Ok(())
}
// This test module and all it's dependencies
let mut modules = HashSet::new();
modules.insert(&specifier);
- get_dependencies(
- &graph,
- graph.get_specifier(&specifier)?,
- &mut modules,
- )?;
+ get_dependencies(&graph, graph.get(&specifier), &mut modules);
paths_to_watch.extend(
modules
diff --git a/cli/tsc.rs b/cli/tsc.rs
index bfaaae971..922bee6d7 100644
--- a/cli/tsc.rs
+++ b/cli/tsc.rs
@@ -2,17 +2,14 @@
use crate::config_file::TsConfig;
use crate::diagnostics::Diagnostics;
-use crate::module_graph::Graph;
-use crate::module_graph::Stats;
+use crate::emit;
use deno_ast::MediaType;
use deno_core::error::anyhow;
-use deno_core::error::bail;
use deno_core::error::AnyError;
use deno_core::error::Context;
use deno_core::located_script_name;
use deno_core::op_sync;
-use deno_core::parking_lot::Mutex;
use deno_core::resolve_url_or_path;
use deno_core::serde::de;
use deno_core::serde::Deserialize;
@@ -25,6 +22,7 @@ use deno_core::ModuleSpecifier;
use deno_core::OpFn;
use deno_core::RuntimeOptions;
use deno_core::Snapshot;
+use deno_graph::ModuleGraph;
use std::collections::HashMap;
use std::path::PathBuf;
use std::sync::Arc;
@@ -93,7 +91,7 @@ pub fn get_asset(asset: &str) -> Option<&'static str> {
}
fn get_maybe_hash(
- maybe_source: &Option<String>,
+ maybe_source: Option<&String>,
hash_data: &[Vec<u8>],
) -> Option<String> {
if let Some(source) = maybe_source {
@@ -105,30 +103,15 @@ fn get_maybe_hash(
}
}
-fn hash_data_url(
- specifier: &ModuleSpecifier,
- media_type: &MediaType,
-) -> String {
- assert_eq!(
- specifier.scheme(),
- "data",
- "Specifier must be a data: specifier."
- );
+/// Hash the URL so it can be sent to `tsc` in a supportable way
+fn hash_url(specifier: &ModuleSpecifier, media_type: &MediaType) -> String {
let hash = crate::checksum::gen(&[specifier.path().as_bytes()]);
- format!("data:///{}{}", hash, media_type.as_ts_extension())
-}
-
-fn hash_blob_url(
- specifier: &ModuleSpecifier,
- media_type: &MediaType,
-) -> String {
- assert_eq!(
+ format!(
+ "{}:///{}{}",
specifier.scheme(),
- "blob",
- "Specifier must be a blob: specifier."
- );
- let hash = crate::checksum::gen(&[specifier.path().as_bytes()]);
- format!("blob:///{}{}", hash, media_type.as_ts_extension())
+ hash,
+ media_type.as_ts_extension()
+ )
}
/// tsc only supports `.ts`, `.tsx`, `.d.ts`, `.js`, or `.jsx` as root modules
@@ -180,7 +163,7 @@ pub struct Request {
pub config: TsConfig,
/// Indicates to the tsc runtime if debug logging should occur.
pub debug: bool,
- pub graph: Arc<Mutex<Graph>>,
+ pub graph: Arc<ModuleGraph>,
pub hash_data: Vec<Vec<u8>>,
pub maybe_config_specifier: Option<ModuleSpecifier>,
pub maybe_tsbuildinfo: Option<String>,
@@ -190,7 +173,7 @@ pub struct Request {
}
#[derive(Debug, Clone, Eq, PartialEq)]
-pub struct Response {
+pub(crate) struct Response {
/// Any diagnostics that have been returned from the checker.
pub diagnostics: Diagnostics,
/// Any files that were emitted during the check.
@@ -198,7 +181,7 @@ pub struct Response {
/// If there was any build info associated with the exec request.
pub maybe_tsbuildinfo: Option<String>,
/// Statistics from the check.
- pub stats: Stats,
+ pub stats: emit::Stats,
}
#[derive(Debug)]
@@ -206,7 +189,7 @@ struct State {
data_url_map: HashMap<String, ModuleSpecifier>,
hash_data: Vec<Vec<u8>>,
emitted_files: Vec<EmittedFile>,
- graph: Arc<Mutex<Graph>>,
+ graph: Arc<ModuleGraph>,
maybe_config_specifier: Option<ModuleSpecifier>,
maybe_tsbuildinfo: Option<String>,
maybe_response: Option<RespondArgs>,
@@ -215,7 +198,7 @@ struct State {
impl State {
pub fn new(
- graph: Arc<Mutex<Graph>>,
+ graph: Arc<ModuleGraph>,
hash_data: Vec<Vec<u8>>,
maybe_config_specifier: Option<ModuleSpecifier>,
maybe_tsbuildinfo: Option<String>,
@@ -334,8 +317,7 @@ fn op_exists(state: &mut State, args: ExistsArgs) -> Result<bool, AnyError> {
if specifier.scheme() == "asset" || specifier.scheme() == "data" {
Ok(true)
} else {
- let graph = state.graph.lock();
- Ok(graph.contains(&specifier))
+ Ok(state.graph.contains(&specifier))
}
} else {
Ok(false)
@@ -366,11 +348,10 @@ fn op_load(state: &mut State, args: Value) -> Result<Value, AnyError> {
} else if v.specifier.starts_with("asset:///") {
let name = v.specifier.replace("asset:///", "");
let maybe_source = get_asset(&name).map(String::from);
- hash = get_maybe_hash(&maybe_source, &state.hash_data);
+ hash = get_maybe_hash(maybe_source.as_ref(), &state.hash_data);
media_type = MediaType::from(&v.specifier);
maybe_source
} else {
- let graph = state.graph.lock();
let specifier = if let Some(data_specifier) =
state.data_url_map.get(&v.specifier)
{
@@ -380,13 +361,14 @@ fn op_load(state: &mut State, args: Value) -> Result<Value, AnyError> {
} else {
specifier
};
- let maybe_source = graph.get_source(&specifier).map(|t| t.to_string());
- media_type = if let Some(media_type) = graph.get_media_type(&specifier) {
- media_type
+ let maybe_source = if let Some(module) = state.graph.get(&specifier) {
+ media_type = module.media_type;
+ Some(module.source.as_str().to_string())
} else {
- MediaType::Unknown
+ media_type = MediaType::Unknown;
+ None
};
- hash = get_maybe_hash(&maybe_source, &state.hash_data);
+ hash = get_maybe_hash(maybe_source.as_ref(), &state.hash_data);
maybe_source
};
@@ -425,48 +407,31 @@ fn op_resolve(state: &mut State, args: Value) -> Result<Value, AnyError> {
MediaType::from(specifier).as_ts_extension().to_string(),
));
} else {
- let graph = state.graph.lock();
- match graph.resolve(specifier, &referrer, true) {
- Ok(resolved_specifier) => {
- let media_type = if let Some(media_type) =
- graph.get_media_type(&resolved_specifier)
- {
- media_type
- } else {
- bail!(
- "Unable to resolve media type for specifier: \"{}\"",
- resolved_specifier
- )
- };
- let resolved_specifier_str = match resolved_specifier.scheme() {
- "data" | "blob" => {
- let specifier_str = if resolved_specifier.scheme() == "data" {
- hash_data_url(&resolved_specifier, &media_type)
- } else {
- hash_blob_url(&resolved_specifier, &media_type)
- };
- state
- .data_url_map
- .insert(specifier_str.clone(), resolved_specifier);
- specifier_str
- }
- _ => resolved_specifier.to_string(),
- };
- resolved.push((
- resolved_specifier_str,
- media_type.as_ts_extension().into(),
- ));
- }
- // in certain situations, like certain dynamic imports, we won't have
- // the source file in the graph, so we will return a fake module to
- // make tsc happy.
- Err(_) => {
- resolved.push((
+ let resolved_dependency =
+ match state.graph.resolve_dependency(specifier, &referrer, true) {
+ Some(resolved_specifier) => {
+ let media_type = state
+ .graph
+ .get(resolved_specifier)
+ .map_or(&MediaType::Unknown, |m| &m.media_type);
+ let resolved_specifier_str = match resolved_specifier.scheme() {
+ "data" | "blob" => {
+ let specifier_str = hash_url(resolved_specifier, media_type);
+ state
+ .data_url_map
+ .insert(specifier_str.clone(), resolved_specifier.clone());
+ specifier_str
+ }
+ _ => resolved_specifier.to_string(),
+ };
+ (resolved_specifier_str, media_type.as_ts_extension().into())
+ }
+ None => (
"deno:///missing_dependency.d.ts".to_string(),
".d.ts".to_string(),
- ));
- }
- }
+ ),
+ };
+ resolved.push(resolved_dependency);
}
}
@@ -476,7 +441,7 @@ fn op_resolve(state: &mut State, args: Value) -> Result<Value, AnyError> {
#[derive(Debug, Deserialize, Eq, PartialEq)]
struct RespondArgs {
pub diagnostics: Diagnostics,
- pub stats: Stats,
+ pub stats: emit::Stats,
}
fn op_respond(state: &mut State, args: Value) -> Result<Value, AnyError> {
@@ -489,7 +454,7 @@ fn op_respond(state: &mut State, args: Value) -> Result<Value, AnyError> {
/// Execute a request on the supplied snapshot, returning a response which
/// contains information, like any emitted files, diagnostics, statistics and
/// optionally an updated TypeScript build info.
-pub fn exec(request: Request) -> Result<Response, AnyError> {
+pub(crate) fn exec(request: Request) -> Result<Response, AnyError> {
let mut runtime = JsRuntime::new(RuntimeOptions {
startup_snapshot: Some(compiler_snapshot()),
..Default::default()
@@ -505,11 +470,7 @@ pub fn exec(request: Request) -> Result<Response, AnyError> {
.iter()
.map(|(s, mt)| match s.scheme() {
"data" | "blob" => {
- let specifier_str = if s.scheme() == "data" {
- hash_data_url(s, mt)
- } else {
- hash_blob_url(s, mt)
- };
+ let specifier_str = hash_url(s, mt);
data_url_map.insert(specifier_str.clone(), s.clone());
specifier_str
}
@@ -530,7 +491,7 @@ pub fn exec(request: Request) -> Result<Response, AnyError> {
let op_state = runtime.op_state();
let mut op_state = op_state.borrow_mut();
op_state.put(State::new(
- request.graph.clone(),
+ request.graph,
request.hash_data.clone(),
request.maybe_config_specifier.clone(),
request.maybe_tsbuildinfo.clone(),
@@ -589,9 +550,39 @@ mod tests {
use crate::config_file::TsConfig;
use crate::diagnostics::Diagnostic;
use crate::diagnostics::DiagnosticCategory;
- use crate::module_graph::tests::MockSpecifierHandler;
- use crate::module_graph::GraphBuilder;
- use deno_core::parking_lot::Mutex;
+ use crate::emit::Stats;
+ use deno_core::futures::future;
+ use std::fs;
+
+ #[derive(Debug, Default)]
+ pub(crate) struct MockLoader {
+ pub fixtures: PathBuf,
+ }
+
+ impl deno_graph::source::Loader for MockLoader {
+ fn load(
+ &mut self,
+ specifier: &ModuleSpecifier,
+ _is_dynamic: bool,
+ ) -> deno_graph::source::LoadFuture {
+ let specifier_text = specifier
+ .to_string()
+ .replace(":///", "_")
+ .replace("://", "_")
+ .replace("/", "-");
+ let source_path = self.fixtures.join(specifier_text);
+ let response = fs::read_to_string(&source_path)
+ .map(|c| {
+ Some(deno_graph::source::LoadResponse {
+ specifier: specifier.clone(),
+ maybe_headers: None,
+ content: Arc::new(c),
+ })
+ })
+ .map_err(|err| err.into());
+ Box::pin(future::ready((specifier.clone(), response)))
+ }
+ }
async fn setup(
maybe_specifier: Option<ModuleSpecifier>,
@@ -602,16 +593,19 @@ mod tests {
.unwrap_or_else(|| resolve_url_or_path("file:///main.ts").unwrap());
let hash_data = maybe_hash_data.unwrap_or_else(|| vec![b"".to_vec()]);
let fixtures = test_util::testdata_path().join("tsc2");
- let handler = Arc::new(Mutex::new(MockSpecifierHandler {
- fixtures,
- ..MockSpecifierHandler::default()
- }));
- let mut builder = GraphBuilder::new(handler.clone(), None, None);
- builder
- .add(&specifier, false)
- .await
- .expect("module not inserted");
- let graph = Arc::new(Mutex::new(builder.get_graph()));
+ let mut loader = MockLoader { fixtures };
+ let graph = Arc::new(
+ deno_graph::create_graph(
+ vec![specifier],
+ false,
+ None,
+ &mut loader,
+ None,
+ None,
+ None,
+ )
+ .await,
+ );
State::new(
graph,
hash_data,
@@ -627,13 +621,19 @@ mod tests {
) -> Result<Response, AnyError> {
let hash_data = vec![b"something".to_vec()];
let fixtures = test_util::testdata_path().join("tsc2");
- let handler = Arc::new(Mutex::new(MockSpecifierHandler {
- fixtures,
- ..Default::default()
- }));
- let mut builder = GraphBuilder::new(handler.clone(), None, None);
- builder.add(specifier, false).await?;
- let graph = Arc::new(Mutex::new(builder.get_graph()));
+ let mut loader = MockLoader { fixtures };
+ let graph = Arc::new(
+ deno_graph::create_graph(
+ vec![specifier.clone()],
+ false,
+ None,
+ &mut loader,
+ None,
+ None,
+ None,
+ )
+ .await,
+ );
let config = TsConfig::new(json!({
"allowJs": true,
"checkJs": false,
@@ -695,12 +695,12 @@ mod tests {
}
#[test]
- fn test_hash_data_url() {
+ fn test_hash_url() {
let specifier = deno_core::resolve_url(
"data:application/javascript,console.log(\"Hello%20Deno\");",
)
.unwrap();
- assert_eq!(hash_data_url(&specifier, &MediaType::JavaScript), "data:///d300ea0796bd72b08df10348e0b70514c021f2e45bfe59cec24e12e97cd79c58.js");
+ assert_eq!(hash_url(&specifier, &MediaType::JavaScript), "data:///d300ea0796bd72b08df10348e0b70514c021f2e45bfe59cec24e12e97cd79c58.js");
}
#[test]