UNPKG

eslint-plugin-vue

Version:

Official ESLint plugin for Vue.js

177 lines (174 loc) 5.17 kB
'use strict'; const require_runtime = require('../_virtual/_rolldown/runtime.js'); const require_index = require('../utils/index.js'); //#region lib/rules/block-lang.js /** * @fileoverview Disallow use other than available `lang` * @author Yosuke Ota */ var require_block_lang = /* @__PURE__ */ require_runtime.__commonJSMin(((exports, module) => { const utils = require_index.default; /** * @typedef {object} BlockOptions * @property {Set<string>} lang * @property {boolean} allowNoLang */ /** * @typedef { { [element: string]: BlockOptions | undefined } } Options */ /** * @typedef {object} UserBlockOptions * @property {string[] | string} [lang] * @property {boolean} [allowNoLang] */ /** * @typedef { { [element: string]: UserBlockOptions | undefined } } UserOptions */ /** * https://vuejs.github.io/vetur/guide/highlighting.html * <template lang="html"></template> * <style lang="css"></style> * <script lang="js"><\/script> * <script lang="javascript"><\/script> * @type {Record<string, string[] | undefined>} */ const DEFAULT_LANGUAGES = { template: ["html"], style: ["css"], script: ["js", "javascript"] }; /** * @param {NonNullable<BlockOptions['lang']>} lang */ function getAllowsLangPhrase(lang) { const langs = [...lang].map((s) => `'${s}'`); switch (langs.length) { case 1: return langs[0]; default: return `${langs.slice(0, -1).join(", ")}, and ${langs.at(-1)}`; } } /** * Normalizes a given option. * @param {string} blockName The block name. * @param {UserBlockOptions} option An option to parse. * @returns {BlockOptions} Normalized option. */ function normalizeOption(blockName, option) { /** @type {Set<string>} */ let lang; if (Array.isArray(option.lang)) lang = new Set(option.lang); else if (typeof option.lang === "string") lang = new Set([option.lang]); else lang = /* @__PURE__ */ new Set(); let hasDefault = false; for (const def of DEFAULT_LANGUAGES[blockName] || []) if (lang.has(def)) { lang.delete(def); hasDefault = true; } if (lang.size === 0) return { lang, allowNoLang: true }; return { lang, allowNoLang: hasDefault || Boolean(option.allowNoLang) }; } /** * Normalizes a given options. * @param { UserOptions } options An option to parse. * @returns {Options} Normalized option. */ function normalizeOptions(options) { if (!options) return {}; /** @type {Options} */ const normalized = {}; for (const blockName of Object.keys(options)) { const value = options[blockName]; if (value) normalized[blockName] = normalizeOption(blockName, value); } return normalized; } module.exports = { meta: { type: "suggestion", docs: { description: "disallow use other than available `lang`", categories: void 0, url: "https://eslint.vuejs.org/rules/block-lang.html" }, schema: [{ type: "object", patternProperties: { "^(?:\\S+)$": { oneOf: [{ type: "object", properties: { lang: { oneOf: [{ type: "string" }, { type: "array", items: { type: "string" }, uniqueItems: true, additionalItems: false }] }, allowNoLang: { type: "boolean" } }, additionalProperties: false }] } }, minProperties: 1, additionalProperties: false }], messages: { expected: "Only {{allows}} can be used for the 'lang' attribute of '<{{tag}}>'.", missing: "The 'lang' attribute of '<{{tag}}>' is missing.", unexpected: "Do not specify the 'lang' attribute of '<{{tag}}>'.", useOrNot: "Only {{allows}} can be used for the 'lang' attribute of '<{{tag}}>'. Or, not specifying the 'lang' attribute is allowed.", unexpectedDefault: "Do not explicitly specify the default language for the 'lang' attribute of '<{{tag}}>'." } }, create(context) { const options = normalizeOptions(context.options[0] || { script: { allowNoLang: true }, template: { allowNoLang: true }, style: { allowNoLang: true } }); if (Object.keys(options).length === 0) return {}; /** * @param {VElement} element * @returns {void} */ function verify(element) { const tag = element.name; const option = options[tag]; if (!option) return; const lang = utils.getAttribute(element, "lang"); if (lang == null || lang.value == null) { if (!option.allowNoLang) context.report({ node: element.startTag, messageId: "missing", data: { tag } }); return; } if (!option.lang.has(lang.value.value)) { let messageId; if (!option.allowNoLang) messageId = "expected"; else if (option.lang.size === 0) messageId = (DEFAULT_LANGUAGES[tag] || []).includes(lang.value.value) ? "unexpectedDefault" : "unexpected"; else messageId = "useOrNot"; context.report({ node: lang, messageId, data: { tag, allows: getAllowsLangPhrase(option.lang) } }); } } return utils.defineDocumentVisitor(context, { "VDocumentFragment > VElement": verify }); } }; })); //#endregion Object.defineProperty(exports, 'default', { enumerable: true, get: function () { return require_block_lang(); } });