@drupal-canvas/eslint-config 0.1.3 → 0.1.4
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/index.js +617 -0
- package/package.json +1 -1
package/dist/index.js
ADDED
|
@@ -0,0 +1,617 @@
|
|
|
1
|
+
import jsxA11y from 'eslint-plugin-jsx-a11y';
|
|
2
|
+
import react from 'eslint-plugin-react';
|
|
3
|
+
import reactHooks from 'eslint-plugin-react-hooks';
|
|
4
|
+
import eslintPluginYml from 'eslint-plugin-yml';
|
|
5
|
+
import { defineConfig, globalIgnores } from 'eslint/config';
|
|
6
|
+
import tseslint from 'typescript-eslint';
|
|
7
|
+
import js from '@eslint/js';
|
|
8
|
+
import globals from 'globals';
|
|
9
|
+
import { dirname, basename } from 'path';
|
|
10
|
+
import { existsSync, readdirSync } from 'fs';
|
|
11
|
+
import { camelCase } from 'lodash-es';
|
|
12
|
+
|
|
13
|
+
// src/configs/recommended.ts
|
|
14
|
+
var JS_EXTENSIONS = ["ts", "tsx", "js", "jsx"];
|
|
15
|
+
var NAMED_SUFFIX = ".component.yml";
|
|
16
|
+
function isComponentEntrypoint(context) {
|
|
17
|
+
if (!isInComponentDir(context)) {
|
|
18
|
+
return false;
|
|
19
|
+
}
|
|
20
|
+
const componentDir = dirname(context.filename);
|
|
21
|
+
const files = getFilesInDirectory(componentDir);
|
|
22
|
+
const namedMetadataFile = files.find((file) => file.endsWith(NAMED_SUFFIX));
|
|
23
|
+
const componentBaseName = namedMetadataFile ? namedMetadataFile.slice(0, -NAMED_SUFFIX.length) : "index";
|
|
24
|
+
return JS_EXTENSIONS.some(
|
|
25
|
+
(ext) => basename(context.filename) === componentBaseName + "." + ext
|
|
26
|
+
);
|
|
27
|
+
}
|
|
28
|
+
function isInComponentDir(context) {
|
|
29
|
+
try {
|
|
30
|
+
const componentDir = dirname(context.filename);
|
|
31
|
+
const files = getFilesInDirectory(componentDir);
|
|
32
|
+
return files.filter(
|
|
33
|
+
(file) => basename(file) === "component.yml" || file.endsWith(NAMED_SUFFIX)
|
|
34
|
+
).length > 0;
|
|
35
|
+
} catch {
|
|
36
|
+
return false;
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
function isComponentYmlFile(context) {
|
|
40
|
+
try {
|
|
41
|
+
const fileName = basename(context.filename);
|
|
42
|
+
return fileName === "component.yml" || fileName.endsWith(NAMED_SUFFIX);
|
|
43
|
+
} catch {
|
|
44
|
+
return false;
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
function getFilesInDirectory(dirPath) {
|
|
48
|
+
if (!existsSync(dirPath)) {
|
|
49
|
+
return [];
|
|
50
|
+
}
|
|
51
|
+
try {
|
|
52
|
+
return readdirSync(dirPath);
|
|
53
|
+
} catch {
|
|
54
|
+
return [];
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
// src/utils/yaml.ts
|
|
59
|
+
function getYAMLStringValue(node) {
|
|
60
|
+
if (node && node.type === "YAMLScalar" && typeof node.value === "string") {
|
|
61
|
+
return node.value;
|
|
62
|
+
}
|
|
63
|
+
return null;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
// src/rules/component-dir-name.ts
|
|
67
|
+
var NAMED_SUFFIX2 = ".component.yml";
|
|
68
|
+
function getExpectedMachineName(filename) {
|
|
69
|
+
const fileName = basename(filename);
|
|
70
|
+
if (fileName !== "component.yml" && fileName.endsWith(NAMED_SUFFIX2)) {
|
|
71
|
+
return {
|
|
72
|
+
name: fileName.slice(0, -NAMED_SUFFIX2.length),
|
|
73
|
+
source: `metadata filename "${fileName}"`
|
|
74
|
+
};
|
|
75
|
+
}
|
|
76
|
+
const dirName = basename(dirname(filename));
|
|
77
|
+
return { name: dirName, source: `directory name "${dirName}"` };
|
|
78
|
+
}
|
|
79
|
+
var rule = {
|
|
80
|
+
meta: {
|
|
81
|
+
type: "problem",
|
|
82
|
+
docs: {
|
|
83
|
+
description: "Validates that the machineName in component metadata matches the component directory name (index-style) or filename prefix (named-style)"
|
|
84
|
+
}
|
|
85
|
+
},
|
|
86
|
+
create(context) {
|
|
87
|
+
if (!isComponentYmlFile(context)) {
|
|
88
|
+
return {};
|
|
89
|
+
}
|
|
90
|
+
let hasMachineName = false;
|
|
91
|
+
const { name: expectedName, source: expectedSource } = getExpectedMachineName(context.filename);
|
|
92
|
+
return {
|
|
93
|
+
YAMLPair(node) {
|
|
94
|
+
const keyName = getYAMLStringValue(node.key);
|
|
95
|
+
if (keyName !== "machineName") {
|
|
96
|
+
return;
|
|
97
|
+
}
|
|
98
|
+
hasMachineName = true;
|
|
99
|
+
const machineName = getYAMLStringValue(node.value);
|
|
100
|
+
if (!node.value || !machineName) {
|
|
101
|
+
context.report({
|
|
102
|
+
node,
|
|
103
|
+
message: "machineName must be a string."
|
|
104
|
+
});
|
|
105
|
+
return;
|
|
106
|
+
}
|
|
107
|
+
if (expectedName !== machineName) {
|
|
108
|
+
context.report({
|
|
109
|
+
node: node.value,
|
|
110
|
+
message: `${expectedSource[0].toUpperCase()}${expectedSource.slice(1)} does not match machineName "${machineName}".`
|
|
111
|
+
});
|
|
112
|
+
}
|
|
113
|
+
},
|
|
114
|
+
"Program:exit"() {
|
|
115
|
+
if (!hasMachineName) {
|
|
116
|
+
context.report({
|
|
117
|
+
loc: { line: 1, column: 0 },
|
|
118
|
+
message: `machineName key is missing. Its value should be "${expectedName}" based on ${expectedSource.toLowerCase()}.`
|
|
119
|
+
});
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
};
|
|
123
|
+
}
|
|
124
|
+
};
|
|
125
|
+
var component_dir_name_default = rule;
|
|
126
|
+
|
|
127
|
+
// src/rules/component-exports.ts
|
|
128
|
+
var rule2 = {
|
|
129
|
+
meta: {
|
|
130
|
+
type: "problem",
|
|
131
|
+
docs: {
|
|
132
|
+
description: "Validates that component has a default export"
|
|
133
|
+
}
|
|
134
|
+
},
|
|
135
|
+
create(context) {
|
|
136
|
+
if (!isComponentEntrypoint(context)) {
|
|
137
|
+
return {};
|
|
138
|
+
}
|
|
139
|
+
let hasDefaultExport = false;
|
|
140
|
+
return {
|
|
141
|
+
ExportDefaultDeclaration() {
|
|
142
|
+
hasDefaultExport = true;
|
|
143
|
+
},
|
|
144
|
+
"Program:exit"(node) {
|
|
145
|
+
if (!hasDefaultExport) {
|
|
146
|
+
context.report({
|
|
147
|
+
node,
|
|
148
|
+
message: "Component must have a default export"
|
|
149
|
+
});
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
};
|
|
153
|
+
}
|
|
154
|
+
};
|
|
155
|
+
var component_exports_default = rule2;
|
|
156
|
+
function extractProps(propsNode) {
|
|
157
|
+
if (!propsNode.value || propsNode.value.type !== "YAMLMapping") {
|
|
158
|
+
return [];
|
|
159
|
+
}
|
|
160
|
+
const propsMapping = propsNode.value;
|
|
161
|
+
const propertiesPair = propsMapping.pairs.find(
|
|
162
|
+
(p) => getYAMLStringValue(p.key) === "properties"
|
|
163
|
+
);
|
|
164
|
+
if (!propertiesPair || !propertiesPair.value || propertiesPair.value.type !== "YAMLMapping") {
|
|
165
|
+
return [];
|
|
166
|
+
}
|
|
167
|
+
const propertiesMapping = propertiesPair.value;
|
|
168
|
+
const props = [];
|
|
169
|
+
for (const pair of propertiesMapping.pairs) {
|
|
170
|
+
const propId = getYAMLStringValue(pair.key);
|
|
171
|
+
if (!propId) continue;
|
|
172
|
+
if (!pair.value || pair.value.type !== "YAMLMapping") continue;
|
|
173
|
+
const propMapping = pair.value;
|
|
174
|
+
const titlePair = propMapping.pairs.find(
|
|
175
|
+
(p) => getYAMLStringValue(p.key) === "title"
|
|
176
|
+
);
|
|
177
|
+
let title = null;
|
|
178
|
+
if (titlePair) {
|
|
179
|
+
title = getYAMLStringValue(titlePair.value);
|
|
180
|
+
}
|
|
181
|
+
props.push({
|
|
182
|
+
id: propId,
|
|
183
|
+
title,
|
|
184
|
+
node: pair
|
|
185
|
+
});
|
|
186
|
+
}
|
|
187
|
+
return props;
|
|
188
|
+
}
|
|
189
|
+
var rule3 = {
|
|
190
|
+
meta: {
|
|
191
|
+
type: "problem",
|
|
192
|
+
docs: {
|
|
193
|
+
description: "Validates that component prop IDs match the camelCase version of their titles"
|
|
194
|
+
}
|
|
195
|
+
},
|
|
196
|
+
create(context) {
|
|
197
|
+
if (!isComponentYmlFile(context)) {
|
|
198
|
+
return {};
|
|
199
|
+
}
|
|
200
|
+
return {
|
|
201
|
+
YAMLPair(node) {
|
|
202
|
+
const keyName = getYAMLStringValue(node.key);
|
|
203
|
+
if (keyName !== "props") {
|
|
204
|
+
return;
|
|
205
|
+
}
|
|
206
|
+
const props = extractProps(node);
|
|
207
|
+
if (props.length === 0) {
|
|
208
|
+
return;
|
|
209
|
+
}
|
|
210
|
+
for (const prop of props) {
|
|
211
|
+
if (!prop.title) {
|
|
212
|
+
context.report({
|
|
213
|
+
node: prop.node,
|
|
214
|
+
message: `Prop "${prop.id}" is missing a title.`
|
|
215
|
+
});
|
|
216
|
+
continue;
|
|
217
|
+
}
|
|
218
|
+
const expectedId = camelCase(prop.title);
|
|
219
|
+
if (prop.id !== expectedId) {
|
|
220
|
+
context.report({
|
|
221
|
+
node: prop.node,
|
|
222
|
+
message: `Prop machine name "${prop.id}" should be the camelCase version of its title. Expected: "${expectedId}". https://drupal.org/i/3524675`
|
|
223
|
+
});
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
};
|
|
228
|
+
}
|
|
229
|
+
};
|
|
230
|
+
var component_prop_names_default = rule3;
|
|
231
|
+
|
|
232
|
+
// src/configs/required.ts
|
|
233
|
+
var required = defineConfig([
|
|
234
|
+
globalIgnores(["**/dist/**"]),
|
|
235
|
+
{
|
|
236
|
+
files: ["**/*.{ts,tsx,js,jsx}"],
|
|
237
|
+
languageOptions: {
|
|
238
|
+
parser: tseslint.parser,
|
|
239
|
+
ecmaVersion: 2020,
|
|
240
|
+
globals: globals.browser,
|
|
241
|
+
parserOptions: {
|
|
242
|
+
ecmaVersion: "latest",
|
|
243
|
+
ecmaFeatures: { jsx: true },
|
|
244
|
+
sourceType: "module"
|
|
245
|
+
}
|
|
246
|
+
},
|
|
247
|
+
settings: { react: { version: "19.0" } }
|
|
248
|
+
},
|
|
249
|
+
eslintPluginYml.configs["flat/base"],
|
|
250
|
+
{
|
|
251
|
+
plugins: {
|
|
252
|
+
"drupal-canvas": {
|
|
253
|
+
rules: {
|
|
254
|
+
"component-dir-name": component_dir_name_default,
|
|
255
|
+
"component-exports": component_exports_default,
|
|
256
|
+
"component-prop-names": component_prop_names_default
|
|
257
|
+
}
|
|
258
|
+
}
|
|
259
|
+
},
|
|
260
|
+
rules: {
|
|
261
|
+
"drupal-canvas/component-dir-name": "error",
|
|
262
|
+
"drupal-canvas/component-exports": "error",
|
|
263
|
+
"drupal-canvas/component-prop-names": "error"
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
]);
|
|
267
|
+
var required_default = required;
|
|
268
|
+
|
|
269
|
+
// src/configs/recommended.ts
|
|
270
|
+
var recommended = defineConfig([
|
|
271
|
+
required_default,
|
|
272
|
+
{
|
|
273
|
+
files: ["**/*.{ts,tsx,js,jsx}"],
|
|
274
|
+
plugins: {
|
|
275
|
+
react,
|
|
276
|
+
"react-hooks": reactHooks,
|
|
277
|
+
"jsx-a11y": jsxA11y
|
|
278
|
+
},
|
|
279
|
+
settings: {
|
|
280
|
+
"jsx-a11y": {
|
|
281
|
+
components: {
|
|
282
|
+
Image: "img"
|
|
283
|
+
}
|
|
284
|
+
}
|
|
285
|
+
},
|
|
286
|
+
rules: {
|
|
287
|
+
...js.configs.recommended.rules,
|
|
288
|
+
...react.configs.recommended.rules,
|
|
289
|
+
...react.configs["jsx-runtime"].rules,
|
|
290
|
+
...reactHooks.configs.recommended.rules,
|
|
291
|
+
...jsxA11y.flatConfigs.recommended.rules,
|
|
292
|
+
"react/jsx-no-target-blank": "off",
|
|
293
|
+
"react/prop-types": "off"
|
|
294
|
+
}
|
|
295
|
+
},
|
|
296
|
+
...tseslint.configs.recommended,
|
|
297
|
+
...eslintPluginYml.configs["flat/recommended"]
|
|
298
|
+
]);
|
|
299
|
+
var recommended_default = recommended;
|
|
300
|
+
var REQUIRED_FILES = ["component.yml", "index.jsx"];
|
|
301
|
+
var ALLOWED_FILES = ["component.yml", "index.jsx", "index.css"];
|
|
302
|
+
var IGNORED_FILES = [
|
|
303
|
+
"dist"
|
|
304
|
+
// 'dist' dir is automatically generated by the canvas build command.
|
|
305
|
+
];
|
|
306
|
+
function isFileAllowed(fileName, allowedFiles) {
|
|
307
|
+
return allowedFiles.some((allowedFile) => allowedFile === fileName);
|
|
308
|
+
}
|
|
309
|
+
var rule4 = {
|
|
310
|
+
meta: {
|
|
311
|
+
type: "problem",
|
|
312
|
+
docs: {
|
|
313
|
+
description: "Validates that component directory contains only allowed files"
|
|
314
|
+
},
|
|
315
|
+
deprecated: true
|
|
316
|
+
},
|
|
317
|
+
create(context) {
|
|
318
|
+
if (!isComponentYmlFile(context)) {
|
|
319
|
+
return {};
|
|
320
|
+
}
|
|
321
|
+
return {
|
|
322
|
+
Program: function(node) {
|
|
323
|
+
const componentDir = dirname(context.filename);
|
|
324
|
+
const filesInDirectory = getFilesInDirectory(componentDir).filter(
|
|
325
|
+
(file) => !IGNORED_FILES.includes(file)
|
|
326
|
+
);
|
|
327
|
+
REQUIRED_FILES.filter(
|
|
328
|
+
(file) => !filesInDirectory.includes(file)
|
|
329
|
+
).forEach((file) => {
|
|
330
|
+
context.report({
|
|
331
|
+
node,
|
|
332
|
+
message: `Missing required component file: ${file}.`
|
|
333
|
+
});
|
|
334
|
+
});
|
|
335
|
+
const disallowedFiles = filesInDirectory.filter(
|
|
336
|
+
(file) => !isFileAllowed(file, ALLOWED_FILES)
|
|
337
|
+
);
|
|
338
|
+
if (disallowedFiles.length > 0) {
|
|
339
|
+
context.report({
|
|
340
|
+
node,
|
|
341
|
+
message: `Component directory contains disallowed files: ${disallowedFiles.join(", ")}. Only the following files are allowed: ${ALLOWED_FILES.join(", ")}. Other files will be overwritten by the "canvas download" command.`
|
|
342
|
+
});
|
|
343
|
+
}
|
|
344
|
+
}
|
|
345
|
+
};
|
|
346
|
+
}
|
|
347
|
+
};
|
|
348
|
+
var component_files_default = rule4;
|
|
349
|
+
|
|
350
|
+
// src/rules/component-imports.ts
|
|
351
|
+
function checkImportSource(context, node, source) {
|
|
352
|
+
if (source.startsWith("./") || source.startsWith("../")) {
|
|
353
|
+
context.report({
|
|
354
|
+
node,
|
|
355
|
+
message: `Relative component imports are not supported. Use '@/components/[component-name]' instead of '${source}'.`
|
|
356
|
+
});
|
|
357
|
+
return;
|
|
358
|
+
}
|
|
359
|
+
if ([
|
|
360
|
+
"preact",
|
|
361
|
+
"preact/hooks",
|
|
362
|
+
"react/jsx-runtime",
|
|
363
|
+
"react",
|
|
364
|
+
"react-dom",
|
|
365
|
+
"react-dom/client",
|
|
366
|
+
"clsx",
|
|
367
|
+
"class-variance-authority",
|
|
368
|
+
"tailwind-merge",
|
|
369
|
+
"@/components/",
|
|
370
|
+
"drupal-jsonapi-params",
|
|
371
|
+
"swr",
|
|
372
|
+
"drupal-canvas"
|
|
373
|
+
].includes(source)) {
|
|
374
|
+
return;
|
|
375
|
+
}
|
|
376
|
+
if (source === "@/lib/drupal-utils") {
|
|
377
|
+
context.report({
|
|
378
|
+
node,
|
|
379
|
+
message: "Drupal utilities were moved into the `drupal-canvas` package.",
|
|
380
|
+
fix(fixer) {
|
|
381
|
+
const importsSortMenu = node.type === "ImportDeclaration" && node.specifiers.some(
|
|
382
|
+
(specifier) => specifier.local.name === "sortMenu" || specifier.type === "ImportSpecifier" && specifier.imported.type === "Identifier" && specifier.imported.name === "sortMenu"
|
|
383
|
+
);
|
|
384
|
+
if (!importsSortMenu) {
|
|
385
|
+
return fixer.replaceText(node.source, "'drupal-canvas'");
|
|
386
|
+
}
|
|
387
|
+
return null;
|
|
388
|
+
}
|
|
389
|
+
});
|
|
390
|
+
return;
|
|
391
|
+
}
|
|
392
|
+
if (source === "@/lib/utils") {
|
|
393
|
+
context.report({
|
|
394
|
+
node,
|
|
395
|
+
message: "Utilities were moved into the `drupal-canvas` package.",
|
|
396
|
+
fix(fixer) {
|
|
397
|
+
return fixer.replaceText(node.source, "'drupal-canvas'");
|
|
398
|
+
}
|
|
399
|
+
});
|
|
400
|
+
return;
|
|
401
|
+
}
|
|
402
|
+
if (source === "@/lib/jsonapi-utils") {
|
|
403
|
+
context.report({
|
|
404
|
+
node,
|
|
405
|
+
message: "JSON:API utilities were moved into the `drupal-canvas` package.",
|
|
406
|
+
fix(fixer) {
|
|
407
|
+
return fixer.replaceText(node.source, "'drupal-canvas'");
|
|
408
|
+
}
|
|
409
|
+
});
|
|
410
|
+
return;
|
|
411
|
+
}
|
|
412
|
+
if (source === "next-image-standalone") {
|
|
413
|
+
context.report({
|
|
414
|
+
node,
|
|
415
|
+
message: "Using `next-image-standalone` directly is deprecated. Use the `Image` component from the `drupal-canvas` package instead.",
|
|
416
|
+
fix(fixer) {
|
|
417
|
+
if (node.type === "ImportDeclaration" && node.specifiers.length === 1 && node.specifiers[0].local.name === "Image") {
|
|
418
|
+
return fixer.replaceText(
|
|
419
|
+
node,
|
|
420
|
+
"import { Image } from 'drupal-canvas';"
|
|
421
|
+
);
|
|
422
|
+
}
|
|
423
|
+
return null;
|
|
424
|
+
}
|
|
425
|
+
});
|
|
426
|
+
return;
|
|
427
|
+
}
|
|
428
|
+
if (source === "@drupal-api-client/json-api-client") {
|
|
429
|
+
if (node.type === "ImportDeclaration") {
|
|
430
|
+
for (const specifier of node.specifiers) {
|
|
431
|
+
if (specifier.local.name === "JsonApiClient") {
|
|
432
|
+
context.report({
|
|
433
|
+
node: specifier.local,
|
|
434
|
+
message: "The preconfigured `JsonApiClient` was moved into the `drupal-canvas` package.",
|
|
435
|
+
fix(fixer) {
|
|
436
|
+
return fixer.replaceText(node.source, "'drupal-canvas'");
|
|
437
|
+
}
|
|
438
|
+
});
|
|
439
|
+
return;
|
|
440
|
+
}
|
|
441
|
+
}
|
|
442
|
+
}
|
|
443
|
+
return;
|
|
444
|
+
}
|
|
445
|
+
if (source === "@/lib/FormattedText") {
|
|
446
|
+
context.report({
|
|
447
|
+
node,
|
|
448
|
+
message: "The `FormattedText` component was moved into the `drupal-canvas` package.",
|
|
449
|
+
fix(fixer) {
|
|
450
|
+
if (node.type === "ImportDeclaration" && node.specifiers.length === 1 && node.specifiers[0].local.name === "FormattedText") {
|
|
451
|
+
return fixer.replaceText(
|
|
452
|
+
node,
|
|
453
|
+
"import { FormattedText } from 'drupal-canvas';"
|
|
454
|
+
);
|
|
455
|
+
}
|
|
456
|
+
return null;
|
|
457
|
+
}
|
|
458
|
+
});
|
|
459
|
+
return;
|
|
460
|
+
}
|
|
461
|
+
if (source.startsWith("@/components/")) {
|
|
462
|
+
if (source.replace("@/components/", "").includes("/")) {
|
|
463
|
+
context.report({
|
|
464
|
+
node,
|
|
465
|
+
message: `Components must be imported from top-level directories under "@/components/". The path "${source}" suggests a nested structure.`
|
|
466
|
+
});
|
|
467
|
+
return;
|
|
468
|
+
}
|
|
469
|
+
return;
|
|
470
|
+
}
|
|
471
|
+
if (source.startsWith("http://") || source.startsWith("https://")) {
|
|
472
|
+
return;
|
|
473
|
+
}
|
|
474
|
+
context.report({
|
|
475
|
+
node,
|
|
476
|
+
message: `Importing "${source}" is not supported. If this is a local import via a path alias, use the "@/components/" alias instead. If you are importing a third-party package, see the list of supported packages at https://project.pages.drupalcode.org/canvas/code-components/packages. (The status of supporting any third-party package can be tracked at https://drupal.org/i/3560197.)`
|
|
477
|
+
});
|
|
478
|
+
}
|
|
479
|
+
var rule5 = {
|
|
480
|
+
meta: {
|
|
481
|
+
type: "problem",
|
|
482
|
+
docs: {
|
|
483
|
+
description: "Validates that component imports only from supported import sources and patterns"
|
|
484
|
+
},
|
|
485
|
+
fixable: "code",
|
|
486
|
+
deprecated: true
|
|
487
|
+
},
|
|
488
|
+
create(context) {
|
|
489
|
+
if (!isInComponentDir(context)) {
|
|
490
|
+
return {};
|
|
491
|
+
}
|
|
492
|
+
return {
|
|
493
|
+
ImportDeclaration(node) {
|
|
494
|
+
if (node.source && typeof node.source.value === "string") {
|
|
495
|
+
checkImportSource(context, node, node.source.value);
|
|
496
|
+
}
|
|
497
|
+
},
|
|
498
|
+
ImportExpression(node) {
|
|
499
|
+
if (node.source.type === "Literal" && typeof node.source.value === "string") {
|
|
500
|
+
checkImportSource(context, node, node.source.value);
|
|
501
|
+
}
|
|
502
|
+
}
|
|
503
|
+
};
|
|
504
|
+
}
|
|
505
|
+
};
|
|
506
|
+
var component_imports_default = rule5;
|
|
507
|
+
function findTopmostComponentsParentDir(currentParentDir, rootDir) {
|
|
508
|
+
if (currentParentDir === rootDir) {
|
|
509
|
+
return currentParentDir;
|
|
510
|
+
}
|
|
511
|
+
const parentDir = dirname(currentParentDir);
|
|
512
|
+
if (hasComponentSubdirectories(parentDir)) {
|
|
513
|
+
return findTopmostComponentsParentDir(parentDir, rootDir);
|
|
514
|
+
}
|
|
515
|
+
return currentParentDir;
|
|
516
|
+
}
|
|
517
|
+
function hasComponentSubdirectories(dirPath) {
|
|
518
|
+
const files = getFilesInDirectory(dirPath);
|
|
519
|
+
for (const file of files) {
|
|
520
|
+
const subdirFiles = getFilesInDirectory(dirPath + "/" + file);
|
|
521
|
+
if (subdirFiles.includes("component.yml")) {
|
|
522
|
+
return true;
|
|
523
|
+
}
|
|
524
|
+
}
|
|
525
|
+
return false;
|
|
526
|
+
}
|
|
527
|
+
var rule6 = {
|
|
528
|
+
meta: {
|
|
529
|
+
type: "problem",
|
|
530
|
+
docs: {
|
|
531
|
+
description: "Validates that all component directories are at the same level with no nesting hierarchy"
|
|
532
|
+
},
|
|
533
|
+
deprecated: true
|
|
534
|
+
},
|
|
535
|
+
create(context) {
|
|
536
|
+
if (!isComponentYmlFile(context)) {
|
|
537
|
+
return {};
|
|
538
|
+
}
|
|
539
|
+
return {
|
|
540
|
+
Program: function(node) {
|
|
541
|
+
const currentComponentDir = dirname(context.filename);
|
|
542
|
+
const componentsDir = dirname(currentComponentDir);
|
|
543
|
+
const topmostComponentsDir = findTopmostComponentsParentDir(
|
|
544
|
+
componentsDir,
|
|
545
|
+
context.cwd
|
|
546
|
+
);
|
|
547
|
+
if (topmostComponentsDir !== componentsDir) {
|
|
548
|
+
const nestedComponent = basename(currentComponentDir);
|
|
549
|
+
let parentComponentPath = componentsDir.replace(context.cwd, "");
|
|
550
|
+
if (!parentComponentPath.startsWith("/")) {
|
|
551
|
+
parentComponentPath = "/" + parentComponentPath;
|
|
552
|
+
}
|
|
553
|
+
context.report({
|
|
554
|
+
node,
|
|
555
|
+
message: `All component directories must be at the same level with no nesting hierarchy. Found "${nestedComponent}" component inside the "${parentComponentPath}" directory.`
|
|
556
|
+
});
|
|
557
|
+
}
|
|
558
|
+
}
|
|
559
|
+
};
|
|
560
|
+
}
|
|
561
|
+
};
|
|
562
|
+
var component_no_hierarchy_default = rule6;
|
|
563
|
+
|
|
564
|
+
// src/configs/requiredDeprecated.ts
|
|
565
|
+
var required2 = defineConfig([
|
|
566
|
+
globalIgnores(["**/dist/**"]),
|
|
567
|
+
{
|
|
568
|
+
files: ["**/*.{js,jsx}"],
|
|
569
|
+
languageOptions: {
|
|
570
|
+
ecmaVersion: 2020,
|
|
571
|
+
globals: globals.browser,
|
|
572
|
+
parserOptions: {
|
|
573
|
+
ecmaVersion: "latest",
|
|
574
|
+
ecmaFeatures: { jsx: true },
|
|
575
|
+
sourceType: "module"
|
|
576
|
+
}
|
|
577
|
+
},
|
|
578
|
+
settings: { react: { version: "19.0" } }
|
|
579
|
+
},
|
|
580
|
+
eslintPluginYml.configs["flat/base"],
|
|
581
|
+
{
|
|
582
|
+
plugins: {
|
|
583
|
+
"drupal-canvas": {
|
|
584
|
+
rules: {
|
|
585
|
+
"component-dir-name": component_dir_name_default,
|
|
586
|
+
"component-exports": component_exports_default,
|
|
587
|
+
"component-files": component_files_default,
|
|
588
|
+
"component-imports": component_imports_default,
|
|
589
|
+
"component-no-hierarchy": component_no_hierarchy_default,
|
|
590
|
+
"component-prop-names": component_prop_names_default
|
|
591
|
+
}
|
|
592
|
+
}
|
|
593
|
+
},
|
|
594
|
+
rules: {
|
|
595
|
+
"drupal-canvas/component-dir-name": "error",
|
|
596
|
+
"drupal-canvas/component-exports": "error",
|
|
597
|
+
"drupal-canvas/component-files": "error",
|
|
598
|
+
"drupal-canvas/component-imports": "error",
|
|
599
|
+
"drupal-canvas/component-no-hierarchy": "error",
|
|
600
|
+
"drupal-canvas/component-prop-names": "error"
|
|
601
|
+
}
|
|
602
|
+
}
|
|
603
|
+
]);
|
|
604
|
+
var requiredDeprecated_default = required2;
|
|
605
|
+
var strict = defineConfig([
|
|
606
|
+
recommended_default,
|
|
607
|
+
{
|
|
608
|
+
files: ["**/*.{ts,tsx,js,jsx}"],
|
|
609
|
+
rules: {
|
|
610
|
+
...jsxA11y.flatConfigs.strict.rules
|
|
611
|
+
}
|
|
612
|
+
},
|
|
613
|
+
...tseslint.configs.strict
|
|
614
|
+
]);
|
|
615
|
+
var strict_default = strict;
|
|
616
|
+
|
|
617
|
+
export { recommended_default as recommended, required_default as required, requiredDeprecated_default as requiredDeprecated, strict_default as strict };
|