/** * @name Unresolvable import * @description An import that cannot be resolved to a module will * cause an exception at runtime. * @kind problem * @problem.severity warning * @id js/node/unresolvable-import * @tags maintainability * frameworks/node.js * @precision low */ import javascript /** * Gets the `package.json` of the nearest enclosing NPM package to which * file `f` belongs. */ PackageJSON getClosestPackageJSON(Folder f) { result = f.(NPMPackage).getPackageJSON() or not f instanceof NPMPackage and result = getClosestPackageJSON(f.getParentContainer()) } from Require r, string path, string mod where path = r.getImportedPath().getValue() and // the imported module is the initial segment of the path, up to // `/` or the end of the string, whichever comes first; we exclude // local paths starting with `.` or `/`, since they might refer to files // downloaded or generated during the build mod = path.regexpCapture("([^./][^/]*)(/.*|$)", 1) and // exclude WebPack/Require.js loaders not mod.matches("%!%") and // import cannot be resolved statically not exists(r.getImportedModule()) and // no enclosing NPM package declares a dependency on `mod` forex(NPMPackage pkg, PackageJSON pkgJSON | pkg.getAModule() = r.getTopLevel() and pkgJSON = pkg.getPackageJSON() | not pkgJSON.declaresDependency(mod, _) and not pkgJSON.getPeerDependencies().getADependency(mod, _) and // exclude packages depending on `fbjs`, which automatically pulls in many otherwise // undeclared dependencies not pkgJSON.declaresDependency("fbjs", _) ) select r, "Module " + mod + " cannot be resolved, and is not declared as a dependency in $@.", getClosestPackageJSON(r.getFile().getParentContainer()), "package.json"