1 |
|
2 | import type {PackageName} from '@parcel/types';
|
3 | import type {SchemaEntity} from '@parcel/utils';
|
4 | import assert from 'assert';
|
5 |
|
6 |
|
7 |
|
8 | export function validatePackageName(
|
9 | pkg: ?PackageName,
|
10 | pluginType: string,
|
11 | key: string,
|
12 | ) {
|
13 |
|
14 | if (!pkg) {
|
15 | return;
|
16 | }
|
17 |
|
18 | assert(typeof pkg === 'string', `"${key}" must be a string`);
|
19 |
|
20 | if (pkg.startsWith('@parcel')) {
|
21 | assert(
|
22 | pkg.replace(/^@parcel\//, '').startsWith(`${pluginType}-`),
|
23 | `Official parcel ${pluginType} packages must be named according to "@parcel/${pluginType}-{name}"`,
|
24 | );
|
25 | } else if (pkg.startsWith('@')) {
|
26 | let [scope, name] = pkg.split('/');
|
27 | assert(
|
28 | name.startsWith(`parcel-${pluginType}-`),
|
29 | `Scoped parcel ${pluginType} packages must be named according to "${scope}/parcel-${pluginType}-{name}"`,
|
30 | );
|
31 | } else {
|
32 | assert(
|
33 | pkg.startsWith(`parcel-${pluginType}-`),
|
34 | `Parcel ${pluginType} packages must be named according to "parcel-${pluginType}-{name}"`,
|
35 | );
|
36 | }
|
37 | }
|
38 |
|
39 | const validatePluginName = (pluginType: string, key: string) => {
|
40 | return (val: string) => {
|
41 |
|
42 | if (val === '...') return;
|
43 |
|
44 | try {
|
45 | validatePackageName(val, pluginType, key);
|
46 | } catch (e) {
|
47 | return e.message;
|
48 | }
|
49 | };
|
50 | };
|
51 |
|
52 | const validateExtends = (val: string) => {
|
53 |
|
54 | if (val.startsWith('.')) return;
|
55 |
|
56 | try {
|
57 | validatePackageName(val, 'config', 'extends');
|
58 | } catch (e) {
|
59 | return e.message;
|
60 | }
|
61 | };
|
62 |
|
63 | const pipelineSchema = (pluginType: string, key: string): SchemaEntity => {
|
64 | return {
|
65 | type: 'array',
|
66 | items: {
|
67 | type: 'string',
|
68 | __validate: validatePluginName(pluginType, key),
|
69 | },
|
70 | };
|
71 | };
|
72 |
|
73 | const mapPipelineSchema = (pluginType: string, key: string): SchemaEntity => {
|
74 | return {
|
75 | type: 'object',
|
76 | properties: {},
|
77 | additionalProperties: pipelineSchema(pluginType, key),
|
78 | };
|
79 | };
|
80 |
|
81 | const mapStringSchema = (pluginType: string, key: string): SchemaEntity => {
|
82 | return {
|
83 | type: 'object',
|
84 | properties: {},
|
85 | additionalProperties: {
|
86 | type: 'string',
|
87 | __validate: validatePluginName(pluginType, key),
|
88 | },
|
89 | };
|
90 | };
|
91 |
|
92 | export default {
|
93 | type: 'object',
|
94 | properties: {
|
95 | extends: {
|
96 | oneOf: [
|
97 | {
|
98 | type: 'string',
|
99 | __validate: validateExtends,
|
100 | },
|
101 | {
|
102 | type: 'array',
|
103 | items: {
|
104 | type: 'string',
|
105 | __validate: validateExtends,
|
106 | },
|
107 | },
|
108 | ],
|
109 | },
|
110 | bundler: {
|
111 | type: 'string',
|
112 | __validate: validatePluginName('bundler', 'bundler'),
|
113 | },
|
114 | resolvers: pipelineSchema('resolver', 'resolvers'),
|
115 | transforms: mapPipelineSchema('transformer', 'transforms'),
|
116 | validators: mapPipelineSchema('validator', 'validators'),
|
117 | namers: pipelineSchema('namer', 'namers'),
|
118 | packagers: mapStringSchema('packager', 'packagers'),
|
119 | optimizers: mapPipelineSchema('optimizer', 'optimizers'),
|
120 | reporters: pipelineSchema('reporter', 'reporters'),
|
121 | runtimes: mapPipelineSchema('runtime', 'runtimes'),
|
122 | filePath: {
|
123 | type: 'string',
|
124 | },
|
125 | resolveFrom: {
|
126 | type: 'string',
|
127 | },
|
128 | },
|
129 | additionalProperties: false,
|
130 | };
|