File: one-dependency-per-line.js

package info (click to toggle)
node-eslint-plugin-requirejs 4.0.0-7
  • links: PTS, VCS
  • area: main
  • in suites: bullseye, sid
  • size: 916 kB
  • sloc: javascript: 4,677; perl: 48; makefile: 31; sh: 6
file content (154 lines) | stat: -rw-r--r-- 4,614 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
/**
 * @file    Rule to enforce or disallow one dependency per line.
 * @author  Casey Visco <cvisco@gmail.com>
 */

"use strict";

const rjs = require("../utils/rjs");
const ast = require("../utils/ast");

const isAmdCall = rjs.isAmdCall;
const withoutModuleId = rjs.withoutModuleId;
const isArrayExpr = ast.isArrayExpr;
const isFunctionExpr = ast.isFunctionExpr;

// -----------------------------------------------------------------------------
// Configuration
// -----------------------------------------------------------------------------

const docs = {
    description: "Require or disallow one dependency per line",
    category: "Stylistic Choices",
    recommended: false,
    url: "https://github.com/cvisco/eslint-plugin-requirejs/blob/master/docs/rules/one-dependency-per-line.md"
};

const schema = [
    {
        type: "object",
        properties: {
            paths: {
                oneOf: [
                    {
                        enum: ["always", "never"]
                    },
                    {
                        type: "number",
                        minimum: 0
                    }
                ]
            },
            names: {
                oneOf: [
                    {
                        enum: ["always", "never"]
                    },
                    {
                        type: "number",
                        minimum: 0
                    }
                ]
            }
        },
        additionalProperties: false
    }
];

const defaults = {
    paths: "always",
    names: "always"
};

const messages = {
    always: {
        paths: "Only one dependency path is permitted per line.",
        names: "Only one dependency name is permitted per line."
    },
    never: {
        paths: "Dependency paths must appear on one line.",
        names: "Dependency names must appear on one line."
    }
};

// -----------------------------------------------------------------------------
// Helpers
// -----------------------------------------------------------------------------

const pad    = amount => val => " ".repeat(amount) + val;
const line   = node => node.loc.start.line;
const column = node => node.loc.start.column;
const unique = list => Array.from(new Set(list));

const hasDuplicates = list => unique(list).length < list.length;
const hasMultiple   = list => unique(list).length > 1;
const isLongerThan  = (val, list) => Number.isInteger(val) && list.length > val;
const isAlways      = val => val === "always";
const isNever       = val => val === "never";

const indentation = node => {
    const statement = node.type === "ArrowFunctionExpression" ? node.body : node.body.body[0];

    return statement && line(node) !== line(statement) ? column(statement) : 0;
};

const formatPaths = indent => node => {
    const paths = node.elements.map(v => v.raw).map(pad(indent)).join(",\n");

    return `[\n${paths}\n]`;
};

const formatNames = (indent, context) => node => {
    const names = node.params.map(v => v.name).map(pad(indent)).join(",\n");
    const body = context.getSourceCode().getText(node.body);

    return `function (\n${names}\n) ${body}`;
};

// -----------------------------------------------------------------------------
// Rule Definition
// -----------------------------------------------------------------------------

function create(context) {
    const settings = Object.assign({}, defaults, context.options[0]);

    function check(setting, node, list, format) {
        const val = settings[setting];
        const lines = list.map(line);

        if ((isLongerThan(val, lines) || isAlways(val)) && hasDuplicates(lines)) {
            const message = messages.always[setting];
            const fix = f => f.replaceTextRange(node.range, format(node));

            context.report({ node, message, fix });
        }

        if (isNever(val) && hasMultiple(lines)) {
            const message = messages.never[setting];

            context.report({ node, message });
        }
    }

    return {
        CallExpression(node) {
            if (!isAmdCall(node) || node.arguments.length < 2) return;

            const args = withoutModuleId(node.arguments);
            const deps = args[0];
            const func = args[1];

            if (!isArrayExpr(deps) || !isFunctionExpr(func)) return;

            const indent = indentation(func);

            check("paths", deps, deps.elements, formatPaths(indent));
            check("names", func, func.params, formatNames(indent, context));
        }
    };
}

module.exports = {
    meta: { docs, schema, fixable: "code" },
    create
};