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

refactor: css loading #8534

Merged
merged 1 commit into from
Nov 27, 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
1 change: 1 addition & 0 deletions Cargo.lock

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

2 changes: 1 addition & 1 deletion crates/rspack_plugin_css/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
#![feature(option_get_or_insert_default)]

pub mod dependency;
mod parser_and_generator;
pub mod parser_and_generator;
pub mod plugin;
pub mod runtime;
mod utils;
Expand Down
133 changes: 52 additions & 81 deletions crates/rspack_plugin_css/src/parser_and_generator/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ use rustc_hash::FxHashSet;

use crate::{
dependency::CssSelfReferenceLocalIdentDependency,
utils::{css_modules_exports_to_string, escape_css, LocalIdentOptions},
utils::{css_modules_exports_to_string, LocalIdentOptions},
};
use crate::{
dependency::CssSelfReferenceLocalIdentReplacement,
Expand All @@ -47,7 +47,8 @@ static REGEX_IS_MODULES: LazyLock<Regex> =
static REGEX_IS_COMMENTS: LazyLock<Regex> =
LazyLock::new(|| Regex::new(r"/\*[\s\S]*?\*/").expect("Invalid regex"));

pub(crate) static CSS_MODULE_SOURCE_TYPE_LIST: &[SourceType; 1] = &[SourceType::Css];
pub(crate) static CSS_MODULE_SOURCE_TYPE_LIST: &[SourceType; 2] =
&[SourceType::Css, SourceType::JavaScript];

pub(crate) static CSS_MODULE_EXPORTS_ONLY_SOURCE_TYPE_LIST: &[SourceType; 1] =
&[SourceType::JavaScript];
Expand Down Expand Up @@ -79,6 +80,7 @@ pub struct CssParserAndGenerator {
pub named_exports: bool,
pub es_module: bool,
pub exports: Option<CssExports>,
pub hot: bool,
}

impl ParserAndGenerator for CssParserAndGenerator {
Expand Down Expand Up @@ -443,71 +445,6 @@ impl ParserAndGenerator for CssParserAndGenerator {
data: generate_context.data,
};

let identifier = module.identifier();
let module_id = compilation
.chunk_graph
.get_module_id(identifier)
.unwrap_or_default();

if let Some(exports) = &self.exports {
let mg = compilation.get_module_graph();
let unused = get_unused_local_ident(exports, identifier, generate_context.runtime, &mg);
context.data.insert(unused);

let used = get_used_exports(exports, identifier, generate_context.runtime, &mg);

static RE: LazyLock<Regex> =
LazyLock::new(|| Regex::new(r#"\\"#).expect("should compile"));
let module_id = RE.replace_all(module_id, "/");

let meta_data = used
.iter()
.map(|(n, v)| {
let escaped = escape_css(n, false);
v.iter()
.map(|v| {
let composed = &v.id;

if let Some(composed) = composed {
let mg = compilation.get_module_graph();
let module = mg
.get_module_by_dependency_id(composed)
.expect("should have from dependency");
let module_id = compilation
.chunk_graph
.get_module_id(module.identifier())
.expect("should have module id");

format!(
"{}:{}@{}/",
escaped,
escape_css(module_id, false),
escape_css(&v.ident, false)
)
} else {
format!("{}:{}/", escaped, escape_css(&v.ident, false))
}
})
.collect::<Vec<_>>()
.join("")
})
.collect::<Vec<_>>()
.join("");

context.data.insert(CssUsedExports(format!(
"{}{}{}",
meta_data,
if self.es_module { "&" } else { "" },
escape_css(&module_id, false)
)));
} else {
context.data.insert(CssUsedExports(format!(
"{}{}",
if self.es_module { "&" } else { "" },
escape_css(module_id, false)
)));
}

module.get_dependencies().iter().for_each(|id| {
if let Some(dependency) = compilation
.get_module_graph()
Expand All @@ -530,11 +467,15 @@ impl ParserAndGenerator for CssParserAndGenerator {
Ok(source.boxed())
}
SourceType::JavaScript => {
let with_hmr = self.hot;
let exports = if generate_context.concatenation_scope.is_some() {
// currently this is dead branch, as css module will never be concatenated expect exportsOnly
let mut concate_source = ConcatSource::default();
if let Some(ref exports) = self.exports {
let mg = generate_context.compilation.get_module_graph();

let unused_exports =
get_unused_local_ident(exports, module.identifier(), generate_context.runtime, &mg);
generate_context.data.insert(unused_exports);
let exports =
get_used_exports(exports, module.identifier(), generate_context.runtime, &mg);

Expand All @@ -560,20 +501,47 @@ impl ParserAndGenerator for CssParserAndGenerator {
("", "", "")
};
if let Some(exports) = &self.exports {
let unused_exports =
get_unused_local_ident(exports, module.identifier(), generate_context.runtime, &mg);
generate_context.data.insert(unused_exports);

let exports =
get_used_exports(exports, module.identifier(), generate_context.runtime, &mg);

css_modules_exports_to_string(
exports,
module,
generate_context.compilation,
generate_context.runtime_requirements,
if with_hmr {
format!(
"{}\nmodule.hot.accept();\n",
css_modules_exports_to_string(
exports,
module,
generate_context.compilation,
generate_context.runtime_requirements,
ns_obj,
left,
right,
)?
)
} else {
css_modules_exports_to_string(
exports,
module,
generate_context.compilation,
generate_context.runtime_requirements,
ns_obj,
left,
right,
)?
}
} else {
format!(
"{}{}module.exports = {{}}{};\n{}",
ns_obj,
left,
right,
)?
} else {
format!("{}{}module.exports = {{}}{};\n", ns_obj, left, right)
with_hmr
.then_some("module.hot.accept();\n")
.unwrap_or_default()
)
}
};
generate_context
Expand Down Expand Up @@ -601,7 +569,13 @@ impl ParserAndGenerator for CssParserAndGenerator {
_mg: &ModuleGraph,
_cg: &ChunkGraph,
) -> Option<Cow<'static, str>> {
Some("Module Concatenation is not implemented for CssParserAndGenerator".into())
if self.exports_only {
None
} else {
// CSS Module cannot be concatenated as it must appear in css chunk, if it's
// concatenated, it will be removed from module graph
Some("Module Concatenation is not implemented for CssParserAndGenerator".into())
}
}

fn update_hash(
Expand All @@ -628,7 +602,7 @@ fn get_used_exports<'a>(
let export_info = mg.get_read_only_export_info(&identifier, name.as_str().into());

if let Some(export_info) = export_info {
!matches!(export_info.get_used(mg, runtime), UsageState::Unused)
export_info.get_used(mg, runtime) != UsageState::Unused
} else {
true
}
Expand Down Expand Up @@ -668,6 +642,3 @@ fn get_unused_local_ident(
.collect(),
}
}

#[derive(Debug, Clone)]
pub struct CssUsedExports(pub String);
77 changes: 6 additions & 71 deletions crates/rspack_plugin_css/src/plugin/impl_plugin_for_css_plugin.rs
Original file line number Diff line number Diff line change
Expand Up @@ -24,11 +24,9 @@ use rspack_hook::plugin_hook;
use rspack_plugin_runtime::is_enabled_for_chunk;
use rustc_hash::{FxHashMap as HashMap, FxHashSet as HashSet};

use crate::parser_and_generator::{
CodeGenerationDataUnusedLocalIdent, CssParserAndGenerator, CssUsedExports,
};
use crate::parser_and_generator::{CodeGenerationDataUnusedLocalIdent, CssParserAndGenerator};
use crate::runtime::CssLoadingRuntimeModule;
use crate::utils::{escape_css, AUTO_PUBLIC_PATH_PLACEHOLDER};
use crate::utils::AUTO_PUBLIC_PATH_PLACEHOLDER;
use crate::{plugin::CssPluginInner, CssPlugin};

struct CssModuleDebugInfo<'a> {
Expand Down Expand Up @@ -62,18 +60,13 @@ impl CssPlugin {
chunk: &Chunk,
ordered_css_modules: &[&dyn Module],
) -> rspack_error::Result<ConcatSource> {
let mut meta_data = vec![];
let with_compression = compilation.options.output.css_head_data_compression;
let module_sources = ordered_css_modules
.iter()
.map(|module| {
let module_id = &module.identifier();
let code_gen_result = compilation
.code_generation_results
.get(module_id, Some(chunk.runtime()));
if let Some(meta_data_str) = code_gen_result.data.get::<CssUsedExports>() {
meta_data.push(meta_data_str.0.as_str());
}

Ok(
code_gen_result
Expand All @@ -83,7 +76,7 @@ impl CssPlugin {
})
.collect::<Result<Vec<_>>>()?;

let mut source = module_sources
let source = module_sources
.into_par_iter()
// TODO(hyf0): I couldn't think of a situation where a module doesn't have `Source`.
// Should we return a Error if there is a `None` in `module_sources`?
Expand All @@ -105,23 +98,6 @@ impl CssPlugin {
acc
});

let name_with_id = format!(
"{}-{}",
&compilation.options.output.unique_name,
chunk.id().unwrap_or_default()
);
let meta_data_str = format!(
"head{{--webpack-{}:{};}}",
escape_css(&name_with_id, true),
if with_compression {
lzw_encode(&meta_data.join(","))
} else {
meta_data.join(",")
}
);

source.add(RawSource::from(meta_data_str));

Ok(source)
}

Expand Down Expand Up @@ -257,50 +233,6 @@ async fn content_hash(
Ok(())
}

fn lzw_encode(input: &str) -> String {
if input.is_empty() {
return input.into();
}
let mut map: HashMap<String, char> = HashMap::default();
let mut encoded = String::new();
let mut phrase = input.chars().next().expect("should have value").to_string();
let mut code = 256u16;
let max_code = 0xFFFF;

for c in input.chars().skip(1) {
let next_phrase = format!("{}{}", phrase, c);
if map.contains_key(&next_phrase) {
phrase = next_phrase;
} else {
if phrase.len() > 1 {
encoded.push(*map.get(&phrase).expect("should convert to u32 correctly"));
} else {
encoded += &phrase;
}
if code <= max_code {
map.insert(
next_phrase,
std::char::from_u32(code as u32).expect("should convert to u32 correctly"),
);
code += 1;
}
if code > max_code {
code = 256;
map.clear();
}
phrase = c.to_string();
}
}

if phrase.len() > 1 {
encoded.push(*map.get(&phrase).expect("should have phrase"));
} else {
encoded += &phrase;
}

encoded
}

#[plugin_hook(CompilationRenderManifest for CssPlugin)]
async fn render_manifest(
&self,
Expand Down Expand Up @@ -453,6 +385,7 @@ impl Plugin for CssPlugin {
exports_only: g.exports_only.expect("should have exports_only"),
named_exports: p.named_exports.expect("should have named_exports"),
es_module: g.es_module.expect("should have es_module"),
hot: false,
}) as Box<dyn ParserAndGenerator>
}),
);
Expand All @@ -479,6 +412,7 @@ impl Plugin for CssPlugin {
exports_only: g.exports_only.expect("should have exports_only"),
named_exports: p.named_exports.expect("should have named_exports"),
es_module: g.es_module.expect("should have es_module"),
hot: false,
}) as Box<dyn ParserAndGenerator>
}),
);
Expand All @@ -505,6 +439,7 @@ impl Plugin for CssPlugin {
exports_only: g.exports_only.expect("should have exports_only"),
named_exports: p.named_exports.expect("should have named_exports"),
es_module: g.es_module.expect("should have es_module"),
hot: false,
}) as Box<dyn ParserAndGenerator>
}),
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ var applyHandler = function (options) {
}
while (newTags.length) {
var info = newTags.pop();
var chunkModuleIds = loadCssChunkData(__webpack_require__.m, info[1], info[0]);
var chunkModuleIds = loadCssChunkData(__webpack_require__.m, info[0]);
chunkModuleIds.forEach(function(id) {
moduleIds.push(id)
});
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ __webpack_require__.f.css = function (chunkId, promises, fetchPriority) {
error.request = realSrc;
installedChunkData[1](error);
} else {
loadCssChunkData(__webpack_require__.m, link, chunkId);
loadCssChunkData(__webpack_require__.m, chunkId);
installedChunkData[0]();
}
}
Expand Down
Loading
Loading