@valbuild/cli 0.46.0 → 0.47.1

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.
@@ -6,6 +6,8 @@ var path = require('path');
6
6
  var server = require('@valbuild/server');
7
7
  var fastGlob = require('fast-glob');
8
8
  var picocolors = require('picocolors');
9
+ var eslint = require('eslint');
10
+ var fs = require('fs/promises');
9
11
 
10
12
  function _interopDefault (e) { return e && e.__esModule ? e : { 'default': e }; }
11
13
 
@@ -13,6 +15,7 @@ var meow__default = /*#__PURE__*/_interopDefault(meow);
13
15
  var chalk__default = /*#__PURE__*/_interopDefault(chalk);
14
16
  var path__default = /*#__PURE__*/_interopDefault(path);
15
17
  var picocolors__default = /*#__PURE__*/_interopDefault(picocolors);
18
+ var fs__default = /*#__PURE__*/_interopDefault(fs);
16
19
 
17
20
  function error(...message) {
18
21
  console.error(chalk__default["default"].red(...message));
@@ -24,6 +27,11 @@ async function validate({
24
27
  fix
25
28
  }) {
26
29
  const projectRoot = root ? path__default["default"].resolve(root) : process.cwd();
30
+ const eslint$1 = new eslint.ESLint({
31
+ cwd: projectRoot,
32
+ ignore: false,
33
+ useEslintrc: true
34
+ });
27
35
  const service = await server.createService(projectRoot, {
28
36
  valConfigPath: cfg ?? "./val.config"
29
37
  });
@@ -31,45 +39,80 @@ async function validate({
31
39
  ignore: ["node_modules/**"],
32
40
  cwd: projectRoot
33
41
  });
34
- console.log(picocolors__default["default"].green("✔"), "Validating", valFiles.length, "files");
42
+ const lintFiles = await fastGlob.glob("**/*.{js,ts}", {
43
+ ignore: ["node_modules/**"],
44
+ cwd: projectRoot
45
+ });
46
+ console.log("Running eslint...");
47
+ const eslintResults = await eslint$1.lintFiles(lintFiles);
48
+ const eslintResultsByFile = eslintResults.reduce((acc, result) => ({
49
+ ...acc,
50
+ [result.filePath.replaceAll(`${projectRoot}/`, "")]: result
51
+ }), {});
52
+ let errors = 0;
53
+ eslintResults.forEach(result => {
54
+ result.messages.forEach(async m => {
55
+ if (m.messageId === "val/export-content-must-be-valid") {
56
+ errors += 1;
57
+ logEslintMessage(await fs__default["default"].readFile(result.filePath, "utf-8"), result.filePath, m);
58
+ }
59
+ });
60
+ });
61
+ console.log(errors === 0 ? picocolors__default["default"].green("✔") : picocolors__default["default"].red("✘"), "ESlint complete", valFiles.length, "files");
62
+ console.log("Validating...", valFiles.length, "files");
35
63
  async function validateFile(file) {
36
64
  const moduleId = `/${file}`.replace(/(\.val\.(ts|js))$/, ""); // TODO: check if this always works? (Windows?)
37
65
  const start = Date.now();
38
66
  const valModule = await service.get(moduleId, "");
39
- if (!valModule.errors) {
40
- console.log(picocolors__default["default"].green("✔"), moduleId, "is valid (", Date.now() - start, "ms)");
67
+ const fileContent = await fs__default["default"].readFile(path__default["default"].join(projectRoot, file), "utf-8");
68
+ const eslintResult = eslintResultsByFile === null || eslintResultsByFile === void 0 ? void 0 : eslintResultsByFile[file];
69
+ eslintResult === null || eslintResult === void 0 || eslintResult.messages.forEach(m => {
70
+ // display surrounding code
71
+ logEslintMessage(fileContent, moduleId, m);
72
+ });
73
+ if (!valModule.errors && (eslintResult === null || eslintResult === void 0 ? void 0 : eslintResult.errorCount) === 0) {
74
+ console.log(picocolors__default["default"].green("✔"), moduleId, "is valid (" + (Date.now() - start) + "ms)");
41
75
  return 0;
42
76
  } else {
43
- let errors = 0;
44
- if (valModule.errors.validation) for (const [sourcePath, validationErrors] of Object.entries(valModule.errors.validation)) {
45
- for (const v of validationErrors) {
46
- if (v.fixes && v.fixes.length > 0) {
47
- var _fixPatch$remainingEr;
48
- const fixPatch = await server.createFixPatch({
49
- projectRoot
50
- }, !!fix, sourcePath, v);
51
- if (fix && fixPatch !== null && fixPatch !== void 0 && fixPatch.patch && (fixPatch === null || fixPatch === void 0 ? void 0 : fixPatch.patch.length) > 0) {
52
- await service.patch(moduleId, fixPatch.patch);
53
- console.log(picocolors__default["default"].green("✔"), "Applied fix for", sourcePath);
77
+ var _eslintResultsByFile$;
78
+ let errors = (eslintResultsByFile === null || eslintResultsByFile === void 0 || (_eslintResultsByFile$ = eslintResultsByFile[file]) === null || _eslintResultsByFile$ === void 0 ? void 0 : _eslintResultsByFile$.messages.reduce((prev, m) => m.severity >= 2 ? prev + 1 : prev, 0)) || 0;
79
+ if (valModule.errors) {
80
+ if (valModule.errors.validation) {
81
+ for (const [sourcePath, validationErrors] of Object.entries(valModule.errors.validation)) {
82
+ for (const v of validationErrors) {
83
+ if (v.fixes && v.fixes.length > 0) {
84
+ var _fixPatch$remainingEr;
85
+ const fixPatch = await server.createFixPatch({
86
+ projectRoot
87
+ }, !!fix, sourcePath, v);
88
+ if (fix && fixPatch !== null && fixPatch !== void 0 && fixPatch.patch && (fixPatch === null || fixPatch === void 0 ? void 0 : fixPatch.patch.length) > 0) {
89
+ await service.patch(moduleId, fixPatch.patch);
90
+ console.log(picocolors__default["default"].green("✔"), "Applied fix for", sourcePath);
91
+ }
92
+ fixPatch === null || fixPatch === void 0 || (_fixPatch$remainingEr = fixPatch.remainingErrors) === null || _fixPatch$remainingEr === void 0 || _fixPatch$remainingEr.forEach(e => {
93
+ errors += 1;
94
+ console.log(v.fixes ? picocolors__default["default"].yellow("⚠") : picocolors__default["default"].red("✘"), `Found ${v.fixes ? "fixable " : ""}error in`, `${sourcePath}:`, e.message);
95
+ });
96
+ } else {
97
+ errors += 1;
98
+ console.log(picocolors__default["default"].red("✘"), "Found error in", `${sourcePath}:`, v.message);
99
+ }
54
100
  }
55
- fixPatch === null || fixPatch === void 0 || (_fixPatch$remainingEr = fixPatch.remainingErrors) === null || _fixPatch$remainingEr === void 0 || _fixPatch$remainingEr.forEach(e => {
56
- errors += 1;
57
- console.log(v.fixes ? picocolors__default["default"].yellow("⚠") : picocolors__default["default"].red("✘"), `Found ${v.fixes ? "fixable " : ""}error in`, `${sourcePath}:`, e.message);
58
- });
59
- } else {
60
- errors += 1;
61
- console.log(picocolors__default["default"].red("✘"), "Found error in", `${sourcePath}:`, v.message);
62
101
  }
63
102
  }
64
- }
65
- for (const fatalError of valModule.errors.fatal || []) {
66
- errors += 1;
67
- console.log(picocolors__default["default"].red("✘"), moduleId, "is invalid:", fatalError.message);
103
+ for (const fatalError of valModule.errors.fatal || []) {
104
+ errors += 1;
105
+ console.log(picocolors__default["default"].red("✘"), moduleId, "is invalid:", fatalError.message);
106
+ }
107
+ } else {
108
+ console.log(picocolors__default["default"].green("✔"), moduleId, "is valid (" + (Date.now() - start) + "ms)");
68
109
  }
69
110
  return errors;
70
111
  }
71
112
  }
72
- const errors = (await Promise.all(valFiles.map(validateFile))).reduce((a, b) => a + b, 0);
113
+ for (const file of valFiles) {
114
+ errors += await validateFile(file);
115
+ }
73
116
  if (errors > 0) {
74
117
  console.log(picocolors__default["default"].red("✘"), "Found", errors, "validation error" + (errors > 1 ? "s" : ""));
75
118
  process.exit(1);
@@ -79,6 +122,20 @@ async function validate({
79
122
  service.dispose();
80
123
  return;
81
124
  }
125
+ function logEslintMessage(fileContent, filePath, eslintMessage) {
126
+ const lines = fileContent.split("\n");
127
+ const line = lines[eslintMessage.line - 1];
128
+ const lineBefore = lines[eslintMessage.line - 2];
129
+ const lineAfter = lines[eslintMessage.line];
130
+ const isError = eslintMessage.severity >= 2;
131
+ console.log(isError ? picocolors__default["default"].red("✘") : picocolors__default["default"].yellow("⚠"), isError ? "Found eslint error:" : "Found eslint warning:", `${filePath}:${eslintMessage.line}:${eslintMessage.column}\n`, eslintMessage.message);
132
+ lineBefore && console.log(picocolors__default["default"].gray(" " + (eslintMessage.line - 1) + " |"), lineBefore);
133
+ line && console.log(picocolors__default["default"].gray(" " + eslintMessage.line + " |"), line);
134
+ // adds ^ below the relevant line:
135
+ const amountOfColumns = eslintMessage.endColumn && eslintMessage.endColumn - eslintMessage.column > 0 ? eslintMessage.endColumn - eslintMessage.column : 1;
136
+ line && console.log(picocolors__default["default"].gray(" " + " ".repeat(eslintMessage.line.toString().length) + " |"), " ".repeat(eslintMessage.column - 1) + (eslintMessage.endColumn ? (isError ? picocolors__default["default"].red("^") : picocolors__default["default"].yellow("^")).repeat(amountOfColumns) : ""));
137
+ lineAfter && console.log(picocolors__default["default"].gray(" " + (eslintMessage.line + 1) + " |"), lineAfter);
138
+ }
82
139
 
83
140
  async function main() {
84
141
  const {
@@ -6,6 +6,8 @@ var path = require('path');
6
6
  var server = require('@valbuild/server');
7
7
  var fastGlob = require('fast-glob');
8
8
  var picocolors = require('picocolors');
9
+ var eslint = require('eslint');
10
+ var fs = require('fs/promises');
9
11
 
10
12
  function _interopDefault (e) { return e && e.__esModule ? e : { 'default': e }; }
11
13
 
@@ -13,6 +15,7 @@ var meow__default = /*#__PURE__*/_interopDefault(meow);
13
15
  var chalk__default = /*#__PURE__*/_interopDefault(chalk);
14
16
  var path__default = /*#__PURE__*/_interopDefault(path);
15
17
  var picocolors__default = /*#__PURE__*/_interopDefault(picocolors);
18
+ var fs__default = /*#__PURE__*/_interopDefault(fs);
16
19
 
17
20
  function error(...message) {
18
21
  console.error(chalk__default["default"].red(...message));
@@ -24,6 +27,11 @@ async function validate({
24
27
  fix
25
28
  }) {
26
29
  const projectRoot = root ? path__default["default"].resolve(root) : process.cwd();
30
+ const eslint$1 = new eslint.ESLint({
31
+ cwd: projectRoot,
32
+ ignore: false,
33
+ useEslintrc: true
34
+ });
27
35
  const service = await server.createService(projectRoot, {
28
36
  valConfigPath: cfg ?? "./val.config"
29
37
  });
@@ -31,45 +39,80 @@ async function validate({
31
39
  ignore: ["node_modules/**"],
32
40
  cwd: projectRoot
33
41
  });
34
- console.log(picocolors__default["default"].green("✔"), "Validating", valFiles.length, "files");
42
+ const lintFiles = await fastGlob.glob("**/*.{js,ts}", {
43
+ ignore: ["node_modules/**"],
44
+ cwd: projectRoot
45
+ });
46
+ console.log("Running eslint...");
47
+ const eslintResults = await eslint$1.lintFiles(lintFiles);
48
+ const eslintResultsByFile = eslintResults.reduce((acc, result) => ({
49
+ ...acc,
50
+ [result.filePath.replaceAll(`${projectRoot}/`, "")]: result
51
+ }), {});
52
+ let errors = 0;
53
+ eslintResults.forEach(result => {
54
+ result.messages.forEach(async m => {
55
+ if (m.messageId === "val/export-content-must-be-valid") {
56
+ errors += 1;
57
+ logEslintMessage(await fs__default["default"].readFile(result.filePath, "utf-8"), result.filePath, m);
58
+ }
59
+ });
60
+ });
61
+ console.log(errors === 0 ? picocolors__default["default"].green("✔") : picocolors__default["default"].red("✘"), "ESlint complete", valFiles.length, "files");
62
+ console.log("Validating...", valFiles.length, "files");
35
63
  async function validateFile(file) {
36
64
  const moduleId = `/${file}`.replace(/(\.val\.(ts|js))$/, ""); // TODO: check if this always works? (Windows?)
37
65
  const start = Date.now();
38
66
  const valModule = await service.get(moduleId, "");
39
- if (!valModule.errors) {
40
- console.log(picocolors__default["default"].green("✔"), moduleId, "is valid (", Date.now() - start, "ms)");
67
+ const fileContent = await fs__default["default"].readFile(path__default["default"].join(projectRoot, file), "utf-8");
68
+ const eslintResult = eslintResultsByFile === null || eslintResultsByFile === void 0 ? void 0 : eslintResultsByFile[file];
69
+ eslintResult === null || eslintResult === void 0 || eslintResult.messages.forEach(m => {
70
+ // display surrounding code
71
+ logEslintMessage(fileContent, moduleId, m);
72
+ });
73
+ if (!valModule.errors && (eslintResult === null || eslintResult === void 0 ? void 0 : eslintResult.errorCount) === 0) {
74
+ console.log(picocolors__default["default"].green("✔"), moduleId, "is valid (" + (Date.now() - start) + "ms)");
41
75
  return 0;
42
76
  } else {
43
- let errors = 0;
44
- if (valModule.errors.validation) for (const [sourcePath, validationErrors] of Object.entries(valModule.errors.validation)) {
45
- for (const v of validationErrors) {
46
- if (v.fixes && v.fixes.length > 0) {
47
- var _fixPatch$remainingEr;
48
- const fixPatch = await server.createFixPatch({
49
- projectRoot
50
- }, !!fix, sourcePath, v);
51
- if (fix && fixPatch !== null && fixPatch !== void 0 && fixPatch.patch && (fixPatch === null || fixPatch === void 0 ? void 0 : fixPatch.patch.length) > 0) {
52
- await service.patch(moduleId, fixPatch.patch);
53
- console.log(picocolors__default["default"].green("✔"), "Applied fix for", sourcePath);
77
+ var _eslintResultsByFile$;
78
+ let errors = (eslintResultsByFile === null || eslintResultsByFile === void 0 || (_eslintResultsByFile$ = eslintResultsByFile[file]) === null || _eslintResultsByFile$ === void 0 ? void 0 : _eslintResultsByFile$.messages.reduce((prev, m) => m.severity >= 2 ? prev + 1 : prev, 0)) || 0;
79
+ if (valModule.errors) {
80
+ if (valModule.errors.validation) {
81
+ for (const [sourcePath, validationErrors] of Object.entries(valModule.errors.validation)) {
82
+ for (const v of validationErrors) {
83
+ if (v.fixes && v.fixes.length > 0) {
84
+ var _fixPatch$remainingEr;
85
+ const fixPatch = await server.createFixPatch({
86
+ projectRoot
87
+ }, !!fix, sourcePath, v);
88
+ if (fix && fixPatch !== null && fixPatch !== void 0 && fixPatch.patch && (fixPatch === null || fixPatch === void 0 ? void 0 : fixPatch.patch.length) > 0) {
89
+ await service.patch(moduleId, fixPatch.patch);
90
+ console.log(picocolors__default["default"].green("✔"), "Applied fix for", sourcePath);
91
+ }
92
+ fixPatch === null || fixPatch === void 0 || (_fixPatch$remainingEr = fixPatch.remainingErrors) === null || _fixPatch$remainingEr === void 0 || _fixPatch$remainingEr.forEach(e => {
93
+ errors += 1;
94
+ console.log(v.fixes ? picocolors__default["default"].yellow("⚠") : picocolors__default["default"].red("✘"), `Found ${v.fixes ? "fixable " : ""}error in`, `${sourcePath}:`, e.message);
95
+ });
96
+ } else {
97
+ errors += 1;
98
+ console.log(picocolors__default["default"].red("✘"), "Found error in", `${sourcePath}:`, v.message);
99
+ }
54
100
  }
55
- fixPatch === null || fixPatch === void 0 || (_fixPatch$remainingEr = fixPatch.remainingErrors) === null || _fixPatch$remainingEr === void 0 || _fixPatch$remainingEr.forEach(e => {
56
- errors += 1;
57
- console.log(v.fixes ? picocolors__default["default"].yellow("⚠") : picocolors__default["default"].red("✘"), `Found ${v.fixes ? "fixable " : ""}error in`, `${sourcePath}:`, e.message);
58
- });
59
- } else {
60
- errors += 1;
61
- console.log(picocolors__default["default"].red("✘"), "Found error in", `${sourcePath}:`, v.message);
62
101
  }
63
102
  }
64
- }
65
- for (const fatalError of valModule.errors.fatal || []) {
66
- errors += 1;
67
- console.log(picocolors__default["default"].red("✘"), moduleId, "is invalid:", fatalError.message);
103
+ for (const fatalError of valModule.errors.fatal || []) {
104
+ errors += 1;
105
+ console.log(picocolors__default["default"].red("✘"), moduleId, "is invalid:", fatalError.message);
106
+ }
107
+ } else {
108
+ console.log(picocolors__default["default"].green("✔"), moduleId, "is valid (" + (Date.now() - start) + "ms)");
68
109
  }
69
110
  return errors;
70
111
  }
71
112
  }
72
- const errors = (await Promise.all(valFiles.map(validateFile))).reduce((a, b) => a + b, 0);
113
+ for (const file of valFiles) {
114
+ errors += await validateFile(file);
115
+ }
73
116
  if (errors > 0) {
74
117
  console.log(picocolors__default["default"].red("✘"), "Found", errors, "validation error" + (errors > 1 ? "s" : ""));
75
118
  process.exit(1);
@@ -79,6 +122,20 @@ async function validate({
79
122
  service.dispose();
80
123
  return;
81
124
  }
125
+ function logEslintMessage(fileContent, filePath, eslintMessage) {
126
+ const lines = fileContent.split("\n");
127
+ const line = lines[eslintMessage.line - 1];
128
+ const lineBefore = lines[eslintMessage.line - 2];
129
+ const lineAfter = lines[eslintMessage.line];
130
+ const isError = eslintMessage.severity >= 2;
131
+ console.log(isError ? picocolors__default["default"].red("✘") : picocolors__default["default"].yellow("⚠"), isError ? "Found eslint error:" : "Found eslint warning:", `${filePath}:${eslintMessage.line}:${eslintMessage.column}\n`, eslintMessage.message);
132
+ lineBefore && console.log(picocolors__default["default"].gray(" " + (eslintMessage.line - 1) + " |"), lineBefore);
133
+ line && console.log(picocolors__default["default"].gray(" " + eslintMessage.line + " |"), line);
134
+ // adds ^ below the relevant line:
135
+ const amountOfColumns = eslintMessage.endColumn && eslintMessage.endColumn - eslintMessage.column > 0 ? eslintMessage.endColumn - eslintMessage.column : 1;
136
+ line && console.log(picocolors__default["default"].gray(" " + " ".repeat(eslintMessage.line.toString().length) + " |"), " ".repeat(eslintMessage.column - 1) + (eslintMessage.endColumn ? (isError ? picocolors__default["default"].red("^") : picocolors__default["default"].yellow("^")).repeat(amountOfColumns) : ""));
137
+ lineAfter && console.log(picocolors__default["default"].gray(" " + (eslintMessage.line + 1) + " |"), lineAfter);
138
+ }
82
139
 
83
140
  async function main() {
84
141
  const {
@@ -4,6 +4,8 @@ import path from 'path';
4
4
  import { createService, createFixPatch } from '@valbuild/server';
5
5
  import { glob } from 'fast-glob';
6
6
  import picocolors from 'picocolors';
7
+ import { ESLint } from 'eslint';
8
+ import fs from 'fs/promises';
7
9
 
8
10
  function error(...message) {
9
11
  console.error(chalk.red(...message));
@@ -15,6 +17,11 @@ async function validate({
15
17
  fix
16
18
  }) {
17
19
  const projectRoot = root ? path.resolve(root) : process.cwd();
20
+ const eslint = new ESLint({
21
+ cwd: projectRoot,
22
+ ignore: false,
23
+ useEslintrc: true
24
+ });
18
25
  const service = await createService(projectRoot, {
19
26
  valConfigPath: cfg ?? "./val.config"
20
27
  });
@@ -22,45 +29,80 @@ async function validate({
22
29
  ignore: ["node_modules/**"],
23
30
  cwd: projectRoot
24
31
  });
25
- console.log(picocolors.green("✔"), "Validating", valFiles.length, "files");
32
+ const lintFiles = await glob("**/*.{js,ts}", {
33
+ ignore: ["node_modules/**"],
34
+ cwd: projectRoot
35
+ });
36
+ console.log("Running eslint...");
37
+ const eslintResults = await eslint.lintFiles(lintFiles);
38
+ const eslintResultsByFile = eslintResults.reduce((acc, result) => ({
39
+ ...acc,
40
+ [result.filePath.replaceAll(`${projectRoot}/`, "")]: result
41
+ }), {});
42
+ let errors = 0;
43
+ eslintResults.forEach(result => {
44
+ result.messages.forEach(async m => {
45
+ if (m.messageId === "val/export-content-must-be-valid") {
46
+ errors += 1;
47
+ logEslintMessage(await fs.readFile(result.filePath, "utf-8"), result.filePath, m);
48
+ }
49
+ });
50
+ });
51
+ console.log(errors === 0 ? picocolors.green("✔") : picocolors.red("✘"), "ESlint complete", valFiles.length, "files");
52
+ console.log("Validating...", valFiles.length, "files");
26
53
  async function validateFile(file) {
27
54
  const moduleId = `/${file}`.replace(/(\.val\.(ts|js))$/, ""); // TODO: check if this always works? (Windows?)
28
55
  const start = Date.now();
29
56
  const valModule = await service.get(moduleId, "");
30
- if (!valModule.errors) {
31
- console.log(picocolors.green("✔"), moduleId, "is valid (", Date.now() - start, "ms)");
57
+ const fileContent = await fs.readFile(path.join(projectRoot, file), "utf-8");
58
+ const eslintResult = eslintResultsByFile === null || eslintResultsByFile === void 0 ? void 0 : eslintResultsByFile[file];
59
+ eslintResult === null || eslintResult === void 0 || eslintResult.messages.forEach(m => {
60
+ // display surrounding code
61
+ logEslintMessage(fileContent, moduleId, m);
62
+ });
63
+ if (!valModule.errors && (eslintResult === null || eslintResult === void 0 ? void 0 : eslintResult.errorCount) === 0) {
64
+ console.log(picocolors.green("✔"), moduleId, "is valid (" + (Date.now() - start) + "ms)");
32
65
  return 0;
33
66
  } else {
34
- let errors = 0;
35
- if (valModule.errors.validation) for (const [sourcePath, validationErrors] of Object.entries(valModule.errors.validation)) {
36
- for (const v of validationErrors) {
37
- if (v.fixes && v.fixes.length > 0) {
38
- var _fixPatch$remainingEr;
39
- const fixPatch = await createFixPatch({
40
- projectRoot
41
- }, !!fix, sourcePath, v);
42
- if (fix && fixPatch !== null && fixPatch !== void 0 && fixPatch.patch && (fixPatch === null || fixPatch === void 0 ? void 0 : fixPatch.patch.length) > 0) {
43
- await service.patch(moduleId, fixPatch.patch);
44
- console.log(picocolors.green("✔"), "Applied fix for", sourcePath);
67
+ var _eslintResultsByFile$;
68
+ let errors = (eslintResultsByFile === null || eslintResultsByFile === void 0 || (_eslintResultsByFile$ = eslintResultsByFile[file]) === null || _eslintResultsByFile$ === void 0 ? void 0 : _eslintResultsByFile$.messages.reduce((prev, m) => m.severity >= 2 ? prev + 1 : prev, 0)) || 0;
69
+ if (valModule.errors) {
70
+ if (valModule.errors.validation) {
71
+ for (const [sourcePath, validationErrors] of Object.entries(valModule.errors.validation)) {
72
+ for (const v of validationErrors) {
73
+ if (v.fixes && v.fixes.length > 0) {
74
+ var _fixPatch$remainingEr;
75
+ const fixPatch = await createFixPatch({
76
+ projectRoot
77
+ }, !!fix, sourcePath, v);
78
+ if (fix && fixPatch !== null && fixPatch !== void 0 && fixPatch.patch && (fixPatch === null || fixPatch === void 0 ? void 0 : fixPatch.patch.length) > 0) {
79
+ await service.patch(moduleId, fixPatch.patch);
80
+ console.log(picocolors.green("✔"), "Applied fix for", sourcePath);
81
+ }
82
+ fixPatch === null || fixPatch === void 0 || (_fixPatch$remainingEr = fixPatch.remainingErrors) === null || _fixPatch$remainingEr === void 0 || _fixPatch$remainingEr.forEach(e => {
83
+ errors += 1;
84
+ console.log(v.fixes ? picocolors.yellow("⚠") : picocolors.red("✘"), `Found ${v.fixes ? "fixable " : ""}error in`, `${sourcePath}:`, e.message);
85
+ });
86
+ } else {
87
+ errors += 1;
88
+ console.log(picocolors.red("✘"), "Found error in", `${sourcePath}:`, v.message);
89
+ }
45
90
  }
46
- fixPatch === null || fixPatch === void 0 || (_fixPatch$remainingEr = fixPatch.remainingErrors) === null || _fixPatch$remainingEr === void 0 || _fixPatch$remainingEr.forEach(e => {
47
- errors += 1;
48
- console.log(v.fixes ? picocolors.yellow("⚠") : picocolors.red("✘"), `Found ${v.fixes ? "fixable " : ""}error in`, `${sourcePath}:`, e.message);
49
- });
50
- } else {
51
- errors += 1;
52
- console.log(picocolors.red("✘"), "Found error in", `${sourcePath}:`, v.message);
53
91
  }
54
92
  }
55
- }
56
- for (const fatalError of valModule.errors.fatal || []) {
57
- errors += 1;
58
- console.log(picocolors.red("✘"), moduleId, "is invalid:", fatalError.message);
93
+ for (const fatalError of valModule.errors.fatal || []) {
94
+ errors += 1;
95
+ console.log(picocolors.red("✘"), moduleId, "is invalid:", fatalError.message);
96
+ }
97
+ } else {
98
+ console.log(picocolors.green("✔"), moduleId, "is valid (" + (Date.now() - start) + "ms)");
59
99
  }
60
100
  return errors;
61
101
  }
62
102
  }
63
- const errors = (await Promise.all(valFiles.map(validateFile))).reduce((a, b) => a + b, 0);
103
+ for (const file of valFiles) {
104
+ errors += await validateFile(file);
105
+ }
64
106
  if (errors > 0) {
65
107
  console.log(picocolors.red("✘"), "Found", errors, "validation error" + (errors > 1 ? "s" : ""));
66
108
  process.exit(1);
@@ -70,6 +112,20 @@ async function validate({
70
112
  service.dispose();
71
113
  return;
72
114
  }
115
+ function logEslintMessage(fileContent, filePath, eslintMessage) {
116
+ const lines = fileContent.split("\n");
117
+ const line = lines[eslintMessage.line - 1];
118
+ const lineBefore = lines[eslintMessage.line - 2];
119
+ const lineAfter = lines[eslintMessage.line];
120
+ const isError = eslintMessage.severity >= 2;
121
+ console.log(isError ? picocolors.red("✘") : picocolors.yellow("⚠"), isError ? "Found eslint error:" : "Found eslint warning:", `${filePath}:${eslintMessage.line}:${eslintMessage.column}\n`, eslintMessage.message);
122
+ lineBefore && console.log(picocolors.gray(" " + (eslintMessage.line - 1) + " |"), lineBefore);
123
+ line && console.log(picocolors.gray(" " + eslintMessage.line + " |"), line);
124
+ // adds ^ below the relevant line:
125
+ const amountOfColumns = eslintMessage.endColumn && eslintMessage.endColumn - eslintMessage.column > 0 ? eslintMessage.endColumn - eslintMessage.column : 1;
126
+ line && console.log(picocolors.gray(" " + " ".repeat(eslintMessage.line.toString().length) + " |"), " ".repeat(eslintMessage.column - 1) + (eslintMessage.endColumn ? (isError ? picocolors.red("^") : picocolors.yellow("^")).repeat(amountOfColumns) : ""));
127
+ lineAfter && console.log(picocolors.gray(" " + (eslintMessage.line + 1) + " |"), lineAfter);
128
+ }
73
129
 
74
130
  async function main() {
75
131
  const {
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@valbuild/cli",
3
3
  "private": false,
4
- "version": "0.46.0",
4
+ "version": "0.47.1",
5
5
  "description": "Val CLI tools",
6
6
  "bin": {
7
7
  "val": "./bin.js"
@@ -17,7 +17,9 @@
17
17
  "typecheck": "tsc --noEmit"
18
18
  },
19
19
  "dependencies": {
20
- "@valbuild/server": "~0.46.0",
20
+ "@valbuild/server": "~0.47.1",
21
+ "@valbuild/eslint-plugin": "~0.47.1",
22
+ "eslint": "^8.31.0",
21
23
  "chalk": "^4.1.2",
22
24
  "cors": "^2.8.5",
23
25
  "express": "^4.18.2",
package/src/validate.ts CHANGED
@@ -3,6 +3,8 @@ import { createFixPatch, createService } from "@valbuild/server";
3
3
  import { ModuleId, ModulePath, SourcePath } from "@valbuild/core";
4
4
  import { glob } from "fast-glob";
5
5
  import picocolors from "picocolors";
6
+ import { ESLint } from "eslint";
7
+ import fs from "fs/promises";
6
8
 
7
9
  export async function validate({
8
10
  root,
@@ -14,6 +16,11 @@ export async function validate({
14
16
  fix?: boolean;
15
17
  }) {
16
18
  const projectRoot = root ? path.resolve(root) : process.cwd();
19
+ const eslint = new ESLint({
20
+ cwd: projectRoot,
21
+ ignore: false,
22
+ useEslintrc: true,
23
+ });
17
24
  const service = await createService(projectRoot, {
18
25
  valConfigPath: cfg ?? "./val.config",
19
26
  });
@@ -22,81 +29,132 @@ export async function validate({
22
29
  ignore: ["node_modules/**"],
23
30
  cwd: projectRoot,
24
31
  });
25
- console.log(picocolors.green("✔"), "Validating", valFiles.length, "files");
32
+
33
+ const lintFiles = await glob("**/*.{js,ts}", {
34
+ ignore: ["node_modules/**"],
35
+ cwd: projectRoot,
36
+ });
37
+ console.log("Running eslint...");
38
+ const eslintResults = await eslint.lintFiles(lintFiles);
39
+ const eslintResultsByFile = eslintResults.reduce(
40
+ (acc, result) => ({
41
+ ...acc,
42
+ [result.filePath.replaceAll(`${projectRoot}/`, "")]: result,
43
+ }),
44
+ {} as Record<string, ESLint.LintResult>
45
+ );
46
+
47
+ let errors = 0;
48
+ eslintResults.forEach((result) => {
49
+ result.messages.forEach(async (m) => {
50
+ if (m.messageId === "val/export-content-must-be-valid") {
51
+ errors += 1;
52
+ logEslintMessage(
53
+ await fs.readFile(result.filePath, "utf-8"),
54
+ result.filePath,
55
+ m
56
+ );
57
+ }
58
+ });
59
+ });
60
+ console.log(
61
+ errors === 0 ? picocolors.green("✔") : picocolors.red("✘"),
62
+ "ESlint complete",
63
+ valFiles.length,
64
+ "files"
65
+ );
66
+ console.log("Validating...", valFiles.length, "files");
26
67
 
27
68
  async function validateFile(file: string): Promise<number> {
28
69
  const moduleId = `/${file}`.replace(/(\.val\.(ts|js))$/, "") as ModuleId; // TODO: check if this always works? (Windows?)
29
70
  const start = Date.now();
30
71
  const valModule = await service.get(moduleId, "" as ModulePath);
31
-
32
- if (!valModule.errors) {
72
+ const fileContent = await fs.readFile(
73
+ path.join(projectRoot, file),
74
+ "utf-8"
75
+ );
76
+ const eslintResult = eslintResultsByFile?.[file];
77
+ eslintResult?.messages.forEach((m) => {
78
+ // display surrounding code
79
+ logEslintMessage(fileContent, moduleId, m);
80
+ });
81
+ if (!valModule.errors && eslintResult?.errorCount === 0) {
33
82
  console.log(
34
83
  picocolors.green("✔"),
35
84
  moduleId,
36
- "is valid (",
37
- Date.now() - start,
38
- "ms)"
85
+ "is valid (" + (Date.now() - start) + "ms)"
39
86
  );
40
87
  return 0;
41
88
  } else {
42
- let errors = 0;
43
- if (valModule.errors.validation)
44
- for (const [sourcePath, validationErrors] of Object.entries(
45
- valModule.errors.validation
46
- )) {
47
- for (const v of validationErrors) {
48
- if (v.fixes && v.fixes.length > 0) {
49
- const fixPatch = await createFixPatch(
50
- { projectRoot },
51
- !!fix,
52
- sourcePath as SourcePath,
53
- v
54
- );
55
- if (fix && fixPatch?.patch && fixPatch?.patch.length > 0) {
56
- await service.patch(moduleId, fixPatch.patch);
57
- console.log(
58
- picocolors.green("✔"),
59
- "Applied fix for",
60
- sourcePath
89
+ let errors =
90
+ eslintResultsByFile?.[file]?.messages.reduce(
91
+ (prev, m) => (m.severity >= 2 ? prev + 1 : prev),
92
+ 0
93
+ ) || 0;
94
+ if (valModule.errors) {
95
+ if (valModule.errors.validation) {
96
+ for (const [sourcePath, validationErrors] of Object.entries(
97
+ valModule.errors.validation
98
+ )) {
99
+ for (const v of validationErrors) {
100
+ if (v.fixes && v.fixes.length > 0) {
101
+ const fixPatch = await createFixPatch(
102
+ { projectRoot },
103
+ !!fix,
104
+ sourcePath as SourcePath,
105
+ v
61
106
  );
62
- }
63
- fixPatch?.remainingErrors?.forEach((e) => {
107
+ if (fix && fixPatch?.patch && fixPatch?.patch.length > 0) {
108
+ await service.patch(moduleId, fixPatch.patch);
109
+ console.log(
110
+ picocolors.green("✔"),
111
+ "Applied fix for",
112
+ sourcePath
113
+ );
114
+ }
115
+ fixPatch?.remainingErrors?.forEach((e) => {
116
+ errors += 1;
117
+ console.log(
118
+ v.fixes ? picocolors.yellow("⚠") : picocolors.red("✘"),
119
+ `Found ${v.fixes ? "fixable " : ""}error in`,
120
+ `${sourcePath}:`,
121
+ e.message
122
+ );
123
+ });
124
+ } else {
64
125
  errors += 1;
65
126
  console.log(
66
- v.fixes ? picocolors.yellow("⚠") : picocolors.red("✘"),
67
- `Found ${v.fixes ? "fixable " : ""}error in`,
127
+ picocolors.red("✘"),
128
+ "Found error in",
68
129
  `${sourcePath}:`,
69
- e.message
130
+ v.message
70
131
  );
71
- });
72
- } else {
73
- errors += 1;
74
- console.log(
75
- picocolors.red("✘"),
76
- "Found error in",
77
- `${sourcePath}:`,
78
- v.message
79
- );
132
+ }
80
133
  }
81
134
  }
82
135
  }
83
- for (const fatalError of valModule.errors.fatal || []) {
84
- errors += 1;
136
+ for (const fatalError of valModule.errors.fatal || []) {
137
+ errors += 1;
138
+ console.log(
139
+ picocolors.red("✘"),
140
+ moduleId,
141
+ "is invalid:",
142
+ fatalError.message
143
+ );
144
+ }
145
+ } else {
85
146
  console.log(
86
- picocolors.red(""),
147
+ picocolors.green(""),
87
148
  moduleId,
88
- "is invalid:",
89
- fatalError.message
149
+ "is valid (" + (Date.now() - start) + "ms)"
90
150
  );
91
151
  }
92
152
  return errors;
93
153
  }
94
154
  }
95
-
96
- const errors = (await Promise.all(valFiles.map(validateFile))).reduce(
97
- (a, b) => a + b,
98
- 0
99
- );
155
+ for (const file of valFiles) {
156
+ errors += await validateFile(file);
157
+ }
100
158
  if (errors > 0) {
101
159
  console.log(
102
160
  picocolors.red("✘"),
@@ -112,3 +170,51 @@ export async function validate({
112
170
  service.dispose();
113
171
  return;
114
172
  }
173
+
174
+ function logEslintMessage(
175
+ fileContent: string,
176
+ filePath: string,
177
+ eslintMessage: ESLint.LintResult["messages"][number]
178
+ ) {
179
+ const lines = fileContent.split("\n");
180
+ const line = lines[eslintMessage.line - 1];
181
+ const lineBefore = lines[eslintMessage.line - 2];
182
+ const lineAfter = lines[eslintMessage.line];
183
+ const isError = eslintMessage.severity >= 2;
184
+ console.log(
185
+ isError ? picocolors.red("✘") : picocolors.yellow("⚠"),
186
+ isError ? "Found eslint error:" : "Found eslint warning:",
187
+ `${filePath}:${eslintMessage.line}:${eslintMessage.column}\n`,
188
+ eslintMessage.message
189
+ );
190
+ lineBefore &&
191
+ console.log(
192
+ picocolors.gray(" " + (eslintMessage.line - 1) + " |"),
193
+ lineBefore
194
+ );
195
+ line && console.log(picocolors.gray(" " + eslintMessage.line + " |"), line);
196
+ // adds ^ below the relevant line:
197
+ const amountOfColumns =
198
+ eslintMessage.endColumn &&
199
+ eslintMessage.endColumn - eslintMessage.column > 0
200
+ ? eslintMessage.endColumn - eslintMessage.column
201
+ : 1;
202
+
203
+ line &&
204
+ console.log(
205
+ picocolors.gray(
206
+ " " + " ".repeat(eslintMessage.line.toString().length) + " |"
207
+ ),
208
+ " ".repeat(eslintMessage.column - 1) +
209
+ (eslintMessage.endColumn
210
+ ? (isError ? picocolors.red("^") : picocolors.yellow("^")).repeat(
211
+ amountOfColumns
212
+ )
213
+ : "")
214
+ );
215
+ lineAfter &&
216
+ console.log(
217
+ picocolors.gray(" " + (eslintMessage.line + 1) + " |"),
218
+ lineAfter
219
+ );
220
+ }