From 1b8dab92b9177946ba9cdd96afd3df3541f7f2cb Mon Sep 17 00:00:00 2001 From: Alex Gleason Date: Fri, 30 May 2025 23:02:12 +0200 Subject: [PATCH] webmanifest eslint --- eslint-rules/index.js | 2 + eslint-rules/require-webmanifest.js | 84 +++++++++++++++++++++++++++++ eslint.config.js | 2 + 3 files changed, 88 insertions(+) create mode 100644 eslint-rules/require-webmanifest.js diff --git a/eslint-rules/index.js b/eslint-rules/index.js index 7569a01..d534be1 100644 --- a/eslint-rules/index.js +++ b/eslint-rules/index.js @@ -1,7 +1,9 @@ import noPlaceholderComments from './no-placeholder-comments.js'; +import requireWebmanifest from './require-webmanifest.js'; export default { rules: { 'no-placeholder-comments': noPlaceholderComments, + 'require-webmanifest': requireWebmanifest, }, }; \ No newline at end of file diff --git a/eslint-rules/require-webmanifest.js b/eslint-rules/require-webmanifest.js new file mode 100644 index 0000000..312bafd --- /dev/null +++ b/eslint-rules/require-webmanifest.js @@ -0,0 +1,84 @@ +import fs from 'fs'; +import path from 'path'; + +export default { + meta: { + type: 'problem', + docs: { + description: 'Require web manifest file and proper HTML link tag', + category: 'Best Practices', + }, + fixable: null, + schema: [], + messages: { + missingManifestFile: 'Web manifest file not found. Expected {{expectedPath}}', + missingManifestLink: 'Missing web manifest link tag in HTML head', + invalidManifestLink: 'Web manifest link tag has incorrect rel or href attribute', + }, + }, + + create(context) { + const filename = context.getFilename(); + + // Only run this rule on HTML files + if (!filename.endsWith('.html')) { + return {}; + } + + return { + Program(node) { + const sourceCode = context.getSourceCode(); + const htmlContent = sourceCode.getText(); + + // Check for manifest link tag in HTML + const manifestLinkRegex = /]*rel=["']manifest["'][^>]*>/i; + const manifestMatch = htmlContent.match(manifestLinkRegex); + + if (!manifestMatch) { + context.report({ + node, + messageId: 'missingManifestLink', + }); + return; + } + + // Extract href from the manifest link + const hrefMatch = manifestMatch[0].match(/href=["']([^"']+)["']/i); + if (!hrefMatch) { + context.report({ + node, + messageId: 'invalidManifestLink', + }); + return; + } + + const manifestPath = hrefMatch[1]; + + // Resolve the manifest file path relative to the project root + const htmlDir = path.dirname(filename); + let resolvedManifestPath; + + if (manifestPath.startsWith('/')) { + // Absolute path - check in public directory first, then project root + const publicPath = path.resolve(htmlDir, 'public' + manifestPath); + const rootPath = path.resolve(htmlDir, '.' + manifestPath); + resolvedManifestPath = fs.existsSync(publicPath) ? publicPath : rootPath; + } else { + // Relative path + resolvedManifestPath = path.resolve(htmlDir, manifestPath); + } + + // Check if the manifest file exists + if (!fs.existsSync(resolvedManifestPath)) { + context.report({ + node, + messageId: 'missingManifestFile', + data: { + expectedPath: manifestPath, + }, + }); + } + }, + }; + }, +}; \ No newline at end of file diff --git a/eslint.config.js b/eslint.config.js index 9fda677..a26dff1 100644 --- a/eslint.config.js +++ b/eslint.config.js @@ -35,6 +35,7 @@ export default tseslint.config( files: ["**/*.html"], plugins: { "@html-eslint": htmlEslint, + "custom": customRules, }, languageOptions: { parser: htmlParser, @@ -43,6 +44,7 @@ export default tseslint.config( "@html-eslint/require-title": "error", "@html-eslint/require-meta-charset": "error", "@html-eslint/require-meta-viewport": "error", + "custom/require-webmanifest": "error", }, } );