Skip to content

Commit

Permalink
feat: support RawStringSource and RawBufferSource (#132)
Browse files Browse the repository at this point in the history
* feat: support `RawStringSource` and `RawBufferSource`

* test
  • Loading branch information
h-a-n-a authored Dec 4, 2024
1 parent 58994d9 commit b85a16c
Show file tree
Hide file tree
Showing 5 changed files with 330 additions and 8 deletions.
102 changes: 101 additions & 1 deletion src/concat_source.rs
Original file line number Diff line number Diff line change
Expand Up @@ -326,7 +326,7 @@ impl<'a> StreamChunks<'a> for ConcatSource {

#[cfg(test)]
mod tests {
use crate::{OriginalSource, RawSource};
use crate::{OriginalSource, RawBufferSource, RawSource, RawStringSource};

use super::*;

Expand Down Expand Up @@ -380,6 +380,106 @@ mod tests {
);
}

#[test]
fn should_concat_two_sources2() {
let mut source = ConcatSource::new([
RawStringSource::from("Hello World\n".to_string()).boxed(),
OriginalSource::new(
"console.log('test');\nconsole.log('test2');\n",
"console.js",
)
.boxed(),
]);
source.add(OriginalSource::new("Hello2\n", "hello.md"));

let expected_source =
"Hello World\nconsole.log('test');\nconsole.log('test2');\nHello2\n";
assert_eq!(source.size(), 62);
assert_eq!(source.source(), expected_source);
assert_eq!(
source.map(&MapOptions::new(false)).unwrap(),
SourceMap::from_json(
r#"{
"version": 3,
"mappings": ";AAAA;AACA;ACDA",
"names": [],
"sources": ["console.js", "hello.md"],
"sourcesContent": [
"console.log('test');\nconsole.log('test2');\n",
"Hello2\n"
]
}"#,
)
.unwrap()
);
assert_eq!(
source.map(&MapOptions::default()).unwrap(),
SourceMap::from_json(
r#"{
"version": 3,
"mappings": ";AAAA;AACA;ACDA",
"names": [],
"sources": ["console.js", "hello.md"],
"sourcesContent": [
"console.log('test');\nconsole.log('test2');\n",
"Hello2\n"
]
}"#
)
.unwrap()
);
}

#[test]
fn should_concat_two_sources3() {
let mut source = ConcatSource::new([
RawBufferSource::from("Hello World\n".as_bytes()).boxed(),
OriginalSource::new(
"console.log('test');\nconsole.log('test2');\n",
"console.js",
)
.boxed(),
]);
source.add(OriginalSource::new("Hello2\n", "hello.md"));

let expected_source =
"Hello World\nconsole.log('test');\nconsole.log('test2');\nHello2\n";
assert_eq!(source.size(), 62);
assert_eq!(source.source(), expected_source);
assert_eq!(
source.map(&MapOptions::new(false)).unwrap(),
SourceMap::from_json(
r#"{
"version": 3,
"mappings": ";AAAA;AACA;ACDA",
"names": [],
"sources": ["console.js", "hello.md"],
"sourcesContent": [
"console.log('test');\nconsole.log('test2');\n",
"Hello2\n"
]
}"#,
)
.unwrap()
);
assert_eq!(
source.map(&MapOptions::default()).unwrap(),
SourceMap::from_json(
r#"{
"version": 3,
"mappings": ";AAAA;AACA;ACDA",
"names": [],
"sources": ["console.js", "hello.md"],
"sourcesContent": [
"console.log('test');\nconsole.log('test2');\n",
"Hello2\n"
]
}"#
)
.unwrap()
);
}

#[test]
fn should_be_able_to_handle_strings_for_all_methods() {
let mut source = ConcatSource::new([
Expand Down
2 changes: 1 addition & 1 deletion src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ pub use cached_source::CachedSource;
pub use concat_source::ConcatSource;
pub use error::{Error, Result};
pub use original_source::OriginalSource;
pub use raw_source::RawSource;
pub use raw_source::{RawBufferSource, RawSource, RawStringSource};
pub use replace_source::{ReplaceSource, ReplacementEnforce};
pub use source::{
BoxSource, MapOptions, Mapping, OriginalLocation, Source, SourceExt,
Expand Down
209 changes: 209 additions & 0 deletions src/raw_source.rs
Original file line number Diff line number Diff line change
Expand Up @@ -215,6 +215,215 @@ impl<'a> StreamChunks<'a> for RawSource {
}
}

/// A string variant of [RawSource].
///
/// - [webpack-sources docs](https://github.com/webpack/webpack-sources/#rawsource).
///
/// ```
/// use rspack_sources::{MapOptions, RawStringSource, Source};
///
/// let code = "some source code";
/// let s = RawStringSource::from(code.to_string());
/// assert_eq!(s.source(), code);
/// assert_eq!(s.map(&MapOptions::default()), None);
/// assert_eq!(s.size(), 16);
/// ```
#[derive(Clone, PartialEq, Eq)]
pub struct RawStringSource(Cow<'static, str>);

#[cfg(any(target_arch = "x86_64", target_arch = "aarch64"))]
static_assertions::assert_eq_size!(RawStringSource, [u8; 24]);

impl RawStringSource {
/// Create a new [RawStringSource] from a static &str.
///
/// ```
/// use rspack_sources::{RawStringSource, Source};
///
/// let code = "some source code";
/// let s = RawStringSource::from_static(code);
/// assert_eq!(s.source(), code);
/// ```
pub fn from_static(s: &'static str) -> Self {
Self(Cow::Borrowed(s))
}
}

impl From<String> for RawStringSource {
fn from(value: String) -> Self {
Self(Cow::Owned(value))
}
}

impl From<&str> for RawStringSource {
fn from(value: &str) -> Self {
Self(Cow::Owned(value.to_owned()))
}
}

impl Source for RawStringSource {
fn source(&self) -> Cow<str> {
Cow::Borrowed(&self.0)
}

fn buffer(&self) -> Cow<[u8]> {
Cow::Borrowed(self.0.as_bytes())
}

fn size(&self) -> usize {
self.0.len()
}

fn map(&self, _: &MapOptions) -> Option<SourceMap> {
None
}

fn to_writer(&self, writer: &mut dyn std::io::Write) -> std::io::Result<()> {
writer.write_all(self.0.as_bytes())
}
}

impl std::fmt::Debug for RawStringSource {
fn fmt(
&self,
f: &mut std::fmt::Formatter<'_>,
) -> Result<(), std::fmt::Error> {
let mut d = f.debug_tuple("RawStringSource");
d.field(&self.0.chars().take(50).collect::<String>());
d.finish()
}
}

impl Hash for RawStringSource {
fn hash<H: Hasher>(&self, state: &mut H) {
"RawStringSource".hash(state);
self.buffer().hash(state);
}
}

impl<'a> StreamChunks<'a> for RawStringSource {
fn stream_chunks(
&'a self,
options: &MapOptions,
on_chunk: OnChunk<'_, 'a>,
on_source: OnSource<'_, 'a>,
on_name: OnName<'_, 'a>,
) -> crate::helpers::GeneratedInfo {
if options.final_source {
get_generated_source_info(&self.source())
} else {
stream_chunks_of_raw_source(
&self.0, options, on_chunk, on_source, on_name,
)
}
}
}

/// A buffer variant of [RawSource].
///
/// - [webpack-sources docs](https://github.com/webpack/webpack-sources/#rawsource).
///
/// ```
/// use rspack_sources::{MapOptions, RawBufferSource, Source};
///
/// let code = "some source code".as_bytes();
/// let s = RawBufferSource::from(code);
/// assert_eq!(s.buffer(), code);
/// assert_eq!(s.map(&MapOptions::default()), None);
/// assert_eq!(s.size(), 16);
/// ```
#[derive(Clone, PartialEq, Eq)]
pub struct RawBufferSource {
value: Vec<u8>,
value_as_string: OnceLock<String>,
}

impl From<Vec<u8>> for RawBufferSource {
fn from(value: Vec<u8>) -> Self {
Self {
value,
value_as_string: Default::default(),
}
}
}

impl From<&[u8]> for RawBufferSource {
fn from(value: &[u8]) -> Self {
Self {
value: value.to_vec(),
value_as_string: Default::default(),
}
}
}

impl Source for RawBufferSource {
fn source(&self) -> Cow<str> {
Cow::Borrowed(
self
.value_as_string
.get_or_init(|| String::from_utf8_lossy(&self.value).to_string()),
)
}

fn buffer(&self) -> Cow<[u8]> {
Cow::Borrowed(&self.value)
}

fn size(&self) -> usize {
self.value.len()
}

fn map(&self, _: &MapOptions) -> Option<SourceMap> {
None
}

fn to_writer(&self, writer: &mut dyn std::io::Write) -> std::io::Result<()> {
writer.write_all(&self.value)
}
}

impl std::fmt::Debug for RawBufferSource {
fn fmt(
&self,
f: &mut std::fmt::Formatter<'_>,
) -> Result<(), std::fmt::Error> {
let mut d = f.debug_tuple("RawBufferSource");
d.field(&self.value.iter().take(50).copied().collect::<Vec<u8>>());
d.finish()
}
}

impl Hash for RawBufferSource {
fn hash<H: Hasher>(&self, state: &mut H) {
"RawBufferSource".hash(state);
self.buffer().hash(state);
}
}

impl<'a> StreamChunks<'a> for RawBufferSource {
fn stream_chunks(
&'a self,
options: &MapOptions,
on_chunk: OnChunk<'_, 'a>,
on_source: OnSource<'_, 'a>,
on_name: OnName<'_, 'a>,
) -> crate::helpers::GeneratedInfo {
if options.final_source {
get_generated_source_info(&self.source())
} else {
stream_chunks_of_raw_source(
self
.value_as_string
.get_or_init(|| String::from_utf8_lossy(&self.value).to_string()),
options,
on_chunk,
on_source,
on_name,
)
}
}
}

#[cfg(test)]
mod tests {
use crate::{ConcatSource, OriginalSource, ReplaceSource, SourceExt};
Expand Down
1 change: 0 additions & 1 deletion src/replace_source.rs
Original file line number Diff line number Diff line change
Expand Up @@ -745,7 +745,6 @@ mod tests {
let mut last_line = 0;
sourcemap
.decoded_mappings()
.into_iter()
.map(|token| {
format!(
"{}:{} ->{} {}:{}{}",
Expand Down
Loading

0 comments on commit b85a16c

Please sign in to comment.