Skip to content

Commit

Permalink
feat: create parsers and checkers
Browse files Browse the repository at this point in the history
  • Loading branch information
EmmanuelDemey committed Sep 6, 2024
1 parent cb4589d commit a3a59b3
Show file tree
Hide file tree
Showing 5 changed files with 191 additions and 131 deletions.
62 changes: 62 additions & 0 deletions audit/apps/audit/src/checkers/async/http-system-checker.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
import { execSync } from 'node:child_process';
import puppeteer from 'puppeteer';

type Checker = (
url: string,
parsers: { name: string; result: any }[]
) => Promise<{ name: string; result: any } | undefined>;

const getStatistics = async (
url: string,
parsers: { name: string; result: any }[]
): Promise<{ name: string; result: any }> => {
const browser = await puppeteer.launch();
const page = await browser.newPage();

let requestsNumber = 0;
let pageSize = 0;

const urls = new Set<string>();

// Écouter les événements de requête pour compter le nombre total de requêtes et calculer le poids total de la page.
page.on('request', (request) => {
urls.add(request.url());
requestsNumber++;
});

page.on('response', async (response) => {
const responseHeaders = response.headers();
const responseSize = parseInt(responseHeaders['content-length'], 10);
if (!isNaN(responseSize)) {
pageSize += responseSize;
}
});

await page.goto(url, { waitUntil: 'networkidle0' });

// Calculer le nombre d'éléments HTML sur la page.
const domComplexity: number = await page.evaluate(
() => document.querySelectorAll('*').length
);

await browser.close();
return {
name: 'statistics',
result: {
domComplexity,
pageSize,
requestsNumber,
urls,
},
};
};

export class HttpChecker {
#checkers: Checker[] = [getStatistics];
constructor(private parsers: { name: string; result: any }[]) {}
check(url: string) {
return Promise.all(
this.#checkers.map((checker) => checker(url, this.parsers))
).then((result) => result.filter((r) => !!r));
}
}
36 changes: 36 additions & 0 deletions audit/apps/audit/src/checkers/sync/file-system-checker.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import { execSync } from 'node:child_process';

type Checker = (
parsers: { name: string; result: any }[]
) => { name: string; result: any } | undefined;

const npmAudit = (parsers: { name: string; result: any }[]) => {
const packageManager = parsers.find(
(parser) => parser.name === 'packageManager'
);
if (!packageManager) {
return;
}

if (
packageManager.result.includes('npm') ||
packageManager.result.includes('yarn')
) {
try {
execSync(`npm --prefix . outdated --json`);
} catch (e) {
return { name: 'npm_outdated', result: JSON.parse(e.stdout.toString()) };
}
}
return;
};

export class FileSystemChecker {
#checkers: Checker[] = [npmAudit];
constructor(private parsers: { name: string; result: any }[]) {}
check() {
return this.#checkers
.map((checker) => checker(this.parsers))
.filter((result) => !!result);
}
}
156 changes: 25 additions & 131 deletions audit/apps/audit/src/main.ts
Original file line number Diff line number Diff line change
@@ -1,175 +1,69 @@
#! /usr/bin/env node

import puppeteer from 'puppeteer';
import { program } from 'commander';
import { z } from 'zod';
import { fdir } from 'fdir';
import { parse } from 'yaml';
import { readFileSync } from 'fs';
import { execSync } from 'child_process';
import { FileSystemParser } from './parsers/file-system-parser';
import { FileSystemChecker } from './checkers/sync/file-system-checker';
import { HttpChecker } from './checkers/async/http-system-checker';

program.name('audit').version('0.0.0');

const nodePackageManager = {
'package-lock.json': 'npm',
'yarn.lock': 'yarn',
};

type PageStatistics = {
requestsNumber: number;
pageSize: number;
domComplexity: number;
urls: Set<string>;
};

const getStatistics = async (url: string): Promise<PageStatistics> => {
const browser = await puppeteer.launch();
const page = await browser.newPage();

let requestsNumber = 0;
let pageSize = 0;

const urls = new Set<string>();

// Écouter les événements de requête pour compter le nombre total de requêtes et calculer le poids total de la page.
page.on('request', (request) => {
urls.add(request.url());
requestsNumber++;
});

page.on('response', async (response) => {
const responseHeaders = response.headers();
const responseSize = parseInt(responseHeaders['content-length'], 10);
if (!isNaN(responseSize)) {
pageSize += responseSize;
}
});

await page.goto(url, { waitUntil: 'networkidle0' });

// Calculer le nombre d'éléments HTML sur la page.
const domComplexity: number = await page.evaluate(
() => document.querySelectorAll('*').length
);

await browser.close();
return {
domComplexity,
pageSize,
requestsNumber,
urls,
};
};

type PageAuditUrl = {
stats: PageStatistics;
};
type AuditResult = {
packageManagerConfigFilePaths?: string[];
packageManager?: string[];
outdated?: Record<
string,
{
current: string;
wanted: string;
latest: string;
dependent: string;
location: string;
}
>;
urls?: Record<string, PageAuditUrl>;
parsers?: Array<{ name: string; result: any }>;
syncChecks?: Array<{ name: string; result: any }>;
asyncChecks?: Record<string, Array<{ name: string; result: any }>>;
};

program
.command('audit')
.option('-u, --url <string...>', 'urls to audit')
.option('-c, --config <char>', 'path to a config file')
.option('--path <char>', 'path to a folder container the project')
.action(async function () {
let config: { urls: string } = {
urls: this.opts().url,
};
console.log(this.opts());
if (this.opts().config) {
console.log('YO', this.opts().config);
config = {
...parse(readFileSync(this.opts().config, 'utf8')),
};
}

const audit: AuditResult = {
urls: {},
};

const validation = z
/*const validation = z
.object({
urls: z.array(z.string().url()),
})
.safeParse(config);
if (!validation.success) {
throw new Error(`La liste doit être ue liste d'URL valides`);
}
}*/

const crawler = new fdir().filter(
(path) =>
!path.includes('node_modules') &&
!path.includes('.nx') &&
!path.includes('tmp') &&
!path.includes('dist')
);
const audit: AuditResult = {};

audit.packageManagerConfigFilePaths = crawler
.filter((path) => path.endsWith('package.json'))
.withFullPaths()
.crawl('.')
.sync();
const fileSystemParser = new FileSystemParser();
audit.parsers = fileSystemParser.parse('.');

audit.packageManager = new fdir()
.filter(
(path) =>
!path.includes('node_modules') &&
!path.includes('.nx') &&
!path.includes('tmp') &&
!path.includes('dist')
)
.filter(
(path) =>
!!Object.keys(nodePackageManager).find((lock) => {
return path.endsWith(lock);
})
)
.withFullPaths()
.crawl('.')
.sync()
.map((path) => {
return Object.entries(nodePackageManager).find(
([file]: [string, string]) => {
return path.endsWith(file);
}
)[1];
})
.filter((packageManager) => !!packageManager);
const fileSystemChecker = new FileSystemChecker(audit.parsers);
audit.syncChecks = fileSystemChecker.check();

if (config.urls) {
const httpChecker = new HttpChecker(audit.parsers);

if (
audit.packageManager.includes('npm') ||
audit.packageManager.includes('yarn')
) {
try {
execSync(`npm --prefix . outdated --json`);
audit.asyncChecks = {};
for (const url of config.urls) {
audit.asyncChecks[url] = await httpChecker.check(url);
}
} catch (e) {
audit.outdated = JSON.parse(e.stdout.toString());
}
}
try {
for (const url of config.urls) {
const stats = await getStatistics(url);
audit.urls[url] = { stats };
console.error(e);
process.exit(1);
}
} catch (e) {
console.error(e);
process.exit(1);
}

console.log(audit);
console.log(JSON.stringify(audit));
});


Expand Down
65 changes: 65 additions & 0 deletions audit/apps/audit/src/parsers/file-system-parser.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
import { fdir } from 'fdir';

export type Parser = (responseootPath) => { name: string; result: any };

const nodePackageManager = {
'package-lock.json': 'npm',
'yarn.lock': 'yarn',
};

const getPackageManagerConfigurationFilePath: Parser = (rootPath) => {
const files = new fdir()
.filter(
(path) =>
!path.includes('node_modules') &&
!path.includes('.nx') &&
!path.includes('tmp') &&
!path.includes('dist')
)
.filter((path) => path.endsWith('package.json'))
.withFullPaths()
.crawl(rootPath)
.sync();

return { name: 'packageManagerConfigurationFilePath', result: files };
};

const getPackageManager: Parser = (rootPath) => {
const pckManagers = new fdir()
.filter(
(path) =>
!path.includes('node_modules') &&
!path.includes('.nx') &&
!path.includes('tmp') &&
!path.includes('dist')
)
.filter(
(path) =>
!!Object.keys(nodePackageManager).find((lock) => {
return path.endsWith(lock);
})
)
.withFullPaths()
.crawl(rootPath)
.sync()
.map((path) => {
return Object.entries(nodePackageManager).find(
([file]: [string, string]) => {
return path.endsWith(file);
}
)[1];
})
.filter((packageManager) => !!packageManager);

return { name: 'packageManager', result: pckManagers };
};

export class FileSystemParser {
#parsers = [getPackageManagerConfigurationFilePath, getPackageManager];
#crawler: any;
constructor() {}

parse(rootPath: string) {
return this.#parsers.map((parser) => parser(rootPath));
}
}
3 changes: 3 additions & 0 deletions audit/apps/audit/src/parsers/http-parser.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
export class HttpParser {
constructor(private readonly crawler: any) {}
}

0 comments on commit a3a59b3

Please sign in to comment.