Skip to content

Commit 347d0e1

Browse files
committed
new rule exports-valid
1 parent 434176c commit 347d0e1

File tree

2 files changed

+70
-57
lines changed

2 files changed

+70
-57
lines changed

src/rules/exports-valid.js

Lines changed: 69 additions & 57 deletions
Original file line numberDiff line numberDiff line change
@@ -54,90 +54,102 @@ const validateFallbacks = (fallbacks) => {
5454
return true;
5555
};
5656

57-
// eslint-disable-next-line max-lines-per-function
58-
const lint = (packageJsonData, severity, config = {conditions: []}) => {
59-
const conditions = [...(config.conditions || []), 'default'];
57+
const validateStringValue = (parentKey, value) => {
58+
if (!isValidPath(value)) {
59+
return {error: 'invalidPath', str: value};
60+
}
6061

61-
if (!exists(packageJsonData, nodeName)) return true;
62+
if (parentKey.endsWith('/') && !value.endsWith('/')) {
63+
return {error: 'folderMappedToFile', str: parentKey};
64+
}
6265

63-
// eslint-disable-next-line complexity,max-statements,max-lines-per-function
64-
const traverse = (parentKey, parentType, exports) => {
65-
if (typeof exports === 'string') {
66-
if (!isValidPath(exports)) {
67-
return {error: 'invalidPath', str: exports};
68-
}
66+
return true;
67+
};
6968

70-
if (parentKey.endsWith('/') && !exports.endsWith('/')) {
71-
return {error: 'folderMappedToFile', str: parentKey};
72-
}
69+
const validateObject = (parentKey, parentType, object, config) => {
70+
// either a paths object or a conditions object
71+
let objectType;
7372

74-
return true;
75-
}
73+
const entries = Object.entries(object);
7674

77-
if (Array.isArray(exports)) {
78-
// https://nodejs.org/api/esm.html#esm_package_exports_fallbacks
79-
// eslint-disable-next-line no-restricted-syntax
80-
return validateFallbacks(exports);
81-
}
75+
for (let i = 0; i < entries.length; i += 1) {
76+
const [key, value] = entries[i];
8277

83-
if (!isPlainObj(exports)) {
84-
return {error: 'unexpectedType', str: typeof exports};
85-
}
78+
if (isValidPathKey(key)) {
79+
if (objectType === 'conditions') {
80+
return {error: 'pathInConditions', str: key};
81+
}
8682

87-
// either a paths object or a conditions object
88-
let objectType;
83+
if (parentType === 'paths') {
84+
return {error: 'nestedPaths', str: parentKey};
85+
}
8986

90-
const entries = Object.entries(exports);
87+
objectType = 'paths';
9188

92-
for (let i = 0; i < entries.length; i += 1) {
93-
const [key, value] = entries[i];
89+
// eslint-disable-next-line no-use-before-define
90+
const result = traverse(key, objectType, value, config);
9491

95-
if (isValidPathKey(key)) {
96-
if (objectType === 'conditions') {
97-
return {error: 'pathInConditions', str: key};
98-
}
92+
if (result !== true) return result;
93+
} else {
94+
// `key` interpreted as a condition
95+
if (!config.conditions.includes(key)) {
96+
return {error: 'unsupportedCondition', str: key};
97+
}
9998

100-
if (parentType === 'paths') {
101-
return {error: 'nestedPaths', str: parentKey};
102-
}
99+
if (objectType === 'paths') {
100+
return {error: 'conditionInPaths', str: key};
101+
}
103102

104-
objectType = 'paths';
103+
objectType = 'conditions';
104+
if (key === 'default' && i + 1 < entries.length) {
105+
return {error: 'defaultConditionNotLast'};
106+
}
105107

106-
const result = traverse(key, objectType, value);
108+
// eslint-disable-next-line no-use-before-define
109+
const result = traverse(key, objectType, value, config);
107110

108-
if (result !== true) return result;
109-
} else {
110-
// `key` interpreted as a condition
111-
if (!conditions.includes(key)) {
112-
return {error: 'unsupportedCondition', str: key};
113-
}
111+
if (result !== true) return result;
112+
}
113+
}
114114

115-
if (objectType === 'paths') {
116-
return {error: 'conditionInPaths', str: key};
117-
}
115+
return true;
116+
};
118117

119-
objectType = 'conditions';
120-
if (key === 'default' && i + 1 < entries.length) {
121-
return {error: 'defaultConditionNotLast'};
122-
}
118+
const traverse = (parentKey, parentType, node, config) => {
119+
if (typeof node === 'string') {
120+
return validateStringValue(parentKey, node);
121+
}
123122

124-
const result = traverse(key, objectType, value);
123+
if (Array.isArray(node)) {
124+
// https://nodejs.org/api/esm.html#esm_package_exports_fallbacks
125+
return validateFallbacks(node);
126+
}
125127

126-
if (result !== true) return result;
127-
}
128-
}
128+
if (!isPlainObj(node)) {
129+
return {error: 'unexpectedType', str: typeof node};
130+
}
131+
132+
return validateObject(parentKey, parentType, node, config);
133+
};
129134

130-
return true;
135+
const lint = (packageJsonData, severity, providedConfig) => {
136+
const config = {
137+
conditions: [],
138+
...providedConfig,
131139
};
132140

133-
const result = traverse(nodeName, 'root', packageJsonData[nodeName]);
141+
config.conditions.push('default');
142+
143+
if (!exists(packageJsonData, nodeName)) return true;
144+
145+
const result = traverse(nodeName, 'root', packageJsonData[nodeName], config);
134146

135147
if (result !== true) {
136148
const message = {
137149
invalidPath: `invalid path \`${result.str}\` must start with \`./\``,
138150
pathInConditions: `found path key \`${result.str}\` in a conditions object`,
139151
nestedPaths: `key \`${result.str}\` has paths object vaule but only conditions may be nested`,
140-
unsupportedCondition: `condition \`${result.str}\` not in supported conditions \`${conditions}\``,
152+
unsupportedCondition: `condition \`${result.str}\` not in supported conditions \`${config.conditions}\``,
141153
conditionInPaths: `found condition key \`${result.str}\` in a paths object`,
142154
unexpectedType: `unexpected \`${result.str}\``,
143155
defaultConditionNotLast: 'condition `default` must be the last key',

test/unit/rules/exports-valid.test.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,7 @@ describe('exports-valid Unit Tests', () => {
8484
title: 'some error in nested conditions',
8585
config: {conditions: ['node']},
8686
input: {node: {foo: './a.js'}},
87+
message: 'condition `foo` not in supported conditions `node,default`',
8788
},
8889
{
8990
title: 'two valid values in fallback array',

0 commit comments

Comments
 (0)