1 |
|
2 |
|
3 | import type {AssetRequestDesc, Dependency, ParcelOptions} from './types';
|
4 | import type {Diagnostic} from '@parcel/diagnostic';
|
5 | import type ParcelConfig from './ParcelConfig';
|
6 |
|
7 | import {PluginLogger} from '@parcel/logger';
|
8 | import ThrowableDiagnostic, {errorToDiagnostic} from '@parcel/diagnostic';
|
9 | import path from 'path';
|
10 | import URL from 'url';
|
11 |
|
12 | import {report} from './ReporterRunner';
|
13 | import PublicDependency from './public/Dependency';
|
14 | import PluginOptions from './public/PluginOptions';
|
15 |
|
16 | type Opts = {|
|
17 | config: ParcelConfig,
|
18 | options: ParcelOptions,
|
19 | |};
|
20 |
|
21 | export 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 |
|
69 |
|
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 |
|
77 |
|
78 | return null;
|
79 | } else {
|
80 | throw new Error(`Unknown pipeline ${pipeline}.`);
|
81 | }
|
82 | }
|
83 | } else {
|
84 | if (dependency.isURL && dependency.moduleSpecifier.startsWith('//')) {
|
85 |
|
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 | }
|