{"record":{"id":"6bab1cdaac6f5ccb","repo":"swc-project/swc","slug":"expected-url-or-function","errorCode":null,"errorMessage":"Expected url or function","messagePattern":"Expected url or function","errorType":"validation","errorClass":"swc_css_parser::error::Error","httpStatus":null,"severity":"error","filePath":"crates/swc_css_parser/src/parser/at_rules/mod.rs","lineNumber":1311,"sourceCode":"        match cur!(self) {\n            tok!(\"url\") => Ok(DocumentPreludeMatchingFunction::Url(self.parse()?)),\n            Token::Function {\n                value: function_name,\n                ..\n            } => {\n                if matches_eq_ignore_ascii_case!(function_name, \"url\", \"src\") {\n                    Ok(DocumentPreludeMatchingFunction::Url(self.parse()?))\n                } else {\n                    // TODO improve me\n                    let function = self.parse()?;\n\n                    Ok(DocumentPreludeMatchingFunction::Function(function))\n                }\n            }\n            _ => {\n                let span = self.input.cur_span();\n\n                Err(Error::new(span, ErrorKind::Expected(\"url or function\")))\n            }\n        }\n    }\n}\n\nimpl<I> Parse<MediaQueryList> for Parser<I>\nwhere\n    I: ParserInput,\n{\n    fn parse(&mut self) -> PResult<MediaQueryList> {\n        let query: MediaQuery = self.parse()?;\n        let mut queries = vec![query];\n\n        // TODO error recovery\n        // To parse a <media-query-list> production, parse a comma-separated list of\n        // component values, then parse each entry in the returned list as a\n        // <media-query>. Its value is the list of <media-query>s so produced.\n        loop {","sourceCodeStart":1293,"sourceCodeEnd":1329,"githubUrl":"https://github.com/swc-project/swc/blob/5176682b65416c6b5de6b47379ae1588ea3ecb3f/crates/swc_css_parser/src/parser/at_rules/mod.rs#L1293-L1329","documentation":"Thrown by Parse<DocumentPreludeMatchingFunction> while parsing the prelude of @document. Every item in the comma-separated URL-matching list must be either a url() token or another function — the legacy Firefox matching functions url-prefix(), domain(), regexp(), or src(). If the current token is neither, ErrorKind::Expected(\"url or function\") is returned for that position. @document is legacy Firefox-only syntax, so this prelude is stricter than most at-rules.","triggerScenarios":"'@document \"https://example.com\" { }' (a string instead of url(...)), '@document example.com { }' (bare ident), '@document #frag { }', or a comma item that is a number/dimension/string rather than a function token.","commonSituations":"Porting old Firefox user-stylesheets or UA CSS forward, generated CSS where a URL was interpolated without the url() wrapper, and code that assumes other engines parse @document (they do not, so the whole sheet fails in swc-based tooling).","solutions":["Wrap plain URLs: @document url(https://example.com) { }","Use the matching functions for the other forms: url-prefix(\"https://\"), domain(\"example.com\"), regexp(\"https:.*\")","Drop the @document rule entirely — it is not supported by modern engines and keeps the parse from succeeding","If you gate imports by URL, move the decision into the bundler instead of CSS"],"exampleFix":"/* before */\n@document \"https://example.com/\" {\n  h1 { color: red; }\n}\n\n/* after */\n@document url-prefix(\"https://example.com/\") {\n  h1 { color: red; }\n}","handlingStrategy":"try-catch","validationCode":"fn document_prelude_ok(css: &str) -> bool {\n    let prelude = css.split_once('{').map_or(css, |(p, _)| p);\n    for item in prelude.split(',') {\n        let t = item.trim();\n        if t.is_empty() {\n            return false;\n        }\n        let head = t.split('(').next().unwrap_or(\"\").trim();\n        let is_fn = t.contains('(') && t.ends_with(')');\n        let is_url_tok = head.eq_ignore_ascii_case(\"url\") && !t.starts_with('\"');\n        if !(is_fn || is_url_tok) {\n            return false;\n        }\n    }\n    true\n}","typeGuard":"fn is_document_prelude_error(e: &swc_css_parser::error::Error) -> bool {\n    matches!(e.kind(), swc_css_parser::error::ErrorKind::Expected(m) if *m == \"url or function\")\n}","tryCatchPattern":"match parse_file::<Stylesheet>(&fm, None, config, &mut errors) {\n    Ok(sheet) => handle(sheet),\n    Err(err) if is_document_prelude_error(&err) => {\n        // legacy Firefox-only rule: safest recovery is to drop @document blocks at generation time\n        log_and_strip_at_rule(css, *err.into_inner().0, \"@document\");\n    }\n    Err(err) => return Err(err.into()),\n}","preventionTips":["Do not emit @document for cross-browser builds; gate imports in the bundler instead","Wrap every URL in url() / url-prefix() / domain() / regexp()","Strip legacy Firefox UA stylesheets before modern tooling","Reject any @document comma item that is a bare string or ident"],"tags":["css","swc","parser","at-rules","document-rule","firefox","syntax"],"backgroundTag":"css-document-rule-syntax","analyzedSha":"5176682b65416c6b5de6b47379ae1588ea3ecb3f","analyzedAt":"2026-08-17T16:16:52.067Z","contentChangedAt":"2026-08-17T16:16:52.067Z","schemaVersion":2},"datasetVersion":"2026-09-14T00:17:10.932Z"}