/** * ESLint rule to require a Cloudflare copyright header at the top of every file. * * Requires files to start with: * // Copyright (c) Cloudflare, Inc. * // Licensed under the Apache 2.0 license found in the LICENSE file or at: * // https://opensource.org/licenses/Apache-2.0 */ export const requireCopyrightHeader = { meta: { type: 'suggestion', docs: { description: 'Require a Cloudflare copyright header at the top of every file', }, messages: { missingHeader: 'File is missing the Cloudflare copyright header. Add the following at the top of the file:\n// Copyright (c) {{year}} Cloudflare, Inc.\n// Licensed under the Apache 2.0 license found in the LICENSE file or at:\n// https://opensource.org/licenses/Apache-2.0', }, fixable: 'code', schema: [], }, create(context) { return { Program(node) { const sourceText = context.sourceCode.getText(); const headerPattern = /^\/\/ Copyright \(c\) \d{4}(-\d{4})? Cloudflare, Inc\.\n\/\/ Licensed under the Apache 2\.0 license found in the LICENSE file or at:\n\/\/ https:\/\/opensource\.org\/licenses\/Apache-2\.0/; if (!headerPattern.test(sourceText)) { const year = new Date().getFullYear(); context.report({ node, messageId: 'missingHeader', data: { year: String(year) }, fix(fixer) { const header = `// Copyright (c) ${year} Cloudflare, Inc.\n` + '// Licensed under the Apache 2.0 license found in the LICENSE file or at:\n' + '// https://opensource.org/licenses/Apache-2.0\n'; return fixer.insertTextBefore(node, header); }, }); } }, }; }, }; /** * ESLint rule to disallow re-exporting a namespace import as default. * * Disallows patterns like: * import * as _default from 'some-module'; * export default _default; * * Instead, use: * import { var1, var2 } from 'some-module'; * export * from 'some-module'; * export default { var1, var2 }; * * @see https://github.com/cloudflare/workerd/issues/5844 */ export const noExportDefaultOfImportStar = { meta: { type: 'problem', docs: { description: 'Disallow exporting a namespace import as the default export', }, messages: { noExportDefaultOfImportStar: "Do not re-export a namespace import as default. Use 'import { var1, var2 } from \"{{source}}\"', 'export * from \"{{source}}\"', and 'export default { var1, var2 }' instead.", }, schema: [], }, create(context) { const namespaceImports = new Map(); return { ImportDeclaration(node) { // Look for: import * as X from '...' for (const specifier of node.specifiers) { if (specifier.type === 'ImportNamespaceSpecifier') { namespaceImports.set(specifier.local.name, node.source.value); } } }, ExportDefaultDeclaration(node) { // Look for: export default X (where X is an identifier) if (node.declaration && node.declaration.type === 'Identifier') { const exportedName = node.declaration.name; const source = namespaceImports.get(exportedName); if (source !== undefined) { context.report({ node, messageId: 'noExportDefaultOfImportStar', data: { source }, }); } } }, }; }, }; export default { rules: { 'require-copyright-header': requireCopyrightHeader, 'no-export-default-of-import-star': noExportDefaultOfImportStar, }, };