2017-12-10 21:51:33 +01:00
"use strict" ;
/ * *
* @ license
* Copyright 2017 Palantir Technologies , Inc .
*
* Licensed under the Apache License , Version 2.0 ( the "License" ) ;
* you may not use this file except in compliance with the License .
* You may obtain a copy of the License at
*
* http : //www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing , software
* distributed under the License is distributed on an "AS IS" BASIS ,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND , either express or implied .
* See the License for the specific language governing permissions and
* limitations under the License .
* /
Object . defineProperty ( exports , "__esModule" , { value : true } ) ;
var tslib _1 = require ( "tslib" ) ;
var builtins = require ( "builtin-modules" ) ;
var fs = require ( "fs" ) ;
var path = require ( "path" ) ;
var tsutils _1 = require ( "tsutils" ) ;
var ts = require ( "typescript" ) ;
var Lint = require ( "../index" ) ;
var OPTION _DEV = "dev" ;
var OPTION _OPTIONAL = "optional" ;
var Rule = /** @class */ ( function ( _super ) {
tslib _1 . _ _extends ( Rule , _super ) ;
function Rule ( ) {
return _super !== null && _super . apply ( this , arguments ) || this ;
}
/* tslint:enable:object-literal-sort-keys */
Rule . FAILURE _STRING _FACTORY = function ( module ) {
return "Module '" + module + "' is not listed as dependency in package.json" ;
} ;
Rule . prototype . apply = function ( sourceFile ) {
2018-09-20 02:56:13 +02:00
var whitelist = this . ruleArguments . find ( function ( arg ) { return Array . isArray ( arg ) ; } ) ;
if ( whitelist === null || whitelist === undefined ) {
whitelist = [ ] ;
}
2017-12-10 21:51:33 +01:00
return this . applyWithFunction ( sourceFile , walk , {
dev : this . ruleArguments . indexOf ( OPTION _DEV ) !== - 1 ,
optional : this . ruleArguments . indexOf ( OPTION _OPTIONAL ) !== - 1 ,
2018-09-20 02:56:13 +02:00
whitelist : whitelist ,
2017-12-10 21:51:33 +01:00
} ) ;
} ;
/* tslint:disable:object-literal-sort-keys */
Rule . metadata = {
ruleName : "no-implicit-dependencies" ,
description : "Disallows importing modules that are not listed as dependency in the project's package.json" ,
2018-09-20 02:56:13 +02:00
descriptionDetails : Lint . Utils . dedent ( templateObject _1 || ( templateObject _1 = tslib _1 . _ _makeTemplateObject ( [ "\n Disallows importing transient dependencies and modules installed above your package's root directory.\n " ] , [ "\n Disallows importing transient dependencies and modules installed above your package's root directory.\n " ] ) ) ) ,
optionsDescription : Lint . Utils . dedent ( templateObject _2 || ( templateObject _2 = tslib _1 . _ _makeTemplateObject ( [ "\n By default the rule looks at `\"dependencies\"` and `\"peerDependencies\"`.\n By adding the `\"" , "\"` option the rule also looks at `\"devDependencies\"`.\n By adding the `\"" , "\"` option the rule also looks at `\"optionalDependencies\"`.\n An array of whitelisted modules can be added to skip checking their existence in package.json.\n " ] , [ "\n By default the rule looks at \\`\"dependencies\"\\` and \\`\"peerDependencies\"\\`.\n By adding the \\`\"" , "\"\\` option the rule also looks at \\`\"devDependencies\"\\`.\n By adding the \\`\"" , "\"\\` option the rule also looks at \\`\"optionalDependencies\"\\`.\n An array of whitelisted modules can be added to skip checking their existence in package.json.\n " ] ) ) , OPTION _DEV , OPTION _OPTIONAL ) ,
2017-12-10 21:51:33 +01:00
options : {
type : "array" ,
2018-09-20 02:56:13 +02:00
items : [
{
type : "string" ,
enum : [ OPTION _DEV , OPTION _OPTIONAL ] ,
} ,
{
type : "array" ,
} ,
] ,
2017-12-10 21:51:33 +01:00
minItems : 0 ,
2018-09-20 02:56:13 +02:00
maxItems : 3 ,
2017-12-10 21:51:33 +01:00
} ,
2018-09-20 02:56:13 +02:00
optionExamples : [
true ,
[ true , OPTION _DEV ] ,
[ true , OPTION _OPTIONAL ] ,
[ true , [ "src" , "app" ] ] ,
] ,
2017-12-10 21:51:33 +01:00
type : "functionality" ,
typescriptOnly : false ,
} ;
return Rule ;
} ( Lint . Rules . AbstractRule ) ) ;
exports . Rule = Rule ;
function walk ( ctx ) {
var options = ctx . options ;
var dependencies ;
2018-09-20 02:56:13 +02:00
var whitelist = new Set ( options . whitelist ) ;
for ( var _i = 0 , _a = tsutils _1 . findImports ( ctx . sourceFile , 63 /* All */ ) ; _i < _a . length ; _i ++ ) {
2017-12-10 21:51:33 +01:00
var name = _a [ _i ] ;
if ( ! ts . isExternalModuleNameRelative ( name . text ) ) {
var packageName = getPackageName ( name . text ) ;
2018-09-20 02:56:13 +02:00
if ( ! whitelist . has ( packageName ) && builtins . indexOf ( packageName ) === - 1 && ! hasDependency ( packageName ) ) {
2017-12-10 21:51:33 +01:00
ctx . addFailureAtNode ( name , Rule . FAILURE _STRING _FACTORY ( packageName ) ) ;
}
}
}
function hasDependency ( module ) {
if ( dependencies === undefined ) {
dependencies = getDependencies ( ctx . sourceFile . fileName , options ) ;
}
return dependencies . has ( module ) ;
}
}
function getPackageName ( name ) {
var parts = name . split ( /\//g ) ;
if ( name [ 0 ] !== "@" ) {
return parts [ 0 ] ;
}
return parts [ 0 ] + "/" + parts [ 1 ] ;
}
function getDependencies ( fileName , options ) {
var result = new Set ( ) ;
var packageJsonPath = findPackageJson ( path . resolve ( path . dirname ( fileName ) ) ) ;
if ( packageJsonPath !== undefined ) {
2018-09-20 02:56:13 +02:00
try {
// don't use require here to avoid caching
// remove BOM from file content before parsing
var content = JSON . parse ( fs . readFileSync ( packageJsonPath , "utf8" ) . replace ( /^\uFEFF/ , "" ) ) ;
if ( content . dependencies !== undefined ) {
addDependencies ( result , content . dependencies ) ;
}
if ( content . peerDependencies !== undefined ) {
addDependencies ( result , content . peerDependencies ) ;
}
if ( options . dev && content . devDependencies !== undefined ) {
addDependencies ( result , content . devDependencies ) ;
}
if ( options . optional && content . optionalDependencies !== undefined ) {
addDependencies ( result , content . optionalDependencies ) ;
}
2017-12-10 21:51:33 +01:00
}
2018-09-20 02:56:13 +02:00
catch ( _a ) {
// treat malformed package.json files as empty
2017-12-10 21:51:33 +01:00
}
}
return result ;
}
function addDependencies ( result , dependencies ) {
for ( var name in dependencies ) {
if ( dependencies . hasOwnProperty ( name ) ) {
result . add ( name ) ;
}
}
}
function findPackageJson ( current ) {
var prev ;
do {
var fileName = path . join ( current , "package.json" ) ;
if ( fs . existsSync ( fileName ) ) {
return fileName ;
}
prev = current ;
current = path . dirname ( current ) ;
} while ( prev !== current ) ;
return undefined ;
}
2018-09-20 02:56:13 +02:00
var templateObject _1 , templateObject _2 ;