cucumber-dressing 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.
@@ -0,0 +1,207 @@
1
+ /**
2
+ * Core data models for DRESSING reporter
3
+ */
4
+ /**
5
+ * Represents a Cucumber/Gherkin feature
6
+ */
7
+ export interface GherkinFeature {
8
+ id: string;
9
+ uri: string;
10
+ keyword: string;
11
+ name: string;
12
+ description?: string;
13
+ line: number;
14
+ tags?: GherkinTag[];
15
+ elements: GherkinScenario[];
16
+ }
17
+ /**
18
+ * Represents a scenario or background in a Gherkin feature
19
+ */
20
+ export interface GherkinScenario {
21
+ id: string;
22
+ keyword: string;
23
+ name: string;
24
+ description?: string;
25
+ line: number;
26
+ type: 'scenario' | 'background';
27
+ tags?: GherkinTag[];
28
+ steps: GherkinStep[];
29
+ before?: GherkinHook[];
30
+ after?: GherkinHook[];
31
+ }
32
+ /**
33
+ * Represents a step in a Gherkin scenario
34
+ */
35
+ export interface GherkinStep {
36
+ keyword: string;
37
+ name: string;
38
+ line: number;
39
+ result: StepResult;
40
+ match?: {
41
+ location?: string;
42
+ };
43
+ embeddings?: Embedding[];
44
+ output?: string[];
45
+ doc_string?: {
46
+ value: string;
47
+ line: number;
48
+ content_type?: string;
49
+ };
50
+ rows?: DataTableRow[];
51
+ }
52
+ /**
53
+ * Represents a row in a Gherkin data table
54
+ */
55
+ export interface DataTableRow {
56
+ cells: string[];
57
+ }
58
+ /**
59
+ * Represents a before/after hook in a Gherkin scenario
60
+ */
61
+ export interface GherkinHook {
62
+ match: {
63
+ location: string;
64
+ };
65
+ result: StepResult;
66
+ }
67
+ /**
68
+ * Represents a tag annotation in Gherkin
69
+ */
70
+ export interface GherkinTag {
71
+ name: string;
72
+ line?: number;
73
+ }
74
+ /**
75
+ * Represents the execution result of a step
76
+ */
77
+ export interface StepResult {
78
+ status: 'passed' | 'failed' | 'skipped' | 'pending' | 'undefined' | 'ambiguous';
79
+ duration?: number;
80
+ error_message?: string;
81
+ }
82
+ /**
83
+ * Represents an embedded asset (screenshot, text, etc.) in a step
84
+ */
85
+ export interface Embedding {
86
+ data: string;
87
+ mime_type: string;
88
+ name?: string;
89
+ }
90
+ /**
91
+ * Color customization options for report theming
92
+ */
93
+ export interface ColorCustomization {
94
+ primary?: string;
95
+ success?: string;
96
+ danger?: string;
97
+ warning?: string;
98
+ info?: string;
99
+ muted?: string;
100
+ background?: string;
101
+ backgroundSecondary?: string;
102
+ border?: string;
103
+ text?: string;
104
+ textSecondary?: string;
105
+ }
106
+ /**
107
+ * Configuration options for report generation
108
+ */
109
+ export interface ReportOptions {
110
+ jsonDir?: string;
111
+ jsonFile?: string;
112
+ output: string;
113
+ reportTitle?: string;
114
+ reportName?: string;
115
+ metadata?: ReportMetadata;
116
+ customData?: CustomData;
117
+ theme?: 'modern' | 'classic' | 'dark';
118
+ colors?: ColorCustomization;
119
+ displayDuration?: boolean;
120
+ durationInMS?: boolean;
121
+ openReportInBrowser?: boolean;
122
+ screenshotsDirectory?: string;
123
+ noInlineScreenshots?: boolean;
124
+ columnLayout?: 1 | 2;
125
+ brandTitle?: string;
126
+ scenarioTimestamp?: boolean;
127
+ disableLog?: boolean;
128
+ customStyle?: string;
129
+ }
130
+ /**
131
+ * Metadata about the test execution environment
132
+ */
133
+ export interface ReportMetadata {
134
+ browser?: {
135
+ name: string;
136
+ version: string;
137
+ };
138
+ device?: string;
139
+ platform?: {
140
+ name: string;
141
+ version: string;
142
+ };
143
+ app?: {
144
+ name: string;
145
+ version: string;
146
+ };
147
+ }
148
+ /**
149
+ * Custom data section for additional report information
150
+ */
151
+ export interface CustomData {
152
+ title?: string;
153
+ data: {
154
+ label: string;
155
+ value: string;
156
+ }[];
157
+ }
158
+ /**
159
+ * Computed statistics for the test execution
160
+ */
161
+ export interface ReportStatistics {
162
+ features: number;
163
+ scenarios: number;
164
+ steps: number;
165
+ passed: number;
166
+ failed: number;
167
+ skipped: number;
168
+ pending: number;
169
+ undefined: number;
170
+ ambiguous: number;
171
+ duration: number;
172
+ passRate: number;
173
+ startTime?: string;
174
+ endTime?: string;
175
+ }
176
+ /**
177
+ * Feature with computed status, duration, and processed scenarios
178
+ */
179
+ export interface ProcessedFeature extends GherkinFeature {
180
+ status: 'passed' | 'failed' | 'skipped' | 'pending';
181
+ duration: number;
182
+ scenarios: ProcessedScenario[];
183
+ metadata?: ReportMetadata;
184
+ }
185
+ /**
186
+ * Scenario with computed status, duration, and feature references
187
+ */
188
+ export interface ProcessedScenario extends GherkinScenario {
189
+ status: 'passed' | 'failed' | 'skipped' | 'pending' | 'undefined';
190
+ duration: number;
191
+ featureName: string;
192
+ featureId: string;
193
+ }
194
+ /**
195
+ * Chart.js compatible data structure for visualizations
196
+ */
197
+ export interface ChartData {
198
+ labels: string[];
199
+ datasets: {
200
+ label: string;
201
+ data: number[];
202
+ backgroundColor?: string[];
203
+ borderColor?: string[];
204
+ borderWidth?: number;
205
+ }[];
206
+ }
207
+ //# sourceMappingURL=types.d.ts.map
package/dist/types.js ADDED
@@ -0,0 +1,5 @@
1
+ /**
2
+ * Core data models for DRESSING reporter
3
+ */
4
+ export {};
5
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1,112 @@
1
+ /**
2
+ * Utility functions for DRESSING reporter
3
+ */
4
+ import { GherkinFeature, GherkinScenario, GherkinStep, StepResult } from './types.js';
5
+ /**
6
+ * Calculate duration in a human-readable format
7
+ * @param nanoseconds - Duration in nanoseconds (or milliseconds if inMS is true)
8
+ * @param inMS - If true, treats input as milliseconds instead of nanoseconds
9
+ * @returns Formatted duration string (e.g., "1.50s", "2m 30s")
10
+ */
11
+ export declare function formatDuration(nanoseconds: number, inMS?: boolean): string;
12
+ /**
13
+ * Get the worst status from a list of statuses (failed > ambiguous > undefined > pending > skipped > passed)
14
+ * @param statuses - Array of status strings
15
+ * @returns The worst status from the hierarchy
16
+ */
17
+ export declare function getWorstStatus(statuses: ('passed' | 'failed' | 'skipped' | 'pending' | 'undefined' | 'ambiguous')[]): 'passed' | 'failed' | 'skipped' | 'pending' | 'undefined' | 'ambiguous';
18
+ /**
19
+ * Calculate scenario status based on steps
20
+ * @param steps - Array of Gherkin steps
21
+ * @returns The worst status among all steps, or 'pending' if no steps
22
+ */
23
+ export declare function calculateScenarioStatus(steps: GherkinStep[]): 'passed' | 'failed' | 'skipped' | 'pending' | 'undefined';
24
+ /**
25
+ * Calculate feature status based on scenarios
26
+ * @param scenarios - Array of Gherkin scenarios
27
+ * @returns 'failed' if any scenario failed, 'skipped' if all skipped, otherwise 'passed'
28
+ */
29
+ export declare function calculateFeatureStatus(scenarios: GherkinScenario[]): 'passed' | 'failed' | 'skipped' | 'pending';
30
+ /**
31
+ * Calculate total duration from steps
32
+ * @param steps - Array of Gherkin steps
33
+ * @returns Total duration in nanoseconds
34
+ */
35
+ export declare function calculateDuration(steps: GherkinStep[]): number;
36
+ /**
37
+ * Calculate scenario duration including hooks (before/after)
38
+ * @param scenario - Gherkin scenario object
39
+ * @returns Total duration including steps and hooks in nanoseconds
40
+ */
41
+ export declare function calculateScenarioDuration(scenario: GherkinScenario): number;
42
+ /**
43
+ * Format timestamp to human-readable string
44
+ * @param timestamp - Unix timestamp (number) or ISO date string
45
+ * @returns Formatted date string or 'N/A' if no timestamp provided
46
+ */
47
+ export declare function formatTimestamp(timestamp?: number | string): string;
48
+ /**
49
+ * Get status icon character for display
50
+ * @param status - Test status
51
+ * @returns Unicode character representing the status (✓, ✗, ⊘, ⧗, ?, ≈)
52
+ */
53
+ export declare function getStatusIcon(status: 'passed' | 'failed' | 'skipped' | 'pending' | 'undefined' | 'ambiguous'): string;
54
+ /**
55
+ * Get CSS class name for status styling
56
+ * @param status - Test status
57
+ * @returns CSS class name (e.g., 'status-passed', 'status-failed')
58
+ */
59
+ export declare function getStatusClass(status: 'passed' | 'failed' | 'skipped' | 'pending' | 'undefined' | 'ambiguous'): string;
60
+ /**
61
+ * Sanitize HTML by escaping special characters
62
+ * @param text - Raw text that may contain HTML characters
63
+ * @returns Escaped text safe for HTML rendering
64
+ */
65
+ export declare function sanitizeHtml(text: string): string;
66
+ /**
67
+ * Generate unique ID with timestamp and random component
68
+ * @param prefix - Prefix for the ID
69
+ * @returns Unique ID string (e.g., 'feature-1234567890-abc123xyz')
70
+ */
71
+ export declare function generateId(prefix: string): string;
72
+ /**
73
+ * Parse tags from Gherkin tags, removing @ prefix
74
+ * @param tags - Array of Gherkin tag objects
75
+ * @returns Array of tag names without @ prefix
76
+ */
77
+ export declare function parseTags(tags?: {
78
+ name: string;
79
+ }[]): string[];
80
+ /**
81
+ * Group features by their directory path
82
+ * @param features - Array of Gherkin features
83
+ * @returns Map of directory paths to features in that directory
84
+ */
85
+ export declare function groupFeaturesByDirectory(features: GherkinFeature[]): Map<string, GherkinFeature[]>;
86
+ /**
87
+ * Calculate pass rate percentage with 2 decimal precision
88
+ * @param passed - Number of passed tests
89
+ * @param total - Total number of tests
90
+ * @returns Pass rate as percentage (0-100) with 2 decimal places
91
+ */
92
+ export declare function calculatePassRate(passed: number, total: number): number;
93
+ /**
94
+ * Extract error message from step result (first line only)
95
+ * @param result - Step result object
96
+ * @returns First line of error message, or empty string if no error
97
+ */
98
+ export declare function extractErrorMessage(result: StepResult): string;
99
+ /**
100
+ * Get file name from URI or path
101
+ * @param uri - File URI or path
102
+ * @returns File name (last part of the path)
103
+ */
104
+ export declare function getFileName(uri: string): string;
105
+ /**
106
+ * Convert markdown to HTML (basic conversion)
107
+ * Supports headers (#, ##, ###), bold (**text**), italic (*text*), and line breaks
108
+ * @param markdown - Markdown formatted text
109
+ * @returns HTML string
110
+ */
111
+ export declare function markdownToHtml(markdown: string): string;
112
+ //# sourceMappingURL=utils.d.ts.map
package/dist/utils.js ADDED
@@ -0,0 +1,250 @@
1
+ /**
2
+ * Utility functions for DRESSING reporter
3
+ */
4
+ /**
5
+ * Calculate duration in a human-readable format
6
+ * @param nanoseconds - Duration in nanoseconds (or milliseconds if inMS is true)
7
+ * @param inMS - If true, treats input as milliseconds instead of nanoseconds
8
+ * @returns Formatted duration string (e.g., "1.50s", "2m 30s")
9
+ */
10
+ export function formatDuration(nanoseconds, inMS = false) {
11
+ const milliseconds = inMS ? nanoseconds : nanoseconds / 1000000;
12
+ if (milliseconds < 1000) {
13
+ return `${Math.round(milliseconds)}ms`;
14
+ }
15
+ const seconds = milliseconds / 1000;
16
+ if (seconds < 60) {
17
+ return `${seconds.toFixed(2)}s`;
18
+ }
19
+ const minutes = Math.floor(seconds / 60);
20
+ const remainingSeconds = seconds % 60;
21
+ return `${minutes}m ${remainingSeconds.toFixed(0)}s`;
22
+ }
23
+ /**
24
+ * Get the worst status from a list of statuses (failed > ambiguous > undefined > pending > skipped > passed)
25
+ * @param statuses - Array of status strings
26
+ * @returns The worst status from the hierarchy
27
+ */
28
+ export function getWorstStatus(statuses) {
29
+ if (statuses.includes('failed')) {
30
+ return 'failed';
31
+ }
32
+ if (statuses.includes('ambiguous')) {
33
+ return 'ambiguous';
34
+ }
35
+ if (statuses.includes('undefined')) {
36
+ return 'undefined';
37
+ }
38
+ if (statuses.includes('pending')) {
39
+ return 'pending';
40
+ }
41
+ if (statuses.includes('skipped')) {
42
+ return 'skipped';
43
+ }
44
+ return 'passed';
45
+ }
46
+ /**
47
+ * Calculate scenario status based on steps
48
+ * @param steps - Array of Gherkin steps
49
+ * @returns The worst status among all steps, or 'pending' if no steps
50
+ */
51
+ export function calculateScenarioStatus(steps) {
52
+ if (steps.length === 0) {
53
+ return 'pending';
54
+ }
55
+ const statuses = steps.map(step => step.result.status);
56
+ return getWorstStatus(statuses);
57
+ }
58
+ /**
59
+ * Calculate feature status based on scenarios
60
+ * @param scenarios - Array of Gherkin scenarios
61
+ * @returns 'failed' if any scenario failed, 'skipped' if all skipped, otherwise 'passed'
62
+ */
63
+ export function calculateFeatureStatus(scenarios) {
64
+ if (scenarios.length === 0) {
65
+ return 'pending';
66
+ }
67
+ const hasFailedScenario = scenarios.some(scenario => {
68
+ const scenarioStatus = calculateScenarioStatus(scenario.steps);
69
+ return scenarioStatus === 'failed' || scenarioStatus === 'undefined';
70
+ });
71
+ if (hasFailedScenario) {
72
+ return 'failed';
73
+ }
74
+ const allSkipped = scenarios.every(scenario => {
75
+ const scenarioStatus = calculateScenarioStatus(scenario.steps);
76
+ return scenarioStatus === 'skipped';
77
+ });
78
+ if (allSkipped) {
79
+ return 'skipped';
80
+ }
81
+ return 'passed';
82
+ }
83
+ /**
84
+ * Calculate total duration from steps
85
+ * @param steps - Array of Gherkin steps
86
+ * @returns Total duration in nanoseconds
87
+ */
88
+ export function calculateDuration(steps) {
89
+ return steps.reduce((total, step) => {
90
+ return total + (step.result.duration ?? 0);
91
+ }, 0);
92
+ }
93
+ /**
94
+ * Calculate scenario duration including hooks (before/after)
95
+ * @param scenario - Gherkin scenario object
96
+ * @returns Total duration including steps and hooks in nanoseconds
97
+ */
98
+ export function calculateScenarioDuration(scenario) {
99
+ const stepsDuration = calculateDuration(scenario.steps);
100
+ const beforeDuration = scenario.before
101
+ ? scenario.before.reduce((sum, hook) => sum + (hook.result.duration ?? 0), 0)
102
+ : 0;
103
+ const afterDuration = scenario.after
104
+ ? scenario.after.reduce((sum, hook) => sum + (hook.result.duration ?? 0), 0)
105
+ : 0;
106
+ return stepsDuration + beforeDuration + afterDuration;
107
+ }
108
+ /**
109
+ * Format timestamp to human-readable string
110
+ * @param timestamp - Unix timestamp (number) or ISO date string
111
+ * @returns Formatted date string or 'N/A' if no timestamp provided
112
+ */
113
+ export function formatTimestamp(timestamp) {
114
+ if (!timestamp) {
115
+ return 'N/A';
116
+ }
117
+ const date = typeof timestamp === 'string' ? new Date(timestamp) : new Date(timestamp);
118
+ return date.toLocaleString('en-US', {
119
+ year: 'numeric',
120
+ month: 'short',
121
+ day: 'numeric',
122
+ hour: '2-digit',
123
+ minute: '2-digit',
124
+ second: '2-digit',
125
+ });
126
+ }
127
+ /**
128
+ * Get status icon character for display
129
+ * @param status - Test status
130
+ * @returns Unicode character representing the status (✓, ✗, ⊘, ⧗, ?, ≈)
131
+ */
132
+ export function getStatusIcon(status) {
133
+ const icons = {
134
+ passed: '✓',
135
+ failed: '✗',
136
+ skipped: '⊘',
137
+ pending: '⧗',
138
+ undefined: '?',
139
+ ambiguous: '≈',
140
+ };
141
+ return icons[status] || '?';
142
+ }
143
+ /**
144
+ * Get CSS class name for status styling
145
+ * @param status - Test status
146
+ * @returns CSS class name (e.g., 'status-passed', 'status-failed')
147
+ */
148
+ export function getStatusClass(status) {
149
+ return `status-${status}`;
150
+ }
151
+ /**
152
+ * Sanitize HTML by escaping special characters
153
+ * @param text - Raw text that may contain HTML characters
154
+ * @returns Escaped text safe for HTML rendering
155
+ */
156
+ export function sanitizeHtml(text) {
157
+ const map = {
158
+ '&': '&amp;',
159
+ '<': '&lt;',
160
+ '>': '&gt;',
161
+ '"': '&quot;',
162
+ "'": '&#039;',
163
+ };
164
+ return text.replace(/[&<>"']/g, m => map[m]);
165
+ }
166
+ /**
167
+ * Generate unique ID with timestamp and random component
168
+ * @param prefix - Prefix for the ID
169
+ * @returns Unique ID string (e.g., 'feature-1234567890-abc123xyz')
170
+ */
171
+ export function generateId(prefix) {
172
+ return `${prefix}-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`;
173
+ }
174
+ /**
175
+ * Parse tags from Gherkin tags, removing @ prefix
176
+ * @param tags - Array of Gherkin tag objects
177
+ * @returns Array of tag names without @ prefix
178
+ */
179
+ export function parseTags(tags) {
180
+ if (!tags || tags.length === 0) {
181
+ return [];
182
+ }
183
+ return tags.map(tag => tag.name.replace('@', ''));
184
+ }
185
+ /**
186
+ * Group features by their directory path
187
+ * @param features - Array of Gherkin features
188
+ * @returns Map of directory paths to features in that directory
189
+ */
190
+ export function groupFeaturesByDirectory(features) {
191
+ const grouped = new Map();
192
+ features.forEach(feature => {
193
+ const dir = feature.uri.split('/').slice(0, -1).join('/') || 'root';
194
+ if (!grouped.has(dir)) {
195
+ grouped.set(dir, []);
196
+ }
197
+ grouped.get(dir).push(feature);
198
+ });
199
+ return grouped;
200
+ }
201
+ /**
202
+ * Calculate pass rate percentage with 2 decimal precision
203
+ * @param passed - Number of passed tests
204
+ * @param total - Total number of tests
205
+ * @returns Pass rate as percentage (0-100) with 2 decimal places
206
+ */
207
+ export function calculatePassRate(passed, total) {
208
+ if (total === 0) {
209
+ return 0;
210
+ }
211
+ return Math.round((passed / total) * 100 * 100) / 100;
212
+ }
213
+ /**
214
+ * Extract error message from step result (first line only)
215
+ * @param result - Step result object
216
+ * @returns First line of error message, or empty string if no error
217
+ */
218
+ export function extractErrorMessage(result) {
219
+ if (!result.error_message) {
220
+ return '';
221
+ }
222
+ // Try to extract just the meaningful part of the error
223
+ const lines = result.error_message.split('\n');
224
+ return lines[0] || result.error_message;
225
+ }
226
+ /**
227
+ * Get file name from URI or path
228
+ * @param uri - File URI or path
229
+ * @returns File name (last part of the path)
230
+ */
231
+ export function getFileName(uri) {
232
+ const parts = uri.split('/');
233
+ return parts[parts.length - 1];
234
+ }
235
+ /**
236
+ * Convert markdown to HTML (basic conversion)
237
+ * Supports headers (#, ##, ###), bold (**text**), italic (*text*), and line breaks
238
+ * @param markdown - Markdown formatted text
239
+ * @returns HTML string
240
+ */
241
+ export function markdownToHtml(markdown) {
242
+ return markdown
243
+ .replace(/^### (.*$)/gim, '<h3>$1</h3>')
244
+ .replace(/^## (.*$)/gim, '<h2>$1</h2>')
245
+ .replace(/^# (.*$)/gim, '<h1>$1</h1>')
246
+ .replace(/\*\*(.*)\*\*/gim, '<strong>$1</strong>')
247
+ .replace(/\*(.*)\*/gim, '<em>$1</em>')
248
+ .replace(/\n/gim, '<br>');
249
+ }
250
+ //# sourceMappingURL=utils.js.map
package/package.json ADDED
@@ -0,0 +1,92 @@
1
+ {
2
+ "name": "cucumber-dressing",
3
+ "version": "0.1.0",
4
+ "type": "module",
5
+ "description": "Detailed Report of Executed Scenarios, Steps and INsights for Gherkin - A comprehensive test automation reporter for Gherkin-based test results",
6
+ "main": "dist/index.js",
7
+ "types": "dist/index.d.ts",
8
+ "bin": {
9
+ "dressing": "dist/cli.js"
10
+ },
11
+ "scripts": {
12
+ "build": "tsc",
13
+ "build:watch": "tsc --watch",
14
+ "test": "cross-env NODE_OPTIONS=--experimental-vm-modules jest",
15
+ "test:watch": "cross-env NODE_OPTIONS=--experimental-vm-modules jest --watch",
16
+ "test:coverage": "cross-env NODE_OPTIONS=--experimental-vm-modules jest --coverage",
17
+ "lint": "eslint .",
18
+ "lint:fix": "eslint . --fix",
19
+ "format": "prettier --write \"src/**/*.ts\"",
20
+ "prepublishOnly": "npm run lint && npm run build && npm test",
21
+ "preversion": "npm run lint && npm test",
22
+ "version": "npm run format && git add -A src",
23
+ "postversion": "git push && git push --tags",
24
+ "example": "node dist/example.js",
25
+ "clean": "rm -rf dist coverage"
26
+ },
27
+ "keywords": [
28
+ "cucumber",
29
+ "gherkin",
30
+ "test",
31
+ "reporter",
32
+ "html",
33
+ "report",
34
+ "automation",
35
+ "testing",
36
+ "bdd",
37
+ "behavior-driven-development",
38
+ "test-results",
39
+ "json-to-html",
40
+ "test-automation",
41
+ "test-reporting",
42
+ "cucumber-report",
43
+ "html-reporter",
44
+ "test-report-generator"
45
+ ],
46
+ "author": "Jed Aureus Gonzales",
47
+ "license": "MIT",
48
+ "repository": {
49
+ "type": "git",
50
+ "url": "https://github.com/jedau/cucumber-dressing.git"
51
+ },
52
+ "bugs": {
53
+ "url": "https://github.com/jedau/cucumber-dressing/issues"
54
+ },
55
+ "homepage": "https://github.com/jedau/cucumber-dressing#readme",
56
+ "files": [
57
+ "dist",
58
+ "!dist/**/*.map",
59
+ "!dist/__tests__",
60
+ "!dist/example.*",
61
+ "README.md",
62
+ "LICENSE",
63
+ "CHANGELOG.md"
64
+ ],
65
+ "engines": {
66
+ "node": ">=18.0.0",
67
+ "npm": ">=8.0.0"
68
+ },
69
+ "devDependencies": {
70
+ "@eslint/js": "^9.0.0",
71
+ "@types/fs-extra": "^11.0.0",
72
+ "@types/jest": "^29.5.0",
73
+ "@types/node": "^20.0.0",
74
+ "cross-env": "^10.1.0",
75
+ "eslint": "^9.0.0",
76
+ "eslint-config-prettier": "^9.0.0",
77
+ "jest": "^29.5.0",
78
+ "prettier": "^3.0.0",
79
+ "ts-jest": "^29.1.0",
80
+ "typescript": "^5.0.0",
81
+ "typescript-eslint": "^8.0.0"
82
+ },
83
+ "dependencies": {
84
+ "chart.js": "^4.4.0",
85
+ "commander": "^11.0.0",
86
+ "fs-extra": "^11.1.0",
87
+ "glob": "^10.3.0",
88
+ "handlebars": "^4.7.8",
89
+ "markdown-it": "^14.0.0",
90
+ "open": "^10.0.0"
91
+ }
92
+ }