Skip to content

Commit

Permalink
feat: add rspack fs node (web-infra-dev#1918)
Browse files Browse the repository at this point in the history
* feat: init fs node

* test: finish node test

* chore: update workflow

* chore: clippy

* chore: tweak feature list
  • Loading branch information
h-a-n-a authored Feb 21, 2023
1 parent 36bcb66 commit 4462648
Show file tree
Hide file tree
Showing 15 changed files with 315 additions and 18 deletions.
2 changes: 2 additions & 0 deletions .github/workflows/check-js.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -32,9 +32,11 @@ jobs:
pnpm run lint:js
pnpm run build:cli:release
pnpm --filter @rspack/plugin-html run build
pnpm --filter @rspack/fs run build:test
- name: binding test
run: |
pnpm --filter @rspack/core run test
pnpm --filter @rspack/fs run test
pnpm --filter @rspack/dev-server run test
pnpm --filter @rspack/plugin-html run test
pnpm run build:webpack
Expand Down
12 changes: 10 additions & 2 deletions Cargo.lock

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

14 changes: 8 additions & 6 deletions crates/rspack_fs/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -6,14 +6,16 @@ repository = "https://github.com/modern-js-dev/rspack"
version = "0.1.0"

[features]
async = []
async = ["dep:futures"]
default = ["native"]
native = []
rspack-error = ["dep:rspack_error"]

[dependencies]
async-trait = "0.1.53"

[dependencies.rspack_error]
optional = true
path = "../rspack_error"
[dependencies.rspack_error]
optional = true
path = "../rspack_error"

[dependencies.futures]
optional = true
version = "0.3.26"
11 changes: 5 additions & 6 deletions crates/rspack_fs/src/async.rs
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
use std::path::Path;

use async_trait::async_trait;
use futures::future::BoxFuture;

use crate::Result;

#[async_trait]
pub trait AsyncWritableFileSystem {
/// Creates a new, empty directory at the provided path.
///
Expand All @@ -16,22 +16,21 @@ pub trait AsyncWritableFileSystem {
/// - User lacks permissions to create directory at path.
/// - A parent of the given path doesn’t exist. (To create a directory and all its missing parents at the same time, use the create_dir_all function.)
/// - Path already exists.
async fn create_dir<P: AsRef<Path>>(&self, dir: P) -> Result<()>;
fn create_dir<P: AsRef<Path>>(&self, dir: P) -> BoxFuture<'_, Result<()>>;

/// Recursively create a directory and all of its parent components if they are missing.
async fn create_dir_all<P: AsRef<Path>>(&self, dir: P) -> Result<()>;
fn create_dir_all<P: AsRef<Path>>(&self, dir: P) -> BoxFuture<'_, Result<()>>;

/// Write a slice as the entire contents of a file.
/// This function will create a file if it does not exist, and will entirely replace its contents if it does.
async fn write<P: AsRef<Path>, D: AsRef<[u8]>>(&self, file: P, data: D) -> Result<()>;
fn write<P: AsRef<Path>, D: AsRef<[u8]>>(&self, file: P, data: D) -> BoxFuture<'_, Result<()>>;
}

#[async_trait]
pub trait AsyncReadableFileSystem {
/// Read the entire contents of a file into a bytes vector.
///
/// Error: This function will return an error if path does not already exist.
async fn read<P: AsRef<Path>>(&self, file: P) -> Result<Vec<u8>>;
fn read<P: AsRef<Path>>(&self, file: P) -> BoxFuture<'_, Result<Vec<u8>>>;
}

/// Async readable and writable file system representation.
Expand Down
4 changes: 2 additions & 2 deletions crates/rspack_fs/src/error.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
use std::fmt::Display;

#[derive(Debug)]
pub enum Error {
/// Generic I/O error
Io(std::io::Error),
Expand All @@ -15,8 +16,7 @@ impl From<std::io::Error> for Error {
impl From<Error> for rspack_error::Error {
fn from(value: Error) -> Self {
match value {
Error::Io(err) => Self::Io(err),
_ => rspack_error::internal_error!(value),
Error::Io(err) => Self::Io { source: err },
}
}
}
Expand Down
1 change: 1 addition & 0 deletions crates/rspack_fs_node/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
*.node
13 changes: 11 additions & 2 deletions crates/rspack_fs_node/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,16 @@ name = "rspack_fs_node"
repository = "https://github.com/modern-js-dev/rspack"
version = "0.1.0"

[lib]
crate-type = ["cdylib", "rlib"]

[dependencies]
async-trait = "0.1.53"
napi = { workspace = true }
napi-derive = { workspace = true }
rspack_error = { path = "../rspack_error" }
rspack_fs = { path = "../rspack_fs", features = ["async"] }
rspack_fs = { path = "../rspack_fs", default-features = false, features = [
"rspack-error",
] }

[build-dependencies]
napi-build = { workspace = true }
3 changes: 3 additions & 0 deletions crates/rspack_fs_node/build.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
fn main() {
napi_build::setup();
}
17 changes: 17 additions & 0 deletions crates/rspack_fs_node/index.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
/* tslint:disable */
/* eslint-disable */

/* auto-generated by NAPI-RS */

export interface NodeFs {
writeFile: (...args: any[]) => any
mkdir: (...args: any[]) => any
mkdirp: (...args: any[]) => any
}
export type TestFS = TestFs
export class TestFs {
constructor(fs: NodeFs)
writeSync(file: string, data: Buffer): void
mkdirSync(file: string): void
mkdirpSync(file: string): void
}
17 changes: 17 additions & 0 deletions crates/rspack_fs_node/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
{
"name": "@rspack/fs",
"version": "0.1.0",
"description": "File system utilities for rspack",
"private": "true",
"license": "MIT",
"main": "index.node",
"scripts": {
"build:test": "RUSTFLAGS=\"--cfg node\" napi build",
"test": "jest --verbose"
},
"devDependencies": {
"@napi-rs/cli": "2.14.2",
"@types/jest": "29.0.2",
"memfs": "3.4.12"
}
}
145 changes: 145 additions & 0 deletions crates/rspack_fs_node/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1 +1,146 @@
#![allow(clippy::unwrap_in_result)]

mod node;

use std::{marker::PhantomData, path::Path};

use napi::Env;
use node::{NodeFS, NodeFSRef, TryIntoNodeFSRef};
use rspack_fs::{sync::WritableFileSystem, Error, Result};

pub struct NodeWritableFileSystem {
env: Env,
fs_ref: NodeFSRef,
_data: PhantomData<*mut ()>,
}

impl NodeWritableFileSystem {
pub fn new(env: Env, fs: NodeFS) -> napi::Result<Self> {
Ok(Self {
env,
fs_ref: fs.try_into_node_fs_ref(&env)?,
_data: PhantomData,
})
}
}

impl WritableFileSystem for NodeWritableFileSystem {
fn create_dir<P: AsRef<Path>>(&self, dir: P) -> Result<()> {
let dir = dir.as_ref().to_string_lossy();
let mkdir = self.fs_ref.mkdir.get().expect("Failed to get mkdir");
mkdir
.call(
None,
&[self
.env
.create_string(&dir)
.expect("Failed to create string")],
)
.map_err(|err| {
Error::Io(std::io::Error::new(
std::io::ErrorKind::Other,
err.to_string(),
))
})?;

Ok(())
}

fn create_dir_all<P: AsRef<Path>>(&self, dir: P) -> Result<()> {
let dir = dir.as_ref().to_string_lossy();
let mkdirp = self.fs_ref.mkdirp.get().expect("Failed to get mkdirp");
mkdirp
.call(
None,
&[self
.env
.create_string(&dir)
.expect("Failed to create string")],
)
.map_err(|err| {
Error::Io(std::io::Error::new(
std::io::ErrorKind::Other,
err.to_string(),
))
})?;

Ok(())
}

fn write<P: AsRef<Path>, D: AsRef<[u8]>>(&self, file: P, data: D) -> Result<()> {
let file = file.as_ref().to_string_lossy();
let buf = data.as_ref().to_vec();
let write_file = self
.fs_ref
.write_file
.get()
.expect("Failed to get write_file");

write_file
.call(
None,
&[
self
.env
.create_string(&file)
.expect("Failed to create string")
.into_unknown(),
self
.env
.create_buffer_with_data(buf)
.expect("Failed to create buffer")
.into_unknown(),
],
)
.map_err(|err| {
Error::Io(std::io::Error::new(
std::io::ErrorKind::Other,
err.to_string(),
))
})?;

Ok(())
}
}

#[cfg(node)]
mod node_test {
use napi::{bindgen_prelude::Buffer, Env};
use napi_derive::napi;

use super::*;

#[napi]
pub struct TestFS {
writable_fs: NodeWritableFileSystem,
}

#[napi]
impl TestFS {
#[napi(constructor)]
pub fn new(env: Env, fs: NodeFS) -> Self {
Self {
writable_fs: NodeWritableFileSystem::new(env, fs).unwrap(),
}
}

#[napi]
pub fn write_sync(&self, file: String, data: Buffer) {
self.writable_fs.write(file, data).unwrap();
}

#[napi]
pub fn mkdir_sync(&self, file: String) {
self.writable_fs.create_dir(file).unwrap();
}

#[napi]
pub fn mkdirp_sync(&self, file: String) {
self.writable_fs.create_dir_all(file).unwrap();
}
}
}

#[cfg(node)]
#[doc(hidden)]
pub use node_test::*;
54 changes: 54 additions & 0 deletions crates/rspack_fs_node/src/node.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
use napi::{Env, JsFunction, Ref};
use napi_derive::napi;

pub(crate) struct JsFunctionRef {
env: Env,
reference: Ref<()>,
}

impl JsFunctionRef {
fn new(env: Env, f: JsFunction) -> napi::Result<Self> {
Ok(Self {
env,
reference: env.create_reference(f)?,
})
}

pub(crate) fn get(&self) -> napi::Result<JsFunction> {
self.env.get_reference_value(&self.reference)
}
}

impl Drop for JsFunctionRef {
fn drop(&mut self) {
let result = self.reference.unref(self.env);
debug_assert!(result.is_ok());
}
}

#[napi(object)]
pub struct NodeFS {
pub write_file: JsFunction,
pub mkdir: JsFunction,
pub mkdirp: JsFunction,
}

pub(crate) trait TryIntoNodeFSRef {
fn try_into_node_fs_ref(self, env: &Env) -> napi::Result<NodeFSRef>;
}

impl TryIntoNodeFSRef for NodeFS {
fn try_into_node_fs_ref(self, env: &Env) -> napi::Result<NodeFSRef> {
Ok(NodeFSRef {
write_file: JsFunctionRef::new(*env, self.write_file)?,
mkdir: JsFunctionRef::new(*env, self.mkdir)?,
mkdirp: JsFunctionRef::new(*env, self.mkdirp)?,
})
}
}

pub(crate) struct NodeFSRef {
pub(crate) write_file: JsFunctionRef,
pub(crate) mkdir: JsFunctionRef,
pub(crate) mkdirp: JsFunctionRef,
}
Loading

0 comments on commit 4462648

Please sign in to comment.