feat(plugin-storage-fs): implement the first version of the File System Storage plugin

This commit is contained in:
Joakim Carlstein 2023-11-16 11:07:17 +01:00
parent 46b9104cda
commit 0c49249bd9
6 changed files with 182 additions and 0 deletions

View file

@ -0,0 +1,5 @@
---
'@emigrate/plugin-storage-fs': minor
---
Implement a first version of the File System Storage plugin for simple migration setups

View file

@ -0,0 +1,24 @@
# @emigrate/plugin-storage-fs
A file system storage plugin for Emigrate, suitable for simple migration setups. To support containerized environments, it is recommended to use a database storage plugin instead.
## Installation
Install the plugin in your project, alongside the Emigrate CLI:
```bash
npm install --save-dev @emigrate/cli @emigrate/plugin-storage-fs
```
## Usage
Configure the plugin in your `emigrate.config.js` file:
```js
import storageFs from '@emigrate/plugin-storage-fs';
export default {
directory: 'migrations',
plugins: [storageFs({ filename: '.migrated.json' })],
};
```

View file

@ -0,0 +1,45 @@
{
"name": "@emigrate/plugin-storage-fs",
"version": "0.0.0",
"publishConfig": {
"access": "public"
},
"description": "A storage plugin for Emigrate for storing the migration history in a file",
"main": "dist/index.js",
"types": "dist/index.d.js",
"type": "module",
"exports": {
".": {
"import": "./dist/index.js",
"types": "./dist/index.d.ts"
}
},
"files": [
"dist"
],
"scripts": {
"build": "tsc --pretty",
"build:watch": "tsc --pretty --watch"
},
"keywords": [
"emigrate",
"emigrate-plugin",
"plugin",
"migrations",
"storage"
],
"author": "Aboviq AB <dev@aboviq.com> (https://www.aboviq.com)",
"homepage": "https://github.com/aboviq/emigrate/tree/main/packages/plugin-storage-fs#readme",
"repository": "https://github.com/aboviq/emigrate/tree/main/packages/plugin-storage-fs",
"bugs": "https://github.com/aboviq/emigrate/issues",
"license": "MIT",
"dependencies": {
"@emigrate/plugin-tools": "workspace:*"
},
"devDependencies": {
"@emigrate/tsconfig": "workspace:*"
},
"volta": {
"extends": "../../package.json"
}
}

View file

@ -0,0 +1,90 @@
import fs from 'node:fs/promises';
import path from 'node:path';
import process from 'node:process';
import { type StoragePlugin, type MigrationStatus } from '@emigrate/plugin-tools/types';
export type StorageFsOptions = {
filename: string;
};
type SerializedError = {
name: string;
message: string;
stack?: string;
};
export default function storageFs({ filename }: StorageFsOptions): StoragePlugin {
const filePath = path.resolve(process.cwd(), filename);
const lockFilePath = `${filePath}.lock`;
const read = async (): Promise<
Record<string, { status: MigrationStatus; date: string; error?: SerializedError }>
> => {
try {
const contents = await fs.readFile(filePath, 'utf8');
// eslint-disable-next-line @typescript-eslint/no-unsafe-return
return JSON.parse(contents);
} catch {
return {};
}
};
let lastUpdate: Promise<void> = Promise.resolve();
const update = async (migration: string, status: MigrationStatus, error?: Error) => {
lastUpdate = lastUpdate.then(async () => {
const history = await read();
const newHistory = {
...history,
[migration]: {
status,
date: new Date().toISOString(),
error: error ? { name: error.name, message: error.message, stack: error.stack } : undefined,
},
};
await fs.writeFile(filePath, JSON.stringify(newHistory, undefined, 2));
});
return lastUpdate;
};
return {
async initializeStorage() {
return {
async lock(migrations) {
const fd = await fs.open(lockFilePath, 'wx');
await fd.close();
return migrations;
},
async unlock() {
try {
await fs.unlink(lockFilePath);
} catch {
// Ignore
}
},
async *getHistory() {
const history = await read();
yield* Object.entries(history).map(([name, { status, date, error }]) => ({
name,
status,
error,
date: new Date(date),
}));
},
async onSuccess(migration) {
await update(migration, 'done');
},
async onError(migration, error) {
await update(migration, 'failed', error);
},
};
},
};
}

View file

@ -0,0 +1,8 @@
{
"extends": "@emigrate/tsconfig/build.json",
"compilerOptions": {
"outDir": "dist"
},
"include": ["src"],
"exclude": ["node_modules", "dist"]
}

10
pnpm-lock.yaml generated
View file

@ -77,6 +77,16 @@ importers:
specifier: workspace:* specifier: workspace:*
version: link:../tsconfig version: link:../tsconfig
packages/plugin-storage-fs:
dependencies:
'@emigrate/plugin-tools':
specifier: workspace:*
version: link:../plugin-tools
devDependencies:
'@emigrate/tsconfig':
specifier: workspace:*
version: link:../tsconfig
packages/plugin-tools: packages/plugin-tools:
dependencies: dependencies:
import-from-esm: import-from-esm: