|
| 1 | +import fs from 'fs'; |
| 2 | +import path from 'path'; |
| 3 | +import { Resolver } from 'webpack'; |
| 4 | + |
| 5 | +/** |
| 6 | + * A webpack resolver plugin that resolves `@src` imports to the closest |
| 7 | + * `src` directory by walking up from the importing file's location. |
| 8 | + * |
| 9 | + * This allows apps to have their own `src` directories, with `@src` always |
| 10 | + * resolving to the nearest one relative to the file doing the import. |
| 11 | + */ |
| 12 | +class ClosestSrcResolverPlugin { |
| 13 | + apply(resolver: Resolver) { |
| 14 | + const target = resolver.ensureHook('resolve'); |
| 15 | + |
| 16 | + resolver.getHook('resolve').tapAsync( |
| 17 | + 'ClosestSrcResolverPlugin', |
| 18 | + (request: any, resolveContext: any, callback: (err?: null | Error, result?: any) => void) => { |
| 19 | + if (!request.request?.startsWith('@src')) { |
| 20 | + return callback(); |
| 21 | + } |
| 22 | + |
| 23 | + // Get the directory of the file doing the import |
| 24 | + const issuer = request.context?.issuer; |
| 25 | + if (!issuer) { |
| 26 | + return callback(); |
| 27 | + } |
| 28 | + |
| 29 | + // Walk up from the issuer to find closest 'src' directory, |
| 30 | + // but don't go above the current working directory |
| 31 | + const cwd = process.cwd(); |
| 32 | + let dir = path.dirname(issuer); |
| 33 | + let srcPath: string | null = null; |
| 34 | + |
| 35 | + while (dir.startsWith(cwd) && dir !== path.parse(dir).root) { |
| 36 | + const candidate = path.join(dir, 'src'); |
| 37 | + if (fs.existsSync(candidate) && fs.statSync(candidate).isDirectory()) { |
| 38 | + srcPath = candidate; |
| 39 | + break; |
| 40 | + } |
| 41 | + dir = path.dirname(dir); |
| 42 | + } |
| 43 | + |
| 44 | + if (!srcPath) { |
| 45 | + return callback(); |
| 46 | + } |
| 47 | + |
| 48 | + // Replace @src with the actual path |
| 49 | + const newRequest = request.request.replace(/^@src/, srcPath); |
| 50 | + |
| 51 | + const obj = { |
| 52 | + ...request, |
| 53 | + request: newRequest, |
| 54 | + }; |
| 55 | + |
| 56 | + resolver.doResolve(target, obj, null, resolveContext, callback); |
| 57 | + } |
| 58 | + ); |
| 59 | + } |
| 60 | +} |
| 61 | + |
| 62 | +export default ClosestSrcResolverPlugin; |
0 commit comments