Newer
Older
PixelPaintWar / apps / client / eslint-rules / prefer-relative-for-local-client-path.js
import path from 'node:path';

const CLIENT_ALIAS_PREFIX = '@client/';

const toPosixPath = (value) => value.split(path.sep).join('/');

const toRelativeImportPath = (fromDir, targetPath) => {
  const relativePath = path.relative(fromDir, targetPath);
  const posixRelativePath = toPosixPath(relativePath);

  if (!posixRelativePath || posixRelativePath.startsWith('..')) {
    return null;
  }

  return posixRelativePath.startsWith('.') ? posixRelativePath : `./${posixRelativePath}`;
};

export default {
  meta: {
    type: 'suggestion',
    docs: {
      description:
        'Warn when @client alias is used even though the import can be written as a same-level relative path.',
    },
    schema: [],
    messages: {
      preferRelative:
        'この import は相対参照で書けます。{{suggestion}} を使ってください(@client は遡りが必要な場合のみ使用)。',
    },
  },
  create(context) {
    const filename = context.filename;
    if (!filename || filename === '<input>') {
      return {};
    }

    const srcRoot = path.resolve(process.cwd(), 'src');
    const absoluteFilename = path.resolve(filename);

    if (!absoluteFilename.startsWith(`${srcRoot}${path.sep}`)) {
      return {};
    }

    const fromDirectory = path.dirname(absoluteFilename);

    return {
      ImportDeclaration(node) {
        const rawImportPath = node.source?.value;
        if (typeof rawImportPath !== 'string') {
          return;
        }

        if (!rawImportPath.startsWith(CLIENT_ALIAS_PREFIX)) {
          return;
        }

        const aliasedSubPath = rawImportPath.slice(CLIENT_ALIAS_PREFIX.length);
        if (!aliasedSubPath) {
          return;
        }

        const targetPath = path.resolve(srcRoot, aliasedSubPath);
        const suggestion = toRelativeImportPath(fromDirectory, targetPath);

        if (!suggestion) {
          return;
        }

        context.report({
          node: node.source,
          messageId: 'preferRelative',
          data: { suggestion },
        });
      },
    };
  },
};