UNPKG

4.07 kBJavaScriptView Raw
1// @flow strict-local
2
3import type {WorkerApi} from '@parcel/workers';
4import type {
5 AssetRequestDesc,
6 ConfigRequestDesc,
7 ParcelOptions,
8 ReportFn,
9} from './types';
10
11import path from 'path';
12import nullthrows from 'nullthrows';
13import {resolveConfig} from '@parcel/utils';
14import logger, {PluginLogger} from '@parcel/logger';
15import ThrowableDiagnostic, {errorToDiagnostic} from '@parcel/diagnostic';
16import ParcelConfig from './ParcelConfig';
17import ConfigLoader from './ConfigLoader';
18import InternalAsset, {createAsset} from './InternalAsset';
19import {Asset} from './public/Asset';
20import PluginOptions from './public/PluginOptions';
21import summarizeRequest from './summarizeRequest';
22
23export type ValidationOpts = {|
24 options: ParcelOptions,
25 request: AssetRequestDesc,
26 report: ReportFn,
27 workerApi: WorkerApi,
28|};
29
30export 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 // If the transformer request passed code rather than a filename,
128 // use a hash as the base for the id to ensure it is unique.
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}