From bb046a12dc7c9d254dad1265d33cd3d0b5c417b5 Mon Sep 17 00:00:00 2001 From: Alex Yang Date: Tue, 17 Oct 2023 18:19:06 -0500 Subject: [PATCH] refactor(native): remove unused code (#4651) --- .github/workflows/build-desktop.yml | 19 ++ packages/native/__tests__/db.spec.mts | 17 +- packages/native/__tests__/fs.spec.mts | 80 --------- packages/native/fs-watcher.d.ts | 3 - packages/native/fs-watcher.js | 5 - packages/native/index.d.ts | 36 ---- packages/native/index.js | 13 +- packages/native/package.json | 20 ++- packages/native/src/fs.rs | 243 -------------------------- packages/native/src/lib.rs | 1 - yarn.lock | 1 + 11 files changed, 47 insertions(+), 391 deletions(-) delete mode 100644 packages/native/__tests__/fs.spec.mts delete mode 100644 packages/native/fs-watcher.d.ts delete mode 100644 packages/native/fs-watcher.js delete mode 100644 packages/native/src/fs.rs diff --git a/.github/workflows/build-desktop.yml b/.github/workflows/build-desktop.yml index 25fcd236d3..7e6f179623 100644 --- a/.github/workflows/build-desktop.yml +++ b/.github/workflows/build-desktop.yml @@ -55,6 +55,25 @@ jobs: path: ./apps/core/dist if-no-files-found: error + build-native: + name: Build Native + runs-on: ubuntu-latest + environment: development + needs: build-core + steps: + - uses: actions/checkout@v3 + - name: Setup Node.js + uses: ./.github/actions/setup-node + - name: Build AFFiNE native + uses: ./.github/actions/build-rust + with: + target: x86_64-unknown-linux-gnu + package: '@affine/native' + nx_token: ${{ secrets.NX_CLOUD_ACCESS_TOKEN }} + - name: Run tests + run: yarn test + working-directory: ./packages/native + desktop-test: name: Desktop Test runs-on: ${{ matrix.spec.os }} diff --git a/packages/native/__tests__/db.spec.mts b/packages/native/__tests__/db.spec.mts index d98889094d..99cbc82cf3 100644 --- a/packages/native/__tests__/db.spec.mts +++ b/packages/native/__tests__/db.spec.mts @@ -1,15 +1,12 @@ -import assert from 'node:assert'; -import { test } from 'node:test'; +import test from 'ava'; import { fileURLToPath } from 'node:url'; import { SqliteConnection, ValidationResult } from '../index'; -test('db', { concurrency: false }, async t => { - await t.test('validate', async () => { - const path = fileURLToPath( - new URL('./fixtures/test01.affine', import.meta.url) - ); - const result = await SqliteConnection.validate(path); - assert.equal(result, ValidationResult.MissingVersionColumn); - }); +test('db validate', async t => { + const path = fileURLToPath( + new URL('./fixtures/test01.affine', import.meta.url) + ); + const result = await SqliteConnection.validate(path); + t.is(result, ValidationResult.MissingVersionColumn); }); diff --git a/packages/native/__tests__/fs.spec.mts b/packages/native/__tests__/fs.spec.mts deleted file mode 100644 index 09044b9e1c..0000000000 --- a/packages/native/__tests__/fs.spec.mts +++ /dev/null @@ -1,80 +0,0 @@ -import assert, { doesNotThrow } from 'node:assert'; -import { promises as fs } from 'node:fs'; -import { join } from 'node:path'; -import { test } from 'node:test'; -import { fileURLToPath } from 'node:url'; - -import { lastValueFrom, Subject } from 'rxjs'; -import { v4 } from 'uuid'; - -import { FsWatcher } from '../index'; - -test('fs watch', { concurrency: false }, async t => { - let watcher: FsWatcher; - let fixture: string; - t.beforeEach(async () => { - const fixtureName = `fs-${v4()}.fixture`; - fixture = join(fileURLToPath(import.meta.url), '..', fixtureName); - await fs.writeFile(fixture, '\n'); - watcher = FsWatcher.watch(fixture); - }); - - t.afterEach(async () => { - FsWatcher.close(); - await fs.unlink(fixture).catch(() => false); - }); - - await t.test('should watch without error', () => { - doesNotThrow(() => { - const subscription = watcher.subscribe(() => {}); - subscription.unsubscribe(); - }); - }); - - await t.test('should watch file change', () => { - return (async () => { - const defer = new Subject(); - const subscription = watcher.subscribe( - event => { - assert.deepEqual(event.paths, [fixture]); - subscription.unsubscribe(); - defer.next(); - defer.complete(); - }, - err => { - subscription.unsubscribe(); - defer.error(err); - } - ); - await fs.appendFile(fixture, 'test'); - return lastValueFrom(defer.asObservable()); - })(); - }); - - await t.test('should watch file delete', () => { - return (async () => { - const defer = new Subject(); - const subscription = watcher.subscribe( - event => { - if (typeof event.type === 'object' && 'rename' in event.type) { - assert.deepEqual(event.paths, [fixture]); - assert.deepEqual(event.type, { - remove: { - kind: 'file', - }, - }); - } - subscription.unsubscribe(); - defer.next(); - defer.complete(); - }, - err => { - subscription.unsubscribe(); - defer.error(err); - } - ); - await fs.unlink(fixture); - return lastValueFrom(defer.asObservable()); - })(); - }); -}); diff --git a/packages/native/fs-watcher.d.ts b/packages/native/fs-watcher.d.ts deleted file mode 100644 index e8b2edbd46..0000000000 --- a/packages/native/fs-watcher.d.ts +++ /dev/null @@ -1,3 +0,0 @@ -import type { FsWatcher } from './index'; - -export function createFSWatcher(): typeof FsWatcher; diff --git a/packages/native/fs-watcher.js b/packages/native/fs-watcher.js deleted file mode 100644 index 350d14cd36..0000000000 --- a/packages/native/fs-watcher.js +++ /dev/null @@ -1,5 +0,0 @@ -module.exports.createFSWatcher = function createFSWatcher() { - // eslint-disable-next-line @typescript-eslint/no-var-requires - const { FsWatcher } = require('./index'); - return FsWatcher; -}; diff --git a/packages/native/index.d.ts b/packages/native/index.d.ts index 1a9cfd92fd..d5a71f6674 100644 --- a/packages/native/index.d.ts +++ b/packages/native/index.d.ts @@ -3,26 +3,6 @@ /* auto-generated by NAPI-RS */ -export interface WatchOptions { - recursive?: boolean; -} -/** Watcher kind enumeration */ -export enum WatcherKind { - /** inotify backend (linux) */ - Inotify = 'Inotify', - /** FS-Event backend (mac) */ - Fsevent = 'Fsevent', - /** KQueue backend (bsd,optionally mac) */ - Kqueue = 'Kqueue', - /** Polling based backend (fallback) */ - PollWatcher = 'PollWatcher', - /** Windows backend */ - ReadDirectoryChangesWatcher = 'ReadDirectoryChangesWatcher', - /** Fake watcher for testing */ - NullWatcher = 'NullWatcher', - Unknown = 'Unknown', -} -export function moveFile(src: string, dst: string): Promise; export interface BlobRow { key: string; data: Buffer; @@ -45,22 +25,6 @@ export enum ValidationResult { GeneralError = 3, Valid = 4, } -export class Subscription { - toString(): string; - unsubscribe(): void; -} -export type FSWatcher = FsWatcher; -export class FsWatcher { - static watch(p: string, options?: WatchOptions | undefined | null): FsWatcher; - static kind(): WatcherKind; - toString(): string; - subscribe( - callback: (event: import('./event').NotifyEvent) => void, - errorCallback?: (err: Error) => void - ): Subscription; - static unwatch(p: string): void; - static close(): void; -} export class SqliteConnection { constructor(path: string); connect(): Promise; diff --git a/packages/native/index.js b/packages/native/index.js index f985c7ef0e..a57f0002f6 100644 --- a/packages/native/index.js +++ b/packages/native/index.js @@ -263,18 +263,7 @@ if (!nativeBinding) { throw new Error(`Failed to load native binding`); } -const { - WatcherKind, - Subscription, - FsWatcher, - moveFile, - SqliteConnection, - ValidationResult, -} = nativeBinding; +const { SqliteConnection, ValidationResult } = nativeBinding; -module.exports.WatcherKind = WatcherKind; -module.exports.Subscription = Subscription; -module.exports.FsWatcher = FsWatcher; -module.exports.moveFile = moveFile; module.exports.SqliteConnection = SqliteConnection; module.exports.ValidationResult = ValidationResult; diff --git a/packages/native/package.json b/packages/native/package.json index 750379aeab..42e4345d9b 100644 --- a/packages/native/package.json +++ b/packages/native/package.json @@ -17,10 +17,28 @@ } }, "license": "MIT", + "ava": { + "extensions": { + "mts": "module" + }, + "nodeArguments": [ + "--loader", + "ts-node/esm.mjs", + "--es-module-specifier-resolution", + "node" + ], + "files": [ + "__tests__/*.spec.mts" + ], + "environmentVariables": { + "TS_NODE_PROJECT": "./tsconfig.json" + } + }, "devDependencies": { "@napi-rs/cli": "^2.16.3", "@types/node": "^18.18.5", "@types/uuid": "^9.0.5", + "ava": "^5.3.1", "cross-env": "^7.0.3", "nx": "^16.10.0", "nx-cloud": "^16.5.2", @@ -37,7 +55,7 @@ "build": "napi build --platform --release --no-const-enum", "build:debug": "napi build --platform --no-const-enum", "universal": "napi universal", - "test": "cross-env TS_NODE_TRANSPILE_ONLY=1 TS_NODE_PROJECT=./tsconfig.json node --test --loader ts-node/esm --experimental-specifier-resolution=node ./__tests__/**/*.mts", + "test": "ava", "version": "napi version" }, "version": "0.10.0-canary.1" diff --git a/packages/native/src/fs.rs b/packages/native/src/fs.rs deleted file mode 100644 index c8476fe478..0000000000 --- a/packages/native/src/fs.rs +++ /dev/null @@ -1,243 +0,0 @@ -use std::{collections::BTreeMap, path::Path, sync::Arc}; - -use napi::{ - bindgen_prelude::{FromNapiValue, ToNapiValue}, - threadsafe_function::{ErrorStrategy, ThreadsafeFunction, ThreadsafeFunctionCallMode}, -}; -use napi_derive::napi; -use notify::{Event, RecommendedWatcher, RecursiveMode, Watcher}; -use once_cell::sync::Lazy; -use parking_lot::Mutex; - -static GLOBAL_WATCHER: Lazy> = Lazy::new(|| { - let event_emitter = Arc::new(Mutex::new(EventEmitter { - listeners: Default::default(), - error_callbacks: Default::default(), - })); - let event_emitter_in_handler = event_emitter.clone(); - let watcher: RecommendedWatcher = - notify::recommended_watcher(move |res: notify::Result| { - event_emitter_in_handler.lock().on(res); - }) - .map_err(anyhow::Error::from)?; - Ok(GlobalWatcher { - inner: Mutex::new(watcher), - event_emitter, - }) -}); - -struct GlobalWatcher { - inner: Mutex, - event_emitter: Arc>, -} - -#[napi(object)] -#[derive(Default)] -pub struct WatchOptions { - pub recursive: Option, -} - -#[napi(string_enum)] -/// Watcher kind enumeration -pub enum WatcherKind { - /// inotify backend (linux) - Inotify, - /// FS-Event backend (mac) - Fsevent, - /// KQueue backend (bsd,optionally mac) - Kqueue, - /// Polling based backend (fallback) - PollWatcher, - /// Windows backend - ReadDirectoryChangesWatcher, - /// Fake watcher for testing - NullWatcher, - Unknown, -} - -impl From for WatcherKind { - fn from(value: notify::WatcherKind) -> Self { - match value { - notify::WatcherKind::Inotify => WatcherKind::Inotify, - notify::WatcherKind::Fsevent => WatcherKind::Fsevent, - notify::WatcherKind::Kqueue => WatcherKind::Kqueue, - notify::WatcherKind::PollWatcher => WatcherKind::PollWatcher, - notify::WatcherKind::ReadDirectoryChangesWatcher => WatcherKind::ReadDirectoryChangesWatcher, - notify::WatcherKind::NullWatcher => WatcherKind::NullWatcher, - _ => WatcherKind::Unknown, - } - } -} - -#[napi] -pub struct Subscription { - id: uuid::Uuid, - error_uuid: Option, -} - -#[napi] -impl Subscription { - #[napi] - #[allow(clippy::inherent_to_string)] - pub fn to_string(&self) -> String { - self.id.to_string() - } - - #[napi] - pub fn unsubscribe(&mut self) -> napi::Result<()> { - let mut event_emitter = GLOBAL_WATCHER - .as_ref() - .map_err(|err| err.clone())? - .event_emitter - .lock(); - event_emitter.listeners.remove(&self.id); - if let Some(error_uuid) = &self.error_uuid { - event_emitter.error_callbacks.remove(error_uuid); - }; - Ok(()) - } -} - -#[napi] -pub struct FSWatcher { - path: String, - recursive: RecursiveMode, -} - -#[napi] -impl FSWatcher { - #[napi(factory)] - pub fn watch(p: String, options: Option) -> Self { - let options = options.unwrap_or_default(); - FSWatcher { - path: p, - recursive: if options.recursive == Some(false) { - RecursiveMode::NonRecursive - } else { - RecursiveMode::Recursive - }, - } - } - - #[napi] - pub fn kind() -> WatcherKind { - RecommendedWatcher::kind().into() - } - - #[napi] - pub fn to_string(&self) -> napi::Result { - Ok(format!( - "{:?}", - GLOBAL_WATCHER.as_ref().map_err(|err| err.clone())?.inner - )) - } - - #[napi] - pub fn subscribe( - &mut self, - #[napi(ts_arg_type = "(event: import('./event').NotifyEvent) => void")] - callback: ThreadsafeFunction, - #[napi(ts_arg_type = "(err: Error) => void")] error_callback: Option>, - ) -> napi::Result { - GLOBAL_WATCHER - .as_ref() - .map_err(|err| err.clone())? - .inner - .lock() - .watch(Path::new(&self.path), self.recursive) - .map_err(anyhow::Error::from)?; - let uuid = uuid::Uuid::new_v4(); - let mut event_emitter = GLOBAL_WATCHER - .as_ref() - .map_err(|err| err.clone())? - .event_emitter - .lock(); - event_emitter - .listeners - .insert(uuid, (self.path.clone(), callback)); - let mut error_uuid = None; - if let Some(error_callback) = error_callback { - let uuid = uuid::Uuid::new_v4(); - event_emitter.error_callbacks.insert(uuid, error_callback); - error_uuid = Some(uuid); - } - drop(event_emitter); - Ok(Subscription { - id: uuid, - error_uuid, - }) - } - - #[napi] - pub fn unwatch(p: String) -> napi::Result<()> { - let mut watcher = GLOBAL_WATCHER - .as_ref() - .map_err(|err| err.clone())? - .inner - .lock(); - watcher - .unwatch(Path::new(&p)) - .map_err(anyhow::Error::from)?; - Ok(()) - } - - #[napi] - pub fn close() -> napi::Result<()> { - let global_watcher = GLOBAL_WATCHER.as_ref().map_err(|err| err.clone())?; - global_watcher.event_emitter.lock().stop(); - let mut inner = global_watcher.inner.lock(); - *inner = notify::recommended_watcher(|_| {}).map_err(anyhow::Error::from)?; - Ok(()) - } -} - -#[derive(Clone)] -struct EventEmitter { - listeners: BTreeMap< - uuid::Uuid, - ( - String, - ThreadsafeFunction, - ), - >, - error_callbacks: BTreeMap>, -} - -impl EventEmitter { - fn on(&self, event: notify::Result) { - match event { - Ok(e) => match serde_json::value::to_value(&e) { - Err(err) => { - let err: napi::Error = anyhow::Error::from(err).into(); - for on_error in self.error_callbacks.values() { - on_error.call(Err(err.clone()), ThreadsafeFunctionCallMode::NonBlocking); - } - } - Ok(v) => { - for (path, on_event) in self.listeners.values() { - if e.paths.iter().any(|p| p.to_str() == Some(path)) { - on_event.call(v.clone(), ThreadsafeFunctionCallMode::NonBlocking); - } - } - } - }, - Err(err) => { - let err: napi::Error = anyhow::Error::from(err).into(); - for on_error in self.error_callbacks.values() { - on_error.call(Err(err.clone()), ThreadsafeFunctionCallMode::NonBlocking); - } - } - } - } - - fn stop(&mut self) { - self.listeners.clear(); - self.error_callbacks.clear(); - } -} - -#[napi] -pub async fn move_file(src: String, dst: String) -> napi::Result<()> { - tokio::fs::rename(src, dst).await?; - Ok(()) -} diff --git a/packages/native/src/lib.rs b/packages/native/src/lib.rs index 2541f255aa..6b1c1083e3 100644 --- a/packages/native/src/lib.rs +++ b/packages/native/src/lib.rs @@ -1,2 +1 @@ -pub mod fs; pub mod sqlite; diff --git a/yarn.lock b/yarn.lock index 5e492d0e69..7b319d7227 100644 --- a/yarn.lock +++ b/yarn.lock @@ -594,6 +594,7 @@ __metadata: "@napi-rs/cli": ^2.16.3 "@types/node": ^18.18.5 "@types/uuid": ^9.0.5 + ava: ^5.3.1 cross-env: ^7.0.3 nx: ^16.10.0 nx-cloud: ^16.5.2