no-duplicate-head.js 1.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960
  1. const url = 'https://nextjs.org/docs/messages/no-duplicate-head'
  2. module.exports = {
  3. meta: {
  4. docs: {
  5. description:
  6. 'Prevent duplicate usage of `<Head>` in `pages/_document.js`.',
  7. recommended: true,
  8. url,
  9. },
  10. type: 'problem',
  11. schema: [],
  12. },
  13. create: function (context) {
  14. let documentImportName
  15. return {
  16. ImportDeclaration(node) {
  17. if (node.source.value === 'next/document') {
  18. const documentImport = node.specifiers.find(
  19. ({ type }) => type === 'ImportDefaultSpecifier'
  20. )
  21. if (documentImport && documentImport.local) {
  22. documentImportName = documentImport.local.name
  23. }
  24. }
  25. },
  26. ReturnStatement(node) {
  27. const ancestors = context.getAncestors()
  28. const documentClass = ancestors.find(
  29. (ancestorNode) =>
  30. ancestorNode.type === 'ClassDeclaration' &&
  31. ancestorNode.superClass &&
  32. ancestorNode.superClass.name === documentImportName
  33. )
  34. if (!documentClass) {
  35. return
  36. }
  37. if (node.argument && node.argument.children) {
  38. const headComponents = node.argument.children.filter(
  39. (childrenNode) =>
  40. childrenNode.openingElement &&
  41. childrenNode.openingElement.name &&
  42. childrenNode.openingElement.name.name === 'Head'
  43. )
  44. if (headComponents.length > 1) {
  45. for (let i = 1; i < headComponents.length; i++) {
  46. context.report({
  47. node: headComponents[i],
  48. message: `Do not include multiple instances of \`<Head/>\`. See: ${url}`,
  49. })
  50. }
  51. }
  52. }
  53. },
  54. }
  55. },
  56. }