UNPKG

4.19 kBJavaScriptView Raw
1// @flow
2
3import type {AssetRequestDesc, Dependency, ParcelOptions} from './types';
4import type {Diagnostic} from '@parcel/diagnostic';
5import type ParcelConfig from './ParcelConfig';
6
7import {PluginLogger} from '@parcel/logger';
8import ThrowableDiagnostic, {errorToDiagnostic} from '@parcel/diagnostic';
9import path from 'path';
10import URL from 'url';
11
12import {report} from './ReporterRunner';
13import PublicDependency from './public/Dependency';
14import PluginOptions from './public/PluginOptions';
15
16type Opts = {|
17 config: ParcelConfig,
18 options: ParcelOptions,
19|};
20
21export default class ResolverRunner {
22 config: ParcelConfig;
23 options: ParcelOptions;
24 pluginOptions: PluginOptions;
25
26 constructor({config, options}: Opts) {
27 this.config = config;
28 this.options = options;
29 this.pluginOptions = new PluginOptions(this.options);
30 }
31
32 async getThrowableDiagnostic(dependency: Dependency, message: string) {
33 let diagnostic: Diagnostic = {
34 message,
35 origin: '@parcel/resolver-default',
36 };
37
38 if (dependency.loc && dependency.sourcePath) {
39 diagnostic.filePath = dependency.sourcePath;
40 diagnostic.codeFrame = {
41 code: await this.options.inputFS.readFile(
42 dependency.sourcePath,
43 'utf8',
44 ),
45 codeHighlights: dependency.loc
46 ? [{start: dependency.loc.start, end: dependency.loc.end}]
47 : [],
48 };
49 }
50
51 return new ThrowableDiagnostic({diagnostic});
52 }
53
54 async resolve(dependency: Dependency): Promise<?AssetRequestDesc> {
55 let dep = new PublicDependency(dependency);
56 report({
57 type: 'buildProgress',
58 phase: 'resolving',
59 dependency: dep,
60 });
61
62 let resolvers = await this.config.getResolvers();
63
64 let pipeline;
65 let filePath;
66 let validPipelines = new Set(this.config.getNamedPipelines());
67 if (
68 // Don't consider absolute paths. Absolute paths are only supported for entries,
69 // and include e.g. `C:\` on Windows, conflicting with pipelines.
70 !path.isAbsolute(dependency.moduleSpecifier) &&
71 dependency.moduleSpecifier.includes(':')
72 ) {
73 [pipeline, filePath] = dependency.moduleSpecifier.split(':');
74 if (!validPipelines.has(pipeline)) {
75 if (dep.isURL) {
76 // This may be a url protocol or scheme rather than a pipeline, such as
77 // `url('http://example.com/foo.png')`
78 return null;
79 } else {
80 throw new Error(`Unknown pipeline ${pipeline}.`);
81 }
82 }
83 } else {
84 if (dependency.isURL && dependency.moduleSpecifier.startsWith('//')) {
85 // A protocol-relative URL, e.g `url('//example.com/foo.png')`
86 return null;
87 }
88 filePath = dependency.moduleSpecifier;
89 }
90
91 if (dependency.isURL) {
92 let parsed = URL.parse(filePath);
93 if (typeof parsed.pathname !== 'string') {
94 throw new Error(`Received URL without a pathname ${filePath}.`);
95 }
96 filePath = decodeURIComponent(parsed.pathname);
97 if (!pipeline) {
98 pipeline = 'url';
99 }
100 }
101
102 for (let resolver of resolvers) {
103 try {
104 let result = await resolver.plugin.resolve({
105 filePath,
106 dependency: dep,
107 options: this.pluginOptions,
108 logger: new PluginLogger({origin: resolver.name}),
109 });
110
111 if (result && result.isExcluded) {
112 return null;
113 }
114
115 if (result && result.filePath) {
116 return {
117 filePath: result.filePath,
118 sideEffects: result.sideEffects,
119 code: result.code,
120 env: dependency.env,
121 pipeline: pipeline ?? dependency.pipeline,
122 };
123 }
124 } catch (e) {
125 throw new ThrowableDiagnostic({
126 diagnostic: errorToDiagnostic(e, resolver.name),
127 });
128 }
129 }
130
131 if (dep.isOptional) {
132 return null;
133 }
134
135 let dir = dependency.sourcePath
136 ? path.dirname(dependency.sourcePath)
137 : '<none>';
138
139 let err: any = await this.getThrowableDiagnostic(
140 dependency,
141 `Cannot find module '${dependency.moduleSpecifier}' from '${dir}'`,
142 );
143
144 err.code = 'MODULE_NOT_FOUND';
145
146 throw err;
147 }
148}