@nitpicker/report-google-sheets 0.4.2 → 0.4.3
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/package.json +7 -4
- package/CHANGELOG.md +0 -16
- package/src/__tests__/api/create-sheets.api.ts +0 -234
- package/src/__tests__/api/helpers.ts +0 -148
- package/src/__tests__/api/sheets.api.ts +0 -217
- package/src/archive.ts +0 -29
- package/src/data/add-to-summary.ts +0 -10
- package/src/data/create-discrepancies.ts +0 -81
- package/src/data/create-image-list.ts +0 -74
- package/src/data/create-links.ts +0 -134
- package/src/data/create-page-list.ts +0 -472
- package/src/data/create-referrers-relational-table.ts +0 -115
- package/src/data/create-resources-relational-table.ts +0 -104
- package/src/data/create-resources.ts +0 -51
- package/src/data/create-violations.spec.ts +0 -95
- package/src/data/create-violations.ts +0 -47
- package/src/debug.ts +0 -7
- package/src/index.ts +0 -1
- package/src/load-config.spec.ts +0 -37
- package/src/load-config.ts +0 -17
- package/src/report.ts +0 -231
- package/src/reports/get-plugin-reports.spec.ts +0 -42
- package/src/reports/get-plugin-reports.ts +0 -24
- package/src/sheets/create-cell-data.ts +0 -1
- package/src/sheets/create-sheets.ts +0 -523
- package/src/sheets/default-cell-format.spec.ts +0 -13
- package/src/sheets/default-cell-format.ts +0 -8
- package/src/sheets/format.spec.ts +0 -17
- package/src/sheets/format.ts +0 -14
- package/src/sheets/types.ts +0 -106
- package/src/types.ts +0 -11
- package/src/utils/has-prop-filter.spec.ts +0 -25
- package/src/utils/has-prop-filter.ts +0 -21
- package/src/utils/non-null-filter.spec.ts +0 -27
- package/src/utils/non-null-filter.ts +0 -15
- package/tsconfig.json +0 -11
- package/tsconfig.tsbuildinfo +0 -1
|
@@ -1,51 +0,0 @@
|
|
|
1
|
-
import type { CreateSheet } from '../sheets/types.js';
|
|
2
|
-
|
|
3
|
-
import { reportLog } from '../debug.js';
|
|
4
|
-
import { createCellData } from '../sheets/create-cell-data.js';
|
|
5
|
-
import { defaultCellFormat } from '../sheets/default-cell-format.js';
|
|
6
|
-
|
|
7
|
-
/**
|
|
8
|
-
* Creates the "Resources" sheet configuration.
|
|
9
|
-
*
|
|
10
|
-
* Lists all network resources (CSS, JS, images, fonts, etc.) discovered
|
|
11
|
-
* during crawling, with one row per unique resource URL. Each row includes
|
|
12
|
-
* HTTP status, content type, size, and the number of pages that reference
|
|
13
|
-
* the resource (with URLs listed in the cell note).
|
|
14
|
-
*
|
|
15
|
-
* Uses `eachResource` (Phase 3) to iterate over the archive's resource
|
|
16
|
-
* table rather than page-by-page iteration.
|
|
17
|
-
*/
|
|
18
|
-
export const createResources: CreateSheet = () => {
|
|
19
|
-
return {
|
|
20
|
-
name: 'Resources',
|
|
21
|
-
createHeaders() {
|
|
22
|
-
return [
|
|
23
|
-
'URL',
|
|
24
|
-
'Status Code',
|
|
25
|
-
'Status Text',
|
|
26
|
-
'Content Type',
|
|
27
|
-
'Content Length',
|
|
28
|
-
'Referrers',
|
|
29
|
-
];
|
|
30
|
-
},
|
|
31
|
-
async eachResource(resource) {
|
|
32
|
-
reportLog(`Read: Resource referrers (Search: ${resource.url})`);
|
|
33
|
-
const referrers = await resource.getReferrers();
|
|
34
|
-
const data = [
|
|
35
|
-
createCellData({ value: resource.url }, defaultCellFormat),
|
|
36
|
-
createCellData({ value: resource.status }, defaultCellFormat),
|
|
37
|
-
createCellData({ value: resource.statusText }, defaultCellFormat),
|
|
38
|
-
createCellData({ value: resource.contentType }, defaultCellFormat),
|
|
39
|
-
createCellData({ value: resource.contentLength }, defaultCellFormat),
|
|
40
|
-
createCellData(
|
|
41
|
-
{
|
|
42
|
-
value: `${referrers.length} pages`,
|
|
43
|
-
note: referrers.join('\n'),
|
|
44
|
-
},
|
|
45
|
-
defaultCellFormat,
|
|
46
|
-
),
|
|
47
|
-
];
|
|
48
|
-
return [data];
|
|
49
|
-
},
|
|
50
|
-
};
|
|
51
|
-
};
|
|
@@ -1,95 +0,0 @@
|
|
|
1
|
-
import type { Report } from '@nitpicker/types';
|
|
2
|
-
|
|
3
|
-
import { describe, it, expect } from 'vitest';
|
|
4
|
-
|
|
5
|
-
import { createViolations } from './create-violations.js';
|
|
6
|
-
|
|
7
|
-
describe('createViolations', () => {
|
|
8
|
-
it('returns sheet config with name "Violations"', () => {
|
|
9
|
-
const sheet = createViolations([]);
|
|
10
|
-
expect(sheet.name).toBe('Violations');
|
|
11
|
-
});
|
|
12
|
-
|
|
13
|
-
it('returns correct headers', () => {
|
|
14
|
-
const sheet = createViolations([]);
|
|
15
|
-
const headers = sheet.createHeaders();
|
|
16
|
-
expect(headers).toEqual(['Validator', 'Severity', 'Rule', 'Code', 'Message', 'URL']);
|
|
17
|
-
});
|
|
18
|
-
|
|
19
|
-
it('generates rows from report violations', () => {
|
|
20
|
-
const reports: Report[] = [
|
|
21
|
-
{
|
|
22
|
-
name: 'axe',
|
|
23
|
-
violations: [
|
|
24
|
-
{
|
|
25
|
-
validator: 'axe',
|
|
26
|
-
severity: 'serious',
|
|
27
|
-
rule: 'color-contrast',
|
|
28
|
-
code: 'color-contrast',
|
|
29
|
-
message: 'Elements must have sufficient color contrast',
|
|
30
|
-
url: 'https://example.com/',
|
|
31
|
-
},
|
|
32
|
-
],
|
|
33
|
-
},
|
|
34
|
-
];
|
|
35
|
-
|
|
36
|
-
const sheet = createViolations(reports);
|
|
37
|
-
const rows = sheet.addRows!();
|
|
38
|
-
|
|
39
|
-
expect(rows).toHaveLength(1);
|
|
40
|
-
expect(rows[0]).toHaveLength(6);
|
|
41
|
-
});
|
|
42
|
-
|
|
43
|
-
it('skips reports without violations', () => {
|
|
44
|
-
const reports: Report[] = [{ name: 'no-violations' }];
|
|
45
|
-
|
|
46
|
-
const sheet = createViolations(reports);
|
|
47
|
-
const rows = sheet.addRows!();
|
|
48
|
-
|
|
49
|
-
expect(rows).toHaveLength(0);
|
|
50
|
-
});
|
|
51
|
-
|
|
52
|
-
it('handles multiple reports with multiple violations', () => {
|
|
53
|
-
const reports: Report[] = [
|
|
54
|
-
{
|
|
55
|
-
name: 'axe',
|
|
56
|
-
violations: [
|
|
57
|
-
{
|
|
58
|
-
validator: 'axe',
|
|
59
|
-
severity: 'serious',
|
|
60
|
-
rule: 'rule-1',
|
|
61
|
-
code: 'code-1',
|
|
62
|
-
message: 'msg-1',
|
|
63
|
-
url: 'https://example.com/a',
|
|
64
|
-
},
|
|
65
|
-
{
|
|
66
|
-
validator: 'axe',
|
|
67
|
-
severity: 'minor',
|
|
68
|
-
rule: 'rule-2',
|
|
69
|
-
code: 'code-2',
|
|
70
|
-
message: 'msg-2',
|
|
71
|
-
url: 'https://example.com/b',
|
|
72
|
-
},
|
|
73
|
-
],
|
|
74
|
-
},
|
|
75
|
-
{
|
|
76
|
-
name: 'markuplint',
|
|
77
|
-
violations: [
|
|
78
|
-
{
|
|
79
|
-
validator: 'markuplint',
|
|
80
|
-
severity: 'warning',
|
|
81
|
-
rule: 'rule-3',
|
|
82
|
-
code: 'code-3',
|
|
83
|
-
message: 'msg-3',
|
|
84
|
-
url: 'https://example.com/c',
|
|
85
|
-
},
|
|
86
|
-
],
|
|
87
|
-
},
|
|
88
|
-
];
|
|
89
|
-
|
|
90
|
-
const sheet = createViolations(reports);
|
|
91
|
-
const rows = sheet.addRows!();
|
|
92
|
-
|
|
93
|
-
expect(rows).toHaveLength(3);
|
|
94
|
-
});
|
|
95
|
-
});
|
|
@@ -1,47 +0,0 @@
|
|
|
1
|
-
import type { CreateSheet } from '../sheets/types.js';
|
|
2
|
-
import type { Cell } from '@d-zero/google-sheets';
|
|
3
|
-
|
|
4
|
-
import { pLog } from '../debug.js';
|
|
5
|
-
import { createCellData } from '../sheets/create-cell-data.js';
|
|
6
|
-
import { defaultCellFormat } from '../sheets/default-cell-format.js';
|
|
7
|
-
|
|
8
|
-
const log = pLog.extend('Violations');
|
|
9
|
-
|
|
10
|
-
/**
|
|
11
|
-
* Creates the "Violations" sheet configuration.
|
|
12
|
-
*
|
|
13
|
-
* Aggregates all violations from analyze plugin reports into a single
|
|
14
|
-
* flat table. Uses `addRows` (Phase 4) rather than `eachPage` because
|
|
15
|
-
* violation data comes from the report objects, not from per-page
|
|
16
|
-
* archive iteration. This means it runs in parallel with page/resource
|
|
17
|
-
* processing phases.
|
|
18
|
-
* @param reports
|
|
19
|
-
*/
|
|
20
|
-
export const createViolations: CreateSheet = (reports) => {
|
|
21
|
-
return {
|
|
22
|
-
name: 'Violations',
|
|
23
|
-
createHeaders() {
|
|
24
|
-
return ['Validator', 'Severity', 'Rule', 'Code', 'Message', 'URL'];
|
|
25
|
-
},
|
|
26
|
-
addRows: () => {
|
|
27
|
-
const data: Cell[][] = [];
|
|
28
|
-
for (const report of reports) {
|
|
29
|
-
if (!report.violations) {
|
|
30
|
-
continue;
|
|
31
|
-
}
|
|
32
|
-
log('From %s', report.name);
|
|
33
|
-
for (const violation of report.violations) {
|
|
34
|
-
data.push([
|
|
35
|
-
createCellData({ value: violation.validator }, defaultCellFormat),
|
|
36
|
-
createCellData({ value: violation.severity }, defaultCellFormat),
|
|
37
|
-
createCellData({ value: violation.rule }, defaultCellFormat),
|
|
38
|
-
createCellData({ value: violation.code }, defaultCellFormat),
|
|
39
|
-
createCellData({ value: violation.message }, defaultCellFormat),
|
|
40
|
-
createCellData({ value: violation.url }, defaultCellFormat),
|
|
41
|
-
]);
|
|
42
|
-
}
|
|
43
|
-
}
|
|
44
|
-
return data;
|
|
45
|
-
},
|
|
46
|
-
};
|
|
47
|
-
};
|
package/src/debug.ts
DELETED
|
@@ -1,7 +0,0 @@
|
|
|
1
|
-
import debug from 'debug';
|
|
2
|
-
|
|
3
|
-
export const log = debug('Nitpicker').extend('GoogleSpreadsheet');
|
|
4
|
-
export const sheetLog = log.extend('Sheet');
|
|
5
|
-
export const archiveLog = log.extend('Archive');
|
|
6
|
-
export const reportLog = log.extend('Report');
|
|
7
|
-
export const pLog = log.extend('Plugin');
|
package/src/index.ts
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export { report } from './report.js';
|
package/src/load-config.spec.ts
DELETED
|
@@ -1,37 +0,0 @@
|
|
|
1
|
-
import fs from 'node:fs/promises';
|
|
2
|
-
import path from 'node:path';
|
|
3
|
-
|
|
4
|
-
import { describe, it, expect, vi } from 'vitest';
|
|
5
|
-
|
|
6
|
-
import { loadConfig } from './load-config.js';
|
|
7
|
-
|
|
8
|
-
vi.mock('node:fs/promises');
|
|
9
|
-
|
|
10
|
-
describe('loadConfig', () => {
|
|
11
|
-
it('returns empty object when filePath is null', async () => {
|
|
12
|
-
const result = await loadConfig(null);
|
|
13
|
-
expect(result).toEqual({});
|
|
14
|
-
});
|
|
15
|
-
|
|
16
|
-
it('reads and parses config from absolute path', async () => {
|
|
17
|
-
const config = { plugins: { analyze: { '@nitpicker/analyze-axe': true } } };
|
|
18
|
-
vi.mocked(fs.readFile).mockResolvedValue(JSON.stringify(config));
|
|
19
|
-
|
|
20
|
-
const result = await loadConfig('/absolute/path/config.json');
|
|
21
|
-
|
|
22
|
-
expect(fs.readFile).toHaveBeenCalledWith('/absolute/path/config.json', {
|
|
23
|
-
encoding: 'utf8',
|
|
24
|
-
});
|
|
25
|
-
expect(result).toEqual(config);
|
|
26
|
-
});
|
|
27
|
-
|
|
28
|
-
it('resolves relative path before reading', async () => {
|
|
29
|
-
const config = {};
|
|
30
|
-
vi.mocked(fs.readFile).mockResolvedValue(JSON.stringify(config));
|
|
31
|
-
|
|
32
|
-
await loadConfig('./relative/config.json');
|
|
33
|
-
|
|
34
|
-
const expectedPath = path.resolve('./relative/config.json');
|
|
35
|
-
expect(fs.readFile).toHaveBeenCalledWith(expectedPath, { encoding: 'utf8' });
|
|
36
|
-
});
|
|
37
|
-
});
|
package/src/load-config.ts
DELETED
|
@@ -1,17 +0,0 @@
|
|
|
1
|
-
import type { ConfigJSON } from '@nitpicker/types';
|
|
2
|
-
|
|
3
|
-
import fs from 'node:fs/promises';
|
|
4
|
-
import path from 'node:path';
|
|
5
|
-
|
|
6
|
-
/**
|
|
7
|
-
*
|
|
8
|
-
* @param filePath
|
|
9
|
-
*/
|
|
10
|
-
export async function loadConfig(filePath: string | null) {
|
|
11
|
-
if (!filePath) {
|
|
12
|
-
return {};
|
|
13
|
-
}
|
|
14
|
-
const absFilePath = path.isAbsolute(filePath) ? filePath : path.resolve(filePath);
|
|
15
|
-
const data = await fs.readFile(absFilePath, { encoding: 'utf8' });
|
|
16
|
-
return JSON.parse(data) as ConfigJSON;
|
|
17
|
-
}
|
package/src/report.ts
DELETED
|
@@ -1,231 +0,0 @@
|
|
|
1
|
-
import type { CreateSheet } from './sheets/types.js';
|
|
2
|
-
import type { ErrorHandlerMessage } from '@d-zero/google-sheets';
|
|
3
|
-
|
|
4
|
-
import { Lanes } from '@d-zero/dealer';
|
|
5
|
-
import { authentication } from '@d-zero/google-auth';
|
|
6
|
-
import { Sheets } from '@d-zero/google-sheets';
|
|
7
|
-
import c from 'ansi-colors';
|
|
8
|
-
import enquirer from 'enquirer';
|
|
9
|
-
|
|
10
|
-
import { getArchive } from './archive.js';
|
|
11
|
-
import { addToSummary } from './data/add-to-summary.js';
|
|
12
|
-
import { createDiscrepancies } from './data/create-discrepancies.js';
|
|
13
|
-
import { createImageList } from './data/create-image-list.js';
|
|
14
|
-
import { createLinks } from './data/create-links.js';
|
|
15
|
-
import { createPageList } from './data/create-page-list.js';
|
|
16
|
-
import { createReferrersRelationalTable } from './data/create-referrers-relational-table.js';
|
|
17
|
-
import { createResourcesRelationalTable } from './data/create-resources-relational-table.js';
|
|
18
|
-
import { createResources } from './data/create-resources.js';
|
|
19
|
-
import { createViolations } from './data/create-violations.js';
|
|
20
|
-
import { archiveLog, log } from './debug.js';
|
|
21
|
-
import { loadConfig } from './load-config.js';
|
|
22
|
-
import { getPluginReports } from './reports/get-plugin-reports.js';
|
|
23
|
-
import { createSheets } from './sheets/create-sheets.js';
|
|
24
|
-
|
|
25
|
-
/**
|
|
26
|
-
* Parameters for {@link report}.
|
|
27
|
-
*/
|
|
28
|
-
export interface ReportParams {
|
|
29
|
-
/** Path to the `.nitpicker` archive file. */
|
|
30
|
-
readonly filePath: string;
|
|
31
|
-
/** URL of the target Google Spreadsheet. */
|
|
32
|
-
readonly sheetUrl: string;
|
|
33
|
-
/** Path to the OAuth2 credentials JSON file. */
|
|
34
|
-
readonly credentialFilePath: string;
|
|
35
|
-
/** Path to the nitpicker config file, or `null` for defaults. */
|
|
36
|
-
readonly configPath: string | null;
|
|
37
|
-
/** Batch size for `getPagesWithRefs()` pagination (default: 100,000). */
|
|
38
|
-
readonly limit: number;
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
/**
|
|
42
|
-
* Generates a Google Sheets report from a `.nitpicker` archive file.
|
|
43
|
-
*
|
|
44
|
-
* This is the main entry point for the `nitpicker report` command.
|
|
45
|
-
* It orchestrates the full reporting pipeline:
|
|
46
|
-
*
|
|
47
|
-
* 1. Authenticates with Google Sheets API using OAuth2 credentials.
|
|
48
|
-
* 2. Opens the `.nitpicker` archive and loads its configuration.
|
|
49
|
-
* 3. Loads analyze plugin reports from the archive.
|
|
50
|
-
* 4. Presents an interactive multi-select prompt for the user to
|
|
51
|
-
* choose which sheets to generate.
|
|
52
|
-
* 5. Delegates to `createSheets()` for phased data generation and upload.
|
|
53
|
-
*
|
|
54
|
-
* Rate limiting from the Google Sheets API (429 / 403) is handled
|
|
55
|
-
* gracefully via the `Sheets.onLog` callback, which displays a
|
|
56
|
-
* countdown timer in the terminal using the `Lanes` progress display.
|
|
57
|
-
* @param params - レポート生成に必要なパラメータ
|
|
58
|
-
* @example
|
|
59
|
-
* ```ts
|
|
60
|
-
* await report({
|
|
61
|
-
* filePath: './output.nitpicker',
|
|
62
|
-
* sheetUrl: 'https://docs.google.com/spreadsheets/d/xxx/edit',
|
|
63
|
-
* credentialFilePath: './credentials.json',
|
|
64
|
-
* configPath: './nitpicker.config.json',
|
|
65
|
-
* limit: 100_000,
|
|
66
|
-
* });
|
|
67
|
-
* ```
|
|
68
|
-
*/
|
|
69
|
-
export async function report(params: ReportParams) {
|
|
70
|
-
const { filePath, sheetUrl, credentialFilePath, configPath, limit } = params;
|
|
71
|
-
log('Initialization');
|
|
72
|
-
|
|
73
|
-
const SCOPES = ['https://www.googleapis.com/auth/spreadsheets'] as const;
|
|
74
|
-
log('Authenticating');
|
|
75
|
-
const auth = await authentication(credentialFilePath, SCOPES, {
|
|
76
|
-
tokenFilePath: 'token.json',
|
|
77
|
-
});
|
|
78
|
-
log('Authentication succeeded');
|
|
79
|
-
|
|
80
|
-
log('Opening archive: %s', filePath);
|
|
81
|
-
const archive = await getArchive(filePath);
|
|
82
|
-
log('Archive opened');
|
|
83
|
-
|
|
84
|
-
log('Loading config');
|
|
85
|
-
const config = await loadConfig(configPath);
|
|
86
|
-
log('Config loaded');
|
|
87
|
-
|
|
88
|
-
const plugins = config.plugins?.analyze
|
|
89
|
-
? Object.keys(config.plugins.analyze)
|
|
90
|
-
: undefined;
|
|
91
|
-
log('Loaded plugins: %O', plugins);
|
|
92
|
-
|
|
93
|
-
log('Loading plugin reports');
|
|
94
|
-
const reports = await getPluginReports(archive /*plugins*/);
|
|
95
|
-
log('Plugin reports loaded: %d', reports.length);
|
|
96
|
-
|
|
97
|
-
const sheets = new Sheets(sheetUrl, auth);
|
|
98
|
-
|
|
99
|
-
log('Reporting starts');
|
|
100
|
-
|
|
101
|
-
const sheetNames = [
|
|
102
|
-
'Page List' as const,
|
|
103
|
-
'Links' as const,
|
|
104
|
-
'Resources' as const,
|
|
105
|
-
'Images' as const,
|
|
106
|
-
'Violations' as const,
|
|
107
|
-
'Discrepancies' as const,
|
|
108
|
-
'Summary' as const,
|
|
109
|
-
'Referrers Relational Table' as const,
|
|
110
|
-
'Resources Relational Table' as const,
|
|
111
|
-
];
|
|
112
|
-
type SheetNames = typeof sheetNames;
|
|
113
|
-
|
|
114
|
-
log('Choice creating data');
|
|
115
|
-
const chosenSheets = await enquirer
|
|
116
|
-
.prompt<{ sheetName: SheetNames }>([
|
|
117
|
-
{
|
|
118
|
-
message: 'What do you report?',
|
|
119
|
-
name: 'sheetName',
|
|
120
|
-
type: 'multiselect',
|
|
121
|
-
choices: sheetNames,
|
|
122
|
-
},
|
|
123
|
-
])
|
|
124
|
-
.catch(() => {
|
|
125
|
-
// enquirer v2.4.1: Ctrl+C 後に readline を二重 close して
|
|
126
|
-
// ERR_USE_AFTER_CLOSE が unhandled rejection になるため、
|
|
127
|
-
// 即座に終了して回避する
|
|
128
|
-
process.exit(0);
|
|
129
|
-
});
|
|
130
|
-
|
|
131
|
-
if (!chosenSheets) {
|
|
132
|
-
log('Choice creating data');
|
|
133
|
-
archiveLog('Closes file');
|
|
134
|
-
await archive.close();
|
|
135
|
-
return;
|
|
136
|
-
}
|
|
137
|
-
|
|
138
|
-
log('Chosen sheets: %O', chosenSheets.sheetName);
|
|
139
|
-
|
|
140
|
-
const createSheetList: CreateSheet[] = [];
|
|
141
|
-
|
|
142
|
-
if (chosenSheets.sheetName.includes('Page List')) {
|
|
143
|
-
createSheetList.push(createPageList);
|
|
144
|
-
}
|
|
145
|
-
|
|
146
|
-
if (chosenSheets.sheetName.includes('Links')) {
|
|
147
|
-
createSheetList.push(createLinks);
|
|
148
|
-
}
|
|
149
|
-
|
|
150
|
-
if (chosenSheets.sheetName.includes('Discrepancies')) {
|
|
151
|
-
createSheetList.push(createDiscrepancies);
|
|
152
|
-
}
|
|
153
|
-
|
|
154
|
-
if (chosenSheets.sheetName.includes('Violations')) {
|
|
155
|
-
createSheetList.push(createViolations);
|
|
156
|
-
}
|
|
157
|
-
|
|
158
|
-
if (chosenSheets.sheetName.includes('Referrers Relational Table')) {
|
|
159
|
-
createSheetList.push(createReferrersRelationalTable);
|
|
160
|
-
}
|
|
161
|
-
|
|
162
|
-
if (chosenSheets.sheetName.includes('Resources Relational Table')) {
|
|
163
|
-
createSheetList.push(createResourcesRelationalTable);
|
|
164
|
-
}
|
|
165
|
-
|
|
166
|
-
if (chosenSheets.sheetName.includes('Resources')) {
|
|
167
|
-
createSheetList.push(createResources);
|
|
168
|
-
}
|
|
169
|
-
|
|
170
|
-
if (chosenSheets.sheetName.includes('Images')) {
|
|
171
|
-
createSheetList.push(createImageList);
|
|
172
|
-
}
|
|
173
|
-
|
|
174
|
-
// eslint-disable-next-line no-console
|
|
175
|
-
console.log(`\nGenerating ${createSheetList.length} sheet(s)...\n`);
|
|
176
|
-
|
|
177
|
-
const lanes = new Lanes({ verbose: !process.stdout.isTTY, indent: ' ' });
|
|
178
|
-
log('Lanes created (verbose: %s)', !process.stdout.isTTY);
|
|
179
|
-
|
|
180
|
-
const RATE_LIMIT_LANE = 10_000;
|
|
181
|
-
let countdownSeq = 0;
|
|
182
|
-
let waitingCount = 0;
|
|
183
|
-
|
|
184
|
-
sheets.onLog = (message: ErrorHandlerMessage) => {
|
|
185
|
-
if (message.waiting && message.waitTime) {
|
|
186
|
-
waitingCount++;
|
|
187
|
-
const id = `rateLimit_${countdownSeq++}`;
|
|
188
|
-
const label =
|
|
189
|
-
message.message === 'TooManyRequestError'
|
|
190
|
-
? 'Too Many Requests (429)'
|
|
191
|
-
: message.message === 'UserRateLimitExceededError'
|
|
192
|
-
? 'Rate Limit Exceeded (403)'
|
|
193
|
-
: 'Connection Reset';
|
|
194
|
-
lanes.update(
|
|
195
|
-
RATE_LIMIT_LANE,
|
|
196
|
-
c.yellow(`${label}: waiting %countdown(${message.waitTime}, ${id}, s)%s`),
|
|
197
|
-
);
|
|
198
|
-
} else {
|
|
199
|
-
waitingCount--;
|
|
200
|
-
if (waitingCount <= 0) {
|
|
201
|
-
waitingCount = 0;
|
|
202
|
-
lanes.delete(RATE_LIMIT_LANE);
|
|
203
|
-
}
|
|
204
|
-
}
|
|
205
|
-
};
|
|
206
|
-
|
|
207
|
-
log('Reporting starts (limit: %d)', limit);
|
|
208
|
-
try {
|
|
209
|
-
await createSheets({
|
|
210
|
-
sheets,
|
|
211
|
-
archive,
|
|
212
|
-
reports,
|
|
213
|
-
limit,
|
|
214
|
-
createSheetList,
|
|
215
|
-
options: { lanes },
|
|
216
|
-
});
|
|
217
|
-
} finally {
|
|
218
|
-
lanes.close();
|
|
219
|
-
}
|
|
220
|
-
log('Reporting done');
|
|
221
|
-
// eslint-disable-next-line no-console
|
|
222
|
-
console.log('\nReport complete.');
|
|
223
|
-
|
|
224
|
-
if (chosenSheets.sheetName.includes('Summary')) {
|
|
225
|
-
await addToSummary(/*sheets, archive, reports*/);
|
|
226
|
-
}
|
|
227
|
-
|
|
228
|
-
archiveLog('Closes file');
|
|
229
|
-
await archive.close();
|
|
230
|
-
log('Done');
|
|
231
|
-
}
|
|
@@ -1,42 +0,0 @@
|
|
|
1
|
-
import type { Report } from '@nitpicker/types';
|
|
2
|
-
|
|
3
|
-
import { describe, it, expect, vi } from 'vitest';
|
|
4
|
-
|
|
5
|
-
import { getPluginReports } from './get-plugin-reports.js';
|
|
6
|
-
|
|
7
|
-
describe('getPluginReports', () => {
|
|
8
|
-
it('returns report when archive has analysis data', async () => {
|
|
9
|
-
const report: Report = {
|
|
10
|
-
name: 'test-plugin',
|
|
11
|
-
violations: [],
|
|
12
|
-
};
|
|
13
|
-
const archive = {
|
|
14
|
-
getData: vi.fn().mockResolvedValue(report),
|
|
15
|
-
};
|
|
16
|
-
|
|
17
|
-
const result = await getPluginReports(archive as never);
|
|
18
|
-
|
|
19
|
-
expect(archive.getData).toHaveBeenCalledWith('analysis/report');
|
|
20
|
-
expect(result).toEqual([report]);
|
|
21
|
-
});
|
|
22
|
-
|
|
23
|
-
it('returns empty array when archive has no analysis data', async () => {
|
|
24
|
-
const archive = {
|
|
25
|
-
getData: vi.fn().mockResolvedValue(null),
|
|
26
|
-
};
|
|
27
|
-
|
|
28
|
-
const result = await getPluginReports(archive as never);
|
|
29
|
-
|
|
30
|
-
expect(result).toEqual([]);
|
|
31
|
-
});
|
|
32
|
-
|
|
33
|
-
it('returns empty array when getData throws', async () => {
|
|
34
|
-
const archive = {
|
|
35
|
-
getData: vi.fn().mockRejectedValue(new Error('not found')),
|
|
36
|
-
};
|
|
37
|
-
|
|
38
|
-
const result = await getPluginReports(archive as never);
|
|
39
|
-
|
|
40
|
-
expect(result).toEqual([]);
|
|
41
|
-
});
|
|
42
|
-
});
|
|
@@ -1,24 +0,0 @@
|
|
|
1
|
-
import type { Archive } from '@nitpicker/crawler';
|
|
2
|
-
import type { Report } from '@nitpicker/types';
|
|
3
|
-
|
|
4
|
-
import { reportLog } from '../debug.js';
|
|
5
|
-
|
|
6
|
-
/**
|
|
7
|
-
*
|
|
8
|
-
* @param archive
|
|
9
|
-
*/
|
|
10
|
-
export async function getPluginReports(archive: Archive) {
|
|
11
|
-
const reports: Report[] = [];
|
|
12
|
-
|
|
13
|
-
reportLog('Load');
|
|
14
|
-
try {
|
|
15
|
-
const report = await archive.getData<Report>('analysis/report');
|
|
16
|
-
if (report) {
|
|
17
|
-
reports.push(report);
|
|
18
|
-
}
|
|
19
|
-
} catch {
|
|
20
|
-
reportLog('Failed: report is not found');
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
return reports;
|
|
24
|
-
}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export { createCellData } from '@d-zero/google-sheets';
|