diff --git a/.gitignore b/.gitignore index 7d6fa97..db9ae10 100644 --- a/.gitignore +++ b/.gitignore @@ -65,5 +65,6 @@ json/ reports/ preview/ dist/ +asset/ .nodesecurerc .DS_Store diff --git a/bin/commands/execute.js b/bin/commands/execute.ts similarity index 88% rename from bin/commands/execute.js rename to bin/commands/execute.ts index d952ace..199c003 100644 --- a/bin/commands/execute.js +++ b/bin/commands/execute.ts @@ -36,7 +36,11 @@ export async function execute(options = {}) { const config = configResult.unwrap(); const { report } = config; - if (report.reporters.length === 0) { + if (!report) { + throw new Error("Report configuration is missing"); + } + + if (!hasReporter(report.reporters)) { throw new Error("At least one reporter must be selected (either 'HTML' or 'PDF')"); } @@ -85,3 +89,6 @@ function debug(obj) { writeFileSync(filePath, inspect(obj, { showHidden: true, depth: null }), "utf8"); } +function hasReporter["reporters"]>(reporters: T): reporters is NonNullable { + return reporters !== undefined && reporters.length > 0; +} diff --git a/bin/commands/index.js b/bin/commands/index.ts similarity index 100% rename from bin/commands/index.js rename to bin/commands/index.ts diff --git a/bin/commands/init.js b/bin/commands/init.ts similarity index 92% rename from bin/commands/init.js rename to bin/commands/init.ts index 5cdd1c8..4365d7f 100644 --- a/bin/commands/init.js +++ b/bin/commands/init.ts @@ -2,7 +2,7 @@ import * as rc from "@nodesecure/rc"; import kleur from "kleur"; -export async function init() { +export async function init(): Promise { const configLocation = process.cwd(); const result = await rc.read(configLocation, { diff --git a/bin/index.js b/bin/index.ts old mode 100755 new mode 100644 similarity index 100% rename from bin/index.js rename to bin/index.ts diff --git a/eslint.config.mjs b/eslint.config.mjs index be7128f..bdb9a09 100644 --- a/eslint.config.mjs +++ b/eslint.config.mjs @@ -13,7 +13,7 @@ const compat = new FlatCompat({ }); export default [{ - ignores: ["**/node_modules/", "**/tmp/", "**/dist/", "**/coverage/", "**/fixtures/", "**/public/lib"] + ignores: ["**/node_modules/", "**/tmp/", "**/dist/", "**/asset/", "**/coverage/", "**/fixtures/", "**/public/lib"] }, ...compat.extends("@nodesecure/eslint-config"), { languageOptions: { sourceType: "module", diff --git a/package.json b/package.json index 96d7d2a..60a5c55 100644 --- a/package.json +++ b/package.json @@ -2,10 +2,10 @@ "name": "@nodesecure/report", "version": "3.0.0", "description": "NodeSecure HTML & PDF graphic security report", - "main": "./bin/index.js", + "main": "./dist/bin/index.js", "type": "module", "bin": { - "nreport": "./bin/index.js" + "nreport": "./dist/bin/index.js" }, "exports": { ".": { @@ -13,6 +13,10 @@ } }, "scripts": { + "build": "rimraf ./dist && npm run copy-views && npm run copy-public && npm run copy-package && tsc", + "copy-views": "copyfiles views/template.html dist/", + "copy-public": "copyfiles public/** dist/", + "copy-package": "copyfiles package.json dist/", "lint": "eslint src test", "test-only": "glob -c \"node --test-reporter=spec --test\" \"./test/**/*.spec.js\"", "test": "c8 --all --src ./src -r html npm run test-only", @@ -60,12 +64,17 @@ "zup": "0.0.2" }, "devDependencies": { - "@nodesecure/eslint-config": "2.0.0-beta.0", + "@openally/config.eslint": "^1.0.0", + "@openally/config.typescript": "^1.0.3", "@types/node": "^22.2.0", "c8": "^10.1.2", + "copyfiles": "^2.4.1", "eslint": "^9.8.0", "glob": "^11.0.0", - "open": "^10.1.0" + "open": "^10.1.0", + "rimraf": "^6.0.1", + "tsx": "^4.17.0", + "typescript": "^5.4.2" }, "engines": { "node": ">=18" diff --git a/scripts/preview.js b/scripts/preview.js index cfd22d5..b5cfbef 100644 --- a/scripts/preview.js +++ b/scripts/preview.js @@ -45,10 +45,10 @@ payload.report_theme = theme; const config = { theme, includeTransitiveInternal: false, - reporters: [ "html" ], + reporters: ["html"], npm: { organizationPrefix: "@nodesecure", - packages: [ "@nodesecure/js-x-ray" ] + packages: ["@nodesecure/js-x-ray"] }, git: { organizationUrl: "https://github.com/NodeSecure", @@ -87,7 +87,7 @@ const config = { const HTMLReport = new HTMLTemplateGenerator( payload, config -).render({ asset_location: "./dist" }); +).render({ asset_location: "./asset" }); const previewLocation = path.join(kPreviewDir, "preview.html"); writeFileSync( @@ -96,7 +96,7 @@ writeFileSync( ); await buildFrontAssets( - path.join(kPreviewDir, "dist"), + path.join(kPreviewDir, "asset"), { theme } ); diff --git a/src/analysis/extractScannerData.js b/src/analysis/extractScannerData.ts similarity index 77% rename from src/analysis/extractScannerData.js rename to src/analysis/extractScannerData.ts index d945821..2accc5b 100644 --- a/src/analysis/extractScannerData.js +++ b/src/analysis/extractScannerData.ts @@ -4,15 +4,47 @@ import fs from "node:fs"; // Import Third-party Dependencies import { formatBytes, getScoreColor, getVCSRepositoryPathAndPlatform } from "@nodesecure/utils"; -import * as Flags from "@nodesecure/flags"; +import { getFlags, getManifest } from "@nodesecure/flags/web"; import * as scorecard from "@nodesecure/ossf-scorecard-sdk"; // Import Internal Dependencies import * as localStorage from "../localStorage.js"; +import { type Dependencies, type Payload } from "@nodesecure/scanner"; +import type { RC } from "@nodesecure/rc"; + +export interface ReportStat { + size: { + all: number, internal: number, external: number + }; + deps: { + transitive: Record; + node: Record + }, + licenses: { + Unknown: number + }, + flags: Record; + flagsList: any; + extensions: Record; + warnings: Record; + authors: Record; + packages: Record; + packages_count: { + all: number, internal: number, external: number + }; + scorecards: Record; + showFlags: boolean; +} + +interface BuildStatOptions { + isJson: boolean; + reportConfig: RC["report"] + +} // CONSTANTS -const kFlagsList = Object.values(Flags.getManifest()); -const kWantedFlags = Flags.getFlags(); +const kFlagsList = Object.values(getManifest()); +const kWantedFlags = getFlags(); const kScorecardVisualizerUrl = `https://kooltheba.github.io/openssf-scorecard-api-visualizer/#/projects`; const kNodeVisualizerUrl = `https://nodejs.org/dist/latest/docs/api`; @@ -28,13 +60,19 @@ function splitPackageWithOrg(pkg) { * @param {string[] | NodeSecure.Payload | NodeSecure.Payload[]} payloadFiles * @param {object} options * @param {boolean} options.isJson - * @returns + * @returns {ReportStat} */ -export async function buildStatsFromNsecurePayloads(payloadFiles = [], options = Object.create(null)) { - const { isJson = false, reportConfig } = options; + +type PayloadFile = string[] | Payload | Payload[] | Payload["dependencies"]; + +export async function buildStatsFromNsecurePayloads( + payloadFiles: PayloadFile = [], + options: BuildStatOptions = Object.create(null) +): Promise { + const { isJson = false, reportConfig = localStorage.getConfig().report } = options; const config = reportConfig ?? localStorage.getConfig().report; - const stats = { + const stats: ReportStat = { size: { all: 0, internal: 0, external: 0 }, @@ -55,15 +93,15 @@ export async function buildStatsFromNsecurePayloads(payloadFiles = [], options = all: 0, internal: 0, external: 0 }, scorecards: {}, - showFlags: config.showFlags + showFlags: reportConfig.showFlags }; /** * @param {string | NodeSecure.Payload} fileOrJson * @returns {NodeSecure.Payload} */ - function getJSONPayload(fileOrJson) { - if (isJson) { + function getJSONPayload(fileOrJson: string | Payload | Dependencies) { + if (isPayload(isJson, fileOrJson)) { return fileOrJson; } @@ -171,3 +209,7 @@ export async function buildStatsFromNsecurePayloads(payloadFiles = [], options = return stats; } +function isPayload(isJson: boolean, fileOrJson: string | Payload | Dependencies): fileOrJson is Payload | Dependencies { + return isJson === true; +} + diff --git a/src/analysis/fetch.js b/src/analysis/fetch.ts similarity index 68% rename from src/analysis/fetch.js rename to src/analysis/fetch.ts index 77cd276..590cd1f 100644 --- a/src/analysis/fetch.js +++ b/src/analysis/fetch.ts @@ -10,21 +10,25 @@ import * as scanner from "./scanner.js"; import * as localStorage from "../localStorage.js"; import * as utils from "../utils/index.js"; import * as CONSTANTS from "../constants.js"; +import { type RC } from "@nodesecure/rc"; export async function fetchPackagesAndRepositoriesData( verbose = true ) { - const config = localStorage.getConfig().report; + const config = localStorage.getConfig()?.report; + // TODO: handle undefined ? + + + const fetchNpm = hasReportConfig(config) && canFetchNpm(config.npm); + const fetchGit = hasReportConfig(config) && canFetchGit(config.git); - const fetchNpm = config.npm?.packages.length > 0; - const fetchGit = config.git?.repositories.length > 0; if (!fetchGit && !fetchNpm) { throw new Error( "No git repositories and no npm packages to fetch in the local configuration!" ); } - const pkgStats = fetchNpm ? + const pkgStats = canFetchNpm(config.npm) ? await fetchPackagesStats( utils.formatNpmPackages( config.npm.organizationPrefix, @@ -34,11 +38,10 @@ export async function fetchPackagesAndRepositoriesData( ) : null; - const { repositories, organizationUrl } = config.git; - const repoStats = fetchGit ? + const repoStats = canFetchGit(config.git) ? await fetchRepositoriesStats( - repositories, - organizationUrl, + config.git.repositories, + config.git.organizationUrl, verbose ) : null; @@ -47,7 +50,7 @@ export async function fetchPackagesAndRepositoriesData( } async function fetchPackagesStats( - packages, + packages: string[], verbose = true ) { const jsonFiles = await utils.runInSpinner( @@ -65,8 +68,8 @@ async function fetchPackagesStats( } async function fetchRepositoriesStats( - repositories, - organizationUrl, + repositories: string[], + organizationUrl: string, verbose = true ) { const jsonFiles = await utils.runInSpinner( @@ -96,3 +99,15 @@ async function fetchRepositoriesStats( jsonFiles.filter((value) => value !== null) ); } + +function hasReportConfig(config: RC["report"]): config is NonNullable { + return config !== undefined; +} + +function canFetchGit["git"]>(gitConfig: T): gitConfig is NonNullable { + return gitConfig !== undefined && gitConfig.repositories.length > 0; +} + +function canFetchNpm["npm"]>(npmConfig: T): npmConfig is NonNullable { + return npmConfig !== undefined && npmConfig.packages.length > 0; +} diff --git a/src/analysis/scanner.js b/src/analysis/scanner.ts similarity index 85% rename from src/analysis/scanner.js rename to src/analysis/scanner.ts index 956f7e5..b3936b5 100644 --- a/src/analysis/scanner.js +++ b/src/analysis/scanner.ts @@ -19,13 +19,13 @@ const kMaxAnalysisLock = new Mutex({ concurrency: 2 }); * @param {!string} packageName * @returns {Promise} */ -export async function from(packageName) { +export async function from(packageName: string): Promise { const release = await kMaxAnalysisLock.acquire(); try { const name = `${packageName}.json`; const { dependencies } = await scanner.from(packageName, { - maxDepth: 4, verbose: false + maxDepth: 4, vulnerabilityStrategy: "none" }); const filePath = path.join(CONSTANTS.DIRS.JSON, name); @@ -34,7 +34,7 @@ export async function from(packageName) { return filePath; } - catch (error) { + catch { return null; } finally { @@ -49,13 +49,13 @@ export async function from(packageName) { * @param {!string} dir * @returns {Promise} */ -export async function cwd(dir) { +export async function cwd(dir: string): Promise { const release = await kMaxAnalysisLock.acquire(); try { const name = `${path.basename(dir)}.json`; const { dependencies } = await scanner.cwd(dir, { - maxDepth: 4, verbose: false, usePackageLock: false + maxDepth: 4, vulnerabilityStrategy: "none" }); const filePath = path.join(CONSTANTS.DIRS.JSON, name); @@ -63,7 +63,7 @@ export async function cwd(dir) { return filePath; } - catch (error) { + catch { return null; } finally { diff --git a/src/api/report.js b/src/api/report.ts similarity index 71% rename from src/api/report.js rename to src/api/report.ts index 4d3c962..ca9a013 100644 --- a/src/api/report.js +++ b/src/api/report.ts @@ -6,6 +6,17 @@ import fs from "node:fs/promises"; // Import Internal Dependencies import { buildStatsFromNsecurePayloads } from "../analysis/extractScannerData.js"; import { HTML, PDF } from "../reporting/index.js"; +import { type Payload } from "@nodesecure/scanner"; +import { type RC } from "@nodesecure/rc"; +import { hasPdfReporter } from "../reporting/pdf.js"; +import { hasHtmlReporter } from "../reporting/html.js"; + +export interface ReportOptions { + reportOutputLocation?: string | null; + includesPDF?: boolean; + savePDFOnDisk?: boolean; + saveHTMLOnDisk?: boolean; +} /** * Determine the final location of the report (on current working directory or in a temporary directory) @@ -16,7 +27,7 @@ import { HTML, PDF } from "../reporting/index.js"; * @param {boolean} options.saveHTMLOnDisk * @returns {Promise} */ -async function reportLocation(location, options) { +async function reportLocation(location: string | null, options: ReportOptions): Promise { const { includesPDF, savePDFOnDisk, @@ -35,17 +46,19 @@ async function reportLocation(location, options) { } export async function report( - scannerDependencies, - reportConfig, - reportOptions = Object.create(null) -) { + scannerDependencies: Payload["dependencies"], + reportConfig: RC["report"], + reportOptions: ReportOptions = Object.create(null) +): Promise { const { reportOutputLocation = null, savePDFOnDisk = false, saveHTMLOnDisk = false } = reportOptions; - const includesPDF = reportConfig.reporters.includes("pdf"); - const includesHTML = reportConfig.reporters.includes("html"); + + const includesPDF = hasPdfReporter(reportConfig); + const includesHTML = hasHtmlReporter(reportConfig); + if (!includesPDF && !includesHTML) { throw new Error("At least one reporter must be enabled (pdf or html)"); } @@ -69,7 +82,7 @@ export async function report( finalReportLocation ); - if (reportConfig.reporters.includes("pdf")) { + if (hasPdfReporter(reportConfig)) { return await PDF(reportHTMLPath, { title: reportConfig.title, saveOnDisk: savePDFOnDisk, @@ -88,3 +101,4 @@ export async function report( } } } + diff --git a/src/constants.js b/src/constants.ts similarity index 100% rename from src/constants.js rename to src/constants.ts diff --git a/src/index.js b/src/index.ts similarity index 100% rename from src/index.js rename to src/index.ts diff --git a/src/localStorage.js b/src/localStorage.js deleted file mode 100644 index 66da95e..0000000 --- a/src/localStorage.js +++ /dev/null @@ -1,8 +0,0 @@ -// Import Node.js Dependencies -import { AsyncLocalStorage } from "node:async_hooks"; - -export const store = new AsyncLocalStorage(); - -export function getConfig() { - return store.getStore(); -} diff --git a/src/localStorage.ts b/src/localStorage.ts new file mode 100644 index 0000000..60774f2 --- /dev/null +++ b/src/localStorage.ts @@ -0,0 +1,9 @@ +// Import Node.js Dependencies +import { type RC } from "@nodesecure/rc"; +import { AsyncLocalStorage } from "node:async_hooks"; + +export const store = new AsyncLocalStorage(); + +export function getConfig(): RC { + return store.getStore() as RC; +} diff --git a/src/reporting/html.js b/src/reporting/html.ts similarity index 87% rename from src/reporting/html.js rename to src/reporting/html.ts index f96a751..9ddfb06 100644 --- a/src/reporting/html.js +++ b/src/reporting/html.ts @@ -11,6 +11,13 @@ import * as CONSTANTS from "../constants.js"; import * as localStorage from "../localStorage.js"; import { HTMLTemplateGenerator } from "./template.js"; +import { type ReportStat } from "../analysis/extractScannerData.js"; +import { type RC } from "@nodesecure/rc"; + +interface HTMLData { + pkgStats: ReportStat | null, + repoStats: ReportStat | null +} // CONSTANTS const kDateFormatter = Intl.DateTimeFormat("en-GB", { @@ -47,14 +54,14 @@ const kAvailableThemes = new Set( ); export async function HTML( - data, - reportOptions = null, + data: HTMLData, + reportOptions?: RC["report"], reportOutputLocation = CONSTANTS.DIRS.REPORTS ) { const { pkgStats, repoStats } = data; const config = reportOptions ?? localStorage.getConfig().report; - const assetsOutputLocation = path.join(reportOutputLocation, "..", "dist"); + const assetsOutputLocation = path.join(reportOutputLocation, "..", "asset"); const reportTheme = kAvailableThemes.has(config.theme) ? config.theme : "dark"; const reportFinalOutputLocation = path.join( reportOutputLocation, @@ -115,3 +122,7 @@ export async function buildFrontAssets( )) ]); } + +export function hasHtmlReporter(config: RC["report"]): config is NonNullable { + return config?.reporters?.includes("html") || false; +} diff --git a/src/reporting/index.js b/src/reporting/index.ts similarity index 65% rename from src/reporting/index.js rename to src/reporting/index.ts index f6edc5b..84a64e9 100644 --- a/src/reporting/index.js +++ b/src/reporting/index.ts @@ -7,10 +7,16 @@ import * as localStorage from "../localStorage.js"; // Import Reporters import { HTML } from "./html.js"; -import { PDF } from "./pdf.js"; +import { hasPdfReporter, PDF } from "./pdf.js"; +import { type ReportStat } from "../analysis/extractScannerData.js"; + +interface ReportingData { + pkgStats: ReportStat | null; + repoStats: ReportStat | null; +} export async function proceed( - data, + data: ReportingData, verbose = true ) { const reportHTMLPath = await utils.runInSpinner( @@ -22,8 +28,8 @@ export async function proceed( async() => HTML(data) ); - const { reporters, title } = localStorage.getConfig().report; - if (!reporters.includes("pdf")) { + const reportConfig = localStorage.getConfig().report; + if (!hasPdfReporter(reportConfig)) { return; } @@ -33,7 +39,7 @@ export async function proceed( start: "Using puppeteer to convert HTML content to PDF", verbose }, - async() => PDF(reportHTMLPath, { title }) + async() => PDF(reportHTMLPath, { title: reportConfig.title }) ); } diff --git a/src/reporting/pdf.js b/src/reporting/pdf.ts similarity index 74% rename from src/reporting/pdf.js rename to src/reporting/pdf.ts index b319f76..7186e89 100644 --- a/src/reporting/pdf.js +++ b/src/reporting/pdf.ts @@ -8,11 +8,18 @@ import puppeteer from "puppeteer"; // Import Internal Dependencies import * as CONSTANTS from "../constants.js"; import * as utils from "../utils/index.js"; +import type { RC } from "@nodesecure/rc"; + +export interface PdfOption { + title: string; + saveOnDisk?: boolean; + reportOutputLocation?: string +} export async function PDF( - reportHTMLPath, - options -) { + reportHTMLPath: string, + options: PdfOption +): Promise { const { title, saveOnDisk = true, @@ -49,3 +56,8 @@ export async function PDF( } } +export function hasPdfReporter(config: RC["report"]): config is NonNullable { + return config?.reporters?.includes("pdf") || false; +} + + diff --git a/src/reporting/template.js b/src/reporting/template.ts similarity index 65% rename from src/reporting/template.js rename to src/reporting/template.ts index 1951ac9..a3c8397 100644 --- a/src/reporting/template.js +++ b/src/reporting/template.ts @@ -9,6 +9,22 @@ import compile from "zup"; import * as utils from "../utils/index.js"; import * as CONSTANTS from "../constants.js"; import * as localStorage from "../localStorage.js"; +import { type ReportStat } from "../analysis/extractScannerData.js"; +import { } from "@nodesecure/rc"; + +interface TemplateGeneratorPayload { + report_theme: string; + report_title: string | undefined; + report_logo: string | undefined; + report_date: Intl.DateTimeFormat; + npm_stats: ReportStat; + git_stats: ReportStat; + charts: ReportChart; +} + +interface RenderOptions { + asset_location?: string +} const kHTMLStaticTemplate = readFileSync( path.join(CONSTANTS.DIRS.VIEWS, "template.html"), @@ -17,15 +33,15 @@ const kHTMLStaticTemplate = readFileSync( export class HTMLTemplateGenerator { constructor( - payload, - config = null + private readonly payload: TemplateGeneratorPayload, + private readonly config = null ) { this.payload = payload; this.config = config; } - render(options = {}) { - const { asset_location = "../dist" } = options; + render(options: RenderOptions = {}) { + const { asset_location = "../asset" } = options; const config = this.config ?? localStorage.getConfig().report; const compiledTemplate = compile(kHTMLStaticTemplate); diff --git a/src/utils/charts.js b/src/utils/charts.js deleted file mode 100644 index 765ce27..0000000 --- a/src/utils/charts.js +++ /dev/null @@ -1,38 +0,0 @@ -// Import Third-party Dependencies -import { taggedString } from "@nodesecure/utils"; - -// CONSTANTS -const kChartTemplate = taggedString`\tcreateChart("${0}", "${4}", { labels: [${1}], interpolate: ${3}, data: [${2}] });`; - -// eslint-disable-next-line max-params -function toChart(baliseName, data, interpolateName, type = "bar") { - const graphLabels = Object - .keys(data) - .map((key) => `"${key}"`) - .join(","); - - return kChartTemplate( - baliseName, - graphLabels, - Object.values(data).join(","), - interpolateName, - type - ); -} - -export function* generateChartArray(pkgStats, repoStats, config) { - const displayableCharts = config.charts.filter((chart) => chart.display); - - if (pkgStats !== null) { - for (const chart of displayableCharts) { - const name = chart.name.toLowerCase(); - yield toChart(`npm_${name}_canvas`, pkgStats[name], chart.interpolation, chart.type); - } - } - if (repoStats !== null) { - for (const chart of displayableCharts) { - const name = chart.name.toLowerCase(); - yield toChart(`git_${name}_canvas`, repoStats[name], chart.interpolation, chart.type); - } - } -} diff --git a/src/utils/charts.ts b/src/utils/charts.ts new file mode 100644 index 0000000..5538f25 --- /dev/null +++ b/src/utils/charts.ts @@ -0,0 +1,50 @@ +// Import Third-party Dependencies +import { taggedString } from "@nodesecure/utils"; +import { type ReportStat } from "../analysis/extractScannerData.js"; +import { type RC } from "@nodesecure/rc"; + +type ChartData = ReportStat["licenses"] | ReportStat["flags"] | ReportStat["warnings"] | ReportStat["extensions"]; + +// CONSTANTS +const kChartTemplate = taggedString`\tcreateChart("${0}", "${4}", { labels: [${1}], interpolate: ${3}, data: [${2}] });`; + +// eslint-disable-next-line max-params +function toChart(baliseName: string, data: ChartData, interpolateName: string, type = "bar") { + const graphLabels = Object + .keys(data) + .map((key) => `"${key}"`) + .join(","); + + return kChartTemplate( + baliseName, + graphLabels, + Object.values(data).join(","), + interpolateName, + type + ); +} + +export function* generateChartArray(pkgStats: ReportStat, repoStats: ReportStat, config: RC["report"]) { + const displayableCharts = config?.charts?.filter((chart) => chart.display); + + if (!displayableCharts) { + return; + } + + if (pkgStats !== null) { + for (const chart of displayableCharts) { + const name: Lowercase = toLowerCaseChartName(chart.name); + yield toChart(`npm_${name}_canvas`, pkgStats[name], chart.interpolation, chart.type); + } + } + if (repoStats !== null) { + for (const chart of displayableCharts) { + const name = toLowerCaseChartName(chart.name); + yield toChart(`git_${name}_canvas`, repoStats[name], chart.interpolation, chart.type); + } + } +} + +function toLowerCaseChartName(chartName: ReportChart["name"]): Lowercase["charts"]["name"]> { + return chartName.toLowerCase() as Lowercase; +} diff --git a/src/utils/cleanReportName.js b/src/utils/cleanReportName.ts similarity index 89% rename from src/utils/cleanReportName.js rename to src/utils/cleanReportName.ts index be5be39..9931a49 100644 --- a/src/utils/cleanReportName.js +++ b/src/utils/cleanReportName.ts @@ -12,9 +12,9 @@ import filenamify from "filenamify"; * @returns {string} */ export function cleanReportName( - name, - extension = null -) { + name: string, + extension: string | null = null +): string { const cleanName = filenamify(name); if (extension === null) { return cleanName; diff --git a/src/utils/cloneGITRepository.js b/src/utils/cloneGITRepository.ts similarity index 85% rename from src/utils/cloneGITRepository.js rename to src/utils/cloneGITRepository.ts index 1671d1f..854e529 100644 --- a/src/utils/cloneGITRepository.js +++ b/src/utils/cloneGITRepository.ts @@ -12,7 +12,7 @@ const execFilePromise = promisify(execFile); * * @returns {Promise} */ -export async function cloneGITRepository(dir, url) { +export async function cloneGITRepository(dir: string, url: string): Promise { const oauthUrl = url.replace("https://", `https://oauth2:${process.env.GIT_TOKEN}@`); await execFilePromise("git", ["clone", oauthUrl, dir]); diff --git a/src/utils/formatNpmPackages.js b/src/utils/formatNpmPackages.ts similarity index 85% rename from src/utils/formatNpmPackages.js rename to src/utils/formatNpmPackages.ts index 4814f28..623aac0 100644 --- a/src/utils/formatNpmPackages.js +++ b/src/utils/formatNpmPackages.ts @@ -5,9 +5,9 @@ * @returns {string[]} */ export function formatNpmPackages( - organizationPrefix, - packages -) { + organizationPrefix: string, + packages: string[] +): string[] { if (organizationPrefix === "") { return packages; } diff --git a/src/utils/index.js b/src/utils/index.ts similarity index 100% rename from src/utils/index.js rename to src/utils/index.ts diff --git a/src/utils/runInSpinner.js b/src/utils/runInSpinner.ts similarity index 65% rename from src/utils/runInSpinner.js rename to src/utils/runInSpinner.ts index 7e7b92b..b6739d2 100644 --- a/src/utils/runInSpinner.js +++ b/src/utils/runInSpinner.ts @@ -2,10 +2,17 @@ import { Spinner } from "@topcli/spinner"; import kleur from "kleur"; -export async function runInSpinner( - options, - asyncHandler -) { +interface RunInSpinnerOption { + title: string, + start: string, + verbose: boolean +} +type RunInSpinnerHandler = (spinner: Spinner) => Promise; + +export async function runInSpinner( + options: RunInSpinnerOption, + asyncHandler: RunInSpinnerHandler +): Promise { const { title, verbose = true, start = void 0 } = options; const spinner = new Spinner({ verbose }) @@ -19,7 +26,7 @@ export async function runInSpinner( return response; } - catch (err) { + catch (err: any) { spinner.failed(err.message); throw err; diff --git a/tsconfig.json b/tsconfig.json new file mode 100644 index 0000000..21c1a4c --- /dev/null +++ b/tsconfig.json @@ -0,0 +1,11 @@ +{ + "extends": "@openally/config.typescript", + "compilerOptions": { + "outDir": "dist", + "types": [ + "node" + ], + }, + "include": ["bin", "views"], + "exclude": ["node_modules", "dist", "asset", "eslint.config.mjs", "scripts"] + }