123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413 |
- var findRoot = require('find-root')
- , path = require('path')
- , get = require('lodash/get')
- , isEqual = require('lodash/isEqual')
- , find = require('array-find')
- , interpret = require('interpret')
- , fs = require('fs')
- , coreLibs = require('node-libs-browser')
- , resolve = require('resolve')
- , semver = require('semver')
- , has = require('has')
- var log = require('debug')('eslint-plugin-import:resolver:webpack')
- exports.interfaceVersion = 2
- /**
- * Find the full path to 'source', given 'file' as a full reference path.
- *
- * resolveImport('./foo', '/Users/ben/bar.js') => '/Users/ben/foo.js'
- * @param {string} source - the module to resolve; i.e './some-module'
- * @param {string} file - the importing file's full path; i.e. '/usr/local/bin/file.js'
- * @param {object} settings - the webpack config file name, as well as cwd
- * @example
- * options: {
- * // Path to the webpack config
- * config: 'webpack.config.js',
- * // Path to be used to determine where to resolve webpack from
- * // (may differ from the cwd in some cases)
- * cwd: process.cwd()
- * }
- * @return {string?} the resolved path to source, undefined if not resolved, or null
- * if resolved to a non-FS resource (i.e. script tag at page load)
- */
- exports.resolve = function (source, file, settings) {
- // strip loaders
- var finalBang = source.lastIndexOf('!')
- if (finalBang >= 0) {
- source = source.slice(finalBang + 1)
- }
- // strip resource query
- var finalQuestionMark = source.lastIndexOf('?')
- if (finalQuestionMark >= 0) {
- source = source.slice(0, finalQuestionMark)
- }
- var webpackConfig
- var configPath = get(settings, 'config')
- /**
- * Attempt to set the current working directory.
- * If none is passed, default to the `cwd` where the config is located.
- */
- , cwd = get(settings, 'cwd')
- , configIndex = get(settings, 'config-index')
- , env = get(settings, 'env')
- , argv = get(settings, 'argv', {})
- , packageDir
- log('Config path from settings:', configPath)
- // see if we've got a config path, a config object, an array of config objects or a config function
- if (!configPath || typeof configPath === 'string') {
- // see if we've got an absolute path
- if (!configPath || !path.isAbsolute(configPath)) {
- // if not, find ancestral package.json and use its directory as base for the path
- packageDir = findRoot(path.resolve(file))
- if (!packageDir) throw new Error('package not found above ' + file)
- }
- configPath = findConfigPath(configPath, packageDir)
- log('Config path resolved to:', configPath)
- if (configPath) {
- try {
- webpackConfig = require(configPath)
- } catch(e) {
- console.log('Error resolving webpackConfig', e)
- throw e
- }
- } else {
- log('No config path found relative to', file, '; using {}')
- webpackConfig = {}
- }
- if (webpackConfig && webpackConfig.default) {
- log('Using ES6 module "default" key instead of module.exports.')
- webpackConfig = webpackConfig.default
- }
- } else {
- webpackConfig = configPath
- configPath = null
- }
- if (typeof webpackConfig === 'function') {
- webpackConfig = webpackConfig(env, argv)
- }
- if (Array.isArray(webpackConfig)) {
- webpackConfig = webpackConfig.map(cfg => {
- if (typeof cfg === 'function') {
- return cfg(env, argv)
- }
- return cfg
- })
- if (typeof configIndex !== 'undefined' && webpackConfig.length > configIndex) {
- webpackConfig = webpackConfig[configIndex]
- }
- else {
- webpackConfig = find(webpackConfig, function findFirstWithResolve(config) {
- return !!config.resolve
- })
- }
- }
- if (webpackConfig == null) {
- webpackConfig = {}
- console.warn('No webpack configuration with a "resolve" field found. Using empty object instead')
- }
- log('Using config: ', webpackConfig)
- // externals
- if (findExternal(source, webpackConfig.externals, path.dirname(file))) {
- return { found: true, path: null }
- }
- // otherwise, resolve "normally"
- var resolveSync = getResolveSync(configPath, webpackConfig, cwd)
- try {
- return { found: true, path: resolveSync(path.dirname(file), source) }
- } catch (err) {
- if (source in coreLibs) {
- return { found: true, path: coreLibs[source] }
- }
- log('Error during module resolution:', err)
- return { found: false }
- }
- }
- var MAX_CACHE = 10
- var _cache = []
- function getResolveSync(configPath, webpackConfig, cwd) {
- var cacheKey = { configPath: configPath, webpackConfig: webpackConfig }
- var cached = find(_cache, function (entry) { return isEqual(entry.key, cacheKey) })
- if (!cached) {
- cached = {
- key: cacheKey,
- value: createResolveSync(configPath, webpackConfig, cwd),
- }
- // put in front and pop last item
- if (_cache.unshift(cached) > MAX_CACHE) {
- _cache.pop()
- }
- }
- return cached.value
- }
- function createResolveSync(configPath, webpackConfig, cwd) {
- var webpackRequire
- , basedir = null
- if (typeof configPath === 'string') {
- // This can be changed via the settings passed in when defining the resolver
- basedir = cwd || configPath
- log(`Attempting to load webpack path from ${basedir}`)
- }
- try {
- // Attempt to resolve webpack from the given `basedir`
- var webpackFilename = resolve.sync('webpack', { basedir, preserveSymlinks: false })
- var webpackResolveOpts = { basedir: path.dirname(webpackFilename), preserveSymlinks: false }
- webpackRequire = function (id) {
- return require(resolve.sync(id, webpackResolveOpts))
- }
- } catch (e) {
- // Something has gone wrong (or we're in a test). Use our own bundled
- // enhanced-resolve.
- log('Using bundled enhanced-resolve.')
- webpackRequire = require
- }
- var enhancedResolvePackage = webpackRequire('enhanced-resolve/package.json')
- var enhancedResolveVersion = enhancedResolvePackage.version
- log('enhanced-resolve version:', enhancedResolveVersion)
- var resolveConfig = webpackConfig.resolve || {}
- if (semver.major(enhancedResolveVersion) >= 2) {
- return createWebpack2ResolveSync(webpackRequire, resolveConfig)
- }
- return createWebpack1ResolveSync(webpackRequire, resolveConfig, webpackConfig.plugins)
- }
- function createWebpack2ResolveSync(webpackRequire, resolveConfig) {
- var EnhancedResolve = webpackRequire('enhanced-resolve')
- return EnhancedResolve.create.sync(Object.assign({}, webpack2DefaultResolveConfig, resolveConfig))
- }
- /**
- * webpack 2 defaults:
- * https://github.com/webpack/webpack/blob/v2.1.0-beta.20/lib/WebpackOptionsDefaulter.js#L72-L87
- * @type {Object}
- */
- var webpack2DefaultResolveConfig = {
- unsafeCache: true, // Probably a no-op, since how can we cache anything at all here?
- modules: ['node_modules'],
- extensions: ['.js', '.json'],
- aliasFields: ['browser'],
- mainFields: ['browser', 'module', 'main'],
- }
- // adapted from tests &
- // https://github.com/webpack/webpack/blob/v1.13.0/lib/WebpackOptionsApply.js#L322
- function createWebpack1ResolveSync(webpackRequire, resolveConfig, plugins) {
- var Resolver = webpackRequire('enhanced-resolve/lib/Resolver')
- var SyncNodeJsInputFileSystem = webpackRequire('enhanced-resolve/lib/SyncNodeJsInputFileSystem')
- var ModuleAliasPlugin = webpackRequire('enhanced-resolve/lib/ModuleAliasPlugin')
- var ModulesInDirectoriesPlugin =
- webpackRequire('enhanced-resolve/lib/ModulesInDirectoriesPlugin')
- var ModulesInRootPlugin = webpackRequire('enhanced-resolve/lib/ModulesInRootPlugin')
- var ModuleAsFilePlugin = webpackRequire('enhanced-resolve/lib/ModuleAsFilePlugin')
- var ModuleAsDirectoryPlugin = webpackRequire('enhanced-resolve/lib/ModuleAsDirectoryPlugin')
- var DirectoryDescriptionFilePlugin =
- webpackRequire('enhanced-resolve/lib/DirectoryDescriptionFilePlugin')
- var DirectoryDefaultFilePlugin =
- webpackRequire('enhanced-resolve/lib/DirectoryDefaultFilePlugin')
- var FileAppendPlugin = webpackRequire('enhanced-resolve/lib/FileAppendPlugin')
- var ResultSymlinkPlugin = webpackRequire('enhanced-resolve/lib/ResultSymlinkPlugin')
- var DirectoryDescriptionFileFieldAliasPlugin =
- webpackRequire('enhanced-resolve/lib/DirectoryDescriptionFileFieldAliasPlugin')
- var resolver = new Resolver(new SyncNodeJsInputFileSystem())
- resolver.apply(
- resolveConfig.packageAlias
- ? new DirectoryDescriptionFileFieldAliasPlugin('package.json', resolveConfig.packageAlias)
- : function() {},
- new ModuleAliasPlugin(resolveConfig.alias || {}),
- makeRootPlugin(ModulesInRootPlugin, 'module', resolveConfig.root),
- new ModulesInDirectoriesPlugin(
- 'module',
- resolveConfig.modulesDirectories || resolveConfig.modules || ['web_modules', 'node_modules']
- ),
- makeRootPlugin(ModulesInRootPlugin, 'module', resolveConfig.fallback),
- new ModuleAsFilePlugin('module'),
- new ModuleAsDirectoryPlugin('module'),
- new DirectoryDescriptionFilePlugin(
- 'package.json',
- ['module', 'jsnext:main'].concat(resolveConfig.packageMains || webpack1DefaultMains)
- ),
- new DirectoryDefaultFilePlugin(['index']),
- new FileAppendPlugin(resolveConfig.extensions || ['', '.webpack.js', '.web.js', '.js']),
- new ResultSymlinkPlugin()
- )
- var resolvePlugins = []
- // support webpack.ResolverPlugin
- if (plugins) {
- plugins.forEach(function (plugin) {
- if (
- plugin.constructor &&
- plugin.constructor.name === 'ResolverPlugin' &&
- Array.isArray(plugin.plugins)
- ) {
- resolvePlugins.push.apply(resolvePlugins, plugin.plugins)
- }
- })
- }
- resolver.apply.apply(resolver, resolvePlugins)
- return function() {
- return resolver.resolveSync.apply(resolver, arguments)
- }
- }
- /* eslint-disable */
- // from https://github.com/webpack/webpack/blob/v1.13.0/lib/WebpackOptionsApply.js#L365
- function makeRootPlugin(ModulesInRootPlugin, name, root) {
- if(typeof root === "string")
- return new ModulesInRootPlugin(name, root);
- else if(Array.isArray(root)) {
- return function() {
- root.forEach(function(root) {
- this.apply(new ModulesInRootPlugin(name, root));
- }, this);
- };
- }
- return function() {};
- }
- /* eslint-enable */
- function findExternal(source, externals, context) {
- if (!externals) return false
- // string match
- if (typeof externals === 'string') return (source === externals)
- // array: recurse
- if (externals instanceof Array) {
- return externals.some(function (e) { return findExternal(source, e, context) })
- }
- if (externals instanceof RegExp) {
- return externals.test(source)
- }
- if (typeof externals === 'function') {
- var functionExternalFound = false
- externals.call(null, context, source, function(err, value) {
- if (err) {
- functionExternalFound = false
- } else {
- functionExternalFound = findExternal(source, value, context)
- }
- })
- return functionExternalFound
- }
- // else, vanilla object
- for (var key in externals) {
- if (!has(externals, key)) continue
- if (source === key) return true
- }
- return false
- }
- /**
- * webpack 1 defaults: http://webpack.github.io/docs/configuration.html#resolve-packagemains
- * @type {Array}
- */
- var webpack1DefaultMains = [
- 'webpack', 'browser', 'web', 'browserify', ['jam', 'main'], 'main',
- ]
- function findConfigPath(configPath, packageDir) {
- var extensions = Object.keys(interpret.extensions).sort(function(a, b) {
- return a === '.js' ? -1 : b === '.js' ? 1 : a.length - b.length
- })
- , extension
- if (configPath) {
- // extensions is not reused below, so safe to mutate it here.
- extensions.reverse()
- extensions.forEach(function (maybeExtension) {
- if (extension) {
- return
- }
- if (configPath.substr(-maybeExtension.length) === maybeExtension) {
- extension = maybeExtension
- }
- })
- // see if we've got an absolute path
- if (!path.isAbsolute(configPath)) {
- configPath = path.join(packageDir, configPath)
- }
- } else {
- extensions.forEach(function (maybeExtension) {
- if (extension) {
- return
- }
- var maybePath = path.resolve(
- path.join(packageDir, 'webpack.config' + maybeExtension)
- )
- if (fs.existsSync(maybePath)) {
- configPath = maybePath
- extension = maybeExtension
- }
- })
- }
- registerCompiler(interpret.extensions[extension])
- return configPath
- }
- function registerCompiler(moduleDescriptor) {
- if(moduleDescriptor) {
- if(typeof moduleDescriptor === 'string') {
- require(moduleDescriptor)
- } else if(!Array.isArray(moduleDescriptor)) {
- moduleDescriptor.register(require(moduleDescriptor.module))
- } else {
- for(var i = 0; i < moduleDescriptor.length; i++) {
- try {
- registerCompiler(moduleDescriptor[i])
- break
- } catch(e) {
- log('Failed to register compiler for moduleDescriptor[]:', i, moduleDescriptor)
- }
- }
- }
- }
- }
|