@atlaskit/eslint-plugin-design-system 8.25.2 → 8.27.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (80) hide show
  1. package/CHANGELOG.md +42 -0
  2. package/README.md +4 -0
  3. package/constellation/index/usage.mdx +402 -6
  4. package/dist/cjs/presets/all.codegen.js +5 -1
  5. package/dist/cjs/presets/recommended.codegen.js +5 -1
  6. package/dist/cjs/rules/consistent-css-prop-usage/index.js +254 -32
  7. package/dist/cjs/rules/index.codegen.js +9 -1
  8. package/dist/cjs/rules/no-empty-styled-expression/index.js +75 -0
  9. package/dist/cjs/rules/no-exported-css/index.js +37 -0
  10. package/dist/cjs/rules/no-exported-keyframes/index.js +37 -0
  11. package/dist/cjs/rules/no-invalid-css-map/index.js +102 -0
  12. package/dist/cjs/rules/no-invalid-css-map/utils.js +193 -0
  13. package/dist/cjs/rules/utils/create-no-exported-rule/check-if-supported-export.js +158 -0
  14. package/dist/cjs/rules/utils/create-no-exported-rule/is-styled-component.js +80 -0
  15. package/dist/cjs/rules/utils/create-no-exported-rule/main.js +66 -0
  16. package/dist/cjs/rules/utils/get-first-supported-import.js +28 -0
  17. package/dist/cjs/rules/utils/is-supported-import.js +53 -16
  18. package/dist/es2019/presets/all.codegen.js +5 -1
  19. package/dist/es2019/presets/recommended.codegen.js +5 -1
  20. package/dist/es2019/rules/consistent-css-prop-usage/index.js +251 -33
  21. package/dist/es2019/rules/index.codegen.js +9 -1
  22. package/dist/es2019/rules/no-empty-styled-expression/index.js +65 -0
  23. package/dist/es2019/rules/no-exported-css/index.js +31 -0
  24. package/dist/es2019/rules/no-exported-keyframes/index.js +31 -0
  25. package/dist/es2019/rules/no-invalid-css-map/index.js +95 -0
  26. package/dist/es2019/rules/no-invalid-css-map/utils.js +134 -0
  27. package/dist/es2019/rules/utils/create-no-exported-rule/check-if-supported-export.js +142 -0
  28. package/dist/es2019/rules/utils/create-no-exported-rule/is-styled-component.js +70 -0
  29. package/dist/es2019/rules/utils/create-no-exported-rule/main.js +59 -0
  30. package/dist/es2019/rules/utils/get-first-supported-import.js +22 -0
  31. package/dist/es2019/rules/utils/is-supported-import.js +50 -15
  32. package/dist/esm/presets/all.codegen.js +5 -1
  33. package/dist/esm/presets/recommended.codegen.js +5 -1
  34. package/dist/esm/rules/consistent-css-prop-usage/index.js +255 -33
  35. package/dist/esm/rules/index.codegen.js +9 -1
  36. package/dist/esm/rules/no-empty-styled-expression/index.js +68 -0
  37. package/dist/esm/rules/no-exported-css/index.js +31 -0
  38. package/dist/esm/rules/no-exported-keyframes/index.js +31 -0
  39. package/dist/esm/rules/no-invalid-css-map/index.js +96 -0
  40. package/dist/esm/rules/no-invalid-css-map/utils.js +186 -0
  41. package/dist/esm/rules/utils/create-no-exported-rule/check-if-supported-export.js +151 -0
  42. package/dist/esm/rules/utils/create-no-exported-rule/is-styled-component.js +74 -0
  43. package/dist/esm/rules/utils/create-no-exported-rule/main.js +60 -0
  44. package/dist/esm/rules/utils/get-first-supported-import.js +22 -0
  45. package/dist/esm/rules/utils/is-supported-import.js +51 -15
  46. package/dist/types/index.codegen.d.ts +8 -0
  47. package/dist/types/presets/all.codegen.d.ts +5 -1
  48. package/dist/types/presets/recommended.codegen.d.ts +5 -1
  49. package/dist/types/rules/consistent-css-prop-usage/types.d.ts +7 -2
  50. package/dist/types/rules/index.codegen.d.ts +4 -0
  51. package/dist/types/rules/no-empty-styled-expression/index.d.ts +3 -0
  52. package/dist/types/rules/no-exported-css/index.d.ts +3 -0
  53. package/dist/types/rules/no-exported-keyframes/index.d.ts +3 -0
  54. package/dist/types/rules/no-invalid-css-map/index.d.ts +3 -0
  55. package/dist/types/rules/no-invalid-css-map/utils.d.ts +14 -0
  56. package/dist/types/rules/use-primitives/utils/update-jsx-attribute-by-name.d.ts +1 -1
  57. package/dist/types/rules/utils/create-no-exported-rule/check-if-supported-export.d.ts +15 -0
  58. package/dist/types/rules/utils/create-no-exported-rule/is-styled-component.d.ts +14 -0
  59. package/dist/types/rules/utils/create-no-exported-rule/main.d.ts +19 -0
  60. package/dist/types/rules/utils/create-rule.d.ts +1 -1
  61. package/dist/types/rules/utils/get-first-supported-import.d.ts +17 -0
  62. package/dist/types/rules/utils/is-supported-import.d.ts +26 -8
  63. package/dist/types-ts4.5/index.codegen.d.ts +8 -0
  64. package/dist/types-ts4.5/presets/all.codegen.d.ts +5 -1
  65. package/dist/types-ts4.5/presets/recommended.codegen.d.ts +5 -1
  66. package/dist/types-ts4.5/rules/consistent-css-prop-usage/types.d.ts +7 -2
  67. package/dist/types-ts4.5/rules/index.codegen.d.ts +4 -0
  68. package/dist/types-ts4.5/rules/no-empty-styled-expression/index.d.ts +3 -0
  69. package/dist/types-ts4.5/rules/no-exported-css/index.d.ts +3 -0
  70. package/dist/types-ts4.5/rules/no-exported-keyframes/index.d.ts +3 -0
  71. package/dist/types-ts4.5/rules/no-invalid-css-map/index.d.ts +3 -0
  72. package/dist/types-ts4.5/rules/no-invalid-css-map/utils.d.ts +14 -0
  73. package/dist/types-ts4.5/rules/use-primitives/utils/update-jsx-attribute-by-name.d.ts +1 -1
  74. package/dist/types-ts4.5/rules/utils/create-no-exported-rule/check-if-supported-export.d.ts +15 -0
  75. package/dist/types-ts4.5/rules/utils/create-no-exported-rule/is-styled-component.d.ts +14 -0
  76. package/dist/types-ts4.5/rules/utils/create-no-exported-rule/main.d.ts +19 -0
  77. package/dist/types-ts4.5/rules/utils/create-rule.d.ts +1 -1
  78. package/dist/types-ts4.5/rules/utils/get-first-supported-import.d.ts +17 -0
  79. package/dist/types-ts4.5/rules/utils/is-supported-import.d.ts +26 -8
  80. package/package.json +3 -1
@@ -0,0 +1,96 @@
1
+ import { checkIfSupportedExport } from '../utils/create-no-exported-rule/check-if-supported-export';
2
+ import { createLintRule } from '../utils/create-rule';
3
+ import { CSS_IN_JS_IMPORTS, isCssMap } from '../utils/is-supported-import';
4
+ import { CssMapObjectChecker, getCssMapObject } from './utils';
5
+ var IMPORT_SOURCES = [CSS_IN_JS_IMPORTS.compiled, CSS_IN_JS_IMPORTS.atlaskitCss];
6
+ var reportIfExported = function reportIfExported(node, context) {
7
+ var state = checkIfSupportedExport(context, node, IMPORT_SOURCES);
8
+ if (!state.isExport) {
9
+ return;
10
+ }
11
+ context.report({
12
+ messageId: 'noExportedCssMap',
13
+ node: state.node
14
+ });
15
+ };
16
+ var reportIfNotTopLevelScope = function reportIfNotTopLevelScope(node, context) {
17
+ // Treat `export` keyword as valid because the reportIfExported function already handles those
18
+ var validTypes = ['ExportDefaultDeclaration', 'ExportNamedDeclaration', 'Program', 'VariableDeclaration', 'VariableDeclarator'];
19
+ var parentNode = node.parent;
20
+ while (parentNode) {
21
+ if (!validTypes.includes(parentNode.type)) {
22
+ context.report({
23
+ node: node,
24
+ messageId: 'mustBeTopLevelScope'
25
+ });
26
+ return;
27
+ }
28
+ parentNode = parentNode.parent;
29
+ }
30
+ };
31
+ var createCssMapRule = function createCssMapRule(context) {
32
+ var _context$getSourceCod = context.getSourceCode(),
33
+ text = _context$getSourceCod.text;
34
+ if (IMPORT_SOURCES.every(function (importSource) {
35
+ return !text.includes(importSource);
36
+ })) {
37
+ return {};
38
+ }
39
+ return {
40
+ CallExpression: function CallExpression(node) {
41
+ var references = context.getScope().references;
42
+ if (!isCssMap(node.callee, references, IMPORT_SOURCES)) {
43
+ return;
44
+ }
45
+ reportIfExported(node, context);
46
+ reportIfNotTopLevelScope(node, context);
47
+ var cssMapObject = getCssMapObject(node);
48
+ if (!cssMapObject) {
49
+ return;
50
+ }
51
+ var cssMapObjectChecker = new CssMapObjectChecker(cssMapObject, context);
52
+ cssMapObjectChecker.run();
53
+ }
54
+ };
55
+ };
56
+ var noInvalidCssMapRule = createLintRule({
57
+ meta: {
58
+ name: 'no-invalid-css-map',
59
+ docs: {
60
+ description: "Checks the validity of a CSS map created through cssMap. This is intended to be used alongside TypeScript's type-checking.",
61
+ recommended: true,
62
+ severity: 'error'
63
+ },
64
+ messages: {
65
+ mustBeTopLevelScope: 'cssMap must only be used in the top-most scope of the module.',
66
+ noNonStaticallyEvaluable: 'Cannot statically evaluate the value of this variable. Values used in the cssMap function call should have a value evaluable at build time.',
67
+ noExportedCssMap: 'cssMap usages cannot be exported.',
68
+ noInlineFunctions: 'Cannot use functions as values in cssMap - values must only be statically evaluable values (e.g. strings, numbers).',
69
+ noFunctionCalls: 'Cannot call external functions in cssMap - values must only be statically evaluable values (e.g. strings, numbers).',
70
+ noSpreadElement: 'Cannot use the spread operator in cssMap.'
71
+ },
72
+ schema: [{
73
+ type: 'object',
74
+ properties: {
75
+ allowedFunctionCalls: {
76
+ type: 'array',
77
+ items: {
78
+ type: 'array',
79
+ minItems: 2,
80
+ maxItems: 2,
81
+ items: [{
82
+ type: 'string'
83
+ }, {
84
+ type: 'string'
85
+ }]
86
+ },
87
+ uniqueItems: true
88
+ }
89
+ },
90
+ additionalProperties: false
91
+ }],
92
+ type: 'problem'
93
+ },
94
+ create: createCssMapRule
95
+ });
96
+ export default noInvalidCssMapRule;
@@ -0,0 +1,186 @@
1
+ import _slicedToArray from "@babel/runtime/helpers/slicedToArray";
2
+ import _classCallCheck from "@babel/runtime/helpers/classCallCheck";
3
+ import _createClass from "@babel/runtime/helpers/createClass";
4
+ function _createForOfIteratorHelper(o, allowArrayLike) { var it = typeof Symbol !== "undefined" && o[Symbol.iterator] || o["@@iterator"]; if (!it) { if (Array.isArray(o) || (it = _unsupportedIterableToArray(o)) || allowArrayLike && o && typeof o.length === "number") { if (it) o = it; var i = 0; var F = function F() {}; return { s: F, n: function n() { if (i >= o.length) return { done: true }; return { done: false, value: o[i++] }; }, e: function e(_e) { throw _e; }, f: F }; } throw new TypeError("Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); } var normalCompletion = true, didErr = false, err; return { s: function s() { it = it.call(o); }, n: function n() { var step = it.next(); normalCompletion = step.done; return step; }, e: function e(_e2) { didErr = true; err = _e2; }, f: function f() { try { if (!normalCompletion && it.return != null) it.return(); } finally { if (didErr) throw err; } } }; }
5
+ function _unsupportedIterableToArray(o, minLen) { if (!o) return; if (typeof o === "string") return _arrayLikeToArray(o, minLen); var n = Object.prototype.toString.call(o).slice(8, -1); if (n === "Object" && o.constructor) n = o.constructor.name; if (n === "Map" || n === "Set") return Array.from(o); if (n === "Arguments" || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)) return _arrayLikeToArray(o, minLen); }
6
+ function _arrayLikeToArray(arr, len) { if (len == null || len > arr.length) len = arr.length; for (var i = 0, arr2 = new Array(len); i < len; i++) arr2[i] = arr[i]; return arr2; }
7
+ export var getCssMapObject = function getCssMapObject(node) {
8
+ // We assume the argument `node` is already a cssMap() call.
9
+
10
+ // Things like the number of arguments to cssMap and the type of
11
+ // cssMap's argument are handled by the TypeScript compiler, so
12
+ // we don't bother with creating eslint errors for these here
13
+
14
+ if (node.arguments.length !== 1 || node.arguments[0].type !== 'ObjectExpression') {
15
+ return;
16
+ }
17
+ return node.arguments[0];
18
+ };
19
+ var findNodeReference = function findNodeReference(references, node) {
20
+ return references.find(function (reference) {
21
+ return reference.identifier === node;
22
+ });
23
+ };
24
+ var getAllowedFunctionCalls = function getAllowedFunctionCalls(options) {
25
+ var _options$;
26
+ if (options.length === 0 || ((_options$ = options[0]) === null || _options$ === void 0 ? void 0 : _options$.allowedFunctionCalls) === undefined) {
27
+ return [];
28
+ }
29
+
30
+ // Beyond the basic check of "does allowedFunctionCalls exist?",
31
+ // we assume ESLint's rule checker type checks the contents of allowedFunctionCalls
32
+ // as it should
33
+ return options[0].allowedFunctionCalls;
34
+ };
35
+ export var CssMapObjectChecker = /*#__PURE__*/function () {
36
+ function CssMapObjectChecker(cssMapObject, context) {
37
+ _classCallCheck(this, CssMapObjectChecker);
38
+ this.allowedFunctionCalls = getAllowedFunctionCalls(context.options);
39
+ this.cssMapObject = cssMapObject;
40
+ this.report = context.report;
41
+ this.references = context.getScope().references;
42
+ }
43
+ _createClass(CssMapObjectChecker, [{
44
+ key: "isNotWhitelistedFunction",
45
+ value: function isNotWhitelistedFunction(callee) {
46
+ var _reference$resolved,
47
+ _this = this;
48
+ if (callee.type !== 'Identifier' || this.allowedFunctionCalls.length === 0) {
49
+ return true;
50
+ }
51
+ var reference = findNodeReference(this.references, callee);
52
+ var definitions = reference === null || reference === void 0 || (_reference$resolved = reference.resolved) === null || _reference$resolved === void 0 ? void 0 : _reference$resolved.defs;
53
+ if (!definitions) {
54
+ return true;
55
+ }
56
+ return definitions.some(function (definition) {
57
+ // We add some restrictions to keep this simple...
58
+ // Forbid non-imported functions
59
+ if (definition.type !== 'ImportBinding') {
60
+ return true;
61
+ }
62
+ // Forbid default imports (e.g. `import React from 'react'`)
63
+ if (definition.node.type !== 'ImportSpecifier') {
64
+ return true;
65
+ }
66
+ var packageName = definition.parent.source.value;
67
+ var importedFunctionName = definition.node.imported.name;
68
+ return !_this.allowedFunctionCalls.some(function (_ref) {
69
+ var _ref2 = _slicedToArray(_ref, 2),
70
+ allowedPackageName = _ref2[0],
71
+ allowedFunctionName = _ref2[1];
72
+ return allowedPackageName === packageName && allowedFunctionName === importedFunctionName;
73
+ });
74
+ });
75
+ }
76
+ }, {
77
+ key: "checkCssMapObjectValue",
78
+ value: function checkCssMapObjectValue(value) {
79
+ if (value.type === 'CallExpression' && this.isNotWhitelistedFunction(value.callee)) {
80
+ // object value is a function call in the style
81
+ // {
82
+ // key: functionCall(), ...
83
+ // }
84
+ this.report({
85
+ node: value,
86
+ messageId: 'noFunctionCalls'
87
+ });
88
+ } else if (value.type === 'ArrowFunctionExpression' || value.type === 'FunctionExpression') {
89
+ // object value is a function call in the style
90
+ // {
91
+ // key: (prop) => prop.color, // ArrowFunctionExpression
92
+ // get danger() { return { ... } }, // FunctionExpression
93
+ // }
94
+ this.report({
95
+ node: value,
96
+ messageId: 'noInlineFunctions'
97
+ });
98
+ } else if (value.type === 'BinaryExpression' || value.type === 'LogicalExpression') {
99
+ this.checkCssMapObjectValue(value.left);
100
+ this.checkCssMapObjectValue(value.right);
101
+ } else if (value.type === 'Identifier') {
102
+ var _reference$resolved2;
103
+ var reference = findNodeReference(this.references, value);
104
+
105
+ // Get the variable's definition when initialised. Assume that the last definition
106
+ // is the most recent one.
107
+ //
108
+ // Ideally we would try to get the variable's value at the point at which
109
+ // cssMap() is run, but ESLint doesn't seem to give us an easy way to
110
+ // do that...
111
+ var definitions = reference === null || reference === void 0 || (_reference$resolved2 = reference.resolved) === null || _reference$resolved2 === void 0 ? void 0 : _reference$resolved2.defs;
112
+ if (!definitions || definitions.length === 0) {
113
+ // Variable is not defined :thinking:
114
+ return;
115
+ }
116
+ var _iterator = _createForOfIteratorHelper(definitions),
117
+ _step;
118
+ try {
119
+ for (_iterator.s(); !(_step = _iterator.n()).done;) {
120
+ var definition = _step.value;
121
+ if (definition.type === 'Variable' && definition.node.init) {
122
+ return this.checkCssMapObjectValue(definition.node.init);
123
+ }
124
+ }
125
+ } catch (err) {
126
+ _iterator.e(err);
127
+ } finally {
128
+ _iterator.f();
129
+ }
130
+ } else if (value.type === 'ObjectExpression') {
131
+ // Object inside another object
132
+ this.checkCssMapObject(value);
133
+ } else if (value.type === 'TemplateLiteral') {
134
+ // object value is a template literal, something like
135
+ // `hello world`
136
+ // `hello ${functionCall()} world`
137
+ // `hello ${someVariable} world`
138
+ // etc.
139
+ //
140
+ // where the expressions are the parts enclosed within the
141
+ // ${ ... }
142
+ var _iterator2 = _createForOfIteratorHelper(value.expressions),
143
+ _step2;
144
+ try {
145
+ for (_iterator2.s(); !(_step2 = _iterator2.n()).done;) {
146
+ var expression = _step2.value;
147
+ this.checkCssMapObjectValue(expression);
148
+ }
149
+ } catch (err) {
150
+ _iterator2.e(err);
151
+ } finally {
152
+ _iterator2.f();
153
+ }
154
+ }
155
+ }
156
+ }, {
157
+ key: "checkCssMapObject",
158
+ value: function checkCssMapObject(cssMapObject) {
159
+ var _iterator3 = _createForOfIteratorHelper(cssMapObject.properties),
160
+ _step3;
161
+ try {
162
+ for (_iterator3.s(); !(_step3 = _iterator3.n()).done;) {
163
+ var property = _step3.value;
164
+ if (property.type === 'SpreadElement') {
165
+ this.report({
166
+ node: property,
167
+ messageId: 'noSpreadElement'
168
+ });
169
+ continue;
170
+ }
171
+ this.checkCssMapObjectValue(property.value);
172
+ }
173
+ } catch (err) {
174
+ _iterator3.e(err);
175
+ } finally {
176
+ _iterator3.f();
177
+ }
178
+ }
179
+ }, {
180
+ key: "run",
181
+ value: function run() {
182
+ this.checkCssMapObject(this.cssMapObject);
183
+ }
184
+ }]);
185
+ return CssMapObjectChecker;
186
+ }();
@@ -0,0 +1,151 @@
1
+ import _defineProperty from "@babel/runtime/helpers/defineProperty";
2
+ function _createForOfIteratorHelper(o, allowArrayLike) { var it = typeof Symbol !== "undefined" && o[Symbol.iterator] || o["@@iterator"]; if (!it) { if (Array.isArray(o) || (it = _unsupportedIterableToArray(o)) || allowArrayLike && o && typeof o.length === "number") { if (it) o = it; var i = 0; var F = function F() {}; return { s: F, n: function n() { if (i >= o.length) return { done: true }; return { done: false, value: o[i++] }; }, e: function e(_e) { throw _e; }, f: F }; } throw new TypeError("Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); } var normalCompletion = true, didErr = false, err; return { s: function s() { it = it.call(o); }, n: function n() { var step = it.next(); normalCompletion = step.done; return step; }, e: function e(_e2) { didErr = true; err = _e2; }, f: function f() { try { if (!normalCompletion && it.return != null) it.return(); } finally { if (didErr) throw err; } } }; }
3
+ function _unsupportedIterableToArray(o, minLen) { if (!o) return; if (typeof o === "string") return _arrayLikeToArray(o, minLen); var n = Object.prototype.toString.call(o).slice(8, -1); if (n === "Object" && o.constructor) n = o.constructor.name; if (n === "Map" || n === "Set") return Array.from(o); if (n === "Arguments" || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)) return _arrayLikeToArray(o, minLen); }
4
+ function _arrayLikeToArray(arr, len) { if (len == null || len > arr.length) len = arr.length; for (var i = 0, arr2 = new Array(len); i < len; i++) arr2[i] = arr[i]; return arr2; }
5
+ function ownKeys(e, r) { var t = Object.keys(e); if (Object.getOwnPropertySymbols) { var o = Object.getOwnPropertySymbols(e); r && (o = o.filter(function (r) { return Object.getOwnPropertyDescriptor(e, r).enumerable; })), t.push.apply(t, o); } return t; }
6
+ function _objectSpread(e) { for (var r = 1; r < arguments.length; r++) { var t = null != arguments[r] ? arguments[r] : {}; r % 2 ? ownKeys(Object(t), !0).forEach(function (r) { _defineProperty(e, r, t[r]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(e, Object.getOwnPropertyDescriptors(t)) : ownKeys(Object(t)).forEach(function (r) { Object.defineProperty(e, r, Object.getOwnPropertyDescriptor(t, r)); }); } return e; }
7
+ import { isStyledComponent } from './is-styled-component';
8
+ var getStack = function getStack(context, node) {
9
+ var _scope;
10
+ var _context$getSourceCod = context.getSourceCode(),
11
+ scopeManager = _context$getSourceCod.scopeManager;
12
+ var stack = {
13
+ nodes: [],
14
+ root: node
15
+ };
16
+ var scope;
17
+ for (var current = node; current.type !== 'Program'; current = current.parent) {
18
+ if (!scope) {
19
+ var currentScope = scopeManager.acquire(current);
20
+ if (currentScope) {
21
+ scope = currentScope;
22
+ }
23
+ }
24
+ switch (current.type) {
25
+ case 'ExportDefaultDeclaration':
26
+ case 'ExportNamedDeclaration':
27
+ stack.root = current;
28
+ break;
29
+ case 'VariableDeclarator':
30
+ stack.root = current;
31
+ break;
32
+ case 'ExportSpecifier':
33
+ case 'ObjectExpression':
34
+ case 'VariableDeclaration':
35
+ break;
36
+ default:
37
+ stack.nodes.unshift(current);
38
+ }
39
+ }
40
+ return _objectSpread(_objectSpread({}, stack), {}, {
41
+ scope: (_scope = scope) !== null && _scope !== void 0 ? _scope : context.getScope()
42
+ });
43
+ };
44
+ var matches = function matches(defs, refs) {
45
+ // When there are no defs, the definition is inlined. This must be a match as we know the refs contain the initial
46
+ // definition.
47
+ if (!defs.length) {
48
+ return true;
49
+ }
50
+
51
+ // When there are no refs, the reference refers to the entire definition and therefore must be a match.
52
+ if (!refs.length) {
53
+ return true;
54
+ }
55
+
56
+ // When both the references and definitions exist, they should match in length
57
+ if (defs.length !== refs.length) {
58
+ return false;
59
+ }
60
+ return defs.every(function (def, i) {
61
+ var ref = refs[i];
62
+ if (def.type === 'Property') {
63
+ // There is a match between the def and the ref when both names match:
64
+ //
65
+ // const fooDef = { bar: '' };
66
+ // const barRef = fooDef.bar
67
+ //
68
+ // There is no match when the ref property does not match the definition key name:
69
+ //
70
+ // const barRef = fooDef.notFound
71
+ return def.key.type === 'Identifier' && ref.type === 'MemberExpression' && ref.property.type === 'Identifier' && ref.property.name === def.key.name;
72
+ }
73
+
74
+ // Anything here is either unsupported or should not match...
75
+ return false;
76
+ });
77
+ };
78
+ export var checkIfSupportedExport = function checkIfSupportedExport(context, node, importSources) {
79
+ var _resolved$references;
80
+ var scope = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : context.getScope();
81
+ // Ignore any expression defined outside of the global or module scope as we have no way of statically analysing them
82
+ if (scope.type !== 'global' && scope.type !== 'module') {
83
+ return {
84
+ isExport: false
85
+ };
86
+ }
87
+ var _getStack = getStack(context, node.parent),
88
+ root = _getStack.root,
89
+ nodes = _getStack.nodes;
90
+ // Exporting a component with a css reference should be allowed
91
+ if (isStyledComponent(nodes, context, importSources)) {
92
+ return {
93
+ isExport: false
94
+ };
95
+ }
96
+ if (root.type === 'ExportDefaultDeclaration' || root.type === 'ExportNamedDeclaration') {
97
+ return {
98
+ isExport: true,
99
+ node: root
100
+ };
101
+ }
102
+ if (root.type !== 'VariableDeclarator') {
103
+ return {
104
+ isExport: false
105
+ };
106
+ }
107
+
108
+ // Find the reference to the variable declarator
109
+ var reference = scope.references.find(function (_ref) {
110
+ var identifier = _ref.identifier;
111
+ return identifier === root.id;
112
+ });
113
+ if (!reference) {
114
+ return {
115
+ isExport: false
116
+ };
117
+ }
118
+
119
+ // Iterate through all of the references to the resolved variable declarator node
120
+ var resolved = reference.resolved;
121
+ var _iterator = _createForOfIteratorHelper((_resolved$references = resolved === null || resolved === void 0 ? void 0 : resolved.references) !== null && _resolved$references !== void 0 ? _resolved$references : []),
122
+ _step;
123
+ try {
124
+ for (_iterator.s(); !(_step = _iterator.n()).done;) {
125
+ var identifier = _step.value.identifier;
126
+ // Skip references to the root, since it has already been processed above
127
+ if (identifier === root.id) {
128
+ continue;
129
+ }
130
+ var _getStack2 = getStack(context, identifier.parent),
131
+ refs = _getStack2.nodes,
132
+ nextScope = _getStack2.scope;
133
+
134
+ // Only validate the resolved reference if it accesses the definition node
135
+ if (matches(nodes, refs.reverse())) {
136
+ // Now validate the identifier reference as a definition
137
+ var validity = checkIfSupportedExport(context, identifier, importSources, nextScope);
138
+ if (validity.isExport) {
139
+ return validity;
140
+ }
141
+ }
142
+ }
143
+ } catch (err) {
144
+ _iterator.e(err);
145
+ } finally {
146
+ _iterator.f();
147
+ }
148
+ return {
149
+ isExport: false
150
+ };
151
+ };
@@ -0,0 +1,74 @@
1
+ import { getFirstSupportedImport } from '../get-first-supported-import';
2
+ /**
3
+ * Given a list of node, find and return the callee of the first Compiled or styled-components `styled` function call found in the list.
4
+ *
5
+ * For example, given `styled.div({ ... })`, we return the node corresponding to the
6
+ * `styled.div` part. Alternatively, given `styled(button)(style)`, we return the `styled`
7
+ * part.
8
+ *
9
+ * @param nodes
10
+ * @returns The callee of the first `styled` function call found.
11
+ */
12
+ var findNode = function findNode(nodes) {
13
+ var node = nodes.find(function (n) {
14
+ return n.type === 'TaggedTemplateExpression' || n.type === 'CallExpression';
15
+ });
16
+ if (!node) {
17
+ return;
18
+ }
19
+ if (node.type === 'CallExpression') {
20
+ // Eg. const Component = styled.button(style)
21
+ if (node.callee.type === 'MemberExpression') {
22
+ return node.callee;
23
+ }
24
+
25
+ // Eg. const Component = styled(button)(style)
26
+ if (node.callee.type === 'CallExpression' && node.callee.callee.type === 'Identifier') {
27
+ return node.callee.callee;
28
+ }
29
+ }
30
+
31
+ // Eg. const Component = styled.div`${styles}`;
32
+ if (node.type === 'TaggedTemplateExpression' && node.tag.type === 'MemberExpression') {
33
+ return node.tag;
34
+ }
35
+ return;
36
+ };
37
+
38
+ /**
39
+ * Given a rule, return the local name used to import the `styled` API. (for Compiled or styled-components).
40
+ *
41
+ * @param context Rule context.
42
+ * @returns The local name used to import the `styled` API.
43
+ */
44
+ var getStyledImportSpecifierName = function getStyledImportSpecifierName(context, importSources) {
45
+ var _supportedImport$spec;
46
+ var supportedImport = getFirstSupportedImport(context, importSources);
47
+ return supportedImport === null || supportedImport === void 0 || (_supportedImport$spec = supportedImport.specifiers.find(function (spec) {
48
+ return spec.type === 'ImportSpecifier' && spec.imported.name === 'styled' || spec.type === 'ImportDefaultSpecifier' && spec.local.name === 'styled';
49
+ })) === null || _supportedImport$spec === void 0 ? void 0 : _supportedImport$spec.local.name;
50
+ };
51
+
52
+ /**
53
+ * Returns whether the node is a usage of the `styled` API in the libraries we support.
54
+ *
55
+ * @param nodes Nodes to check.
56
+ * @param context Rule context.
57
+ * @param importSources A list of libraries we support.
58
+ * @returns Whether the node is a usage of the `styled` API.
59
+ */
60
+ export var isStyledComponent = function isStyledComponent(nodes, context, importSources) {
61
+ var node = findNode(nodes);
62
+ if (!node) {
63
+ return false;
64
+ }
65
+ var styledImportSpecifierName = getStyledImportSpecifierName(context, importSources);
66
+ if (styledImportSpecifierName) {
67
+ if (node.type === 'Identifier') {
68
+ return node.name === styledImportSpecifierName;
69
+ } else {
70
+ return node.object.type === 'Identifier' && node.object.name === styledImportSpecifierName;
71
+ }
72
+ }
73
+ return false;
74
+ };
@@ -0,0 +1,60 @@
1
+ import { getImportSources } from '../is-supported-import';
2
+ import { checkIfSupportedExport } from './check-if-supported-export';
3
+ /**
4
+ * Creates a new ESLint rule for banning exporting certain function calls, e.g.
5
+ * `css` and `keyframes`.
6
+ *
7
+ * Copied from the `utils/create-no-exported-rule/` folder in @compiled/eslint-plugin.
8
+ *
9
+ * Requires an importSources option defined on the rule, which is used to define additional
10
+ * packages which should be checked as part of this rule.
11
+ *
12
+ * @param isUsage A function that checks whether the current node matches the desired
13
+ * function call to check.
14
+ * @param messageId The ESLint error message to use for lint violations.
15
+ * @returns An eslint rule.
16
+ */
17
+ export var createNoExportedRule = function createNoExportedRule(isUsage, messageId) {
18
+ return function (context) {
19
+ var importSources = getImportSources(context);
20
+ var _context$getSourceCod = context.getSourceCode(),
21
+ text = _context$getSourceCod.text;
22
+ if (importSources.every(function (importSource) {
23
+ return !text.includes(importSource);
24
+ })) {
25
+ return {};
26
+ }
27
+ return {
28
+ CallExpression: function CallExpression(node) {
29
+ var _context$getScope = context.getScope(),
30
+ references = _context$getScope.references;
31
+ if (!isUsage(node.callee, references, importSources)) {
32
+ return;
33
+ }
34
+ var state = checkIfSupportedExport(context, node, importSources);
35
+ if (!state.isExport) {
36
+ return;
37
+ }
38
+ context.report({
39
+ messageId: messageId,
40
+ node: state.node
41
+ });
42
+ },
43
+ TaggedTemplateExpression: function TaggedTemplateExpression(node) {
44
+ var _context$getScope2 = context.getScope(),
45
+ references = _context$getScope2.references;
46
+ if (!isUsage(node.tag, references, importSources)) {
47
+ return;
48
+ }
49
+ var state = checkIfSupportedExport(context, node, importSources);
50
+ if (!state.isExport) {
51
+ return;
52
+ }
53
+ context.report({
54
+ messageId: messageId,
55
+ node: state.node
56
+ });
57
+ }
58
+ };
59
+ };
60
+ };
@@ -0,0 +1,22 @@
1
+ import { isNodeOfType } from 'eslint-codemod-utils';
2
+ /**
3
+ * Get the first import declaration in the file that matches any of the packages
4
+ * in `importSources`.
5
+ *
6
+ * @param context Rule context.
7
+ * @param importSources The packages to check import statements for. If importSources
8
+ * contains more than one package, the first import statement
9
+ * detected in the file that matches any of the packages will be
10
+ * returned.
11
+ * @returns The first import declaration found in the file.
12
+ */
13
+ export var getFirstSupportedImport = function getFirstSupportedImport(context, importSources) {
14
+ var isSupportedImport = function isSupportedImport(node) {
15
+ return isNodeOfType(node, 'ImportDeclaration') && typeof node.source.value === 'string' && importSources.includes(node.source.value);
16
+ };
17
+ var source = context.getSourceCode();
18
+ var supportedImports = source.ast.body.filter(isSupportedImport);
19
+ if (supportedImports.length) {
20
+ return supportedImports[0];
21
+ }
22
+ };