diff --git a/.github/workflows/check-for-resources-update.yml b/.github/workflows/check-for-resources-update.yml new file mode 100644 index 000000000..f95a8aa82 --- /dev/null +++ b/.github/workflows/check-for-resources-update.yml @@ -0,0 +1,30 @@ +name: Check for utils resources update +on: + workflow_dispatch: null + schedule: + - cron: 0 0 * * 0 # At 00:00 on Sunday, see https://crontab.guru/#0_0_*_*_0 + +permissions: + contents: write + pull-requests: write + +jobs: + check-for-resources-update: + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v3 + - name: Install Node.js + uses: actions/setup-node@v3 + with: + node-version: 18 + - name: Install Packages + run: npm install + - name: Update + run: npm run update-resources + - uses: peter-evans/create-pull-request@v7 + with: + commit-message: Updates resources with latest + branch: update-unicode-alias + branch-suffix: timestamp + title: Updates resources with latest diff --git a/lib/utils/deprecated-html-elements.json b/lib/utils/deprecated-html-elements.json index 1d9d67968..63a3f7162 100644 --- a/lib/utils/deprecated-html-elements.json +++ b/lib/utils/deprecated-html-elements.json @@ -6,14 +6,10 @@ "big", "blink", "center", - "command", - "content", "dir", - "element", "font", "frame", "frameset", - "image", "isindex", "keygen", "listing", @@ -24,8 +20,10 @@ "nobr", "noembed", "noframes", + "param", "plaintext", - "shadow", + "rb", + "rtc", "spacer", "strike", "tt", diff --git a/lib/utils/html-elements.json b/lib/utils/html-elements.json index 6911e3482..829b6f841 100644 --- a/lib/utils/html-elements.json +++ b/lib/utils/html-elements.json @@ -1,120 +1,116 @@ [ - "html", - "body", - "base", - "head", - "link", - "meta", - "style", - "title", + "a", + "abbr", "address", + "area", "article", "aside", + "audio", + "b", + "base", + "bdi", + "bdo", + "blockquote", + "body", + "br", + "button", + "canvas", + "caption", + "cite", + "code", + "col", + "colgroup", + "data", + "datalist", + "dd", + "del", + "details", + "dfn", + "dialog", + "div", + "dl", + "dt", + "em", + "embed", + "fencedframe", + "fieldset", + "figcaption", + "figure", "footer", - "header", + "form", "h1", "h2", "h3", "h4", "h5", "h6", + "head", + "header", "hgroup", - "nav", - "section", - "div", - "dd", - "dl", - "dt", - "figcaption", - "figure", "hr", + "html", + "i", + "iframe", "img", + "input", + "ins", + "kbd", + "label", + "legend", "li", + "link", "main", + "map", + "mark", + "menu", + "meta", + "meter", + "nav", + "noscript", + "object", "ol", + "optgroup", + "option", + "output", "p", + "picture", + "portal", "pre", - "ul", - "a", - "b", - "abbr", - "bdi", - "bdo", - "br", - "cite", - "code", - "data", - "dfn", - "em", - "i", - "kbd", - "mark", + "progress", "q", "rp", "rt", - "rtc", "ruby", "s", "samp", + "script", + "search", + "section", + "select", + "slot", "small", + "source", "span", "strong", + "style", "sub", + "summary", "sup", - "time", - "u", - "var", - "wbr", - "area", - "audio", - "map", - "track", - "video", - "embed", - "object", - "param", - "source", - "canvas", - "script", - "noscript", - "del", - "ins", - "caption", - "col", - "colgroup", "table", - "thead", "tbody", - "tfoot", "td", + "template", + "textarea", + "tfoot", "th", + "thead", + "time", + "title", "tr", - "button", - "datalist", - "fieldset", - "form", - "input", - "label", - "legend", - "meter", - "optgroup", - "option", - "output", - "progress", - "select", - "textarea", - "details", - "dialog", - "menu", - "menuitem", - "summary", - "content", - "element", - "shadow", - "template", - "slot", - "blockquote", - "iframe", - "noframes", - "picture" + "track", + "u", + "ul", + "var", + "video", + "wbr" ] diff --git a/lib/utils/index.js b/lib/utils/index.js index 167edf208..8d0dfa80d 100644 --- a/lib/utils/index.js +++ b/lib/utils/index.js @@ -1581,7 +1581,7 @@ module.exports = { * Get the Vue component definition type from given node * Vue.component('xxx', {}) || component('xxx', {}) * @param {ObjectExpression} node Node to check - * @returns {'component' | 'mixin' | 'extend' | 'createApp' | 'defineComponent' | null} + * @returns {'component' | 'mixin' | 'extend' | 'createApp' | 'defineComponent' | 'defineNuxtComponent' | null} */ getVueComponentDefinitionType, /** @@ -2755,7 +2755,7 @@ function isVueComponentFile(node, path) { * Get the Vue component definition type from given node * Vue.component('xxx', {}) || component('xxx', {}) * @param {ObjectExpression} node Node to check - * @returns {'component' | 'mixin' | 'extend' | 'createApp' | 'defineComponent' | null} + * @returns {'component' | 'mixin' | 'extend' | 'createApp' | 'defineComponent' | 'defineNuxtComponent' | null} */ function getVueComponentDefinitionType(node) { const parent = getParent(node) @@ -2811,6 +2811,12 @@ function getVueComponentDefinitionType(node) { const isDestructedVueComponent = isObjectArgument(parent) return isDestructedVueComponent ? 'defineComponent' : null } + if (callee.name === 'defineNuxtComponent') { + // for Nuxt 3.x + // defineNuxtComponent({}) + const isDestructedVueComponent = isObjectArgument(parent) + return isDestructedVueComponent ? 'defineNuxtComponent' : null + } } } @@ -2955,7 +2961,9 @@ function isSFCObject(context, node) { } const { callee } = parent if ( - (callee.type === 'Identifier' && callee.name === 'defineComponent') || + (callee.type === 'Identifier' && + (callee.name === 'defineComponent' || + callee.name === 'defineNuxtComponent')) || (callee.type === 'MemberExpression' && callee.object.type === 'Identifier' && callee.object.name === 'Vue' && diff --git a/lib/utils/svg-elements.json b/lib/utils/svg-elements.json index 7e5ba6052..f214aad24 100644 --- a/lib/utils/svg-elements.json +++ b/lib/utils/svg-elements.json @@ -3,13 +3,10 @@ "animate", "animateMotion", "animateTransform", - "audio", - "canvas", "circle", "clipPath", "defs", "desc", - "discard", "ellipse", "feBlend", "feColorMatrix", @@ -39,7 +36,6 @@ "filter", "foreignObject", "g", - "iframe", "image", "line", "linearGradient", @@ -64,8 +60,6 @@ "textPath", "title", "tspan", - "unknown", "use", - "video", "view" ] diff --git a/package.json b/package.json index 503f711f1..307cdc0aa 100644 --- a/package.json +++ b/package.json @@ -20,6 +20,7 @@ "preversion": "npm test && git add .", "version": "env-cmd -e version npm run update && npm run lint -- --fix && git add .", "update": "node ./tools/update.js", + "update-resources": "node ./tools/update-resources.js", "docs:watch": "vitepress dev docs", "predocs:build": "npm run update", "docs:build": "vitepress build docs" @@ -90,6 +91,7 @@ "eslint-plugin-vue": "file:.", "espree": "^9.6.1", "events": "^3.3.0", + "jsdom": "^22.0.0", "markdownlint-cli": "^0.42.0", "mocha": "^10.7.3", "nyc": "^17.1.0", diff --git a/tests/lib/rules/no-reserved-component-names.js b/tests/lib/rules/no-reserved-component-names.js index 231e1f98d..809d35103 100644 --- a/tests/lib/rules/no-reserved-component-names.js +++ b/tests/lib/rules/no-reserved-component-names.js @@ -247,12 +247,6 @@ const invalidElements = [ 'menuitem', 'summary', 'Summary', - 'content', - 'Content', - 'element', - 'Element', - 'shadow', - 'Shadow', 'template', 'Template', 'slot', @@ -278,8 +272,6 @@ const invalidElements = [ 'Defs', 'desc', 'Desc', - 'discard', - 'Discard', 'ellipse', 'Ellipse', 'feBlend', @@ -351,8 +343,6 @@ const invalidElements = [ 'textPath', 'tspan', 'Tspan', - 'unknown', - 'Unknown', 'use', 'Use', 'view', @@ -373,8 +363,6 @@ const invalidElements = [ 'Blink', 'center', 'Center', - 'command', - 'Command', 'dir', 'Dir', 'font', diff --git a/tests/lib/rules/no-undef-components.js b/tests/lib/rules/no-undef-components.js index e9992e39d..37a3bec68 100644 --- a/tests/lib/rules/no-undef-components.js +++ b/tests/lib/rules/no-undef-components.js @@ -269,6 +269,21 @@ tester.run('no-undef-components', rule, { } ] }, + { + filename: 'test.vue', + code: ` + + + ` + }, { filename: 'test.vue', code: ` diff --git a/tests/lib/rules/order-in-components.js b/tests/lib/rules/order-in-components.js index 2cbe721c5..130a319b0 100644 --- a/tests/lib/rules/order-in-components.js +++ b/tests/lib/rules/order-in-components.js @@ -234,6 +234,84 @@ ruleTester.run('order-in-components', rule, { } ] }, + { + filename: 'test.vue', + code: ` + import { defineComponent } from 'vue' + export default defineComponent({ + name: 'app', + data () { + return { + msg: 'Welcome to Your Vue.js App' + } + }, + props: { + propA: Number, + }, + }) + `, + output: ` + import { defineComponent } from 'vue' + export default defineComponent({ + name: 'app', + props: { + propA: Number, + }, + data () { + return { + msg: 'Welcome to Your Vue.js App' + } + }, + }) + `, + languageOptions, + errors: [ + { + message: + 'The "props" property should be above the "data" property on line 5.', + line: 10 + } + ] + }, + { + filename: 'test.vue', + code: ` + import { defineNuxtComponent } from '#app' + export default defineNuxtComponent({ + name: 'app', + data () { + return { + msg: 'Welcome to Your Vue.js App' + } + }, + props: { + propA: Number, + }, + }) + `, + output: ` + import { defineNuxtComponent } from '#app' + export default defineNuxtComponent({ + name: 'app', + props: { + propA: Number, + }, + data () { + return { + msg: 'Welcome to Your Vue.js App' + } + }, + }) + `, + languageOptions, + errors: [ + { + message: + 'The "props" property should be above the "data" property on line 5.', + line: 10 + } + ] + }, { filename: 'test.jsx', code: ` diff --git a/tests/lib/utils/vue-component.js b/tests/lib/utils/vue-component.js index 8970672ad..12fcd904f 100644 --- a/tests/lib/utils/vue-component.js +++ b/tests/lib/utils/vue-component.js @@ -325,6 +325,12 @@ function invalidTests(ext) { code: `export default defineComponent({})`, languageOptions, errors: [makeError(1)] + }, + { + filename: `test.${ext}`, + code: `export default defineNuxtComponent({})`, + languageOptions, + errors: [makeError(1)] } ] } diff --git a/tools/lib/http.js b/tools/lib/http.js new file mode 100644 index 000000000..27ff970f4 --- /dev/null +++ b/tools/lib/http.js @@ -0,0 +1,6 @@ +module.exports = { + httpGet +} +function httpGet(url) { + return fetch(url).then((res) => res.text()) +} diff --git a/tools/update-html-resources.js b/tools/update-html-resources.js new file mode 100644 index 000000000..fcbdacbb0 --- /dev/null +++ b/tools/update-html-resources.js @@ -0,0 +1,105 @@ +'use strict' + +const fs = require('fs') +const jsdom = require('jsdom') +const { httpGet } = require('./lib/http') + +main() + +async function main() { + const [bcdJson, obsoleteHtml] = await Promise.all([ + httpGet('https://unpkg.com/@mdn/browser-compat-data/data.json'), + httpGet('https://html.spec.whatwg.org/multipage/obsolete.html') + ]) + const bcd = JSON.parse(bcdJson) + + updateDeprecatedHTMLElements() + updateHTMLElements() + updateSVGElements() + + // ------------------------------------------------------------------------------ + // Update deprecated-html-elements.json + // ------------------------------------------------------------------------------ + function updateDeprecatedHTMLElements() { + const DEPRECATED_HTML_ELEMENTS_PATH = require.resolve( + '../lib/utils/deprecated-html-elements.json' + ) + const elements = new Set() + + const domDl = jsdom.JSDOM.fragment(obsoleteHtml).querySelector( + '[id="non-conforming-features"] ~ dl' + ) + for (const code of domDl.querySelectorAll('dt code')) { + const name = code.textContent.trim() + if (name) elements.add(name) + } + + if (elements.size === 0) { + throw new Error('No deprecated HTML elements found') + } + + fs.writeFileSync( + DEPRECATED_HTML_ELEMENTS_PATH, + `${JSON.stringify([...elements].sort(), null, 2)}\n`, + 'utf8' + ) + } + + // ------------------------------------------------------------------------------ + // Update html-elements.json + // ------------------------------------------------------------------------------ + function updateHTMLElements() { + const HTML_ELEMENTS_PATH = require.resolve( + '../lib/utils/html-elements.json' + ) + const elements = new Set() + const deprecatedHtmlElements = new Set( + require('../lib/utils/deprecated-html-elements.json') + ) + + for (const [name, element] of Object.entries(bcd.html.elements)) { + if (deprecatedHtmlElements.has(name)) { + continue + } + if (element.__compat.status.deprecated) { + continue + } + elements.add(name) + } + + if (elements.size === 0) { + throw new Error('No HTML elements found') + } + + fs.writeFileSync( + HTML_ELEMENTS_PATH, + `${JSON.stringify([...elements].sort(), null, 2)}\n`, + 'utf8' + ) + } + + // ------------------------------------------------------------------------------ + // Update svg-elements.json + // ------------------------------------------------------------------------------ + function updateSVGElements() { + const SVG_ELEMENTS_PATH = require.resolve('../lib/utils/svg-elements.json') + const elements = new Set() + + for (const [name, element] of Object.entries(bcd.svg.elements)) { + if (element.__compat.status.deprecated) { + continue + } + elements.add(name) + } + + if (elements.size === 0) { + throw new Error('No SVG elements found') + } + + fs.writeFileSync( + SVG_ELEMENTS_PATH, + `${JSON.stringify([...elements].sort(), null, 2)}\n`, + 'utf8' + ) + } +} diff --git a/tools/update-resources.js b/tools/update-resources.js new file mode 100644 index 000000000..a157c61ef --- /dev/null +++ b/tools/update-resources.js @@ -0,0 +1,4 @@ +'use strict' + +require('./update-vue3-export-names') +require('./update-html-resources') diff --git a/tools/update-vue3-export-names.js b/tools/update-vue3-export-names.js index 37e07e03d..0dabecb3a 100644 --- a/tools/update-vue3-export-names.js +++ b/tools/update-vue3-export-names.js @@ -6,9 +6,8 @@ const fs = require('fs') const path = require('path') -const https = require('https') -const { URL } = require('url') const tsParser = require('@typescript-eslint/parser') +const { httpGet } = require('./lib/http') main() @@ -144,33 +143,3 @@ async function resolveTypeContents(m) { } return await httpGet(`https://unpkg.com/${m}/${typesPath}`) } - -function httpGet(url) { - return new Promise((resolve, reject) => { - let result = '' - https - .get(url, (res) => { - if (res.statusCode && res.statusCode >= 300 && res.statusCode < 400) { - // redirect - let redirectUrl = res.headers.location - if (!redirectUrl.startsWith('http')) { - const baseUrl = new URL(url) - baseUrl.pathname = redirectUrl - redirectUrl = String(baseUrl) - } - res.destroy() - resolve(httpGet(redirectUrl)) - return - } - res.setEncoding('utf8') - res.on('data', (chunk) => { - result += String(chunk) - }) - res.on('end', () => { - resolve(result) - }) - res.on('error', reject) - }) - .on('error', reject) - }) -} diff --git a/tools/update.js b/tools/update.js index de792d507..64da1be1b 100644 --- a/tools/update.js +++ b/tools/update.js @@ -11,7 +11,3 @@ require('./update-lib-flat-configs') require('./update-lib-index') require('./update-docs') require('./update-docs-rules-index') - -if (process.env.IN_VERSION_SCRIPT) { - require('./update-vue3-export-names') -}