@atlaskit/eslint-plugin-design-system 4.16.2 → 4.16.3

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 CHANGED
@@ -1,5 +1,11 @@
1
1
  # @atlaskit/eslint-plugin-design-system
2
2
 
3
+ ## 4.16.3
4
+
5
+ ### Patch Changes
6
+
7
+ - [`ed34264c827`](https://bitbucket.org/atlassian/atlassian-frontend/commits/ed34264c827) - Fix errors on tagged template literals for eslint rule ensure-design-token-usage-spacing and handle edgecases ensuring seamless fallback on errors
8
+
3
9
  ## 4.16.2
4
10
 
5
11
  ### Patch Changes
@@ -205,8 +205,12 @@ var rule = (0, _createRule.createRule)({
205
205
  if (node.type !== 'TaggedTemplateExpression') {
206
206
  return;
207
207
  }
208
- var parentNode = (0, _utils.findParentNodeForLine)(node);
209
208
  var processedCssLines = (0, _utils.processCssNode)(node, context);
209
+ if (!processedCssLines) {
210
+ // if we can't get a processed css we bail
211
+ return;
212
+ }
213
+ var parentNode = (0, _utils.findParentNodeForLine)(node);
210
214
  var globalFontSize = (0, _utils.getFontSizeValueInScope)(processedCssLines);
211
215
  var textForSource = context.getSourceCode().getText(node.quasi);
212
216
  var allReplacedValues = [];
@@ -4,6 +4,7 @@ var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefau
4
4
  Object.defineProperty(exports, "__esModule", {
5
5
  value: true
6
6
  });
7
+ exports.cleanComments = cleanComments;
7
8
  exports.emToPixels = exports.convertHyphenatedNameToCamelCase = void 0;
8
9
  exports.findIdentifierInParentScope = findIdentifierInParentScope;
9
10
  exports.findParentNodeForLine = void 0;
@@ -77,10 +78,21 @@ exports.isSpacingProperty = isSpacingProperty;
77
78
  var isTypographyProperty = function isTypographyProperty(propertyName) {
78
79
  return typographyProperties.includes(propertyName);
79
80
  };
81
+
82
+ /**
83
+ * Accomplishes split str by whitespace but preserves expressions in between ${...}
84
+ * even if they might have whitepaces or nested brackets
85
+ * @param str
86
+ * @returns string[]
87
+ * @example
88
+ * Regex has two parts, first attempts to capture anything in between `${...}` in a capture group
89
+ * Whilst allowing nested brackets and non empty characters leading or traling wrapping expression e.g `${gridSize}`, `-${gridSize}px`
90
+ * second part is a white space delimiter
91
+ * For input `-${gridSize / 2}px ${token(...)} 18px -> [`-${gridSize / 2}px`, `${token(...)}`, `18px`]
92
+ */
80
93
  exports.isTypographyProperty = isTypographyProperty;
81
94
  var splitShorthandValues = function splitShorthandValues(str) {
82
- // Regex accomplishes split str by whitespace but ignore spaces in between ${}
83
- return str.split(/(\${[^}]*}\S*)|\s+/g).filter(Boolean);
95
+ return str.split(/(\S*\$\{[^{}]*(?:\{[^{}]*\}[^{}]*)*\}\S*)|\s+/g).filter(Boolean);
84
96
  };
85
97
  exports.splitShorthandValues = splitShorthandValues;
86
98
  var getValueFromShorthand = function getValueFromShorthand(str) {
@@ -176,7 +188,7 @@ var getRawExpression = function getRawExpression(node, context) {
176
188
  if (!(
177
189
  // if not one of our recognized types or doesn't have a range prop, early return
178
190
 
179
- (0, _eslintCodemodUtils.isNodeOfType)(node, 'Literal') || (0, _eslintCodemodUtils.isNodeOfType)(node, 'Identifier') || (0, _eslintCodemodUtils.isNodeOfType)(node, 'BinaryExpression') || (0, _eslintCodemodUtils.isNodeOfType)(node, 'UnaryExpression') || (0, _eslintCodemodUtils.isNodeOfType)(node, 'TemplateLiteral') || (0, _eslintCodemodUtils.isNodeOfType)(node, 'CallExpression') || (0, _eslintCodemodUtils.isNodeOfType)(node, 'ArrowFunctionExpression')) || !Array.isArray(node.range)) {
191
+ (0, _eslintCodemodUtils.isNodeOfType)(node, 'Literal') || (0, _eslintCodemodUtils.isNodeOfType)(node, 'Identifier') || (0, _eslintCodemodUtils.isNodeOfType)(node, 'BinaryExpression') || (0, _eslintCodemodUtils.isNodeOfType)(node, 'UnaryExpression') || (0, _eslintCodemodUtils.isNodeOfType)(node, 'TemplateLiteral') || (0, _eslintCodemodUtils.isNodeOfType)(node, 'CallExpression')) || !Array.isArray(node.range)) {
180
192
  return null;
181
193
  }
182
194
  var _node$range = (0, _slicedToArray2.default)(node.range, 2),
@@ -358,6 +370,14 @@ function shouldAnalyzeProperty(propertyName, targetOptions) {
358
370
  return false;
359
371
  }
360
372
 
373
+ /**
374
+ * Function that removes JS comments from a string of code,
375
+ * sometimes makers will have single or multiline comments in their tagged template literals styles, this can mess with our parsing logic
376
+ */
377
+ function cleanComments(str) {
378
+ return str.replace(/\/\*([\s\S]*?)\*\//g, '').replace(/\/\/(.*)/g, '');
379
+ }
380
+
361
381
  /**
362
382
  * Returns an array of tuples representing a processed css within `TaggedTemplateExpression` node.
363
383
  * each element of the array is a tuple `[string, string]`,
@@ -375,10 +395,14 @@ function processCssNode(node, context) {
375
395
  return "".concat(q.value.raw).concat(node.quasi.expressions[i] ? getValue(node.quasi.expressions[i], context) : '');
376
396
  }).join('');
377
397
  var rawString = node.quasi.quasis.map(function (q, i) {
378
- return "".concat(q.value.raw).concat(node.quasi.expressions[i] ? "${".concat(getRawExpression(node.quasi.expressions[i], context), "}") : '');
398
+ return "".concat(q.value.raw).concat(node.quasi.expressions[i] ? getRawExpression(node.quasi.expressions[i], context) ? "${".concat(getRawExpression(node.quasi.expressions[i], context), "}") : null : '');
379
399
  }).join('');
380
- var cssProperties = splitCssProperties(combinedString);
381
- var unalteredCssProperties = splitCssProperties(rawString);
400
+ var cssProperties = splitCssProperties(cleanComments(combinedString));
401
+ var unalteredCssProperties = splitCssProperties(cleanComments(rawString));
402
+ if (cssProperties.length !== unalteredCssProperties.length) {
403
+ // this means something wen't wrong with the parsing, the original lines can't be reconciliated with the processed lines
404
+ return undefined;
405
+ }
382
406
  return cssProperties.map(function (cssProperty, index) {
383
407
  return [cssProperty, unalteredCssProperties[index]];
384
408
  });
@@ -442,9 +466,21 @@ function getFontSizeValueInScope(cssProperties) {
442
466
  function splitCssProperties(styleString) {
443
467
  return styleString.split('\n').filter(function (line) {
444
468
  return !line.trim().startsWith('@');
469
+ })
470
+ // sometimes makers will end a css line with `;` that's output from a function expression
471
+ // since we'll rely on `;` to split each line, we need to ensure it's there
472
+ .map(function (line) {
473
+ return line.endsWith(';') ? line : "".concat(line, ";");
445
474
  }).join('\n').replace(/\n/g, '').split(/;|(?<!\$){|(?<!\${.+?)}/) // don't split on template literal expressions i.e. `${...}`
446
- .map(function (el) {
475
+ // filters lines that are completely null, this could be from function expressions that output both property and value
476
+ .filter(function (line) {
477
+ return line.trim() !== 'null' && line.trim() !== 'null;';
478
+ }).map(function (el) {
447
479
  return el.trim() || '';
480
+ })
481
+ // we won't be able to reason about lines that don't have colon (:)
482
+ .filter(function (line) {
483
+ return line.split(':').length === 2;
448
484
  }).filter(Boolean);
449
485
  }
450
486
 
@@ -1,5 +1,5 @@
1
1
  {
2
2
  "name": "@atlaskit/eslint-plugin-design-system",
3
- "version": "4.16.2",
3
+ "version": "4.16.3",
4
4
  "sideEffects": false
5
5
  }
@@ -194,8 +194,12 @@ const rule = createRule({
194
194
  if (node.type !== 'TaggedTemplateExpression') {
195
195
  return;
196
196
  }
197
- const parentNode = findParentNodeForLine(node);
198
197
  const processedCssLines = processCssNode(node, context);
198
+ if (!processedCssLines) {
199
+ // if we can't get a processed css we bail
200
+ return;
201
+ }
202
+ const parentNode = findParentNodeForLine(node);
199
203
  const globalFontSize = getFontSizeValueInScope(processedCssLines);
200
204
  const textForSource = context.getSourceCode().getText(node.quasi);
201
205
  const allReplacedValues = [];
@@ -39,9 +39,20 @@ export const isSpacingProperty = propertyName => {
39
39
  export const isTypographyProperty = propertyName => {
40
40
  return typographyProperties.includes(propertyName);
41
41
  };
42
+
43
+ /**
44
+ * Accomplishes split str by whitespace but preserves expressions in between ${...}
45
+ * even if they might have whitepaces or nested brackets
46
+ * @param str
47
+ * @returns string[]
48
+ * @example
49
+ * Regex has two parts, first attempts to capture anything in between `${...}` in a capture group
50
+ * Whilst allowing nested brackets and non empty characters leading or traling wrapping expression e.g `${gridSize}`, `-${gridSize}px`
51
+ * second part is a white space delimiter
52
+ * For input `-${gridSize / 2}px ${token(...)} 18px -> [`-${gridSize / 2}px`, `${token(...)}`, `18px`]
53
+ */
42
54
  export const splitShorthandValues = str => {
43
- // Regex accomplishes split str by whitespace but ignore spaces in between ${}
44
- return str.split(/(\${[^}]*}\S*)|\s+/g).filter(Boolean);
55
+ return str.split(/(\S*\$\{[^{}]*(?:\{[^{}]*\}[^{}]*)*\}\S*)|\s+/g).filter(Boolean);
45
56
  };
46
57
  export const getValueFromShorthand = str => {
47
58
  const valueString = String(str);
@@ -122,7 +133,7 @@ export const getRawExpression = (node, context) => {
122
133
  if (!(
123
134
  // if not one of our recognized types or doesn't have a range prop, early return
124
135
 
125
- isNodeOfType(node, 'Literal') || isNodeOfType(node, 'Identifier') || isNodeOfType(node, 'BinaryExpression') || isNodeOfType(node, 'UnaryExpression') || isNodeOfType(node, 'TemplateLiteral') || isNodeOfType(node, 'CallExpression') || isNodeOfType(node, 'ArrowFunctionExpression')) || !Array.isArray(node.range)) {
136
+ isNodeOfType(node, 'Literal') || isNodeOfType(node, 'Identifier') || isNodeOfType(node, 'BinaryExpression') || isNodeOfType(node, 'UnaryExpression') || isNodeOfType(node, 'TemplateLiteral') || isNodeOfType(node, 'CallExpression')) || !Array.isArray(node.range)) {
126
137
  return null;
127
138
  }
128
139
  const [start, end] = node.range;
@@ -296,6 +307,14 @@ export function shouldAnalyzeProperty(propertyName, targetOptions) {
296
307
  return false;
297
308
  }
298
309
 
310
+ /**
311
+ * Function that removes JS comments from a string of code,
312
+ * sometimes makers will have single or multiline comments in their tagged template literals styles, this can mess with our parsing logic
313
+ */
314
+ export function cleanComments(str) {
315
+ return str.replace(/\/\*([\s\S]*?)\*\//g, '').replace(/\/\/(.*)/g, '');
316
+ }
317
+
299
318
  /**
300
319
  * Returns an array of tuples representing a processed css within `TaggedTemplateExpression` node.
301
320
  * each element of the array is a tuple `[string, string]`,
@@ -313,10 +332,14 @@ export function processCssNode(node, context) {
313
332
  return `${q.value.raw}${node.quasi.expressions[i] ? getValue(node.quasi.expressions[i], context) : ''}`;
314
333
  }).join('');
315
334
  const rawString = node.quasi.quasis.map((q, i) => {
316
- return `${q.value.raw}${node.quasi.expressions[i] ? `\${${getRawExpression(node.quasi.expressions[i], context)}}` : ''}`;
335
+ return `${q.value.raw}${node.quasi.expressions[i] ? getRawExpression(node.quasi.expressions[i], context) ? `\${${getRawExpression(node.quasi.expressions[i], context)}}` : null : ''}`;
317
336
  }).join('');
318
- const cssProperties = splitCssProperties(combinedString);
319
- const unalteredCssProperties = splitCssProperties(rawString);
337
+ const cssProperties = splitCssProperties(cleanComments(combinedString));
338
+ const unalteredCssProperties = splitCssProperties(cleanComments(rawString));
339
+ if (cssProperties.length !== unalteredCssProperties.length) {
340
+ // this means something wen't wrong with the parsing, the original lines can't be reconciliated with the processed lines
341
+ return undefined;
342
+ }
320
343
  return cssProperties.map((cssProperty, index) => [cssProperty, unalteredCssProperties[index]]);
321
344
  }
322
345
 
@@ -368,8 +391,14 @@ export function getFontSizeValueInScope(cssProperties) {
368
391
  * @param styleString string of css properties
369
392
  */
370
393
  export function splitCssProperties(styleString) {
371
- return styleString.split('\n').filter(line => !line.trim().startsWith('@')).join('\n').replace(/\n/g, '').split(/;|(?<!\$){|(?<!\${.+?)}/) // don't split on template literal expressions i.e. `${...}`
372
- .map(el => el.trim() || '').filter(Boolean);
394
+ return styleString.split('\n').filter(line => !line.trim().startsWith('@'))
395
+ // sometimes makers will end a css line with `;` that's output from a function expression
396
+ // since we'll rely on `;` to split each line, we need to ensure it's there
397
+ .map(line => line.endsWith(';') ? line : `${line};`).join('\n').replace(/\n/g, '').split(/;|(?<!\$){|(?<!\${.+?)}/) // don't split on template literal expressions i.e. `${...}`
398
+ // filters lines that are completely null, this could be from function expressions that output both property and value
399
+ .filter(line => line.trim() !== 'null' && line.trim() !== 'null;').map(el => el.trim() || '')
400
+ // we won't be able to reason about lines that don't have colon (:)
401
+ .filter(line => line.split(':').length === 2).filter(Boolean);
373
402
  }
374
403
 
375
404
  /**
@@ -1,5 +1,5 @@
1
1
  {
2
2
  "name": "@atlaskit/eslint-plugin-design-system",
3
- "version": "4.16.2",
3
+ "version": "4.16.3",
4
4
  "sideEffects": false
5
5
  }
@@ -200,8 +200,12 @@ var rule = createRule({
200
200
  if (node.type !== 'TaggedTemplateExpression') {
201
201
  return;
202
202
  }
203
- var parentNode = findParentNodeForLine(node);
204
203
  var processedCssLines = processCssNode(node, context);
204
+ if (!processedCssLines) {
205
+ // if we can't get a processed css we bail
206
+ return;
207
+ }
208
+ var parentNode = findParentNodeForLine(node);
205
209
  var globalFontSize = getFontSizeValueInScope(processedCssLines);
206
210
  var textForSource = context.getSourceCode().getText(node.quasi);
207
211
  var allReplacedValues = [];
@@ -49,9 +49,20 @@ export var isSpacingProperty = function isSpacingProperty(propertyName) {
49
49
  export var isTypographyProperty = function isTypographyProperty(propertyName) {
50
50
  return typographyProperties.includes(propertyName);
51
51
  };
52
+
53
+ /**
54
+ * Accomplishes split str by whitespace but preserves expressions in between ${...}
55
+ * even if they might have whitepaces or nested brackets
56
+ * @param str
57
+ * @returns string[]
58
+ * @example
59
+ * Regex has two parts, first attempts to capture anything in between `${...}` in a capture group
60
+ * Whilst allowing nested brackets and non empty characters leading or traling wrapping expression e.g `${gridSize}`, `-${gridSize}px`
61
+ * second part is a white space delimiter
62
+ * For input `-${gridSize / 2}px ${token(...)} 18px -> [`-${gridSize / 2}px`, `${token(...)}`, `18px`]
63
+ */
52
64
  export var splitShorthandValues = function splitShorthandValues(str) {
53
- // Regex accomplishes split str by whitespace but ignore spaces in between ${}
54
- return str.split(/(\${[^}]*}\S*)|\s+/g).filter(Boolean);
65
+ return str.split(/(\S*\$\{[^{}]*(?:\{[^{}]*\}[^{}]*)*\}\S*)|\s+/g).filter(Boolean);
55
66
  };
56
67
  export var getValueFromShorthand = function getValueFromShorthand(str) {
57
68
  var valueString = String(str);
@@ -144,7 +155,7 @@ export var getRawExpression = function getRawExpression(node, context) {
144
155
  if (!(
145
156
  // if not one of our recognized types or doesn't have a range prop, early return
146
157
 
147
- isNodeOfType(node, 'Literal') || isNodeOfType(node, 'Identifier') || isNodeOfType(node, 'BinaryExpression') || isNodeOfType(node, 'UnaryExpression') || isNodeOfType(node, 'TemplateLiteral') || isNodeOfType(node, 'CallExpression') || isNodeOfType(node, 'ArrowFunctionExpression')) || !Array.isArray(node.range)) {
158
+ isNodeOfType(node, 'Literal') || isNodeOfType(node, 'Identifier') || isNodeOfType(node, 'BinaryExpression') || isNodeOfType(node, 'UnaryExpression') || isNodeOfType(node, 'TemplateLiteral') || isNodeOfType(node, 'CallExpression')) || !Array.isArray(node.range)) {
148
159
  return null;
149
160
  }
150
161
  var _node$range = _slicedToArray(node.range, 2),
@@ -320,6 +331,14 @@ export function shouldAnalyzeProperty(propertyName, targetOptions) {
320
331
  return false;
321
332
  }
322
333
 
334
+ /**
335
+ * Function that removes JS comments from a string of code,
336
+ * sometimes makers will have single or multiline comments in their tagged template literals styles, this can mess with our parsing logic
337
+ */
338
+ export function cleanComments(str) {
339
+ return str.replace(/\/\*([\s\S]*?)\*\//g, '').replace(/\/\/(.*)/g, '');
340
+ }
341
+
323
342
  /**
324
343
  * Returns an array of tuples representing a processed css within `TaggedTemplateExpression` node.
325
344
  * each element of the array is a tuple `[string, string]`,
@@ -337,10 +356,14 @@ export function processCssNode(node, context) {
337
356
  return "".concat(q.value.raw).concat(node.quasi.expressions[i] ? getValue(node.quasi.expressions[i], context) : '');
338
357
  }).join('');
339
358
  var rawString = node.quasi.quasis.map(function (q, i) {
340
- return "".concat(q.value.raw).concat(node.quasi.expressions[i] ? "${".concat(getRawExpression(node.quasi.expressions[i], context), "}") : '');
359
+ return "".concat(q.value.raw).concat(node.quasi.expressions[i] ? getRawExpression(node.quasi.expressions[i], context) ? "${".concat(getRawExpression(node.quasi.expressions[i], context), "}") : null : '');
341
360
  }).join('');
342
- var cssProperties = splitCssProperties(combinedString);
343
- var unalteredCssProperties = splitCssProperties(rawString);
361
+ var cssProperties = splitCssProperties(cleanComments(combinedString));
362
+ var unalteredCssProperties = splitCssProperties(cleanComments(rawString));
363
+ if (cssProperties.length !== unalteredCssProperties.length) {
364
+ // this means something wen't wrong with the parsing, the original lines can't be reconciliated with the processed lines
365
+ return undefined;
366
+ }
344
367
  return cssProperties.map(function (cssProperty, index) {
345
368
  return [cssProperty, unalteredCssProperties[index]];
346
369
  });
@@ -404,9 +427,21 @@ export function getFontSizeValueInScope(cssProperties) {
404
427
  export function splitCssProperties(styleString) {
405
428
  return styleString.split('\n').filter(function (line) {
406
429
  return !line.trim().startsWith('@');
430
+ })
431
+ // sometimes makers will end a css line with `;` that's output from a function expression
432
+ // since we'll rely on `;` to split each line, we need to ensure it's there
433
+ .map(function (line) {
434
+ return line.endsWith(';') ? line : "".concat(line, ";");
407
435
  }).join('\n').replace(/\n/g, '').split(/;|(?<!\$){|(?<!\${.+?)}/) // don't split on template literal expressions i.e. `${...}`
408
- .map(function (el) {
436
+ // filters lines that are completely null, this could be from function expressions that output both property and value
437
+ .filter(function (line) {
438
+ return line.trim() !== 'null' && line.trim() !== 'null;';
439
+ }).map(function (el) {
409
440
  return el.trim() || '';
441
+ })
442
+ // we won't be able to reason about lines that don't have colon (:)
443
+ .filter(function (line) {
444
+ return line.split(':').length === 2;
410
445
  }).filter(Boolean);
411
446
  }
412
447
 
@@ -1,5 +1,5 @@
1
1
  {
2
2
  "name": "@atlaskit/eslint-plugin-design-system",
3
- "version": "4.16.2",
3
+ "version": "4.16.3",
4
4
  "sideEffects": false
5
5
  }
@@ -73,6 +73,17 @@ export declare function findIdentifierInParentScope({ scope, identifierName, }:
73
73
  export declare function insertTokensImport(fixer: Rule.RuleFixer): Rule.Fix;
74
74
  export declare const isSpacingProperty: (propertyName: string) => boolean;
75
75
  export declare const isTypographyProperty: (propertyName: string) => boolean;
76
+ /**
77
+ * Accomplishes split str by whitespace but preserves expressions in between ${...}
78
+ * even if they might have whitepaces or nested brackets
79
+ * @param str
80
+ * @returns string[]
81
+ * @example
82
+ * Regex has two parts, first attempts to capture anything in between `${...}` in a capture group
83
+ * Whilst allowing nested brackets and non empty characters leading or traling wrapping expression e.g `${gridSize}`, `-${gridSize}px`
84
+ * second part is a white space delimiter
85
+ * For input `-${gridSize / 2}px ${token(...)} 18px -> [`-${gridSize / 2}px`, `${token(...)}`, `18px`]
86
+ */
76
87
  export declare const splitShorthandValues: (str: string) => string[];
77
88
  export declare const getValueFromShorthand: (str: unknown) => any[];
78
89
  export declare const getValue: (node: EslintNode, context: Rule.RuleContext) => string | number | any[] | null | undefined;
@@ -97,6 +108,11 @@ export declare const findParentNodeForLine: (node: Rule.Node) => Rule.Node;
97
108
  * ```
98
109
  */
99
110
  export declare function shouldAnalyzeProperty(propertyName: string, targetOptions: TargetOptions): boolean;
111
+ /**
112
+ * Function that removes JS comments from a string of code,
113
+ * sometimes makers will have single or multiline comments in their tagged template literals styles, this can mess with our parsing logic
114
+ */
115
+ export declare function cleanComments(str: string): string;
100
116
  /**
101
117
  * Returns an array of tuples representing a processed css within `TaggedTemplateExpression` node.
102
118
  * each element of the array is a tuple `[string, string]`,
@@ -109,7 +125,7 @@ export declare function shouldAnalyzeProperty(propertyName: string, targetOption
109
125
  * `[['padding: 8', 'padding: ${gridSize()}'], ['margin: 6', 'margin: 6px' ]]`
110
126
  * ```
111
127
  */
112
- export declare function processCssNode(node: TaggedTemplateExpression & Rule.NodeParentExtension, context: Rule.RuleContext): ProcessedCSSLines;
128
+ export declare function processCssNode(node: TaggedTemplateExpression & Rule.NodeParentExtension, context: Rule.RuleContext): ProcessedCSSLines | undefined;
113
129
  /**
114
130
  * Returns a token node for a given value including fallbacks.
115
131
  * @param propertyName camelCase CSS property
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@atlaskit/eslint-plugin-design-system",
3
3
  "description": "The essential plugin for use with the Atlassian Design System.",
4
- "version": "4.16.2",
4
+ "version": "4.16.3",
5
5
  "author": "Atlassian Pty Ltd",
6
6
  "publishConfig": {
7
7
  "registry": "https://registry.npmjs.org/"