# core/stylelint/shrinkable-grid-track.stylelint.rule.ts

> 57 lines of code and 11 definitions.

Tree: GovLab Quality
Language: typescript
Canonical: https://banes-lab.com/anatomy/quality#file-quality-core-stylelint-shrinkable-grid-track-stylelint-rule-ts
Source text: https://banes-lab.com/source/quality/core/stylelint/shrinkable-grid-track.stylelint.rule.ts.txt

Listed in [core/stylelint](https://banes-lab.com/api/source/quality/core/stylelint.md), after [core/stylelint/responsive-units.stylelint.rule.ts](https://banes-lab.com/source/quality/core/stylelint/responsive-units.stylelint.rule.ts.md).

## Definitions

- `{ createPlugin, utils }` (lexical_declaration, line 7)
- `ruleName` (lexical_declaration, line 8)
- `ruleId` (lexical_declaration, line 9)
- `RULE_META` (lexical_declaration, line 11, exported)
- `TRACK_PROPS` (lexical_declaration, line 21)
- `FLEX_UNIT` (lexical_declaration, line 22)
- `SHRINKING_FUNCTION` (lexical_declaration, line 23)
- `messages` (lexical_declaration, line 25)
- `bareFractions` (lexical_declaration, line 33)
- `parsed` (lexical_declaration, line 41)
- `rule` (lexical_declaration, line 46)

## Contained in

- [core/stylelint](https://banes-lab.com/anatomy/quality/folder-quality-core-stylelint.md)

## Source

```typescript
import type { Declaration, Root } from "postcss";
import stylelint, { type PostcssResult, type Rule } from "stylelint";
import type { GovlabStylelintMeta } from "#types/stylelint.types";
import valueParser, { type Node } from "postcss-value-parser";
import { withRuleId } from "#core/formatters/stylelint.formatter";

const { createPlugin, utils } = stylelint;
const ruleName = "govlab/shrinkable-grid-track";
const ruleId = "shrinkable_grid_track";

export const RULE_META: GovlabStylelintMeta = {
    meta: {
        canonical: ["responsive-design"],
        description: "Require flexible grid column tracks to be able to shrink below their content",
        fixable: false,
    },
    ruleId,
    ruleName,
};

const TRACK_PROPS = new Set(["grid-template-columns", "grid-auto-columns"]);
const FLEX_UNIT = "fr";
const SHRINKING_FUNCTION = "minmax";

const messages = utils.ruleMessages(ruleName, {
    bareFraction: (track: string): string =>
        withRuleId(
            `The column track '${track}' is a bare fraction, which never shrinks below its content, so one long word or code line pushes the grid wider than a narrow screen. Write the track as minmax(0, ${track}) so it can shrink to the space it is given.`,
            ruleId,
        ),
});

const bareFractions = function bareFractions(nodes: readonly Node[], shrinkable: boolean): string[] {
    return nodes.flatMap((node) => {
        if (node.type === "function") {
            return bareFractions(node.nodes, shrinkable || node.value.toLowerCase() === SHRINKING_FUNCTION);
        }
        if (node.type !== "word" || shrinkable) {
            return [];
        }
        const parsed = valueParser.unit(node.value);
        return parsed !== false && parsed.unit.toLowerCase() === FLEX_UNIT ? [node.value] : [];
    });
};

const rule: Rule = Object.assign(
    (primary: unknown) =>
        (root: Root, result: PostcssResult): void => {
            if (primary !== true) {
                return;
            }
            root.walkDecls((decl: Declaration) => {
                if (!TRACK_PROPS.has(decl.prop.toLowerCase())) {
                    return;
                }
                for (const track of new Set(bareFractions(valueParser(decl.value).nodes, false))) {
                    utils.report({ message: messages.bareFraction(track), node: decl, result, ruleName, word: track });
                }
            });
        },
    { messages, ruleName },
);

export default createPlugin(ruleName, rule);
```
