Skip to content

Commit bbab0d7

Browse files
authored
Merge pull request #718 from dev-five-git/fix/vanilla-extract-companions
fix(extractor): report vanilla-extract recipes and sprinkles imports under the css alias
2 parents 5f6ec30 + 94be5a6 commit bbab0d7

3 files changed

Lines changed: 106 additions & 0 deletions

File tree

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
{
2+
"changes": {
3+
"bindings/devup-ui-wasm/package.json": "Patch"
4+
},
5+
"note": "While @vanilla-extract/css is aliased, importing @vanilla-extract/recipes or @vanilla-extract/sprinkles is a build error naming the file, line and column: those packages generate CSS Devup UI does not compile, so their rules used to be silently missing from the stylesheet. The error says to write the styles with css(), or to disable the alias and build with the vanilla-extract plugin. Type-only imports are allowed",
6+
"date": "2026-10-01T00:00:00.000Z"
7+
}

‎libs/extractor/src/import_alias_visit.rs‎

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -231,6 +231,45 @@ pub fn transform_import_aliases<'a>(
231231
transform_import_aliases_with_edits(code, filename, package, import_aliases).0
232232
}
233233

234+
/// Packages generating CSS on top of vanilla-extract's, which Devup UI does not
235+
/// compile
236+
const VANILLA_EXTRACT_COMPANIONS: [&str; 2] =
237+
["@vanilla-extract/recipes", "@vanilla-extract/sprinkles"];
238+
239+
/// The value imports of vanilla-extract's companion packages, as errors at
240+
/// their offsets: while `@vanilla-extract/css` compiles to Devup UI, the
241+
/// rules they generate would never reach the stylesheet
242+
pub fn companion_errors(code: &str, filename: &str) -> Vec<(u32, String)> {
243+
if !VANILLA_EXTRACT_COMPANIONS
244+
.iter()
245+
.any(|package| code.contains(package))
246+
{
247+
return Vec::new();
248+
}
249+
let allocator = Allocator::default();
250+
let source_type = SourceType::from_path(filename).unwrap_or_default();
251+
let program = Parser::new(&allocator, code, source_type).parse().program;
252+
program
253+
.body
254+
.iter()
255+
.filter_map(|statement| match statement {
256+
Statement::ImportDeclaration(import)
257+
if !import.import_kind.is_type()
258+
&& VANILLA_EXTRACT_COMPANIONS.contains(&import.source.value.as_str()) =>
259+
{
260+
Some((
261+
import.span.start,
262+
format!(
263+
"`{}` generates CSS that Devup UI does not compile, so with the `@vanilla-extract/css` alias its rules would never reach the stylesheet. Fix: write the styles with `css()`, or set `importAliases: {{ '@vanilla-extract/css': false }}` and build with the vanilla-extract plugin",
264+
import.source.value
265+
),
266+
))
267+
}
268+
_ => None,
269+
})
270+
.collect()
271+
}
272+
234273
/// A replacement of `code[start..end]` by text of `length` bytes
235274
pub type Edit = (usize, usize, usize);
236275

@@ -783,6 +822,32 @@ mod tests {
783822
assert!(!code.contains("from 'styled-components'"), "{code}");
784823
}
785824

825+
#[test]
826+
fn test_vanilla_extract_companions_are_reported() {
827+
let errors = companion_errors(
828+
"import type { RecipeVariants } from '@vanilla-extract/recipes'
829+
import { recipe } from '@vanilla-extract/recipes'
830+
import { style } from '@vanilla-extract/css'
831+
import { createSprinkles } from '@vanilla-extract/sprinkles'",
832+
"a.css.ts",
833+
);
834+
assert_eq!(errors.len(), 2, "{errors:?}");
835+
assert!(
836+
errors[0]
837+
.1
838+
.starts_with("`@vanilla-extract/recipes` generates CSS")
839+
);
840+
assert!(
841+
errors[1]
842+
.1
843+
.starts_with("`@vanilla-extract/sprinkles` generates CSS")
844+
);
845+
assert_eq!(
846+
companion_errors("import { style } from '@vanilla-extract/css'", "a.css.ts"),
847+
vec![]
848+
);
849+
}
850+
786851
#[test]
787852
fn test_no_matching_alias() {
788853
assert_snapshot!(transform_import_aliases(

‎libs/extractor/src/lib.rs‎

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -294,6 +294,12 @@ fn extract_source(
294294
source_map: bool,
295295
resolver: Option<&ModuleResolver>,
296296
) -> Result<ExtractOutput, Box<dyn Error>> {
297+
if evaluated.is_none() && option.import_aliases.contains_key("@vanilla-extract/css") {
298+
let errors = import_alias_visit::companion_errors(code, filename);
299+
if !errors.is_empty() {
300+
return Err(located_errors(filename, code, &[], errors).into());
301+
}
302+
}
297303
// Step 1: Transform import aliases
298304
// e.g., `import styled from '@emotion/styled'` → `import { styled } from '@devup-ui/react'`
299305
// e.g., `import { style } from '@vanilla-extract/css'` → `import { style } from '@devup-ui/react'`
@@ -11238,6 +11244,34 @@ export const e = spin.toString()",
1123811244
}
1123911245
}
1124011246

11247+
#[test]
11248+
#[serial]
11249+
fn test_vanilla_extract_companion_import_is_an_error() {
11250+
let option = |aliased: bool| ExtractOption {
11251+
package: "@devup-ui/react".to_string(),
11252+
css_dir: "@devup-ui/react".to_string(),
11253+
single_css: true,
11254+
import_main_css: false,
11255+
import_aliases: if aliased {
11256+
HashMap::from([(
11257+
"@vanilla-extract/css".to_string(),
11258+
ImportAlias::NamedToNamed,
11259+
)])
11260+
} else {
11261+
HashMap::new()
11262+
},
11263+
};
11264+
let code = "import { recipe } from '@vanilla-extract/recipes'\nexport const b = recipe({})";
11265+
let error = extract("a.css.ts", code, option(true))
11266+
.unwrap_err()
11267+
.to_string();
11268+
assert!(
11269+
error.starts_with("a.css.ts:1:1: `@vanilla-extract/recipes`"),
11270+
"{error}"
11271+
);
11272+
assert!(extract("a.css.ts", code, option(false)).is_ok());
11273+
}
11274+
1124111275
#[test]
1124211276
#[serial]
1124311277
fn test_keyframes_no_args() {

0 commit comments

Comments
 (0)