@valbuild/eslint-plugin 0.43.0 → 0.43.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.
@@ -8,43 +8,92 @@ function _interopDefault (e) { return e && e.__esModule ? e : { 'default': e };
8
8
 
9
9
  var path__default = /*#__PURE__*/_interopDefault(path);
10
10
 
11
- // @ts-check
12
- var getExpectedValModuleName = function getExpectedValModuleName( /** @type {{ report?: (arg0: { node: any; message: string; fix: ((fixer: any) => any) | ((fixer: any) => any); }) => void; getFilename?: any; cwd?: any; }} */context) {
13
- var filename = context.getFilename();
14
- if (filename.endsWith(".val.ts") || filename.endsWith(".val.js")) {
15
- var root = context.cwd || process.cwd();
16
- var relativePath = path__default["default"].relative(root, filename);
17
- var expectedValue = relativePath.replace(/\.val\.(ts|js)$/, "");
18
- return "/".concat(expectedValue);
19
- }
20
- };
11
+ function _arrayLikeToArray(arr, len) {
12
+ if (len == null || len > arr.length) len = arr.length;
13
+ for (var i = 0, arr2 = new Array(len); i < len; i++) arr2[i] = arr[i];
14
+ return arr2;
15
+ }
16
+
17
+ function _arrayWithoutHoles(arr) {
18
+ if (Array.isArray(arr)) return _arrayLikeToArray(arr);
19
+ }
20
+
21
+ function _iterableToArray(iter) {
22
+ if (typeof Symbol !== "undefined" && iter[Symbol.iterator] != null || iter["@@iterator"] != null) return Array.from(iter);
23
+ }
24
+
25
+ function _unsupportedIterableToArray(o, minLen) {
26
+ if (!o) return;
27
+ if (typeof o === "string") return _arrayLikeToArray(o, minLen);
28
+ var n = Object.prototype.toString.call(o).slice(8, -1);
29
+ if (n === "Object" && o.constructor) n = o.constructor.name;
30
+ if (n === "Map" || n === "Set") return Array.from(o);
31
+ if (n === "Arguments" || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)) return _arrayLikeToArray(o, minLen);
32
+ }
33
+
34
+ function _nonIterableSpread() {
35
+ throw new TypeError("Invalid attempt to spread non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.");
36
+ }
37
+
38
+ function _toConsumableArray(arr) {
39
+ return _arrayWithoutHoles(arr) || _iterableToArray(arr) || _unsupportedIterableToArray(arr) || _nonIterableSpread();
40
+ }
41
+
42
+ /**
43
+ * @type {import('eslint').Rule.RuleModule}
44
+ */
21
45
  var noIllegalModuleIds = {
22
46
  meta: {
23
47
  type: "problem",
24
48
  docs: {
25
49
  description: "Check that the first argument of export default declaration matches the string from val.config.ts file.",
26
50
  category: "Best Practices",
27
- recommended: "error"
51
+ recommended: true
28
52
  },
29
53
  fixable: "code",
30
54
  schema: []
31
55
  },
32
- create: function create( /** @type {{ report: (arg0: { node: any; message: string; fix: ((fixer: any) => any) | ((fixer: any) => any); }) => void; }} */context) {
33
- var expectedValue = getExpectedValModuleName(context);
56
+ create: function create(context) {
34
57
  return {
35
- /**
36
- * @param {{ declaration: { arguments: string | any[]; }; }} node
37
- */
38
58
  ExportDefaultDeclaration: function ExportDefaultDeclaration(node) {
59
+ /**
60
+ * @type {string | undefined}
61
+ */
62
+ var expectedValue;
63
+ if (node.parent.type === "Program") {
64
+ var maybeValConfigImportDeclaration = node.parent.body.find(function (n) {
65
+ return n.type === "ImportDeclaration" && typeof n.source.value === "string" && (n.source.value.endsWith("val.config") || n.source.value.endsWith("val.config.ts") || n.source.value.endsWith("val.config.js")) ? n.source.value : false;
66
+ });
67
+ if ((maybeValConfigImportDeclaration === null || maybeValConfigImportDeclaration === void 0 ? void 0 : maybeValConfigImportDeclaration.type) === "ImportDeclaration" && typeof maybeValConfigImportDeclaration.source.value === "string") {
68
+ var valConfigImportSource = maybeValConfigImportDeclaration.source.value;
69
+ var filename = context.filename || context.getFilename();
70
+ if (filename !== null && filename !== void 0 && filename.endsWith(".val.ts") || filename !== null && filename !== void 0 && filename.endsWith(".val.js")) {
71
+ var root = context.cwd || process.cwd();
72
+ var relativePath = path__default["default"].relative(root, filename);
73
+ expectedValue = relativePath.replace(/\.val\.(ts|js)$/, "");
74
+ // TODO: this feels like a weird way to figure out the correct relative path,
75
+ // in a monorepo, the root dir will be the root of the monorepo, not the root of the package
76
+ // so we need to account for that
77
+ // Assume the import of the val.config is correct and that it is in the root folder
78
+ var numberOfDirsToRoot = valConfigImportSource.split(".." + path__default["default"].sep).reduce(function (acc, curr) {
79
+ return curr === "" ? acc + 1 : acc;
80
+ }, 0);
81
+ var pathSegments = expectedValue.split(path__default["default"].sep);
82
+ expectedValue = "/".concat(path__default["default"].join.apply(path__default["default"], _toConsumableArray(pathSegments.slice(pathSegments.length - (numberOfDirsToRoot + 1), pathSegments.length))));
83
+ }
84
+ }
85
+ } else {
86
+ console.warn("Unexpected parent type", node.parent.type);
87
+ }
39
88
  if (!expectedValue) {
40
89
  return;
41
90
  }
42
- if (node.declaration && node.declaration.arguments && node.declaration.arguments.length > 0) {
91
+ if (node.declaration && node.declaration.type === "CallExpression" && node.declaration.arguments && node.declaration.arguments.length > 0) {
43
92
  var firstArg = node.declaration.arguments[0];
44
93
  if (firstArg.type === "TemplateLiteral") {
45
94
  context.report({
46
95
  node: firstArg,
47
- message: "val.content first argument should not be a template literal",
96
+ message: "Val: val.content id should not be a template literal",
48
97
  fix: function fix(fixer) {
49
98
  return fixer.replaceText(firstArg, "\"".concat(expectedValue, "\""));
50
99
  }
@@ -52,13 +101,17 @@ var noIllegalModuleIds = {
52
101
  }
53
102
  if (firstArg.type === "Literal" && typeof firstArg.value === "string") {
54
103
  if (firstArg.value !== expectedValue) {
55
- context.report({
56
- node: firstArg,
57
- message: "val.content first argument must match filename",
58
- fix: function fix(fixer) {
59
- return fixer.replaceText(firstArg, "".concat(firstArg.raw[0]).concat(expectedValue).concat(firstArg.raw[0]));
60
- }
61
- });
104
+ var _firstArg$raw;
105
+ var rawArg = (_firstArg$raw = firstArg.raw) === null || _firstArg$raw === void 0 ? void 0 : _firstArg$raw[0];
106
+ if (rawArg) {
107
+ context.report({
108
+ node: firstArg,
109
+ message: "Val: val.content path should match the filename. Expected: '".concat(expectedValue, "'. Found: '").concat(firstArg.value, "'"),
110
+ fix: function fix(fixer) {
111
+ return fixer.replaceText(firstArg, "".concat(rawArg).concat(expectedValue).concat(rawArg));
112
+ }
113
+ });
114
+ }
62
115
  }
63
116
  }
64
117
  }
@@ -68,8 +121,59 @@ var noIllegalModuleIds = {
68
121
  };
69
122
 
70
123
  // @ts-check
124
+
125
+ /**
126
+ * @type {Plugin["rules"]}
127
+ */
71
128
  var rules = {
72
129
  "no-illegal-module-ids": noIllegalModuleIds
73
130
  };
74
131
 
132
+ /**
133
+ * @type {Plugin["processors"]}
134
+ */
135
+ var processors = {
136
+ val: {
137
+ /**
138
+ * @param {string} text
139
+ * @param {string} filename
140
+ * @returns {{ filename: string, text: string }[]}
141
+ */
142
+ preprocess: function preprocess(text, filename) {
143
+ console.log("preprocess", {
144
+ text: text,
145
+ filename: filename
146
+ });
147
+ return [{
148
+ text: text,
149
+ filename: filename
150
+ }];
151
+ },
152
+ /**
153
+ * Transforms generated messages for output.
154
+ * @param {LintMessage[][]} messages An array containing one array of messages
155
+ * for each code block returned from `preprocess`.
156
+ * @param {string} filename The filename of the file
157
+ * @returns {LintMessage[]} A flattened array of messages with mapped locations.
158
+ */
159
+ postprocess: function postprocess(messages, filename) {
160
+ console.log({
161
+ messages: messages,
162
+ filename: filename
163
+ });
164
+ return messages.flat();
165
+ }
166
+ }
167
+ };
168
+
169
+ /**
170
+ * @type {Plugin}
171
+ */
172
+ var index = {
173
+ rules: rules,
174
+ processors: processors
175
+ };
176
+
177
+ exports["default"] = index;
178
+ exports.processors = processors;
75
179
  exports.rules = rules;
@@ -8,43 +8,92 @@ function _interopDefault (e) { return e && e.__esModule ? e : { 'default': e };
8
8
 
9
9
  var path__default = /*#__PURE__*/_interopDefault(path);
10
10
 
11
- // @ts-check
12
- var getExpectedValModuleName = function getExpectedValModuleName( /** @type {{ report?: (arg0: { node: any; message: string; fix: ((fixer: any) => any) | ((fixer: any) => any); }) => void; getFilename?: any; cwd?: any; }} */context) {
13
- var filename = context.getFilename();
14
- if (filename.endsWith(".val.ts") || filename.endsWith(".val.js")) {
15
- var root = context.cwd || process.cwd();
16
- var relativePath = path__default["default"].relative(root, filename);
17
- var expectedValue = relativePath.replace(/\.val\.(ts|js)$/, "");
18
- return "/".concat(expectedValue);
19
- }
20
- };
11
+ function _arrayLikeToArray(arr, len) {
12
+ if (len == null || len > arr.length) len = arr.length;
13
+ for (var i = 0, arr2 = new Array(len); i < len; i++) arr2[i] = arr[i];
14
+ return arr2;
15
+ }
16
+
17
+ function _arrayWithoutHoles(arr) {
18
+ if (Array.isArray(arr)) return _arrayLikeToArray(arr);
19
+ }
20
+
21
+ function _iterableToArray(iter) {
22
+ if (typeof Symbol !== "undefined" && iter[Symbol.iterator] != null || iter["@@iterator"] != null) return Array.from(iter);
23
+ }
24
+
25
+ function _unsupportedIterableToArray(o, minLen) {
26
+ if (!o) return;
27
+ if (typeof o === "string") return _arrayLikeToArray(o, minLen);
28
+ var n = Object.prototype.toString.call(o).slice(8, -1);
29
+ if (n === "Object" && o.constructor) n = o.constructor.name;
30
+ if (n === "Map" || n === "Set") return Array.from(o);
31
+ if (n === "Arguments" || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)) return _arrayLikeToArray(o, minLen);
32
+ }
33
+
34
+ function _nonIterableSpread() {
35
+ throw new TypeError("Invalid attempt to spread non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.");
36
+ }
37
+
38
+ function _toConsumableArray(arr) {
39
+ return _arrayWithoutHoles(arr) || _iterableToArray(arr) || _unsupportedIterableToArray(arr) || _nonIterableSpread();
40
+ }
41
+
42
+ /**
43
+ * @type {import('eslint').Rule.RuleModule}
44
+ */
21
45
  var noIllegalModuleIds = {
22
46
  meta: {
23
47
  type: "problem",
24
48
  docs: {
25
49
  description: "Check that the first argument of export default declaration matches the string from val.config.ts file.",
26
50
  category: "Best Practices",
27
- recommended: "error"
51
+ recommended: true
28
52
  },
29
53
  fixable: "code",
30
54
  schema: []
31
55
  },
32
- create: function create( /** @type {{ report: (arg0: { node: any; message: string; fix: ((fixer: any) => any) | ((fixer: any) => any); }) => void; }} */context) {
33
- var expectedValue = getExpectedValModuleName(context);
56
+ create: function create(context) {
34
57
  return {
35
- /**
36
- * @param {{ declaration: { arguments: string | any[]; }; }} node
37
- */
38
58
  ExportDefaultDeclaration: function ExportDefaultDeclaration(node) {
59
+ /**
60
+ * @type {string | undefined}
61
+ */
62
+ var expectedValue;
63
+ if (node.parent.type === "Program") {
64
+ var maybeValConfigImportDeclaration = node.parent.body.find(function (n) {
65
+ return n.type === "ImportDeclaration" && typeof n.source.value === "string" && (n.source.value.endsWith("val.config") || n.source.value.endsWith("val.config.ts") || n.source.value.endsWith("val.config.js")) ? n.source.value : false;
66
+ });
67
+ if ((maybeValConfigImportDeclaration === null || maybeValConfigImportDeclaration === void 0 ? void 0 : maybeValConfigImportDeclaration.type) === "ImportDeclaration" && typeof maybeValConfigImportDeclaration.source.value === "string") {
68
+ var valConfigImportSource = maybeValConfigImportDeclaration.source.value;
69
+ var filename = context.filename || context.getFilename();
70
+ if (filename !== null && filename !== void 0 && filename.endsWith(".val.ts") || filename !== null && filename !== void 0 && filename.endsWith(".val.js")) {
71
+ var root = context.cwd || process.cwd();
72
+ var relativePath = path__default["default"].relative(root, filename);
73
+ expectedValue = relativePath.replace(/\.val\.(ts|js)$/, "");
74
+ // TODO: this feels like a weird way to figure out the correct relative path,
75
+ // in a monorepo, the root dir will be the root of the monorepo, not the root of the package
76
+ // so we need to account for that
77
+ // Assume the import of the val.config is correct and that it is in the root folder
78
+ var numberOfDirsToRoot = valConfigImportSource.split(".." + path__default["default"].sep).reduce(function (acc, curr) {
79
+ return curr === "" ? acc + 1 : acc;
80
+ }, 0);
81
+ var pathSegments = expectedValue.split(path__default["default"].sep);
82
+ expectedValue = "/".concat(path__default["default"].join.apply(path__default["default"], _toConsumableArray(pathSegments.slice(pathSegments.length - (numberOfDirsToRoot + 1), pathSegments.length))));
83
+ }
84
+ }
85
+ } else {
86
+ console.warn("Unexpected parent type", node.parent.type);
87
+ }
39
88
  if (!expectedValue) {
40
89
  return;
41
90
  }
42
- if (node.declaration && node.declaration.arguments && node.declaration.arguments.length > 0) {
91
+ if (node.declaration && node.declaration.type === "CallExpression" && node.declaration.arguments && node.declaration.arguments.length > 0) {
43
92
  var firstArg = node.declaration.arguments[0];
44
93
  if (firstArg.type === "TemplateLiteral") {
45
94
  context.report({
46
95
  node: firstArg,
47
- message: "val.content first argument should not be a template literal",
96
+ message: "Val: val.content id should not be a template literal",
48
97
  fix: function fix(fixer) {
49
98
  return fixer.replaceText(firstArg, "\"".concat(expectedValue, "\""));
50
99
  }
@@ -52,13 +101,17 @@ var noIllegalModuleIds = {
52
101
  }
53
102
  if (firstArg.type === "Literal" && typeof firstArg.value === "string") {
54
103
  if (firstArg.value !== expectedValue) {
55
- context.report({
56
- node: firstArg,
57
- message: "val.content first argument must match filename",
58
- fix: function fix(fixer) {
59
- return fixer.replaceText(firstArg, "".concat(firstArg.raw[0]).concat(expectedValue).concat(firstArg.raw[0]));
60
- }
61
- });
104
+ var _firstArg$raw;
105
+ var rawArg = (_firstArg$raw = firstArg.raw) === null || _firstArg$raw === void 0 ? void 0 : _firstArg$raw[0];
106
+ if (rawArg) {
107
+ context.report({
108
+ node: firstArg,
109
+ message: "Val: val.content path should match the filename. Expected: '".concat(expectedValue, "'. Found: '").concat(firstArg.value, "'"),
110
+ fix: function fix(fixer) {
111
+ return fixer.replaceText(firstArg, "".concat(rawArg).concat(expectedValue).concat(rawArg));
112
+ }
113
+ });
114
+ }
62
115
  }
63
116
  }
64
117
  }
@@ -68,8 +121,59 @@ var noIllegalModuleIds = {
68
121
  };
69
122
 
70
123
  // @ts-check
124
+
125
+ /**
126
+ * @type {Plugin["rules"]}
127
+ */
71
128
  var rules = {
72
129
  "no-illegal-module-ids": noIllegalModuleIds
73
130
  };
74
131
 
132
+ /**
133
+ * @type {Plugin["processors"]}
134
+ */
135
+ var processors = {
136
+ val: {
137
+ /**
138
+ * @param {string} text
139
+ * @param {string} filename
140
+ * @returns {{ filename: string, text: string }[]}
141
+ */
142
+ preprocess: function preprocess(text, filename) {
143
+ console.log("preprocess", {
144
+ text: text,
145
+ filename: filename
146
+ });
147
+ return [{
148
+ text: text,
149
+ filename: filename
150
+ }];
151
+ },
152
+ /**
153
+ * Transforms generated messages for output.
154
+ * @param {LintMessage[][]} messages An array containing one array of messages
155
+ * for each code block returned from `preprocess`.
156
+ * @param {string} filename The filename of the file
157
+ * @returns {LintMessage[]} A flattened array of messages with mapped locations.
158
+ */
159
+ postprocess: function postprocess(messages, filename) {
160
+ console.log({
161
+ messages: messages,
162
+ filename: filename
163
+ });
164
+ return messages.flat();
165
+ }
166
+ }
167
+ };
168
+
169
+ /**
170
+ * @type {Plugin}
171
+ */
172
+ var index = {
173
+ rules: rules,
174
+ processors: processors
175
+ };
176
+
177
+ exports["default"] = index;
178
+ exports.processors = processors;
75
179
  exports.rules = rules;
@@ -1,42 +1,91 @@
1
1
  import path from 'path';
2
2
 
3
- // @ts-check
4
- var getExpectedValModuleName = function getExpectedValModuleName( /** @type {{ report?: (arg0: { node: any; message: string; fix: ((fixer: any) => any) | ((fixer: any) => any); }) => void; getFilename?: any; cwd?: any; }} */context) {
5
- var filename = context.getFilename();
6
- if (filename.endsWith(".val.ts") || filename.endsWith(".val.js")) {
7
- var root = context.cwd || process.cwd();
8
- var relativePath = path.relative(root, filename);
9
- var expectedValue = relativePath.replace(/\.val\.(ts|js)$/, "");
10
- return "/".concat(expectedValue);
11
- }
12
- };
3
+ function _arrayLikeToArray(arr, len) {
4
+ if (len == null || len > arr.length) len = arr.length;
5
+ for (var i = 0, arr2 = new Array(len); i < len; i++) arr2[i] = arr[i];
6
+ return arr2;
7
+ }
8
+
9
+ function _arrayWithoutHoles(arr) {
10
+ if (Array.isArray(arr)) return _arrayLikeToArray(arr);
11
+ }
12
+
13
+ function _iterableToArray(iter) {
14
+ if (typeof Symbol !== "undefined" && iter[Symbol.iterator] != null || iter["@@iterator"] != null) return Array.from(iter);
15
+ }
16
+
17
+ function _unsupportedIterableToArray(o, minLen) {
18
+ if (!o) return;
19
+ if (typeof o === "string") return _arrayLikeToArray(o, minLen);
20
+ var n = Object.prototype.toString.call(o).slice(8, -1);
21
+ if (n === "Object" && o.constructor) n = o.constructor.name;
22
+ if (n === "Map" || n === "Set") return Array.from(o);
23
+ if (n === "Arguments" || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)) return _arrayLikeToArray(o, minLen);
24
+ }
25
+
26
+ function _nonIterableSpread() {
27
+ throw new TypeError("Invalid attempt to spread non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.");
28
+ }
29
+
30
+ function _toConsumableArray(arr) {
31
+ return _arrayWithoutHoles(arr) || _iterableToArray(arr) || _unsupportedIterableToArray(arr) || _nonIterableSpread();
32
+ }
33
+
34
+ /**
35
+ * @type {import('eslint').Rule.RuleModule}
36
+ */
13
37
  var noIllegalModuleIds = {
14
38
  meta: {
15
39
  type: "problem",
16
40
  docs: {
17
41
  description: "Check that the first argument of export default declaration matches the string from val.config.ts file.",
18
42
  category: "Best Practices",
19
- recommended: "error"
43
+ recommended: true
20
44
  },
21
45
  fixable: "code",
22
46
  schema: []
23
47
  },
24
- create: function create( /** @type {{ report: (arg0: { node: any; message: string; fix: ((fixer: any) => any) | ((fixer: any) => any); }) => void; }} */context) {
25
- var expectedValue = getExpectedValModuleName(context);
48
+ create: function create(context) {
26
49
  return {
27
- /**
28
- * @param {{ declaration: { arguments: string | any[]; }; }} node
29
- */
30
50
  ExportDefaultDeclaration: function ExportDefaultDeclaration(node) {
51
+ /**
52
+ * @type {string | undefined}
53
+ */
54
+ var expectedValue;
55
+ if (node.parent.type === "Program") {
56
+ var maybeValConfigImportDeclaration = node.parent.body.find(function (n) {
57
+ return n.type === "ImportDeclaration" && typeof n.source.value === "string" && (n.source.value.endsWith("val.config") || n.source.value.endsWith("val.config.ts") || n.source.value.endsWith("val.config.js")) ? n.source.value : false;
58
+ });
59
+ if ((maybeValConfigImportDeclaration === null || maybeValConfigImportDeclaration === void 0 ? void 0 : maybeValConfigImportDeclaration.type) === "ImportDeclaration" && typeof maybeValConfigImportDeclaration.source.value === "string") {
60
+ var valConfigImportSource = maybeValConfigImportDeclaration.source.value;
61
+ var filename = context.filename || context.getFilename();
62
+ if (filename !== null && filename !== void 0 && filename.endsWith(".val.ts") || filename !== null && filename !== void 0 && filename.endsWith(".val.js")) {
63
+ var root = context.cwd || process.cwd();
64
+ var relativePath = path.relative(root, filename);
65
+ expectedValue = relativePath.replace(/\.val\.(ts|js)$/, "");
66
+ // TODO: this feels like a weird way to figure out the correct relative path,
67
+ // in a monorepo, the root dir will be the root of the monorepo, not the root of the package
68
+ // so we need to account for that
69
+ // Assume the import of the val.config is correct and that it is in the root folder
70
+ var numberOfDirsToRoot = valConfigImportSource.split(".." + path.sep).reduce(function (acc, curr) {
71
+ return curr === "" ? acc + 1 : acc;
72
+ }, 0);
73
+ var pathSegments = expectedValue.split(path.sep);
74
+ expectedValue = "/".concat(path.join.apply(path, _toConsumableArray(pathSegments.slice(pathSegments.length - (numberOfDirsToRoot + 1), pathSegments.length))));
75
+ }
76
+ }
77
+ } else {
78
+ console.warn("Unexpected parent type", node.parent.type);
79
+ }
31
80
  if (!expectedValue) {
32
81
  return;
33
82
  }
34
- if (node.declaration && node.declaration.arguments && node.declaration.arguments.length > 0) {
83
+ if (node.declaration && node.declaration.type === "CallExpression" && node.declaration.arguments && node.declaration.arguments.length > 0) {
35
84
  var firstArg = node.declaration.arguments[0];
36
85
  if (firstArg.type === "TemplateLiteral") {
37
86
  context.report({
38
87
  node: firstArg,
39
- message: "val.content first argument should not be a template literal",
88
+ message: "Val: val.content id should not be a template literal",
40
89
  fix: function fix(fixer) {
41
90
  return fixer.replaceText(firstArg, "\"".concat(expectedValue, "\""));
42
91
  }
@@ -44,13 +93,17 @@ var noIllegalModuleIds = {
44
93
  }
45
94
  if (firstArg.type === "Literal" && typeof firstArg.value === "string") {
46
95
  if (firstArg.value !== expectedValue) {
47
- context.report({
48
- node: firstArg,
49
- message: "val.content first argument must match filename",
50
- fix: function fix(fixer) {
51
- return fixer.replaceText(firstArg, "".concat(firstArg.raw[0]).concat(expectedValue).concat(firstArg.raw[0]));
52
- }
53
- });
96
+ var _firstArg$raw;
97
+ var rawArg = (_firstArg$raw = firstArg.raw) === null || _firstArg$raw === void 0 ? void 0 : _firstArg$raw[0];
98
+ if (rawArg) {
99
+ context.report({
100
+ node: firstArg,
101
+ message: "Val: val.content path should match the filename. Expected: '".concat(expectedValue, "'. Found: '").concat(firstArg.value, "'"),
102
+ fix: function fix(fixer) {
103
+ return fixer.replaceText(firstArg, "".concat(rawArg).concat(expectedValue).concat(rawArg));
104
+ }
105
+ });
106
+ }
54
107
  }
55
108
  }
56
109
  }
@@ -60,8 +113,57 @@ var noIllegalModuleIds = {
60
113
  };
61
114
 
62
115
  // @ts-check
116
+
117
+ /**
118
+ * @type {Plugin["rules"]}
119
+ */
63
120
  var rules = {
64
121
  "no-illegal-module-ids": noIllegalModuleIds
65
122
  };
66
123
 
67
- export { rules };
124
+ /**
125
+ * @type {Plugin["processors"]}
126
+ */
127
+ var processors = {
128
+ val: {
129
+ /**
130
+ * @param {string} text
131
+ * @param {string} filename
132
+ * @returns {{ filename: string, text: string }[]}
133
+ */
134
+ preprocess: function preprocess(text, filename) {
135
+ console.log("preprocess", {
136
+ text: text,
137
+ filename: filename
138
+ });
139
+ return [{
140
+ text: text,
141
+ filename: filename
142
+ }];
143
+ },
144
+ /**
145
+ * Transforms generated messages for output.
146
+ * @param {LintMessage[][]} messages An array containing one array of messages
147
+ * for each code block returned from `preprocess`.
148
+ * @param {string} filename The filename of the file
149
+ * @returns {LintMessage[]} A flattened array of messages with mapped locations.
150
+ */
151
+ postprocess: function postprocess(messages, filename) {
152
+ console.log({
153
+ messages: messages,
154
+ filename: filename
155
+ });
156
+ return messages.flat();
157
+ }
158
+ }
159
+ };
160
+
161
+ /**
162
+ * @type {Plugin}
163
+ */
164
+ var index = {
165
+ rules: rules,
166
+ processors: processors
167
+ };
168
+
169
+ export { index as default, processors, rules };
@@ -0,0 +1,6 @@
1
+ {
2
+ "plugins": ["@valbuild"],
3
+ "rules": {
4
+ "@valbuild/no-illegal-module-ids": 2
5
+ }
6
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@valbuild/eslint-plugin",
3
- "version": "0.43.0",
3
+ "version": "0.43.2",
4
4
  "description": "ESLint rules for val",
5
5
  "keywords": [
6
6
  "eslint",
@@ -26,7 +26,9 @@
26
26
  "eslint": "6 || 7 || 8"
27
27
  },
28
28
  "devDependencies": {
29
- "eslint": "^7.10.0"
29
+ "@types/jest": "^29.5.11",
30
+ "eslint": "^7.10.0",
31
+ "jest": "^29.6"
30
32
  },
31
33
  "scripts": {
32
34
  "typecheck": "tsc -p jsconfig.json"
package/src/index.js CHANGED
@@ -1,7 +1,49 @@
1
1
  // @ts-check
2
+ /**
3
+ * @typedef {import('eslint').ESLint.Plugin} Plugin
4
+ * @typedef {import('eslint').Linter.LintMessage} LintMessage
5
+ * @typedef {import('eslint').Linter.Processor} Processor
6
+ * @typedef {import('eslint').Linter } RuleModule
7
+ */
2
8
 
3
9
  import noIllegalModuleIds from "./rules/noIllegalModuleIds";
4
10
 
11
+ /**
12
+ * @type {Plugin["rules"]}
13
+ */
5
14
  export let rules = {
6
15
  "no-illegal-module-ids": noIllegalModuleIds,
7
16
  };
17
+
18
+ /**
19
+ * @type {Plugin["processors"]}
20
+ */
21
+ export const processors = {
22
+ val: {
23
+ /**
24
+ * @param {string} text
25
+ * @param {string} filename
26
+ * @returns {{ filename: string, text: string }[]}
27
+ */
28
+ preprocess: (text, filename) => {
29
+ console.log("preprocess", { text, filename });
30
+ return [{ text, filename }];
31
+ },
32
+ /**
33
+ * Transforms generated messages for output.
34
+ * @param {LintMessage[][]} messages An array containing one array of messages
35
+ * for each code block returned from `preprocess`.
36
+ * @param {string} filename The filename of the file
37
+ * @returns {LintMessage[]} A flattened array of messages with mapped locations.
38
+ */
39
+ postprocess: (messages, filename) => {
40
+ console.log({ messages, filename });
41
+ return messages.flat();
42
+ },
43
+ },
44
+ };
45
+
46
+ /**
47
+ * @type {Plugin}
48
+ */
49
+ export default { rules, processors };
@@ -1,18 +1,9 @@
1
1
  // @ts-check
2
2
  import path from "path";
3
3
 
4
- const getExpectedValModuleName = (
5
- /** @type {{ report?: (arg0: { node: any; message: string; fix: ((fixer: any) => any) | ((fixer: any) => any); }) => void; getFilename?: any; cwd?: any; }} */ context
6
- ) => {
7
- const filename = context.getFilename();
8
- if (filename.endsWith(".val.ts") || filename.endsWith(".val.js")) {
9
- const root = context.cwd || process.cwd();
10
- const relativePath = path.relative(root, filename);
11
- const expectedValue = relativePath.replace(/\.val\.(ts|js)$/, "");
12
- return `/${expectedValue}`;
13
- }
14
- };
15
-
4
+ /**
5
+ * @type {import('eslint').Rule.RuleModule}
6
+ */
16
7
  export default {
17
8
  meta: {
18
9
  type: "problem",
@@ -20,25 +11,67 @@ export default {
20
11
  description:
21
12
  "Check that the first argument of export default declaration matches the string from val.config.ts file.",
22
13
  category: "Best Practices",
23
- recommended: "error",
14
+ recommended: true,
24
15
  },
25
16
  fixable: "code",
26
17
  schema: [],
27
18
  },
28
- create: function (
29
- /** @type {{ report: (arg0: { node: any; message: string; fix: ((fixer: any) => any) | ((fixer: any) => any); }) => void; }} */ context
30
- ) {
31
- const expectedValue = getExpectedValModuleName(context);
19
+ create: function (context) {
32
20
  return {
33
- /**
34
- * @param {{ declaration: { arguments: string | any[]; }; }} node
35
- */
36
21
  ExportDefaultDeclaration(node) {
22
+ /**
23
+ * @type {string | undefined}
24
+ */
25
+ let expectedValue;
26
+ if (node.parent.type === "Program") {
27
+ const maybeValConfigImportDeclaration = node.parent.body.find((n) =>
28
+ n.type === "ImportDeclaration" &&
29
+ typeof n.source.value === "string" &&
30
+ (n.source.value.endsWith("val.config") ||
31
+ n.source.value.endsWith("val.config.ts") ||
32
+ n.source.value.endsWith("val.config.js"))
33
+ ? n.source.value
34
+ : false
35
+ );
36
+ if (
37
+ maybeValConfigImportDeclaration?.type === "ImportDeclaration" &&
38
+ typeof maybeValConfigImportDeclaration.source.value === "string"
39
+ ) {
40
+ const valConfigImportSource =
41
+ maybeValConfigImportDeclaration.source.value;
42
+ const filename = context.filename || context.getFilename();
43
+ if (
44
+ filename?.endsWith(".val.ts") ||
45
+ filename?.endsWith(".val.js")
46
+ ) {
47
+ const root = context.cwd || process.cwd();
48
+ const relativePath = path.relative(root, filename);
49
+ expectedValue = relativePath.replace(/\.val\.(ts|js)$/, "");
50
+ // TODO: this feels like a weird way to figure out the correct relative path,
51
+ // in a monorepo, the root dir will be the root of the monorepo, not the root of the package
52
+ // so we need to account for that
53
+ // Assume the import of the val.config is correct and that it is in the root folder
54
+ const numberOfDirsToRoot = valConfigImportSource
55
+ .split(".." + path.sep)
56
+ .reduce((acc, curr) => (curr === "" ? acc + 1 : acc), 0);
57
+ const pathSegments = expectedValue.split(path.sep);
58
+ expectedValue = `/${path.join(
59
+ ...pathSegments.slice(
60
+ pathSegments.length - (numberOfDirsToRoot + 1),
61
+ pathSegments.length
62
+ )
63
+ )}`;
64
+ }
65
+ }
66
+ } else {
67
+ console.warn("Unexpected parent type", node.parent.type);
68
+ }
37
69
  if (!expectedValue) {
38
70
  return;
39
71
  }
40
72
  if (
41
73
  node.declaration &&
74
+ node.declaration.type === "CallExpression" &&
42
75
  node.declaration.arguments &&
43
76
  node.declaration.arguments.length > 0
44
77
  ) {
@@ -46,8 +79,7 @@ export default {
46
79
  if (firstArg.type === "TemplateLiteral") {
47
80
  context.report({
48
81
  node: firstArg,
49
- message:
50
- "val.content first argument should not be a template literal",
82
+ message: "Val: val.content id should not be a template literal",
51
83
  fix: (fixer) => fixer.replaceText(firstArg, `"${expectedValue}"`),
52
84
  });
53
85
  }
@@ -56,15 +88,18 @@ export default {
56
88
  typeof firstArg.value === "string"
57
89
  ) {
58
90
  if (firstArg.value !== expectedValue) {
59
- context.report({
60
- node: firstArg,
61
- message: "val.content first argument must match filename",
62
- fix: (fixer) =>
63
- fixer.replaceText(
64
- firstArg,
65
- `${firstArg.raw[0]}${expectedValue}${firstArg.raw[0]}`
66
- ),
67
- });
91
+ const rawArg = firstArg.raw?.[0];
92
+ if (rawArg) {
93
+ context.report({
94
+ node: firstArg,
95
+ message: `Val: val.content path should match the filename. Expected: '${expectedValue}'. Found: '${firstArg.value}'`,
96
+ fix: (fixer) =>
97
+ fixer.replaceText(
98
+ firstArg,
99
+ `${rawArg}${expectedValue}${rawArg}`
100
+ ),
101
+ });
102
+ }
68
103
  }
69
104
  }
70
105
  }
@@ -0,0 +1,90 @@
1
+ // @ts-check
2
+ /* eslint-disable no-undef */
3
+ import { ESLint } from "eslint";
4
+ import path from "path";
5
+
6
+ /**
7
+ * @param {string} fixtureConfigName ESLint JSON config fixture filename.
8
+ */
9
+ function initESLint(fixtureConfigName) {
10
+ return new ESLint({
11
+ cwd: path.resolve(__dirname, "../fixtures/"),
12
+ ignore: false,
13
+ overrideConfigFile: path.resolve(
14
+ __dirname,
15
+ "../fixtures/",
16
+ fixtureConfigName
17
+ ),
18
+ });
19
+ }
20
+
21
+ describe("plugin", () => {
22
+ /**
23
+ * @type {ESLint}
24
+ */
25
+ let eslint;
26
+
27
+ beforeAll(() => {
28
+ eslint = initESLint("eslintrc.json");
29
+ });
30
+
31
+ test("no illegal modules for monorepos (projects that are not at root)", async () => {
32
+ const code = `import { s, val } from "../val.config";
33
+
34
+ export const schema = s.string();
35
+
36
+ export default val.content(
37
+ "/something",
38
+ schema,
39
+ "React Server components also works"
40
+ );`;
41
+ const results = await eslint.lintText(code, {
42
+ filePath: "./app/test.val.ts",
43
+ });
44
+
45
+ expect(results).toHaveLength(1);
46
+ expect(results[0].messages).toHaveLength(1);
47
+ expect(results[0].messages[0].fix?.text).toEqual('"/app/test"');
48
+ });
49
+
50
+ test("no illegal modules for monorepos (projects that are not at root) - nested", async () => {
51
+ const code = `import { s, val } from "../../../../val.config";
52
+
53
+ export const schema = s.string();
54
+
55
+ export default val.content(
56
+ "/something",
57
+ schema,
58
+ "React Server components also works"
59
+ );`;
60
+ const results = await eslint.lintText(code, {
61
+ filePath: "./content/stuff/with/all/test.val.ts",
62
+ });
63
+
64
+ expect(results).toHaveLength(1);
65
+ expect(results[0].messages).toHaveLength(1);
66
+ expect(results[0].messages[0].fix?.text).toEqual(
67
+ '"/content/stuff/with/all/test"'
68
+ );
69
+ });
70
+ test("no illegal modules for monorepos (projects that are not at root) - src", async () => {
71
+ const code = `import { s, val } from "../../../../val.config";
72
+
73
+ export const schema = s.string();
74
+
75
+ export default val.content(
76
+ "/something",
77
+ schema,
78
+ "React Server components also works"
79
+ );`;
80
+ const results = await eslint.lintText(code, {
81
+ filePath: "./src/content/stuff/with/all/test.val.ts",
82
+ });
83
+
84
+ expect(results).toHaveLength(1);
85
+ expect(results[0].messages).toHaveLength(1);
86
+ expect(results[0].messages[0].fix?.text).toEqual(
87
+ '"/content/stuff/with/all/test"'
88
+ );
89
+ });
90
+ });
@@ -33,7 +33,8 @@ ruleTester.run("no-illegal-module-ids", rule, {
33
33
  export default val.content('foo', schema, 'String')`,
34
34
  errors: [
35
35
  {
36
- message: "val.content first argument must match filename",
36
+ message:
37
+ "Val: val.content path should match the filename. Expected: '/foo/test'. Found: 'foo'",
37
38
  },
38
39
  ],
39
40
  output: `import { val, s } from '../val.config.ts';
@@ -47,7 +48,8 @@ ruleTester.run("no-illegal-module-ids", rule, {
47
48
  export default val.content("foo", schema, 'String')`,
48
49
  errors: [
49
50
  {
50
- message: "val.content first argument must match filename",
51
+ message:
52
+ "Val: val.content path should match the filename. Expected: '/foo/test'. Found: 'foo'",
51
53
  },
52
54
  ],
53
55
  output: `import { val, s } from "../val.config.ts";
@@ -61,8 +63,7 @@ ruleTester.run("no-illegal-module-ids", rule, {
61
63
  export default val.content(\`foo\`, schema, 'String')`,
62
64
  errors: [
63
65
  {
64
- message:
65
- "val.content first argument should not be a template literal",
66
+ message: "Val: val.content id should not be a template literal",
66
67
  },
67
68
  ],
68
69
  output: `import { val, s } from "../val.config.ts";