UNPKG

webpack-dependency-suite

Version:

A set of Webpack plugins, loaders and utilities designed for advanced dependency resolution

122 lines (105 loc) 5.28 kB
import { AddLoadersOptions, RequireData, RequireDataBase, RequireDataBaseMaybeResolved, RequireDataBaseResolved, ListBasedRequireOptions } from '../typings/definitions' import { addBundleLoader, appendCodeAndCallback, expandAllRequiresForGlob, getRequireStrings, resolveLiteral, wrapInRequireInclude } from '../utils/inject' import * as SourceMap from 'source-map' import * as loaderUtils from 'loader-utils' import { concatPromiseResults, getResourcesFromList } from '../utils' import * as path from 'path' import * as debug from 'debug' const log = debug('list-based-require-loader') export default async function ListBasedRequireLoader (this: Webpack.Core.LoaderContext, source: string, sourceMap?: SourceMap.RawSourceMap) { this.async() // add defaults: const query = Object.assign({ requireInFirstFileOnly: true, enableGlobbing: false }, loaderUtils.parseQuery(this.query)) as ListBasedRequireOptions if (this.cacheable) { this.cacheable() } /** * 1. resolve SELF to get the package.json contents * 2. _.get to the object containing resource info * 3. include */ try { const self = await resolveLiteral({ literal: this.resourcePath }, this) const resolve = self.resolve // only do require.include in the FIRST file that comes along, when that option is enabled const listBasedRequireDone: Set<string> = this._compilation.listBasedRequireDone || (this._compilation.listBasedRequireDone = new Set<string>()) if (!resolve || (query.requireInFirstFileOnly && listBasedRequireDone.has(resolve.descriptionFileRoot))) { return this.callback(undefined, source, sourceMap) } else if (query.requireInFirstFileOnly) { listBasedRequireDone.add(resolve.descriptionFileRoot) } const resources = resolve ? getResourcesFromList(resolve.descriptionFileData, query.packagePropertyPath) : [] if (!resources.length) { return this.callback(undefined, source, sourceMap) } let resourceData = await addBundleLoader(resources, 'loaders') const isRootRequest = query.rootDir === resolve.descriptionFileRoot if (query.enableGlobbing) { resourceData = await expandAllRequiresForGlob(resourceData, this, isRootRequest ? false : resolve.descriptionFileRoot) } else { resourceData = resourceData.filter(r => !r.literal.includes(`*`)) } // log(`resourceData for ${this.resourcePath}`, resourceData.map(r => r.literal)) const resolvedResources = (await Promise.all( resourceData.map(async r => { let resource: RequireDataBaseMaybeResolved | null = null const packageName = resolve.descriptionFileData && resolve.descriptionFileData.name const tryContexts = [resolve.descriptionFileRoot, ...(query.fallbackToMainContext ? [query.rootDir] : [])] let contextDir: string | undefined let tryCount = 0 const isSameModuleRequest = packageName && (r.literal.startsWith(`${packageName}/`) || r.literal === packageName) while ((!resource || !resource.resolve) && (contextDir = tryContexts.shift())) { if (!isSameModuleRequest && packageName && !path.isAbsolute(r.literal) && !isRootRequest) { const literal = `${packageName}/${r.literal}` // resolve as MODULE_NAME/REQUEST_PATH resource = await resolveLiteral(Object.assign({}, r, { literal }), this, contextDir, false) log(`[${resource && resource.resolve ? 'SUCCESS' : 'FAIL'}] [${++tryCount}] '${literal}' in '${contextDir}'`) } if (!resource || !resource.resolve) { // resolve as REQUEST_PATH resource = await resolveLiteral(r, this, contextDir, false) as RequireDataBaseMaybeResolved log(`[${resource && resource.resolve ? 'SUCCESS' : 'FAIL'}] [${++tryCount}] '${r.literal}' in '${contextDir}'`) } } if (!resource || !resource.resolve) { return this.emitWarning(`Unable to resolve ${r.literal} in context of ${packageName}`) } if (!resource.literal.startsWith('.') && (resource.resolve.descriptionFileData && resource.resolve.descriptionFileData.name) === packageName) { // we're dealing with a request from within the same package // let's make sure its relative: let relativeLiteral = path.relative(path.dirname(resolve.path), resource.resolve.path) if (!relativeLiteral.startsWith('..')) { relativeLiteral = `./${relativeLiteral}` } log(`Mapped an internal module-based literal to a relative one: ${resource.literal} => ${relativeLiteral}`) resource.literal = relativeLiteral } return resource as RequireData }) )).filter(r => !!r && r.resolve.path !== this.resourcePath) as Array<RequireData> log(`Adding resources to ${this.resourcePath}: ${resolvedResources.map(r => r.literal).join(', ')}`) let requireStrings = await getRequireStrings(resolvedResources, query.addLoadersCallback, this) const inject = requireStrings.map(wrapInRequireInclude).join('\n') appendCodeAndCallback(this, source, inject, sourceMap) } catch (e) { log(e) this.emitError(e.message) this.callback(undefined, source, sourceMap) } }