Skip to content

Commit b85a16c

Browse files
authored
feat: support RawStringSource and RawBufferSource (#132)
* feat: support `RawStringSource` and `RawBufferSource` * test
1 parent 58994d9 commit b85a16c

File tree

5 files changed

+330
-8
lines changed

5 files changed

+330
-8
lines changed

src/concat_source.rs

Lines changed: 101 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -326,7 +326,7 @@ impl<'a> StreamChunks<'a> for ConcatSource {
326326

327327
#[cfg(test)]
328328
mod tests {
329-
use crate::{OriginalSource, RawSource};
329+
use crate::{OriginalSource, RawBufferSource, RawSource, RawStringSource};
330330

331331
use super::*;
332332

@@ -380,6 +380,106 @@ mod tests {
380380
);
381381
}
382382

383+
#[test]
384+
fn should_concat_two_sources2() {
385+
let mut source = ConcatSource::new([
386+
RawStringSource::from("Hello World\n".to_string()).boxed(),
387+
OriginalSource::new(
388+
"console.log('test');\nconsole.log('test2');\n",
389+
"console.js",
390+
)
391+
.boxed(),
392+
]);
393+
source.add(OriginalSource::new("Hello2\n", "hello.md"));
394+
395+
let expected_source =
396+
"Hello World\nconsole.log('test');\nconsole.log('test2');\nHello2\n";
397+
assert_eq!(source.size(), 62);
398+
assert_eq!(source.source(), expected_source);
399+
assert_eq!(
400+
source.map(&MapOptions::new(false)).unwrap(),
401+
SourceMap::from_json(
402+
r#"{
403+
"version": 3,
404+
"mappings": ";AAAA;AACA;ACDA",
405+
"names": [],
406+
"sources": ["console.js", "hello.md"],
407+
"sourcesContent": [
408+
"console.log('test');\nconsole.log('test2');\n",
409+
"Hello2\n"
410+
]
411+
}"#,
412+
)
413+
.unwrap()
414+
);
415+
assert_eq!(
416+
source.map(&MapOptions::default()).unwrap(),
417+
SourceMap::from_json(
418+
r#"{
419+
"version": 3,
420+
"mappings": ";AAAA;AACA;ACDA",
421+
"names": [],
422+
"sources": ["console.js", "hello.md"],
423+
"sourcesContent": [
424+
"console.log('test');\nconsole.log('test2');\n",
425+
"Hello2\n"
426+
]
427+
}"#
428+
)
429+
.unwrap()
430+
);
431+
}
432+
433+
#[test]
434+
fn should_concat_two_sources3() {
435+
let mut source = ConcatSource::new([
436+
RawBufferSource::from("Hello World\n".as_bytes()).boxed(),
437+
OriginalSource::new(
438+
"console.log('test');\nconsole.log('test2');\n",
439+
"console.js",
440+
)
441+
.boxed(),
442+
]);
443+
source.add(OriginalSource::new("Hello2\n", "hello.md"));
444+
445+
let expected_source =
446+
"Hello World\nconsole.log('test');\nconsole.log('test2');\nHello2\n";
447+
assert_eq!(source.size(), 62);
448+
assert_eq!(source.source(), expected_source);
449+
assert_eq!(
450+
source.map(&MapOptions::new(false)).unwrap(),
451+
SourceMap::from_json(
452+
r#"{
453+
"version": 3,
454+
"mappings": ";AAAA;AACA;ACDA",
455+
"names": [],
456+
"sources": ["console.js", "hello.md"],
457+
"sourcesContent": [
458+
"console.log('test');\nconsole.log('test2');\n",
459+
"Hello2\n"
460+
]
461+
}"#,
462+
)
463+
.unwrap()
464+
);
465+
assert_eq!(
466+
source.map(&MapOptions::default()).unwrap(),
467+
SourceMap::from_json(
468+
r#"{
469+
"version": 3,
470+
"mappings": ";AAAA;AACA;ACDA",
471+
"names": [],
472+
"sources": ["console.js", "hello.md"],
473+
"sourcesContent": [
474+
"console.log('test');\nconsole.log('test2');\n",
475+
"Hello2\n"
476+
]
477+
}"#
478+
)
479+
.unwrap()
480+
);
481+
}
482+
383483
#[test]
384484
fn should_be_able_to_handle_strings_for_all_methods() {
385485
let mut source = ConcatSource::new([

src/lib.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ pub use cached_source::CachedSource;
1818
pub use concat_source::ConcatSource;
1919
pub use error::{Error, Result};
2020
pub use original_source::OriginalSource;
21-
pub use raw_source::RawSource;
21+
pub use raw_source::{RawBufferSource, RawSource, RawStringSource};
2222
pub use replace_source::{ReplaceSource, ReplacementEnforce};
2323
pub use source::{
2424
BoxSource, MapOptions, Mapping, OriginalLocation, Source, SourceExt,

src/raw_source.rs

Lines changed: 209 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -215,6 +215,215 @@ impl<'a> StreamChunks<'a> for RawSource {
215215
}
216216
}
217217

218+
/// A string variant of [RawSource].
219+
///
220+
/// - [webpack-sources docs](https://github.com/webpack/webpack-sources/#rawsource).
221+
///
222+
/// ```
223+
/// use rspack_sources::{MapOptions, RawStringSource, Source};
224+
///
225+
/// let code = "some source code";
226+
/// let s = RawStringSource::from(code.to_string());
227+
/// assert_eq!(s.source(), code);
228+
/// assert_eq!(s.map(&MapOptions::default()), None);
229+
/// assert_eq!(s.size(), 16);
230+
/// ```
231+
#[derive(Clone, PartialEq, Eq)]
232+
pub struct RawStringSource(Cow<'static, str>);
233+
234+
#[cfg(any(target_arch = "x86_64", target_arch = "aarch64"))]
235+
static_assertions::assert_eq_size!(RawStringSource, [u8; 24]);
236+
237+
impl RawStringSource {
238+
/// Create a new [RawStringSource] from a static &str.
239+
///
240+
/// ```
241+
/// use rspack_sources::{RawStringSource, Source};
242+
///
243+
/// let code = "some source code";
244+
/// let s = RawStringSource::from_static(code);
245+
/// assert_eq!(s.source(), code);
246+
/// ```
247+
pub fn from_static(s: &'static str) -> Self {
248+
Self(Cow::Borrowed(s))
249+
}
250+
}
251+
252+
impl From<String> for RawStringSource {
253+
fn from(value: String) -> Self {
254+
Self(Cow::Owned(value))
255+
}
256+
}
257+
258+
impl From<&str> for RawStringSource {
259+
fn from(value: &str) -> Self {
260+
Self(Cow::Owned(value.to_owned()))
261+
}
262+
}
263+
264+
impl Source for RawStringSource {
265+
fn source(&self) -> Cow<str> {
266+
Cow::Borrowed(&self.0)
267+
}
268+
269+
fn buffer(&self) -> Cow<[u8]> {
270+
Cow::Borrowed(self.0.as_bytes())
271+
}
272+
273+
fn size(&self) -> usize {
274+
self.0.len()
275+
}
276+
277+
fn map(&self, _: &MapOptions) -> Option<SourceMap> {
278+
None
279+
}
280+
281+
fn to_writer(&self, writer: &mut dyn std::io::Write) -> std::io::Result<()> {
282+
writer.write_all(self.0.as_bytes())
283+
}
284+
}
285+
286+
impl std::fmt::Debug for RawStringSource {
287+
fn fmt(
288+
&self,
289+
f: &mut std::fmt::Formatter<'_>,
290+
) -> Result<(), std::fmt::Error> {
291+
let mut d = f.debug_tuple("RawStringSource");
292+
d.field(&self.0.chars().take(50).collect::<String>());
293+
d.finish()
294+
}
295+
}
296+
297+
impl Hash for RawStringSource {
298+
fn hash<H: Hasher>(&self, state: &mut H) {
299+
"RawStringSource".hash(state);
300+
self.buffer().hash(state);
301+
}
302+
}
303+
304+
impl<'a> StreamChunks<'a> for RawStringSource {
305+
fn stream_chunks(
306+
&'a self,
307+
options: &MapOptions,
308+
on_chunk: OnChunk<'_, 'a>,
309+
on_source: OnSource<'_, 'a>,
310+
on_name: OnName<'_, 'a>,
311+
) -> crate::helpers::GeneratedInfo {
312+
if options.final_source {
313+
get_generated_source_info(&self.source())
314+
} else {
315+
stream_chunks_of_raw_source(
316+
&self.0, options, on_chunk, on_source, on_name,
317+
)
318+
}
319+
}
320+
}
321+
322+
/// A buffer variant of [RawSource].
323+
///
324+
/// - [webpack-sources docs](https://github.com/webpack/webpack-sources/#rawsource).
325+
///
326+
/// ```
327+
/// use rspack_sources::{MapOptions, RawBufferSource, Source};
328+
///
329+
/// let code = "some source code".as_bytes();
330+
/// let s = RawBufferSource::from(code);
331+
/// assert_eq!(s.buffer(), code);
332+
/// assert_eq!(s.map(&MapOptions::default()), None);
333+
/// assert_eq!(s.size(), 16);
334+
/// ```
335+
#[derive(Clone, PartialEq, Eq)]
336+
pub struct RawBufferSource {
337+
value: Vec<u8>,
338+
value_as_string: OnceLock<String>,
339+
}
340+
341+
impl From<Vec<u8>> for RawBufferSource {
342+
fn from(value: Vec<u8>) -> Self {
343+
Self {
344+
value,
345+
value_as_string: Default::default(),
346+
}
347+
}
348+
}
349+
350+
impl From<&[u8]> for RawBufferSource {
351+
fn from(value: &[u8]) -> Self {
352+
Self {
353+
value: value.to_vec(),
354+
value_as_string: Default::default(),
355+
}
356+
}
357+
}
358+
359+
impl Source for RawBufferSource {
360+
fn source(&self) -> Cow<str> {
361+
Cow::Borrowed(
362+
self
363+
.value_as_string
364+
.get_or_init(|| String::from_utf8_lossy(&self.value).to_string()),
365+
)
366+
}
367+
368+
fn buffer(&self) -> Cow<[u8]> {
369+
Cow::Borrowed(&self.value)
370+
}
371+
372+
fn size(&self) -> usize {
373+
self.value.len()
374+
}
375+
376+
fn map(&self, _: &MapOptions) -> Option<SourceMap> {
377+
None
378+
}
379+
380+
fn to_writer(&self, writer: &mut dyn std::io::Write) -> std::io::Result<()> {
381+
writer.write_all(&self.value)
382+
}
383+
}
384+
385+
impl std::fmt::Debug for RawBufferSource {
386+
fn fmt(
387+
&self,
388+
f: &mut std::fmt::Formatter<'_>,
389+
) -> Result<(), std::fmt::Error> {
390+
let mut d = f.debug_tuple("RawBufferSource");
391+
d.field(&self.value.iter().take(50).copied().collect::<Vec<u8>>());
392+
d.finish()
393+
}
394+
}
395+
396+
impl Hash for RawBufferSource {
397+
fn hash<H: Hasher>(&self, state: &mut H) {
398+
"RawBufferSource".hash(state);
399+
self.buffer().hash(state);
400+
}
401+
}
402+
403+
impl<'a> StreamChunks<'a> for RawBufferSource {
404+
fn stream_chunks(
405+
&'a self,
406+
options: &MapOptions,
407+
on_chunk: OnChunk<'_, 'a>,
408+
on_source: OnSource<'_, 'a>,
409+
on_name: OnName<'_, 'a>,
410+
) -> crate::helpers::GeneratedInfo {
411+
if options.final_source {
412+
get_generated_source_info(&self.source())
413+
} else {
414+
stream_chunks_of_raw_source(
415+
self
416+
.value_as_string
417+
.get_or_init(|| String::from_utf8_lossy(&self.value).to_string()),
418+
options,
419+
on_chunk,
420+
on_source,
421+
on_name,
422+
)
423+
}
424+
}
425+
}
426+
218427
#[cfg(test)]
219428
mod tests {
220429
use crate::{ConcatSource, OriginalSource, ReplaceSource, SourceExt};

src/replace_source.rs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -745,7 +745,6 @@ mod tests {
745745
let mut last_line = 0;
746746
sourcemap
747747
.decoded_mappings()
748-
.into_iter()
749748
.map(|token| {
750749
format!(
751750
"{}:{} ->{} {}:{}{}",

0 commit comments

Comments
 (0)