Skip to content

Commit

Permalink
feat(css): Recover from invalid properties (#2312)
Browse files Browse the repository at this point in the history
swc_css_ast:
 - Add `DeclBlockItem`.
 - Change `DeclBlock.properties` to `DeclBlock.items`.

swc_css_parser:
 - Add a way to recovered errors.
  • Loading branch information
kdy1 authored Sep 28, 2021
1 parent e8a1710 commit b206404
Show file tree
Hide file tree
Showing 348 changed files with 614 additions and 459 deletions.
14 changes: 7 additions & 7 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

12 changes: 6 additions & 6 deletions css/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,11 @@ edition = "2018"
license = "Apache-2.0/MIT"
name = "swc_css"
repository = "https://github.com/swc-project/swc.git"
version = "0.7.0"
version = "0.8.0"

[dependencies]
swc_css_ast = {version = "0.6.0", path = "./ast"}
swc_css_codegen = {version = "0.5.0", path = "./codegen"}
swc_css_parser = {version = "0.7.0", path = "./parser"}
swc_css_utils = {version = "0.3.0", path = "./utils/"}
swc_css_visit = {version = "0.5.0", path = "./visit"}
swc_css_ast = {version = "0.7.0", path = "./ast"}
swc_css_codegen = {version = "0.6.0", path = "./codegen"}
swc_css_parser = {version = "0.8.0", path = "./parser"}
swc_css_utils = {version = "0.4.0", path = "./utils/"}
swc_css_visit = {version = "0.6.0", path = "./visit"}
2 changes: 1 addition & 1 deletion css/ast/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ edition = "2018"
license = "Apache-2.0/MIT"
name = "swc_css_ast"
repository = "https://github.com/swc-project/swc.git"
version = "0.6.0"
version = "0.7.0"

# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html

Expand Down
12 changes: 10 additions & 2 deletions css/ast/src/style_rule.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use crate::{ComplexSelector, Property};
use crate::{ComplexSelector, Property, Tokens};
use swc_common::{ast_node, Span};

#[ast_node("StyleRule")]
Expand All @@ -11,5 +11,13 @@ pub struct StyleRule {
#[ast_node("DeclBlock")]
pub struct DeclBlock {
pub span: Span,
pub properties: Vec<Property>,
pub items: Vec<DeclBlockItem>,
}

#[ast_node]
pub enum DeclBlockItem {
#[tag("Tokens")]
Invalid(Tokens),
#[tag("Property")]
Property(Property),
}
8 changes: 4 additions & 4 deletions css/codegen/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -6,17 +6,17 @@ edition = "2018"
license = "Apache-2.0/MIT"
name = "swc_css_codegen"
repository = "https://github.com/swc-project/swc.git"
version = "0.5.0"
version = "0.6.0"

[dependencies]
auto_impl = "0.4.1"
bitflags = "1.3.2"
swc_atoms = {version = "0.2.7", path = "../../atoms"}
swc_common = {version = "0.13.0", path = "../../common"}
swc_css_ast = {version = "0.6.0", path = "../ast/"}
swc_css_ast = {version = "0.7.0", path = "../ast/"}
swc_css_codegen_macros = {version = "0.2.0", path = "macros/"}

[dev-dependencies]
swc_css_parser = {version = "0.7.0", path = "../parser"}
swc_css_visit = {version = "0.5.0", path = "../visit"}
swc_css_parser = {version = "0.8.0", path = "../parser"}
swc_css_visit = {version = "0.6.0", path = "../visit"}
testing = {version = "0.14.0", path = "../../testing"}
13 changes: 9 additions & 4 deletions css/codegen/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -320,14 +320,19 @@ where
fn emit_decl_block(&mut self, n: &DeclBlock) -> Result {
punct!(self, "{");

self.emit_list(
&n.properties,
ListFormat::SemiDelimited | ListFormat::MultiLine,
)?;
self.emit_list(&n.items, ListFormat::SemiDelimited | ListFormat::MultiLine)?;

punct!(self, "}");
}

#[emitter]
fn emit_decl_block_item(&mut self, n: &DeclBlockItem) -> Result {
match n {
DeclBlockItem::Invalid(n) => emit!(self, n),
DeclBlockItem::Property(n) => emit!(self, n),
}
}

#[emitter]
fn emit_property(&mut self, n: &Property) -> Result {
emit!(self, n.name);
Expand Down
23 changes: 17 additions & 6 deletions css/codegen/tests/fixture.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use std::path::PathBuf;
use std::{mem::take, path::PathBuf};
use swc_common::{FileName, Span};
use swc_css_ast::Stylesheet;
use swc_css_codegen::{
Expand All @@ -18,8 +18,13 @@ fn parse_again(input: PathBuf) {

eprintln!("==== ==== Input ==== ====\n{}\n", fm.src);

let mut errors = vec![];
let mut stylesheet: Stylesheet =
parse_file(&fm, ParserConfig { parse_values: true }).unwrap();
parse_file(&fm, ParserConfig { parse_values: true }, &mut errors).unwrap();

for err in take(&mut errors) {
err.to_diagnostics(&handler).emit();
}

let mut css_str = String::new();
{
Expand All @@ -32,10 +37,16 @@ fn parse_again(input: PathBuf) {
eprintln!("==== ==== Codegen ==== ====\n{}\n", css_str);

let new_fm = cm.new_source_file(FileName::Anon, css_str);
let mut parsed: Stylesheet = parse_file(&new_fm, ParserConfig { parse_values: true })
.map_err(|err| {
err.to_diagnostics(&handler).emit();
})?;
let mut parsed: Stylesheet =
parse_file(&new_fm, ParserConfig { parse_values: true }, &mut errors).map_err(
|err| {
err.to_diagnostics(&handler).emit();
},
)?;

for err in errors {
err.to_diagnostics(&handler).emit();
}

stylesheet.visit_mut_with(&mut DropSpan);
parsed.visit_mut_with(&mut DropSpan);
Expand Down
6 changes: 3 additions & 3 deletions css/parser/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ edition = "2018"
license = "Apache-2.0/MIT"
name = "swc_css_parser"
repository = "https://github.com/swc-project/swc.git"
version = "0.7.0"
version = "0.8.0"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html

[features]
Expand All @@ -17,11 +17,11 @@ bitflags = "1.2.1"
lexical = "5.2.2"
swc_atoms = {version = "0.2.7", path = "../../atoms"}
swc_common = {version = "0.13.0", path = "../../common"}
swc_css_ast = {version = "0.6.0", path = "../ast"}
swc_css_ast = {version = "0.7.0", path = "../ast"}
unicode-xid = "0.2.2"

[dev-dependencies]
serde = "1.0.127"
serde_json = "1.0.66"
swc_css_visit = {version = "0.5.0", path = "../visit"}
swc_css_visit = {version = "0.6.0", path = "../visit"}
testing = {version = "0.14.0", path = "../../testing"}
41 changes: 34 additions & 7 deletions css/parser/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
#![deny(unused_must_use)]

use lexer::Lexer;
use parser::{input::TokensInput, PResult, Parser, ParserConfig};
use crate::{
error::Error,
lexer::Lexer,
parser::{input::TokensInput, PResult, Parser, ParserConfig},
};
use swc_common::{input::StringInput, BytePos, SourceFile};
use swc_css_ast::Tokens;

Expand All @@ -27,39 +30,63 @@ where
}

/// Parse a given string as `T`.
///
/// If there are syntax errors but if it was recoverable, it will be appendend
/// to `errors`.
pub fn parse_str<'a, T>(
src: &'a str,
start_pos: BytePos,
end_pos: BytePos,
config: ParserConfig,
errors: &mut Vec<Error>,
) -> PResult<T>
where
Parser<Lexer<StringInput<'a>>>: Parse<T>,
{
let lexer = Lexer::new(StringInput::new(src, start_pos, end_pos));
let mut parser = Parser::new(lexer, config);

parser.parse()
let res = parser.parse();
errors.extend(parser.take_errors());
res
}

/// Parse a given file as `T`.
pub fn parse_file<'a, T>(fm: &'a SourceFile, config: ParserConfig) -> PResult<T>
///
/// If there are syntax errors but if it was recoverable, it will be appendend
/// to `errors`.
pub fn parse_file<'a, T>(
fm: &'a SourceFile,
config: ParserConfig,
errors: &mut Vec<Error>,
) -> PResult<T>
where
Parser<Lexer<StringInput<'a>>>: Parse<T>,
{
let lexer = Lexer::new(StringInput::from(fm));
let mut parser = Parser::new(lexer, config);

parser.parse()
let res = parser.parse();
errors.extend(parser.take_errors());
res
}

/// Parse a given file as `T`.
pub fn parse_tokens<'a, T>(tokens: &'a Tokens, config: ParserConfig) -> PResult<T>
///
/// If there are syntax errors but if it was recoverable, it will be appendend
/// to `errors`.
pub fn parse_tokens<'a, T>(
tokens: &'a Tokens,
config: ParserConfig,
errors: &mut Vec<Error>,
) -> PResult<T>
where
Parser<TokensInput<'a>>: Parse<T>,
{
let lexer = TokensInput::new(tokens);
let mut parser = Parser::new(lexer, config);

parser.parse()
let res = parser.parse();
errors.extend(parser.take_errors());
res
}
3 changes: 1 addition & 2 deletions css/parser/src/parser/mod.rs
Original file line number Diff line number Diff line change
@@ -1,10 +1,9 @@
use std::mem::take;

use self::input::{Buffer, ParserInput};
use crate::{
error::{Error, ErrorKind},
Parse,
};
use std::mem::take;
use swc_atoms::js_word;
use swc_common::Span;
use swc_css_ast::*;
Expand Down
59 changes: 57 additions & 2 deletions css/parser/src/parser/style_rule.rs
Original file line number Diff line number Diff line change
Expand Up @@ -34,13 +34,29 @@ where

self.input.skip_ws()?;

let properties = self.parse_properties()?;
let items = self.parse_decl_block_items()?;

expect!(self, "}");

let span = span!(self, start);

Ok(DeclBlock { span, properties })
Ok(DeclBlock { span, items })
}

fn parse_decl_block_items(&mut self) -> PResult<Vec<DeclBlockItem>> {
let mut items = vec![];

while is!(self, Ident) {
items.push(self.parse()?);

if !eat!(self, ";") {
break;
}

self.input.skip_ws()?;
}

Ok(items)
}

pub(crate) fn parse_properties(&mut self) -> PResult<Vec<Property>> {
Expand Down Expand Up @@ -140,3 +156,42 @@ where
self.parse_properties()
}
}

impl<I> Parse<Vec<DeclBlockItem>> for Parser<I>
where
I: ParserInput,
{
fn parse(&mut self) -> PResult<Vec<DeclBlockItem>> {
self.parse_decl_block_items()
}
}

impl<I> Parse<DeclBlockItem> for Parser<I>
where
I: ParserInput,
{
fn parse(&mut self) -> PResult<DeclBlockItem> {
let start = self.input.state();
let start_pos = self.input.cur_span()?.lo;

let prop = self.parse().map(DeclBlockItem::Property);

match prop {
Ok(v) => return Ok(v),
Err(err) => {
self.errors.push(err);
}
}

self.input.reset(&start);
let mut tokens = vec![];
while !is_one_of!(self, EOF, ";", "}") {
tokens.extend(self.input.bump()?);
}

Ok(DeclBlockItem::Invalid(Tokens {
span: span!(self, start_pos),
tokens,
}))
}
}
Loading

0 comments on commit b206404

Please sign in to comment.