@nitpicker/report-google-sheets 0.4.1
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.md +8 -0
- package/LICENSE +191 -0
- package/README.md +13 -0
- package/lib/archive.d.ts +6 -0
- package/lib/archive.js +24 -0
- package/lib/data/add-to-summary.d.ts +8 -0
- package/lib/data/add-to-summary.js +10 -0
- package/lib/data/create-compares.d.ts +19 -0
- package/lib/data/create-compares.js +64 -0
- package/lib/data/create-discrepancies.d.ts +19 -0
- package/lib/data/create-discrepancies.js +64 -0
- package/lib/data/create-image-list.d.ts +13 -0
- package/lib/data/create-image-list.js +63 -0
- package/lib/data/create-links.d.ts +15 -0
- package/lib/data/create-links.js +109 -0
- package/lib/data/create-page-list.d.ts +29 -0
- package/lib/data/create-page-list.js +380 -0
- package/lib/data/create-referrers-relational-table.d.ts +18 -0
- package/lib/data/create-referrers-relational-table.js +96 -0
- package/lib/data/create-resources-relational-table.d.ts +14 -0
- package/lib/data/create-resources-relational-table.js +84 -0
- package/lib/data/create-resources.d.ts +13 -0
- package/lib/data/create-resources.js +45 -0
- package/lib/data/create-violations.d.ts +12 -0
- package/lib/data/create-violations.js +42 -0
- package/lib/debug.d.ts +6 -0
- package/lib/debug.js +6 -0
- package/lib/index.d.ts +1 -0
- package/lib/index.js +1 -0
- package/lib/load-config.d.ts +6 -0
- package/lib/load-config.js +14 -0
- package/lib/report.d.ts +44 -0
- package/lib/report.js +180 -0
- package/lib/reports/get-plugin-reports.d.ts +7 -0
- package/lib/reports/get-plugin-reports.js +19 -0
- package/lib/sheets/create-cell-data.d.ts +1 -0
- package/lib/sheets/create-cell-data.js +1 -0
- package/lib/sheets/create-sheets.d.ts +74 -0
- package/lib/sheets/create-sheets.js +420 -0
- package/lib/sheets/default-cell-format.d.ts +8 -0
- package/lib/sheets/default-cell-format.js +8 -0
- package/lib/sheets/format.d.ts +2 -0
- package/lib/sheets/format.js +12 -0
- package/lib/sheets/types.d.ts +91 -0
- package/lib/sheets/types.js +1 -0
- package/lib/types.d.ts +11 -0
- package/lib/types.js +1 -0
- package/lib/utils/array-duplicated.d.ts +5 -0
- package/lib/utils/array-duplicated.js +15 -0
- package/lib/utils/get-rank-top.d.ts +6 -0
- package/lib/utils/get-rank-top.js +36 -0
- package/lib/utils/has-prop-filter.d.ts +18 -0
- package/lib/utils/has-prop-filter.js +19 -0
- package/lib/utils/is-bitmap-image.d.ts +6 -0
- package/lib/utils/is-bitmap-image.js +10 -0
- package/lib/utils/non-null-filter.d.ts +13 -0
- package/lib/utils/non-null-filter.js +15 -0
- package/package.json +45 -0
- package/src/__tests__/api/create-sheets.api.ts +234 -0
- package/src/__tests__/api/helpers.ts +148 -0
- package/src/__tests__/api/sheets.api.ts +217 -0
- package/src/archive.ts +29 -0
- package/src/data/add-to-summary.ts +10 -0
- package/src/data/create-discrepancies.ts +81 -0
- package/src/data/create-image-list.ts +74 -0
- package/src/data/create-links.ts +134 -0
- package/src/data/create-page-list.ts +472 -0
- package/src/data/create-referrers-relational-table.ts +115 -0
- package/src/data/create-resources-relational-table.ts +104 -0
- package/src/data/create-resources.ts +51 -0
- package/src/data/create-violations.spec.ts +95 -0
- package/src/data/create-violations.ts +47 -0
- package/src/debug.ts +7 -0
- package/src/index.ts +1 -0
- package/src/load-config.spec.ts +37 -0
- package/src/load-config.ts +17 -0
- package/src/report.ts +231 -0
- package/src/reports/get-plugin-reports.spec.ts +42 -0
- package/src/reports/get-plugin-reports.ts +24 -0
- package/src/sheets/create-cell-data.ts +1 -0
- package/src/sheets/create-sheets.ts +523 -0
- package/src/sheets/default-cell-format.spec.ts +13 -0
- package/src/sheets/default-cell-format.ts +8 -0
- package/src/sheets/format.spec.ts +17 -0
- package/src/sheets/format.ts +14 -0
- package/src/sheets/types.ts +106 -0
- package/src/types.ts +11 -0
- package/src/utils/has-prop-filter.spec.ts +25 -0
- package/src/utils/has-prop-filter.ts +21 -0
- package/src/utils/non-null-filter.spec.ts +27 -0
- package/src/utils/non-null-filter.ts +15 -0
- package/tsconfig.json +11 -0
- package/tsconfig.tsbuildinfo +1 -0
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import { reportLog } from '../debug.js';
|
|
2
|
+
import { createCellData } from '../sheets/create-cell-data.js';
|
|
3
|
+
import { defaultCellFormat } from '../sheets/default-cell-format.js';
|
|
4
|
+
/**
|
|
5
|
+
* Creates the "Resources" sheet configuration.
|
|
6
|
+
*
|
|
7
|
+
* Lists all network resources (CSS, JS, images, fonts, etc.) discovered
|
|
8
|
+
* during crawling, with one row per unique resource URL. Each row includes
|
|
9
|
+
* HTTP status, content type, size, and the number of pages that reference
|
|
10
|
+
* the resource (with URLs listed in the cell note).
|
|
11
|
+
*
|
|
12
|
+
* Uses `eachResource` (Phase 3) to iterate over the archive's resource
|
|
13
|
+
* table rather than page-by-page iteration.
|
|
14
|
+
*/
|
|
15
|
+
export const createResources = () => {
|
|
16
|
+
return {
|
|
17
|
+
name: 'Resources',
|
|
18
|
+
createHeaders() {
|
|
19
|
+
return [
|
|
20
|
+
'URL',
|
|
21
|
+
'Status Code',
|
|
22
|
+
'Status Text',
|
|
23
|
+
'Content Type',
|
|
24
|
+
'Content Length',
|
|
25
|
+
'Referrers',
|
|
26
|
+
];
|
|
27
|
+
},
|
|
28
|
+
async eachResource(resource) {
|
|
29
|
+
reportLog(`Read: Resource referrers (Search: ${resource.url})`);
|
|
30
|
+
const referrers = await resource.getReferrers();
|
|
31
|
+
const data = [
|
|
32
|
+
createCellData({ value: resource.url }, defaultCellFormat),
|
|
33
|
+
createCellData({ value: resource.status }, defaultCellFormat),
|
|
34
|
+
createCellData({ value: resource.statusText }, defaultCellFormat),
|
|
35
|
+
createCellData({ value: resource.contentType }, defaultCellFormat),
|
|
36
|
+
createCellData({ value: resource.contentLength }, defaultCellFormat),
|
|
37
|
+
createCellData({
|
|
38
|
+
value: `${referrers.length} pages`,
|
|
39
|
+
note: referrers.join('\n'),
|
|
40
|
+
}, defaultCellFormat),
|
|
41
|
+
];
|
|
42
|
+
return [data];
|
|
43
|
+
},
|
|
44
|
+
};
|
|
45
|
+
};
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import type { CreateSheet } from '../sheets/types.js';
|
|
2
|
+
/**
|
|
3
|
+
* Creates the "Violations" sheet configuration.
|
|
4
|
+
*
|
|
5
|
+
* Aggregates all violations from analyze plugin reports into a single
|
|
6
|
+
* flat table. Uses `addRows` (Phase 4) rather than `eachPage` because
|
|
7
|
+
* violation data comes from the report objects, not from per-page
|
|
8
|
+
* archive iteration. This means it runs in parallel with page/resource
|
|
9
|
+
* processing phases.
|
|
10
|
+
* @param reports
|
|
11
|
+
*/
|
|
12
|
+
export declare const createViolations: CreateSheet;
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import { pLog } from '../debug.js';
|
|
2
|
+
import { createCellData } from '../sheets/create-cell-data.js';
|
|
3
|
+
import { defaultCellFormat } from '../sheets/default-cell-format.js';
|
|
4
|
+
const log = pLog.extend('Violations');
|
|
5
|
+
/**
|
|
6
|
+
* Creates the "Violations" sheet configuration.
|
|
7
|
+
*
|
|
8
|
+
* Aggregates all violations from analyze plugin reports into a single
|
|
9
|
+
* flat table. Uses `addRows` (Phase 4) rather than `eachPage` because
|
|
10
|
+
* violation data comes from the report objects, not from per-page
|
|
11
|
+
* archive iteration. This means it runs in parallel with page/resource
|
|
12
|
+
* processing phases.
|
|
13
|
+
* @param reports
|
|
14
|
+
*/
|
|
15
|
+
export const createViolations = (reports) => {
|
|
16
|
+
return {
|
|
17
|
+
name: 'Violations',
|
|
18
|
+
createHeaders() {
|
|
19
|
+
return ['Validator', 'Severity', 'Rule', 'Code', 'Message', 'URL'];
|
|
20
|
+
},
|
|
21
|
+
addRows: () => {
|
|
22
|
+
const data = [];
|
|
23
|
+
for (const report of reports) {
|
|
24
|
+
if (!report.violations) {
|
|
25
|
+
continue;
|
|
26
|
+
}
|
|
27
|
+
log('From %s', report.name);
|
|
28
|
+
for (const violation of report.violations) {
|
|
29
|
+
data.push([
|
|
30
|
+
createCellData({ value: violation.validator }, defaultCellFormat),
|
|
31
|
+
createCellData({ value: violation.severity }, defaultCellFormat),
|
|
32
|
+
createCellData({ value: violation.rule }, defaultCellFormat),
|
|
33
|
+
createCellData({ value: violation.code }, defaultCellFormat),
|
|
34
|
+
createCellData({ value: violation.message }, defaultCellFormat),
|
|
35
|
+
createCellData({ value: violation.url }, defaultCellFormat),
|
|
36
|
+
]);
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
return data;
|
|
40
|
+
},
|
|
41
|
+
};
|
|
42
|
+
};
|
package/lib/debug.d.ts
ADDED
package/lib/debug.js
ADDED
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import debug from 'debug';
|
|
2
|
+
export const log = debug('Nitpicker').extend('GoogleSpreadsheet');
|
|
3
|
+
export const sheetLog = log.extend('Sheet');
|
|
4
|
+
export const archiveLog = log.extend('Archive');
|
|
5
|
+
export const reportLog = log.extend('Report');
|
|
6
|
+
export const pLog = log.extend('Plugin');
|
package/lib/index.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { report } from './report.js';
|
package/lib/index.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { report } from './report.js';
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import fs from 'node:fs/promises';
|
|
2
|
+
import path from 'node:path';
|
|
3
|
+
/**
|
|
4
|
+
*
|
|
5
|
+
* @param filePath
|
|
6
|
+
*/
|
|
7
|
+
export async function loadConfig(filePath) {
|
|
8
|
+
if (!filePath) {
|
|
9
|
+
return {};
|
|
10
|
+
}
|
|
11
|
+
const absFilePath = path.isAbsolute(filePath) ? filePath : path.resolve(filePath);
|
|
12
|
+
const data = await fs.readFile(absFilePath, { encoding: 'utf8' });
|
|
13
|
+
return JSON.parse(data);
|
|
14
|
+
}
|
package/lib/report.d.ts
ADDED
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Parameters for {@link report}.
|
|
3
|
+
*/
|
|
4
|
+
export interface ReportParams {
|
|
5
|
+
/** Path to the `.nitpicker` archive file. */
|
|
6
|
+
readonly filePath: string;
|
|
7
|
+
/** URL of the target Google Spreadsheet. */
|
|
8
|
+
readonly sheetUrl: string;
|
|
9
|
+
/** Path to the OAuth2 credentials JSON file. */
|
|
10
|
+
readonly credentialFilePath: string;
|
|
11
|
+
/** Path to the nitpicker config file, or `null` for defaults. */
|
|
12
|
+
readonly configPath: string | null;
|
|
13
|
+
/** Batch size for `getPagesWithRefs()` pagination (default: 100,000). */
|
|
14
|
+
readonly limit: number;
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* Generates a Google Sheets report from a `.nitpicker` archive file.
|
|
18
|
+
*
|
|
19
|
+
* This is the main entry point for the `nitpicker report` command.
|
|
20
|
+
* It orchestrates the full reporting pipeline:
|
|
21
|
+
*
|
|
22
|
+
* 1. Authenticates with Google Sheets API using OAuth2 credentials.
|
|
23
|
+
* 2. Opens the `.nitpicker` archive and loads its configuration.
|
|
24
|
+
* 3. Loads analyze plugin reports from the archive.
|
|
25
|
+
* 4. Presents an interactive multi-select prompt for the user to
|
|
26
|
+
* choose which sheets to generate.
|
|
27
|
+
* 5. Delegates to `createSheets()` for phased data generation and upload.
|
|
28
|
+
*
|
|
29
|
+
* Rate limiting from the Google Sheets API (429 / 403) is handled
|
|
30
|
+
* gracefully via the `Sheets.onLog` callback, which displays a
|
|
31
|
+
* countdown timer in the terminal using the `Lanes` progress display.
|
|
32
|
+
* @param params - レポート生成に必要なパラメータ
|
|
33
|
+
* @example
|
|
34
|
+
* ```ts
|
|
35
|
+
* await report({
|
|
36
|
+
* filePath: './output.nitpicker',
|
|
37
|
+
* sheetUrl: 'https://docs.google.com/spreadsheets/d/xxx/edit',
|
|
38
|
+
* credentialFilePath: './credentials.json',
|
|
39
|
+
* configPath: './nitpicker.config.json',
|
|
40
|
+
* limit: 100_000,
|
|
41
|
+
* });
|
|
42
|
+
* ```
|
|
43
|
+
*/
|
|
44
|
+
export declare function report(params: ReportParams): Promise<void>;
|
package/lib/report.js
ADDED
|
@@ -0,0 +1,180 @@
|
|
|
1
|
+
import { Lanes } from '@d-zero/dealer';
|
|
2
|
+
import { authentication } from '@d-zero/google-auth';
|
|
3
|
+
import { Sheets } from '@d-zero/google-sheets';
|
|
4
|
+
import c from 'ansi-colors';
|
|
5
|
+
import enquirer from 'enquirer';
|
|
6
|
+
import { getArchive } from './archive.js';
|
|
7
|
+
import { addToSummary } from './data/add-to-summary.js';
|
|
8
|
+
import { createDiscrepancies } from './data/create-discrepancies.js';
|
|
9
|
+
import { createImageList } from './data/create-image-list.js';
|
|
10
|
+
import { createLinks } from './data/create-links.js';
|
|
11
|
+
import { createPageList } from './data/create-page-list.js';
|
|
12
|
+
import { createReferrersRelationalTable } from './data/create-referrers-relational-table.js';
|
|
13
|
+
import { createResourcesRelationalTable } from './data/create-resources-relational-table.js';
|
|
14
|
+
import { createResources } from './data/create-resources.js';
|
|
15
|
+
import { createViolations } from './data/create-violations.js';
|
|
16
|
+
import { archiveLog, log } from './debug.js';
|
|
17
|
+
import { loadConfig } from './load-config.js';
|
|
18
|
+
import { getPluginReports } from './reports/get-plugin-reports.js';
|
|
19
|
+
import { createSheets } from './sheets/create-sheets.js';
|
|
20
|
+
/**
|
|
21
|
+
* Generates a Google Sheets report from a `.nitpicker` archive file.
|
|
22
|
+
*
|
|
23
|
+
* This is the main entry point for the `nitpicker report` command.
|
|
24
|
+
* It orchestrates the full reporting pipeline:
|
|
25
|
+
*
|
|
26
|
+
* 1. Authenticates with Google Sheets API using OAuth2 credentials.
|
|
27
|
+
* 2. Opens the `.nitpicker` archive and loads its configuration.
|
|
28
|
+
* 3. Loads analyze plugin reports from the archive.
|
|
29
|
+
* 4. Presents an interactive multi-select prompt for the user to
|
|
30
|
+
* choose which sheets to generate.
|
|
31
|
+
* 5. Delegates to `createSheets()` for phased data generation and upload.
|
|
32
|
+
*
|
|
33
|
+
* Rate limiting from the Google Sheets API (429 / 403) is handled
|
|
34
|
+
* gracefully via the `Sheets.onLog` callback, which displays a
|
|
35
|
+
* countdown timer in the terminal using the `Lanes` progress display.
|
|
36
|
+
* @param params - レポート生成に必要なパラメータ
|
|
37
|
+
* @example
|
|
38
|
+
* ```ts
|
|
39
|
+
* await report({
|
|
40
|
+
* filePath: './output.nitpicker',
|
|
41
|
+
* sheetUrl: 'https://docs.google.com/spreadsheets/d/xxx/edit',
|
|
42
|
+
* credentialFilePath: './credentials.json',
|
|
43
|
+
* configPath: './nitpicker.config.json',
|
|
44
|
+
* limit: 100_000,
|
|
45
|
+
* });
|
|
46
|
+
* ```
|
|
47
|
+
*/
|
|
48
|
+
export async function report(params) {
|
|
49
|
+
const { filePath, sheetUrl, credentialFilePath, configPath, limit } = params;
|
|
50
|
+
log('Initialization');
|
|
51
|
+
const SCOPES = ['https://www.googleapis.com/auth/spreadsheets'];
|
|
52
|
+
log('Authenticating');
|
|
53
|
+
const auth = await authentication(credentialFilePath, SCOPES, {
|
|
54
|
+
tokenFilePath: 'token.json',
|
|
55
|
+
});
|
|
56
|
+
log('Authentication succeeded');
|
|
57
|
+
log('Opening archive: %s', filePath);
|
|
58
|
+
const archive = await getArchive(filePath);
|
|
59
|
+
log('Archive opened');
|
|
60
|
+
log('Loading config');
|
|
61
|
+
const config = await loadConfig(configPath);
|
|
62
|
+
log('Config loaded');
|
|
63
|
+
const plugins = config.plugins?.analyze
|
|
64
|
+
? Object.keys(config.plugins.analyze)
|
|
65
|
+
: undefined;
|
|
66
|
+
log('Loaded plugins: %O', plugins);
|
|
67
|
+
log('Loading plugin reports');
|
|
68
|
+
const reports = await getPluginReports(archive /*plugins*/);
|
|
69
|
+
log('Plugin reports loaded: %d', reports.length);
|
|
70
|
+
const sheets = new Sheets(sheetUrl, auth);
|
|
71
|
+
log('Reporting starts');
|
|
72
|
+
const sheetNames = [
|
|
73
|
+
'Page List',
|
|
74
|
+
'Links',
|
|
75
|
+
'Resources',
|
|
76
|
+
'Images',
|
|
77
|
+
'Violations',
|
|
78
|
+
'Discrepancies',
|
|
79
|
+
'Summary',
|
|
80
|
+
'Referrers Relational Table',
|
|
81
|
+
'Resources Relational Table',
|
|
82
|
+
];
|
|
83
|
+
log('Choice creating data');
|
|
84
|
+
const chosenSheets = await enquirer
|
|
85
|
+
.prompt([
|
|
86
|
+
{
|
|
87
|
+
message: 'What do you report?',
|
|
88
|
+
name: 'sheetName',
|
|
89
|
+
type: 'multiselect',
|
|
90
|
+
choices: sheetNames,
|
|
91
|
+
},
|
|
92
|
+
])
|
|
93
|
+
.catch(() => {
|
|
94
|
+
// enquirer v2.4.1: Ctrl+C 後に readline を二重 close して
|
|
95
|
+
// ERR_USE_AFTER_CLOSE が unhandled rejection になるため、
|
|
96
|
+
// 即座に終了して回避する
|
|
97
|
+
process.exit(0);
|
|
98
|
+
});
|
|
99
|
+
if (!chosenSheets) {
|
|
100
|
+
log('Choice creating data');
|
|
101
|
+
archiveLog('Closes file');
|
|
102
|
+
await archive.close();
|
|
103
|
+
return;
|
|
104
|
+
}
|
|
105
|
+
log('Chosen sheets: %O', chosenSheets.sheetName);
|
|
106
|
+
const createSheetList = [];
|
|
107
|
+
if (chosenSheets.sheetName.includes('Page List')) {
|
|
108
|
+
createSheetList.push(createPageList);
|
|
109
|
+
}
|
|
110
|
+
if (chosenSheets.sheetName.includes('Links')) {
|
|
111
|
+
createSheetList.push(createLinks);
|
|
112
|
+
}
|
|
113
|
+
if (chosenSheets.sheetName.includes('Discrepancies')) {
|
|
114
|
+
createSheetList.push(createDiscrepancies);
|
|
115
|
+
}
|
|
116
|
+
if (chosenSheets.sheetName.includes('Violations')) {
|
|
117
|
+
createSheetList.push(createViolations);
|
|
118
|
+
}
|
|
119
|
+
if (chosenSheets.sheetName.includes('Referrers Relational Table')) {
|
|
120
|
+
createSheetList.push(createReferrersRelationalTable);
|
|
121
|
+
}
|
|
122
|
+
if (chosenSheets.sheetName.includes('Resources Relational Table')) {
|
|
123
|
+
createSheetList.push(createResourcesRelationalTable);
|
|
124
|
+
}
|
|
125
|
+
if (chosenSheets.sheetName.includes('Resources')) {
|
|
126
|
+
createSheetList.push(createResources);
|
|
127
|
+
}
|
|
128
|
+
if (chosenSheets.sheetName.includes('Images')) {
|
|
129
|
+
createSheetList.push(createImageList);
|
|
130
|
+
}
|
|
131
|
+
// eslint-disable-next-line no-console
|
|
132
|
+
console.log(`\nGenerating ${createSheetList.length} sheet(s)...\n`);
|
|
133
|
+
const lanes = new Lanes({ verbose: !process.stdout.isTTY, indent: ' ' });
|
|
134
|
+
log('Lanes created (verbose: %s)', !process.stdout.isTTY);
|
|
135
|
+
const RATE_LIMIT_LANE = 10_000;
|
|
136
|
+
let countdownSeq = 0;
|
|
137
|
+
let waitingCount = 0;
|
|
138
|
+
sheets.onLog = (message) => {
|
|
139
|
+
if (message.waiting && message.waitTime) {
|
|
140
|
+
waitingCount++;
|
|
141
|
+
const id = `rateLimit_${countdownSeq++}`;
|
|
142
|
+
const label = message.message === 'TooManyRequestError'
|
|
143
|
+
? 'Too Many Requests (429)'
|
|
144
|
+
: message.message === 'UserRateLimitExceededError'
|
|
145
|
+
? 'Rate Limit Exceeded (403)'
|
|
146
|
+
: 'Connection Reset';
|
|
147
|
+
lanes.update(RATE_LIMIT_LANE, c.yellow(`${label}: waiting %countdown(${message.waitTime}, ${id}, s)%s`));
|
|
148
|
+
}
|
|
149
|
+
else {
|
|
150
|
+
waitingCount--;
|
|
151
|
+
if (waitingCount <= 0) {
|
|
152
|
+
waitingCount = 0;
|
|
153
|
+
lanes.delete(RATE_LIMIT_LANE);
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
};
|
|
157
|
+
log('Reporting starts (limit: %d)', limit);
|
|
158
|
+
try {
|
|
159
|
+
await createSheets({
|
|
160
|
+
sheets,
|
|
161
|
+
archive,
|
|
162
|
+
reports,
|
|
163
|
+
limit,
|
|
164
|
+
createSheetList,
|
|
165
|
+
options: { lanes },
|
|
166
|
+
});
|
|
167
|
+
}
|
|
168
|
+
finally {
|
|
169
|
+
lanes.close();
|
|
170
|
+
}
|
|
171
|
+
log('Reporting done');
|
|
172
|
+
// eslint-disable-next-line no-console
|
|
173
|
+
console.log('\nReport complete.');
|
|
174
|
+
if (chosenSheets.sheetName.includes('Summary')) {
|
|
175
|
+
await addToSummary( /*sheets, archive, reports*/);
|
|
176
|
+
}
|
|
177
|
+
archiveLog('Closes file');
|
|
178
|
+
await archive.close();
|
|
179
|
+
log('Done');
|
|
180
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { reportLog } from '../debug.js';
|
|
2
|
+
/**
|
|
3
|
+
*
|
|
4
|
+
* @param archive
|
|
5
|
+
*/
|
|
6
|
+
export async function getPluginReports(archive) {
|
|
7
|
+
const reports = [];
|
|
8
|
+
reportLog('Load');
|
|
9
|
+
try {
|
|
10
|
+
const report = await archive.getData('analysis/report');
|
|
11
|
+
if (report) {
|
|
12
|
+
reports.push(report);
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
catch {
|
|
16
|
+
reportLog('Failed: report is not found');
|
|
17
|
+
}
|
|
18
|
+
return reports;
|
|
19
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { createCellData } from '@d-zero/google-sheets';
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { createCellData } from '@d-zero/google-sheets';
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
import type { CreateSheet } from './types.js';
|
|
2
|
+
import type { Lanes } from '@d-zero/dealer';
|
|
3
|
+
import type { Sheets } from '@d-zero/google-sheets';
|
|
4
|
+
import type { Archive } from '@nitpicker/crawler';
|
|
5
|
+
import type { Report } from '@nitpicker/types';
|
|
6
|
+
/**
|
|
7
|
+
* Parameters for {@link createSheets}.
|
|
8
|
+
*/
|
|
9
|
+
export interface CreateSheetsParams {
|
|
10
|
+
/** Google Sheets API ラッパー */
|
|
11
|
+
readonly sheets: Sheets;
|
|
12
|
+
/** クロール結果のアーカイブ */
|
|
13
|
+
readonly archive: Archive;
|
|
14
|
+
/** 監査プラグインのレポート配列 */
|
|
15
|
+
readonly reports: Report[];
|
|
16
|
+
/** getPagesWithRefs のバッチサイズ(デフォルト 100,000) */
|
|
17
|
+
readonly limit: number;
|
|
18
|
+
/** シート設定のファクトリ関数配列 */
|
|
19
|
+
readonly createSheetList: CreateSheet[];
|
|
20
|
+
/** Lanes インスタンスを含むオプション */
|
|
21
|
+
readonly options?: {
|
|
22
|
+
/** Lanes instance for terminal progress display. */
|
|
23
|
+
readonly lanes: Lanes;
|
|
24
|
+
};
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* Google Sheets にシートを作成し、データを投入してフォーマットする。
|
|
28
|
+
*
|
|
29
|
+
* ## 処理フェーズ
|
|
30
|
+
*
|
|
31
|
+
* 5つのフェーズで構成され、Phase 2+3 と Phase 4 は並列実行される:
|
|
32
|
+
*
|
|
33
|
+
* ```
|
|
34
|
+
* Phase 1 (Creating sheets)
|
|
35
|
+
* → Phase 2 (Processing pages) ─→ Phase 3 (Processing resources)
|
|
36
|
+
* → Phase 4 (Plugin data / addRows) ← Phase 2+3 と並列
|
|
37
|
+
* → Phase 5 (Formatting sheets)
|
|
38
|
+
* ```
|
|
39
|
+
*
|
|
40
|
+
* ## Lanes 進捗表示
|
|
41
|
+
*
|
|
42
|
+
* ### ヘッダー: 加重平均による集計
|
|
43
|
+
*
|
|
44
|
+
* Phase 2/3 のヘッダーは全子タスク(シート)の進捗を加重平均で集計する。
|
|
45
|
+
*
|
|
46
|
+
* 以前の実装では `Math.min()` で最遅シートのページ生成進捗のみを表示していたが、
|
|
47
|
+
* シートごとに「生成完了→行送信中」「まだ生成中」「送信完了」と異なるサブフェーズに
|
|
48
|
+
* いる場合、ヘッダーの数値と各レーンの表示が乖離する問題があった。
|
|
49
|
+
* 例: ヘッダー「174/755 (23%)」だが Page List は既に Sent、
|
|
50
|
+
* Referrers RT は Sending rows 7500/13256 — ヘッダーが全体を見ていない。
|
|
51
|
+
*
|
|
52
|
+
* 解決策として、各シートの進捗を 0.0〜1.0 の fraction で管理し、
|
|
53
|
+
* 全シートの平均値をヘッダーに表示する:
|
|
54
|
+
*
|
|
55
|
+
* - **生成フェーズ**: `(pageNum / max) * 0.5` → 0%〜50%
|
|
56
|
+
* - **送信フェーズ**: `0.5 + (sentRows / totalRows) * 0.5` → 50%〜100%
|
|
57
|
+
* (`sendRowsInChunks` の `onProgress` コールバック経由)
|
|
58
|
+
* - **完了**: 1.0 (100%)
|
|
59
|
+
*
|
|
60
|
+
* 生成と送信を 50:50 で重み付けしている理由:
|
|
61
|
+
* - 生成だけで 0〜100% にすると、全シートが生成完了(100%)になっても
|
|
62
|
+
* 送信が残っている状態で「100%」と表示されてしまう
|
|
63
|
+
* - 送信(特に大量行のチャンク送信)は Google API の
|
|
64
|
+
* レート制限により生成と同程度の時間がかかるため、等配分が妥当
|
|
65
|
+
*
|
|
66
|
+
* ### レーン: フェーズ遷移に応じた状態表示
|
|
67
|
+
*
|
|
68
|
+
* - **アクティブフェーズ内で完了 + 将来フェーズあり**: `c.green("Sent (N rows)")`
|
|
69
|
+
* - **アクティブフェーズ内で完了 + 全フェーズ完了**: `c.green("Done (N rows)")`
|
|
70
|
+
* - **非アクティブ + 完了済み**: `c.dim` で同じテキスト(色だけ変化、Waiting に戻らない)
|
|
71
|
+
* - **非アクティブ + 未着手**: `c.dim("Waiting...")`
|
|
72
|
+
* @param params - シート作成に必要なパラメータ
|
|
73
|
+
*/
|
|
74
|
+
export declare function createSheets(params: CreateSheetsParams): Promise<void>;
|