Skip to content

Commit

Permalink
feat: add inline_css config (style-loader) (#957)
Browse files Browse the repository at this point in the history
  • Loading branch information
sorrycc authored Mar 18, 2024
1 parent 17259de commit 16942b3
Show file tree
Hide file tree
Showing 20 changed files with 174 additions and 16 deletions.
2 changes: 1 addition & 1 deletion crates/mako/src/build.rs
Original file line number Diff line number Diff line change
Expand Up @@ -102,7 +102,7 @@ impl Compiler {
let dep_module_id = ModuleId::new(path.clone());
if !module_graph.has_module(&dep_module_id) {
let module = match dep.resolver_resource {
ResolverResource::Resolved(_) => {
ResolverResource::Virtual(_) | ResolverResource::Resolved(_) => {
count += 1;

let file = File::new(path.clone(), self.context.clone());
Expand Down
9 changes: 8 additions & 1 deletion crates/mako/src/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -345,6 +345,8 @@ pub struct OptimizationConfig {
pub skip_modules: Option<bool>,
}
#[derive(Deserialize, Serialize, Debug)]
pub struct InlineCssConfig {}
#[derive(Deserialize, Serialize, Debug)]
#[serde(rename_all = "camelCase")]
pub struct HmrConfig {
pub host: String,
Expand Down Expand Up @@ -413,6 +415,8 @@ pub struct Config {
pub emit_assets: bool,
#[serde(rename = "cssModulesExportOnlyLocales")]
pub css_modules_export_only_locales: bool,
#[serde(rename = "inlineCSS")]
pub inline_css: Option<InlineCssConfig>,
}

#[allow(dead_code)]
Expand Down Expand Up @@ -633,11 +637,14 @@ impl Config {
}
}

// cjs and umd cannot be used at the same time
if config.cjs && config.umd.is_some() {
return Err(anyhow!("cjs and umd cannot be used at the same time",));
}

if config.inline_css.is_some() && config.umd.is_none() {
return Err(anyhow!("inlineCSS can only be used with umd",));
}

let mode = format!("\"{}\"", config.mode);
config
.define
Expand Down
24 changes: 24 additions & 0 deletions crates/mako/src/load.rs
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,30 @@ impl Load {
return Ok(content);
}

// virtual:inline_css:runtime
if file.path.to_str().unwrap() == "virtual:inline_css:runtime" {
return Ok(Content::Js(
r#"
var memo = {};
export function moduleToDom(css) {
var styleElement = document.createElement("style");
// TODO: support nonce
// styleElement.setAttribute("nonce", nonce);
var target = 'head';
function getTarget(target) {
if (!memo[target]) {
var styleTarget = document.querySelector(target);
memo[target] = styleTarget;
}
return memo[target];
}
target.appendChild(styleElement);
}
"#
.to_string(),
));
}

// file exists check must after virtual modules handling
if !file.pathname.exists() || !file.pathname.is_file() {
return Err(anyhow!(LoadError::FileNotFound {
Expand Down
8 changes: 8 additions & 0 deletions crates/mako/src/module.rs
Original file line number Diff line number Diff line change
Expand Up @@ -194,6 +194,14 @@ impl ModuleAst {
panic!("ModuleAst is not Script")
}
}

pub fn as_css_mut(&mut self) -> &mut CssAst {
if let Self::Css(css) = self {
css
} else {
panic!("ModuleAst is not Css")
}
}
}

#[derive(PartialEq, Eq, Debug)]
Expand Down
54 changes: 53 additions & 1 deletion crates/mako/src/parse.rs
Original file line number Diff line number Diff line change
@@ -1,20 +1,26 @@
use std::sync::Arc;

use mako_core::anyhow::{anyhow, Result};
use mako_core::swc_css_visit::VisitMutWith as CSSVisitMutWith;
use mako_core::thiserror::Error;
use mako_core::tracing::debug;

use crate::analyze_deps::AnalyzeDeps;
use crate::ast_2::css_ast::CssAst;
use crate::ast_2::file::{Content, File};
use crate::ast_2::js_ast::JsAst;
use crate::compiler::Context;
use crate::module::ModuleAst;
use crate::plugin::PluginParseParam;
use crate::transform::Transform;
use crate::transformers::transform_css_handler::CssHandler;

#[derive(Debug, Error)]
pub enum ParseError {
#[error("Unsupported content: {path:?}")]
UnsupportedContent { path: String },
#[error("Inline CSS missing deps: {path:?}")]
InlineCSSMissingDeps { path: String },
}

pub struct Parse {}
Expand Down Expand Up @@ -45,6 +51,7 @@ impl Parse {
let is_asmodule = file.has_param("asmodule");
let css_modules = is_modules || is_asmodule;
let mut ast = CssAst::new(file, context.clone(), css_modules)?;
// ?asmodule
if is_asmodule {
let mut file = file.clone();
file.set_content(Content::Js(CssAst::generate_css_modules_exports(
Expand All @@ -55,7 +62,52 @@ impl Parse {
let ast = JsAst::new(&file, context)?;
return Ok(ModuleAst::Script(ast));
} else {
return Ok(ModuleAst::Css(ast));
// when inline_css is enabled
// we need to go through the css-related process first
// and then hand it over to js for processing
if context.config.inline_css.is_some() {
let mut ast = ModuleAst::Css(ast);
// transform
Transform::transform(&mut ast, file, context.clone())?;
// analyze_deps
// TODO: do not need to resolve here
let deps = AnalyzeDeps::analyze_deps(&ast, file, context.clone())?;
if !deps.missing_deps.is_empty() {
return Err(anyhow!(ParseError::InlineCSSMissingDeps {
path: file.path.to_string_lossy().to_string(),
}));
}
let deps = deps
.resolved_deps
.iter()
.map(|dep| {
format!("import '{}';", dep.resolver_resource.get_resolved_path())
})
.collect::<Vec<String>>()
.join("\n");
let ast = ast.as_css_mut();
// transform (remove @imports)
// TODO: Render::transform(&mut ast, &file, context.clone())?;
let mut css_handler = CssHandler {};
ast.ast.visit_mut_with(&mut css_handler);
// ast to code
let code = ast.generate(context.clone())?.code;
let mut file = file.clone();
file.set_content(Content::Js(format!(
r#"
import {{ moduleToDom }} from 'virtual:inline_css:runtime';
{}
moduleToDom(`
{}
`);
"#,
deps, code
)));
let ast = JsAst::new(&file, context.clone())?;
return Ok(ModuleAst::Script(ast));
} else {
return Ok(ModuleAst::Css(ast));
}
}
}

Expand Down
5 changes: 5 additions & 0 deletions crates/mako/src/resolve/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,11 @@ pub fn resolve(
) -> Result<ResolverResource> {
mako_core::mako_profile_function!();
mako_core::mako_profile_scope!("resolve", &dep.source);

if dep.source.starts_with("virtual:") {
return Ok(ResolverResource::Virtual(PathBuf::from(&dep.source)));
}

let resolver = if parse_path(&dep.source)?.has_query("context") {
resolvers.get(&ResolverType::Ctxt)
} else if dep.resolve_type == ResolveType::Require {
Expand Down
4 changes: 4 additions & 0 deletions crates/mako/src/resolve/resource.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ pub enum ResolverResource {
External(ExternalResource),
Resolved(ResolvedResource),
Ignored(PathBuf),
Virtual(PathBuf),
}

impl ResolverResource {
Expand All @@ -27,20 +28,23 @@ impl ResolverResource {
resolution.full_path().to_string_lossy().to_string()
}
ResolverResource::Ignored(path) => path.to_string_lossy().to_string(),
ResolverResource::Virtual(path) => path.to_string_lossy().to_string(),
}
}
pub fn get_external(&self) -> Option<String> {
match self {
ResolverResource::External(ExternalResource { external, .. }) => Some(external.clone()),
ResolverResource::Resolved(_) => None,
ResolverResource::Ignored(_) => None,
ResolverResource::Virtual(_) => None,
}
}
pub fn get_script(&self) -> Option<String> {
match self {
ResolverResource::External(ExternalResource { script, .. }) => script.clone(),
ResolverResource::Resolved(_) => None,
ResolverResource::Ignored(_) => None,
ResolverResource::Virtual(_) => None,
}
}
}
10 changes: 9 additions & 1 deletion crates/mako/src/transform.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ use mako_core::swc_ecma_transforms_optimization::simplify::{dce, Config as Simpi
use mako_core::swc_ecma_transforms_proposals::decorators;
use mako_core::swc_ecma_transforms_typescript::strip_with_jsx;
use mako_core::swc_ecma_visit::{Fold, VisitMut};
use mako_core::{swc_css_compat, swc_css_visit};
use mako_core::{swc_css_compat, swc_css_prefixer, swc_css_visit};
use swc_core::common::GLOBALS;

use crate::ast_2::css_ast::CssAst;
Expand Down Expand Up @@ -172,6 +172,14 @@ impl Transform {
current_selector: None,
}));
}
// prefixer
visitors.push(Box::new(swc_css_prefixer::prefixer(
swc_css_prefixer::options::Options {
env: Some(targets::swc_preset_env_targets_from_map(
context.config.targets.clone(),
)),
},
)));
ast.transform(&mut visitors)?;

// css modules
Expand Down
14 changes: 3 additions & 11 deletions crates/mako/src/transform_in_generate.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ use std::time::Instant;
use mako_core::anyhow::Result;
use mako_core::swc_common::errors::HANDLER;
use mako_core::swc_common::GLOBALS;
use mako_core::swc_css_ast;
use mako_core::swc_css_visit::VisitMutWith as CSSVisitMutWith;
use mako_core::swc_ecma_transforms::feature::FeatureFlag;
use mako_core::swc_ecma_transforms::fixer::fixer;
Expand All @@ -18,21 +19,20 @@ use mako_core::swc_ecma_transforms_modules::util::{Config, ImportInterop};
use mako_core::swc_ecma_visit::VisitMutWith;
use mako_core::swc_error_reporters::handler::try_with_handler;
use mako_core::tracing::debug;
use mako_core::{swc_css_ast, swc_css_prefixer};

use crate::ast_2::js_ast::JsAst;
use crate::compiler::{Compiler, Context};
use crate::config::OutputMode;
use crate::module::{Dependency, ModuleAst, ModuleId, ResolveType};
use crate::swc_helpers::SwcHelpers;
use crate::thread_pool;
use crate::transformers::transform_async_module::AsyncModule;
use crate::transformers::transform_css_handler::CssHandler;
use crate::transformers::transform_dep_replacer::{DepReplacer, DependenciesToReplace};
use crate::transformers::transform_dynamic_import::DynamicImport;
use crate::transformers::transform_mako_require::MakoRequire;
use crate::transformers::transform_meta_url_replacer::MetaUrlReplacer;
use crate::transformers::transform_optimize_define_utils::OptimizeDefineUtils;
use crate::{targets, thread_pool};

impl Compiler {
pub fn transform_all(&self) -> Result<()> {
Expand Down Expand Up @@ -306,19 +306,11 @@ pub fn transform_js_generate(transform_js_param: TransformJsParam) -> Result<()>
})
}

pub fn transform_css_generate(ast: &mut swc_css_ast::Stylesheet, context: &Arc<Context>) {
pub fn transform_css_generate(ast: &mut swc_css_ast::Stylesheet, _context: &Arc<Context>) {
mako_core::mako_profile_function!();
// replace deps
let mut css_handler = CssHandler {};
ast.visit_mut_with(&mut css_handler);

// prefixer
let mut prefixer = swc_css_prefixer::prefixer(swc_css_prefixer::options::Options {
env: Some(targets::swc_preset_env_targets_from_map(
context.config.targets.clone(),
)),
});
ast.visit_mut_with(&mut prefixer);
}

#[cfg(test)]
Expand Down
5 changes: 4 additions & 1 deletion crates/mako/src/transformers/transform_dep_replacer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -113,7 +113,10 @@ impl VisitMut for DepReplacer<'_> {
self.to_replace.resolved.get(&source_string)
{
let file_request = parse_path(raw_id).unwrap();
is_css_path(&file_request.path)
// when inline_css is enabled
// css is parsed as js modules
self.context.config.inline_css.is_none()
&& is_css_path(&file_request.path)
&& (file_request.query.is_empty() || file_request.has_query("modules"))
} else {
false
Expand Down
1 change: 1 addition & 0 deletions crates/node/index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -136,6 +136,7 @@ export interface BuildParams {
};
emitAssets: boolean;
cssModulesExportOnlyLocales: boolean;
inlineCSS?: false | {};
};
hooks: JsHooks;
watch: boolean;
Expand Down
1 change: 1 addition & 0 deletions crates/node/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -128,6 +128,7 @@ pub struct BuildParams {
};
emitAssets: boolean;
cssModulesExportOnlyLocales: boolean;
inlineCSS?: false | {};
}"#)]
pub config: serde_json::Value,
pub hooks: JsHooks,
Expand Down
9 changes: 9 additions & 0 deletions docs/config.md
Original file line number Diff line number Diff line change
Expand Up @@ -205,6 +205,15 @@ import("./a.js")

需要忽略的文件。忽略的文件会输出空模块。

## inlineCSS

- 类型:`{} | false`
- 默认值:`false`

是否以内联到 JS 的方式输出 CSS。

注:此配置只能在 umd 开始时使用,因为注入 CSS 并不是推荐的方式,可能有潜在的性能问题。

## inlineLimit

- 类型:`number`
Expand Down
22 changes: 22 additions & 0 deletions e2e/fixtures/config.inline_css/expect.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
const assert = require("assert");
const { parseBuildResult, moduleReg } = require("../../../scripts/test-utils");
const { files } = parseBuildResult(__dirname);

const names = Object.keys(files).join(",");
const content = files["index.js"];

// check files
assert.match(names, /bigfish-log.(.*).png/, "should have bigfish-log.png");
assert.doesNotMatch(names, /umi-logo.(.*).png/, "should not have umi-logo.png");
assert.doesNotMatch(names, /index.css/, "should not have index.css");

// check content
assert(
content.includes(`__mako_require__("src/a.css")`, `should work`),
);
assert(
content.includes(`__mako_require__("src/b.css")`, `should support deps in css`),
);
assert(
content.includes(`@import "//c";`, `should keep remote imports`),
);
5 changes: 5 additions & 0 deletions e2e/fixtures/config.inline_css/mako.config.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
{
"minify": false,
"inlineCSS": {},
"umd": "fooo"
}
6 changes: 6 additions & 0 deletions e2e/fixtures/config.inline_css/src/a.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
@import "b.css";
@import "//c";

.a {
color: red;
}
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
9 changes: 9 additions & 0 deletions e2e/fixtures/config.inline_css/src/b.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
.b {
color: red;
background: url(assets/bigfish-log.png);
}

.b-2 {
color: red;
background: url(assets/umi-logo.png);
}
2 changes: 2 additions & 0 deletions e2e/fixtures/config.inline_css/src/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
import './a.css';
console.log('index');

0 comments on commit 16942b3

Please sign in to comment.