inline-script-id.js 2.2 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273
  1. const url = 'https://nextjs.org/docs/messages/inline-script-id'
  2. module.exports = {
  3. meta: {
  4. docs: {
  5. description:
  6. 'Enforce `id` attribute on `next/script` components with inline content.',
  7. recommended: true,
  8. url,
  9. },
  10. type: 'problem',
  11. schema: [],
  12. },
  13. create: function (context) {
  14. let nextScriptImportName = null
  15. return {
  16. ImportDeclaration(node) {
  17. if (node.source.value === 'next/script') {
  18. nextScriptImportName = node.specifiers[0].local.name
  19. }
  20. },
  21. JSXElement(node) {
  22. if (nextScriptImportName == null) return
  23. if (
  24. node.openingElement &&
  25. node.openingElement.name &&
  26. node.openingElement.name.name !== nextScriptImportName
  27. ) {
  28. return
  29. }
  30. const attributeNames = new Set()
  31. let hasNonCheckableSpreadAttribute = false
  32. node.openingElement.attributes.forEach((attribute) => {
  33. // Early return if we already have a non-checkable spread attribute, for better performance
  34. if (hasNonCheckableSpreadAttribute) return
  35. if (attribute.type === 'JSXAttribute') {
  36. attributeNames.add(attribute.name.name)
  37. } else if (attribute.type === 'JSXSpreadAttribute') {
  38. if (attribute.argument && attribute.argument.properties) {
  39. attribute.argument.properties.forEach((property) => {
  40. attributeNames.add(property.key.name)
  41. })
  42. } else {
  43. // JSXSpreadAttribute without properties is not checkable
  44. hasNonCheckableSpreadAttribute = true
  45. }
  46. }
  47. })
  48. // https://github.com/vercel/next.js/issues/34030
  49. // If there is a non-checkable spread attribute, we simply ignore them
  50. if (hasNonCheckableSpreadAttribute) return
  51. if (
  52. node.children.length > 0 ||
  53. attributeNames.has('dangerouslySetInnerHTML')
  54. ) {
  55. if (!attributeNames.has('id')) {
  56. context.report({
  57. node,
  58. message: `\`next/script\` components with inline content must specify an \`id\` attribute. See: ${url}`,
  59. })
  60. }
  61. }
  62. },
  63. }
  64. },
  65. }