Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: support webpackInclude and webpackExclude #7055

Merged
merged 4 commits into from
Jul 5, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
34 changes: 27 additions & 7 deletions crates/rspack_core/src/context_module.rs
Original file line number Diff line number Diff line change
Expand Up @@ -129,8 +129,10 @@ pub struct ContextOptions {
pub recursive: bool,
#[derivative(Hash = "ignore", PartialEq = "ignore")]
pub reg_exp: Option<RspackRegex>,
pub include: Option<String>,
pub exclude: Option<String>,
#[derivative(Hash = "ignore", PartialEq = "ignore")]
pub include: Option<RspackRegex>,
#[derivative(Hash = "ignore", PartialEq = "ignore")]
pub exclude: Option<RspackRegex>,
pub category: DependencyCategory,
pub request: String,
pub context: String,
Expand Down Expand Up @@ -1000,8 +1002,19 @@ impl ContextModule {
if !dir.is_dir() {
return Ok(());
}
let include = &options.context_options.include;
let exclude = &options.context_options.exclude;
for entry in fs::read_dir(dir).into_diagnostic()? {
let path = entry.into_diagnostic()?.path();
let path_str = path.to_string_lossy().to_string();

if let Some(exclude) = exclude
&& exclude.test(&path_str)
{
// ignore excluded files
continue;
}

if path.is_dir() {
if options.context_options.recursive {
Self::visit_dirs(ctx, &path, dependencies, options, resolve_options)?;
Expand All @@ -1013,11 +1026,17 @@ impl ContextModule {
// ignore hidden files
continue;
} else {
if let Some(include) = include
&& !include.test(&path_str)
{
// ignore not included files
continue;
}

// FIXME: nodejs resolver return path of context, sometimes is '/a/b', sometimes is '/a/b/'
let relative_path = {
let p = path
.to_string_lossy()
.to_string()
let p = path_str
.clone()
.drain(ctx.len()..)
.collect::<String>()
.replace('\\', "/");
Expand All @@ -1027,6 +1046,7 @@ impl ContextModule {
format!("./{p}")
}
};

let requests = alternative_requests(
resolve_options,
vec![AlternativeRequest::new(ctx.to_string(), relative_path)],
Expand Down Expand Up @@ -1191,11 +1211,11 @@ fn create_identifier(options: &ContextModuleOptions) -> Identifier {
}
if let Some(include) = &options.context_options.include {
id += "|include: ";
id += &include;
id += &include.to_source_string();
}
if let Some(exclude) = &options.context_options.exclude {
id += "|exclude: ";
id += &exclude;
id += &exclude.to_source_string();
}
if let Some(GroupOptions::ChunkGroup(group)) = &options.context_options.group_options {
if let Some(chunk_name) = &group.name {
Expand Down
12 changes: 10 additions & 2 deletions crates/rspack_plugin_javascript/src/dependency/context/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,16 @@ fn create_resource_identifier_for_context_dependency(
.as_ref()
.map(|r| r.to_source_string())
.unwrap_or_default();
let include = options.include.as_deref().unwrap_or_default();
let exclude = options.exclude.as_deref().unwrap_or_default();
let include = options
.include
.as_ref()
.map(|x| x.to_source_string())
.unwrap_or_default();
let exclude = options
.exclude
.as_ref()
.map(|x| x.to_source_string())
.unwrap_or_default();
let mode = options.mode.as_str();
// TODO: need `RawChunkGroupOptions`
let id = format!(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,8 @@ impl JavascriptParserPlugin for ImportParserPlugin {
.get_fetch_priority()
.map(|x| DynamicImportFetchPriority::from(x.as_str()))
.or(dynamic_import_fetch_priority);
let include = magic_comment_options.get_webpack_include();
let exclude = magic_comment_options.get_webpack_exclude();

let param = parser.evaluate_expression(dyn_imported.expr.as_ref());

Expand Down Expand Up @@ -131,8 +133,8 @@ impl JavascriptParserPlugin for ImportParserPlugin {
mode: mode.into(),
recursive: true,
reg_exp,
include: None,
exclude: None,
include,
exclude,
category: DependencyCategory::Esm,
request: format!("{}{}{}", context.clone(), query, fragment),
context,
Expand Down
80 changes: 79 additions & 1 deletion crates/rspack_plugin_javascript/src/webpack_comment.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
use once_cell::sync::Lazy;
use regex::Captures;
use rspack_error::miette::{Diagnostic, Severity};
use rspack_regex::RspackRegex;
use rustc_hash::{FxHashMap, FxHashSet};
use swc_core::common::comments::{Comment, CommentKind, Comments};
use swc_core::common::{SourceFile, Span};
Expand All @@ -14,6 +15,10 @@ pub enum WebpackComment {
Preload,
Ignore,
FetchPriority,
IncludeRegexp,
IncludeFlags,
ExcludeRegexp,
ExcludeFlags,
Mode,
}

Expand Down Expand Up @@ -60,6 +65,36 @@ impl WebpackCommentMap {
pub fn get_fetch_priority(&self) -> Option<&String> {
self.0.get(&WebpackComment::FetchPriority)
}

pub fn get_webpack_include(&self) -> Option<RspackRegex> {
self.0.get(&WebpackComment::IncludeRegexp).map(|expr| {
let flags = self
.0
.get(&WebpackComment::IncludeFlags)
.map(|x| x.as_str())
.unwrap_or_default();

RspackRegex::with_flags(expr, flags).unwrap_or_else(|_| {
// test when capture
unreachable!();
})
})
}

pub fn get_webpack_exclude(&self) -> Option<RspackRegex> {
self.0.get(&WebpackComment::ExcludeRegexp).map(|expr| {
let flags = self
.0
.get(&WebpackComment::ExcludeFlags)
.map(|x| x.as_str())
.unwrap_or_default();

RspackRegex::with_flags(expr, flags).unwrap_or_else(|_| {
// test when capture
unreachable!();
})
})
}
}

fn add_magic_comment_warning(
Expand Down Expand Up @@ -91,9 +126,10 @@ fn add_magic_comment_warning(
// _3 for `xxx`
// _4 for number
// _5 for true/false
// _6 for regexp
// TODO: regexp/array
static WEBPACK_MAGIC_COMMENT_REGEXP: Lazy<regex::Regex> = Lazy::new(|| {
regex::Regex::new(r#"(?P<_0>webpack[a-zA-Z\d_-]+)\s*:\s*("(?P<_1>[^"]+)"|'(?P<_2>[^']+)'|`(?P<_3>[^`]+)`|(?P<_4>[\d.-]+)|(?P<_5>true|false))"#)
regex::Regex::new(r#"(?P<_0>webpack[a-zA-Z\d_-]+)\s*:\s*("(?P<_1>[^"]+)"|'(?P<_2>[^']+)'|`(?P<_3>[^`]+)`|(?P<_4>[\d.-]+)|(?P<_5>true|false)|(?P<_6>/([^,]+)/([dgimsuvy]*)))"#)
.expect("invalid regex")
});

Expand Down Expand Up @@ -260,6 +296,48 @@ fn analyze_comments(
}
}
}
"webpackInclude" => {
if captures.name("_6").is_some() {
if let Some(regexp) = captures.get(9).map(|x| x.as_str()) {
let flags = captures.get(10).map(|x| x.as_str()).unwrap_or_default();
if RspackRegex::with_flags(regexp, flags).is_ok() {
result.insert(WebpackComment::IncludeRegexp, regexp.to_string());
result.insert(WebpackComment::IncludeFlags, flags.to_string());
return;
} else {
add_magic_comment_warning(
source_file,
item_name,
r#"a regular expression"#,
&captures,
warning_diagnostics,
error_span,
);
}
}
}
}
"webpackExclude" => {
if captures.name("_6").is_some() {
if let Some(regexp) = captures.get(9).map(|x| x.as_str()) {
let flags = captures.get(10).map(|x| x.as_str()).unwrap_or_default();
if RspackRegex::with_flags(regexp, flags).is_ok() {
result.insert(WebpackComment::ExcludeRegexp, regexp.to_string());
result.insert(WebpackComment::ExcludeFlags, flags.to_string());
return;
} else {
add_magic_comment_warning(
source_file,
item_name,
r#"a regular expression"#,
&captures,
warning_diagnostics,
error_span,
);
}
}
}
}
_ => {
// TODO: other magic comment
}
Expand Down
16 changes: 16 additions & 0 deletions tests/webpack-test/__snapshots__/StatsTestCases.basictest.js.snap
Original file line number Diff line number Diff line change
@@ -1,5 +1,21 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP

exports[`StatsTestCases should print correct stats for import-context-filter 1`] = `
"asset entry.js 9.65 KiB [emitted] (name: entry)
asset 228.js 426 bytes [emitted]
asset 271.js 426 bytes [emitted]
asset 536.js 426 bytes [emitted]
Entrypoint entry 9.65 KiB = entry.js
runtime modules 6.94 KiB 9 modules
cacheable modules 724 bytes
./entry.js 450 bytes [built] [code generated]
Xdir/import-context-filter/templates|lazy|/^\\\\.\\\\/.*$/|exclude: /\\\\.noimport\\\\.js$/|groupOptions: {}|namespace object 160 bytes [built] [code generated]
./templates/bar.js 38 bytes [built] [code generated]
./templates/baz.js 38 bytes [built] [code generated]
./templates/foo.js 38 bytes [built] [code generated]
Rspack x.x.x compiled successfully in X s"
`;

exports[`StatsTestCases should print correct stats for performance-different-mode-and-target 1`] = `
"asset warning.pro-web.js 294 KiB [emitted] (name: main)
Entrypoint main 294 KiB = warning.pro-web.js
Expand Down

This file was deleted.

35 changes: 32 additions & 3 deletions website/docs/en/api/modules/module-methods.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -103,15 +103,24 @@ import(`./locale/${language}.json`).then(module => {
Inline comments to make features work. By adding comments to the import, we can do things such as name our chunk or select different modes. For a full list of these magic comments see the code below followed by an explanation of what these comments do.

```js
// Single target
import(
/* webpackChunkName: "my-chunk-name" */
/* webpackPrefetch: true */
/* webpackPreload: true */
/* webpackMode: "lazy" */
/* webpackIgnore: true */
/* webpackFetchPriority: "high" */
'module'
);

// Multiple possible targets
import(
/* webpackInclude: /\.json$/ */
/* webpackExclude: /\.noimport\.json$/ */
/* webpackChunkName: "my-chunk-name" */
/* webpackMode: "lazy" */
/* webpackPrefetch: true */
/* webpackPreload: true */
`./locale/${language}`
);
```

##### webpackIgnore
Expand Down Expand Up @@ -171,6 +180,26 @@ A name for the new chunk.

Set [`fetchPriority`](https://developer.mozilla.org/en-US/docs/Web/API/HTMLImageElement/fetchPriority) for specific dynamic imports. It's also possible to set a global default value for all dynamic imports by using the `module.parser.javascript.dynamicImportFetchPriority` option.

##### webpackInclude

<ApiMeta addedVersion="1.0.0" />

- **Type:**: `Regexp`

A regular expression that will be matched against during import resolution. Only modules that match **will be bundled**.

##### webpackExclude

<ApiMeta addedVersion="1.0.0" />

- **Type:**: `Regexp`

A regular expression that will be matched against during import resolution. Any module that matches **will not be bundled**.

:::info
Note that `webpackInclude` and `webpackExclude` options do not interfere with the prefix. eg: `./locale`.
:::

## CommonJS

Rspack is also support `CommonJS` syntax natively, you can use `require` and `module.exports` methods.
Expand Down
35 changes: 32 additions & 3 deletions website/docs/zh/api/modules/module-methods.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -101,15 +101,24 @@ import(`./locale/${language}.json`).then(module => {
通过向 import 语句添加注释,我们可以执行诸如命名 chunk 或选择不同模式等操作。有关这些魔法注释的完整列表,请参见下面的代码,以及对这些注释功能的解释。

```js
// 单个模块
import(
/* webpackChunkName: "my-chunk-name" */
/* webpackPrefetch: true */
/* webpackPreload: true */
/* webpackMode: "lazy" */
/* webpackIgnore: true */
/* webpackFetchPriority: "high" */
'module'
);

// 多个可能模块
import(
/* webpackInclude: /\.json$/ */
/* webpackExclude: /\.noimport\.json$/ */
/* webpackChunkName: "my-chunk-name" */
/* webpackMode: "lazy" */
/* webpackPrefetch: true */
/* webpackPreload: true */
`./locale/${language}`
);
```

##### webpackIgnore
Expand Down Expand Up @@ -169,6 +178,26 @@ import(

为指定的动态导入设置 [`fetchPriority`](https://developer.mozilla.org/en-US/docs/Web/API/HTMLImageElement/fetchPriority)。也可以通过使用 `module.parser.javascript.dynamicImportFetchPriority` 选项为所有动态导入设置全局默认值。

##### webpackInclude

<ApiMeta addedVersion="1.0.0" />

- **类型:**: `Regexp`

在导入解析时匹配的正则表达式。只有匹配的模块**才会被打包**。

##### webpackExclude

<ApiMeta addedVersion="1.0.0" />

- **类型:**: `Regexp`

在导入解析时匹配的正则表达式。只有匹配的模块**不会被打包**。

:::info
请注意,`webpackInclude` 和 `webpackExclude` 选项不会影响前缀。例如:`./locale`。
:::

## CommonJS

Rspack 也支持 `CommonJS` 语法,可以使用 `require` 和 `module.exports` 语法。
Expand Down
2 changes: 2 additions & 0 deletions website/project-words.txt
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,7 @@ NAPI-RS
nativizing
nestjs
NestJS
noimport
nosources
nwjs
optionsrspackexperiments
Expand Down Expand Up @@ -162,3 +163,4 @@ xxhash
Zack
Zack Jackson
zackarychapple

Loading