eslint-module-utils
Advanced tools
Comparing version 0.2.2 to 1.0.0-beta.0
@@ -52,3 +52,3 @@ /** | ||
}) | ||
hash.update("}") | ||
hash.update('}') | ||
@@ -55,0 +55,0 @@ return hash |
"use strict" | ||
exports.__esModule = true | ||
const extname = require('path').extname | ||
const log = require('debug')('eslint-plugin-import:utils:ignore') | ||
// one-shot memoized | ||
let cachedSet, lastSettings | ||
function validExtensions(context) { | ||
if (cachedSet && context.settings === lastSettings) { | ||
return cachedSet | ||
} | ||
lastSettings = context.settings | ||
cachedSet = makeValidExtensionSet(context.settings) | ||
return cachedSet | ||
} | ||
function makeValidExtensionSet(settings) { | ||
// start with explicit JS-parsed extensions | ||
const exts = new Set(settings['import/extensions'] || [ '.js' ]) | ||
// all alternate parser extensions are also valid | ||
if ('import/parsers' in settings) { | ||
for (let parser in settings['import/parsers']) { | ||
settings['import/parsers'][parser] | ||
.forEach(ext => exts.add(ext)) | ||
} | ||
} | ||
return exts | ||
} | ||
exports.default = function ignore(path, context) { | ||
// ignore node_modules by default | ||
// check extension whitelist first (cheap) | ||
if (!hasValidExtension(path, context)) return true | ||
if (!('import/ignore' in context.settings)) return false | ||
const ignoreStrings = context.settings['import/ignore'] | ||
? [].concat(context.settings['import/ignore']) | ||
: ['node_modules'] | ||
if (ignoreStrings.length === 0) return false | ||
for (let i = 0; i < ignoreStrings.length; i++) { | ||
const regex = new RegExp(ignoreStrings[i]) | ||
if (regex.test(path)) return true | ||
if (regex.test(path)) { | ||
log(`ignoring ${path}, matched pattern /${ignoreStrings[i]}/`) | ||
return true | ||
} | ||
} | ||
@@ -19,1 +52,6 @@ | ||
} | ||
function hasValidExtension(path, context) { | ||
return validExtensions(context).has(extname(path)) | ||
} | ||
exports.hasValidExtension = hasValidExtension |
"use strict" | ||
exports.__esModule = true | ||
const log = require('debug')('eslint-module-utils:ModuleCache') | ||
class ModuleCache { | ||
@@ -16,2 +18,3 @@ constructor(map) { | ||
this.map.set(cacheKey, { result, lastSeen: Date.now() }) | ||
log('setting entry for', cacheKey) | ||
return result | ||
@@ -25,3 +28,3 @@ } | ||
if (Date.now() - f.lastSeen < (settings.lifetime * 1000)) return f.result | ||
} | ||
} else log('cache miss for', cacheKey) | ||
// cache miss | ||
@@ -28,0 +31,0 @@ return undefined |
@@ -91,2 +91,36 @@ "use strict" | ||
/** | ||
* make an options schema for the module visitor, optionally | ||
* adding extra fields. | ||
* @param {[type]} additionalProperties [description] | ||
* @return {[type]} [description] | ||
*/ | ||
function makeOptionsSchema(additionalProperties) { | ||
const base = { | ||
'type': 'object', | ||
'properties': { | ||
'commonjs': { 'type': 'boolean' }, | ||
'amd': { 'type': 'boolean' }, | ||
'esmodule': { 'type': 'boolean' }, | ||
'ignore': { | ||
'type': 'array', | ||
'minItems': 1, | ||
'items': { 'type': 'string' }, | ||
'uniqueItems': true, | ||
}, | ||
}, | ||
'additionalProperties': false, | ||
} | ||
if (additionalProperties){ | ||
for (let key in additionalProperties) { | ||
base.properties[key] = additionalProperties[key] | ||
} | ||
} | ||
return base | ||
} | ||
exports.makeOptionsSchema = makeOptionsSchema | ||
/** | ||
* json schema object for options parameter. can be used to build | ||
@@ -96,16 +130,2 @@ * rule options schema object. | ||
*/ | ||
exports.optionsSchema = { | ||
'type': 'object', | ||
'properties': { | ||
'commonjs': { 'type': 'boolean' }, | ||
'amd': { 'type': 'boolean' }, | ||
'esmodule': { 'type': 'boolean' }, | ||
'ignore': { | ||
'type': 'array', | ||
'minItems': 1, | ||
'items': { 'type': 'string' }, | ||
'uniqueItems': true, | ||
}, | ||
}, | ||
'additionalProperties': false, | ||
} | ||
exports.optionsSchema = makeOptionsSchema() |
{ | ||
"name": "eslint-module-utils", | ||
"engines": { "node": ">=4" }, | ||
"version": "0.2.2", | ||
"version": "1.0.0-beta.0", | ||
"description": "Core utilities to support eslint-plugin-import and other module-related plugins.", | ||
"engines": { | ||
"node": ">=4" | ||
}, | ||
"scripts": { | ||
@@ -24,3 +26,7 @@ "test": "echo \"Error: no test specified\" && exit 1" | ||
}, | ||
"homepage": "https://github.com/benmosher/eslint-plugin-import#readme" | ||
"homepage": "https://github.com/benmosher/eslint-plugin-import#readme", | ||
"dependencies": { | ||
"debug": "2.2.0", | ||
"pkg-dir": "^1.0.0" | ||
} | ||
} |
27
parse.js
@@ -5,11 +5,14 @@ "use strict" | ||
const moduleRequire = require('./module-require').default | ||
const extname = require('path').extname | ||
exports.default = function parse(content, context) { | ||
const log = require('debug')('eslint-plugin-import:parse') | ||
if (context == null) throw new Error("need context to parse properly") | ||
exports.default = function parse(path, content, context) { | ||
if (context == null) throw new Error('need context to parse properly') | ||
let parserOptions = context.parserOptions | ||
, parserPath = context.parserPath | ||
const parserPath = getParserPath(path, context) | ||
if (!parserPath) throw new Error("parserPath is required!") | ||
if (!parserPath) throw new Error('parserPath is required!') | ||
@@ -28,1 +31,17 @@ // hack: espree blows up with frozen options | ||
} | ||
function getParserPath(path, context) { | ||
const parsers = context.settings['import/parsers'] | ||
if (parsers != null) { | ||
const extension = extname(path) | ||
for (let parserPath in parsers) { | ||
if (parsers[parserPath].indexOf(extension) > -1) { | ||
// use this alternate parser | ||
log('using alt parser:', parserPath) | ||
return parserPath | ||
} | ||
} | ||
} | ||
// default to use ESLint parser | ||
return context.parserPath | ||
} |
"use strict" | ||
exports.__esModule = true | ||
const pkgDir = require('pkg-dir') | ||
const fs = require('fs') | ||
@@ -10,4 +12,4 @@ const path = require('path') | ||
const CASE_INSENSITIVE = fs.existsSync(path.join(__dirname, 'reSOLVE.js')) | ||
exports.CASE_INSENSITIVE = CASE_INSENSITIVE | ||
const CASE_SENSITIVE_FS = !fs.existsSync(path.join(__dirname, 'reSOLVE.js')) | ||
exports.CASE_SENSITIVE_FS = CASE_SENSITIVE_FS | ||
@@ -17,5 +19,11 @@ const fileExistsCache = new ModuleCache() | ||
// http://stackoverflow.com/a/27382838 | ||
function fileExistsWithCaseSync(filepath, cacheSettings) { | ||
const dir = path.dirname(filepath) | ||
exports.fileExistsWithCaseSync = function fileExistsWithCaseSync(filepath, cacheSettings) { | ||
// don't care if the FS is case-sensitive | ||
if (CASE_SENSITIVE_FS) return true | ||
// null means it resolved to a builtin | ||
if (filepath === null) return true | ||
const parsedPath = path.parse(filepath) | ||
, dir = parsedPath.dir | ||
let result = fileExistsCache.get(filepath, cacheSettings) | ||
@@ -25,7 +33,7 @@ if (result != null) return result | ||
// base case | ||
if (dir === '/' || dir === '.' || /^[A-Z]:\\$/i.test(dir)) { | ||
if (dir === '' || parsedPath.root === filepath) { | ||
result = true | ||
} else { | ||
const filenames = fs.readdirSync(dir) | ||
if (filenames.indexOf(path.basename(filepath)) === -1) { | ||
if (filenames.indexOf(parsedPath.base) === -1) { | ||
result = false | ||
@@ -41,3 +49,10 @@ } else { | ||
function relative(modulePath, sourceFile, settings) { | ||
return fullResolve(modulePath, sourceFile, settings).path | ||
} | ||
function fullResolve(modulePath, sourceFile, settings) { | ||
// check if this is a bonus core module | ||
const coreSet = new Set(settings['import/core-modules']) | ||
if (coreSet != null && coreSet.has(modulePath)) return { found: true, path: null } | ||
const sourceDir = path.dirname(sourceFile) | ||
@@ -49,7 +64,6 @@ , cacheKey = sourceDir + hashObject(settings).digest('hex') + modulePath | ||
const cachedPath = fileExistsCache.get(cacheKey, cacheSettings) | ||
if (cachedPath !== undefined) return cachedPath | ||
if (cachedPath !== undefined) return { found: true, path: cachedPath } | ||
function cache(p) { | ||
fileExistsCache.set(cacheKey, p) | ||
return p | ||
function cache(resolvedPath) { | ||
fileExistsCache.set(cacheKey, resolvedPath) | ||
} | ||
@@ -61,5 +75,5 @@ | ||
try { | ||
const path = resolver.resolveImport(modulePath, sourceFile, config) | ||
if (path === undefined) return { found: false } | ||
return { found: true, path } | ||
const resolved = resolver.resolveImport(modulePath, sourceFile, config) | ||
if (resolved === undefined) return { found: false } | ||
return { found: true, path: resolved } | ||
} catch (err) { | ||
@@ -92,18 +106,15 @@ return { found: false } | ||
, config = pair[1] | ||
const resolver = requireResolver(name, sourceFile) | ||
, resolved = withResolver(resolver, config) | ||
const resolver = requireResolver(name) | ||
if (!resolved.found) continue | ||
const resolved = withResolver(resolver, config) | ||
let resolvedPath = resolved.path | ||
// resolvers imply file existence, this double-check just ensures the case matches | ||
if (resolved.found && CASE_INSENSITIVE && !fileExistsWithCaseSync(resolvedPath, cacheSettings)) { | ||
// reject resolved path | ||
resolvedPath = undefined | ||
} | ||
if (resolved.found) return cache(resolvedPath) | ||
// else, counts | ||
cache(resolved.path) | ||
return resolved | ||
} | ||
return cache(undefined) | ||
// failed | ||
// cache(undefined) | ||
return { found: false } | ||
} | ||
@@ -133,8 +144,23 @@ exports.relative = relative | ||
function requireResolver(name) { | ||
function requireResolver(name, sourceFile) { | ||
// Try to resolve package with conventional name | ||
try { | ||
return require(`eslint-import-resolver-${name}`) | ||
} catch (err) { | ||
throw new Error(`unable to load resolver "${name}".`) | ||
} | ||
} catch (err) { /* continue */ } | ||
// Try to resolve package with custom name (@myorg/resolver-name) | ||
try { | ||
return require(name) | ||
} catch (err) { /* continue */ } | ||
// Try to resolve package with path, relative to closest package.json | ||
// or current working directory | ||
try { | ||
const baseDir = pkgDir.sync(sourceFile) || process.cwd() | ||
// absolute paths ignore base, so this covers both | ||
return require(path.resolve(baseDir, name)) | ||
} catch (err) { /* continue */ } | ||
// all else failed | ||
throw new Error(`unable to load resolver "${name}".`) | ||
} | ||
@@ -162,3 +188,3 @@ | ||
message: `Resolve error: ${err.message}`, | ||
loc: { line: 1, col: 0 }, | ||
loc: { line: 1, column: 0 }, | ||
}) | ||
@@ -165,0 +191,0 @@ erroredContexts.add(context) |
Dynamic require
Supply chain riskDynamic require can indicate the package is performing dangerous or unsafe dynamic code execution.
Found 1 instance in 1 package
16348
10
484
2
7
+ Addeddebug@2.2.0
+ Addedpkg-dir@^1.0.0
+ Addeddebug@2.2.0(transitive)
+ Addedfind-up@1.1.2(transitive)
+ Addedms@0.7.1(transitive)
+ Addedpath-exists@2.1.0(transitive)
+ Addedpinkie@2.0.4(transitive)
+ Addedpinkie-promise@2.0.1(transitive)
+ Addedpkg-dir@1.0.0(transitive)