@lage-run/reporters 0.2.39 → 0.2.41

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/CHANGELOG.json CHANGED
@@ -2,7 +2,43 @@
2
2
  "name": "@lage-run/reporters",
3
3
  "entries": [
4
4
  {
5
- "date": "Fri, 11 Nov 2022 07:29:32 GMT",
5
+ "date": "Tue, 06 Dec 2022 00:47:50 GMT",
6
+ "tag": "@lage-run/reporters_v0.2.41",
7
+ "version": "0.2.41",
8
+ "comments": {
9
+ "patch": [
10
+ {
11
+ "author": "kchau@microsoft.com",
12
+ "package": "@lage-run/reporters",
13
+ "commit": "3fa4b9c8aa67cbc2198385e32fa38de123541f09",
14
+ "comment": "creating the beginnings of a progress reporter - it's in beta"
15
+ },
16
+ {
17
+ "author": "beachball",
18
+ "package": "@lage-run/reporters",
19
+ "comment": "Bump @lage-run/scheduler-types to v0.2.10",
20
+ "commit": "3fa4b9c8aa67cbc2198385e32fa38de123541f09"
21
+ }
22
+ ]
23
+ }
24
+ },
25
+ {
26
+ "date": "Mon, 21 Nov 2022 06:32:03 GMT",
27
+ "tag": "@lage-run/reporters_v0.2.40",
28
+ "version": "0.2.40",
29
+ "comments": {
30
+ "patch": [
31
+ {
32
+ "author": "beachball",
33
+ "package": "@lage-run/reporters",
34
+ "comment": "Bump @lage-run/scheduler-types to v0.2.9",
35
+ "commit": "aafe75c34b61ed10f11c829a7bb1f5ad86f0b810"
36
+ }
37
+ ]
38
+ }
39
+ },
40
+ {
41
+ "date": "Fri, 11 Nov 2022 07:29:47 GMT",
6
42
  "tag": "@lage-run/reporters_v0.2.39",
7
43
  "version": "0.2.39",
8
44
  "comments": {
package/CHANGELOG.md CHANGED
@@ -1,12 +1,29 @@
1
1
  # Change Log - @lage-run/reporters
2
2
 
3
- This log was last generated on Fri, 11 Nov 2022 07:29:32 GMT and should not be manually modified.
3
+ This log was last generated on Tue, 06 Dec 2022 00:47:50 GMT and should not be manually modified.
4
4
 
5
5
  <!-- Start content -->
6
6
 
7
+ ## 0.2.41
8
+
9
+ Tue, 06 Dec 2022 00:47:50 GMT
10
+
11
+ ### Patches
12
+
13
+ - creating the beginnings of a progress reporter - it's in beta (kchau@microsoft.com)
14
+ - Bump @lage-run/scheduler-types to v0.2.10
15
+
16
+ ## 0.2.40
17
+
18
+ Mon, 21 Nov 2022 06:32:03 GMT
19
+
20
+ ### Patches
21
+
22
+ - Bump @lage-run/scheduler-types to v0.2.9
23
+
7
24
  ## 0.2.39
8
25
 
9
- Fri, 11 Nov 2022 07:29:32 GMT
26
+ Fri, 11 Nov 2022 07:29:47 GMT
10
27
 
11
28
  ### Patches
12
29
 
@@ -1,7 +1,6 @@
1
1
  /// <reference types="node" />
2
- import type { LogEntry, Reporter } from "@lage-run/logger";
2
+ import type { Reporter } from "@lage-run/logger";
3
3
  import type { SchedulerRunSummary, TargetRun } from "@lage-run/scheduler-types";
4
- import type { TargetMessageEntry, TargetStatusEntry } from "./types/TargetLogEntry.js";
5
4
  import type { Writable } from "stream";
6
5
  export interface ChromeTraceEventsReporterOptions {
7
6
  outputFile?: string;
@@ -17,6 +16,6 @@ export declare class ChromeTraceEventsReporter implements Reporter {
17
16
  private events;
18
17
  private outputFile;
19
18
  constructor(options: ChromeTraceEventsReporterOptions);
20
- log(entry: LogEntry<TargetStatusEntry | TargetMessageEntry>): void;
19
+ log(): void;
21
20
  summarize(schedulerRunSummary: SchedulerRunSummary): void;
22
21
  }
@@ -6,8 +6,6 @@ Object.defineProperty(exports, "ChromeTraceEventsReporter", {
6
6
  enumerable: true,
7
7
  get: ()=>ChromeTraceEventsReporter
8
8
  });
9
- const _targetGraph = require("@lage-run/target-graph");
10
- const _isTargetStatusLogEntryJs = require("./isTargetStatusLogEntry.js");
11
9
  const _chalk = /*#__PURE__*/ _interopRequireDefault(require("chalk"));
12
10
  const _fs = /*#__PURE__*/ _interopRequireDefault(require("fs"));
13
11
  const _path = /*#__PURE__*/ _interopRequireDefault(require("path"));
@@ -29,42 +27,33 @@ function getTimeBasedFilename(prefix) {
29
27
  return `${prefix ? prefix + "-" : ""}${datetimeNormalized}.json`;
30
28
  }
31
29
  class ChromeTraceEventsReporter {
32
- log(entry) {
33
- const data = entry.data;
34
- if ((0, _isTargetStatusLogEntryJs.isTargetStatusLogEntry)(data) && data.status !== "pending" && data.status !== "queued" && data.target.id !== (0, _targetGraph.getStartTargetId)()) {
35
- if (data.status === "running") {
36
- const threadId = this.threads.shift() ?? 0;
37
- this.targetIdThreadMap.set(data.target.id, threadId);
38
- } else {
39
- const threadId1 = this.targetIdThreadMap.get(data.target.id);
40
- this.events.traceEvents.push({
41
- name: data.target.id,
42
- cat: "",
43
- ph: "X",
44
- ts: 0,
45
- dur: hrTimeToMicroseconds(data.duration ?? [
46
- 0,
47
- 1000
48
- ]),
49
- pid: 1,
50
- tid: threadId1 ?? 0
51
- });
52
- this.threads.unshift(threadId1);
53
- this.threads.sort((a, b)=>a - b);
54
- }
55
- }
30
+ log() {
31
+ // pass
56
32
  }
57
33
  summarize(schedulerRunSummary) {
58
34
  const { targetRuns , startTime } = schedulerRunSummary;
59
35
  // categorize events
60
36
  const { categorize } = this.options;
61
- for (const event of this.events.traceEvents){
62
- const targetRun = targetRuns.get(event.name);
63
- event.ts = hrTimeToMicroseconds(targetRun.startTime) - hrTimeToMicroseconds(startTime);
64
- event.cat = targetRun?.status ?? "";
37
+ for (const targetRun of targetRuns.values()){
38
+ if (targetRun.target.hidden) {
39
+ continue;
40
+ }
41
+ const event = {
42
+ name: targetRun.target.id,
43
+ cat: targetRun.status,
44
+ ph: "X",
45
+ ts: hrTimeToMicroseconds(targetRun.startTime) - hrTimeToMicroseconds(startTime),
46
+ dur: hrTimeToMicroseconds(targetRun.duration ?? [
47
+ 0,
48
+ 1000
49
+ ]),
50
+ pid: 1,
51
+ tid: targetRun.threadId ?? 0
52
+ };
65
53
  if (categorize) {
66
54
  event.cat += `,${categorize(targetRun)}`;
67
55
  }
56
+ this.events.traceEvents.push(event);
68
57
  }
69
58
  // write events to stream
70
59
  this.logStream.write(JSON.stringify(this.events, null, 2));
@@ -108,7 +108,9 @@ class LogReporter {
108
108
  return this.logTargetEntry(entry);
109
109
  }
110
110
  // log generic entries (not related to target)
111
- return this.print(entry.msg);
111
+ if (entry.msg) {
112
+ return this.print(entry.msg);
113
+ }
112
114
  }
113
115
  printEntry(entry, message) {
114
116
  let prefix = "";
@@ -0,0 +1,13 @@
1
+ /// <reference types="node" />
2
+ import type { LogEntry, Reporter } from "@lage-run/logger";
3
+ import type { SchedulerRunSummary } from "@lage-run/scheduler-types";
4
+ import EventEmitter from "events";
5
+ export declare class ProgressReporter implements Reporter {
6
+ logEvent: EventEmitter;
7
+ logEntries: Map<string, LogEntry<import("@lage-run/logger").LogStructuredData>[]>;
8
+ constructor(options?: {
9
+ concurrency: number;
10
+ });
11
+ log(entry: LogEntry<any>): void;
12
+ summarize(schedulerRunSummary: SchedulerRunSummary): void;
13
+ }
@@ -0,0 +1,221 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", {
3
+ value: true
4
+ });
5
+ Object.defineProperty(exports, "ProgressReporter", {
6
+ enumerable: true,
7
+ get: ()=>ProgressReporter
8
+ });
9
+ const _react = /*#__PURE__*/ _interopRequireWildcard(require("react"));
10
+ const _ink = require("ink");
11
+ const _events = /*#__PURE__*/ _interopRequireDefault(require("events"));
12
+ const _formatHrtime = require("@lage-run/format-hrtime");
13
+ function _interopRequireDefault(obj) {
14
+ return obj && obj.__esModule ? obj : {
15
+ default: obj
16
+ };
17
+ }
18
+ function _getRequireWildcardCache(nodeInterop) {
19
+ if (typeof WeakMap !== "function") return null;
20
+ var cacheBabelInterop = new WeakMap();
21
+ var cacheNodeInterop = new WeakMap();
22
+ return (_getRequireWildcardCache = function(nodeInterop) {
23
+ return nodeInterop ? cacheNodeInterop : cacheBabelInterop;
24
+ })(nodeInterop);
25
+ }
26
+ function _interopRequireWildcard(obj, nodeInterop) {
27
+ if (!nodeInterop && obj && obj.__esModule) {
28
+ return obj;
29
+ }
30
+ if (obj === null || typeof obj !== "object" && typeof obj !== "function") {
31
+ return {
32
+ default: obj
33
+ };
34
+ }
35
+ var cache = _getRequireWildcardCache(nodeInterop);
36
+ if (cache && cache.has(obj)) {
37
+ return cache.get(obj);
38
+ }
39
+ var newObj = {};
40
+ var hasPropertyDescriptor = Object.defineProperty && Object.getOwnPropertyDescriptor;
41
+ for(var key in obj){
42
+ if (key !== "default" && Object.prototype.hasOwnProperty.call(obj, key)) {
43
+ var desc = hasPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : null;
44
+ if (desc && (desc.get || desc.set)) {
45
+ Object.defineProperty(newObj, key, desc);
46
+ } else {
47
+ newObj[key] = obj[key];
48
+ }
49
+ }
50
+ }
51
+ newObj.default = obj;
52
+ if (cache) {
53
+ cache.set(obj, newObj);
54
+ }
55
+ return newObj;
56
+ }
57
+ function ProgressStatus(props) {
58
+ const { waiting , completed , total } = props.progress;
59
+ const percentage = total > 0 ? `${(completed / total * 100).toFixed(2)}%` : "0%";
60
+ const status = `Waiting: ${waiting} | Completed: ${completed} | Total: ${total} | ${percentage}`;
61
+ return /*#__PURE__*/ _react.createElement(_ink.Box, null, /*#__PURE__*/ _react.createElement(_ink.Text, null, status));
62
+ }
63
+ function ThreadItem(props) {
64
+ const { targetId } = props;
65
+ return /*#__PURE__*/ _react.createElement(_ink.Box, null, targetId ? /*#__PURE__*/ _react.createElement(_ink.Text, {
66
+ color: "whiteBright"
67
+ }, "[ ", targetId, " ]") : /*#__PURE__*/ _react.createElement(_ink.Text, {
68
+ color: "gray"
69
+ }, "[ IDLE ]"));
70
+ }
71
+ function SummaryInfo(props) {
72
+ const { summary } = props;
73
+ const { schedulerRunSummary , logEntries } = summary;
74
+ const { targetRunByStatus , targetRuns , duration } = schedulerRunSummary;
75
+ const slowestTargetRuns = [
76
+ ...targetRuns.values()
77
+ ].sort((a, b)=>parseFloat((0, _formatHrtime.hrToSeconds)((0, _formatHrtime.hrtimeDiff)(a.duration, b.duration))));
78
+ const { failed , aborted , skipped , success , pending } = targetRunByStatus;
79
+ const errors = failed && failed.length > 0 ? new Map(failed.map((targetId)=>[
80
+ targetId,
81
+ logEntries.get(targetId) || []
82
+ ])) : new Map();
83
+ return /*#__PURE__*/ _react.createElement(_ink.Box, {
84
+ flexDirection: "column"
85
+ }, /*#__PURE__*/ _react.createElement(_ink.Text, {
86
+ color: "greenBright"
87
+ }, "Summary"), /*#__PURE__*/ _react.createElement(_ink.Newline, null), /*#__PURE__*/ _react.createElement(_ink.Text, {
88
+ color: "yellow"
89
+ }, "Slowest targets"), /*#__PURE__*/ _react.createElement(_ink.Box, {
90
+ flexDirection: "column",
91
+ marginLeft: 2,
92
+ marginY: 1
93
+ }, slowestTargetRuns.slice(0, 10).filter((run)=>!run.target.hidden).map((targetRun)=>/*#__PURE__*/ _react.createElement(_ink.Text, {
94
+ key: targetRun.target.id
95
+ }, targetRun.target.id, " - ", (0, _formatHrtime.formatDuration)((0, _formatHrtime.hrToSeconds)(targetRun.duration))))), errors.size > 0 ? /*#__PURE__*/ _react.createElement(ErrorMessages, {
96
+ errors: errors
97
+ }) : null, /*#__PURE__*/ _react.createElement(_ink.Text, null, `success: ${success.length}, skipped: ${skipped.length}, pending: ${pending.length}, aborted: ${aborted.length}, failed: ${failed.length}`), /*#__PURE__*/ _react.createElement(_ink.Text, null, "Took a total of ", (0, _formatHrtime.formatDuration)((0, _formatHrtime.hrToSeconds)(duration)), " to complete."));
98
+ }
99
+ function ErrorMessages(props) {
100
+ const { errors } = props;
101
+ return /*#__PURE__*/ _react.createElement(_ink.Box, {
102
+ flexDirection: "column"
103
+ }, /*#__PURE__*/ _react.createElement(_ink.Text, {
104
+ color: "redBright"
105
+ }, "Errors"), /*#__PURE__*/ _react.createElement(_ink.Box, {
106
+ flexDirection: "column",
107
+ marginLeft: 2,
108
+ marginY: 1
109
+ }, [
110
+ ...errors.entries()
111
+ ].map(([targetId, logs])=>/*#__PURE__*/ _react.createElement(_ink.Box, {
112
+ flexDirection: "column",
113
+ key: `errorlogs-${targetId}`,
114
+ marginBottom: 1
115
+ }, /*#__PURE__*/ _react.createElement(_ink.Text, {
116
+ color: "cyanBright",
117
+ underline: true,
118
+ bold: true
119
+ }, targetId), /*#__PURE__*/ _react.createElement(_ink.Text, null, logs.map((entry)=>entry.msg).join("\n"))))));
120
+ }
121
+ function ReporterApp(props) {
122
+ const [threadInfo, setThreadInfo] = _react.useState({});
123
+ const [progress, setProgress] = _react.useState({
124
+ waiting: 0,
125
+ completed: 0,
126
+ total: 0
127
+ });
128
+ const [summary, setSummary] = _react.useState();
129
+ const { logEvent } = props;
130
+ _react.useEffect(()=>{
131
+ logEvent.on("status", (entry)=>{
132
+ const { target , threadId , status } = entry.data;
133
+ if (status && status === "running") {
134
+ setThreadInfo((threadInfo)=>({
135
+ ...threadInfo,
136
+ [threadId]: target.id
137
+ }));
138
+ } else if (status === "success" || status === "aborted" || status === "failed") {
139
+ setThreadInfo((threadInfo)=>{
140
+ const newThreadInfo = {
141
+ ...threadInfo
142
+ };
143
+ newThreadInfo[threadId] = "";
144
+ return newThreadInfo;
145
+ });
146
+ }
147
+ });
148
+ logEvent.on("progress", (progress)=>{
149
+ setProgress(progress);
150
+ });
151
+ logEvent.on("summary", (summary)=>{
152
+ setSummary(summary);
153
+ });
154
+ }, [
155
+ logEvent
156
+ ]);
157
+ const idleWorkerDummyThreadInfo = new Array(props.concurrency - Object.keys(threadInfo).length).fill(0);
158
+ return /*#__PURE__*/ _react.createElement(_ink.Box, {
159
+ flexDirection: "column"
160
+ }, /*#__PURE__*/ _react.createElement(_ink.Text, null, "Lage running tasks"), /*#__PURE__*/ _react.createElement(_ink.Text, {
161
+ color: "yellow"
162
+ }, "[warning: this progress reporter is currently in beta and unstable]"), summary ? /*#__PURE__*/ _react.createElement(SummaryInfo, {
163
+ summary: summary
164
+ }) : /*#__PURE__*/ _react.createElement(_ink.Box, {
165
+ flexDirection: "column"
166
+ }, /*#__PURE__*/ _react.createElement(_ink.Box, {
167
+ flexDirection: "column",
168
+ marginLeft: 2,
169
+ marginY: 1
170
+ }, Object.entries(threadInfo).map(([threadId, targetId])=>{
171
+ return /*#__PURE__*/ _react.createElement(ThreadItem, {
172
+ key: threadId,
173
+ targetId: targetId
174
+ });
175
+ }), idleWorkerDummyThreadInfo.map((_, index)=>{
176
+ return /*#__PURE__*/ _react.createElement(_react.Fragment, {
177
+ key: `idle-${index}`
178
+ }, /*#__PURE__*/ _react.createElement(ThreadItem, {
179
+ targetId: ""
180
+ }));
181
+ })), /*#__PURE__*/ _react.createElement(ProgressStatus, {
182
+ progress: progress
183
+ })));
184
+ }
185
+ class ProgressReporter {
186
+ log(entry) {
187
+ // save the logs for errors
188
+ if (entry.data?.target?.id) {
189
+ if (!this.logEntries.has(entry.data.target.id)) {
190
+ this.logEntries.set(entry.data.target.id, []);
191
+ }
192
+ this.logEntries.get(entry.data.target.id).push(entry);
193
+ }
194
+ // if "hidden", do not even attempt to record or report the entry
195
+ if (entry?.data?.target?.hidden) {
196
+ return;
197
+ }
198
+ if (entry.data && entry.data.target && typeof entry.data.threadId !== "undefined") {
199
+ this.logEvent.emit("status", entry);
200
+ }
201
+ if (entry.data && entry.data.progress) {
202
+ this.logEvent.emit("progress", entry.data.progress);
203
+ }
204
+ }
205
+ summarize(schedulerRunSummary) {
206
+ this.logEvent.emit("summary", {
207
+ schedulerRunSummary,
208
+ logEntries: this.logEntries
209
+ });
210
+ }
211
+ constructor(options = {
212
+ concurrency: 0
213
+ }){
214
+ this.logEvent = new _events.default();
215
+ this.logEntries = new Map();
216
+ (0, _ink.render)(/*#__PURE__*/ _react.createElement(ReporterApp, {
217
+ logEvent: this.logEvent,
218
+ concurrency: options.concurrency
219
+ }));
220
+ }
221
+ }
@@ -2,9 +2,12 @@ import { LogLevel } from "@lage-run/logger";
2
2
  import { JsonReporter } from "./JsonReporter.js";
3
3
  import { AdoReporter } from "./AdoReporter.js";
4
4
  import { LogReporter } from "./LogReporter.js";
5
- export declare function createReporter({ reporter, grouped, verbose, logLevel, }: {
5
+ import { ProgressReporter } from "./ProgressReporter.js";
6
+ export declare function createReporter({ reporter, progress, grouped, concurrency, verbose, logLevel, }: {
6
7
  reporter: string;
8
+ progress?: boolean;
7
9
  grouped?: boolean;
10
+ concurrency?: number;
8
11
  verbose?: boolean;
9
12
  logLevel?: LogLevel;
10
- }): AdoReporter | JsonReporter | LogReporter;
13
+ }): AdoReporter | JsonReporter | LogReporter | ProgressReporter;
@@ -10,7 +10,8 @@ const _logger = require("@lage-run/logger");
10
10
  const _jsonReporterJs = require("./JsonReporter.js");
11
11
  const _adoReporterJs = require("./AdoReporter.js");
12
12
  const _logReporterJs = require("./LogReporter.js");
13
- function createReporter({ reporter ="npmLog" , grouped =false , verbose =false , logLevel =_logger.LogLevel.info }) {
13
+ const _progressReporterJs = require("./ProgressReporter.js");
14
+ function createReporter({ reporter ="npmLog" , progress =false , grouped =false , concurrency =0 , verbose =false , logLevel =_logger.LogLevel.info }) {
14
15
  switch(reporter){
15
16
  case "json":
16
17
  return new _jsonReporterJs.JsonReporter({
@@ -23,7 +24,9 @@ function createReporter({ reporter ="npmLog" , grouped =false , verbose =false ,
23
24
  logLevel: verbose ? _logger.LogLevel.verbose : logLevel
24
25
  });
25
26
  default:
26
- return new _logReporterJs.LogReporter({
27
+ return progress ? new _progressReporterJs.ProgressReporter({
28
+ concurrency
29
+ }) : new _logReporterJs.LogReporter({
27
30
  grouped,
28
31
  logLevel: verbose ? _logger.LogLevel.verbose : logLevel
29
32
  });
@@ -2,8 +2,10 @@ import { LogLevel } from "@lage-run/logger";
2
2
  import type { Logger } from "@lage-run/logger";
3
3
  export interface ReporterInitOptions {
4
4
  reporter: string[] | string;
5
+ progress: boolean;
5
6
  verbose: boolean;
6
7
  grouped: boolean;
8
+ concurrency: number;
7
9
  logLevel: keyof typeof LogLevel;
8
10
  }
9
11
  export declare function initializeReporters(logger: Logger, options: ReporterInitOptions): import("@lage-run/logger").Reporter<import("@lage-run/logger").LogStructuredData>[];
package/lib/initialize.js CHANGED
@@ -9,14 +9,16 @@ Object.defineProperty(exports, "initializeReporters", {
9
9
  const _logger = require("@lage-run/logger");
10
10
  const _createReporterJs = require("./createReporter.js");
11
11
  function initializeReporters(logger, options) {
12
- const { reporter , verbose , grouped , logLevel } = options;
12
+ const { reporter , verbose , grouped , logLevel , progress , concurrency } = options;
13
13
  const reporterOptions = Array.isArray(reporter) ? reporter : [
14
14
  reporter
15
15
  ];
16
16
  for (const reporter1 of reporterOptions){
17
17
  const reporterInstance = (0, _createReporterJs.createReporter)({
18
18
  verbose,
19
+ progress,
19
20
  grouped,
21
+ concurrency,
20
22
  logLevel: _logger.LogLevel[logLevel],
21
23
  reporter: reporter1
22
24
  });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lage-run/reporters",
3
- "version": "0.2.39",
3
+ "version": "0.2.41",
4
4
  "description": "Log reporters for Lage",
5
5
  "repository": {
6
6
  "url": "https://github.com/microsoft/lage"
@@ -16,14 +16,17 @@
16
16
  },
17
17
  "dependencies": {
18
18
  "@lage-run/logger": "^1.2.2",
19
- "@lage-run/scheduler-types": "^0.2.8",
19
+ "@lage-run/scheduler-types": "^0.2.10",
20
20
  "@lage-run/target-graph": "^0.6.1",
21
21
  "@lage-run/format-hrtime": "^0.1.3",
22
22
  "chalk": "^4.0.0",
23
23
  "ansi-regex": "^5.0.1",
24
- "gradient-string": "^2.0.1"
24
+ "gradient-string": "^2.0.1",
25
+ "ink": "^3.2.0",
26
+ "react": "^18.2.0"
25
27
  },
26
28
  "devDependencies": {
29
+ "@types/react": "18.0.25",
27
30
  "memory-streams": "0.1.3"
28
31
  },
29
32
  "publishConfig": {
package/tsconfig.json CHANGED
@@ -1,7 +1,8 @@
1
1
  {
2
2
  "extends": "../tsconfig.lage2.json",
3
3
  "compilerOptions": {
4
- "outDir": "./lib"
4
+ "outDir": "./lib",
5
+ "jsx": "react"
5
6
  },
6
7
  "include": ["src"]
7
8
  }