Skip to content

Commit 16a7004

Browse files
committed
Initial commit (moving over from pipedream repo)
0 parents  commit 16a7004

File tree

2 files changed

+215
-0
lines changed

2 files changed

+215
-0
lines changed

index.js

Lines changed: 203 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,203 @@
1+
function isModuleExports(node) {
2+
if (!node) return false;
3+
if (!node?.object || !node?.property) return false;
4+
if (node.object.name !== "module" || node.property.name !== "exports") return false;
5+
return true;
6+
}
7+
8+
function isObjectWithProperties(node) {
9+
if (!node) return false;
10+
const {
11+
properties,
12+
type,
13+
} = node;
14+
if (type !== "ObjectExpression") return false;
15+
if (!properties || Object.keys(properties).length === 0) return false;
16+
return true;
17+
}
18+
19+
// Does a component contain the right property? e.g. key, version
20+
function componentContainsPropertyCheck(context, node, propertyName, message) {
21+
const {
22+
left,
23+
right,
24+
} = node.expression;
25+
if (!isModuleExports(left)) return;
26+
if (!isObjectWithProperties(right)) return;
27+
28+
if (!right.properties.map((p) => p?.key?.name).includes(propertyName)) {
29+
context.report({
30+
node: node,
31+
message: message ?? `Components must export a ${propertyName} property. See https://pipedream.com/docs/components/guidelines/#required-metadata`,
32+
});
33+
}
34+
}
35+
36+
// Do component props contain the right properties? e.g. label, description
37+
function componentPropsContainsPropertyCheck(context, node, propertyName) {
38+
const {
39+
left,
40+
right,
41+
} = node.expression;
42+
if (!isModuleExports(left)) return;
43+
if (!isObjectWithProperties(right)) return;
44+
45+
const { properties } = right;
46+
const propertyNames = properties.map((p) => p?.key?.name);
47+
if (propertyNames.includes("props") || propertyNames.includes("propDefinitions")) {
48+
const props = properties.find((p) => p?.key?.name === "props" || p?.key?.name === "propDefinitions");
49+
if (!isObjectWithProperties(props?.value)) return;
50+
for (const prop of props.value?.properties) {
51+
const {
52+
key,
53+
value: propDef,
54+
} = prop;
55+
56+
// We don't want to lint app props or props that are defined in propDefinitions
57+
if (!isObjectWithProperties(propDef)) continue;
58+
if (!isObjectWithProperties(right)) continue;
59+
const propProperties = propDef.properties.map((p) => p?.key?.name);
60+
if (propProperties.includes("propDefinition")) continue;
61+
62+
if (!propProperties.includes(propertyName)) {
63+
context.report({
64+
node: prop,
65+
message: `Component prop ${key?.name} must have a ${propertyName}. See https://pipedream.com/docs/components/guidelines/#props`,
66+
});
67+
}
68+
}
69+
}
70+
}
71+
72+
function optionalComponentPropsHaveDefaultProperty(context, node) {
73+
const {
74+
left,
75+
right,
76+
} = node.expression;
77+
if (!isModuleExports(left)) return;
78+
if (!isObjectWithProperties(right)) return;
79+
80+
const { properties } = right;
81+
const propertyNames = properties.map((p) => p?.key?.name);
82+
if (propertyNames.includes("props") || propertyNames.includes("propDefinitions")) {
83+
const props = properties.find((p) => p?.key?.name === "props" || p?.key?.name === "propDefinitions");
84+
if (!isObjectWithProperties(props?.value)) return;
85+
for (const prop of props.value?.properties) {
86+
const {
87+
key,
88+
value: propDef,
89+
} = prop;
90+
91+
// We don't want to lint app props or props that are defined in propDefinitions
92+
if (!isObjectWithProperties(propDef)) continue;
93+
if (!isObjectWithProperties(right)) continue;
94+
const propProperties = propDef.properties.map((p) => p?.key?.name);
95+
if (propProperties.includes("propDefinition")) continue;
96+
97+
const optionalValue = propDef.properties.find((p) => p?.key?.name === "optional")?.value?.value;
98+
console.log(optionalValue);
99+
100+
if (propProperties.includes("optional") && optionalValue && !propProperties.includes("default")) {
101+
context.report({
102+
node: prop,
103+
message: `Component prop ${key?.name} is marked "optional", so it must have a "default" property. See https://pipedream.com/docs/components/guidelines/#default-values`,
104+
});
105+
}
106+
}
107+
}
108+
}
109+
110+
module.exports = {
111+
rules: {
112+
"required-properties-key": {
113+
create: function (context) {
114+
return {
115+
ExpressionStatement(node) {
116+
componentContainsPropertyCheck(context, node, "key");
117+
},
118+
};
119+
},
120+
},
121+
"required-properties-name": {
122+
create: function (context) {
123+
return {
124+
ExpressionStatement(node) {
125+
componentContainsPropertyCheck(context, node, "name");
126+
},
127+
};
128+
},
129+
},
130+
"required-properties-version": {
131+
create: function (context) {
132+
return {
133+
ExpressionStatement(node) {
134+
componentContainsPropertyCheck(context, node, "version");
135+
},
136+
};
137+
},
138+
},
139+
"required-properties-description": {
140+
create: function (context) {
141+
return {
142+
ExpressionStatement(node) {
143+
componentContainsPropertyCheck(context, node, "description");
144+
},
145+
};
146+
},
147+
},
148+
"required-properties-type": {
149+
create: function (context) {
150+
return {
151+
ExpressionStatement(node) {
152+
componentContainsPropertyCheck(context, node, "type", "Components must export a type property (\"source\" or \"action\")");
153+
},
154+
};
155+
},
156+
},
157+
"props-label": {
158+
create: function (context) {
159+
return {
160+
ExpressionStatement(node) {
161+
componentPropsContainsPropertyCheck(context, node, "label");
162+
},
163+
};
164+
},
165+
},
166+
"props-description": {
167+
create: function (context) {
168+
return {
169+
ExpressionStatement(node) {
170+
componentPropsContainsPropertyCheck(context, node, "description");
171+
},
172+
};
173+
},
174+
},
175+
"default-value-required-for-optional-props": {
176+
create: function (context) {
177+
return {
178+
ExpressionStatement(node) {
179+
optionalComponentPropsHaveDefaultProperty(context, node);
180+
},
181+
};
182+
},
183+
},
184+
"source-name": {
185+
create: function (context) {
186+
return {
187+
ExpressionStatement(node) {
188+
componentContainsPropertyCheck(context, node, "type", "Components must export a type property (\"source\" or \"action\")");
189+
},
190+
};
191+
},
192+
},
193+
"source-description": {
194+
create: function (context) {
195+
return {
196+
ExpressionStatement(node) {
197+
componentContainsPropertyCheck(context, node, "type", "Components must export a type property (\"source\" or \"action\")");
198+
},
199+
};
200+
},
201+
},
202+
},
203+
};

package.json

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
{
2+
"name": "eslint-plugin-pipedream",
3+
"version": "0.0.1",
4+
"description": "ESLint plugin for Pipedream components: https://pipedream.com/docs/components/api/",
5+
"main": "index.js",
6+
"scripts": {
7+
"test": "echo \"Error: no test specified\" && exit 1"
8+
},
9+
"keywords": [],
10+
"author": "Pipedream, Inc",
11+
"license": "MIT"
12+
}

0 commit comments

Comments
 (0)