/** * Copyright (c) Meta Platforms, Inc. and affiliates. * * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * * @format */ 'use strict'; const fs = require('node:fs'); const path = require('node:path'); const RN_ROOT_DIRECTORY = path.resolve(__dirname, '..', '..', '..'); const RN_PACKAGE_DIRECTORY = path.resolve( RN_ROOT_DIRECTORY, 'packages', 'react-native', ); const RN_IMPORT_PATTERN = /^react-native(?:\/(?.+))?$/; const RN_IMPORT_EXTS = ['.android.js', '.ios.js', '.js']; module.exports = { meta: { type: 'problem', docs: { description: "Disallow importing from 'react-native'", recommended: true, }, fixable: 'code', messages: { rnImport: "Use relative paths when importing within 'react-native'", }, schema: [], }, create(context) { return { CallExpression(node) { if ( node.callee.type === 'Identifier' && node.callee.name === 'require' && node.arguments.length === 1 ) { processSource(node.arguments[0]); } }, ImportExpression(node) { processSource(node.source); }, ImportDeclaration(node) { processSource(node.source); }, }; function processSource(source) { if (source.type !== 'Literal' || typeof source.value !== 'string') { return; } const match = source.value.match(RN_IMPORT_PATTERN); if (match == null) { return; } let fix; // NOTE: We only attempt to resolve `react-native/`. We // cannot safely fix named imports from `react-native` because the index // does arbitrary things like access the `default` property, etc. if (match.groups.relativeImport != null) { const suggestion = resolveRelativeImport(match.groups.relativeImport); if (suggestion != null) { fix = fixer => { const delimiter = source.raw[0]; const replacement = [delimiter, suggestion, delimiter].join(''); return fixer.replaceText(source, replacement); }; } } context.report({ node: source, messageId: 'rnImport', fix, }); } function resolveRelativeImport(relativeImport) { const importPath = path.resolve(RN_PACKAGE_DIRECTORY, relativeImport); if (!isValidImportPath(importPath)) { return null; } const sourcePath = path.dirname(context.getPhysicalFilename()); const relativePath = path.relative(sourcePath, importPath); // If importing from the same directory, prepend `./` to source path. const relativePathWithLeadingSlash = relativePath.includes(path.sep) ? relativePath : ['.', path.sep, relativePath].join(''); // Normalize backslash (on Windows) to forward slash. return relativePathWithLeadingSlash.replace(/\\/g, '/'); } function isValidImportPath(importPath) { const candidatePaths = []; if (importPath.endsWith('.js')) { candidatePaths.push(importPath); } else { for (const extension of RN_IMPORT_EXTS) { candidatePaths.push(importPath + extension); } } return candidatePaths.some(candidatePath => fs.existsSync(candidatePath)); } }, };