mirror of
https://github.com/swc-project/swc.git
synced 2024-12-24 22:22:34 +03:00
feat(css/modules): Implement css modules (#6000)
This commit is contained in:
parent
37286e369e
commit
2cce1c82b2
3
.github/workflows/CI.yml
vendored
3
.github/workflows/CI.yml
vendored
@ -275,6 +275,9 @@ jobs:
|
||||
- crate: swc_css_minifier
|
||||
os: ubuntu-latest
|
||||
runner: ubuntu-latest
|
||||
- crate: swc_css_modules
|
||||
os: ubuntu-latest
|
||||
runner: ubuntu-latest
|
||||
- crate: swc_css_parser
|
||||
os: ubuntu-latest
|
||||
runner: ubuntu-latest
|
||||
|
18
Cargo.lock
generated
18
Cargo.lock
generated
@ -3197,6 +3197,7 @@ dependencies = [
|
||||
"swc_css_ast",
|
||||
"swc_css_codegen",
|
||||
"swc_css_minifier",
|
||||
"swc_css_modules",
|
||||
"swc_css_parser",
|
||||
"swc_css_utils",
|
||||
"swc_css_visit",
|
||||
@ -3280,6 +3281,23 @@ dependencies = [
|
||||
"testing",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "swc_css_modules"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"rustc-hash",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"swc_atoms",
|
||||
"swc_common",
|
||||
"swc_css_ast",
|
||||
"swc_css_codegen",
|
||||
"swc_css_parser",
|
||||
"swc_css_visit",
|
||||
"swc_ecma_loader",
|
||||
"testing",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "swc_css_parser"
|
||||
version = "0.123.5"
|
||||
|
@ -1,27 +1,29 @@
|
||||
[package]
|
||||
authors = ["강동윤 <kdy1997.dev@gmail.com>"]
|
||||
description = "CSS apis for rust"
|
||||
authors = ["강동윤 <kdy1997.dev@gmail.com>"]
|
||||
description = "CSS apis for rust"
|
||||
documentation = "https://rustdoc.swc.rs/swc_css/"
|
||||
edition = "2021"
|
||||
license = "Apache-2.0"
|
||||
name = "swc_css"
|
||||
repository = "https://github.com/swc-project/swc.git"
|
||||
version = "0.127.6"
|
||||
edition = "2021"
|
||||
license = "Apache-2.0"
|
||||
name = "swc_css"
|
||||
repository = "https://github.com/swc-project/swc.git"
|
||||
version = "0.127.6"
|
||||
|
||||
[package.metadata.docs.rs]
|
||||
all-features = true
|
||||
rustdoc-args = ["--cfg", "docsrs"]
|
||||
[package.metadata.docs.rs]
|
||||
all-features = true
|
||||
rustdoc-args = ["--cfg", "docsrs"]
|
||||
|
||||
[lib]
|
||||
bench = false
|
||||
|
||||
[features]
|
||||
minifier = ["swc_css_minifier"]
|
||||
modules = ["swc_css_modules"]
|
||||
|
||||
[dependencies]
|
||||
swc_css_ast = {version = "0.114.5", path = "../swc_css_ast"}
|
||||
swc_css_codegen = {version = "0.124.5", path = "../swc_css_codegen"}
|
||||
swc_css_minifier = {version = "0.89.6", path = "../swc_css_minifier", optional = true}
|
||||
swc_css_parser = {version = "0.123.5", path = "../swc_css_parser"}
|
||||
swc_css_utils = {version = "0.111.5", path = "../swc_css_utils/"}
|
||||
swc_css_visit = {version = "0.113.5", path = "../swc_css_visit"}
|
||||
swc_css_ast = { version = "0.114.5", path = "../swc_css_ast" }
|
||||
swc_css_codegen = { version = "0.124.5", path = "../swc_css_codegen" }
|
||||
swc_css_minifier = { version = "0.89.6", path = "../swc_css_minifier", optional = true }
|
||||
swc_css_modules = { version = "0.1.0", path = "../swc_css_modules", optional = true }
|
||||
swc_css_parser = { version = "0.123.5", path = "../swc_css_parser" }
|
||||
swc_css_utils = { version = "0.111.5", path = "../swc_css_utils/" }
|
||||
swc_css_visit = { version = "0.113.5", path = "../swc_css_visit" }
|
||||
|
@ -3,6 +3,9 @@ pub extern crate swc_css_codegen as codegen;
|
||||
#[cfg(feature = "swc_css_minifier")]
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "minifier")))]
|
||||
pub extern crate swc_css_minifier as minifier;
|
||||
#[cfg(feature = "swc_css_modules")]
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "modules")))]
|
||||
pub extern crate swc_css_modules as modules;
|
||||
pub extern crate swc_css_parser as parser;
|
||||
pub extern crate swc_css_utils as utils;
|
||||
pub extern crate swc_css_visit as visit;
|
||||
|
@ -1,7 +1,7 @@
|
||||
use is_macro::Is;
|
||||
use string_enum::StringEnum;
|
||||
use swc_atoms::JsWord;
|
||||
use swc_common::{ast_node, EqIgnoreSpan, Span};
|
||||
use swc_common::{ast_node, util::take::Take, EqIgnoreSpan, Span};
|
||||
|
||||
use crate::{Delimiter, Ident, ListOfComponentValues, Str, TokenAndSpan};
|
||||
|
||||
@ -65,6 +65,15 @@ pub struct ComplexSelector {
|
||||
pub children: Vec<ComplexSelectorChildren>,
|
||||
}
|
||||
|
||||
impl Take for ComplexSelector {
|
||||
fn dummy() -> Self {
|
||||
Self {
|
||||
span: Take::dummy(),
|
||||
children: Take::dummy(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[ast_node]
|
||||
#[derive(Eq, Hash, Is, EqIgnoreSpan)]
|
||||
pub enum ComplexSelectorChildren {
|
||||
|
@ -6,7 +6,7 @@ use std::{
|
||||
use is_macro::Is;
|
||||
use string_enum::StringEnum;
|
||||
use swc_atoms::JsWord;
|
||||
use swc_common::{ast_node, EqIgnoreSpan, Span};
|
||||
use swc_common::{ast_node, util::take::Take, EqIgnoreSpan, Span};
|
||||
|
||||
use crate::Function;
|
||||
|
||||
@ -26,6 +26,16 @@ impl EqIgnoreSpan for Ident {
|
||||
}
|
||||
}
|
||||
|
||||
impl Take for Ident {
|
||||
fn dummy() -> Self {
|
||||
Self {
|
||||
span: Default::default(),
|
||||
value: Default::default(),
|
||||
raw: Default::default(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[ast_node("CustomIdent")]
|
||||
#[derive(Eq, Hash)]
|
||||
pub struct CustomIdent {
|
||||
|
30
crates/swc_css_modules/Cargo.toml
Normal file
30
crates/swc_css_modules/Cargo.toml
Normal file
@ -0,0 +1,30 @@
|
||||
[package]
|
||||
authors = ["강동윤 <kdy1997.dev@gmail.com>"]
|
||||
description = "CSS modules"
|
||||
documentation = "https://rustdoc.swc.rs/swc_css_modules/"
|
||||
edition = "2021"
|
||||
include = ["Cargo.toml", "src/**/*.rs"]
|
||||
license = "Apache-2.0"
|
||||
name = "swc_css_modules"
|
||||
repository = "https://github.com/swc-project/swc.git"
|
||||
version = "0.1.0"
|
||||
|
||||
[lib]
|
||||
bench = false
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
rustc-hash = "1.1.0"
|
||||
serde = { version = "1", features = ["derive"] }
|
||||
swc_atoms = { version = "0.4.18", path = "../swc_atoms" }
|
||||
swc_common = { version = "0.29.4", path = "../swc_common" }
|
||||
swc_css_ast = { version = "0.114.4", path = "../swc_css_ast" }
|
||||
swc_css_codegen = { version = "0.124.5", path = "../swc_css_codegen" }
|
||||
swc_css_parser = { version = "0.123.5", path = "../swc_css_parser" }
|
||||
swc_css_visit = { version = "0.113.4", path = "../swc_css_visit" }
|
||||
swc_ecma_loader = { version = "0.41.3", path = "../swc_ecma_loader" }
|
||||
|
||||
[dev-dependencies]
|
||||
serde_json = "1"
|
||||
testing = { version = "0.31.4", path = "../testing" }
|
68
crates/swc_css_modules/src/imports.rs
Normal file
68
crates/swc_css_modules/src/imports.rs
Normal file
@ -0,0 +1,68 @@
|
||||
//! Import/export analyzer
|
||||
|
||||
use swc_atoms::{js_word, JsWord};
|
||||
use swc_css_ast::{
|
||||
ComponentValue, Declaration, DeclarationName, Ident, ImportPrelude, ImportPreludeHref,
|
||||
Stylesheet, UrlValue,
|
||||
};
|
||||
use swc_css_visit::{Visit, VisitWith};
|
||||
|
||||
pub fn analyze_imports(ss: &Stylesheet) -> Vec<JsWord> {
|
||||
let mut v = Analyzer {
|
||||
imports: Default::default(),
|
||||
};
|
||||
ss.visit_with(&mut v);
|
||||
v.imports.sort();
|
||||
v.imports.dedup();
|
||||
v.imports
|
||||
}
|
||||
|
||||
struct Analyzer {
|
||||
imports: Vec<JsWord>,
|
||||
}
|
||||
|
||||
impl Visit for Analyzer {
|
||||
fn visit_import_prelude(&mut self, n: &ImportPrelude) {
|
||||
n.visit_children_with(self);
|
||||
|
||||
match &*n.href {
|
||||
ImportPreludeHref::Url(u) => {
|
||||
if let Some(s) = &u.value {
|
||||
match &**s {
|
||||
UrlValue::Str(s) => {
|
||||
self.imports.push(s.value.clone());
|
||||
}
|
||||
UrlValue::Raw(v) => {
|
||||
self.imports.push(v.value.clone());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
ImportPreludeHref::Str(s) => {
|
||||
self.imports.push(s.value.clone());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn visit_declaration(&mut self, d: &Declaration) {
|
||||
d.visit_children_with(self);
|
||||
|
||||
if let DeclarationName::Ident(name) = &d.name {
|
||||
if &*name.value == "composes" {
|
||||
// comoses: name from 'foo.css'
|
||||
if d.value.len() >= 3 {
|
||||
if let (
|
||||
ComponentValue::Ident(Ident {
|
||||
value: js_word!("from"),
|
||||
..
|
||||
}),
|
||||
ComponentValue::Str(s),
|
||||
) = (&d.value[d.value.len() - 2], &d.value[d.value.len() - 1])
|
||||
{
|
||||
self.imports.push(s.value.clone());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
437
crates/swc_css_modules/src/lib.rs
Normal file
437
crates/swc_css_modules/src/lib.rs
Normal file
@ -0,0 +1,437 @@
|
||||
use rustc_hash::FxHashMap;
|
||||
use serde::Serialize;
|
||||
use swc_atoms::{js_word, JsWord};
|
||||
use swc_common::util::take::Take;
|
||||
use swc_css_ast::{
|
||||
ComplexSelector, ComplexSelectorChildren, ComponentValue, Declaration, DeclarationName,
|
||||
DeclarationOrAtRule, Delimiter, DelimiterValue, Ident, KeyframesName, QualifiedRule,
|
||||
QualifiedRulePrelude, StyleBlock, Stylesheet, SubclassSelector,
|
||||
};
|
||||
use swc_css_parser::{parse_tokens, parser::ParserConfig};
|
||||
use swc_css_visit::{VisitMut, VisitMutWith};
|
||||
use util::to_tokens::to_tokens_vec;
|
||||
|
||||
pub mod imports;
|
||||
mod util;
|
||||
|
||||
/// Various configurations for the css modules.
|
||||
///
|
||||
/// # Note
|
||||
///
|
||||
/// This is a trait rather than a struct because api like `fn() -> String` is
|
||||
/// too restricted and `Box<Fn() -> String` is (needlessly) slow.
|
||||
pub trait TransformConfig {
|
||||
/// Creates a class name for the given `local_name`.
|
||||
fn new_name_for(&self, local: &JsWord) -> JsWord;
|
||||
|
||||
// /// Used for `@value` imports.
|
||||
// fn get_value(&self, import_source: &str, value_name: &JsWord) ->
|
||||
// ComponentValue;
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize)]
|
||||
#[serde(tag = "type", rename_all = "camelCase")]
|
||||
pub enum CssClassName {
|
||||
Local {
|
||||
/// Tranformed css class name
|
||||
name: JsWord,
|
||||
},
|
||||
Global {
|
||||
name: JsWord,
|
||||
},
|
||||
Import {
|
||||
/// The exported class name. This is the value specified by the user.
|
||||
name: JsWord,
|
||||
/// The module specifier.
|
||||
from: JsWord,
|
||||
},
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct TransformResult {
|
||||
/// A map of js class name to css class names.
|
||||
pub renamed: FxHashMap<JsWord, Vec<CssClassName>>,
|
||||
}
|
||||
|
||||
/// Returns a map from local name to exported name.
|
||||
pub fn compile(ss: &mut Stylesheet, config: impl TransformConfig) -> TransformResult {
|
||||
let mut compiler = Compiler {
|
||||
config,
|
||||
data: Default::default(),
|
||||
result: TransformResult {
|
||||
renamed: Default::default(),
|
||||
},
|
||||
};
|
||||
|
||||
ss.visit_mut_with(&mut compiler);
|
||||
|
||||
compiler.result
|
||||
}
|
||||
|
||||
struct Compiler<C>
|
||||
where
|
||||
C: TransformConfig,
|
||||
{
|
||||
config: C,
|
||||
data: Data,
|
||||
result: TransformResult,
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
struct Data {
|
||||
/// Context for `composes`
|
||||
composes_for_current: Option<Vec<CssClassName>>,
|
||||
|
||||
renamed_to_orig: FxHashMap<JsWord, JsWord>,
|
||||
orig_to_renamed: FxHashMap<JsWord, JsWord>,
|
||||
}
|
||||
|
||||
impl<C> VisitMut for Compiler<C>
|
||||
where
|
||||
C: TransformConfig,
|
||||
{
|
||||
fn visit_mut_qualified_rule(&mut self, n: &mut QualifiedRule) {
|
||||
let old_compose_stack = self.data.composes_for_current.take();
|
||||
self.data.composes_for_current = Some(Default::default());
|
||||
|
||||
n.visit_mut_children_with(self);
|
||||
|
||||
if let QualifiedRulePrelude::SelectorList(sel) = &n.prelude {
|
||||
//
|
||||
if sel.children.len() == 1 && sel.children[0].children.len() == 1 {
|
||||
if let ComplexSelectorChildren::CompoundSelector(sel) = &sel.children[0].children[0]
|
||||
{
|
||||
if sel.subclass_selectors.len() == 1 {
|
||||
if let SubclassSelector::Class(class_sel) = &sel.subclass_selectors[0] {
|
||||
if let Some(composes) = self.data.composes_for_current.take() {
|
||||
let key = self
|
||||
.data
|
||||
.renamed_to_orig
|
||||
.get(&class_sel.text.value)
|
||||
.cloned();
|
||||
|
||||
if let Some(key) = key {
|
||||
self.result.renamed.entry(key).or_default().extend(composes);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
self.data.composes_for_current = old_compose_stack;
|
||||
}
|
||||
|
||||
fn visit_mut_component_values(&mut self, n: &mut Vec<ComponentValue>) {
|
||||
n.visit_mut_children_with(self);
|
||||
|
||||
n.retain(|v| match v {
|
||||
ComponentValue::StyleBlock(StyleBlock::Declaration(d))
|
||||
| ComponentValue::DeclarationOrAtRule(DeclarationOrAtRule::Declaration(d)) => {
|
||||
if let DeclarationName::Ident(ident) = &d.name {
|
||||
if &*ident.value == "composes" {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
true
|
||||
}
|
||||
_ => true,
|
||||
});
|
||||
}
|
||||
|
||||
/// Handles `composes`
|
||||
fn visit_mut_declaration(&mut self, n: &mut Declaration) {
|
||||
n.visit_mut_children_with(self);
|
||||
|
||||
if let Some(composes_for_current) = &mut self.data.composes_for_current {
|
||||
if let DeclarationName::Ident(name) = &n.name {
|
||||
if &*name.value == "composes" {
|
||||
// comoses: name from 'foo.css'
|
||||
if n.value.len() >= 3 {
|
||||
match (&n.value[n.value.len() - 2], &n.value[n.value.len() - 1]) {
|
||||
(
|
||||
ComponentValue::Ident(Ident {
|
||||
value: js_word!("from"),
|
||||
..
|
||||
}),
|
||||
ComponentValue::Str(import_source),
|
||||
) => {
|
||||
for class_name in n.value.iter().take(n.value.len() - 2) {
|
||||
if let ComponentValue::Ident(Ident { value, .. }) = class_name {
|
||||
composes_for_current.push(CssClassName::Import {
|
||||
name: value.clone(),
|
||||
from: import_source.value.clone(),
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
(
|
||||
ComponentValue::Ident(Ident {
|
||||
value: js_word!("from"),
|
||||
..
|
||||
}),
|
||||
ComponentValue::Ident(Ident {
|
||||
value: js_word!("global"),
|
||||
..
|
||||
}),
|
||||
) => {
|
||||
for class_name in n.value.iter().take(n.value.len() - 2) {
|
||||
if let ComponentValue::Ident(Ident { value, .. }) = class_name {
|
||||
composes_for_current.push(CssClassName::Global {
|
||||
name: value.clone(),
|
||||
});
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
_ => (),
|
||||
}
|
||||
}
|
||||
|
||||
for class_name in n.value.iter() {
|
||||
if let ComponentValue::Ident(Ident { value, .. }) = class_name {
|
||||
if let Some(value) = self.data.orig_to_renamed.get(value) {
|
||||
composes_for_current.push(CssClassName::Local {
|
||||
name: value.clone(),
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if let DeclarationName::Ident(name) = &n.name {
|
||||
match name.value.to_ascii_lowercase() {
|
||||
js_word!("animation") => {
|
||||
let mut can_change = true;
|
||||
|
||||
for v in &mut n.value {
|
||||
if can_change {
|
||||
if let ComponentValue::Ident(Ident { value, raw, .. }) = v {
|
||||
*raw = None;
|
||||
|
||||
rename(
|
||||
&mut self.config,
|
||||
&mut self.result,
|
||||
&mut self.data.orig_to_renamed,
|
||||
&mut self.data.renamed_to_orig,
|
||||
value,
|
||||
);
|
||||
can_change = false;
|
||||
}
|
||||
} else if let ComponentValue::Delimiter(Delimiter {
|
||||
value: DelimiterValue::Comma,
|
||||
..
|
||||
}) = v
|
||||
{
|
||||
can_change = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
js_word!("animation-name") => {
|
||||
for v in &mut n.value {
|
||||
if let ComponentValue::Ident(Ident { value, raw, .. }) = v {
|
||||
*raw = None;
|
||||
|
||||
rename(
|
||||
&mut self.config,
|
||||
&mut self.result,
|
||||
&mut self.data.orig_to_renamed,
|
||||
&mut self.data.renamed_to_orig,
|
||||
value,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn visit_mut_complex_selector(&mut self, n: &mut ComplexSelector) {
|
||||
n.visit_mut_children_with(self);
|
||||
|
||||
let mut new_children = Vec::with_capacity(n.children.len());
|
||||
|
||||
'complex: for mut n in n.children.take() {
|
||||
match &mut n {
|
||||
ComplexSelectorChildren::CompoundSelector(sel) => {
|
||||
//
|
||||
|
||||
for sel in &mut sel.subclass_selectors {
|
||||
match sel {
|
||||
SubclassSelector::Class(..) | SubclassSelector::Id(..) => {
|
||||
process_local(
|
||||
&mut self.config,
|
||||
&mut self.result,
|
||||
&mut self.data.orig_to_renamed,
|
||||
&mut self.data.renamed_to_orig,
|
||||
sel,
|
||||
);
|
||||
}
|
||||
SubclassSelector::PseudoClass(class_sel) => {
|
||||
match &*class_sel.name.value {
|
||||
"local" => {
|
||||
if let Some(children) = &mut class_sel.children {
|
||||
let tokens = to_tokens_vec(&*children);
|
||||
|
||||
let mut sel: ComplexSelector = parse_tokens(
|
||||
&tokens,
|
||||
ParserConfig {
|
||||
..Default::default()
|
||||
},
|
||||
&mut vec![],
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
sel.visit_mut_with(self);
|
||||
|
||||
new_children.extend(sel.children);
|
||||
|
||||
continue 'complex;
|
||||
}
|
||||
}
|
||||
"global" => {
|
||||
if let Some(children) = &mut class_sel.children {
|
||||
let tokens = to_tokens_vec(&*children);
|
||||
|
||||
let sel: ComplexSelector = parse_tokens(
|
||||
&tokens,
|
||||
ParserConfig {
|
||||
..Default::default()
|
||||
},
|
||||
&mut vec![],
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
new_children.extend(sel.children);
|
||||
|
||||
continue 'complex;
|
||||
}
|
||||
}
|
||||
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
_ => {
|
||||
sel.visit_mut_children_with(self);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
ComplexSelectorChildren::Combinator(_) => {}
|
||||
}
|
||||
|
||||
new_children.push(n);
|
||||
}
|
||||
|
||||
n.children = new_children;
|
||||
}
|
||||
|
||||
fn visit_mut_complex_selectors(&mut self, n: &mut Vec<ComplexSelector>) {
|
||||
n.visit_mut_children_with(self);
|
||||
|
||||
n.retain_mut(|s| !s.children.is_empty());
|
||||
}
|
||||
|
||||
fn visit_mut_keyframes_name(&mut self, n: &mut KeyframesName) {
|
||||
n.visit_mut_children_with(self);
|
||||
|
||||
match n {
|
||||
KeyframesName::CustomIdent(n) => {
|
||||
n.raw = None;
|
||||
rename(
|
||||
&mut self.config,
|
||||
&mut self.result,
|
||||
&mut self.data.orig_to_renamed,
|
||||
&mut self.data.renamed_to_orig,
|
||||
&mut n.value,
|
||||
)
|
||||
}
|
||||
KeyframesName::Str(n) => {
|
||||
n.raw = None;
|
||||
rename(
|
||||
&mut self.config,
|
||||
&mut self.result,
|
||||
&mut self.data.orig_to_renamed,
|
||||
&mut self.data.renamed_to_orig,
|
||||
&mut n.value,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn rename<C>(
|
||||
config: &mut C,
|
||||
result: &mut TransformResult,
|
||||
orig_to_renamed: &mut FxHashMap<JsWord, JsWord>,
|
||||
renamed_to_orig: &mut FxHashMap<JsWord, JsWord>,
|
||||
name: &mut JsWord,
|
||||
) where
|
||||
C: TransformConfig,
|
||||
{
|
||||
if let Some(renamed) = orig_to_renamed.get(name) {
|
||||
*name = renamed.clone();
|
||||
return;
|
||||
}
|
||||
|
||||
let new = config.new_name_for(name);
|
||||
|
||||
orig_to_renamed.insert(name.clone(), new.clone());
|
||||
renamed_to_orig.insert(new.clone(), name.clone());
|
||||
|
||||
{
|
||||
let e = result.renamed.entry(name.clone()).or_default();
|
||||
|
||||
let v = CssClassName::Local { name: new.clone() };
|
||||
if !e.contains(&v) {
|
||||
e.push(v);
|
||||
}
|
||||
}
|
||||
|
||||
*name = new;
|
||||
}
|
||||
|
||||
fn process_local<C>(
|
||||
config: &mut C,
|
||||
result: &mut TransformResult,
|
||||
orig_to_renamed: &mut FxHashMap<JsWord, JsWord>,
|
||||
renamed_to_orig: &mut FxHashMap<JsWord, JsWord>,
|
||||
sel: &mut SubclassSelector,
|
||||
) where
|
||||
C: TransformConfig,
|
||||
{
|
||||
match sel {
|
||||
SubclassSelector::Id(sel) => {
|
||||
sel.text.raw = None;
|
||||
|
||||
rename(
|
||||
config,
|
||||
result,
|
||||
orig_to_renamed,
|
||||
renamed_to_orig,
|
||||
&mut sel.text.value,
|
||||
);
|
||||
}
|
||||
SubclassSelector::Class(sel) => {
|
||||
sel.text.raw = None;
|
||||
|
||||
rename(
|
||||
config,
|
||||
result,
|
||||
orig_to_renamed,
|
||||
renamed_to_orig,
|
||||
&mut sel.text.value,
|
||||
);
|
||||
}
|
||||
SubclassSelector::Attribute(_) => {}
|
||||
SubclassSelector::PseudoClass(_) => {}
|
||||
SubclassSelector::PseudoElement(_) => {}
|
||||
}
|
||||
}
|
1
crates/swc_css_modules/src/util/mod.rs
Normal file
1
crates/swc_css_modules/src/util/mod.rs
Normal file
@ -0,0 +1 @@
|
||||
pub mod to_tokens;
|
63
crates/swc_css_modules/src/util/to_tokens.rs
Normal file
63
crates/swc_css_modules/src/util/to_tokens.rs
Normal file
@ -0,0 +1,63 @@
|
||||
use swc_common::{input::StringInput, Span, Spanned};
|
||||
use swc_css_ast::Tokens;
|
||||
use swc_css_codegen::{
|
||||
writer::basic::{BasicCssWriter, BasicCssWriterConfig, IndentType},
|
||||
CodeGenerator, CodegenConfig, Emit,
|
||||
};
|
||||
use swc_css_parser::{lexer::Lexer, parser::ParserConfig};
|
||||
|
||||
pub(crate) fn to_tokens_vec<N>(n: &[N]) -> Tokens
|
||||
where
|
||||
N: Spanned,
|
||||
for<'aa, 'ab> CodeGenerator<BasicCssWriter<'aa, &'ab mut String>>: swc_css_codegen::Emit<N>,
|
||||
{
|
||||
let lo = n.first().span().lo();
|
||||
let hi = n.last().span().lo();
|
||||
|
||||
let tokens = n.iter().flat_map(|n| to_tokens(n).tokens).collect();
|
||||
|
||||
Tokens {
|
||||
span: Span::new(lo, hi, Default::default()),
|
||||
tokens,
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn to_tokens<N>(n: &N) -> Tokens
|
||||
where
|
||||
N: Spanned,
|
||||
for<'aa, 'ab> CodeGenerator<BasicCssWriter<'aa, &'ab mut String>>: swc_css_codegen::Emit<N>,
|
||||
{
|
||||
let span = n.span();
|
||||
|
||||
let mut buf = String::new();
|
||||
{
|
||||
let wr = BasicCssWriter::new(
|
||||
&mut buf,
|
||||
None,
|
||||
BasicCssWriterConfig {
|
||||
indent_type: IndentType::Tab,
|
||||
..Default::default()
|
||||
},
|
||||
);
|
||||
let mut g = CodeGenerator::new(
|
||||
wr,
|
||||
CodegenConfig {
|
||||
..Default::default()
|
||||
},
|
||||
);
|
||||
|
||||
g.emit(n).unwrap();
|
||||
}
|
||||
|
||||
let lexer = Lexer::new(
|
||||
StringInput::new(&buf, span.lo, span.hi),
|
||||
ParserConfig {
|
||||
allow_wrong_line_comments: true,
|
||||
},
|
||||
);
|
||||
|
||||
Tokens {
|
||||
span,
|
||||
tokens: lexer.collect(),
|
||||
}
|
||||
}
|
95
crates/swc_css_modules/tests/fixture.rs
Normal file
95
crates/swc_css_modules/tests/fixture.rs
Normal file
@ -0,0 +1,95 @@
|
||||
use std::path::PathBuf;
|
||||
|
||||
use swc_atoms::JsWord;
|
||||
use swc_css_codegen::{
|
||||
writer::basic::{BasicCssWriter, BasicCssWriterConfig, IndentType},
|
||||
CodeGenerator, CodegenConfig, Emit,
|
||||
};
|
||||
use testing::NormalizedOutput;
|
||||
|
||||
#[testing::fixture("tests/fixture/**/*.css", exclude("compiled\\.css"))]
|
||||
fn imports(input: PathBuf) {
|
||||
testing::run_test(false, |cm, handler| {
|
||||
let fm = cm.load_file(&input).unwrap();
|
||||
let mut errors = vec![];
|
||||
let ss = swc_css_parser::parse_file(&fm, Default::default(), &mut errors).unwrap();
|
||||
let result = swc_css_modules::imports::analyze_imports(&ss);
|
||||
|
||||
if result.is_empty() {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
let s = serde_json::to_string_pretty(&result).unwrap();
|
||||
NormalizedOutput::from(s)
|
||||
.compare_to_file(input.with_file_name(format!(
|
||||
"{}.imports.json",
|
||||
input.file_stem().unwrap().to_string_lossy()
|
||||
)))
|
||||
.unwrap();
|
||||
|
||||
Ok(())
|
||||
})
|
||||
.unwrap();
|
||||
}
|
||||
|
||||
#[testing::fixture("tests/fixture/**/*.css", exclude("compiled\\.css"))]
|
||||
fn compile(input: PathBuf) {
|
||||
testing::run_test(false, |cm, handler| {
|
||||
let fm = cm.load_file(&input).unwrap();
|
||||
let mut errors = vec![];
|
||||
let mut ss = swc_css_parser::parse_file(&fm, Default::default(), &mut errors).unwrap();
|
||||
let result = swc_css_modules::imports::analyze_imports(&ss);
|
||||
|
||||
let transform_result = swc_css_modules::compile(&mut ss, TestConfig {});
|
||||
|
||||
let mut buf = String::new();
|
||||
{
|
||||
let mut wr = BasicCssWriter::new(
|
||||
&mut buf,
|
||||
None,
|
||||
BasicCssWriterConfig {
|
||||
indent_type: IndentType::Space,
|
||||
indent_width: 2,
|
||||
..Default::default()
|
||||
},
|
||||
);
|
||||
let mut g = CodeGenerator::new(
|
||||
wr,
|
||||
CodegenConfig {
|
||||
..Default::default()
|
||||
},
|
||||
);
|
||||
|
||||
g.emit(&ss).unwrap();
|
||||
}
|
||||
|
||||
NormalizedOutput::from(buf)
|
||||
.compare_to_file(input.with_file_name(format!(
|
||||
"{}.compiled.css",
|
||||
input.file_stem().unwrap().to_string_lossy()
|
||||
)))
|
||||
.unwrap();
|
||||
|
||||
if !transform_result.renamed.is_empty() {
|
||||
let transformed_classes =
|
||||
serde_json::to_string_pretty(&transform_result.renamed).unwrap();
|
||||
|
||||
NormalizedOutput::from(transformed_classes)
|
||||
.compare_to_file(input.with_file_name(format!(
|
||||
"{}.transform.json",
|
||||
input.file_stem().unwrap().to_string_lossy()
|
||||
)))
|
||||
.unwrap();
|
||||
}
|
||||
Ok(())
|
||||
})
|
||||
.unwrap();
|
||||
}
|
||||
|
||||
struct TestConfig {}
|
||||
|
||||
impl swc_css_modules::TransformConfig for TestConfig {
|
||||
fn new_name_for(&self, local: &JsWord) -> JsWord {
|
||||
format!("__local__{}", local).into()
|
||||
}
|
||||
}
|
@ -0,0 +1,111 @@
|
||||
@charset "UTF-8";
|
||||
.__local__class {
|
||||
color: red;
|
||||
background: url("./url/img.png");
|
||||
}
|
||||
.__local__class-duplicate-url {
|
||||
background: url("./url/img.png");
|
||||
}
|
||||
:root {
|
||||
--foo: 1px;
|
||||
--bar: 2px;
|
||||
}
|
||||
.__local__class {
|
||||
a: b c d;
|
||||
}
|
||||
.__local__two {}
|
||||
.__local__u-m\+ {
|
||||
a: b c d;
|
||||
}
|
||||
.__local__class {
|
||||
content: "\F10C";
|
||||
}
|
||||
@media only screen and (max-width: 600px) {
|
||||
body {
|
||||
background-color: lightblue;
|
||||
}
|
||||
}
|
||||
.__local__class {
|
||||
content: "\2193";
|
||||
content: "\2193\2193";
|
||||
content: "\2193 \2193";
|
||||
content: "\2193\2193\2193";
|
||||
content: "\2193 \2193 \2193";
|
||||
}
|
||||
.__local__-top {}
|
||||
.__local__-top {}
|
||||
#__local__\#test {}
|
||||
.__local__grid {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
.__local__grid.__local__-top {
|
||||
align-items: flex-start;
|
||||
}
|
||||
.__local__grid.__local__-top {
|
||||
align-items: flex-start;
|
||||
}
|
||||
.__local__grid.__local__-middle {
|
||||
align-items: center;
|
||||
}
|
||||
.__local__grid.__local__-bottom {
|
||||
align-items: flex-end;
|
||||
}
|
||||
.__local__u-m\+ {}
|
||||
.__local__u-m00002b {}
|
||||
#__local__u-m\+ {}
|
||||
body {
|
||||
font-family: '微软雅黑';
|
||||
}
|
||||
.__local__myStyle {
|
||||
content: '\e901';
|
||||
}
|
||||
.__local__myStyle {
|
||||
content: '\E901';
|
||||
}
|
||||
.__local__♫ {}
|
||||
.__local__\:\`\( {}
|
||||
.__local__1a2b3c {}
|
||||
#__local__\#fake-id {}
|
||||
#__local__-a-b-c- {}
|
||||
#__local__© {}
|
||||
:root {
|
||||
--title-align: center;
|
||||
--sr-only: {
|
||||
position: absolute;
|
||||
width: 1px;
|
||||
height: 1px;
|
||||
padding: 0;
|
||||
overflow: hidden;
|
||||
clip: rect(0,0,0,0);
|
||||
white-space: nowrap;
|
||||
clip-path: inset(50%);
|
||||
border: 0;
|
||||
};
|
||||
}
|
||||
.__local__test {
|
||||
content: "\2014\A0";
|
||||
content: "\2014 \A0";
|
||||
content: "\A0 \2014";
|
||||
content: "\A0\2014";
|
||||
margin-top: 1px\9;
|
||||
background-color: #000\9;
|
||||
}
|
||||
.__local__light.__local__on .__local__bulb:before {
|
||||
content: '💡';
|
||||
}
|
||||
.__local__base64 {
|
||||
background: url(data:img/jpg;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAADgdz34AAAAAXNSR0IArs4c6QAAAhxJREFUSA3tk71rU1EYxnMTEoJUkowWwdJ2akEHBfGjCiIF6ZylVUKSm2TqZLGI+A/oIu2UXm8C4lAyF4SWji0tdFLo1Eo7VN0SaBEhH7e/Nz0nPTfGOjiaCyfPc5734zlfCQT6X/8E/vUErL81KBaL9y3LSnued5PcITjUOwR3gsFg2bbtjYt6/NGgXC4P1et1l2aPLmpAbD0SidjpdPqgV15PA9d17zQajU8UxHQRK/4G35Q5pveAK8LlI1ZjPMnlcltnyvnvbwaO41xvtVqy7YHztMACq5xnlb9EY3dRdvcGo1kj5wR+t1AofDG0gM+A875E8DNjRCexsrV8Pj9ZqVQitVrtqejxePxjMpmss5hVTB4buXvMb2DyU2tBTRS+BjvNlVYUpPl7iuVO3Gq1uoQx1FtSOW1gPgp5ZWrdBtNmUDgv5asgxQ8F1af5vhY0YjyjuWC3wTszKJz7GBOkcFlQfW2ONq4FjWi+Hj6DRCKxQOK2TlY4x92EuYd5dvMAbYIzfikau3pu5tJ8KxaLLfo0cyKci7tK4TZjUMcoXAmHwzle0Q/RaC5P1GFMyVx9R9Fo9HYqlTrSgqDvFelAqVQa5hmuMR/WGtjAaBdjwBoDQ0ZsnwVMZjKZ9n0Zem8DSeDPdrnZbL6F2l3NOvUYNZk4oVDoRTabPe4EDNJzB0ZcjAYxeoZ2i3FNxQ7BHYw/cB/fldaH//UETgHHO8S44KbfXgAAAABJRU5ErkJggg==);
|
||||
}
|
||||
a[href=''] {
|
||||
color: red;
|
||||
}
|
||||
a[href='' i] {
|
||||
color: red;
|
||||
}
|
||||
a[href=""] {
|
||||
color: blue;
|
||||
}
|
||||
a[href="" i] {
|
||||
color: blue;
|
||||
}
|
135
crates/swc_css_modules/tests/fixture/basic-css-style-sheet.css
Normal file
135
crates/swc_css_modules/tests/fixture/basic-css-style-sheet.css
Normal file
@ -0,0 +1,135 @@
|
||||
@charset "UTF-8";
|
||||
|
||||
/* Comment */
|
||||
|
||||
.class {
|
||||
color: red;
|
||||
background: url("./url/img.png");
|
||||
}
|
||||
|
||||
.class-duplicate-url {
|
||||
background: url("./url/img.png");
|
||||
}
|
||||
|
||||
:root {
|
||||
--foo: 1px;
|
||||
--bar: 2px;
|
||||
}
|
||||
|
||||
.class { a: b c d; }
|
||||
|
||||
.two {}
|
||||
|
||||
.u-m\+ { a: b c d; }
|
||||
|
||||
.class { content: "\F10C" }
|
||||
|
||||
@media only screen and (max-width: 600px) {
|
||||
body {
|
||||
background-color: lightblue;
|
||||
}
|
||||
}
|
||||
|
||||
.class {
|
||||
content: "\2193";
|
||||
content: "\2193\2193";
|
||||
content: "\2193 \2193";
|
||||
content: "\2193\2193\2193";
|
||||
content: "\2193 \2193 \2193";
|
||||
}
|
||||
|
||||
.-top {}
|
||||
.\-top {}
|
||||
|
||||
#\#test {}
|
||||
|
||||
.grid {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
.grid.\-top {
|
||||
align-items: flex-start;
|
||||
}
|
||||
.grid.-top {
|
||||
align-items: flex-start;
|
||||
}
|
||||
.grid.\-middle {
|
||||
align-items: center;
|
||||
}
|
||||
.grid.\-bottom {
|
||||
align-items: flex-end;
|
||||
}
|
||||
|
||||
.u-m\00002b {}
|
||||
|
||||
.u-m00002b {}
|
||||
|
||||
#u-m\+ {}
|
||||
|
||||
body {
|
||||
font-family: '微软雅黑'; /* some chinese font name */
|
||||
}
|
||||
|
||||
.myStyle {
|
||||
content: '\e901';
|
||||
}
|
||||
|
||||
.myStyle {
|
||||
content: '\E901';
|
||||
}
|
||||
|
||||
.♫ {}
|
||||
|
||||
.\3A \`\( {} /* matches elements with class=":`(" */
|
||||
.\31 a2b3c {} /* matches elements with class="1a2b3c" */
|
||||
#\#fake-id {} /* matches the element with id="#fake-id" */
|
||||
#-a-b-c- {} /* matches the element with id="-a-b-c-" */
|
||||
#© {} /* matches the element with id="©" */
|
||||
|
||||
:root {
|
||||
--title-align: center;
|
||||
--sr-only: {
|
||||
position: absolute;
|
||||
width: 1px;
|
||||
height: 1px;
|
||||
padding: 0;
|
||||
overflow: hidden;
|
||||
clip: rect(0,0,0,0);
|
||||
white-space: nowrap;
|
||||
clip-path: inset(50%);
|
||||
border: 0;
|
||||
};
|
||||
}
|
||||
|
||||
.test {
|
||||
content: "\2014\A0";
|
||||
content: "\2014 \A0";
|
||||
content: "\A0 \2014";
|
||||
content: "\A0\2014";
|
||||
margin-top: 1px\9;
|
||||
background-color: #000\9;
|
||||
}
|
||||
|
||||
.light.on .bulb:before{
|
||||
content: '💡';
|
||||
}
|
||||
|
||||
.base64 {
|
||||
background: url(data:img/jpg;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAADgdz34AAAAAXNSR0IArs4c6QAAAhxJREFUSA3tk71rU1EYxnMTEoJUkowWwdJ2akEHBfGjCiIF6ZylVUKSm2TqZLGI+A/oIu2UXm8C4lAyF4SWji0tdFLo1Eo7VN0SaBEhH7e/Nz0nPTfGOjiaCyfPc5734zlfCQT6X/8E/vUErL81KBaL9y3LSnued5PcITjUOwR3gsFg2bbtjYt6/NGgXC4P1et1l2aPLmpAbD0SidjpdPqgV15PA9d17zQajU8UxHQRK/4G35Q5pveAK8LlI1ZjPMnlcltnyvnvbwaO41xvtVqy7YHztMACq5xnlb9EY3dRdvcGo1kj5wR+t1AofDG0gM+A875E8DNjRCexsrV8Pj9ZqVQitVrtqejxePxjMpmss5hVTB4buXvMb2DyU2tBTRS+BjvNlVYUpPl7iuVO3Gq1uoQx1FtSOW1gPgp5ZWrdBtNmUDgv5asgxQ8F1af5vhY0YjyjuWC3wTszKJz7GBOkcFlQfW2ONq4FjWi+Hj6DRCKxQOK2TlY4x92EuYd5dvMAbYIzfikau3pu5tJ8KxaLLfo0cyKci7tK4TZjUMcoXAmHwzle0Q/RaC5P1GFMyVx9R9Fo9HYqlTrSgqDvFelAqVQa5hmuMR/WGtjAaBdjwBoDQ0ZsnwVMZjKZ9n0Zem8DSeDPdrnZbL6F2l3NOvUYNZk4oVDoRTabPe4EDNJzB0ZcjAYxeoZ2i3FNxQ7BHYw/cB/fldaH//UETgHHO8S44KbfXgAAAABJRU5ErkJggg==);
|
||||
}
|
||||
|
||||
a[href=''] {
|
||||
color: red;
|
||||
}
|
||||
|
||||
a[href='' i] {
|
||||
color: red;
|
||||
}
|
||||
|
||||
a[href=""] {
|
||||
color: blue;
|
||||
}
|
||||
|
||||
a[href="" i] {
|
||||
color: blue;
|
||||
}
|
@ -0,0 +1,134 @@
|
||||
{
|
||||
"bulb": [
|
||||
{
|
||||
"type": "local",
|
||||
"name": "__local__bulb"
|
||||
}
|
||||
],
|
||||
"class": [
|
||||
{
|
||||
"type": "local",
|
||||
"name": "__local__class"
|
||||
}
|
||||
],
|
||||
"class-duplicate-url": [
|
||||
{
|
||||
"type": "local",
|
||||
"name": "__local__class-duplicate-url"
|
||||
}
|
||||
],
|
||||
"two": [
|
||||
{
|
||||
"type": "local",
|
||||
"name": "__local__two"
|
||||
}
|
||||
],
|
||||
"grid": [
|
||||
{
|
||||
"type": "local",
|
||||
"name": "__local__grid"
|
||||
}
|
||||
],
|
||||
"-top": [
|
||||
{
|
||||
"type": "local",
|
||||
"name": "__local__-top"
|
||||
}
|
||||
],
|
||||
"-bottom": [
|
||||
{
|
||||
"type": "local",
|
||||
"name": "__local__-bottom"
|
||||
}
|
||||
],
|
||||
"#test": [
|
||||
{
|
||||
"type": "local",
|
||||
"name": "__local__#test"
|
||||
}
|
||||
],
|
||||
"myStyle": [
|
||||
{
|
||||
"type": "local",
|
||||
"name": "__local__myStyle"
|
||||
}
|
||||
],
|
||||
"♫": [
|
||||
{
|
||||
"type": "local",
|
||||
"name": "__local__♫"
|
||||
}
|
||||
],
|
||||
":`(": [
|
||||
{
|
||||
"type": "local",
|
||||
"name": "__local__:`("
|
||||
}
|
||||
],
|
||||
"#fake-id": [
|
||||
{
|
||||
"type": "local",
|
||||
"name": "__local__#fake-id"
|
||||
}
|
||||
],
|
||||
"-a-b-c-": [
|
||||
{
|
||||
"type": "local",
|
||||
"name": "__local__-a-b-c-"
|
||||
}
|
||||
],
|
||||
"light": [
|
||||
{
|
||||
"type": "local",
|
||||
"name": "__local__light"
|
||||
}
|
||||
],
|
||||
"u-m+": [
|
||||
{
|
||||
"type": "local",
|
||||
"name": "__local__u-m+"
|
||||
}
|
||||
],
|
||||
"base64": [
|
||||
{
|
||||
"type": "local",
|
||||
"name": "__local__base64"
|
||||
}
|
||||
],
|
||||
"©": [
|
||||
{
|
||||
"type": "local",
|
||||
"name": "__local__©"
|
||||
}
|
||||
],
|
||||
"on": [
|
||||
{
|
||||
"type": "local",
|
||||
"name": "__local__on"
|
||||
}
|
||||
],
|
||||
"-middle": [
|
||||
{
|
||||
"type": "local",
|
||||
"name": "__local__-middle"
|
||||
}
|
||||
],
|
||||
"u-m00002b": [
|
||||
{
|
||||
"type": "local",
|
||||
"name": "__local__u-m00002b"
|
||||
}
|
||||
],
|
||||
"test": [
|
||||
{
|
||||
"type": "local",
|
||||
"name": "__local__test"
|
||||
}
|
||||
],
|
||||
"1a2b3c": [
|
||||
{
|
||||
"type": "local",
|
||||
"name": "__local__1a2b3c"
|
||||
}
|
||||
]
|
||||
}
|
112
crates/swc_css_modules/tests/fixture/basic.compiled.css
Normal file
112
crates/swc_css_modules/tests/fixture/basic.compiled.css
Normal file
@ -0,0 +1,112 @@
|
||||
@charset "UTF-8";
|
||||
@import 'imported.css';
|
||||
.__local__class {
|
||||
color: red;
|
||||
background: url("./url/img.png");
|
||||
}
|
||||
.__local__class-duplicate-url {
|
||||
background: url("./url/img.png");
|
||||
}
|
||||
:root {
|
||||
--foo: 1px;
|
||||
--bar: 2px;
|
||||
}
|
||||
.__local__class {
|
||||
a: b c d;
|
||||
}
|
||||
.__local__two {}
|
||||
.__local__u-m\+ {
|
||||
a: b c d;
|
||||
}
|
||||
.__local__class {
|
||||
content: "\F10C";
|
||||
}
|
||||
@media only screen and (max-width: 600px) {
|
||||
body {
|
||||
background-color: lightblue;
|
||||
}
|
||||
}
|
||||
.__local__class {
|
||||
content: "\2193";
|
||||
content: "\2193\2193";
|
||||
content: "\2193 \2193";
|
||||
content: "\2193\2193\2193";
|
||||
content: "\2193 \2193 \2193";
|
||||
}
|
||||
.__local__-top {}
|
||||
.__local__-top {}
|
||||
#__local__\#test {}
|
||||
.__local__grid {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
.__local__grid.__local__-top {
|
||||
align-items: flex-start;
|
||||
}
|
||||
.__local__grid.__local__-top {
|
||||
align-items: flex-start;
|
||||
}
|
||||
.__local__grid.__local__-middle {
|
||||
align-items: center;
|
||||
}
|
||||
.__local__grid.__local__-bottom {
|
||||
align-items: flex-end;
|
||||
}
|
||||
.__local__u-m\+ {}
|
||||
.__local__u-m00002b {}
|
||||
#__local__u-m\+ {}
|
||||
body {
|
||||
font-family: '微软雅黑';
|
||||
}
|
||||
.__local__myStyle {
|
||||
content: '\e901';
|
||||
}
|
||||
.__local__myStyle {
|
||||
content: '\E901';
|
||||
}
|
||||
.__local__♫ {}
|
||||
.__local__\:\`\( {}
|
||||
.__local__1a2b3c {}
|
||||
#__local__\#fake-id {}
|
||||
#__local__-a-b-c- {}
|
||||
#__local__© {}
|
||||
:root {
|
||||
--title-align: center;
|
||||
--sr-only: {
|
||||
position: absolute;
|
||||
width: 1px;
|
||||
height: 1px;
|
||||
padding: 0;
|
||||
overflow: hidden;
|
||||
clip: rect(0,0,0,0);
|
||||
white-space: nowrap;
|
||||
clip-path: inset(50%);
|
||||
border: 0;
|
||||
};
|
||||
}
|
||||
.__local__test {
|
||||
content: "\2014\A0";
|
||||
content: "\2014 \A0";
|
||||
content: "\A0 \2014";
|
||||
content: "\A0\2014";
|
||||
margin-top: 1px\9;
|
||||
background-color: #000\9;
|
||||
}
|
||||
.__local__light.__local__on .__local__bulb:before {
|
||||
content: '💡';
|
||||
}
|
||||
.__local__base64 {
|
||||
background: url(data:img/jpg;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAADgdz34AAAAAXNSR0IArs4c6QAAAhxJREFUSA3tk71rU1EYxnMTEoJUkowWwdJ2akEHBfGjCiIF6ZylVUKSm2TqZLGI+A/oIu2UXm8C4lAyF4SWji0tdFLo1Eo7VN0SaBEhH7e/Nz0nPTfGOjiaCyfPc5734zlfCQT6X/8E/vUErL81KBaL9y3LSnued5PcITjUOwR3gsFg2bbtjYt6/NGgXC4P1et1l2aPLmpAbD0SidjpdPqgV15PA9d17zQajU8UxHQRK/4G35Q5pveAK8LlI1ZjPMnlcltnyvnvbwaO41xvtVqy7YHztMACq5xnlb9EY3dRdvcGo1kj5wR+t1AofDG0gM+A875E8DNjRCexsrV8Pj9ZqVQitVrtqejxePxjMpmss5hVTB4buXvMb2DyU2tBTRS+BjvNlVYUpPl7iuVO3Gq1uoQx1FtSOW1gPgp5ZWrdBtNmUDgv5asgxQ8F1af5vhY0YjyjuWC3wTszKJz7GBOkcFlQfW2ONq4FjWi+Hj6DRCKxQOK2TlY4x92EuYd5dvMAbYIzfikau3pu5tJ8KxaLLfo0cyKci7tK4TZjUMcoXAmHwzle0Q/RaC5P1GFMyVx9R9Fo9HYqlTrSgqDvFelAqVQa5hmuMR/WGtjAaBdjwBoDQ0ZsnwVMZjKZ9n0Zem8DSeDPdrnZbL6F2l3NOvUYNZk4oVDoRTabPe4EDNJzB0ZcjAYxeoZ2i3FNxQ7BHYw/cB/fldaH//UETgHHO8S44KbfXgAAAABJRU5ErkJggg==);
|
||||
}
|
||||
a[href=''] {
|
||||
color: red;
|
||||
}
|
||||
a[href='' i] {
|
||||
color: red;
|
||||
}
|
||||
a[href=""] {
|
||||
color: blue;
|
||||
}
|
||||
a[href="" i] {
|
||||
color: blue;
|
||||
}
|
137
crates/swc_css_modules/tests/fixture/basic.css
Normal file
137
crates/swc_css_modules/tests/fixture/basic.css
Normal file
@ -0,0 +1,137 @@
|
||||
@charset "UTF-8";
|
||||
|
||||
@import 'imported.css';
|
||||
|
||||
/* Comment */
|
||||
|
||||
.class {
|
||||
color: red;
|
||||
background: url("./url/img.png");
|
||||
}
|
||||
|
||||
.class-duplicate-url {
|
||||
background: url("./url/img.png");
|
||||
}
|
||||
|
||||
:root {
|
||||
--foo: 1px;
|
||||
--bar: 2px;
|
||||
}
|
||||
|
||||
.class { a: b c d; }
|
||||
|
||||
.two {}
|
||||
|
||||
.u-m\+ { a: b c d; }
|
||||
|
||||
.class { content: "\F10C" }
|
||||
|
||||
@media only screen and (max-width: 600px) {
|
||||
body {
|
||||
background-color: lightblue;
|
||||
}
|
||||
}
|
||||
|
||||
.class {
|
||||
content: "\2193";
|
||||
content: "\2193\2193";
|
||||
content: "\2193 \2193";
|
||||
content: "\2193\2193\2193";
|
||||
content: "\2193 \2193 \2193";
|
||||
}
|
||||
|
||||
.-top {}
|
||||
.\-top {}
|
||||
|
||||
#\#test {}
|
||||
|
||||
.grid {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
.grid.\-top {
|
||||
align-items: flex-start;
|
||||
}
|
||||
.grid.-top {
|
||||
align-items: flex-start;
|
||||
}
|
||||
.grid.\-middle {
|
||||
align-items: center;
|
||||
}
|
||||
.grid.\-bottom {
|
||||
align-items: flex-end;
|
||||
}
|
||||
|
||||
.u-m\00002b {}
|
||||
|
||||
.u-m00002b {}
|
||||
|
||||
#u-m\+ {}
|
||||
|
||||
body {
|
||||
font-family: '微软雅黑'; /* some chinese font name */
|
||||
}
|
||||
|
||||
.myStyle {
|
||||
content: '\e901';
|
||||
}
|
||||
|
||||
.myStyle {
|
||||
content: '\E901';
|
||||
}
|
||||
|
||||
.♫ {}
|
||||
|
||||
.\3A \`\( {} /* matches elements with class=":`(" */
|
||||
.\31 a2b3c {} /* matches elements with class="1a2b3c" */
|
||||
#\#fake-id {} /* matches the element with id="#fake-id" */
|
||||
#-a-b-c- {} /* matches the element with id="-a-b-c-" */
|
||||
#© {} /* matches the element with id="©" */
|
||||
|
||||
:root {
|
||||
--title-align: center;
|
||||
--sr-only: {
|
||||
position: absolute;
|
||||
width: 1px;
|
||||
height: 1px;
|
||||
padding: 0;
|
||||
overflow: hidden;
|
||||
clip: rect(0,0,0,0);
|
||||
white-space: nowrap;
|
||||
clip-path: inset(50%);
|
||||
border: 0;
|
||||
};
|
||||
}
|
||||
|
||||
.test {
|
||||
content: "\2014\A0";
|
||||
content: "\2014 \A0";
|
||||
content: "\A0 \2014";
|
||||
content: "\A0\2014";
|
||||
margin-top: 1px\9;
|
||||
background-color: #000\9;
|
||||
}
|
||||
|
||||
.light.on .bulb:before{
|
||||
content: '💡';
|
||||
}
|
||||
|
||||
.base64 {
|
||||
background: url(data:img/jpg;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAADgdz34AAAAAXNSR0IArs4c6QAAAhxJREFUSA3tk71rU1EYxnMTEoJUkowWwdJ2akEHBfGjCiIF6ZylVUKSm2TqZLGI+A/oIu2UXm8C4lAyF4SWji0tdFLo1Eo7VN0SaBEhH7e/Nz0nPTfGOjiaCyfPc5734zlfCQT6X/8E/vUErL81KBaL9y3LSnued5PcITjUOwR3gsFg2bbtjYt6/NGgXC4P1et1l2aPLmpAbD0SidjpdPqgV15PA9d17zQajU8UxHQRK/4G35Q5pveAK8LlI1ZjPMnlcltnyvnvbwaO41xvtVqy7YHztMACq5xnlb9EY3dRdvcGo1kj5wR+t1AofDG0gM+A875E8DNjRCexsrV8Pj9ZqVQitVrtqejxePxjMpmss5hVTB4buXvMb2DyU2tBTRS+BjvNlVYUpPl7iuVO3Gq1uoQx1FtSOW1gPgp5ZWrdBtNmUDgv5asgxQ8F1af5vhY0YjyjuWC3wTszKJz7GBOkcFlQfW2ONq4FjWi+Hj6DRCKxQOK2TlY4x92EuYd5dvMAbYIzfikau3pu5tJ8KxaLLfo0cyKci7tK4TZjUMcoXAmHwzle0Q/RaC5P1GFMyVx9R9Fo9HYqlTrSgqDvFelAqVQa5hmuMR/WGtjAaBdjwBoDQ0ZsnwVMZjKZ9n0Zem8DSeDPdrnZbL6F2l3NOvUYNZk4oVDoRTabPe4EDNJzB0ZcjAYxeoZ2i3FNxQ7BHYw/cB/fldaH//UETgHHO8S44KbfXgAAAABJRU5ErkJggg==);
|
||||
}
|
||||
|
||||
a[href=''] {
|
||||
color: red;
|
||||
}
|
||||
|
||||
a[href='' i] {
|
||||
color: red;
|
||||
}
|
||||
|
||||
a[href=""] {
|
||||
color: blue;
|
||||
}
|
||||
|
||||
a[href="" i] {
|
||||
color: blue;
|
||||
}
|
3
crates/swc_css_modules/tests/fixture/basic.imports.json
Normal file
3
crates/swc_css_modules/tests/fixture/basic.imports.json
Normal file
@ -0,0 +1,3 @@
|
||||
[
|
||||
"imported.css"
|
||||
]
|
134
crates/swc_css_modules/tests/fixture/basic.transform.json
Normal file
134
crates/swc_css_modules/tests/fixture/basic.transform.json
Normal file
@ -0,0 +1,134 @@
|
||||
{
|
||||
"bulb": [
|
||||
{
|
||||
"type": "local",
|
||||
"name": "__local__bulb"
|
||||
}
|
||||
],
|
||||
"class": [
|
||||
{
|
||||
"type": "local",
|
||||
"name": "__local__class"
|
||||
}
|
||||
],
|
||||
"class-duplicate-url": [
|
||||
{
|
||||
"type": "local",
|
||||
"name": "__local__class-duplicate-url"
|
||||
}
|
||||
],
|
||||
"two": [
|
||||
{
|
||||
"type": "local",
|
||||
"name": "__local__two"
|
||||
}
|
||||
],
|
||||
"grid": [
|
||||
{
|
||||
"type": "local",
|
||||
"name": "__local__grid"
|
||||
}
|
||||
],
|
||||
"-top": [
|
||||
{
|
||||
"type": "local",
|
||||
"name": "__local__-top"
|
||||
}
|
||||
],
|
||||
"-bottom": [
|
||||
{
|
||||
"type": "local",
|
||||
"name": "__local__-bottom"
|
||||
}
|
||||
],
|
||||
"#test": [
|
||||
{
|
||||
"type": "local",
|
||||
"name": "__local__#test"
|
||||
}
|
||||
],
|
||||
"myStyle": [
|
||||
{
|
||||
"type": "local",
|
||||
"name": "__local__myStyle"
|
||||
}
|
||||
],
|
||||
"♫": [
|
||||
{
|
||||
"type": "local",
|
||||
"name": "__local__♫"
|
||||
}
|
||||
],
|
||||
":`(": [
|
||||
{
|
||||
"type": "local",
|
||||
"name": "__local__:`("
|
||||
}
|
||||
],
|
||||
"#fake-id": [
|
||||
{
|
||||
"type": "local",
|
||||
"name": "__local__#fake-id"
|
||||
}
|
||||
],
|
||||
"-a-b-c-": [
|
||||
{
|
||||
"type": "local",
|
||||
"name": "__local__-a-b-c-"
|
||||
}
|
||||
],
|
||||
"light": [
|
||||
{
|
||||
"type": "local",
|
||||
"name": "__local__light"
|
||||
}
|
||||
],
|
||||
"u-m+": [
|
||||
{
|
||||
"type": "local",
|
||||
"name": "__local__u-m+"
|
||||
}
|
||||
],
|
||||
"base64": [
|
||||
{
|
||||
"type": "local",
|
||||
"name": "__local__base64"
|
||||
}
|
||||
],
|
||||
"©": [
|
||||
{
|
||||
"type": "local",
|
||||
"name": "__local__©"
|
||||
}
|
||||
],
|
||||
"on": [
|
||||
{
|
||||
"type": "local",
|
||||
"name": "__local__on"
|
||||
}
|
||||
],
|
||||
"-middle": [
|
||||
{
|
||||
"type": "local",
|
||||
"name": "__local__-middle"
|
||||
}
|
||||
],
|
||||
"u-m00002b": [
|
||||
{
|
||||
"type": "local",
|
||||
"name": "__local__u-m00002b"
|
||||
}
|
||||
],
|
||||
"test": [
|
||||
{
|
||||
"type": "local",
|
||||
"name": "__local__test"
|
||||
}
|
||||
],
|
||||
"1a2b3c": [
|
||||
{
|
||||
"type": "local",
|
||||
"name": "__local__1a2b3c"
|
||||
}
|
||||
]
|
||||
}
|
0
crates/swc_css_modules/tests/fixture/empty.css
Normal file
0
crates/swc_css_modules/tests/fixture/empty.css
Normal file
2
crates/swc_css_modules/tests/fixture/error.compiled.css
Normal file
2
crates/swc_css_modules/tests/fixture/error.compiled.css
Normal file
@ -0,0 +1,2 @@
|
||||
.__local__some {
|
||||
invalid css;}
|
3
crates/swc_css_modules/tests/fixture/error.css
Normal file
3
crates/swc_css_modules/tests/fixture/error.css
Normal file
@ -0,0 +1,3 @@
|
||||
.some {
|
||||
invalid css;
|
||||
}
|
@ -0,0 +1,8 @@
|
||||
{
|
||||
"some": [
|
||||
{
|
||||
"type": "local",
|
||||
"name": "__local__some"
|
||||
}
|
||||
]
|
||||
}
|
@ -0,0 +1,3 @@
|
||||
.__local__foo {
|
||||
color: red;
|
||||
}
|
@ -0,0 +1,3 @@
|
||||
.foo {
|
||||
color: red;
|
||||
}
|
@ -0,0 +1,8 @@
|
||||
{
|
||||
"foo": [
|
||||
{
|
||||
"type": "local",
|
||||
"name": "__local__foo"
|
||||
}
|
||||
]
|
||||
}
|
@ -0,0 +1,6 @@
|
||||
@charset "UTF-8";
|
||||
@import './imported.css';
|
||||
.__local__class {
|
||||
color: red;
|
||||
background: url("./img.png");
|
||||
}
|
10
crates/swc_css_modules/tests/fixture/es-module/source.css
Normal file
10
crates/swc_css_modules/tests/fixture/es-module/source.css
Normal file
@ -0,0 +1,10 @@
|
||||
@charset "UTF-8";
|
||||
|
||||
@import './imported.css';
|
||||
|
||||
/* Comment */
|
||||
|
||||
.class {
|
||||
color: red;
|
||||
background: url("./img.png");
|
||||
}
|
@ -0,0 +1,3 @@
|
||||
[
|
||||
"./imported.css"
|
||||
]
|
@ -0,0 +1,8 @@
|
||||
{
|
||||
"class": [
|
||||
{
|
||||
"type": "local",
|
||||
"name": "__local__class"
|
||||
}
|
||||
]
|
||||
}
|
@ -0,0 +1,9 @@
|
||||
.__local__header-baz {
|
||||
color: red;
|
||||
}
|
||||
.__local__body {
|
||||
color: coral;
|
||||
}
|
||||
.__local__footer {
|
||||
color: blue;
|
||||
}
|
@ -0,0 +1,11 @@
|
||||
:local(.header-baz) {
|
||||
color: red;
|
||||
}
|
||||
|
||||
:local(.body) {
|
||||
color: coral;
|
||||
}
|
||||
|
||||
:local(.footer) {
|
||||
color: blue;
|
||||
}
|
@ -0,0 +1,20 @@
|
||||
{
|
||||
"body": [
|
||||
{
|
||||
"type": "local",
|
||||
"name": "__local__body"
|
||||
}
|
||||
],
|
||||
"footer": [
|
||||
{
|
||||
"type": "local",
|
||||
"name": "__local__footer"
|
||||
}
|
||||
],
|
||||
"header-baz": [
|
||||
{
|
||||
"type": "local",
|
||||
"name": "__local__header-baz"
|
||||
}
|
||||
]
|
||||
}
|
@ -0,0 +1,4 @@
|
||||
@import url("https://raw.githubusercontent.com/webpack-contrib/css-loader/master/test/fixtures/url/imported.css");
|
||||
a {
|
||||
background: url("https://raw.githubusercontent.com/webpack-contrib/css-loader/master/test/fixtures/url/img.png");
|
||||
}
|
@ -0,0 +1,5 @@
|
||||
@import url("https://raw.githubusercontent.com/webpack-contrib/css-loader/master/test/fixtures/url/imported.css");
|
||||
|
||||
a {
|
||||
background: url("https://raw.githubusercontent.com/webpack-contrib/css-loader/master/test/fixtures/url/img.png");
|
||||
}
|
@ -0,0 +1,3 @@
|
||||
[
|
||||
"https://raw.githubusercontent.com/webpack-contrib/css-loader/master/test/fixtures/url/imported.css"
|
||||
]
|
@ -0,0 +1,3 @@
|
||||
.__local__alias {
|
||||
color: red;
|
||||
}
|
3
crates/swc_css_modules/tests/fixture/import/alias.css
Normal file
3
crates/swc_css_modules/tests/fixture/import/alias.css
Normal file
@ -0,0 +1,3 @@
|
||||
.alias {
|
||||
color: red;
|
||||
}
|
@ -0,0 +1,8 @@
|
||||
{
|
||||
"alias": [
|
||||
{
|
||||
"type": "local",
|
||||
"name": "__local__alias"
|
||||
}
|
||||
]
|
||||
}
|
@ -0,0 +1,4 @@
|
||||
@import url(circular.css);
|
||||
div {
|
||||
color: red;
|
||||
}
|
@ -0,0 +1,5 @@
|
||||
@import url(circular.css);
|
||||
|
||||
div {
|
||||
color: red;
|
||||
}
|
@ -0,0 +1,3 @@
|
||||
[
|
||||
"circular.css"
|
||||
]
|
@ -0,0 +1,6 @@
|
||||
@import url(circular.css);
|
||||
@import url(circular.css);
|
||||
@import url("relative.css");
|
||||
a {
|
||||
color: red;
|
||||
}
|
11
crates/swc_css_modules/tests/fixture/import/circular.css
Normal file
11
crates/swc_css_modules/tests/fixture/import/circular.css
Normal file
@ -0,0 +1,11 @@
|
||||
@import url(circular.css);
|
||||
@import url(circular.css);
|
||||
/*
|
||||
// TODO fixed nested circular `@import`
|
||||
@import url(circular-nested.css);
|
||||
*/
|
||||
@import url("relative.css");
|
||||
|
||||
a {
|
||||
color: red;
|
||||
}
|
@ -0,0 +1,4 @@
|
||||
[
|
||||
"circular.css",
|
||||
"relative.css"
|
||||
]
|
@ -0,0 +1,5 @@
|
||||
@import url("data:text/css;charset=utf-8;base64,YSB7DQogIGNvbG9yOiByZWQ7DQp9");
|
||||
a {
|
||||
display: block;
|
||||
width: 100px;
|
||||
}
|
6
crates/swc_css_modules/tests/fixture/import/data-uri.css
Normal file
6
crates/swc_css_modules/tests/fixture/import/data-uri.css
Normal file
@ -0,0 +1,6 @@
|
||||
@import url("data:text/css;charset=utf-8;base64,YSB7DQogIGNvbG9yOiByZWQ7DQp9");
|
||||
|
||||
a {
|
||||
display: block;
|
||||
width: 100px;
|
||||
}
|
@ -0,0 +1,3 @@
|
||||
[
|
||||
"data:text/css;charset=utf-8;base64,YSB7DQogIGNvbG9yOiByZWQ7DQp9"
|
||||
]
|
@ -0,0 +1,6 @@
|
||||
@import url("./import-with-layer.css") layer(form);
|
||||
@layer form {
|
||||
.__local__bar {
|
||||
color: red;
|
||||
}
|
||||
}
|
@ -0,0 +1,7 @@
|
||||
@import url("./import-with-layer.css") layer(form);
|
||||
|
||||
@layer form {
|
||||
.bar {
|
||||
color: red;
|
||||
}
|
||||
}
|
@ -0,0 +1,3 @@
|
||||
[
|
||||
"./import-with-layer.css"
|
||||
]
|
@ -0,0 +1,8 @@
|
||||
{
|
||||
"bar": [
|
||||
{
|
||||
"type": "local",
|
||||
"name": "__local__bar"
|
||||
}
|
||||
]
|
||||
}
|
@ -0,0 +1 @@
|
||||
@import url("./import-with-media.css") screen and (min-width: 400px);
|
@ -0,0 +1 @@
|
||||
@import url("./import-with-media.css") screen and (min-width: 400px);
|
@ -0,0 +1,3 @@
|
||||
[
|
||||
"./import-with-media.css"
|
||||
]
|
@ -0,0 +1 @@
|
||||
@import url("./import-with-supports.css") supports(display: block);
|
@ -0,0 +1 @@
|
||||
@import url("./import-with-supports.css") supports(display: block);
|
@ -0,0 +1,3 @@
|
||||
[
|
||||
"./import-with-supports.css"
|
||||
]
|
@ -0,0 +1,2 @@
|
||||
@import url("./relative.css") layer;
|
||||
@import url("./test.css") layer;
|
@ -0,0 +1,3 @@
|
||||
/* unnamed wrapper layers around each sub-file */
|
||||
@import url("./relative.css") layer;
|
||||
@import url("./test.css") layer;
|
@ -0,0 +1,4 @@
|
||||
[
|
||||
"./relative.css",
|
||||
"./test.css"
|
||||
]
|
@ -0,0 +1 @@
|
||||
@import url("./deep-layer-base.css") layer(base);
|
@ -0,0 +1,2 @@
|
||||
/* the internal names are hidden from access, subsumed in "base" */
|
||||
@import url("./deep-layer-base.css") layer(base);
|
@ -0,0 +1,3 @@
|
||||
[
|
||||
"./deep-layer-base.css"
|
||||
]
|
@ -0,0 +1,6 @@
|
||||
@import url(./deep-layer-first-level.css) layer(bootstrap);
|
||||
@layer bootstrap {
|
||||
.__local__test {
|
||||
color: red;
|
||||
}
|
||||
}
|
@ -0,0 +1,8 @@
|
||||
@import url(./deep-layer-first-level.css) layer(bootstrap);
|
||||
|
||||
/* Adds additional styles to the bootstrap layer: */
|
||||
@layer bootstrap {
|
||||
.test {
|
||||
color: red;
|
||||
}
|
||||
}
|
@ -0,0 +1,3 @@
|
||||
[
|
||||
"./deep-layer-first-level.css"
|
||||
]
|
@ -0,0 +1,8 @@
|
||||
{
|
||||
"test": [
|
||||
{
|
||||
"type": "local",
|
||||
"name": "__local__test"
|
||||
}
|
||||
]
|
||||
}
|
@ -0,0 +1,3 @@
|
||||
div {
|
||||
color: red;
|
||||
}
|
@ -0,0 +1,4 @@
|
||||
@import url(./extensions-imported);
|
||||
a {
|
||||
color: red;
|
||||
}
|
@ -0,0 +1,5 @@
|
||||
@import url(./extensions-imported);
|
||||
|
||||
a {
|
||||
color: red;
|
||||
}
|
@ -0,0 +1,3 @@
|
||||
[
|
||||
"./extensions-imported"
|
||||
]
|
@ -0,0 +1,4 @@
|
||||
@import "/style.css";
|
||||
.__local__class {
|
||||
color: red;
|
||||
}
|
@ -0,0 +1,5 @@
|
||||
@import "/style.css";
|
||||
|
||||
.class {
|
||||
color: red;
|
||||
}
|
@ -0,0 +1,3 @@
|
||||
[
|
||||
"/style.css"
|
||||
]
|
@ -0,0 +1,8 @@
|
||||
{
|
||||
"class": [
|
||||
{
|
||||
"type": "local",
|
||||
"name": "__local__class"
|
||||
}
|
||||
]
|
||||
}
|
@ -0,0 +1 @@
|
||||
@import "~package-with-exports";
|
@ -0,0 +1 @@
|
||||
@import "~package-with-exports";
|
@ -0,0 +1,3 @@
|
||||
[
|
||||
"~package-with-exports"
|
||||
]
|
@ -0,0 +1 @@
|
||||
@import url("./import-with-supports-and-media.css") supports(display: flex) screen and (min-width: 400px);
|
@ -0,0 +1 @@
|
||||
@import url("./import-with-supports-and-media.css") supports(display: flex) screen and (min-width: 400px);
|
@ -0,0 +1,3 @@
|
||||
[
|
||||
"./import-with-supports-and-media.css"
|
||||
]
|
@ -0,0 +1,5 @@
|
||||
@import url("./test.css") layer;
|
||||
@import url("./relative.css") layer;
|
||||
.__local__foo {
|
||||
color: red;
|
||||
}
|
@ -0,0 +1,6 @@
|
||||
@import url("./test.css") layer;
|
||||
@import url("./relative.css") layer;
|
||||
|
||||
.foo {
|
||||
color: red;
|
||||
}
|
@ -0,0 +1,4 @@
|
||||
[
|
||||
"./relative.css",
|
||||
"./test.css"
|
||||
]
|
@ -0,0 +1,8 @@
|
||||
{
|
||||
"foo": [
|
||||
{
|
||||
"type": "local",
|
||||
"name": "__local__foo"
|
||||
}
|
||||
]
|
||||
}
|
@ -0,0 +1,7 @@
|
||||
@import url('./test.css') layer(base);
|
||||
@import url('./relative.css') layer(base);
|
||||
@layer base {
|
||||
.__local__foo {
|
||||
color: red;
|
||||
}
|
||||
}
|
@ -0,0 +1,8 @@
|
||||
@import url('./test.css') layer(base);
|
||||
@import url('./relative.css') layer(base);
|
||||
|
||||
@layer base {
|
||||
.foo {
|
||||
color: red;
|
||||
}
|
||||
}
|
@ -0,0 +1,4 @@
|
||||
[
|
||||
"./relative.css",
|
||||
"./test.css"
|
||||
]
|
@ -0,0 +1,8 @@
|
||||
{
|
||||
"foo": [
|
||||
{
|
||||
"type": "local",
|
||||
"name": "__local__foo"
|
||||
}
|
||||
]
|
||||
}
|
@ -0,0 +1,3 @@
|
||||
@import "test";
|
||||
@import "issue-683";
|
||||
@import "aliasesPackage";
|
@ -0,0 +1,3 @@
|
||||
@import "test";
|
||||
@import "issue-683";
|
||||
@import "aliasesPackage";
|
@ -0,0 +1,5 @@
|
||||
[
|
||||
"aliasesPackage",
|
||||
"issue-683",
|
||||
"test"
|
||||
]
|
@ -0,0 +1,5 @@
|
||||
@import url(/import/test.css);
|
||||
@import "/import/test.css";
|
||||
.__local__class {
|
||||
a: b c d;
|
||||
}
|
@ -0,0 +1,6 @@
|
||||
@import url(/import/test.css);
|
||||
@import "/import/test.css";
|
||||
|
||||
.class {
|
||||
a: b c d;
|
||||
}
|
@ -0,0 +1,3 @@
|
||||
[
|
||||
"/import/test.css"
|
||||
]
|
@ -0,0 +1,8 @@
|
||||
{
|
||||
"class": [
|
||||
{
|
||||
"type": "local",
|
||||
"name": "__local__class"
|
||||
}
|
||||
]
|
||||
}
|
@ -0,0 +1,4 @@
|
||||
@import url("./test.css") layer;
|
||||
.__local__foo {
|
||||
color: red;
|
||||
}
|
@ -0,0 +1,5 @@
|
||||
@import url("./test.css") layer;
|
||||
|
||||
.foo {
|
||||
color: red;
|
||||
}
|
@ -0,0 +1,3 @@
|
||||
[
|
||||
"./test.css"
|
||||
]
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user