177 lines
5.0 KiB
JavaScript
177 lines
5.0 KiB
JavaScript
'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();
|
|
}
|
|
}); |