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
5
.changeset/olive-phones-heal.md
Normal file
5
.changeset/olive-phones-heal.md
Normal file
|
|
@ -0,0 +1,5 @@
|
||||||
|
---
|
||||||
|
'@emigrate/plugin-storage-fs': minor
|
||||||
|
---
|
||||||
|
|
||||||
|
Implement a first version of the File System Storage plugin for simple migration setups
|
||||||
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"]
|
||||||
|
}
|
||||||
10
pnpm-lock.yaml
generated
10
pnpm-lock.yaml
generated
|
|
@ -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:
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue