UNPKG

@pega/custom-dx-components

Version:

Utility for building custom UI components

104 lines (90 loc) 3.29 kB
import fs from 'fs'; import path from 'path'; /** Cache: component directory → parsed config.json (or null) */ const configCache = new Map(); /** * Walks up the directory tree from `startDir` to find a `config.json` file, * stopping at the project root (`process.cwd()`) to avoid picking up unrelated configs. * Results are cached per directory to minimise repeated filesystem reads. */ function findComponentConfig(startDir) { if (configCache.has(startDir)) { return configCache.get(startDir); } const projectRoot = path.resolve(process.cwd()); let dir = path.resolve(startDir); while (dir.startsWith(projectRoot)) { const candidate = path.join(dir, 'config.json'); if (fs.existsSync(candidate)) { let parsed = null; try { parsed = JSON.parse(fs.readFileSync(candidate, 'utf8')); } catch { // ignore malformed config } configCache.set(startDir, parsed); return parsed; } const parent = path.dirname(dir); if (parent === dir) break; dir = parent; } configCache.set(startDir, null); return null; } /** @type {import('eslint').Rule.RuleModule} */ const rule = { meta: { type: 'problem', docs: { description: 'Disallow PCore and getPConnect usage in Presentation components', category: 'Pega DX Components', recommended: true }, messages: { noPCore: 'PCore must not be used in Presentation components (type="Presentation" in config.json). These are presentation-only components with no platform API access.', noGetPConnect: 'getPConnect must not be used in Presentation components (type="Presentation" in config.json). These are presentation-only components with no platform API access.' }, schema: [] }, create(context) { const filename = context.filename ?? context.getFilename(); const fileDir = path.dirname(filename); const config = findComponentConfig(fileDir); if (!config || config.type !== 'Presentation') { return {}; } return { // Flag `PCore` used as a standalone identifier (e.g. PCore.getConstants()) Identifier(node) { if (node.name === 'PCore') { context.report({ node, messageId: 'noPCore' }); } }, // Flag `getPConnect` in destructuring: const { getPConnect } = props // and as a property access: props.getPConnect() Property(node) { if (node.key && node.key.name === 'getPConnect') { context.report({ node, messageId: 'noGetPConnect' }); } }, // Flag `getPConnect` as a member access: props.getPConnect or props['getPConnect'] MemberExpression(node) { const prop = node.property; const isGetPConnect = node.computed ? prop.type === 'Literal' && prop.value === 'getPConnect' : prop.name === 'getPConnect'; if (isGetPConnect) { context.report({ node, messageId: 'noGetPConnect' }); } }, // Flag `getPConnect` called directly as a standalone function: getPConnect() CallExpression(node) { if (node.callee.type === 'Identifier' && node.callee.name === 'getPConnect') { context.report({ node, messageId: 'noGetPConnect' }); } } }; } }; export default rule;