@atlaskit/eslint-plugin-platform 2.4.1 → 2.5.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.
- package/CHANGELOG.md +21 -0
- package/dist/cjs/index.js +2 -0
- package/dist/cjs/rules/ensure-no-private-dependencies/index.js +101 -0
- package/dist/cjs/rules/ensure-valid-platform-yarn-protocol-usage/index.js +3 -15
- package/dist/es2019/index.js +2 -0
- package/dist/es2019/rules/ensure-no-private-dependencies/index.js +63 -0
- package/dist/es2019/rules/ensure-valid-platform-yarn-protocol-usage/index.js +4 -16
- package/dist/esm/index.js +2 -0
- package/dist/esm/rules/ensure-no-private-dependencies/index.js +96 -0
- package/dist/esm/rules/ensure-valid-platform-yarn-protocol-usage/index.js +4 -16
- package/dist/types/index.d.ts +2 -0
- package/dist/types/rules/ensure-no-private-dependencies/index.d.ts +3 -0
- package/dist/types-ts4.5/index.d.ts +2 -0
- package/dist/types-ts4.5/rules/ensure-no-private-dependencies/index.d.ts +3 -0
- package/package.json +6 -3
- package/src/index.tsx +2 -0
- package/src/rules/ensure-no-private-dependencies/__tests__/unit/rule.test.ts +212 -0
- package/src/rules/ensure-no-private-dependencies/index.ts +64 -0
- package/src/rules/ensure-valid-platform-yarn-protocol-usage/__tests__/unit/rule.test.ts +57 -109
- package/src/rules/ensure-valid-platform-yarn-protocol-usage/index.ts +4 -16
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,26 @@
|
|
|
1
1
|
# @atlaskit/eslint-plugin-platform
|
|
2
2
|
|
|
3
|
+
## 2.5.0
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
- [#127293](https://bitbucket.org/atlassian/atlassian-frontend-monorepo/pull-requests/127293)
|
|
8
|
+
[`3ee2e5b640591`](https://bitbucket.org/atlassian/atlassian-frontend-monorepo/commits/3ee2e5b640591) -
|
|
9
|
+
Created new rule `@atlaskit/platform/ensure-no-private-dependencies` which ensures that private
|
|
10
|
+
dependencies are not used in published packages
|
|
11
|
+
|
|
12
|
+
## 2.4.2
|
|
13
|
+
|
|
14
|
+
### Patch Changes
|
|
15
|
+
|
|
16
|
+
- [#120533](https://bitbucket.org/atlassian/atlassian-frontend-monorepo/pull-requests/120533)
|
|
17
|
+
[`f1bec731e278f`](https://bitbucket.org/atlassian/atlassian-frontend-monorepo/commits/f1bec731e278f) -
|
|
18
|
+
Adds a `sideEffects` field to ensure this package does not have Compiled styles tree-shaken in the
|
|
19
|
+
future to avoid an accidental regression.
|
|
20
|
+
|
|
21
|
+
This is related to
|
|
22
|
+
https://community.developer.atlassian.com/t/rfc-73-migrating-our-components-to-compiled-css-in-js/85953
|
|
23
|
+
|
|
3
24
|
## 2.4.1
|
|
4
25
|
|
|
5
26
|
### Patch Changes
|
package/dist/cjs/index.js
CHANGED
|
@@ -18,6 +18,7 @@ var _ensureFeatureFlagPrefix = _interopRequireDefault(require("./rules/ensure-fe
|
|
|
18
18
|
var _ensureCriticalDependencyResolutions = _interopRequireDefault(require("./rules/ensure-critical-dependency-resolutions"));
|
|
19
19
|
var _ensureValidPlatformYarnProtocolUsage = _interopRequireDefault(require("./rules/ensure-valid-platform-yarn-protocol-usage"));
|
|
20
20
|
var _ensureValidBinValues = _interopRequireDefault(require("./rules/ensure-valid-bin-values"));
|
|
21
|
+
var _ensureNoPrivateDependencies = _interopRequireDefault(require("./rules/ensure-no-private-dependencies"));
|
|
21
22
|
var _expandBorderShorthand = _interopRequireDefault(require("./rules/compiled/expand-border-shorthand"));
|
|
22
23
|
var _noInvalidStorybookDecoratorUsage = _interopRequireDefault(require("./rules/no-invalid-storybook-decorator-usage"));
|
|
23
24
|
var _ensurePublishValid = _interopRequireDefault(require("./rules/ensure-publish-valid"));
|
|
@@ -45,6 +46,7 @@ var rules = exports.rules = {
|
|
|
45
46
|
'ensure-critical-dependency-resolutions': _ensureCriticalDependencyResolutions.default,
|
|
46
47
|
'ensure-valid-platform-yarn-protocol-usage': _ensureValidPlatformYarnProtocolUsage.default,
|
|
47
48
|
'ensure-valid-bin-values': _ensureValidBinValues.default,
|
|
49
|
+
'ensure-no-private-dependencies': _ensureNoPrivateDependencies.default,
|
|
48
50
|
'expand-border-shorthand': _expandBorderShorthand.default,
|
|
49
51
|
'expand-background-shorthand': _expandBackgroundShorthand.default,
|
|
50
52
|
'expand-spacing-shorthand': _expandSpacingShorthand.default,
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");
|
|
4
|
+
Object.defineProperty(exports, "__esModule", {
|
|
5
|
+
value: true
|
|
6
|
+
});
|
|
7
|
+
exports.default = void 0;
|
|
8
|
+
var _regenerator = _interopRequireDefault(require("@babel/runtime/regenerator"));
|
|
9
|
+
var _asyncToGenerator2 = _interopRequireDefault(require("@babel/runtime/helpers/asyncToGenerator"));
|
|
10
|
+
var _handleAstObject = require("../util/handle-ast-object");
|
|
11
|
+
var _getPackages = require("@manypkg/get-packages");
|
|
12
|
+
var _findRoot = require("@manypkg/find-root");
|
|
13
|
+
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; } } }; }
|
|
14
|
+
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; } }
|
|
15
|
+
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; } // eslint-disable-next-line import/no-extraneous-dependencies
|
|
16
|
+
var root = (0, _findRoot.findRootSync)(process.cwd());
|
|
17
|
+
var pkgs = (0, _getPackages.getPackagesSync)(root).packages;
|
|
18
|
+
var pkgMap = new Map(pkgs.map(function (pkg) {
|
|
19
|
+
return [pkg.packageJson.name, pkg];
|
|
20
|
+
}));
|
|
21
|
+
var rule = {
|
|
22
|
+
meta: {
|
|
23
|
+
type: 'problem',
|
|
24
|
+
docs: {
|
|
25
|
+
description: "Ensures that private dependencies are not used in published packages.",
|
|
26
|
+
recommended: true
|
|
27
|
+
},
|
|
28
|
+
hasSuggestions: false,
|
|
29
|
+
messages: {
|
|
30
|
+
invalidPrivateDependency: "Published package has private dependency '{{ pkgName }}'. To resolve this error, remove the private dependency or set this package to private."
|
|
31
|
+
}
|
|
32
|
+
},
|
|
33
|
+
create: function create(context) {
|
|
34
|
+
return {
|
|
35
|
+
ObjectExpression: function () {
|
|
36
|
+
var _ObjectExpression = (0, _asyncToGenerator2.default)( /*#__PURE__*/_regenerator.default.mark(function _callee(node) {
|
|
37
|
+
var isPrivatePkg, dependencies, peerDependencies, _i, _arr, obj, _iterator, _step, p, key, _pkgMap$get, isPrivateDependency;
|
|
38
|
+
return _regenerator.default.wrap(function _callee$(_context) {
|
|
39
|
+
while (1) switch (_context.prev = _context.next) {
|
|
40
|
+
case 0:
|
|
41
|
+
if (!(!context.filename.endsWith('package.json') || node.type !== 'ObjectExpression')) {
|
|
42
|
+
_context.next = 2;
|
|
43
|
+
break;
|
|
44
|
+
}
|
|
45
|
+
return _context.abrupt("return");
|
|
46
|
+
case 2:
|
|
47
|
+
// Private dependencies can be used in private packages
|
|
48
|
+
isPrivatePkg = (0, _handleAstObject.getObjectPropertyAsLiteral)(node, 'private') === true;
|
|
49
|
+
if (!(isPrivatePkg === true)) {
|
|
50
|
+
_context.next = 5;
|
|
51
|
+
break;
|
|
52
|
+
}
|
|
53
|
+
return _context.abrupt("return");
|
|
54
|
+
case 5:
|
|
55
|
+
// Check for private dependencies in dependencies and peerDependencies
|
|
56
|
+
// Note: devDependencies are not checked here as they don't end up in consumer lockfiles
|
|
57
|
+
dependencies = (0, _handleAstObject.getObjectPropertyAsObject)(node, 'dependencies');
|
|
58
|
+
peerDependencies = (0, _handleAstObject.getObjectPropertyAsObject)(node, 'peerDependencies');
|
|
59
|
+
for (_i = 0, _arr = [dependencies, peerDependencies]; _i < _arr.length; _i++) {
|
|
60
|
+
obj = _arr[_i];
|
|
61
|
+
_iterator = _createForOfIteratorHelper((obj === null || obj === void 0 ? void 0 : obj.properties) || []);
|
|
62
|
+
try {
|
|
63
|
+
for (_iterator.s(); !(_step = _iterator.n()).done;) {
|
|
64
|
+
p = _step.value;
|
|
65
|
+
if (p.type === 'Property' && p.key.type === 'Literal') {
|
|
66
|
+
key = p.key.value;
|
|
67
|
+
if (typeof key === 'string' && pkgMap.has(key)) {
|
|
68
|
+
isPrivateDependency = ((_pkgMap$get = pkgMap.get(key)) === null || _pkgMap$get === void 0 ? void 0 : _pkgMap$get.packageJson.private) === true;
|
|
69
|
+
if (isPrivateDependency) {
|
|
70
|
+
context.report({
|
|
71
|
+
node: node,
|
|
72
|
+
messageId: 'invalidPrivateDependency',
|
|
73
|
+
data: {
|
|
74
|
+
pkgName: key
|
|
75
|
+
}
|
|
76
|
+
});
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
} catch (err) {
|
|
82
|
+
_iterator.e(err);
|
|
83
|
+
} finally {
|
|
84
|
+
_iterator.f();
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
case 8:
|
|
88
|
+
case "end":
|
|
89
|
+
return _context.stop();
|
|
90
|
+
}
|
|
91
|
+
}, _callee);
|
|
92
|
+
}));
|
|
93
|
+
function ObjectExpression(_x) {
|
|
94
|
+
return _ObjectExpression.apply(this, arguments);
|
|
95
|
+
}
|
|
96
|
+
return ObjectExpression;
|
|
97
|
+
}()
|
|
98
|
+
};
|
|
99
|
+
}
|
|
100
|
+
};
|
|
101
|
+
var _default = exports.default = rule;
|
|
@@ -8,7 +8,6 @@ var _handleAstObject = require("../util/handle-ast-object");
|
|
|
8
8
|
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; } } }; }
|
|
9
9
|
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; } }
|
|
10
10
|
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; } // eslint-disable-next-line import/no-extraneous-dependencies
|
|
11
|
-
var workspaceProtocolRegex = /^workspace:[\^~\*]$/;
|
|
12
11
|
var rootProtocolRegex = /^root:[\^~\*]$/;
|
|
13
12
|
|
|
14
13
|
/**
|
|
@@ -21,7 +20,8 @@ function getYarnProtocolsUsed(node) {
|
|
|
21
20
|
};
|
|
22
21
|
var dependencies = (0, _handleAstObject.getObjectPropertyAsObject)(node, 'dependencies');
|
|
23
22
|
var devDependencies = (0, _handleAstObject.getObjectPropertyAsObject)(node, 'devDependencies');
|
|
24
|
-
|
|
23
|
+
var peerDependencies = (0, _handleAstObject.getObjectPropertyAsObject)(node, 'peerDependencies');
|
|
24
|
+
for (var _i = 0, _arr = [dependencies, devDependencies, peerDependencies]; _i < _arr.length; _i++) {
|
|
25
25
|
var obj = _arr[_i];
|
|
26
26
|
var _iterator = _createForOfIteratorHelper((obj === null || obj === void 0 ? void 0 : obj.properties) || []),
|
|
27
27
|
_step;
|
|
@@ -30,9 +30,6 @@ function getYarnProtocolsUsed(node) {
|
|
|
30
30
|
var p = _step.value;
|
|
31
31
|
if (p.type === 'Property' && p.value.type === 'Literal') {
|
|
32
32
|
if (typeof p.value.value === 'string') {
|
|
33
|
-
if (workspaceProtocolRegex.test(p.value.value)) {
|
|
34
|
-
protocolsUsed.workspace = true;
|
|
35
|
-
}
|
|
36
33
|
if (rootProtocolRegex.test(p.value.value)) {
|
|
37
34
|
protocolsUsed.root = true;
|
|
38
35
|
}
|
|
@@ -56,7 +53,7 @@ var rule = {
|
|
|
56
53
|
},
|
|
57
54
|
hasSuggestions: false,
|
|
58
55
|
messages: {
|
|
59
|
-
invalidWorkspaceProtocolUsage: "The 'workspace
|
|
56
|
+
invalidWorkspaceProtocolUsage: "The 'workspace:^' or 'workspace:~' protocol is Used. To resolve this error, please use the 'workspace:*' protocol instead.",
|
|
60
57
|
invalidRootProtocolUsage: "The 'root:' protocol is not allowed in platform packages. To resolve this error, replace the 'root:' protocol with specific package versions (e.g. '^1.0.0')."
|
|
61
58
|
}
|
|
62
59
|
},
|
|
@@ -75,15 +72,6 @@ var rule = {
|
|
|
75
72
|
messageId: 'invalidRootProtocolUsage'
|
|
76
73
|
});
|
|
77
74
|
}
|
|
78
|
-
|
|
79
|
-
// The 'workspace:' protocol can not be used in public packages
|
|
80
|
-
var isPrivatePackage = (0, _handleAstObject.getObjectPropertyAsLiteral)(node, 'private') === true;
|
|
81
|
-
if (!isPrivatePackage && yarnProtocolsUsed.workspace) {
|
|
82
|
-
context.report({
|
|
83
|
-
node: node,
|
|
84
|
-
messageId: 'invalidWorkspaceProtocolUsage'
|
|
85
|
-
});
|
|
86
|
-
}
|
|
87
75
|
}
|
|
88
76
|
};
|
|
89
77
|
}
|
package/dist/es2019/index.js
CHANGED
|
@@ -11,6 +11,7 @@ import ensureFeatureFlagPrefix from './rules/ensure-feature-flag-prefix';
|
|
|
11
11
|
import ensureCriticalDependencyResolutions from './rules/ensure-critical-dependency-resolutions';
|
|
12
12
|
import ensureValidPlatformYarnProtocolUsage from './rules/ensure-valid-platform-yarn-protocol-usage';
|
|
13
13
|
import ensureValidBinValues from './rules/ensure-valid-bin-values';
|
|
14
|
+
import ensureNoPrivateDependencies from './rules/ensure-no-private-dependencies';
|
|
14
15
|
import expandBorderShorthand from './rules/compiled/expand-border-shorthand';
|
|
15
16
|
import noInvalidStorybookDecoratorUsage from './rules/no-invalid-storybook-decorator-usage';
|
|
16
17
|
import ensurePublishValid from './rules/ensure-publish-valid';
|
|
@@ -36,6 +37,7 @@ const rules = {
|
|
|
36
37
|
'ensure-critical-dependency-resolutions': ensureCriticalDependencyResolutions,
|
|
37
38
|
'ensure-valid-platform-yarn-protocol-usage': ensureValidPlatformYarnProtocolUsage,
|
|
38
39
|
'ensure-valid-bin-values': ensureValidBinValues,
|
|
40
|
+
'ensure-no-private-dependencies': ensureNoPrivateDependencies,
|
|
39
41
|
'expand-border-shorthand': expandBorderShorthand,
|
|
40
42
|
'expand-background-shorthand': expandBackgroundShorthand,
|
|
41
43
|
'expand-spacing-shorthand': expandSpacingShorthand,
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
// eslint-disable-next-line import/no-extraneous-dependencies
|
|
2
|
+
|
|
3
|
+
import { getObjectPropertyAsObject, getObjectPropertyAsLiteral } from '../util/handle-ast-object';
|
|
4
|
+
import { getPackagesSync } from '@manypkg/get-packages';
|
|
5
|
+
import { findRootSync } from '@manypkg/find-root';
|
|
6
|
+
const root = findRootSync(process.cwd());
|
|
7
|
+
const pkgs = getPackagesSync(root).packages;
|
|
8
|
+
const pkgMap = new Map(pkgs.map(pkg => [pkg.packageJson.name, pkg]));
|
|
9
|
+
const rule = {
|
|
10
|
+
meta: {
|
|
11
|
+
type: 'problem',
|
|
12
|
+
docs: {
|
|
13
|
+
description: `Ensures that private dependencies are not used in published packages.`,
|
|
14
|
+
recommended: true
|
|
15
|
+
},
|
|
16
|
+
hasSuggestions: false,
|
|
17
|
+
messages: {
|
|
18
|
+
invalidPrivateDependency: `Published package has private dependency '{{ pkgName }}'. To resolve this error, remove the private dependency or set this package to private.`
|
|
19
|
+
}
|
|
20
|
+
},
|
|
21
|
+
create(context) {
|
|
22
|
+
return {
|
|
23
|
+
ObjectExpression: async node => {
|
|
24
|
+
// Only run this rule on package.json files
|
|
25
|
+
if (!context.filename.endsWith('package.json') || node.type !== 'ObjectExpression') {
|
|
26
|
+
return;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
// Private dependencies can be used in private packages
|
|
30
|
+
const isPrivatePkg = getObjectPropertyAsLiteral(node, 'private') === true;
|
|
31
|
+
if (isPrivatePkg === true) {
|
|
32
|
+
return;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
// Check for private dependencies in dependencies and peerDependencies
|
|
36
|
+
// Note: devDependencies are not checked here as they don't end up in consumer lockfiles
|
|
37
|
+
const dependencies = getObjectPropertyAsObject(node, 'dependencies');
|
|
38
|
+
const peerDependencies = getObjectPropertyAsObject(node, 'peerDependencies');
|
|
39
|
+
for (const obj of [dependencies, peerDependencies]) {
|
|
40
|
+
for (const p of (obj === null || obj === void 0 ? void 0 : obj.properties) || []) {
|
|
41
|
+
if (p.type === 'Property' && p.key.type === 'Literal') {
|
|
42
|
+
const key = p.key.value;
|
|
43
|
+
if (typeof key === 'string' && pkgMap.has(key)) {
|
|
44
|
+
var _pkgMap$get;
|
|
45
|
+
const isPrivateDependency = ((_pkgMap$get = pkgMap.get(key)) === null || _pkgMap$get === void 0 ? void 0 : _pkgMap$get.packageJson.private) === true;
|
|
46
|
+
if (isPrivateDependency) {
|
|
47
|
+
context.report({
|
|
48
|
+
node,
|
|
49
|
+
messageId: 'invalidPrivateDependency',
|
|
50
|
+
data: {
|
|
51
|
+
pkgName: key
|
|
52
|
+
}
|
|
53
|
+
});
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
};
|
|
61
|
+
}
|
|
62
|
+
};
|
|
63
|
+
export default rule;
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
// eslint-disable-next-line import/no-extraneous-dependencies
|
|
2
2
|
|
|
3
|
-
import { getObjectPropertyAsObject
|
|
4
|
-
const workspaceProtocolRegex = /^workspace:[\^~\*]$/;
|
|
3
|
+
import { getObjectPropertyAsObject } from '../util/handle-ast-object';
|
|
5
4
|
const rootProtocolRegex = /^root:[\^~\*]$/;
|
|
6
5
|
|
|
7
6
|
/**
|
|
@@ -14,13 +13,11 @@ function getYarnProtocolsUsed(node) {
|
|
|
14
13
|
};
|
|
15
14
|
const dependencies = getObjectPropertyAsObject(node, 'dependencies');
|
|
16
15
|
const devDependencies = getObjectPropertyAsObject(node, 'devDependencies');
|
|
17
|
-
|
|
16
|
+
const peerDependencies = getObjectPropertyAsObject(node, 'peerDependencies');
|
|
17
|
+
for (const obj of [dependencies, devDependencies, peerDependencies]) {
|
|
18
18
|
for (const p of (obj === null || obj === void 0 ? void 0 : obj.properties) || []) {
|
|
19
19
|
if (p.type === 'Property' && p.value.type === 'Literal') {
|
|
20
20
|
if (typeof p.value.value === 'string') {
|
|
21
|
-
if (workspaceProtocolRegex.test(p.value.value)) {
|
|
22
|
-
protocolsUsed.workspace = true;
|
|
23
|
-
}
|
|
24
21
|
if (rootProtocolRegex.test(p.value.value)) {
|
|
25
22
|
protocolsUsed.root = true;
|
|
26
23
|
}
|
|
@@ -39,7 +36,7 @@ const rule = {
|
|
|
39
36
|
},
|
|
40
37
|
hasSuggestions: false,
|
|
41
38
|
messages: {
|
|
42
|
-
invalidWorkspaceProtocolUsage: `The 'workspace
|
|
39
|
+
invalidWorkspaceProtocolUsage: `The 'workspace:^' or 'workspace:~' protocol is Used. To resolve this error, please use the 'workspace:*' protocol instead.`,
|
|
43
40
|
invalidRootProtocolUsage: `The 'root:' protocol is not allowed in platform packages. To resolve this error, replace the 'root:' protocol with specific package versions (e.g. '^1.0.0').`
|
|
44
41
|
}
|
|
45
42
|
},
|
|
@@ -58,15 +55,6 @@ const rule = {
|
|
|
58
55
|
messageId: 'invalidRootProtocolUsage'
|
|
59
56
|
});
|
|
60
57
|
}
|
|
61
|
-
|
|
62
|
-
// The 'workspace:' protocol can not be used in public packages
|
|
63
|
-
const isPrivatePackage = getObjectPropertyAsLiteral(node, 'private') === true;
|
|
64
|
-
if (!isPrivatePackage && yarnProtocolsUsed.workspace) {
|
|
65
|
-
context.report({
|
|
66
|
-
node,
|
|
67
|
-
messageId: 'invalidWorkspaceProtocolUsage'
|
|
68
|
-
});
|
|
69
|
-
}
|
|
70
58
|
}
|
|
71
59
|
};
|
|
72
60
|
}
|
package/dist/esm/index.js
CHANGED
|
@@ -14,6 +14,7 @@ import ensureFeatureFlagPrefix from './rules/ensure-feature-flag-prefix';
|
|
|
14
14
|
import ensureCriticalDependencyResolutions from './rules/ensure-critical-dependency-resolutions';
|
|
15
15
|
import ensureValidPlatformYarnProtocolUsage from './rules/ensure-valid-platform-yarn-protocol-usage';
|
|
16
16
|
import ensureValidBinValues from './rules/ensure-valid-bin-values';
|
|
17
|
+
import ensureNoPrivateDependencies from './rules/ensure-no-private-dependencies';
|
|
17
18
|
import expandBorderShorthand from './rules/compiled/expand-border-shorthand';
|
|
18
19
|
import noInvalidStorybookDecoratorUsage from './rules/no-invalid-storybook-decorator-usage';
|
|
19
20
|
import ensurePublishValid from './rules/ensure-publish-valid';
|
|
@@ -39,6 +40,7 @@ var rules = {
|
|
|
39
40
|
'ensure-critical-dependency-resolutions': ensureCriticalDependencyResolutions,
|
|
40
41
|
'ensure-valid-platform-yarn-protocol-usage': ensureValidPlatformYarnProtocolUsage,
|
|
41
42
|
'ensure-valid-bin-values': ensureValidBinValues,
|
|
43
|
+
'ensure-no-private-dependencies': ensureNoPrivateDependencies,
|
|
42
44
|
'expand-border-shorthand': expandBorderShorthand,
|
|
43
45
|
'expand-background-shorthand': expandBackgroundShorthand,
|
|
44
46
|
'expand-spacing-shorthand': expandSpacingShorthand,
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
import _asyncToGenerator from "@babel/runtime/helpers/asyncToGenerator";
|
|
2
|
+
import _regeneratorRuntime from "@babel/runtime/regenerator";
|
|
3
|
+
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; } } }; }
|
|
4
|
+
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; } }
|
|
5
|
+
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; }
|
|
6
|
+
// eslint-disable-next-line import/no-extraneous-dependencies
|
|
7
|
+
|
|
8
|
+
import { getObjectPropertyAsObject, getObjectPropertyAsLiteral } from '../util/handle-ast-object';
|
|
9
|
+
import { getPackagesSync } from '@manypkg/get-packages';
|
|
10
|
+
import { findRootSync } from '@manypkg/find-root';
|
|
11
|
+
var root = findRootSync(process.cwd());
|
|
12
|
+
var pkgs = getPackagesSync(root).packages;
|
|
13
|
+
var pkgMap = new Map(pkgs.map(function (pkg) {
|
|
14
|
+
return [pkg.packageJson.name, pkg];
|
|
15
|
+
}));
|
|
16
|
+
var rule = {
|
|
17
|
+
meta: {
|
|
18
|
+
type: 'problem',
|
|
19
|
+
docs: {
|
|
20
|
+
description: "Ensures that private dependencies are not used in published packages.",
|
|
21
|
+
recommended: true
|
|
22
|
+
},
|
|
23
|
+
hasSuggestions: false,
|
|
24
|
+
messages: {
|
|
25
|
+
invalidPrivateDependency: "Published package has private dependency '{{ pkgName }}'. To resolve this error, remove the private dependency or set this package to private."
|
|
26
|
+
}
|
|
27
|
+
},
|
|
28
|
+
create: function create(context) {
|
|
29
|
+
return {
|
|
30
|
+
ObjectExpression: function () {
|
|
31
|
+
var _ObjectExpression = _asyncToGenerator( /*#__PURE__*/_regeneratorRuntime.mark(function _callee(node) {
|
|
32
|
+
var isPrivatePkg, dependencies, peerDependencies, _i, _arr, obj, _iterator, _step, p, key, _pkgMap$get, isPrivateDependency;
|
|
33
|
+
return _regeneratorRuntime.wrap(function _callee$(_context) {
|
|
34
|
+
while (1) switch (_context.prev = _context.next) {
|
|
35
|
+
case 0:
|
|
36
|
+
if (!(!context.filename.endsWith('package.json') || node.type !== 'ObjectExpression')) {
|
|
37
|
+
_context.next = 2;
|
|
38
|
+
break;
|
|
39
|
+
}
|
|
40
|
+
return _context.abrupt("return");
|
|
41
|
+
case 2:
|
|
42
|
+
// Private dependencies can be used in private packages
|
|
43
|
+
isPrivatePkg = getObjectPropertyAsLiteral(node, 'private') === true;
|
|
44
|
+
if (!(isPrivatePkg === true)) {
|
|
45
|
+
_context.next = 5;
|
|
46
|
+
break;
|
|
47
|
+
}
|
|
48
|
+
return _context.abrupt("return");
|
|
49
|
+
case 5:
|
|
50
|
+
// Check for private dependencies in dependencies and peerDependencies
|
|
51
|
+
// Note: devDependencies are not checked here as they don't end up in consumer lockfiles
|
|
52
|
+
dependencies = getObjectPropertyAsObject(node, 'dependencies');
|
|
53
|
+
peerDependencies = getObjectPropertyAsObject(node, 'peerDependencies');
|
|
54
|
+
for (_i = 0, _arr = [dependencies, peerDependencies]; _i < _arr.length; _i++) {
|
|
55
|
+
obj = _arr[_i];
|
|
56
|
+
_iterator = _createForOfIteratorHelper((obj === null || obj === void 0 ? void 0 : obj.properties) || []);
|
|
57
|
+
try {
|
|
58
|
+
for (_iterator.s(); !(_step = _iterator.n()).done;) {
|
|
59
|
+
p = _step.value;
|
|
60
|
+
if (p.type === 'Property' && p.key.type === 'Literal') {
|
|
61
|
+
key = p.key.value;
|
|
62
|
+
if (typeof key === 'string' && pkgMap.has(key)) {
|
|
63
|
+
isPrivateDependency = ((_pkgMap$get = pkgMap.get(key)) === null || _pkgMap$get === void 0 ? void 0 : _pkgMap$get.packageJson.private) === true;
|
|
64
|
+
if (isPrivateDependency) {
|
|
65
|
+
context.report({
|
|
66
|
+
node: node,
|
|
67
|
+
messageId: 'invalidPrivateDependency',
|
|
68
|
+
data: {
|
|
69
|
+
pkgName: key
|
|
70
|
+
}
|
|
71
|
+
});
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
} catch (err) {
|
|
77
|
+
_iterator.e(err);
|
|
78
|
+
} finally {
|
|
79
|
+
_iterator.f();
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
case 8:
|
|
83
|
+
case "end":
|
|
84
|
+
return _context.stop();
|
|
85
|
+
}
|
|
86
|
+
}, _callee);
|
|
87
|
+
}));
|
|
88
|
+
function ObjectExpression(_x) {
|
|
89
|
+
return _ObjectExpression.apply(this, arguments);
|
|
90
|
+
}
|
|
91
|
+
return ObjectExpression;
|
|
92
|
+
}()
|
|
93
|
+
};
|
|
94
|
+
}
|
|
95
|
+
};
|
|
96
|
+
export default rule;
|
|
@@ -3,8 +3,7 @@ function _unsupportedIterableToArray(r, a) { if (r) { if ("string" == typeof r)
|
|
|
3
3
|
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; }
|
|
4
4
|
// eslint-disable-next-line import/no-extraneous-dependencies
|
|
5
5
|
|
|
6
|
-
import { getObjectPropertyAsObject
|
|
7
|
-
var workspaceProtocolRegex = /^workspace:[\^~\*]$/;
|
|
6
|
+
import { getObjectPropertyAsObject } from '../util/handle-ast-object';
|
|
8
7
|
var rootProtocolRegex = /^root:[\^~\*]$/;
|
|
9
8
|
|
|
10
9
|
/**
|
|
@@ -17,7 +16,8 @@ function getYarnProtocolsUsed(node) {
|
|
|
17
16
|
};
|
|
18
17
|
var dependencies = getObjectPropertyAsObject(node, 'dependencies');
|
|
19
18
|
var devDependencies = getObjectPropertyAsObject(node, 'devDependencies');
|
|
20
|
-
|
|
19
|
+
var peerDependencies = getObjectPropertyAsObject(node, 'peerDependencies');
|
|
20
|
+
for (var _i = 0, _arr = [dependencies, devDependencies, peerDependencies]; _i < _arr.length; _i++) {
|
|
21
21
|
var obj = _arr[_i];
|
|
22
22
|
var _iterator = _createForOfIteratorHelper((obj === null || obj === void 0 ? void 0 : obj.properties) || []),
|
|
23
23
|
_step;
|
|
@@ -26,9 +26,6 @@ function getYarnProtocolsUsed(node) {
|
|
|
26
26
|
var p = _step.value;
|
|
27
27
|
if (p.type === 'Property' && p.value.type === 'Literal') {
|
|
28
28
|
if (typeof p.value.value === 'string') {
|
|
29
|
-
if (workspaceProtocolRegex.test(p.value.value)) {
|
|
30
|
-
protocolsUsed.workspace = true;
|
|
31
|
-
}
|
|
32
29
|
if (rootProtocolRegex.test(p.value.value)) {
|
|
33
30
|
protocolsUsed.root = true;
|
|
34
31
|
}
|
|
@@ -52,7 +49,7 @@ var rule = {
|
|
|
52
49
|
},
|
|
53
50
|
hasSuggestions: false,
|
|
54
51
|
messages: {
|
|
55
|
-
invalidWorkspaceProtocolUsage: "The 'workspace
|
|
52
|
+
invalidWorkspaceProtocolUsage: "The 'workspace:^' or 'workspace:~' protocol is Used. To resolve this error, please use the 'workspace:*' protocol instead.",
|
|
56
53
|
invalidRootProtocolUsage: "The 'root:' protocol is not allowed in platform packages. To resolve this error, replace the 'root:' protocol with specific package versions (e.g. '^1.0.0')."
|
|
57
54
|
}
|
|
58
55
|
},
|
|
@@ -71,15 +68,6 @@ var rule = {
|
|
|
71
68
|
messageId: 'invalidRootProtocolUsage'
|
|
72
69
|
});
|
|
73
70
|
}
|
|
74
|
-
|
|
75
|
-
// The 'workspace:' protocol can not be used in public packages
|
|
76
|
-
var isPrivatePackage = getObjectPropertyAsLiteral(node, 'private') === true;
|
|
77
|
-
if (!isPrivatePackage && yarnProtocolsUsed.workspace) {
|
|
78
|
-
context.report({
|
|
79
|
-
node: node,
|
|
80
|
-
messageId: 'invalidWorkspaceProtocolUsage'
|
|
81
|
-
});
|
|
82
|
-
}
|
|
83
71
|
}
|
|
84
72
|
};
|
|
85
73
|
}
|
package/dist/types/index.d.ts
CHANGED
|
@@ -8,6 +8,7 @@ declare const rules: {
|
|
|
8
8
|
'ensure-critical-dependency-resolutions': import("eslint").Rule.RuleModule;
|
|
9
9
|
'ensure-valid-platform-yarn-protocol-usage': import("eslint").Rule.RuleModule;
|
|
10
10
|
'ensure-valid-bin-values': import("eslint").Rule.RuleModule;
|
|
11
|
+
'ensure-no-private-dependencies': import("eslint").Rule.RuleModule;
|
|
11
12
|
'expand-border-shorthand': import("eslint").Rule.RuleModule;
|
|
12
13
|
'expand-background-shorthand': import("eslint").Rule.RuleModule;
|
|
13
14
|
'expand-spacing-shorthand': import("eslint").Rule.RuleModule;
|
|
@@ -41,6 +42,7 @@ declare const plugin: {
|
|
|
41
42
|
'ensure-critical-dependency-resolutions': import("eslint").Rule.RuleModule;
|
|
42
43
|
'ensure-valid-platform-yarn-protocol-usage': import("eslint").Rule.RuleModule;
|
|
43
44
|
'ensure-valid-bin-values': import("eslint").Rule.RuleModule;
|
|
45
|
+
'ensure-no-private-dependencies': import("eslint").Rule.RuleModule;
|
|
44
46
|
'expand-border-shorthand': import("eslint").Rule.RuleModule;
|
|
45
47
|
'expand-background-shorthand': import("eslint").Rule.RuleModule;
|
|
46
48
|
'expand-spacing-shorthand': import("eslint").Rule.RuleModule;
|
|
@@ -8,6 +8,7 @@ declare const rules: {
|
|
|
8
8
|
'ensure-critical-dependency-resolutions': import("eslint").Rule.RuleModule;
|
|
9
9
|
'ensure-valid-platform-yarn-protocol-usage': import("eslint").Rule.RuleModule;
|
|
10
10
|
'ensure-valid-bin-values': import("eslint").Rule.RuleModule;
|
|
11
|
+
'ensure-no-private-dependencies': import("eslint").Rule.RuleModule;
|
|
11
12
|
'expand-border-shorthand': import("eslint").Rule.RuleModule;
|
|
12
13
|
'expand-background-shorthand': import("eslint").Rule.RuleModule;
|
|
13
14
|
'expand-spacing-shorthand': import("eslint").Rule.RuleModule;
|
|
@@ -41,6 +42,7 @@ declare const plugin: {
|
|
|
41
42
|
'ensure-critical-dependency-resolutions': import("eslint").Rule.RuleModule;
|
|
42
43
|
'ensure-valid-platform-yarn-protocol-usage': import("eslint").Rule.RuleModule;
|
|
43
44
|
'ensure-valid-bin-values': import("eslint").Rule.RuleModule;
|
|
45
|
+
'ensure-no-private-dependencies': import("eslint").Rule.RuleModule;
|
|
44
46
|
'expand-border-shorthand': import("eslint").Rule.RuleModule;
|
|
45
47
|
'expand-background-shorthand': import("eslint").Rule.RuleModule;
|
|
46
48
|
'expand-spacing-shorthand': import("eslint").Rule.RuleModule;
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@atlaskit/eslint-plugin-platform",
|
|
3
3
|
"description": "The essential plugin for use with Atlassian frontend platform tools",
|
|
4
|
-
"version": "2.
|
|
4
|
+
"version": "2.5.0",
|
|
5
5
|
"author": "Atlassian Pty Ltd",
|
|
6
6
|
"atlassian": {
|
|
7
7
|
"team": "Build Infra",
|
|
@@ -25,7 +25,9 @@
|
|
|
25
25
|
]
|
|
26
26
|
}
|
|
27
27
|
},
|
|
28
|
-
"sideEffects":
|
|
28
|
+
"sideEffects": [
|
|
29
|
+
"*.compiled.css"
|
|
30
|
+
],
|
|
29
31
|
"atlaskit:src": "src/index.tsx",
|
|
30
32
|
"af:exports": {
|
|
31
33
|
".": "./src/index.tsx"
|
|
@@ -35,11 +37,12 @@
|
|
|
35
37
|
"@babel/runtime": "^7.0.0",
|
|
36
38
|
"@compiled/eslint-plugin": "^0.18.2",
|
|
37
39
|
"@manypkg/find-root": "^1.1.0",
|
|
40
|
+
"@manypkg/get-packages": "^1.1.3",
|
|
38
41
|
"fuse.js": "^6.6.2",
|
|
39
42
|
"read-pkg-up": "^7.0.1"
|
|
40
43
|
},
|
|
41
44
|
"devDependencies": {
|
|
42
|
-
"@atlassian/ts-loader": "
|
|
45
|
+
"@atlassian/ts-loader": "^0.1.0",
|
|
43
46
|
"@types/eslint": "^8.56.6",
|
|
44
47
|
"eslint": "^8.57.0",
|
|
45
48
|
"outdent": "^0.5.0"
|
package/src/index.tsx
CHANGED
|
@@ -12,6 +12,7 @@ import ensureFeatureFlagPrefix from './rules/ensure-feature-flag-prefix';
|
|
|
12
12
|
import ensureCriticalDependencyResolutions from './rules/ensure-critical-dependency-resolutions';
|
|
13
13
|
import ensureValidPlatformYarnProtocolUsage from './rules/ensure-valid-platform-yarn-protocol-usage';
|
|
14
14
|
import ensureValidBinValues from './rules/ensure-valid-bin-values';
|
|
15
|
+
import ensureNoPrivateDependencies from './rules/ensure-no-private-dependencies';
|
|
15
16
|
import expandBorderShorthand from './rules/compiled/expand-border-shorthand';
|
|
16
17
|
import noInvalidStorybookDecoratorUsage from './rules/no-invalid-storybook-decorator-usage';
|
|
17
18
|
import ensurePublishValid from './rules/ensure-publish-valid';
|
|
@@ -43,6 +44,7 @@ const rules = {
|
|
|
43
44
|
'ensure-critical-dependency-resolutions': ensureCriticalDependencyResolutions,
|
|
44
45
|
'ensure-valid-platform-yarn-protocol-usage': ensureValidPlatformYarnProtocolUsage,
|
|
45
46
|
'ensure-valid-bin-values': ensureValidBinValues,
|
|
47
|
+
'ensure-no-private-dependencies': ensureNoPrivateDependencies,
|
|
46
48
|
'expand-border-shorthand': expandBorderShorthand,
|
|
47
49
|
'expand-background-shorthand': expandBackgroundShorthand,
|
|
48
50
|
'expand-spacing-shorthand': expandSpacingShorthand,
|
|
@@ -0,0 +1,212 @@
|
|
|
1
|
+
import { tester } from '../../../../__tests__/utils/_tester';
|
|
2
|
+
import rule from '../../index';
|
|
3
|
+
|
|
4
|
+
const cwd = process.cwd();
|
|
5
|
+
|
|
6
|
+
jest.mock('@manypkg/find-root');
|
|
7
|
+
|
|
8
|
+
jest.mock('@manypkg/get-packages', () => ({
|
|
9
|
+
getPackagesSync: () => {
|
|
10
|
+
return {
|
|
11
|
+
packages: [
|
|
12
|
+
// Private packages
|
|
13
|
+
{
|
|
14
|
+
packageJson: {
|
|
15
|
+
name: '@af/private-pkg',
|
|
16
|
+
private: true,
|
|
17
|
+
},
|
|
18
|
+
},
|
|
19
|
+
{
|
|
20
|
+
packageJson: {
|
|
21
|
+
name: '@atlassian/private-pkg',
|
|
22
|
+
private: true,
|
|
23
|
+
},
|
|
24
|
+
},
|
|
25
|
+
{
|
|
26
|
+
packageJson: {
|
|
27
|
+
name: '@atlassiansox/private-pkg',
|
|
28
|
+
private: true,
|
|
29
|
+
},
|
|
30
|
+
},
|
|
31
|
+
// Public packages
|
|
32
|
+
{
|
|
33
|
+
packageJson: {
|
|
34
|
+
name: '@atlassian/public-pkg',
|
|
35
|
+
publishConfig: { registry: 'https://pkg-registry.com' },
|
|
36
|
+
},
|
|
37
|
+
},
|
|
38
|
+
{
|
|
39
|
+
packageJson: {
|
|
40
|
+
name: '@atlassiansox/public-pkg',
|
|
41
|
+
publishConfig: { registry: 'https://pkg-registry.com' },
|
|
42
|
+
},
|
|
43
|
+
},
|
|
44
|
+
{
|
|
45
|
+
packageJson: {
|
|
46
|
+
name: '@atlaskit/public-pkg',
|
|
47
|
+
publishConfig: { registry: 'https://pkg-registry.com' },
|
|
48
|
+
},
|
|
49
|
+
},
|
|
50
|
+
],
|
|
51
|
+
};
|
|
52
|
+
},
|
|
53
|
+
}));
|
|
54
|
+
|
|
55
|
+
const getErrorMessage = (pkgName: string) =>
|
|
56
|
+
`Published package has private dependency '${pkgName}'. To resolve this error, remove the private dependency or set this package to private.`;
|
|
57
|
+
|
|
58
|
+
describe('test ensure-no-private-dependencies', () => {
|
|
59
|
+
tester.run('ensure-no-private-dependencies', rule, {
|
|
60
|
+
valid: [
|
|
61
|
+
// Private and public dependencies are allowed in private '@af' scoped packages
|
|
62
|
+
{
|
|
63
|
+
code: `const foo = {
|
|
64
|
+
"name:": "@af/test",
|
|
65
|
+
"private": true,
|
|
66
|
+
"dependencies": {
|
|
67
|
+
"@af/private-pkg": "workspace:*",
|
|
68
|
+
"@af/public-pkg": "workspace:*",
|
|
69
|
+
"@atlassian/private-pkg": "workspace:*",
|
|
70
|
+
"@atlassian/public-pkg": "workspace:*",
|
|
71
|
+
"@atlassiansox/private-pkg": "workspace:*",
|
|
72
|
+
"@atlassiansox/public-pkg": "workspace:*",
|
|
73
|
+
"react": "root:*"
|
|
74
|
+
}
|
|
75
|
+
}`,
|
|
76
|
+
filename: `${cwd}/packages/fpp/package.json`,
|
|
77
|
+
},
|
|
78
|
+
// Private and public dependencies are allowed in private '@atlassian' scoped packages
|
|
79
|
+
{
|
|
80
|
+
code: `const foo = {
|
|
81
|
+
"name:": "@atlassian/test",
|
|
82
|
+
"private": true,
|
|
83
|
+
"dependencies": {
|
|
84
|
+
"@af/private-pkg": "workspace:*",
|
|
85
|
+
"@af/public-pkg": "workspace:*",
|
|
86
|
+
"@atlassian/private-pkg": "workspace:*",
|
|
87
|
+
"@atlassian/public-pkg": "workspace:*",
|
|
88
|
+
"@atlassiansox/private-pkg": "workspace:*",
|
|
89
|
+
"@atlassiansox/public-pkg": "workspace:*",
|
|
90
|
+
"react": "root:*"
|
|
91
|
+
}
|
|
92
|
+
}`,
|
|
93
|
+
filename: `${cwd}/packages/fpp/package.json`,
|
|
94
|
+
},
|
|
95
|
+
// Private dependencies are allowed in private '@atlassiansox' scoped packages
|
|
96
|
+
{
|
|
97
|
+
code: `const foo = {
|
|
98
|
+
"name:": "@atlassiansox/test",
|
|
99
|
+
"private": true,
|
|
100
|
+
"dependencies": {
|
|
101
|
+
"@af/private-pkg": "workspace:*",
|
|
102
|
+
"@atlassian/private-pkg": "workspace:*",
|
|
103
|
+
"@atlassiansox/private-pkg": "workspace:*",
|
|
104
|
+
"react": "root:*"
|
|
105
|
+
}
|
|
106
|
+
}`,
|
|
107
|
+
filename: `${cwd}/packages/foo/package.json`,
|
|
108
|
+
},
|
|
109
|
+
// Private and public dependencies are allowed in private '@atlassiansox' scoped packages
|
|
110
|
+
{
|
|
111
|
+
code: `const foo = {
|
|
112
|
+
"name:": "@atlassiansox/test",
|
|
113
|
+
"private": true,
|
|
114
|
+
"dependencies": {
|
|
115
|
+
"@af/private-pkg": "workspace:*",
|
|
116
|
+
"@af/public-pkg": "workspace:*",
|
|
117
|
+
"@atlassian/private-pkg": "workspace:*",
|
|
118
|
+
"@atlassian/public-pkg": "workspace:*",
|
|
119
|
+
"@atlassiansox/private-pkg": "workspace:*",
|
|
120
|
+
"@atlassiansox/public-pkg": "workspace:*",
|
|
121
|
+
"react": "root:*"
|
|
122
|
+
}
|
|
123
|
+
}`,
|
|
124
|
+
filename: `${cwd}/packages/fpp/package.json`,
|
|
125
|
+
},
|
|
126
|
+
],
|
|
127
|
+
invalid: [
|
|
128
|
+
// Disallow private dependencies in public '@atlaskit' scoped packages
|
|
129
|
+
{
|
|
130
|
+
code: `const foo = {
|
|
131
|
+
"name": "@atlaskit/test",
|
|
132
|
+
"publishConfig": { "registry": "https://pkg-registry.com" },
|
|
133
|
+
"dependencies": {
|
|
134
|
+
"@af/private-pkg": "workspace:*",
|
|
135
|
+
"@atlassian/private-pkg": "workspace:*",
|
|
136
|
+
"@atlassiansox/private-pkg": "workspace:*",
|
|
137
|
+
"react": "root:*"
|
|
138
|
+
}
|
|
139
|
+
}`,
|
|
140
|
+
filename: `${cwd}/packages/foo/package.json`,
|
|
141
|
+
errors: [
|
|
142
|
+
{
|
|
143
|
+
message:
|
|
144
|
+
"Published package has private dependency '@af/private-pkg'. To resolve this error, remove the private dependency or set this package to private.",
|
|
145
|
+
},
|
|
146
|
+
{
|
|
147
|
+
message:
|
|
148
|
+
"Published package has private dependency '@atlassian/private-pkg'. To resolve this error, remove the private dependency or set this package to private.",
|
|
149
|
+
},
|
|
150
|
+
{
|
|
151
|
+
message:
|
|
152
|
+
"Published package has private dependency '@atlassiansox/private-pkg'. To resolve this error, remove the private dependency or set this package to private.",
|
|
153
|
+
},
|
|
154
|
+
],
|
|
155
|
+
},
|
|
156
|
+
// Disallow private dependencies in public '@atlassian' scoped packages
|
|
157
|
+
{
|
|
158
|
+
code: `const foo = {
|
|
159
|
+
"name": "@atlassian/test",
|
|
160
|
+
"publishConfig": { "registry": "https://registry.npmjs.org/" },
|
|
161
|
+
"dependencies": {
|
|
162
|
+
"@af/private-pkg": "workspace:*",
|
|
163
|
+
"@atlassian/private-pkg": "workspace:*",
|
|
164
|
+
"@atlassiansox/private-pkg": "workspace:*"
|
|
165
|
+
}
|
|
166
|
+
}`,
|
|
167
|
+
filename: `${cwd}/packages/foo/package.json`,
|
|
168
|
+
errors: [
|
|
169
|
+
{ message: getErrorMessage('@af/private-pkg') },
|
|
170
|
+
{ message: getErrorMessage('@atlassian/private-pkg') },
|
|
171
|
+
{ message: getErrorMessage('@atlassiansox/private-pkg') },
|
|
172
|
+
],
|
|
173
|
+
},
|
|
174
|
+
// Disallow private dependencies in public '@atlassiansox' scoped packages
|
|
175
|
+
{
|
|
176
|
+
code: `const foo = {
|
|
177
|
+
"name": "@atlassian/test3",
|
|
178
|
+
"publishConfig": { "registry": "https://registry.npmjs.org/" },
|
|
179
|
+
"dependencies": {
|
|
180
|
+
"@af/private-pkg": "workspace:*",
|
|
181
|
+
"@atlassian/private-pkg": "workspace:*",
|
|
182
|
+
"@atlassiansox/private-pkg": "workspace:*"
|
|
183
|
+
}
|
|
184
|
+
}`,
|
|
185
|
+
filename: `${cwd}/packages/foo/package.json`,
|
|
186
|
+
errors: [
|
|
187
|
+
{ message: getErrorMessage('@af/private-pkg') },
|
|
188
|
+
{ message: getErrorMessage('@atlassian/private-pkg') },
|
|
189
|
+
{ message: getErrorMessage('@atlassiansox/private-pkg') },
|
|
190
|
+
],
|
|
191
|
+
},
|
|
192
|
+
// Disallow private peer dependencies
|
|
193
|
+
{
|
|
194
|
+
code: `const foo = {
|
|
195
|
+
"name": "@atlaskit/test",
|
|
196
|
+
"publishConfig": { "registry": "https://registry.npmjs.org/" },
|
|
197
|
+
"peerDependencies": {
|
|
198
|
+
"@af/private-pkg": "workspace:*",
|
|
199
|
+
"@atlassian/private-pkg": "workspace:*",
|
|
200
|
+
"@atlassiansox/private-pkg": "workspace:*"
|
|
201
|
+
}
|
|
202
|
+
}`,
|
|
203
|
+
filename: `${cwd}/packages/foo/package.json`,
|
|
204
|
+
errors: [
|
|
205
|
+
{ message: getErrorMessage('@af/private-pkg') },
|
|
206
|
+
{ message: getErrorMessage('@atlassian/private-pkg') },
|
|
207
|
+
{ message: getErrorMessage('@atlassiansox/private-pkg') },
|
|
208
|
+
],
|
|
209
|
+
},
|
|
210
|
+
],
|
|
211
|
+
});
|
|
212
|
+
});
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
// eslint-disable-next-line import/no-extraneous-dependencies
|
|
2
|
+
import type { Rule } from 'eslint';
|
|
3
|
+
import { getObjectPropertyAsObject, getObjectPropertyAsLiteral } from '../util/handle-ast-object';
|
|
4
|
+
import { getPackagesSync } from '@manypkg/get-packages';
|
|
5
|
+
import { findRootSync } from '@manypkg/find-root';
|
|
6
|
+
|
|
7
|
+
const root = findRootSync(process.cwd());
|
|
8
|
+
const pkgs = getPackagesSync(root).packages;
|
|
9
|
+
const pkgMap = new Map(pkgs.map((pkg) => [pkg.packageJson.name, pkg]));
|
|
10
|
+
|
|
11
|
+
const rule: Rule.RuleModule = {
|
|
12
|
+
meta: {
|
|
13
|
+
type: 'problem',
|
|
14
|
+
docs: {
|
|
15
|
+
description: `Ensures that private dependencies are not used in published packages.`,
|
|
16
|
+
recommended: true,
|
|
17
|
+
},
|
|
18
|
+
hasSuggestions: false,
|
|
19
|
+
messages: {
|
|
20
|
+
invalidPrivateDependency: `Published package has private dependency '{{ pkgName }}'. To resolve this error, remove the private dependency or set this package to private.`,
|
|
21
|
+
},
|
|
22
|
+
},
|
|
23
|
+
create(context) {
|
|
24
|
+
return {
|
|
25
|
+
ObjectExpression: async (node: Rule.Node) => {
|
|
26
|
+
// Only run this rule on package.json files
|
|
27
|
+
if (!context.filename.endsWith('package.json') || node.type !== 'ObjectExpression') {
|
|
28
|
+
return;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
// Private dependencies can be used in private packages
|
|
32
|
+
const isPrivatePkg = getObjectPropertyAsLiteral(node, 'private') === true;
|
|
33
|
+
if (isPrivatePkg === true) {
|
|
34
|
+
return;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
// Check for private dependencies in dependencies and peerDependencies
|
|
38
|
+
// Note: devDependencies are not checked here as they don't end up in consumer lockfiles
|
|
39
|
+
const dependencies = getObjectPropertyAsObject(node, 'dependencies');
|
|
40
|
+
const peerDependencies = getObjectPropertyAsObject(node, 'peerDependencies');
|
|
41
|
+
|
|
42
|
+
for (const obj of [dependencies, peerDependencies]) {
|
|
43
|
+
for (const p of obj?.properties || []) {
|
|
44
|
+
if (p.type === 'Property' && p.key.type === 'Literal') {
|
|
45
|
+
const key = p.key.value;
|
|
46
|
+
if (typeof key === 'string' && pkgMap.has(key)) {
|
|
47
|
+
const isPrivateDependency = pkgMap.get(key)?.packageJson.private === true;
|
|
48
|
+
if (isPrivateDependency) {
|
|
49
|
+
context.report({
|
|
50
|
+
node,
|
|
51
|
+
messageId: 'invalidPrivateDependency',
|
|
52
|
+
data: { pkgName: key },
|
|
53
|
+
});
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
},
|
|
60
|
+
};
|
|
61
|
+
},
|
|
62
|
+
};
|
|
63
|
+
|
|
64
|
+
export default rule;
|
|
@@ -6,95 +6,43 @@ const cwd = process.cwd();
|
|
|
6
6
|
describe('test ensure-valid-platform-yarn-protocol-usage rule', () => {
|
|
7
7
|
tester.run('workspace protocol', rule, {
|
|
8
8
|
valid: [
|
|
9
|
-
// Workspace protocol is allowed in
|
|
9
|
+
// Workspace protocol 'workspace:*' is allowed in packages as dependencies
|
|
10
10
|
{
|
|
11
11
|
code: `const foo = {
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
}`,
|
|
12
|
+
"dependencies": {
|
|
13
|
+
"@atlaskit/button": "workspace:*",
|
|
14
|
+
"@atlaskit/primitives": "workspace:*",
|
|
15
|
+
"@atlaskit/tokens": "workspace:*"
|
|
16
|
+
}
|
|
17
|
+
}`,
|
|
19
18
|
filename: `${cwd}/packages/foo/package.json`,
|
|
20
19
|
},
|
|
21
|
-
// Workspace protocol is allowed in
|
|
20
|
+
// Workspace protocol 'workspace:*' is allowed in packages as devDependencies
|
|
22
21
|
{
|
|
23
22
|
code: `const foo = {
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
}`,
|
|
23
|
+
"devDependencies": {
|
|
24
|
+
"@atlaskit/button": "workspace:*",
|
|
25
|
+
"@atlaskit/primitives": "workspace:*"
|
|
26
|
+
}
|
|
27
|
+
}`,
|
|
30
28
|
filename: `${cwd}/packages/foo/package.json`,
|
|
31
29
|
},
|
|
32
|
-
// Workspace protocol is allowed in
|
|
30
|
+
// Workspace protocol is allowed in packages dependencies and devDependencies
|
|
33
31
|
{
|
|
34
32
|
code: `const foo = {
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
}`,
|
|
33
|
+
"dependencies": {
|
|
34
|
+
"@atlaskit/button": "workspace:*",
|
|
35
|
+
"@atlaskit/primitives": "workspace:*"
|
|
36
|
+
},
|
|
37
|
+
"devDependencies": {
|
|
38
|
+
"@atlaskit/button": "workspace:*",
|
|
39
|
+
"@atlaskit/primitives": "workspace:*"
|
|
40
|
+
}
|
|
41
|
+
}`,
|
|
45
42
|
filename: `${cwd}/packages/foo/package.json`,
|
|
46
43
|
},
|
|
47
44
|
],
|
|
48
|
-
invalid: [
|
|
49
|
-
// Workspace protocol is not allowed in public packages as dependencies
|
|
50
|
-
{
|
|
51
|
-
code: `const foo = {
|
|
52
|
-
"dependencies": {
|
|
53
|
-
"@atlaskit/button": "workspace:^",
|
|
54
|
-
"@atlaskit/primitives": "workspace:*",
|
|
55
|
-
"@atlaskit/tokens": "workspace:*"
|
|
56
|
-
}
|
|
57
|
-
}`,
|
|
58
|
-
filename: `${cwd}/packages/foo/package.json`,
|
|
59
|
-
errors: [
|
|
60
|
-
{
|
|
61
|
-
messageId: 'invalidWorkspaceProtocolUsage',
|
|
62
|
-
},
|
|
63
|
-
],
|
|
64
|
-
},
|
|
65
|
-
// Workspace protocol is not allowed in public packages as devDependencies
|
|
66
|
-
{
|
|
67
|
-
code: `const foo = {
|
|
68
|
-
"devDependencies": {
|
|
69
|
-
"@atlaskit/button": "workspace:^",
|
|
70
|
-
"@atlaskit/primitives": "^1.0.0"
|
|
71
|
-
}
|
|
72
|
-
}`,
|
|
73
|
-
filename: `${cwd}/packages/foo/package.json`,
|
|
74
|
-
errors: [
|
|
75
|
-
{
|
|
76
|
-
messageId: 'invalidWorkspaceProtocolUsage',
|
|
77
|
-
},
|
|
78
|
-
],
|
|
79
|
-
},
|
|
80
|
-
// Workspace protocol is not allowed in public packages as dependencies and devDependencies
|
|
81
|
-
{
|
|
82
|
-
code: `const foo = {
|
|
83
|
-
"dependencies": {
|
|
84
|
-
"@atlaskit/button": "workspace:^"
|
|
85
|
-
},
|
|
86
|
-
"devDependencies": {
|
|
87
|
-
"@atlaskit/primitives": "workspace:^"
|
|
88
|
-
}
|
|
89
|
-
}`,
|
|
90
|
-
filename: `${cwd}/packages/foo/package.json`,
|
|
91
|
-
errors: [
|
|
92
|
-
{
|
|
93
|
-
messageId: 'invalidWorkspaceProtocolUsage',
|
|
94
|
-
},
|
|
95
|
-
],
|
|
96
|
-
},
|
|
97
|
-
],
|
|
45
|
+
invalid: [],
|
|
98
46
|
});
|
|
99
47
|
|
|
100
48
|
tester.run('root: protocol', rule, {
|
|
@@ -104,10 +52,10 @@ describe('test ensure-valid-platform-yarn-protocol-usage rule', () => {
|
|
|
104
52
|
// 'root:' protocol is not allowed in packages as dependencies
|
|
105
53
|
{
|
|
106
54
|
code: `const foo = {
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
55
|
+
"dependencies": {
|
|
56
|
+
"react": "root:*",
|
|
57
|
+
}
|
|
58
|
+
}`,
|
|
111
59
|
filename: `${cwd}/packages/foo/package.json`,
|
|
112
60
|
errors: [
|
|
113
61
|
{
|
|
@@ -118,11 +66,11 @@ describe('test ensure-valid-platform-yarn-protocol-usage rule', () => {
|
|
|
118
66
|
// 'root:' protocol is not allowed in private packages as dependencies
|
|
119
67
|
{
|
|
120
68
|
code: `const foo = {
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
69
|
+
"private": true,
|
|
70
|
+
"dependencies": {
|
|
71
|
+
"react": "root:*",
|
|
72
|
+
}
|
|
73
|
+
}`,
|
|
126
74
|
filename: `${cwd}/packages/foo/package.json`,
|
|
127
75
|
errors: [
|
|
128
76
|
{
|
|
@@ -133,10 +81,10 @@ describe('test ensure-valid-platform-yarn-protocol-usage rule', () => {
|
|
|
133
81
|
// 'root:' protocol is not allowed in public packages as devDependencies
|
|
134
82
|
{
|
|
135
83
|
code: `const foo = {
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
84
|
+
"devDependencies": {
|
|
85
|
+
"react": "root:*",
|
|
86
|
+
}
|
|
87
|
+
}`,
|
|
140
88
|
filename: `${cwd}/packages/foo/package.json`,
|
|
141
89
|
errors: [
|
|
142
90
|
{
|
|
@@ -147,11 +95,11 @@ describe('test ensure-valid-platform-yarn-protocol-usage rule', () => {
|
|
|
147
95
|
// 'root:' protocol is not allowed in private packages as devDependencies
|
|
148
96
|
{
|
|
149
97
|
code: `const foo = {
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
98
|
+
"private": true,
|
|
99
|
+
"devDependencies": {
|
|
100
|
+
"react": "root:*",
|
|
101
|
+
}
|
|
102
|
+
}`,
|
|
155
103
|
filename: `${cwd}/packages/foo/package.json`,
|
|
156
104
|
errors: [
|
|
157
105
|
{
|
|
@@ -162,13 +110,13 @@ describe('test ensure-valid-platform-yarn-protocol-usage rule', () => {
|
|
|
162
110
|
// 'root:' protocol is not allowed in public packages as dependencies and devDependencies
|
|
163
111
|
{
|
|
164
112
|
code: `const foo = {
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
113
|
+
"dependencies": {
|
|
114
|
+
"lodash": "root:*"
|
|
115
|
+
},
|
|
116
|
+
"devDependencies": {
|
|
117
|
+
"react": "root:*"
|
|
118
|
+
}
|
|
119
|
+
}`,
|
|
172
120
|
filename: `${cwd}/packages/foo/package.json`,
|
|
173
121
|
errors: [
|
|
174
122
|
{
|
|
@@ -179,14 +127,14 @@ describe('test ensure-valid-platform-yarn-protocol-usage rule', () => {
|
|
|
179
127
|
// 'root:' protocol is not allowed in private packages as dependencies and devDependencies
|
|
180
128
|
{
|
|
181
129
|
code: `const foo = {
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
130
|
+
"private": true,
|
|
131
|
+
"dependencies": {
|
|
132
|
+
"lodash": "root:*"
|
|
133
|
+
},
|
|
134
|
+
"devDependencies": {
|
|
135
|
+
"react": "root:*"
|
|
136
|
+
}
|
|
137
|
+
}`,
|
|
190
138
|
filename: `${cwd}/packages/foo/package.json`,
|
|
191
139
|
errors: [
|
|
192
140
|
{
|
|
@@ -1,9 +1,8 @@
|
|
|
1
1
|
// eslint-disable-next-line import/no-extraneous-dependencies
|
|
2
2
|
import type { Rule } from 'eslint';
|
|
3
3
|
import type { ObjectExpression } from 'estree';
|
|
4
|
-
import { getObjectPropertyAsObject
|
|
4
|
+
import { getObjectPropertyAsObject } from '../util/handle-ast-object';
|
|
5
5
|
|
|
6
|
-
const workspaceProtocolRegex = /^workspace:[\^~\*]$/;
|
|
7
6
|
const rootProtocolRegex = /^root:[\^~\*]$/;
|
|
8
7
|
|
|
9
8
|
/**
|
|
@@ -14,14 +13,12 @@ function getYarnProtocolsUsed(node: ObjectExpression) {
|
|
|
14
13
|
|
|
15
14
|
const dependencies = getObjectPropertyAsObject(node, 'dependencies');
|
|
16
15
|
const devDependencies = getObjectPropertyAsObject(node, 'devDependencies');
|
|
16
|
+
const peerDependencies = getObjectPropertyAsObject(node, 'peerDependencies');
|
|
17
17
|
|
|
18
|
-
for (const obj of [dependencies, devDependencies]) {
|
|
18
|
+
for (const obj of [dependencies, devDependencies, peerDependencies]) {
|
|
19
19
|
for (const p of obj?.properties || []) {
|
|
20
20
|
if (p.type === 'Property' && p.value.type === 'Literal') {
|
|
21
21
|
if (typeof p.value.value === 'string') {
|
|
22
|
-
if (workspaceProtocolRegex.test(p.value.value)) {
|
|
23
|
-
protocolsUsed.workspace = true;
|
|
24
|
-
}
|
|
25
22
|
if (rootProtocolRegex.test(p.value.value)) {
|
|
26
23
|
protocolsUsed.root = true;
|
|
27
24
|
}
|
|
@@ -42,7 +39,7 @@ const rule: Rule.RuleModule = {
|
|
|
42
39
|
},
|
|
43
40
|
hasSuggestions: false,
|
|
44
41
|
messages: {
|
|
45
|
-
invalidWorkspaceProtocolUsage: `The 'workspace
|
|
42
|
+
invalidWorkspaceProtocolUsage: `The 'workspace:^' or 'workspace:~' protocol is Used. To resolve this error, please use the 'workspace:*' protocol instead.`,
|
|
46
43
|
invalidRootProtocolUsage: `The 'root:' protocol is not allowed in platform packages. To resolve this error, replace the 'root:' protocol with specific package versions (e.g. '^1.0.0').`,
|
|
47
44
|
},
|
|
48
45
|
},
|
|
@@ -62,15 +59,6 @@ const rule: Rule.RuleModule = {
|
|
|
62
59
|
messageId: 'invalidRootProtocolUsage',
|
|
63
60
|
});
|
|
64
61
|
}
|
|
65
|
-
|
|
66
|
-
// The 'workspace:' protocol can not be used in public packages
|
|
67
|
-
const isPrivatePackage = getObjectPropertyAsLiteral(node, 'private') === true;
|
|
68
|
-
if (!isPrivatePackage && yarnProtocolsUsed.workspace) {
|
|
69
|
-
context.report({
|
|
70
|
-
node,
|
|
71
|
-
messageId: 'invalidWorkspaceProtocolUsage',
|
|
72
|
-
});
|
|
73
|
-
}
|
|
74
62
|
},
|
|
75
63
|
};
|
|
76
64
|
},
|