| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129 | /** * @author Toru Nagashima * See LICENSE file in root directory for full license. */"use strict"const path = require("path")const fs = require("fs")const getTryExtensions = require("../util/get-try-extensions")const visitImport = require("../util/visit-import")const packageNamePattern = /^(?:@[^/\\]+[/\\])?[^/\\]+$/uconst corePackageOverridePattern = /^(?:assert|async_hooks|buffer|child_process|cluster|console|constants|crypto|dgram|dns|domain|events|fs|http|http2|https|inspector|module|net|os|path|perf_hooks|process|punycode|querystring|readline|repl|stream|string_decoder|sys|timers|tls|trace_events|tty|url|util|v8|vm|worker_threads|zlib)[/\\]$/u/** * Get all file extensions of the files which have the same basename. * @param {string} filePath The path to the original file to check. * @returns {string[]} File extensions. */function getExistingExtensions(filePath) {    const basename = path.basename(filePath, path.extname(filePath))    try {        return fs            .readdirSync(path.dirname(filePath))            .filter(                filename =>                    path.basename(filename, path.extname(filename)) === basename            )            .map(filename => path.extname(filename))    } catch (_error) {        return []    }}module.exports = {    meta: {        docs: {            description:                "enforce the style of file extensions in `import` declarations",            category: "Stylistic Issues",            recommended: false,            url:                "https://github.com/mysticatea/eslint-plugin-node/blob/v11.1.0/docs/rules/file-extension-in-import.md",        },        fixable: "code",        messages: {            requireExt: "require file extension '{{ext}}'.",            forbidExt: "forbid file extension '{{ext}}'.",        },        schema: [            {                enum: ["always", "never"],            },            {                type: "object",                properties: {                    tryExtensions: getTryExtensions.schema,                },                additionalProperties: {                    enum: ["always", "never"],                },            },        ],        type: "suggestion",    },    create(context) {        if (context.getFilename().startsWith("<")) {            return {}        }        const defaultStyle = context.options[0] || "always"        const overrideStyle = context.options[1] || {}        function verify({ filePath, name, node }) {            // Ignore if it's not resolved to a file or it's a bare module.            if (                !filePath ||                packageNamePattern.test(name) ||                corePackageOverridePattern.test(name)            ) {                return            }            // Get extension.            const originalExt = path.extname(name)            const resolvedExt = path.extname(filePath)            const existingExts = getExistingExtensions(filePath)            if (!resolvedExt && existingExts.length !== 1) {                // Ignore if the file extension could not be determined one.                return            }            const ext = resolvedExt || existingExts[0]            const style = overrideStyle[ext] || defaultStyle            // Verify.            if (style === "always" && ext !== originalExt) {                context.report({                    node,                    messageId: "requireExt",                    data: { ext },                    fix(fixer) {                        if (existingExts.length !== 1) {                            return null                        }                        const index = node.range[1] - 1                        return fixer.insertTextBeforeRange([index, index], ext)                    },                })            } else if (style === "never" && ext === originalExt) {                context.report({                    node,                    messageId: "forbidExt",                    data: { ext },                    fix(fixer) {                        if (existingExts.length !== 1) {                            return null                        }                        const index = name.lastIndexOf(ext)                        const start = node.range[0] + 1 + index                        const end = start + ext.length                        return fixer.removeRange([start, end])                    },                })            }        }        return visitImport(context, { optionIndex: 1 }, targets => {            targets.forEach(verify)        })    },}
 |