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