@bigbinary/neeto-audit-frontend 1.0.2

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.
Files changed (38) hide show
  1. package/.github/CODEOWNERS +1 -0
  2. package/.github/pull_request_template.md +14 -0
  3. package/.github/release-drafter.yml +8 -0
  4. package/.github/workflows/auto_rebase_from_main.yml +15 -0
  5. package/.github/workflows/create_and_publish_release.yml +100 -0
  6. package/.nvmrc +1 -0
  7. package/README.md +3 -0
  8. package/common/.husky/_/husky.sh +36 -0
  9. package/common/.husky/helpers/lint_staged.sh +5 -0
  10. package/common/.husky/helpers/prevent_conflict_markers.sh +34 -0
  11. package/common/.husky/helpers/prevent_pushing_to_main.sh +22 -0
  12. package/common/.husky/pre-commit +9 -0
  13. package/common/.husky/pre-push +5 -0
  14. package/common/eslint/.eslintrc.js +1 -0
  15. package/common/prettier/.prettierrc.js +16 -0
  16. package/common/prettier/tailwind.config.js +11 -0
  17. package/common/recommendedDependencies/extension.js +11 -0
  18. package/common/recommendedDependencies/frontend.js +109 -0
  19. package/common/recommendedDependencies/index.js +5 -0
  20. package/common/recommendedDependencies/widget.js +11 -0
  21. package/dist/index.js +6099 -0
  22. package/package.json +36 -0
  23. package/rollup.config.js +33 -0
  24. package/src/cli.js +37 -0
  25. package/src/utils/index.js +159 -0
  26. package/src/verifiers/currentNodeVersion/constants.js +3 -0
  27. package/src/verifiers/currentNodeVersion/index.js +37 -0
  28. package/src/verifiers/currentNodeVersion/utils.js +5 -0
  29. package/src/verifiers/eslint/constants.js +7 -0
  30. package/src/verifiers/eslint/index.js +37 -0
  31. package/src/verifiers/husky/constants.js +9 -0
  32. package/src/verifiers/husky/index.js +46 -0
  33. package/src/verifiers/index.js +16 -0
  34. package/src/verifiers/prettier/constants.js +7 -0
  35. package/src/verifiers/prettier/index.js +40 -0
  36. package/src/verifiers/recommendedPackageVersions/constants.js +9 -0
  37. package/src/verifiers/recommendedPackageVersions/index.js +41 -0
  38. package/src/verifiers/recommendedPackageVersions/utils.js +30 -0
package/package.json ADDED
@@ -0,0 +1,36 @@
1
+ {
2
+ "name": "@bigbinary/neeto-audit-frontend",
3
+ "version": "1.0.2",
4
+ "description": "Audits neeto frontend codebase for issues and suggests a fix.",
5
+ "type": "module",
6
+ "bin": "./dist/index.js",
7
+ "keywords": [
8
+ "neeto",
9
+ "audit",
10
+ "frontend"
11
+ ],
12
+ "scripts": {
13
+ "build": "rollup -c"
14
+ },
15
+ "author": "Thejus Paul <thejuspaul@protonmail.ch>",
16
+ "license": "UNLICENSED",
17
+ "devDependencies": {
18
+ "@rollup/plugin-commonjs": "25.0.2",
19
+ "@rollup/plugin-json": "6.0.0",
20
+ "@rollup/plugin-node-resolve": "15.1.0",
21
+ "rollup": "3.26.2",
22
+ "rollup-plugin-analyzer": "4.0.0",
23
+ "rollup-plugin-cleaner": "1.0.0",
24
+ "rollup-plugin-executable-script": "1.0.1",
25
+ "rollup-plugin-peer-deps-external": "2.2.4"
26
+ },
27
+ "dependencies": {
28
+ "chalk": "5.3.0",
29
+ "commander": "11.0.0",
30
+ "ramda": "0.29.0"
31
+ },
32
+ "engines": {
33
+ "node": ">=18.16",
34
+ "pnpm": ">=8.6"
35
+ }
36
+ }
@@ -0,0 +1,33 @@
1
+ import path from "path";
2
+
3
+ import cleaner from "rollup-plugin-cleaner";
4
+ import analyze from "rollup-plugin-analyzer";
5
+ import { nodeResolve } from "@rollup/plugin-node-resolve";
6
+ import json from "@rollup/plugin-json";
7
+ import commonjs from "@rollup/plugin-commonjs";
8
+ import executableScript from "rollup-plugin-executable-script";
9
+
10
+ const output = {
11
+ file: "dist/index.js",
12
+ format: "es",
13
+ name: "neetoAudit",
14
+ };
15
+
16
+ export default {
17
+ input: "./src/cli.js",
18
+ output,
19
+ plugins: [
20
+ // To clean dist directory.
21
+ cleaner({ targets: [path.resolve("./dist")] }),
22
+ // Analyze created bundle.
23
+ analyze({ summaryOnly: true }),
24
+ // Locates third party modules in node_modules.
25
+ nodeResolve({ exportConditions: ["node"] }),
26
+ // To convert CommonJS modules to ES6.
27
+ commonjs({ include: /node_modules/ }),
28
+ // To convert .json files to ES6 modules.
29
+ json(),
30
+ // To convert output file to an node executable file.
31
+ executableScript(),
32
+ ],
33
+ };
package/src/cli.js ADDED
@@ -0,0 +1,37 @@
1
+ #!/usr/bin/env node
2
+ import { renderErrors } from "./utils/index.js";
3
+ import verifiers from "./verifiers/index.js";
4
+ import chalk from "chalk";
5
+ import { program } from "commander";
6
+
7
+ const run = async () => {
8
+ program
9
+ .usage("[OPTIONS]...")
10
+ .option("-d, --debug", "Runs in debug mode")
11
+ .option("-a, --auto-fix", "Automatically fixes the errors")
12
+ .parse(process.argv);
13
+
14
+ const { autoFix, debug } = program.opts();
15
+
16
+ let errors = [];
17
+ const results = verifiers.map(async ([name, verifier]) => {
18
+ const { isSuccess = false, error, fix } = await verifier(debug);
19
+
20
+ !isSuccess && errors.push(error);
21
+ error && autoFix && fix(debug);
22
+
23
+ return { name, isSuccess };
24
+ });
25
+
26
+ const allResults = await Promise.all(results);
27
+
28
+ allResults.map(({ name, isSuccess }) => {
29
+ const resultText = isSuccess ? chalk.bgGreen("PASS") : chalk.bgRed("FAIL");
30
+ console.log(resultText, name);
31
+ });
32
+
33
+ errors.length > 0 && renderErrors(errors, autoFix);
34
+ autoFix && console.log(chalk.green("All issues have been fixed!"));
35
+ };
36
+
37
+ run();
@@ -0,0 +1,159 @@
1
+ import chalk from "chalk";
2
+ import { lstat, mkdir, readFile, writeFile } from "fs/promises";
3
+ import path from "path";
4
+ import { promises } from "fs";
5
+ import util from "util";
6
+ import { exec } from "child_process";
7
+ import { identity } from "ramda";
8
+
9
+ const run = util.promisify(exec);
10
+
11
+ export const execute = async (command, debug) => {
12
+ const { stdout, stderr } = await run(command);
13
+ debug && console.log("stdout:", stdout);
14
+ debug && console.log("stderr:", stderr);
15
+ };
16
+
17
+ export const getFileContent = async ({
18
+ relativeFilePath = "",
19
+ debug = false,
20
+ }) => {
21
+ const resolvedFilePath = path.resolve(relativeFilePath);
22
+
23
+ try {
24
+ return await readFile(resolvedFilePath, { encoding: "utf8" });
25
+ } catch (error) {
26
+ debug && console.error(error);
27
+ return null;
28
+ }
29
+ };
30
+
31
+ export const createOrReplaceFile = async ({
32
+ relativeFilePath = "",
33
+ content = "",
34
+ debug = false,
35
+ options = {},
36
+ }) => {
37
+ const resolvedFilePath = path.resolve(relativeFilePath);
38
+
39
+ try {
40
+ return await writeFile(resolvedFilePath, content, {
41
+ encoding: "utf8",
42
+ flag: "w",
43
+ ...options,
44
+ });
45
+ } catch (error) {
46
+ debug && console.error(error);
47
+ return null;
48
+ }
49
+ };
50
+
51
+ export const renderErrors = (errors = [], isAutoFixFlagPresent = false) => {
52
+ console.log();
53
+ console.log(chalk.red("Errors:"));
54
+ console.log(chalk.red(`- ${errors.join("\n- ")}`));
55
+ console.log();
56
+ if (!isAutoFixFlagPresent) process.exitCode = 1;
57
+ };
58
+
59
+ export const identicalFiles = async ({
60
+ sourceFile = "",
61
+ destinationFile = "",
62
+ debug = false,
63
+ }) => {
64
+ try {
65
+ const sourceFileContent = await readFile(sourceFile);
66
+ const destinationFileContent = await readFile(destinationFile);
67
+
68
+ const result = Buffer.compare(sourceFileContent, destinationFileContent);
69
+
70
+ return result === 0;
71
+ } catch (error) {
72
+ debug && console.error(error);
73
+ return false;
74
+ }
75
+ };
76
+
77
+ export const identicalDirectoryFiles = async ({
78
+ sourceDirectory,
79
+ destinationDirectory,
80
+ debug,
81
+ }) => {
82
+ try {
83
+ const files = await promises.readdir(sourceDirectory);
84
+ await mkdir(destinationDirectory, { recursive: true });
85
+ const results = files.map(async (file) => {
86
+ const sourceFile = `${sourceDirectory}/${file}`;
87
+ const destinationFile = `${destinationDirectory}/${file}`;
88
+
89
+ const isFile = (await lstat(sourceFile)).isFile();
90
+
91
+ if (isFile) {
92
+ return await identicalFiles({
93
+ sourceFile,
94
+ destinationFile,
95
+ debug,
96
+ });
97
+ } else {
98
+ await mkdir(destinationFile, { recursive: true });
99
+ const identicalFiles = await identicalDirectoryFiles({
100
+ sourceDirectory: sourceFile,
101
+ destinationDirectory: destinationFile,
102
+ debug,
103
+ });
104
+
105
+ const results = await Promise.all(identicalFiles);
106
+
107
+ return results.every(identity);
108
+ }
109
+ });
110
+
111
+ return results;
112
+ } catch (error) {
113
+ debug && console.error(error);
114
+ return null;
115
+ }
116
+ };
117
+
118
+ export const createOrReplaceDirectoryFiles = async ({
119
+ sourceDirectory,
120
+ destinationDirectory,
121
+ debug,
122
+ options,
123
+ }) => {
124
+ try {
125
+ const files = await promises.readdir(sourceDirectory);
126
+ await mkdir(destinationDirectory, { recursive: true });
127
+ files.forEach(async (file) => {
128
+ const sourcePath = `${sourceDirectory}/${file}`;
129
+ const destinationPath = `${destinationDirectory}/${file}`;
130
+
131
+ const isFile = (await lstat(sourcePath)).isFile();
132
+
133
+ if (isFile) {
134
+ const content = await getFileContent({
135
+ relativeFilePath: sourcePath,
136
+ debug,
137
+ });
138
+
139
+ await createOrReplaceFile({
140
+ relativeFilePath: destinationPath,
141
+ content,
142
+ debug,
143
+ options,
144
+ });
145
+ } else {
146
+ await mkdir(destinationPath, { recursive: true });
147
+ await createOrReplaceDirectoryFiles({
148
+ sourceDirectory: sourcePath,
149
+ destinationDirectory: destinationPath,
150
+ debug,
151
+ options,
152
+ });
153
+ }
154
+ });
155
+ } catch (error) {
156
+ debug && console.log(error);
157
+ return null;
158
+ }
159
+ };
@@ -0,0 +1,3 @@
1
+ export const NVMRC_FILE_PATH = "./.nvmrc";
2
+
3
+ export const REQUIRED_NODE_VERSION = { major: 18, minor: 0, patch: 0 };
@@ -0,0 +1,37 @@
1
+ import { createOrReplaceFile, getFileContent } from "../../utils/index.js";
2
+ import { NVMRC_FILE_PATH, REQUIRED_NODE_VERSION } from "./constants.js";
3
+ import { getSemVer } from "./utils.js";
4
+
5
+ const currentNodeVersion = async (debug) => {
6
+ const fix = async (debug) => {
7
+ await createOrReplaceFile({
8
+ relativeFilePath: NVMRC_FILE_PATH,
9
+ content: Object.values(REQUIRED_NODE_VERSION).join("."),
10
+ debug,
11
+ });
12
+ };
13
+
14
+ const nodeVersionFileContent = await getFileContent({
15
+ relativeFilePath: NVMRC_FILE_PATH,
16
+ debug,
17
+ });
18
+
19
+ if (!nodeVersionFileContent) {
20
+ return { error: "No file or content found in the .nvmrc file.", fix };
21
+ }
22
+
23
+ const [major = 0, minor = 0, patch = 0] = getSemVer(nodeVersionFileContent);
24
+
25
+ const isSuccess =
26
+ major === REQUIRED_NODE_VERSION.major &&
27
+ minor >= REQUIRED_NODE_VERSION.minor &&
28
+ patch >= REQUIRED_NODE_VERSION.patch;
29
+
30
+ if (!isSuccess) {
31
+ return { error: "There is a mismatch in the node version.", fix };
32
+ }
33
+
34
+ return { isSuccess };
35
+ };
36
+
37
+ export default currentNodeVersion;
@@ -0,0 +1,5 @@
1
+ export const getSemVer = (version) =>
2
+ version
3
+ .trim()
4
+ .split(".")
5
+ .map((value) => parseInt(value, 10));
@@ -0,0 +1,7 @@
1
+ import path from "path";
2
+
3
+ export const ESLINT_SOURCE_DIRECTORY = path.resolve(
4
+ "./node_modules/@bigbinary/neeto-audit-frontend/common/eslint"
5
+ );
6
+
7
+ export const ESLINT_DESTINATION_DIRECTORY = path.resolve("./");
@@ -0,0 +1,37 @@
1
+ import { identity } from "ramda";
2
+ import {
3
+ createOrReplaceDirectoryFiles,
4
+ identicalDirectoryFiles,
5
+ } from "../../utils/index.js";
6
+ import {
7
+ ESLINT_DESTINATION_DIRECTORY,
8
+ ESLINT_SOURCE_DIRECTORY,
9
+ } from "./constants.js";
10
+
11
+ const eslint = async (debug) => {
12
+ const fix = async (debug) => {
13
+ await createOrReplaceDirectoryFiles({
14
+ sourceDirectory: ESLINT_SOURCE_DIRECTORY,
15
+ destinationDirectory: ESLINT_DESTINATION_DIRECTORY,
16
+ debug,
17
+ });
18
+ };
19
+
20
+ const eslintDirectory = await identicalDirectoryFiles({
21
+ sourceDirectory: ESLINT_SOURCE_DIRECTORY,
22
+ destinationDirectory: ESLINT_DESTINATION_DIRECTORY,
23
+ debug,
24
+ });
25
+
26
+ const eslintFiles = await Promise.all(eslintDirectory);
27
+
28
+ const isSuccess = eslintFiles.every(identity);
29
+
30
+ if (!isSuccess) {
31
+ return { error: "ESLint and its related files are not present.", fix };
32
+ }
33
+
34
+ return { isSuccess };
35
+ };
36
+
37
+ export default eslint;
@@ -0,0 +1,9 @@
1
+ import path from "path";
2
+
3
+ export const HUSKY_SOURCE_DIRECTORY = path.resolve(
4
+ "./node_modules/@bigbinary/neeto-audit-frontend/common/.husky"
5
+ );
6
+
7
+ export const HUSKY_DESTINATION_DIRECTORY = path.resolve("./.husky");
8
+
9
+ export const HUSKY_GIT_IGNORE_PATH = path.resolve("./.husky/.gitignore");
@@ -0,0 +1,46 @@
1
+ import { identity } from "ramda";
2
+ import {
3
+ createOrReplaceDirectoryFiles,
4
+ createOrReplaceFile,
5
+ identicalDirectoryFiles,
6
+ } from "../../utils/index.js";
7
+ import {
8
+ HUSKY_DESTINATION_DIRECTORY,
9
+ HUSKY_GIT_IGNORE_PATH,
10
+ HUSKY_SOURCE_DIRECTORY,
11
+ } from "./constants.js";
12
+
13
+ const husky = async (debug) => {
14
+ const fix = async (debug) => {
15
+ await createOrReplaceDirectoryFiles({
16
+ sourceDirectory: HUSKY_SOURCE_DIRECTORY,
17
+ destinationDirectory: HUSKY_DESTINATION_DIRECTORY,
18
+ debug,
19
+ options: { mode: 0o755 },
20
+ });
21
+
22
+ await createOrReplaceFile({
23
+ relativeFilePath: HUSKY_GIT_IGNORE_PATH,
24
+ content: "_\n",
25
+ debug,
26
+ });
27
+ };
28
+
29
+ const huskyDirectory = await identicalDirectoryFiles({
30
+ sourceDirectory: HUSKY_SOURCE_DIRECTORY,
31
+ destinationDirectory: HUSKY_DESTINATION_DIRECTORY,
32
+ debug,
33
+ });
34
+
35
+ const huskyFiles = await Promise.all(huskyDirectory);
36
+
37
+ const isSuccess = huskyFiles.every(identity);
38
+
39
+ if (!isSuccess) {
40
+ return { error: "Some husky helper files are not present.", fix };
41
+ }
42
+
43
+ return { isSuccess };
44
+ };
45
+
46
+ export default husky;
@@ -0,0 +1,16 @@
1
+ import currentNodeVersion from "./currentNodeVersion/index.js";
2
+
3
+ import husky from "./husky/index.js";
4
+ import prettier from "./prettier/index.js";
5
+ import eslint from "./eslint/index.js";
6
+ import recommendedPackageVersions from "./recommendedPackageVersions/index.js";
7
+
8
+ const verifiers = [
9
+ ["Current node version", currentNodeVersion],
10
+ ["Use recommended package versions", recommendedPackageVersions],
11
+ ["Husky and its helper files", husky],
12
+ ["Prettier and its helper files", prettier],
13
+ ["ESLint configuration file", eslint],
14
+ ];
15
+
16
+ export default verifiers;
@@ -0,0 +1,7 @@
1
+ import path from "path";
2
+
3
+ export const PRETTIER_SOURCE_PATH = path.resolve(
4
+ "./node_modules/@bigbinary/neeto-audit-frontend/common/prettier"
5
+ );
6
+
7
+ export const PRETTIER_DESTINATION_PATH = path.resolve("./");
@@ -0,0 +1,40 @@
1
+ import { identity } from "ramda";
2
+ import {
3
+ createOrReplaceDirectoryFiles,
4
+ identicalDirectoryFiles,
5
+ } from "../../utils/index.js";
6
+ import {
7
+ PRETTIER_DESTINATION_PATH,
8
+ PRETTIER_SOURCE_PATH,
9
+ } from "./constants.js";
10
+
11
+ const prettier = async (debug) => {
12
+ const fix = async (debug) => {
13
+ await createOrReplaceDirectoryFiles({
14
+ sourceDirectory: PRETTIER_SOURCE_PATH,
15
+ destinationDirectory: PRETTIER_DESTINATION_PATH,
16
+ debug,
17
+ });
18
+ };
19
+
20
+ const prettierDirectory = await identicalDirectoryFiles({
21
+ sourceDirectory: PRETTIER_SOURCE_PATH,
22
+ destinationDirectory: PRETTIER_DESTINATION_PATH,
23
+ debug,
24
+ });
25
+
26
+ const prettierFiles = await Promise.all(prettierDirectory);
27
+
28
+ const isSuccess = prettierFiles.every(identity);
29
+
30
+ if (!isSuccess) {
31
+ return {
32
+ error: "There are some discrepancy in the prettier configurations.",
33
+ fix,
34
+ };
35
+ }
36
+
37
+ return { isSuccess };
38
+ };
39
+
40
+ export default prettier;
@@ -0,0 +1,9 @@
1
+ import path from "path";
2
+
3
+ export const PACKAGE_JSON_PATH = path.resolve("./package.json");
4
+
5
+ export const DEPENDENCY_TYPES = [
6
+ "dependencies",
7
+ "peerDependencies",
8
+ "devDependencies",
9
+ ];
@@ -0,0 +1,41 @@
1
+ import { identity, isEmpty, mergeDeepRight } from "ramda";
2
+ import { getNanoType, getOutdatedPackages, getPackageJson } from "./utils.js";
3
+ import { createOrReplaceFile, execute } from "../../utils/index.js";
4
+ import recommendedDependencies from "../../../common/recommendedDependencies/index.js";
5
+ import { PACKAGE_JSON_PATH } from "./constants.js";
6
+
7
+ const recommendedPackageVersions = async (debug) => {
8
+ const packageJson = await getPackageJson(debug);
9
+
10
+ const nanoType = getNanoType(packageJson);
11
+
12
+ const dependencyTypes = getOutdatedPackages(nanoType, packageJson);
13
+
14
+ const fix = async (debug) => {
15
+ const recommendedVersions = recommendedDependencies[nanoType];
16
+ const newPackageJson = mergeDeepRight(packageJson, recommendedVersions);
17
+
18
+ await createOrReplaceFile({
19
+ relativeFilePath: PACKAGE_JSON_PATH,
20
+ content: JSON.stringify(newPackageJson, null, 2),
21
+ debug,
22
+ });
23
+
24
+ await execute("yarn install --check-files");
25
+ };
26
+
27
+ const isSuccess = dependencyTypes
28
+ .map(({ outdatedPackages }) => isEmpty(outdatedPackages))
29
+ .every(identity);
30
+
31
+ if (!isSuccess) {
32
+ return {
33
+ error: "The dependency packages are not using the recommended versions.",
34
+ fix,
35
+ };
36
+ }
37
+
38
+ return { isSuccess };
39
+ };
40
+
41
+ export default recommendedPackageVersions;
@@ -0,0 +1,30 @@
1
+ import recommendedDependencies from "../../../common/recommendedDependencies/index.js";
2
+ import { getFileContent } from "../../utils/index.js";
3
+ import { DEPENDENCY_TYPES, PACKAGE_JSON_PATH } from "./constants.js";
4
+
5
+ import { eqProps, last, pickBy, mapObjIndexed, not, split } from "ramda";
6
+
7
+ export const getPackageJson = async (debug) => {
8
+ const packageJsonContent = await getFileContent({
9
+ relativeFilePath: PACKAGE_JSON_PATH,
10
+ debug,
11
+ });
12
+
13
+ return JSON.parse(packageJsonContent);
14
+ };
15
+
16
+ export const getNanoType = ({ name }) => last(split("-", name));
17
+
18
+ export const getOutdatedPackages = (nanoType, packageJson) =>
19
+ DEPENDENCY_TYPES.map((type) => {
20
+ const recommendedList = recommendedDependencies[nanoType][type];
21
+
22
+ const packages = mapObjIndexed(
23
+ (_, key) => eqProps(key, recommendedList, packageJson[type]),
24
+ recommendedList
25
+ );
26
+
27
+ const outdatedPackages = pickBy(not, packages);
28
+
29
+ return { type, outdatedPackages: Object.keys(outdatedPackages) };
30
+ });