21 个不稳定版本 (8 个破坏性更新)

0.9.0 2024年7月11日
0.7.2 2024年7月9日
0.6.0 2023年4月1日
0.5.4 2023年3月25日

69压缩

Download history 1/week @ 2024-05-17 1/week @ 2024-06-07 1/week @ 2024-06-14 2/week @ 2024-06-28 535/week @ 2024-07-05 116/week @ 2024-07-12 317/week @ 2024-07-19 368/week @ 2024-07-26 332/week @ 2024-08-02 346/week @ 2024-08-09

1,365 每月下载量
用于 rspack_resolver

BSD-2-Clause

1MB
19K SLoC

JavaScript 18K SLoC // 0.0% comments Rust 1K SLoC // 0.0% comments

pnp-rs

该软件包实现了 Yarn Plug'n'Play 的 解析算法,以便在基于 Rust 的工具中轻松重用。它还包括允许透明地从 zip 存档中读取文件的实用工具。

安装

cargo add pnp

解析

fn example() {
    let manifest
        = load_pnp_manifest(".pnp.cjs").unwrap();

    let host = ResolutionHost {
        find_pnp_manifest: Box::new(move |_| Ok(Some(manifest.clone()))),
        ..Default::default()
    };

    let config = ResolutionConfig {
        host,
        ..Default::default()
    };

    let resolution = resolve_to_unqualified(
        "lodash/cloneDeep",
        std::path::PathBuf::from("/path/to/index.js"),
        &config,
    );

    match resolution {
        Ok(Resolution::Resolved(path, subpath)) => {
            // path = "/path/to/lodash.zip"
            // subpath = "cloneDeep"
        },
        Ok(Resolution::Skipped) => {
            // This is returned when the PnP resolver decides that it shouldn't
            // handle the resolution for this particular specifier. In that case,
            // the specifier should be forwarded to the default resolver.
        },
        Err(err) => {
            // An error happened during the resolution. Falling back to the default
            // resolver isn't recommended.
        },
    };
}

文件系统工具

虽然 PnP 仅处理解析,而不是文件系统,但 Yarn 生成的文件映射依赖于虚拟文件系统层,原因如下:

  • 虚拟包,需要具有不同路径的同包来考虑不同的依赖集(这仅发生在列出依赖项的包中)

  • Zip 存储空间,Yarn 使用它,因此安装的文件无需从存档中解包,从而实现更快的安装和更少的缓存损坏风险。

为了更容易地处理这些虚拟文件系统,pnp 软件包还包括一个名为 VPath 的枚举,允许您解析虚拟路径,以及一组 zip 操作实用工具(默认为 open_zip_via_read,如果启用了 mmap 功能,则为 open_zip_via_mmap)。

use pnp::fs::{VPath, open_zip_via_read};

fn read_file(p: PathBuf) -> std::io::Result<String> {
    match VPath::from(&p).unwrap() {
        // The path was virtual and stored within a zip file; we need to read from the zip file
        // Note that this opens the zip file every time, which is expensive; we'll see how to optimize that
        VPath::Zip(info) => {
            open_zip_via_read(info.physical_base_path()).unwrap().read_to_string(&zip_path)
        },

        // The path was virtual but not a zip file; we just need to read from the provided location
        VPath::Virtual(info) => {
            std::fs::read_to_string(info.physical_base_path())
        },

        // Nothing special to do, it's a regular path
        VPath::Native(p) => {
            std::fs::read_to_string(&p)
        },
    }
}

缓存重用

为每次单个文件访问打开和丢弃 zip 存档将会非常昂贵。为了避免这种情况,pnp-rs 提供了一个名为 LruZipCache 的辅助类,允许您抽象化 zip 的打开和关闭,并且只保留最近使用的存档打开。

use pnp::fs::{VPath, LruZipCache, open_zip_via_read};

const ZIP_CACHE: Lazy<LruZipCache<Vec<u8>>> = Lazy::new(|| {
    // It'll keep the last 50 zip archives open
    LruZipCache::new(50, open_zip_via_read_p)
});

fn read_file(p: PathBuf) -> std::io::Result<String> {
    match VPath::from(&p).unwrap() {
        // The path was virtual and stored within a zip file; we need to read from the zip file
        VPath::Zip(info) => {
            ZIP_CACHE.read_to_string(info.physical_base_path(), &zip_path)
        },

        // The path was virtual but not a zip file; we just need to read from the provided location
        VPath::Virtual(info) => {
            std::fs::read_to_string(info.physical_base_path())
        },

        // Nothing special to do, it's a regular path
        VPath::Native(p) => {
            std::fs::read_to_string(&p)
        },
    }
}

依赖项

~4–35MB
~476K SLoC