Skip to content
Merged
Changes from 1 commit
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
82 changes: 3 additions & 79 deletions crates/turbopack-css/src/chunk/writer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,7 @@ use std::{

use anyhow::Result;
use turbo_tasks::{primitives::StringVc, ValueToString};
use turbopack_core::{
chunk::{ChunkItem, ModuleId},
code_builder::CodeBuilder,
};
use turbopack_core::{chunk::ChunkItem, code_builder::CodeBuilder};

use super::{CssChunkItemVc, CssImport};
use crate::chunk::CssChunkItem;
Expand Down Expand Up @@ -72,24 +69,15 @@ pub async fn expand_imports(
external_imports.push(url_vc);
}
None => {
let id = module_id_to_css_ident(&*chunk_item.id().await?);

// CSS chunk items can be duplicated across chunks. This can cause precedence
// issues (WEB-456). We use CSS layers to make sure that the first occurrence of
// a CSS chunk item determines its precedence.
// TODO(alexkirsz) This currently breaks users using @layer. We can fix that by
// moving our @layer into the user layer.
writeln!(code, "@layer {id} {{")?;
let id = &*chunk_item.id().await?;

writeln!(code, "/* {} */", id)?;
let content = chunk_item.content().await?;
code.push_source(
&content.inner_code,
content.source_map.map(|sm| sm.as_generate_source_map()),
);

// Closing @layer.
writeln!(code, "\n}}")?;

writeln!(code, "\n{}", close)?;

stack.pop();
Expand All @@ -99,67 +87,3 @@ pub async fn expand_imports(

Ok(external_imports)
}

fn module_id_to_css_ident(id: &ModuleId) -> String {
match id {
ModuleId::Number(n) => format!("n{}", n),
ModuleId::String(s) => format!("s{}", escape_css_ident(s)),
}
}

/// Escapes a string to be a valid CSS identifier, according to the rules
/// defined in https://developer.mozilla.org/en-US/docs/Web/CSS/ident
fn escape_css_ident(s: &str) -> String {
let mut escaped = String::new();

let mut starts_as_a_number = true;
for char in s.chars() {
if starts_as_a_number {
if char.is_ascii_digit() {
escaped.push('_');
starts_as_a_number = false;
} else if char != '-' {
starts_as_a_number = false;
}
}

if char.is_ascii_alphanumeric() || char == '-' || char == '_' {
escaped.push(char);
} else {
escaped.push('\\');
escaped.push(char);
}
}

escaped
}

#[cfg(test)]
mod tests {
//! These cases are taken from https://developer.mozilla.org/en-US/docs/Web/CSS/ident#examples.

use super::*;

#[test]
fn test_escape_css_ident_noop() {
assert_eq!(escape_css_ident("nono79"), "nono79");
assert_eq!(escape_css_ident("ground-level"), "ground-level");
assert_eq!(escape_css_ident("-test"), "-test");
assert_eq!(escape_css_ident("--toto"), "--toto");
assert_eq!(escape_css_ident("_internal"), "_internal");
// TODO(alexkirsz) Support unicode characters?
// assert_eq!(escape_css_ident("\\22 toto"), "\\22 toto");
// TODO(alexkirsz) This CSS identifier is already valid, but we escape
// it anyway.
assert_eq!(escape_css_ident("bili\\.bob"), "bili\\\\\\.bob");
}

#[test]
fn test_escape_css_ident() {
assert_eq!(escape_css_ident("34rem"), "_34rem");
assert_eq!(escape_css_ident("-12rad"), "-_12rad");
assert_eq!(escape_css_ident("bili.bob"), "bili\\.bob");
assert_eq!(escape_css_ident("'bilibob'"), "\\'bilibob\\'");
assert_eq!(escape_css_ident("\"bilibob\""), "\\\"bilibob\\\"");
}
}