@zohodesk/codestandard-validator 0.0.6-exp-6 → 0.0.6-exp-7
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.
|
@@ -0,0 +1,247 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, "__esModule", {
|
|
4
|
+
value: true
|
|
5
|
+
});
|
|
6
|
+
exports.default = void 0;
|
|
7
|
+
const {
|
|
8
|
+
exec
|
|
9
|
+
} = require('child_process');
|
|
10
|
+
const fs = require('fs');
|
|
11
|
+
const path = require('path');
|
|
12
|
+
const {
|
|
13
|
+
getEslintExecutablePath
|
|
14
|
+
} = require('../../utils/EslintConfigFileUtils/getEslintExecutablePath');
|
|
15
|
+
const {
|
|
16
|
+
getNodeModulesPath
|
|
17
|
+
} = require('../../utils/General/getNodeModulesPath');
|
|
18
|
+
const {
|
|
19
|
+
filterFiles
|
|
20
|
+
} = require('../../utils/FileAndFolderOperations/filterFiles');
|
|
21
|
+
const {
|
|
22
|
+
Logger
|
|
23
|
+
} = require('../../utils/Logger/Logger');
|
|
24
|
+
const {
|
|
25
|
+
checkIfPluginsAreInstalled
|
|
26
|
+
} = require('../../utils/PluginsInstallation/checkIfPluginsAreInstalled');
|
|
27
|
+
const {
|
|
28
|
+
getBranchName
|
|
29
|
+
} = require('../../utils/GitActions/gitActions');
|
|
30
|
+
const {
|
|
31
|
+
getConfigurationPrecommit,
|
|
32
|
+
getSupportedLanguage
|
|
33
|
+
} = require('../../utils/General/getGeneralInfo');
|
|
34
|
+
const {
|
|
35
|
+
getRootDirectory
|
|
36
|
+
} = require('../../utils/General/RootDirectoryUtils/getRootDirectory');
|
|
37
|
+
const {
|
|
38
|
+
impactBasedPrecommit,
|
|
39
|
+
shouldWarningsAbortCommit
|
|
40
|
+
} = getConfigurationPrecommit();
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* Checks if the current commit is a merge commit
|
|
44
|
+
* @returns {Promise<boolean>} True if it's a merge commit
|
|
45
|
+
*/
|
|
46
|
+
async function isMergeCommit() {
|
|
47
|
+
return new Promise(resolve => {
|
|
48
|
+
exec('git rev-parse -q --verify MERGE_HEAD', error => {
|
|
49
|
+
resolve(!error);
|
|
50
|
+
});
|
|
51
|
+
});
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* Gets staged files while filtering out deleted files
|
|
56
|
+
* @returns {Promise<string[]>} Array of staged file paths
|
|
57
|
+
*/
|
|
58
|
+
async function getStagedFiles() {
|
|
59
|
+
return new Promise((resolve, reject) => {
|
|
60
|
+
exec('git diff --staged --name-only', (error, stdout) => {
|
|
61
|
+
if (error) {
|
|
62
|
+
return reject("Couldn't fetch staged files");
|
|
63
|
+
}
|
|
64
|
+
const files = stdout.trim().split('\n').filter(Boolean);
|
|
65
|
+
resolve(filterDeletedFiles(files));
|
|
66
|
+
});
|
|
67
|
+
});
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
* Filters out deleted files from the staged files list
|
|
72
|
+
* @param {string[]} files - Array of file paths
|
|
73
|
+
* @returns {string[]} Filtered array of existing files
|
|
74
|
+
*/
|
|
75
|
+
function filterDeletedFiles(files) {
|
|
76
|
+
return files.filter(file => {
|
|
77
|
+
const absolutePath = path.resolve(getRootDirectory(), file);
|
|
78
|
+
return fs.existsSync(absolutePath);
|
|
79
|
+
});
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
/**
|
|
83
|
+
* Runs ESLint on a specific file
|
|
84
|
+
* @param {string} file - File path to lint
|
|
85
|
+
* @returns {Promise<string[]>} ESLint output lines
|
|
86
|
+
*/
|
|
87
|
+
async function runEslintOnFile(file) {
|
|
88
|
+
const nodeModulesPath = getNodeModulesPath();
|
|
89
|
+
const eslintPath = getEslintExecutablePath();
|
|
90
|
+
const configPath = `${nodeModulesPath}/.eslintrc.js`;
|
|
91
|
+
if (!fs.existsSync(nodeModulesPath)) {
|
|
92
|
+
Logger.log(Logger.INFO_TYPE, 'node_modules not found');
|
|
93
|
+
return [];
|
|
94
|
+
}
|
|
95
|
+
if (!fs.existsSync(eslintPath)) {
|
|
96
|
+
Logger.log(Logger.INFO_TYPE, 'Eslint executable not found');
|
|
97
|
+
return [];
|
|
98
|
+
}
|
|
99
|
+
return new Promise(resolve => {
|
|
100
|
+
const command = `npx --ignore-existing "${eslintPath}" --config "${configPath}" --no-inline-config --resolve-plugins-relative-to="${nodeModulesPath}/node_modules" ${file}`;
|
|
101
|
+
exec(command, (error, stderr) => {
|
|
102
|
+
if (stderr) {
|
|
103
|
+
resolve(stderr.trim().split('\n'));
|
|
104
|
+
} else {
|
|
105
|
+
resolve([]);
|
|
106
|
+
}
|
|
107
|
+
});
|
|
108
|
+
});
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
/**
|
|
112
|
+
* Computes git diff for a file against current branch
|
|
113
|
+
* @param {string} branch - Current branch name
|
|
114
|
+
* @param {string} file - File path
|
|
115
|
+
* @returns {Promise<string[]>} Git diff output lines
|
|
116
|
+
*/
|
|
117
|
+
async function getGitDiff(branch, file) {
|
|
118
|
+
return new Promise((resolve, reject) => {
|
|
119
|
+
exec(`git diff -U0 ${branch.trim()} ${file}`, (error, stderr) => {
|
|
120
|
+
if (error) return reject(error);
|
|
121
|
+
resolve(stderr ? stderr.trim().split('\n') : []);
|
|
122
|
+
});
|
|
123
|
+
});
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
/**
|
|
127
|
+
* Extracts changed line ranges from git diff output
|
|
128
|
+
* @param {string[]} diffLines - Git diff output
|
|
129
|
+
* @returns {Array<{start: number, end: number}>} Array of changed line ranges
|
|
130
|
+
*/
|
|
131
|
+
function parseChangedLines(diffLines) {
|
|
132
|
+
return diffLines.filter(line => line.startsWith('@@')).map(line => {
|
|
133
|
+
const parts = line.split(' ')[2].split(',');
|
|
134
|
+
const start = parseInt(parts[0]);
|
|
135
|
+
const end = parts[1] ? start + parseInt(parts[1]) - 1 : start;
|
|
136
|
+
return {
|
|
137
|
+
start,
|
|
138
|
+
end
|
|
139
|
+
};
|
|
140
|
+
});
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
/**
|
|
144
|
+
* Filters ESLint errors to only include those in changed lines
|
|
145
|
+
* @param {string[]} eslintErrors - ESLint output lines
|
|
146
|
+
* @param {Array<{start: number, end: number}>} changedRanges - Changed line ranges
|
|
147
|
+
* @returns {string[]} Filtered ESLint errors
|
|
148
|
+
*/
|
|
149
|
+
function filterErrorsToChangedLines(eslintErrors, changedRanges) {
|
|
150
|
+
return eslintErrors.filter(error => {
|
|
151
|
+
const match = error.match(/^(\d+):/);
|
|
152
|
+
if (!match) return false;
|
|
153
|
+
const line = parseInt(match[1]);
|
|
154
|
+
return changedRanges.some(({
|
|
155
|
+
start,
|
|
156
|
+
end
|
|
157
|
+
}) => line >= start && line <= end);
|
|
158
|
+
});
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
/**
|
|
162
|
+
* Checks if ESLint output contains only warnings
|
|
163
|
+
* @param {string[]} eslintErrors - ESLint output lines
|
|
164
|
+
* @returns {boolean} True if only warnings are present
|
|
165
|
+
*/
|
|
166
|
+
function hasOnlyWarnings(eslintErrors) {
|
|
167
|
+
return eslintErrors.every(error => error.includes('warning'));
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
/**
|
|
171
|
+
* Processes a file based on impact-based precommit configuration
|
|
172
|
+
* @param {string} file - File path to process
|
|
173
|
+
* @param {string} branch - Current branch name
|
|
174
|
+
* @returns {Promise<boolean>} True if errors should abort commit
|
|
175
|
+
*/
|
|
176
|
+
async function processFile(file, branch) {
|
|
177
|
+
if (!getSupportedLanguage().includes(path.extname(file))) return false;
|
|
178
|
+
try {
|
|
179
|
+
const eslintOutput = await runEslintOnFile(file);
|
|
180
|
+
if (eslintOutput.length < 3) return false; // No meaningful errors
|
|
181
|
+
|
|
182
|
+
const errors = eslintOutput.slice(1, -2);
|
|
183
|
+
if (!impactBasedPrecommit) {
|
|
184
|
+
Logger.log(Logger.FAILURE_TYPE, `\x1b[1m${file}\x1b[0m`);
|
|
185
|
+
errors.forEach(err => Logger.log(Logger.FAILURE_TYPE, `\x1b[37m${err.trimEnd()}\x1b[0m`));
|
|
186
|
+
if (!shouldWarningsAbortCommit && hasOnlyWarnings(errors)) {
|
|
187
|
+
return false;
|
|
188
|
+
}
|
|
189
|
+
return true;
|
|
190
|
+
}
|
|
191
|
+
const diffOutput = await getGitDiff(branch, file);
|
|
192
|
+
const changedRanges = parseChangedLines(diffOutput);
|
|
193
|
+
const changedLineErrors = filterErrorsToChangedLines(errors, changedRanges);
|
|
194
|
+
if (changedLineErrors.length > 0) {
|
|
195
|
+
Logger.log(Logger.FAILURE_TYPE, `\x1b[1m${file}\x1b[0m`);
|
|
196
|
+
changedLineErrors.forEach(err => Logger.log(Logger.FAILURE_TYPE, `\x1b[37m${err.trimEnd()}\x1b[0m`));
|
|
197
|
+
if (!shouldWarningsAbortCommit && hasOnlyWarnings(changedLineErrors)) {
|
|
198
|
+
return false;
|
|
199
|
+
}
|
|
200
|
+
return true;
|
|
201
|
+
}
|
|
202
|
+
} catch (error) {
|
|
203
|
+
Logger.log(Logger.FAILURE_TYPE, `Error processing ${file}: ${error}`);
|
|
204
|
+
}
|
|
205
|
+
return false;
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
/**
|
|
209
|
+
* Main pre-push hook execution logic
|
|
210
|
+
*/
|
|
211
|
+
async function preCommitCheck() {
|
|
212
|
+
Logger.log(Logger.INFO_TYPE, 'Executing pre commit hook...');
|
|
213
|
+
if (await isMergeCommit()) {
|
|
214
|
+
Logger.log(Logger.INFO_TYPE, 'Merge commit detected. Skipping checks.');
|
|
215
|
+
process.exit(0);
|
|
216
|
+
}
|
|
217
|
+
const pluginsStatus = checkIfPluginsAreInstalled();
|
|
218
|
+
if (pluginsStatus.uninstalledPlugins.length > 0) {
|
|
219
|
+
Logger.log(Logger.FAILURE_TYPE, 'Commit failed: Required plugins missing');
|
|
220
|
+
Logger.log(Logger.INFO_TYPE, 'Run: npx ZDPrecommit setupPlugins');
|
|
221
|
+
process.exit(1);
|
|
222
|
+
}
|
|
223
|
+
try {
|
|
224
|
+
const stagedFiles = await getStagedFiles();
|
|
225
|
+
if (stagedFiles.length === 0) {
|
|
226
|
+
Logger.log(Logger.INFO_TYPE, 'No staged files found');
|
|
227
|
+
process.exit(0);
|
|
228
|
+
}
|
|
229
|
+
const filteredFiles = filterFiles(stagedFiles, ['.eslintrc.js'], true);
|
|
230
|
+
const currentBranch = await getBranchName();
|
|
231
|
+
let shouldAbort = false;
|
|
232
|
+
for (const file of filteredFiles) {
|
|
233
|
+
const fileShouldAbort = await processFile(file, currentBranch);
|
|
234
|
+
if (fileShouldAbort) shouldAbort = true;
|
|
235
|
+
}
|
|
236
|
+
if (shouldAbort) {
|
|
237
|
+
Logger.log(Logger.FAILURE_TYPE, 'Commit aborted due to ESLint issues');
|
|
238
|
+
process.exit(1);
|
|
239
|
+
}
|
|
240
|
+
Logger.log(Logger.SUCCESS_TYPE, 'Commit successful');
|
|
241
|
+
process.exit(0);
|
|
242
|
+
} catch (error) {
|
|
243
|
+
Logger.log(Logger.FAILURE_TYPE, `Pre-commit hook failed: ${error}`);
|
|
244
|
+
process.exit(1);
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
var _default = exports.default = preCommitCheck;
|
|
@@ -20,8 +20,8 @@ const {
|
|
|
20
20
|
arePluginsInstalled
|
|
21
21
|
} = require("../../utils/PluginsInstallation/arePluginsInstalled");
|
|
22
22
|
const {
|
|
23
|
-
default:
|
|
24
|
-
} = require("
|
|
23
|
+
default: preCommitCheck
|
|
24
|
+
} = require("./lint");
|
|
25
25
|
async function validateRemotePackages() {
|
|
26
26
|
try {
|
|
27
27
|
if (!(getStoredCommitHash() == getLastCommitHash())) {
|
|
@@ -36,5 +36,5 @@ async function validateRemotePackages() {
|
|
|
36
36
|
}
|
|
37
37
|
(async function () {
|
|
38
38
|
await validateRemotePackages();
|
|
39
|
-
await
|
|
39
|
+
await preCommitCheck();
|
|
40
40
|
})();
|
|
@@ -21,33 +21,17 @@ const {
|
|
|
21
21
|
*/
|
|
22
22
|
function configurePrecommitHook() {
|
|
23
23
|
let rootDirectory = getRootDirectory();
|
|
24
|
-
let customPrecomitHookPath = path.join(getLibraryInstalledLocation(), "bin", "
|
|
25
|
-
|
|
26
|
-
// let customPrePushHookPath = path.join(
|
|
27
|
-
// getLibraryInstalledLocation(),
|
|
28
|
-
// "bin",
|
|
29
|
-
// "push.js"
|
|
30
|
-
// );
|
|
31
|
-
|
|
24
|
+
let customPrecomitHookPath = path.join(getLibraryInstalledLocation(), "bin", "execute.js");
|
|
32
25
|
let huskyPrecommitPath = path.join(rootDirectory, ".husky", "pre-commit");
|
|
33
|
-
// let huskyPrePushPath = path.join(rootDirectory,".husky","pre-push")
|
|
34
26
|
let huskyPrecommitHookContent = `#!/bin/sh
|
|
35
27
|
. "$(dirname "$0")/_/husky.sh"
|
|
36
28
|
|
|
37
29
|
"${customPrecomitHookPath}"
|
|
38
30
|
`;
|
|
39
|
-
|
|
40
|
-
// let huskyPrePushHookContent = `#!/bin/sh
|
|
41
|
-
// . "$(dirname "$0")/_/husky.sh"
|
|
42
|
-
|
|
43
|
-
// "${customPrePushHookPath}"
|
|
44
|
-
// `
|
|
45
31
|
let isCustomPrecommitConfigurationSuccessful = executeSynchronizedCommands(writeFileSync, [huskyPrecommitPath, huskyPrecommitHookContent, 'utf-8'], '', 'Could not create and write pre-commit.sh inisde husky directory', false, true);
|
|
46
|
-
// let isCustomPrePushConfigurationSuccessful = executeSynchronizedCommands(writeFileSync, [huskyPrePushPath, huskyPrePushHookContent, 'utf-8'], '', 'Could not create and write pre-push.sh inisde husky directory', false, true);
|
|
47
32
|
if (isCustomPrecommitConfigurationSuccessful) {
|
|
48
|
-
let
|
|
49
|
-
|
|
50
|
-
return isExecutionPermissionGivenSuccessfully_precommit;
|
|
33
|
+
let isExecutionPermissionGivenSuccessfully = grantExecutionPermission([huskyPrecommitPath, customPrecomitHookPath]);
|
|
34
|
+
return isExecutionPermissionGivenSuccessfully;
|
|
51
35
|
} else {
|
|
52
36
|
return false;
|
|
53
37
|
}
|
package/package.json
CHANGED
package/bin/push.js
DELETED
|
File without changes
|