Skip to content

Commit

Permalink
feat: support minifyoptions extract comments (web-infra-dev#2882)
Browse files Browse the repository at this point in the history
* chore: init

* feat: support regex match

* chore: clear comments in minimized file

* chore: update

* chore: no license no LICENSE.TXT

* test: add test cases

* test: add comments for expected

* chore: update

* chore: remove comments

* chore: add expected file

* chore: add comments

* chore: changeset

* chore: comment

* chore: update snapshots

* chore: update

* chore: resolve config

* chore: update filename

* chore: update name

* chore: update name
  • Loading branch information
faga295 authored Apr 27, 2023
1 parent 088220b commit 2bb2bcd
Show file tree
Hide file tree
Showing 16 changed files with 337 additions and 116 deletions.
6 changes: 6 additions & 0 deletions .changeset/curvy-pants-tap.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
"@rspack/binding": patch
"@rspack/core": patch
---

add extractComments option
1 change: 1 addition & 0 deletions crates/node_binding/binding.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -137,6 +137,7 @@ export interface RawMinification {
passes: number
dropConsole: boolean
pureFuncs: Array<string>
extractComments?: string
}
export interface RawPresetEnv {
targets: Array<string>
Expand Down
2 changes: 2 additions & 0 deletions crates/rspack_binding_options/src/options/raw_builtins/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ pub struct RawMinification {
pub passes: u32,
pub drop_console: bool,
pub pure_funcs: Vec<String>,
pub extract_comments: Option<String>,
}

#[derive(Debug, Deserialize)]
Expand All @@ -68,6 +69,7 @@ impl From<RawMinification> for Minification {
passes: value.passes as usize,
drop_console: value.drop_console,
pure_funcs: value.pure_funcs,
extract_comments: value.extract_comments,
}
}
}
Expand Down
1 change: 1 addition & 0 deletions crates/rspack_core/src/options/builtins.rs
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@ pub struct Minification {
pub passes: usize,
pub drop_console: bool,
pub pure_funcs: Vec<String>,
pub extract_comments: Option<String>,
}

#[derive(Debug, Copy, Clone, Default)]
Expand Down
85 changes: 76 additions & 9 deletions crates/rspack_plugin_javascript/src/ast/minify.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,13 @@
use std::sync::{mpsc, Arc};
use std::{
collections::HashMap,
sync::{mpsc, Arc, Mutex},
};

use rspack_core::ModuleType;
use regex::Regex;
use rspack_core::{
rspack_sources::{ConcatSource, RawSource, Source, SourceExt},
ModuleType,
};
use rspack_error::{internal_error, DiagnosticKind, Error, Result, TraceableError};
use swc_core::{
base::{
Expand Down Expand Up @@ -31,11 +38,17 @@ use swc_core::{
},
};

use super::stringify::print;
use super::{parse::parse_js, stringify::SourceMapConfig};
use crate::utils::ecma_parse_error_to_rspack_error;

pub fn minify(opts: &JsMinifyOptions, input: String, filename: &str) -> Result<TransformOutput> {
use super::parse::parse_js;
use super::stringify::{print, SourceMapConfig};
use crate::{utils::ecma_parse_error_to_rspack_error, ExtractedCommentsInfo};

pub fn minify(
opts: &JsMinifyOptions,
input: String,
filename: &str,
all_extract_comments: &Mutex<HashMap<String, ExtractedCommentsInfo>>,
extract_comments: &Option<String>,
) -> Result<TransformOutput> {
let cm: Arc<SourceMap> = Default::default();
GLOBALS.set(&Default::default(), || -> Result<TransformOutput> {
with_rspack_error_handler(
Expand Down Expand Up @@ -103,7 +116,7 @@ pub fn minify(opts: &JsMinifyOptions, input: String, filename: &str) -> Result<T
}
}

let comments = SingleThreadedComments::default();
let mut comments = SingleThreadedComments::default();

let program = parse_js(
fm.clone(),
Expand Down Expand Up @@ -173,7 +186,61 @@ pub fn minify(opts: &JsMinifyOptions, input: String, filename: &str) -> Result<T
.clone()
.into_inner()
.unwrap_or(BoolOr::Data(JsMinifyCommentOption::PreserveSomeComments));
minify_file_comments(&comments, preserve_comments);

if let Some(extract_comments) = extract_comments {
let comments_file_name = filename.to_string() + ".LICENSE.txt";
let reg = if extract_comments.eq("true") {
// copied from terser-webpack-plugin
Regex::new(r"@preserve|@lic|@cc_on|^\**!")
} else {
Regex::new(&extract_comments[1..extract_comments.len() - 2])
}
.expect("Invalid extractComments");
let mut source = ConcatSource::default();
// add all matched comments to source
{
let (l, t) = comments.borrow_all();

l.iter().for_each(|(_, vc)| {
vc.iter().for_each(|c| {
if reg.is_match(&c.text) {
source.add(RawSource::from("/*"));
source.add(RawSource::from(&*c.text));
source.add(RawSource::from("*/"));
source.add(RawSource::from("\n"));
}
});
});
t.iter().for_each(|(_, vc)| {
vc.iter().for_each(|c| {
if reg.is_match(&c.text) {
source.add(RawSource::from("/*"));
source.add(RawSource::from(&*c.text));
source.add(RawSource::from("*/"));
source.add(RawSource::from("\n"));
}
});
});
}
// if not matched comments, we don't need to emit .License.txt file
if source.size() > 0 {
all_extract_comments
.lock()
.expect("all_extract_comments lock failed")
.insert(
filename.to_string(),
ExtractedCommentsInfo {
source: source.boxed(),
comments_file_name,
},
);
}
// if comments are extracted, we don't need to preserve them
comments = SingleThreadedComments::default();
} else {
// if comments are not extracted, then we minify them
minify_file_comments(&comments, preserve_comments);
}

print(
&program,
Expand Down
46 changes: 37 additions & 9 deletions crates/rspack_plugin_javascript/src/plugin.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
use std::collections::HashSet;
use std::collections::{HashMap, HashSet};
use std::hash::{Hash, Hasher};
use std::sync::mpsc;
use std::sync::{mpsc, Mutex};

use async_trait::async_trait;
use linked_hash_set::LinkedHashSet;
Expand All @@ -10,12 +10,12 @@ use rspack_core::rspack_sources::{
SourceMapSourceOptions,
};
use rspack_core::{
get_js_chunk_filename_template, AstOrSource, ChunkHashArgs, ChunkKind, ChunkUkey, Compilation,
DependencyType, GenerateContext, GenerationResult, JsChunkHashArgs, Module, ModuleAst,
ModuleType, ParseContext, ParseResult, ParserAndGenerator, PathData, Plugin,
PluginChunkHashHookOutput, PluginContext, PluginJsChunkHashHookOutput, PluginProcessAssetsOutput,
PluginRenderManifestHookOutput, ProcessAssetsArgs, RenderArgs, RenderChunkArgs,
RenderManifestEntry, RenderStartupArgs, RuntimeGlobals, SourceType,
get_js_chunk_filename_template, AssetInfo, AstOrSource, ChunkHashArgs, ChunkKind, ChunkUkey,
Compilation, CompilationAsset, DependencyType, GenerateContext, GenerationResult,
JsChunkHashArgs, Module, ModuleAst, ModuleType, ParseContext, ParseResult, ParserAndGenerator,
PathData, Plugin, PluginChunkHashHookOutput, PluginContext, PluginJsChunkHashHookOutput,
PluginProcessAssetsOutput, PluginRenderManifestHookOutput, ProcessAssetsArgs, RenderArgs,
RenderChunkArgs, RenderManifestEntry, RenderStartupArgs, RuntimeGlobals, SourceType,
};
use rspack_error::{
internal_error, Diagnostic, IntoTWithDiagnosticArray, Result, TWithDiagnosticArray,
Expand Down Expand Up @@ -642,6 +642,9 @@ impl Plugin for JsPlugin {

if let Some(minify_options) = minify_options {
let (tx, rx) = mpsc::channel::<Vec<Diagnostic>>();
// collect all extracted comments info
let all_extracted_comments = Mutex::new(HashMap::new());
let extract_comments = &minify_options.extract_comments.clone();
let emit_source_map_columns = !compilation.options.devtool.cheap();
let compress = TerserCompressorOptions {
passes: minify_options.passes,
Expand Down Expand Up @@ -671,7 +674,7 @@ impl Plugin for JsPlugin {
inline_sources_content: true, // Using true so original_source can be None in SourceMapSource
emit_source_map_columns,
..Default::default()
}, input, filename) {
}, input, filename, &all_extracted_comments, extract_comments) {
Ok(r) => r,
Err(e) => {
tx.send(e.into()).map_err(|e| internal_error!(e.to_string()))?;
Expand Down Expand Up @@ -699,6 +702,25 @@ impl Plugin for JsPlugin {
})?;

compilation.push_batch_diagnostic(rx.into_iter().flatten().collect::<Vec<_>>());

// write all extracted comments to assets
all_extracted_comments
.lock()
.expect("all_extracted_comments lock failed")
.clone()
.into_iter()
.for_each(|(_, comments)| {
compilation.emit_asset(
comments.comments_file_name,
CompilationAsset {
source: Some(comments.source),
info: AssetInfo {
minimized: true,
..Default::default()
},
},
)
});
}

Ok(())
Expand Down Expand Up @@ -761,3 +783,9 @@ impl Plugin for InferAsyncModulesPlugin {
Ok(())
}
}

#[derive(Debug, Clone)]
pub struct ExtractedCommentsInfo {
pub source: BoxSource,
pub comments_file_name: String,
}

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
/**
* Utility functions for the foo package.
* @license Apache-2.0
*/
/* @license */
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
/*! Legal Comment */

/**
* @preserve Copyright 2009 SomeThirdParty.
* Here is the full license text and copyright
* notice for this file. Note that the notice can span several
* lines and is only terminated by the closing star and slash:
*/

/**
* Utility functions for the foo package.
* @license Apache-2.0
*/

/*! Legal Foo */

// Foo

/*
Foo Bar
*/

// @license

/*
* Foo
*/
// @lic
module.exports = Math.random();
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
{
"builtins": {
"minifyOptions": {
"extractComments": "/@license/"
},
"codeGeneration": {
"keepComments": true
}
}
}
3 changes: 3 additions & 0 deletions crates/rspack_testing/src/test_config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -124,6 +124,8 @@ pub struct Minification {
pub drop_console: bool,
#[serde(default)]
pub pure_funcs: Vec<String>,
#[serde(default)]
pub extract_comments: Option<String>,
}

#[derive(Debug, Default, JsonSchema, Deserialize)]
Expand Down Expand Up @@ -422,6 +424,7 @@ impl TestConfig {
passes: op.passes,
drop_console: op.drop_console,
pure_funcs: op.pure_funcs,
extract_comments: op.extract_comments,
}),
preset_env: self.builtins.preset_env.map(Into::into),
code_generation: self.builtins.code_generation.map(|op| c::CodeGeneration {
Expand Down
2 changes: 1 addition & 1 deletion examples/basic/rspack.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,6 @@ module.exports = {
{
template: "./index.html"
}
]
],
}
};
28 changes: 19 additions & 9 deletions packages/rspack/src/config/builtins.ts
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,13 @@ export type CssPluginConfig = {
modules?: Partial<RawCssModulesConfig>;
};

export type MinificationConfig = {
passes?: number;
dropConsole?: boolean;
pureFuncs?: Array<string>;
extractComments?: boolean | RegExp;
};

export interface Builtins {
css?: CssPluginConfig;
postcss?: RawPostCssConfig;
Expand All @@ -57,7 +64,7 @@ export interface Builtins {
provide?: Record<string, string | string[]>;
html?: Array<BuiltinsHtmlPluginConfig>;
decorator?: boolean | Partial<RawDecoratorOptions>;
minifyOptions?: Partial<RawMinification>;
minifyOptions?: MinificationConfig;
emotion?: EmotionConfig;
presetEnv?: Partial<RawBuiltins["presetEnv"]>;
polyfill?: boolean;
Expand Down Expand Up @@ -376,7 +383,7 @@ export function resolveBuiltinsOptions(
relay: builtins.relay
? resolveRelay(builtins.relay, contextPath)
: undefined,
codeGeneration: resolveCodeGeneration(builtins.codeGeneration)
codeGeneration: resolveCodeGeneration(builtins)
};
}

Expand Down Expand Up @@ -421,22 +428,25 @@ export function resolveMinifyOptions(
return undefined;
}

let extractComments = builtins.minifyOptions?.extractComments
? String(builtins.minifyOptions.extractComments)
: undefined;

return {
passes: 1,
dropConsole: false,
pureFuncs: [],
...builtins.minifyOptions
...builtins.minifyOptions,
extractComments
};
}

export function resolveCodeGeneration(
codeGeneration: Builtins["codeGeneration"]
): RawCodeGeneration {
if (!codeGeneration) {
return { keepComments: false };
export function resolveCodeGeneration(builtins: Builtins): RawCodeGeneration {
if (!builtins.codeGeneration) {
return { keepComments: Boolean(builtins.minifyOptions?.extractComments) };
}
return {
keepComments: false,
...codeGeneration
...builtins.codeGeneration
};
}
Loading

0 comments on commit 2bb2bcd

Please sign in to comment.