Skip to content
This repository was archived by the owner on Jan 9, 2023. It is now read-only.

Commit ebf4807

Browse files
committed
feat: add extract command
1 parent ba2cfe6 commit ebf4807

17 files changed

Lines changed: 637 additions & 259 deletions

File tree

.eslintrc.js

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
module.exports = {
2-
ignorePatterns: ['bin'],
2+
ignorePatterns: ['bin', 'commitlint.config.js'],
33
extends: [
44
'plugin:@typescript-eslint/recommended',
55
'prettier',
@@ -10,8 +10,6 @@ module.exports = {
1010
parserOptions: {
1111
project: './tsconfig.json',
1212
tsconfigRootDir: './',
13-
// TODO: we need this because of an issue with @typescript-eslint/parser: https://github.com/typescript-eslint/typescript-eslint/issues/864
14-
createDefaultProgram: true,
1513
},
1614
plugins: ['@typescript-eslint', 'prettier'],
1715
rules: {

bin/ddoc.js

Lines changed: 0 additions & 99 deletions
This file was deleted.

bin/ddoc/build/build.js

Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,90 @@
1+
"use strict";
2+
var __importDefault = (this && this.__importDefault) || function (mod) {
3+
return (mod && mod.__esModule) ? mod : { "default": mod };
4+
};
5+
Object.defineProperty(exports, "__esModule", { value: true });
6+
const glob_1 = __importDefault(require("glob"));
7+
const path_1 = __importDefault(require("path"));
8+
const fs_1 = __importDefault(require("fs"));
9+
const util_1 = require("util");
10+
const typescript_1 = __importDefault(require("typescript"));
11+
const require_from_string_1 = __importDefault(require("require-from-string"));
12+
const mkdirp_1 = __importDefault(require("mkdirp"));
13+
const chalk_1 = __importDefault(require("chalk"));
14+
const stat = util_1.promisify(fs_1.default.stat);
15+
const readFile = util_1.promisify(fs_1.default.readFile);
16+
const writeFile = util_1.promisify(fs_1.default.writeFile);
17+
const unlink = util_1.promisify(fs_1.default.unlink);
18+
const glob = util_1.promisify(glob_1.default);
19+
async function deleteOldDdocs(dest) {
20+
const oldDdocs = await glob(path_1.default.join(dest, '**/*.json'));
21+
return Promise.all(oldDdocs.map(file => unlink(file)));
22+
}
23+
async function build(src, opts) {
24+
var _a;
25+
try {
26+
const tsconfigPath = path_1.default.resolve(opts.config);
27+
console.log(`> ${chalk_1.default.bgBlueBright(chalk_1.default.black(' ddoc build config '))} ${chalk_1.default.cyan(tsconfigPath)}`);
28+
const tsconfig = require(tsconfigPath);
29+
src = path_1.default.resolve(src);
30+
let srcStats;
31+
try {
32+
srcStats = await stat(src);
33+
}
34+
catch (err) {
35+
if (err.code === 'ENOENT') {
36+
console.log(chalk_1.default.bgGreen(chalk_1.default.black(`\n ddoc build - No input files found. Done. `)));
37+
process.exit(0);
38+
}
39+
throw err;
40+
}
41+
let dest = ((_a = tsconfig === null || tsconfig === void 0 ? void 0 : tsconfig.compilerOptions) === null || _a === void 0 ? void 0 : _a.outDir) ? path_1.default.join(path_1.default.dirname(tsconfigPath), tsconfig.compilerOptions.outDir)
42+
: '';
43+
let ddocs;
44+
if (srcStats.isDirectory()) {
45+
dest = dest || src;
46+
ddocs = await glob(path_1.default.join(src, '**/*.ts'));
47+
}
48+
else {
49+
dest = dest || path_1.default.dirname(src);
50+
ddocs = [src];
51+
}
52+
console.log(`> ${chalk_1.default.bgBlueBright(chalk_1.default.black(' ddoc build src '))} ${chalk_1.default.cyan(src)}`);
53+
await mkdirp_1.default(dest);
54+
await deleteOldDdocs(dest);
55+
console.log(`> ${chalk_1.default.bgBlueBright(chalk_1.default.black(' ddoc build dest '))} ${chalk_1.default.cyan(dest)}`);
56+
const errors = [];
57+
await Promise.all(ddocs.map(async (srcPath) => {
58+
var _a;
59+
try {
60+
const sourceFile = (await readFile(srcPath)).toString();
61+
const output = typescript_1.default.transpileModule(sourceFile, tsconfig);
62+
const filename = path_1.default.basename(srcPath, '.ts');
63+
const ddoc = require_from_string_1.default(output.outputText);
64+
const stringifiedDesign = JSON.stringify((_a = ddoc.default) !== null && _a !== void 0 ? _a : ddoc, (_, val) => {
65+
if (typeof val === 'function') {
66+
return val.toString();
67+
}
68+
return val;
69+
}, 1);
70+
await writeFile(path_1.default.join(dest, `${filename}.json`), stringifiedDesign);
71+
}
72+
catch (error) {
73+
errors.push({ file: srcPath, error });
74+
}
75+
}));
76+
if (errors.length > 0) {
77+
errors.forEach(err => {
78+
var _a;
79+
console.log(`\n> ${chalk_1.default.bgRed(chalk_1.default.white(' ddoc build compile error '))} ${chalk_1.default.cyan(err.file)}${(_a = err.error.stack) === null || _a === void 0 ? void 0 : _a.toString()}\n`);
80+
});
81+
throw new Error(`ddoc compilation failed. Resolve errors ${errors.length} ${errors.length > 1 ? 'files' : 'file'} and try again.`);
82+
}
83+
console.log(chalk_1.default.bgGreen(chalk_1.default.black(`\n ddoc build - done on ${ddocs.length} files. `)));
84+
}
85+
catch (err) {
86+
console.error(chalk_1.default.bgRed(chalk_1.default.white(` ${err.message} `)));
87+
process.exit(1);
88+
}
89+
}
90+
exports.default = build;

bin/ddoc/build/index.js

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
"use strict";
2+
var __importDefault = (this && this.__importDefault) || function (mod) {
3+
return (mod && mod.__esModule) ? mod : { "default": mod };
4+
};
5+
Object.defineProperty(exports, "__esModule", { value: true });
6+
const build_1 = __importDefault(require("./build"));
7+
exports.default = (prog) => {
8+
prog
9+
.command('ddoc build <src>', 'Build design document(s) from TypeScript. Expects TypeScript <src> folder or file.')
10+
.option('-c, --config', 'Provide path to custom tsconfig.json', './tsconfig.json')
11+
.example('ddoc build src/db/designs')
12+
.example('ddoc build src/db/designs/patient.ts -c src/db/tsconfig.json')
13+
.action(build_1.default);
14+
};

bin/ddoc/extract/extract.js

Lines changed: 168 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,168 @@
1+
"use strict";
2+
var __importDefault = (this && this.__importDefault) || function (mod) {
3+
return (mod && mod.__esModule) ? mod : { "default": mod };
4+
};
5+
Object.defineProperty(exports, "__esModule", { value: true });
6+
const fs_1 = __importDefault(require("fs"));
7+
const util_1 = __importDefault(require("util"));
8+
const path_1 = __importDefault(require("path"));
9+
const mkdirp_1 = __importDefault(require("mkdirp"));
10+
const chalk_1 = __importDefault(require("chalk"));
11+
const typebox_1 = require("@sinclair/typebox");
12+
const ajv_1 = __importDefault(require("ajv"));
13+
const ajv = new ajv_1.default({});
14+
const writeFile = util_1.default.promisify(fs_1.default.writeFile);
15+
const readFIle = util_1.default.promisify(fs_1.default.readFile);
16+
const FORMATS = ['js', 'json', 'ts'];
17+
const ViewsSchema = typebox_1.Type.Object({
18+
map: typebox_1.Type.Optional(typebox_1.Type.String()),
19+
reduce: typebox_1.Type.Optional(typebox_1.Type.String()),
20+
});
21+
const MapSchema = typebox_1.Type.Map(typebox_1.Type.String());
22+
const DesignDocumentSchema = typebox_1.Type.Object({
23+
_id: typebox_1.Type.Optional(typebox_1.Type.String()),
24+
_rev: typebox_1.Type.Optional(typebox_1.Type.String()),
25+
language: typebox_1.Type.Optional(typebox_1.Type.String()),
26+
autoupdate: typebox_1.Type.Optional(typebox_1.Type.Boolean()),
27+
options: typebox_1.Type.Optional(typebox_1.Type.Object({
28+
local_seq: typebox_1.Type.Optional(typebox_1.Type.Boolean()),
29+
include_design: typebox_1.Type.Optional(typebox_1.Type.Boolean()),
30+
})),
31+
views: typebox_1.Type.Optional(typebox_1.Type.Map(ViewsSchema)),
32+
updates: typebox_1.Type.Optional(MapSchema),
33+
filters: typebox_1.Type.Optional(MapSchema),
34+
validate_doc_update: typebox_1.Type.Optional(typebox_1.Type.String()),
35+
shows: typebox_1.Type.Optional(MapSchema),
36+
lists: typebox_1.Type.Optional(MapSchema),
37+
rewrites: typebox_1.Type.Optional(typebox_1.Type.String()),
38+
});
39+
async function default_1(src, opts) {
40+
var _a;
41+
const methods = new Map();
42+
try {
43+
if (!FORMATS.includes(opts.format)) {
44+
throw Error(`Wrong destination format provided ${opts.format}, must be one of ${FORMATS}`);
45+
}
46+
const srcPath = path_1.default.resolve(src);
47+
const dbBackup = (await readFIle(srcPath)).toString();
48+
console.log(`> ${chalk_1.default.bgBlueBright(chalk_1.default.black(' ddoc extract src '))} ${chalk_1.default.cyan(srcPath)}`);
49+
const main = JSON.parse(dbBackup.replace(/\]\n\[/g, ','));
50+
const designDocuments = main.filter(doc => doc._id.startsWith('_design/'));
51+
const stats = {
52+
views: 0,
53+
updates: 0,
54+
filters: 0,
55+
validate_doc_update: 0,
56+
shows: 0,
57+
lists: 0,
58+
rewrites: 0,
59+
};
60+
if (opts.format === 'json') {
61+
for (const doc of designDocuments) {
62+
ajv.validate(DesignDocumentSchema, doc);
63+
stats.views = stats.views + (doc.views ? 1 : 0);
64+
stats.updates = stats.updates + (doc.updates ? 1 : 0);
65+
stats.filters = stats.filters + (doc.filters ? 1 : 0);
66+
stats.validate_doc_update = stats.validate_doc_update + (doc.validate_doc_update ? 1 : 0);
67+
stats.shows = stats.shows + (doc.shows ? 1 : 0);
68+
stats.lists = stats.lists + (doc.lists ? 1 : 0);
69+
stats.rewrites = stats.rewrites + (doc.rewrites ? 1 : 0);
70+
}
71+
const filename = `${path_1.default.basename(src, path_1.default.extname(src))}-designs.${opts.format}`;
72+
const dest = path_1.default.resolve(path_1.default.join(opts.destination, filename));
73+
console.log(`> ${chalk_1.default.bgBlueBright(chalk_1.default.black(' ddoc extract designs found '))}`);
74+
console.table(stats);
75+
await mkdirp_1.default(path_1.default.dirname(dest));
76+
console.log(`> ${chalk_1.default.bgGreenBright(chalk_1.default.black(' ddoc extract destination '))} ${chalk_1.default.cyan(dest)}`);
77+
await writeFile(dest, JSON.stringify(designDocuments, null, 1));
78+
}
79+
else {
80+
for (const doc of designDocuments) {
81+
if (!ajv.validate(DesignDocumentSchema, doc)) {
82+
throw Error(`The ${doc._id}-${doc._rev} schema is invalid.`);
83+
}
84+
if (doc.views) {
85+
stats.views = stats.views + 1;
86+
for (const view of Object.keys(doc.views)) {
87+
if (doc.views[view].map) {
88+
const name = `${doc._id}-${doc._rev}.views.${view}.map`;
89+
methods.set(name, doc.views[view].map);
90+
doc.views[view].map = name;
91+
}
92+
if (doc.views[view].reduce) {
93+
const name = `${doc._id}-${doc._rev}.views.${view}.reduce`;
94+
methods.set(name, doc.views[view].reduce);
95+
doc.views[view].reduce = name;
96+
}
97+
}
98+
}
99+
if (doc.updates) {
100+
stats.updates = stats.updates + 1;
101+
for (const update of Object.keys(doc.updates)) {
102+
const name = `${doc._id}-${doc._rev}.updates.${update}`;
103+
methods.set(name, doc.updates[update]);
104+
doc.updates[update] = name;
105+
}
106+
}
107+
if (doc.filters) {
108+
stats.filters = stats.filters + 1;
109+
for (const filter of Object.keys(doc.filters)) {
110+
const name = `${doc._id}-${doc._rev}.filters.${filter}`;
111+
methods.set(name, doc.filters[filter]);
112+
doc.filters[filter] = name;
113+
}
114+
}
115+
if (doc.validate_doc_update) {
116+
stats.validate_doc_update = stats.validate_doc_update + 1;
117+
const name = `${doc._id}-${doc._rev}.validate_doc_update`;
118+
methods.set(name, doc.validate_doc_update);
119+
doc.validate_doc_update = name;
120+
}
121+
if (doc.shows) {
122+
stats.shows = stats.shows + 1;
123+
for (const show of Object.keys(doc.shows)) {
124+
const name = `${doc._id}-${doc._rev}.shows.${show}`;
125+
methods.set(name, doc.shows[show]);
126+
doc.shows[show] = name;
127+
}
128+
}
129+
if (doc.lists) {
130+
stats.lists = stats.lists + 1;
131+
for (const list of Object.keys(doc.lists)) {
132+
const name = `${doc._id}-${doc._rev}.lists.${list}`;
133+
methods.set(name, doc.lists[list]);
134+
doc.lists[list] = name;
135+
}
136+
}
137+
if (doc.rewrites) {
138+
stats.rewrites = stats.rewrites + 1;
139+
const name = `${doc._id}-${doc._rev}.rewrites`;
140+
methods.set(name, doc.rewrites);
141+
doc.rewrites = name;
142+
}
143+
}
144+
console.log(`> ${chalk_1.default.bgBlueBright(chalk_1.default.black(' ddoc extract designs creating destination folder '))}`);
145+
const destFolder = path_1.default.resolve(path_1.default.join(opts.destination, `${path_1.default.basename(src, path_1.default.extname(src))}-designs`));
146+
await mkdirp_1.default(destFolder);
147+
for (const doc of designDocuments) {
148+
const filename = `${(_a = doc._id) === null || _a === void 0 ? void 0 : _a.split('_design/')[1]}-${doc._rev}.${opts.format === 'js' ? 'js' : 'ts'}`;
149+
console.log(`> ${chalk_1.default.bgBlueBright(chalk_1.default.black(` ddoc extract designs creating ${filename} `))}`);
150+
const dest = path_1.default.resolve(path_1.default.join(destFolder, filename));
151+
let docString = opts.format === 'js'
152+
? `module.exports = ${JSON.stringify(doc, null, 1)}`
153+
: `export default ${JSON.stringify(doc, null, 1)}`;
154+
for (const [name, method] of methods) {
155+
docString = docString.replace(`\"${name}\"`, method);
156+
}
157+
await writeFile(dest, docString);
158+
}
159+
console.log(`> ${chalk_1.default.bgGreenBright(chalk_1.default.black(' ddoc extract designs done '))}`);
160+
console.table(stats);
161+
}
162+
}
163+
catch (err) {
164+
console.error(chalk_1.default.bgRed(chalk_1.default.white(` ${err.message} `)));
165+
process.exit(1);
166+
}
167+
}
168+
exports.default = default_1;

0 commit comments

Comments
 (0)