diff --git a/Cargo.lock b/Cargo.lock index 2c41d14697f..b037f853b86 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -56,9 +56,9 @@ dependencies = [ [[package]] name = "anyhow" -version = "1.0.56" +version = "1.0.57" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4361135be9122e0870de935d7c439aef945b9f9ddd4199a553b5270b49c82a27" +checksum = "08f9b8508dccb7687a1d6c4ce66b2b0ecef467c94667de27d8d7fe1f8d2a9cdc" [[package]] name = "arrayvec" @@ -150,9 +150,9 @@ dependencies = [ [[package]] name = "browserslist-rs" -version = "0.9.0" +version = "0.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a0f43be8e0fc9203f6ed7731d2a9a6bf5924cb78907e67e1fe9133617be402be" +checksum = "e55d9cadf66efd56338797ada06140423bd87f290eac200027265d79d621a266" dependencies = [ "ahash", "anyhow", @@ -430,23 +430,13 @@ dependencies = [ [[package]] name = "dashmap" -version = "4.0.2" +version = "5.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e77a43b28d0668df09411cb0bc9a8c2adc40f9a048afe863e05fd43251e8e39c" +checksum = "391b56fbd302e585b7a9494fb70e40949567b1cf9003a8e4a6041a1687c26573" dependencies = [ "cfg-if 1.0.0", - "num_cpus", -] - -[[package]] -name = "dashmap" -version = "5.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4c8858831f7781322e539ea39e72449c46b059638250c14344fec8d0aa6e539c" -dependencies = [ - "cfg-if 1.0.0", - "num_cpus", - "parking_lot", + "hashbrown 0.12.1", + "lock_api", ] [[package]] @@ -519,9 +509,9 @@ dependencies = [ [[package]] name = "filetime" -version = "0.2.15" +version = "0.2.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "975ccf83d8d9d0d84682850a38c8169027be83368805971cc4f238c2b245bc98" +checksum = "c0408e2626025178a6a7f7ffc05a25bc47103229f19c113755de7bf63816290c" dependencies = [ "cfg-if 1.0.0", "libc", @@ -596,6 +586,12 @@ version = "0.11.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ab5ef0d4909ef3724cc8cce6ccc8572c5c817592e9285f5464f8e86f8bd3726e" +[[package]] +name = "hashbrown" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "db0d4cf898abf0081f964436dc980e96670a0f36863e4b83aaacdb65c9d7ccc3" + [[package]] name = "hermit-abi" version = "0.1.19" @@ -650,18 +646,15 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0f647032dfaa1f8b6dc29bd3edb7bbef4861b8b8007ebb118d6db284fd59f6ee" dependencies = [ "autocfg", - "hashbrown", + "hashbrown 0.11.2", "rayon", ] [[package]] name = "indoc" -version = "1.0.4" +version = "1.0.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e7906a9fababaeacb774f72410e497a1d18de916322e33797bb2cd29baa23c9e" -dependencies = [ - "unindent", -] +checksum = "05a0bd019339e5d968b37855180087b7b9d512c5046fbd244cf8c95687927d6e" [[package]] name = "instant" @@ -820,9 +813,9 @@ dependencies = [ [[package]] name = "libc" -version = "0.2.122" +version = "0.2.125" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ec647867e2bf0772e28c8bcde4f0d19a9216916e890543b5a03ed8ef27b8f259" +checksum = "5916d2ae698f6de9bfb891ad7a8d65c09d232dc58cc4ac433c7da3b2fd84bc2b" [[package]] name = "libdeflate-sys" @@ -842,11 +835,21 @@ dependencies = [ "libdeflate-sys", ] +[[package]] +name = "libloading" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "efbc0f03f9a775e9f6aed295c6a1ba2253c5757a9e03d55c6caa46a681abcddd" +dependencies = [ + "cfg-if 1.0.0", + "winapi", +] + [[package]] name = "libmimalloc-sys" -version = "0.1.24" +version = "0.1.25" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7705fc40f6ed493f73584abbb324e74f96b358ff60dfe5659a0f8fc12c590a69" +checksum = "11ca136052550448f55df7898c6dbe651c6b574fe38a0d9ea687a9f8088a2e2c" dependencies = [ "cc", ] @@ -863,9 +866,9 @@ dependencies = [ [[package]] name = "log" -version = "0.4.16" +version = "0.4.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6389c490849ff5bc16be905ae24bc913a9c8892e19b2341dbc175e14c341c2b8" +checksum = "abb12e687cfb44aa40f41fc3978ef76448f9b6038cad6aef4259d3c095a2382e" dependencies = [ "cfg-if 1.0.0", ] @@ -878,9 +881,9 @@ checksum = "a3e378b66a060d48947b590737b30a1be76706c8dd7b8ba0f2fe3989c68a853f" [[package]] name = "memchr" -version = "2.4.1" +version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "308cc39be01b73d0d18f82a0e7b2a3df85245f84af96fdddc5d202d27e47b86a" +checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d" [[package]] name = "memoffset" @@ -893,9 +896,9 @@ dependencies = [ [[package]] name = "mimalloc" -version = "0.1.28" +version = "0.1.29" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b0dfa131390c2f6bdb3242f65ff271fcdaca5ff7b6c08f28398be7f2280e3926" +checksum = "2f64ad83c969af2e732e907564deb0d0ed393cec4af80776f77dd77a1a427698" dependencies = [ "libmimalloc-sys", ] @@ -938,16 +941,16 @@ dependencies = [ [[package]] name = "napi" -version = "2.3.3" +version = "2.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4c7806930f3d742fade085524e131dd4407c5bd68dff77086cf6b8ef300e0b12" +checksum = "eba1b04555247b935187ea1dddac523ebc470c45806501d7203ed1bec7fa26b2" dependencies = [ "ctor", "lazy_static", "napi-sys", "serde", "serde_json", - "windows", + "thread_local", ] [[package]] @@ -958,9 +961,9 @@ checksum = "ebd4419172727423cf30351406c54f6cc1b354a2cfb4f1dba3e6cd07f6d5522b" [[package]] name = "napi-derive" -version = "2.3.2" +version = "2.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "048a9a6127a77b541184324c622ddcbf15ca0af1557076a0993b304932347272" +checksum = "bffed314331505d8a4ce6831dfb5fff9bad37113344ff55824bd398eb3157a1d" dependencies = [ "convert_case", "napi-derive-backend", @@ -971,9 +974,9 @@ dependencies = [ [[package]] name = "napi-derive-backend" -version = "1.0.29" +version = "1.0.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5c4e5660e8013eb5027e6d8ece13c2aaec9674b2332c503e658bfff8c90b4acb" +checksum = "a5cab83da14f4202c1f3858dde5b68e1d8c5bb7ee804c5955911cbbe00b835b8" dependencies = [ "convert_case", "once_cell", @@ -985,9 +988,12 @@ dependencies = [ [[package]] name = "napi-sys" -version = "2.1.0" +version = "2.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8a385494dac3c52cbcacb393bb3b42669e7db8ab240c7ad5115f549eb061f2cc" +checksum = "529671ebfae679f2ce9630b62dd53c72c56b3eb8b2c852e7e2fa91704ff93d67" +dependencies = [ + "libloading", +] [[package]] name = "nasm-rs" @@ -1037,9 +1043,9 @@ dependencies = [ [[package]] name = "num-integer" -version = "0.1.44" +version = "0.1.45" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d2cc698a63b549a70bc047073d2949cce27cd1c7b0a4a862d08a8031bc2801db" +checksum = "225d3389fb3509a24c93f5c29eb6bde2586b98d9f016636dff58d7c6f7569cd9" dependencies = [ "autocfg", "num-traits", @@ -1047,9 +1053,9 @@ dependencies = [ [[package]] name = "num-iter" -version = "0.1.42" +version = "0.1.43" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b2021c8337a54d21aca0d59a92577a029af9431cb59b909b03252b9c164fad59" +checksum = "7d03e6c028c5dc5cac6e2dec0efda81fc887605bb3d884578bb6d6bf7514e252" dependencies = [ "autocfg", "num-integer", @@ -1069,9 +1075,9 @@ dependencies = [ [[package]] name = "num-traits" -version = "0.2.14" +version = "0.2.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9a64b1ec5cda2586e284722486d802acf1f7dbdc623e2bfc57e65ca1cd099290" +checksum = "578ede34cf02f8924ab9447f50c28075b4d3e5b269972345e7e0372b38c6cdcd" dependencies = [ "autocfg", ] @@ -1214,7 +1220,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "87f5ec2493a61ac0506c0f4199f99070cbe83857b0337006a30f3e6719b8ef58" dependencies = [ "lock_api", - "parking_lot_core 0.9.2", + "parking_lot_core 0.9.3", ] [[package]] @@ -1234,9 +1240,9 @@ dependencies = [ [[package]] name = "parking_lot_core" -version = "0.9.2" +version = "0.9.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "995f667a6c822200b0433ac218e05582f0e2efa1b922a3fd2fbaadc5f87bab37" +checksum = "09a279cbf25cb0757810394fbc1e359949b59e348145c643a939a525692e6929" dependencies = [ "cfg-if 1.0.0", "libc", @@ -1315,9 +1321,9 @@ dependencies = [ [[package]] name = "pin-project-lite" -version = "0.2.8" +version = "0.2.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e280fbe77cc62c91527259e9442153f4688736748d24660126286329742b4c6c" +checksum = "e0a7ae3ac2f1173085d398531c705756c94a4c56843785df85a60c1a0afac116" [[package]] name = "pmutil" @@ -1356,17 +1362,17 @@ checksum = "925383efa346730478fb4838dbe9137d2a47675ad789c546d150a6e1dd4ab31c" [[package]] name = "preset_env_base" -version = "0.2.1" +version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "44b10336bf81e96a223c487607acb08a1407d3e208a65e477190e3fe51fc5dea" +checksum = "3ef989b02dabee8f04ce1048e423a55b2a71a1ba68b9df31266170e6220b61ad" dependencies = [ "ahash", "anyhow", "browserslist-rs", - "dashmap 5.2.0", + "dashmap", "from_variant", "once_cell", - "semver 1.0.7", + "semver 1.0.9", "serde", "st-map", ] @@ -1379,18 +1385,18 @@ checksum = "dbf0c48bc1d91375ae5c3cd81e3722dff1abcf81a30960240640d223f59fe0e5" [[package]] name = "proc-macro2" -version = "1.0.37" +version = "1.0.38" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ec757218438d5fda206afc041538b2f6d889286160d649a86a24d37e1235afd1" +checksum = "9027b48e9d4c9175fa2218adf3557f91c1137021739951d4932f5f8268ac48aa" dependencies = [ "unicode-xid", ] [[package]] name = "quote" -version = "1.0.17" +version = "1.0.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "632d02bff7f874a36f33ea8bb416cd484b90cc66c1194b1a1110d067a7013f58" +checksum = "a1feb54ed693b93a84e14094943b84b7c4eae204c512b7ccb95ab0c66d278ad1" dependencies = [ "proc-macro2", ] @@ -1427,9 +1433,9 @@ dependencies = [ [[package]] name = "rayon" -version = "1.5.1" +version = "1.5.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c06aca804d41dbc8ba42dfd964f0d01334eceb64314b9ecf7c5fad5188a06d90" +checksum = "bd99e5772ead8baa5215278c9b15bf92087709e9c1b2d1f97cdb5a183c933a7d" dependencies = [ "autocfg", "crossbeam-deque", @@ -1439,14 +1445,13 @@ dependencies = [ [[package]] name = "rayon-core" -version = "1.9.1" +version = "1.9.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d78120e2c850279833f1dd3582f730c4ab53ed95aeaaaa862a2a5c71b1656d8e" +checksum = "258bcdb5ac6dad48491bb2992db6b7cf74878b0384908af124823d118c99683f" dependencies = [ "crossbeam-channel", "crossbeam-deque", "crossbeam-utils", - "lazy_static", "num_cpus", ] @@ -1512,7 +1517,7 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bfa0f585226d2e68097d4f95d113b15b83a82e819ab25717ec0590d9584ef366" dependencies = [ - "semver 1.0.7", + "semver 1.0.9", ] [[package]] @@ -1544,9 +1549,9 @@ dependencies = [ [[package]] name = "semver" -version = "1.0.7" +version = "1.0.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d65bd28f48be7196d222d95b9243287f48d27aca604e08497513019ff0502cc4" +checksum = "8cb243bdfdb5936c8dc3c45762a19d12ab4550cdc753bc247637d4ec35a040fd" dependencies = [ "serde", ] @@ -1559,9 +1564,9 @@ checksum = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3" [[package]] name = "serde" -version = "1.0.136" +version = "1.0.137" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ce31e24b01e1e524df96f1c2fdd054405f8d7376249a5110886fb4b658484789" +checksum = "61ea8d54c77f8315140a05f4c7237403bf38b72704d031543aa1d16abbf517d1" dependencies = [ "serde_derive", ] @@ -1580,18 +1585,18 @@ dependencies = [ [[package]] name = "serde_bytes" -version = "0.11.5" +version = "0.11.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "16ae07dd2f88a366f15bd0632ba725227018c69a1c8550a927324f8eb8368bb9" +checksum = "212e73464ebcde48d723aa02eb270ba62eff38a9b732df31f33f1b4e145f3a54" dependencies = [ "serde", ] [[package]] name = "serde_derive" -version = "1.0.136" +version = "1.0.137" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "08597e7152fcd306f41838ed3e37be9eaeed2b61c42e2117266a554fab4662f9" +checksum = "1f26faba0c3959972377d3b2d306ee9f71faee9714294e41bb777f83f88578be" dependencies = [ "proc-macro2", "quote", @@ -1600,9 +1605,9 @@ dependencies = [ [[package]] name = "serde_json" -version = "1.0.79" +version = "1.0.81" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e8d9fa5c3b304765ce1fd9c4c8a3de2c8db365a5b91be52f186efc675681d95" +checksum = "9b7ce2b32a1aed03c558dc61a5cd328f15aff2dbc17daad8fb8af04d2100e15c" dependencies = [ "itoa", "ryu", @@ -1647,9 +1652,9 @@ checksum = "f2dd574626839106c320a323308629dcb1acfc96e32a8cba364ddc61ac23ee83" [[package]] name = "sourcemap" -version = "6.0.1" +version = "6.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6e031f2463ecbdd5f34c950f89f5c1e1032f22c0f8e3dc4bdb2e8b6658cf61eb" +checksum = "a2ca89636b276071e7276488131f531dbf43ad1c19bc4bd5a04f6a0ce1ddc138" dependencies = [ "base64 0.11.0", "if_chain", @@ -1771,7 +1776,7 @@ checksum = "84fed4a980e12c737171a7b17c5e0a2f4272899266fa0632ea4e31264ebdfdb5" dependencies = [ "ahash", "anyhow", - "dashmap 5.2.0", + "dashmap", "once_cell", "regex", "serde", @@ -1780,9 +1785,9 @@ dependencies = [ [[package]] name = "swc_common" -version = "0.17.21" +version = "0.18.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "daac2e6fea713c68d37b29ea5fd6342213ca2915a868f38a3fd4aa6750a9b9f8" +checksum = "a14c5d15a47c404bc1e1597f4412643d293aed2c3143c9c4a9c20abe879a934b" dependencies = [ "ahash", "ast_node", @@ -1807,11 +1812,37 @@ dependencies = [ "url", ] +[[package]] +name = "swc_config" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8bb05ef56c14b95dd7e62e95960153af811b9a447287f1f6ca59f1337fb83d4" +dependencies = [ + "anyhow", + "indexmap", + "serde", + "serde_json", + "swc_config_macro", +] + +[[package]] +name = "swc_config_macro" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fb64bc03d90fd5c90d6ab917bb2b1d7fbd31957df39e31ea24a3f554b4372251" +dependencies = [ + "pmutil", + "proc-macro2", + "quote", + "swc_macros_common", + "syn", +] + [[package]] name = "swc_ecma_ast" -version = "0.75.0" +version = "0.78.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "72961898fbe56591997e667a1ec6a268383582810351c279a15ec710b6177d33" +checksum = "6ed68ad13e4489f309ffed9d302337d7c8bde11d00b8b275b7aa7fda4da035bf" dependencies = [ "is-macro", "num-bigint", @@ -1824,9 +1855,9 @@ dependencies = [ [[package]] name = "swc_ecma_codegen" -version = "0.103.0" +version = "0.108.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "99ca430d8ea2c8791d1341c4035431c90b87330e39479b4a6dabb4fded124e30" +checksum = "897c6f0574ee1280a97d9d45210f50f8841c643a74ddbb09de2ba2d6f9598a33" dependencies = [ "bitflags", "memchr", @@ -1856,16 +1887,17 @@ dependencies = [ [[package]] name = "swc_ecma_loader" -version = "0.29.0" +version = "0.30.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f9ab69df5d4de425833e02de111f14b5544b39ad9c9b82c97e4835fc55c8f1b6" +checksum = "0f7baaa5b99cdf49e830caf54b837891c5c38275ac94c31d555859be95f6479c" dependencies = [ "ahash", "anyhow", - "dashmap 4.0.2", + "dashmap", "normpath", "once_cell", "path-clean", + "pathdiff", "serde", "serde_json", "swc_common", @@ -1874,9 +1906,9 @@ dependencies = [ [[package]] name = "swc_ecma_parser" -version = "0.100.2" +version = "0.104.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "890d967031e3e7330cd7892f27d826b7b4f37c7caa19db85c78a0862e1fe3974" +checksum = "efb97dc6efc95313dedc5158055cc811da77395ef7b54be61948b5ad097a3671" dependencies = [ "either", "enum_kind", @@ -1894,17 +1926,17 @@ dependencies = [ [[package]] name = "swc_ecma_preset_env" -version = "0.117.0" +version = "0.129.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "77a37c95c8e7e47a1fd6bf09ff2744fe55570235e8aba2c9373200213ec2ce25" +checksum = "b5454c4b5a1e9b8278a5f40e3e03e986c70669768630d7a1252665d63cce3e5b" dependencies = [ "ahash", "anyhow", - "dashmap 5.2.0", + "dashmap", "indexmap", "once_cell", "preset_env_base", - "semver 1.0.7", + "semver 1.0.9", "serde", "serde_json", "st-map", @@ -1919,9 +1951,9 @@ dependencies = [ [[package]] name = "swc_ecma_transforms" -version = "0.142.0" +version = "0.154.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f20e5e2d8ab843fa0454e049f73f6d99c444a8c0e2320f77028361ab75e2d18e" +checksum = "2bce21d9e8ff785aaf9b4ac11375d9f5767630fcaf882f72e6af0516224085a6" dependencies = [ "swc_atoms", "swc_common", @@ -1939,13 +1971,14 @@ dependencies = [ [[package]] name = "swc_ecma_transforms_base" -version = "0.75.0" +version = "0.85.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "404c6ea7ca61ceb2ce1f4ed448d1436a38c31b8c572850f04541c0229c966bbf" +checksum = "8262876d5387887776f23c4894fbddff26e5f184edadf2375f3dc19fca2b42a4" dependencies = [ "better_scoped_tls", "once_cell", "phf", + "rustc-hash", "serde", "smallvec", "swc_atoms", @@ -1959,9 +1992,9 @@ dependencies = [ [[package]] name = "swc_ecma_transforms_classes" -version = "0.63.0" +version = "0.73.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "503f2f6bd0f9e6363a93406753bf64675163423774256a267c85a5d9b5b44b08" +checksum = "e74a27c29def9db5ff03db4d3ab3d37701fb6d100951162223b71132908451eb" dependencies = [ "swc_atoms", "swc_common", @@ -1973,9 +2006,9 @@ dependencies = [ [[package]] name = "swc_ecma_transforms_compat" -version = "0.89.0" +version = "0.99.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1d234c84cee8aeeda2ec60087f65acd420e2475bb334a64bbf988b538c21b31d" +checksum = "b8765716f50186e937f79726384cced3c2d4d15293fb0df7f32bd2feb25fb314" dependencies = [ "ahash", "arrayvec", @@ -1987,6 +2020,7 @@ dependencies = [ "smallvec", "swc_atoms", "swc_common", + "swc_config", "swc_ecma_ast", "swc_ecma_transforms_base", "swc_ecma_transforms_classes", @@ -2012,9 +2046,9 @@ dependencies = [ [[package]] name = "swc_ecma_transforms_module" -version = "0.102.0" +version = "0.112.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6c340a0228a9a49240d97a4a4e99a0a61e6613b29b427cc09a60f6ad4dcbf728" +checksum = "48a5415e54a7b7a95e3dd2ede463e377a6254c6a37fae9830b113a2ad45226fe" dependencies = [ "Inflector", "ahash", @@ -2036,14 +2070,15 @@ dependencies = [ [[package]] name = "swc_ecma_transforms_optimization" -version = "0.112.1" +version = "0.124.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "af43d7d92e0bb8ba60d64ce8a7edcab7738f7e858b8e42814fca5c133ba17c19" +checksum = "e27080f65285ccd53bd9ad68e64e62faba2595d57144e4552a1752664525e474" dependencies = [ "ahash", - "dashmap 5.2.0", + "dashmap", "indexmap", "once_cell", + "rustc-hash", "serde_json", "swc_atoms", "swc_common", @@ -2058,9 +2093,9 @@ dependencies = [ [[package]] name = "swc_ecma_transforms_proposal" -version = "0.97.0" +version = "0.107.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "93d08411e517736b0167f3c9784fe9b98cc09308ae12e6072abd2bb2c2236da2" +checksum = "47fc0f3b336764f89adf1899830321c3f5a7e845ede3ad5949eeb7468aa260ab" dependencies = [ "either", "serde", @@ -2077,13 +2112,13 @@ dependencies = [ [[package]] name = "swc_ecma_transforms_react" -version = "0.104.0" +version = "0.114.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "43cda44270dfcc95d61582981baddaf53d96c5233ea7384e81cd6e462816c58e" +checksum = "83e0f5f3cf67dd7d57f23b152c9d55159ceaa10b73b2a32f973398d2304f3363" dependencies = [ "ahash", "base64 0.13.0", - "dashmap 5.2.0", + "dashmap", "indexmap", "once_cell", "regex", @@ -2092,6 +2127,7 @@ dependencies = [ "string_enum", "swc_atoms", "swc_common", + "swc_config", "swc_ecma_ast", "swc_ecma_parser", "swc_ecma_transforms_base", @@ -2102,9 +2138,9 @@ dependencies = [ [[package]] name = "swc_ecma_transforms_typescript" -version = "0.107.0" +version = "0.117.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a09397169ed7ce0751a82cb71655f3a4a1fb00d8863aabd5cca9b46eff3dd5f2" +checksum = "fa8f32954c5a7c6bdead39c8a8a1580127a1759f33ef8b87d00f754882e6090a" dependencies = [ "serde", "swc_atoms", @@ -2118,9 +2154,9 @@ dependencies = [ [[package]] name = "swc_ecma_utils" -version = "0.79.1" +version = "0.85.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "44ee8d60b9977f58214af7102dc30855a6754e742afe6d6e26e5bf13883c7b91" +checksum = "dff9d469b284a48317a695a81346a9609d04ce3a31da4493aac508e0d48a4257" dependencies = [ "indexmap", "once_cell", @@ -2133,9 +2169,9 @@ dependencies = [ [[package]] name = "swc_ecma_visit" -version = "0.61.0" +version = "0.64.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b5ea00a52ba2b971955c62275696d5c59f3cf0cd06db74a66dec378ec9843c78" +checksum = "f2d3783a0dd1e301ae2945ab1241405f913427f9512ec62756d3d2072f7c21bb" dependencies = [ "num-bigint", "swc_atoms", @@ -2147,9 +2183,9 @@ dependencies = [ [[package]] name = "swc_ecmascript" -version = "0.143.0" +version = "0.157.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ebda93aa6422956c184a9eb5fdb0f0f0ff433169fa15e55ef445e5ad0b5e0abe" +checksum = "bd35679e1dc392f776b691b125692d90a7bebd5d23ec96699cfe37d8ae8633b1" dependencies = [ "swc_ecma_ast", "swc_ecma_codegen", @@ -2174,9 +2210,9 @@ dependencies = [ [[package]] name = "swc_macros_common" -version = "0.3.4" +version = "0.3.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "033f8b6e2fc4991a8e422a20b4f52741affcac2267c29357c931508a1a500797" +checksum = "d5dca3f08d02da4684c3373150f7c045128f81ea00f0c434b1b012bc65a6cce3" dependencies = [ "pmutil", "proc-macro2", @@ -2221,9 +2257,9 @@ dependencies = [ [[package]] name = "syn" -version = "1.0.91" +version = "1.0.94" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b683b2b825c8eef438b77c36a06dc262294da3d5a5813fac20da149241dcd44d" +checksum = "a07e33e919ebcd69113d5be0e4d70c5707004ff45188910106854f38b960df4a" dependencies = [ "proc-macro2", "quote", @@ -2250,18 +2286,18 @@ dependencies = [ [[package]] name = "thiserror" -version = "1.0.30" +version = "1.0.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "854babe52e4df1653706b98fcfc05843010039b406875930a70e4d9644e5c417" +checksum = "bd829fe32373d27f76265620b5309d0340cb8550f523c1dda251d6298069069a" dependencies = [ "thiserror-impl", ] [[package]] name = "thiserror-impl" -version = "1.0.30" +version = "1.0.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "aa32fd3f627f367fe16f893e2597ae3c05020f8bba2666a4e6ea73d377e5714b" +checksum = "0396bc89e626244658bef819e22d0cc459e795a5ebe878e6ec336d1674a8d79a" dependencies = [ "proc-macro2", "quote", @@ -2289,9 +2325,9 @@ dependencies = [ [[package]] name = "tinyvec" -version = "1.5.1" +version = "1.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2c1c1d5a42b6245520c249549ec267180beaffcc0615401ac8e31853d4b6d8d2" +checksum = "87cc5ceb3875bb20c2890005a4e226a4651264a5c75edb2421b52861a0a0cb50" dependencies = [ "tinyvec_macros", ] @@ -2304,9 +2340,9 @@ checksum = "cda74da7e1a664f795bb1f8a87ec406fb89a02522cf6e50620d016add6dbbf5c" [[package]] name = "tracing" -version = "0.1.33" +version = "0.1.34" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "80b9fa4360528139bc96100c160b7ae879f5567f49f1782b0b02035b0358ebf3" +checksum = "5d0ecdcb44a79f0fe9844f0c4f33a342cbcbb5117de8001e6ba0dc2351327d09" dependencies = [ "cfg-if 1.0.0", "pin-project-lite", @@ -2316,9 +2352,9 @@ dependencies = [ [[package]] name = "tracing-attributes" -version = "0.1.20" +version = "0.1.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2e65ce065b4b5c53e73bb28912318cb8c9e9ad3921f1d669eb0e68b4c8143a2b" +checksum = "cc6b8ad3567499f98a1db7a752b07a7c8c7c7c34c332ec00effb2b0027974b7c" dependencies = [ "proc-macro2", "quote", @@ -2327,9 +2363,9 @@ dependencies = [ [[package]] name = "tracing-core" -version = "0.1.24" +version = "0.1.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "90442985ee2f57c9e1b548ee72ae842f4a9a20e3f417cc38dbc5dc684d9bb4ee" +checksum = "f54c8ca710e81886d498c2fd3331b56c93aa248d49de2222ad2742247c60072f" dependencies = [ "lazy_static", ] @@ -2354,9 +2390,9 @@ checksum = "dcf81ac59edc17cc8697ff311e8f5ef2d99fcbd9817b34cec66f90b6c3dfd987" [[package]] name = "unicode-bidi" -version = "0.3.7" +version = "0.3.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1a01404663e3db436ed2746d9fefef640d868edae3cceb81c3b8d5732fda678f" +checksum = "099b7128301d285f79ddd55b9a83d5e6b9e97c92e0ea0daebee7263e932de992" [[package]] name = "unicode-id" @@ -2381,15 +2417,9 @@ checksum = "3ed742d4ea2bd1176e236172c8429aaf54486e7ac098db29ffe6529e0ce50973" [[package]] name = "unicode-xid" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8ccb82d61f80a663efe1f787a51b16b5a51e3314d6ac365b08639f52387b33f3" - -[[package]] -name = "unindent" -version = "0.1.8" +version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "514672a55d7380da379785a4d70ca8386c8883ff7eaae877be4d2081cebe73d8" +checksum = "957e51f3646910546462e67d5f7599b9e4fb8acdd304b087a6494730f9eebf04" [[package]] name = "unreachable" @@ -2532,97 +2562,54 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" -[[package]] -name = "windows" -version = "0.36.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "36e5436ab30c3d1422272fc6f7b5e7d46e93c94bfca83be808404df9ea5bea76" -dependencies = [ - "windows_aarch64_msvc 0.36.0", - "windows_i686_gnu 0.36.0", - "windows_i686_msvc 0.36.0", - "windows_x86_64_gnu 0.36.0", - "windows_x86_64_msvc 0.36.0", -] - [[package]] name = "windows-sys" -version = "0.34.0" +version = "0.36.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5acdd78cb4ba54c0045ac14f62d8f94a03d10047904ae2a40afa1e99d8f70825" +checksum = "ea04155a16a59f9eab786fe12a4a450e75cdb175f9e0d80da1e17db09f55b8d2" dependencies = [ - "windows_aarch64_msvc 0.34.0", - "windows_i686_gnu 0.34.0", - "windows_i686_msvc 0.34.0", - "windows_x86_64_gnu 0.34.0", - "windows_x86_64_msvc 0.34.0", + "windows_aarch64_msvc", + "windows_i686_gnu", + "windows_i686_msvc", + "windows_x86_64_gnu", + "windows_x86_64_msvc", ] [[package]] name = "windows_aarch64_msvc" -version = "0.34.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "17cffbe740121affb56fad0fc0e421804adf0ae00891205213b5cecd30db881d" - -[[package]] -name = "windows_aarch64_msvc" -version = "0.36.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4bee8cd327bbef19bf86d30bd66379f57905166d3103b0e2eff4a491b85e421d" - -[[package]] -name = "windows_i686_gnu" -version = "0.34.0" +version = "0.36.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2564fde759adb79129d9b4f54be42b32c89970c18ebf93124ca8870a498688ed" +checksum = "9bb8c3fd39ade2d67e9874ac4f3db21f0d710bee00fe7cab16949ec184eeaa47" [[package]] name = "windows_i686_gnu" -version = "0.36.0" +version = "0.36.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3b759cc6e3d97970c98cffe461739e89ab6d424ba5e2e7d3b9b05a2d56116057" +checksum = "180e6ccf01daf4c426b846dfc66db1fc518f074baa793aa7d9b9aaeffad6a3b6" [[package]] name = "windows_i686_msvc" -version = "0.34.0" +version = "0.36.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9cd9d32ba70453522332c14d38814bceeb747d80b3958676007acadd7e166956" - -[[package]] -name = "windows_i686_msvc" -version = "0.36.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a0cee91bff283876711f91e7db0aa234438bc663a9d8304596df00b0a6fd6ef" - -[[package]] -name = "windows_x86_64_gnu" -version = "0.34.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cfce6deae227ee8d356d19effc141a509cc503dfd1f850622ec4b0f84428e1f4" +checksum = "e2e7917148b2812d1eeafaeb22a97e4813dfa60a3f8f78ebe204bcc88f12f024" [[package]] name = "windows_x86_64_gnu" -version = "0.36.0" +version = "0.36.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "51e8c6f778aa4383b033ff785191aea0f1ebeceedc160c2c92f944ef7e191476" +checksum = "4dcd171b8776c41b97521e5da127a2d86ad280114807d0b2ab1e462bc764d9e1" [[package]] name = "windows_x86_64_msvc" -version = "0.34.0" +version = "0.36.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d19538ccc21819d01deaf88d6a17eae6596a12e9aafdbb97916fb49896d89de9" - -[[package]] -name = "windows_x86_64_msvc" -version = "0.36.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1dd6a8b0b1ea4331e4db47192729fce42ac8a110fd22bb3abac555d8d7700f29" +checksum = "c811ca4a8c853ef420abd8592ba53ddbbac90410fab6903b3e79972a631f7680" [[package]] name = "xxhash-rust" -version = "0.8.4" +version = "0.8.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "83a16b7b403377d61184bb601d8349a4ff2c4cec08a305d004f710b7eaafef24" +checksum = "074914ea4eec286eb8d1fd745768504f420a1f7b7919185682a4a267bed7d2e7" [[package]] name = "zopfli" diff --git a/packages/bundlers/experimental/src/ExperimentalBundler.js b/packages/bundlers/experimental/src/ExperimentalBundler.js index c4ed607eac7..12d85953d9a 100644 --- a/packages/bundlers/experimental/src/ExperimentalBundler.js +++ b/packages/bundlers/experimental/src/ExperimentalBundler.js @@ -19,7 +19,7 @@ import {ContentGraph, Graph} from '@parcel/graph'; import invariant from 'assert'; import {ALL_EDGE_TYPES} from '@parcel/graph'; import {Bundler} from '@parcel/plugin'; -import {validateSchema, DefaultMap} from '@parcel/utils'; +import {setIntersect, validateSchema, DefaultMap} from '@parcel/utils'; import nullthrows from 'nullthrows'; import {encodeJSONKeyComponent} from '@parcel/diagnostic'; @@ -58,6 +58,7 @@ export type Bundle = {| internalizedAssetIds: Array, bundleBehavior?: ?BundleBehavior, needsStableName: boolean, + mainEntryAsset: ?Asset, size: number, sourceBundles: Array, target: Target, @@ -118,11 +119,12 @@ function decorateLegacyGraph( // Step 1: Create bundle groups, bundles, and shared bundles and add assets to them for (let [bundleNodeId, idealBundle] of idealBundleGraph.nodes) { if (idealBundle === 'root') continue; - let [entryAsset] = [...idealBundle.assets]; + let entryAsset = idealBundle.mainEntryAsset; let bundleGroup; let bundle; if (bundleGroupBundleIds.includes(bundleNodeId)) { + invariant(entryAsset != null); let dependencies = dependencyBundleGraph .getNodeIdsConnectedTo( dependencyBundleGraph.getNodeIdByContentKey(String(bundleNodeId)), @@ -178,6 +180,7 @@ function decorateLegacyGraph( }), ); } else { + invariant(entryAsset != null); bundle = nullthrows( bundleGraph.createBundle({ entryAsset, @@ -194,6 +197,7 @@ function decorateLegacyGraph( bundleGraph.addAssetToBundle(asset, bundle); } } + // Step 2: Internalize dependencies for bundles for (let [, idealBundle] of idealBundleGraph.nodes) { if (idealBundle === 'root') continue; @@ -212,18 +216,31 @@ function decorateLegacyGraph( } } } + // Step 3: Add bundles to their bundle groups - for (let [bundleId, bundleGroup] of entryBundleToBundleGroup) { - let outboundNodeIds = idealBundleGraph.getNodeIdsConnectedFrom(bundleId); + idealBundleGraph.traverse((nodeId, _, actions) => { + let node = idealBundleGraph.getNode(nodeId); + if (node === 'root') { + return; + } + actions.skipChildren(); + + let outboundNodeIds = idealBundleGraph.getNodeIdsConnectedFrom(nodeId); + let entryBundle = nullthrows(idealBundleGraph.getNode(nodeId)); + invariant(entryBundle !== 'root'); + let legacyEntryBundle = nullthrows( + idealBundleToLegacyBundle.get(entryBundle), + ); + for (let id of outboundNodeIds) { let siblingBundle = nullthrows(idealBundleGraph.getNode(id)); invariant(siblingBundle !== 'root'); let legacySiblingBundle = nullthrows( idealBundleToLegacyBundle.get(siblingBundle), ); - bundleGraph.addBundleToBundleGroup(legacySiblingBundle, bundleGroup); + bundleGraph.createBundleReference(legacyEntryBundle, legacySiblingBundle); } - } + }); // Step 4: Add references to all bundles for (let [asset, references] of idealGraph.assetReference) { @@ -263,12 +280,6 @@ function createIdealGraph( Array<[Dependency, Bundle]>, > = new DefaultMap(() => []); - // bundleRoot to all bundleRoot descendants - let reachableBundles: DefaultMap< - BundleRoot, - Set, - > = new DefaultMap(() => new Set()); - let bundleGraph: Graph = new Graph(); let stack: Array<[BundleRoot, NodeId]> = []; @@ -411,20 +422,6 @@ function createIdealGraph( ), dependencyPriorityEdges[dependency.priority], ); - - // Walk up the stack until we hit a different asset type - // and mark each bundle as reachable from every parent bundle - for (let i = stack.length - 1; i >= 0; i--) { - let [stackAsset] = stack[i]; - if ( - stackAsset.type !== childAsset.type || - stackAsset.env.context !== childAsset.env.context || - stackAsset.env.isIsolated() - ) { - break; - } - reachableBundles.get(stackAsset).add(childAsset); - } continue; } if ( @@ -432,9 +429,7 @@ function createIdealGraph( dependency.priority === 'parallel' || childAsset.bundleBehavior === 'inline' ) { - let [parentBundleRoot, bundleGroupNodeId] = nullthrows( - stack[stack.length - 1], - ); + let [, bundleGroupNodeId] = nullthrows(stack[stack.length - 1]); let bundleGroup = nullthrows( bundleGraph.getNode(bundleGroupNodeId), ); @@ -467,12 +462,6 @@ function createIdealGraph( let bundle; if (bundleId == null) { - let parentBundleId = nullthrows(bundles.get(parentBundleRoot.id)); - let parentBundle = nullthrows( - bundleGraph.getNode(parentBundleId), - ); - invariant(parentBundle !== 'root'); - // Create a new bundle if none of the same type exists already. bundle = createBundle({ // We either have an entry asset or a unique key. @@ -489,7 +478,7 @@ function createIdealGraph( (dependency.priority === 'parallel' && !dependency.needsStableName) ? false - : parentBundle.needsStableName, + : bundleGroup.needsStableName, }); bundleId = bundleGraph.addNode(bundle); } else { @@ -566,22 +555,18 @@ function createIdealGraph( let reachableRoots: ContentGraph = new ContentGraph(); for (let [root] of bundleRoots) { let rootNodeId = reachableRoots.addNodeByContentKeyIfNeeded(root.id, root); - assetGraph.traverse((node, isAsync, actions) => { + assetGraph.traverse((node, _, actions) => { if (node.value === root) { return; } - if (node.type === 'dependency') { let dependency = node.value; if (dependencyBundleGraph.hasContentKey(dependency.id)) { - if ( - dependency.priority === 'lazy' || - dependency.priority === 'parallel' - ) { + if (dependency.priority !== 'sync') { let assets = assetGraph.getDependencyAssets(dependency); if (assets.length === 0) { - return node; + return; } invariant(assets.length === 1); @@ -601,16 +586,23 @@ function createIdealGraph( ); } } - return; + } + + if (dependency.priority !== 'sync') { + actions.skipChildren(); } return; } - - if (bundleRoots.has(node.value)) { + //asset node type + let asset = node.value; + if ( + asset.bundleBehavior === 'isolated' || + asset.bundleBehavior === 'inline' || + root.type !== asset.type + ) { actions.skipChildren(); return; } - let nodeId = reachableRoots.addNodeByContentKeyIfNeeded( node.value.id, node.value, @@ -621,99 +613,75 @@ function createIdealGraph( // Maps a given bundleRoot to the assets reachable from it, // and the bundleRoots reachable from each of these assets - let ancestorAssets: Map< - BundleRoot, - Map | null>, - > = new Map(); - - // Reference count of each asset available within a given bundleRoot's bundle group - let assetRefsInBundleGroup: DefaultMap< - BundleRoot, - DefaultMap, - > = new DefaultMap(() => new DefaultMap(() => 0)); + let asyncAncestorAssets: Map> = new Map(); // Step 4: Determine assets that should be duplicated by computing asset availability in each bundle group + for (let entry of entries.keys()) { + // Initialize an empty set of ancestors available to entries + asyncAncestorAssets.set(entry, new Set()); + } + + // Visit nodes in a topological order, visiting parent nodes before child nodes. + // This allows us to construct an understanding of which assets will already be + // loaded and available when a bundle runs, by pushing available assets downwards and + // computing the intersection of assets available through all possible paths to a bundle. for (let nodeId of asyncBundleRootGraph.topoSort()) { const bundleRoot = asyncBundleRootGraph.getNode(nodeId); if (bundleRoot === 'root') continue; invariant(bundleRoot != null); - let ancestors = ancestorAssets.get(bundleRoot); - - // First consider bundle group asset availability, processing only - // non-isolated bundles within that bundle group let bundleGroupId = nullthrows(bundleRoots.get(bundleRoot))[1]; - // Map of assets in the bundle group to their refcounts - let assetRefs = assetRefsInBundleGroup.get(bundleRoot); - - for (let bundleIdInGroup of [ - bundleGroupId, - ...bundleGraph.getNodeIdsConnectedFrom(bundleGroupId), - ]) { - let bundleInGroup = nullthrows(bundleGraph.getNode(bundleIdInGroup)); - invariant(bundleInGroup !== 'root'); - if ( - bundleInGroup.bundleBehavior === 'isolated' || - bundleInGroup.bundleBehavior === 'inline' - ) { - continue; - } - let [bundleRoot] = [...bundleInGroup.assets]; - // Assets directly connected to current bundleRoot - let assetsFromBundleRoot = reachableRoots - .getNodeIdsConnectedFrom( - reachableRoots.getNodeIdByContentKey(bundleRoot.id), - ) - .map(id => nullthrows(reachableRoots.getNode(id))); - for (let asset of assetsFromBundleRoot) { - assetRefs.set(asset, assetRefs.get(asset) + 1); - } - } + let available; + if (bundleRoot.bundleBehavior === 'isolated') { + available = new Set(); + } else { + available = new Set(asyncAncestorAssets.get(bundleRoot)); + for (let bundleIdInGroup of [ + bundleGroupId, + ...bundleGraph.getNodeIdsConnectedFrom(bundleGroupId), + ]) { + let bundleInGroup = nullthrows(bundleGraph.getNode(bundleIdInGroup)); + invariant(bundleInGroup !== 'root'); + if ( + bundleInGroup.bundleBehavior === 'isolated' || + bundleInGroup.bundleBehavior === 'inline' + ) { + continue; + } - // Enumerate bundleRoots connected to the node (parent), taking the intersection - // between the assets synchronously loaded by the parent, and those loaded by the child. - let bundleGroupAssets = new Set(assetRefs.keys()); + for (let bundleRoot of bundleInGroup.assets) { + // Assets directly connected to current bundleRoot + let assetsFromBundleRoot = reachableRoots + .getNodeIdsConnectedFrom( + reachableRoots.getNodeIdByContentKey(bundleRoot.id), + ) + .map(id => nullthrows(reachableRoots.getNode(id))); - let combined = ancestors - ? new Map([...bundleGroupAssets, ...ancestors.keys()].map(a => [a, null])) - : new Map([...bundleGroupAssets].map(a => [a, null])); + for (let asset of [bundleRoot, ...assetsFromBundleRoot]) { + available.add(asset); + } + } + } + } let children = asyncBundleRootGraph.getNodeIdsConnectedFrom(nodeId); - + // Group assets available across our children by the child. This will be used + // to determine borrowers if needed below. for (let childId of children) { let child = asyncBundleRootGraph.getNode(childId); invariant(child !== 'root' && child != null); - const availableAssets = ancestorAssets.get(child); - - if (availableAssets != null) { - ancestryIntersect(availableAssets, combined); - } else { - ancestorAssets.set(child, combined); + if ( + child.bundleBehavior === 'isolated' || + child.bundleBehavior === 'inline' + ) { + continue; } - } - - let siblingAncestors = ancestors - ? ancestryUnion(new Set(ancestors.keys()), assetRefs, bundleRoot) - : new Map([...bundleGroupAssets].map(a => [a, [bundleRoot]])); - - for (let bundleIdInGroup of bundleGraph.getNodeIdsConnectedFrom( - bundleGroupId, - )) { - let bundleInGroup = bundleGraph.getNode(bundleIdInGroup); - invariant( - bundleInGroup != null && - bundleInGroup !== 'root' && - bundleInGroup.assets != null, - ); - let [bundleRoot] = [...bundleInGroup.assets]; - - const availableAssets = ancestorAssets.get(bundleRoot); - - if (availableAssets != null) { - ancestryIntersect(availableAssets, siblingAncestors); + const childAvailableAssets = asyncAncestorAssets.get(child); + if (childAvailableAssets != null) { + setIntersect(childAvailableAssets, available); } else { - ancestorAssets.set(bundleRoot, siblingAncestors); + asyncAncestorAssets.set(child, new Set(available)); } } } @@ -730,96 +698,24 @@ function createIdealGraph( // Filter out bundles from this asset's reachable array if // bundle does not contain the asset in its ancestry - // or if any of the bundles in the ancestry have a refcount of <= 1 for that asset, - // meaning it may not be deduplicated. Otherwise, decrement all references in - // the ancestry and keep it - reachable = reachable.filter(b => { - let ancestry = ancestorAssets.get(b)?.get(asset); - if (ancestry === undefined) { - // No reachable bundles from this asset - return true; - } else if (ancestry === null) { - // Asset is reachable from this bundle - return false; - } else { - // If every bundle in its ancestry has more than 1 reference to the asset - if ( - ancestry.every( - bundleId => assetRefsInBundleGroup.get(bundleId).get(asset) > 1, - ) - ) { - for (let bundleRoot of ancestry) { - assetRefsInBundleGroup - .get(bundleRoot) - .set( - asset, - assetRefsInBundleGroup.get(bundleRoot).get(asset) - 1, - ); - } - return false; - } - return true; - } - }); - - let rootBundleTuple = bundleRoots.get(asset); - if (rootBundleTuple != null) { - let rootBundle = nullthrows(bundleGraph.getNode(rootBundleTuple[0])); - invariant(rootBundle !== 'root'); - - if (!rootBundle.env.isIsolated()) { - if (!bundles.has(asset.id)) { - bundles.set(asset.id, rootBundleTuple[0]); - } - - for (let reachableAsset of reachable) { - if (reachableAsset !== asset) { - bundleGraph.addEdge( - nullthrows(bundleRoots.get(reachableAsset))[1], - rootBundleTuple[0], - ); - } - } - - let willInternalizeRoots = asyncBundleRootGraph - .getNodeIdsConnectedTo( - asyncBundleRootGraph.getNodeIdByContentKey(asset.id), - ) - .map(id => nullthrows(asyncBundleRootGraph.getNode(id))) - .filter(bundleRoot => { - if (bundleRoot === 'root') { - return false; - } + reachable = reachable.filter(b => !asyncAncestorAssets.get(b)?.has(asset)); - return ( - reachableRoots.hasEdge( - reachableRoots.getNodeIdByContentKey(bundleRoot.id), - reachableRoots.getNodeIdByContentKey(asset.id), - ) || ancestorAssets.get(bundleRoot)?.has(asset) - ); - }) - .map(bundleRoot => { - // For Flow - invariant(bundleRoot !== 'root'); - return bundleRoot; - }); - - for (let bundleRoot of willInternalizeRoots) { - if (bundleRoot !== asset) { - let bundle = nullthrows( - bundleGraph.getNode(nullthrows(bundles.get(bundleRoot.id))), - ); - invariant(bundle !== 'root'); - bundle.internalizedAssetIds.push(asset.id); - } - } - } - } else if (reachable.length > 0) { + if (reachable.length > 0) { let reachableEntries = reachable.filter( - a => entries.has(a) || !a.isBundleSplittable, + a => + entries.has(a) || + !a.isBundleSplittable || + getBundleFromBundleRoot(a).needsStableName || + getBundleFromBundleRoot(a).bundleBehavior === 'inline' || + getBundleFromBundleRoot(a).bundleBehavior === 'isolated', ); reachable = reachable.filter( - a => !entries.has(a) && a.isBundleSplittable, + a => + !entries.has(a) && + a.isBundleSplittable && + !getBundleFromBundleRoot(a).needsStableName && + getBundleFromBundleRoot(a).bundleBehavior !== 'inline' && + getBundleFromBundleRoot(a).bundleBehavior !== 'isolated', ); // Add assets to non-splittable bundles. @@ -872,8 +768,8 @@ function createIdealGraph( } } - // Step 7: Merge any sibling bundles required by entry bundles back into the entry bundle. - // Entry bundles must be predictable, so cannot have unpredictable siblings. + // Step 7: Merge any shared bundles under the minimum bundle size back into + // their source bundles, and remove the bundle. for (let [bundleNodeId, bundle] of bundleGraph.nodes) { if (bundle === 'root') continue; if (bundle.sourceBundles.length > 0 && bundle.size < config.minBundleSize) { @@ -882,6 +778,14 @@ function createIdealGraph( } } + function getBundleFromBundleRoot(bundleRoot: BundleRoot): Bundle { + let bundle = bundleGraph.getNode( + nullthrows(bundleRoots.get(bundleRoot))[0], + ); + invariant(bundle !== 'root' && bundle != null); + return bundle; + } + return { bundleGraph, dependencyBundleGraph, @@ -925,6 +829,7 @@ function createBundle(opts: {| uniqueKey: opts.uniqueKey, assets: new Set(), internalizedAssetIds: [], + mainEntryAsset: null, size: 0, sourceBundles: [], target: opts.target, @@ -940,6 +845,7 @@ function createBundle(opts: {| uniqueKey: opts.uniqueKey, assets: new Set([asset]), internalizedAssetIds: [], + mainEntryAsset: asset, size: asset.stats.size, sourceBundles: [], target: opts.target, @@ -1002,43 +908,6 @@ async function loadBundlerConfig( }; } -function ancestryUnion( - ancestors: Set, - assetRefs: Map, - bundleRoot: BundleRoot, -): Map | null> { - let map = new Map(); - for (let a of ancestors) { - map.set(a, null); - } - for (let [asset, refCount] of assetRefs) { - if (!ancestors.has(asset) && refCount > 1) { - map.set(asset, [bundleRoot]); - } - } - return map; -} - -function ancestryIntersect( - currentMap: Map | null>, - map: Map | null>, -): void { - for (let [bundleRoot, currentAssets] of currentMap) { - if (map.has(bundleRoot)) { - let assets = map.get(bundleRoot); - if (assets) { - if (currentAssets) { - currentAssets.push(...assets); - } else { - currentMap.set(bundleRoot, [...assets]); - } - } - } else { - currentMap.delete(bundleRoot); - } - } -} - function getReachableBundleRoots(asset, graph): Array { return graph .getNodeIdsConnectedTo(graph.getNodeIdByContentKey(asset.id)) diff --git a/packages/core/cache/package.json b/packages/core/cache/package.json index 7f4a5fb4022..87899b1a2cf 100644 --- a/packages/core/cache/package.json +++ b/packages/core/cache/package.json @@ -27,7 +27,7 @@ "@parcel/fs": "2.5.0", "@parcel/logger": "2.5.0", "@parcel/utils": "2.5.0", - "lmdb": "2.3.7" + "lmdb": "2.3.10" }, "peerDependencies": { "@parcel/core": "^2.5.0" diff --git a/packages/core/core/src/ParcelConfig.schema.js b/packages/core/core/src/ParcelConfig.schema.js index 1010154c7fb..34d13f5722b 100644 --- a/packages/core/core/src/ParcelConfig.schema.js +++ b/packages/core/core/src/ParcelConfig.schema.js @@ -93,6 +93,9 @@ const mapStringSchema = (pluginType: string, key: string): SchemaEntity => { export default { type: 'object', properties: { + $schema: { + type: 'string', + }, extends: { oneOf: [ { diff --git a/packages/core/core/src/Transformation.js b/packages/core/core/src/Transformation.js index d8e90198d8f..4880b509e65 100644 --- a/packages/core/core/src/Transformation.js +++ b/packages/core/core/src/Transformation.js @@ -74,9 +74,14 @@ import { toProjectPath, } from './projectPath'; import {invalidateOnFileCreateToInternal} from './utils'; +import invariant from 'assert'; type GenerateFunc = (input: UncommittedAsset) => Promise; +type PostProcessFunc = ( + Array, +) => Promise | null>; + export type TransformationOpts = {| options: ParcelOptions, config: ParcelConfig, @@ -336,7 +341,34 @@ export default class Transformation { } } - return finalAssets; + if (!pipeline.postProcess) { + return finalAssets; + } + + let pipelineHash = await this.getPipelineHash(pipeline); + let invalidationHash = await getInvalidationHash( + finalAssets.flatMap(asset => asset.getInvalidations()), + this.options, + ); + let processedCacheEntry = await this.readFromCache( + this.getCacheKey(finalAssets, invalidationHash, pipelineHash), + ); + + invariant(pipeline.postProcess != null); + let processedFinalAssets: Array = + processedCacheEntry ?? (await pipeline.postProcess(finalAssets)) ?? []; + + if (!processedCacheEntry) { + await this.writeToCache( + this.getCacheKey(processedFinalAssets, invalidationHash, pipelineHash), + processedFinalAssets, + + invalidationHash, + pipelineHash, + ); + } + + return processedFinalAssets; } async getPipelineHash(pipeline: Pipeline): Promise { @@ -426,6 +458,8 @@ export default class Transformation { transformer.plugin, transformer.name, transformer.config, + transformer.configKeyPath, + this.parcelConfig, ); for (let result of transformerResults) { @@ -695,6 +729,8 @@ export default class Transformation { transformer: Transformer, transformerName: string, preloadedConfig: ?Config, + configKeyPath?: string, + parcelConfig: ParcelConfig, ): Promise<$ReadOnlyArray> { const logger = new PluginLogger({origin: transformerName}); @@ -786,7 +822,7 @@ export default class Transformation { }); let results = await normalizeAssets(this.options, transfomerResult); - // Create generate function that can be called later + // Create generate and postProcess function that can be called later asset.generate = (): Promise => { let publicAsset = new Asset(asset); if (transformer.generate && asset.ast) { @@ -805,6 +841,32 @@ export default class Transformation { ); }; + let postProcess = transformer.postProcess; + if (postProcess) { + pipeline.postProcess = async ( + assets: Array, + ): Promise | null> => { + let results = await postProcess.call(transformer, { + assets: assets.map(asset => new MutableAsset(asset)), + config, + options: pipeline.pluginOptions, + resolve, + logger, + }); + + return Promise.all( + results.map(result => + asset.createChildAsset( + result, + transformerName, + parcelConfig.filePath, + // configKeyPath, + ), + ), + ); + }; + } + return results; } } @@ -816,6 +878,7 @@ type Pipeline = {| pluginOptions: PluginOptions, resolverRunner: ResolverRunner, workerApi: WorkerApi, + postProcess?: PostProcessFunc, generate?: GenerateFunc, |}; diff --git a/packages/core/integration-tests/test/cache.js b/packages/core/integration-tests/test/cache.js index b03c13689e8..b72cc66eefd 100644 --- a/packages/core/integration-tests/test/cache.js +++ b/packages/core/integration-tests/test/cache.js @@ -4901,17 +4901,17 @@ describe('cache', function () { }, async update(b) { let res = await run(b.bundleGraph); - assert(res.includes("let a = 'a'")); + assert(res.includes(`let a = "a"`)); await overlayFS.writeFile( path.join(inputDir, 'src/entries/a.js'), - "export let a = 'b';", + `export let a = "b";`, ); }, }); let res = await run(b.bundleGraph); - assert(res.includes("let a = 'b'")); + assert(res.includes(`let a = "b"`)); }); it('should invalidate when switching to a different packager for an inline bundle', async function () { diff --git a/packages/core/integration-tests/test/elm.js b/packages/core/integration-tests/test/elm.js index 0c48b4914f5..702be99b4f1 100644 --- a/packages/core/integration-tests/test/elm.js +++ b/packages/core/integration-tests/test/elm.js @@ -81,4 +81,56 @@ describe('elm', function () { assert(js.includes('Elm')); assert(js.includes('init')); }); + + it('should produce correct formatting and indentation when compilation fails', async function () { + const normalizedPath = path.normalize( + 'test/integration/elm-compile-error/src/Main.elm', + ); + await assert.rejects( + () => + bundle(path.join(__dirname, 'integration/elm-compile-error/index.js'), { + mode: 'production', + }), + + { + name: 'BuildError', + diagnostics: [ + { + message: + '\n' + + `-- TYPE MISMATCH --------------- ${normalizedPath}\n` + + '\n' + + 'The 1st argument to `text` is not what I expect:\n' + + '\n' + + '7| Html.text 5 "Hello, world!"\n' + + ' **^**\n' + + 'This argument is a number of type:\n' + + '\n' + + ' **number**\n' + + '\n' + + 'But `text` needs the 1st argument to be:\n' + + '\n' + + ' **String**\n' + + '\n' + + '__Hint__: Try using **String.fromInt** to convert it to a string?', + origin: '@parcel/elm-transformer', + stack: '', + }, + { + message: + '\n' + + `-- TOO MANY ARGS --------------- ${normalizedPath}\n` + + '\n' + + 'The `text` function expects 1 argument, but it got 2 instead.\n' + + '\n' + + '7| Html.text 5 "Hello, world!"\n' + + ' **^^^^^^^^^**\n' + + 'Are there any missing commas? Or missing parentheses?', + origin: '@parcel/elm-transformer', + stack: '', + }, + ], + }, + ); + }); }); diff --git a/packages/core/integration-tests/test/fs.js b/packages/core/integration-tests/test/fs.js index d25c3dd44bb..800432aca0d 100644 --- a/packages/core/integration-tests/test/fs.js +++ b/packages/core/integration-tests/test/fs.js @@ -198,7 +198,7 @@ describe('fs', function () { path.join(distDir, 'index.js'), 'utf8', ); - assert(contents.includes("require('fs')")); + assert(contents.includes(`require("fs")`)); assert(contents.includes('readFileSync')); await outputFS.writeFile( diff --git a/packages/core/integration-tests/test/hmr.js b/packages/core/integration-tests/test/hmr.js index 4818bd97400..17ba5a10670 100644 --- a/packages/core/integration-tests/test/hmr.js +++ b/packages/core/integration-tests/test/hmr.js @@ -10,6 +10,7 @@ import { overlayFS, sleep, run, + request, } from '@parcel/test-utils'; import WebSocket from 'ws'; import json5 from 'json5'; @@ -110,6 +111,7 @@ describe('hmr', function () { return { outputs: JSON.parse(JSON.stringify(outputs)), reloaded, + bundleGraph, }; } @@ -157,10 +159,12 @@ describe('hmr', function () { assert.equal(message.type, 'update'); // Figure out why output doesn't change... - let localAsset = message.assets.find( - asset => asset.output === 'exports.a = 5;\nexports.b = 5;\n', + let localAsset = message.assets.find(asset => + asset.output.includes('exports.a = 5;\nexports.b = 5;\n'), ); assert(!!localAsset); + assert(localAsset.output.includes('//# sourceMappingURL')); + assert(localAsset.output.includes('//# sourceURL')); }); it('should emit an HMR update for all new dependencies along with the changed file', async function () { @@ -331,6 +335,31 @@ describe('hmr', function () { assert.equal(message.type, 'update'); }); + + it('should respond to requests for assets by id', async function () { + let port = await getPort(); + let b = bundler(path.join(__dirname, '/input/index.js'), { + serveOptions: {port}, + hmrOptions: {port}, + inputFS: overlayFS, + config, + }); + + subscription = await b.watch(); + let event = await getNextBuild(b); + + let bundleGraph = nullthrows(event.bundleGraph); + let asset = nullthrows(bundleGraph.getBundles()[0].getMainEntry()); + let contents = await request('/__parcel_hmr/' + asset.id, port); + let publicId = nullthrows(bundleGraph).getAssetPublicId(asset); + assert( + contents.startsWith( + `parcelHotUpdate['${publicId}'] = function (require, module, exports) {`, + ), + ); + assert(contents.includes('//# sourceMappingURL')); + assert(contents.includes('//# sourceURL')); + }); }); // TODO: add test for 4532 (`require` call in modified asset in child bundle where HMR runtime runs in parent bundle) @@ -548,6 +577,29 @@ module.hot.dispose((data) => { assert.notEqual(url.search, search); }); + it('should have correct source locations in errors', async function () { + let {outputs, bundleGraph} = await testHMRClient( + 'hmr-accept-self', + () => { + return { + 'local.js': 'output(new Error().stack);', + }; + }, + ); + + let asset = bundleGraph + .getBundles()[0] + .traverseAssets((asset, _, actions) => { + if (asset.filePath.endsWith('local.js')) { + actions.stop(); + return asset; + } + }); + + let stack = outputs.pop(); + assert(stack.includes('/__parcel_hmr/' + nullthrows(asset).id)); + }); + /* it.skip('should accept HMR updates in the runtime after an initial error', async function() { await fs.mkdirp(path.join(__dirname, '/input')); diff --git a/packages/core/integration-tests/test/html.js b/packages/core/integration-tests/test/html.js index a215957a03c..21436020961 100644 --- a/packages/core/integration-tests/test/html.js +++ b/packages/core/integration-tests/test/html.js @@ -2549,7 +2549,7 @@ describe('html', function () { await getNextBuild(b); let html = await outputFS.readFile('/dist/index.html', 'utf8'); - assert(html.includes("console.log('test')")); + assert(html.includes(`console.log("test")`)); await overlayFS.writeFile( path.join(__dirname, '/html-inline-js-require/test.js'), @@ -2558,7 +2558,7 @@ describe('html', function () { await getNextBuild(b); html = await outputFS.readFile(path.join(distDir, '/index.html'), 'utf8'); - assert(html.includes("console.log('foo')")); + assert(html.includes(`console.log("foo")`)); }); it('should invalidate parent bundle when nested inline bundles change', async function () { @@ -2841,4 +2841,34 @@ describe('html', function () { }, ); }); + + it('extracts shared bundles that load referenced bundle roots across entries', async () => { + let b = await bundle( + ['index1.html', 'index2.html'].map(entry => + path.join(__dirname, 'integration/html-shared-referenced', entry), + ), + { + mode: 'production', + defaultTargetOptions: { + shouldOptimize: false, + }, + }, + ); + + await run(b); + }); + + it('should not skip bundleRoots if an asset is both async required and static required', async function () { + let b = await bundle( + path.join(__dirname, 'integration/html-sync-async-asset/index.html'), + { + mode: 'production', + defaultTargetOptions: { + shouldOptimize: false, + }, + }, + ); + + await run(b, {output: null}, {require: false}); + }); }); diff --git a/packages/core/integration-tests/test/integration/elm-compile-error/elm.json b/packages/core/integration-tests/test/integration/elm-compile-error/elm.json new file mode 100644 index 00000000000..fd754fc77a8 --- /dev/null +++ b/packages/core/integration-tests/test/integration/elm-compile-error/elm.json @@ -0,0 +1,24 @@ +{ + "type": "application", + "source-directories": [ + "src" + ], + "elm-version": "0.19.1", + "dependencies": { + "direct": { + "elm/browser": "1.0.1", + "elm/core": "1.0.2", + "elm/html": "1.0.0" + }, + "indirect": { + "elm/json": "1.1.3", + "elm/time": "1.0.0", + "elm/url": "1.0.0", + "elm/virtual-dom": "1.0.2" + } + }, + "test-dependencies": { + "direct": {}, + "indirect": {} + } +} diff --git a/packages/core/integration-tests/test/integration/elm-compile-error/index.js b/packages/core/integration-tests/test/integration/elm-compile-error/index.js new file mode 100644 index 00000000000..61906ed5a66 --- /dev/null +++ b/packages/core/integration-tests/test/integration/elm-compile-error/index.js @@ -0,0 +1,5 @@ +var local = require('./src/Main.elm'); + +module.exports = function () { + return local; +}; \ No newline at end of file diff --git a/packages/core/integration-tests/test/integration/elm-compile-error/src/Main.elm b/packages/core/integration-tests/test/integration/elm-compile-error/src/Main.elm new file mode 100644 index 00000000000..9b0fb5379b1 --- /dev/null +++ b/packages/core/integration-tests/test/integration/elm-compile-error/src/Main.elm @@ -0,0 +1,7 @@ +module Main exposing (main) + +import Html + + +main = + Html.text 5 "Hello, world!" diff --git a/packages/core/integration-tests/test/integration/html-shared-referenced/async.js b/packages/core/integration-tests/test/integration/html-shared-referenced/async.js new file mode 100644 index 00000000000..ad07c1e9102 --- /dev/null +++ b/packages/core/integration-tests/test/integration/html-shared-referenced/async.js @@ -0,0 +1,3 @@ +import './async2.js'; + +import('./async2.js'); diff --git a/packages/core/integration-tests/test/integration/html-shared-referenced/async2.js b/packages/core/integration-tests/test/integration/html-shared-referenced/async2.js new file mode 100644 index 00000000000..e69de29bb2d diff --git a/packages/core/integration-tests/test/integration/html-shared-referenced/index1.html b/packages/core/integration-tests/test/integration/html-shared-referenced/index1.html new file mode 100644 index 00000000000..befa6f91239 --- /dev/null +++ b/packages/core/integration-tests/test/integration/html-shared-referenced/index1.html @@ -0,0 +1 @@ + diff --git a/packages/core/integration-tests/test/integration/html-shared-referenced/index1.js b/packages/core/integration-tests/test/integration/html-shared-referenced/index1.js new file mode 100644 index 00000000000..c21c06aca8a --- /dev/null +++ b/packages/core/integration-tests/test/integration/html-shared-referenced/index1.js @@ -0,0 +1 @@ +import './shared'; diff --git a/packages/core/integration-tests/test/integration/html-shared-referenced/index2.html b/packages/core/integration-tests/test/integration/html-shared-referenced/index2.html new file mode 100644 index 00000000000..edbd81997e9 --- /dev/null +++ b/packages/core/integration-tests/test/integration/html-shared-referenced/index2.html @@ -0,0 +1 @@ + diff --git a/packages/core/integration-tests/test/integration/html-shared-referenced/index2.js b/packages/core/integration-tests/test/integration/html-shared-referenced/index2.js new file mode 100644 index 00000000000..04c9991d158 --- /dev/null +++ b/packages/core/integration-tests/test/integration/html-shared-referenced/index2.js @@ -0,0 +1,3 @@ +import './async.js'; +import './shared.js'; + diff --git a/packages/core/integration-tests/test/integration/html-shared-referenced/package.json b/packages/core/integration-tests/test/integration/html-shared-referenced/package.json new file mode 100644 index 00000000000..c5f216e03fd --- /dev/null +++ b/packages/core/integration-tests/test/integration/html-shared-referenced/package.json @@ -0,0 +1,5 @@ +{ + "@parcel/bundler-default": { + "minBundleSize": 0 + } +} diff --git a/packages/core/integration-tests/test/integration/html-shared-referenced/shared.js b/packages/core/integration-tests/test/integration/html-shared-referenced/shared.js new file mode 100644 index 00000000000..6c111b7bca1 --- /dev/null +++ b/packages/core/integration-tests/test/integration/html-shared-referenced/shared.js @@ -0,0 +1 @@ +import('./async.js'); diff --git a/packages/core/integration-tests/test/integration/html-shared-referenced/yarn.lock b/packages/core/integration-tests/test/integration/html-shared-referenced/yarn.lock new file mode 100644 index 00000000000..e69de29bb2d diff --git a/packages/core/integration-tests/test/integration/html-sync-async-asset/index.html b/packages/core/integration-tests/test/integration/html-sync-async-asset/index.html new file mode 100644 index 00000000000..8ef9a70b216 --- /dev/null +++ b/packages/core/integration-tests/test/integration/html-sync-async-asset/index.html @@ -0,0 +1 @@ + diff --git a/packages/core/integration-tests/test/integration/html-sync-async-asset/index.js b/packages/core/integration-tests/test/integration/html-sync-async-asset/index.js new file mode 100644 index 00000000000..afd241a43f8 --- /dev/null +++ b/packages/core/integration-tests/test/integration/html-sync-async-asset/index.js @@ -0,0 +1,4 @@ +import t from "./test.js"; + +import("./other.js") + .then((v) => v.default) diff --git a/packages/core/integration-tests/test/integration/html-sync-async-asset/other.js b/packages/core/integration-tests/test/integration/html-sync-async-asset/other.js new file mode 100644 index 00000000000..6be91ea6cc7 --- /dev/null +++ b/packages/core/integration-tests/test/integration/html-sync-async-asset/other.js @@ -0,0 +1 @@ +export default import("./test.js"); diff --git a/packages/core/integration-tests/test/integration/html-sync-async-asset/test.js b/packages/core/integration-tests/test/integration/html-sync-async-asset/test.js new file mode 100644 index 00000000000..58c57157d36 --- /dev/null +++ b/packages/core/integration-tests/test/integration/html-sync-async-asset/test.js @@ -0,0 +1 @@ +export default "test"; diff --git a/packages/core/integration-tests/test/integration/js-import-this/index.js b/packages/core/integration-tests/test/integration/js-import-this/index.js new file mode 100644 index 00000000000..7b8c0725ad5 --- /dev/null +++ b/packages/core/integration-tests/test/integration/js-import-this/index.js @@ -0,0 +1,17 @@ +import returnThisDefault, { returnThis } from "./other.js"; +import * as other from "./other.js"; + +import returnThisWrappedDefault, { returnThis as returnThisWrapped } from "./other-wrapped.js"; +import * as otherWrapped from "./other-wrapped.js"; + +let result = { + unwrappedNamed: returnThis(), + unwrappedDefault: returnThisDefault(), + unwrappedNamespace: other.returnThis(), + wrappedNamed: returnThisWrapped(), + wrappedDefault: returnThisWrappedDefault(), + wrappedNamespace: otherWrapped.returnThis(), +}; + +output = result; +export default result; diff --git a/packages/core/integration-tests/test/integration/js-import-this/other-wrapped.js b/packages/core/integration-tests/test/integration/js-import-this/other-wrapped.js new file mode 100644 index 00000000000..8eb0cfb8f4f --- /dev/null +++ b/packages/core/integration-tests/test/integration/js-import-this/other-wrapped.js @@ -0,0 +1,13 @@ +import * as ns from "./other-wrapped.js"; + +let y = typeof module !== "undefined" ? module : {}; + +export function returnThis() { + if (y != null) { + return [this === undefined, this === ns]; + } else { + throw new Error(); + } +} + +export default returnThis; diff --git a/packages/core/integration-tests/test/integration/js-import-this/other.js b/packages/core/integration-tests/test/integration/js-import-this/other.js new file mode 100644 index 00000000000..cf6924b8f63 --- /dev/null +++ b/packages/core/integration-tests/test/integration/js-import-this/other.js @@ -0,0 +1,7 @@ +import * as ns from "./other.js"; + +export function returnThis() { + return [this === undefined, this === ns]; +} + +export default returnThis; diff --git a/packages/core/integration-tests/test/integration/scope-hoisting/es6/export-intermediate-wrapped-reexports/index.html b/packages/core/integration-tests/test/integration/scope-hoisting/es6/export-intermediate-wrapped-reexports/index.html new file mode 100644 index 00000000000..233f9c37633 --- /dev/null +++ b/packages/core/integration-tests/test/integration/scope-hoisting/es6/export-intermediate-wrapped-reexports/index.html @@ -0,0 +1,6 @@ + + + + + + diff --git a/packages/core/integration-tests/test/integration/scope-hoisting/es6/re-export-default-hybrid/a.js b/packages/core/integration-tests/test/integration/scope-hoisting/es6/re-export-default-hybrid/a.js new file mode 100644 index 00000000000..7f129231c85 --- /dev/null +++ b/packages/core/integration-tests/test/integration/scope-hoisting/es6/re-export-default-hybrid/a.js @@ -0,0 +1,3 @@ +import b from './b'; + +output = b.foo + b.bar; diff --git a/packages/core/integration-tests/test/integration/scope-hoisting/es6/re-export-default-hybrid/b.js b/packages/core/integration-tests/test/integration/scope-hoisting/es6/re-export-default-hybrid/b.js new file mode 100644 index 00000000000..1d134aa51d1 --- /dev/null +++ b/packages/core/integration-tests/test/integration/scope-hoisting/es6/re-export-default-hybrid/b.js @@ -0,0 +1,2 @@ +import * as c from './c'; +export default c; diff --git a/packages/core/integration-tests/test/integration/scope-hoisting/es6/re-export-default-hybrid/c.js b/packages/core/integration-tests/test/integration/scope-hoisting/es6/re-export-default-hybrid/c.js new file mode 100644 index 00000000000..29a35de9403 --- /dev/null +++ b/packages/core/integration-tests/test/integration/scope-hoisting/es6/re-export-default-hybrid/c.js @@ -0,0 +1,2 @@ +export foo from './d'; +export const bar = require('./d'); diff --git a/packages/core/integration-tests/test/integration/scope-hoisting/es6/re-export-default-hybrid/d.js b/packages/core/integration-tests/test/integration/scope-hoisting/es6/re-export-default-hybrid/d.js new file mode 100644 index 00000000000..bd816eaba4c --- /dev/null +++ b/packages/core/integration-tests/test/integration/scope-hoisting/es6/re-export-default-hybrid/d.js @@ -0,0 +1 @@ +module.exports = 1; diff --git a/packages/core/integration-tests/test/integration/scope-hoisting/es6/re-export-hybrid/a.js b/packages/core/integration-tests/test/integration/scope-hoisting/es6/re-export-hybrid/a.js new file mode 100644 index 00000000000..d1d39e3bc64 --- /dev/null +++ b/packages/core/integration-tests/test/integration/scope-hoisting/es6/re-export-hybrid/a.js @@ -0,0 +1,3 @@ +import {foo, bar} from './b'; + +output = foo + bar; diff --git a/packages/core/integration-tests/test/integration/scope-hoisting/es6/re-export-hybrid/b.js b/packages/core/integration-tests/test/integration/scope-hoisting/es6/re-export-hybrid/b.js new file mode 100644 index 00000000000..34a28aa08d0 --- /dev/null +++ b/packages/core/integration-tests/test/integration/scope-hoisting/es6/re-export-hybrid/b.js @@ -0,0 +1 @@ +export * from './c'; diff --git a/packages/core/integration-tests/test/integration/scope-hoisting/es6/re-export-hybrid/c.js b/packages/core/integration-tests/test/integration/scope-hoisting/es6/re-export-hybrid/c.js new file mode 100644 index 00000000000..29a35de9403 --- /dev/null +++ b/packages/core/integration-tests/test/integration/scope-hoisting/es6/re-export-hybrid/c.js @@ -0,0 +1,2 @@ +export foo from './d'; +export const bar = require('./d'); diff --git a/packages/core/integration-tests/test/integration/scope-hoisting/es6/re-export-hybrid/d.js b/packages/core/integration-tests/test/integration/scope-hoisting/es6/re-export-hybrid/d.js new file mode 100644 index 00000000000..bd816eaba4c --- /dev/null +++ b/packages/core/integration-tests/test/integration/scope-hoisting/es6/re-export-hybrid/d.js @@ -0,0 +1 @@ +module.exports = 1; diff --git a/packages/core/integration-tests/test/integration/scope-hoisting/es6/rename-helpers/a.js b/packages/core/integration-tests/test/integration/scope-hoisting/es6/rename-helpers/a.js new file mode 100644 index 00000000000..da98e4e5420 --- /dev/null +++ b/packages/core/integration-tests/test/integration/scope-hoisting/es6/rename-helpers/a.js @@ -0,0 +1,4 @@ +import {B} from './b.js'; +import {C} from './c.js'; + +output = [new B()[Symbol.toStringTag], new C()[Symbol.toStringTag]]; diff --git a/packages/core/integration-tests/test/integration/scope-hoisting/es6/rename-helpers/b.js b/packages/core/integration-tests/test/integration/scope-hoisting/es6/rename-helpers/b.js new file mode 100644 index 00000000000..b55a15508b7 --- /dev/null +++ b/packages/core/integration-tests/test/integration/scope-hoisting/es6/rename-helpers/b.js @@ -0,0 +1,5 @@ +export class B { + get [Symbol.toStringTag]() { + return '1'; + } +} diff --git a/packages/core/integration-tests/test/integration/scope-hoisting/es6/rename-helpers/c.js b/packages/core/integration-tests/test/integration/scope-hoisting/es6/rename-helpers/c.js new file mode 100644 index 00000000000..3f9efc572e8 --- /dev/null +++ b/packages/core/integration-tests/test/integration/scope-hoisting/es6/rename-helpers/c.js @@ -0,0 +1,5 @@ +export class C { + get [Symbol.toStringTag]() { + return '2'; + } +} diff --git a/packages/core/integration-tests/test/integration/scope-hoisting/es6/rename-helpers/package.json b/packages/core/integration-tests/test/integration/scope-hoisting/es6/rename-helpers/package.json new file mode 100644 index 00000000000..601436668c0 --- /dev/null +++ b/packages/core/integration-tests/test/integration/scope-hoisting/es6/rename-helpers/package.json @@ -0,0 +1,3 @@ +{ + "browserslist": "Chrome 50" +} diff --git a/packages/core/integration-tests/test/integration/scope-hoisting/es6/rename-helpers/yarn.lock b/packages/core/integration-tests/test/integration/scope-hoisting/es6/rename-helpers/yarn.lock new file mode 100644 index 00000000000..e69de29bb2d diff --git a/packages/core/integration-tests/test/integration/sourcemap-comments/index.js b/packages/core/integration-tests/test/integration/sourcemap-comments/index.js index 370f721276c..c764d6be5fa 100644 --- a/packages/core/integration-tests/test/integration/sourcemap-comments/index.js +++ b/packages/core/integration-tests/test/integration/sourcemap-comments/index.js @@ -4,6 +4,6 @@ console.log('bar'); /* block comment line */ console.log('baz'); /* multi line - block comment + block comment */ console.log('idhf'); diff --git a/packages/core/integration-tests/test/integration/ts-types/main/expected.d.ts b/packages/core/integration-tests/test/integration/ts-types/main/expected.d.ts index ad621f4099b..f1f1655c63e 100644 --- a/packages/core/integration-tests/test/integration/ts-types/main/expected.d.ts +++ b/packages/core/integration-tests/test/integration/ts-types/main/expected.d.ts @@ -11,5 +11,8 @@ export default function test(params: Params): number; export function foo(): number; export var x: number; export var hi: number; +export declare module mod { + function bar(): number; +} //# sourceMappingURL=types.d.ts.map diff --git a/packages/core/integration-tests/test/integration/ts-types/main/index.ts b/packages/core/integration-tests/test/integration/ts-types/main/index.ts index 62ea94b0aaf..bd198d08f2a 100644 --- a/packages/core/integration-tests/test/integration/ts-types/main/index.ts +++ b/packages/core/integration-tests/test/integration/ts-types/main/index.ts @@ -23,3 +23,9 @@ export function foo() { var x = 2; var p = x + 2, q = 3; export {p as hi, x}; + +export module mod { + export function bar() { + return 2; + } +} \ No newline at end of file diff --git a/packages/core/integration-tests/test/javascript.js b/packages/core/integration-tests/test/javascript.js index 2ca75a87198..c0c0c6d50f3 100644 --- a/packages/core/integration-tests/test/javascript.js +++ b/packages/core/integration-tests/test/javascript.js @@ -1036,31 +1036,42 @@ describe('javascript', function () { let b = await bundle( path.join(__dirname, '/integration/workers-module/index.js'), { + mode: 'production', defaultTargetOptions: { + shouldOptimize: false, shouldScopeHoist: true, }, }, ); - assertBundles(b, [ { - assets: ['dedicated-worker.js', 'index.js'], + assets: ['dedicated-worker.js'], }, { name: 'index.js', - assets: ['index.js', 'bundle-url.js', 'get-worker-url.js'], + assets: [ + 'index.js', + 'bundle-url.js', + 'get-worker-url.js', + 'bundle-manifest.js', + ], }, { - assets: ['shared-worker.js', 'index.js'], + assets: ['shared-worker.js'], + }, + { + assets: ['index.js'], }, ]); let dedicated, shared; b.traverseBundles((bundle, ctx, traversal) => { - if (bundle.getMainEntry().filePath.endsWith('shared-worker.js')) { + let mainEntry = bundle.getMainEntry(); + if (mainEntry && mainEntry.filePath.endsWith('shared-worker.js')) { shared = bundle; } else if ( - bundle.getMainEntry().filePath.endsWith('dedicated-worker.js') + mainEntry && + mainEntry.filePath.endsWith('dedicated-worker.js') ) { dedicated = bundle; } @@ -1073,8 +1084,8 @@ describe('javascript', function () { let main = await outputFS.readFile(b.getBundles()[0].filePath, 'utf8'); dedicated = await outputFS.readFile(dedicated.filePath, 'utf8'); shared = await outputFS.readFile(shared.filePath, 'utf8'); - assert(/new Worker(.*?, {[\n\s]+type: 'module'[\n\s]+})/.test(main)); - assert(/new SharedWorker(.*?, {[\n\s]+type: 'module'[\n\s]+})/.test(main)); + assert(/new Worker(.*?, {[\n\s]+type: "module"[\n\s]+})/.test(main)); + assert(/new SharedWorker(.*?, {[\n\s]+type: "module"[\n\s]+})/.test(main)); }); for (let shouldScopeHoist of [true, false]) { @@ -1086,7 +1097,9 @@ describe('javascript', function () { let b = await bundle( path.join(__dirname, '/integration/workers-module/index.js'), { + mode: 'production', defaultTargetOptions: { + shouldOptimize: false, shouldScopeHoist, engines: { browsers: '>= 0.25%', @@ -1097,31 +1110,36 @@ describe('javascript', function () { assertBundles(b, [ { - assets: [ - 'dedicated-worker.js', - !shouldScopeHoist && 'esmodule-helpers.js', - 'index.js', - ].filter(Boolean), + assets: ['dedicated-worker.js'], }, { name: 'index.js', - assets: ['index.js', 'bundle-url.js', 'get-worker-url.js'], + assets: [ + 'index.js', + 'bundle-url.js', + 'get-worker-url.js', + 'bundle-manifest.js', + ], }, { assets: [ !shouldScopeHoist && 'esmodule-helpers.js', - 'shared-worker.js', 'index.js', ].filter(Boolean), }, + { + assets: ['shared-worker.js'], + }, ]); let dedicated, shared; b.traverseBundles((bundle, ctx, traversal) => { - if (bundle.getMainEntry().filePath.endsWith('shared-worker.js')) { + let mainEntry = bundle.getMainEntry(); + if (mainEntry && mainEntry.filePath.endsWith('shared-worker.js')) { shared = bundle; } else if ( - bundle.getMainEntry().filePath.endsWith('dedicated-worker.js') + mainEntry && + mainEntry.filePath.endsWith('dedicated-worker.js') ) { dedicated = bundle; } @@ -1220,8 +1238,8 @@ describe('javascript', function () { ); let main = await outputFS.readFile(b.getBundles()[0].filePath, 'utf8'); - assert(/new Worker(.*?, {[\n\s]+name: 'worker'[\n\s]+})/.test(main)); - assert(/new SharedWorker(.*?, {[\n\s]+name: 'shared'[\n\s]+})/.test(main)); + assert(/new Worker(.*?, {[\n\s]+name: "worker"[\n\s]+})/.test(main)); + assert(/new SharedWorker(.*?, {[\n\s]+name: "shared"[\n\s]+})/.test(main)); }); it('should error if importing in a worker without type: module', async function () { @@ -1420,7 +1438,7 @@ describe('javascript', function () { ]); let res = await outputFS.readFile(b.getBundles()[0].filePath, 'utf8'); - assert(res.includes("importScripts('imported.js')")); + assert(res.includes(`importScripts("imported.js")`)); }); it('should ignore importScripts in script workers when not passed a string literal', async function () { @@ -1466,7 +1484,7 @@ describe('javascript', function () { ]); let res = await outputFS.readFile(b.getBundles()[1].filePath, 'utf8'); - assert(res.includes("importScripts('https://unpkg.com/parcel')")); + assert(res.includes(`importScripts("https://unpkg.com/parcel")`)); }); it('should support bundling service-workers', async function () { @@ -1541,7 +1559,7 @@ describe('javascript', function () { let main = bundles.find(b => !b.env.isWorker()); let mainContents = await outputFS.readFile(main.filePath, 'utf8'); assert( - /navigator.serviceWorker.register\(.*?, {[\n\s]*scope: 'foo'[\n\s]*}\)/.test( + /navigator.serviceWorker.register\(.*?, {[\n\s]*scope: "foo"[\n\s]*}\)/.test( mainContents, ), ); @@ -1977,6 +1995,7 @@ describe('javascript', function () { }); it('should contain duplicate assets in workers when in development', async () => { + if (process.env.PARCEL_TEST_EXPERIMENTAL_BUNDLER) return; let b = await bundle( path.join(__dirname, '/integration/worker-shared/index.js'), {mode: 'development'}, @@ -4365,7 +4384,7 @@ describe('javascript', function () { let res = await run(b); assert.equal( res.default, - "

test

\n\n", + `

test

\n\n`, ); }); @@ -5248,6 +5267,21 @@ describe('javascript', function () { assert.deepEqual(res.default, 'x: 123'); }); + it('should call named imports without this context', async function () { + let b = await bundle( + path.join(__dirname, 'integration/js-import-this/index.js'), + ); + let res = await run(b, {output: null}, {strict: true}); + assert.deepEqual(res.default, { + unwrappedNamed: [true, false], + unwrappedDefault: [true, false], + unwrappedNamespace: [false, true], + wrappedNamed: [true, false], + wrappedDefault: [true, false], + wrappedNamespace: [false, true], + }); + }); + it('should only replace free references to require', async () => { let b = await bundle( path.join(__dirname, 'integration/js-require-free/index.js'), @@ -5522,7 +5556,7 @@ describe('javascript', function () { }, end: { line: 1, - column: 0, + column: 1, }, }, ], diff --git a/packages/core/integration-tests/test/output-formats.js b/packages/core/integration-tests/test/output-formats.js index 7b71ef26b90..01c20793e13 100644 --- a/packages/core/integration-tests/test/output-formats.js +++ b/packages/core/integration-tests/test/output-formats.js @@ -191,7 +191,7 @@ describe('output formats', function () { let dist = await outputFS.readFile(b.getBundles()[0].filePath, 'utf8'); assert(dist.includes('= require("lodash")')); - assert(dist.includes('= ($parcel$interopDefault(')); + assert(dist.includes('= (0, ($parcel$interopDefault(')); assert(/var {add: \s*\$.+?\$add\s*} = lodash/); assert.equal((await run(b)).bar, 6); }); @@ -987,7 +987,7 @@ describe('output formats', function () { assert.equal(await res.output, 4); }); - it('should support use an import polyfill for older browsers', async function () { + it('should support using an import polyfill for older browsers', async function () { let b = await bundle( path.join(__dirname, '/integration/formats/esm-browser/index.html'), { @@ -1024,7 +1024,7 @@ describe('output formats', function () { .find(bundle => bundle.name.startsWith('async')); assert( new RegExp( - "getBundleURL\\('[a-zA-Z0-9]+'\\) \\+ \"" + + `getBundleURL\\("[a-zA-Z0-9]+"\\) \\+ "` + path.basename(asyncBundle.filePath) + '"', ).test(entry), diff --git a/packages/core/integration-tests/test/scope-hoisting.js b/packages/core/integration-tests/test/scope-hoisting.js index 63ddafd760a..cb669087c7a 100644 --- a/packages/core/integration-tests/test/scope-hoisting.js +++ b/packages/core/integration-tests/test/scope-hoisting.js @@ -169,6 +169,23 @@ describe('scope hoisting', function () { assert.equal(output, 2); }); + it('supports renaming helpers inserted during transpiling', async function () { + let b = await bundle( + path.join( + __dirname, + '/integration/scope-hoisting/es6/rename-helpers/a.js', + ), + ); + let contents = await outputFS.readFile( + b.getBundles()[0].filePath, + 'utf8', + ); + assert(/let \S* = Symbol.toStringTag;/.test(contents)); + + let output = await run(b); + assert.deepEqual(output, ['1', '2']); + }); + it('supports renaming imports', async function () { let b = await bundle( path.join( @@ -1997,6 +2014,28 @@ describe('scope hoisting', function () { assert(new output[3]() instanceof output[2]); }); + it('should support chained reexports from hybrid modules', async function () { + let b = await bundle( + path.join( + __dirname, + '/integration/scope-hoisting/es6/re-export-hybrid/a.js', + ), + ); + let output = await run(b); + assert.strictEqual(output, 2); + }); + + it('should support chained reexports as default from hybrid modules', async function () { + let b = await bundle( + path.join( + __dirname, + '/integration/scope-hoisting/es6/re-export-default-hybrid/a.js', + ), + ); + let output = await run(b); + assert.strictEqual(output, 2); + }); + it('support chained namespace reexports of CommonJS', async function () { let b = await bundle( path.join( @@ -5041,7 +5080,11 @@ describe('scope hoisting', function () { ], }, {assets: ['dep.js']}, - {assets: ['async-has-dep.js', 'dep.js', 'get-dep.js']}, + { + assets: process.env.PARCEL_TEST_EXPERIMENTAL_BUNDLER + ? ['async-has-dep.js'] + : ['async-has-dep.js', 'dep.js', 'get-dep.js'], + }, {assets: ['get-dep.js']}, ]); @@ -5258,6 +5301,23 @@ describe('scope hoisting', function () { assert.deepEqual(res, 'x: 123'); }); + it('should call named imports without this context', async function () { + let b = await bundle( + path.join(__dirname, 'integration/js-import-this/index.js'), + ); + let res = await run(b, {output: null}, {strict: true}); + assert.deepEqual(res, { + unwrappedNamed: [true, false], + unwrappedDefault: [true, false], + // TODO: unwrappedNamespace should actually be `[false, true]` but we optimize + // the `ns.foo` expression into a named import, so that namespace isn't available anymore. + unwrappedNamespace: [true, false], + wrappedNamed: [true, false], + wrappedDefault: [true, false], + wrappedNamespace: [false, true], + }); + }); + it('should insert the prelude for sibling bundles referenced in HTML', async function () { let b = await bundle( path.join( diff --git a/packages/core/integration-tests/test/server.js b/packages/core/integration-tests/test/server.js index 0c820c4ff7c..563a81d8370 100644 --- a/packages/core/integration-tests/test/server.js +++ b/packages/core/integration-tests/test/server.js @@ -10,8 +10,8 @@ import { outputFS, overlayFS, ncp, + request as get, } from '@parcel/test-utils'; -import http from 'http'; import https from 'https'; import getPort from 'get-port'; import type {BuildEvent} from '@parcel/types'; @@ -22,32 +22,6 @@ const config = path.join( './integration/custom-configs/.parcelrc-dev-server', ); -function get(file, port, client = http) { - return new Promise((resolve, reject) => { - // $FlowFixMe - client.get( - { - hostname: 'localhost', - port: port, - path: file, - rejectUnauthorized: false, - }, - res => { - res.setEncoding('utf8'); - let data = ''; - res.on('data', c => (data += c)); - res.on('end', () => { - if (res.statusCode !== 200) { - return reject({statusCode: res.statusCode, data}); - } - - resolve(data); - }); - }, - ); - }); -} - describe('server', function () { let subscription; diff --git a/packages/core/integration-tests/test/sourcemaps.js b/packages/core/integration-tests/test/sourcemaps.js index 3594231d2c4..42de11f19ca 100644 --- a/packages/core/integration-tests/test/sourcemaps.js +++ b/packages/core/integration-tests/test/sourcemaps.js @@ -1,6 +1,9 @@ +// @flow import assert from 'assert'; +import invariant from 'assert'; import path from 'path'; import SourceMap from '@parcel/source-map'; +import type {InitialParcelOptions} from '@parcel/types'; import { bundle as _bundle, inputFS, @@ -11,11 +14,11 @@ import { mergeParcelOptions, } from '@parcel/test-utils'; import {loadSourceMapUrl} from '@parcel/utils'; +import nullthrows from 'nullthrows'; -const bundle = (name, opts = {}) => { +const bundle = (name, opts?: InitialParcelOptions) => { return _bundle( name, - // $FlowFixMe mergeParcelOptions( { defaultTargetOptions: { @@ -43,7 +46,15 @@ function checkSourceMapping({ generatedStr = str, sourcePath, msg = '', -}) { +}: {| + map: SourceMap, + source: string, + generated: string, + str: string, + generatedStr?: string, + sourcePath: string, + msg?: string, +|}) { assert( generated.indexOf(generatedStr) !== -1, "'" + generatedStr + "' not found in generated code", @@ -90,13 +101,14 @@ function checkSourceMapping({ mapping = map.indexedMappingToStringMapping(mappings[closestIndex]); } - assert(mapping, "no mapping for '" + str + "'" + msg); + invariant(mapping, "no mapping for '" + str + "'" + msg); let generatedDiff = { line: generatedPosition.line - mapping.generated.line, column: generatedPosition.column - mapping.generated.column, }; + invariant(mapping.original); let computedSourcePosition = { line: mapping.original.line + generatedDiff.line, column: mapping.original.column + generatedDiff.column, @@ -610,7 +622,9 @@ describe('sourcemaps', function () { '/integration/sourcemap-css/style.css', ); - await bundle(inputFilePath, {minify}); + await bundle(inputFilePath, { + defaultTargetOptions: {shouldOptimize: minify}, + }); let distDir = path.join(__dirname, '../dist/'); let filename = path.join(distDir, 'style.css'); let raw = await outputFS.readFile(filename, 'utf8'); @@ -656,7 +670,9 @@ describe('sourcemaps', function () { '/integration/sourcemap-css-import/style.css', ); - await bundle(inputFilePath, {minify}); + await bundle(inputFilePath, { + defaultTargetOptions: {shouldOptimize: minify}, + }); let distDir = path.join(__dirname, '../dist/'); let filename = path.join(distDir, 'style.css'); let raw = await outputFS.readFile(filename, 'utf8'); @@ -673,22 +689,33 @@ describe('sourcemaps', function () { sourceMap.addVLQMap(map); let mapData = sourceMap.getMap(); - assert.deepEqual(mapData.sources, [ - 'other-style.css', - 'another-style.css', - 'style.css', - ]); + let sources = minify + ? ['style.css', 'other-style.css', 'another-style.css'] + : ['other-style.css', 'another-style.css', 'style.css']; + assert.deepEqual(mapData.sources, sources); let otherStyle = await inputFS.readFile( - path.join(path.dirname(filename), map.sourceRoot, map.sources[0]), + path.join( + path.dirname(filename), + map.sourceRoot, + map.sources[sources.indexOf('other-style.css')], + ), 'utf-8', ); let anotherStyle = await inputFS.readFile( - path.join(path.dirname(filename), map.sourceRoot, map.sources[1]), + path.join( + path.dirname(filename), + map.sourceRoot, + map.sources[sources.indexOf('another-style.css')], + ), 'utf-8', ); let style = await inputFS.readFile( - path.join(path.dirname(filename), map.sourceRoot, map.sources[2]), + path.join( + path.dirname(filename), + map.sourceRoot, + map.sources[sources.indexOf('style.css')], + ), 'utf8', ); @@ -1123,7 +1150,9 @@ describe('sourcemaps', function () { __dirname, '/integration/sourcemap-css-existing/style.css', ); - let b = await bundle(sourceFilename, {minify}); + let b = await bundle(sourceFilename, { + defaultTargetOptions: {shouldOptimize: minify}, + }); let filename = b.getBundles()[0].filePath; let raw = await outputFS.readFile(filename, 'utf8'); @@ -1228,6 +1257,7 @@ describe('sourcemaps', function () { source: input, generated: raw, str: "console.log('foo')", + generatedStr: `console.log("foo")`, sourcePath, }); @@ -1236,6 +1266,7 @@ describe('sourcemaps', function () { source: input, generated: raw, str: "console.log('bar')", + generatedStr: `console.log("bar")`, sourcePath, }); @@ -1244,6 +1275,7 @@ describe('sourcemaps', function () { source: input, generated: raw, str: "console.log('baz')", + generatedStr: `console.log("baz")`, sourcePath, }); @@ -1252,6 +1284,7 @@ describe('sourcemaps', function () { source: input, generated: raw, str: "console.log('idhf')", + generatedStr: `console.log("idhf")`, sourcePath, }); }); @@ -1281,7 +1314,7 @@ describe('sourcemaps', function () { let sourceMap = new SourceMap('/'); sourceMap.addVLQMap(map); let sourcePath = 'index.js'; - let sourceContent = sourceMap.getSourceContent(sourcePath); + let sourceContent = nullthrows(sourceMap.getSourceContent(sourcePath)); checkSourceMapping({ map: sourceMap, @@ -1326,7 +1359,7 @@ describe('sourcemaps', function () { let sourceMap = new SourceMap('/'); sourceMap.addVLQMap(map); let sourcePath = 'index.tsx'; - let sourceContent = sourceMap.getSourceContent(sourcePath); + let sourceContent = nullthrows(sourceMap.getSourceContent(sourcePath)); checkSourceMapping({ map: sourceMap, @@ -1390,8 +1423,8 @@ describe('sourcemaps', function () { map: sourceMap, source: sourceContent, generated: raw, - str: "foo = 'Lorem ipsum", - generatedStr: "foo = 'Lorem ipsum", + str: `foo = 'Lorem ipsum`, + generatedStr: `foo = "Lorem ipsum`, sourcePath, }); }); diff --git a/packages/core/integration-tests/test/svg.js b/packages/core/integration-tests/test/svg.js index dd44e8dd6ec..f8e2d06ad5f 100644 --- a/packages/core/integration-tests/test/svg.js +++ b/packages/core/integration-tests/test/svg.js @@ -195,7 +195,7 @@ describe('svg', function () { ), ); assert(svg.includes(' + <% } %> diff --git a/packages/reporters/dev-server/src/types.js.flow b/packages/reporters/dev-server/src/types.js.flow index 81874f3e6f9..afcac1274f9 100644 --- a/packages/reporters/dev-server/src/types.js.flow +++ b/packages/reporters/dev-server/src/types.js.flow @@ -1,5 +1,5 @@ // @flow -import type {ServerOptions, PluginLogger} from '@parcel/types'; +import type {ServerOptions, PluginLogger, HMROptions} from '@parcel/types'; import type {FileSystem} from '@parcel/fs'; import type {HTTPServer} from '@parcel/utils'; import { @@ -27,6 +27,7 @@ export type DevServerOptions = {| inputFS: FileSystem, outputFS: FileSystem, logger: PluginLogger, + hmrOptions: ?HMROptions, |}; // TODO: Figure out if there is a node.js type that could be imported with a complete ServerError diff --git a/packages/runtimes/hmr/src/loaders/.babelrc b/packages/runtimes/hmr/src/loaders/.babelrc deleted file mode 100644 index b3a9d5d0a61..00000000000 --- a/packages/runtimes/hmr/src/loaders/.babelrc +++ /dev/null @@ -1,9 +0,0 @@ -{ - "presets": [ - ["@babel/preset-env", { - "targets": { - "ie": 11 - } - }] - ] -} diff --git a/packages/runtimes/hmr/src/loaders/hmr-runtime.js b/packages/runtimes/hmr/src/loaders/hmr-runtime.js index c51d5df8bf5..933b3d31a2a 100644 --- a/packages/runtimes/hmr/src/loaders/hmr-runtime.js +++ b/packages/runtimes/hmr/src/loaders/hmr-runtime.js @@ -1,5 +1,5 @@ // @flow -/* global HMR_HOST, HMR_PORT, HMR_ENV_HASH, HMR_SECURE, chrome, browser */ +/* global HMR_HOST, HMR_PORT, HMR_ENV_HASH, HMR_SECURE, chrome, browser, importScripts */ /*:: import type { @@ -92,8 +92,18 @@ if ((!parent || !parent.isParcelRequire) && typeof WebSocket !== 'undefined') { var ws = new WebSocket( protocol + '://' + hostname + (port ? ':' + port : '') + '/', ); + + // Safari doesn't support sourceURL in error stacks. + // eval may also be disabled via CSP, so do a quick check. + var supportsSourceURL = false; + try { + (0, eval)('throw new Error("test"); //# sourceURL=test.js'); + } catch (err) { + supportsSourceURL = err.stack.includes('test.js'); + } + // $FlowFixMe - ws.onmessage = function (event /*: {data: string, ...} */) { + ws.onmessage = async function (event /*: {data: string, ...} */) { checkedAssets = ({} /*: {|[string]: boolean|} */); acceptedAssets = ({} /*: {|[string]: boolean|} */); assetsToAccept = []; @@ -120,9 +130,15 @@ if ((!parent || !parent.isParcelRequire) && typeof WebSocket !== 'undefined') { if (handled) { console.clear(); - assets.forEach(function (asset) { - hmrApply(module.bundle.root, asset); - }); + // Dispatch custom event so other runtimes (e.g React Refresh) are aware. + if ( + typeof window !== 'undefined' && + typeof CustomEvent !== 'undefined' + ) { + window.dispatchEvent(new CustomEvent('parcelhmraccept')); + } + + await hmrApplyUpdates(assets); for (var i = 0; i < assetsToAccept.length; i++) { var id = assetsToAccept[i][1]; @@ -198,7 +214,17 @@ function createErrorOverlay(diagnostics) { '
'; for (let diagnostic of diagnostics) { - let stack = diagnostic.codeframe ? diagnostic.codeframe : diagnostic.stack; + let stack = diagnostic.frames.length + ? diagnostic.frames.reduce((p, frame) => { + return `${p} +${ + frame.location + } +${frame.code}`; + }, '') + : diagnostic.stack; errorHTML += `
@@ -299,6 +325,59 @@ function reloadCSS() { }, 50); } +async function hmrApplyUpdates(assets) { + global.parcelHotUpdate = Object.create(null); + + let scriptsToRemove; + try { + // If sourceURL comments aren't supported in eval, we need to load + // the update from the dev server over HTTP so that stack traces + // are correct in errors/logs. This is much slower than eval, so + // we only do it if needed (currently just Safari). + // https://bugs.webkit.org/show_bug.cgi?id=137297 + // This path is also taken if a CSP disallows eval. + if (!supportsSourceURL) { + let promises = assets.map(asset => { + if (asset.type === 'js') { + if (typeof document !== 'undefined') { + let script = document.createElement('script'); + script.src = asset.url; + return new Promise((resolve, reject) => { + script.onload = () => resolve(script); + script.onerror = reject; + document.head?.appendChild(script); + }); + } else if (typeof importScripts === 'function') { + return new Promise((resolve, reject) => { + try { + importScripts(asset.url); + } catch (err) { + reject(err); + } + }); + } + } + }); + + scriptsToRemove = await Promise.all(promises); + } + + assets.forEach(function (asset) { + hmrApply(module.bundle.root, asset); + }); + } finally { + delete global.parcelHotUpdate; + + if (scriptsToRemove) { + scriptsToRemove.forEach(script => { + if (script) { + document.head?.removeChild(script); + } + }); + } + } +} + function hmrApply(bundle /*: ParcelRequire */, asset /*: HMRAsset */) { var modules = bundle.modules; if (!modules) { @@ -325,7 +404,13 @@ function hmrApply(bundle /*: ParcelRequire */, asset /*: HMRAsset */) { } } - var fn = new Function('require', 'module', 'exports', asset.output); + if (supportsSourceURL) { + // Global eval. We would use `new Function` here but browser + // support for source maps is better with eval. + (0, eval)(asset.output); + } + + let fn = global.parcelHotUpdate[asset.id]; modules[asset.id] = [fn, deps]; } else if (bundle.parent) { hmrApply(bundle.parent, asset); diff --git a/packages/runtimes/js/src/JSRuntime.js b/packages/runtimes/js/src/JSRuntime.js index f7751dbe971..a05f1cf7a13 100644 --- a/packages/runtimes/js/src/JSRuntime.js +++ b/packages/runtimes/js/src/JSRuntime.js @@ -552,6 +552,11 @@ function getRegisterCode( idToName[bundle.publicId] = path.basename(nullthrows(bundle.name)); if (bundle !== entryBundle && isNewContext(bundle, bundleGraph)) { + for (let referenced of bundleGraph.getReferencedBundles(bundle)) { + idToName[referenced.publicId] = path.basename( + nullthrows(referenced.name), + ); + } // New contexts have their own manifests, so there's no need to continue. actions.skipChildren(); } diff --git a/packages/runtimes/react-refresh/package.json b/packages/runtimes/react-refresh/package.json index c64fc7f9298..5c60b007fe3 100644 --- a/packages/runtimes/react-refresh/package.json +++ b/packages/runtimes/react-refresh/package.json @@ -22,6 +22,7 @@ "dependencies": { "@parcel/plugin": "2.5.0", "@parcel/utils": "2.5.0", - "react-refresh": "^0.9.0" + "react-refresh": "^0.9.0", + "react-error-overlay": "6.0.9" } } diff --git a/packages/runtimes/react-refresh/src/ReactRefreshRuntime.js b/packages/runtimes/react-refresh/src/ReactRefreshRuntime.js index 84586fd669f..d9953766377 100644 --- a/packages/runtimes/react-refresh/src/ReactRefreshRuntime.js +++ b/packages/runtimes/react-refresh/src/ReactRefreshRuntime.js @@ -5,6 +5,7 @@ import {loadConfig} from '@parcel/utils'; const CODE = ` var Refresh = require('react-refresh/runtime'); +var ErrorOverlay = require('react-error-overlay'); Refresh.injectIntoGlobalHook(window); window.$RefreshReg$ = function() {}; @@ -12,7 +13,21 @@ window.$RefreshSig$ = function() { return function(type) { return type; }; -};`; +}; + +ErrorOverlay.setEditorHandler(function editorHandler(errorLocation) { + let file = \`\${errorLocation.fileName}:\${errorLocation.lineNumber || 1}:\${errorLocation.colNumber || 1}\`; + fetch(\`/__parcel_launch_editor?file=\${encodeURIComponent(file)}\`); +}); + +ErrorOverlay.startReportingRuntimeErrors({ + onError: function () {}, +}); + +window.addEventListener('parcelhmraccept', () => { + ErrorOverlay.dismissRuntimeErrors(); +}); +`; export default (new Runtime({ async apply({bundle, options}) { diff --git a/packages/transformers/elm/src/ElmTransformer.js b/packages/transformers/elm/src/ElmTransformer.js index 7636a101bf9..21aa53460bc 100644 --- a/packages/transformers/elm/src/ElmTransformer.js +++ b/packages/transformers/elm/src/ElmTransformer.js @@ -6,7 +6,7 @@ import spawn from 'cross-spawn'; import path from 'path'; import {minify} from 'terser'; import nullthrows from 'nullthrows'; -import ThrowableDiagnostic from '@parcel/diagnostic'; +import ThrowableDiagnostic, {md} from '@parcel/diagnostic'; // $FlowFixMe import elm from 'node-elm-compiler'; // $FlowFixMe @@ -46,6 +46,7 @@ export default (new Transformer({ // $FlowFixMe[sketchy-null-string] debug: !options.env.PARCEL_ELM_NO_DEBUG && options.mode !== 'production', optimize: asset.env.shouldOptimize, + report: 'json', }; asset.invalidateOnEnvChange('PARCEL_ELM_NO_DEBUG'); @@ -72,12 +73,13 @@ export default (new Transformer({ try { code = await compileToString(elm, elmBinary, sources, compilerConfig); } catch (e) { + let compilerJson = e.message.split('\n')[1]; + let compilerDiagnostics = JSON.parse(compilerJson); + throw new ThrowableDiagnostic({ - diagnostic: { - message: 'Compilation failed', - origin: '@parcel/elm-transformer', - stack: e.toString(), - }, + diagnostic: compilerDiagnostics.errors.flatMap( + elmErrorToParcelDiagnostics, + ), }); } @@ -182,3 +184,33 @@ async function minifyElmOutput(source) { if (result.code != null) return result.code; throw result.error; } + +function formatMessagePiece(piece) { + if (piece.string) { + if (piece.underline) { + return md`${md.underline(piece.string)}`; + } + return md`${md.bold(piece.string)}`; + } + return md`${piece}`; +} + +function elmErrorToParcelDiagnostics(error) { + const relativePath = path.relative(process.cwd(), error.path); + return error.problems.map(problem => { + const padLength = 80 - 5 - problem.title.length - relativePath.length; + const dashes = '-'.repeat(padLength); + const message = [ + '', + `-- ${problem.title} ${dashes} ${relativePath}`, + '', + problem.message.map(formatMessagePiece).join(''), + ].join('\n'); + + return { + message, + origin: '@parcel/elm-transformer', + stack: '', // set stack to empty since it is not useful + }; + }); +} diff --git a/packages/transformers/js/core/Cargo.toml b/packages/transformers/js/core/Cargo.toml index 8e76f2ea6b0..0669d031b9b 100644 --- a/packages/transformers/js/core/Cargo.toml +++ b/packages/transformers/js/core/Cargo.toml @@ -8,8 +8,8 @@ edition = "2018" crate-type = ["rlib"] [dependencies] -swc_ecmascript = { version = "0.143.0", features = ["parser", "transforms", "module", "optimization", "react", "typescript", "utils", "visit", "codegen", "utils", "preset_env"] } -swc_common = { version = "0.17.21", features = ["tty-emitter", "sourcemap"] } +swc_ecmascript = { version = "0.157.0", features = ["parser", "transforms", "module", "optimization", "react", "typescript", "utils", "visit", "codegen", "utils", "preset_env"] } +swc_common = { version = "0.18.2", features = ["tty-emitter", "sourcemap"] } swc_atoms = "0.2.11" indoc = "1.0.3" serde = "1.0.123" diff --git a/packages/transformers/js/core/src/dependency_collector.rs b/packages/transformers/js/core/src/dependency_collector.rs index f28bcf923c3..b1d83f65650 100644 --- a/packages/transformers/js/core/src/dependency_collector.rs +++ b/packages/transformers/js/core/src/dependency_collector.rs @@ -7,7 +7,6 @@ use serde::{Deserialize, Serialize}; use swc_atoms::JsWord; use swc_common::{Mark, SourceMap, Span, SyntaxContext, DUMMY_SP}; use swc_ecmascript::ast::{self, Callee, MemberProp}; -use swc_ecmascript::utils::ident::IdentLike; use swc_ecmascript::visit::{Fold, FoldWith}; use crate::fold_member_expr_skip_prop; diff --git a/packages/transformers/js/core/src/global_replacer.rs b/packages/transformers/js/core/src/global_replacer.rs index 92a072193aa..f2c5417e5f2 100644 --- a/packages/transformers/js/core/src/global_replacer.rs +++ b/packages/transformers/js/core/src/global_replacer.rs @@ -3,22 +3,21 @@ use std::collections::{HashMap, HashSet}; use std::path::Path; use swc_atoms::JsWord; -use swc_common::{SourceMap, SyntaxContext, DUMMY_SP}; +use swc_common::{Mark, SourceMap, SyntaxContext, DUMMY_SP}; use swc_ecmascript::ast::{self, ComputedPropName}; -use swc_ecmascript::utils::ident::IdentLike; use swc_ecmascript::visit::{Fold, FoldWith}; use crate::dependency_collector::{DependencyDescriptor, DependencyKind}; -use crate::utils::{create_require, SourceLocation, SourceType}; +use crate::utils::{create_global_decl_stmt, create_require, SourceLocation, SourceType}; pub struct GlobalReplacer<'a> { pub source_map: &'a SourceMap, pub items: &'a mut Vec, - pub globals: HashMap, + pub global_mark: Mark, + pub globals: HashMap, pub project_root: &'a Path, pub filename: &'a Path, pub decls: &'a mut HashSet<(JsWord, SyntaxContext)>, - pub global_mark: swc_common::Mark, pub scope_hoist: bool, } @@ -27,7 +26,7 @@ impl<'a> Fold for GlobalReplacer<'a> { use ast::{Expr::*, Ident, MemberExpr, MemberProp}; // Do not traverse into the `prop` side of member expressions unless computed. - let node = match node { + let mut node = match node { Member(expr) => { if let MemberProp::Computed(_) = expr.prop { Member(MemberExpr { @@ -45,130 +44,88 @@ impl<'a> Fold for GlobalReplacer<'a> { _ => node.fold_children_with(self), }; - if let Ident(ref id) = node { + if let Ident(id) = &mut node { // Only handle global variables - if self.globals.contains_key(&id.sym) - || self.decls.contains(&(id.sym.clone(), id.span.ctxt())) - { + if self.decls.contains(&(id.sym.clone(), id.span.ctxt())) { return node; } match id.sym.to_string().as_str() { "process" => { - self.globals.insert( - id.sym.clone(), - create_decl_stmt( - id.sym.clone(), - self.global_mark, - Call(create_require(js_word!("process"))), - ), - ); - - // So it gets renamed during scope hoisting. - self.decls.insert(id.to_id()); - - let specifier = id.sym.clone(); - self.items.push(DependencyDescriptor { - kind: DependencyKind::Require, - loc: SourceLocation::from(self.source_map, id.span), - specifier, - attributes: None, - is_optional: false, - is_helper: false, - source_type: Some(SourceType::Module), - placeholder: None, - }); + if self.update_binding(id, |_| Call(create_require(js_word!("process")))) { + let specifier = id.sym.clone(); + self.items.push(DependencyDescriptor { + kind: DependencyKind::Require, + loc: SourceLocation::from(self.source_map, id.span), + specifier, + attributes: None, + is_optional: false, + is_helper: false, + source_type: Some(SourceType::Module), + placeholder: None, + }); + } } "Buffer" => { let specifier = swc_atoms::JsWord::from("buffer"); - self.globals.insert( - id.sym.clone(), - create_decl_stmt( - id.sym.clone(), - self.global_mark, - Member(MemberExpr { - obj: Box::new(Call(create_require(specifier.clone()))), - prop: MemberProp::Ident(ast::Ident::new("Buffer".into(), DUMMY_SP)), - span: DUMMY_SP, - }), - ), - ); - - self.decls.insert(id.to_id()); - - self.items.push(DependencyDescriptor { - kind: DependencyKind::Require, - loc: SourceLocation::from(self.source_map, id.span), - specifier, - attributes: None, - is_optional: false, - is_helper: false, - source_type: Some(SourceType::Module), - placeholder: None, - }); + if self.update_binding(id, |_| { + Member(MemberExpr { + obj: Box::new(Call(create_require(specifier.clone()))), + prop: MemberProp::Ident(ast::Ident::new("Buffer".into(), DUMMY_SP)), + span: DUMMY_SP, + }) + }) { + self.items.push(DependencyDescriptor { + kind: DependencyKind::Require, + loc: SourceLocation::from(self.source_map, id.span), + specifier, + attributes: None, + is_optional: false, + is_helper: false, + source_type: Some(SourceType::Module), + placeholder: None, + }); + } } "__filename" => { - let filename = - if let Some(relative) = pathdiff::diff_paths(self.filename, self.project_root) { - relative.to_slash_lossy() - } else if let Some(filename) = self.filename.file_name() { - format!("/{}", filename.to_string_lossy()) - } else { - String::from("/unknown.js") - }; - - self.globals.insert( - id.sym.clone(), - create_decl_stmt( - id.sym.clone(), - self.global_mark, - ast::Expr::Lit(ast::Lit::Str(swc_atoms::JsWord::from(filename).into())), - ), - ); - - self.decls.insert(id.to_id()); + self.update_binding(id, |this| { + let filename = + if let Some(relative) = pathdiff::diff_paths(this.filename, this.project_root) { + relative.to_slash_lossy() + } else if let Some(filename) = this.filename.file_name() { + format!("/{}", filename.to_string_lossy()) + } else { + String::from("/unknown.js") + }; + ast::Expr::Lit(ast::Lit::Str(swc_atoms::JsWord::from(filename).into())) + }); } "__dirname" => { - let dirname = if let Some(dirname) = self.filename.parent() { - if let Some(relative) = pathdiff::diff_paths(dirname, self.project_root) { - relative.to_slash_lossy() + self.update_binding(id, |this| { + let dirname = if let Some(dirname) = this.filename.parent() { + if let Some(relative) = pathdiff::diff_paths(dirname, this.project_root) { + relative.to_slash_lossy() + } else { + String::from("/") + } } else { String::from("/") - } - } else { - String::from("/") - }; - - self.globals.insert( - id.sym.clone(), - create_decl_stmt( - id.sym.clone(), - self.global_mark, - ast::Expr::Lit(ast::Lit::Str(swc_atoms::JsWord::from(dirname).into())), - ), - ); - - self.decls.insert(id.to_id()); + }; + ast::Expr::Lit(ast::Lit::Str(swc_atoms::JsWord::from(dirname).into())) + }); } "global" => { if !self.scope_hoist { - self.globals.insert( - id.sym.clone(), - create_decl_stmt( - id.sym.clone(), - self.global_mark, - ast::Expr::Member(ast::MemberExpr { - obj: Box::new(Ident(Ident::new(js_word!("arguments"), DUMMY_SP))), - prop: MemberProp::Computed(ComputedPropName { - span: DUMMY_SP, - expr: Box::new(Lit(ast::Lit::Num(3.into()))), - }), + self.update_binding(id, |_| { + ast::Expr::Member(ast::MemberExpr { + obj: Box::new(Ident(Ident::new(js_word!("arguments"), DUMMY_SP))), + prop: MemberProp::Computed(ComputedPropName { span: DUMMY_SP, + expr: Box::new(Lit(ast::Lit::Num(3.into()))), }), - ), - ); - - self.decls.insert(id.to_id()); + span: DUMMY_SP, + }) + }); } } _ => {} @@ -186,29 +143,29 @@ impl<'a> Fold for GlobalReplacer<'a> { self .globals .values() - .map(|stmt| ast::ModuleItem::Stmt(stmt.clone())), + .map(|(_, stmt)| ast::ModuleItem::Stmt(stmt.clone())), ); node } } -fn create_decl_stmt( - name: swc_atoms::JsWord, - global_mark: swc_common::Mark, - init: ast::Expr, -) -> ast::Stmt { - ast::Stmt::Decl(ast::Decl::Var(ast::VarDecl { - kind: ast::VarDeclKind::Var, - declare: false, - span: DUMMY_SP, - decls: vec![ast::VarDeclarator { - name: ast::Pat::Ident(ast::BindingIdent::from(ast::Ident::new( - name, - DUMMY_SP.apply_mark(global_mark), - ))), - span: DUMMY_SP, - definite: false, - init: Some(Box::new(init)), - }], - })) +impl GlobalReplacer<'_> { + fn update_binding(&mut self, id: &mut ast::Ident, expr: F) -> bool + where + F: FnOnce(&Self) -> ast::Expr, + { + if let Some((ctxt, _)) = self.globals.get(&id.sym) { + id.span.ctxt = *ctxt; + false + } else { + let (decl, ctxt) = create_global_decl_stmt(id.sym.clone(), expr(self), self.global_mark); + + id.span.ctxt = ctxt; + + self.globals.insert(id.sym.clone(), (ctxt, decl)); + self.decls.insert(id.to_id()); + + true + } + } } diff --git a/packages/transformers/js/core/src/hoist.rs b/packages/transformers/js/core/src/hoist.rs index 59f7f10101c..e1142574249 100644 --- a/packages/transformers/js/core/src/hoist.rs +++ b/packages/transformers/js/core/src/hoist.rs @@ -668,6 +668,17 @@ impl<'a> Fold for Hoist<'a> { } } } + Expr::Ident(ident) => { + if let Some(Import { specifier, .. }) = self.collect.imports.get(&id!(ident)) { + if specifier != "*" { + return Expr::Seq(SeqExpr { + span: ident.span, + exprs: vec![0.into(), Box::new(Expr::Ident(ident.fold_with(self)))], + }); + } + } + return Expr::Ident(ident.fold_with(self)); + } _ => {} } @@ -785,7 +796,7 @@ impl<'a> Fold for Hoist<'a> { return Ident::new("$parcel$global".into(), node.span); } - if node.span.ctxt() == self.collect.global_ctxt + if node.span.has_mark(self.collect.global_mark) && self.collect.decls.contains(&id!(node)) && !self.collect.should_wrap { @@ -1073,7 +1084,7 @@ pub struct Collect { pub source_map: Lrc, pub decls: HashSet, pub ignore_mark: Mark, - pub global_ctxt: SyntaxContext, + pub global_mark: Mark, pub static_cjs_exports: bool, pub has_cjs_exports: bool, pub is_esm: bool, @@ -1139,7 +1150,7 @@ impl Collect { source_map, decls, ignore_mark, - global_ctxt: SyntaxContext::empty().apply_mark(global_mark), + global_mark, static_cjs_exports: true, has_cjs_exports: false, is_esm: false, @@ -1548,7 +1559,7 @@ impl Visit for Collect { .or_insert_with(|| node.id.sym.clone()); } - if self.in_assign && node.id.span.ctxt() == self.global_ctxt { + if self.in_assign && node.id.span.has_mark(self.global_mark) { self .non_const_bindings .entry(id!(node.id)) @@ -1573,7 +1584,7 @@ impl Visit for Collect { .or_insert_with(|| node.key.sym.clone()); } - if self.in_assign && node.key.span.ctxt() == self.global_ctxt { + if self.in_assign && node.key.span.has_mark(self.global_mark) { self .non_const_bindings .entry(id!(node.key)) @@ -2097,7 +2108,7 @@ mod tests { use swc_ecmascript::codegen::text_writer::JsWriter; use swc_ecmascript::parser::lexer::Lexer; use swc_ecmascript::parser::{Parser, StringInput}; - use swc_ecmascript::transforms::resolver_with_mark; + use swc_ecmascript::transforms::resolver; extern crate indoc; use self::indoc::indoc; @@ -2119,8 +2130,9 @@ mod tests { swc_ecmascript::transforms::helpers::HELPERS.set( &swc_ecmascript::transforms::helpers::Helpers::new(false), || { + let unresolved_mark = Mark::fresh(Mark::root()); let global_mark = Mark::fresh(Mark::root()); - let module = module.fold_with(&mut resolver_with_mark(global_mark)); + let module = module.fold_with(&mut resolver(unresolved_mark, global_mark, false)); let mut collect = Collect::new( source_map.clone(), @@ -2161,7 +2173,11 @@ mod tests { &mut buf, Some(&mut src_map_buf), )); - let config = swc_ecmascript::codegen::Config { minify: false }; + let config = swc_ecmascript::codegen::Config { + minify: false, + ascii_only: false, + target: swc_ecmascript::ast::EsVersion::Es5, + }; let mut emitter = swc_ecmascript::codegen::Emitter { cfg: config, comments: Some(&comments), diff --git a/packages/transformers/js/core/src/lib.rs b/packages/transformers/js/core/src/lib.rs index 33ca1165251..9f561aafba9 100644 --- a/packages/transformers/js/core/src/lib.rs +++ b/packages/transformers/js/core/src/lib.rs @@ -37,7 +37,7 @@ use swc_ecmascript::parser::lexer::Lexer; use swc_ecmascript::parser::{EsConfig, PResult, Parser, StringInput, Syntax, TsConfig}; use swc_ecmascript::preset_env::{preset_env, Mode::Entry, Targets, Version, Versions}; use swc_ecmascript::transforms::fixer::paren_remover; -use swc_ecmascript::transforms::resolver::resolver_with_mark; +use swc_ecmascript::transforms::resolver; use swc_ecmascript::transforms::{ compat::reserved_words::reserved_words, fixer, helpers, hygiene, optimization::simplify::dead_branch_remover, optimization::simplify::expr_simplifier, @@ -202,14 +202,14 @@ pub fn transform(config: Config) -> Result { || { let mut react_options = react::Options::default(); if config.is_jsx { - react_options.use_spread = true; + react_options.use_spread = Some(true); if let Some(jsx_pragma) = &config.jsx_pragma { - react_options.pragma = jsx_pragma.clone(); + react_options.pragma = Some(jsx_pragma.clone()); } if let Some(jsx_pragma_frag) = &config.jsx_pragma_frag { - react_options.pragma_frag = jsx_pragma_frag.clone(); + react_options.pragma_frag = Some(jsx_pragma_frag.clone()); } - react_options.development = config.is_development; + react_options.development = Some(config.is_development); react_options.refresh = if config.react_refresh { Some(react::RefreshOptions::default()) } else { @@ -218,7 +218,7 @@ pub fn transform(config: Config) -> Result { react_options.runtime = if config.automatic_jsx_runtime { if let Some(import_source) = &config.jsx_import_source { - react_options.import_source = import_source.clone(); + react_options.import_source = Some(import_source.clone()); } Some(react::Runtime::Automatic) } else { @@ -227,7 +227,7 @@ pub fn transform(config: Config) -> Result { } let global_mark = Mark::fresh(Mark::root()); - let ignore_mark = Mark::fresh(Mark::root()); + let unresolved_mark = Mark::fresh(Mark::root()); module = { let mut passes = chain!( // Decorators can use type information, so must run before the TypeScript pass. @@ -235,7 +235,8 @@ pub fn transform(config: Config) -> Result { decorators::decorators(decorators::Config { legacy: true, // Always disabled for now, SWC's implementation doesn't match TSC. - emit_metadata: false + emit_metadata: false, + use_define_for_class_fields: true }), config.decorators ), @@ -243,8 +244,8 @@ pub fn transform(config: Config) -> Result { typescript::strip_with_jsx( source_map.clone(), typescript::Config { - pragma: Some(react_options.pragma.clone()), - pragma_frag: Some(react_options.pragma_frag.clone()), + pragma: react_options.pragma.clone(), + pragma_frag: react_options.pragma_frag.clone(), ..Default::default() }, Some(&comments), @@ -256,7 +257,7 @@ pub fn transform(config: Config) -> Result { typescript::strip(global_mark), config.is_type_script && !config.is_jsx ), - resolver_with_mark(global_mark), + resolver(unresolved_mark, global_mark, config.is_type_script), Optional::new( react::react( source_map.clone(), @@ -278,11 +279,13 @@ pub fn transform(config: Config) -> Result { ..Default::default() }; let versions = targets_to_versions(&config.targets); + let mut should_run_preset_env = false; if !config.is_swc_helpers { // Avoid transpiling @swc/helpers so that we don't cause infinite recursion. // Filter the versions for preset_env only so that syntax support checks // (e.g. in esm2cjs) still work correctly. if let Some(versions) = versions { + should_run_preset_env = true; preset_env_config.targets = Some(Targets::Versions(versions)); preset_env_config.shipped_proposals = true; preset_env_config.mode = Some(Entry); @@ -313,13 +316,14 @@ pub fn transform(config: Config) -> Result { paren_remover(Some(&comments)), // Simplify expressions and remove dead branches so that we // don't include dependencies inside conditionals that are always false. - expr_simplifier(Default::default()), - dead_branch_remover(), + expr_simplifier(unresolved_mark, Default::default()), + dead_branch_remover(unresolved_mark), // Inline Node fs.readFileSync calls Optional::new( inline_fs( config.filename.as_str(), source_map.clone(), + // TODO this clone is unnecessary if we get the lifetimes right decls.clone(), global_mark, &config.project_root, @@ -332,6 +336,24 @@ pub fn transform(config: Config) -> Result { module.fold_with(&mut passes) }; + let module = module.fold_with( + // Replace __dirname and __filename with placeholders in Node env + &mut Optional::new( + NodeReplacer { + source_map: &source_map, + items: &mut global_deps, + global_mark, + globals: HashMap::new(), + project_root: Path::new(&config.project_root), + filename: Path::new(&config.filename), + decls: &mut decls, + scope_hoist: config.scope_hoist, + has_node_replacements: &mut result.has_node_replacements, + }, + config.node_replacer, + ), + ); + let module = { let mut passes = chain!( // Insert dependencies for node globals @@ -339,28 +361,25 @@ pub fn transform(config: Config) -> Result { GlobalReplacer { source_map: &source_map, items: &mut global_deps, + global_mark, globals: HashMap::new(), project_root: Path::new(&config.project_root), filename: Path::new(&config.filename), decls: &mut decls, - global_mark, scope_hoist: config.scope_hoist }, - config.insert_node_globals && config.source_type != SourceType::Script + config.insert_node_globals ), // Transpile new syntax to older syntax if needed - { - let should_transpile = preset_env_config.targets.is_some(); - Optional::new( - preset_env( - global_mark, - Some(&comments), - preset_env_config, - Default::default(), - ), - should_transpile, - ) - }, + Optional::new( + preset_env( + global_mark, + Some(&comments), + preset_env_config, + Default::default(), + ), + should_run_preset_env, + ), // Inject SWC helpers if needed. helpers::inject_helpers(), ); @@ -368,26 +387,23 @@ pub fn transform(config: Config) -> Result { module.fold_with(&mut passes) }; - let mut has_node_replacements = false; - let module = module.fold_with( - // Replace __dirname and __filename with placeholders in Node env - &mut Optional::new( - NodeReplacer { - source_map: &source_map, - items: &mut global_deps, - globals: HashMap::new(), - project_root: Path::new(&config.project_root), - filename: Path::new(&config.filename), - decls: &mut decls, - global_mark, - scope_hoist: config.scope_hoist, - has_node_replacements: &mut has_node_replacements, - }, - config.node_replacer, - ), - ); - result.has_node_replacements = has_node_replacements; + // Flush (JsWord, SyntaxContexts) into unique names and reresolve to + // set global_mark for all nodes, even generated ones. + // - This changes the syntax context ids and therefore invalidates decls + // - This will also remove any other other marks (like ignore_mark) + // This only needs to be done if preset_env ran because all other transforms + // insert declarations with global_mark (even though they are generated). + let (decls, module) = if config.scope_hoist && should_run_preset_env { + let module = module.fold_with(&mut chain!( + hygiene(), + resolver(unresolved_mark, global_mark, false) + )); + (collect_decls(&module), module) + } else { + (decls, module) + }; + let ignore_mark = Mark::fresh(Mark::root()); let module = module.fold_with( // Collect dependencies &mut dependency_collector( @@ -505,7 +521,6 @@ fn parse( Syntax::Es(EsConfig { jsx: config.is_jsx, export_default_from: true, - static_blocks: true, decorators: config.decorators, ..Default::default() }) @@ -544,7 +559,11 @@ fn emit( None }, )); - let config = swc_ecmascript::codegen::Config { minify: false }; + let config = swc_ecmascript::codegen::Config { + minify: false, + ascii_only: false, + target: swc_ecmascript::ast::EsVersion::Es5, + }; let mut emitter = swc_ecmascript::codegen::Emitter { cfg: config, comments: Some(&comments), diff --git a/packages/transformers/js/core/src/modules.rs b/packages/transformers/js/core/src/modules.rs index ff8fd8df7e2..2e36bf0cce1 100644 --- a/packages/transformers/js/core/src/modules.rs +++ b/packages/transformers/js/core/src/modules.rs @@ -230,9 +230,15 @@ impl ESMFold { self.get_require_name(source, DUMMY_SP) }; - Expr::Member(MemberExpr { - obj: Box::new(Expr::Ident(obj)), - prop: MemberProp::Ident(Ident::new(imported.clone(), DUMMY_SP)), + Expr::Seq(SeqExpr { + exprs: vec![ + 0.into(), + Box::new(Expr::Member(MemberExpr { + obj: Box::new(Expr::Ident(obj)), + prop: MemberProp::Ident(Ident::new(imported.clone(), DUMMY_SP)), + span, + })), + ], span, }) } diff --git a/packages/transformers/js/core/src/node_replacer.rs b/packages/transformers/js/core/src/node_replacer.rs index 313417cd8b5..4055805f194 100644 --- a/packages/transformers/js/core/src/node_replacer.rs +++ b/packages/transformers/js/core/src/node_replacer.rs @@ -3,22 +3,21 @@ use std::ffi::OsStr; use std::path::Path; use swc_atoms::JsWord; -use swc_common::{SourceMap, SyntaxContext, DUMMY_SP}; +use swc_common::{Mark, SourceMap, SyntaxContext, DUMMY_SP}; use swc_ecmascript::ast::{self}; -use swc_ecmascript::utils::ident::IdentLike; use swc_ecmascript::visit::{Fold, FoldWith}; use crate::dependency_collector::{DependencyDescriptor, DependencyKind}; -use crate::utils::{create_require, SourceLocation, SourceType}; +use crate::utils::{create_global_decl_stmt, create_require, SourceLocation, SourceType}; pub struct NodeReplacer<'a> { pub source_map: &'a SourceMap, pub items: &'a mut Vec, - pub globals: HashMap, + pub global_mark: Mark, + pub globals: HashMap, pub project_root: &'a Path, pub filename: &'a Path, pub decls: &'a mut HashSet<(JsWord, SyntaxContext)>, - pub global_mark: swc_common::Mark, pub scope_hoist: bool, pub has_node_replacements: &'a mut bool, } @@ -28,7 +27,7 @@ impl<'a> Fold for NodeReplacer<'a> { use ast::{Expr::*, MemberExpr, MemberProp}; // Do not traverse into the `prop` side of member expressions unless computed. - let node = match node { + let mut node = match node { Member(expr) => { if let MemberProp::Computed(_) = expr.prop { Member(MemberExpr { @@ -46,11 +45,9 @@ impl<'a> Fold for NodeReplacer<'a> { _ => node.fold_children_with(self), }; - if let Ident(ref id) = node { + if let Ident(id) = &mut node { // Only handle global variables - if self.globals.contains_key(&id.sym) - || self.decls.contains(&(id.sym.clone(), id.span.ctxt())) - { + if self.decls.contains(&(id.sym.clone(), id.span.ctxt())) { return node; } @@ -59,121 +56,111 @@ impl<'a> Fold for NodeReplacer<'a> { let specifier = swc_atoms::JsWord::from("path"); let replace_me_value = swc_atoms::JsWord::from("$parcel$filenameReplace"); - let filename = if let Some(name) = self.filename.file_name() { - name - } else { - OsStr::new("unknown.js") - }; - - let inserted_expr = ast::Expr::Call(ast::CallExpr { - span: DUMMY_SP, - type_args: None, - args: vec![ - ast::ExprOrSpread { - spread: None, - expr: Box::new(ast::Expr::Ident(ast::Ident { - optional: false, - span: DUMMY_SP, - // This also uses __dirname as later in the path.join call the hierarchy is then correct - // Otherwise path.join(__filename, '..') would be one level to shallow (due to the /filename.js at the end) - sym: swc_atoms::JsWord::from("__dirname"), - })), - }, - ast::ExprOrSpread { - spread: None, - expr: Box::new(ast::Expr::Lit(ast::Lit::Str(ast::Str { - span: DUMMY_SP, - value: replace_me_value, - raw: None, - }))), - }, - ast::ExprOrSpread { - spread: None, - expr: Box::new(ast::Expr::Lit(ast::Lit::Str(ast::Str { - span: DUMMY_SP, - value: swc_atoms::JsWord::from(filename.to_string_lossy()), - raw: None, - }))), - }, - ], - callee: ast::Callee::Expr(Box::new(ast::Expr::Member(ast::MemberExpr { + let expr = |this: &NodeReplacer| { + let filename = if let Some(name) = this.filename.file_name() { + name + } else { + OsStr::new("unknown.js") + }; + ast::Expr::Call(ast::CallExpr { span: DUMMY_SP, - obj: (Box::new(Call(create_require(specifier.clone())))), - prop: MemberProp::Ident(ast::Ident::new("resolve".into(), DUMMY_SP)), - }))), - }); - - self.globals.insert( - id.sym.clone(), - create_decl_stmt(id.sym.clone(), self.global_mark, inserted_expr), - ); - - self.decls.insert(id.to_id()); - - self.items.push(DependencyDescriptor { - kind: DependencyKind::Require, - loc: SourceLocation::from(self.source_map, id.span), - specifier, - attributes: None, - is_optional: false, - is_helper: false, - source_type: Some(SourceType::Module), - placeholder: None, - }); - - *self.has_node_replacements = true; + type_args: None, + args: vec![ + ast::ExprOrSpread { + spread: None, + expr: Box::new(ast::Expr::Ident(ast::Ident { + optional: false, + span: DUMMY_SP, + // This also uses __dirname as later in the path.join call the hierarchy is then correct + // Otherwise path.join(__filename, '..') would be one level to shallow (due to the /filename.js at the end) + sym: swc_atoms::JsWord::from("__dirname"), + })), + }, + ast::ExprOrSpread { + spread: None, + expr: Box::new(ast::Expr::Lit(ast::Lit::Str(ast::Str { + span: DUMMY_SP, + value: replace_me_value, + raw: None, + }))), + }, + ast::ExprOrSpread { + spread: None, + expr: Box::new(ast::Expr::Lit(ast::Lit::Str(ast::Str { + span: DUMMY_SP, + value: swc_atoms::JsWord::from(filename.to_string_lossy()), + raw: None, + }))), + }, + ], + callee: ast::Callee::Expr(Box::new(ast::Expr::Member(ast::MemberExpr { + span: DUMMY_SP, + obj: (Box::new(Call(create_require(specifier.clone())))), + prop: MemberProp::Ident(ast::Ident::new("resolve".into(), DUMMY_SP)), + }))), + }) + }; + if self.update_binding(id, expr) { + self.items.push(DependencyDescriptor { + kind: DependencyKind::Require, + loc: SourceLocation::from(self.source_map, id.span), + specifier, + attributes: None, + is_optional: false, + is_helper: false, + source_type: Some(SourceType::Module), + placeholder: None, + }); + + *self.has_node_replacements = true; + } } "__dirname" => { let specifier = swc_atoms::JsWord::from("path"); let replace_me_value = swc_atoms::JsWord::from("$parcel$dirnameReplace"); - let inserted_expr = ast::Expr::Call(ast::CallExpr { - span: DUMMY_SP, - type_args: None, - args: vec![ - ast::ExprOrSpread { - spread: None, - expr: Box::new(ast::Expr::Ident(ast::Ident { - optional: false, - span: DUMMY_SP, - sym: swc_atoms::JsWord::from("__dirname"), - })), - }, - ast::ExprOrSpread { - spread: None, - expr: Box::new(ast::Expr::Lit(ast::Lit::Str(ast::Str { - span: DUMMY_SP, - value: replace_me_value, - raw: None, - }))), - }, - ], - callee: ast::Callee::Expr(Box::new(ast::Expr::Member(ast::MemberExpr { + if self.update_binding(id, |_| { + ast::Expr::Call(ast::CallExpr { span: DUMMY_SP, - obj: (Box::new(Call(create_require(specifier.clone())))), - prop: MemberProp::Ident(ast::Ident::new("resolve".into(), DUMMY_SP)), - }))), - }); - - self.globals.insert( - id.sym.clone(), - create_decl_stmt(id.sym.clone(), self.global_mark, inserted_expr), - ); - - self.decls.insert(id.to_id()); - - self.items.push(DependencyDescriptor { - kind: DependencyKind::Require, - loc: SourceLocation::from(self.source_map, id.span), - specifier, - attributes: None, - is_optional: false, - is_helper: false, - source_type: Some(SourceType::Module), - placeholder: None, - }); - - *self.has_node_replacements = true; + type_args: None, + args: vec![ + ast::ExprOrSpread { + spread: None, + expr: Box::new(ast::Expr::Ident(ast::Ident { + optional: false, + span: DUMMY_SP, + sym: swc_atoms::JsWord::from("__dirname"), + })), + }, + ast::ExprOrSpread { + spread: None, + expr: Box::new(ast::Expr::Lit(ast::Lit::Str(ast::Str { + span: DUMMY_SP, + value: replace_me_value, + raw: None, + }))), + }, + ], + callee: ast::Callee::Expr(Box::new(ast::Expr::Member(ast::MemberExpr { + span: DUMMY_SP, + obj: (Box::new(Call(create_require(specifier.clone())))), + prop: MemberProp::Ident(ast::Ident::new("resolve".into(), DUMMY_SP)), + }))), + }) + }) { + self.items.push(DependencyDescriptor { + kind: DependencyKind::Require, + loc: SourceLocation::from(self.source_map, id.span), + specifier, + attributes: None, + is_optional: false, + is_helper: false, + source_type: Some(SourceType::Module), + placeholder: None, + }); + + *self.has_node_replacements = true; + } } _ => {} } @@ -190,29 +177,29 @@ impl<'a> Fold for NodeReplacer<'a> { self .globals .values() - .map(|stmt| ast::ModuleItem::Stmt(stmt.clone())), + .map(|(_, stmt)| ast::ModuleItem::Stmt(stmt.clone())), ); node } } -fn create_decl_stmt( - name: swc_atoms::JsWord, - global_mark: swc_common::Mark, - init: ast::Expr, -) -> ast::Stmt { - ast::Stmt::Decl(ast::Decl::Var(ast::VarDecl { - kind: ast::VarDeclKind::Var, - declare: false, - span: DUMMY_SP, - decls: vec![ast::VarDeclarator { - name: ast::Pat::Ident(ast::BindingIdent::from(ast::Ident::new( - name, - DUMMY_SP.apply_mark(global_mark), - ))), - span: DUMMY_SP, - definite: false, - init: Some(Box::new(init)), - }], - })) +impl NodeReplacer<'_> { + fn update_binding(&mut self, id: &mut ast::Ident, expr: F) -> bool + where + F: FnOnce(&Self) -> ast::Expr, + { + if let Some((ctxt, _)) = self.globals.get(&id.sym) { + id.span.ctxt = *ctxt; + false + } else { + let (decl, ctxt) = create_global_decl_stmt(id.sym.clone(), expr(self), self.global_mark); + + id.span.ctxt = ctxt; + + self.globals.insert(id.sym.clone(), (ctxt, decl)); + self.decls.insert(id.to_id()); + + true + } + } } diff --git a/packages/transformers/js/core/src/utils.rs b/packages/transformers/js/core/src/utils.rs index 6dec6e238c2..113aa35c091 100644 --- a/packages/transformers/js/core/src/utils.rs +++ b/packages/transformers/js/core/src/utils.rs @@ -175,6 +175,32 @@ pub fn match_import(node: &ast::Expr, ignore_mark: Mark) -> Option { } } +// `name` must not be an existing binding. +pub fn create_global_decl_stmt( + name: swc_atoms::JsWord, + init: ast::Expr, + global_mark: Mark, +) -> (ast::Stmt, SyntaxContext) { + // The correct value would actually be `DUMMY_SP.apply_mark(Mark::fresh(Mark::root()))`. + // But this saves us from running the resolver again in some cases. + let span = DUMMY_SP.apply_mark(global_mark); + + ( + ast::Stmt::Decl(ast::Decl::Var(ast::VarDecl { + kind: ast::VarDeclKind::Var, + declare: false, + span: DUMMY_SP, + decls: vec![ast::VarDeclarator { + name: ast::Pat::Ident(ast::BindingIdent::from(ast::Ident::new(name, span))), + span: DUMMY_SP, + definite: false, + init: Some(Box::new(init)), + }], + })), + span.ctxt, + ) +} + #[derive(Debug, Serialize, Deserialize, Clone, Eq, PartialEq)] pub struct SourceLocation { pub start_line: usize, @@ -185,6 +211,15 @@ pub struct SourceLocation { impl SourceLocation { pub fn from(source_map: &swc_common::SourceMap, span: swc_common::Span) -> Self { + if span.lo.is_dummy() || span.hi.is_dummy() { + return SourceLocation { + start_line: 1, + start_col: 1, + end_line: 1, + end_col: 1, + }; + } + let start = source_map.lookup_char_pos(span.lo); let end = source_map.lookup_char_pos(span.hi); // - SWC's columns are exclusive, ours are inclusive (column - 1) diff --git a/packages/transformers/js/native.js b/packages/transformers/js/native.js index ad423318745..ce1d82238c9 100644 --- a/packages/transformers/js/native.js +++ b/packages/transformers/js/native.js @@ -18,7 +18,7 @@ if (process.env.PARCEL_BUILD_ENV === 'production') { } else if (process.env.PARCEL_SWC_WASM) { const {transform} = require('./wasm/dist-node/parcel_js_swc_wasm.js'); - module.exports.transform = function(config) { + module.exports.transform = function (config) { let result = transform(config); return { ...result, diff --git a/packages/transformers/js/package.json b/packages/transformers/js/package.json index 27bec9385e6..af059408312 100644 --- a/packages/transformers/js/package.json +++ b/packages/transformers/js/package.json @@ -35,7 +35,7 @@ "@parcel/source-map": "^2.0.0", "@parcel/utils": "2.5.0", "@parcel/workers": "2.5.0", - "@swc/helpers": "^0.3.6", + "@swc/helpers": "^0.3.13", "browserslist": "^4.6.6", "detect-libc": "^1.0.3", "nullthrows": "^1.1.1", diff --git a/packages/transformers/js/src/JSTransformer.js b/packages/transformers/js/src/JSTransformer.js index c8cbf426f4e..2b3cb225b8a 100644 --- a/packages/transformers/js/src/JSTransformer.js +++ b/packages/transformers/js/src/JSTransformer.js @@ -387,7 +387,8 @@ export default (new Transformer({ project_root: options.projectRoot, replace_env: !asset.env.isNode(), inline_fs: Boolean(config?.inlineFS) && !asset.env.isNode(), - insert_node_globals: !asset.env.isNode(), + insert_node_globals: + !asset.env.isNode() && asset.env.sourceType !== 'script', node_replacer: asset.env.isNode(), is_browser: asset.env.isBrowser(), is_worker: asset.env.isWorker(), diff --git a/packages/transformers/typescript-types/src/shake.js b/packages/transformers/typescript-types/src/shake.js index 6e54366ffb2..571b1158b97 100644 --- a/packages/transformers/typescript-types/src/shake.js +++ b/packages/transformers/typescript-types/src/shake.js @@ -36,7 +36,15 @@ export function shake( // Deeply nested module declarations are assumed to be module augmentations and left alone. if (moduleStack.length >= 1) { // Since we are hoisting them to the top-level scope, we need to add a "declare" keyword to make them ambient. - node.modifiers.unshift(ts.createModifier(ts.SyntaxKind.DeclareKeyword)); + // we also want the declare keyword to come after the export keyword to guarantee a valid typings file. + node.modifiers ??= []; + const index = + node.modifiers[0]?.kind === ts.SyntaxKind.ExportKeyword ? 1 : 0; + node.modifiers.splice( + index, + 0, + ts.createModifier(ts.SyntaxKind.DeclareKeyword), + ); return node; } diff --git a/packages/transformers/vue/src/VueTransformer.js b/packages/transformers/vue/src/VueTransformer.js index 09cb188703e..7547f63c3cf 100644 --- a/packages/transformers/vue/src/VueTransformer.js +++ b/packages/transformers/vue/src/VueTransformer.js @@ -225,7 +225,14 @@ async function processPipeline({ } let content = template.content; if (template.lang && !['htm', 'html'].includes(template.lang)) { + let options = {}; let preprocessor = consolidate[template.lang]; + // Pug doctype fix (fixes #7756) + switch (template.lang) { + case 'pug': + options.doctype = 'html'; + break; + } if (!preprocessor) { // TODO: codeframe throw new ThrowableDiagnostic({ @@ -235,7 +242,7 @@ async function processPipeline({ }, }); } - content = await preprocessor.render(content, {}); + content = await preprocessor.render(content, options); } let templateComp = compiler.compileTemplate({ filename: asset.filePath, diff --git a/packages/utils/fs-write-stream-atomic/index.js b/packages/utils/fs-write-stream-atomic/index.js index eec056fb25f..849ea300260 100644 --- a/packages/utils/fs-write-stream-atomic/index.js +++ b/packages/utils/fs-write-stream-atomic/index.js @@ -64,25 +64,25 @@ function WriteStreamAtomic(path, options) { // data has been written to our target stream. So we suppress // finish from being emitted here, and only emit it after our // target stream is closed and we've moved everything around. -WriteStreamAtomic.prototype.emit = function(event) { +WriteStreamAtomic.prototype.emit = function (event) { if (event === 'finish') return this.__atomicStream.end(); return Writable.prototype.emit.apply(this, arguments); }; -WriteStreamAtomic.prototype._write = function(buffer, encoding, cb) { +WriteStreamAtomic.prototype._write = function (buffer, encoding, cb) { var flushed = this.__atomicStream.write(buffer, encoding); if (flushed) return cb(); this.__atomicStream.once('drain', cb); }; function handleOpen(writeStream) { - return function(fd) { + return function (fd) { writeStream.emit('open', fd); }; } function handleClose(writeStream) { - return function() { + return function () { if (writeStream.__atomicClosed) return; writeStream.__atomicClosed = true; if (writeStream.__atomicChown) { @@ -127,13 +127,13 @@ function handleClose(writeStream) { var targetFileHash = crypto.createHash('sha512'); fs.createReadStream(writeStream.__atomicTmp) - .on('data', function(data, enc) { + .on('data', function (data, enc) { tmpFileHash.update(data, enc); }) .on('error', fileHashError) .on('end', fileHashComplete); fs.createReadStream(writeStream.__atomicTarget) - .on('data', function(data, enc) { + .on('data', function (data, enc) { targetFileHash.update(data, enc); }) .on('error', fileHashError) @@ -157,7 +157,7 @@ function handleClose(writeStream) { } function cleanup(err) { - fs.unlink(writeStream.__atomicTmp, function() { + fs.unlink(writeStream.__atomicTmp, function () { if (err) { writeStream.emit('error', err); writeStream.emit('close'); @@ -175,14 +175,14 @@ function handleClose(writeStream) { // Delay the close to provide the same temporal separation a physical // file operation would have– that is, the close event is emitted only // after the async close operation completes. - setImmediate(function() { + setImmediate(function () { writeStream.emit('close'); }); } } function handleError(writeStream) { - return function(er) { + return function (er) { cleanupSync(); writeStream.emit('error', er); writeStream.__atomicClosed = true; diff --git a/packages/utils/hash/browser.js b/packages/utils/hash/browser.js index d5f4dbc8d27..7f75a142370 100644 --- a/packages/utils/hash/browser.js +++ b/packages/utils/hash/browser.js @@ -49,13 +49,7 @@ function concatUint8Arrays(arrays) { function toHex(arr) { let dataView = new DataView(arr.buffer); return ( - dataView - .getUint32(0, true) - .toString(16) - .padStart(8, '0') + - dataView - .getUint32(4, true) - .toString(16) - .padStart(8, '0') + dataView.getUint32(0, true).toString(16).padStart(8, '0') + + dataView.getUint32(4, true).toString(16).padStart(8, '0') ); } diff --git a/yarn.lock b/yarn.lock index 2e46ab7f141..f53b75c51c1 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2327,10 +2327,12 @@ "@swc/core-win32-ia32-msvc" "^1.2.106" "@swc/core-win32-x64-msvc" "^1.2.106" -"@swc/helpers@^0.3.6": - version "0.3.6" - resolved "https://registry.yarnpkg.com/@swc/helpers/-/helpers-0.3.6.tgz#b41e77dbf14e9cb138988484563ad9680ae5dc3d" - integrity sha512-xVl7Sddrl9/eMjEMqkH9lT8fLOGCuWHH9VmR2IBKQ8xTcjA6UQw4O3/4oS3xnyZHLtL9vC3P+C0kLneHpiqeEg== +"@swc/helpers@^0.3.13": + version "0.3.13" + resolved "https://registry.yarnpkg.com/@swc/helpers/-/helpers-0.3.13.tgz#b9af856aaa3804fefdd1544632dde35b7b6ff978" + integrity sha512-A1wswJhnqaLRn8uYVQ8YiNTtY5i/JIPmV08EXXjjTresIkUVUEUaFv/wXVhGXfRNYMvHPkuoMR1Nb6NgpxGjNg== + dependencies: + tslib "^2.4.0" "@tootallnate/once@2": version "2.0.0" @@ -7986,6 +7988,14 @@ last-run@^1.1.0: default-resolution "^2.0.0" es6-weak-map "^2.0.1" +launch-editor@^2.3.0: + version "2.3.0" + resolved "https://registry.yarnpkg.com/launch-editor/-/launch-editor-2.3.0.tgz#23b2081403b7eeaae2918bda510f3535ccab0ee4" + integrity sha512-3QrsCXejlWYHjBPFXTyGNhPj4rrQdB+5+r5r3wArpLH201aR+nWUgw/zKKkTmilCfY/sv6u8qo98pNvtg8LUTA== + dependencies: + picocolors "^1.0.0" + shell-quote "^1.6.1" + lazystream@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/lazystream/-/lazystream-1.0.0.tgz#f6995fe0f820392f61396be89462407bb77168e4" @@ -8123,40 +8133,40 @@ listr2@^2.1.0: rxjs "^6.5.5" through "^2.3.8" -lmdb-darwin-arm64@2.3.7: - version "2.3.7" - resolved "https://registry.yarnpkg.com/lmdb-darwin-arm64/-/lmdb-darwin-arm64-2.3.7.tgz#7cf694ff36ccb391ffdb25da5a754db5b900857e" - integrity sha512-1MylnXCB6kT7ug6onYTbFdxQL9wcgke0y6/nkpD+j7d58e5lUEggUhayqmmn2U8uvnL4UkNv5UBecTJp92cULw== - -lmdb-darwin-x64@2.3.7: - version "2.3.7" - resolved "https://registry.yarnpkg.com/lmdb-darwin-x64/-/lmdb-darwin-x64-2.3.7.tgz#6cbde7398427fdb346a3989faa5a0f10adb50922" - integrity sha512-KKq96InbgAprCEPNQSZjD5sKs95U9+jPrD5EimV22zv9W6VCuz2DQV1XAWxN/kQh9VWVU49CMZYrRZK38PNfXw== - -lmdb-linux-arm64@2.3.7: - version "2.3.7" - resolved "https://registry.yarnpkg.com/lmdb-linux-arm64/-/lmdb-linux-arm64-2.3.7.tgz#9d48be812f6bfe72e3fff457c6d83cc9eea17d76" - integrity sha512-tukWdxBZ6pcqIk7AGpBD8PSdVcWE2gwUt7wexqQLRnfaxubBCcea1tbV1rW3/a4RPEw60nQnjiPLIB2T37/vWg== - -lmdb-linux-arm@2.3.7: - version "2.3.7" - resolved "https://registry.yarnpkg.com/lmdb-linux-arm/-/lmdb-linux-arm-2.3.7.tgz#370f4b552fbb0d773bb5e25eb6f8781be93a44c3" - integrity sha512-Jd9l2TIgjhm6gjQhxv7lktwL0Lvyd216RBdLtASaUtRK5MsBjhnK/DTvsBv2UQly9Rz0Z8WgQvPwXD9CYSbL/Q== - -lmdb-linux-x64@2.3.7: - version "2.3.7" - resolved "https://registry.yarnpkg.com/lmdb-linux-x64/-/lmdb-linux-x64-2.3.7.tgz#35f2fe2175a47a755b05abed28b155d45581d808" - integrity sha512-7/+hBGVPpd6Pe1r8+YvvfzESrZw3U4lHmwPnD95m6ykkXRk4d0Zs7B4rD96GRg2DMA4g7mGsT4NAvdcJGPAzMw== - -lmdb-win32-x64@2.3.7: - version "2.3.7" - resolved "https://registry.yarnpkg.com/lmdb-win32-x64/-/lmdb-win32-x64-2.3.7.tgz#7af77cd7a89214e5ede00eb3dd02646aa4ce1a0b" - integrity sha512-g55xFj+4e22aNwEI1FoI2b4BP5l4HE2r+RwnfYbnHHPpnTrXpoeulcFraoIPLyJj7zBtcvAwbR7u75oUO688pA== - -lmdb@2.3.7: - version "2.3.7" - resolved "https://registry.yarnpkg.com/lmdb/-/lmdb-2.3.7.tgz#73086412639964c044fdd7f3628f3e2acaac61f6" - integrity sha512-oI7D1di+LbjISVh1Cv7Y5DUjHHRJ8pbU29PUccfQH72BIBknvC8o3E8ZJmKYHlL2sSmUXUY3yi/aOjd5nSb5WA== +lmdb-darwin-arm64@2.3.10: + version "2.3.10" + resolved "https://registry.yarnpkg.com/lmdb-darwin-arm64/-/lmdb-darwin-arm64-2.3.10.tgz#4e20f75770eeedc60af3d4630975fd105a89ffe8" + integrity sha512-LVXbH2MYu7/ZuQ8+P9rv+SwNyBKltxo7vHAGJS94HWyfwnCbKEYER9PImBvNBwzvgtaYk6x0RMX3oor6e6KdDQ== + +lmdb-darwin-x64@2.3.10: + version "2.3.10" + resolved "https://registry.yarnpkg.com/lmdb-darwin-x64/-/lmdb-darwin-x64-2.3.10.tgz#e53637a6735488eaa15feb7c0e9da142015b9476" + integrity sha512-gAc/1b/FZOb9yVOT+o0huA+hdW82oxLo5r22dFTLoRUFG1JMzxdTjmnW6ONVOHdqC9a5bt3vBCEY3jmXNqV26A== + +lmdb-linux-arm64@2.3.10: + version "2.3.10" + resolved "https://registry.yarnpkg.com/lmdb-linux-arm64/-/lmdb-linux-arm64-2.3.10.tgz#ac7db8bdfe0e9dbf2be1cc3362d6f2b79e2a9722" + integrity sha512-Ihr8mdICTK3jA4GXHxrXGK2oekn0mY6zuDSXQDNtyRSH19j3D2Y04A7SEI9S0EP/t5sjKSudYgZbiHDxRCsI5A== + +lmdb-linux-arm@2.3.10: + version "2.3.10" + resolved "https://registry.yarnpkg.com/lmdb-linux-arm/-/lmdb-linux-arm-2.3.10.tgz#74235418bbe7bf41e8ea5c9d52365c4ff5ca4b49" + integrity sha512-Rb8+4JjsThuEcJ7GLLwFkCFnoiwv/3hAAbELWITz70buQFF+dCZvCWWgEgmDTxwn5r+wIkdUjmFv4dqqiKQFmQ== + +lmdb-linux-x64@2.3.10: + version "2.3.10" + resolved "https://registry.yarnpkg.com/lmdb-linux-x64/-/lmdb-linux-x64-2.3.10.tgz#d790b95061d03c5c99a57b3ad5126f7723c60a2f" + integrity sha512-E3l3pDiCA9uvnLf+t3qkmBGRO01dp1EHD0x0g0iRnfpAhV7wYbayJGfG93BUt22Tj3fnq4HDo4dQ6ZWaDI1nuw== + +lmdb-win32-x64@2.3.10: + version "2.3.10" + resolved "https://registry.yarnpkg.com/lmdb-win32-x64/-/lmdb-win32-x64-2.3.10.tgz#bff73d12d94084343c569b16069d8d38626eb2d6" + integrity sha512-gspWk34tDANhjn+brdqZstJMptGiwj4qFNVg0Zey9ds+BUlif+Lgf5szrfOVzZ8gVRkk1Lgbz7i78+V7YK7SCA== + +lmdb@2.3.10: + version "2.3.10" + resolved "https://registry.yarnpkg.com/lmdb/-/lmdb-2.3.10.tgz#640fc60815846babcbe088d7f8ed0a51da857f6a" + integrity sha512-GtH+nStn9V59CfYeQ5ddx6YTfuFCmu86UJojIjJAweG+/Fm0PDknuk3ovgYDtY/foMeMdZa8/P7oSljW/d5UPw== dependencies: msgpackr "^1.5.4" nan "^2.14.2" @@ -8165,12 +8175,12 @@ lmdb@2.3.7: ordered-binary "^1.2.4" weak-lru-cache "^1.2.2" optionalDependencies: - lmdb-darwin-arm64 "2.3.7" - lmdb-darwin-x64 "2.3.7" - lmdb-linux-arm "2.3.7" - lmdb-linux-arm64 "2.3.7" - lmdb-linux-x64 "2.3.7" - lmdb-win32-x64 "2.3.7" + lmdb-darwin-arm64 "2.3.10" + lmdb-darwin-x64 "2.3.10" + lmdb-linux-arm "2.3.10" + lmdb-linux-arm64 "2.3.10" + lmdb-linux-x64 "2.3.10" + lmdb-win32-x64 "2.3.10" load-json-file@^1.0.0: version "1.1.0" @@ -10720,6 +10730,11 @@ react-dom@^17.0.2: object-assign "^4.1.1" scheduler "^0.20.2" +react-error-overlay@6.0.9: + version "6.0.9" + resolved "https://registry.yarnpkg.com/react-error-overlay/-/react-error-overlay-6.0.9.tgz#3c743010c9359608c375ecd6bc76f35d93995b0a" + integrity sha512-nQTTcUu+ATDbrSD1BZHr5kgSD4oF8OFjxun8uAaL8RwPBacGBNPf/yAuVVdx17N8XNzRDMrZ9XcKZHCjPW+9ew== + react-fast-compare@^2.0.0: version "2.0.4" resolved "https://registry.yarnpkg.com/react-fast-compare/-/react-fast-compare-2.0.4.tgz#e84b4d455b0fec113e0402c329352715196f81f9" @@ -11520,6 +11535,11 @@ shebang-regex@^3.0.0: resolved "https://registry.yarnpkg.com/shebang-regex/-/shebang-regex-3.0.0.tgz#ae16f1644d873ecad843b0307b143362d4c42172" integrity sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A== +shell-quote@^1.6.1: + version "1.7.3" + resolved "https://registry.yarnpkg.com/shell-quote/-/shell-quote-1.7.3.tgz#aa40edac170445b9a431e17bb62c0b881b9c4123" + integrity sha512-Vpfqwm4EnqGdlsBFNmHhxhElJYrdfcxPThu+ryKS5J8L/fhAwLazFZtq+S+TWZ9ANj2piSQLGj6NQg+lKPmxrw== + side-channel@^1.0.3, side-channel@^1.0.4: version "1.0.4" resolved "https://registry.yarnpkg.com/side-channel/-/side-channel-1.0.4.tgz#efce5c8fdc104ee751b25c58d4290011fa5ea2cf" @@ -12608,6 +12628,11 @@ tslib@^1.10.0, tslib@^1.9.0: resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.14.1.tgz#cf2d38bdc34a134bcaf1091c41f6619e2f672d00" integrity sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg== +tslib@^2.4.0: + version "2.4.0" + resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.4.0.tgz#7cecaa7f073ce680a05847aa77be941098f36dc3" + integrity sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ== + tty-browserify@^0.0.1: version "0.0.1" resolved "https://registry.yarnpkg.com/tty-browserify/-/tty-browserify-0.0.1.tgz#3f05251ee17904dfd0677546670db9651682b811" @@ -12690,9 +12715,9 @@ typedarray@^0.0.6: integrity sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c= typescript@>=3.0.0: - version "4.5.3" - resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.5.3.tgz#afaa858e68c7103317d89eb90c5d8906268d353c" - integrity sha512-eVYaEHALSt+s9LbvgEv4Ef+Tdq7hBiIZgii12xXJnukryt3pMgJf6aKhoCZ3FWQsu6sydEnkg11fYXLzhLBjeQ== + version "4.6.4" + resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.6.4.tgz#caa78bbc3a59e6a5c510d35703f6a09877ce45e9" + integrity sha512-9ia/jWHIEbo49HfjrLGfKbZSuWo9iTMwXO+Ca3pRsSpbsMbc7/IU8NKdCZVRRBafVPGnoJeFL76ZOAA84I9fEg== uglify-js@^3.1.4: version "3.7.6"