refactor(native): remove unused code (#4651)

This commit is contained in:
Alex Yang 2023-10-17 18:19:06 -05:00 committed by GitHub
parent a430266389
commit bb046a12dc
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
11 changed files with 47 additions and 391 deletions

View File

@ -55,6 +55,25 @@ jobs:
path: ./apps/core/dist path: ./apps/core/dist
if-no-files-found: error 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: desktop-test:
name: Desktop Test name: Desktop Test
runs-on: ${{ matrix.spec.os }} runs-on: ${{ matrix.spec.os }}

View File

@ -1,15 +1,12 @@
import assert from 'node:assert'; import test from 'ava';
import { test } from 'node:test';
import { fileURLToPath } from 'node:url'; import { fileURLToPath } from 'node:url';
import { SqliteConnection, ValidationResult } from '../index'; import { SqliteConnection, ValidationResult } from '../index';
test('db', { concurrency: false }, async t => { test('db validate', async t => {
await t.test('validate', async () => {
const path = fileURLToPath( const path = fileURLToPath(
new URL('./fixtures/test01.affine', import.meta.url) new URL('./fixtures/test01.affine', import.meta.url)
); );
const result = await SqliteConnection.validate(path); const result = await SqliteConnection.validate(path);
assert.equal(result, ValidationResult.MissingVersionColumn); t.is(result, ValidationResult.MissingVersionColumn);
});
}); });

View File

@ -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<void>();
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<void>();
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());
})();
});
});

View File

@ -1,3 +0,0 @@
import type { FsWatcher } from './index';
export function createFSWatcher(): typeof FsWatcher;

View File

@ -1,5 +0,0 @@
module.exports.createFSWatcher = function createFSWatcher() {
// eslint-disable-next-line @typescript-eslint/no-var-requires
const { FsWatcher } = require('./index');
return FsWatcher;
};

View File

@ -3,26 +3,6 @@
/* auto-generated by NAPI-RS */ /* 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<void>;
export interface BlobRow { export interface BlobRow {
key: string; key: string;
data: Buffer; data: Buffer;
@ -45,22 +25,6 @@ export enum ValidationResult {
GeneralError = 3, GeneralError = 3,
Valid = 4, 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 { export class SqliteConnection {
constructor(path: string); constructor(path: string);
connect(): Promise<void>; connect(): Promise<void>;

View File

@ -263,18 +263,7 @@ if (!nativeBinding) {
throw new Error(`Failed to load native binding`); throw new Error(`Failed to load native binding`);
} }
const { const { SqliteConnection, ValidationResult } = nativeBinding;
WatcherKind,
Subscription,
FsWatcher,
moveFile,
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.SqliteConnection = SqliteConnection;
module.exports.ValidationResult = ValidationResult; module.exports.ValidationResult = ValidationResult;

View File

@ -17,10 +17,28 @@
} }
}, },
"license": "MIT", "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": { "devDependencies": {
"@napi-rs/cli": "^2.16.3", "@napi-rs/cli": "^2.16.3",
"@types/node": "^18.18.5", "@types/node": "^18.18.5",
"@types/uuid": "^9.0.5", "@types/uuid": "^9.0.5",
"ava": "^5.3.1",
"cross-env": "^7.0.3", "cross-env": "^7.0.3",
"nx": "^16.10.0", "nx": "^16.10.0",
"nx-cloud": "^16.5.2", "nx-cloud": "^16.5.2",
@ -37,7 +55,7 @@
"build": "napi build --platform --release --no-const-enum", "build": "napi build --platform --release --no-const-enum",
"build:debug": "napi build --platform --no-const-enum", "build:debug": "napi build --platform --no-const-enum",
"universal": "napi universal", "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": "napi version"
}, },
"version": "0.10.0-canary.1" "version": "0.10.0-canary.1"

View File

@ -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<napi::Result<GlobalWatcher>> = 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>| {
event_emitter_in_handler.lock().on(res);
})
.map_err(anyhow::Error::from)?;
Ok(GlobalWatcher {
inner: Mutex::new(watcher),
event_emitter,
})
});
struct GlobalWatcher {
inner: Mutex<RecommendedWatcher>,
event_emitter: Arc<Mutex<EventEmitter>>,
}
#[napi(object)]
#[derive(Default)]
pub struct WatchOptions {
pub recursive: Option<bool>,
}
#[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<notify::WatcherKind> 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<uuid::Uuid>,
}
#[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<WatchOptions>) -> 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<String> {
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<serde_json::Value, ErrorStrategy::Fatal>,
#[napi(ts_arg_type = "(err: Error) => void")] error_callback: Option<ThreadsafeFunction<()>>,
) -> napi::Result<Subscription> {
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<serde_json::Value, ErrorStrategy::Fatal>,
),
>,
error_callbacks: BTreeMap<uuid::Uuid, ThreadsafeFunction<()>>,
}
impl EventEmitter {
fn on(&self, event: notify::Result<Event>) {
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(())
}

View File

@ -1,2 +1 @@
pub mod fs;
pub mod sqlite; pub mod sqlite;

View File

@ -594,6 +594,7 @@ __metadata:
"@napi-rs/cli": ^2.16.3 "@napi-rs/cli": ^2.16.3
"@types/node": ^18.18.5 "@types/node": ^18.18.5
"@types/uuid": ^9.0.5 "@types/uuid": ^9.0.5
ava: ^5.3.1
cross-env: ^7.0.3 cross-env: ^7.0.3
nx: ^16.10.0 nx: ^16.10.0
nx-cloud: ^16.5.2 nx-cloud: ^16.5.2