Skip to content

Commit 754ba38

Browse files
committed
Refactor into multiple crates
1 parent 6c5df3e commit 754ba38

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

50 files changed

+548
-478
lines changed

Cargo.lock

Lines changed: 12 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 6 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,19 @@
1+
[workspace]
2+
members = [
3+
"node"
4+
]
5+
16
[package]
27
authors = ["Devon Govett <devongovett@gmail.com>"]
38
name = "parcel-css"
49
version = "0.1.0"
510
edition = "2018"
611

712
[lib]
8-
crate-type = ["cdylib"]
13+
crate-type = ["rlib"]
914

1015
[dependencies]
1116
serde = { version = "1.0.123", features = ["derive"] }
12-
serde_bytes = "0.11.5"
13-
serde_json = "*"
1417
cssparser = "0.28.1"
1518
cssparser-macros = "*"
1619
selectors = "*"
@@ -20,20 +23,5 @@ smallvec = { version = "1.7.0", features = ["union"] }
2023
bitflags = "*"
2124
parcel_sourcemap = "2.0.0"
2225

23-
[target.'cfg(target_os = "macos")'.dependencies]
24-
jemallocator = { version = "0.3.2", features = ["disable_initial_exec_tls"] }
25-
26-
[target.'cfg(not(target_arch = "wasm32"))'.dependencies]
27-
napi = { version = "1.7.10", features = ["serde-json"] }
28-
napi-derive = "1"
29-
30-
[target.'cfg(target_arch = "wasm32")'.dependencies]
31-
js-sys = "0.3"
32-
serde-wasm-bindgen = "0.3.0"
33-
wasm-bindgen = "0.2"
34-
35-
[target.'cfg(not(target_arch = "wasm32"))'.build-dependencies]
36-
napi-build = "1"
37-
3826
[profile.release]
3927
lto = true

build-prefixes.js

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -223,15 +223,22 @@ let prefixMapping = {
223223

224224
let enumify = (f) => f.replace(/^@([a-z])/, (_, x) => 'At' + x.toUpperCase()).replace(/^::([a-z])/, (_, x) => 'PseudoElement' + x.toUpperCase()).replace(/^:([a-z])/, (_, x) => 'PseudoClass' + x.toUpperCase()).replace(/(^|-)([a-z])/g, (_, a, x) => x.toUpperCase())
225225

226-
let s = `// This file is autogenerated by build-prefixes.js. DO NOT EDIT!
226+
let targets = `// This file is autogenerated by build-prefixes.js. DO NOT EDIT!
227227
228228
use serde::{Deserialize, Serialize};
229-
use super::VendorPrefix;
230229
231230
#[derive(Serialize, Debug, Deserialize, Clone, Copy, Default)]
232231
pub struct Browsers {
233232
pub ${Object.keys(browsers).filter(b => !(b in BROWSER_MAPPING)).sort().join(': Option<u32>,\n pub ')}: Option<u32>
234233
}
234+
`;
235+
236+
fs.writeFileSync('src/targets.rs', targets);
237+
238+
let s = `// This file is autogenerated by build-prefixes.js. DO NOT EDIT!
239+
240+
use crate::vendor_prefix::VendorPrefix;
241+
use crate::targets::Browsers;
235242
236243
#[allow(dead_code)]
237244
pub enum Feature {
@@ -286,11 +293,11 @@ pub fn is_webkit_gradient(browsers: Browsers) -> bool {
286293
}
287294
`;
288295

289-
fs.writeFileSync('src/properties/prefixes.rs', s);
296+
fs.writeFileSync('src/prefixes.rs', s);
290297

291298
let c = `// This file is autogenerated by build-prefixes.js. DO NOT EDIT!
292299
293-
use crate::properties::prefixes::Browsers;
300+
use crate::targets::Browsers;
294301
295302
pub enum Feature {
296303
${[...compat.keys()].flat().map(enumify).sort().join(',\n ')}

build.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ async function build() {
1212
}
1313

1414
await new Promise((resolve, reject) => {
15-
let args = ['build', '--platform'];
15+
let args = ['build', '--platform', '--cargo-cwd', 'node'];
1616
if (release) {
1717
args.push('--release');
1818
}

node/Cargo.toml

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
[package]
2+
authors = ["Devon Govett <devongovett@gmail.com>"]
3+
name = "parcel-css-node"
4+
version = "0.1.0"
5+
edition = "2018"
6+
7+
[lib]
8+
crate-type = ["cdylib"]
9+
10+
[dependencies]
11+
serde = { version = "1.0.123", features = ["derive"] }
12+
serde_bytes = "0.11.5"
13+
serde_json = "*"
14+
cssparser = "0.28.1"
15+
parcel-css = { path = "../" }
16+
parcel_sourcemap = "2.0.0"
17+
18+
[target.'cfg(target_os = "macos")'.dependencies]
19+
jemallocator = { version = "0.3.2", features = ["disable_initial_exec_tls"] }
20+
21+
[target.'cfg(not(target_arch = "wasm32"))'.dependencies]
22+
napi = { version = "1.7.10", features = ["serde-json"] }
23+
napi-derive = "1"
24+
25+
[target.'cfg(target_arch = "wasm32")'.dependencies]
26+
js-sys = "0.3"
27+
serde-wasm-bindgen = "0.3.0"
28+
wasm-bindgen = "0.2"
29+
30+
[target.'cfg(not(target_arch = "wasm32"))'.build-dependencies]
31+
napi-build = "1"
File renamed without changes.

node/src/lib.rs

Lines changed: 207 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,207 @@
1+
#[cfg(target_os = "macos")]
2+
#[global_allocator]
3+
static GLOBAL: jemallocator::Jemalloc = jemallocator::Jemalloc;
4+
5+
use serde::{Serialize, Deserialize};
6+
use parcel_css::stylesheet::StyleSheet;
7+
use parcel_css::targets::Browsers;
8+
9+
// ---------------------------------------------
10+
11+
#[cfg(target_arch = "wasm32")]
12+
use serde_wasm_bindgen::{from_value, Serializer};
13+
#[cfg(target_arch = "wasm32")]
14+
use wasm_bindgen::prelude::*;
15+
16+
#[cfg(target_arch = "wasm32")]
17+
#[wasm_bindgen]
18+
pub fn transform(config_val: JsValue) -> Result<JsValue, JsValue> {
19+
let config: Config = from_value(config_val).map_err(JsValue::from)?;
20+
let code = unsafe { std::str::from_utf8_unchecked(&config.code) };
21+
let res = compile(code, &config)?;
22+
let serializer = Serializer::new().serialize_maps_as_objects(true);
23+
res.serialize(&serializer).map_err(JsValue::from)
24+
}
25+
26+
// ---------------------------------------------
27+
28+
#[cfg(not(target_arch = "wasm32"))]
29+
use napi_derive::{js_function, module_exports};
30+
#[cfg(not(target_arch = "wasm32"))]
31+
use napi::{CallContext, JsObject, JsUnknown};
32+
33+
#[derive(Serialize)]
34+
#[serde(rename_all = "camelCase")]
35+
struct SourceMapJson<'a> {
36+
version: u8,
37+
mappings: String,
38+
sources: &'a Vec<String>,
39+
sources_content: &'a Vec<String>,
40+
names: &'a Vec<String>
41+
}
42+
43+
#[derive(Serialize)]
44+
struct TransformResult {
45+
#[serde(with = "serde_bytes")]
46+
code: Vec<u8>,
47+
#[serde(with = "serde_bytes")]
48+
map: Option<Vec<u8>>
49+
}
50+
51+
#[cfg(not(target_arch = "wasm32"))]
52+
#[js_function(1)]
53+
fn transform(ctx: CallContext) -> napi::Result<JsUnknown> {
54+
let opts = ctx.get::<JsObject>(0)?;
55+
let config: Config = ctx.env.from_js_value(opts)?;
56+
let code = unsafe { std::str::from_utf8_unchecked(&config.code) };
57+
let res = compile(code, &config);
58+
59+
match res {
60+
Ok(res) => ctx.env.to_js_value(&res),
61+
Err(err) => {
62+
match &err {
63+
CompileError::ParseError(e) => {
64+
// Generate an error with location information.
65+
let syntax_error = ctx.env.get_global()?
66+
.get_named_property::<napi::JsFunction>("SyntaxError")?;
67+
let reason = ctx.env.create_string_from_std(err.reason())?;
68+
let line = ctx.env.create_int32((e.location.line + 1) as i32)?;
69+
let col = ctx.env.create_int32(e.location.column as i32)?;
70+
let mut obj = syntax_error.new(&[reason])?;
71+
let filename = ctx.env.create_string_from_std(config.filename)?;
72+
obj.set_named_property("fileName", filename)?;
73+
let source = ctx.env.create_string(code)?;
74+
obj.set_named_property("source", source)?;
75+
let mut loc = ctx.env.create_object()?;
76+
loc.set_named_property("line", line)?;
77+
loc.set_named_property("column", col)?;
78+
obj.set_named_property("loc", loc)?;
79+
ctx.env.throw(obj)?;
80+
Ok(ctx.env.get_undefined()?.into_unknown())
81+
}
82+
_ => Err(err.into())
83+
}
84+
}
85+
}
86+
}
87+
88+
#[cfg(not(target_arch = "wasm32"))]
89+
#[module_exports]
90+
fn init(mut exports: JsObject) -> napi::Result<()> {
91+
exports.create_named_method("transform", transform)?;
92+
93+
Ok(())
94+
}
95+
96+
// ---------------------------------------------
97+
98+
#[derive(Serialize, Debug, Deserialize)]
99+
struct Config {
100+
pub filename: String,
101+
#[serde(with = "serde_bytes")]
102+
pub code: Vec<u8>,
103+
pub targets: Option<Browsers>,
104+
pub minify: Option<bool>,
105+
pub source_map: Option<bool>
106+
}
107+
108+
fn compile<'i>(code: &'i str, config: &Config) -> Result<TransformResult, CompileError<'i>> {
109+
let mut stylesheet = StyleSheet::parse(config.filename.clone(), &code)?;
110+
stylesheet.minify(config.targets); // TODO: should this be conditional?
111+
let (res, source_map) = stylesheet.to_css(
112+
config.minify.unwrap_or(false),
113+
config.source_map.unwrap_or(false),
114+
config.targets
115+
)?;
116+
117+
let map = if let Some(mut source_map) = source_map {
118+
source_map.set_source_content(0, code)?;
119+
let mut vlq_output: Vec<u8> = Vec::new();
120+
source_map.write_vlq(&mut vlq_output)?;
121+
122+
let sm = SourceMapJson {
123+
version: 3,
124+
mappings: unsafe { String::from_utf8_unchecked(vlq_output) },
125+
sources: source_map.get_sources(),
126+
sources_content: source_map.get_sources_content(),
127+
names: source_map.get_names()
128+
};
129+
130+
serde_json::to_vec(&sm).ok()
131+
} else {
132+
None
133+
};
134+
135+
Ok(TransformResult {
136+
code: res.into_bytes(),
137+
map
138+
})
139+
}
140+
141+
enum CompileError<'i> {
142+
ParseError(cssparser::ParseError<'i, ()>),
143+
PrinterError,
144+
SourceMapError(parcel_sourcemap::SourceMapError)
145+
}
146+
147+
impl<'i> CompileError<'i> {
148+
fn reason(&self) -> String {
149+
match self {
150+
CompileError::ParseError(e) => {
151+
match &e.kind {
152+
cssparser::ParseErrorKind::Basic(b) => {
153+
use cssparser::BasicParseErrorKind::*;
154+
match b {
155+
AtRuleBodyInvalid => "Invalid at rule body".into(),
156+
EndOfInput => "Unexpected end of input".into(),
157+
AtRuleInvalid(name) => format!("Unknown at rule: @{}", name),
158+
QualifiedRuleInvalid => "Invalid qualified rule".into(),
159+
UnexpectedToken(token) => format!("Unexpected token {:?}", token)
160+
}
161+
},
162+
_ => "Unknown error".into()
163+
}
164+
}
165+
CompileError::PrinterError => "Printer error".into(),
166+
_ => "Unknown error".into()
167+
}
168+
}
169+
}
170+
171+
impl<'i> From<cssparser::ParseError<'i, ()>> for CompileError<'i> {
172+
fn from(e: cssparser::ParseError<'i, ()>) -> CompileError<'i> {
173+
CompileError::ParseError(e)
174+
}
175+
}
176+
177+
impl<'i> From<std::fmt::Error> for CompileError<'i> {
178+
fn from(_: std::fmt::Error) -> CompileError<'i> {
179+
CompileError::PrinterError
180+
}
181+
}
182+
183+
impl<'i> From<parcel_sourcemap::SourceMapError> for CompileError<'i> {
184+
fn from(e: parcel_sourcemap::SourceMapError) -> CompileError<'i> {
185+
CompileError::SourceMapError(e)
186+
}
187+
}
188+
189+
#[cfg(not(target_arch = "wasm32"))]
190+
impl<'i> From<CompileError<'i>> for napi::Error {
191+
fn from(e: CompileError) -> napi::Error {
192+
match e {
193+
CompileError::SourceMapError(e) => e.into(),
194+
_ => napi::Error::new(napi::Status::GenericFailure, e.reason())
195+
}
196+
}
197+
}
198+
199+
#[cfg(target_arch = "wasm32")]
200+
impl<'i> From<CompileError<'i>> for wasm_bindgen::JsValue {
201+
fn from(e: CompileError) -> wasm_bindgen::JsValue {
202+
match e {
203+
CompileError::SourceMapError(e) => e.into(),
204+
_ => js_sys::Error::new(&e.reason()).into()
205+
}
206+
}
207+
}

package.json

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -44,10 +44,10 @@
4444
"scripts": {
4545
"build": "node build.js",
4646
"build-release": "node build.js --release",
47-
"wasm:build": "wasm-pack build --target nodejs",
48-
"wasm:build-release": "wasm-pack build --target nodejs --release",
49-
"wasm-browser:build": "wasm-pack build --target web",
50-
"wasm-browser:build-release": "wasm-pack build --target web --release",
47+
"wasm:build": "wasm-pack build node --target nodejs",
48+
"wasm:build-release": "wasm-pack build node --target nodejs --release",
49+
"wasm-browser:build": "wasm-pack build node --target web",
50+
"wasm-browser:build-release": "wasm-pack build node --target web --release",
5151
"playground:start": "parcel playground/index.html",
5252
"playground:build": "yarn wasm-browser:build-release && parcel build playground/index.html"
5353
}

playground/playground.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import init, {transform} from '../pkg';
1+
import init, {transform} from '../node/pkg';
22

33
let enc = new TextEncoder();
44
let dec = new TextDecoder();

src/compat.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
// This file is autogenerated by build-prefixes.js. DO NOT EDIT!
22

3-
use crate::properties::prefixes::Browsers;
3+
use crate::targets::Browsers;
44

55
pub enum Feature {
66
Clamp,

0 commit comments

Comments
 (0)