@atlaskit/eslint-plugin-design-system
Version:
The essential plugin for use with the Atlassian Design System.
186 lines (177 loc) • 8.01 kB
JavaScript
;
var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.rule = exports.default = void 0;
var _slicedToArray2 = _interopRequireDefault(require("@babel/runtime/helpers/slicedToArray"));
var _eslintCodemodUtils = require("eslint-codemod-utils");
var _createLintRule = require("../utils/create-lint-rule");
function _createForOfIteratorHelper(r, e) { var t = "undefined" != typeof Symbol && r[Symbol.iterator] || r["@@iterator"]; if (!t) { if (Array.isArray(r) || (t = _unsupportedIterableToArray(r)) || e && r && "number" == typeof r.length) { t && (r = t); var _n = 0, F = function F() {}; return { s: F, n: function n() { return _n >= r.length ? { done: !0 } : { done: !1, value: r[_n++] }; }, e: function e(r) { throw r; }, 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 o, a = !0, u = !1; return { s: function s() { t = t.call(r); }, n: function n() { var r = t.next(); return a = r.done, r; }, e: function e(r) { u = !0, o = r; }, f: function f() { try { a || null == t.return || t.return(); } finally { if (u) throw o; } } }; }
function _unsupportedIterableToArray(r, a) { if (r) { if ("string" == typeof r) return _arrayLikeToArray(r, a); var t = {}.toString.call(r).slice(8, -1); return "Object" === t && r.constructor && (t = r.constructor.name), "Map" === t || "Set" === t ? Array.from(r) : "Arguments" === t || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(t) ? _arrayLikeToArray(r, a) : void 0; } }
function _arrayLikeToArray(r, a) { (null == a || a > r.length) && (a = r.length); for (var e = 0, n = Array(a); e < a; e++) n[e] = r[e]; return n; }
var schema = {
type: 'array',
items: [{
type: 'object',
properties: {
autofix: {
type: 'boolean'
}
},
additionalProperties: false
}]
};
var defaultConfig = {
autofix: false
};
function readConfig(context) {
var _ref = context.options,
_ref2 = (0, _slicedToArray2.default)(_ref, 1),
config = _ref2[0];
return Object.assign({}, defaultConfig, config);
}
var PACKAGE_NAME = '@atlaskit/primitives';
var COMPILED_PACKAGE_NAME = '@atlaskit/primitives/compiled';
/**
* The set of Emotion entrypoints that have a direct Compiled equivalent under
* `@atlaskit/primitives/compiled/<subpath>`. Imports from these entrypoints can be
* safely autofixed by prefixing the subpath with `compiled/`.
*/
var AUTOFIXABLE_ENTRYPOINTS = new Set(['anchor', 'bleed', 'box', 'flex', 'grid', 'hide', 'inline', 'media-helper', 'metric-text', 'pressable', 'show', 'stack', 'text']);
/**
* Named exports with a supported dedicated Compiled component entrypoint.
*/
var COMPILED_COMPONENT_ENTRYPOINTS = {
Anchor: 'anchor',
Bleed: 'bleed',
Box: 'box',
Flex: 'flex',
Grid: 'grid',
Inline: 'inline',
MetricText: 'metric-text',
Pressable: 'pressable',
Stack: 'stack',
Text: 'text'
};
function getCompiledRootImports(node, context) {
var sourceCode = context.getSourceCode();
var isTypeImport = /^import\s+type\b/.test(sourceCode.getText(node));
if (sourceCode.getCommentsInside(node).length) {
return null;
}
var imports = new Map();
var _iterator = _createForOfIteratorHelper(node.specifiers),
_step;
try {
for (_iterator.s(); !(_step = _iterator.n()).done;) {
var _imports$get;
var specifier = _step.value;
if (specifier.type !== 'ImportSpecifier' || specifier.imported.type !== 'Identifier') {
return null;
}
var name = specifier.imported.name;
var componentName = name.endsWith('Props') ? name.slice(0, -5) : name;
var subpath = COMPILED_COMPONENT_ENTRYPOINTS[componentName];
// Legacy styling APIs have no equivalent Compiled export. Leave them for manual migration.
if (!subpath) {
return null;
}
var bindings = (_imports$get = imports.get(subpath)) !== null && _imports$get !== void 0 ? _imports$get : [];
bindings.push(sourceCode.getText(specifier));
imports.set(subpath, bindings);
}
} catch (err) {
_iterator.e(err);
} finally {
_iterator.f();
}
return imports.size ? Array.from(imports, function (_ref3) {
var _ref4 = (0, _slicedToArray2.default)(_ref3, 2),
subpath = _ref4[0],
bindings = _ref4[1];
return "import ".concat(isTypeImport ? 'type ' : '', "{ ").concat(bindings.join(', '), " } from '").concat(COMPILED_PACKAGE_NAME, "/").concat(subpath, "';");
}).join('\n') : null;
}
/**
* Determines whether an import source points at an Emotion `@atlaskit/primitives` entrypoint
* that should be migrated to Compiled.
*
* Returns the suggested Compiled import source to use for the autofix, or `null` when the
* import should be ignored (i.e. it is not an `@atlaskit/primitives` import, or it is already
* a Compiled entrypoint).
*
* When the import is an Emotion entrypoint that has no direct Compiled equivalent, the rule
* still reports a violation but there is no safe autofix; in that case the original source is
* returned so the fixer becomes a no-op replacement.
*/
function getCompiledSource(importSource) {
// The barrel import, e.g. `@atlaskit/primitives`.
if (importSource === PACKAGE_NAME) {
return COMPILED_PACKAGE_NAME;
}
// Ignore anything that isn't scoped to `@atlaskit/primitives/...`.
if (!importSource.startsWith("".concat(PACKAGE_NAME, "/"))) {
return null;
}
var subpath = importSource.slice("".concat(PACKAGE_NAME, "/").length);
// Compiled entrypoints (`@atlaskit/primitives/compiled` and `@atlaskit/primitives/compiled/*`)
// are already valid and should be ignored.
if (subpath === 'compiled' || subpath.startsWith('compiled/')) {
return null;
}
// Emotion entrypoint with a known Compiled equivalent — safe to autofix.
if (AUTOFIXABLE_ENTRYPOINTS.has(subpath)) {
return "".concat(COMPILED_PACKAGE_NAME, "/").concat(subpath);
}
// Emotion entrypoint without a direct Compiled equivalent (e.g. `xcss`, `constants`,
// `responsive/*`). Report the violation but leave the source unchanged so no broken
// autofix is produced.
return importSource;
}
var rule = exports.rule = (0, _createLintRule.createLintRule)({
meta: {
name: 'no-emotion-primitives',
type: 'problem',
fixable: 'code',
docs: {
description: 'Ensures usage of Compiled Primitives import instead of Emotion entrypoint.',
severity: 'warn',
recommended: false
},
messages: {
'no-emotion-primitives': 'Use a dedicated @atlaskit/primitives/compiled/<component> entrypoint instead of @atlaskit/primitives. Refer to go/akcss for more information.'
},
schema: schema
},
create: function create(context) {
var config = readConfig(context);
return {
ImportDeclaration: function ImportDeclaration(node) {
var importSource = node.source.value;
var compiledSource = getCompiledSource(importSource);
if (compiledSource === null) {
return;
}
context.report({
node: node.source,
messageId: 'no-emotion-primitives',
fix: function fix(fixer) {
// No safe autofix when there is no Compiled equivalent for this entrypoint.
if (!config.autofix || compiledSource === importSource) {
return null;
}
if (importSource === PACKAGE_NAME) {
var imports = getCompiledRootImports(node, context);
return imports ? fixer.replaceText(node, imports) : null;
}
var newSource = (0, _eslintCodemodUtils.literal)(compiledSource);
return fixer.replaceText(node.source, newSource.raw || "'".concat(compiledSource, "'"));
}
});
}
};
}
});
// eslint-disable-next-line @atlaskit/volt-strict-mode/no-multiple-exports
var _default = exports.default = rule;