├── .yarnrc.yml ├── crates ├── package.json ├── plugin_manifest │ ├── src │ │ ├── lib.rs │ │ └── plugin.rs │ └── Cargo.toml ├── swc_compiler │ ├── src │ │ └── lib.rs │ └── Cargo.toml ├── node_binding │ ├── build.rs │ ├── npm │ │ ├── darwin-x64 │ │ │ ├── README.md │ │ │ └── package.json │ │ ├── darwin-arm64 │ │ │ ├── README.md │ │ │ └── package.json │ │ ├── darwin-universal │ │ │ ├── README.md │ │ │ └── package.json │ │ ├── linux-x64-gnu │ │ │ ├── README.md │ │ │ └── package.json │ │ ├── win32-ia32-msvc │ │ │ ├── README.md │ │ │ └── package.json │ │ ├── win32-x64-msvc │ │ │ ├── README.md │ │ │ └── package.json │ │ ├── linux-arm64-gnu │ │ │ ├── README.md │ │ │ └── package.json │ │ ├── linux-x64-musl │ │ │ ├── README.md │ │ │ └── package.json │ │ ├── win32-arm64-msvc │ │ │ ├── README.md │ │ │ └── package.json │ │ └── linux-arm64-musl │ │ │ ├── README.md │ │ │ └── package.json │ ├── package.json │ ├── src │ │ ├── compiler.rs │ │ ├── panic.rs │ │ ├── diagnostic.rs │ │ └── resolver_factory.rs │ └── Cargo.toml ├── swc_keep_export │ ├── src │ │ └── lib.rs │ └── Cargo.toml ├── binding_values │ ├── src │ │ ├── raw_options │ │ │ ├── raw_mode.rs │ │ │ ├── raw_node.rs │ │ │ ├── raw_experiments │ │ │ │ ├── raw_rspack_future.rs │ │ │ │ ├── raw_cache │ │ │ │ │ ├── raw_storage.rs │ │ │ │ │ ├── raw_snapshot.rs │ │ │ │ │ └── mod.rs │ │ │ │ ├── mod.rs │ │ │ │ └── raw_incremental.rs │ │ │ ├── raw_stats.rs │ │ │ ├── raw_cache.rs │ │ │ ├── raw_builtins │ │ │ │ ├── raw_ids.rs │ │ │ │ ├── raw_limit_chunk_count.rs │ │ │ │ ├── raw_bundle_info.rs │ │ │ │ ├── raw_ignore.rs │ │ │ │ ├── raw_size_limits.rs │ │ │ │ ├── raw_css_extract.rs │ │ │ │ ├── raw_runtime_chunk.rs │ │ │ │ ├── raw_progress.rs │ │ │ │ ├── raw_banner.rs │ │ │ │ ├── raw_swc_js_minimizer.rs │ │ │ │ ├── raw_lazy_compilation.rs │ │ │ │ ├── raw_lightning_css_minimizer.rs │ │ │ │ ├── raw_dll.rs │ │ │ │ ├── raw_html.rs │ │ │ │ ├── raw_copy.rs │ │ │ │ └── raw_mf.rs │ │ │ ├── raw_split_chunks │ │ │ │ ├── raw_split_chunk_size.rs │ │ │ │ ├── raw_split_chunk_chunks.rs │ │ │ │ ├── raw_split_chunk_cache_group_test.rs │ │ │ │ └── raw_split_chunk_name.rs │ │ │ ├── raw_dynamic_entry.rs │ │ │ ├── raw_optimization.rs │ │ │ └── raw_external.rs │ │ ├── options │ │ │ ├── mod.rs │ │ │ └── entry.rs │ │ ├── plugins │ │ │ ├── mod.rs │ │ │ ├── buildtime_plugins.rs │ │ │ ├── js_loader │ │ │ │ ├── mod.rs │ │ │ │ ├── scheduler.rs │ │ │ │ └── context.rs │ │ │ └── context_replacement.rs │ │ ├── identifier.rs │ │ ├── utils.rs │ │ ├── clean_options.rs │ │ ├── resource_data.rs │ │ ├── asset_condition.rs │ │ ├── lib.rs │ │ ├── normal_module_factory.rs │ │ ├── codegen_result.rs │ │ ├── resolver.rs │ │ ├── path_data.rs │ │ ├── rspack_error.rs │ │ ├── compilation │ │ │ └── dependencies.rs │ │ ├── exports_info.rs │ │ ├── filename.rs │ │ ├── chunk_graph.rs │ │ ├── dependency_block.rs │ │ ├── asset.rs │ │ ├── module_graph_connection.rs │ │ └── module_graph.rs │ └── Cargo.toml ├── swc_change_package_import │ ├── .gitignore │ ├── tests │ │ └── fixture │ │ │ ├── ice_basic_transform │ │ │ ├── input.js │ │ │ └── output.js │ │ │ ├── single_specific_transform │ │ │ ├── input.js │ │ │ └── output.js │ │ │ ├── ice_alias_transform │ │ │ ├── input.js │ │ │ └── output.js │ │ │ ├── ice_as_transform │ │ │ ├── input.js │ │ │ └── output.js │ │ │ ├── single_specific_transform_2 │ │ │ ├── input.js │ │ │ └── output.js │ │ │ ├── ice_alias_with_as_transform │ │ │ ├── input.js │ │ │ └── output.js │ │ │ ├── ice_miss_match_transform │ │ │ ├── input.js │ │ │ └── output.js │ │ │ ├── ice_multiple_transform │ │ │ ├── input.js │ │ │ └── output.js │ │ │ ├── ice_matched_transform │ │ │ ├── input.js │ │ │ └── output.js │ │ │ ├── single_literal_transform │ │ │ ├── input.js │ │ │ └── output.js │ │ │ ├── mix_specific_transform │ │ │ ├── input.js │ │ │ └── output.js │ │ │ ├── multi_specific_transform │ │ │ ├── input.js │ │ │ └── output.js │ │ │ └── multi_literal_transform │ │ │ ├── input.js │ │ │ └── output.js │ ├── src │ │ ├── lib.rs │ │ └── config.rs │ ├── package.json │ └── Cargo.toml ├── swc_remove_export │ ├── src │ │ └── lib.rs │ └── Cargo.toml ├── swc_env_replacement │ ├── src │ │ └── lib.rs │ └── Cargo.toml ├── swc_named_import_transform │ ├── Cargo.toml │ └── src │ │ └── lib.rs ├── swc_optimize_barrel │ └── Cargo.toml ├── loader_barrel │ └── Cargo.toml └── loader_compilation │ ├── Cargo.toml │ └── src │ └── transform.rs ├── pnpm-workspace.yaml ├── scripts ├── clone-rspack.mjs ├── clean.mjs ├── test.mjs └── github.mjs ├── .npmignore ├── rust-toolchain.toml ├── rustfmt.toml ├── README.md ├── package.json ├── .github ├── actions │ ├── clone-crates │ │ └── action.yml │ ├── pnpm-cache │ │ └── action.yml │ └── rustup │ │ └── action.yml └── workflows │ └── ci.yml ├── .cargo └── config.toml └── .gitignore /.yarnrc.yml: -------------------------------------------------------------------------------- 1 | nodeLinker: node-modules -------------------------------------------------------------------------------- /crates/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "1.2.2" 3 | } -------------------------------------------------------------------------------- /pnpm-workspace.yaml: -------------------------------------------------------------------------------- 1 | packages: 2 | - "crates/node_binding" -------------------------------------------------------------------------------- /crates/plugin_manifest/src/lib.rs: -------------------------------------------------------------------------------- 1 | mod plugin; 2 | pub use plugin::*; 3 | -------------------------------------------------------------------------------- /crates/swc_compiler/src/lib.rs: -------------------------------------------------------------------------------- 1 | mod compiler; 2 | pub use compiler::*; 3 | -------------------------------------------------------------------------------- /crates/node_binding/build.rs: -------------------------------------------------------------------------------- 1 | fn main() { 2 | napi_build::setup(); 3 | } 4 | -------------------------------------------------------------------------------- /crates/swc_keep_export/src/lib.rs: -------------------------------------------------------------------------------- 1 | mod transform; 2 | pub use transform::*; 3 | -------------------------------------------------------------------------------- /crates/binding_values/src/raw_options/raw_mode.rs: -------------------------------------------------------------------------------- 1 | pub type RawMode = String; 2 | -------------------------------------------------------------------------------- /crates/swc_change_package_import/.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | ^target/ 3 | target 4 | -------------------------------------------------------------------------------- /crates/swc_remove_export/src/lib.rs: -------------------------------------------------------------------------------- 1 | mod transform; 2 | pub use transform::*; 3 | -------------------------------------------------------------------------------- /crates/swc_change_package_import/tests/fixture/ice_basic_transform/input.js: -------------------------------------------------------------------------------- 1 | import {runApp} from "ice"; -------------------------------------------------------------------------------- /crates/swc_change_package_import/tests/fixture/single_specific_transform/input.js: -------------------------------------------------------------------------------- 1 | import { x } from "y"; -------------------------------------------------------------------------------- /crates/binding_values/src/options/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod entry; 2 | pub mod library; 3 | pub mod raw_resolve; 4 | -------------------------------------------------------------------------------- /crates/swc_change_package_import/tests/fixture/ice_alias_transform/input.js: -------------------------------------------------------------------------------- 1 | import { Head } from 'ice'; 2 | -------------------------------------------------------------------------------- /crates/swc_change_package_import/tests/fixture/ice_as_transform/input.js: -------------------------------------------------------------------------------- 1 | import {runApp as run} from 'ice'; -------------------------------------------------------------------------------- /crates/swc_change_package_import/tests/fixture/single_specific_transform_2/input.js: -------------------------------------------------------------------------------- 1 | import {x as k} from "y"; -------------------------------------------------------------------------------- /scripts/clone-rspack.mjs: -------------------------------------------------------------------------------- 1 | import { getRspackCrates } from './github.mjs'; 2 | 3 | getRspackCrates(); 4 | -------------------------------------------------------------------------------- /crates/swc_change_package_import/tests/fixture/ice_alias_transform/output.js: -------------------------------------------------------------------------------- 1 | import Head from "react-helmet"; 2 | -------------------------------------------------------------------------------- /crates/swc_change_package_import/tests/fixture/single_specific_transform_2/output.js: -------------------------------------------------------------------------------- 1 | import { a as k } from "m/n"; -------------------------------------------------------------------------------- /crates/swc_env_replacement/src/lib.rs: -------------------------------------------------------------------------------- 1 | #![feature(box_patterns)] 2 | mod transform; 3 | pub use transform::*; 4 | -------------------------------------------------------------------------------- /crates/swc_change_package_import/tests/fixture/ice_as_transform/output.js: -------------------------------------------------------------------------------- 1 | import { runApp as run } from "@ice/runtime"; -------------------------------------------------------------------------------- /crates/swc_change_package_import/tests/fixture/ice_basic_transform/output.js: -------------------------------------------------------------------------------- 1 | import { runApp } from "@ice/runtime"; -------------------------------------------------------------------------------- /crates/swc_change_package_import/tests/fixture/single_specific_transform/output.js: -------------------------------------------------------------------------------- 1 | import { a as x } from "m/n"; 2 | -------------------------------------------------------------------------------- /crates/swc_change_package_import/tests/fixture/ice_alias_with_as_transform/input.js: -------------------------------------------------------------------------------- 1 | import { Head as Header } from 'ice'; -------------------------------------------------------------------------------- /crates/swc_change_package_import/tests/fixture/ice_alias_with_as_transform/output.js: -------------------------------------------------------------------------------- 1 | import Header from "react-helmet"; 2 | -------------------------------------------------------------------------------- /crates/swc_change_package_import/tests/fixture/ice_miss_match_transform/input.js: -------------------------------------------------------------------------------- 1 | import { defineDataLoader } from 'ice'; -------------------------------------------------------------------------------- /crates/swc_change_package_import/tests/fixture/ice_miss_match_transform/output.js: -------------------------------------------------------------------------------- 1 | import { defineDataLoader } from 'ice'; -------------------------------------------------------------------------------- /crates/swc_change_package_import/tests/fixture/ice_multiple_transform/input.js: -------------------------------------------------------------------------------- 1 | import { request, store, test } from 'ice'; -------------------------------------------------------------------------------- /crates/swc_change_package_import/tests/fixture/ice_matched_transform/input.js: -------------------------------------------------------------------------------- 1 | import { runApp, defineDataLoader } from "ice"; -------------------------------------------------------------------------------- /crates/swc_change_package_import/tests/fixture/single_literal_transform/input.js: -------------------------------------------------------------------------------- 1 | import {x} from "y"; 2 | import {p} from "q"; 3 | import j from "k"; -------------------------------------------------------------------------------- /crates/node_binding/npm/darwin-x64/README.md: -------------------------------------------------------------------------------- 1 | # `@ice/pack-binding-darwin-x64` 2 | 3 | This is the **x86_64-apple-darwin** binary for `@ice/pack-binding` 4 | -------------------------------------------------------------------------------- /crates/swc_change_package_import/src/lib.rs: -------------------------------------------------------------------------------- 1 | mod change_package_import; 2 | mod config; 3 | 4 | pub use change_package_import::*; 5 | pub use config::*; 6 | -------------------------------------------------------------------------------- /crates/swc_change_package_import/tests/fixture/single_literal_transform/output.js: -------------------------------------------------------------------------------- 1 | import x from "y/x"; 2 | import { p } from "q"; 3 | import j from "k"; 4 | -------------------------------------------------------------------------------- /crates/node_binding/npm/darwin-arm64/README.md: -------------------------------------------------------------------------------- 1 | # `@ice/pack-binding-darwin-arm64` 2 | 3 | This is the **aarch64-apple-darwin** binary for `@ice/pack-binding` 4 | -------------------------------------------------------------------------------- /crates/node_binding/npm/darwin-universal/README.md: -------------------------------------------------------------------------------- 1 | # `@ice/pack-binding-darwin-universal` 2 | 3 | This is the **universal-apple-darwin** binary for `@ice/pack-binding` -------------------------------------------------------------------------------- /crates/node_binding/npm/linux-x64-gnu/README.md: -------------------------------------------------------------------------------- 1 | # `@ice/pack-binding-linux-x64-gnu` 2 | 3 | This is the **x86_64-unknown-linux-gnu** binary for `@ice/pack-binding` 4 | -------------------------------------------------------------------------------- /crates/node_binding/npm/win32-ia32-msvc/README.md: -------------------------------------------------------------------------------- 1 | # `@ice/pack-binding-win32-ia32-msvc` 2 | 3 | This is the **i686-pc-windows-msvc** binary for `@ice/pack-binding` 4 | -------------------------------------------------------------------------------- /crates/node_binding/npm/win32-x64-msvc/README.md: -------------------------------------------------------------------------------- 1 | # `@ice/pack-binding-win32-x64-msvc` 2 | 3 | This is the **x86_64-pc-windows-msvc** binary for `@ice/pack-binding` 4 | -------------------------------------------------------------------------------- /scripts/clean.mjs: -------------------------------------------------------------------------------- 1 | import { copyAndCleanUp, getGithubInfo } from "./github.mjs"; 2 | 3 | const { temp, dest } = getGithubInfo(); 4 | copyAndCleanUp(temp, dest); 5 | -------------------------------------------------------------------------------- /crates/node_binding/npm/linux-arm64-gnu/README.md: -------------------------------------------------------------------------------- 1 | # `@ice/pack-binding-linux-arm64-gnu` 2 | 3 | This is the **aarch64-unknown-linux-gnu** binary for `@ice/pack-binding` 4 | -------------------------------------------------------------------------------- /crates/node_binding/npm/linux-x64-musl/README.md: -------------------------------------------------------------------------------- 1 | # `@ice/pack-binding-linux-x64-musl` 2 | 3 | This is the **x86_64-unknown-linux-musl** binary for `@ice/pack-binding` 4 | -------------------------------------------------------------------------------- /crates/node_binding/npm/win32-arm64-msvc/README.md: -------------------------------------------------------------------------------- 1 | # `@ice/pack-binding-win32-arm64-msvc` 2 | 3 | This is the **aarch64-pc-windows-msvc** binary for `@ice/pack-binding` 4 | -------------------------------------------------------------------------------- /crates/swc_change_package_import/tests/fixture/ice_matched_transform/output.js: -------------------------------------------------------------------------------- 1 | import { runApp } from "@ice/runtime"; 2 | import { defineDataLoader } from "@ice/runtime"; -------------------------------------------------------------------------------- /crates/node_binding/npm/linux-arm64-musl/README.md: -------------------------------------------------------------------------------- 1 | # `@ice/pack-binding-linux-arm64-musl` 2 | 3 | This is the **arm64-unknown-linux-musl** binary for `@ice/pack-binding` 4 | -------------------------------------------------------------------------------- /crates/swc_change_package_import/tests/fixture/mix_specific_transform/input.js: -------------------------------------------------------------------------------- 1 | import {Button, Spin} from "antd"; 2 | import {a} from "ice"; 3 | import {isArray} from "lodash"; -------------------------------------------------------------------------------- /crates/swc_change_package_import/tests/fixture/ice_multiple_transform/output.js: -------------------------------------------------------------------------------- 1 | import { request } from "axios"; 2 | import store from "@ice/store"; 3 | import { test } from "axios"; 4 | -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | target 2 | Cargo.lock 3 | .cargo 4 | .github 5 | npm 6 | .eslintrc 7 | .prettierignore 8 | rustfmt.toml 9 | yarn.lock 10 | *.node 11 | .yarn 12 | __test__ 13 | renovate.json 14 | -------------------------------------------------------------------------------- /crates/swc_change_package_import/tests/fixture/multi_specific_transform/input.js: -------------------------------------------------------------------------------- 1 | import {a, b, c as d} from "e"; 2 | import {j, q} from "k" 3 | // import "f"; 4 | // import g from "k"; 5 | // import y from "z"; -------------------------------------------------------------------------------- /crates/swc_change_package_import/tests/fixture/multi_specific_transform/output.js: -------------------------------------------------------------------------------- 1 | import { a, b, c as d } from "e"; 2 | import { j, q } from "k"; // import "f"; 3 | // import g from "k"; 4 | // import y from "z"; -------------------------------------------------------------------------------- /crates/swc_change_package_import/tests/fixture/multi_literal_transform/input.js: -------------------------------------------------------------------------------- 1 | import a from "b"; 2 | import {x, y} from "z"; 3 | import c from "d"; 4 | import {p, q as r} from "o"; 5 | // import {a, c, d} from "i"; -------------------------------------------------------------------------------- /crates/swc_change_package_import/tests/fixture/mix_specific_transform/output.js: -------------------------------------------------------------------------------- 1 | import Button from "antd/Button"; 2 | import Spin from "antd/Spin"; 3 | import a from "@ice/x/y"; 4 | import { isArray } from "lodash"; 5 | -------------------------------------------------------------------------------- /rust-toolchain.toml: -------------------------------------------------------------------------------- 1 | [toolchain] 2 | profile = "default" 3 | # Use nightly for better access to the latest Rust features. 4 | # This date is aligned to stable release dates. 5 | channel = "nightly-2024-11-27" # v1.83.0 6 | -------------------------------------------------------------------------------- /crates/binding_values/src/plugins/mod.rs: -------------------------------------------------------------------------------- 1 | mod context_replacement; 2 | mod js_loader; 3 | 4 | pub use context_replacement::*; 5 | pub(super) use js_loader::{JsLoaderRspackPlugin, JsLoaderRunner}; 6 | pub mod buildtime_plugins; 7 | -------------------------------------------------------------------------------- /rustfmt.toml: -------------------------------------------------------------------------------- 1 | format_code_in_doc_comments = true # https://rust-lang.github.io/rustfmt/?version=v1.5.1&search=#format_code_in_doc_comments 2 | tab_spaces = 2 3 | 4 | group_imports = "StdExternalCrate" 5 | unstable_features = true 6 | -------------------------------------------------------------------------------- /crates/binding_values/src/raw_options/raw_node.rs: -------------------------------------------------------------------------------- 1 | use napi_derive::napi; 2 | 3 | #[derive(Debug, Default)] 4 | #[napi(object)] 5 | pub struct RawNodeOption { 6 | pub dirname: String, 7 | pub filename: String, 8 | pub global: String, 9 | } 10 | -------------------------------------------------------------------------------- /crates/swc_change_package_import/tests/fixture/multi_literal_transform/output.js: -------------------------------------------------------------------------------- 1 | import a from "b"; 2 | import x from "z/x"; 3 | import y from "z/y"; 4 | import c from "d"; 5 | import p from "o/p"; 6 | import r from "o/q"; 7 | // import {a, c, d} from "i"; 8 | -------------------------------------------------------------------------------- /crates/swc_env_replacement/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "swc_env_replacement" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | [dependencies] 7 | swc_core = { workspace = true, features = [ 8 | "base", 9 | "ecma_ast", 10 | "common" 11 | ] } -------------------------------------------------------------------------------- /crates/swc_named_import_transform/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "swc_named_import_transform" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | [dependencies] 7 | swc_core = { workspace = true, features = [ 8 | "base", 9 | "ecma_ast", 10 | "common" 11 | ]} -------------------------------------------------------------------------------- /crates/swc_keep_export/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "swc_keep_export" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | [dependencies] 7 | fxhash = "0.2.1" 8 | swc_core = { workspace = true, features = [ 9 | "base", 10 | "ecma_ast", 11 | "common" 12 | ] } 13 | rspack_error = { path = "../.rspack_crates/rspack_error" } -------------------------------------------------------------------------------- /crates/swc_remove_export/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "swc_remove_export" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | [dependencies] 7 | fxhash = "0.2.1" 8 | swc_core = { workspace = true, features = [ 9 | "base", 10 | "ecma_ast", 11 | "common" 12 | ] } 13 | rspack_error = { path = "../.rspack_crates/rspack_error" } -------------------------------------------------------------------------------- /crates/swc_optimize_barrel/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "swc_optimize_barrel" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | [dependencies] 7 | serde_json = { workspace = true } 8 | serde = { workspace = true, features = ["derive"] } 9 | swc_core = { workspace = true, features = [ 10 | "base", 11 | "ecma_ast", 12 | "common" 13 | ]} -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |

A bundler based on Rspack

2 | 3 | This project is under active development. It is based on Rspack, and it will add more built-in features required by ice framework. 4 | 5 | ## Credits 6 | 7 | Thanks to the [rspack](https://github.com/web-infra-dev/rspack) project which provides the ability to customize plugins and loaders implemented by Rust. 8 | -------------------------------------------------------------------------------- /crates/binding_values/src/raw_options/raw_experiments/raw_rspack_future.rs: -------------------------------------------------------------------------------- 1 | use napi_derive::napi; 2 | use rspack_core::RspackFuture; 3 | 4 | #[allow(clippy::empty_structs_with_brackets)] 5 | #[derive(Debug, Default)] 6 | #[napi(object)] 7 | pub struct RawRspackFuture {} 8 | 9 | impl From for RspackFuture { 10 | fn from(_value: RawRspackFuture) -> Self { 11 | Self {} 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /crates/binding_values/src/raw_options/raw_stats.rs: -------------------------------------------------------------------------------- 1 | use napi_derive::napi; 2 | use rspack_core::StatsOptions; 3 | 4 | #[derive(Debug, Default)] 5 | #[napi(object)] 6 | pub struct RawStatsOptions { 7 | pub colors: bool, 8 | } 9 | 10 | impl From for StatsOptions { 11 | fn from(value: RawStatsOptions) -> Self { 12 | Self { 13 | colors: value.colors, 14 | } 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /crates/binding_values/src/plugins/buildtime_plugins.rs: -------------------------------------------------------------------------------- 1 | use rspack_core::{BoxPlugin, PluginExt}; 2 | use rspack_plugin_javascript::{api_plugin::APIPlugin, JsPlugin}; 3 | use rspack_plugin_runtime::RuntimePlugin; 4 | 5 | pub fn buildtime_plugins() -> Vec { 6 | vec![ 7 | JsPlugin::default().boxed(), 8 | RuntimePlugin::default().boxed(), 9 | APIPlugin::default().boxed(), 10 | ] 11 | } 12 | -------------------------------------------------------------------------------- /crates/node_binding/npm/darwin-universal/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@ice/pack-binding-darwin-universal", 3 | "version": "0.0.1", 4 | "os": [ 5 | "darwin" 6 | ], 7 | "main": "pack-binding.darwin-universal.node", 8 | "files": [ 9 | "pack-binding.darwin-universal.node" 10 | ], 11 | "license": "MIT", 12 | "engines": { 13 | "node": ">= 10" 14 | }, 15 | "repository": { 16 | "url": "https://github.com/ice-lab/icepack" 17 | } 18 | } -------------------------------------------------------------------------------- /crates/swc_change_package_import/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "plugin_optimize_package_import", 3 | "version": "0.1.0", 4 | "description": "", 5 | "author": "", 6 | "license": "ISC", 7 | "keywords": ["swc-plugin"], 8 | "main": "target/wasm32-wasi/release/plugin_optimize_package_import.wasm", 9 | "scripts": { 10 | "prepublishOnly": "cargo build-wasi --release" 11 | }, 12 | "files": [], 13 | "preferUnplugged": true 14 | } 15 | -------------------------------------------------------------------------------- /crates/node_binding/npm/darwin-x64/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@ice/pack-binding-darwin-x64", 3 | "version": "0.0.1", 4 | "os": [ 5 | "darwin" 6 | ], 7 | "cpu": [ 8 | "x64" 9 | ], 10 | "main": "pack-binding.darwin-x64.node", 11 | "files": [ 12 | "pack-binding.darwin-x64.node" 13 | ], 14 | "license": "MIT", 15 | "engines": { 16 | "node": ">= 10" 17 | }, 18 | "repository": { 19 | "url": "https://github.com/ice-lab/icepack" 20 | } 21 | } -------------------------------------------------------------------------------- /crates/node_binding/npm/darwin-arm64/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@ice/pack-binding-darwin-arm64", 3 | "version": "0.0.1", 4 | "os": [ 5 | "darwin" 6 | ], 7 | "cpu": [ 8 | "arm64" 9 | ], 10 | "main": "pack-binding.darwin-arm64.node", 11 | "files": [ 12 | "pack-binding.darwin-arm64.node" 13 | ], 14 | "license": "MIT", 15 | "engines": { 16 | "node": ">= 10" 17 | }, 18 | "repository": { 19 | "url": "https://github.com/ice-lab/icepack" 20 | } 21 | } -------------------------------------------------------------------------------- /crates/node_binding/npm/win32-x64-msvc/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@ice/pack-binding-win32-x64-msvc", 3 | "version": "0.0.1", 4 | "os": [ 5 | "win32" 6 | ], 7 | "cpu": [ 8 | "x64" 9 | ], 10 | "main": "pack-binding.win32-x64-msvc.node", 11 | "files": [ 12 | "pack-binding.win32-x64-msvc.node" 13 | ], 14 | "license": "MIT", 15 | "engines": { 16 | "node": ">= 10" 17 | }, 18 | "repository": { 19 | "url": "https://github.com/ice-lab/icepack" 20 | } 21 | } -------------------------------------------------------------------------------- /crates/node_binding/npm/win32-ia32-msvc/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@ice/pack-binding-win32-ia32-msvc", 3 | "version": "0.0.1", 4 | "os": [ 5 | "win32" 6 | ], 7 | "cpu": [ 8 | "i686" 9 | ], 10 | "main": "pack-binding.win32-ia32-msvc.node", 11 | "files": [ 12 | "pack-binding.win32-ia32-msvc.node" 13 | ], 14 | "license": "MIT", 15 | "engines": { 16 | "node": ">= 10" 17 | }, 18 | "repository": { 19 | "url": "https://github.com/ice-lab/icepack" 20 | } 21 | } -------------------------------------------------------------------------------- /crates/node_binding/npm/win32-arm64-msvc/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@ice/pack-binding-win32-arm64-msvc", 3 | "version": "0.0.1", 4 | "os": [ 5 | "win32" 6 | ], 7 | "cpu": [ 8 | "arm64" 9 | ], 10 | "main": "pack-binding.win32-arm64-msvc.node", 11 | "files": [ 12 | "pack-binding.win32-arm64-msvc.node" 13 | ], 14 | "license": "MIT", 15 | "engines": { 16 | "node": ">= 10" 17 | }, 18 | "repository": { 19 | "url": "https://github.com/ice-lab/icepack" 20 | } 21 | } -------------------------------------------------------------------------------- /crates/node_binding/npm/linux-x64-gnu/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@ice/pack-binding-linux-x64-gnu", 3 | "version": "0.0.1", 4 | "os": [ 5 | "linux" 6 | ], 7 | "cpu": [ 8 | "x64" 9 | ], 10 | "main": "pack-binding.linux-x64-gnu.node", 11 | "files": [ 12 | "pack-binding.linux-x64-gnu.node" 13 | ], 14 | "license": "MIT", 15 | "engines": { 16 | "node": ">= 10" 17 | }, 18 | "libc": [ 19 | "glibc" 20 | ], 21 | "repository": { 22 | "url": "https://github.com/ice-lab/icepack" 23 | } 24 | } -------------------------------------------------------------------------------- /crates/node_binding/npm/linux-x64-musl/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@ice/pack-binding-linux-x64-musl", 3 | "version": "0.0.1", 4 | "os": [ 5 | "linux" 6 | ], 7 | "cpu": [ 8 | "x64" 9 | ], 10 | "main": "pack-binding.linux-x64-musl.node", 11 | "files": [ 12 | "pack-binding.linux-x64-musl.node" 13 | ], 14 | "license": "MIT", 15 | "engines": { 16 | "node": ">= 10" 17 | }, 18 | "libc": [ 19 | "musl" 20 | ], 21 | "repository": { 22 | "url": "https://github.com/ice-lab/icepack" 23 | } 24 | } -------------------------------------------------------------------------------- /crates/node_binding/npm/linux-arm64-gnu/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@ice/pack-binding-linux-arm64-gnu", 3 | "version": "0.0.1", 4 | "os": [ 5 | "linux" 6 | ], 7 | "cpu": [ 8 | "arm64" 9 | ], 10 | "main": "pack-binding.linux-arm64-gnu.node", 11 | "files": [ 12 | "pack-binding.linux-arm64-gnu.node" 13 | ], 14 | "license": "MIT", 15 | "engines": { 16 | "node": ">= 10" 17 | }, 18 | "libc": [ 19 | "glibc" 20 | ], 21 | "repository": { 22 | "url": "https://github.com/ice-lab/icepack" 23 | } 24 | } -------------------------------------------------------------------------------- /crates/node_binding/npm/linux-arm64-musl/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@ice/pack-binding-linux-arm64-musl", 3 | "version": "0.0.1", 4 | "os": [ 5 | "linux" 6 | ], 7 | "cpu": [ 8 | "arm64" 9 | ], 10 | "main": "pack-binding.linux-arm64-musl.node", 11 | "files": [ 12 | "pack-binding.linux-arm64-musl.node" 13 | ], 14 | "license": "MIT", 15 | "engines": { 16 | "node": ">= 10" 17 | }, 18 | "libc": [ 19 | "musl" 20 | ], 21 | "repository": { 22 | "url": "https://github.com/ice-lab/icepack" 23 | } 24 | } -------------------------------------------------------------------------------- /crates/binding_values/src/raw_options/raw_cache.rs: -------------------------------------------------------------------------------- 1 | use napi_derive::napi; 2 | use rspack_core::CacheOptions; 3 | 4 | #[derive(Debug, Default)] 5 | #[napi(object, object_to_js = false)] 6 | pub struct RawCacheOptions { 7 | pub r#type: String, 8 | } 9 | 10 | impl From for CacheOptions { 11 | fn from(value: RawCacheOptions) -> CacheOptions { 12 | let RawCacheOptions { r#type } = value; 13 | 14 | match r#type.as_str() { 15 | "memory" => CacheOptions::Memory, 16 | _ => CacheOptions::Disabled, 17 | } 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /crates/plugin_manifest/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "plugin_manifest" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 7 | 8 | [dependencies] 9 | async-trait = { workspace = true } 10 | tracing = { workspace = true } 11 | regex = { workspace = true } 12 | serde = { workspace = true, features = ["derive"] } 13 | serde_json = { workspace = true } 14 | rspack_core = { workspace = true } 15 | rspack_hook = { workspace = true } 16 | rspack_error = { workspace = true } -------------------------------------------------------------------------------- /crates/binding_values/src/raw_options/raw_builtins/raw_ids.rs: -------------------------------------------------------------------------------- 1 | use napi_derive::napi; 2 | use rspack_ids::OccurrenceChunkIdsPluginOptions; 3 | 4 | #[derive(Debug)] 5 | #[napi(object, object_to_js = false)] 6 | pub struct RawOccurrenceChunkIdsPluginOptions { 7 | pub prioritise_initial: Option, 8 | } 9 | 10 | impl From for OccurrenceChunkIdsPluginOptions { 11 | fn from(value: RawOccurrenceChunkIdsPluginOptions) -> Self { 12 | Self { 13 | prioritise_initial: value.prioritise_initial.unwrap_or_default(), 14 | } 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /crates/swc_change_package_import/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "swc_change_package_import" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | [lib] 7 | crate-type = ["cdylib", "rlib"] 8 | 9 | [dependencies] 10 | serde = { workspace = true } 11 | serde_json = { workspace = true } 12 | swc_core = { workspace = true, features = [ 13 | "base", 14 | "ecma_ast", 15 | "ecma_utils", 16 | "ecma_visit", 17 | "common", 18 | ] } 19 | 20 | [dev-dependencies] 21 | swc_core = { workspace = true, features = [ 22 | "testing_transform", 23 | ] } 24 | testing = { workspace = true } 25 | -------------------------------------------------------------------------------- /crates/binding_values/src/identifier.rs: -------------------------------------------------------------------------------- 1 | use rspack_collections::Identifier; 2 | use rspack_napi::napi::bindgen_prelude::{Result, ToNapiValue}; 3 | 4 | pub struct JsIdentifier(Identifier); 5 | 6 | impl JsIdentifier { 7 | pub fn raw(&self) -> Identifier { 8 | self.0 9 | } 10 | } 11 | 12 | impl From for JsIdentifier { 13 | fn from(value: Identifier) -> Self { 14 | JsIdentifier(value) 15 | } 16 | } 17 | 18 | impl ToNapiValue for JsIdentifier { 19 | unsafe fn to_napi_value(env: napi::sys::napi_env, val: Self) -> Result { 20 | ToNapiValue::to_napi_value(env, val.0.as_str()) 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /scripts/test.mjs: -------------------------------------------------------------------------------- 1 | import { spawnSync } from 'child_process'; 2 | import fse from 'fs-extra'; 3 | 4 | const cratesDir = 'crates'; 5 | const crates = fse.readdirSync(cratesDir); 6 | 7 | const packageScripts = []; 8 | crates.forEach((crate) => { 9 | // Check the file if it is a directory. 10 | if (fse.statSync(cratesDir + '/' + crate).isDirectory()) { 11 | // Ingore crates which is temporary and use for binding. 12 | if (!crate.startsWith('.')) { 13 | packageScripts.push('--package', crate); 14 | } 15 | } 16 | }); 17 | 18 | spawnSync('cargo', ['test', ...packageScripts], { 19 | cwd: process.cwd(), 20 | stdio: 'inherit', 21 | }); -------------------------------------------------------------------------------- /crates/binding_values/src/utils.rs: -------------------------------------------------------------------------------- 1 | use futures::Future; 2 | use rspack_napi::napi::threadsafe_function::{ThreadsafeFunction, ThreadsafeFunctionCallMode}; 3 | use rspack_napi::napi::{bindgen_prelude::*, Env, NapiRaw, Result}; 4 | 5 | pub fn callbackify(env: Env, f: Function, fut: F) -> Result<()> 6 | where 7 | R: 'static + ToNapiValue, 8 | F: 'static + Send + Future>, 9 | { 10 | let tsfn = unsafe { ThreadsafeFunction::::from_napi_value(env.raw(), f.raw()) }?; 11 | napi::bindgen_prelude::spawn(async move { 12 | let res = fut.await; 13 | tsfn.call(res, ThreadsafeFunctionCallMode::NonBlocking); 14 | }); 15 | Ok(()) 16 | } 17 | -------------------------------------------------------------------------------- /crates/binding_values/src/raw_options/raw_split_chunks/raw_split_chunk_size.rs: -------------------------------------------------------------------------------- 1 | use std::collections::HashMap; 2 | 3 | use napi_derive::napi; 4 | use rspack_plugin_split_chunks::SplitChunkSizes; 5 | 6 | #[derive(Debug)] 7 | #[napi(object, object_to_js = false)] 8 | pub struct RawSplitChunkSizes { 9 | pub sizes: HashMap, 10 | } 11 | 12 | impl From for SplitChunkSizes { 13 | fn from(sizes: RawSplitChunkSizes) -> Self { 14 | let mut split_chunk_sizes = SplitChunkSizes::default(); 15 | for (chunk_name, size) in sizes.sizes { 16 | split_chunk_sizes.insert((*chunk_name).into(), size); 17 | } 18 | split_chunk_sizes 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /crates/binding_values/src/raw_options/raw_experiments/raw_cache/raw_storage.rs: -------------------------------------------------------------------------------- 1 | use napi_derive::napi; 2 | use rspack_core::cache::persistent::storage::StorageOptions; 3 | 4 | #[derive(Debug, Default)] 5 | #[napi(object)] 6 | pub struct RawStorageOptions { 7 | #[napi(ts_type = r#""filesystem""#)] 8 | pub r#type: String, 9 | pub directory: String, 10 | } 11 | 12 | impl From for StorageOptions { 13 | fn from(value: RawStorageOptions) -> Self { 14 | match value.r#type.as_str() { 15 | "filesystem" => StorageOptions::FileSystem { 16 | directory: value.directory.into(), 17 | }, 18 | s => panic!("unsupported storage type {s}"), 19 | } 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /crates/swc_compiler/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "swc_compiler" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | [dependencies] 7 | anyhow = { workspace = true } 8 | base64 = { version = "0.22.1" } 9 | dashmap = { workspace = true } 10 | jsonc-parser = { version = "0.26.2", features = ["serde"] } 11 | rspack_ast = { workspace = true } 12 | rspack_util = { workspace = true } 13 | swc = { workspace = true, features = ["manual-tokio-runtmie"] } 14 | swc_config = { workspace = true } 15 | swc_core = { workspace = true, features = ["base", "ecma_ast", "common"] } 16 | url = "2.5.4" -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "ice-monorepo", 3 | "version": "0.0.1", 4 | "type": "module", 5 | "homepage": "https://ice.work", 6 | "bugs": "https://github.com/alibaba/ice/issues", 7 | "scripts": { 8 | "setup": "rm -rf node_modules packages/*/node_modules && pnpm install && pnpm setup-crates", 9 | "setup-crates": "node scripts/clone-rspack.mjs", 10 | "test": "node scripts/test.mjs" 11 | }, 12 | "devDependencies": { 13 | "git-clone": "0.2.0", 14 | "rimraf": "^5.0.5", 15 | "fs-extra": "^11.1.1", 16 | "ora": "^7.0.1", 17 | "js-yaml": "4.1.0" 18 | }, 19 | "repository": { 20 | "type": "git", 21 | "url": "https://github.com/alibaba/ice" 22 | }, 23 | "packageManager": "pnpm@8.9.2" 24 | } -------------------------------------------------------------------------------- /.github/actions/clone-crates/action.yml: -------------------------------------------------------------------------------- 1 | name: clone crates 2 | 3 | description: clone rspack crates for github 4 | 5 | inputs: 6 | repo: 7 | default: 'web-infra-dev/rspack' 8 | required: false 9 | type: string 10 | dest: 11 | default: 'crates/.rspack_crates' 12 | required: false 13 | type: string 14 | ref: 15 | default: 'v1.2.2' 16 | required: false 17 | type: string 18 | temp: 19 | default: 'crates/.rspack_crates/.temp' 20 | required: false 21 | type: string 22 | 23 | runs: 24 | using: composite 25 | steps: 26 | - name: Clone Repo 27 | uses: actions/checkout@v4 28 | with: 29 | repository: web-infra-dev/rspack 30 | path: ${{ inputs.temp }} 31 | ref: ${{ inputs.ref }} 32 | 33 | - name: Clean up 34 | shell: bash 35 | run: node scripts/clean.mjs 36 | env: 37 | IS_GITHUB: true 38 | -------------------------------------------------------------------------------- /crates/binding_values/src/clean_options.rs: -------------------------------------------------------------------------------- 1 | use napi_derive::napi; 2 | use rspack_core::CleanOptions; 3 | use rspack_napi::napi; 4 | 5 | /// File clean options 6 | /// 7 | /// This matches with: 8 | /// - keep: 9 | /// - If a string, keep the files under this path 10 | #[napi(object, object_to_js = false)] 11 | #[derive(Debug)] 12 | pub struct JsCleanOptions { 13 | pub keep: Option, 14 | // todo: 15 | // - support RegExp type 16 | // if path match the RegExp, keep the file 17 | // - support function type 18 | // if the fn returns true on path str, keep the file 19 | } 20 | 21 | impl JsCleanOptions { 22 | pub fn to_clean_options(&self) -> CleanOptions { 23 | let keep = self.keep.as_ref(); 24 | if let Some(path) = keep { 25 | let p = path.as_str(); 26 | CleanOptions::from(p) 27 | } else { 28 | CleanOptions::CleanAll(false) 29 | } 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /crates/binding_values/src/raw_options/raw_builtins/raw_limit_chunk_count.rs: -------------------------------------------------------------------------------- 1 | use napi_derive::napi; 2 | use rspack_plugin_limit_chunk_count::LimitChunkCountPluginOptions; 3 | 4 | #[derive(Debug, Clone)] 5 | #[napi(object)] 6 | pub struct RawLimitChunkCountPluginOptions { 7 | // Constant overhead for a chunk. 8 | pub chunk_overhead: Option, 9 | // Multiplicator for initial chunks. 10 | pub entry_chunk_multiplicator: Option, 11 | // Limit the maximum number of chunks using a value greater greater than or equal to 1. 12 | pub max_chunks: f64, 13 | } 14 | 15 | impl From for LimitChunkCountPluginOptions { 16 | fn from(value: RawLimitChunkCountPluginOptions) -> Self { 17 | Self { 18 | chunk_overhead: value.chunk_overhead, 19 | entry_chunk_multiplicator: value.entry_chunk_multiplicator, 20 | max_chunks: value.max_chunks as usize, 21 | } 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /crates/binding_values/src/raw_options/raw_builtins/raw_bundle_info.rs: -------------------------------------------------------------------------------- 1 | use napi::Either; 2 | use napi_derive::napi; 3 | use rspack_plugin_runtime::BundlerInfoForceMode; 4 | use rustc_hash::FxHashSet; 5 | 6 | type RawBundlerInfoMode = Either>; 7 | pub struct RawBundlerInfoModeWrapper(pub RawBundlerInfoMode); 8 | 9 | #[derive(Debug, Clone)] 10 | #[napi(object)] 11 | pub struct RawBundlerInfoPluginOptions { 12 | pub version: String, 13 | pub bundler: String, 14 | #[napi(ts_type = "boolean | string[]")] 15 | pub force: RawBundlerInfoMode, 16 | } 17 | 18 | impl From for BundlerInfoForceMode { 19 | fn from(x: RawBundlerInfoModeWrapper) -> Self { 20 | match x.0 { 21 | Either::A(v) => { 22 | if v { 23 | BundlerInfoForceMode::All 24 | } else { 25 | BundlerInfoForceMode::Auto 26 | } 27 | } 28 | Either::B(v) => BundlerInfoForceMode::Partial(v.into_iter().collect::>()), 29 | } 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /crates/binding_values/src/raw_options/raw_split_chunks/raw_split_chunk_chunks.rs: -------------------------------------------------------------------------------- 1 | use std::sync::Arc; 2 | 3 | use napi::{bindgen_prelude::Either3, JsString}; 4 | use rspack_collections::DatabaseItem; 5 | use rspack_napi::string::JsStringExt; 6 | use rspack_napi::threadsafe_function::ThreadsafeFunction; 7 | use rspack_regex::RspackRegex; 8 | 9 | use crate::JsChunkWrapper; 10 | 11 | pub type Chunks = Either3>; 12 | 13 | pub fn create_chunks_filter(raw: Chunks) -> rspack_plugin_split_chunks::ChunkFilter { 14 | use pollster::block_on; 15 | match raw { 16 | Either3::A(regex) => rspack_plugin_split_chunks::create_regex_chunk_filter_from_str(regex), 17 | Either3::B(js_str) => { 18 | let js_str = js_str.into_string(); 19 | rspack_plugin_split_chunks::create_chunk_filter_from_str(&js_str) 20 | } 21 | Either3::C(f) => Arc::new(move |chunk, compilation| { 22 | block_on(f.call(JsChunkWrapper::new(chunk.ukey(), compilation))) 23 | }), 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /crates/loader_barrel/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "loader_barrel" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | [dependencies] 7 | lazy_static = "1.4.0" 8 | serde_json = { workspace = true } 9 | stacker = { workspace = true } 10 | futures = { workspace = true } 11 | regex = { workspace = true } 12 | anyhow = { workspace = true } 13 | tokio = { workspace = true } 14 | async-trait = { workspace = true } 15 | serde = { workspace = true, features = ["derive"] } 16 | swc_core = { workspace = true, features = [ 17 | "base", 18 | "common" 19 | ] } 20 | rspack_ast = { workspace = true } 21 | rspack_core = { workspace = true } 22 | rspack_error = { workspace = true } 23 | rspack_regex = { workspace = true } 24 | rspack_loader_runner = { workspace = true } 25 | rspack_plugin_javascript = { workspace = true } 26 | rspack_cacheable = { workspace = true } 27 | swc_compiler = { path = "../swc_compiler" } 28 | swc_optimize_barrel = { path = "../swc_optimize_barrel" } 29 | 30 | [dev-dependencies] 31 | rspack_util = { path = "../.rspack_crates/rspack_util" } -------------------------------------------------------------------------------- /crates/binding_values/src/resource_data.rs: -------------------------------------------------------------------------------- 1 | use napi_derive::napi; 2 | use rspack_core::ResourceData; 3 | 4 | #[napi(object)] 5 | pub struct JsResourceData { 6 | /// Resource with absolute path, query and fragment 7 | pub resource: String, 8 | /// Absolute resource path only 9 | pub path: Option, 10 | /// Resource query with `?` prefix 11 | pub query: Option, 12 | /// Resource fragment with `#` prefix 13 | pub fragment: Option, 14 | } 15 | 16 | impl From for JsResourceData { 17 | fn from(value: ResourceData) -> Self { 18 | Self { 19 | resource: value.resource, 20 | path: value.resource_path.map(|p| p.as_str().to_string()), 21 | query: value.resource_query, 22 | fragment: value.resource_fragment, 23 | } 24 | } 25 | } 26 | 27 | impl From<&ResourceData> for JsResourceData { 28 | fn from(value: &ResourceData) -> Self { 29 | Self { 30 | resource: value.resource.to_owned(), 31 | path: value.resource_path.as_ref().map(|p| p.as_str().to_string()), 32 | fragment: value.resource_fragment.as_ref().map(|r| r.to_owned()), 33 | query: value.resource_query.as_ref().map(|r| r.to_owned()), 34 | } 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /crates/node_binding/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@ice/pack-binding", 3 | "version": "1.2.2", 4 | "main": "index.js", 5 | "types": "index.d.ts", 6 | "napi": { 7 | "name": "pack-binding", 8 | "triples": { 9 | "additional": [ 10 | "aarch64-apple-darwin", 11 | "aarch64-pc-windows-msvc", 12 | "x86_64-unknown-linux-musl", 13 | "aarch64-unknown-linux-gnu", 14 | "aarch64-unknown-linux-musl", 15 | "universal-apple-darwin", 16 | "i686-pc-windows-msvc" 17 | ] 18 | } 19 | }, 20 | "files": [ 21 | "index.js", 22 | "index.d.ts" 23 | ], 24 | "license": "MIT", 25 | "devDependencies": { 26 | "@napi-rs/cli": "^2.16.4" 27 | }, 28 | "ava": { 29 | "timeout": "3m" 30 | }, 31 | "engines": { 32 | "node": ">= 10" 33 | }, 34 | "scripts": { 35 | "artifacts": "napi artifacts", 36 | "build": "napi build --platform --release", 37 | "build:debug": "napi build --platform", 38 | "build:debug:aarch64": "napi build --platform --target aarch64-apple-darwin", 39 | "prepublishOnly": "napi prepublish -t npm", 40 | "universal": "napi universal", 41 | "version": "napi version" 42 | }, 43 | "repository": { 44 | "url": "https://github.com/ice-lab/icepack" 45 | } 46 | } -------------------------------------------------------------------------------- /crates/binding_values/src/raw_options/raw_builtins/raw_ignore.rs: -------------------------------------------------------------------------------- 1 | use napi_derive::napi; 2 | use rspack_napi::threadsafe_function::ThreadsafeFunction; 3 | use rspack_plugin_ignore::{CheckResourceContent, IgnorePluginOptions}; 4 | use rspack_regex::RspackRegex; 5 | 6 | type RawCheckResource = ThreadsafeFunction<(String, String), bool>; 7 | 8 | #[derive(Debug)] 9 | #[napi(object, object_to_js = false)] 10 | pub struct RawIgnorePluginOptions { 11 | #[napi(ts_type = "RegExp")] 12 | pub resource_reg_exp: Option, 13 | #[napi(ts_type = "RegExp")] 14 | pub context_reg_exp: Option, 15 | #[napi(ts_type = "(resource: string, context: string) => boolean")] 16 | pub check_resource: Option, 17 | } 18 | 19 | impl From for IgnorePluginOptions { 20 | fn from(value: RawIgnorePluginOptions) -> Self { 21 | Self { 22 | resource_reg_exp: value.resource_reg_exp, 23 | context_reg_exp: value.context_reg_exp, 24 | 25 | check_resource: value.check_resource.map(|check_resource| { 26 | CheckResourceContent::Fn(Box::new(move |resource, context| { 27 | let f = check_resource.clone(); 28 | 29 | Box::pin(async move { f.call((resource.to_owned(), context.to_owned())).await }) 30 | })) 31 | }), 32 | } 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /crates/binding_values/src/raw_options/raw_builtins/raw_size_limits.rs: -------------------------------------------------------------------------------- 1 | use derive_more::Debug; 2 | use napi_derive::napi; 3 | use rspack_napi::threadsafe_function::ThreadsafeFunction; 4 | use rspack_plugin_size_limits::{AssetFilterFn, SizeLimitsPluginOptions}; 5 | 6 | #[derive(Debug)] 7 | #[napi(object, object_to_js = false)] 8 | pub struct RawSizeLimitsPluginOptions { 9 | #[debug(skip)] 10 | #[napi(ts_type = "(assetFilename: string) => boolean")] 11 | pub asset_filter: Option>, 12 | #[napi(ts_type = "\"error\" | \"warning\"")] 13 | pub hints: Option, 14 | pub max_asset_size: Option, 15 | pub max_entrypoint_size: Option, 16 | } 17 | 18 | impl From for SizeLimitsPluginOptions { 19 | fn from(value: RawSizeLimitsPluginOptions) -> Self { 20 | SizeLimitsPluginOptions { 21 | asset_filter: value.asset_filter.map(|asset_filter| { 22 | let asset_filter_fn: AssetFilterFn = Box::new(move |name| { 23 | let f = asset_filter.clone(); 24 | 25 | Box::pin(async move { f.call(name.to_owned()).await }) 26 | }); 27 | asset_filter_fn 28 | }), 29 | hints: value.hints, 30 | max_asset_size: value.max_asset_size, 31 | max_entrypoint_size: value.max_entrypoint_size, 32 | } 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /crates/binding_values/src/asset_condition.rs: -------------------------------------------------------------------------------- 1 | use napi::Either; 2 | use rspack_regex::RspackRegex; 3 | use rspack_util::asset_condition::{AssetCondition, AssetConditions}; 4 | 5 | pub type RawAssetCondition = Either; 6 | pub type RawAssetConditions = Either>; 7 | 8 | struct RawAssetConditionWrapper(RawAssetCondition); 9 | struct RawAssetConditionsWrapper(RawAssetConditions); 10 | 11 | impl From for AssetCondition { 12 | fn from(x: RawAssetConditionWrapper) -> Self { 13 | match x.0 { 14 | Either::A(v) => Self::String(v), 15 | Either::B(v) => Self::Regexp(v), 16 | } 17 | } 18 | } 19 | 20 | impl From for AssetConditions { 21 | fn from(value: RawAssetConditionsWrapper) -> Self { 22 | match value.0 { 23 | Either::A(v) => Self::Single(RawAssetConditionWrapper(v).into()), 24 | Either::B(v) => Self::Multiple( 25 | v.into_iter() 26 | .map(|v| RawAssetConditionWrapper(v).into()) 27 | .collect(), 28 | ), 29 | } 30 | } 31 | } 32 | 33 | pub fn into_asset_condition(r: RawAssetCondition) -> AssetCondition { 34 | RawAssetConditionWrapper(r).into() 35 | } 36 | 37 | pub fn into_asset_conditions(r: RawAssetConditions) -> AssetConditions { 38 | RawAssetConditionsWrapper(r).into() 39 | } 40 | -------------------------------------------------------------------------------- /crates/binding_values/src/raw_options/raw_builtins/raw_css_extract.rs: -------------------------------------------------------------------------------- 1 | use std::collections::HashMap; 2 | 3 | use napi_derive::napi; 4 | use rspack_plugin_extract_css::plugin::{CssExtractOptions, InsertType}; 5 | 6 | use crate::JsFilename; 7 | 8 | #[napi(object, object_to_js = false)] 9 | pub struct RawCssExtractPluginOption { 10 | pub filename: JsFilename, 11 | pub chunk_filename: JsFilename, 12 | pub ignore_order: bool, 13 | pub insert: Option, 14 | pub attributes: HashMap, 15 | pub link_type: Option, 16 | pub runtime: bool, 17 | pub pathinfo: bool, 18 | pub enforce_relative: bool, 19 | } 20 | 21 | impl From for CssExtractOptions { 22 | fn from(value: RawCssExtractPluginOption) -> Self { 23 | Self { 24 | filename: value.filename.into(), 25 | chunk_filename: value.chunk_filename.into(), 26 | ignore_order: value.ignore_order, 27 | insert: value 28 | .insert 29 | .map(|insert| { 30 | if insert.starts_with("function") || insert.starts_with('(') { 31 | InsertType::Fn(insert) 32 | } else { 33 | InsertType::Selector(insert) 34 | } 35 | }) 36 | .unwrap_or(InsertType::Default), 37 | attributes: value.attributes.into_iter().collect(), 38 | link_type: value.link_type, 39 | runtime: value.runtime, 40 | pathinfo: value.pathinfo, 41 | enforce_relative: value.enforce_relative, 42 | } 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /crates/node_binding/src/compiler.rs: -------------------------------------------------------------------------------- 1 | use std::{ 2 | marker::PhantomPinned, 3 | ops::{Deref, DerefMut}, 4 | sync::{ 5 | atomic::{AtomicBool, Ordering}, 6 | Arc, 7 | }, 8 | }; 9 | 10 | type CompilerInner = rspack_core::Compiler; 11 | 12 | /// `Compiler` struct that is `!Unpin`. 13 | pub(crate) struct Compiler(CompilerInner, PhantomPinned); 14 | 15 | impl From for Compiler { 16 | fn from(value: CompilerInner) -> Self { 17 | Self(value, PhantomPinned) 18 | } 19 | } 20 | 21 | impl Deref for Compiler { 22 | type Target = CompilerInner; 23 | 24 | fn deref(&self) -> &Self::Target { 25 | &self.0 26 | } 27 | } 28 | 29 | impl DerefMut for Compiler { 30 | fn deref_mut(&mut self) -> &mut Self::Target { 31 | &mut self.0 32 | } 33 | } 34 | 35 | pub(crate) struct CompilerState(Arc); 36 | 37 | impl CompilerState { 38 | pub(crate) fn init() -> Self { 39 | Self(Arc::new(AtomicBool::new(false))) 40 | } 41 | } 42 | 43 | impl CompilerState { 44 | pub(crate) fn running(&self) -> bool { 45 | self.0.load(Ordering::Relaxed) 46 | } 47 | 48 | pub(crate) fn enter(&self) -> CompilerStateGuard { 49 | self.0.store(true, Ordering::Relaxed); 50 | CompilerStateGuard(self.0.clone()) 51 | } 52 | } 53 | 54 | pub(crate) struct CompilerStateGuard(Arc); 55 | 56 | unsafe impl Send for CompilerStateGuard {} 57 | 58 | impl Drop for CompilerStateGuard { 59 | fn drop(&mut self) { 60 | self.0.store(false, Ordering::Relaxed); 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /crates/binding_values/src/raw_options/raw_experiments/raw_cache/raw_snapshot.rs: -------------------------------------------------------------------------------- 1 | use napi::Either; 2 | use napi_derive::napi; 3 | use rspack_core::cache::persistent::snapshot::{PathMatcher, SnapshotOptions}; 4 | use rspack_regex::RspackRegex; 5 | 6 | #[derive(Debug, Default)] 7 | #[napi(object)] 8 | pub struct RawExperimentSnapshotOptions { 9 | #[napi(ts_type = r#"Array"#)] 10 | pub immutable_paths: Vec, 11 | #[napi(ts_type = r#"Array"#)] 12 | pub unmanaged_paths: Vec, 13 | #[napi(ts_type = r#"Array"#)] 14 | pub managed_paths: Vec, 15 | } 16 | 17 | type RawPathMatcher = Either; 18 | 19 | impl From for SnapshotOptions { 20 | fn from(value: RawExperimentSnapshotOptions) -> Self { 21 | SnapshotOptions::new( 22 | value 23 | .immutable_paths 24 | .into_iter() 25 | .map(normalize_raw_path_matcher) 26 | .collect(), 27 | value 28 | .unmanaged_paths 29 | .into_iter() 30 | .map(normalize_raw_path_matcher) 31 | .collect(), 32 | value 33 | .managed_paths 34 | .into_iter() 35 | .map(normalize_raw_path_matcher) 36 | .collect(), 37 | ) 38 | } 39 | } 40 | 41 | fn normalize_raw_path_matcher(value: RawPathMatcher) -> PathMatcher { 42 | match value { 43 | Either::A(s) => PathMatcher::String(s), 44 | Either::B(reg) => PathMatcher::Regexp(reg), 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /crates/binding_values/src/raw_options/raw_split_chunks/raw_split_chunk_cache_group_test.rs: -------------------------------------------------------------------------------- 1 | use std::sync::Arc; 2 | 3 | use napi::bindgen_prelude::Either3; 4 | use napi_derive::napi; 5 | use rspack_napi::threadsafe_function::ThreadsafeFunction; 6 | use rspack_plugin_split_chunks::{CacheGroupTest, CacheGroupTestFnCtx}; 7 | use rspack_regex::RspackRegex; 8 | 9 | use crate::JsModuleWrapper; 10 | 11 | pub(super) type RawCacheGroupTest = 12 | Either3>>; 13 | 14 | #[napi(object, object_from_js = false)] 15 | pub struct JsCacheGroupTestCtx { 16 | #[napi(ts_type = "JsModule")] 17 | pub module: JsModuleWrapper, 18 | } 19 | 20 | impl<'a> From> for JsCacheGroupTestCtx { 21 | fn from(value: CacheGroupTestFnCtx<'a>) -> Self { 22 | JsCacheGroupTestCtx { 23 | module: JsModuleWrapper::new( 24 | value.module, 25 | value.compilation.id(), 26 | Some(value.compilation), 27 | ), 28 | } 29 | } 30 | } 31 | 32 | pub(super) fn normalize_raw_cache_group_test(raw: RawCacheGroupTest) -> CacheGroupTest { 33 | use pollster::block_on; 34 | match raw { 35 | Either3::A(str) => CacheGroupTest::String(str), 36 | Either3::B(regexp) => CacheGroupTest::RegExp(regexp), 37 | Either3::C(v) => CacheGroupTest::Fn(Arc::new(move |ctx| block_on(v.call(ctx.into())))), 38 | } 39 | } 40 | 41 | #[inline] 42 | pub(super) fn default_cache_group_test() -> CacheGroupTest { 43 | CacheGroupTest::Enabled 44 | } 45 | -------------------------------------------------------------------------------- /crates/binding_values/src/raw_options/raw_dynamic_entry.rs: -------------------------------------------------------------------------------- 1 | use napi_derive::napi; 2 | use rspack_napi::threadsafe_function::ThreadsafeFunction; 3 | use rspack_plugin_dynamic_entry::{DynamicEntryPluginOptions, EntryDynamicResult}; 4 | 5 | use crate::entry::JsEntryOptions; 6 | 7 | #[derive(Debug)] 8 | #[napi(object, object_to_js = false)] 9 | pub struct RawEntryDynamicResult { 10 | pub import: Vec, 11 | pub options: JsEntryOptions, 12 | } 13 | 14 | pub type RawEntryDynamic = ThreadsafeFunction<(), Vec>; 15 | 16 | #[derive(Debug)] 17 | #[napi(object, object_to_js = false)] 18 | pub struct RawDynamicEntryPluginOptions { 19 | pub context: String, 20 | #[napi(ts_type = "() => Promise")] 21 | pub entry: RawEntryDynamic, 22 | } 23 | 24 | impl From for DynamicEntryPluginOptions { 25 | fn from(opts: RawDynamicEntryPluginOptions) -> Self { 26 | Self { 27 | context: opts.context.into(), 28 | entry: Box::new(move || { 29 | let f = opts.entry.clone(); 30 | Box::pin(async move { 31 | let raw_result = f.call(()).await?; 32 | let result = raw_result 33 | .into_iter() 34 | .map( 35 | |RawEntryDynamicResult { import, options }| EntryDynamicResult { 36 | import, 37 | options: options.into(), 38 | }, 39 | ) 40 | .collect::>(); 41 | Ok(result) 42 | }) 43 | }), 44 | } 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /crates/binding_values/src/lib.rs: -------------------------------------------------------------------------------- 1 | #![feature(let_chains)] 2 | #![feature(try_blocks)] 3 | 4 | mod asset; 5 | mod asset_condition; 6 | mod chunk; 7 | mod chunk_graph; 8 | mod chunk_group; 9 | mod clean_options; 10 | mod codegen_result; 11 | mod compilation; 12 | mod context_module_factory; 13 | mod dependency; 14 | mod dependency_block; 15 | mod exports_info; 16 | mod filename; 17 | mod html; 18 | mod identifier; 19 | mod module; 20 | mod module_graph; 21 | mod module_graph_connection; 22 | mod normal_module_factory; 23 | mod options; 24 | mod path_data; 25 | mod plugins; 26 | mod raw_options; 27 | mod resolver; 28 | mod resource_data; 29 | mod rspack_error; 30 | mod runtime; 31 | mod source; 32 | mod stats; 33 | mod utils; 34 | 35 | pub use asset::*; 36 | pub use asset_condition::*; 37 | pub use chunk::*; 38 | pub use chunk_graph::*; 39 | pub use chunk_group::*; 40 | pub use clean_options::*; 41 | pub use codegen_result::*; 42 | pub use compilation::*; 43 | pub use context_module_factory::*; 44 | pub use dependency::*; 45 | pub use dependency_block::*; 46 | pub use exports_info::*; 47 | pub use filename::*; 48 | pub use html::*; 49 | pub use module::*; 50 | pub use module_graph::*; 51 | pub use module_graph_connection::*; 52 | pub use normal_module_factory::*; 53 | pub use options::*; 54 | pub use path_data::*; 55 | pub use plugins::buildtime_plugins; 56 | pub(crate) use plugins::*; 57 | pub use raw_options::*; 58 | pub use resolver::*; 59 | pub use resource_data::*; 60 | pub use rspack_error::*; 61 | pub use runtime::*; 62 | pub use source::*; 63 | pub use stats::*; 64 | pub use utils::*; 65 | -------------------------------------------------------------------------------- /crates/binding_values/src/plugins/js_loader/mod.rs: -------------------------------------------------------------------------------- 1 | mod context; 2 | mod resolver; 3 | mod scheduler; 4 | 5 | use std::fmt::Debug; 6 | 7 | pub use context::JsLoaderContext; 8 | use napi::bindgen_prelude::*; 9 | use rspack_core::{ApplyContext, CompilerOptions, Plugin, PluginContext}; 10 | use rspack_error::Result; 11 | use rspack_hook::plugin; 12 | use rspack_napi::threadsafe_function::ThreadsafeFunction; 13 | 14 | pub type JsLoaderRunner = ThreadsafeFunction>; 15 | 16 | #[plugin] 17 | pub(crate) struct JsLoaderRspackPlugin { 18 | pub(crate) runner: JsLoaderRunner, 19 | } 20 | 21 | impl JsLoaderRspackPlugin { 22 | pub fn new(runner: JsLoaderRunner) -> Self { 23 | Self::new_inner(runner) 24 | } 25 | } 26 | 27 | impl Debug for JsLoaderRspackPlugin { 28 | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { 29 | f.debug_tuple("JsLoaderResolver").finish() 30 | } 31 | } 32 | 33 | impl Plugin for JsLoaderRspackPlugin { 34 | fn name(&self) -> &'static str { 35 | "rspack.JsLoaderRspackPlugin" 36 | } 37 | 38 | fn apply(&self, ctx: PluginContext<&mut ApplyContext>, _options: &CompilerOptions) -> Result<()> { 39 | ctx 40 | .context 41 | .normal_module_factory_hooks 42 | .resolve_loader 43 | .tap(resolver::resolve_loader::new(self)); 44 | ctx 45 | .context 46 | .normal_module_hooks 47 | .loader_should_yield 48 | .tap(scheduler::loader_should_yield::new(self)); 49 | ctx 50 | .context 51 | .normal_module_hooks 52 | .loader_yield 53 | .tap(scheduler::loader_yield::new(self)); 54 | Ok(()) 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /crates/binding_values/src/raw_options/raw_builtins/raw_runtime_chunk.rs: -------------------------------------------------------------------------------- 1 | use napi::Either; 2 | use napi_derive::napi; 3 | use rspack_napi::threadsafe_function::ThreadsafeFunction; 4 | use rspack_plugin_runtime_chunk::{RuntimeChunkName, RuntimeChunkOptions}; 5 | 6 | #[napi(object, object_to_js = false)] 7 | pub struct RawRuntimeChunkOptions { 8 | #[napi(ts_type = "string | ((entrypoint: { name: string }) => string)")] 9 | pub name: RawRuntimeChunkName, 10 | } 11 | 12 | impl From for RuntimeChunkOptions { 13 | fn from(value: RawRuntimeChunkOptions) -> Self { 14 | Self { 15 | name: RawRuntimeChunkNameWrapper(value.name).into(), 16 | } 17 | } 18 | } 19 | 20 | type RawRuntimeChunkName = Either>; 21 | struct RawRuntimeChunkNameWrapper(RawRuntimeChunkName); 22 | 23 | #[napi(object)] 24 | pub struct RawRuntimeChunkNameFnCtx { 25 | pub name: String, 26 | } 27 | 28 | impl From for RuntimeChunkName { 29 | fn from(value: RawRuntimeChunkNameWrapper) -> Self { 30 | match value.0 { 31 | Either::A(s) => { 32 | if s == "single" { 33 | Self::Single 34 | } else if s == "multiple" { 35 | Self::Multiple 36 | } else { 37 | Self::String(s) 38 | } 39 | } 40 | Either::B(f) => RuntimeChunkName::Fn(Box::new(move |name| { 41 | let f = f.clone(); 42 | Box::pin(async move { 43 | f.call(RawRuntimeChunkNameFnCtx { 44 | name: name.to_string(), 45 | }) 46 | .await 47 | }) 48 | })), 49 | } 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /crates/loader_compilation/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "loader_compilation" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 7 | 8 | [dependencies] 9 | anyhow = { workspace = true } 10 | async-trait = { workspace = true } 11 | either = "1" 12 | once_cell = { workspace = true } 13 | rspack_ast = { workspace = true } 14 | rspack_core = { workspace = true } 15 | rspack_error = { workspace = true } 16 | rspack_loader_runner = { workspace = true } 17 | rspack_plugin_javascript = { workspace = true } 18 | rspack_regex = { workspace = true } 19 | rspack_util = { workspace = true } 20 | rspack_cacheable = { workspace = true } 21 | regex = { workspace = true } 22 | stacker = { workspace = true } 23 | serde = { workspace = true, features = ["derive"] } 24 | serde_json = { workspace = true } 25 | swc_config = { workspace = true } 26 | swc_core = { workspace = true, features = [ 27 | "base", 28 | "ecma_ast", 29 | "common" 30 | ] } 31 | xxhash-rust = { workspace = true, features = ["xxh32"] } 32 | swc_compiler = { path = "../swc_compiler" } 33 | swc_env_replacement = { path = "../swc_env_replacement" } 34 | swc_keep_export = { path = "../swc_keep_export" } 35 | swc_remove_export = { path = "../swc_remove_export" } 36 | swc_named_import_transform = { path = "../swc_named_import_transform" } 37 | swc_change_package_import = { path = "../swc_change_package_import" } 38 | tokio = { workspace = true } 39 | 40 | [dev-dependencies] 41 | indexmap = { workspace = true } 42 | tokio = { workspace = true, features = ["rt", "rt-multi-thread", "macros", "test-util", "parking_lot"] } -------------------------------------------------------------------------------- /crates/binding_values/src/raw_options/raw_experiments/mod.rs: -------------------------------------------------------------------------------- 1 | mod raw_cache; 2 | mod raw_incremental; 3 | mod raw_rspack_future; 4 | 5 | use napi_derive::napi; 6 | use raw_cache::{normalize_raw_experiment_cache_options, RawExperimentCacheOptions}; 7 | use raw_incremental::RawIncremental; 8 | use raw_rspack_future::RawRspackFuture; 9 | use rspack_core::{incremental::IncrementalPasses, Experiments}; 10 | 11 | use super::WithFalse; 12 | 13 | #[derive(Debug)] 14 | #[napi(object, object_to_js = false)] 15 | pub struct RawExperiments { 16 | pub layers: bool, 17 | pub top_level_await: bool, 18 | #[napi(ts_type = "false | { [key: string]: boolean }")] 19 | pub incremental: Option>, 20 | pub parallel_code_splitting: bool, 21 | pub rspack_future: Option, 22 | #[napi( 23 | ts_type = r#"boolean | { type: "persistent" } & RawExperimentCacheOptionsPersistent | { type: "memory" }"# 24 | )] 25 | pub cache: RawExperimentCacheOptions, 26 | } 27 | 28 | impl From for Experiments { 29 | fn from(value: RawExperiments) -> Self { 30 | Self { 31 | incremental: match value.incremental { 32 | Some(value) => match value { 33 | WithFalse::True(value) => value.into(), 34 | WithFalse::False => IncrementalPasses::empty(), 35 | }, 36 | None => IncrementalPasses::empty(), 37 | }, 38 | parallel_code_splitting: value.parallel_code_splitting, 39 | layers: value.layers, 40 | top_level_await: value.top_level_await, 41 | rspack_future: value.rspack_future.unwrap_or_default().into(), 42 | cache: normalize_raw_experiment_cache_options(value.cache), 43 | } 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /crates/node_binding/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | description = "node binding" 3 | edition = "2021" 4 | license = "MIT" 5 | name = "node_binding" 6 | publish = false 7 | repository = "https://github.com/web-infra-dev/rspack" 8 | version = "0.2.0" 9 | [lib] 10 | crate-type = ["cdylib"] 11 | 12 | [features] 13 | plugin = ["binding_values/plugin"] 14 | 15 | [dependencies] 16 | anyhow = { workspace = true } 17 | ropey = { workspace = true } 18 | rspack_allocator = { workspace = true } 19 | binding_values = { path = "../binding_values" } 20 | rspack_collections = { workspace = true } 21 | rspack_core = { workspace = true } 22 | rspack_error = { workspace = true } 23 | rspack_fs = { workspace = true } 24 | rspack_fs_node = { workspace = true } 25 | rspack_hash = { workspace = true } 26 | rspack_hook = { workspace = true } 27 | rspack_napi = { workspace = true } 28 | rspack_paths = { workspace = true } 29 | rspack_plugin_html = { workspace = true } 30 | rspack_plugin_javascript = { workspace = true } 31 | rspack_plugin_runtime = { workspace = true } 32 | rspack_util = { workspace = true } 33 | 34 | rspack_tracing = { workspace = true } 35 | 36 | async-trait = { workspace = true } 37 | cow-utils = { workspace = true } 38 | tracing = { workspace = true } 39 | tracing-subscriber = { workspace = true } 40 | 41 | napi = { workspace = true } 42 | napi-derive = { workspace = true } 43 | 44 | color-backtrace = "0.6.1" 45 | 46 | 47 | [build-dependencies] 48 | napi-build = { workspace = true } 49 | -------------------------------------------------------------------------------- /crates/swc_change_package_import/src/config.rs: -------------------------------------------------------------------------------- 1 | use std::collections::HashMap; 2 | use serde::{Deserialize, Serialize}; 3 | 4 | #[derive(Debug, Clone, Deserialize, Serialize)] 5 | pub enum Config { 6 | /// 配置: 7 | /// ```rs 8 | /// Config::LiteralConfig(String::from("antd")) 9 | /// ``` 10 | /// 效果: 11 | /// ```js 12 | /// import { Button } from "antd"; 13 | /// // ---> 14 | /// import Button from "antd/Button"; 15 | /// ``` 16 | LiteralConfig(String), 17 | /// 配置: 18 | /// ```rs 19 | /// Config::SpecificConfig( 20 | // SpecificConfigs { 21 | // name: String::from("ice"), 22 | // map: HashMap::from([ 23 | // ( 24 | // "a".to_string(), 25 | // MapProperty { 26 | // to: String::from("@ice/x/y"), 27 | // import_type: None, 28 | // name: None, 29 | // } 30 | // ), 31 | // ]), 32 | // } 33 | // ), 34 | /// ``` 35 | /// 效果: 36 | /// ```js 37 | /// import { a } from "ice"; 38 | /// // ---> 39 | /// import a from "@ice/x/y"; 40 | /// ``` 41 | /// 42 | /// 更多配置请参考[文档](https://alidocs.dingtalk.com/i/nodes/20eMKjyp810mMdK4Ho1LpqX7JxAZB1Gv?utm_scene=team_space) 43 | SpecificConfig(SpecificConfigs), 44 | } 45 | 46 | #[derive(Debug, Clone ,Serialize, Deserialize)] 47 | pub struct SpecificConfigs { 48 | pub name: String, 49 | pub map: HashMap, 50 | } 51 | 52 | #[derive(Debug, Clone, Serialize, Deserialize)] 53 | #[serde(rename_all = "camelCase")] 54 | pub struct MapProperty { 55 | pub to: String, 56 | pub import_type: Option, 57 | pub name: Option, 58 | } 59 | 60 | #[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] 61 | pub enum ImportType { 62 | Named, 63 | Default, 64 | } 65 | -------------------------------------------------------------------------------- /.github/actions/pnpm-cache/action.yml: -------------------------------------------------------------------------------- 1 | name: pnpm cache 2 | 3 | description: Install Node.js with pnpm global cache 4 | 5 | inputs: 6 | node-version: 7 | default: '18' 8 | required: false 9 | type: string 10 | save-if: 11 | default: false 12 | required: false 13 | type: boolean 14 | 15 | env: 16 | IS_GITHUB_RUNNER: startsWith(runner.name, 'GitHub Actions') 17 | 18 | runs: 19 | using: composite 20 | steps: 21 | - name: Install Node.js 22 | uses: actions/setup-node@v4 23 | with: 24 | node-version: ${{ inputs.node-version }} 25 | check-latest: true 26 | 27 | # https://pnpm.io/continuous-integration#github-actions 28 | # Uses `packageManagement` field from package.json 29 | - name: Install pnpm 30 | uses: pnpm/action-setup@v2 31 | with: 32 | dest: ${{ runner.tool_cache }}/pnpm 33 | 34 | - name: Get pnpm store directory 35 | id: pnpm-cache 36 | shell: bash 37 | run: echo "STORE_PATH=$(pnpm store path)" >> $GITHUB_OUTPUT 38 | 39 | - name: Restore pnpm cache 40 | id: restore 41 | if: ${{ env.IS_GITHUB_RUNNER }} 42 | uses: actions/cache/restore@v3 43 | with: 44 | path: ${{ steps.pnpm-cache.outputs.STORE_PATH }} 45 | key: node-cache-${{ runner.os }}-pnpm-${{ hashFiles('pnpm-lock.yaml') }} 46 | restore-keys: | 47 | node-cache-${{ runner.os }}-pnpm- 48 | 49 | - name: Install dependencies 50 | shell: bash 51 | run: pnpm install --no-frozen-lockfile 52 | 53 | - name: Save pnpm cache 54 | uses: actions/cache/save@v3 55 | if: ${{ env.IS_GITHUB_RUNNER && inputs.save-if == 'true' && steps.restore.outputs.cache-hit != 'true' }} 56 | with: 57 | path: ${{ steps.pnpm-cache.outputs.STORE_PATH }} 58 | key: node-cache-${{ runner.os }}-pnpm-${{ hashFiles('pnpm-lock.yaml') }} 59 | -------------------------------------------------------------------------------- /crates/binding_values/src/raw_options/raw_split_chunks/raw_split_chunk_name.rs: -------------------------------------------------------------------------------- 1 | use std::sync::Arc; 2 | 3 | use napi::bindgen_prelude::Either3; 4 | use napi_derive::napi; 5 | use rspack_collections::DatabaseItem; 6 | use rspack_napi::threadsafe_function::ThreadsafeFunction; 7 | use rspack_plugin_split_chunks::{ChunkNameGetter, ChunkNameGetterFnCtx}; 8 | 9 | use crate::{JsChunkWrapper, JsModuleWrapper}; 10 | 11 | pub(super) type RawChunkOptionName = 12 | Either3>>; 13 | 14 | #[inline] 15 | pub(super) fn default_chunk_option_name() -> ChunkNameGetter { 16 | ChunkNameGetter::Disabled 17 | } 18 | 19 | #[napi(object, object_from_js = false)] 20 | pub struct JsChunkOptionNameCtx { 21 | #[napi(ts_type = "JsModule")] 22 | pub module: JsModuleWrapper, 23 | #[napi(ts_type = "JsChunk[]")] 24 | pub chunks: Vec, 25 | pub cache_group_key: String, 26 | } 27 | 28 | impl<'a> From> for JsChunkOptionNameCtx { 29 | fn from(value: ChunkNameGetterFnCtx<'a>) -> Self { 30 | JsChunkOptionNameCtx { 31 | module: JsModuleWrapper::new( 32 | value.module, 33 | value.compilation.id(), 34 | Some(value.compilation), 35 | ), 36 | chunks: value 37 | .chunks 38 | .iter() 39 | .map(|chunk| JsChunkWrapper::new(chunk.ukey(), value.compilation)) 40 | .collect(), 41 | cache_group_key: value.cache_group_key.to_string(), 42 | } 43 | } 44 | } 45 | 46 | pub(super) fn normalize_raw_chunk_name(raw: RawChunkOptionName) -> ChunkNameGetter { 47 | use pollster::block_on; 48 | match raw { 49 | Either3::A(str) => ChunkNameGetter::String(str), 50 | Either3::B(_) => ChunkNameGetter::Disabled, // FIXME: when set bool is true? 51 | Either3::C(v) => ChunkNameGetter::Fn(Arc::new(move |ctx| block_on(v.call(ctx.into())))), 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /crates/node_binding/src/panic.rs: -------------------------------------------------------------------------------- 1 | use color_backtrace::{default_output_stream, BacktracePrinter}; 2 | 3 | pub fn install_panic_handler() { 4 | let panic_handler = BacktracePrinter::default() 5 | .message("Panic occurred at runtime. Please file an issue on GitHub with the backtrace below: https://github.com/web-infra-dev/rspack/issues") 6 | .add_frame_filter(Box::new(|frames| { 7 | static NAME_PREFIXES: &[&str] = &[ 8 | "rust_panic", 9 | "rayon", 10 | "rust_begin_unwind", 11 | "start_thread", 12 | "__clone", 13 | "call_once", 14 | "catch_unwind", 15 | "tokio", 16 | ", 13 | pub new_content_recursive: Option, 14 | #[napi(ts_type = "RegExp")] 15 | pub new_content_reg_exp: Option, 16 | #[napi(ts_type = "Record")] 17 | pub new_content_create_context_map: Option, 18 | // new_content_callback 19 | } 20 | 21 | impl TryFrom for ContextReplacementPluginOptions { 22 | type Error = Error; 23 | 24 | fn try_from(val: RawContextReplacementPluginOptions) -> Result { 25 | let RawContextReplacementPluginOptions { 26 | resource_reg_exp, 27 | new_content_resource, 28 | new_content_recursive, 29 | new_content_reg_exp, 30 | new_content_create_context_map, 31 | } = val; 32 | 33 | let new_content_create_context_map = if let Some(raw) = new_content_create_context_map { 34 | let mut map = HashMap::default(); 35 | let keys = Object::keys(&raw).into_diagnostic()?; 36 | for key in keys { 37 | let value = raw.get::(&key).into_diagnostic()?; 38 | if let Some(value) = value { 39 | map.insert(key, value); 40 | } 41 | } 42 | Some(map) 43 | } else { 44 | None 45 | }; 46 | 47 | Ok(Self { 48 | resource_reg_exp, 49 | new_content_resource, 50 | new_content_recursive, 51 | new_content_reg_exp, 52 | new_content_create_context_map, 53 | }) 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /crates/binding_values/src/raw_options/raw_optimization.rs: -------------------------------------------------------------------------------- 1 | use napi_derive::napi; 2 | use rspack_core::{MangleExportsOption, Optimization, SideEffectOption, UsedExportsOption}; 3 | 4 | use super::WithBool; 5 | 6 | #[derive(Debug, Default)] 7 | #[napi(object, object_to_js = false)] 8 | pub struct RawOptimizationOptions { 9 | pub remove_available_modules: bool, 10 | #[napi(ts_type = "boolean | string")] 11 | pub side_effects: WithBool, 12 | #[napi(ts_type = "boolean | string")] 13 | pub used_exports: WithBool, 14 | pub provided_exports: bool, 15 | pub inner_graph: bool, 16 | #[napi(ts_type = "boolean | string")] 17 | pub mangle_exports: WithBool, 18 | pub concatenate_modules: bool, 19 | pub avoid_entry_iife: bool, 20 | } 21 | 22 | macro_rules! impl_from_with_bool { 23 | ($ident:ident) => { 24 | impl From> for $ident { 25 | fn from(value: WithBool) -> Self { 26 | match value { 27 | WithBool::True => Self::True, 28 | WithBool::False => Self::False, 29 | WithBool::Value(s) => Self::from(s.as_str()), 30 | } 31 | } 32 | } 33 | }; 34 | } 35 | 36 | impl_from_with_bool!(UsedExportsOption); 37 | impl_from_with_bool!(MangleExportsOption); 38 | impl_from_with_bool!(SideEffectOption); 39 | 40 | impl TryFrom for Optimization { 41 | type Error = rspack_error::Error; 42 | 43 | fn try_from(value: RawOptimizationOptions) -> rspack_error::Result { 44 | Ok(Optimization { 45 | remove_available_modules: value.remove_available_modules, 46 | side_effects: value.side_effects.into(), 47 | provided_exports: value.provided_exports, 48 | used_exports: value.used_exports.into(), 49 | inner_graph: value.inner_graph, 50 | mangle_exports: value.mangle_exports.into(), 51 | concatenate_modules: value.concatenate_modules, 52 | avoid_entry_iife: value.avoid_entry_iife, 53 | }) 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /crates/binding_values/src/raw_options/raw_builtins/raw_progress.rs: -------------------------------------------------------------------------------- 1 | use std::sync::Arc; 2 | 3 | use derive_more::Debug; 4 | use napi::Either; 5 | use napi_derive::napi; 6 | use rspack_napi::threadsafe_function::ThreadsafeFunction; 7 | use rspack_plugin_progress::{ProgressPluginDisplayOptions, ProgressPluginOptions}; 8 | 9 | type HandlerFn = ThreadsafeFunction<(f64, String, Vec), ()>; 10 | #[derive(Debug)] 11 | #[napi(object, object_to_js = false)] 12 | pub struct RawProgressPluginOptions { 13 | // the prefix name of progress bar 14 | pub prefix: Option, 15 | // tells ProgressPlugin to collect profile data for progress steps. 16 | pub profile: Option, 17 | // the template of progress bar 18 | pub template: Option, 19 | // the tick string sequence for spinners, if it's string then it will be split into characters 20 | pub tick: Option>>, 21 | // the progress characters 22 | pub progress_chars: Option, 23 | // the handler for progress event 24 | #[debug(skip)] 25 | #[napi(ts_type = "(percent: number, msg: string, items: string[]) => void")] 26 | pub handler: Option, 27 | } 28 | 29 | impl From for ProgressPluginOptions { 30 | fn from(value: RawProgressPluginOptions) -> Self { 31 | if let Some(f) = value.handler { 32 | Self::Handler(Arc::new(move |percent, msg, items| { 33 | f.blocking_call_with_sync((percent, msg, items)) 34 | })) 35 | } else { 36 | Self::Default(ProgressPluginDisplayOptions { 37 | prefix: value.prefix.unwrap_or_default(), 38 | profile: value.profile.unwrap_or_default(), 39 | template: value.template.unwrap_or( 40 | "● {prefix:.bold} {bar:25.green/white.dim} ({percent}%) {wide_msg:.dim}".to_string(), 41 | ), 42 | progress_chars: value.progress_chars.unwrap_or("━━".to_string()), 43 | tick_strings: value.tick.map(|tick| match tick { 44 | Either::A(str) => str.chars().map(|c| c.to_string()).collect(), 45 | Either::B(vec) => vec, 46 | }), 47 | }) 48 | } 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /crates/binding_values/src/normal_module_factory.rs: -------------------------------------------------------------------------------- 1 | use napi_derive::napi; 2 | use rspack_core::NormalModuleCreateData; 3 | 4 | use crate::JsResourceData; 5 | 6 | #[napi(object)] 7 | pub struct JsResolveForSchemeArgs { 8 | pub resource_data: JsResourceData, 9 | pub scheme: String, 10 | } 11 | 12 | pub type JsResolveForSchemeOutput = (Option, JsResourceData); 13 | 14 | #[napi(object)] 15 | pub struct JsBeforeResolveArgs { 16 | pub request: String, 17 | pub context: String, 18 | pub issuer: String, 19 | } 20 | 21 | pub type JsBeforeResolveOutput = (Option, JsBeforeResolveArgs); 22 | 23 | #[napi(object)] 24 | pub struct JsFactorizeArgs { 25 | pub request: String, 26 | pub context: String, 27 | pub issuer: String, 28 | } 29 | 30 | pub type JsFactorizeOutput = JsFactorizeArgs; 31 | 32 | #[napi(object)] 33 | pub struct JsResolveArgs { 34 | pub request: String, 35 | pub context: String, 36 | pub issuer: String, 37 | } 38 | 39 | pub type JsResolveOutput = JsResolveArgs; 40 | 41 | #[napi(object)] 42 | pub struct JsCreateData { 43 | pub request: String, 44 | pub user_request: String, 45 | pub resource: String, 46 | } 47 | 48 | #[napi(object)] 49 | pub struct JsAfterResolveData { 50 | pub request: String, 51 | pub context: String, 52 | pub issuer: String, 53 | pub file_dependencies: Vec, 54 | pub context_dependencies: Vec, 55 | pub missing_dependencies: Vec, 56 | pub create_data: Option, 57 | } 58 | 59 | pub type JsAfterResolveOutput = (Option, Option); 60 | 61 | #[napi(object)] 62 | pub struct JsNormalModuleFactoryCreateModuleArgs { 63 | pub dependency_type: String, 64 | pub raw_request: String, 65 | pub resource_resolve_data: JsResourceData, 66 | pub context: String, 67 | pub match_resource: Option, 68 | } 69 | 70 | impl From<&NormalModuleCreateData> for JsCreateData { 71 | fn from(value: &NormalModuleCreateData) -> Self { 72 | Self { 73 | request: value.request.to_owned(), 74 | user_request: value.user_request.to_owned(), 75 | resource: value.resource_resolve_data.resource.to_owned(), 76 | } 77 | } 78 | } 79 | -------------------------------------------------------------------------------- /crates/binding_values/src/options/entry.rs: -------------------------------------------------------------------------------- 1 | use napi::Either; 2 | use napi_derive::napi; 3 | use rspack_core::{EntryOptions, EntryRuntime}; 4 | 5 | use super::library::JsLibraryOptions; 6 | use crate::{JsFilename, RawChunkLoading}; 7 | 8 | #[derive(Debug)] 9 | #[napi(object, object_to_js = false)] 10 | pub struct JsEntryPluginOptions { 11 | pub context: String, 12 | pub entry: String, 13 | pub options: JsEntryOptions, 14 | } 15 | 16 | pub type JsEntryRuntime = Either; 17 | pub struct JsEntryRuntimeWrapper(pub JsEntryRuntime); 18 | 19 | impl From for EntryRuntime { 20 | fn from(value: JsEntryRuntimeWrapper) -> Self { 21 | match value.0 { 22 | Either::A(b) => { 23 | assert!(!b, "RawEntryRuntime should be false or string"); 24 | Self::False 25 | } 26 | Either::B(s) => Self::String(s), 27 | } 28 | } 29 | } 30 | 31 | #[derive(Debug)] 32 | #[napi(object, object_to_js = false)] 33 | pub struct JsEntryOptions { 34 | pub name: Option, 35 | #[napi(ts_type = "false | string")] 36 | pub runtime: Option, 37 | #[napi(ts_type = "false | string")] 38 | pub chunk_loading: Option, 39 | pub async_chunks: Option, 40 | #[napi(ts_type = "\"auto\" | JsFilename")] 41 | pub public_path: Option, 42 | pub base_uri: Option, 43 | pub filename: Option, 44 | pub library: Option, 45 | pub depend_on: Option>, 46 | pub layer: Option, 47 | } 48 | 49 | impl From for EntryOptions { 50 | fn from(value: JsEntryOptions) -> Self { 51 | Self { 52 | name: value.name, 53 | runtime: value.runtime.map(|r| JsEntryRuntimeWrapper(r).into()), 54 | chunk_loading: value.chunk_loading.map(Into::into), 55 | async_chunks: value.async_chunks, 56 | public_path: value.public_path.map(Into::into), 57 | base_uri: value.base_uri, 58 | filename: value.filename.map(Into::into), 59 | library: value.library.map(Into::into), 60 | depend_on: value.depend_on.map(Into::into), 61 | layer: value.layer, 62 | } 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /crates/binding_values/src/codegen_result.rs: -------------------------------------------------------------------------------- 1 | use std::collections::HashMap; 2 | 3 | use napi_derive::napi; 4 | use rspack_core::{get_runtime_key, CodeGenerationResult, CodeGenerationResults}; 5 | 6 | #[napi(object)] 7 | #[derive(Debug)] 8 | pub struct JsCodegenerationResults { 9 | pub map: HashMap>, 10 | } 11 | 12 | #[napi(object)] 13 | #[derive(Debug)] 14 | pub struct JsCodegenerationResult { 15 | pub sources: HashMap, 16 | } 17 | 18 | impl From for JsCodegenerationResult { 19 | fn from(result: CodeGenerationResult) -> Self { 20 | Self { 21 | sources: result 22 | .inner 23 | .into_iter() 24 | .map(|(source_type, source)| (source_type.to_string(), source.source().to_string())) 25 | .collect(), 26 | } 27 | } 28 | } 29 | 30 | impl From for JsCodegenerationResults { 31 | fn from(results: CodeGenerationResults) -> Self { 32 | let (map, id_result_map) = results.into_inner(); 33 | 34 | Self { 35 | map: map 36 | .into_iter() 37 | .map(|(module_id, runtime_result_map)| { 38 | let mut runtime_map: HashMap = Default::default(); 39 | match &runtime_result_map.mode { 40 | rspack_core::RuntimeMode::Empty => {} 41 | rspack_core::RuntimeMode::SingleEntry => { 42 | runtime_map.insert( 43 | get_runtime_key(runtime_result_map.single_runtime.as_ref().expect("exist")) 44 | .to_string(), 45 | id_result_map 46 | .get(&runtime_result_map.single_value.expect("TODO")) 47 | .expect("TODO") 48 | .clone() 49 | .into(), 50 | ); 51 | } 52 | rspack_core::RuntimeMode::Map => { 53 | runtime_result_map.map.into_iter().for_each(|(k, v)| { 54 | runtime_map.insert(k, id_result_map.get(&v).expect("TODO").clone().into()); 55 | }); 56 | } 57 | }; 58 | 59 | (module_id.to_string(), runtime_map) 60 | }) 61 | .collect(), 62 | } 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /.cargo/config.toml: -------------------------------------------------------------------------------- 1 | # workaround for getting workspace root dir, reference: https://github.com/rust-lang/cargo/issues/3946 2 | [env] 3 | CARGO_WORKSPACE_DIR = { value = "", relative = true } 4 | 5 | [alias] 6 | lint = "clippy --workspace --all-targets -- --deny warnings" 7 | # AKA `test-update`, handy cargo rst update without install `cargo-rst` binary 8 | t = "test --no-fail-fast" 9 | tu = "run -p cargo-rst -- update" 10 | build-wasi = "build --target wasm32-wasi" 11 | build-wasm32 = "build --target wasm32-unknown-unknown" 12 | 13 | [target.'cfg(all())'] 14 | rustflags = [ 15 | # CLIPPY LINT SETTINGS 16 | # This is a workaround to configure lints for the entire workspace, pending the ability to configure this via TOML. 17 | # See: `https://github.com/rust-lang/cargo/issues/5034` 18 | # `https://github.com/EmbarkStudios/rust-ecosystem/issues/22#issuecomment-947011395` 19 | "-Wclippy::all", # all lints that are on by default (correctness, suspicious, style, complexity, perf) 20 | 21 | # restriction 22 | "-Wclippy::dbg_macro", 23 | "-Wclippy::unwrap_in_result", 24 | "-Wclippy::unwrap_used", 25 | "-Wclippy::empty_drop", 26 | "-Wclippy::exit", 27 | "-Wclippy::empty_structs_with_brackets", 28 | "-Wclippy::rc_buffer", 29 | "-Wclippy::rc_mutex", 30 | "-Wclippy::same_name_method", 31 | 32 | "-Aclippy::default_constructed_unit_structs", 33 | ] 34 | # To be able to run unit tests on macOS, support compilation to 'x86_64-apple-darwin'. 35 | [target.'cfg(target_vendor = "apple")'] 36 | rustflags = ["-C", "link-args=-Wl,-undefined,dynamic_lookup"] 37 | 38 | # To be able to run unit tests on Linux, support compilation to 'x86_64-unknown-linux-gnu'. 39 | [target.'cfg(target_os = "linux")'] 40 | rustflags = ["-C", "link-args=-Wl,--warn-unresolved-symbols"] 41 | 42 | # To be able to run unit tests on Windows, support compilation to 'x86_64-pc-windows-msvc'. 43 | [target.'cfg(target_os = "windows")'] 44 | rustflags = ["-C", "link-args=/FORCE"] 45 | 46 | [target.x86_64-pc-windows-msvc] 47 | rustflags = ["-C", "target-feature=+crt-static"] 48 | [target.i686-pc-windows-msvc] 49 | rustflags = ["-C", "target-feature=+crt-static"] 50 | 51 | [target.x86_64-apple-darwin] 52 | rustflags = ["-C", "link-arg=-undefined", "-C", "link-arg=dynamic_lookup"] 53 | -------------------------------------------------------------------------------- /crates/binding_values/src/resolver.rs: -------------------------------------------------------------------------------- 1 | use std::path::Path; 2 | use std::sync::Arc; 3 | 4 | use napi::Either; 5 | use napi_derive::napi; 6 | use rspack_core::{ResolveOptionsWithDependencyType, Resolver, ResolverFactory}; 7 | 8 | use crate::raw_resolve::{ 9 | normalize_raw_resolve_options_with_dependency_type, RawResolveOptionsWithDependencyType, 10 | }; 11 | 12 | #[napi] 13 | #[derive(Debug)] 14 | pub struct JsResolver { 15 | resolver_factory: Arc, 16 | resolver: Arc, 17 | options: ResolveOptionsWithDependencyType, 18 | } 19 | 20 | impl JsResolver { 21 | pub fn new( 22 | resolver_factory: Arc, 23 | options: ResolveOptionsWithDependencyType, 24 | ) -> Self { 25 | let resolver = resolver_factory.get(options.clone()); 26 | Self { 27 | resolver_factory, 28 | resolver, 29 | options, 30 | } 31 | } 32 | } 33 | 34 | #[napi] 35 | impl JsResolver { 36 | #[napi(ts_return_type = "string | false")] 37 | pub fn resolve_sync(&self, path: String, request: String) -> napi::Result> { 38 | match self.resolver.resolve(Path::new(&path), &request) { 39 | Ok(rspack_core::ResolveResult::Resource(resource)) => Ok(Either::A(resource.full_path())), 40 | Ok(rspack_core::ResolveResult::Ignored) => Ok(Either::B(false)), 41 | Err(err) => Err(napi::Error::from_reason(format!("{:?}", err))), 42 | } 43 | } 44 | 45 | #[napi] 46 | pub fn with_options( 47 | &self, 48 | raw: Option, 49 | ) -> napi::Result { 50 | let options = 51 | normalize_raw_resolve_options_with_dependency_type(raw, self.options.resolve_to_context); 52 | match options { 53 | Ok(mut options) => { 54 | options.resolve_options = match options.resolve_options.take() { 55 | Some(resolve_options) => match &self.options.resolve_options { 56 | Some(origin_resolve_options) => Some(Box::new( 57 | resolve_options.merge(*origin_resolve_options.clone()), 58 | )), 59 | None => Some(resolve_options), 60 | }, 61 | None => self.options.resolve_options.clone(), 62 | }; 63 | 64 | Ok(Self::new(self.resolver_factory.clone(), options)) 65 | } 66 | Err(e) => Err(napi::Error::from_reason(format!("{e}"))), 67 | } 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /crates/binding_values/src/path_data.rs: -------------------------------------------------------------------------------- 1 | use napi_derive::napi; 2 | 3 | use super::JsAssetInfo; 4 | 5 | #[napi(object)] 6 | pub struct JsPathData { 7 | pub filename: Option, 8 | pub hash: Option, 9 | pub content_hash: Option, 10 | pub runtime: Option, 11 | pub url: Option, 12 | pub id: Option, 13 | pub chunk: Option, 14 | } 15 | 16 | #[napi(object)] 17 | pub struct JsPathDataChunkLike { 18 | pub name: Option, 19 | pub hash: Option, 20 | pub id: Option, 21 | } 22 | 23 | impl JsPathData { 24 | pub fn from_path_data(path_data: rspack_core::PathData) -> JsPathData { 25 | Self { 26 | filename: path_data.filename.map(|s| s.to_string()), 27 | hash: path_data.hash.map(|s| s.to_string()), 28 | content_hash: path_data.content_hash.map(|s| s.to_string()), 29 | runtime: path_data.runtime.map(|s| s.to_string()), 30 | url: path_data.url.map(|s| s.to_string()), 31 | id: path_data.id.map(|s| s.to_string()), 32 | chunk: (path_data.chunk_name.is_some() 33 | || path_data.chunk_id.is_some() 34 | || path_data.chunk_name.is_some()) 35 | .then(|| JsPathDataChunkLike { 36 | name: path_data.chunk_name.map(|s| s.to_string()), 37 | hash: path_data.chunk_hash.map(|s| s.to_string()), 38 | id: path_data.chunk_id.map(|s| s.to_string()), 39 | }), 40 | } 41 | } 42 | 43 | pub fn to_path_data(&self) -> rspack_core::PathData { 44 | rspack_core::PathData { 45 | filename: self.filename.as_deref(), 46 | chunk_name: self.chunk.as_ref().and_then(|c| c.name.as_deref()), 47 | chunk_hash: self.chunk.as_ref().and_then(|c| c.hash.as_deref()), 48 | chunk_id: self.chunk.as_ref().and_then(|c| c.id.as_deref()), 49 | module_id: None, 50 | hash: self.hash.as_deref(), 51 | content_hash: self.content_hash.as_deref(), 52 | runtime: self.runtime.as_deref(), 53 | url: self.url.as_deref(), 54 | id: self.id.as_deref(), 55 | } 56 | } 57 | } 58 | 59 | #[napi(object)] 60 | pub struct PathWithInfo { 61 | pub path: String, 62 | pub info: JsAssetInfo, 63 | } 64 | 65 | impl From<(String, rspack_core::AssetInfo)> for PathWithInfo { 66 | fn from(value: (String, rspack_core::AssetInfo)) -> Self { 67 | Self { 68 | path: value.0, 69 | info: value.1.into(), 70 | } 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /crates/swc_named_import_transform/src/lib.rs: -------------------------------------------------------------------------------- 1 | use std::collections::HashSet; 2 | 3 | use swc_core::{ 4 | common::DUMMY_SP, 5 | ecma::{ast::*, visit::{fold_pass, Fold}}, 6 | }; 7 | 8 | pub struct TransformConfig { 9 | pub packages: Vec, 10 | } 11 | 12 | pub struct NamedImportTransform { 13 | pub packages: Vec, 14 | } 15 | 16 | pub fn named_import_transform(config: TransformConfig) -> impl Pass { 17 | fold_pass(NamedImportTransform { 18 | packages: config.packages, 19 | }) 20 | } 21 | 22 | impl Fold for NamedImportTransform { 23 | fn fold_import_decl(&mut self, decl: ImportDecl) -> ImportDecl { 24 | let src_value = decl.src.value.clone(); 25 | if self.packages.iter().any(|p| src_value == *p) { 26 | let mut specifier_names = HashSet::new(); 27 | let mut skip = false; 28 | for specifier in &decl.specifiers { 29 | match specifier { 30 | ImportSpecifier::Named(specifier) => { 31 | if let Some(imported) = &specifier.imported { 32 | match imported { 33 | ModuleExportName::Ident(ident) => { 34 | specifier_names.insert(ident.sym.to_string()); 35 | } 36 | ModuleExportName::Str(str) => { 37 | specifier_names.insert(str.value.to_string()); 38 | } 39 | } 40 | } else { 41 | specifier_names.insert(specifier.local.sym.to_string()); 42 | } 43 | } 44 | ImportSpecifier::Default(_) => { 45 | skip = true; 46 | } 47 | ImportSpecifier::Namespace(_) => { 48 | skip = true; 49 | } 50 | } 51 | } 52 | if !skip { 53 | let mut names = specifier_names.into_iter().collect::>(); 54 | names.sort(); 55 | 56 | let new_src = format!( 57 | // Add unique query string to avoid loader cache. 58 | "__barrel_optimize__?names={}!=!{}?{}", 59 | names.join(","), 60 | src_value, 61 | names.join(","), 62 | ); 63 | 64 | // Create a new import declaration, keep everything the same except the source 65 | let mut new_decl = decl.clone(); 66 | new_decl.src = Box::new(Str { 67 | span: DUMMY_SP, 68 | value: new_src.into(), 69 | raw: None, 70 | }); 71 | 72 | return new_decl; 73 | } 74 | } 75 | decl 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /.github/actions/rustup/action.yml: -------------------------------------------------------------------------------- 1 | # This action installs the minimal Rust profile and configures Swatinem/rust-cache. 2 | # 3 | # It is needed to install as few Rust components as possbile because 4 | # it takes a few minutes to install some of components on Windows and Mac, especially rust-doc. 5 | 6 | name: Rustup 7 | 8 | description: Install Rust with cache 9 | 10 | inputs: 11 | # See https://rust-lang.github.io/rustup/concepts/components.html 12 | clippy: 13 | default: false 14 | required: false 15 | type: boolean 16 | fmt: 17 | default: false 18 | required: false 19 | type: boolean 20 | docs: 21 | default: false 22 | required: false 23 | type: boolean 24 | save-cache: 25 | default: false 26 | required: false 27 | type: boolean 28 | shared-key: 29 | default: 'check' 30 | required: false 31 | type: string 32 | 33 | env: 34 | IS_GITHUB_RUNNER: startsWith(runner.name, 'GitHub Actions') 35 | 36 | runs: 37 | using: composite 38 | steps: 39 | - name: Print Inputs 40 | shell: bash 41 | run: | 42 | echo 'clippy: ${{ inputs.clippy }}' 43 | echo 'fmt: ${{ inputs.fmt }}' 44 | echo 'docs: ${{ inputs.docs }}' 45 | echo 'save-cache: ${{ inputs.save-cache }}' 46 | echo 'shared-key: ${{ inputs.shared-key }}' 47 | 48 | - name: Remove `profile` line on MacOS 49 | shell: bash 50 | if: runner.os == 'macOS' 51 | run: sed -i '' '/profile/d' rust-toolchain.toml 52 | 53 | - name: Remove `profile` line on non-MacOS 54 | shell: bash 55 | if: runner.os != 'macOS' 56 | run: sed -i '/profile/d' rust-toolchain.toml 57 | 58 | - name: Set minimal 59 | shell: bash 60 | run: rustup set profile minimal 61 | 62 | - name: Add Clippy 63 | shell: bash 64 | if: ${{ inputs.clippy == 'true' }} 65 | run: rustup component add clippy 66 | 67 | - name: Add Rustfmt 68 | shell: bash 69 | if: ${{ inputs.fmt == 'true' }} 70 | run: rustup component add rustfmt 71 | 72 | - name: Add docs 73 | shell: bash 74 | if: ${{ inputs.docs == 'true' }} 75 | run: rustup component add rust-docs 76 | 77 | - name: Install 78 | shell: bash 79 | run: rustup show 80 | 81 | - name: Cache on ${{ github.ref_name }} 82 | uses: Swatinem/rust-cache@v2 83 | if: ${{ env.IS_GITHUB_RUNNER }} 84 | with: 85 | shared-key: ${{ inputs.shared-key }} 86 | save-if: ${{ inputs.save-cache == 'true' }} 87 | -------------------------------------------------------------------------------- /crates/binding_values/src/raw_options/raw_experiments/raw_incremental.rs: -------------------------------------------------------------------------------- 1 | use napi_derive::napi; 2 | use rspack_core::incremental::IncrementalPasses; 3 | 4 | #[derive(Debug, Default)] 5 | #[napi(object)] 6 | pub struct RawIncremental { 7 | pub make: bool, 8 | pub infer_async_modules: bool, 9 | pub provided_exports: bool, 10 | pub dependencies_diagnostics: bool, 11 | pub side_effects: bool, 12 | pub build_chunk_graph: bool, 13 | pub module_ids: bool, 14 | pub chunk_ids: bool, 15 | pub modules_hashes: bool, 16 | pub modules_codegen: bool, 17 | pub modules_runtime_requirements: bool, 18 | pub chunks_runtime_requirements: bool, 19 | pub chunks_hashes: bool, 20 | pub chunks_render: bool, 21 | pub emit_assets: bool, 22 | } 23 | 24 | impl From for IncrementalPasses { 25 | fn from(value: RawIncremental) -> Self { 26 | let mut passes = IncrementalPasses::empty(); 27 | if value.make { 28 | passes.insert(IncrementalPasses::MAKE); 29 | } 30 | if value.infer_async_modules { 31 | passes.insert(IncrementalPasses::INFER_ASYNC_MODULES); 32 | } 33 | if value.provided_exports { 34 | passes.insert(IncrementalPasses::PROVIDED_EXPORTS); 35 | } 36 | if value.dependencies_diagnostics { 37 | passes.insert(IncrementalPasses::DEPENDENCIES_DIAGNOSTICS); 38 | } 39 | if value.side_effects { 40 | passes.insert(IncrementalPasses::SIDE_EFFECTS); 41 | } 42 | if value.build_chunk_graph { 43 | passes.insert(IncrementalPasses::BUILD_CHUNK_GRAPH); 44 | } 45 | if value.module_ids { 46 | passes.insert(IncrementalPasses::MODULE_IDS); 47 | } 48 | if value.chunk_ids { 49 | passes.insert(IncrementalPasses::CHUNK_IDS); 50 | } 51 | if value.modules_hashes { 52 | passes.insert(IncrementalPasses::MODULES_HASHES); 53 | } 54 | if value.modules_codegen { 55 | passes.insert(IncrementalPasses::MODULES_CODEGEN); 56 | } 57 | if value.modules_runtime_requirements { 58 | passes.insert(IncrementalPasses::MODULES_RUNTIME_REQUIREMENTS); 59 | } 60 | if value.chunks_runtime_requirements { 61 | passes.insert(IncrementalPasses::CHUNKS_RUNTIME_REQUIREMENTS); 62 | } 63 | if value.chunks_hashes { 64 | passes.insert(IncrementalPasses::CHUNKS_HASHES); 65 | } 66 | if value.chunks_render { 67 | passes.insert(IncrementalPasses::CHUNKS_RENDER); 68 | } 69 | if value.emit_assets { 70 | passes.insert(IncrementalPasses::EMIT_ASSETS); 71 | } 72 | passes 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /crates/binding_values/src/rspack_error.rs: -------------------------------------------------------------------------------- 1 | use napi_derive::napi; 2 | use rspack_error::{miette, Diagnostic, Result, RspackSeverity}; 3 | 4 | #[napi(object)] 5 | pub struct JsRspackDiagnostic { 6 | pub severity: JsRspackSeverity, 7 | pub error: JsRspackError, 8 | } 9 | 10 | impl From for Diagnostic { 11 | fn from(value: JsRspackDiagnostic) -> Self { 12 | value.error.into_diagnostic(value.severity.into()) 13 | } 14 | } 15 | 16 | #[napi(string_enum)] 17 | pub enum JsRspackSeverity { 18 | Error, 19 | Warn, 20 | } 21 | 22 | impl From for RspackSeverity { 23 | fn from(value: JsRspackSeverity) -> Self { 24 | match value { 25 | JsRspackSeverity::Error => RspackSeverity::Error, 26 | JsRspackSeverity::Warn => RspackSeverity::Warn, 27 | } 28 | } 29 | } 30 | 31 | impl From for miette::Severity { 32 | fn from(value: JsRspackSeverity) -> Self { 33 | match value { 34 | JsRspackSeverity::Error => miette::Severity::Error, 35 | JsRspackSeverity::Warn => miette::Severity::Warning, 36 | } 37 | } 38 | } 39 | 40 | #[napi(object)] 41 | #[derive(Debug)] 42 | pub struct JsRspackError { 43 | pub name: String, 44 | pub message: String, 45 | pub module_identifier: Option, 46 | pub loc: Option, 47 | pub file: Option, 48 | pub stack: Option, 49 | pub hide_stack: Option, 50 | } 51 | 52 | impl JsRspackError { 53 | pub fn try_from_diagnostic(diagnostic: &Diagnostic, colored: bool) -> Result { 54 | Ok(Self { 55 | name: diagnostic.code().map(|n| n.to_string()).unwrap_or_else(|| { 56 | match diagnostic.severity() { 57 | rspack_error::RspackSeverity::Error => "Error".to_string(), 58 | rspack_error::RspackSeverity::Warn => "Warn".to_string(), 59 | } 60 | }), 61 | message: diagnostic.render_report(colored)?, 62 | module_identifier: diagnostic.module_identifier().map(|d| d.to_string()), 63 | loc: diagnostic.loc(), 64 | file: diagnostic.file().map(|f| f.as_str().to_string()), 65 | stack: diagnostic.stack(), 66 | hide_stack: diagnostic.hide_stack(), 67 | }) 68 | } 69 | 70 | pub fn into_diagnostic(self, severity: RspackSeverity) -> Diagnostic { 71 | (match severity { 72 | RspackSeverity::Error => Diagnostic::error, 73 | RspackSeverity::Warn => Diagnostic::warn, 74 | })(self.name, self.message) 75 | .with_file(self.file.map(Into::into)) 76 | .with_module_identifier(self.module_identifier.map(Into::into)) 77 | .with_stack(self.stack) 78 | .with_hide_stack(self.hide_stack) 79 | } 80 | } 81 | -------------------------------------------------------------------------------- /scripts/github.mjs: -------------------------------------------------------------------------------- 1 | import path from 'path'; 2 | import gitclone from 'git-clone/promise.js'; 3 | import { rimraf } from 'rimraf'; 4 | import ora from 'ora'; 5 | import yaml from 'js-yaml'; 6 | import fse from 'fs-extra'; 7 | 8 | export function getGithubInfo() { 9 | try { 10 | const content = yaml.load( 11 | fse.readFileSync('.github/actions/clone-crates/action.yml', 'utf-8') 12 | ); 13 | return ['repo', 'dest', 'temp', 'ref'].reduce((info, key) => { 14 | info[key] = content.inputs[key].default; 15 | return info; 16 | }, {}); 17 | } catch (e) { 18 | console.log(e); 19 | return {}; 20 | } 21 | } 22 | 23 | export async function overwriteContent(content, dest) { 24 | fse.writeFileSync(dest, content); 25 | } 26 | 27 | export async function copyAndCleanUp(temp, dest, spinner) { 28 | const updateSpinner = text => { 29 | if (spinner) { 30 | spinner.text = text; 31 | } 32 | } 33 | 34 | updateSpinner('Copying crates to the dest...'); 35 | 36 | fse.copySync(path.join(temp, 'crates'), dest); 37 | 38 | const pkg = JSON.parse(fse.readFileSync(path.join(temp, 'package.json'), 'utf-8')); 39 | 40 | // Update build.rs content 41 | const buildRsPath = path.join(dest, 'rspack_loader_swc/build.rs'); 42 | const buildRsContent = fse.readFileSync(buildRsPath, 'utf-8') 43 | .replace('"../../Cargo.toml"', '"../../../Cargo.toml"'); 44 | fse.writeFileSync(buildRsPath, buildRsContent); 45 | 46 | // Write package.json 47 | fse.writeFileSync( 48 | path.join(dest, '../package.json'), 49 | JSON.stringify({ version: pkg.version }, null, 2) 50 | ); 51 | 52 | updateSpinner('Clean up...'); 53 | await rimraf(temp); 54 | 55 | if (process.env.IS_GITHUB) { 56 | await Promise.all( 57 | ['node_binding', 'bench'].map(dir => 58 | rimraf(path.join(dest, dir)) 59 | ) 60 | ); 61 | } 62 | 63 | spinner?.succeed('Cloning rspack repo succeed.'); 64 | } 65 | 66 | export function createSpinner(text, options = {}) { 67 | const spinner = ora({ 68 | text, 69 | stream: process.stdout, 70 | isEnabled: process.stdout.isTTY, 71 | interval: 200, 72 | ...options, 73 | }); 74 | spinner.start(); 75 | return spinner; 76 | } 77 | 78 | export async function getRspackCrates() { 79 | const { repo, dest, temp, ref } = getGithubInfo(); 80 | const spinner = createSpinner('Cloning rspack repo...'); 81 | 82 | try { 83 | await rimraf(dest); 84 | await gitclone(`git@github.com:${repo}.git`, temp, { checkout: ref }); 85 | await copyAndCleanUp(temp, dest, spinner); 86 | } catch (err) { 87 | spinner.fail('Cloning rspack repo failed.'); 88 | await rimraf(temp); 89 | console.log(err); 90 | } 91 | } 92 | 93 | export default getGithubInfo(); -------------------------------------------------------------------------------- /crates/binding_values/src/raw_options/raw_builtins/raw_banner.rs: -------------------------------------------------------------------------------- 1 | use derive_more::Debug; 2 | use napi::Either; 3 | use napi_derive::napi; 4 | use rspack_collections::DatabaseItem; 5 | use rspack_error::Result; 6 | use rspack_napi::threadsafe_function::ThreadsafeFunction; 7 | use rspack_plugin_banner::{BannerContent, BannerContentFnCtx, BannerPluginOptions}; 8 | 9 | use crate::{into_asset_conditions, JsChunkWrapper, RawAssetConditions}; 10 | 11 | #[napi(object, object_from_js = false)] 12 | pub struct JsBannerContentFnCtx { 13 | pub hash: String, 14 | #[napi(ts_type = "JsChunk")] 15 | pub chunk: JsChunkWrapper, 16 | pub filename: String, 17 | } 18 | 19 | impl From> for JsBannerContentFnCtx { 20 | fn from(value: BannerContentFnCtx) -> Self { 21 | Self { 22 | hash: value.hash.to_string(), 23 | chunk: JsChunkWrapper::new(value.chunk.ukey(), value.compilation), 24 | filename: value.filename.to_string(), 25 | } 26 | } 27 | } 28 | 29 | type RawBannerContent = Either>; 30 | struct RawBannerContentWrapper(RawBannerContent); 31 | 32 | impl TryFrom for BannerContent { 33 | type Error = rspack_error::Error; 34 | fn try_from(value: RawBannerContentWrapper) -> Result { 35 | match value.0 { 36 | Either::A(s) => Ok(Self::String(s)), 37 | Either::B(f) => Ok(BannerContent::Fn(Box::new( 38 | move |ctx: BannerContentFnCtx| { 39 | let ctx = ctx.into(); 40 | let f = f.clone(); 41 | Box::pin(async move { f.call(ctx).await }) 42 | }, 43 | ))), 44 | } 45 | } 46 | } 47 | 48 | #[derive(Debug)] 49 | #[napi(object, object_to_js = false)] 50 | pub struct RawBannerPluginOptions { 51 | #[debug(skip)] 52 | #[napi(ts_type = "string | ((...args: any[]) => any)")] 53 | pub banner: RawBannerContent, 54 | pub entry_only: Option, 55 | pub footer: Option, 56 | pub raw: Option, 57 | pub stage: Option, 58 | #[napi(ts_type = "string | RegExp | (string | RegExp)[]")] 59 | pub test: Option, 60 | #[napi(ts_type = "string | RegExp | (string | RegExp)[]")] 61 | pub include: Option, 62 | #[napi(ts_type = "string | RegExp | (string | RegExp)[]")] 63 | pub exclude: Option, 64 | } 65 | 66 | impl TryFrom for BannerPluginOptions { 67 | type Error = rspack_error::Error; 68 | fn try_from(value: RawBannerPluginOptions) -> Result { 69 | Ok(BannerPluginOptions { 70 | banner: RawBannerContentWrapper(value.banner).try_into()?, 71 | entry_only: value.entry_only, 72 | footer: value.footer, 73 | raw: value.raw, 74 | stage: value.stage, 75 | test: value.test.map(into_asset_conditions), 76 | include: value.include.map(into_asset_conditions), 77 | exclude: value.exclude.map(into_asset_conditions), 78 | }) 79 | } 80 | } 81 | -------------------------------------------------------------------------------- /crates/binding_values/src/raw_options/raw_experiments/raw_cache/mod.rs: -------------------------------------------------------------------------------- 1 | mod raw_snapshot; 2 | mod raw_storage; 3 | 4 | use core::panic; 5 | 6 | use napi::{ 7 | bindgen_prelude::{FromNapiValue, Object, TypeName, ValidateNapiValue}, 8 | Either, 9 | }; 10 | use napi_derive::napi; 11 | use raw_snapshot::RawExperimentSnapshotOptions; 12 | use raw_storage::RawStorageOptions; 13 | use rspack_core::{cache::persistent::PersistentCacheOptions, ExperimentCacheOptions}; 14 | 15 | pub type RawExperimentCacheOptions = Either; 16 | 17 | #[derive(Debug, Default)] 18 | #[napi(object)] 19 | pub struct RawExperimentCacheOptionsPersistent { 20 | pub build_dependencies: Option>, 21 | pub version: Option, 22 | pub snapshot: Option, 23 | pub storage: Option, 24 | } 25 | 26 | impl From for PersistentCacheOptions { 27 | fn from(value: RawExperimentCacheOptionsPersistent) -> Self { 28 | Self { 29 | build_dependencies: value 30 | .build_dependencies 31 | .unwrap_or_default() 32 | .into_iter() 33 | .map(Into::into) 34 | .collect(), 35 | version: value.version.unwrap_or_default(), 36 | snapshot: value.snapshot.unwrap_or_default().into(), 37 | storage: value.storage.unwrap_or_default().into(), 38 | } 39 | } 40 | } 41 | 42 | #[derive(Debug, Default)] 43 | pub enum RawExperimentCache { 44 | #[default] 45 | Memory, 46 | Persistent(RawExperimentCacheOptionsPersistent), 47 | } 48 | 49 | impl TypeName for RawExperimentCache { 50 | fn type_name() -> &'static str { 51 | "RawExperimentCache" 52 | } 53 | 54 | fn value_type() -> napi::ValueType { 55 | napi::ValueType::Object 56 | } 57 | } 58 | 59 | impl ValidateNapiValue for RawExperimentCache {} 60 | 61 | impl FromNapiValue for RawExperimentCache { 62 | unsafe fn from_napi_value( 63 | env: napi::sys::napi_env, 64 | napi_val: napi::sys::napi_value, 65 | ) -> napi::Result { 66 | let o = Object::from_napi_value(env, napi_val)?; 67 | let t = o.get_named_property::("type")?; 68 | 69 | let v = match &*t { 70 | "persistent" => { 71 | let o = RawExperimentCacheOptionsPersistent::from_napi_value(env, napi_val)?; 72 | Self::Persistent(o) 73 | } 74 | "memory" => Self::Memory, 75 | _ => panic!("Unexpected cache type: {t}, expected 'persistent' or 'memory'"), 76 | }; 77 | 78 | Ok(v) 79 | } 80 | } 81 | 82 | pub fn normalize_raw_experiment_cache_options( 83 | options: RawExperimentCacheOptions, 84 | ) -> ExperimentCacheOptions { 85 | match options { 86 | Either::A(options) => { 87 | if options { 88 | ExperimentCacheOptions::Memory 89 | } else { 90 | ExperimentCacheOptions::Disabled 91 | } 92 | } 93 | Either::B(options) => match options { 94 | RawExperimentCache::Persistent(options) => ExperimentCacheOptions::Persistent(options.into()), 95 | RawExperimentCache::Memory => ExperimentCacheOptions::Memory, 96 | }, 97 | } 98 | } 99 | -------------------------------------------------------------------------------- /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | name: CI 2 | 3 | on: 4 | merge_group: 5 | types: [checks_requested] 6 | workflow_dispatch: 7 | inputs: 8 | debug_enabled: 9 | type: boolean 10 | description: "Run the build with tmate debugging enabled (https://github.com/marketplace/actions/debugging-with-tmate)" 11 | required: false 12 | default: false 13 | pull_request: 14 | types: [opened, synchronize] 15 | paths-ignore: 16 | - "**/*.md" 17 | branches-ignore: 18 | - "release-**" 19 | push: 20 | branches: 21 | - main 22 | paths-ignore: 23 | - "**/*.md" 24 | tags-ignore: 25 | - "**" 26 | 27 | concurrency: 28 | group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }} 29 | cancel-in-progress: ${{ github.ref_name != 'main' }} 30 | 31 | jobs: 32 | rust_changes: 33 | name: Rust Changes 34 | runs-on: ubuntu-latest 35 | outputs: 36 | changed: ${{ steps.filter.outputs.changed }} 37 | steps: 38 | - uses: actions/checkout@v4 39 | 40 | - uses: dorny/paths-filter@v2 41 | id: filter 42 | with: 43 | filters: | 44 | changed: 45 | - '.github/workflows/ci.yml' 46 | - 'crates/**' 47 | - 'Cargo.lock' 48 | - 'Cargo.toml' 49 | - 'rust-toolchain.toml' 50 | 51 | # rust_check: 52 | # name: Rust check 53 | # needs: rust_changes 54 | # if: ${{ needs.rust_changes.outputs.changed == 'true' }} 55 | # runs-on: ${{ fromJSON(vars.LINUX_RUNNER_LABELS || '"ubuntu-latest"') }} 56 | # steps: 57 | # - uses: actions/checkout@v4 58 | 59 | # - name: Pnpm Cache # Required by some tests 60 | # uses: ./.github/actions/pnpm-cache 61 | 62 | # - name: Clone Crates 63 | # uses: ./.github/actions/clone-crates 64 | 65 | # - name: Install Rust Toolchain 66 | # uses: ./.github/actions/rustup 67 | # with: 68 | # clippy: true 69 | # fmt: true 70 | # shared-key: check 71 | 72 | # - name: Run Cargo Check 73 | # run: cargo check --workspace --all-targets # Not using --release because it uses too much cache, and is also slow. 74 | 75 | rust_test: 76 | name: Rust test 77 | needs: rust_changes 78 | if: ${{ needs.rust_changes.outputs.changed == 'true' }} 79 | runs-on: ${{ fromJSON(vars.LINUX_RUNNER_LABELS || '"ubuntu-latest"') }} 80 | steps: 81 | - uses: actions/checkout@v4 82 | 83 | - name: Pnpm Cache # Required by some tests 84 | uses: ./.github/actions/pnpm-cache 85 | 86 | - name: Clone Crates 87 | uses: ./.github/actions/clone-crates 88 | 89 | - name: Install Rust Toolchain 90 | uses: ./.github/actions/rustup 91 | with: 92 | save-cache: ${{ github.ref_name == 'master' }} 93 | shared-key: check 94 | 95 | # Compile test without debug info for reducing the CI cache size 96 | - name: Change profile.test 97 | shell: bash 98 | run: | 99 | echo '[profile.test]' >> Cargo.toml 100 | echo 'debug = false' >> Cargo.toml 101 | 102 | - name: Run test 103 | run: pnpm test 104 | -------------------------------------------------------------------------------- /crates/node_binding/src/diagnostic.rs: -------------------------------------------------------------------------------- 1 | use napi::bindgen_prelude::*; 2 | use rspack_error::{ 3 | miette::{self, LabeledSpan, MietteDiagnostic, Severity}, 4 | Diagnostic, 5 | }; 6 | use rspack_util::location::{ 7 | try_line_column_length_to_location, try_line_column_length_to_offset_length, 8 | }; 9 | 10 | #[napi(object)] 11 | pub struct JsDiagnosticLocation { 12 | pub text: Option, 13 | /// 1-based 14 | pub line: u32, 15 | /// 0-based in bytes 16 | pub column: u32, 17 | /// Length in bytes 18 | pub length: u32, 19 | } 20 | 21 | #[napi(object)] 22 | pub struct JsDiagnostic { 23 | pub message: String, 24 | pub help: Option, 25 | pub source_code: Option, 26 | pub location: Option, 27 | pub file: Option, 28 | 29 | #[napi(ts_type = "\"error\" | \"warning\"")] 30 | pub severity: String, 31 | pub module_identifier: Option, 32 | } 33 | 34 | #[napi(ts_return_type = "ExternalObject<'Diagnostic'>")] 35 | pub fn format_diagnostic(diagnostic: JsDiagnostic) -> Result> { 36 | let JsDiagnostic { 37 | message, 38 | help, 39 | source_code, 40 | location, 41 | severity, 42 | module_identifier, 43 | file, 44 | } = diagnostic; 45 | let mut d = MietteDiagnostic::new(message).with_severity(match severity.as_str() { 46 | "warning" => Severity::Warning, 47 | _ => Severity::Error, 48 | }); 49 | if let Some(help) = help { 50 | d = d.with_help(help); 51 | } 52 | let mut loc = None; 53 | if let Some(ref source_code) = source_code { 54 | let rope = ropey::Rope::from_str(source_code); 55 | if let Some(location) = location { 56 | loc = try_line_column_length_to_location( 57 | &rope, 58 | location.line as usize, 59 | location.column as usize, 60 | location.length as usize, 61 | ); 62 | let (offset, length) = try_line_column_length_to_offset_length( 63 | &rope, 64 | location.line as usize, 65 | location.column as usize, 66 | location.length as usize, 67 | ) 68 | .ok_or_else(|| { 69 | Error::new( 70 | Status::Unknown, 71 | "Format diagnostic failed: Invalid location. Did you pass the correct line, column and length?", 72 | ) 73 | })?; 74 | let end_byte = offset.saturating_add(length); 75 | if end_byte > rope.len_bytes() { 76 | return Err(Error::new( 77 | Status::Unknown, 78 | "Format diagnostic failed: Invalid `length` in location.", 79 | )); 80 | } 81 | if !source_code.is_char_boundary(offset) || !source_code.is_char_boundary(end_byte) { 82 | return Err(Error::new( 83 | Status::Unknown, 84 | "Format diagnostic failed: Invalid char boundary. Did you pass the correct line, column and length?", 85 | )); 86 | } 87 | d = d.with_label(LabeledSpan::new(location.text, offset, length)); 88 | } 89 | } 90 | 91 | let mut error = miette::Error::new(d); 92 | if let Some(source_code) = source_code { 93 | error = error.with_source_code(source_code); 94 | } 95 | Ok(External::new( 96 | Diagnostic::from(error) 97 | .with_file(file.map(Into::into)) 98 | .with_loc(loc.map(|l| l.to_string())) 99 | .with_module_identifier(module_identifier.map(Into::into)), 100 | )) 101 | } 102 | -------------------------------------------------------------------------------- /crates/binding_values/src/raw_options/raw_builtins/raw_swc_js_minimizer.rs: -------------------------------------------------------------------------------- 1 | use napi::Either; 2 | use napi_derive::napi; 3 | use rspack_error::{miette::IntoDiagnostic, Result}; 4 | use rspack_plugin_swc_js_minimizer::{ 5 | ExtractComments, MinimizerOptions, OptionWrapper, PluginOptions, 6 | }; 7 | use serde::de::DeserializeOwned; 8 | use swc_core::base::BoolOrDataConfig; 9 | 10 | use crate::{into_asset_conditions, RawAssetConditions}; 11 | 12 | #[derive(Debug)] 13 | #[napi(object)] 14 | pub struct RawExtractComments { 15 | pub banner: Option>, 16 | pub condition: Option, 17 | } 18 | 19 | #[derive(Debug)] 20 | #[napi(object, object_to_js = false)] 21 | pub struct RawSwcJsMinimizerRspackPluginOptions { 22 | #[napi(ts_type = "string | RegExp | (string | RegExp)[]")] 23 | pub test: Option, 24 | #[napi(ts_type = "string | RegExp | (string | RegExp)[]")] 25 | pub include: Option, 26 | #[napi(ts_type = "string | RegExp | (string | RegExp)[]")] 27 | pub exclude: Option, 28 | pub extract_comments: Option, 29 | pub minimizer_options: RawSwcJsMinimizerOptions, 30 | } 31 | 32 | #[derive(Debug)] 33 | #[napi(object, object_to_js = false)] 34 | pub struct RawSwcJsMinimizerOptions { 35 | pub compress: serde_json::Value, 36 | pub mangle: serde_json::Value, 37 | pub format: serde_json::Value, 38 | pub module: Option, 39 | pub minify: Option, 40 | } 41 | 42 | fn try_deserialize_into(value: serde_json::Value) -> Result 43 | where 44 | T: DeserializeOwned, 45 | { 46 | serde_json::from_value(value).into_diagnostic() 47 | } 48 | 49 | fn into_extract_comments(c: Option) -> Option { 50 | let c = c?; 51 | let condition = c.condition?; 52 | let banner = match c.banner { 53 | Some(banner) => match banner { 54 | Either::A(s) => OptionWrapper::Custom(s), 55 | Either::B(b) => { 56 | if b { 57 | OptionWrapper::Default 58 | } else { 59 | OptionWrapper::Disabled 60 | } 61 | } 62 | }, 63 | None => OptionWrapper::Default, 64 | }; 65 | 66 | Some(ExtractComments { condition, banner }) 67 | } 68 | 69 | impl TryFrom for PluginOptions { 70 | type Error = rspack_error::Error; 71 | 72 | fn try_from(value: RawSwcJsMinimizerRspackPluginOptions) -> Result { 73 | let compress = try_deserialize_into::< 74 | BoolOrDataConfig, 75 | >(value.minimizer_options.compress)? 76 | .or(|| BoolOrDataConfig::from_bool(true)); 77 | let mangle = try_deserialize_into::< 78 | BoolOrDataConfig, 79 | >(value.minimizer_options.mangle)? 80 | .or(|| BoolOrDataConfig::from_bool(true)); 81 | Ok(Self { 82 | extract_comments: into_extract_comments(value.extract_comments), 83 | test: value.test.map(into_asset_conditions), 84 | include: value.include.map(into_asset_conditions), 85 | exclude: value.exclude.map(into_asset_conditions), 86 | minimizer_options: MinimizerOptions { 87 | compress, 88 | mangle, 89 | format: try_deserialize_into(value.minimizer_options.format)?, 90 | module: value.minimizer_options.module, 91 | minify: value.minimizer_options.minify, 92 | ..Default::default() 93 | }, 94 | }) 95 | } 96 | } 97 | -------------------------------------------------------------------------------- /crates/binding_values/src/compilation/dependencies.rs: -------------------------------------------------------------------------------- 1 | use napi_derive::napi; 2 | use rspack_core::Compilation; 3 | 4 | #[napi] 5 | pub struct JsDependencies { 6 | pub(crate) compilation: &'static Compilation, 7 | } 8 | 9 | impl JsDependencies { 10 | pub(crate) fn new(compilation: &'static Compilation) -> Self { 11 | Self { compilation } 12 | } 13 | } 14 | 15 | #[napi] 16 | impl JsDependencies { 17 | #[napi(getter)] 18 | pub fn file_dependencies(&self) -> Vec { 19 | self 20 | .compilation 21 | .file_dependencies() 22 | .0 23 | .map(|i| i.to_string_lossy().to_string()) 24 | .collect() 25 | } 26 | #[napi(getter)] 27 | pub fn added_file_dependencies(&self) -> Vec { 28 | self 29 | .compilation 30 | .file_dependencies() 31 | .1 32 | .map(|i| i.to_string_lossy().to_string()) 33 | .collect() 34 | } 35 | #[napi(getter)] 36 | pub fn removed_file_dependencies(&self) -> Vec { 37 | self 38 | .compilation 39 | .file_dependencies() 40 | .2 41 | .map(|i| i.to_string_lossy().to_string()) 42 | .collect() 43 | } 44 | 45 | #[napi(getter)] 46 | pub fn context_dependencies(&self) -> Vec { 47 | self 48 | .compilation 49 | .context_dependencies() 50 | .0 51 | .map(|i| i.to_string_lossy().to_string()) 52 | .collect() 53 | } 54 | #[napi(getter)] 55 | pub fn added_context_dependencies(&self) -> Vec { 56 | self 57 | .compilation 58 | .context_dependencies() 59 | .1 60 | .map(|i| i.to_string_lossy().to_string()) 61 | .collect() 62 | } 63 | #[napi(getter)] 64 | pub fn removed_context_dependencies(&self) -> Vec { 65 | self 66 | .compilation 67 | .context_dependencies() 68 | .2 69 | .map(|i| i.to_string_lossy().to_string()) 70 | .collect() 71 | } 72 | 73 | #[napi(getter)] 74 | pub fn missing_dependencies(&self) -> Vec { 75 | self 76 | .compilation 77 | .missing_dependencies() 78 | .0 79 | .map(|i| i.to_string_lossy().to_string()) 80 | .collect() 81 | } 82 | #[napi(getter)] 83 | pub fn added_missing_dependencies(&self) -> Vec { 84 | self 85 | .compilation 86 | .missing_dependencies() 87 | .1 88 | .map(|i| i.to_string_lossy().to_string()) 89 | .collect() 90 | } 91 | #[napi(getter)] 92 | pub fn removed_missing_dependencies(&self) -> Vec { 93 | self 94 | .compilation 95 | .missing_dependencies() 96 | .2 97 | .map(|i| i.to_string_lossy().to_string()) 98 | .collect() 99 | } 100 | 101 | #[napi(getter)] 102 | pub fn build_dependencies(&self) -> Vec { 103 | self 104 | .compilation 105 | .build_dependencies() 106 | .0 107 | .map(|i| i.to_string_lossy().to_string()) 108 | .collect() 109 | } 110 | #[napi(getter)] 111 | pub fn added_build_dependencies(&self) -> Vec { 112 | self 113 | .compilation 114 | .build_dependencies() 115 | .1 116 | .map(|i| i.to_string_lossy().to_string()) 117 | .collect() 118 | } 119 | #[napi(getter)] 120 | pub fn removed_build_dependencies(&self) -> Vec { 121 | self 122 | .compilation 123 | .build_dependencies() 124 | .2 125 | .map(|i| i.to_string_lossy().to_string()) 126 | .collect() 127 | } 128 | } 129 | -------------------------------------------------------------------------------- /crates/node_binding/src/resolver_factory.rs: -------------------------------------------------------------------------------- 1 | use std::sync::Arc; 2 | 3 | use napi_derive::napi; 4 | use rspack_core::{Resolve, ResolverFactory}; 5 | use rspack_fs::{NativeFileSystem, ReadableFileSystem}; 6 | 7 | use crate::{ 8 | raw_resolve::{ 9 | normalize_raw_resolve_options_with_dependency_type, RawResolveOptionsWithDependencyType, 10 | }, 11 | JsResolver, 12 | }; 13 | 14 | #[napi] 15 | pub struct JsResolverFactory { 16 | pub(crate) resolver_factory: Option>, 17 | pub(crate) loader_resolver_factory: Option>, 18 | pub(crate) input_filesystem: Arc, 19 | } 20 | 21 | #[napi] 22 | impl JsResolverFactory { 23 | #[napi(constructor)] 24 | pub fn new(pnp: bool) -> napi::Result { 25 | let input_filesystem = Arc::new(NativeFileSystem::new(pnp)); 26 | Ok(Self { 27 | resolver_factory: None, 28 | loader_resolver_factory: None, 29 | input_filesystem, 30 | }) 31 | } 32 | 33 | pub fn get_resolver_factory(&mut self, resolve_options: Resolve) -> Arc { 34 | match &self.resolver_factory { 35 | Some(resolver_factory) => resolver_factory.clone(), 36 | 37 | None => { 38 | let resolver_factory = Arc::new(ResolverFactory::new( 39 | resolve_options, 40 | self.input_filesystem.clone(), 41 | )); 42 | self.resolver_factory = Some(resolver_factory.clone()); 43 | resolver_factory 44 | } 45 | } 46 | } 47 | 48 | pub fn get_loader_resolver_factory(&mut self, resolve_options: Resolve) -> Arc { 49 | match &self.loader_resolver_factory { 50 | Some(resolver_factory) => resolver_factory.clone(), 51 | None => { 52 | let resolver_factory = Arc::new(ResolverFactory::new( 53 | resolve_options, 54 | self.input_filesystem.clone(), 55 | )); 56 | self.loader_resolver_factory = Some(resolver_factory.clone()); 57 | resolver_factory 58 | } 59 | } 60 | } 61 | 62 | #[napi(ts_args_type = "type: string, options?: RawResolveOptionsWithDependencyType")] 63 | pub fn get( 64 | &mut self, 65 | r#type: String, 66 | raw: Option, 67 | ) -> napi::Result { 68 | match r#type.as_str() { 69 | "normal" => { 70 | let options = normalize_raw_resolve_options_with_dependency_type(raw, false).map_err(|e| napi::Error::from_reason(format!("{e}")))?; 71 | let resolver_factory = self.get_resolver_factory(*options.resolve_options.clone().unwrap_or_default()); 72 | Ok(JsResolver::new(resolver_factory, options)) 73 | } 74 | "loader" => { 75 | let options = normalize_raw_resolve_options_with_dependency_type(raw, false).map_err(|e| napi::Error::from_reason(format!("{e}")))?; 76 | let resolver_factory = self.get_loader_resolver_factory(*options.resolve_options.clone().unwrap_or_default()); 77 | Ok(JsResolver::new(resolver_factory, options)) 78 | } 79 | "context" => { 80 | let options = normalize_raw_resolve_options_with_dependency_type(raw, true).map_err(|e| napi::Error::from_reason(format!("{e}")))?; 81 | let resolver_factory = self.get_resolver_factory(*options.resolve_options.clone().unwrap_or_default()); 82 | Ok(JsResolver::new(resolver_factory, options)) 83 | } 84 | _ => { 85 | Err(napi::Error::from_reason(format!("Invalid resolver type '{}' specified. Rspack only supports 'normal', 'context', and 'loader' types.", r#type))) 86 | } 87 | } 88 | } 89 | } 90 | -------------------------------------------------------------------------------- /crates/binding_values/src/exports_info.rs: -------------------------------------------------------------------------------- 1 | use std::{ptr::NonNull, sync::Arc}; 2 | 3 | use napi::Either; 4 | use napi_derive::napi; 5 | use rspack_core::{Compilation, ExportsInfo, ModuleGraph, RuntimeSpec, UsedName}; 6 | 7 | use crate::JsRuntimeSpec; 8 | 9 | #[napi] 10 | pub struct JsExportsInfo { 11 | exports_info: ExportsInfo, 12 | compilation: NonNull, 13 | } 14 | 15 | impl JsExportsInfo { 16 | pub fn new(exports_info: ExportsInfo, compilation: &Compilation) -> Self { 17 | #[allow(clippy::unwrap_used)] 18 | Self { 19 | exports_info, 20 | compilation: NonNull::new(compilation as *const Compilation as *mut Compilation).unwrap(), 21 | } 22 | } 23 | 24 | fn as_ref(&self) -> napi::Result> { 25 | let compilation = unsafe { self.compilation.as_ref() }; 26 | let module_graph = compilation.get_module_graph(); 27 | Ok(module_graph) 28 | } 29 | 30 | fn as_mut(&mut self) -> napi::Result> { 31 | let compilation = unsafe { self.compilation.as_mut() }; 32 | let module_graph = compilation.get_module_graph_mut(); 33 | Ok(module_graph) 34 | } 35 | } 36 | 37 | #[napi] 38 | impl JsExportsInfo { 39 | #[napi(ts_args_type = "runtime: string | string[] | undefined")] 40 | pub fn is_used(&self, js_runtime: Option) -> napi::Result { 41 | let module_graph = self.as_ref()?; 42 | let runtime: Option = js_runtime.map(|js_rt| match js_rt { 43 | Either::A(str) => vec![str].into_iter().map(Arc::from).collect(), 44 | Either::B(vec) => vec.into_iter().map(Arc::from).collect(), 45 | }); 46 | Ok(self.exports_info.is_used(&module_graph, runtime.as_ref())) 47 | } 48 | 49 | #[napi(ts_args_type = "runtime: string | string[] | undefined")] 50 | pub fn is_module_used(&self, js_runtime: Option) -> napi::Result { 51 | let module_graph = self.as_ref()?; 52 | let runtime: Option = js_runtime.map(|js_rt| match js_rt { 53 | Either::A(str) => vec![str].into_iter().map(Arc::from).collect(), 54 | Either::B(vec) => vec.into_iter().map(Arc::from).collect(), 55 | }); 56 | Ok( 57 | self 58 | .exports_info 59 | .is_module_used(&module_graph, runtime.as_ref()), 60 | ) 61 | } 62 | 63 | #[napi(ts_args_type = "runtime: string | string[] | undefined")] 64 | pub fn set_used_in_unknown_way( 65 | &mut self, 66 | js_runtime: Option, 67 | ) -> napi::Result { 68 | let mut module_graph = self.as_mut()?; 69 | let runtime: Option = js_runtime.map(|js_rt| match js_rt { 70 | Either::A(str) => vec![str].into_iter().map(Arc::from).collect(), 71 | Either::B(vec) => vec.into_iter().map(Arc::from).collect(), 72 | }); 73 | Ok( 74 | self 75 | .exports_info 76 | .set_used_in_unknown_way(&mut module_graph, runtime.as_ref()), 77 | ) 78 | } 79 | 80 | #[napi( 81 | ts_args_type = "name: string | string[], runtime: string | string[] | undefined", 82 | ts_return_type = " 0 | 1 | 2 | 3 | 4" 83 | )] 84 | pub fn get_used( 85 | &self, 86 | js_name: Either>, 87 | js_runtime: Option, 88 | ) -> napi::Result { 89 | let module_graph = self.as_ref()?; 90 | let name = match js_name { 91 | Either::A(s) => UsedName::Str(s.into()), 92 | Either::B(v) => UsedName::Vec(v.into_iter().map(Into::into).collect::>()), 93 | }; 94 | let runtime: Option = js_runtime.map(|js_rt| match js_rt { 95 | Either::A(str) => vec![str].into_iter().map(Arc::from).collect(), 96 | Either::B(vec) => vec.into_iter().map(Arc::from).collect(), 97 | }); 98 | Ok( 99 | self 100 | .exports_info 101 | .get_used(&module_graph, name, runtime.as_ref()) as u32, 102 | ) 103 | } 104 | } 105 | -------------------------------------------------------------------------------- /crates/binding_values/src/plugins/js_loader/scheduler.rs: -------------------------------------------------------------------------------- 1 | use napi::Either; 2 | use rspack_core::{ 3 | diagnostics::CapturedLoaderError, AdditionalData, LoaderContext, NormalModuleLoaderShouldYield, 4 | NormalModuleLoaderStartYielding, RunnerContext, BUILTIN_LOADER_PREFIX, 5 | }; 6 | use rspack_error::{error, Result}; 7 | use rspack_hook::plugin_hook; 8 | use rspack_loader_runner::State as LoaderState; 9 | 10 | use super::{JsLoaderContext, JsLoaderRspackPlugin, JsLoaderRspackPluginInner}; 11 | 12 | #[plugin_hook(NormalModuleLoaderShouldYield for JsLoaderRspackPlugin)] 13 | pub(crate) fn loader_should_yield( 14 | &self, 15 | loader_context: &LoaderContext, 16 | ) -> Result> { 17 | match loader_context.state() { 18 | s @ LoaderState::Init | s @ LoaderState::ProcessResource | s @ LoaderState::Finished => { 19 | panic!("Unexpected loader runner state: {s:?}") 20 | } 21 | LoaderState::Pitching | LoaderState::Normal => Ok(Some( 22 | !loader_context 23 | .current_loader() 24 | .request() 25 | .starts_with(BUILTIN_LOADER_PREFIX), 26 | )), 27 | } 28 | } 29 | 30 | #[plugin_hook(NormalModuleLoaderStartYielding for JsLoaderRspackPlugin)] 31 | pub(crate) async fn loader_yield( 32 | &self, 33 | loader_context: &mut LoaderContext, 34 | ) -> Result<()> { 35 | let new_cx = self 36 | .runner 37 | .call_with_promise(loader_context.try_into()?) 38 | .await?; 39 | merge_loader_context(loader_context, new_cx)?; 40 | Ok(()) 41 | } 42 | 43 | pub(crate) fn merge_loader_context( 44 | to: &mut LoaderContext, 45 | mut from: JsLoaderContext, 46 | ) -> Result<()> { 47 | if let Some(error) = from.error { 48 | return Err( 49 | CapturedLoaderError::new( 50 | error.message, 51 | error.stack, 52 | error.hide_stack, 53 | from.file_dependencies, 54 | from.context_dependencies, 55 | from.missing_dependencies, 56 | from.build_dependencies, 57 | ) 58 | .into(), 59 | ); 60 | } 61 | 62 | to.cacheable = from.cacheable; 63 | to.file_dependencies = from.file_dependencies.into_iter().map(Into::into).collect(); 64 | to.context_dependencies = from 65 | .context_dependencies 66 | .into_iter() 67 | .map(Into::into) 68 | .collect(); 69 | to.missing_dependencies = from 70 | .missing_dependencies 71 | .into_iter() 72 | .map(Into::into) 73 | .collect(); 74 | to.build_dependencies = from 75 | .build_dependencies 76 | .into_iter() 77 | .map(Into::into) 78 | .collect(); 79 | 80 | let content = match from.content { 81 | Either::A(_) => None, 82 | Either::B(c) => Some(rspack_core::Content::from(Into::>::into(c))), 83 | }; 84 | let source_map = from 85 | .source_map 86 | .as_ref() 87 | .map(|s| rspack_core::rspack_sources::SourceMap::from_slice(s)) 88 | .transpose() 89 | .map_err(|e| error!(e.to_string()))?; 90 | let additional_data = from.additional_data.take().map(|data| { 91 | let mut additional = AdditionalData::default(); 92 | additional.insert(data); 93 | additional 94 | }); 95 | to.__finish_with((content, source_map, additional_data)); 96 | 97 | // update loader status 98 | to.loader_items = to 99 | .loader_items 100 | .drain(..) 101 | .zip(from.loader_items.drain(..)) 102 | .map(|(mut to, from)| { 103 | if from.normal_executed { 104 | to.set_normal_executed() 105 | } 106 | if from.pitch_executed { 107 | to.set_pitch_executed() 108 | } 109 | to.set_data(from.data); 110 | // JS loader should always be considered as finished 111 | to.set_finish_called(); 112 | to 113 | }) 114 | .collect(); 115 | to.loader_index = from.loader_index; 116 | to.parse_meta = from.parse_meta.into_iter().collect(); 117 | 118 | Ok(()) 119 | } 120 | -------------------------------------------------------------------------------- /crates/binding_values/src/raw_options/raw_builtins/raw_lazy_compilation.rs: -------------------------------------------------------------------------------- 1 | use napi::{ 2 | bindgen_prelude::{FromNapiValue, ToNapiValue, ValidateNapiValue}, 3 | Either, 4 | }; 5 | use napi_derive::napi; 6 | use rspack_core::{CompilationId, ModuleIdentifier}; 7 | use rspack_napi::threadsafe_function::ThreadsafeFunction; 8 | use rspack_plugin_lazy_compilation::{ 9 | backend::{Backend, ModuleInfo}, 10 | plugin::{LazyCompilationTest, LazyCompilationTestCheck}, 11 | }; 12 | use rspack_regex::RspackRegex; 13 | 14 | use crate::JsModuleWrapper; 15 | 16 | #[derive(Debug)] 17 | pub struct RawLazyCompilationTest>>( 18 | pub Either, 19 | ); 20 | 21 | impl FromNapiValue for RawLazyCompilationTest { 22 | unsafe fn from_napi_value( 23 | env: napi::sys::napi_env, 24 | napi_val: napi::sys::napi_value, 25 | ) -> napi::Result { 26 | Ok(Self(Either::from_napi_value(env, napi_val)?)) 27 | } 28 | } 29 | 30 | impl ToNapiValue for RawLazyCompilationTest { 31 | unsafe fn to_napi_value( 32 | env: napi::sys::napi_env, 33 | val: Self, 34 | ) -> napi::Result { 35 | Either::to_napi_value(env, val.0) 36 | } 37 | } 38 | 39 | #[derive(Debug)] 40 | pub struct LazyCompilationTestFn { 41 | tsfn: ThreadsafeFunction>, 42 | } 43 | 44 | impl LazyCompilationTestCheck for LazyCompilationTestFn { 45 | fn test(&self, compilation_id: CompilationId, m: &dyn rspack_core::Module) -> bool { 46 | let res = self 47 | .tsfn 48 | .blocking_call_with_sync(JsModuleWrapper::new(m, compilation_id, None)) 49 | .expect("failed to invoke lazyCompilation.test"); 50 | 51 | res.unwrap_or(false) 52 | } 53 | } 54 | 55 | impl From for LazyCompilationTest { 56 | fn from(value: RawLazyCompilationTest) -> Self { 57 | match value.0 { 58 | Either::A(regex) => Self::Regex( 59 | RspackRegex::with_flags(®ex.source, ®ex.flags).unwrap_or_else(|_| { 60 | let msg = format!("[lazyCompilation]incorrect regex {:?}", regex); 61 | panic!("{msg}"); 62 | }), 63 | ), 64 | Either::B(tsfn) => Self::Fn(LazyCompilationTestFn { tsfn }), 65 | } 66 | } 67 | } 68 | 69 | #[napi(object)] 70 | pub struct RawModuleInfo { 71 | pub active: bool, 72 | pub client: String, 73 | pub data: String, 74 | } 75 | 76 | #[napi(object, object_to_js = false)] 77 | pub struct RawLazyCompilationOption { 78 | pub module: ThreadsafeFunction, 79 | pub test: Option, 80 | pub entries: bool, 81 | pub imports: bool, 82 | pub cacheable: bool, 83 | } 84 | 85 | #[napi(object)] 86 | pub struct RawModuleArg { 87 | pub module: String, 88 | pub path: String, 89 | } 90 | 91 | pub(crate) struct JsBackend { 92 | module: ThreadsafeFunction, 93 | } 94 | 95 | impl std::fmt::Debug for JsBackend { 96 | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { 97 | f.debug_struct("JsBackend").finish() 98 | } 99 | } 100 | 101 | impl From<&RawLazyCompilationOption> for JsBackend { 102 | fn from(value: &RawLazyCompilationOption) -> Self { 103 | Self { 104 | module: value.module.clone(), 105 | } 106 | } 107 | } 108 | 109 | #[async_trait::async_trait] 110 | impl Backend for JsBackend { 111 | async fn module( 112 | &mut self, 113 | identifier: ModuleIdentifier, 114 | path: String, 115 | ) -> rspack_error::Result { 116 | let module_info = self 117 | .module 118 | .call(RawModuleArg { 119 | module: identifier.to_string(), 120 | path, 121 | }) 122 | .await 123 | .expect("channel should have result"); 124 | 125 | Ok(ModuleInfo { 126 | active: module_info.active, 127 | client: module_info.client, 128 | data: module_info.data, 129 | }) 130 | } 131 | } 132 | -------------------------------------------------------------------------------- /crates/binding_values/src/filename.rs: -------------------------------------------------------------------------------- 1 | use std::fmt::Debug; 2 | use std::sync::Arc; 3 | 4 | use napi::{ 5 | bindgen_prelude::{FromNapiValue, Function, ToNapiValue, ValidateNapiValue}, 6 | Either, 7 | }; 8 | use rspack_core::{AssetInfo, LocalFilenameFn, PathData, PublicPath}; 9 | use rspack_core::{Filename, FilenameFn}; 10 | use rspack_napi::threadsafe_function::ThreadsafeFunction; 11 | use serde::Deserialize; 12 | 13 | use crate::{JsAssetInfo, JsPathData}; 14 | 15 | /// A js filename value. Either a string or a function 16 | /// 17 | /// The function type is generic. By default the function type is tsfn. 18 | #[derive(Debug)] 19 | pub struct JsFilename), String>>( 20 | Either, 21 | ); 22 | 23 | /// A local js filename value. Only valid in the current native call. 24 | /// 25 | /// Useful as the type of a parameter that is invoked immediately inside the function. 26 | pub type LocalJsFilename<'f> = JsFilename), String>>; 27 | 28 | impl<'f> From> for Filename> { 29 | fn from(value: LocalJsFilename<'f>) -> Self { 30 | match value.0 { 31 | Either::A(template) => Filename::from(template), 32 | Either::B(js_func) => Filename::from_fn(LocalJsFilenameFn(js_func)), 33 | } 34 | } 35 | } 36 | impl From for Filename { 37 | fn from(value: JsFilename) -> Self { 38 | match value.0 { 39 | Either::A(template) => Filename::from(template), 40 | Either::B(theadsafe_filename_fn) => { 41 | Filename::from_fn(Arc::new(ThreadSafeFilenameFn(theadsafe_filename_fn))) 42 | } 43 | } 44 | } 45 | } 46 | 47 | impl From for PublicPath { 48 | fn from(value: JsFilename) -> Self { 49 | match value.0 { 50 | Either::A(template) => template.into(), 51 | Either::B(theadsafe_filename_fn) => PublicPath::Filename(Filename::from_fn(Arc::new( 52 | ThreadSafeFilenameFn(theadsafe_filename_fn), 53 | ))), 54 | } 55 | } 56 | } 57 | 58 | impl FromNapiValue for JsFilename { 59 | unsafe fn from_napi_value( 60 | env: napi::sys::napi_env, 61 | napi_val: napi::sys::napi_value, 62 | ) -> napi::Result { 63 | Ok(Self(Either::from_napi_value(env, napi_val)?)) 64 | } 65 | } 66 | 67 | impl ToNapiValue for JsFilename { 68 | unsafe fn to_napi_value( 69 | env: napi::sys::napi_env, 70 | val: Self, 71 | ) -> napi::Result { 72 | Either::to_napi_value(env, val.0) 73 | } 74 | } 75 | 76 | impl<'de> Deserialize<'de> for JsFilename { 77 | fn deserialize(deserializer: D) -> Result 78 | where 79 | D: serde::Deserializer<'de>, 80 | { 81 | Ok(Self(Either::A(String::deserialize(deserializer)?))) 82 | } 83 | } 84 | 85 | /// Wrapper of a thread-safe filename js function. Implements `FilenameFn` 86 | #[derive(Debug)] 87 | struct ThreadSafeFilenameFn(ThreadsafeFunction<(JsPathData, Option), String>); 88 | impl LocalFilenameFn for ThreadSafeFilenameFn { 89 | type Error = rspack_error::Error; 90 | fn call( 91 | &self, 92 | path_data: &PathData, 93 | asset_info: Option<&AssetInfo>, 94 | ) -> rspack_error::Result { 95 | self.0.blocking_call_with_sync(( 96 | JsPathData::from_path_data(*path_data), 97 | asset_info.cloned().map(JsAssetInfo::from), 98 | )) 99 | } 100 | } 101 | impl FilenameFn for ThreadSafeFilenameFn {} 102 | 103 | /// Wrapper of a local filename js function. Implements `LocalFilenameFn`. Only valid in the current native call. 104 | pub struct LocalJsFilenameFn<'f>(Function<'f, (JsPathData, Option), String>); 105 | 106 | impl LocalFilenameFn for LocalJsFilenameFn<'_> { 107 | type Error = napi::Error; 108 | 109 | fn call( 110 | &self, 111 | path_data: &PathData, 112 | asset_info: Option<&AssetInfo>, 113 | ) -> Result { 114 | let js_path_data = JsPathData::from_path_data(*path_data); 115 | let js_asset_info = asset_info.cloned().map(JsAssetInfo::from); 116 | self.0.call((js_path_data, js_asset_info)) 117 | } 118 | } 119 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Created by https://www.toptal.com/developers/gitignore/api/node 2 | # Edit at https://www.toptal.com/developers/gitignore?templates=node 3 | 4 | ### Node ### 5 | # Logs 6 | logs 7 | *.log 8 | npm-debug.log* 9 | yarn-debug.log* 10 | yarn-error.log* 11 | lerna-debug.log* 12 | 13 | # Diagnostic reports (https://nodejs.org/api/report.html) 14 | report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json 15 | 16 | # Runtime data 17 | pids 18 | *.pid 19 | *.seed 20 | *.pid.lock 21 | 22 | # Directory for instrumented libs generated by jscoverage/JSCover 23 | lib-cov 24 | 25 | # Coverage directory used by tools like istanbul 26 | coverage 27 | *.lcov 28 | 29 | # nyc test coverage 30 | .nyc_output 31 | 32 | # Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files) 33 | .grunt 34 | 35 | # Bower dependency directory (https://bower.io/) 36 | bower_components 37 | 38 | # node-waf configuration 39 | .lock-wscript 40 | 41 | # Compiled binary addons (https://nodejs.org/api/addons.html) 42 | build/Release 43 | 44 | # Dependency directories 45 | node_modules/ 46 | jspm_packages/ 47 | 48 | # TypeScript v1 declaration files 49 | typings/ 50 | 51 | # TypeScript cache 52 | *.tsbuildinfo 53 | 54 | # Optional npm cache directory 55 | .npm 56 | 57 | # Optional eslint cache 58 | .eslintcache 59 | 60 | # Microbundle cache 61 | .rpt2_cache/ 62 | .rts2_cache_cjs/ 63 | .rts2_cache_es/ 64 | .rts2_cache_umd/ 65 | 66 | # Optional REPL history 67 | .node_repl_history 68 | 69 | # Output of 'npm pack' 70 | *.tgz 71 | 72 | # Yarn Integrity file 73 | .yarn-integrity 74 | 75 | # dotenv environment variables file 76 | .env 77 | .env.test 78 | 79 | # parcel-bundler cache (https://parceljs.org/) 80 | .cache 81 | 82 | # Next.js build output 83 | .next 84 | 85 | # Nuxt.js build / generate output 86 | .nuxt 87 | dist 88 | 89 | # Gatsby files 90 | .cache/ 91 | # Comment in the public line in if your project uses Gatsby and not Next.js 92 | # https://nextjs.org/blog/next-9-1#public-directory-support 93 | # public 94 | 95 | # vuepress build output 96 | .vuepress/dist 97 | 98 | # Serverless directories 99 | .serverless/ 100 | 101 | # FuseBox cache 102 | .fusebox/ 103 | 104 | # DynamoDB Local files 105 | .dynamodb/ 106 | 107 | # TernJS port file 108 | .tern-port 109 | 110 | # Stores VSCode versions used for testing VSCode extensions 111 | .vscode-test 112 | 113 | # End of https://www.toptal.com/developers/gitignore/api/node 114 | 115 | # Created by https://www.toptal.com/developers/gitignore/api/macos 116 | # Edit at https://www.toptal.com/developers/gitignore?templates=macos 117 | 118 | ### macOS ### 119 | # General 120 | .DS_Store 121 | .AppleDouble 122 | .LSOverride 123 | 124 | # Icon must end with two 125 | Icon 126 | 127 | 128 | # Thumbnails 129 | ._* 130 | 131 | # Files that might appear in the root of a volume 132 | .DocumentRevisions-V100 133 | .fseventsd 134 | .Spotlight-V100 135 | .TemporaryItems 136 | .Trashes 137 | .VolumeIcon.icns 138 | .com.apple.timemachine.donotpresent 139 | 140 | # Directories potentially created on remote AFP share 141 | .AppleDB 142 | .AppleDesktop 143 | Network Trash Folder 144 | Temporary Items 145 | .apdisk 146 | 147 | ### macOS Patch ### 148 | # iCloud generated files 149 | *.icloud 150 | 151 | # End of https://www.toptal.com/developers/gitignore/api/macos 152 | 153 | # Created by https://www.toptal.com/developers/gitignore/api/windows 154 | # Edit at https://www.toptal.com/developers/gitignore?templates=windows 155 | 156 | ### Windows ### 157 | # Windows thumbnail cache files 158 | Thumbs.db 159 | Thumbs.db:encryptable 160 | ehthumbs.db 161 | ehthumbs_vista.db 162 | 163 | # Dump file 164 | *.stackdump 165 | 166 | # Folder config file 167 | [Dd]esktop.ini 168 | 169 | # Recycle Bin used on file shares 170 | $RECYCLE.BIN/ 171 | 172 | # Windows Installer files 173 | *.cab 174 | *.msi 175 | *.msix 176 | *.msm 177 | *.msp 178 | 179 | # Windows shortcuts 180 | *.lnk 181 | 182 | # End of https://www.toptal.com/developers/gitignore/api/windows 183 | 184 | #Added by cargo 185 | 186 | /target 187 | 188 | .pnp.* 189 | .yarn/* 190 | !.yarn/patches 191 | !.yarn/plugins 192 | !.yarn/releases 193 | !.yarn/sdks 194 | !.yarn/versions 195 | 196 | *.node 197 | 198 | .rspack_crates/ 199 | node_modules/ 200 | .idea/ 201 | -------------------------------------------------------------------------------- /crates/binding_values/src/raw_options/raw_builtins/raw_lightning_css_minimizer.rs: -------------------------------------------------------------------------------- 1 | use napi_derive::napi; 2 | use rspack_error::Result; 3 | use rspack_plugin_lightning_css_minimizer::{ 4 | Draft, MinimizerOptions, NonStandard, PluginOptions, PseudoClasses, 5 | }; 6 | 7 | use crate::{into_asset_conditions, RawAssetConditions}; 8 | 9 | #[derive(Debug)] 10 | #[napi(object)] 11 | pub struct RawLightningCssMinimizerRspackPluginOptions { 12 | #[napi(ts_type = "string | RegExp | (string | RegExp)[]")] 13 | pub test: Option, 14 | #[napi(ts_type = "string | RegExp | (string | RegExp)[]")] 15 | pub include: Option, 16 | #[napi(ts_type = "string | RegExp | (string | RegExp)[]")] 17 | pub exclude: Option, 18 | pub remove_unused_local_idents: bool, 19 | pub minimizer_options: RawLightningCssMinimizerOptions, 20 | } 21 | 22 | #[derive(Debug)] 23 | #[napi(object)] 24 | pub struct RawLightningCssMinimizerOptions { 25 | pub error_recovery: bool, 26 | pub targets: Option>, 27 | pub include: Option, 28 | pub exclude: Option, 29 | // TODO: deprecate `draft` in favor of `drafts` 30 | pub draft: Option, 31 | pub drafts: Option, 32 | pub non_standard: Option, 33 | pub pseudo_classes: Option, 34 | pub unused_symbols: Vec, 35 | } 36 | 37 | #[derive(Debug)] 38 | #[napi(object)] 39 | pub struct RawLightningCssBrowsers { 40 | pub android: Option, 41 | pub chrome: Option, 42 | pub edge: Option, 43 | pub firefox: Option, 44 | pub ie: Option, 45 | #[napi(js_name = "ios_saf")] 46 | pub ios_saf: Option, 47 | pub opera: Option, 48 | pub safari: Option, 49 | pub samsung: Option, 50 | } 51 | 52 | #[derive(Debug)] 53 | #[napi(object)] 54 | pub struct RawDraft { 55 | pub custom_media: bool, 56 | } 57 | 58 | #[derive(Debug)] 59 | #[napi(object)] 60 | pub struct RawNonStandard { 61 | pub deep_selector_combinator: bool, 62 | } 63 | 64 | #[derive(Debug)] 65 | #[napi(object)] 66 | pub struct RawLightningCssPseudoClasses { 67 | pub hover: Option, 68 | pub active: Option, 69 | pub focus: Option, 70 | pub focus_visible: Option, 71 | pub focus_within: Option, 72 | } 73 | 74 | impl TryFrom for PluginOptions { 75 | type Error = rspack_error::Error; 76 | 77 | fn try_from(value: RawLightningCssMinimizerRspackPluginOptions) -> Result { 78 | Ok(Self { 79 | test: value.test.map(into_asset_conditions), 80 | include: value.include.map(into_asset_conditions), 81 | exclude: value.exclude.map(into_asset_conditions), 82 | remove_unused_local_idents: value.remove_unused_local_idents, 83 | minimizer_options: MinimizerOptions { 84 | error_recovery: value.minimizer_options.error_recovery, 85 | targets: value 86 | .minimizer_options 87 | .targets 88 | .map(|t| { 89 | rspack_loader_lightningcss::lightningcss::targets::Browsers::from_browserslist(t) 90 | }) 91 | .transpose() 92 | .map_err(|e| rspack_error::error!("Failed to parse browserslist: {}", e))? 93 | .flatten(), 94 | include: value.minimizer_options.include, 95 | exclude: value.minimizer_options.exclude, 96 | // We should use `drafts` if it is present, otherwise use `draft` 97 | draft: value 98 | .minimizer_options 99 | .drafts 100 | .or(value.minimizer_options.draft) 101 | .map(|d| Draft { 102 | custom_media: d.custom_media, 103 | }), 104 | non_standard: value.minimizer_options.non_standard.map(|n| NonStandard { 105 | deep_selector_combinator: n.deep_selector_combinator, 106 | }), 107 | pseudo_classes: value 108 | .minimizer_options 109 | .pseudo_classes 110 | .map(|p| PseudoClasses { 111 | hover: p.hover, 112 | active: p.active, 113 | focus: p.focus, 114 | focus_visible: p.focus_visible, 115 | focus_within: p.focus_within, 116 | }), 117 | unused_symbols: value.minimizer_options.unused_symbols, 118 | }, 119 | }) 120 | } 121 | } 122 | -------------------------------------------------------------------------------- /crates/binding_values/src/chunk_graph.rs: -------------------------------------------------------------------------------- 1 | use std::ptr::NonNull; 2 | 3 | use napi::Result; 4 | use napi_derive::napi; 5 | use rspack_core::{ChunkGraph, Compilation, SourceType}; 6 | 7 | use crate::{JsChunk, JsChunkWrapper, JsModule, JsModuleWrapper}; 8 | 9 | #[napi] 10 | pub struct JsChunkGraph { 11 | compilation: NonNull, 12 | } 13 | 14 | impl JsChunkGraph { 15 | pub fn new(compilation: &Compilation) -> Self { 16 | #[allow(clippy::unwrap_used)] 17 | JsChunkGraph { 18 | compilation: NonNull::new(compilation as *const Compilation as *mut Compilation).unwrap(), 19 | } 20 | } 21 | 22 | fn as_ref(&self) -> Result<&'static Compilation> { 23 | let compilation = unsafe { self.compilation.as_ref() }; 24 | Ok(compilation) 25 | } 26 | } 27 | 28 | #[napi] 29 | impl JsChunkGraph { 30 | #[napi(ts_return_type = "JsModule[]")] 31 | pub fn get_chunk_modules(&self, chunk: &JsChunk) -> Result> { 32 | let compilation = self.as_ref()?; 33 | 34 | let module_graph = compilation.get_module_graph(); 35 | let modules = compilation 36 | .chunk_graph 37 | .get_chunk_modules(&chunk.chunk_ukey, &module_graph); 38 | 39 | Ok( 40 | modules 41 | .iter() 42 | .map(|module| JsModuleWrapper::new(module.as_ref(), compilation.id(), Some(compilation))) 43 | .collect::>(), 44 | ) 45 | } 46 | 47 | #[napi(ts_return_type = "JsModule[]")] 48 | pub fn get_chunk_entry_modules(&self, chunk: &JsChunk) -> Result> { 49 | let compilation = self.as_ref()?; 50 | 51 | let modules = compilation 52 | .chunk_graph 53 | .get_chunk_entry_modules(&chunk.chunk_ukey); 54 | let module_graph = compilation.get_module_graph(); 55 | Ok( 56 | modules 57 | .iter() 58 | .filter_map(|module| module_graph.module_by_identifier(module)) 59 | .map(|module| JsModuleWrapper::new(module.as_ref(), compilation.id(), Some(compilation))) 60 | .collect::>(), 61 | ) 62 | } 63 | 64 | #[napi(ts_return_type = "number")] 65 | pub fn get_number_of_entry_modules(&self, chunk: &JsChunk) -> Result { 66 | let compilation = self.as_ref()?; 67 | 68 | Ok( 69 | compilation 70 | .chunk_graph 71 | .get_number_of_entry_modules(&chunk.chunk_ukey) as u32, 72 | ) 73 | } 74 | 75 | #[napi(ts_return_type = "JsChunk[]")] 76 | pub fn get_chunk_entry_dependent_chunks_iterable( 77 | &self, 78 | chunk: &JsChunk, 79 | ) -> Result> { 80 | let compilation = self.as_ref()?; 81 | 82 | let chunks = compilation 83 | .chunk_graph 84 | .get_chunk_entry_dependent_chunks_iterable( 85 | &chunk.chunk_ukey, 86 | &compilation.chunk_by_ukey, 87 | &compilation.chunk_group_by_ukey, 88 | ); 89 | 90 | Ok( 91 | chunks 92 | .into_iter() 93 | .map(|c| JsChunkWrapper::new(c, compilation)) 94 | .collect::>(), 95 | ) 96 | } 97 | 98 | #[napi(ts_return_type = "JsModule[]")] 99 | pub fn get_chunk_modules_iterable_by_source_type( 100 | &self, 101 | chunk: &JsChunk, 102 | source_type: String, 103 | ) -> Result> { 104 | let compilation = self.as_ref()?; 105 | 106 | Ok( 107 | compilation 108 | .chunk_graph 109 | .get_chunk_modules_iterable_by_source_type( 110 | &chunk.chunk_ukey, 111 | SourceType::from(source_type.as_str()), 112 | &compilation.get_module_graph(), 113 | ) 114 | .map(|module| JsModuleWrapper::new(module, compilation.id(), Some(compilation))) 115 | .collect(), 116 | ) 117 | } 118 | 119 | #[napi(ts_return_type = "JsChunk[]")] 120 | pub fn get_module_chunks(&self, module: &JsModule) -> Result> { 121 | let compilation = self.as_ref()?; 122 | 123 | Ok( 124 | compilation 125 | .chunk_graph 126 | .get_module_chunks(module.identifier) 127 | .iter() 128 | .map(|chunk| JsChunkWrapper::new(*chunk, compilation)) 129 | .collect(), 130 | ) 131 | } 132 | 133 | #[napi] 134 | pub fn get_module_id(&self, js_module: &JsModule) -> napi::Result> { 135 | let compilation = self.as_ref()?; 136 | Ok( 137 | ChunkGraph::get_module_id(&compilation.module_ids_artifact, js_module.identifier) 138 | .map(|module_id| module_id.as_str()), 139 | ) 140 | } 141 | } 142 | -------------------------------------------------------------------------------- /crates/binding_values/src/dependency_block.rs: -------------------------------------------------------------------------------- 1 | use std::{cell::RefCell, ptr::NonNull}; 2 | 3 | use napi_derive::napi; 4 | use rspack_core::{ 5 | AsyncDependenciesBlock, AsyncDependenciesBlockIdentifier, Compilation, CompilationId, 6 | DependenciesBlock, 7 | }; 8 | use rspack_napi::{napi::bindgen_prelude::*, OneShotRef}; 9 | use rustc_hash::FxHashMap as HashMap; 10 | 11 | use crate::JsDependencyWrapper; 12 | 13 | #[napi] 14 | pub struct JsDependenciesBlock { 15 | block_id: AsyncDependenciesBlockIdentifier, 16 | compilation: NonNull, 17 | } 18 | 19 | #[napi] 20 | impl JsDependenciesBlock { 21 | #[napi(getter, ts_return_type = "JsDependency[]")] 22 | pub fn dependencies(&mut self) -> Vec { 23 | let compilation = unsafe { self.compilation.as_ref() }; 24 | let module_graph = compilation.get_module_graph(); 25 | if let Some(block) = module_graph.block_by_id(&self.block_id) { 26 | block 27 | .get_dependencies() 28 | .iter() 29 | .filter_map(|dependency_id| { 30 | module_graph 31 | .dependency_by_id(dependency_id) 32 | .map(|dep| JsDependencyWrapper::new(dep.as_ref(), compilation.id(), Some(compilation))) 33 | }) 34 | .collect::>() 35 | } else { 36 | vec![] 37 | } 38 | } 39 | 40 | #[napi(getter, ts_return_type = "JsDependenciesBlock[]")] 41 | pub fn blocks(&mut self) -> Vec { 42 | let compilation = unsafe { self.compilation.as_ref() }; 43 | let module_graph = compilation.get_module_graph(); 44 | if let Some(block) = module_graph.block_by_id(&self.block_id) { 45 | block 46 | .get_blocks() 47 | .iter() 48 | .filter_map(|block_id| { 49 | module_graph 50 | .block_by_id(block_id) 51 | .map(|block| JsDependenciesBlockWrapper::new(block, compilation)) 52 | }) 53 | .collect::>() 54 | } else { 55 | vec![] 56 | } 57 | } 58 | } 59 | 60 | type BlockInstanceRefs = HashMap>; 61 | 62 | type BlockInstanceRefsByCompilationId = RefCell>; 63 | 64 | thread_local! { 65 | static BLOCK_INSTANCE_REFS: BlockInstanceRefsByCompilationId = Default::default(); 66 | } 67 | 68 | pub struct JsDependenciesBlockWrapper { 69 | block_id: AsyncDependenciesBlockIdentifier, 70 | compilation: NonNull, 71 | } 72 | 73 | impl JsDependenciesBlockWrapper { 74 | pub fn new(block: &AsyncDependenciesBlock, compilation: &Compilation) -> Self { 75 | let block_id = block.identifier(); 76 | 77 | #[allow(clippy::unwrap_used)] 78 | Self { 79 | block_id, 80 | compilation: NonNull::new(compilation as *const Compilation as *mut Compilation).unwrap(), 81 | } 82 | } 83 | 84 | pub fn cleanup_last_compilation(compilation_id: CompilationId) { 85 | BLOCK_INSTANCE_REFS.with(|refs| { 86 | let mut refs_by_compilation_id = refs.borrow_mut(); 87 | refs_by_compilation_id.remove(&compilation_id) 88 | }); 89 | } 90 | } 91 | 92 | impl ToNapiValue for JsDependenciesBlockWrapper { 93 | unsafe fn to_napi_value( 94 | env: napi::sys::napi_env, 95 | val: Self, 96 | ) -> napi::Result { 97 | BLOCK_INSTANCE_REFS.with(|refs| { 98 | let compilation = unsafe { val.compilation.as_ref() }; 99 | let mut refs_by_compilation_id = refs.borrow_mut(); 100 | let entry = refs_by_compilation_id.entry(compilation.id()); 101 | let refs = match entry { 102 | std::collections::hash_map::Entry::Occupied(entry) => entry.into_mut(), 103 | std::collections::hash_map::Entry::Vacant(entry) => { 104 | let refs = HashMap::default(); 105 | entry.insert(refs) 106 | } 107 | }; 108 | 109 | match refs.entry(val.block_id) { 110 | std::collections::hash_map::Entry::Occupied(occupied_entry) => { 111 | let r = occupied_entry.get(); 112 | ToNapiValue::to_napi_value(env, r) 113 | } 114 | std::collections::hash_map::Entry::Vacant(vacant_entry) => { 115 | let js_block = JsDependenciesBlock { 116 | block_id: val.block_id, 117 | compilation: val.compilation, 118 | }; 119 | let r = vacant_entry.insert(OneShotRef::new(env, js_block)?); 120 | ToNapiValue::to_napi_value(env, r) 121 | } 122 | } 123 | }) 124 | } 125 | } 126 | -------------------------------------------------------------------------------- /crates/plugin_manifest/src/plugin.rs: -------------------------------------------------------------------------------- 1 | use std::{collections::HashMap, path::Path}; 2 | 3 | use rspack_core::{ 4 | rspack_sources::{RawSource, SourceExt}, 5 | CompilationAsset, Plugin, 6 | PublicPath, Compilation, 7 | CompilationProcessAssets, 8 | }; 9 | use rspack_error::Result; 10 | use rspack_hook::{plugin, plugin_hook}; 11 | use serde::{Deserialize, Serialize}; 12 | 13 | #[plugin] 14 | #[derive(Debug)] 15 | pub struct ManifestPlugin; 16 | 17 | #[derive(Debug, Serialize, Deserialize)] 18 | #[serde(rename_all = "camelCase")] 19 | pub struct AssetsManifest { 20 | pub pages: HashMap>, 21 | pub entries: HashMap>, 22 | pub assets: HashMap, 23 | pub public_path: String, 24 | pub data_loader: Option, 25 | } 26 | 27 | const AUTO_PUBLIC_PATH_PLACEHOLDER: &str = "__RSPACK_PLUGIN_CSS_AUTO_PUBLIC_PATH__"; 28 | 29 | impl ManifestPlugin { 30 | pub fn new() -> Self { 31 | Self::new_inner() 32 | } 33 | } 34 | 35 | #[plugin_hook(CompilationProcessAssets for ManifestPlugin, stage = Compilation::PROCESS_ASSETS_STAGE_ADDITIONS)] 36 | async fn process_assets(&self, compilation: &mut Compilation) -> Result<()> { 37 | let public_path = match &compilation.options.output.public_path { 38 | PublicPath::Filename(p) => p.template(), 39 | PublicPath::Auto => Some(AUTO_PUBLIC_PATH_PLACEHOLDER), 40 | }; 41 | let mut assets_mainfest = AssetsManifest { 42 | pages: HashMap::new(), 43 | entries: HashMap::new(), 44 | assets: HashMap::new(), 45 | public_path: public_path.unwrap_or_default().to_string(), 46 | data_loader: None, 47 | }; 48 | let entry_points = &compilation.entrypoints; 49 | let assets = &compilation.assets(); 50 | 51 | assets.into_iter().for_each(|(_, asset)| { 52 | let version = &asset.info.version; 53 | let source_file = &asset.info.source_filename; 54 | if let Some(name) = source_file { 55 | assets_mainfest 56 | .assets 57 | .insert(name.to_string(), version.to_string()); 58 | } 59 | }); 60 | entry_points.iter().for_each(|(name, _entry)| { 61 | let mut files: Vec = Vec::new(); 62 | compilation 63 | .entrypoint_by_name(name) 64 | .chunks 65 | .iter() 66 | .for_each(|chunk| { 67 | let chunk = compilation.chunk_by_ukey.expect_get(chunk); 68 | chunk.files().iter().for_each(|file| { 69 | if let Some(asset) = assets.get(file) { 70 | if !asset.info.hot_module_replacement.unwrap_or(false) && !asset.info.development.unwrap_or(false) { 71 | files.push(file.to_string()); 72 | } 73 | } else { 74 | files.push(file.to_string()); 75 | } 76 | }); 77 | }); 78 | assets_mainfest.entries.insert(name.to_string(), files); 79 | }); 80 | // Check .ice/data-loader.ts is exists 81 | let data_loader_file = 82 | Path::new(&compilation.options.context.as_str()).join(".ice/data-loader.ts"); 83 | if data_loader_file.exists() { 84 | assets_mainfest.data_loader = Some("js/data-loader.js".to_string()); 85 | } 86 | 87 | let page_chunk_name_regex = regex::Regex::new(r"^p_").unwrap(); 88 | compilation.chunk_by_ukey.values().for_each(|c| { 89 | if let Some(name) = c.name() { 90 | if !c.has_entry_module(&compilation.chunk_graph) 91 | && !c.can_be_initial(&compilation.chunk_group_by_ukey) 92 | { 93 | assets_mainfest.pages.insert( 94 | page_chunk_name_regex.replace(name, "").to_string(), 95 | Vec::from_iter( 96 | c.files() 97 | .iter() 98 | // Only collect js and css files. 99 | .filter(|f| f.ends_with(".js") || f.ends_with(".css")) 100 | .cloned(), 101 | ), 102 | ); 103 | } 104 | } 105 | }); 106 | let json_string = serde_json::to_string(&assets_mainfest).unwrap(); 107 | compilation.emit_asset( 108 | "assets-manifest.json".to_string(), 109 | CompilationAsset::from(RawSource::from(json_string).boxed()), 110 | ); 111 | Ok(()) 112 | } 113 | 114 | impl Plugin for ManifestPlugin { 115 | fn name(&self) -> &'static str { 116 | "ManifestPlugin" 117 | } 118 | 119 | fn apply( 120 | &self, 121 | ctx: rspack_core::PluginContext<&mut rspack_core::ApplyContext>, 122 | _options: &rspack_core::CompilerOptions, 123 | ) -> Result<()> { 124 | ctx 125 | .context 126 | .compilation_hooks 127 | .process_assets 128 | .tap(process_assets::new(self)); 129 | Ok(()) 130 | } 131 | } 132 | -------------------------------------------------------------------------------- /crates/binding_values/src/asset.rs: -------------------------------------------------------------------------------- 1 | use napi_derive::napi; 2 | 3 | #[napi(object)] 4 | pub struct JsAssetInfoRelated { 5 | pub source_map: Option, 6 | } 7 | 8 | impl From for rspack_core::AssetInfoRelated { 9 | fn from(i: JsAssetInfoRelated) -> Self { 10 | Self { 11 | source_map: i.source_map, 12 | } 13 | } 14 | } 15 | 16 | #[napi(object)] 17 | pub struct JsAssetInfo { 18 | /// if the asset can be long term cached forever (contains a hash) 19 | pub immutable: Option, 20 | /// whether the asset is minimized 21 | pub minimized: Option, 22 | /// the value(s) of the full hash used for this asset 23 | pub fullhash: Vec, 24 | /// the value(s) of the chunk hash used for this asset 25 | pub chunkhash: Vec, 26 | /// the value(s) of the module hash used for this asset 27 | // pub modulehash: 28 | /// the value(s) of the content hash used for this asset 29 | pub contenthash: Vec, 30 | // when asset was created from a source file (potentially transformed), the original filename relative to compilation context 31 | pub source_filename: Option, 32 | /// when asset was created from a source file (potentially transformed), it should be flagged as copied 33 | pub copied: Option, 34 | /// size in bytes, only set after asset has been emitted 35 | // pub size: f64, 36 | /// when asset is only used for development and doesn't count towards user-facing assets 37 | pub development: Option, 38 | /// when asset ships data for updating an existing application (HMR) 39 | pub hot_module_replacement: Option, 40 | /// when asset is javascript and an ESM 41 | pub javascript_module: Option, 42 | /// related object to other assets, keyed by type of relation (only points from parent to child) 43 | pub related: JsAssetInfoRelated, 44 | /// unused css local ident for the css chunk 45 | pub css_unused_idents: Option>, 46 | /// Webpack: AssetInfo = KnownAssetInfo & Record 47 | /// But Napi.rs does not support Intersectiont types. This is a hack to store the additional fields 48 | /// in the rust struct and have the Js side to reshape and align with webpack 49 | /// Related: packages/rspack/src/Compilation.ts 50 | pub extras: serde_json::Map, 51 | /// whether this asset is over the size limit 52 | pub is_over_size_limit: Option, 53 | } 54 | 55 | impl From for rspack_core::AssetInfo { 56 | fn from(i: JsAssetInfo) -> Self { 57 | Self { 58 | immutable: i.immutable, 59 | minimized: i.minimized, 60 | development: i.development, 61 | hot_module_replacement: i.hot_module_replacement, 62 | chunk_hash: i.chunkhash.into_iter().collect(), 63 | related: i.related.into(), 64 | full_hash: i.fullhash.into_iter().collect(), 65 | content_hash: i.contenthash.into_iter().collect(), 66 | version: String::from(""), 67 | source_filename: i.source_filename, 68 | copied: i.copied, 69 | javascript_module: i.javascript_module, 70 | css_unused_idents: i.css_unused_idents.map(|i| i.into_iter().collect()), 71 | extras: i.extras, 72 | is_over_size_limit: i.is_over_size_limit, 73 | } 74 | } 75 | } 76 | 77 | #[napi(object)] 78 | pub struct JsAsset { 79 | pub name: String, 80 | pub info: JsAssetInfo, 81 | } 82 | 83 | impl From for JsAssetInfoRelated { 84 | fn from(related: rspack_core::AssetInfoRelated) -> Self { 85 | Self { 86 | source_map: related.source_map, 87 | } 88 | } 89 | } 90 | 91 | impl From for JsAssetInfo { 92 | fn from(info: rspack_core::AssetInfo) -> Self { 93 | Self { 94 | immutable: info.immutable, 95 | minimized: info.minimized, 96 | development: info.development, 97 | hot_module_replacement: info.hot_module_replacement, 98 | related: info.related.into(), 99 | chunkhash: info.chunk_hash.into_iter().collect(), 100 | fullhash: info.full_hash.into_iter().collect(), 101 | contenthash: info.content_hash.into_iter().collect(), 102 | source_filename: info.source_filename, 103 | copied: info.copied, 104 | javascript_module: info.javascript_module, 105 | css_unused_idents: info.css_unused_idents.map(|i| i.into_iter().collect()), 106 | extras: info.extras, 107 | is_over_size_limit: info.is_over_size_limit, 108 | } 109 | } 110 | } 111 | 112 | #[napi(object)] 113 | pub struct JsAssetEmittedArgs { 114 | pub filename: String, 115 | pub output_path: String, 116 | pub target_path: String, 117 | } 118 | -------------------------------------------------------------------------------- /crates/binding_values/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | description = "fork form rspack binding values" 3 | edition = "2021" 4 | license = "MIT" 5 | name = "binding_values" 6 | version = "0.2.0" 7 | 8 | [features] 9 | plugin = ["rspack_loader_swc/plugin"] 10 | 11 | [package.metadata.cargo-shear] 12 | ignored = ["tracing"] 13 | 14 | [dependencies] 15 | async-trait = { workspace = true } 16 | cow-utils = { workspace = true } 17 | derive_more = { workspace = true, features = ["debug"] } 18 | futures = { workspace = true } 19 | glob = { workspace = true } 20 | heck = { workspace = true } 21 | napi = { workspace = true, features = ["async", "tokio_rt", "serde-json", "anyhow"] } 22 | napi-derive = { workspace = true } 23 | pollster = { workspace = true } 24 | rspack_cacheable = { workspace = true } 25 | rspack_collections = { workspace = true } 26 | rspack_core = { workspace = true } 27 | rspack_error = { workspace = true } 28 | rspack_hook = { workspace = true } 29 | rspack_ids = { workspace = true } 30 | rspack_napi = { workspace = true } 31 | rspack_napi_macros = { workspace = true } 32 | rspack_paths = { workspace = true } 33 | rspack_regex = { workspace = true } 34 | rspack_util = { workspace = true } 35 | rustc-hash = { workspace = true } 36 | serde = { workspace = true } 37 | serde_json = { workspace = true } 38 | swc_core = { workspace = true, default-features = false, features = ["ecma_transforms_react"] } 39 | tokio = { workspace = true, features = ["rt", "rt-multi-thread", "macros", "test-util", "parking_lot"] } 40 | tracing = { workspace = true } 41 | 42 | loader_compilation = { path = "../loader_compilation" } 43 | plugin_manifest = { path = "../plugin_manifest" } 44 | rspack_loader_lightningcss = { workspace = true } 45 | rspack_loader_preact_refresh = { workspace = true } 46 | rspack_loader_react_refresh = { workspace = true } 47 | rspack_loader_runner = { workspace = true } 48 | rspack_loader_swc = { workspace = true } 49 | rspack_loader_testing = { workspace = true } 50 | rspack_plugin_asset = { workspace = true } 51 | rspack_plugin_banner = { workspace = true } 52 | rspack_plugin_context_replacement = { workspace = true } 53 | rspack_plugin_copy = { workspace = true } 54 | rspack_plugin_css = { workspace = true } 55 | rspack_plugin_devtool = { workspace = true } 56 | rspack_plugin_dll = { workspace = true } 57 | rspack_plugin_dynamic_entry = { workspace = true } 58 | rspack_plugin_ensure_chunk_conditions = { workspace = true } 59 | rspack_plugin_entry = { workspace = true } 60 | rspack_plugin_externals = { workspace = true } 61 | rspack_plugin_extract_css = { workspace = true } 62 | rspack_plugin_hmr = { workspace = true } 63 | rspack_plugin_html = { workspace = true } 64 | rspack_plugin_ignore = { workspace = true } 65 | rspack_plugin_javascript = { workspace = true } 66 | rspack_plugin_json = { workspace = true } 67 | rspack_plugin_lazy_compilation = { workspace = true } 68 | rspack_plugin_library = { workspace = true } 69 | rspack_plugin_lightning_css_minimizer = { workspace = true } 70 | rspack_plugin_limit_chunk_count = { workspace = true } 71 | rspack_plugin_merge_duplicate_chunks = { workspace = true } 72 | rspack_plugin_mf = { workspace = true } 73 | rspack_plugin_no_emit_on_errors = { workspace = true } 74 | rspack_plugin_progress = { workspace = true } 75 | rspack_plugin_real_content_hash = { workspace = true } 76 | rspack_plugin_remove_duplicate_modules = { workspace = true } 77 | rspack_plugin_remove_empty_chunks = { workspace = true } 78 | rspack_plugin_runtime = { workspace = true } 79 | rspack_plugin_runtime_chunk = { workspace = true } 80 | rspack_plugin_schemes = { workspace = true } 81 | rspack_plugin_size_limits = { workspace = true } 82 | rspack_plugin_split_chunks = { workspace = true } 83 | rspack_plugin_swc_js_minimizer = { workspace = true } 84 | rspack_plugin_warn_sensitive_module = { workspace = true } 85 | rspack_plugin_wasm = { workspace = true } 86 | rspack_plugin_web_worker_template = { workspace = true } 87 | rspack_plugin_worker = { workspace = true } 88 | rspack_tracing = { workspace = true } 89 | -------------------------------------------------------------------------------- /crates/binding_values/src/module_graph_connection.rs: -------------------------------------------------------------------------------- 1 | use std::{cell::RefCell, ptr::NonNull}; 2 | 3 | use napi::bindgen_prelude::ToNapiValue; 4 | use napi_derive::napi; 5 | use rspack_core::{Compilation, CompilationId, DependencyId, ModuleGraph}; 6 | use rspack_napi::OneShotRef; 7 | use rustc_hash::FxHashMap as HashMap; 8 | 9 | use crate::{JsDependencyWrapper, JsModuleWrapper}; 10 | 11 | #[napi] 12 | pub struct JsModuleGraphConnection { 13 | compilation: NonNull, 14 | dependency_id: DependencyId, 15 | } 16 | 17 | impl JsModuleGraphConnection { 18 | fn as_ref(&self) -> napi::Result<(&'static Compilation, ModuleGraph<'static>)> { 19 | let compilation = unsafe { self.compilation.as_ref() }; 20 | let module_graph = compilation.get_module_graph(); 21 | 22 | Ok((compilation, module_graph)) 23 | } 24 | } 25 | 26 | #[napi] 27 | impl JsModuleGraphConnection { 28 | #[napi(getter, ts_return_type = "JsDependency")] 29 | pub fn dependency(&self) -> napi::Result { 30 | let (compilation, module_graph) = self.as_ref()?; 31 | if let Some(dependency) = module_graph.dependency_by_id(&self.dependency_id) { 32 | Ok(JsDependencyWrapper::new( 33 | dependency.as_ref(), 34 | compilation.id(), 35 | Some(compilation), 36 | )) 37 | } else { 38 | Err(napi::Error::from_reason(format!( 39 | "Unable to access Dependency with id = {:#?} now. The Dependency have been removed on the Rust side.", 40 | self.dependency_id 41 | ))) 42 | } 43 | } 44 | 45 | #[napi(getter, ts_return_type = "JsModule | null")] 46 | pub fn module(&self) -> napi::Result> { 47 | let (compilation, module_graph) = self.as_ref()?; 48 | if let Some(connection) = module_graph.connection_by_dependency_id(&self.dependency_id) { 49 | let module = module_graph.module_by_identifier(connection.module_identifier()); 50 | Ok(module.map(|m| JsModuleWrapper::new(m.as_ref(), compilation.id(), Some(compilation)))) 51 | } else { 52 | Err(napi::Error::from_reason(format!( 53 | "Unable to access ModuleGraphConnection with id = {:#?} now. The ModuleGraphConnection have been removed on the Rust side.", 54 | self.dependency_id 55 | ))) 56 | } 57 | } 58 | } 59 | 60 | type ModuleGraphConnectionRefs = HashMap>; 61 | 62 | type ModuleGraphConnectionRefsByCompilationId = 63 | RefCell>; 64 | 65 | thread_local! { 66 | static MODULE_GRAPH_CONNECTION_INSTANCE_REFS: ModuleGraphConnectionRefsByCompilationId = Default::default(); 67 | } 68 | 69 | pub struct JsModuleGraphConnectionWrapper { 70 | compilation_id: CompilationId, 71 | compilation: NonNull, 72 | dependency_id: DependencyId, 73 | } 74 | 75 | impl JsModuleGraphConnectionWrapper { 76 | pub fn new(dependency_id: DependencyId, compilation: &Compilation) -> Self { 77 | #[allow(clippy::unwrap_used)] 78 | Self { 79 | dependency_id, 80 | compilation_id: compilation.id(), 81 | compilation: NonNull::new(compilation as *const Compilation as *mut Compilation).unwrap(), 82 | } 83 | } 84 | 85 | pub fn cleanup_last_compilation(compilation_id: CompilationId) { 86 | MODULE_GRAPH_CONNECTION_INSTANCE_REFS.with(|refs| { 87 | let mut refs_by_compilation_id = refs.borrow_mut(); 88 | refs_by_compilation_id.remove(&compilation_id) 89 | }); 90 | } 91 | } 92 | 93 | impl ToNapiValue for JsModuleGraphConnectionWrapper { 94 | unsafe fn to_napi_value( 95 | env: napi::sys::napi_env, 96 | val: Self, 97 | ) -> napi::Result { 98 | MODULE_GRAPH_CONNECTION_INSTANCE_REFS.with(|refs| { 99 | let mut refs_by_compilation_id = refs.borrow_mut(); 100 | let entry = refs_by_compilation_id.entry(val.compilation_id); 101 | let refs = match entry { 102 | std::collections::hash_map::Entry::Occupied(entry) => entry.into_mut(), 103 | std::collections::hash_map::Entry::Vacant(entry) => { 104 | let refs = HashMap::default(); 105 | entry.insert(refs) 106 | } 107 | }; 108 | 109 | match refs.entry(val.dependency_id) { 110 | std::collections::hash_map::Entry::Occupied(occupied_entry) => { 111 | let r = occupied_entry.get(); 112 | ToNapiValue::to_napi_value(env, r) 113 | } 114 | std::collections::hash_map::Entry::Vacant(vacant_entry) => { 115 | let js_dependency = JsModuleGraphConnection { 116 | compilation: val.compilation, 117 | dependency_id: val.dependency_id, 118 | }; 119 | let r = vacant_entry.insert(OneShotRef::new(env, js_dependency)?); 120 | ToNapiValue::to_napi_value(env, r) 121 | } 122 | } 123 | }) 124 | } 125 | } 126 | -------------------------------------------------------------------------------- /crates/binding_values/src/raw_options/raw_builtins/raw_dll.rs: -------------------------------------------------------------------------------- 1 | use napi::Either; 2 | use napi_derive::napi; 3 | use rspack_core::ModuleId; 4 | use rspack_plugin_dll::{ 5 | DllEntryPluginOptions, DllManifest, DllManifestContent, DllManifestContentItem, 6 | DllManifestContentItemExports, DllReferenceAgencyPluginOptions, LibManifestPluginOptions, 7 | }; 8 | use rustc_hash::FxHashMap as HashMap; 9 | use swc_core::atoms::Atom; 10 | 11 | use crate::{JsBuildMeta, JsFilename}; 12 | 13 | #[derive(Debug)] 14 | #[napi(object)] 15 | pub struct RawDllEntryPluginOptions { 16 | pub context: String, 17 | pub entries: Vec, 18 | pub name: String, 19 | } 20 | 21 | impl From for DllEntryPluginOptions { 22 | fn from(value: RawDllEntryPluginOptions) -> Self { 23 | let RawDllEntryPluginOptions { 24 | name, 25 | context, 26 | entries, 27 | } = value; 28 | 29 | Self { 30 | name, 31 | context: context.into(), 32 | entries, 33 | } 34 | } 35 | } 36 | 37 | #[derive(Debug)] 38 | #[napi(object, object_to_js = false)] 39 | pub struct RawLibManifestPluginOptions { 40 | pub context: Option, 41 | pub entry_only: Option, 42 | pub name: Option, 43 | pub path: JsFilename, 44 | pub format: Option, 45 | pub r#type: Option, 46 | } 47 | 48 | impl From for LibManifestPluginOptions { 49 | fn from(value: RawLibManifestPluginOptions) -> Self { 50 | let RawLibManifestPluginOptions { 51 | context, 52 | entry_only, 53 | name, 54 | path, 55 | r#type, 56 | format, 57 | } = value; 58 | 59 | Self { 60 | context: context.map(|c| c.into()), 61 | format, 62 | entry_only, 63 | name: name.map(|n| n.into()), 64 | path: path.into(), 65 | r#type, 66 | } 67 | } 68 | } 69 | 70 | #[napi(object, object_to_js = false)] 71 | pub struct RawDllReferenceAgencyPluginOptions { 72 | pub context: Option, 73 | pub name: Option, 74 | pub extensions: Vec, 75 | pub scope: Option, 76 | pub source_type: Option, 77 | pub r#type: String, 78 | pub content: Option>, 79 | pub manifest: Option, 80 | } 81 | 82 | #[napi(object, object_to_js = false)] 83 | pub struct RawDllManifestContentItem { 84 | pub build_meta: Option, 85 | #[napi(ts_type = "string[] | true")] 86 | pub exports: Option, bool>>, 87 | pub id: Option>, 88 | } 89 | 90 | impl From for DllManifestContentItem { 91 | fn from(value: RawDllManifestContentItem) -> Self { 92 | let raw_exports = value.exports; 93 | 94 | let exports = raw_exports.map(|exports| match exports { 95 | Either::A(seq) => { 96 | DllManifestContentItemExports::Vec(seq.into_iter().map(Atom::from).collect::>()) 97 | } 98 | Either::B(bool) => { 99 | if bool { 100 | DllManifestContentItemExports::True 101 | } else { 102 | unreachable!() 103 | } 104 | } 105 | }); 106 | 107 | Self { 108 | build_meta: value.build_meta.map(|meta| meta.into()), 109 | exports, 110 | id: value.id.map(|id| match id { 111 | Either::A(n) => ModuleId::from(n), 112 | Either::B(s) => ModuleId::from(s), 113 | }), 114 | } 115 | } 116 | } 117 | 118 | #[napi(object, object_to_js = false)] 119 | pub struct RawDllManifest { 120 | pub content: HashMap, 121 | pub name: Option, 122 | pub r#type: Option, 123 | } 124 | 125 | impl From for DllManifest { 126 | fn from(value: RawDllManifest) -> Self { 127 | Self { 128 | content: value 129 | .content 130 | .into_iter() 131 | .map(|(k, v)| (k, v.into())) 132 | .collect::(), 133 | name: value.name, 134 | r#type: value.r#type, 135 | } 136 | } 137 | } 138 | 139 | impl From for DllReferenceAgencyPluginOptions { 140 | fn from(value: RawDllReferenceAgencyPluginOptions) -> Self { 141 | let RawDllReferenceAgencyPluginOptions { 142 | context, 143 | name, 144 | extensions, 145 | scope, 146 | source_type, 147 | r#type, 148 | content, 149 | manifest, 150 | } = value; 151 | 152 | Self { 153 | context: context.map(|ctx| ctx.into()), 154 | name, 155 | extensions, 156 | scope, 157 | source_type, 158 | r#type, 159 | content: content.map(|c| { 160 | c.into_iter() 161 | .map(|(k, v)| (k, v.into())) 162 | .collect::() 163 | }), 164 | manifest: manifest.map(|m| m.into()), 165 | } 166 | } 167 | } 168 | 169 | #[derive(Debug)] 170 | #[napi(object)] 171 | pub struct RawFlagAllModulesAsUsedPluginOptions { 172 | pub explanation: String, 173 | } 174 | -------------------------------------------------------------------------------- /crates/loader_compilation/src/transform.rs: -------------------------------------------------------------------------------- 1 | use std::path::Path; 2 | use anyhow::{Context, Error}; 3 | use either::Either; 4 | use serde::Deserialize; 5 | use swc_core::atoms::Atom; 6 | use swc_core::common::collections::AHashMap; 7 | use swc_core::common::BytePos; 8 | use swc_core::ecma::ast::{Ident, Pass, noop_pass}; 9 | use swc_core::ecma::visit::{noop_visit_type, Visit}; 10 | use swc_env_replacement::env_replacement; 11 | use swc_keep_export::keep_export; 12 | use swc_named_import_transform::{named_import_transform, TransformConfig}; 13 | use swc_remove_export::remove_export; 14 | use swc_change_package_import::{change_package_import, Config as ImportConfig, SpecificConfigs}; 15 | 16 | macro_rules! either { 17 | ($config:expr, $f:expr) => { 18 | if let Some(config) = &$config { 19 | #[allow(clippy::redundant_closure_call)] 20 | Either::Left($f(config)) 21 | } else { 22 | Either::Right(noop_pass()) 23 | } 24 | }; 25 | ($config:expr, $f:expr, $enabled:expr) => { 26 | if $enabled() { 27 | either!($config, $f) 28 | } else { 29 | Either::Right(noop_pass()) 30 | } 31 | }; 32 | } 33 | 34 | // Only define the stuct which is used in the following function. 35 | #[derive(Deserialize, Debug)] 36 | struct NestedRoutesManifest { 37 | file: String, 38 | children: Option>, 39 | } 40 | 41 | fn get_routes_file(routes: Vec) -> Vec { 42 | let mut result: Vec = vec![]; 43 | for route in routes { 44 | // Add default prefix of src/pages/ to the route file. 45 | let mut path_str = String::from("src/pages/"); 46 | path_str.push_str(&route.file); 47 | 48 | result.push(path_str.to_string()); 49 | 50 | if let Some(children) = route.children { 51 | result.append(&mut get_routes_file(children)); 52 | } 53 | } 54 | result 55 | } 56 | 57 | fn parse_routes_config(c: String) -> Result, Error> { 58 | let routes = serde_json::from_str(&c)?; 59 | Ok(get_routes_file(routes)) 60 | } 61 | 62 | pub(crate) fn load_routes_config(path: &Path) -> Result, Error> { 63 | let content = std::fs::read_to_string(path).context("failed to read routes config")?; 64 | parse_routes_config(content) 65 | } 66 | 67 | fn match_route_entry(resource_path: &str, routes: Option<&Vec>) -> bool { 68 | if let Some(routes) = routes { 69 | for route in routes { 70 | if resource_path.ends_with(&route.to_string()) { 71 | return true; 72 | } 73 | } 74 | } 75 | false 76 | } 77 | 78 | fn match_app_entry(resource_path: &str) -> bool { 79 | // File path ends with src/app.(ts|tsx|js|jsx) 80 | let regex_for_app = regex::Regex::new(r"src/app\.(ts|tsx|js|jsx)$").unwrap(); 81 | regex_for_app.is_match(resource_path) 82 | } 83 | 84 | #[derive(Debug, Default, Deserialize)] 85 | #[serde(rename_all = "camelCase", default)] 86 | pub struct TransformFeatureOptions { 87 | pub keep_export: Option>, 88 | pub remove_export: Option>, 89 | pub optimize_import: Option>, 90 | pub import_config: Option>, 91 | } 92 | 93 | pub(crate) fn transform<'a>( 94 | resource_path: &'a str, 95 | routes_config: Option<&Vec>, 96 | feature_options: &TransformFeatureOptions, 97 | ) -> impl Pass + 'a { 98 | ( 99 | either!(feature_options.optimize_import, |options: &Vec| { 100 | named_import_transform(TransformConfig { 101 | packages: options.clone(), 102 | }) 103 | }), 104 | either!( 105 | feature_options.import_config, 106 | |options: &Vec| { 107 | let import_config = options.to_vec(); 108 | change_package_import(import_config.into_iter().map(ImportConfig::SpecificConfig).collect()) 109 | } 110 | ), 111 | either!( 112 | Some(&vec!["@uni/env".to_string(), "universal-env".to_string()]), 113 | |options: &Vec| { env_replacement(options.clone()) } 114 | ), 115 | either!( 116 | feature_options.keep_export, 117 | |options: &Vec| { 118 | let mut exports_name = options.clone(); 119 | // Special case for app entry. 120 | // When keep pageConfig, we should also keep the default export of app entry. 121 | if match_app_entry(resource_path) && exports_name.contains(&String::from("pageConfig")) { 122 | exports_name.push(String::from("default")); 123 | } 124 | keep_export(exports_name) 125 | }, 126 | || { match_app_entry(resource_path) || match_route_entry(resource_path, routes_config) } 127 | ), 128 | either!( 129 | feature_options.remove_export, 130 | |options: &Vec| { remove_export(options.clone()) }, 131 | || { 132 | // Remove export only work for app entry and route entry. 133 | match_app_entry(resource_path) || match_route_entry(resource_path, routes_config) 134 | } 135 | ), 136 | ) 137 | } 138 | 139 | pub struct IdentCollector { 140 | pub names: AHashMap, 141 | } 142 | 143 | impl Visit for IdentCollector { 144 | noop_visit_type!(); 145 | 146 | fn visit_ident(&mut self, ident: &Ident) { 147 | self.names.insert(ident.span.lo, ident.sym.clone()); 148 | } 149 | } 150 | -------------------------------------------------------------------------------- /crates/binding_values/src/raw_options/raw_external.rs: -------------------------------------------------------------------------------- 1 | use std::collections::HashMap; 2 | use std::fmt::Debug; 3 | use std::sync::Arc; 4 | 5 | use napi::bindgen_prelude::Either4; 6 | use napi_derive::napi; 7 | use rspack_core::{ExternalItem, ExternalItemFnResult, ExternalItemValue}; 8 | use rspack_core::{ExternalItemFnCtx, ResolveOptionsWithDependencyType, ResolverFactory}; 9 | use rspack_napi::threadsafe_function::ThreadsafeFunction; 10 | use rspack_regex::RspackRegex; 11 | 12 | use crate::JsResolver; 13 | 14 | #[napi(object)] 15 | pub struct RawHttpExternalsRspackPluginOptions { 16 | pub css: bool, 17 | pub web_async: bool, 18 | } 19 | 20 | #[napi(object, object_to_js = false)] 21 | pub struct RawExternalsPluginOptions { 22 | pub r#type: String, 23 | #[napi( 24 | ts_type = "(string | RegExp | Record> | ((...args: any[]) => any))[]" 25 | )] 26 | pub externals: Vec, 27 | } 28 | 29 | type RawExternalItem = Either4< 30 | String, 31 | RspackRegex, 32 | HashMap, 33 | ThreadsafeFunction, 34 | >; 35 | type RawExternalItemValue = Either4, HashMap>>; 36 | pub(crate) struct RawExternalItemWrapper(pub(crate) RawExternalItem); 37 | struct RawExternalItemValueWrapper(RawExternalItemValue); 38 | 39 | impl From for ExternalItemValue { 40 | fn from(value: RawExternalItemValueWrapper) -> Self { 41 | match value.0 { 42 | Either4::A(v) => Self::String(v), 43 | Either4::B(v) => Self::Bool(v), 44 | Either4::C(v) => Self::Array(v), 45 | Either4::D(v) => Self::Object(v.into_iter().collect()), 46 | } 47 | } 48 | } 49 | 50 | #[derive(Debug, Clone)] 51 | #[napi(object)] 52 | pub struct RawExternalItemFnResult { 53 | pub external_type: Option, 54 | // sadly, napi.rs does not support type alias at the moment. Need to add Either here 55 | #[napi(ts_type = "string | boolean | string[] | Record")] 56 | pub result: Option, 57 | } 58 | 59 | impl From for ExternalItemFnResult { 60 | fn from(value: RawExternalItemFnResult) -> Self { 61 | Self { 62 | external_type: value.external_type, 63 | result: value.result.map(|v| RawExternalItemValueWrapper(v).into()), 64 | } 65 | } 66 | } 67 | 68 | #[derive(Debug, Clone)] 69 | #[napi(object)] 70 | pub struct ContextInfo { 71 | pub issuer: String, 72 | pub issuer_layer: Option, 73 | } 74 | 75 | #[derive(Debug)] 76 | #[napi] 77 | pub struct RawExternalItemFnCtx { 78 | request: String, 79 | context: String, 80 | dependency_type: String, 81 | context_info: ContextInfo, 82 | resolve_options_with_dependency_type: ResolveOptionsWithDependencyType, 83 | resolver_factory: Arc, 84 | } 85 | 86 | #[derive(Debug)] 87 | #[napi(object)] 88 | pub struct RawExternalItemFnCtxData { 89 | pub request: String, 90 | pub context: String, 91 | pub dependency_type: String, 92 | pub context_info: ContextInfo, 93 | } 94 | 95 | #[napi] 96 | impl RawExternalItemFnCtx { 97 | #[napi] 98 | pub fn data(&self) -> RawExternalItemFnCtxData { 99 | RawExternalItemFnCtxData { 100 | request: self.request.clone(), 101 | context: self.context.clone(), 102 | dependency_type: self.dependency_type.clone(), 103 | context_info: self.context_info.clone(), 104 | } 105 | } 106 | 107 | #[napi] 108 | pub fn get_resolver(&self) -> JsResolver { 109 | JsResolver::new( 110 | self.resolver_factory.clone(), 111 | self.resolve_options_with_dependency_type.clone(), 112 | ) 113 | } 114 | } 115 | 116 | impl From for RawExternalItemFnCtx { 117 | fn from(value: ExternalItemFnCtx) -> Self { 118 | Self { 119 | request: value.request, 120 | dependency_type: value.dependency_type, 121 | context: value.context, 122 | context_info: ContextInfo { 123 | issuer: value.context_info.issuer, 124 | issuer_layer: value.context_info.issuer_layer, 125 | }, 126 | resolve_options_with_dependency_type: value.resolve_options_with_dependency_type, 127 | resolver_factory: value.resolver_factory, 128 | } 129 | } 130 | } 131 | 132 | impl TryFrom for ExternalItem { 133 | type Error = rspack_error::Error; 134 | 135 | #[allow(clippy::unwrap_in_result)] 136 | fn try_from(value: RawExternalItemWrapper) -> rspack_error::Result { 137 | match value.0 { 138 | Either4::A(v) => Ok(Self::String(v)), 139 | Either4::B(v) => Ok(Self::RegExp(v)), 140 | Either4::C(v) => Ok(Self::Object( 141 | v.into_iter() 142 | .map(|(k, v)| (k, RawExternalItemValueWrapper(v).into())) 143 | .collect(), 144 | )), 145 | Either4::D(v) => Ok(Self::Fn(Box::new(move |ctx: ExternalItemFnCtx| { 146 | let v = v.clone(); 147 | Box::pin(async move { v.call(ctx.into()).await.map(|r| r.into()) }) 148 | }))), 149 | } 150 | } 151 | } 152 | 153 | #[derive(Debug, Clone)] 154 | #[napi(object)] 155 | pub struct RawExternalsPresets { 156 | pub node: bool, 157 | pub web: bool, 158 | pub electron: bool, 159 | pub electron_main: bool, 160 | pub electron_preload: bool, 161 | pub electron_renderer: bool, 162 | } 163 | -------------------------------------------------------------------------------- /crates/binding_values/src/plugins/js_loader/context.rs: -------------------------------------------------------------------------------- 1 | use std::collections::HashMap; 2 | 3 | use napi::bindgen_prelude::*; 4 | use napi_derive::napi; 5 | use rspack_core::{LoaderContext, RunnerContext}; 6 | use rspack_error::error; 7 | use rspack_loader_runner::{LoaderItem, State as LoaderState}; 8 | use rspack_napi::threadsafe_js_value_ref::ThreadsafeJsValueRef; 9 | use rspack_tracing::otel::{opentelemetry::global, tracing::OpenTelemetrySpanExt as _}; 10 | use tracing::Span; 11 | 12 | use crate::{JsModuleWrapper, JsResourceData, JsRspackError}; 13 | 14 | #[napi(object)] 15 | pub struct JsLoaderItem { 16 | pub request: String, 17 | pub r#type: String, 18 | 19 | // data 20 | pub data: serde_json::Value, 21 | 22 | // status 23 | pub normal_executed: bool, 24 | pub pitch_executed: bool, 25 | } 26 | 27 | impl From<&LoaderItem> for JsLoaderItem { 28 | fn from(value: &LoaderItem) -> Self { 29 | JsLoaderItem { 30 | request: value.request().to_string(), 31 | r#type: value.r#type().to_string(), 32 | 33 | data: value.data().clone(), 34 | normal_executed: value.normal_executed(), 35 | pitch_executed: value.pitch_executed(), 36 | } 37 | } 38 | } 39 | 40 | #[napi(string_enum)] 41 | pub enum JsLoaderState { 42 | Pitching, 43 | Normal, 44 | } 45 | 46 | impl From for JsLoaderState { 47 | fn from(value: LoaderState) -> Self { 48 | match value { 49 | LoaderState::Init | LoaderState::ProcessResource | LoaderState::Finished => { 50 | panic!("Unexpected loader runner state: {value:?}") 51 | } 52 | LoaderState::Pitching => JsLoaderState::Pitching, 53 | LoaderState::Normal => JsLoaderState::Normal, 54 | } 55 | } 56 | } 57 | 58 | #[napi(object)] 59 | pub struct JsLoaderContext { 60 | #[napi(ts_type = "Readonly")] 61 | pub resource_data: JsResourceData, 62 | /// Will be deprecated. Use module.module_identifier instead 63 | #[napi(js_name = "_moduleIdentifier", ts_type = "Readonly")] 64 | pub module_identifier: String, 65 | #[napi(js_name = "_module", ts_type = "JsModule")] 66 | pub module: JsModuleWrapper, 67 | #[napi(ts_type = "Readonly")] 68 | pub hot: bool, 69 | 70 | /// Content maybe empty in pitching stage 71 | pub content: Either, 72 | #[napi(ts_type = "any")] 73 | pub additional_data: Option>, 74 | #[napi(js_name = "__internal__parseMeta")] 75 | pub parse_meta: HashMap, 76 | pub source_map: Option, 77 | pub cacheable: bool, 78 | pub file_dependencies: Vec, 79 | pub context_dependencies: Vec, 80 | pub missing_dependencies: Vec, 81 | pub build_dependencies: Vec, 82 | 83 | pub loader_items: Vec, 84 | pub loader_index: i32, 85 | #[napi(ts_type = "Readonly")] 86 | pub loader_state: JsLoaderState, 87 | #[napi(js_name = "__internal__error")] 88 | pub error: Option, 89 | 90 | #[napi(js_name = "__internal__tracingCarrier")] 91 | pub carrier: Option>, 92 | } 93 | 94 | impl TryFrom<&mut LoaderContext> for JsLoaderContext { 95 | type Error = rspack_error::Error; 96 | 97 | fn try_from( 98 | cx: &mut rspack_core::LoaderContext, 99 | ) -> std::result::Result { 100 | let module = unsafe { cx.context.module.as_ref() }; 101 | 102 | let mut carrier = HashMap::new(); 103 | global::get_text_map_propagator(|propagator| { 104 | let cx = Span::current().context(); 105 | propagator.inject_context(&cx, &mut carrier); 106 | }); 107 | Ok(JsLoaderContext { 108 | resource_data: cx.resource_data.as_ref().into(), 109 | module_identifier: module.identifier().to_string(), 110 | module: JsModuleWrapper::new(module, cx.context.compilation_id, None), 111 | hot: cx.hot, 112 | content: match cx.content() { 113 | Some(c) => Either::B(c.to_owned().into_bytes().into()), 114 | None => Either::A(Null), 115 | }, 116 | parse_meta: cx.parse_meta.clone().into_iter().collect(), 117 | additional_data: cx 118 | .additional_data() 119 | .and_then(|data| data.get::>()) 120 | .cloned(), 121 | source_map: cx 122 | .source_map() 123 | .cloned() 124 | .map(|v| v.to_json()) 125 | .transpose() 126 | .map_err(|e| error!(e.to_string()))? 127 | .map(|v| v.into_bytes().into()), 128 | cacheable: cx.cacheable, 129 | file_dependencies: cx 130 | .file_dependencies 131 | .iter() 132 | .map(|i| i.to_string_lossy().to_string()) 133 | .collect(), 134 | context_dependencies: cx 135 | .context_dependencies 136 | .iter() 137 | .map(|i| i.to_string_lossy().to_string()) 138 | .collect(), 139 | missing_dependencies: cx 140 | .missing_dependencies 141 | .iter() 142 | .map(|i| i.to_string_lossy().to_string()) 143 | .collect(), 144 | build_dependencies: cx 145 | .build_dependencies 146 | .iter() 147 | .map(|i| i.to_string_lossy().to_string()) 148 | .collect(), 149 | 150 | loader_items: cx.loader_items.iter().map(Into::into).collect(), 151 | loader_index: cx.loader_index, 152 | loader_state: cx.state().into(), 153 | error: None, 154 | carrier: Some(carrier), 155 | }) 156 | } 157 | } 158 | -------------------------------------------------------------------------------- /crates/binding_values/src/module_graph.rs: -------------------------------------------------------------------------------- 1 | use std::{ptr::NonNull, sync::Arc}; 2 | 3 | use napi::{Either, Env, JsString}; 4 | use napi_derive::napi; 5 | use rspack_core::{Compilation, ModuleGraph, RuntimeSpec}; 6 | use rustc_hash::FxHashSet; 7 | 8 | use crate::{ 9 | JsDependency, JsExportsInfo, JsModule, JsModuleGraphConnectionWrapper, JsModuleWrapper, 10 | }; 11 | 12 | #[napi] 13 | pub struct JsModuleGraph { 14 | compilation: NonNull, 15 | } 16 | 17 | impl JsModuleGraph { 18 | pub fn new(compilation: &Compilation) -> Self { 19 | #[allow(clippy::unwrap_used)] 20 | Self { 21 | compilation: NonNull::new(compilation as *const Compilation as *mut Compilation).unwrap(), 22 | } 23 | } 24 | 25 | fn as_ref(&self) -> napi::Result<(&'static Compilation, ModuleGraph<'static>)> { 26 | let compilation = unsafe { self.compilation.as_ref() }; 27 | let module_graph = compilation.get_module_graph(); 28 | 29 | Ok((compilation, module_graph)) 30 | } 31 | } 32 | 33 | #[napi] 34 | impl JsModuleGraph { 35 | #[napi(ts_return_type = "JsModule | null")] 36 | pub fn get_module(&self, js_dependency: &JsDependency) -> napi::Result> { 37 | let (compilation, module_graph) = self.as_ref()?; 38 | let module = module_graph.get_module_by_dependency_id(&js_dependency.dependency_id); 39 | let js_module = module 40 | .map(|module| JsModuleWrapper::new(module.as_ref(), compilation.id(), Some(compilation))); 41 | Ok(js_module) 42 | } 43 | 44 | #[napi(ts_return_type = "JsModule | null")] 45 | pub fn get_resolved_module( 46 | &self, 47 | js_dependency: &JsDependency, 48 | ) -> napi::Result> { 49 | let (compilation, module_graph) = self.as_ref()?; 50 | Ok( 51 | match module_graph.connection_by_dependency_id(&js_dependency.dependency_id) { 52 | Some(connection) => module_graph 53 | .module_by_identifier(&connection.resolved_module) 54 | .map(|module| JsModuleWrapper::new(module.as_ref(), compilation.id(), Some(compilation))), 55 | None => None, 56 | }, 57 | ) 58 | } 59 | 60 | #[napi] 61 | pub fn get_used_exports( 62 | &self, 63 | env: Env, 64 | js_module: &JsModule, 65 | js_runtime: Either>, 66 | ) -> napi::Result>>> { 67 | let (_, module_graph) = self.as_ref()?; 68 | 69 | let mut runtime: FxHashSet> = FxHashSet::default(); 70 | match js_runtime { 71 | Either::A(s) => { 72 | runtime.insert(Arc::from(s)); 73 | } 74 | Either::B(vec) => { 75 | runtime.extend(vec.into_iter().map(Arc::from)); 76 | } 77 | }; 78 | let used_exports = 79 | module_graph.get_used_exports(&js_module.identifier, Some(&RuntimeSpec::new(runtime))); 80 | Ok(match used_exports { 81 | rspack_core::UsedExports::Null => None, 82 | rspack_core::UsedExports::Bool(b) => Some(Either::A(b)), 83 | rspack_core::UsedExports::Vec(vec) => Some(Either::B( 84 | vec 85 | .into_iter() 86 | .map(|atom| env.create_string(atom.as_str())) 87 | .collect::>>()?, 88 | )), 89 | }) 90 | } 91 | 92 | #[napi(ts_return_type = "JsModule | null")] 93 | pub fn get_issuer(&self, module: &JsModule) -> napi::Result> { 94 | let (compilation, module_graph) = self.as_ref()?; 95 | let issuer = module_graph.get_issuer(&module.identifier); 96 | Ok( 97 | issuer 98 | .map(|module| JsModuleWrapper::new(module.as_ref(), compilation.id(), Some(compilation))), 99 | ) 100 | } 101 | 102 | #[napi] 103 | pub fn get_exports_info(&self, module: &JsModule) -> napi::Result { 104 | let (compilation, module_graph) = self.as_ref()?; 105 | let exports_info = module_graph.get_exports_info(&module.identifier); 106 | Ok(JsExportsInfo::new(exports_info, compilation)) 107 | } 108 | 109 | #[napi(ts_return_type = "JsModuleGraphConnection | null")] 110 | pub fn get_connection( 111 | &self, 112 | dependency: &JsDependency, 113 | ) -> napi::Result> { 114 | let (compilation, module_graph) = self.as_ref()?; 115 | Ok( 116 | module_graph 117 | .connection_by_dependency_id(&dependency.dependency_id) 118 | .map(|connection| { 119 | JsModuleGraphConnectionWrapper::new(connection.dependency_id, compilation) 120 | }), 121 | ) 122 | } 123 | 124 | #[napi(ts_return_type = "JsModuleGraphConnection[]")] 125 | pub fn get_outgoing_connections( 126 | &self, 127 | module: &JsModule, 128 | ) -> napi::Result> { 129 | let (compilation, module_graph) = self.as_ref()?; 130 | Ok( 131 | module_graph 132 | .get_outgoing_connections(&module.identifier) 133 | .map(|connection| { 134 | JsModuleGraphConnectionWrapper::new(connection.dependency_id, compilation) 135 | }) 136 | .collect::>(), 137 | ) 138 | } 139 | 140 | #[napi(ts_return_type = "JsModuleGraphConnection[]")] 141 | pub fn get_incoming_connections( 142 | &self, 143 | module: &JsModule, 144 | ) -> napi::Result> { 145 | let (compilation, module_graph) = self.as_ref()?; 146 | Ok( 147 | module_graph 148 | .get_incoming_connections(&module.identifier) 149 | .map(|connection| { 150 | JsModuleGraphConnectionWrapper::new(connection.dependency_id, compilation) 151 | }) 152 | .collect::>(), 153 | ) 154 | } 155 | } 156 | -------------------------------------------------------------------------------- /crates/binding_values/src/raw_options/raw_builtins/raw_html.rs: -------------------------------------------------------------------------------- 1 | use std::collections::HashMap; 2 | use std::str::FromStr; 3 | 4 | use napi::bindgen_prelude::Either3; 5 | use napi_derive::napi; 6 | use rspack_napi::threadsafe_function::ThreadsafeFunction; 7 | use rspack_plugin_html::config::HtmlChunkSortMode; 8 | use rspack_plugin_html::config::HtmlInject; 9 | use rspack_plugin_html::config::HtmlRspackPluginBaseOptions; 10 | use rspack_plugin_html::config::HtmlRspackPluginOptions; 11 | use rspack_plugin_html::config::HtmlScriptLoading; 12 | use rspack_plugin_html::config::TemplateParameterFn; 13 | use rspack_plugin_html::config::TemplateParameters; 14 | use rspack_plugin_html::config::TemplateRenderFn; 15 | use rspack_plugin_html::sri::HtmlSriHashFunction; 16 | 17 | pub type RawHtmlScriptLoading = String; 18 | pub type RawHtmlInject = String; 19 | pub type RawHtmlSriHashFunction = String; 20 | pub type RawHtmlFilename = Vec; 21 | type RawChunkSortMode = String; 22 | 23 | type RawTemplateRenderFn = ThreadsafeFunction; 24 | 25 | type RawTemplateParameter = 26 | Either3, bool, ThreadsafeFunction>; 27 | 28 | #[derive(Debug)] 29 | #[napi(object, object_to_js = false)] 30 | pub struct RawHtmlRspackPluginOptions { 31 | /// emitted file name in output path 32 | #[napi(ts_type = "string[]")] 33 | pub filename: Option, 34 | /// template html file 35 | pub template: Option, 36 | #[napi(ts_type = "(data: string) => Promise")] 37 | pub template_fn: Option, 38 | pub template_content: Option, 39 | #[napi(ts_type = "boolean | Record | ((params: string) => Promise)")] 40 | pub template_parameters: Option, 41 | /// "head", "body" or "false" 42 | #[napi(ts_type = "\"head\" | \"body\" | \"false\"")] 43 | pub inject: RawHtmlInject, 44 | /// path or `auto` 45 | pub public_path: Option, 46 | /// `blocking`, `defer`, `module` or `systemjs-module` 47 | #[napi(ts_type = "\"blocking\" | \"defer\" | \"module\" | \"systemjs-module\"")] 48 | pub script_loading: RawHtmlScriptLoading, 49 | 50 | /// entry_chunk_name (only entry chunks are supported) 51 | pub chunks: Option>, 52 | pub exclude_chunks: Option>, 53 | #[napi(ts_type = "\"auto\" | \"manual\"")] 54 | pub chunks_sort_mode: RawChunkSortMode, 55 | 56 | #[napi(ts_type = "\"sha256\" | \"sha384\" | \"sha512\"")] 57 | pub sri: Option, 58 | pub minify: Option, 59 | pub title: Option, 60 | pub favicon: Option, 61 | pub meta: Option>>, 62 | pub hash: Option, 63 | pub base: Option, 64 | } 65 | 66 | impl From for HtmlRspackPluginOptions { 67 | fn from(value: RawHtmlRspackPluginOptions) -> Self { 68 | let inject = HtmlInject::from_str(&value.inject).expect("Invalid inject value"); 69 | 70 | let script_loading = 71 | HtmlScriptLoading::from_str(&value.script_loading).expect("Invalid script_loading value"); 72 | 73 | let chunks_sort_mode = 74 | HtmlChunkSortMode::from_str(&value.chunks_sort_mode).expect("Invalid chunks_sort_mode value"); 75 | 76 | let sri = value.sri.as_ref().map(|s| { 77 | HtmlSriHashFunction::from_str(s).unwrap_or_else(|_| panic!("Invalid sri value: {s}")) 78 | }); 79 | 80 | HtmlRspackPluginOptions { 81 | filename: value 82 | .filename 83 | .unwrap_or_else(|| vec![String::from("index.html")]), 84 | template: value.template, 85 | template_fn: value.template_fn.map(|func| TemplateRenderFn { 86 | inner: Box::new(move |data| { 87 | let f = func.clone(); 88 | Box::pin(async move { f.call(data).await }) 89 | }), 90 | }), 91 | template_content: value.template_content, 92 | template_parameters: match value.template_parameters { 93 | Some(parameters) => match parameters { 94 | Either3::A(data) => TemplateParameters::Map(data), 95 | Either3::B(enabled) => { 96 | if enabled { 97 | TemplateParameters::Map(Default::default()) 98 | } else { 99 | TemplateParameters::Disabled 100 | } 101 | } 102 | Either3::C(func) => TemplateParameters::Function(TemplateParameterFn { 103 | inner: Box::new(move |data| { 104 | let f = func.clone(); 105 | Box::pin(async move { f.call(data).await }) 106 | }), 107 | }), 108 | }, 109 | None => TemplateParameters::Map(Default::default()), 110 | }, 111 | inject, 112 | public_path: value.public_path, 113 | script_loading, 114 | chunks: value.chunks, 115 | exclude_chunks: value.exclude_chunks, 116 | chunks_sort_mode, 117 | sri, 118 | minify: value.minify, 119 | title: value.title, 120 | favicon: value.favicon, 121 | meta: value.meta, 122 | hash: value.hash, 123 | base: value.base.map(|v| v.into()), 124 | } 125 | } 126 | } 127 | 128 | #[derive(Debug)] 129 | #[napi(object)] 130 | pub struct RawHtmlRspackPluginBaseOptions { 131 | pub href: Option, 132 | #[napi(ts_type = "\"_self\" | \"_blank\" | \"_parent\" | \"_top\"")] 133 | pub target: Option, 134 | } 135 | 136 | impl From for HtmlRspackPluginBaseOptions { 137 | fn from(value: RawHtmlRspackPluginBaseOptions) -> Self { 138 | HtmlRspackPluginBaseOptions { 139 | href: value.href, 140 | target: value.target, 141 | } 142 | } 143 | } 144 | -------------------------------------------------------------------------------- /crates/binding_values/src/raw_options/raw_builtins/raw_copy.rs: -------------------------------------------------------------------------------- 1 | use cow_utils::CowUtils; 2 | use derive_more::Debug; 3 | use napi::{bindgen_prelude::Buffer, Either}; 4 | use napi_derive::napi; 5 | use rspack_core::rspack_sources::RawSource; 6 | use rspack_napi::threadsafe_function::ThreadsafeFunction; 7 | use rspack_plugin_copy::{ 8 | CopyGlobOptions, CopyPattern, CopyRspackPluginOptions, Info, Related, ToOption, ToType, 9 | Transformer, 10 | }; 11 | 12 | type RawTransformer = ThreadsafeFunction<(Buffer, String), Either>; 13 | 14 | type RawToFn = ThreadsafeFunction; 15 | 16 | type RawTo = Either; 17 | 18 | #[derive(Debug, Clone)] 19 | #[napi(object)] 20 | pub struct RawToOptions { 21 | pub context: String, 22 | pub absolute_filename: String, 23 | } 24 | 25 | #[derive(Debug, Clone)] 26 | #[napi(object, object_to_js = false)] 27 | pub struct RawCopyPattern { 28 | pub from: String, 29 | #[debug(skip)] 30 | #[napi( 31 | ts_type = "string | ((pathData: { context: string; absoluteFilename?: string }) => string | Promise)" 32 | )] 33 | pub to: Option, 34 | pub context: Option, 35 | pub to_type: Option, 36 | pub no_error_on_missing: bool, 37 | pub force: bool, 38 | pub priority: i32, 39 | pub glob_options: RawCopyGlobOptions, 40 | pub info: Option, 41 | #[debug(skip)] 42 | #[napi( 43 | ts_type = "(input: Buffer, absoluteFilename: string) => string | Buffer | Promise | Promise" 44 | )] 45 | pub transform: Option, 46 | } 47 | 48 | #[derive(Debug, Clone)] 49 | #[napi(object)] 50 | pub struct RawInfo { 51 | pub immutable: Option, 52 | pub minimized: Option, 53 | pub chunk_hash: Option>, 54 | pub content_hash: Option>, 55 | pub development: Option, 56 | pub hot_module_replacement: Option, 57 | pub related: Option, 58 | pub version: Option, 59 | } 60 | 61 | #[derive(Debug, Clone)] 62 | #[napi(object)] 63 | pub struct RawRelated { 64 | pub source_map: Option, 65 | } 66 | 67 | #[derive(Debug, Clone)] 68 | #[napi(object)] 69 | pub struct RawCopyGlobOptions { 70 | pub case_sensitive_match: Option, 71 | pub dot: Option, 72 | pub ignore: Option>, 73 | } 74 | 75 | #[derive(Debug)] 76 | #[napi(object, object_to_js = false)] 77 | pub struct RawCopyRspackPluginOptions { 78 | pub patterns: Vec, 79 | } 80 | 81 | impl From for CopyPattern { 82 | fn from(value: RawCopyPattern) -> Self { 83 | let RawCopyPattern { 84 | from, 85 | to, 86 | context, 87 | to_type, 88 | no_error_on_missing, 89 | force, 90 | priority, 91 | glob_options, 92 | info, 93 | transform, 94 | } = value; 95 | 96 | Self { 97 | from, 98 | to: to.map(|to| match to { 99 | Either::A(s) => ToOption::String(s), 100 | Either::B(f) => ToOption::Fn(Box::new(move |ctx| { 101 | let f = f.clone(); 102 | Box::pin(async move { 103 | f.call(RawToOptions { 104 | context: ctx.context.as_str().to_owned(), 105 | absolute_filename: ctx.absolute_filename.as_str().to_owned(), 106 | }) 107 | .await 108 | }) 109 | })), 110 | }), 111 | context: context.map(Into::into), 112 | to_type: if let Some(to_type) = to_type { 113 | match to_type.cow_to_lowercase().as_ref() { 114 | "dir" => Some(ToType::Dir), 115 | "file" => Some(ToType::File), 116 | "template" => Some(ToType::Template), 117 | _ => { 118 | //TODO how should we handle wrong input ? 119 | None 120 | } 121 | } 122 | } else { 123 | None 124 | }, 125 | no_error_on_missing, 126 | info: info.map(Into::into), 127 | force, 128 | priority, 129 | glob_options: CopyGlobOptions { 130 | case_sensitive_match: glob_options.case_sensitive_match, 131 | dot: glob_options.dot, 132 | ignore: glob_options.ignore.map(|ignore| { 133 | ignore 134 | .into_iter() 135 | .map(|filter| glob::Pattern::new(filter.as_ref()).expect("Invalid pattern option")) 136 | .collect() 137 | }), 138 | }, 139 | transform: transform.map(|transformer| { 140 | Transformer::Fn(Box::new(move |input, absolute_filename| { 141 | let f = transformer.clone(); 142 | 143 | fn convert_to_enum(input: Either) -> RawSource { 144 | match input { 145 | Either::A(s) => RawSource::from(s), 146 | Either::B(b) => RawSource::from(Vec::::from(b)), 147 | } 148 | } 149 | 150 | Box::pin(async move { 151 | f.call((input.into(), absolute_filename.to_owned())) 152 | .await 153 | .map(convert_to_enum) 154 | }) 155 | })) 156 | }), 157 | } 158 | } 159 | } 160 | 161 | impl From for CopyRspackPluginOptions { 162 | fn from(val: RawCopyRspackPluginOptions) -> Self { 163 | Self { 164 | patterns: val.patterns.into_iter().map(Into::into).collect(), 165 | } 166 | } 167 | } 168 | 169 | impl From for Info { 170 | fn from(value: RawInfo) -> Self { 171 | Self { 172 | immutable: value.immutable, 173 | minimized: value.minimized, 174 | chunk_hash: value.chunk_hash, 175 | content_hash: value.content_hash, 176 | development: value.development, 177 | hot_module_replacement: value.hot_module_replacement, 178 | related: value.related.map(|r| Related { 179 | source_map: r.source_map, 180 | }), 181 | version: value.version, 182 | } 183 | } 184 | } 185 | -------------------------------------------------------------------------------- /crates/binding_values/src/raw_options/raw_builtins/raw_mf.rs: -------------------------------------------------------------------------------- 1 | use std::sync::Arc; 2 | 3 | use napi::Either; 4 | use napi_derive::napi; 5 | use rspack_plugin_mf::{ 6 | ConsumeOptions, ConsumeSharedPluginOptions, ConsumeVersion, ContainerPluginOptions, 7 | ContainerReferencePluginOptions, ExposeOptions, ProvideOptions, ProvideVersion, RemoteOptions, 8 | }; 9 | 10 | use crate::{ 11 | entry::{JsEntryRuntime, JsEntryRuntimeWrapper}, 12 | library::JsLibraryOptions, 13 | }; 14 | 15 | #[derive(Debug)] 16 | #[napi(object)] 17 | pub struct RawContainerPluginOptions { 18 | pub name: String, 19 | pub share_scope: String, 20 | pub library: JsLibraryOptions, 21 | #[napi(ts_type = "false | string")] 22 | pub runtime: Option, 23 | pub filename: Option, 24 | pub exposes: Vec, 25 | pub enhanced: bool, 26 | } 27 | 28 | impl From for ContainerPluginOptions { 29 | fn from(value: RawContainerPluginOptions) -> Self { 30 | Self { 31 | name: value.name, 32 | share_scope: value.share_scope, 33 | library: value.library.into(), 34 | runtime: value.runtime.map(|r| JsEntryRuntimeWrapper(r).into()), 35 | filename: value.filename.map(|f| f.into()), 36 | exposes: value.exposes.into_iter().map(|e| e.into()).collect(), 37 | enhanced: value.enhanced, 38 | } 39 | } 40 | } 41 | 42 | #[derive(Debug, Clone)] 43 | #[napi(object)] 44 | pub struct RawExposeOptions { 45 | pub key: String, 46 | pub name: Option, 47 | pub import: Vec, 48 | } 49 | 50 | impl From for (String, ExposeOptions) { 51 | fn from(value: RawExposeOptions) -> Self { 52 | ( 53 | value.key, 54 | ExposeOptions { 55 | name: value.name, 56 | import: value.import, 57 | }, 58 | ) 59 | } 60 | } 61 | 62 | #[derive(Debug)] 63 | #[napi(object)] 64 | pub struct RawContainerReferencePluginOptions { 65 | pub remote_type: String, 66 | pub remotes: Vec, 67 | pub share_scope: Option, 68 | pub enhanced: bool, 69 | } 70 | 71 | impl From for ContainerReferencePluginOptions { 72 | fn from(value: RawContainerReferencePluginOptions) -> Self { 73 | Self { 74 | remote_type: value.remote_type, 75 | remotes: value.remotes.into_iter().map(|e| e.into()).collect(), 76 | share_scope: value.share_scope, 77 | enhanced: value.enhanced, 78 | } 79 | } 80 | } 81 | 82 | #[derive(Debug)] 83 | #[napi(object)] 84 | pub struct RawRemoteOptions { 85 | pub key: String, 86 | pub external: Vec, 87 | pub share_scope: String, 88 | } 89 | 90 | impl From for (String, RemoteOptions) { 91 | fn from(value: RawRemoteOptions) -> Self { 92 | ( 93 | value.key, 94 | RemoteOptions { 95 | external: value.external, 96 | share_scope: value.share_scope, 97 | }, 98 | ) 99 | } 100 | } 101 | 102 | #[derive(Debug)] 103 | #[napi(object)] 104 | pub struct RawProvideOptions { 105 | pub key: String, 106 | pub share_key: String, 107 | pub share_scope: String, 108 | #[napi(ts_type = "string | false | undefined")] 109 | pub version: Option, 110 | pub eager: bool, 111 | pub singleton: Option, 112 | #[napi(ts_type = "string | false | undefined")] 113 | pub required_version: Option, 114 | pub strict_version: Option, 115 | } 116 | 117 | impl From for (String, ProvideOptions) { 118 | fn from(value: RawProvideOptions) -> Self { 119 | ( 120 | value.key, 121 | ProvideOptions { 122 | share_key: value.share_key, 123 | share_scope: value.share_scope, 124 | version: value.version.map(|v| RawVersionWrapper(v).into()), 125 | eager: value.eager, 126 | singleton: value.singleton, 127 | required_version: value.required_version.map(|v| RawVersionWrapper(v).into()), 128 | strict_version: value.strict_version, 129 | }, 130 | ) 131 | } 132 | } 133 | 134 | #[derive(Debug)] 135 | #[napi(object)] 136 | pub struct RawConsumeSharedPluginOptions { 137 | pub consumes: Vec, 138 | pub enhanced: bool, 139 | } 140 | 141 | impl From for ConsumeSharedPluginOptions { 142 | fn from(value: RawConsumeSharedPluginOptions) -> Self { 143 | Self { 144 | consumes: value 145 | .consumes 146 | .into_iter() 147 | .map(|c| c.into()) 148 | .map(|(k, v)| (k, Arc::new(v))) 149 | .collect(), 150 | enhanced: value.enhanced, 151 | } 152 | } 153 | } 154 | 155 | #[derive(Debug)] 156 | #[napi(object)] 157 | pub struct RawConsumeOptions { 158 | pub key: String, 159 | pub import: Option, 160 | pub import_resolved: Option, 161 | pub share_key: String, 162 | pub share_scope: String, 163 | #[napi(ts_type = "string | false | undefined")] 164 | pub required_version: Option, 165 | pub package_name: Option, 166 | pub strict_version: bool, 167 | pub singleton: bool, 168 | pub eager: bool, 169 | } 170 | 171 | impl From for (String, ConsumeOptions) { 172 | fn from(value: RawConsumeOptions) -> Self { 173 | ( 174 | value.key, 175 | ConsumeOptions { 176 | import: value.import, 177 | import_resolved: value.import_resolved, 178 | share_key: value.share_key, 179 | share_scope: value.share_scope, 180 | required_version: value.required_version.map(|v| RawVersionWrapper(v).into()), 181 | package_name: value.package_name, 182 | strict_version: value.strict_version, 183 | singleton: value.singleton, 184 | eager: value.eager, 185 | }, 186 | ) 187 | } 188 | } 189 | 190 | pub type RawVersion = Either; 191 | 192 | struct RawVersionWrapper(RawVersion); 193 | 194 | impl From for ProvideVersion { 195 | fn from(value: RawVersionWrapper) -> Self { 196 | match value.0 { 197 | Either::A(s) => ProvideVersion::Version(s), 198 | Either::B(_) => ProvideVersion::False, 199 | } 200 | } 201 | } 202 | 203 | impl From for ConsumeVersion { 204 | fn from(value: RawVersionWrapper) -> Self { 205 | match value.0 { 206 | Either::A(s) => ConsumeVersion::Version(s), 207 | Either::B(_) => ConsumeVersion::False, 208 | } 209 | } 210 | } 211 | --------------------------------------------------------------------------------