const NEXT_POLYFILLED_FEATURES = [ 'Array.prototype.@@iterator', 'Array.prototype.copyWithin', 'Array.prototype.fill', 'Array.prototype.find', 'Array.prototype.findIndex', 'Array.prototype.flatMap', 'Array.prototype.flat', 'Array.from', 'Array.prototype.includes', 'Array.of', 'Function.prototype.name', 'fetch', 'Map', 'Number.EPSILON', 'Number.Epsilon', 'Number.isFinite', 'Number.isNaN', 'Number.isInteger', 'Number.isSafeInteger', 'Number.MAX_SAFE_INTEGER', 'Number.MIN_SAFE_INTEGER', 'Object.entries', 'Object.getOwnPropertyDescriptor', 'Object.getOwnPropertyDescriptors', 'Object.is', 'Object.keys', 'Object.values', 'Reflect', 'Set', 'Symbol', 'Symbol.asyncIterator', 'String.prototype.codePointAt', 'String.prototype.endsWith', 'String.fromCodePoint', 'String.prototype.includes', 'String.prototype.@@iterator', 'String.prototype.padEnd', 'String.prototype.padStart', 'String.prototype.repeat', 'String.raw', 'String.prototype.startsWith', 'String.prototype.trimEnd', 'String.prototype.trimStart', 'String.prototype.trim', 'URL', 'URLSearchParams', 'WeakMap', 'WeakSet', 'Promise', 'Promise.prototype.finally', 'es2015', // Should be covered by babel-preset-env instead. 'es2016', // Should be covered by babel-preset-env instead. 'es2017', // Should be covered by babel-preset-env instead. 'es2018', // Should be covered by babel-preset-env instead. 'es2019', // Should be covered by babel-preset-env instead. 'es5', // Should be covered by babel-preset-env instead. 'es6', // Should be covered by babel-preset-env instead. 'es7', // Should be covered by babel-preset-env instead. ] //------------------------------------------------------------------------------ // Rule Definition //------------------------------------------------------------------------------ module.exports = { meta: { docs: { description: 'Prohibit unwanted features to be listed in Polyfill.io tag.', category: 'HTML', recommended: true, url: 'https://nextjs.org/docs/messages/no-unwanted-polyfillio', }, fixable: null, // or "code" or "whitespace" }, create: function (context) { let scriptImport = null return { ImportDeclaration(node) { if (node.source && node.source.value === 'next/script') { scriptImport = node.specifiers[0].local.name } }, JSXOpeningElement(node) { if ( node.name && node.name.name !== 'script' && node.name.name !== scriptImport ) { return } if (node.attributes.length === 0) { return } const srcNode = node.attributes.find( (attr) => attr.type === 'JSXAttribute' && attr.name.name === 'src' ) if (!srcNode || srcNode.value.type !== 'Literal') { return } const src = srcNode.value.value if ( src.startsWith('https://cdn.polyfill.io/v2/') || src.startsWith('https://polyfill.io/v3/') ) { const featureQueryString = new URL(src).searchParams.get('features') const featuresRequested = (featureQueryString || '').split(',') const unwantedFeatures = featuresRequested.filter((feature) => NEXT_POLYFILLED_FEATURES.includes(feature) ) if (unwantedFeatures.length > 0) { context.report({ node, message: `No duplicate polyfills from Polyfill.io are allowed. ${unwantedFeatures.join( ', ' )} ${ unwantedFeatures.length > 1 ? 'are' : 'is' } already shipped with Next.js. See: https://nextjs.org/docs/messages/no-unwanted-polyfillio.`, }) } } }, } }, }