bananareporter 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +127 -0
- package/bin/dev +17 -0
- package/bin/dev.cmd +3 -0
- package/bin/run +5 -0
- package/bin/run.cmd +3 -0
- package/dist/commands/run/index.d.ts +17 -0
- package/dist/commands/run/index.js +162 -0
- package/dist/core/index.d.ts +140 -0
- package/dist/core/index.js +47 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +5 -0
- package/dist/integrations/base.d.ts +19 -0
- package/dist/integrations/base.js +18 -0
- package/dist/integrations/github.d.ts +215 -0
- package/dist/integrations/github.js +140 -0
- package/dist/integrations/gitlab.d.ts +122 -0
- package/dist/integrations/gitlab.js +246 -0
- package/dist/integrations/todotxt.d.ts +51 -0
- package/dist/integrations/todotxt.js +93 -0
- package/dist/util/common.d.ts +4 -0
- package/dist/util/common.js +14 -0
- package/dist/util/logger.d.ts +2 -0
- package/dist/util/logger.js +13 -0
- package/oclif.manifest.json +92 -0
- package/package.json +88 -0
@@ -0,0 +1,246 @@
|
|
1
|
+
"use strict";
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
3
|
+
exports.GitlabIntegration = exports.GitlabConfig = void 0;
|
4
|
+
const base_1 = require("./base");
|
5
|
+
// import {Axios} from 'axios'
|
6
|
+
const common_1 = require("../util/common");
|
7
|
+
const zod_1 = require("zod");
|
8
|
+
const axios_1 = require("axios");
|
9
|
+
const dayjs = require("dayjs");
|
10
|
+
const logger_1 = require("../util/logger");
|
11
|
+
exports.GitlabConfig = zod_1.z.object({
|
12
|
+
committerUsername: zod_1.z.string().min(1),
|
13
|
+
userId: zod_1.z.number().optional(),
|
14
|
+
token: zod_1.z.string().min(1),
|
15
|
+
filters: zod_1.z.array(zod_1.z.object({
|
16
|
+
on: zod_1.z.string().min(1),
|
17
|
+
regex: zod_1.z.string().min(1),
|
18
|
+
})).nonempty().optional(),
|
19
|
+
domain: zod_1.z.string().optional().default('gitlab.com'),
|
20
|
+
apiVersion: zod_1.z.string().startsWith('v').optional().default('v4'),
|
21
|
+
protocol: zod_1.z.union([zod_1.z.literal('http'), zod_1.z.literal('https')]).optional().default('https'),
|
22
|
+
apiBasePath: zod_1.z.string().optional().default('api'),
|
23
|
+
// TODO should be extended from a base integration
|
24
|
+
from: common_1.zIsoString.optional(),
|
25
|
+
to: common_1.zIsoString.optional(),
|
26
|
+
delay: zod_1.z.number().min(0).optional(),
|
27
|
+
});
|
28
|
+
class GitlabIntegration extends base_1.IntegrationBase {
|
29
|
+
constructor(_rawConfig, bananaReporterConfig) {
|
30
|
+
var _a, _b, _c;
|
31
|
+
super(_rawConfig, bananaReporterConfig);
|
32
|
+
this.projectIdToDetails = {};
|
33
|
+
logger_1.logger.debug('gitlab integration');
|
34
|
+
this.config = exports.GitlabConfig.parse(_rawConfig);
|
35
|
+
logger_1.logger.debug('gitlab integration config', this.config);
|
36
|
+
this.bananaReporterConfig = bananaReporterConfig;
|
37
|
+
this.delayToUse = (_a = this.config.delay) !== null && _a !== void 0 ? _a : this.bananaReporterConfig.delay;
|
38
|
+
logger_1.logger.debug(`gitlab integration delayToUse ${this.delayToUse}`);
|
39
|
+
this.dateRange = {
|
40
|
+
from: (_b = this.config.from) !== null && _b !== void 0 ? _b : this.bananaReporterConfig.from,
|
41
|
+
to: (_c = this.config.to) !== null && _c !== void 0 ? _c : this.bananaReporterConfig.to,
|
42
|
+
};
|
43
|
+
logger_1.logger.debug('gitlab integration dateRange', this.dateRange);
|
44
|
+
const baseURL = `${this.config.protocol}://${this.config.domain}/${this.config.apiBasePath}/${this.config.apiVersion}`;
|
45
|
+
logger_1.logger.debug(`gitlab integration baseURL ${baseURL}`);
|
46
|
+
this.httpClient = axios_1.default.create({
|
47
|
+
baseURL,
|
48
|
+
headers: {
|
49
|
+
Authorization: `Bearer ${this.config.token}`,
|
50
|
+
},
|
51
|
+
timeout: 35000,
|
52
|
+
});
|
53
|
+
}
|
54
|
+
setup(options) {
|
55
|
+
for (const p of options.projectDetails) {
|
56
|
+
if (!(p.id in this.projectIdToDetails)) {
|
57
|
+
this.projectIdToDetails[p.id] = p;
|
58
|
+
}
|
59
|
+
}
|
60
|
+
}
|
61
|
+
async httpRequest(path, config) {
|
62
|
+
logger_1.logger.debug(`gitlab integration http request ${path}`, config);
|
63
|
+
const res = await this.httpClient(path, {
|
64
|
+
...config,
|
65
|
+
});
|
66
|
+
return res.data;
|
67
|
+
}
|
68
|
+
/**
|
69
|
+
* events API needs the user id, retrieve it
|
70
|
+
* from the username provided
|
71
|
+
* @returns GitLab user id
|
72
|
+
*/
|
73
|
+
async getUserId() {
|
74
|
+
if (typeof this.config.userId !== 'undefined') {
|
75
|
+
return this.config.userId;
|
76
|
+
}
|
77
|
+
const username = this.config.committerUsername;
|
78
|
+
const data = await this.httpRequest(`/users?username=${username}`);
|
79
|
+
if (data.length === 0) {
|
80
|
+
throw new Error(`unable to find the GitLab username ${username}`);
|
81
|
+
}
|
82
|
+
return data[0].id;
|
83
|
+
}
|
84
|
+
async fetchData() {
|
85
|
+
const userId = await this.getUserId();
|
86
|
+
logger_1.logger.debug(`gitlab integration userId ${userId}`);
|
87
|
+
let page = 1;
|
88
|
+
let eventList = [];
|
89
|
+
const projectIds = new Set();
|
90
|
+
while (page > 0) {
|
91
|
+
logger_1.logger.debug(`gitlab integration working on ${page}`);
|
92
|
+
/* eslint-disable no-await-in-loop */
|
93
|
+
const events = await this.getUserEvents(userId, {
|
94
|
+
from: this.dateRange.from,
|
95
|
+
to: this.dateRange.to,
|
96
|
+
page,
|
97
|
+
});
|
98
|
+
// no more data, break loop
|
99
|
+
if (events.length === 0) {
|
100
|
+
logger_1.logger.debug('gitlab integration no more events to process');
|
101
|
+
break;
|
102
|
+
}
|
103
|
+
for (const e of events) {
|
104
|
+
projectIds.add(e.project_id);
|
105
|
+
}
|
106
|
+
// eslint-disable-next-line unicorn/prefer-spread
|
107
|
+
eventList = eventList.concat(events);
|
108
|
+
logger_1.logger.debug(`gitlab integration eventList ${eventList.length} (added ${events.length} events)`);
|
109
|
+
page += 1;
|
110
|
+
await (0, common_1.delay)(this.delayToUse);
|
111
|
+
}
|
112
|
+
// get all project details
|
113
|
+
const projectDetails = await this.getProjects(userId, projectIds);
|
114
|
+
logger_1.logger.debug('gitlab integration projectDetails', projectDetails);
|
115
|
+
for (const p of projectDetails) {
|
116
|
+
this.projectIdToDetails[p.id] = p;
|
117
|
+
}
|
118
|
+
this.setup({
|
119
|
+
projectDetails,
|
120
|
+
});
|
121
|
+
// format data to common obj
|
122
|
+
let filteredData = eventList
|
123
|
+
.filter(e => e.action_name.startsWith('pushed'));
|
124
|
+
const projectsToRemove = new Set();
|
125
|
+
for (const filter of this.config.filters || []) {
|
126
|
+
const targetKey = filter.on;
|
127
|
+
if (targetKey.startsWith('$project.')) {
|
128
|
+
// custom logic for project matching
|
129
|
+
for (const projectId of Object.keys(this.projectIdToDetails)) {
|
130
|
+
const project = this.projectIdToDetails[Number(projectId)];
|
131
|
+
const targetProjectKey = targetKey.split('.').pop();
|
132
|
+
if (!(targetProjectKey in project)) {
|
133
|
+
const errorMsg = `while filtering the project ${project.name} (id ${project.id}) unable to find the key ${targetProjectKey} in the project's object (available keys: ${Object.keys(project).join(',')})`;
|
134
|
+
logger_1.logger.error(errorMsg);
|
135
|
+
throw new Error(errorMsg);
|
136
|
+
}
|
137
|
+
const regex = new RegExp(filter.regex);
|
138
|
+
if (!regex.test(project[targetProjectKey])) {
|
139
|
+
// false, remove
|
140
|
+
projectsToRemove.add(Number(projectId));
|
141
|
+
}
|
142
|
+
}
|
143
|
+
}
|
144
|
+
filteredData = filteredData.filter(e => {
|
145
|
+
const targetKey = filter.on;
|
146
|
+
// custom project is filtered later on
|
147
|
+
if (targetKey.startsWith('$project.')) {
|
148
|
+
return true;
|
149
|
+
}
|
150
|
+
const regex = new RegExp(filter.regex);
|
151
|
+
if (!(targetKey in e)) {
|
152
|
+
const errorMsg = `while filtering event id ${e.id} unable to find the key ${targetKey} in the event's object (available keys: ${Object.keys(e).join(',')})`;
|
153
|
+
logger_1.logger.error(errorMsg);
|
154
|
+
throw new Error(errorMsg);
|
155
|
+
}
|
156
|
+
return regex.test(e[targetKey]);
|
157
|
+
});
|
158
|
+
}
|
159
|
+
if (projectsToRemove.size > 0) {
|
160
|
+
filteredData = filteredData.filter(e => {
|
161
|
+
return !projectsToRemove.has(e.project_id);
|
162
|
+
});
|
163
|
+
}
|
164
|
+
const formattedData = filteredData.map(e => this.toBananaReporterObj(e));
|
165
|
+
return formattedData;
|
166
|
+
}
|
167
|
+
/**
|
168
|
+
* GitLab doesn't return project information in the events call,
|
169
|
+
* need to fetch all projects to enrich the events data
|
170
|
+
* @param userId identifier of gitlab user
|
171
|
+
* @param projectIds set of project ids found in events call
|
172
|
+
* @returns list of projects found
|
173
|
+
*/
|
174
|
+
async getProjects(userId, projectIds) {
|
175
|
+
// get all user projects, if some projects are missing
|
176
|
+
// try to get the project via a direct GET call
|
177
|
+
const remainingProjectIdsToFetch = projectIds;
|
178
|
+
const projects = await this.httpRequest(`/users/${userId}/projects`);
|
179
|
+
for (const p of projects) {
|
180
|
+
remainingProjectIdsToFetch.delete(p.id);
|
181
|
+
}
|
182
|
+
const projectsLoaded = projects;
|
183
|
+
if (remainingProjectIdsToFetch.size > 0) {
|
184
|
+
// delay before starting to fetch other data
|
185
|
+
await (0, common_1.delay)(this.delayToUse);
|
186
|
+
// eslint-disable-next-line unicorn/prefer-spread
|
187
|
+
for (const projectId of Array.from(remainingProjectIdsToFetch)) {
|
188
|
+
try {
|
189
|
+
const project = await this.httpRequest(`/projects/${projectId}`);
|
190
|
+
if (project) {
|
191
|
+
remainingProjectIdsToFetch.delete(projectId);
|
192
|
+
projectsLoaded.push(project);
|
193
|
+
}
|
194
|
+
}
|
195
|
+
catch (error) {
|
196
|
+
logger_1.logger.debug(`gitlab integration unable to fetch details for project ID ${projectId}`, error);
|
197
|
+
}
|
198
|
+
await (0, common_1.delay)(this.delayToUse);
|
199
|
+
}
|
200
|
+
}
|
201
|
+
if (remainingProjectIdsToFetch.size > 0) {
|
202
|
+
logger_1.logger.warn(`unable to retrieve details for the following project ids: ${[...remainingProjectIdsToFetch].join(',')} make sure you have still access to these projects, the report won't include the project names`);
|
203
|
+
}
|
204
|
+
return projectsLoaded;
|
205
|
+
}
|
206
|
+
async getUserEvents(userId, options) {
|
207
|
+
var _a;
|
208
|
+
const fromDate = dayjs(options.from);
|
209
|
+
const toDate = dayjs(options.to);
|
210
|
+
// recalculate from - to according on GitLab API behaviour
|
211
|
+
const from = fromDate.subtract(1, 'day').format('YYYY-MM-DD');
|
212
|
+
const to = toDate.add(1, 'day').format('YYYY-MM-DD');
|
213
|
+
logger_1.logger.debug(`gitlab integration getUserEvents from ${from} to ${to}`);
|
214
|
+
const page = (_a = options.page) !== null && _a !== void 0 ? _a : 1;
|
215
|
+
const url = `/users/${userId}/events`;
|
216
|
+
const events = await this.httpRequest(url, {
|
217
|
+
params: {
|
218
|
+
after: from,
|
219
|
+
before: to,
|
220
|
+
sort: 'asc',
|
221
|
+
// eslint-disable-next-line camelcase
|
222
|
+
per_page: 100,
|
223
|
+
action: 'pushed',
|
224
|
+
page,
|
225
|
+
},
|
226
|
+
});
|
227
|
+
return events;
|
228
|
+
}
|
229
|
+
toBananaReporterObj(rawData) {
|
230
|
+
const projectId = rawData.project_id;
|
231
|
+
const projectDetails = this.projectIdToDetails[projectId];
|
232
|
+
const projectName = projectDetails === null || projectDetails === void 0 ? void 0 : projectDetails.path;
|
233
|
+
const description = `${rawData.push_data.commit_title} branch:${rawData.push_data.ref} git:${rawData.push_data.commit_to.slice(0, 7)}`;
|
234
|
+
return {
|
235
|
+
date: rawData.created_at,
|
236
|
+
username: rawData.author_username,
|
237
|
+
description,
|
238
|
+
projectId: String(projectId),
|
239
|
+
projectName,
|
240
|
+
type: GitlabIntegration.type,
|
241
|
+
__raw: this.bananaReporterConfig.includeRawObject ? rawData : undefined,
|
242
|
+
};
|
243
|
+
}
|
244
|
+
}
|
245
|
+
exports.GitlabIntegration = GitlabIntegration;
|
246
|
+
GitlabIntegration.type = base_1.SourceType.Enum.gitlab;
|
@@ -0,0 +1,51 @@
|
|
1
|
+
import { CommonBananaReporterObj, IntegrationBase } from './base';
|
2
|
+
import { BananaConfig } from '../core';
|
3
|
+
import { z } from 'zod';
|
4
|
+
import { Task } from '@lggruspe/todo-txt-parser';
|
5
|
+
export type TodoTxtConfig = z.infer<typeof TodoTxtConfig>;
|
6
|
+
export declare const TodoTxtConfig: z.ZodObject<{
|
7
|
+
file: z.ZodEffects<z.ZodDefault<z.ZodString>, string, string | undefined>;
|
8
|
+
filters: z.ZodOptional<z.ZodArray<z.ZodObject<{
|
9
|
+
on: z.ZodString;
|
10
|
+
regex: z.ZodString;
|
11
|
+
}, "strip", z.ZodTypeAny, {
|
12
|
+
on: string;
|
13
|
+
regex: string;
|
14
|
+
}, {
|
15
|
+
on: string;
|
16
|
+
regex: string;
|
17
|
+
}>, "atleastone">>;
|
18
|
+
from: z.ZodOptional<z.ZodString>;
|
19
|
+
to: z.ZodOptional<z.ZodString>;
|
20
|
+
}, "strip", z.ZodTypeAny, {
|
21
|
+
from?: string | undefined;
|
22
|
+
to?: string | undefined;
|
23
|
+
filters?: [{
|
24
|
+
on: string;
|
25
|
+
regex: string;
|
26
|
+
}, ...{
|
27
|
+
on: string;
|
28
|
+
regex: string;
|
29
|
+
}[]] | undefined;
|
30
|
+
file: string;
|
31
|
+
}, {
|
32
|
+
file?: string | undefined;
|
33
|
+
from?: string | undefined;
|
34
|
+
to?: string | undefined;
|
35
|
+
filters?: [{
|
36
|
+
on: string;
|
37
|
+
regex: string;
|
38
|
+
}, ...{
|
39
|
+
on: string;
|
40
|
+
regex: string;
|
41
|
+
}[]] | undefined;
|
42
|
+
}>;
|
43
|
+
export declare class TodoTxtIntegration extends IntegrationBase {
|
44
|
+
static type: "todo.txt";
|
45
|
+
private config;
|
46
|
+
private dateRange;
|
47
|
+
private bananaReporterConfig;
|
48
|
+
constructor(_rawConfig: unknown, bananaReporterConfig: BananaConfig);
|
49
|
+
fetchData(): Promise<CommonBananaReporterObj[]>;
|
50
|
+
toBananaReporterObj(rawData: Task): CommonBananaReporterObj;
|
51
|
+
}
|
@@ -0,0 +1,93 @@
|
|
1
|
+
"use strict";
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
3
|
+
exports.TodoTxtIntegration = exports.TodoTxtConfig = void 0;
|
4
|
+
const base_1 = require("./base");
|
5
|
+
// import {Axios} from 'axios'
|
6
|
+
const common_1 = require("../util/common");
|
7
|
+
const zod_1 = require("zod");
|
8
|
+
const dayjs = require("dayjs");
|
9
|
+
const logger_1 = require("../util/logger");
|
10
|
+
const objectPath = require("object-path");
|
11
|
+
const node_fs_1 = require("node:fs");
|
12
|
+
const todo_txt_parser_1 = require("@lggruspe/todo-txt-parser");
|
13
|
+
const path = require("path");
|
14
|
+
exports.TodoTxtConfig = zod_1.z.object({
|
15
|
+
// TODO support wildcard
|
16
|
+
file: zod_1.z.string().min(1).default('./todo.txt').refine(s => {
|
17
|
+
return path.resolve(s);
|
18
|
+
}),
|
19
|
+
filters: zod_1.z.array(zod_1.z.object({
|
20
|
+
on: zod_1.z.string().min(1),
|
21
|
+
regex: zod_1.z.string().min(1),
|
22
|
+
})).nonempty().optional(),
|
23
|
+
from: common_1.zIsoString.optional(),
|
24
|
+
to: common_1.zIsoString.optional(),
|
25
|
+
});
|
26
|
+
class TodoTxtIntegration extends base_1.IntegrationBase {
|
27
|
+
constructor(_rawConfig, bananaReporterConfig) {
|
28
|
+
var _a, _b;
|
29
|
+
super(_rawConfig, bananaReporterConfig);
|
30
|
+
logger_1.logger.debug('todo.txt integration');
|
31
|
+
this.config = exports.TodoTxtConfig.parse(_rawConfig);
|
32
|
+
logger_1.logger.debug('todo.txt integration config', this.config);
|
33
|
+
this.bananaReporterConfig = bananaReporterConfig;
|
34
|
+
this.dateRange = {
|
35
|
+
from: (_a = this.config.from) !== null && _a !== void 0 ? _a : this.bananaReporterConfig.from,
|
36
|
+
to: (_b = this.config.to) !== null && _b !== void 0 ? _b : this.bananaReporterConfig.to,
|
37
|
+
};
|
38
|
+
logger_1.logger.debug('todo.txt integration dateRange', this.dateRange);
|
39
|
+
if (!(0, node_fs_1.existsSync)(this.config.file)) {
|
40
|
+
const errorMsg = `Unable to find file ${this.config.file}`;
|
41
|
+
logger_1.logger.error(errorMsg);
|
42
|
+
throw new Error(errorMsg);
|
43
|
+
}
|
44
|
+
logger_1.logger.debug(`todo.txt file ${this.config.file} exists`);
|
45
|
+
}
|
46
|
+
async fetchData() {
|
47
|
+
// parse
|
48
|
+
const rawTodoTxt = (0, node_fs_1.readFileSync)(this.config.file).toString();
|
49
|
+
const parsedTodoTxtFile = (0, todo_txt_parser_1.parse)(rawTodoTxt);
|
50
|
+
const fromDate = dayjs(this.dateRange.from).subtract(1, 'minute');
|
51
|
+
const toDate = dayjs(this.dateRange.to).add(1, 'minute');
|
52
|
+
let filteredTasks = parsedTodoTxtFile.filter(t => {
|
53
|
+
const rawTaskDate = t.completion || t.creation;
|
54
|
+
// unknown task date, don't include it
|
55
|
+
if (typeof rawTaskDate === 'undefined') {
|
56
|
+
return false;
|
57
|
+
}
|
58
|
+
const taskDateObj = dayjs(rawTaskDate);
|
59
|
+
return (taskDateObj.isAfter(fromDate) &&
|
60
|
+
taskDateObj.isBefore(toDate));
|
61
|
+
});
|
62
|
+
// format data to common obj
|
63
|
+
for (const filter of this.config.filters || []) {
|
64
|
+
const targetKey = filter.on;
|
65
|
+
filteredTasks = filteredTasks.filter(t => {
|
66
|
+
const regex = new RegExp(filter.regex);
|
67
|
+
const value = objectPath.get(t, targetKey);
|
68
|
+
if (typeof value === 'undefined') {
|
69
|
+
const errorMsg = `while filtering "${t.description}" unable to find the key ${targetKey} in the task's object (available keys: ${Object.keys(t).join(',')})`;
|
70
|
+
logger_1.logger.error(errorMsg);
|
71
|
+
throw new Error(errorMsg);
|
72
|
+
}
|
73
|
+
return regex.test(value);
|
74
|
+
});
|
75
|
+
}
|
76
|
+
const formattedData = filteredTasks.map(e => this.toBananaReporterObj(e));
|
77
|
+
return formattedData;
|
78
|
+
}
|
79
|
+
toBananaReporterObj(rawData) {
|
80
|
+
var _a;
|
81
|
+
const projectName = (_a = rawData.projects) === null || _a === void 0 ? void 0 : _a.map(p => p.replace('+', '')).join(',');
|
82
|
+
const description = `${rawData.description}`;
|
83
|
+
return {
|
84
|
+
date: rawData.completion || rawData.creation || '',
|
85
|
+
description,
|
86
|
+
projectName,
|
87
|
+
type: TodoTxtIntegration.type,
|
88
|
+
__raw: this.bananaReporterConfig.includeRawObject ? rawData : undefined,
|
89
|
+
};
|
90
|
+
}
|
91
|
+
}
|
92
|
+
exports.TodoTxtIntegration = TodoTxtIntegration;
|
93
|
+
TodoTxtIntegration.type = base_1.SourceType.Enum['todo.txt'];
|
@@ -0,0 +1,14 @@
|
|
1
|
+
"use strict";
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
3
|
+
exports.delay = exports.zIsoString = void 0;
|
4
|
+
const zod_1 = require("zod");
|
5
|
+
const ISO_DATE_REGEX = /\d{4}-[01]\d-[0-3]\d/;
|
6
|
+
// from https://github.com/colinhacks/zod/issues/482#issuecomment-1369800957
|
7
|
+
exports.zIsoString = zod_1.z
|
8
|
+
.string()
|
9
|
+
.regex(ISO_DATE_REGEX, 'date must be a valid ISO8601 date');
|
10
|
+
function delay(ms) {
|
11
|
+
// eslint-disable-next-line no-promise-executor-return
|
12
|
+
return new Promise(resolve => setTimeout(resolve, ms));
|
13
|
+
}
|
14
|
+
exports.delay = delay;
|
@@ -0,0 +1,13 @@
|
|
1
|
+
"use strict";
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
3
|
+
exports.logger = void 0;
|
4
|
+
const winston = require("winston");
|
5
|
+
exports.logger = winston.createLogger({
|
6
|
+
level: process.env.BANANAREPORTER_LOG_LEVEL || 'info',
|
7
|
+
format: winston.format.json(),
|
8
|
+
transports: [
|
9
|
+
new winston.transports.Console({
|
10
|
+
format: winston.format.combine(winston.format.colorize(), winston.format.simple()),
|
11
|
+
}),
|
12
|
+
],
|
13
|
+
});
|
@@ -0,0 +1,92 @@
|
|
1
|
+
{
|
2
|
+
"version": "0.1.0",
|
3
|
+
"commands": {
|
4
|
+
"run": {
|
5
|
+
"id": "run",
|
6
|
+
"description": "Run report",
|
7
|
+
"strict": true,
|
8
|
+
"pluginName": "bananareporter",
|
9
|
+
"pluginAlias": "bananareporter",
|
10
|
+
"pluginType": "core",
|
11
|
+
"aliases": [],
|
12
|
+
"examples": [
|
13
|
+
"$ banana-reporter run --from 2023-01-01 --to 2023-01-31\nreport with 138 entries saved to ./bananareporter.json\n"
|
14
|
+
],
|
15
|
+
"flags": {
|
16
|
+
"config": {
|
17
|
+
"name": "config",
|
18
|
+
"type": "option",
|
19
|
+
"char": "c",
|
20
|
+
"description": "config file location, by default ~/.config/bananareporter/config.yaml",
|
21
|
+
"required": false,
|
22
|
+
"multiple": false
|
23
|
+
},
|
24
|
+
"from": {
|
25
|
+
"name": "from",
|
26
|
+
"type": "option",
|
27
|
+
"description": "from date (ISO8601)",
|
28
|
+
"required": false,
|
29
|
+
"helpValue": "2023-03-01",
|
30
|
+
"multiple": false
|
31
|
+
},
|
32
|
+
"to": {
|
33
|
+
"name": "to",
|
34
|
+
"type": "option",
|
35
|
+
"description": "to date (ISO8601)",
|
36
|
+
"required": false,
|
37
|
+
"helpValue": "2023-03-31",
|
38
|
+
"multiple": false,
|
39
|
+
"dependsOn": [
|
40
|
+
"from"
|
41
|
+
]
|
42
|
+
},
|
43
|
+
"out": {
|
44
|
+
"name": "out",
|
45
|
+
"type": "option",
|
46
|
+
"char": "o",
|
47
|
+
"description": "file path to save the output",
|
48
|
+
"required": true,
|
49
|
+
"multiple": false,
|
50
|
+
"default": "./bananareporter.json",
|
51
|
+
"aliases": [
|
52
|
+
"output"
|
53
|
+
]
|
54
|
+
},
|
55
|
+
"format": {
|
56
|
+
"name": "format",
|
57
|
+
"type": "option",
|
58
|
+
"description": "output file format",
|
59
|
+
"required": true,
|
60
|
+
"multiple": false,
|
61
|
+
"options": [
|
62
|
+
"json",
|
63
|
+
"jsonl",
|
64
|
+
"csv"
|
65
|
+
],
|
66
|
+
"default": "json"
|
67
|
+
},
|
68
|
+
"delay": {
|
69
|
+
"name": "delay",
|
70
|
+
"type": "option",
|
71
|
+
"description": "global delay in millisecons between http requests",
|
72
|
+
"multiple": false,
|
73
|
+
"default": 300
|
74
|
+
},
|
75
|
+
"include-raw-object": {
|
76
|
+
"name": "include-raw-object",
|
77
|
+
"type": "boolean",
|
78
|
+
"description": "include raw object in json/jsonl reporter output",
|
79
|
+
"allowNo": false
|
80
|
+
},
|
81
|
+
"disable-welcome-emoji": {
|
82
|
+
"name": "disable-welcome-emoji",
|
83
|
+
"type": "boolean",
|
84
|
+
"description": "disable banana welcome emoji",
|
85
|
+
"hidden": true,
|
86
|
+
"allowNo": false
|
87
|
+
}
|
88
|
+
},
|
89
|
+
"args": {}
|
90
|
+
}
|
91
|
+
}
|
92
|
+
}
|
package/package.json
ADDED
@@ -0,0 +1,88 @@
|
|
1
|
+
{
|
2
|
+
"name": "bananareporter",
|
3
|
+
"version": "0.1.0",
|
4
|
+
"description": "Easily generate a report from multiple sources",
|
5
|
+
"author": "nya1",
|
6
|
+
"bin": {
|
7
|
+
"bananareporter": "./bin/run"
|
8
|
+
},
|
9
|
+
"homepage": "https://github.com/nya1/bananareporter",
|
10
|
+
"license": "MIT",
|
11
|
+
"main": "dist/index.js",
|
12
|
+
"repository": "nya1/bananareporter",
|
13
|
+
"files": [
|
14
|
+
"/bin",
|
15
|
+
"/dist",
|
16
|
+
"/npm-shrinkwrap.json",
|
17
|
+
"/oclif.manifest.json"
|
18
|
+
],
|
19
|
+
"dependencies": {
|
20
|
+
"@lggruspe/todo-txt-parser": "^1.1.1",
|
21
|
+
"@oclif/core": "^2",
|
22
|
+
"@oclif/plugin-help": "^5",
|
23
|
+
"axios": "^1.3.4",
|
24
|
+
"dayjs": "^1.11.7",
|
25
|
+
"js-yaml": "^4.1.0",
|
26
|
+
"lodash": "^4.17.21",
|
27
|
+
"object-path": "^0.11.8",
|
28
|
+
"papaparse": "^5.4.0",
|
29
|
+
"winston": "^3.8.2",
|
30
|
+
"zod": "^3.21.0"
|
31
|
+
},
|
32
|
+
"devDependencies": {
|
33
|
+
"@oclif/test": "^2.3.8",
|
34
|
+
"@types/chai": "^4",
|
35
|
+
"@types/js-yaml": "^4.0.5",
|
36
|
+
"@types/mocha": "^9.1.1",
|
37
|
+
"@types/node": "^18.14.6",
|
38
|
+
"@types/node-fetch": "^2.6.2",
|
39
|
+
"@types/object-path": "^0.11.1",
|
40
|
+
"@types/papaparse": "^5.3.7",
|
41
|
+
"chai": "^4",
|
42
|
+
"eslint": "^7.32.0",
|
43
|
+
"eslint-config-oclif": "^4",
|
44
|
+
"eslint-config-oclif-typescript": "^1.0.3",
|
45
|
+
"mocha": "^9",
|
46
|
+
"oclif": "^3",
|
47
|
+
"shx": "^0.3.4",
|
48
|
+
"ts-node": "^10.9.1",
|
49
|
+
"tslib": "^2.5.0",
|
50
|
+
"typescript": "^4.9.5"
|
51
|
+
},
|
52
|
+
"oclif": {
|
53
|
+
"bin": "bananareporter",
|
54
|
+
"dirname": "bananareporter",
|
55
|
+
"default": "run",
|
56
|
+
"commands": "./dist/commands",
|
57
|
+
"plugins": [
|
58
|
+
"@oclif/plugin-help"
|
59
|
+
],
|
60
|
+
"topicSeparator": " ",
|
61
|
+
"topics": {
|
62
|
+
"run": {
|
63
|
+
"description": "Run reporter"
|
64
|
+
}
|
65
|
+
}
|
66
|
+
},
|
67
|
+
"scripts": {
|
68
|
+
"build": "shx rm -rf dist && tsc -b",
|
69
|
+
"lint": "eslint . --ext .ts --config .eslintrc",
|
70
|
+
"postpack": "shx rm -f oclif.manifest.json",
|
71
|
+
"posttest": "yarn lint",
|
72
|
+
"prepack": "yarn build && oclif manifest && oclif readme",
|
73
|
+
"test": "mocha --forbid-only \"test/**/*.test.ts\"",
|
74
|
+
"version": "oclif readme && git add README.md"
|
75
|
+
},
|
76
|
+
"engines": {
|
77
|
+
"node": ">=12.0.0"
|
78
|
+
},
|
79
|
+
"bugs": "https://github.com/nya1/bananareporter/issues",
|
80
|
+
"keywords": [
|
81
|
+
"report",
|
82
|
+
"banana",
|
83
|
+
"reporter",
|
84
|
+
"export",
|
85
|
+
"oclif"
|
86
|
+
],
|
87
|
+
"types": "dist/index.d.ts"
|
88
|
+
}
|