UNPKG

@jenssimon/eslint-config-sfcc

Version:

A collection of shareable ESLint configurations for Salesforce Commerce Cloud (SFCC)

877 lines (876 loc) 31 kB
import { fixupPluginRules } from "@eslint/compat"; import es from "eslint-plugin-es"; import globals from "globals"; import fs from "node:fs"; import path from "node:path"; import { XMLParser } from "fast-xml-parser"; //#region src/plugins/_utils/sfcc-settings.ts function getSfccSettings(context) { return context.settings?.sfcc ?? {}; } function withSfccSettings(createWithSettings) { return (context) => createWithSettings(context, getSfccSettings(context)); } //#endregion //#region src/plugins/sfcc/no-ds-files.ts function isDsFile(filename) { return /\.ds$/iu.test(filename); } const noDsFiles = { meta: { type: "problem", docs: { description: "Disallow .ds files in SFCC projects. Use .js files instead.", recommended: true }, schema: [], messages: { noDsFiles: "Legacy .ds files are not allowed. Rename this file to .js and use CommonJS modules." } }, create: withSfccSettings((context, _sfccSettings) => { return { Program(node) { if (!isDsFile(context.filename)) return; context.report({ node, messageId: "noDsFiles" }); } }; }) }; //#endregion //#region src/plugins/sfcc/no-e4x-syntax.ts function isAstNode(value) { return value !== null && typeof value === "object" && "type" in value && typeof value.type === "string"; } function hasDynamicJsxParts(root) { const stack = [root]; const seen = /* @__PURE__ */ new Set(); while (stack.length > 0) { const node = stack.pop(); if (!node || seen.has(node)) continue; seen.add(node); if (node.type === "JSXExpressionContainer" || node.type === "JSXSpreadAttribute" || node.type === "JSXSpreadChild") return true; const entries = Object.entries(node); for (const [key, value] of entries) { if (key === "parent" || value === null || value === void 0) continue; if (Array.isArray(value)) { for (const item of value) if (isAstNode(item)) stack.push(item); continue; } if (isAstNode(value)) stack.push(value); } } return false; } function toXmlSuggestionText(markup) { if (markup.includes("\n") && !markup.includes("`") && !markup.includes("${")) return `XML(\`${markup}\`)`; return `XML(${JSON.stringify(markup)})`; } const noE4xSyntax = { meta: { type: "problem", docs: { description: "Disallow JSX/E4X-like syntax in SFCC JavaScript to avoid parser ambiguity and unsupported runtime patterns.", recommended: true }, hasSuggestions: true, schema: [], messages: { forbiddenSyntax: "JSX/E4X-like syntax is not allowed in SFCC JavaScript. Use plain JavaScript and SFCC XML APIs instead.", suggestXmlCtor: "Convert static markup to XML(\"...\") for explicit XML construction." } }, create: withSfccSettings((context) => { return { JSXElement(node) { const isDynamic = hasDynamicJsxParts(node); context.report({ node, messageId: "forbiddenSyntax", ...isDynamic ? {} : { suggest: [{ messageId: "suggestXmlCtor", fix: (fixer) => fixer.replaceText(node, toXmlSuggestionText(context.sourceCode.getText(node))) }] } }); }, JSXFragment(node) { context.report({ node, messageId: "forbiddenSyntax" }); } }; }) }; //#endregion //#region src/plugins/sfcc/no-rhino-import-globals.ts const LEGACY_RHINO_IMPORTS = new Set([ "importScript", "importPackage", "importClass" ]); function isJavaScriptTarget$1(filename) { if (filename === "<input>") return true; return /\.(?:[cm]?js|ds)$/iu.test(filename); } const noRhinoImportGlobals = { meta: { type: "problem", docs: { description: "Disallow legacy Rhino globals importScript, importPackage, and importClass in JavaScript files. Use CommonJS require() instead.", recommended: true }, schema: [], messages: { forbiddenLegacyImport: "{{name}}() is a legacy Rhino global. Use CommonJS require() instead." } }, create: withSfccSettings((context) => { if (!isJavaScriptTarget$1(context.filename)) return {}; return { CallExpression(node) { const callee = node.callee; if (callee.type !== "Identifier" || !LEGACY_RHINO_IMPORTS.has(callee.name ?? "")) return; context.report({ node: callee, messageId: "forbiddenLegacyImport", data: { name: callee.name } }); } }; }) }; //#endregion //#region src/plugins/sfcc/no-type-annotations.ts function isJavaScriptTarget(filename) { if (filename === "<input>") return true; return /\.(?:[cm]?js|ds)$/iu.test(filename); } function canAutoFix(node) { const parent = node.parent; if (!parent) return false; if (parent.type === "FunctionDeclaration" || parent.type === "FunctionExpression" || parent.type === "ArrowFunctionExpression") return true; if (parent.type !== "Identifier") return false; if (parent.optional === true || parent.definite === true) return false; if (parent.name === "this") return false; return true; } function hasLeadingJsdoc(sourceCode, node) { return sourceCode.getCommentsBefore(node).some((comment) => comment.type === "Block" && comment.value.trimStart().startsWith("*")); } function getTypeText(sourceCode, node) { return sourceCode.getText(node).replace(/^:\s*/u, ""); } function getVariableDeclaration(node) { const identifier = node.parent; const declarator = identifier?.parent; const declaration = declarator?.parent; if (!identifier || identifier.type !== "Identifier") return; if (declarator?.type !== "VariableDeclarator" || declaration?.type !== "VariableDeclaration") return; if (declaration.declarations.length !== 1 || declarator.id !== identifier || declarator.init == null) return; return declaration; } function getFunctionNode(node) { const parent = node.parent; if (parent?.type === "FunctionDeclaration" || parent?.type === "FunctionExpression" || parent?.type === "ArrowFunctionExpression") return parent; } const noTypeAnnotations = { meta: { type: "problem", docs: { description: "Disallow type-annotation syntax in JavaScript files. Rhino/E4X may accept it, but it is invalid in standard JavaScript. Use JSDoc types instead.", recommended: true }, fixable: "code", hasSuggestions: true, schema: [], messages: { forbiddenTypeAnnotation: "Type annotation syntax may be valid in Rhino/E4X, but is invalid in standard JavaScript and not allowed in .js files. Use JSDoc typing instead.", suggestJsdocType: "Add a JSDoc @type annotation and remove the inline type annotation.", suggestJsdocReturns: "Add a JSDoc @returns annotation and remove the inline type annotation." } }, create: withSfccSettings((context) => { if (!isJavaScriptTarget(context.filename)) return {}; return { TSTypeAnnotation(rawNode) { const node = rawNode; const sourceCode = context.sourceCode; const variableDeclaration = getVariableDeclaration(node); const functionNode = getFunctionNode(node); const typeText = getTypeText(sourceCode, node); const shouldSuggestTypeJsdoc = variableDeclaration !== void 0 && !hasLeadingJsdoc(sourceCode, variableDeclaration); const shouldSuggestReturnsJsdoc = functionNode !== void 0 && !hasLeadingJsdoc(sourceCode, functionNode); context.report({ node, messageId: "forbiddenTypeAnnotation", ...canAutoFix(node) ? { fix: (fixer) => fixer.remove(node) } : {}, ...shouldSuggestTypeJsdoc && variableDeclaration !== void 0 ? { suggest: [{ messageId: "suggestJsdocType", fix: (fixer) => [fixer.insertTextBefore(variableDeclaration, `/** @type {${typeText}} */\n`), fixer.remove(node)] }] } : shouldSuggestReturnsJsdoc && functionNode !== void 0 ? { suggest: [{ messageId: "suggestJsdocReturns", fix: (fixer) => [fixer.insertTextBefore(functionNode, `/** @returns {${typeText}} */\n`), fixer.remove(node)] }] } : {} }); } }; }) }; //#endregion //#region src/plugins/_utils/rhino-scope.ts const FUNCTION_NODE_TYPES = new Set([ "FunctionDeclaration", "FunctionExpression", "ArrowFunctionExpression" ]); const LOOP_NODE_TYPES = new Set([ "ForStatement", "ForInStatement", "ForOfStatement", "WhileStatement", "DoWhileStatement" ]); function isFunctionBodyBlock(node) { const parent = node.parent; if (!parent) return false; return FUNCTION_NODE_TYPES.has(parent.type) && parent.body === node; } function hasLoopAncestor(node) { let current = node.parent; while (current) { if (LOOP_NODE_TYPES.has(current.type)) return true; if (FUNCTION_NODE_TYPES.has(current.type) || current.type === "Program") return false; current = current.parent; } return false; } /** * Returns true when a VariableDeclaration node sits in a block-scoped context * where Rhino's const implementation is broken due to loop re-execution — * i.e. the same const binding would be re-declared on each iteration. * * Affected contexts (see https://github.com/mozilla/rhino/issues/326): * - The left-hand side of for-in / for-of statements * - The init position of a standard for-loop * - Any BlockStatement that has a loop ancestor within the same function * - SwitchCase (iterated via fall-through semantics in Rhino) */ function isRhinoCriticalScope(node) { const parent = node.parent; if (!parent) return false; if (parent.type === "Program") return false; if (parent.type === "BlockStatement") { if (isFunctionBodyBlock(parent)) return false; return hasLoopAncestor(node); } return parent.type === "ForStatement" || parent.type === "ForInStatement" || parent.type === "ForOfStatement" || parent.type === "SwitchCase"; } /** * Returns true when a VariableDeclaration node is inside a nested block * (any BlockStatement that is NOT the direct body of a function). * Used to detect potential same-name const conflicts across sibling blocks. */ function isInNestedBlock(node) { const parent = node.parent; if (!parent || parent.type !== "BlockStatement") return false; return !isFunctionBodyBlock(parent); } //#endregion //#region src/plugins/sfcc/prefer-const.ts function isNeverReassigned(variable) { return variable.references.every((ref) => !ref.isWrite() || ref.init === true); } const preferConst = { meta: { type: "suggestion", docs: { description: "Require const for let declarations that are never reassigned, except in Rhino-sensitive nested and loop scopes.", recommended: true }, fixable: "code", schema: [], messages: { useConst: "Prefer const over {{kind}} when the variable is never reassigned." } }, create: withSfccSettings((context) => { function checkDeclaration(node) { if (isInNestedBlock(node) || isRhinoCriticalScope(node)) return; const variables = context.sourceCode.getDeclaredVariables(node); if (variables.length === 0 || !variables.every(isNeverReassigned)) return; const kind = node.kind; context.report({ node, messageId: "useConst", data: { kind }, fix: (fixer) => { const keywordToken = context.sourceCode.getFirstToken(node); if (!keywordToken || keywordToken.value !== kind) return null; return fixer.replaceText(keywordToken, "const"); } }); } function checkVarDeclaration(node) { if (isRhinoCriticalScope(node)) return; const variables = context.sourceCode.getDeclaredVariables(node); if (variables.length === 0 || !variables.every(isNeverReassigned)) return; context.report({ node, messageId: "useConst", data: { kind: "var" }, fix: (fixer) => { const keywordToken = context.sourceCode.getFirstToken(node); if (!keywordToken || keywordToken.value !== "var") return null; return fixer.replaceText(keywordToken, "const"); } }); } return { "VariableDeclaration[kind='let']": checkDeclaration, "VariableDeclaration[kind='var']": checkVarDeclaration }; }) }; //#endregion //#region src/plugins/sfcc/rhino-const-compat.ts function isRhinoCriticalConstDeclaration(node) { return node.type === "VariableDeclaration" && node.kind === "const" && isRhinoCriticalScope(node); } const rhinoConstCompat = { meta: { type: "problem", docs: { description: "Enforce let instead of const in Rhino-unsafe loop-related scopes.", recommended: true }, fixable: "code", schema: [], messages: { useLet: "Use let instead of const in this block-scoped context for Rhino compatibility." } }, create: withSfccSettings((context) => ({ VariableDeclaration(node) { if (!isRhinoCriticalConstDeclaration(node)) return; context.report({ node, messageId: "useLet", fix: (fixer) => { const keywordToken = context.sourceCode.getFirstToken(node); if (!keywordToken || keywordToken.value !== "const") return null; return fixer.replaceText(keywordToken, "let"); } }); } })) }; //#endregion //#region src/plugins/sfcc/rhino-const-conflict.ts const rhinoConstConflict = { meta: { type: "problem", docs: { description: "Disallow const in nested blocks when the same identifier is declared as const elsewhere in the same function, as Rhino treats const as function-scoped and would throw a re-declaration error.", recommended: true }, fixable: "code", schema: [], messages: { conflict: "'{{name}}' is declared as const in multiple block scopes of the same function. Use let to avoid a Rhino re-declaration error." } }, create: withSfccSettings((context) => { const stack = []; function enterFunction() { stack.push({ allConsts: /* @__PURE__ */ new Map(), nestedConsts: /* @__PURE__ */ new Map() }); } function exitFunction() { const scope = stack.pop(); if (!scope) return; for (const [name, nestedNodes] of scope.nestedConsts) if ((scope.allConsts.get(name)?.length ?? 0) > 1) for (const node of nestedNodes) context.report({ node, messageId: "conflict", data: { name }, fix: (fixer) => { const token = context.sourceCode.getFirstToken(node); if (!token || token.value !== "const") return null; return fixer.replaceText(token, "let"); } }); } return { FunctionDeclaration: enterFunction, FunctionExpression: enterFunction, ArrowFunctionExpression: enterFunction, "FunctionDeclaration:exit": exitFunction, "FunctionExpression:exit": exitFunction, "ArrowFunctionExpression:exit": exitFunction, VariableDeclaration(rawNode) { if (rawNode.kind !== "const" || stack.length === 0) return; const node = rawNode; const scope = stack[stack.length - 1]; const nested = isInNestedBlock(node); const variables = context.sourceCode.getDeclaredVariables(rawNode); for (const variable of variables) { const { name } = variable; if (!scope.allConsts.has(name)) scope.allConsts.set(name, []); scope.allConsts.get(name).push(node); if (nested) { if (!scope.nestedConsts.has(name)) scope.nestedConsts.set(name, []); scope.nestedConsts.get(name).push(node); } } } }; }) }; //#endregion //#region src/plugins/_utils/site-template-cartridge-path.ts function resolveSiteTemplateXmlPath(siteTemplatePath, site, cwd) { if (!siteTemplatePath || !site) return; const resolvedSiteTemplatePath = path.isAbsolute(siteTemplatePath) ? siteTemplatePath : path.resolve(cwd, siteTemplatePath); return path.join(resolvedSiteTemplatePath, "sites", site, "site.xml"); } function findCustomCartridges(value) { if (!value || typeof value !== "object") return; if (Array.isArray(value)) { for (const item of value) { const found = findCustomCartridges(item); if (found) return found; } return; } const objectValue = value; const directValue = objectValue["custom-cartridges"]; if (typeof directValue === "string") return directValue; for (const nestedValue of Object.values(objectValue)) { const found = findCustomCartridges(nestedValue); if (found) return found; } } function getSiteTemplateCartridgePath(siteTemplatePath, site, cwd) { const siteTemplateXmlPath = resolveSiteTemplateXmlPath(siteTemplatePath, site, cwd); if (!siteTemplateXmlPath) return []; try { const xmlContent = fs.readFileSync(siteTemplateXmlPath, "utf8"); const customCartridges = findCustomCartridges(new XMLParser({ ignoreAttributes: true, trimValues: true, parseTagValue: false, parseAttributeValue: false }).parse(xmlContent)); if (!customCartridges) return []; return customCartridges.split(":").map((entry) => entry.trim()).filter((entry) => entry.length > 0); } catch { return []; } } //#endregion //#region src/plugins/sfcc/valid-require-path.ts const SUPPORTED_EXTENSIONS = [ "js", "ds", "json" ]; function getStringArgument(node) { if (node.type === "Literal" && typeof node.value === "string") return node.value; if (node.type === "TemplateLiteral" && node.expressions.length === 0) return node.quasis[0]?.value.cooked ?? void 0; } function isAllowedPrefix(requirePath) { return requirePath.startsWith("dw/") || requirePath.startsWith("./") || requirePath.startsWith("../") || requirePath.startsWith("*/") || requirePath.startsWith("~/"); } function isCartridgeStylePath(requirePath) { return /^[A-Za-z0-9_-]+\/.+/u.test(requirePath); } function getFirstSegment(requirePath) { const slashIndex = requirePath.indexOf("/"); return slashIndex === -1 ? requirePath : requirePath.slice(0, slashIndex); } function resolveCartridgesDir(cartridgesDir, cwd) { return path.isAbsolute(cartridgesDir) ? cartridgesDir : path.resolve(cwd, cartridgesDir); } function getConfiguredCartridgePath(cartridgePath) { if (!cartridgePath) return []; return cartridgePath.filter((entry) => entry.trim().length > 0); } function getFilesystemCartridges(cartridgesDir, cwd) { const baseDir = resolveCartridgesDir(cartridgesDir, cwd); try { return fs.readdirSync(baseDir, { withFileTypes: true }).filter((entry) => entry.isDirectory()).map((entry) => entry.name); } catch { return []; } } function moduleExistsInCartridge(cartridgeName, moduleTarget, cartridgesDir, cwd) { const baseDir = resolveCartridgesDir(cartridgesDir, cwd); const normalizedTarget = moduleTarget.replace(/^\/+/, ""); const targetPath = path.join(baseDir, cartridgeName, normalizedTarget); if (path.extname(normalizedTarget)) return fs.existsSync(targetPath); return SUPPORTED_EXTENSIONS.some((extension) => fs.existsSync(`${targetPath}.${extension}`)); } function getOwnCartridge(filename, cartridgesDir, cwd) { if (filename === "<input>") return; const resolvedFilename = path.isAbsolute(filename) ? filename : path.resolve(cwd, filename); const baseDir = resolveCartridgesDir(cartridgesDir, cwd); const relativePath = path.relative(baseDir, resolvedFilename); if (relativePath.startsWith("..") || path.isAbsolute(relativePath)) return; const firstSegment = relativePath.split(path.sep)[0]; return firstSegment && firstSegment !== "." ? firstSegment : void 0; } function starReferenceExists(requirePath, cartridgesDir, cwd, cartridgePath) { const moduleTarget = requirePath.slice(2); if (!moduleTarget) return false; return (cartridgePath.length > 0 ? cartridgePath : getFilesystemCartridges(cartridgesDir, cwd)).some((name) => moduleExistsInCartridge(name, moduleTarget, cartridgesDir, cwd)); } function tildeReferenceExists(requirePath, filename, cartridgesDir, cwd) { const moduleTarget = requirePath.slice(2); if (!moduleTarget) return false; const ownCartridge = getOwnCartridge(filename, cartridgesDir, cwd); if (!ownCartridge) return false; return moduleExistsInCartridge(ownCartridge, moduleTarget, cartridgesDir, cwd); } function cartridgeExists(cartridgeName, cartridgesDir, cwd) { const baseDir = resolveCartridgesDir(cartridgesDir, cwd); const cartridgeRoot = path.join(baseDir, cartridgeName); try { return fs.statSync(cartridgeRoot).isDirectory(); } catch { return false; } } //#endregion //#region src/plugins/sfcc/index.ts const sfcc = { rules: { "no-ds-files": noDsFiles, "no-e4x-syntax": noE4xSyntax, "no-type-annotations": noTypeAnnotations, "no-rhino-import-globals": noRhinoImportGlobals, "prefer-const": preferConst, "rhino-const-compat": rhinoConstCompat, "rhino-const-conflict": rhinoConstConflict, "valid-require-path": { meta: { type: "problem", docs: { description: "Enforce SFCC-compatible require paths (dw/, relative, cartridge-name/, */, ~/).", recommended: true }, schema: [], messages: { invalidPath: "Invalid require path \"{{requirePath}}\". Allowed: dw/*, cartridgeName/*, ./*, ../*, */*, ~/* or configured bare modules.", unknownCartridge: "Unknown cartridge \"{{cartridgeName}}\" in require path \"{{requirePath}}\" (checked in \"{{cartridgesDir}}/\").", unresolvedStarPath: "Cannot resolve \"{{requirePath}}\" against configured cartridges in \"{{cartridgesDir}}/\".", unresolvedTildePath: "Cannot resolve \"{{requirePath}}\" in current cartridge (checked in \"{{cartridgesDir}}/\")." } }, create: withSfccSettings((context, options) => { const allowBareModules = new Set(options.allowBareModules ?? ["server"]); const checkCartridgeExists = options.checkCartridgeExists === true; const cartridgesDir = options.cartridgesDir ?? "cartridges"; const cwd = context.cwd ?? context.getCwd?.() ?? process.cwd(); const configuredCartridgePath = getConfiguredCartridgePath(options.cartridgePath); const templateCartridgePath = getSiteTemplateCartridgePath(options.siteTemplatePath, options.site, cwd); const cartridgePath = configuredCartridgePath.length > 0 ? configuredCartridgePath : templateCartridgePath; const filename = context.filename ?? context.getFilename?.() ?? "<input>"; return { CallExpression(node) { const callNode = node; if (callNode.callee?.type !== "Identifier" || callNode.callee.name !== "require") return; const firstArgument = callNode.arguments?.[0]; if (!firstArgument) return; const requirePath = getStringArgument(firstArgument); if (!requirePath) return; if (requirePath.startsWith("*/")) { if (checkCartridgeExists && !starReferenceExists(requirePath, cartridgesDir, cwd, cartridgePath)) context.report({ node: firstArgument, messageId: "unresolvedStarPath", data: { requirePath, cartridgesDir } }); return; } if (requirePath.startsWith("~/")) { if (checkCartridgeExists && !tildeReferenceExists(requirePath, filename, cartridgesDir, cwd)) context.report({ node: firstArgument, messageId: "unresolvedTildePath", data: { requirePath, cartridgesDir } }); return; } if (isAllowedPrefix(requirePath)) return; if (!requirePath.includes("/")) { if (!allowBareModules.has(requirePath)) context.report({ node: firstArgument, messageId: "invalidPath", data: { requirePath } }); return; } if (!isCartridgeStylePath(requirePath)) { context.report({ node: firstArgument, messageId: "invalidPath", data: { requirePath } }); return; } if (!checkCartridgeExists) return; const cartridgeName = getFirstSegment(requirePath); if (!cartridgeExists(cartridgeName, cartridgesDir, cwd)) context.report({ node: firstArgument, messageId: "unknownCartridge", data: { cartridgeName, requirePath, cartridgesDir } }); } }; }) } } }; //#endregion //#region src/plugins/sitegenesis/no-global-require.ts function getParserGlobalReturn(context) { const parserOptions = context.parserOptions; return Boolean(parserOptions?.ecmaFeatures?.globalReturn); } function getGlobalScope(context, node) { const scope = context.sourceCode.getScope(node); const hasGlobalReturn = getParserGlobalReturn(context); const sourceType = node.sourceType; if (hasGlobalReturn || sourceType === "module" || sourceType === "commonjs") return scope.childScopes[0] ?? scope; return scope; } function isReadOnlyVariable(variable) { return variable.writeable === void 0; } function getRequireCalleeName(variable) { return (variable.defs[0]?.node).init?.callee?.name; } function getReportNode(variable) { const identifierNode = variable.identifiers[0]; if (identifierNode) return identifierNode; return variable.defs[0]?.node; } //#endregion //#region src/plugins/sitegenesis/index.ts const sitegenesis = { rules: { "no-global-require": { meta: { docs: { description: "Prohibites global use of require unless every function is using it.", recommended: true }, schema: [] }, create: (context) => { if (!context.filename.replaceAll("\\", "/").includes("/cartridge/controllers/")) return {}; let routeCount = 0; const requires = {}; function processFunction(node) { const scope = context.sourceCode.getScope(node); if (scope.upper?.block?.type !== "Program") return; routeCount += 1; scope.through.filter((item) => item.from.type === "function").forEach((item) => { const requireItem = requires[item.identifier.name]; if (requireItem) requireItem.useCount += 1; }); } return { Program(node) { const globalScope = getGlobalScope(context, node); globalScope.variables.filter(isReadOnlyVariable).filter((item) => item.name !== "arguments").forEach((item) => { const isRequire = getRequireCalleeName(item) === "require"; const usedGlobally = globalScope.references.some((ref) => !ref.init && ref.identifier.name === item.name); if (isRequire) requires[item.name] = { variable: item, useCount: 0, usedGlobally }; }); }, FunctionExpression(node) { processFunction(node); }, FunctionDeclaration(node) { processFunction(node); }, "Program:exit"() { Object.keys(requires).forEach((key) => { const value = requires[key]; const reportNode = value ? getReportNode(value.variable) : void 0; if (value && reportNode && value.useCount < routeCount && !value.usedGlobally) context.report({ node: reportNode, message: "\"{{a}}\" should be declared inside route", data: { a: key } }); }); } }; } } } }; //#endregion //#region src/rules/core.ts const core = { "no-restricted-properties": "off", "object-shorthand": "off", "prefer-arrow-callback": "off", "prefer-const": "off", "prefer-exponentiation-operator": "off", "prefer-object-has-own": "off", "prefer-object-spread": "off", "prefer-rest-params": "off", "prefer-spread": "off", "preserve-caught-error": "off" }; //#endregion //#region src/rules/es.ts const esVersionPresets = ["no-new-in-esnext"]; for (let year = 2020; year >= 2016; year--) esVersionPresets.push(`no-new-in-es${year}`); const getRulesFromPreset = (preset) => Object.entries(es.configs[preset]?.rules ?? {}); const getRulesFromPresets = (presets) => Object.fromEntries(presets.flatMap(getRulesFromPreset)); const es$1 = { ...getRulesFromPresets(esVersionPresets), "es/no-object-values": "off", "es/no-object-entries": "off", "es/no-for-of-loops": "off", "es/no-classes": "error", "es/no-computed-properties": "error", "es/no-default-parameters": "error", "es/no-dynamic-import": "error", "es/no-generators": "error", "es/no-modules": "error", "es/no-new-target": "error", "es/no-promise": "error", "es/no-proxy": "error", "es/no-reflect": "error", "es/no-regexp-u-flag": "error", "es/no-regexp-y-flag": "error", "es/no-rest-parameters": "error", "es/no-rest-spread-properties": "error", "es/no-spread-elements": "error", "es/no-subclassing-builtins": "error" }; //#endregion //#region src/rules/sfcc.ts const sfcc$1 = { "sfcc/no-ds-files": "error", "sfcc/no-e4x-syntax": "error", "sfcc/no-type-annotations": "error", "sfcc/no-rhino-import-globals": "error", "sfcc/prefer-const": "error", "sfcc/rhino-const-compat": "error", "sfcc/rhino-const-conflict": "error", "sfcc/valid-require-path": "error" }; //#endregion //#region src/rules/sitegenesis.ts const sitegenesis$1 = { "sitegenesis/no-global-require": "error" }; //#endregion //#region src/rules/sonarjs.ts const sonarjs = { "sonarjs/no-implicit-global": "off" }; //#endregion //#region src/rules/typescript-eslint.ts const typescriptEslint = { "@typescript-eslint/no-require-imports": "off" }; //#endregion //#region src/rules/unicorn.ts const unicorn = { "unicorn/no-array-for-each": "off", "unicorn/no-array-sort": "off", "unicorn/no-for-loop": "off", "unicorn/no-useless-iterator-to-array": "off", "unicorn/numeric-separators-style": "off", "unicorn/prefer-array-flat": "off", "unicorn/prefer-array-flat-map": "off", "unicorn/prefer-at": "off", "unicorn/prefer-default-parameters": "off", "unicorn/prefer-modern-math-apis": "off", "unicorn/prefer-module": "off", "unicorn/prefer-node-protocol": "off", "unicorn/prefer-optional-catch-binding": "off", "unicorn/prefer-reflect-apply": "off", "unicorn/prefer-structured-clone": "off", "unicorn/prefer-string-replace-all": "off", "unicorn/prefer-spread": "off" }; //#endregion //#region src/rules/index.ts const rules = { ...core, ...unicorn, ...sonarjs, ...typescriptEslint, ...es$1, ...sfcc$1, ...sitegenesis$1 }; //#endregion //#region src/sfcc-globals.ts const sfccGlobals = { dw: true, global: true, APIException: true, ConversionError: true, customer: true, empty: true, Fault: true, IOError: true, Iterator: true, PIPELET_ERROR: true, PIPELET_NEXT: true, QName: true, request: true, response: true, session: true, slotcontent: true, StopIteration: true, SystemError: true, webreferences: true, webreferences2: true, XML: true, XMLList: true, XMLStreamError: true, importScript: true, importPackage: true, importClass: true }; //#endregion //#region src/configs/recommended.ts /** Creates the recommended flat config for SFCC projects. */ function createRecommendedConfig(options = {}) { const { cartridgesDir = "cartridges", files, ignores, sfcc: sfccOptions } = options; const normalizedCartridgesDir = cartridgesDir.replace(/\/+$/u, "") || "/"; const sfccSettings = sfccOptions !== void 0 && (sfccOptions.allowBareModules !== void 0 || sfccOptions.checkCartridgeExists !== void 0 || sfccOptions.cartridgePath !== void 0 || sfccOptions.cartridgesDir !== void 0 || sfccOptions.siteTemplatePath !== void 0 || sfccOptions.site !== void 0) ? { ...sfccOptions, ...sfccOptions?.cartridgesDir === void 0 ? { cartridgesDir: normalizedCartridgesDir } : {} } : void 0; function withBaseDir(suffix) { return normalizedCartridgesDir === "/" ? `/${suffix}` : `${normalizedCartridgesDir}/${suffix}`; } return [{ files: files ?? [withBaseDir("**/*.{js,ds}")], ignores: ignores ?? [withBaseDir("*/cartridge/client/**"), withBaseDir("*/cartridge/static/**")], languageOptions: { sourceType: "commonjs", globals: { ...globals.commonjs, ...sfccGlobals } }, plugins: { es: fixupPluginRules(es), sfcc, sitegenesis }, ...sfccSettings === void 0 ? {} : { settings: { sfcc: sfccSettings } }, rules }]; } /** Shareable config for SFCC projects */ const recommended = createRecommendedConfig(); //#endregion //#region src/index.ts const configs = { recommended }; const plugins = { sfcc, sitegenesis }; const eslintConfigSfcc = { configs, plugins }; //#endregion export { configs, createRecommendedConfig, eslintConfigSfcc as default, plugins, recommended, sfcc, sitegenesis };