@knapsack/app
Version:
Build Design Systems on top of knapsack, by Basalt
307 lines (249 loc) • 7.96 kB
JavaScript
"use strict";
var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.FileDb2 = exports.FileDb = void 0;
var _defineProperty2 = _interopRequireDefault(require("@babel/runtime/helpers/defineProperty"));
var _lowdb = _interopRequireDefault(require("lowdb"));
var _FileSync = _interopRequireDefault(require("lowdb/adapters/FileSync"));
var _path = require("path");
var _chokidar = _interopRequireDefault(require("chokidar"));
var _os = _interopRequireDefault(require("os"));
var _fsExtra = _interopRequireDefault(require("fs-extra"));
var _jsYaml = _interopRequireDefault(require("js-yaml"));
var _schemaUtils = require("@knapsack/schema-utils");
var _serverUtils = require("../server-utils");
var _events = require("../events");
/**
* Copyright (C) 2018 Basalt
This file is part of Knapsack.
Knapsack is free software; you can redistribute it and/or modify it
under the terms of the GNU General Public License as published by the Free
Software Foundation; either version 2 of the License, or (at your option)
any later version.
Knapsack is distributed in the hope that it will be useful, but WITHOUT
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
more details.
You should have received a copy of the GNU General Public License along
with Knapsack; if not, see <https://www.gnu.org/licenses>.
*/
/* eslint-disable max-classes-per-file */
/**
* Creates a LoDash powered JSON file database, via `lowdb` that is created using the `_.chain` method.
* Each database has all of the power of LoDash for parsing the data.
* @link https://www.npmjs.com/package/lowdb
* @link https://lodash.com/docs/4.17.11#chain
*/
class FileDb {
constructor({
dbDir,
name,
defaults = {}
}) {
(0, _defineProperty2.default)(this, "db", void 0);
// @todo kebab-case `name`
// @todo should this read a pre-existing file first?
const dbPath = (0, _path.join)(dbDir, `${name}.json`);
const adapter = new _FileSync.default(dbPath, {
serialize: data => JSON.stringify(data, null, 2) + _os.default.EOL
});
const db = (0, _lowdb.default)(adapter); // Set some defaults (required if your JSON file is empty)
db.defaults(defaults).write();
this.db = db; // You can use any lodash function like _.get and _.find with shorthand syntax.
//
// // Use .value() instead of .write() if you're only reading from db
// db.get('posts')
// .find({ id: 1 })
// .value()
// Start watching the file in case user manually changes it so we can re-read it into memory
const watcher = _chokidar.default.watch(dbPath, {
ignoreInitial: true
});
watcher.on('all', () => {
this.db = db.read();
});
_events.knapsackEvents.on(_events.EVENTS.SHUTDOWN, () => {
watcher.close();
});
}
getDb() {
return this.db;
}
get(key) {
// @todo improve types
return this.db.get(key).value();
}
getAll() {
// @todo improve types
return this.db.value();
}
/**
* @link https://lodash.com/docs/4.17.11#find
*/
find(data) {
// @todo improve types
return this.db.find(data).value();
}
values() {
return this.db.values().value();
}
set(key, value) {
return this.db.set(key, value).write();
}
setAll(data) {
// @todo improve types
return this.db.setState(data).write();
}
update(key, func) {
// @todo improve types
return this.db.update(key, func).write();
}
}
exports.FileDb = FileDb;
class FileDb2 {
/**
* Full path to file used for storage
*/
// config: ConfigType;
constructor({
filePath,
// dbDir,
// name,
type = 'json',
defaults,
validationSchema,
watch = true,
writeFileIfAbsent = true
}) {
(0, _defineProperty2.default)(this, "filePath", void 0);
(0, _defineProperty2.default)(this, "type", void 0);
(0, _defineProperty2.default)(this, "validationSchema", void 0);
this.type = type;
this.validationSchema = validationSchema;
this.filePath = filePath;
if (writeFileIfAbsent && !_fsExtra.default.existsSync(this.filePath)) {
const {
contents
} = this.serialize(defaults);
_fsExtra.default.writeFileSync(this.filePath, contents); // this.write(defaults, { sync: true }).then(() => {});
} // if (writeFileIfAbsent) {
//
// }
// this.config = this.read();
if (watch) {// Start watching the file in case user manually changes it so we can re-read it into memory
// const watcher = chokidar.watch(this.filePath, {
// ignoreInitial: true,
// });
//
// watcher.on('all', () => {
// // @todo if file is changed, trigger client ui to get new changes - we can't have diverged data
// this.config = this.read();
// });
//
// knapsackEvents.on(EVENTS.SHUTDOWN, () => {
// watcher.close();
// });
}
}
/**
* Ensure the data is good during run-time by using provided JSON Schemas to validate
* Requires `validationSchema` to be passed in during initial creation
* @throws Error if it's not valid
*/
validateConfig(config) {
if (!this.validationSchema) return;
const {
ok,
message,
errors
} = (0, _schemaUtils.validateDataAgainstSchema)(this.validationSchema, config);
if (ok) return;
const msg = [`Data validation error for ${this.filePath}`, 'The data:', JSON.stringify(config, null, ' '), '', 'The error:', message, JSON.stringify(errors, null, ' ')].join('\n');
throw new Error(msg);
}
serialize(config) {
this.validateConfig(config);
switch (this.type) {
case 'json':
{
const contents = (0, _serverUtils.formatCode)({
code: JSON.stringify(config) + _os.default.EOL,
language: 'json'
});
return {
contents,
encoding: 'utf8'
};
}
case 'yml':
{
const contents = (0, _serverUtils.formatCode)({
code: _jsYaml.default.safeDump(config),
language: 'yml'
});
return {
contents,
encoding: 'utf8'
};
}
default:
throw new Error('Un-supported type used');
}
}
parse(fileString) {
let config;
switch (this.type) {
case 'json':
config = JSON.parse(fileString);
this.validateConfig(config);
return config;
case 'yml':
config = _jsYaml.default.safeLoad(fileString);
this.validateConfig(config);
return config;
default:
throw new Error('Un-supported type used');
}
}
async read() {
const dbString = await _fsExtra.default.readFile(this.filePath, 'utf8');
return this.parse(dbString);
}
readSync() {
const dbString = _fsExtra.default.readFileSync(this.filePath, 'utf8');
return this.parse(dbString);
}
async savePrep(config) {
this.validateConfig(config);
const {
contents,
encoding
} = this.serialize(config);
return [{
contents,
encoding,
path: this.filePath
}];
} // async write(config: ConfigType, { sync = false } = {}): Promise<string> {
// const { contents, path, encoding } = this.savePrep(config);
// if (sync) {
// fs.writeFileSync(path, contents, { encoding });
// } else {
// await fs.writeFile(path, contents, { encoding });
// }
// return path;
// }
async getData() {
const config = await this.read();
this.validateConfig(config);
return config;
}
getDataSync() {
const config = this.readSync();
this.validateConfig(config);
return config;
}
}
exports.FileDb2 = FileDb2;