From 189a89d7dc8346219c3eba1579589f6245a6a756 Mon Sep 17 00:00:00 2001 From: Lucasion Date: Wed, 21 Jun 2023 17:18:33 +0200 Subject: [PATCH] Updated to typescript --- dist/clone.d.ts | 3 + dist/clone.d.ts.map | 1 + dist/clone.js | 276 ++++++++++++++ dist/hljstest.d.ts | 6 + dist/hljstest.d.ts.map | 1 + dist/hljstest.js | 15 + dist/syntax.d.ts | 8 + dist/syntax.d.ts.map | 1 + dist/syntax.js | 118 ++++++ output.pdf | Bin 9108 -> 9728 bytes package-lock.json | 47 ++- package.json | 16 +- clone.cjs => src/clone.ts | 669 +++++++++++++++++---------------- hljstest.js => src/hljstest.ts | 0 src/syntax.ts | 114 ++++++ syntax.js | 57 --- tsconfig.json | 110 ++++++ 17 files changed, 1056 insertions(+), 386 deletions(-) create mode 100644 dist/clone.d.ts create mode 100644 dist/clone.d.ts.map create mode 100644 dist/clone.js create mode 100644 dist/hljstest.d.ts create mode 100644 dist/hljstest.d.ts.map create mode 100644 dist/hljstest.js create mode 100644 dist/syntax.d.ts create mode 100644 dist/syntax.d.ts.map create mode 100644 dist/syntax.js rename clone.cjs => src/clone.ts (58%) rename hljstest.js => src/hljstest.ts (100%) create mode 100644 src/syntax.ts delete mode 100644 syntax.js create mode 100644 tsconfig.json diff --git a/dist/clone.d.ts b/dist/clone.d.ts new file mode 100644 index 0000000..b808589 --- /dev/null +++ b/dist/clone.d.ts @@ -0,0 +1,3 @@ +#!/usr/bin/env node +export {}; +//# sourceMappingURL=clone.d.ts.map \ No newline at end of file diff --git a/dist/clone.d.ts.map b/dist/clone.d.ts.map new file mode 100644 index 0000000..ecb0273 --- /dev/null +++ b/dist/clone.d.ts.map @@ -0,0 +1 @@ +{"version":3,"file":"clone.d.ts","sourceRoot":"","sources":["../src/clone.ts"],"names":[],"mappings":""} \ No newline at end of file diff --git a/dist/clone.js b/dist/clone.js new file mode 100644 index 0000000..7a9e1e8 --- /dev/null +++ b/dist/clone.js @@ -0,0 +1,276 @@ +#!/usr/bin/env node +"use strict"; +var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { + function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } + return new (P || (P = Promise))(function (resolve, reject) { + function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } + function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } + function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } + step((generator = generator.apply(thisArg, _arguments || [])).next()); + }); +}; +var __importDefault = (this && this.__importDefault) || function (mod) { + return (mod && mod.__esModule) ? mod : { "default": mod }; +}; +Object.defineProperty(exports, "__esModule", { value: true }); +const fs_1 = __importDefault(require("fs")); +const fsPromises = fs_1.default.promises; +const path_1 = __importDefault(require("path")); +const simple_git_1 = __importDefault(require("simple-git")); +const pdfkit_1 = __importDefault(require("pdfkit")); +const highlight_js_1 = __importDefault(require("highlight.js")); +const syntax_1 = require("./syntax"); +const isbinaryfile_1 = require("isbinaryfile"); +// TODO IDEAS +// TODO add option to condiotionaly remove comments from code +// TODO add option to condiotionaly remove empty lines from code +// TODO add option to condiotionaly add line numbers to code +// TODO add option to condiotionaly add linting to code +// TODO add option to make one pdf per file +let chalk; +let inquirer; +let ora; +const spinnerPromise = import("ora").then((oraModule) => { + ora = oraModule.default; + return ora("Setting everything up...").start(); +}); +Promise.all([ + import("chalk").then((chalkModule) => chalkModule.default), + import("inquirer").then((inquirerModule) => inquirerModule.default), + spinnerPromise +]) + .then(([chalkModule, inquirerModule, spinner]) => { + chalk = chalkModule; + inquirer = inquirerModule; + spinner.succeed("Setup complete"); + askForRepoUrl(); +}) + .catch((err) => { + spinnerPromise.then((spinner) => { + spinner.fail("An error occurred during setup"); + }); + console.error(err); +}); +function askForRepoUrl() { + return __awaiter(this, void 0, void 0, function* () { + const questions = [ + { + name: "repoUrl", + message: "Please provide a GitHub repository URL:", + validate: function (value) { + var pass = value.match(/^https:\/\/github.com\/[A-Za-z0-9_.-]+\/[A-Za-z0-9_.-]+$/); + if (pass) { + return true; + } + return "Please enter a valid GitHub repository URL."; + }, + }, + { + name: "optionalExcludedNames", + message: "Please provide a list of file names to exclude, separated by commas:", + filter: function (value) { + return value.split(",").map((v) => v.trim()); + }, + }, + { + name: "optionalExcludedExtensions", + message: "Please provide a list of file extensions to exclude, separated by commas:", + filter: function (value) { + return value.split(",").map((v) => v.trim()); + }, + }, + { + name: "addLineNumbers", + message: "Do you want to add line numbers to the PDF?", + choices: ["Yes", "No"], + filter: function (val) { + return val.toLowerCase() === "yes"; + }, + }, + { + name: "addLinting", + message: "Do you want to add linting to the PDF?", + choices: [/*"Yes",*/ "No"], + filter: function (val) { + return val.toLowerCase() === "yes"; + }, + }, + { + name: "removeComments", + message: "Do you want to remove comments from the PDF?", + choices: [/*"Yes",*/ "No"], + filter: function (val) { + return val.toLowerCase() === "yes"; + }, + }, + { + name: "removeEmptyLines", + message: "Do you want to remove empty lines from the PDF?", + choices: [/*"Yes",*/ "No"], + filter: function (val) { + return val.toLowerCase() === "yes"; + }, + }, + { + name: "onePdfPerFile", + message: "Do you want to make one PDF per file?", + choices: [/*"Yes",*/ "No"], + filter: function (val) { + return val.toLowerCase() === "yes"; + }, + }, + { + name: "outputFileName", + message: "Please provide an output file name:", + default: "output.pdf", + when(answers) { + return !answers.onePdfPerFile; + }, + }, + { + name: "outputFolderName", + message: "Please provide an output folder name:", + default: "./output", + when(answers) { + return answers.onePdfPerFile; + }, + }, + { + type: "list", + name: "keepRepo", + message: "Do you want to keep the cloned repository?", + choices: ["Yes", "No"], + filter: function (val) { + return val.toLowerCase() === "yes"; + }, + }, + ]; + console.log(chalk.cyanBright(` + +██████╗ ███████╗██████╗ ██████╗ ██████╗ ██████╗ ██████╗ ███████╗ +██╔══██╗██╔════╝██╔══██╗██╔═══██╗ ╚════██╗ ██╔══██╗██╔══██╗██╔════╝ +██████╔╝█████╗ ██████╔╝██║ ██║ █████╔╝ ██████╔╝██║ ██║█████╗ +██╔══██╗██╔══╝ ██╔═══╝ ██║ ██║ ██╔═══╝ ██╔═══╝ ██║ ██║██╔══╝ +██║ ██║███████╗██║ ╚██████╔╝ ███████╗ ██║ ██████╔╝██║ +╚═╝ ╚═╝╚══════╝╚═╝ ╚═════╝ ╚══════╝ ╚═╝ ╚═════╝ ╚═╝ + +Welcome to Repo-to-PDF! Let's get started... +`)); + const answers = yield inquirer.prompt(questions); + console.log(chalk.cyanBright("\nProcessing your request...\n")); + main(answers.repoUrl, answers.optionalExcludedNames, answers.optionalExcludedExtensions, answers.addLineNumbers, answers.addLinting, answers.removeComments, answers.removeEmptyLines, answers.onePdfPerFile, answers.outputFileName, answers.outputFolderName, answers.keepRepo); + }); +} +function main(repoUrl, optionalExcludedNames, optionalExcludedExtensions, addLineNumbers, addLinting, removeComments, removeEmptyLines, onePdfPerFile, outputFileName, outputFolderName, keepRepo) { + return __awaiter(this, void 0, void 0, function* () { + const gitP = (0, simple_git_1.default)(); + const tempDir = "./tempRepo"; + const doc = new pdfkit_1.default(); + doc.pipe(fs_1.default.createWriteStream(outputFileName)); + let fileCount = 0; + const spinner = ora(chalk.blueBright("Cloning repository...")).start(); + gitP + .clone(repoUrl, tempDir) + .then(() => { + spinner.succeed(chalk.greenBright("Repository cloned successfully")); + spinner.start(chalk.blueBright("Processing files...")); + appendFilesToPdf(tempDir, optionalExcludedNames, optionalExcludedExtensions).then(() => { + doc.end(); + spinner.succeed(chalk.greenBright(`PDF created with ${fileCount} files processed.`)); + if (!keepRepo) { + fs_1.default.rmSync(tempDir, { recursive: true, force: true }); + spinner.succeed(chalk.greenBright("Temporary repository has been deleted.")); + } + }); + }) + .catch((err) => { + spinner.fail(chalk.redBright("An error occurred")); + console.error(err); + }); + function appendFilesToPdf(directory, optionalExcludedNames, optionalExcludedExtensions) { + var _a; + return __awaiter(this, void 0, void 0, function* () { + const files = yield fsPromises.readdir(directory); + for (let file of files) { + const filePath = path_1.default.join(directory, file); + const stat = yield fsPromises.stat(filePath); + const excludedNames = [ + ".gitignore", + ".gitmodules", + "package-lock.json", + "yarn.lock", + ".git", + ]; + excludedNames.push(...optionalExcludedNames); + const excludedExtensions = [ + ".png", + ".yml", + ".jpg", + ".jpeg", + ".gif", + ".svg", + ".bmp", + ".webp", + ".ico", + ".mp4", + ".mov", + ".avi", + ".wmv", + ]; + excludedExtensions.push(...optionalExcludedExtensions); + // Check if file or directory should be excluded + if (excludedNames.includes(path_1.default.basename(filePath)) || + excludedExtensions.includes(path_1.default.extname(filePath))) { + continue; + } + if (stat.isFile()) { + fileCount++; + spinner.text = chalk.blueBright(`Processing files... (${fileCount} processed)`); + let fileName = path_1.default.relative(tempDir, filePath); + if ((0, isbinaryfile_1.isBinaryFileSync)(filePath)) { + const data = fs_1.default.readFileSync(filePath).toString("base64"); + doc + .addPage() + .font("Courier") + .fontSize(10) + .text(`${fileName}\n\nBASE64:\n\n${data}`, { lineGap: 4 }); + } + else { + let data = yield fsPromises.readFile(filePath, "utf8"); + data = data.replace(/Ð/g, "\n"); + data = data.replace(/\r\n/g, "\n"); + data = data.replace(/\r/g, "\n"); + doc + .addPage() + .font("Courier") + .fontSize(10) + .text(`${fileName}\n\n`, { lineGap: 4 }); + const highlightedCode = highlight_js_1.default.highlight(data, { language: "ps1" }).value; + const hlData = (0, syntax_1.htmlToJson)(highlightedCode); + let lineNum = 1; + for (let i = 0; i < hlData.length; i++) { + const { text, color } = hlData[i]; + if (i == 0 || ((_a = hlData[i - 1]) === null || _a === void 0 ? void 0 : _a.text) === "\n") + doc.text(String(lineNum++).padStart(3, " "), { continued: true }); + if (text !== "\n") + doc.text(text, { continued: true }); + else + doc.text(text); + if (color) + doc.fillColor(color); + else + doc.fillColor("black"); + } + } + } + else if (stat.isDirectory()) { + yield appendFilesToPdf(filePath, optionalExcludedNames, optionalExcludedExtensions); + } + } + }); + } + doc.on("finish", () => { + spinner.succeed(chalk.greenBright(`PDF created with ${fileCount} files processed.`)); + }); + }); +} diff --git a/dist/hljstest.d.ts b/dist/hljstest.d.ts new file mode 100644 index 0000000..3f2bf3a --- /dev/null +++ b/dist/hljstest.d.ts @@ -0,0 +1,6 @@ +declare const hljs: any; +declare const htmlToJson: any; +declare const code = "\nfunction helloWorld() {\n console.log(\"Hello, world!\");\n}\nhelloWorld();\n"; +declare const highlightedCode: any; +declare const data: any; +//# sourceMappingURL=hljstest.d.ts.map \ No newline at end of file diff --git a/dist/hljstest.d.ts.map b/dist/hljstest.d.ts.map new file mode 100644 index 0000000..467ac99 --- /dev/null +++ b/dist/hljstest.d.ts.map @@ -0,0 +1 @@ +{"version":3,"file":"hljstest.d.ts","sourceRoot":"","sources":["../src/hljstest.ts"],"names":[],"mappings":"AAAA,QAAA,MAAM,IAAI,KAA0B,CAAA;AACpC,QAAA,MAAQ,UAAU,KAAwB,CAAA;AAG1C,QAAA,MAAM,IAAI,qFAKT,CAAA;AAGD,QAAA,MAAM,eAAe,KAAiD,CAAA;AAGtE,QAAA,MAAM,IAAI,KAA8B,CAAC"} \ No newline at end of file diff --git a/dist/hljstest.js b/dist/hljstest.js new file mode 100644 index 0000000..32af8a5 --- /dev/null +++ b/dist/hljstest.js @@ -0,0 +1,15 @@ +"use strict"; +const hljs = require("highlight.js"); +const { htmlToJson } = require("./syntax"); +// Here's a simple JavaScript code snippet +const code = ` +function helloWorld() { + console.log("Hello, world!"); +} +helloWorld(); +`; +// Here, we're using the 'javascript' language for highlighting +const highlightedCode = hljs.highlight(code, { language: "js" }).value; +console.log(highlightedCode); +const data = htmlToJson(highlightedCode); +console.log(data); diff --git a/dist/syntax.d.ts b/dist/syntax.d.ts new file mode 100644 index 0000000..dc9fb61 --- /dev/null +++ b/dist/syntax.d.ts @@ -0,0 +1,8 @@ +/** + * @param {string} htmlCode + */ +export declare function htmlToJson(htmlCode: string): { + text: string; + color?: string; +}[]; +//# sourceMappingURL=syntax.d.ts.map \ No newline at end of file diff --git a/dist/syntax.d.ts.map b/dist/syntax.d.ts.map new file mode 100644 index 0000000..dd62f89 --- /dev/null +++ b/dist/syntax.d.ts.map @@ -0,0 +1 @@ +{"version":3,"file":"syntax.d.ts","sourceRoot":"","sources":["../src/syntax.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,wBAAgB,UAAU,CAAC,QAAQ,EAAE,MAAM,GAAG;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,KAAK,CAAC,EAAE,MAAM,CAAA;CAAE,EAAE,CA8G/E"} \ No newline at end of file diff --git a/dist/syntax.js b/dist/syntax.js new file mode 100644 index 0000000..9197cd1 --- /dev/null +++ b/dist/syntax.js @@ -0,0 +1,118 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.htmlToJson = void 0; +/** + * @param {string} htmlCode + */ +function htmlToJson(htmlCode) { + var _a; + const originalCode = htmlCode; + /** + * @type {{text: string, color?: string}[]} + */ + const data = []; + const elementRegex = /^]*>([^<]*)(?:<\/span>)?/; + const nonelementRegex = /[^<]*/; + while (htmlCode) { + const match = htmlCode.match(elementRegex); + if (match) { + const fullText = match[0]; + const cls = match[1]; + const text = match[2]; + let color = "black"; + // const color = cls; + const type = (_a = cls.split(" ")[0].toLowerCase()) !== null && _a !== void 0 ? _a : "unknown"; + switch (type) { + case "comment": + color = "#697070"; + break; + case "punctuation": + case "tag": + color = "#444a"; + break; + case "attribute": + case "doctag": + case "keyword": + case "meta": + case "keyword": + case "name": + case "selector-tag": + color = "#7ddcfe"; + break; + case "deletion": + case "number": + case "quote": + case "selector-class": + case "selector-id": + case "string": + case "template-tag": + case "type": + case "section": + case "title": + color = "#800"; + break; + case "link": + case "operator": + case "regexp": + case "selector-attr": + case "selector-pseudo": + case "symbol": + case "template-variable": + case "variable": + color = "#ab5656"; + break; + case "literal": + color = "#695"; + break; + case "addition": + case "built_in": + case "bullet": + case "code": + color = "#397300"; + break; + case "meta": + color = "#1f7199"; + break; + case "string": + color = "#38a"; + break; + } + console.log({ type, text, color, fullText }); + data.push({ text, color }); + htmlCode = htmlCode.slice(fullText.length); + } + else if (htmlCode.startsWith("")) { // Failed ending from hljs + const text = ""; + data.push({ text: "" }); // Empty text on purpose + htmlCode = htmlCode.slice(text.length); + } + else if (htmlCode.startsWith("\n")) { + const text = "\n"; + htmlCode = htmlCode.slice(1); + data.push({ text }); + } + else { + const match = htmlCode.match(nonelementRegex); + const text = match[0]; + htmlCode = htmlCode.slice(text.length); + data.push({ text }); + } + } + /** + * @type {{text: string, color?: string}[]} + */ + const fixedData = []; + // Fix newlines + for (let i = 0; i < data.length; i++) { + const { text, color } = data[i]; + const lines = text.split("\n"); + for (let j = 0; j < lines.length; j++) { + const line = lines[j]; + if (j > 0) + fixedData.push({ text: "\n" }); + fixedData.push({ text: line, color }); + } + } + return fixedData; +} +exports.htmlToJson = htmlToJson; diff --git a/output.pdf b/output.pdf index f8190a029d3aa403bf83ec85e399f87abbb5e306..ae7403c5dae368192ec0f79ea6e08a7b5fc4c791 100644 GIT binary patch delta 8062 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 BH)sF= diff --git a/package-lock.json b/package-lock.json index f573db3..fce7727 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9,6 +9,8 @@ "version": "1.2.1", "license": "MIT", "dependencies": { + "@types/inquirer": "^9.0.3", + "@types/pdfkit": "^0.12.10", "chalk": "^5.2.0", "highlight.js": "^11.8.0", "inquirer": "^9.2.6", @@ -16,10 +18,11 @@ "ora": "^6.3.1", "pdfkit": "^0.13.0", "puppeteer": "^20.7.3", - "simple-git": "^3.18.0" + "simple-git": "^3.18.0", + "typescript": "^5.1.3" }, "bin": { - "repo2pdf": "clone.cjs" + "repo2pdf": "clone.js" }, "engines": { "node": ">=14.0.0" @@ -170,11 +173,35 @@ "tslib": "^2.4.0" } }, + "node_modules/@types/inquirer": { + "version": "9.0.3", + "resolved": "https://registry.npmjs.org/@types/inquirer/-/inquirer-9.0.3.tgz", + "integrity": "sha512-CzNkWqQftcmk2jaCWdBTf9Sm7xSw4rkI1zpU/Udw3HX5//adEZUIm9STtoRP1qgWj0CWQtJ9UTvqmO2NNjhMJw==", + "dependencies": { + "@types/through": "*", + "rxjs": "^7.2.0" + } + }, "node_modules/@types/node": { "version": "20.3.1", "resolved": "https://registry.npmjs.org/@types/node/-/node-20.3.1.tgz", - "integrity": "sha512-EhcH/wvidPy1WeML3TtYFGR83UzjxeWRen9V402T8aUGYsCHOmfoisV3ZSg03gAFIbLq8TnWOJ0f4cALtnSEUg==", - "optional": true + "integrity": "sha512-EhcH/wvidPy1WeML3TtYFGR83UzjxeWRen9V402T8aUGYsCHOmfoisV3ZSg03gAFIbLq8TnWOJ0f4cALtnSEUg==" + }, + "node_modules/@types/pdfkit": { + "version": "0.12.10", + "resolved": "https://registry.npmjs.org/@types/pdfkit/-/pdfkit-0.12.10.tgz", + "integrity": "sha512-DPqNCuLXj50NehiFehndH+fzQLzb2fwHOLOvG+Zsm7rJBHgpMLeJrB4eC3RQf7Zl1uiWVYyBuFqVbZnveUb4mA==", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/through": { + "version": "0.0.30", + "resolved": "https://registry.npmjs.org/@types/through/-/through-0.0.30.tgz", + "integrity": "sha512-FvnCJljyxhPM3gkRgWmxmDZyAQSiBQQWLI0A0VFL0K7W1oRUrPJSqNO0NvTnLkBcotdlp3lKvaT0JrnyRDkzOg==", + "dependencies": { + "@types/node": "*" + } }, "node_modules/@types/yauzl": { "version": "2.10.0", @@ -2339,6 +2366,18 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/typescript": { + "version": "5.1.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.1.3.tgz", + "integrity": "sha512-XH627E9vkeqhlZFQuL+UsyAXEnibT0kWR2FWONlr4sTjvxyJYnyefgrkyECLzM5NenmKzRAy2rR/OlYLA1HkZw==", + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=14.17" + } + }, "node_modules/unbzip2-stream": { "version": "1.4.3", "resolved": "https://registry.npmjs.org/unbzip2-stream/-/unbzip2-stream-1.4.3.tgz", diff --git a/package.json b/package.json index 2b7ec93..b313bde 100644 --- a/package.json +++ b/package.json @@ -2,17 +2,18 @@ "name": "repo2pdf", "version": "1.2.1", "description": "A Node.js utility for generating a PDF document from a GitHub repository", - "main": "clone.cjs", + "main": "dist/clone.js", "bin": { - "repo2pdf": "./clone.cjs" + "repo2pdf": "dist/clone.js" }, "scripts": { - "start": "node clone.cjs", + "start": "node dist/clone.js", + "watch": "tsc -w", "test": "echo \"Error: no test specified\" && exit 1" }, "repository": { "type": "git", - "url": "https://github.com/BankkRoll/Repo-to-PDF.git" + "url": "https://github.com/malpou/Repo-to-PDF.git" }, "keywords": [ "github", @@ -36,6 +37,8 @@ "homepage": "https://github.com/BankkRoll/Repo-to-PDF#readme", "documentation": "https://github.com/BankkRoll/Repo-to-PDF#readme", "dependencies": { + "@types/inquirer": "^9.0.3", + "@types/pdfkit": "^0.12.10", "chalk": "^5.2.0", "highlight.js": "^11.8.0", "inquirer": "^9.2.6", @@ -43,9 +46,10 @@ "ora": "^6.3.1", "pdfkit": "^0.13.0", "puppeteer": "^20.7.3", - "simple-git": "^3.18.0" + "simple-git": "^3.18.0", + "typescript": "^5.1.3" }, "engines": { "node": ">=14.0.0" } -} +} \ No newline at end of file diff --git a/clone.cjs b/src/clone.ts similarity index 58% rename from clone.cjs rename to src/clone.ts index 8d77292..ccec6e2 100644 --- a/clone.cjs +++ b/src/clone.ts @@ -1,319 +1,350 @@ -#!/usr/bin/env node -const fs = require("fs") -const fsPromises = fs.promises -const path = require("path") -const { execSync } = require("child_process") - -const git = require("simple-git") -const PDFDocument = require("pdfkit") -const { default: hljs } = require("highlight.js") -const { htmlToJson } = require("./syntax") -const isBinaryFile = require("isbinaryfile").isBinaryFileSync - -// TODO IDEAS -// TODO add option to condiotionaly remove comments from code -// TODO add option to condiotionaly remove empty lines from code -// TODO add option to condiotionaly add line numbers to code -// TODO add option to condiotionaly add linting to code -// TODO add option to make one pdf per file - -let chalk -let inquirer -let ora - -const spinnerPromise = import("ora").then((oraModule) => { - ora = oraModule.default - return ora("Setting everything up...").start() -}) - -Promise.all([import("chalk"), import("inquirer"), spinnerPromise]) - .then(([chalkModule, inquirerModule, spinner]) => { - chalk = chalkModule.default - inquirer = inquirerModule.default - spinner.succeed("Setup complete") - askForRepoUrl() - }) - .catch((err) => { - spinnerPromise.then((spinner) => { - spinner.fail("An error occurred during setup") - }) - console.error(err) - }) - -async function askForRepoUrl() { - const questions = [ - { - name: "repoUrl", - message: "Please provide a GitHub repository URL:", - validate: function (value) { - var pass = value.match( - /^https:\/\/github.com\/[A-Za-z0-9_.-]+\/[A-Za-z0-9_.-]+$/ - ) - if (pass) { - return true - } - return "Please enter a valid GitHub repository URL." - }, - }, - { - name: "optionalExcludedNames", - message: - "Please provide a list of file names to exclude, separated by commas:", - filter: function (value) { - return value.split(",").map((v) => v.trim()) - }, - }, - { - name: "optionalExcludedExtensions", - message: - "Please provide a list of file extensions to exclude, separated by commas:", - filter: function (value) { - return value.split(",").map((v) => v.trim()) - }, - }, - { - name: "addLineNumbers", - message: "Do you want to add line numbers to the PDF?", - choices: ["Yes", "No"], - filter: function (val) { - return val.toLowerCase() === "yes" - }, - }, - { - name: "addLinting", - message: "Do you want to add linting to the PDF?", - choices: [/*"Yes",*/ "No"], - filter: function (val) { - return val.toLowerCase() === "yes" - }, - }, - { - name: "removeComments", - message: "Do you want to remove comments from the PDF?", - choices: [/*"Yes",*/ "No"], - filter: function (val) { - return val.toLowerCase() === "yes" - }, - }, - { - name: "removeEmptyLines", - message: "Do you want to remove empty lines from the PDF?", - choices: [/*"Yes",*/ "No"], - filter: function (val) { - return val.toLowerCase() === "yes" - }, - }, - { - name: "onePdfPerFile", - message: "Do you want to make one PDF per file?", - choices: [/*"Yes",*/ "No"], - filter: function (val) { - return val.toLowerCase() === "yes" - }, - }, - { - name: "outputFileName", - message: "Please provide an output file name:", - default: "output.pdf", - when(answers) { - return !answers.onePdfPerFile - }, - }, - { - name: "outputFolderName", - message: "Please provide an output folder name:", - default: "./output", - when(answers) { - return answers.onePdfPerFile - }, - }, - { - type: "list", - name: "keepRepo", - message: "Do you want to keep the cloned repository?", - choices: ["Yes", "No"], - filter: function (val) { - return val.toLowerCase() === "yes" - }, - }, - ] - - console.log( - chalk.cyanBright(` - -██████╗ ███████╗██████╗ ██████╗ ██████╗ ██████╗ ██████╗ ███████╗ -██╔══██╗██╔════╝██╔══██╗██╔═══██╗ ╚════██╗ ██╔══██╗██╔══██╗██╔════╝ -██████╔╝█████╗ ██████╔╝██║ ██║ █████╔╝ ██████╔╝██║ ██║█████╗ -██╔══██╗██╔══╝ ██╔═══╝ ██║ ██║ ██╔═══╝ ██╔═══╝ ██║ ██║██╔══╝ -██║ ██║███████╗██║ ╚██████╔╝ ███████╗ ██║ ██████╔╝██║ -╚═╝ ╚═╝╚══════╝╚═╝ ╚═════╝ ╚══════╝ ╚═╝ ╚═════╝ ╚═╝ - -Welcome to Repo-to-PDF! Let's get started... -`) - ) - - const answers = await inquirer.prompt(questions) - console.log(chalk.cyanBright("\nProcessing your request...\n")) - main( - answers.repoUrl, - answers.optionalExcludedNames, - answers.optionalExcludedExtensions, - answers.addLineNumbers, - answers.addLinting, - answers.removeComments, - answers.removeEmptyLines, - answers.onePdfPerFile, - answers.outputFileName, - answers.outputFolderName, - answers.keepRepo - ) -} - -async function main( - repoUrl, - optionalExcludedNames, - optionalExcludedExtensions, - addLineNumbers, - addLinting, - removeComments, - removeEmptyLines, - onePdfPerFile, - outputFileName, - outputFolderName, - keepRepo -) { - const gitP = git() - const tempDir = "./tempRepo" - const doc = new PDFDocument() - doc.pipe(fs.createWriteStream(outputFileName)) - - let fileCount = 0 - const spinner = ora(chalk.blueBright("Cloning repository...")).start() - - gitP - .clone(repoUrl, tempDir) - .then(() => { - spinner.succeed(chalk.greenBright("Repository cloned successfully")) - spinner.start(chalk.blueBright("Processing files...")) - appendFilesToPdf( - tempDir, - optionalExcludedNames, - optionalExcludedExtensions - ).then(() => { - doc.end() - spinner.succeed( - chalk.greenBright(`PDF created with ${fileCount} files processed.`) - ) - if (!keepRepo) { - fs.rmSync(tempDir, { recursive: true, force: true }) - spinner.succeed( - chalk.greenBright("Temporary repository has been deleted.") - ) - } - }) - }) - .catch((err) => { - spinner.fail(chalk.redBright("An error occurred")) - console.error(err) - }) - - async function appendFilesToPdf( - directory, - optionalExcludedNames, - optionalExcludedExtensions - ) { - const files = await fsPromises.readdir(directory) - for (let file of files) { - const filePath = path.join(directory, file) - const stat = await fsPromises.stat(filePath) - - const excludedNames = [ - ".gitignore", - ".gitmodules", - "package-lock.json", - "yarn.lock", - ".git", - ] - excludedNames.push(...optionalExcludedNames) - - const excludedExtensions = [ - ".png", - ".yml", - ".jpg", - ".jpeg", - ".gif", - ".svg", - ".bmp", - ".webp", - ".ico", - ".mp4", - ".mov", - ".avi", - ".wmv", - ] - excludedExtensions.push(...optionalExcludedExtensions) - - // Check if file or directory should be excluded - if ( - excludedNames.includes(path.basename(filePath)) || - excludedExtensions.includes(path.extname(filePath)) - ) { - continue - } - - if (stat.isFile()) { - fileCount++ - spinner.text = chalk.blueBright( - `Processing files... (${fileCount} processed)` - ) - let fileName = path.relative(tempDir, filePath) - if (isBinaryFile(filePath)) { - const data = fs.readFileSync(filePath).toString("base64") - doc - .addPage() - .font("Courier") - .fontSize(10) - .text(`${fileName}\n\nBASE64:\n\n${data}`, { lineGap: 4 }) - } else { - let data = await fsPromises.readFile(filePath, "utf8") - data = data.replace(/Ð/g, "\n") - data = data.replace(/\r\n/g, "\n") - data = data.replace(/\r/g, "\n") - - doc - .addPage() - .font("Courier") - .fontSize(10) - .text(`${fileName}\n\n`, { lineGap: 4 }) - - const highlightedCode = hljs.highlight(data, { language: "ps1" }).value - const hlData = htmlToJson(highlightedCode); - let lineNum = 1; - for (let i = 0; i < hlData.length; i++) { - const { text, color } = hlData[i]; - if (i == 0 || hlData[i - 1]?.text === "\n") - doc.text(String(lineNum++).padStart(3, " "), { continued: true }); - - if (text !== "\n") doc.text(text, { continued: true }); - else doc.text(text); - - if (color) doc.fillColor(color); - else doc.fillColor("black"); - } - } - } else if (stat.isDirectory()) { - await appendFilesToPdf( - filePath, - optionalExcludedNames, - optionalExcludedExtensions - ) - } - } - } - - doc.on("finish", () => { - spinner.succeed( - chalk.greenBright(`PDF created with ${fileCount} files processed.`) - ) - }) -} +#!/usr/bin/env node +import fs from "fs" +const fsPromises = fs.promises +import path from "path" +import { execSync } from "child_process" + +import git from "simple-git" +import PDFDocument from "pdfkit" +import { default as hljs } from "highlight.js" +import { htmlToJson } from "./syntax" +import { isBinaryFileSync } from "isbinaryfile" + +//@ts-ignore +import type chalkType from "chalk"; +//@ts-ignore +import type inquirerType from "inquirer"; +//@ts-ignore +import type oraType from "ora"; + +// TODO IDEAS +// TODO add option to condiotionaly remove comments from code +// TODO add option to condiotionaly remove empty lines from code +// TODO add option to condiotionaly add line numbers to code +// TODO add option to condiotionaly add linting to code +// TODO add option to make one pdf per file + +let chalk: typeof chalkType; +let inquirer: typeof inquirerType; +let ora: typeof oraType; + +const spinnerPromise = import("ora").then((oraModule) => { + ora = oraModule.default + return ora("Setting everything up...").start() +}) + +Promise.all([ + import("chalk").then((chalkModule) => chalkModule.default), + import("inquirer").then((inquirerModule) => inquirerModule.default), + spinnerPromise]) + .then(([chalkModule, inquirerModule, spinner]) => { + chalk = chalkModule + inquirer = inquirerModule + spinner.succeed("Setup complete") + askForRepoUrl() + }) + .catch((err) => { + spinnerPromise.then((spinner) => { + spinner.fail("An error occurred during setup") + }) + console.error(err) + }) + +async function askForRepoUrl() { + const questions: { + type?: string, + name: [ + "repoUrl", + "optionalExcludedNames", + "optionalExcludedExtensions", + "addLineNumbers", + "addLinting", + "removeComments", + "removeEmptyLines", + "onePdfPerFile", + "outputFileName", + "outputFolderName", + "keepRepo" + ][number], + message: string, + validate?: (value: string) => boolean | string, + filter?: (value: string) => boolean | string | string[], + choices?: string[], + default?: string | string[], + when?: (answers: any) => boolean, + }[] = [ + { + name: "repoUrl", + message: "Please provide a GitHub repository URL:", + validate: function (value: string) { + var pass = value.match( + /^https:\/\/github.com\/[A-Za-z0-9_.-]+\/[A-Za-z0-9_.-]+$/ + ) + if (pass) { + return true + } + return "Please enter a valid GitHub repository URL." + }, + }, + { + name: "optionalExcludedNames", + message: + "Please provide a list of file names to exclude, separated by commas:", + filter: function (value: string) { + return value.split(",").map((v) => v.trim()) + }, + }, + { + name: "optionalExcludedExtensions", + message: + "Please provide a list of file extensions to exclude, separated by commas:", + filter: function (value: string) { + return value.split(",").map((v: string) => v.trim()) + }, + }, + { + name: "addLineNumbers", + message: "Do you want to add line numbers to the PDF?", + choices: ["Yes", "No"], + filter: function (val: string) { + return val.toLowerCase() === "yes" + }, + }, + { + name: "addLinting", + message: "Do you want to add linting to the PDF?", + choices: [/*"Yes",*/ "No"], + filter: function (val: string) { + return val.toLowerCase() === "yes" + }, + }, + { + name: "removeComments", + message: "Do you want to remove comments from the PDF?", + choices: [/*"Yes",*/ "No"], + filter: function (val: string) { + return val.toLowerCase() === "yes" + }, + }, + { + name: "removeEmptyLines", + message: "Do you want to remove empty lines from the PDF?", + choices: [/*"Yes",*/ "No"], + filter: function (val: string) { + return val.toLowerCase() === "yes" + }, + }, + { + name: "onePdfPerFile", + message: "Do you want to make one PDF per file?", + choices: [/*"Yes",*/ "No"], + filter: function (val: string) { + return val.toLowerCase() === "yes" + }, + }, + { + name: "outputFileName", + message: "Please provide an output file name:", + default: "output.pdf", + when(answers: { onePdfPerFile: any }) { + return !answers.onePdfPerFile + }, + }, + { + name: "outputFolderName", + message: "Please provide an output folder name:", + default: "./output", + when(answers: { onePdfPerFile: any }) { + return answers.onePdfPerFile + }, + }, + { + type: "list", + name: "keepRepo", + message: "Do you want to keep the cloned repository?", + choices: ["Yes", "No"], + filter: function (val: string) { + return val.toLowerCase() === "yes" + }, + }, + ] + + console.log( + chalk.cyanBright(` + +██████╗ ███████╗██████╗ ██████╗ ██████╗ ██████╗ ██████╗ ███████╗ +██╔══██╗██╔════╝██╔══██╗██╔═══██╗ ╚════██╗ ██╔══██╗██╔══██╗██╔════╝ +██████╔╝█████╗ ██████╔╝██║ ██║ █████╔╝ ██████╔╝██║ ██║█████╗ +██╔══██╗██╔══╝ ██╔═══╝ ██║ ██║ ██╔═══╝ ██╔═══╝ ██║ ██║██╔══╝ +██║ ██║███████╗██║ ╚██████╔╝ ███████╗ ██║ ██████╔╝██║ +╚═╝ ╚═╝╚══════╝╚═╝ ╚═════╝ ╚══════╝ ╚═╝ ╚═════╝ ╚═╝ + +Welcome to Repo-to-PDF! Let's get started... +`) + ) + + const answers = await inquirer.prompt(questions) + console.log(chalk.cyanBright("\nProcessing your request...\n")) + main( + answers.repoUrl, + answers.optionalExcludedNames, + answers.optionalExcludedExtensions, + answers.addLineNumbers, + answers.addLinting, + answers.removeComments, + answers.removeEmptyLines, + answers.onePdfPerFile, + answers.outputFileName, + answers.outputFolderName, + answers.keepRepo + ) +} + +async function main( + repoUrl: string, + optionalExcludedNames: any, + optionalExcludedExtensions: any, + addLineNumbers: any, + addLinting: any, + removeComments: any, + removeEmptyLines: any, + onePdfPerFile: any, + outputFileName: fs.PathLike, + outputFolderName: any, + keepRepo: any +) { + const gitP = git() + const tempDir = "./tempRepo" + const doc = new PDFDocument() + doc.pipe(fs.createWriteStream(outputFileName)) + + let fileCount = 0 + const spinner = ora(chalk.blueBright("Cloning repository...")).start() + + gitP + .clone(repoUrl, tempDir) + .then(() => { + spinner.succeed(chalk.greenBright("Repository cloned successfully")) + spinner.start(chalk.blueBright("Processing files...")) + appendFilesToPdf( + tempDir, + optionalExcludedNames, + optionalExcludedExtensions + ).then(() => { + doc.end() + spinner.succeed( + chalk.greenBright(`PDF created with ${fileCount} files processed.`) + ) + if (!keepRepo) { + fs.rmSync(tempDir, { recursive: true, force: true }) + spinner.succeed( + chalk.greenBright("Temporary repository has been deleted.") + ) + } + }) + }) + .catch((err) => { + spinner.fail(chalk.redBright("An error occurred")) + console.error(err) + }) + + async function appendFilesToPdf( + directory: string, + optionalExcludedNames: any, + optionalExcludedExtensions: any + ) { + const files = await fsPromises.readdir(directory) + for (let file of files) { + const filePath = path.join(directory, file) + const stat = await fsPromises.stat(filePath) + + const excludedNames = [ + ".gitignore", + ".gitmodules", + "package-lock.json", + "yarn.lock", + ".git", + ] + excludedNames.push(...optionalExcludedNames) + + const excludedExtensions = [ + ".png", + ".yml", + ".jpg", + ".jpeg", + ".gif", + ".svg", + ".bmp", + ".webp", + ".ico", + ".mp4", + ".mov", + ".avi", + ".wmv", + ] + excludedExtensions.push(...optionalExcludedExtensions) + + // Check if file or directory should be excluded + if ( + excludedNames.includes(path.basename(filePath)) || + excludedExtensions.includes(path.extname(filePath)) + ) { + continue + } + + if (stat.isFile()) { + fileCount++ + spinner.text = chalk.blueBright( + `Processing files... (${fileCount} processed)` + ) + let fileName = path.relative(tempDir, filePath) + if (isBinaryFileSync(filePath)) { + const data = fs.readFileSync(filePath).toString("base64") + doc + .addPage() + .font("Courier") + .fontSize(10) + .text(`${fileName}\n\nBASE64:\n\n${data}`, { lineGap: 4 }) + } else { + let data = await fsPromises.readFile(filePath, "utf8") + data = data.replace(/Ð/g, "\n") + data = data.replace(/\r\n/g, "\n") + data = data.replace(/\r/g, "\n") + + doc + .addPage() + .font("Courier") + .fontSize(10) + .text(`${fileName}\n\n`, { lineGap: 4 }) + + const highlightedCode = hljs.highlight(data, { language: "ps1" }).value + const hlData = htmlToJson(highlightedCode); + let lineNum = 1; + for (let i = 0; i < hlData.length; i++) { + const { text, color } = hlData[i]; + if (i == 0 || hlData[i - 1]?.text === "\n") + doc.text(String(lineNum++).padStart(3, " "), { continued: true }); + + if (text !== "\n") doc.text(text, { continued: true }); + else doc.text(text); + + if (color) doc.fillColor(color); + else doc.fillColor("black"); + } + } + } else if (stat.isDirectory()) { + await appendFilesToPdf( + filePath, + optionalExcludedNames, + optionalExcludedExtensions + ) + } + } + } + + doc.on("finish", () => { + spinner.succeed( + chalk.greenBright(`PDF created with ${fileCount} files processed.`) + ) + }) +} diff --git a/hljstest.js b/src/hljstest.ts similarity index 100% rename from hljstest.js rename to src/hljstest.ts diff --git a/src/syntax.ts b/src/syntax.ts new file mode 100644 index 0000000..2779d81 --- /dev/null +++ b/src/syntax.ts @@ -0,0 +1,114 @@ +/** + * @param {string} htmlCode + */ +export function htmlToJson(htmlCode: string): { text: string, color?: string }[] { + const originalCode = htmlCode; + /** + * @type {{text: string, color?: string}[]} + */ + const data: { text: string, color?: string }[] = []; + const elementRegex = /^]*>([^<]*)(?:<\/span>)?/; + const nonelementRegex = /[^<]*/; + while (htmlCode) { + const match = htmlCode.match(elementRegex); + if (match) { + const fullText = match[0]; + const cls = match[1]; + const text = match[2]; + let color = "black"; + // const color = cls; + const type = cls.split(" ")[0].toLowerCase() ?? "unknown"; + switch (type) { + case "comment": + color = "#697070"; + break; + case "punctuation": + case "tag": + color = "#444a"; + break; + case "attribute": + case "doctag": + case "keyword": + case "meta": + case "keyword": + case "name": + case "selector-tag": + color = "#7ddcfe"; + break; + case "deletion": + case "number": + case "quote": + case "selector-class": + case "selector-id": + case "string": + case "template-tag": + case "type": + case "section": + case "title": + color = "#800"; + break; + case "link": + case "operator": + case "regexp": + case "selector-attr": + case "selector-pseudo": + case "symbol": + case "template-variable": + case "variable": + color = "#ab5656"; + break; + case "literal": + color = "#695"; + break; + case "addition": + case "built_in": + case "bullet": + case "code": + color = "#397300"; + break; + case "meta": + color = "#1f7199"; + break; + case "string": + color = "#38a"; + break; + } + console.log({ type, text, color, fullText }); + data.push({ text, color }); + htmlCode = htmlCode.slice(fullText.length); + } + else if (htmlCode.startsWith("")) { // Failed ending from hljs + const text = ""; + data.push({ text: "" }); // Empty text on purpose + htmlCode = htmlCode.slice(text.length); + } + else if (htmlCode.startsWith("\n")) { + const text = "\n"; + htmlCode = htmlCode.slice(1); + data.push({ text }); + } + else { + const match = htmlCode.match(nonelementRegex); + const text = match![0]; + htmlCode = htmlCode.slice(text.length); + data.push({ text }); + } + } + + /** + * @type {{text: string, color?: string}[]} + */ + const fixedData: { text: string, color?: string }[] = []; + // Fix newlines + for (let i = 0; i < data.length; i++) { + const { text, color } = data[i]; + const lines = text.split("\n"); + for (let j = 0; j < lines.length; j++) { + const line = lines[j]; + if (j > 0) fixedData.push({ text: "\n" }); + fixedData.push({ text: line, color }); + } + } + + return fixedData; +} \ No newline at end of file diff --git a/syntax.js b/syntax.js deleted file mode 100644 index acb6038..0000000 --- a/syntax.js +++ /dev/null @@ -1,57 +0,0 @@ -/** - * @param {string} htmlCode - */ -function htmlToJson(htmlCode) { - const originalCode = htmlCode; - /** - * @type {{text: string, color?: string}[]} - */ - const data = []; - const elementRegex = /^]*>([^<]*)(?:<\/span>)?/; - const nonelementRegex = /[^<]*/; - while (htmlCode) { - const match = htmlCode.match(elementRegex); - if (match) { - const fullText = match[0]; - const cls = match[1]; - const text = match[2]; - const color = "blue"; - // const color = cls; - data.push({ text, color }); - htmlCode = htmlCode.slice(fullText.length); - } - else if (htmlCode.startsWith("<\/span>")) { // Failed ending from hljs - htmlCode = htmlCode.slice("<\/span>".length); - } - else if (htmlCode.startsWith("\n")) { - const text = "\n"; - htmlCode = htmlCode.slice(1); - data.push({ text }); - } - else { - const match = htmlCode.match(nonelementRegex); - const text = match[0]; - htmlCode = htmlCode.slice(text.length); - data.push({ text }); - } - } - - /** - * @type {{text: string, color?: string}[]} - */ - const fixedData = []; - // Fix newlines - for (let i = 0; i < data.length; i++) { - const { text, color } = data[i]; - const lines = text.split("\n"); - for (let j = 0; j < lines.length; j++) { - const line = lines[j]; - if (j > 0) fixedData.push({ text: "\n" }); - fixedData.push({ text: line, color }); - } - } - - return fixedData; -} - -exports.htmlToJson = htmlToJson; \ No newline at end of file diff --git a/tsconfig.json b/tsconfig.json new file mode 100644 index 0000000..5f9e0de --- /dev/null +++ b/tsconfig.json @@ -0,0 +1,110 @@ +{ + "compilerOptions": { + /* Visit https://aka.ms/tsconfig to read more about this file */ + + /* Projects */ + // "incremental": true, /* Save .tsbuildinfo files to allow for incremental compilation of projects. */ + // "composite": true, /* Enable constraints that allow a TypeScript project to be used with project references. */ + // "tsBuildInfoFile": "./.tsbuildinfo", /* Specify the path to .tsbuildinfo incremental compilation file. */ + // "disableSourceOfProjectReferenceRedirect": true, /* Disable preferring source files instead of declaration files when referencing composite projects. */ + // "disableSolutionSearching": true, /* Opt a project out of multi-project reference checking when editing. */ + // "disableReferencedProjectLoad": true, /* Reduce the number of projects loaded automatically by TypeScript. */ + + /* Language and Environment */ + "target": "es2016", /* Set the JavaScript language version for emitted JavaScript and include compatible library declarations. */ + // "lib": [], /* Specify a set of bundled library declaration files that describe the target runtime environment. */ + // "jsx": "preserve", /* Specify what JSX code is generated. */ + // "experimentalDecorators": true, /* Enable experimental support for TC39 stage 2 draft decorators. */ + // "emitDecoratorMetadata": true, /* Emit design-type metadata for decorated declarations in source files. */ + // "jsxFactory": "", /* Specify the JSX factory function used when targeting React JSX emit, e.g. 'React.createElement' or 'h'. */ + // "jsxFragmentFactory": "", /* Specify the JSX Fragment reference used for fragments when targeting React JSX emit e.g. 'React.Fragment' or 'Fragment'. */ + // "jsxImportSource": "", /* Specify module specifier used to import the JSX factory functions when using 'jsx: react-jsx*'. */ + // "reactNamespace": "", /* Specify the object invoked for 'createElement'. This only applies when targeting 'react' JSX emit. */ + // "noLib": true, /* Disable including any library files, including the default lib.d.ts. */ + // "useDefineForClassFields": true, /* Emit ECMAScript-standard-compliant class fields. */ + // "moduleDetection": "auto", /* Control what method is used to detect module-format JS files. */ + + /* Modules */ + "module": "CommonJS", /* Specify what module code is generated. */ + // "rootDir": "./", /* Specify the root folder within your source files. */ + "moduleResolution": "Node16", /* Specify how TypeScript looks up a file from a given module specifier. */ + // "baseUrl": "./", /* Specify the base directory to resolve non-relative module names. */ + // "paths": {}, /* Specify a set of entries that re-map imports to additional lookup locations. */ + // "rootDirs": [], /* Allow multiple folders to be treated as one when resolving modules. */ + // "typeRoots": [], /* Specify multiple folders that act like './node_modules/@types'. */ + // "types": [], /* Specify type package names to be included without being referenced in a source file. */ + // "allowUmdGlobalAccess": true, /* Allow accessing UMD globals from modules. */ + // "moduleSuffixes": [], /* List of file name suffixes to search when resolving a module. */ + // "resolveJsonModule": true, /* Enable importing .json files. */ + // "noResolve": true, /* Disallow 'import's, 'require's or ''s from expanding the number of files TypeScript should add to a project. */ + + /* JavaScript Support */ + // "allowJs": true, /* Allow JavaScript files to be a part of your program. Use the 'checkJS' option to get errors from these files. */ + // "checkJs": true, /* Enable error reporting in type-checked JavaScript files. */ + // "maxNodeModuleJsDepth": 1, /* Specify the maximum folder depth used for checking JavaScript files from 'node_modules'. Only applicable with 'allowJs'. */ + + /* Emit */ + "declaration": true, /* Generate .d.ts files from TypeScript and JavaScript files in your project. */ + "declarationMap": true, /* Create sourcemaps for d.ts files. */ + // "emitDeclarationOnly": true, /* Only output d.ts files and not JavaScript files. */ + // "sourceMap": true, /* Create source map files for emitted JavaScript files. */ + // "outFile": "./", /* Specify a file that bundles all outputs into one JavaScript file. If 'declaration' is true, also designates a file that bundles all .d.ts output. */ + "outDir": "./dist", /* Specify an output folder for all emitted files. */ + // "removeComments": true, /* Disable emitting comments. */ + // "noEmit": true, /* Disable emitting files from a compilation. */ + // "importHelpers": true, /* Allow importing helper functions from tslib once per project, instead of including them per-file. */ + // "importsNotUsedAsValues": "remove", /* Specify emit/checking behavior for imports that are only used for types. */ + // "downlevelIteration": true, /* Emit more compliant, but verbose and less performant JavaScript for iteration. */ + // "sourceRoot": "", /* Specify the root path for debuggers to find the reference source code. */ + // "mapRoot": "", /* Specify the location where debugger should locate map files instead of generated locations. */ + // "inlineSourceMap": true, /* Include sourcemap files inside the emitted JavaScript. */ + // "inlineSources": true, /* Include source code in the sourcemaps inside the emitted JavaScript. */ + // "emitBOM": true, /* Emit a UTF-8 Byte Order Mark (BOM) in the beginning of output files. */ + // "newLine": "crlf", /* Set the newline character for emitting files. */ + // "stripInternal": true, /* Disable emitting declarations that have '@internal' in their JSDoc comments. */ + // "noEmitHelpers": true, /* Disable generating custom helper functions like '__extends' in compiled output. */ + // "noEmitOnError": true, /* Disable emitting files if any type checking errors are reported. */ + // "preserveConstEnums": true, /* Disable erasing 'const enum' declarations in generated code. */ + // "declarationDir": "./", /* Specify the output directory for generated declaration files. */ + // "preserveValueImports": true, /* Preserve unused imported values in the JavaScript output that would otherwise be removed. */ + + /* Interop Constraints */ + // "isolatedModules": true, /* Ensure that each file can be safely transpiled without relying on other imports. */ + // "allowSyntheticDefaultImports": true, /* Allow 'import x from y' when a module doesn't have a default export. */ + "esModuleInterop": true, /* Emit additional JavaScript to ease support for importing CommonJS modules. This enables 'allowSyntheticDefaultImports' for type compatibility. */ + // "preserveSymlinks": true, /* Disable resolving symlinks to their realpath. This correlates to the same flag in node. */ + "forceConsistentCasingInFileNames": true, /* Ensure that casing is correct in imports. */ + + /* Type Checking */ + "strict": true, /* Enable all strict type-checking options. */ + // "noImplicitAny": true, /* Enable error reporting for expressions and declarations with an implied 'any' type. */ + // "strictNullChecks": true, /* When type checking, take into account 'null' and 'undefined'. */ + // "strictFunctionTypes": true, /* When assigning functions, check to ensure parameters and the return values are subtype-compatible. */ + // "strictBindCallApply": true, /* Check that the arguments for 'bind', 'call', and 'apply' methods match the original function. */ + // "strictPropertyInitialization": true, /* Check for class properties that are declared but not set in the constructor. */ + // "noImplicitThis": true, /* Enable error reporting when 'this' is given the type 'any'. */ + // "useUnknownInCatchVariables": true, /* Default catch clause variables as 'unknown' instead of 'any'. */ + // "alwaysStrict": true, /* Ensure 'use strict' is always emitted. */ + // "noUnusedLocals": true, /* Enable error reporting when local variables aren't read. */ + // "noUnusedParameters": true, /* Raise an error when a function parameter isn't read. */ + // "exactOptionalPropertyTypes": true, /* Interpret optional property types as written, rather than adding 'undefined'. */ + // "noImplicitReturns": true, /* Enable error reporting for codepaths that do not explicitly return in a function. */ + // "noFallthroughCasesInSwitch": true, /* Enable error reporting for fallthrough cases in switch statements. */ + // "noUncheckedIndexedAccess": true, /* Add 'undefined' to a type when accessed using an index. */ + // "noImplicitOverride": true, /* Ensure overriding members in derived classes are marked with an override modifier. */ + // "noPropertyAccessFromIndexSignature": true, /* Enforces using indexed accessors for keys declared using an indexed type. */ + // "allowUnusedLabels": true, /* Disable error reporting for unused labels. */ + // "allowUnreachableCode": true, /* Disable error reporting for unreachable code. */ + + /* Completeness */ + // "skipDefaultLibCheck": true, /* Skip type checking .d.ts files that are included with TypeScript. */ + "skipLibCheck": true /* Skip type checking all .d.ts files. */ + }, + "include": [ + "src" + ], + "exclude": [ + "node_modules", + "dist" + ] +}