1 |
|
2 |
|
3 | import type {WorkerApi} from '@parcel/workers';
|
4 | import type {
|
5 | AssetRequestDesc,
|
6 | ConfigRequestDesc,
|
7 | ParcelOptions,
|
8 | ReportFn,
|
9 | } from './types';
|
10 |
|
11 | import path from 'path';
|
12 | import nullthrows from 'nullthrows';
|
13 | import {resolveConfig} from '@parcel/utils';
|
14 | import logger, {PluginLogger} from '@parcel/logger';
|
15 | import ThrowableDiagnostic, {errorToDiagnostic} from '@parcel/diagnostic';
|
16 | import ParcelConfig from './ParcelConfig';
|
17 | import ConfigLoader from './ConfigLoader';
|
18 | import InternalAsset, {createAsset} from './InternalAsset';
|
19 | import {Asset} from './public/Asset';
|
20 | import PluginOptions from './public/PluginOptions';
|
21 | import summarizeRequest from './summarizeRequest';
|
22 |
|
23 | export type ValidationOpts = {|
|
24 | options: ParcelOptions,
|
25 | request: AssetRequestDesc,
|
26 | report: ReportFn,
|
27 | workerApi: WorkerApi,
|
28 | |};
|
29 |
|
30 | export default class Validation {
|
31 | request: AssetRequestDesc;
|
32 | configRequests: Array<ConfigRequestDesc>;
|
33 | configLoader: ConfigLoader;
|
34 | options: ParcelOptions;
|
35 | impactfulOptions: $Shape<ParcelOptions>;
|
36 | report: ReportFn;
|
37 | workerApi: WorkerApi;
|
38 |
|
39 | constructor({request, report, options, workerApi}: ValidationOpts) {
|
40 | this.configLoader = new ConfigLoader(options);
|
41 | this.options = options;
|
42 | this.report = report;
|
43 | this.request = request;
|
44 | this.workerApi = workerApi;
|
45 | }
|
46 |
|
47 | async run(): Promise<void> {
|
48 | this.report({
|
49 | type: 'validation',
|
50 | filePath: this.request.filePath,
|
51 | });
|
52 |
|
53 | let asset = await this.loadAsset();
|
54 |
|
55 | let configRequest = {
|
56 | filePath: this.request.filePath,
|
57 | isSource: asset.value.isSource,
|
58 | meta: {
|
59 | actionType: 'validation',
|
60 | },
|
61 | env: this.request.env,
|
62 | };
|
63 |
|
64 | let config = await this.configLoader.load(configRequest);
|
65 | nullthrows(config.result);
|
66 | let parcelConfig = new ParcelConfig(
|
67 | config.result,
|
68 | this.options.packageManager,
|
69 | );
|
70 |
|
71 | let validators = await parcelConfig.getValidators(this.request.filePath);
|
72 | let pluginOptions = new PluginOptions(this.options);
|
73 |
|
74 | for (let validator of validators) {
|
75 | let validatorLogger = new PluginLogger({origin: validator.name});
|
76 | try {
|
77 | let config = null;
|
78 | if (validator.plugin.getConfig) {
|
79 | config = await validator.plugin.getConfig({
|
80 | asset: new Asset(asset),
|
81 | options: pluginOptions,
|
82 | logger: validatorLogger,
|
83 | resolveConfig: (configNames: Array<string>) =>
|
84 | resolveConfig(
|
85 | this.options.inputFS,
|
86 | asset.value.filePath,
|
87 | configNames,
|
88 | ),
|
89 | });
|
90 | }
|
91 |
|
92 | let validatorResult = await validator.plugin.validate({
|
93 | asset: new Asset(asset),
|
94 | options: pluginOptions,
|
95 | config,
|
96 | logger: validatorLogger,
|
97 | });
|
98 |
|
99 | if (validatorResult) {
|
100 | let {warnings, errors} = validatorResult;
|
101 |
|
102 | if (errors.length > 0) {
|
103 | throw new ThrowableDiagnostic({
|
104 | diagnostic: errors,
|
105 | });
|
106 | }
|
107 |
|
108 | if (warnings.length > 0) {
|
109 | logger.warn(warnings);
|
110 | }
|
111 | }
|
112 | } catch (e) {
|
113 | throw new ThrowableDiagnostic({
|
114 | diagnostic: errorToDiagnostic(e, validator.name),
|
115 | });
|
116 | }
|
117 | }
|
118 | }
|
119 |
|
120 | async loadAsset(): Promise<InternalAsset> {
|
121 | let {filePath, env, code, sideEffects} = this.request;
|
122 | let {content, size, hash, isSource} = await summarizeRequest(
|
123 | this.options.inputFS,
|
124 | this.request,
|
125 | );
|
126 |
|
127 |
|
128 |
|
129 | let idBase = code != null ? hash : filePath;
|
130 | return new InternalAsset({
|
131 | idBase,
|
132 | value: createAsset({
|
133 | idBase,
|
134 | filePath: filePath,
|
135 | isSource,
|
136 | type: path.extname(filePath).slice(1),
|
137 | hash,
|
138 | env: env,
|
139 | stats: {
|
140 | time: 0,
|
141 | size,
|
142 | },
|
143 | sideEffects: sideEffects,
|
144 | }),
|
145 | options: this.options,
|
146 | content,
|
147 | });
|
148 | }
|
149 | }
|