feat(plugin-storage-fs): implement the first version of the File System Storage plugin
This commit is contained in:
parent
46b9104cda
commit
0c49249bd9
6 changed files with 182 additions and 0 deletions
24
packages/plugin-storage-fs/README.md
Normal file
24
packages/plugin-storage-fs/README.md
Normal 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' })],
|
||||
};
|
||||
```
|
||||
45
packages/plugin-storage-fs/package.json
Normal file
45
packages/plugin-storage-fs/package.json
Normal 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"
|
||||
}
|
||||
}
|
||||
90
packages/plugin-storage-fs/src/index.ts
Normal file
90
packages/plugin-storage-fs/src/index.ts
Normal 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);
|
||||
},
|
||||
};
|
||||
},
|
||||
};
|
||||
}
|
||||
8
packages/plugin-storage-fs/tsconfig.json
Normal file
8
packages/plugin-storage-fs/tsconfig.json
Normal file
|
|
@ -0,0 +1,8 @@
|
|||
{
|
||||
"extends": "@emigrate/tsconfig/build.json",
|
||||
"compilerOptions": {
|
||||
"outDir": "dist"
|
||||
},
|
||||
"include": ["src"],
|
||||
"exclude": ["node_modules", "dist"]
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue