@gallop.software/canon 2.0.1 → 2.1.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.
@@ -8,6 +8,7 @@ declare const recommendedConfig: {
8
8
  'gallop/no-client-blocks': string;
9
9
  'gallop/no-container-in-section': string;
10
10
  'gallop/prefer-component-props': string;
11
+ 'gallop/prefer-layout-components': string;
11
12
  };
12
13
  };
13
14
  export default recommendedConfig;
@@ -9,7 +9,8 @@ const recommendedConfig = {
9
9
  'gallop/no-client-blocks': 'warn',
10
10
  'gallop/no-container-in-section': 'warn',
11
11
  'gallop/prefer-component-props': 'warn',
12
+ 'gallop/prefer-layout-components': 'warn',
12
13
  },
13
14
  };
14
15
  export default recommendedConfig;
15
- //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoicmVjb21tZW5kZWQuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi9zcmMvZXNsaW50L2NvbmZpZ3MvcmVjb21tZW5kZWQudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUE7OztHQUdHO0FBQ0gsTUFBTSxpQkFBaUIsR0FBRztJQUN4QixPQUFPLEVBQUUsQ0FBQyxRQUFRLENBQUM7SUFDbkIsS0FBSyxFQUFFO1FBQ0wsMENBQTBDO1FBQzFDLHlCQUF5QixFQUFFLE1BQU07UUFDakMsZ0NBQWdDLEVBQUUsTUFBTTtRQUN4QywrQkFBK0IsRUFBRSxNQUFNO0tBQ3hDO0NBQ0YsQ0FBQTtBQUVELGVBQWUsaUJBQWlCLENBQUEiLCJzb3VyY2VzQ29udGVudCI6WyIvKipcbiAqIFJlY29tbWVuZGVkIGNvbmZpZ3VyYXRpb25cbiAqIEEgc2Vuc2libGUgZGVmYXVsdCBmb3IgYW55IEdhbGxvcC1iYXNlZCB0ZW1wbGF0ZVxuICovXG5jb25zdCByZWNvbW1lbmRlZENvbmZpZyA9IHtcbiAgcGx1Z2luczogWydnYWxsb3AnXSxcbiAgcnVsZXM6IHtcbiAgICAvLyBDb3JlIHJ1bGVzIHRoYXQgYXBwbHkgdG8gbW9zdCB0ZW1wbGF0ZXNcbiAgICAnZ2FsbG9wL25vLWNsaWVudC1ibG9ja3MnOiAnd2FybicsXG4gICAgJ2dhbGxvcC9uby1jb250YWluZXItaW4tc2VjdGlvbic6ICd3YXJuJyxcbiAgICAnZ2FsbG9wL3ByZWZlci1jb21wb25lbnQtcHJvcHMnOiAnd2FybicsXG4gIH0sXG59XG5cbmV4cG9ydCBkZWZhdWx0IHJlY29tbWVuZGVkQ29uZmlnXG4iXX0=
16
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoicmVjb21tZW5kZWQuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi9zcmMvZXNsaW50L2NvbmZpZ3MvcmVjb21tZW5kZWQudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUE7OztHQUdHO0FBQ0gsTUFBTSxpQkFBaUIsR0FBRztJQUN4QixPQUFPLEVBQUUsQ0FBQyxRQUFRLENBQUM7SUFDbkIsS0FBSyxFQUFFO1FBQ0wsMENBQTBDO1FBQzFDLHlCQUF5QixFQUFFLE1BQU07UUFDakMsZ0NBQWdDLEVBQUUsTUFBTTtRQUN4QywrQkFBK0IsRUFBRSxNQUFNO1FBQ3ZDLGlDQUFpQyxFQUFFLE1BQU07S0FDMUM7Q0FDRixDQUFBO0FBRUQsZUFBZSxpQkFBaUIsQ0FBQSIsInNvdXJjZXNDb250ZW50IjpbIi8qKlxuICogUmVjb21tZW5kZWQgY29uZmlndXJhdGlvblxuICogQSBzZW5zaWJsZSBkZWZhdWx0IGZvciBhbnkgR2FsbG9wLWJhc2VkIHRlbXBsYXRlXG4gKi9cbmNvbnN0IHJlY29tbWVuZGVkQ29uZmlnID0ge1xuICBwbHVnaW5zOiBbJ2dhbGxvcCddLFxuICBydWxlczoge1xuICAgIC8vIENvcmUgcnVsZXMgdGhhdCBhcHBseSB0byBtb3N0IHRlbXBsYXRlc1xuICAgICdnYWxsb3Avbm8tY2xpZW50LWJsb2Nrcyc6ICd3YXJuJyxcbiAgICAnZ2FsbG9wL25vLWNvbnRhaW5lci1pbi1zZWN0aW9uJzogJ3dhcm4nLFxuICAgICdnYWxsb3AvcHJlZmVyLWNvbXBvbmVudC1wcm9wcyc6ICd3YXJuJyxcbiAgICAnZ2FsbG9wL3ByZWZlci1sYXlvdXQtY29tcG9uZW50cyc6ICd3YXJuJyxcbiAgfSxcbn1cblxuZXhwb3J0IGRlZmF1bHQgcmVjb21tZW5kZWRDb25maWdcbiJdfQ==
@@ -8,6 +8,7 @@ declare const speedwellConfig: {
8
8
  'gallop/no-client-blocks': string;
9
9
  'gallop/no-container-in-section': string;
10
10
  'gallop/prefer-component-props': string;
11
+ 'gallop/prefer-layout-components': string;
11
12
  };
12
13
  };
13
14
  export default speedwellConfig;
@@ -11,7 +11,9 @@ const speedwellConfig = {
11
11
  'gallop/no-container-in-section': 'warn',
12
12
  // Use component props instead of className for style values
13
13
  'gallop/prefer-component-props': 'warn',
14
+ // Use Grid/Columns instead of raw div with grid classes
15
+ 'gallop/prefer-layout-components': 'warn',
14
16
  },
15
17
  };
16
18
  export default speedwellConfig;
17
- //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoic3BlZWR3ZWxsLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vLi4vc3JjL2VzbGludC9jb25maWdzL3NwZWVkd2VsbC50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQTs7O0dBR0c7QUFDSCxNQUFNLGVBQWUsR0FBRztJQUN0QixPQUFPLEVBQUUsQ0FBQyxRQUFRLENBQUM7SUFDbkIsS0FBSyxFQUFFO1FBQ0wsMEVBQTBFO1FBQzFFLHlCQUF5QixFQUFFLE1BQU07UUFFakMsdUNBQXVDO1FBQ3ZDLGdDQUFnQyxFQUFFLE1BQU07UUFFeEMsNERBQTREO1FBQzVELCtCQUErQixFQUFFLE1BQU07S0FDeEM7Q0FDRixDQUFBO0FBRUQsZUFBZSxlQUFlLENBQUEiLCJzb3VyY2VzQ29udGVudCI6WyIvKipcbiAqIFNwZWVkd2VsbCB0ZW1wbGF0ZSBjb25maWd1cmF0aW9uXG4gKiBFbmFibGVzIGFsbCBHYWxsb3AgcnVsZXMgcmVsZXZhbnQgdG8gdGhlIFNwZWVkd2VsbCBhcmNoaXRlY3R1cmVcbiAqL1xuY29uc3Qgc3BlZWR3ZWxsQ29uZmlnID0ge1xuICBwbHVnaW5zOiBbJ2dhbGxvcCddLFxuICBydWxlczoge1xuICAgIC8vIEJsb2NrcyBzaG91bGQgYmUgc2VydmVyIGNvbXBvbmVudHMgLSBleHRyYWN0IGNsaWVudCBsb2dpYyB0byBjb21wb25lbnRzXG4gICAgJ2dhbGxvcC9uby1jbGllbnQtYmxvY2tzJzogJ3dhcm4nLFxuXG4gICAgLy8gU2VjdGlvbiBhbHJlYWR5IHByb3ZpZGVzIGNvbnRhaW5tZW50XG4gICAgJ2dhbGxvcC9uby1jb250YWluZXItaW4tc2VjdGlvbic6ICd3YXJuJyxcblxuICAgIC8vIFVzZSBjb21wb25lbnQgcHJvcHMgaW5zdGVhZCBvZiBjbGFzc05hbWUgZm9yIHN0eWxlIHZhbHVlc1xuICAgICdnYWxsb3AvcHJlZmVyLWNvbXBvbmVudC1wcm9wcyc6ICd3YXJuJyxcbiAgfSxcbn1cblxuZXhwb3J0IGRlZmF1bHQgc3BlZWR3ZWxsQ29uZmlnXG4iXX0=
19
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoic3BlZWR3ZWxsLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vLi4vc3JjL2VzbGludC9jb25maWdzL3NwZWVkd2VsbC50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQTs7O0dBR0c7QUFDSCxNQUFNLGVBQWUsR0FBRztJQUN0QixPQUFPLEVBQUUsQ0FBQyxRQUFRLENBQUM7SUFDbkIsS0FBSyxFQUFFO1FBQ0wsMEVBQTBFO1FBQzFFLHlCQUF5QixFQUFFLE1BQU07UUFFakMsdUNBQXVDO1FBQ3ZDLGdDQUFnQyxFQUFFLE1BQU07UUFFeEMsNERBQTREO1FBQzVELCtCQUErQixFQUFFLE1BQU07UUFFdkMsd0RBQXdEO1FBQ3hELGlDQUFpQyxFQUFFLE1BQU07S0FDMUM7Q0FDRixDQUFBO0FBRUQsZUFBZSxlQUFlLENBQUEiLCJzb3VyY2VzQ29udGVudCI6WyIvKipcbiAqIFNwZWVkd2VsbCB0ZW1wbGF0ZSBjb25maWd1cmF0aW9uXG4gKiBFbmFibGVzIGFsbCBHYWxsb3AgcnVsZXMgcmVsZXZhbnQgdG8gdGhlIFNwZWVkd2VsbCBhcmNoaXRlY3R1cmVcbiAqL1xuY29uc3Qgc3BlZWR3ZWxsQ29uZmlnID0ge1xuICBwbHVnaW5zOiBbJ2dhbGxvcCddLFxuICBydWxlczoge1xuICAgIC8vIEJsb2NrcyBzaG91bGQgYmUgc2VydmVyIGNvbXBvbmVudHMgLSBleHRyYWN0IGNsaWVudCBsb2dpYyB0byBjb21wb25lbnRzXG4gICAgJ2dhbGxvcC9uby1jbGllbnQtYmxvY2tzJzogJ3dhcm4nLFxuXG4gICAgLy8gU2VjdGlvbiBhbHJlYWR5IHByb3ZpZGVzIGNvbnRhaW5tZW50XG4gICAgJ2dhbGxvcC9uby1jb250YWluZXItaW4tc2VjdGlvbic6ICd3YXJuJyxcblxuICAgIC8vIFVzZSBjb21wb25lbnQgcHJvcHMgaW5zdGVhZCBvZiBjbGFzc05hbWUgZm9yIHN0eWxlIHZhbHVlc1xuICAgICdnYWxsb3AvcHJlZmVyLWNvbXBvbmVudC1wcm9wcyc6ICd3YXJuJyxcblxuICAgIC8vIFVzZSBHcmlkL0NvbHVtbnMgaW5zdGVhZCBvZiByYXcgZGl2IHdpdGggZ3JpZCBjbGFzc2VzXG4gICAgJ2dhbGxvcC9wcmVmZXItbGF5b3V0LWNvbXBvbmVudHMnOiAnd2FybicsXG4gIH0sXG59XG5cbmV4cG9ydCBkZWZhdWx0IHNwZWVkd2VsbENvbmZpZ1xuIl19
@@ -14,6 +14,7 @@ declare const plugin: {
14
14
  name: string;
15
15
  };
16
16
  'prefer-typography-components': import("eslint").Rule.RuleModule;
17
+ 'prefer-layout-components': import("eslint").Rule.RuleModule;
17
18
  };
18
19
  configs: {
19
20
  speedwell: {
@@ -22,6 +23,7 @@ declare const plugin: {
22
23
  'gallop/no-client-blocks': string;
23
24
  'gallop/no-container-in-section': string;
24
25
  'gallop/prefer-component-props': string;
26
+ 'gallop/prefer-layout-components': string;
25
27
  };
26
28
  };
27
29
  recommended: {
@@ -30,6 +32,7 @@ declare const plugin: {
30
32
  'gallop/no-client-blocks': string;
31
33
  'gallop/no-container-in-section': string;
32
34
  'gallop/prefer-component-props': string;
35
+ 'gallop/prefer-layout-components': string;
33
36
  };
34
37
  };
35
38
  };
@@ -2,6 +2,7 @@ import noClientBlocks from './rules/no-client-blocks.js';
2
2
  import noContainerInSection from './rules/no-container-in-section.js';
3
3
  import preferComponentProps from './rules/prefer-component-props.js';
4
4
  import preferTypographyComponents from './rules/prefer-typography-components.js';
5
+ import preferLayoutComponents from './rules/prefer-layout-components.js';
5
6
  import speedwellConfig from './configs/speedwell.js';
6
7
  import recommendedConfig from './configs/recommended.js';
7
8
  const plugin = {
@@ -14,6 +15,7 @@ const plugin = {
14
15
  'no-container-in-section': noContainerInSection,
15
16
  'prefer-component-props': preferComponentProps,
16
17
  'prefer-typography-components': preferTypographyComponents,
18
+ 'prefer-layout-components': preferLayoutComponents,
17
19
  },
18
20
  configs: {
19
21
  speedwell: speedwellConfig,
@@ -21,4 +23,4 @@ const plugin = {
21
23
  },
22
24
  };
23
25
  export default plugin;
24
- //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaW5kZXguanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi9zcmMvZXNsaW50L2luZGV4LnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBLE9BQU8sY0FBYyxNQUFNLDZCQUE2QixDQUFBO0FBQ3hELE9BQU8sb0JBQW9CLE1BQU0sb0NBQW9DLENBQUE7QUFDckUsT0FBTyxvQkFBb0IsTUFBTSxtQ0FBbUMsQ0FBQTtBQUNwRSxPQUFPLDBCQUEwQixNQUFNLHlDQUF5QyxDQUFBO0FBQ2hGLE9BQU8sZUFBZSxNQUFNLHdCQUF3QixDQUFBO0FBQ3BELE9BQU8saUJBQWlCLE1BQU0sMEJBQTBCLENBQUE7QUFFeEQsTUFBTSxNQUFNLEdBQUc7SUFDYixJQUFJLEVBQUU7UUFDSixJQUFJLEVBQUUsc0JBQXNCO1FBQzVCLE9BQU8sRUFBRSxPQUFPO0tBQ2pCO0lBQ0QsS0FBSyxFQUFFO1FBQ0wsa0JBQWtCLEVBQUUsY0FBYztRQUNsQyx5QkFBeUIsRUFBRSxvQkFBb0I7UUFDL0Msd0JBQXdCLEVBQUUsb0JBQW9CO1FBQzlDLDhCQUE4QixFQUFFLDBCQUEwQjtLQUMzRDtJQUNELE9BQU8sRUFBRTtRQUNQLFNBQVMsRUFBRSxlQUFlO1FBQzFCLFdBQVcsRUFBRSxpQkFBaUI7S0FDL0I7Q0FDRixDQUFBO0FBRUQsZUFBZSxNQUFNLENBQUEiLCJzb3VyY2VzQ29udGVudCI6WyJpbXBvcnQgbm9DbGllbnRCbG9ja3MgZnJvbSAnLi9ydWxlcy9uby1jbGllbnQtYmxvY2tzLmpzJ1xuaW1wb3J0IG5vQ29udGFpbmVySW5TZWN0aW9uIGZyb20gJy4vcnVsZXMvbm8tY29udGFpbmVyLWluLXNlY3Rpb24uanMnXG5pbXBvcnQgcHJlZmVyQ29tcG9uZW50UHJvcHMgZnJvbSAnLi9ydWxlcy9wcmVmZXItY29tcG9uZW50LXByb3BzLmpzJ1xuaW1wb3J0IHByZWZlclR5cG9ncmFwaHlDb21wb25lbnRzIGZyb20gJy4vcnVsZXMvcHJlZmVyLXR5cG9ncmFwaHktY29tcG9uZW50cy5qcydcbmltcG9ydCBzcGVlZHdlbGxDb25maWcgZnJvbSAnLi9jb25maWdzL3NwZWVkd2VsbC5qcydcbmltcG9ydCByZWNvbW1lbmRlZENvbmZpZyBmcm9tICcuL2NvbmZpZ3MvcmVjb21tZW5kZWQuanMnXG5cbmNvbnN0IHBsdWdpbiA9IHtcbiAgbWV0YToge1xuICAgIG5hbWU6ICdlc2xpbnQtcGx1Z2luLWdhbGxvcCcsXG4gICAgdmVyc2lvbjogJzEuMC4xJyxcbiAgfSxcbiAgcnVsZXM6IHtcbiAgICAnbm8tY2xpZW50LWJsb2Nrcyc6IG5vQ2xpZW50QmxvY2tzLFxuICAgICduby1jb250YWluZXItaW4tc2VjdGlvbic6IG5vQ29udGFpbmVySW5TZWN0aW9uLFxuICAgICdwcmVmZXItY29tcG9uZW50LXByb3BzJzogcHJlZmVyQ29tcG9uZW50UHJvcHMsXG4gICAgJ3ByZWZlci10eXBvZ3JhcGh5LWNvbXBvbmVudHMnOiBwcmVmZXJUeXBvZ3JhcGh5Q29tcG9uZW50cyxcbiAgfSxcbiAgY29uZmlnczoge1xuICAgIHNwZWVkd2VsbDogc3BlZWR3ZWxsQ29uZmlnLFxuICAgIHJlY29tbWVuZGVkOiByZWNvbW1lbmRlZENvbmZpZyxcbiAgfSxcbn1cblxuZXhwb3J0IGRlZmF1bHQgcGx1Z2luXG4iXX0=
26
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaW5kZXguanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi9zcmMvZXNsaW50L2luZGV4LnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBLE9BQU8sY0FBYyxNQUFNLDZCQUE2QixDQUFBO0FBQ3hELE9BQU8sb0JBQW9CLE1BQU0sb0NBQW9DLENBQUE7QUFDckUsT0FBTyxvQkFBb0IsTUFBTSxtQ0FBbUMsQ0FBQTtBQUNwRSxPQUFPLDBCQUEwQixNQUFNLHlDQUF5QyxDQUFBO0FBQ2hGLE9BQU8sc0JBQXNCLE1BQU0scUNBQXFDLENBQUE7QUFDeEUsT0FBTyxlQUFlLE1BQU0sd0JBQXdCLENBQUE7QUFDcEQsT0FBTyxpQkFBaUIsTUFBTSwwQkFBMEIsQ0FBQTtBQUV4RCxNQUFNLE1BQU0sR0FBRztJQUNiLElBQUksRUFBRTtRQUNKLElBQUksRUFBRSxzQkFBc0I7UUFDNUIsT0FBTyxFQUFFLE9BQU87S0FDakI7SUFDRCxLQUFLLEVBQUU7UUFDTCxrQkFBa0IsRUFBRSxjQUFjO1FBQ2xDLHlCQUF5QixFQUFFLG9CQUFvQjtRQUMvQyx3QkFBd0IsRUFBRSxvQkFBb0I7UUFDOUMsOEJBQThCLEVBQUUsMEJBQTBCO1FBQzFELDBCQUEwQixFQUFFLHNCQUFzQjtLQUNuRDtJQUNELE9BQU8sRUFBRTtRQUNQLFNBQVMsRUFBRSxlQUFlO1FBQzFCLFdBQVcsRUFBRSxpQkFBaUI7S0FDL0I7Q0FDRixDQUFBO0FBRUQsZUFBZSxNQUFNLENBQUEiLCJzb3VyY2VzQ29udGVudCI6WyJpbXBvcnQgbm9DbGllbnRCbG9ja3MgZnJvbSAnLi9ydWxlcy9uby1jbGllbnQtYmxvY2tzLmpzJ1xuaW1wb3J0IG5vQ29udGFpbmVySW5TZWN0aW9uIGZyb20gJy4vcnVsZXMvbm8tY29udGFpbmVyLWluLXNlY3Rpb24uanMnXG5pbXBvcnQgcHJlZmVyQ29tcG9uZW50UHJvcHMgZnJvbSAnLi9ydWxlcy9wcmVmZXItY29tcG9uZW50LXByb3BzLmpzJ1xuaW1wb3J0IHByZWZlclR5cG9ncmFwaHlDb21wb25lbnRzIGZyb20gJy4vcnVsZXMvcHJlZmVyLXR5cG9ncmFwaHktY29tcG9uZW50cy5qcydcbmltcG9ydCBwcmVmZXJMYXlvdXRDb21wb25lbnRzIGZyb20gJy4vcnVsZXMvcHJlZmVyLWxheW91dC1jb21wb25lbnRzLmpzJ1xuaW1wb3J0IHNwZWVkd2VsbENvbmZpZyBmcm9tICcuL2NvbmZpZ3Mvc3BlZWR3ZWxsLmpzJ1xuaW1wb3J0IHJlY29tbWVuZGVkQ29uZmlnIGZyb20gJy4vY29uZmlncy9yZWNvbW1lbmRlZC5qcydcblxuY29uc3QgcGx1Z2luID0ge1xuICBtZXRhOiB7XG4gICAgbmFtZTogJ2VzbGludC1wbHVnaW4tZ2FsbG9wJyxcbiAgICB2ZXJzaW9uOiAnMS4wLjEnLFxuICB9LFxuICBydWxlczoge1xuICAgICduby1jbGllbnQtYmxvY2tzJzogbm9DbGllbnRCbG9ja3MsXG4gICAgJ25vLWNvbnRhaW5lci1pbi1zZWN0aW9uJzogbm9Db250YWluZXJJblNlY3Rpb24sXG4gICAgJ3ByZWZlci1jb21wb25lbnQtcHJvcHMnOiBwcmVmZXJDb21wb25lbnRQcm9wcyxcbiAgICAncHJlZmVyLXR5cG9ncmFwaHktY29tcG9uZW50cyc6IHByZWZlclR5cG9ncmFwaHlDb21wb25lbnRzLFxuICAgICdwcmVmZXItbGF5b3V0LWNvbXBvbmVudHMnOiBwcmVmZXJMYXlvdXRDb21wb25lbnRzLFxuICB9LFxuICBjb25maWdzOiB7XG4gICAgc3BlZWR3ZWxsOiBzcGVlZHdlbGxDb25maWcsXG4gICAgcmVjb21tZW5kZWQ6IHJlY29tbWVuZGVkQ29uZmlnLFxuICB9LFxufVxuXG5leHBvcnQgZGVmYXVsdCBwbHVnaW5cbiJdfQ==
@@ -0,0 +1,3 @@
1
+ import type { Rule } from 'eslint';
2
+ declare const rule: Rule.RuleModule;
3
+ export default rule;
@@ -0,0 +1,113 @@
1
+ import { getCanonUrl, getCanonPattern } from '../utils/canon.js';
2
+ const RULE_NAME = 'prefer-layout-components';
3
+ const pattern = getCanonPattern(RULE_NAME);
4
+ const rule = {
5
+ meta: {
6
+ type: 'suggestion',
7
+ docs: {
8
+ description: pattern?.summary || 'Use Grid/Columns, not raw div with grid',
9
+ recommended: true,
10
+ url: getCanonUrl(RULE_NAME),
11
+ },
12
+ messages: {
13
+ useLayoutComponent: `[Canon ${pattern?.id || '018'}] Use the Grid or Columns component instead of <div className="grid ...">. Import: import { Grid, Columns, Column } from "@/components"`,
14
+ },
15
+ schema: [],
16
+ },
17
+ create(context) {
18
+ const filename = context.filename || context.getFilename();
19
+ // Only apply to block files
20
+ if (!filename.includes('/blocks/')) {
21
+ return {};
22
+ }
23
+ return {
24
+ JSXOpeningElement(node) {
25
+ const elementName = node.name?.name;
26
+ // Only check div elements
27
+ if (elementName !== 'div') {
28
+ return;
29
+ }
30
+ // Check if className contains 'grid'
31
+ const classNameAttr = node.attributes?.find((attr) => attr.type === 'JSXAttribute' &&
32
+ attr.name?.name === 'className');
33
+ if (!classNameAttr) {
34
+ return;
35
+ }
36
+ // Handle string literal className
37
+ if (classNameAttr.value?.type === 'Literal') {
38
+ const classValue = classNameAttr.value.value;
39
+ if (typeof classValue === 'string' && hasGridClass(classValue)) {
40
+ context.report({
41
+ node,
42
+ messageId: 'useLayoutComponent',
43
+ });
44
+ }
45
+ return;
46
+ }
47
+ // Handle template literal className
48
+ if (classNameAttr.value?.type === 'JSXExpressionContainer') {
49
+ const expr = classNameAttr.value.expression;
50
+ // Direct template literal: className={`grid ...`}
51
+ if (expr.type === 'TemplateLiteral') {
52
+ const quasis = expr.quasis || [];
53
+ for (const quasi of quasis) {
54
+ if (quasi.value?.raw && hasGridClass(quasi.value.raw)) {
55
+ context.report({
56
+ node,
57
+ messageId: 'useLayoutComponent',
58
+ });
59
+ return;
60
+ }
61
+ }
62
+ }
63
+ // clsx call: className={clsx('grid', ...)}
64
+ if (expr.type === 'CallExpression') {
65
+ const args = expr.arguments || [];
66
+ for (const arg of args) {
67
+ if (arg.type === 'Literal' && typeof arg.value === 'string') {
68
+ if (hasGridClass(arg.value)) {
69
+ context.report({
70
+ node,
71
+ messageId: 'useLayoutComponent',
72
+ });
73
+ return;
74
+ }
75
+ }
76
+ // Check template literals in clsx args
77
+ if (arg.type === 'TemplateLiteral') {
78
+ const quasis = arg.quasis || [];
79
+ for (const quasi of quasis) {
80
+ if (quasi.value?.raw && hasGridClass(quasi.value.raw)) {
81
+ context.report({
82
+ node,
83
+ messageId: 'useLayoutComponent',
84
+ });
85
+ return;
86
+ }
87
+ }
88
+ }
89
+ }
90
+ }
91
+ }
92
+ },
93
+ };
94
+ },
95
+ };
96
+ /**
97
+ * Check if a className string contains grid classes
98
+ * Matches: 'grid', 'grid-cols-', etc.
99
+ * Does NOT match: 'grid-area', component names with 'grid' in them
100
+ */
101
+ function hasGridClass(classString) {
102
+ // Split by whitespace and check each class
103
+ const classes = classString.split(/\s+/);
104
+ for (const cls of classes) {
105
+ // Match standalone 'grid' or 'grid-cols-*' patterns
106
+ if (cls === 'grid' || cls.startsWith('grid-cols-')) {
107
+ return true;
108
+ }
109
+ }
110
+ return false;
111
+ }
112
+ export default rule;
113
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoicHJlZmVyLWxheW91dC1jb21wb25lbnRzLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vLi4vc3JjL2VzbGludC9ydWxlcy9wcmVmZXItbGF5b3V0LWNvbXBvbmVudHMudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQ0EsT0FBTyxFQUFFLFdBQVcsRUFBRSxlQUFlLEVBQUUsTUFBTSxtQkFBbUIsQ0FBQTtBQUVoRSxNQUFNLFNBQVMsR0FBRywwQkFBMEIsQ0FBQTtBQUM1QyxNQUFNLE9BQU8sR0FBRyxlQUFlLENBQUMsU0FBUyxDQUFDLENBQUE7QUFFMUMsTUFBTSxJQUFJLEdBQW9CO0lBQzVCLElBQUksRUFBRTtRQUNKLElBQUksRUFBRSxZQUFZO1FBQ2xCLElBQUksRUFBRTtZQUNKLFdBQVcsRUFBRSxPQUFPLEVBQUUsT0FBTyxJQUFJLHlDQUF5QztZQUMxRSxXQUFXLEVBQUUsSUFBSTtZQUNqQixHQUFHLEVBQUUsV0FBVyxDQUFDLFNBQVMsQ0FBQztTQUM1QjtRQUNELFFBQVEsRUFBRTtZQUNSLGtCQUFrQixFQUFFLFVBQVUsT0FBTyxFQUFFLEVBQUUsSUFBSSxLQUFLLHlJQUF5STtTQUM1TDtRQUNELE1BQU0sRUFBRSxFQUFFO0tBQ1g7SUFFRCxNQUFNLENBQUMsT0FBTztRQUNaLE1BQU0sUUFBUSxHQUFHLE9BQU8sQ0FBQyxRQUFRLElBQUksT0FBTyxDQUFDLFdBQVcsRUFBRSxDQUFBO1FBRTFELDRCQUE0QjtRQUM1QixJQUFJLENBQUMsUUFBUSxDQUFDLFFBQVEsQ0FBQyxVQUFVLENBQUMsRUFBRSxDQUFDO1lBQ25DLE9BQU8sRUFBRSxDQUFBO1FBQ1gsQ0FBQztRQUVELE9BQU87WUFDTCxpQkFBaUIsQ0FBQyxJQUFTO2dCQUN6QixNQUFNLFdBQVcsR0FBRyxJQUFJLENBQUMsSUFBSSxFQUFFLElBQUksQ0FBQTtnQkFFbkMsMEJBQTBCO2dCQUMxQixJQUFJLFdBQVcsS0FBSyxLQUFLLEVBQUUsQ0FBQztvQkFDMUIsT0FBTTtn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QkFDSCxDQUFDO29CQUNILENBQUM7Z0JBQ0gsQ0FBQztZQUNILENBQUM7U0FDRixDQUFBO0lBQ0gsQ0FBQztDQUNGLENBQUE7QUFFRDs7OztHQUlHO0FBQ0gsU0FBUyxZQUFZLENBQUMsV0FBbUI7SUFDdkMsMkNBQTJDO0lBQzNDLE1BQU0sT0FBTyxHQUFHLFdBQVcsQ0FBQyxLQUFLLENBQUMsS0FBSyxDQUFDLENBQUE7SUFFeEMsS0FBSyxNQUFNLEdBQUcsSUFBSSxPQUFPLEVBQUUsQ0FBQztRQUMxQixvREFBb0Q7UUFDcEQsSUFBSSxHQUFHLEtBQUssTUFBTSxJQUFJLEdBQUcsQ0FBQyxVQUFVLENBQUMsWUFBWSxDQUFDLEVBQUUsQ0FBQztZQUNuRCxPQUFPLElBQUksQ0FBQTtRQUNiLENBQUM7SUFDSCxDQUFDO0lBRUQsT0FBTyxLQUFLLENBQUE7QUFDZCxDQUFDO0FBRUQsZUFBZSxJQUFJLENBQUEiLCJzb3VyY2VzQ29udGVudCI6WyJpbXBvcnQgdHlwZSB7IFJ1bGUgfSBmcm9tICdlc2xpbnQnXG5pbXBvcnQgeyBnZXRDYW5vblVybCwgZ2V0Q2Fub25QYXR0ZXJuIH0gZnJvbSAnLi4vdXRpbHMvY2Fub24uanMnXG5cbmNvbnN0IFJVTEVfTkFNRSA9ICdwcmVmZXItbGF5b3V0LWNvbXBvbmVudHMnXG5jb25zdCBwYXR0ZXJuID0gZ2V0Q2Fub25QYXR0ZXJuKFJVTEVfTkFNRSlcblxuY29uc3QgcnVsZTogUnVsZS5SdWxlTW9kdWxlID0ge1xuICBtZXRhOiB7XG4gICAgdHlwZTogJ3N1Z2dlc3Rpb24nLFxuICAgIGRvY3M6IHtcbiAgICAgIGRlc2NyaXB0aW9uOiBwYXR0ZXJuPy5zdW1tYXJ5IHx8ICdVc2UgR3JpZC9Db2x1bW5zLCBub3QgcmF3IGRpdiB3aXRoIGdyaWQnLFxuICAgICAgcmVjb21tZW5kZWQ6IHRydWUsXG4gICAgICB1cmw6IGdldENhbm9uVXJsKFJVTEVfTkFNRSksXG4gICAgfSxcbiAgICBtZXNzYWdlczoge1xuICAgICAgdXNlTGF5b3V0Q29tcG9uZW50OiBgW0Nhbm9uICR7cGF0dGVybj8uaWQgfHwgJzAxOCd9XSBVc2UgdGhlIEdyaWQgb3IgQ29sdW1ucyBjb21wb25lbnQgaW5zdGVhZCBvZiA8ZGl2IGNsYXNzTmFtZT1cImdyaWQgLi4uXCI+LiBJbXBvcnQ6IGltcG9ydCB7IEdyaWQsIENvbHVtbnMsIENvbHVtbiB9IGZyb20gXCJAL2NvbXBvbmVudHNcImAsXG4gICAgfSxcbiAgICBzY2hlbWE6IFtdLFxuICB9LFxuXG4gIGNyZWF0ZShjb250ZXh0KSB7XG4gICAgY29uc3QgZmlsZW5hbWUgPSBjb250ZXh0LmZpbGVuYW1lIHx8IGNvbnRleHQuZ2V0RmlsZW5hbWUoKVxuXG4gICAgLy8gT25seSBhcHBseSB0byBibG9jayBmaWxlc1xuICAgIGlmICghZmlsZW5hbWUuaW5jbHVkZXMoJy9ibG9ja3MvJykpIHtcbiAgICAgIHJldHVybiB7fVxuICAgIH1cblxuICAgIHJldHVybiB7XG4gICAgICBKU1hPcGVuaW5nRWxlbWVudChub2RlOiBhbnkpIHtcbiAgICAgICAgY29uc3QgZWxlbWVudE5hbWUgPSBub2RlLm5hbWU/Lm5hbWVcblxuICAgICAgICAvLyBPbmx5IGNoZWNrIGRpdiBlbGVtZW50c1xuICAgICAgICBpZiAoZWxlbWVudE5hbWUgIT09ICdkaXYnKSB7XG4gICAgICAgICAgcmV0dXJuXG4gICAgICAgIH1cblxuICAgICAgICAvLyBDaGVjayBpZiBjbGFzc05hbWUgY29udGFpbnMgJ2dyaWQnXG4gICAgICAgIGNvbnN0IGNsYXNzTmFtZUF0dHIgPSBub2RlLmF0dHJpYnV0ZXM/LmZpbmQoXG4gICAgICAgICAgKGF0dHI6IGFueSkgPT5cbiAgICAgICAgICAgIGF0dHIudHlwZSA9PT0gJ0pTWEF0dHJpYnV0ZScgJiZcbiAgICAgICAgICAgIGF0dHIubmFtZT8ubmFtZSA9PT0gJ2NsYXNzTmFtZSdcbiAgICAgICAgKVxuXG4gICAgICAgIGlmICghY2xhc3NOYW1lQXR0cikge1xuICAgICAgICAgIHJldHVyblxuICAgICAgICB9XG5cbiAgICAgICAgLy8gSGFuZGxlIHN0cmluZyBsaXRlcmFsIGNsYXNzTmFtZVxuICAgICAgICBpZiAoY2xhc3NOYW1lQXR0ci52YWx1ZT8udHlwZSA9PT0gJ0xpdGVyYWwnKSB7XG4gICAgICAgICAgY29uc3QgY2xhc3NWYWx1ZSA9IGNsYXNzTmFtZUF0dHIudmFsdWUudmFsdWVcbiAgICAgICAgICBpZiAodHlwZW9mIGNsYXNzVmFsdWUgPT09ICdzdHJpbmcnICYmIGhhc0dyaWRDbGFzcyhjbGFzc1ZhbHVlKSkge1xuICAgICAgICAgICAgY29udGV4dC5yZXBvcnQoe1xuICAgICAgICAgICAgICBub2RlLFxuICAgICAgICAgICAgICBtZXNzYWdlSWQ6ICd1c2VMYXlvdXRDb21wb25lbnQnLFxuICAgICAgICAgICAgfSlcbiAgICAgICAgICB9XG4gICAgICAgICAgcmV0dXJuXG4gICAgICAgIH1cblxuICAgICAgICAvLyBIYW5kbGUgdGVtcGxhdGUgbGl0ZXJhbCBjbGFzc05hbWVcbiAgICAgICAgaWYgKGNsYXNzTmFtZUF0dHIudmFsdWU/LnR5cGUgPT09ICdKU1hFeHByZXNzaW9uQ29udGFpbmVyJykge1xuICAgICAgICAgIGNvbnN0IGV4cHIgPSBjbGFzc05hbWVBdHRyLnZhbHVlLmV4cHJlc3Npb25cblxuICAgICAgICAgIC8vIERpcmVjdCB0ZW1wbGF0ZSBsaXRlcmFsOiBjbGFzc05hbWU9e2BncmlkIC4uLmB9XG4gICAgICAgICAgaWYgKGV4cHIudHlwZSA9PT0gJ1RlbXBsYXRlTGl0ZXJhbCcpIHtcbiAgICAgICAgICAgIGNvbnN0IHF1YXNpcyA9IGV4cHIucXVhc2lzIHx8IFtdXG4gICAgICAgICAgICBmb3IgKGNvbnN0IHF1YXNpIG9mIHF1YXNpcykge1xuICAgICAgICAgICAgICBpZiAocXVhc2kudmFsdWU/LnJhdyAmJiBoYXNHcmlkQ2xhc3MocXVhc2kudmFsdWUucmF3KSkge1xuICAgICAgICAgICAgICAgIGNvbnRleHQucmVwb3J0KHtcbiAgICAgICAgICAgICAgICAgIG5vZGUsXG4gICAgICAgICAgICAgICAgICBtZXNzYWdlSWQ6ICd1c2VMYXlvdXRDb21wb25lbnQnLFxuICAgICAgICAgICAgICAgIH0pXG4gICAgICAgICAgICAgICAgcmV0dXJuXG4gICAgICAgICAgICAgIH1cbiAgICAgICAgICAgIH1cbiAgICAgICAgICB9XG5cbiAgICAgICAgICAvLyBjbHN4IGNhbGw6IGNsYXNzTmFtZT17Y2xzeCgnZ3JpZCcsIC4uLil9XG4gICAgICAgICAgaWYgKGV4cHIudHlwZSA9PT0gJ0NhbGxFeHByZXNzaW9uJykge1xuICAgICAgICAgICAgY29uc3QgYXJncyA9IGV4cHIuYXJndW1lbnRzIHx8IFtdXG4gICAgICAgICAgICBmb3IgKGNvbnN0IGFyZyBvZiBhcmdzKSB7XG4gICAgICAgICAgICAgIGlmIChhcmcudHlwZSA9PT0gJ0xpdGVyYWwnICYmIHR5cGVvZiBhcmcudmFsdWUgPT09ICdzdHJpbmcnKSB7XG4gICAgICAgICAgICAgICAgaWYgKGhhc0dyaWRDbGFzcyhhcmcudmFsdWUpKSB7XG4gICAgICAgICAgICAgICAgICBjb250ZXh0LnJlcG9ydCh7XG4gICAgICAgICAgICAgICAgICAgIG5vZGUsXG4gICAgICAgICAgICAgICAgICAgIG1lc3NhZ2VJZDogJ3VzZUxheW91dENvbXBvbmVudCcsXG4gICAgICAgICAgICAgICAgICB9KVxuICAgICAgICAgICAgICAgICAgcmV0dXJuXG4gICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgIC8vIENoZWNrIHRlbXBsYXRlIGxpdGVyYWxzIGluIGNsc3ggYXJnc1xuICAgICAgICAgICAgICBpZiAoYXJnLnR5cGUgPT09ICdUZW1wbGF0ZUxpdGVyYWwnKSB7XG4gICAgICAgICAgICAgICAgY29uc3QgcXVhc2lzID0gYXJnLnF1YXNpcyB8fCBbXVxuICAgICAgICAgICAgICAgIGZvciAoY29uc3QgcXVhc2kgb2YgcXVhc2lzKSB7XG4gICAgICAgICAgICAgICAgICBpZiAocXVhc2kudmFsdWU/LnJhdyAmJiBoYXNHcmlkQ2xhc3MocXVhc2kudmFsdWUucmF3KSkge1xuICAgICAgICAgICAgICAgICAgICBjb250ZXh0LnJlcG9ydCh7XG4gICAgICAgICAgICAgICAgICAgICAgbm9kZSxcbiAgICAgICAgICAgICAgICAgICAgICBtZXNzYWdlSWQ6ICd1c2VMYXlvdXRDb21wb25lbnQnLFxuICAgICAgICAgICAgICAgICAgICB9KVxuICAgICAgICAgICAgICAgICAgICByZXR1cm5cbiAgICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgIH1cbiAgICAgICAgICAgIH1cbiAgICAgICAgICB9XG4gICAgICAgIH1cbiAgICAgIH0sXG4gICAgfVxuICB9LFxufVxuXG4vKipcbiAqIENoZWNrIGlmIGEgY2xhc3NOYW1lIHN0cmluZyBjb250YWlucyBncmlkIGNsYXNzZXNcbiAqIE1hdGNoZXM6ICdncmlkJywgJ2dyaWQtY29scy0nLCBldGMuXG4gKiBEb2VzIE5PVCBtYXRjaDogJ2dyaWQtYXJlYScsIGNvbXBvbmVudCBuYW1lcyB3aXRoICdncmlkJyBpbiB0aGVtXG4gKi9cbmZ1bmN0aW9uIGhhc0dyaWRDbGFzcyhjbGFzc1N0cmluZzogc3RyaW5nKTogYm9vbGVhbiB7XG4gIC8vIFNwbGl0IGJ5IHdoaXRlc3BhY2UgYW5kIGNoZWNrIGVhY2ggY2xhc3NcbiAgY29uc3QgY2xhc3NlcyA9IGNsYXNzU3RyaW5nLnNwbGl0KC9cXHMrLylcbiAgXG4gIGZvciAoY29uc3QgY2xzIG9mIGNsYXNzZXMpIHtcbiAgICAvLyBNYXRjaCBzdGFuZGFsb25lICdncmlkJyBvciAnZ3JpZC1jb2xzLSonIHBhdHRlcm5zXG4gICAgaWYgKGNscyA9PT0gJ2dyaWQnIHx8IGNscy5zdGFydHNXaXRoKCdncmlkLWNvbHMtJykpIHtcbiAgICAgIHJldHVybiB0cnVlXG4gICAgfVxuICB9XG4gIFxuICByZXR1cm4gZmFsc2Vcbn1cblxuZXhwb3J0IGRlZmF1bHQgcnVsZVxuIl19
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@gallop.software/canon",
3
- "version": "2.0.1",
3
+ "version": "2.1.0",
4
4
  "type": "module",
5
5
  "description": "Gallop Canon - Architecture patterns, ESLint plugin, and CLI for template governance",
6
6
  "main": "dist/index.js",
@@ -49,7 +49,7 @@
49
49
  "license": "MIT",
50
50
  "repository": {
51
51
  "type": "git",
52
- "url": "https://github.com/gallop-software/gallop",
52
+ "url": "https://github.com/gallop-software/canon",
53
53
  "directory": "canon"
54
54
  },
55
55
  "peerDependencies": {
@@ -0,0 +1,85 @@
1
+ # 018: Layout Components
2
+
3
+ **Category:** Layout
4
+ **Status:** Stable
5
+ **Enforcement:** ESLint (`gallop/prefer-layout-components`)
6
+
7
+ ## Summary
8
+
9
+ Use the `Grid` or `Columns` component instead of raw `<div>` elements with grid classes.
10
+
11
+ ## Rationale
12
+
13
+ Raw `<div>` elements with grid classes bypass the design system's layout abstractions. The `Grid` and `Columns` components provide:
14
+
15
+ - **Consistent defaults** for gaps, columns, and alignment
16
+ - **Semantic intent** - code is more readable when intent is clear
17
+ - **Centralized updates** - layout defaults can be updated in one place
18
+ - **Reduced errors** - no need to remember all required grid classes
19
+
20
+ ## Bad
21
+
22
+ ```tsx
23
+ <div className="grid grid-cols-3 gap-6">
24
+ <Card>...</Card>
25
+ <Card>...</Card>
26
+ <Card>...</Card>
27
+ </div>
28
+
29
+ <div className="grid grid-cols-1 lg:grid-cols-2 gap-8 items-center">
30
+ <div>Left content</div>
31
+ <div>Right content</div>
32
+ </div>
33
+ ```
34
+
35
+ ## Good
36
+
37
+ ```tsx
38
+ import { Grid, Columns, Column } from '@/components'
39
+
40
+ <Grid cols="grid-cols-3" gap="gap-6">
41
+ <Card>...</Card>
42
+ <Card>...</Card>
43
+ <Card>...</Card>
44
+ </Grid>
45
+
46
+ <Columns>
47
+ <Column>Left content</Column>
48
+ <Column>Right content</Column>
49
+ </Columns>
50
+ ```
51
+
52
+ ## Component Reference
53
+
54
+ ### Grid
55
+
56
+ For multi-item grid layouts (3+ columns, card grids, galleries):
57
+
58
+ ```tsx
59
+ <Grid
60
+ cols="grid-cols-1 lg:grid-cols-3" // optional, defaults to 1 → 3
61
+ gap="gap-6" // optional, has sensible default
62
+ >
63
+ {children}
64
+ </Grid>
65
+ ```
66
+
67
+ ### Columns
68
+
69
+ For two-column layouts with optional reversal:
70
+
71
+ ```tsx
72
+ <Columns
73
+ cols="grid-cols-1 lg:grid-cols-2" // optional
74
+ gap="gap-8" // optional
75
+ align="items-center" // optional
76
+ reverseColumns={true} // swap order on lg+
77
+ >
78
+ <Column>Left</Column>
79
+ <Column>Right</Column>
80
+ </Columns>
81
+ ```
82
+
83
+ ## Exceptions
84
+
85
+ The rule only applies to files in `/blocks/`. Component files may use raw divs with grid when building the layout primitives themselves.
package/schema.json CHANGED
@@ -210,6 +210,16 @@
210
210
  "enforcement": "documentation",
211
211
  "rule": null,
212
212
  "summary": "PageMetadata structure, structured data"
213
+ },
214
+ {
215
+ "id": "018",
216
+ "title": "Layout Components",
217
+ "file": "patterns/018-layout-components.md",
218
+ "category": "layout",
219
+ "status": "stable",
220
+ "enforcement": "eslint",
221
+ "rule": "gallop/prefer-layout-components",
222
+ "summary": "Use Grid/Columns, not raw div with grid"
213
223
  }
214
224
  ],
215
225
  "guarantees": [
@@ -239,7 +249,7 @@
239
249
  "name": "Design System Compliance",
240
250
  "since": "1.0.0",
241
251
  "status": "stable",
242
- "patterns": ["003", "004", "009", "010", "011"]
252
+ "patterns": ["003", "004", "009", "010", "011", "018"]
243
253
  }
244
254
  ]
245
255
  }