UNPKG

atom-nuclide

Version:

A unified developer experience for web and mobile development, built as a suite of features on top of Atom to provide hackability and the support of an active community.

161 lines (147 loc) 4.51 kB
'use strict'; /* @noflow */ /* * Copyright (c) 2015-present, Facebook, Inc. * All rights reserved. * * This source code is licensed under the license found in the LICENSE file in * the root directory of this source tree. */ /* NON-TRANSPILED FILE */ /* eslint-disable babel/func-params-comma-dangle, prefer-object-spread/prefer-object-spread */ const resolveFrom = require('resolve-from'); const path = require('path'); const fs = require('fs'); module.exports = function(context) { const filename = context.getFilename(); const dirname = path.dirname(filename); const ownPackage = getPackage(filename); const whitelist = new Set(context.options[0] && context.options[0].whitelist || []); if (!ownPackage) { throw new Error(`"${filename}" does not have a package.json`); } // Packages come in 3 flavors: // packageType: Atom & testRunner: apm // packageType: Node & testRunner: apm // packageType: Node & testRunner: npm function getCrossImportPackage(id) { const resolved = resolveFrom(dirname, id); // Exclude modules that are not found or not ours. if (resolved == null || resolved.includes('/node_modules/')) { return null; } const resolvedPackage = getPackage(resolved); // Requiring anything within our own package is ok. if (resolvedPackage.__dirname === ownPackage.__dirname) { return null; } // An apm package requiring a whitelisted package is ok. if (ownPackage.nuclide && ownPackage.nuclide.testRunner === 'apm' && whitelist.has(resolvedPackage.name)) { return null; } // Nothing can require into an Atom package if (resolvedPackage.nuclide && resolvedPackage.nuclide.packageType === 'Atom') { return {type: 'NO_ATOM', pkg: resolvedPackage}; } // npm packages can only require other npm packages. if (ownPackage.nuclide && ownPackage.nuclide.testRunner === 'npm' && resolvedPackage.nuclide && resolvedPackage.nuclide.testRunner === 'apm') { return {type: 'NO_NPM_TO_APM', pkg: resolvedPackage}; } return null; } function reportError(node, action, result) { const message = result.type === 'NO_ATOM' ? 'Atom package "{{name}}" is not {{action}} from other packages.' : result.type === 'NO_NPM_TO_APM' ? 'apm package "{{name}}" is not {{action}} from an npm package.' : null; context.report({ node, data: {name: result.pkg.name, action}, message, }); } return { CallExpression(node) { if (!isRequire(node)) { return; } // require("…") const id = node.arguments[0].value; const result = getCrossImportPackage(id); if (result) { reportError(node, 'requireable', result); } }, ExportNamedDeclaration(node) { if (node.source == null || node.exportKind === 'type') { return; } // export foo from "…" const id = node.source.value; const result = getCrossImportPackage(id); if (result) { reportError(node, 'exportable', result); } }, ExportAllDeclaration(node) { // export * from "…" const id = node.source.value; const result = getCrossImportPackage(id); if (result) { reportError(node, 'exportable', result); } }, ImportDeclaration(node) { if (node.importKind === 'type' || node.importKind === 'typeof') { return; } // import foo from "…" const id = node.source.value; const result = getCrossImportPackage(id); if (result) { reportError(node, 'importable', result); } }, }; }; function getPackage(start) { let current = path.resolve(start); for (;;) { const filename = path.join(current, 'package.json'); try { const source = fs.readFileSync(filename, 'utf8'); const json = JSON.parse(source); json.__filename = filename; json.__dirname = current; return json; } catch (err) { if (err.code === 'ENOENT' || err.code === 'ENOTDIR') { const next = path.join(current, '..'); if (next === current) { return null; } else { current = next; } } else { throw err; } } } } function isRequire(node) { return ( node.callee.type === 'Identifier' && node.callee.name === 'require' && node.arguments[0] && node.arguments[0].type === 'Literal' ); }