@gallop.software/canon 2.10.0 → 2.12.0
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/eslint/index.d.ts +4 -0
- package/dist/eslint/index.js +8 -2
- package/dist/eslint/rules/no-component-in-blocks.d.ts +3 -0
- package/dist/eslint/rules/no-component-in-blocks.js +74 -0
- package/dist/eslint/rules/prefer-list-components.d.ts +3 -0
- package/dist/eslint/rules/prefer-list-components.js +48 -0
- package/package.json +1 -1
- package/schema.json +20 -0
package/dist/eslint/index.d.ts
CHANGED
|
@@ -29,6 +29,8 @@ declare const plugin: {
|
|
|
29
29
|
name: string;
|
|
30
30
|
};
|
|
31
31
|
'no-native-intersection-observer': import("eslint").Rule.RuleModule;
|
|
32
|
+
'no-component-in-blocks': import("eslint").Rule.RuleModule;
|
|
33
|
+
'prefer-list-components': import("eslint").Rule.RuleModule;
|
|
32
34
|
};
|
|
33
35
|
/**
|
|
34
36
|
* Recommended rule configurations - spread into your ESLint config
|
|
@@ -46,6 +48,8 @@ declare const plugin: {
|
|
|
46
48
|
readonly 'gallop/no-cross-zone-imports': "warn";
|
|
47
49
|
readonly 'gallop/no-data-imports': "warn";
|
|
48
50
|
readonly 'gallop/no-native-intersection-observer': "warn";
|
|
51
|
+
readonly 'gallop/no-component-in-blocks': "warn";
|
|
52
|
+
readonly 'gallop/prefer-list-components': "warn";
|
|
49
53
|
};
|
|
50
54
|
};
|
|
51
55
|
export default plugin;
|
package/dist/eslint/index.js
CHANGED
|
@@ -9,6 +9,8 @@ import noArbitraryColors from './rules/no-arbitrary-colors.js';
|
|
|
9
9
|
import noCrossZoneImports from './rules/no-cross-zone-imports.js';
|
|
10
10
|
import noDataImports from './rules/no-data-imports.js';
|
|
11
11
|
import noNativeIntersectionObserver from './rules/no-native-intersection-observer.js';
|
|
12
|
+
import noComponentInBlocks from './rules/no-component-in-blocks.js';
|
|
13
|
+
import preferListComponents from './rules/prefer-list-components.js';
|
|
12
14
|
/**
|
|
13
15
|
* All Canon ESLint rules with recommended severity levels
|
|
14
16
|
*/
|
|
@@ -24,11 +26,13 @@ const recommended = {
|
|
|
24
26
|
'gallop/no-cross-zone-imports': 'warn',
|
|
25
27
|
'gallop/no-data-imports': 'warn',
|
|
26
28
|
'gallop/no-native-intersection-observer': 'warn',
|
|
29
|
+
'gallop/no-component-in-blocks': 'warn',
|
|
30
|
+
'gallop/prefer-list-components': 'warn',
|
|
27
31
|
};
|
|
28
32
|
const plugin = {
|
|
29
33
|
meta: {
|
|
30
34
|
name: 'eslint-plugin-gallop',
|
|
31
|
-
version: '2.
|
|
35
|
+
version: '2.12.0',
|
|
32
36
|
},
|
|
33
37
|
rules: {
|
|
34
38
|
'no-client-blocks': noClientBlocks,
|
|
@@ -42,6 +46,8 @@ const plugin = {
|
|
|
42
46
|
'no-cross-zone-imports': noCrossZoneImports,
|
|
43
47
|
'no-data-imports': noDataImports,
|
|
44
48
|
'no-native-intersection-observer': noNativeIntersectionObserver,
|
|
49
|
+
'no-component-in-blocks': noComponentInBlocks,
|
|
50
|
+
'prefer-list-components': preferListComponents,
|
|
45
51
|
},
|
|
46
52
|
/**
|
|
47
53
|
* Recommended rule configurations - spread into your ESLint config
|
|
@@ -50,4 +56,4 @@ const plugin = {
|
|
|
50
56
|
recommended,
|
|
51
57
|
};
|
|
52
58
|
export default plugin;
|
|
53
|
-
//# sourceMappingURL=data:application/json;base64,
|
|
59
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaW5kZXguanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi9zcmMvZXNsaW50L2luZGV4LnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBLE9BQU8sY0FBYyxNQUFNLDZCQUE2QixDQUFBO0FBQ3hELE9BQU8sb0JBQW9CLE1BQU0sb0NBQW9DLENBQUE7QUFDckUsT0FBTyxvQkFBb0IsTUFBTSxtQ0FBbUMsQ0FBQTtBQUNwRSxPQUFPLDBCQUEwQixNQUFNLHlDQUF5QyxDQUFBO0FBQ2hGLE9BQU8sc0JBQXNCLE1BQU0scUNBQXFDLENBQUE7QUFDeEUsT0FBTyxzQkFBc0IsTUFBTSxxQ0FBcUMsQ0FBQTtBQUN4RSxPQUFPLGNBQWMsTUFBTSw2QkFBNkIsQ0FBQTtBQUN4RCxPQUFPLGlCQUFpQixNQUFNLGdDQUFnQyxDQUFBO0FBQzlELE9BQU8sa0JBQWtCLE1BQU0sa0NBQWtDLENBQUE7QUFDakUsT0FBTyxhQUFhLE1BQU0sNEJBQTRCLENBQUE7QUFDdEQsT0FBTyw0QkFBNEIsTUFBTSw0Q0FBNEMsQ0FBQTtBQUNyRixPQUFPLG1CQUFtQixNQUFNLG1DQUFtQyxDQUFBO0FBQ25FLE9BQU8sb0JBQW9CLE1BQU0sbUNBQW1DLENBQUE7QUFFcEU7O0dBRUc7QUFDSCxNQUFNLFdBQVcsR0FBRztJQUNsQix5QkFBeUIsRUFBRSxNQUFNO0lBQ2pDLGdDQUFnQyxFQUFFLE1BQU07SUFDeEMsK0JBQStCLEVBQUUsTUFBTTtJQUN2QyxxQ0FBcUMsRUFBRSxNQUFNO0lBQzdDLGlDQUFpQyxFQUFFLE1BQU07SUFDekMsaUNBQWlDLEVBQUUsTUFBTTtJQUN6Qyx5QkFBeUIsRUFBRSxNQUFNO0lBQ2pDLDRCQUE0QixFQUFFLE1BQU07SUFDcEMsOEJBQThCLEVBQUUsTUFBTTtJQUN0Qyx3QkFBd0IsRUFBRSxNQUFNO0lBQ2hDLHdDQUF3QyxFQUFFLE1BQU07SUFDaEQsK0JBQStCLEVBQUUsTUFBTTtJQUN2QywrQkFBK0IsRUFBRSxNQUFNO0NBQy9CLENBQUE7QUFFVixNQUFNLE1BQU0sR0FBRztJQUNiLElBQUksRUFBRTtRQUNKLElBQUksRUFBRSxzQkFBc0I7UUFDNUIsT0FBTyxFQUFFLFFBQVE7S0FDbEI7SUFDRCxLQUFLLEVBQUU7UUFDTCxrQkFBa0IsRUFBRSxjQUFjO1FBQ2xDLHlCQUF5QixFQUFFLG9CQUFvQjtRQUMvQyx3QkFBd0IsRUFBRSxvQkFBb0I7UUFDOUMsOEJBQThCLEVBQUUsMEJBQTBCO1FBQzFELDBCQUEwQixFQUFFLHNCQUFzQjtRQUNsRCwwQkFBMEIsRUFBRSxzQkFBc0I7UUFDbEQsa0JBQWtCLEVBQUUsY0FBYztRQUNsQyxxQkFBcUIsRUFBRSxpQkFBaUI7UUFDeEMsdUJBQXVCLEVBQUUsa0JBQWtCO1FBQzNDLGlCQUFpQixFQUFFLGFBQWE7UUFDaEMsaUNBQWlDLEVBQUUsNEJBQTRCO1FBQy9ELHdCQUF3QixFQUFFLG1CQUFtQjtRQUM3Qyx3QkFBd0IsRUFBRSxvQkFBb0I7S0FDL0M7SUFDRDs7O09BR0c7SUFDSCxXQUFXO0NBQ1osQ0FBQTtBQUVELGVBQWUsTUFBTSxDQUFBIiwic291cmNlc0NvbnRlbnQiOlsiaW1wb3J0IG5vQ2xpZW50QmxvY2tzIGZyb20gJy4vcnVsZXMvbm8tY2xpZW50LWJsb2Nrcy5qcydcbmltcG9ydCBub0NvbnRhaW5lckluU2VjdGlvbiBmcm9tICcuL3J1bGVzL25vLWNvbnRhaW5lci1pbi1zZWN0aW9uLmpzJ1xuaW1wb3J0IHByZWZlckNvbXBvbmVudFByb3BzIGZyb20gJy4vcnVsZXMvcHJlZmVyLWNvbXBvbmVudC1wcm9wcy5qcydcbmltcG9ydCBwcmVmZXJUeXBvZ3JhcGh5Q29tcG9uZW50cyBmcm9tICcuL3J1bGVzL3ByZWZlci10eXBvZ3JhcGh5LWNvbXBvbmVudHMuanMnXG5pbXBvcnQgcHJlZmVyTGF5b3V0Q29tcG9uZW50cyBmcm9tICcuL3J1bGVzL3ByZWZlci1sYXlvdXQtY29tcG9uZW50cy5qcydcbmltcG9ydCBiYWNrZ3JvdW5kSW1hZ2VSb3VuZGVkIGZyb20gJy4vcnVsZXMvYmFja2dyb3VuZC1pbWFnZS1yb3VuZGVkLmpzJ1xuaW1wb3J0IG5vSW5saW5lU3R5bGVzIGZyb20gJy4vcnVsZXMvbm8taW5saW5lLXN0eWxlcy5qcydcbmltcG9ydCBub0FyYml0cmFyeUNvbG9ycyBmcm9tICcuL3J1bGVzL25vLWFyYml0cmFyeS1jb2xvcnMuanMnXG5pbXBvcnQgbm9Dcm9zc1pvbmVJbXBvcnRzIGZyb20gJy4vcnVsZXMvbm8tY3Jvc3Mtem9uZS1pbXBvcnRzLmpzJ1xuaW1wb3J0IG5vRGF0YUltcG9ydHMgZnJvbSAnLi9ydWxlcy9uby1kYXRhLWltcG9ydHMuanMnXG5pbXBvcnQgbm9OYXRpdmVJbnRlcnNlY3Rpb25PYnNlcnZlciBmcm9tICcuL3J1bGVzL25vLW5hdGl2ZS1pbnRlcnNlY3Rpb24tb2JzZXJ2ZXIuanMnXG5pbXBvcnQgbm9Db21wb25lbnRJbkJsb2NrcyBmcm9tICcuL3J1bGVzL25vLWNvbXBvbmVudC1pbi1ibG9ja3MuanMnXG5pbXBvcnQgcHJlZmVyTGlzdENvbXBvbmVudHMgZnJvbSAnLi9ydWxlcy9wcmVmZXItbGlzdC1jb21wb25lbnRzLmpzJ1xuXG4vKipcbiAqIEFsbCBDYW5vbiBFU0xpbnQgcnVsZXMgd2l0aCByZWNvbW1lbmRlZCBzZXZlcml0eSBsZXZlbHNcbiAqL1xuY29uc3QgcmVjb21tZW5kZWQgPSB7XG4gICdnYWxsb3Avbm8tY2xpZW50LWJsb2Nrcyc6ICd3YXJuJyxcbiAgJ2dhbGxvcC9uby1jb250YWluZXItaW4tc2VjdGlvbic6ICd3YXJuJyxcbiAgJ2dhbGxvcC9wcmVmZXItY29tcG9uZW50LXByb3BzJzogJ3dhcm4nLFxuICAnZ2FsbG9wL3ByZWZlci10eXBvZ3JhcGh5LWNvbXBvbmVudHMnOiAnd2FybicsXG4gICdnYWxsb3AvcHJlZmVyLWxheW91dC1jb21wb25lbnRzJzogJ3dhcm4nLFxuICAnZ2FsbG9wL2JhY2tncm91bmQtaW1hZ2Utcm91bmRlZCc6ICd3YXJuJyxcbiAgJ2dhbGxvcC9uby1pbmxpbmUtc3R5bGVzJzogJ3dhcm4nLFxuICAnZ2FsbG9wL25vLWFyYml0cmFyeS1jb2xvcnMnOiAnd2FybicsXG4gICdnYWxsb3Avbm8tY3Jvc3Mtem9uZS1pbXBvcnRzJzogJ3dhcm4nLFxuICAnZ2FsbG9wL25vLWRhdGEtaW1wb3J0cyc6ICd3YXJuJyxcbiAgJ2dhbGxvcC9uby1uYXRpdmUtaW50ZXJzZWN0aW9uLW9ic2VydmVyJzogJ3dhcm4nLFxuICAnZ2FsbG9wL25vLWNvbXBvbmVudC1pbi1ibG9ja3MnOiAnd2FybicsXG4gICdnYWxsb3AvcHJlZmVyLWxpc3QtY29tcG9uZW50cyc6ICd3YXJuJyxcbn0gYXMgY29uc3RcblxuY29uc3QgcGx1Z2luID0ge1xuICBtZXRhOiB7XG4gICAgbmFtZTogJ2VzbGludC1wbHVnaW4tZ2FsbG9wJyxcbiAgICB2ZXJzaW9uOiAnMi4xMi4wJyxcbiAgfSxcbiAgcnVsZXM6IHtcbiAgICAnbm8tY2xpZW50LWJsb2Nrcyc6IG5vQ2xpZW50QmxvY2tzLFxuICAgICduby1jb250YWluZXItaW4tc2VjdGlvbic6IG5vQ29udGFpbmVySW5TZWN0aW9uLFxuICAgICdwcmVmZXItY29tcG9uZW50LXByb3BzJzogcHJlZmVyQ29tcG9uZW50UHJvcHMsXG4gICAgJ3ByZWZlci10eXBvZ3JhcGh5LWNvbXBvbmVudHMnOiBwcmVmZXJUeXBvZ3JhcGh5Q29tcG9uZW50cyxcbiAgICAncHJlZmVyLWxheW91dC1jb21wb25lbnRzJzogcHJlZmVyTGF5b3V0Q29tcG9uZW50cyxcbiAgICAnYmFja2dyb3VuZC1pbWFnZS1yb3VuZGVkJzogYmFja2dyb3VuZEltYWdlUm91bmRlZCxcbiAgICAnbm8taW5saW5lLXN0eWxlcyc6IG5vSW5saW5lU3R5bGVzLFxuICAgICduby1hcmJpdHJhcnktY29sb3JzJzogbm9BcmJpdHJhcnlDb2xvcnMsXG4gICAgJ25vLWNyb3NzLXpvbmUtaW1wb3J0cyc6IG5vQ3Jvc3Nab25lSW1wb3J0cyxcbiAgICAnbm8tZGF0YS1pbXBvcnRzJzogbm9EYXRhSW1wb3J0cyxcbiAgICAnbm8tbmF0aXZlLWludGVyc2VjdGlvbi1vYnNlcnZlcic6IG5vTmF0aXZlSW50ZXJzZWN0aW9uT2JzZXJ2ZXIsXG4gICAgJ25vLWNvbXBvbmVudC1pbi1ibG9ja3MnOiBub0NvbXBvbmVudEluQmxvY2tzLFxuICAgICdwcmVmZXItbGlzdC1jb21wb25lbnRzJzogcHJlZmVyTGlzdENvbXBvbmVudHMsXG4gIH0sXG4gIC8qKlxuICAgKiBSZWNvbW1lbmRlZCBydWxlIGNvbmZpZ3VyYXRpb25zIC0gc3ByZWFkIGludG8geW91ciBFU0xpbnQgY29uZmlnXG4gICAqIEBleGFtcGxlIHJ1bGVzOiB7IC4uLmdhbGxvcC5yZWNvbW1lbmRlZCB9XG4gICAqL1xuICByZWNvbW1lbmRlZCxcbn1cblxuZXhwb3J0IGRlZmF1bHQgcGx1Z2luXG4iXX0=
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
const rule = {
|
|
2
|
+
meta: {
|
|
3
|
+
type: 'suggestion',
|
|
4
|
+
docs: {
|
|
5
|
+
description: 'Prevent defining component functions inside block files - components should be in the components folder',
|
|
6
|
+
category: 'Best Practices',
|
|
7
|
+
recommended: true,
|
|
8
|
+
},
|
|
9
|
+
messages: {
|
|
10
|
+
noComponentInBlocks: '[Canon 025] Component functions should not be defined in block files. Move this component to src/components/ and import it.',
|
|
11
|
+
},
|
|
12
|
+
schema: [],
|
|
13
|
+
},
|
|
14
|
+
create(context) {
|
|
15
|
+
const filename = context.filename || context.getFilename();
|
|
16
|
+
// Only check block files
|
|
17
|
+
const isBlock = filename.includes('/blocks/') || filename.includes('\\blocks\\');
|
|
18
|
+
if (!isBlock) {
|
|
19
|
+
return {};
|
|
20
|
+
}
|
|
21
|
+
// Track the default export name to allow it
|
|
22
|
+
let defaultExportName = null;
|
|
23
|
+
return {
|
|
24
|
+
// Track the default export
|
|
25
|
+
ExportDefaultDeclaration(node) {
|
|
26
|
+
if (node.declaration.type === 'FunctionDeclaration' && node.declaration.id) {
|
|
27
|
+
defaultExportName = node.declaration.id.name;
|
|
28
|
+
}
|
|
29
|
+
else if (node.declaration.type === 'Identifier') {
|
|
30
|
+
defaultExportName = node.declaration.name;
|
|
31
|
+
}
|
|
32
|
+
},
|
|
33
|
+
// Check for function declarations that look like components (PascalCase)
|
|
34
|
+
FunctionDeclaration(node) {
|
|
35
|
+
if (!node.id)
|
|
36
|
+
return;
|
|
37
|
+
const name = node.id.name;
|
|
38
|
+
// Skip the default export (the block itself)
|
|
39
|
+
if (name === defaultExportName)
|
|
40
|
+
return;
|
|
41
|
+
// Check if it's PascalCase (likely a component)
|
|
42
|
+
if (/^[A-Z]/.test(name)) {
|
|
43
|
+
context.report({
|
|
44
|
+
node,
|
|
45
|
+
messageId: 'noComponentInBlocks',
|
|
46
|
+
});
|
|
47
|
+
}
|
|
48
|
+
},
|
|
49
|
+
// Check for arrow function components: const MyComponent = () => {}
|
|
50
|
+
VariableDeclaration(node) {
|
|
51
|
+
for (const declarator of node.declarations) {
|
|
52
|
+
if (declarator.id.type === 'Identifier' &&
|
|
53
|
+
declarator.init &&
|
|
54
|
+
(declarator.init.type === 'ArrowFunctionExpression' ||
|
|
55
|
+
declarator.init.type === 'FunctionExpression')) {
|
|
56
|
+
const name = declarator.id.name;
|
|
57
|
+
// Skip the default export
|
|
58
|
+
if (name === defaultExportName)
|
|
59
|
+
return;
|
|
60
|
+
// Check if it's PascalCase (likely a component)
|
|
61
|
+
if (/^[A-Z]/.test(name)) {
|
|
62
|
+
context.report({
|
|
63
|
+
node: declarator,
|
|
64
|
+
messageId: 'noComponentInBlocks',
|
|
65
|
+
});
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
},
|
|
70
|
+
};
|
|
71
|
+
},
|
|
72
|
+
};
|
|
73
|
+
export default rule;
|
|
74
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoibm8tY29tcG9uZW50LWluLWJsb2Nrcy5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uLy4uL3NyYy9lc2xpbnQvcnVsZXMvbm8tY29tcG9uZW50LWluLWJsb2Nrcy50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFFQSxNQUFNLElBQUksR0FBb0I7SUFDNUIsSUFBSSxFQUFFO1FBQ0osSUFBSSxFQUFFLFlBQVk7UUFDbEIsSUFBSSxFQUFFO1lBQ0osV0FBVyxFQUNULHlHQUF5RztZQUMzRyxRQUFRLEVBQUUsZ0JBQWdCO1lBQzFCLFdBQVcsRUFBRSxJQUFJO1NBQ2xCO1FBQ0QsUUFBUSxFQUFFO1lBQ1IsbUJBQW1CLEVBQ2pCLDZIQUE2SDtTQUNoSTtRQUNELE1BQU0sRUFBRSxFQUFFO0tBQ1g7SUFDRCxNQUFNLENBQUMsT0FBeUI7UUFDOUIsTUFBTSxRQUFRLEdBQUcsT0FBTyxDQUFDLFFBQVEsSUFBSSxPQUFPLENBQUMsV0FBVyxFQUFFLENBQUE7UUFFMUQseUJBQXlCO1FBQ3pCLE1BQU0sT0FBTyxHQUFHLFFBQVEsQ0FBQyxRQUFRLENBQUMsVUFBVSxDQUFDLElBQUksUUFBUSxDQUFDLFFBQVEsQ0FBQyxZQUFZLENBQUMsQ0FBQTtRQUNoRixJQUFJLENBQUMsT0FBTyxFQUFFLENBQUM7WUFDYixPQUFPLEVBQUUsQ0FBQTtRQUNYLENBQUM7UUFFRCw0Q0FBNEM7UUFDNUMsSUFBSSxpQkFBaUIsR0FBa0IsSUFBSSxDQUFBO1FBRTNDLE9BQU87WUFDTCwyQkFBMkI7WUFDM0Isd0JBQXdCLENBQUMsSUFBSTtnQkFDM0IsSUFBSSxJQUFJLENBQUMsV0FBVyxDQUFDLElBQUksS0FBSyxxQkFBcUIsSUFBSSxJQUFJLENBQUMsV0FBVyxDQUFDLEVBQUUsRUFBRSxDQUFDO29CQUMzRSxpQkFBaUIsR0FBRyxJQUFJLENBQUMsV0FBVyxDQUFDLEVBQUUsQ0FBQyxJQUFJLENBQUE7Z0JBQzlDLENBQUM7cUJBQU0sSUFBSSxJQUFJLENBQUMsV0FBVyxDQUFDLElBQUksS0FBSyxZQUFZLEVBQUUsQ0FBQztvQkFDbEQsaUJBQWlCLEdBQUcsSUFBSSxDQUFDLFdBQVcsQ0FBQyxJQUFJLENBQUE7Z0JBQzNDLENBQUM7WUFDSCxDQUFDO1lBRUQseUVBQXlFO1lBQ3pFLG1CQUFtQixDQUFDLElBQUk7Z0JBQ3RCLElBQUksQ0FBQyxJQUFJLENBQUMsRUFBRTtvQkFBRSxPQUFNO2dCQUVwQixNQUFNLElBQUksR0FBRyxJQUFJLENBQUMsRUFBRSxDQUFDLElBQUksQ0FBQTtnQkFFekIsNkNBQTZDO2dCQUM3QyxJQUFJLElBQUksS0FBSyxpQkFBaUI7b0JBQUUsT0FBTTtnQkFFdEMsZ0RBQWdEO2dCQUNoRCxJQUFJLFFBQVEsQ0FBQyxJQUFJLENBQUMsSUFBSSxDQUFDLEVBQUUsQ0FBQztvQkFDeEIsT0FBTyxDQUFDLE1BQU0sQ0FBQzt3QkFDYixJQUFJO3dCQUNKLFNBQVMsRUFBRSxxQkFBcUI7cUJBQ2pDLENBQUMsQ0FBQTtnQkFDSixDQUFDO1lBQ0gsQ0FBQztZQUVELG9FQUFvRTtZQUNwRSxtQkFBbUIsQ0FBQyxJQUFJO2dCQUN0QixLQUFLLE1BQU0sVUFBVSxJQUFJLElBQUksQ0FBQyxZQUFZLEVBQUUsQ0FBQztvQkFDM0MsSUFDRSxVQUFVLENBQUMsRUFBRSxDQUFDLElBQUksS0FBSyxZQUFZO3dCQUNuQyxVQUFVLENBQUMsSUFBSTt3QkFDZixDQUFDLFVBQVUsQ0FBQyxJQUFJLENBQUMsSUFBSSxLQUFLLHlCQUF5Qjs0QkFDakQsVUFBVSxDQUFDLElBQUksQ0FBQyxJQUFJLEtBQUssb0JBQW9CLENBQUMsRUFDaEQsQ0FBQzt3QkFDRCxNQUFNLElBQUksR0FBRyxVQUFVLENBQUMsRUFBRSxDQUFDLElBQUksQ0FBQTt3QkFFL0IsMEJBQTBCO3dCQUMxQixJQUFJLElBQUksS0FBSyxpQkFBaUI7NEJBQUUsT0FBTTt3QkFFdEMsZ0RBQWdEO3dCQUNoRCxJQUFJLFFBQVEsQ0FBQyxJQUFJLENBQUMsSUFBSSxDQUFDLEVBQUUsQ0FBQzs0QkFDeEIsT0FBTyxDQUFDLE1BQU0sQ0FBQztnQ0FDYixJQUFJLEVBQUUsVUFBVTtnQ0FDaEIsU0FBUyxFQUFFLHFCQUFxQjs2QkFDakMsQ0FBQyxDQUFBO3dCQUNKLENBQUM7b0JBQ0gsQ0FBQztnQkFDSCxDQUFDO1lBQ0gsQ0FBQztTQUNGLENBQUE7SUFDSCxDQUFDO0NBQ0YsQ0FBQTtBQUVELGVBQWUsSUFBSSxDQUFBIiwic291cmNlc0NvbnRlbnQiOlsiaW1wb3J0IHsgUnVsZSB9IGZyb20gJ2VzbGludCdcblxuY29uc3QgcnVsZTogUnVsZS5SdWxlTW9kdWxlID0ge1xuICBtZXRhOiB7XG4gICAgdHlwZTogJ3N1Z2dlc3Rpb24nLFxuICAgIGRvY3M6IHtcbiAgICAgIGRlc2NyaXB0aW9uOlxuICAgICAgICAnUHJldmVudCBkZWZpbmluZyBjb21wb25lbnQgZnVuY3Rpb25zIGluc2lkZSBibG9jayBmaWxlcyAtIGNvbXBvbmVudHMgc2hvdWxkIGJlIGluIHRoZSBjb21wb25lbnRzIGZvbGRlcicsXG4gICAgICBjYXRlZ29yeTogJ0Jlc3QgUHJhY3RpY2VzJyxcbiAgICAgIHJlY29tbWVuZGVkOiB0cnVlLFxuICAgIH0sXG4gICAgbWVzc2FnZXM6IHtcbiAgICAgIG5vQ29tcG9uZW50SW5CbG9ja3M6XG4gICAgICAgICdbQ2Fub24gMDI1XSBDb21wb25lbnQgZnVuY3Rpb25zIHNob3VsZCBub3QgYmUgZGVmaW5lZCBpbiBibG9jayBmaWxlcy4gTW92ZSB0aGlzIGNvbXBvbmVudCB0byBzcmMvY29tcG9uZW50cy8gYW5kIGltcG9ydCBpdC4nLFxuICAgIH0sXG4gICAgc2NoZW1hOiBbXSxcbiAgfSxcbiAgY3JlYXRlKGNvbnRleHQ6IFJ1bGUuUnVsZUNvbnRleHQpIHtcbiAgICBjb25zdCBmaWxlbmFtZSA9IGNvbnRleHQuZmlsZW5hbWUgfHwgY29udGV4dC5nZXRGaWxlbmFtZSgpXG5cbiAgICAvLyBPbmx5IGNoZWNrIGJsb2NrIGZpbGVzXG4gICAgY29uc3QgaXNCbG9jayA9IGZpbGVuYW1lLmluY2x1ZGVzKCcvYmxvY2tzLycpIHx8IGZpbGVuYW1lLmluY2x1ZGVzKCdcXFxcYmxvY2tzXFxcXCcpXG4gICAgaWYgKCFpc0Jsb2NrKSB7XG4gICAgICByZXR1cm4ge31cbiAgICB9XG5cbiAgICAvLyBUcmFjayB0aGUgZGVmYXVsdCBleHBvcnQgbmFtZSB0byBhbGxvdyBpdFxuICAgIGxldCBkZWZhdWx0RXhwb3J0TmFtZTogc3RyaW5nIHwgbnVsbCA9IG51bGxcblxuICAgIHJldHVybiB7XG4gICAgICAvLyBUcmFjayB0aGUgZGVmYXVsdCBleHBvcnRcbiAgICAgIEV4cG9ydERlZmF1bHREZWNsYXJhdGlvbihub2RlKSB7XG4gICAgICAgIGlmIChub2RlLmRlY2xhcmF0aW9uLnR5cGUgPT09ICdGdW5jdGlvbkRlY2xhcmF0aW9uJyAmJiBub2RlLmRlY2xhcmF0aW9uLmlkKSB7XG4gICAgICAgICAgZGVmYXVsdEV4cG9ydE5hbWUgPSBub2RlLmRlY2xhcmF0aW9uLmlkLm5hbWVcbiAgICAgICAgfSBlbHNlIGlmIChub2RlLmRlY2xhcmF0aW9uLnR5cGUgPT09ICdJZGVudGlmaWVyJykge1xuICAgICAgICAgIGRlZmF1bHRFeHBvcnROYW1lID0gbm9kZS5kZWNsYXJhdGlvbi5uYW1lXG4gICAgICAgIH1cbiAgICAgIH0sXG5cbiAgICAgIC8vIENoZWNrIGZvciBmdW5jdGlvbiBkZWNsYXJhdGlvbnMgdGhhdCBsb29rIGxpa2UgY29tcG9uZW50cyAoUGFzY2FsQ2FzZSlcbiAgICAgIEZ1bmN0aW9uRGVjbGFyYXRpb24obm9kZSkge1xuICAgICAgICBpZiAoIW5vZGUuaWQpIHJldHVyblxuXG4gICAgICAgIGNvbnN0IG5hbWUgPSBub2RlLmlkLm5hbWVcblxuICAgICAgICAvLyBTa2lwIHRoZSBkZWZhdWx0IGV4cG9ydCAodGhlIGJsb2NrIGl0c2VsZilcbiAgICAgICAgaWYgKG5hbWUgPT09IGRlZmF1bHRFeHBvcnROYW1lKSByZXR1cm5cblxuICAgICAgICAvLyBDaGVjayBpZiBpdCdzIFBhc2NhbENhc2UgKGxpa2VseSBhIGNvbXBvbmVudClcbiAgICAgICAgaWYgKC9eW0EtWl0vLnRlc3QobmFtZSkpIHtcbiAgICAgICAgICBjb250ZXh0LnJlcG9ydCh7XG4gICAgICAgICAgICBub2RlLFxuICAgICAgICAgICAgbWVzc2FnZUlkOiAnbm9Db21wb25lbnRJbkJsb2NrcycsXG4gICAgICAgICAgfSlcbiAgICAgICAgfVxuICAgICAgfSxcblxuICAgICAgLy8gQ2hlY2sgZm9yIGFycm93IGZ1bmN0aW9uIGNvbXBvbmVudHM6IGNvbnN0IE15Q29tcG9uZW50ID0gKCkgPT4ge31cbiAgICAgIFZhcmlhYmxlRGVjbGFyYXRpb24obm9kZSkge1xuICAgICAgICBmb3IgKGNvbnN0IGRlY2xhcmF0b3Igb2Ygbm9kZS5kZWNsYXJhdGlvbnMpIHtcbiAgICAgICAgICBpZiAoXG4gICAgICAgICAgICBkZWNsYXJhdG9yLmlkLnR5cGUgPT09ICdJZGVudGlmaWVyJyAmJlxuICAgICAgICAgICAgZGVjbGFyYXRvci5pbml0ICYmXG4gICAgICAgICAgICAoZGVjbGFyYXRvci5pbml0LnR5cGUgPT09ICdBcnJvd0Z1bmN0aW9uRXhwcmVzc2lvbicgfHxcbiAgICAgICAgICAgICAgZGVjbGFyYXRvci5pbml0LnR5cGUgPT09ICdGdW5jdGlvbkV4cHJlc3Npb24nKVxuICAgICAgICAgICkge1xuICAgICAgICAgICAgY29uc3QgbmFtZSA9IGRlY2xhcmF0b3IuaWQubmFtZVxuXG4gICAgICAgICAgICAvLyBTa2lwIHRoZSBkZWZhdWx0IGV4cG9ydFxuICAgICAgICAgICAgaWYgKG5hbWUgPT09IGRlZmF1bHRFeHBvcnROYW1lKSByZXR1cm5cblxuICAgICAgICAgICAgLy8gQ2hlY2sgaWYgaXQncyBQYXNjYWxDYXNlIChsaWtlbHkgYSBjb21wb25lbnQpXG4gICAgICAgICAgICBpZiAoL15bQS1aXS8udGVzdChuYW1lKSkge1xuICAgICAgICAgICAgICBjb250ZXh0LnJlcG9ydCh7XG4gICAgICAgICAgICAgICAgbm9kZTogZGVjbGFyYXRvcixcbiAgICAgICAgICAgICAgICBtZXNzYWdlSWQ6ICdub0NvbXBvbmVudEluQmxvY2tzJyxcbiAgICAgICAgICAgICAgfSlcbiAgICAgICAgICAgIH1cbiAgICAgICAgICB9XG4gICAgICAgIH1cbiAgICAgIH0sXG4gICAgfVxuICB9LFxufVxuXG5leHBvcnQgZGVmYXVsdCBydWxlXG4iXX0=
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import { getCanonUrl, getCanonPattern } from '../utils/canon.js';
|
|
2
|
+
const RULE_NAME = 'prefer-list-components';
|
|
3
|
+
const pattern = getCanonPattern(RULE_NAME);
|
|
4
|
+
const rule = {
|
|
5
|
+
meta: {
|
|
6
|
+
type: 'suggestion',
|
|
7
|
+
docs: {
|
|
8
|
+
description: pattern?.summary || 'Use List/Li, not raw ul/li tags',
|
|
9
|
+
recommended: true,
|
|
10
|
+
url: getCanonUrl(RULE_NAME),
|
|
11
|
+
},
|
|
12
|
+
messages: {
|
|
13
|
+
useList: `[Canon ${pattern?.id || '026'}] Use the List component instead of <ul>. Import: import { List } from "@/components"`,
|
|
14
|
+
useLi: `[Canon ${pattern?.id || '026'}] Use the Li component instead of <li>. Import: import { Li } from "@/components"`,
|
|
15
|
+
},
|
|
16
|
+
schema: [],
|
|
17
|
+
},
|
|
18
|
+
create(context) {
|
|
19
|
+
const filename = context.filename || context.getFilename();
|
|
20
|
+
// Only apply to block files
|
|
21
|
+
if (!filename.includes('/blocks/')) {
|
|
22
|
+
return {};
|
|
23
|
+
}
|
|
24
|
+
return {
|
|
25
|
+
JSXOpeningElement(node) {
|
|
26
|
+
const elementName = node.name?.name;
|
|
27
|
+
// Check <ul> tags
|
|
28
|
+
if (elementName === 'ul') {
|
|
29
|
+
context.report({
|
|
30
|
+
node,
|
|
31
|
+
messageId: 'useList',
|
|
32
|
+
});
|
|
33
|
+
return;
|
|
34
|
+
}
|
|
35
|
+
// Check <li> tags
|
|
36
|
+
if (elementName === 'li') {
|
|
37
|
+
context.report({
|
|
38
|
+
node,
|
|
39
|
+
messageId: 'useLi',
|
|
40
|
+
});
|
|
41
|
+
return;
|
|
42
|
+
}
|
|
43
|
+
},
|
|
44
|
+
};
|
|
45
|
+
},
|
|
46
|
+
};
|
|
47
|
+
export default rule;
|
|
48
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoicHJlZmVyLWxpc3QtY29tcG9uZW50cy5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uLy4uL3NyYy9lc2xpbnQvcnVsZXMvcHJlZmVyLWxpc3QtY29tcG9uZW50cy50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFDQSxPQUFPLEVBQUUsV0FBVyxFQUFFLGVBQWUsRUFBRSxNQUFNLG1CQUFtQixDQUFBO0FBRWhFLE1BQU0sU0FBUyxHQUFHLHdCQUF3QixDQUFBO0FBQzFDLE1BQU0sT0FBTyxHQUFHLGVBQWUsQ0FBQyxTQUFTLENBQUMsQ0FBQTtBQUUxQyxNQUFNLElBQUksR0FBb0I7SUFDNUIsSUFBSSxFQUFFO1FBQ0osSUFBSSxFQUFFLFlBQVk7UUFDbEIsSUFBSSxFQUFFO1lBQ0osV0FBVyxFQUFFLE9BQU8sRUFBRSxPQUFPLElBQUksaUNBQWlDO1lBQ2xFLFdBQVcsRUFBRSxJQUFJO1lBQ2pCLEdBQUcsRUFBRSxXQUFXLENBQUMsU0FBUyxDQUFDO1NBQzVCO1FBQ0QsUUFBUSxFQUFFO1lBQ1IsT0FBTyxFQUFFLFVBQVUsT0FBTyxFQUFFLEVBQUUsSUFBSSxLQUFLLHVGQUF1RjtZQUM5SCxLQUFLLEVBQUUsVUFBVSxPQUFPLEVBQUUsRUFBRSxJQUFJLEtBQUssbUZBQW1GO1NBQ3pIO1FBQ0QsTUFBTSxFQUFFLEVBQUU7S0FDWDtJQUVELE1BQU0sQ0FBQyxPQUFPO1FBQ1osTUFBTSxRQUFRLEdBQUcsT0FBTyxDQUFDLFFBQVEsSUFBSSxPQUFPLENBQUMsV0FBVyxFQUFFLENBQUE7UUFFMUQsNEJBQTRCO1FBQzVCLElBQUksQ0FBQyxRQUFRLENBQUMsUUFBUSxDQUFDLFVBQVUsQ0FBQyxFQUFFLENBQUM7WUFDbkMsT0FBTyxFQUFFLENBQUE7UUFDWCxDQUFDO1FBRUQsT0FBTztZQUNMLGlCQUFpQixDQUFDLElBQVM7Z0JBQ3pCLE1BQU0sV0FBVyxHQUFHLElBQUksQ0FBQyxJQUFJLEVBQUUsSUFBSSxDQUFBO2dCQUVuQyxrQkFBa0I7Z0JBQ2xCLElBQUksV0FBVyxLQUFLLElBQUksRUFBRSxDQUFDO29CQUN6QixPQUFPLENBQUMsTUFBTSxDQUFDO3dCQUNiLElBQUk7d0JBQ0osU0FBUyxFQUFFLFNBQVM7cUJBQ3JCLENBQUMsQ0FBQTtvQkFDRixPQUFNO2dCQUNSLENBQUM7Z0JBRUQsa0JBQWtCO2dCQUNsQixJQUFJLFdBQVcsS0FBSyxJQUFJLEVBQUUsQ0FBQztvQkFDekIsT0FBTyxDQUFDLE1BQU0sQ0FBQzt3QkFDYixJQUFJO3dCQUNKLFNBQVMsRUFBRSxPQUFPO3FCQUNuQixDQUFDLENBQUE7b0JBQ0YsT0FBTTtnQkFDUixDQUFDO1lBQ0gsQ0FBQztTQUNGLENBQUE7SUFDSCxDQUFDO0NBQ0YsQ0FBQTtBQUVELGVBQWUsSUFBSSxDQUFBIiwic291cmNlc0NvbnRlbnQiOlsiaW1wb3J0IHR5cGUgeyBSdWxlIH0gZnJvbSAnZXNsaW50J1xuaW1wb3J0IHsgZ2V0Q2Fub25VcmwsIGdldENhbm9uUGF0dGVybiB9IGZyb20gJy4uL3V0aWxzL2Nhbm9uLmpzJ1xuXG5jb25zdCBSVUxFX05BTUUgPSAncHJlZmVyLWxpc3QtY29tcG9uZW50cydcbmNvbnN0IHBhdHRlcm4gPSBnZXRDYW5vblBhdHRlcm4oUlVMRV9OQU1FKVxuXG5jb25zdCBydWxlOiBSdWxlLlJ1bGVNb2R1bGUgPSB7XG4gIG1ldGE6IHtcbiAgICB0eXBlOiAnc3VnZ2VzdGlvbicsXG4gICAgZG9jczoge1xuICAgICAgZGVzY3JpcHRpb246IHBhdHRlcm4/LnN1bW1hcnkgfHwgJ1VzZSBMaXN0L0xpLCBub3QgcmF3IHVsL2xpIHRhZ3MnLFxuICAgICAgcmVjb21tZW5kZWQ6IHRydWUsXG4gICAgICB1cmw6IGdldENhbm9uVXJsKFJVTEVfTkFNRSksXG4gICAgfSxcbiAgICBtZXNzYWdlczoge1xuICAgICAgdXNlTGlzdDogYFtDYW5vbiAke3BhdHRlcm4/LmlkIHx8ICcwMjYnfV0gVXNlIHRoZSBMaXN0IGNvbXBvbmVudCBpbnN0ZWFkIG9mIDx1bD4uIEltcG9ydDogaW1wb3J0IHsgTGlzdCB9IGZyb20gXCJAL2NvbXBvbmVudHNcImAsXG4gICAgICB1c2VMaTogYFtDYW5vbiAke3BhdHRlcm4/LmlkIHx8ICcwMjYnfV0gVXNlIHRoZSBMaSBjb21wb25lbnQgaW5zdGVhZCBvZiA8bGk+LiBJbXBvcnQ6IGltcG9ydCB7IExpIH0gZnJvbSBcIkAvY29tcG9uZW50c1wiYCxcbiAgICB9LFxuICAgIHNjaGVtYTogW10sXG4gIH0sXG5cbiAgY3JlYXRlKGNvbnRleHQpIHtcbiAgICBjb25zdCBmaWxlbmFtZSA9IGNvbnRleHQuZmlsZW5hbWUgfHwgY29udGV4dC5nZXRGaWxlbmFtZSgpXG5cbiAgICAvLyBPbmx5IGFwcGx5IHRvIGJsb2NrIGZpbGVzXG4gICAgaWYgKCFmaWxlbmFtZS5pbmNsdWRlcygnL2Jsb2Nrcy8nKSkge1xuICAgICAgcmV0dXJuIHt9XG4gICAgfVxuXG4gICAgcmV0dXJuIHtcbiAgICAgIEpTWE9wZW5pbmdFbGVtZW50KG5vZGU6IGFueSkge1xuICAgICAgICBjb25zdCBlbGVtZW50TmFtZSA9IG5vZGUubmFtZT8ubmFtZVxuXG4gICAgICAgIC8vIENoZWNrIDx1bD4gdGFnc1xuICAgICAgICBpZiAoZWxlbWVudE5hbWUgPT09ICd1bCcpIHtcbiAgICAgICAgICBjb250ZXh0LnJlcG9ydCh7XG4gICAgICAgICAgICBub2RlLFxuICAgICAgICAgICAgbWVzc2FnZUlkOiAndXNlTGlzdCcsXG4gICAgICAgICAgfSlcbiAgICAgICAgICByZXR1cm5cbiAgICAgICAgfVxuXG4gICAgICAgIC8vIENoZWNrIDxsaT4gdGFnc1xuICAgICAgICBpZiAoZWxlbWVudE5hbWUgPT09ICdsaScpIHtcbiAgICAgICAgICBjb250ZXh0LnJlcG9ydCh7XG4gICAgICAgICAgICBub2RlLFxuICAgICAgICAgICAgbWVzc2FnZUlkOiAndXNlTGknLFxuICAgICAgICAgIH0pXG4gICAgICAgICAgcmV0dXJuXG4gICAgICAgIH1cbiAgICAgIH0sXG4gICAgfVxuICB9LFxufVxuXG5leHBvcnQgZGVmYXVsdCBydWxlXG4iXX0=
|
package/package.json
CHANGED
package/schema.json
CHANGED
|
@@ -280,6 +280,26 @@
|
|
|
280
280
|
"enforcement": "eslint",
|
|
281
281
|
"rule": "gallop/no-native-intersection-observer",
|
|
282
282
|
"summary": "Use react-intersection-observer package, not native API"
|
|
283
|
+
},
|
|
284
|
+
{
|
|
285
|
+
"id": "025",
|
|
286
|
+
"title": "No Components in Blocks",
|
|
287
|
+
"file": "patterns/025-no-component-in-blocks.md",
|
|
288
|
+
"category": "structure",
|
|
289
|
+
"status": "stable",
|
|
290
|
+
"enforcement": "eslint",
|
|
291
|
+
"rule": "gallop/no-component-in-blocks",
|
|
292
|
+
"summary": "Component functions must be in components folder, not blocks"
|
|
293
|
+
},
|
|
294
|
+
{
|
|
295
|
+
"id": "026",
|
|
296
|
+
"title": "List Components",
|
|
297
|
+
"file": "patterns/026-list-components.md",
|
|
298
|
+
"category": "components",
|
|
299
|
+
"status": "stable",
|
|
300
|
+
"enforcement": "eslint",
|
|
301
|
+
"rule": "gallop/prefer-list-components",
|
|
302
|
+
"summary": "Use List/Li, not raw ul/li tags"
|
|
283
303
|
}
|
|
284
304
|
],
|
|
285
305
|
"guarantees": [
|