@knapsack/app
Version:
Build Design Systems with Knapsack
170 lines • 6.03 kB
JavaScript
"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.FileDb = void 0;
const file_utils_1 = require("@knapsack/file-utils");
const os_1 = __importDefault(require("os"));
const fs_extra_1 = __importDefault(require("fs-extra"));
const chokidar_1 = __importDefault(require("chokidar"));
const schema_utils_1 = require("@knapsack/schema-utils");
const md5_1 = __importDefault(require("md5"));
const utils_1 = require("@knapsack/utils");
const events_1 = require("../events");
class FileDb {
/**
* Full path to file used for storage
*/
filePath;
type;
// config: ConfigType;
validationSchema;
defaults;
orderAlphabetically;
fileHashes;
#singleQuote;
constructor({ filePath,
// dbDir,
// name,
type = 'json', defaults, validationSchema, writeFileIfAbsent = true, orderAlphabetically = false, }) {
this.orderAlphabetically = orderAlphabetically;
this.type = type;
this.validationSchema = validationSchema;
this.filePath = filePath;
this.fileHashes = new Map();
this.defaults = defaults;
this.#singleQuote = true;
const exists = fs_extra_1.default.existsSync(this.filePath);
if (writeFileIfAbsent && !exists) {
const { contents } = this.serialize(defaults);
fs_extra_1.default.writeFileSync(this.filePath, contents);
}
}
async init(_opt) {
const prettierConfig = await (0, file_utils_1.getPrettierConfig)(this.filePath);
this.#singleQuote = prettierConfig?.singleQuote ?? this.#singleQuote;
}
watchFiles({ handleChange, extraPaths = [], }) {
if (!handleChange)
return;
// Start watching the file in case user manually changes it so we can re-read it into memory
const watcher = chokidar_1.default.watch([this.filePath, ...extraPaths], {
ignoreInitial: true,
});
watcher.on('all', async (eventName, path) => {
let hash = '';
try {
const dbString = await (0, file_utils_1.readFile)(path);
hash = (0, md5_1.default)(dbString);
}
catch (e) {
// oh well
}
const oldHash = this.fileHashes.get(path);
if (hash !== oldHash) {
this.fileHashes.set(path, hash);
handleChange(path);
}
});
events_1.knapsackEvents.onShutdown(() => watcher.close());
return watcher;
}
/**
* 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, schema_utils_1.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': {
return {
contents: JSON.stringify(this.orderAlphabetically ? (0, utils_1.sortKeysRecursive)(config) : config, null, ' ') + os_1.default.EOL,
encoding: 'utf8',
};
}
case 'yml': {
// no need to run through formatCodeSync, it's already formatted
return {
contents: (0, file_utils_1.stringifyYaml)(this.orderAlphabetically ? (0, utils_1.sortKeysRecursive)(config) : config, { doubleQuote: !this.#singleQuote }),
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 = (0, file_utils_1.parseYaml)(fileString);
this.validateConfig(config);
return config;
default:
throw new Error('Un-supported type used');
}
}
async read() {
const dbString = await (0, file_utils_1.readFile)(this.filePath);
return this.parse(dbString);
}
readSync() {
const dbString = fs_extra_1.default.readFileSync(this.filePath, 'utf8');
return this.parse(dbString);
}
async savePrep(config = this.defaults) {
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.FileDb = FileDb;
//# sourceMappingURL=file-db.js.map