@valbuild/eslint-plugin 0.43.0 → 0.43.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.
- package/dist/valbuild-eslint-plugin.cjs.dev.js +129 -25
- package/dist/valbuild-eslint-plugin.cjs.prod.js +129 -25
- package/dist/valbuild-eslint-plugin.esm.js +128 -26
- package/fixtures/eslintrc.json +6 -0
- package/package.json +4 -2
- package/src/index.js +42 -0
- package/src/rules/noIllegalModuleIds.js +66 -31
- package/test/plugin.test.js +49 -0
- package/test/rules/noIllegalModuleIds.test.js +5 -4
@@ -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
|
-
|
12
|
-
|
13
|
-
var
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
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:
|
51
|
+
recommended: true
|
28
52
|
},
|
29
53
|
fixable: "code",
|
30
54
|
schema: []
|
31
55
|
},
|
32
|
-
create: function create(
|
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("..").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
|
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
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
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
|
-
|
12
|
-
|
13
|
-
var
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
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:
|
51
|
+
recommended: true
|
28
52
|
},
|
29
53
|
fixable: "code",
|
30
54
|
schema: []
|
31
55
|
},
|
32
|
-
create: function create(
|
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("..").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
|
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
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
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
|
-
|
4
|
-
|
5
|
-
var
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
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:
|
43
|
+
recommended: true
|
20
44
|
},
|
21
45
|
fixable: "code",
|
22
46
|
schema: []
|
23
47
|
},
|
24
|
-
create: function create(
|
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("..").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
|
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
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
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
|
-
|
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 };
|
package/package.json
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
{
|
2
2
|
"name": "@valbuild/eslint-plugin",
|
3
|
-
"version": "0.43.
|
3
|
+
"version": "0.43.1",
|
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
|
-
"
|
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
|
-
|
5
|
-
|
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:
|
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("..")
|
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
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
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,49 @@
|
|
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
|
+
});
|
@@ -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:
|
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:
|
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";
|