@atlaskit/code 14.1.4 → 14.3.1

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 (42) hide show
  1. package/CHANGELOG.md +24 -0
  2. package/bidi-warning/package.json +7 -0
  3. package/bidi-warning-decorator/package.json +7 -0
  4. package/dist/cjs/bidi-warning/bidi-warning-decorator.js +73 -0
  5. package/dist/cjs/bidi-warning/index.js +15 -0
  6. package/dist/cjs/bidi-warning/ui/index.js +55 -0
  7. package/dist/cjs/bidi-warning/ui/styled.js +74 -0
  8. package/dist/cjs/bidi-warning/ui/types.js +5 -0
  9. package/dist/cjs/code-block.js +14 -2
  10. package/dist/cjs/code.js +65 -2
  11. package/dist/cjs/react-syntax-highlighter-bidi-warning-renderer.js +180 -0
  12. package/dist/cjs/version.json +1 -1
  13. package/dist/es2019/bidi-warning/bidi-warning-decorator.js +42 -0
  14. package/dist/es2019/bidi-warning/index.js +1 -0
  15. package/dist/es2019/bidi-warning/ui/index.js +40 -0
  16. package/dist/es2019/bidi-warning/ui/styled.js +65 -0
  17. package/dist/es2019/bidi-warning/ui/types.js +1 -0
  18. package/dist/es2019/code-block.js +11 -2
  19. package/dist/es2019/code.js +54 -2
  20. package/dist/es2019/react-syntax-highlighter-bidi-warning-renderer.js +156 -0
  21. package/dist/es2019/version.json +1 -1
  22. package/dist/esm/bidi-warning/bidi-warning-decorator.js +61 -0
  23. package/dist/esm/bidi-warning/index.js +1 -0
  24. package/dist/esm/bidi-warning/ui/index.js +41 -0
  25. package/dist/esm/bidi-warning/ui/styled.js +64 -0
  26. package/dist/esm/bidi-warning/ui/types.js +1 -0
  27. package/dist/esm/code-block.js +13 -2
  28. package/dist/esm/code.js +56 -2
  29. package/dist/esm/react-syntax-highlighter-bidi-warning-renderer.js +168 -0
  30. package/dist/esm/version.json +1 -1
  31. package/dist/types/bidi-warning/bidi-warning-decorator.d.ts +5 -0
  32. package/dist/types/bidi-warning/index.d.ts +1 -0
  33. package/dist/types/bidi-warning/ui/index.d.ts +2 -0
  34. package/dist/types/bidi-warning/ui/styled.d.ts +7 -0
  35. package/dist/types/bidi-warning/ui/types.d.ts +29 -0
  36. package/dist/types/code.d.ts +3 -2
  37. package/dist/types/internal/theme/get-theme.d.ts +1 -1
  38. package/dist/types/internal/types.d.ts +19 -0
  39. package/dist/types/internal/utils/get-normalized-language.d.ts +1 -1
  40. package/dist/types/react-syntax-highlighter-bidi-warning-renderer.d.ts +9 -0
  41. package/dist/types/types.d.ts +19 -0
  42. package/package.json +7 -4
@@ -0,0 +1,40 @@
1
+ import _extends from "@babel/runtime/helpers/extends";
2
+ import React from 'react';
3
+ import Tooltip from '@atlaskit/tooltip';
4
+ import { Decorator } from './styled';
5
+ export default function BidiWarning({
6
+ testId,
7
+ bidiCharacter,
8
+ skipChildren,
9
+ tooltipEnabled,
10
+ label = 'Bidirectional characters change the order that text is rendered. This could be used to obscure malicious code.'
11
+ }) {
12
+ if (tooltipEnabled) {
13
+ return (
14
+ /*#__PURE__*/
15
+ // Following patches, this should be updated to use the render props signature which will provide aria attributes.
16
+ // Note: this should be tested, as initial testing did not see attributes work with current tooltip implementation.
17
+ React.createElement(Tooltip, {
18
+ content: label,
19
+ tag: CustomizedTagWithRef
20
+ }, /*#__PURE__*/React.createElement(Decorator, {
21
+ testId: testId,
22
+ bidiCharacter: bidiCharacter
23
+ }, skipChildren ? null : bidiCharacter))
24
+ );
25
+ }
26
+
27
+ return /*#__PURE__*/React.createElement(Decorator, {
28
+ testId: testId,
29
+ bidiCharacter: bidiCharacter
30
+ }, skipChildren ? null : bidiCharacter);
31
+ }
32
+ const CustomizedTagWithRef = /*#__PURE__*/React.forwardRef((props, ref) => {
33
+ const {
34
+ children,
35
+ ...rest
36
+ } = props;
37
+ return /*#__PURE__*/React.createElement("span", _extends({}, rest, {
38
+ ref: ref
39
+ }), children);
40
+ });
@@ -0,0 +1,65 @@
1
+ /** @jsx jsx */
2
+ import { css, jsx } from '@emotion/core';
3
+ import { Y75 } from '@atlaskit/theme/colors';
4
+ const decoration = css({
5
+ // Required as otherwise the following bidi characters cause the span
6
+ // to not receive hover events.
7
+ //
8
+ // U+2066 LEFT-TO-RIGHT ISOLATE (when using pseudo element before)
9
+ // U+202E RIGHT-TO-LEFT OVERRIDE' (when using pseudo element after)
10
+ position: 'relative',
11
+ ':before': {
12
+ /* layout */
13
+ display: 'inline-flex',
14
+ flexDirection: 'row',
15
+ justifyContent: 'center',
16
+ alignItems: 'center',
17
+ padding: '0 4px',
18
+ fontSize: '14px',
19
+ lineHeight: '18px',
20
+ background: Y75,
21
+
22
+ /**
23
+ * Ensures the decoration receives pointer events when it occurs with
24
+ * an ancestor that disables them.
25
+ */
26
+ pointerEvents: 'auto',
27
+
28
+ /* contents */
29
+ content: '"<"attr(data-bidi-character-code)">"',
30
+ // This color is Y800 which is not yet rolled out
31
+ // https://hello.atlassian.net/wiki/spaces/~tswan/pages/1366555782?focusedCommentId=1370387374#comment-1370387374
32
+ color: '#7F5F01',
33
+ fontStyle: 'normal'
34
+ },
35
+ ':hover:before': {
36
+ color: '#533F04'
37
+ }
38
+ });
39
+ export function Decorator({
40
+ bidiCharacter,
41
+ children,
42
+ testId
43
+ }) {
44
+ const bidiCharacterCode = getBidiCharacterCode(bidiCharacter);
45
+ return jsx("span", {
46
+ "aria-label": bidiCharacterCode
47
+ }, jsx("span", {
48
+ css: decoration,
49
+ "data-testid": testId,
50
+ "data-bidi-character-code": bidiCharacterCode // This is set to true so that the content is not read out by
51
+ // screen readers as the content includes angle brackets for
52
+ // visual decoration purposes.
53
+ // We use a span with the aria-label set to the bidi character code
54
+ // above this span for screen readers.
55
+ ,
56
+ "aria-hidden": "true"
57
+ }, children));
58
+ }
59
+
60
+ function getBidiCharacterCode(bidiCharacter) {
61
+ var _bidiCharacter$codePo;
62
+
63
+ const bidiCharacterCode = (_bidiCharacter$codePo = bidiCharacter.codePointAt(0)) === null || _bidiCharacter$codePo === void 0 ? void 0 : _bidiCharacter$codePo.toString(16);
64
+ return `U+${bidiCharacterCode}`;
65
+ }
@@ -0,0 +1 @@
1
+ export {};
@@ -6,6 +6,7 @@ import { useGlobalTheme } from '@atlaskit/theme/components';
6
6
  import { useHighlightLines } from './internal/hooks/use-highlight';
7
7
  import { getCodeBlockStyles, getCodeBlockTheme } from './internal/theme/styles';
8
8
  import { normalizeLanguage } from './internal/utils/get-normalized-language';
9
+ import { createBidiWarningRenderer } from './react-syntax-highlighter-bidi-warning-renderer';
9
10
  const CodeBlock = /*#__PURE__*/memo(function CodeBlock({
10
11
  showLineNumbers = true,
11
12
  language: providedLanguage = 'text',
@@ -13,7 +14,10 @@ const CodeBlock = /*#__PURE__*/memo(function CodeBlock({
13
14
  highlightedStartText = 'Highlight start',
14
15
  highlightedEndText = 'Highlight end',
15
16
  testId,
16
- text
17
+ text,
18
+ codeBidiWarnings = true,
19
+ codeBidiWarningLabel,
20
+ codeBidiWarningTooltipEnabled = true
17
21
  }) {
18
22
  const numLines = (text || '').split('\n').length;
19
23
  const globalTheme = useGlobalTheme();
@@ -31,6 +35,10 @@ const CodeBlock = /*#__PURE__*/memo(function CodeBlock({
31
35
  const language = useMemo(() => normalizeLanguage(providedLanguage), [providedLanguage]); // https://product-fabric.atlassian.net/browse/DST-2472
32
36
 
33
37
  const languageToUse = text ? language : 'text';
38
+ const renderer = codeBidiWarnings ? createBidiWarningRenderer({
39
+ codeBidiWarningLabel,
40
+ codeBidiWarningTooltipEnabled
41
+ }) : undefined;
34
42
  return jsx(SyntaxHighlighter, {
35
43
  "data-testid": testId,
36
44
  "data-code-lang": language,
@@ -42,7 +50,8 @@ const CodeBlock = /*#__PURE__*/memo(function CodeBlock({
42
50
  ,
43
51
  wrapLines: highlight.length > 0 || !!testId,
44
52
  lineProps: getLineProps,
45
- useInlineStyles: false
53
+ useInlineStyles: false,
54
+ renderer: renderer
46
55
  }, text);
47
56
  });
48
57
  CodeBlock.displayName = 'CodeBlock';
@@ -1,9 +1,11 @@
1
1
  import _extends from "@babel/runtime/helpers/extends";
2
2
 
3
3
  /** @jsx jsx */
4
- import { forwardRef, memo, useMemo } from 'react';
4
+ import React, { forwardRef, memo, useMemo } from 'react';
5
5
  import { jsx } from '@emotion/core';
6
6
  import { useGlobalTheme } from '@atlaskit/theme/components';
7
+ import CodeBidiWarning from './bidi-warning';
8
+ import codeBidiWarningDecorator from './bidi-warning/bidi-warning-decorator';
7
9
  import { getCodeStyles } from './internal/theme/styles';
8
10
  const Code = /*#__PURE__*/memo( /*#__PURE__*/forwardRef(function Code({
9
11
  testId,
@@ -11,12 +13,62 @@ const Code = /*#__PURE__*/memo( /*#__PURE__*/forwardRef(function Code({
11
13
  }, ref) {
12
14
  const theme = useGlobalTheme();
13
15
  const styles = useMemo(() => getCodeStyles(theme), [theme]);
16
+ const {
17
+ children,
18
+ codeBidiWarnings = true,
19
+ codeBidiWarningLabel,
20
+ codeBidiWarningTooltipEnabled = true,
21
+ ...otherProps
22
+ } = props;
23
+ const decoratedChildren = codeBidiWarnings ? jsx(RenderCodeChildrenWithBidiWarnings, {
24
+ codeBidiWarningLabel: codeBidiWarningLabel,
25
+ codeBidiWarningTooltipEnabled: codeBidiWarningTooltipEnabled
26
+ }, children) : children;
14
27
  return jsx("code", _extends({
15
28
  ref: ref,
16
29
  "data-testid": testId,
17
30
  css: styles
18
- }, props));
31
+ }, otherProps), decoratedChildren);
19
32
  }));
33
+
34
+ function RenderCodeChildrenWithBidiWarnings({
35
+ children,
36
+ codeBidiWarningLabel,
37
+ codeBidiWarningTooltipEnabled
38
+ }) {
39
+ const replacedChildren = React.Children.map(children, childNode => {
40
+ if (typeof childNode === 'string') {
41
+ const decorated = codeBidiWarningDecorator(childNode, ({
42
+ bidiCharacter,
43
+ index
44
+ }) => jsx(CodeBidiWarning, {
45
+ bidiCharacter: bidiCharacter,
46
+ key: index,
47
+ label: codeBidiWarningLabel,
48
+ tooltipEnabled: codeBidiWarningTooltipEnabled
49
+ }));
50
+ return decorated;
51
+ }
52
+
53
+ if (isReactElement(childNode) && childNode.props.children) {
54
+ const newChildNode = /*#__PURE__*/React.cloneElement(childNode, {
55
+ children: jsx(RenderCodeChildrenWithBidiWarnings, {
56
+ codeBidiWarningLabel: codeBidiWarningLabel,
57
+ codeBidiWarningTooltipEnabled: codeBidiWarningTooltipEnabled
58
+ }, childNode.props.children)
59
+ });
60
+ return newChildNode;
61
+ }
62
+
63
+ return childNode;
64
+ });
65
+ return jsx(React.Fragment, null, replacedChildren);
66
+ }
67
+
68
+ function isReactElement(child) {
69
+ return !!child.type;
70
+ }
71
+
20
72
  Code.displayName = 'Code';
21
73
  export { getCodeStyles };
22
74
  export default Code;
@@ -0,0 +1,156 @@
1
+ import _extends from "@babel/runtime/helpers/extends";
2
+ // @ts-nocheck
3
+ import React from 'react';
4
+ import CodeBidiWarning from './bidi-warning';
5
+ import codeBidiWarningDecorator from './bidi-warning/bidi-warning-decorator'; // File mostly vendored from react-syntax-highlighter
6
+ //
7
+ // - https://github.com/react-syntax-highlighter/react-syntax-highlighter/blob/efc3f7b7537d1729193b7a472067bcbe6cbecaf1/src/highlight.js#L272-L281
8
+ // - https://github.com/react-syntax-highlighter/react-syntax-highlighter/blob/efc3f7b7537d1729193b7a472067bcbe6cbecaf1/src/create-element.js
9
+ //
10
+ // Patching react syntax-highlighter with a decoration feature is likely preferable
11
+
12
+ export function createBidiWarningRenderer(codeBidiWarningConfig) {
13
+ return function bidiWarningRenderer({
14
+ rows,
15
+ stylesheet,
16
+ useInlineStyles
17
+ }) {
18
+ return rows.map((node, i) => createElement({
19
+ node,
20
+ stylesheet,
21
+ useInlineStyles,
22
+ codeBidiWarningConfig,
23
+ key: `code-segement${i}`
24
+ }));
25
+ };
26
+ } // Get all possible permutations of all power sets
27
+ //
28
+ // Super simple, non-algorithmic solution since the
29
+ // number of class names will not be greater than 4
30
+
31
+ function powerSetPermutations(arr) {
32
+ const arrLength = arr.length;
33
+
34
+ if (arrLength === 0 || arrLength === 1) {
35
+ return arr;
36
+ }
37
+
38
+ if (arrLength === 2) {
39
+ // prettier-ignore
40
+ return [arr[0], arr[1], `${arr[0]}.${arr[1]}`, `${arr[1]}.${arr[0]}`];
41
+ }
42
+
43
+ if (arrLength === 3) {
44
+ return [arr[0], arr[1], arr[2], `${arr[0]}.${arr[1]}`, `${arr[0]}.${arr[2]}`, `${arr[1]}.${arr[0]}`, `${arr[1]}.${arr[2]}`, `${arr[2]}.${arr[0]}`, `${arr[2]}.${arr[1]}`, `${arr[0]}.${arr[1]}.${arr[2]}`, `${arr[0]}.${arr[2]}.${arr[1]}`, `${arr[1]}.${arr[0]}.${arr[2]}`, `${arr[1]}.${arr[2]}.${arr[0]}`, `${arr[2]}.${arr[0]}.${arr[1]}`, `${arr[2]}.${arr[1]}.${arr[0]}`];
45
+ }
46
+
47
+ if (arrLength >= 4) {
48
+ // Currently does not support more than 4 extra
49
+ // class names (after `.token` has been removed)
50
+ return [arr[0], arr[1], arr[2], arr[3], `${arr[0]}.${arr[1]}`, `${arr[0]}.${arr[2]}`, `${arr[0]}.${arr[3]}`, `${arr[1]}.${arr[0]}`, `${arr[1]}.${arr[2]}`, `${arr[1]}.${arr[3]}`, `${arr[2]}.${arr[0]}`, `${arr[2]}.${arr[1]}`, `${arr[2]}.${arr[3]}`, `${arr[3]}.${arr[0]}`, `${arr[3]}.${arr[1]}`, `${arr[3]}.${arr[2]}`, `${arr[0]}.${arr[1]}.${arr[2]}`, `${arr[0]}.${arr[1]}.${arr[3]}`, `${arr[0]}.${arr[2]}.${arr[1]}`, `${arr[0]}.${arr[2]}.${arr[3]}`, `${arr[0]}.${arr[3]}.${arr[1]}`, `${arr[0]}.${arr[3]}.${arr[2]}`, `${arr[1]}.${arr[0]}.${arr[2]}`, `${arr[1]}.${arr[0]}.${arr[3]}`, `${arr[1]}.${arr[2]}.${arr[0]}`, `${arr[1]}.${arr[2]}.${arr[3]}`, `${arr[1]}.${arr[3]}.${arr[0]}`, `${arr[1]}.${arr[3]}.${arr[2]}`, `${arr[2]}.${arr[0]}.${arr[1]}`, `${arr[2]}.${arr[0]}.${arr[3]}`, `${arr[2]}.${arr[1]}.${arr[0]}`, `${arr[2]}.${arr[1]}.${arr[3]}`, `${arr[2]}.${arr[3]}.${arr[0]}`, `${arr[2]}.${arr[3]}.${arr[1]}`, `${arr[3]}.${arr[0]}.${arr[1]}`, `${arr[3]}.${arr[0]}.${arr[2]}`, `${arr[3]}.${arr[1]}.${arr[0]}`, `${arr[3]}.${arr[1]}.${arr[2]}`, `${arr[3]}.${arr[2]}.${arr[0]}`, `${arr[3]}.${arr[2]}.${arr[1]}`, `${arr[0]}.${arr[1]}.${arr[2]}.${arr[3]}`, `${arr[0]}.${arr[1]}.${arr[3]}.${arr[2]}`, `${arr[0]}.${arr[2]}.${arr[1]}.${arr[3]}`, `${arr[0]}.${arr[2]}.${arr[3]}.${arr[1]}`, `${arr[0]}.${arr[3]}.${arr[1]}.${arr[2]}`, `${arr[0]}.${arr[3]}.${arr[2]}.${arr[1]}`, `${arr[1]}.${arr[0]}.${arr[2]}.${arr[3]}`, `${arr[1]}.${arr[0]}.${arr[3]}.${arr[2]}`, `${arr[1]}.${arr[2]}.${arr[0]}.${arr[3]}`, `${arr[1]}.${arr[2]}.${arr[3]}.${arr[0]}`, `${arr[1]}.${arr[3]}.${arr[0]}.${arr[2]}`, `${arr[1]}.${arr[3]}.${arr[2]}.${arr[0]}`, `${arr[2]}.${arr[0]}.${arr[1]}.${arr[3]}`, `${arr[2]}.${arr[0]}.${arr[3]}.${arr[1]}`, `${arr[2]}.${arr[1]}.${arr[0]}.${arr[3]}`, `${arr[2]}.${arr[1]}.${arr[3]}.${arr[0]}`, `${arr[2]}.${arr[3]}.${arr[0]}.${arr[1]}`, `${arr[2]}.${arr[3]}.${arr[1]}.${arr[0]}`, `${arr[3]}.${arr[0]}.${arr[1]}.${arr[2]}`, `${arr[3]}.${arr[0]}.${arr[2]}.${arr[1]}`, `${arr[3]}.${arr[1]}.${arr[0]}.${arr[2]}`, `${arr[3]}.${arr[1]}.${arr[2]}.${arr[0]}`, `${arr[3]}.${arr[2]}.${arr[0]}.${arr[1]}`, `${arr[3]}.${arr[2]}.${arr[1]}.${arr[0]}`];
51
+ }
52
+ }
53
+
54
+ const classNameCombinations = {};
55
+
56
+ function getClassNameCombinations(classNames) {
57
+ if (classNames.length === 0 || classNames.length === 1) {
58
+ return classNames;
59
+ }
60
+
61
+ const key = classNames.join('.');
62
+
63
+ if (!classNameCombinations[key]) {
64
+ classNameCombinations[key] = powerSetPermutations(classNames);
65
+ }
66
+
67
+ return classNameCombinations[key];
68
+ }
69
+
70
+ export function createStyleObject(classNames, elementStyle = {}, stylesheet) {
71
+ const nonTokenClassNames = classNames.filter(className => className !== 'token');
72
+ const classNamesCombinations = getClassNameCombinations(nonTokenClassNames);
73
+ return classNamesCombinations.reduce((styleObject, className) => {
74
+ return { ...styleObject,
75
+ ...stylesheet[className]
76
+ };
77
+ }, elementStyle);
78
+ }
79
+ export function createClassNameString(classNames) {
80
+ return classNames.join(' ');
81
+ }
82
+ export function createChildren(stylesheet, useInlineStyles, codeBidiWarningConfig) {
83
+ let childrenCount = 0;
84
+ return children => {
85
+ childrenCount += 1;
86
+ return children.map((child, i) => createElement({
87
+ node: child,
88
+ stylesheet,
89
+ useInlineStyles,
90
+ codeBidiWarningConfig,
91
+ key: `code-segment-${childrenCount}-${i}`
92
+ }));
93
+ };
94
+ }
95
+
96
+ function createElement({
97
+ node,
98
+ stylesheet,
99
+ style = {},
100
+ useInlineStyles,
101
+ codeBidiWarningConfig,
102
+ key
103
+ }) {
104
+ const {
105
+ properties,
106
+ type,
107
+ tagName: TagName,
108
+ value
109
+ } = node;
110
+
111
+ if (type === 'text') {
112
+ // occasionally react-syntax-highlighter passes a numeric value when the
113
+ // type is text
114
+ const textValue = value + '';
115
+ const decorated = codeBidiWarningDecorator(textValue, ({
116
+ bidiCharacter,
117
+ index
118
+ }) => /*#__PURE__*/React.createElement(CodeBidiWarning, {
119
+ bidiCharacter: bidiCharacter,
120
+ key: index,
121
+ label: codeBidiWarningConfig.codeBidiWarningLabel,
122
+ tooltipEnabled: codeBidiWarningConfig.codeBidiWarningTooltipEnabled
123
+ }));
124
+ return decorated;
125
+ } else if (TagName) {
126
+ const childrenCreator = createChildren(stylesheet, useInlineStyles, codeBidiWarningConfig);
127
+ let props;
128
+
129
+ if (!useInlineStyles) {
130
+ props = { ...properties,
131
+ className: createClassNameString(properties.className)
132
+ };
133
+ } else {
134
+ const allStylesheetSelectors = Object.keys(stylesheet).reduce((classes, selector) => {
135
+ selector.split('.').forEach(className => {
136
+ if (!classes.includes(className)) {
137
+ classes.push(className);
138
+ }
139
+ });
140
+ return classes;
141
+ }, []); // For compatibility with older versions of react-syntax-highlighter
142
+
143
+ const startingClassName = properties.className && properties.className.includes('token') ? ['token'] : [];
144
+ const className = properties.className && startingClassName.concat(properties.className.filter(className => !allStylesheetSelectors.includes(className)));
145
+ props = { ...properties,
146
+ className: createClassNameString(className) || undefined,
147
+ style: createStyleObject(properties.className, Object.assign({}, properties.style, style), stylesheet)
148
+ };
149
+ }
150
+
151
+ const children = childrenCreator(node.children);
152
+ return /*#__PURE__*/React.createElement(TagName, _extends({
153
+ key: key
154
+ }, props), children);
155
+ }
156
+ }
@@ -1,5 +1,5 @@
1
1
  {
2
2
  "name": "@atlaskit/code",
3
- "version": "14.1.4",
3
+ "version": "14.3.1",
4
4
  "sideEffects": false
5
5
  }
@@ -0,0 +1,61 @@
1
+ import _toConsumableArray from "@babel/runtime/helpers/toConsumableArray";
2
+
3
+ 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; } } }; }
4
+
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
+
7
+ 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; }
8
+
9
+ export var bidiCharacterRegex = /[\u202A-\u202E\u2066-\u2069]/g;
10
+ export default function codeBidiWarningDecorator(originalText, decorate) {
11
+ var matches = _toConsumableArray(originalText.matchAll(bidiCharacterRegex));
12
+
13
+ if (matches.length === 0) {
14
+ // No matches encountered, so we return the originalText value
15
+ return originalText;
16
+ }
17
+
18
+ var children = [];
19
+ var mappedTo = 0;
20
+
21
+ var _iterator = _createForOfIteratorHelper(matches),
22
+ _step;
23
+
24
+ try {
25
+ for (_iterator.s(); !(_step = _iterator.n()).done;) {
26
+ var match = _step.value;
27
+
28
+ if (mappedTo !== match.index) {
29
+ // There were unmatched characters prior to this match which haven't been
30
+ // mapped to the children.
31
+ // Add them as plain text.
32
+ children.push(originalText.substring(mappedTo, match.index));
33
+ }
34
+
35
+ children.push(decorate({
36
+ bidiCharacter: match[0],
37
+ index: match.index
38
+ })); // While index is guaranteed to be present, it needs to be asserted due
39
+ // to a limitation of typescripts regex handling
40
+ //
41
+ // https://github.com/microsoft/TypeScript/issues/36788
42
+ // Decorate bidi character
43
+
44
+ mappedTo = match.index + match[0].length;
45
+ }
46
+ } catch (err) {
47
+ _iterator.e(err);
48
+ } finally {
49
+ _iterator.f();
50
+ }
51
+
52
+ if (mappedTo !== originalText.length) {
53
+ // There is text following the final match, which needs to be mapped
54
+ // to the children.
55
+ // Added as plain text.
56
+ children.push(originalText.substring(mappedTo, originalText.length));
57
+ } // return the mapped children with decorated bidi characters
58
+
59
+
60
+ return children;
61
+ }
@@ -0,0 +1 @@
1
+ export { default } from './ui';
@@ -0,0 +1,41 @@
1
+ import _extends from "@babel/runtime/helpers/extends";
2
+ import _objectWithoutProperties from "@babel/runtime/helpers/objectWithoutProperties";
3
+ import React from 'react';
4
+ import Tooltip from '@atlaskit/tooltip';
5
+ import { Decorator } from './styled';
6
+ export default function BidiWarning(_ref) {
7
+ var testId = _ref.testId,
8
+ bidiCharacter = _ref.bidiCharacter,
9
+ skipChildren = _ref.skipChildren,
10
+ tooltipEnabled = _ref.tooltipEnabled,
11
+ _ref$label = _ref.label,
12
+ label = _ref$label === void 0 ? 'Bidirectional characters change the order that text is rendered. This could be used to obscure malicious code.' : _ref$label;
13
+
14
+ if (tooltipEnabled) {
15
+ return (
16
+ /*#__PURE__*/
17
+ // Following patches, this should be updated to use the render props signature which will provide aria attributes.
18
+ // Note: this should be tested, as initial testing did not see attributes work with current tooltip implementation.
19
+ React.createElement(Tooltip, {
20
+ content: label,
21
+ tag: CustomizedTagWithRef
22
+ }, /*#__PURE__*/React.createElement(Decorator, {
23
+ testId: testId,
24
+ bidiCharacter: bidiCharacter
25
+ }, skipChildren ? null : bidiCharacter))
26
+ );
27
+ }
28
+
29
+ return /*#__PURE__*/React.createElement(Decorator, {
30
+ testId: testId,
31
+ bidiCharacter: bidiCharacter
32
+ }, skipChildren ? null : bidiCharacter);
33
+ }
34
+ var CustomizedTagWithRef = /*#__PURE__*/React.forwardRef(function (props, ref) {
35
+ var children = props.children,
36
+ rest = _objectWithoutProperties(props, ["children"]);
37
+
38
+ return /*#__PURE__*/React.createElement("span", _extends({}, rest, {
39
+ ref: ref
40
+ }), children);
41
+ });
@@ -0,0 +1,64 @@
1
+ /** @jsx jsx */
2
+ import { css, jsx } from '@emotion/core';
3
+ import { Y75 } from '@atlaskit/theme/colors';
4
+ var decoration = css({
5
+ // Required as otherwise the following bidi characters cause the span
6
+ // to not receive hover events.
7
+ //
8
+ // U+2066 LEFT-TO-RIGHT ISOLATE (when using pseudo element before)
9
+ // U+202E RIGHT-TO-LEFT OVERRIDE' (when using pseudo element after)
10
+ position: 'relative',
11
+ ':before': {
12
+ /* layout */
13
+ display: 'inline-flex',
14
+ flexDirection: 'row',
15
+ justifyContent: 'center',
16
+ alignItems: 'center',
17
+ padding: '0 4px',
18
+ fontSize: '14px',
19
+ lineHeight: '18px',
20
+ background: Y75,
21
+
22
+ /**
23
+ * Ensures the decoration receives pointer events when it occurs with
24
+ * an ancestor that disables them.
25
+ */
26
+ pointerEvents: 'auto',
27
+
28
+ /* contents */
29
+ content: '"<"attr(data-bidi-character-code)">"',
30
+ // This color is Y800 which is not yet rolled out
31
+ // https://hello.atlassian.net/wiki/spaces/~tswan/pages/1366555782?focusedCommentId=1370387374#comment-1370387374
32
+ color: '#7F5F01',
33
+ fontStyle: 'normal'
34
+ },
35
+ ':hover:before': {
36
+ color: '#533F04'
37
+ }
38
+ });
39
+ export function Decorator(_ref) {
40
+ var bidiCharacter = _ref.bidiCharacter,
41
+ children = _ref.children,
42
+ testId = _ref.testId;
43
+ var bidiCharacterCode = getBidiCharacterCode(bidiCharacter);
44
+ return jsx("span", {
45
+ "aria-label": bidiCharacterCode
46
+ }, jsx("span", {
47
+ css: decoration,
48
+ "data-testid": testId,
49
+ "data-bidi-character-code": bidiCharacterCode // This is set to true so that the content is not read out by
50
+ // screen readers as the content includes angle brackets for
51
+ // visual decoration purposes.
52
+ // We use a span with the aria-label set to the bidi character code
53
+ // above this span for screen readers.
54
+ ,
55
+ "aria-hidden": "true"
56
+ }, children));
57
+ }
58
+
59
+ function getBidiCharacterCode(bidiCharacter) {
60
+ var _bidiCharacter$codePo;
61
+
62
+ var bidiCharacterCode = (_bidiCharacter$codePo = bidiCharacter.codePointAt(0)) === null || _bidiCharacter$codePo === void 0 ? void 0 : _bidiCharacter$codePo.toString(16);
63
+ return "U+".concat(bidiCharacterCode);
64
+ }
@@ -0,0 +1 @@
1
+ export {};
@@ -6,6 +6,7 @@ import { useGlobalTheme } from '@atlaskit/theme/components';
6
6
  import { useHighlightLines } from './internal/hooks/use-highlight';
7
7
  import { getCodeBlockStyles, getCodeBlockTheme } from './internal/theme/styles';
8
8
  import { normalizeLanguage } from './internal/utils/get-normalized-language';
9
+ import { createBidiWarningRenderer } from './react-syntax-highlighter-bidi-warning-renderer';
9
10
  var CodeBlock = /*#__PURE__*/memo(function CodeBlock(_ref) {
10
11
  var _ref$showLineNumbers = _ref.showLineNumbers,
11
12
  showLineNumbers = _ref$showLineNumbers === void 0 ? true : _ref$showLineNumbers,
@@ -18,7 +19,12 @@ var CodeBlock = /*#__PURE__*/memo(function CodeBlock(_ref) {
18
19
  _ref$highlightedEndTe = _ref.highlightedEndText,
19
20
  highlightedEndText = _ref$highlightedEndTe === void 0 ? 'Highlight end' : _ref$highlightedEndTe,
20
21
  testId = _ref.testId,
21
- text = _ref.text;
22
+ text = _ref.text,
23
+ _ref$codeBidiWarnings = _ref.codeBidiWarnings,
24
+ codeBidiWarnings = _ref$codeBidiWarnings === void 0 ? true : _ref$codeBidiWarnings,
25
+ codeBidiWarningLabel = _ref.codeBidiWarningLabel,
26
+ _ref$codeBidiWarningT = _ref.codeBidiWarningTooltipEnabled,
27
+ codeBidiWarningTooltipEnabled = _ref$codeBidiWarningT === void 0 ? true : _ref$codeBidiWarningT;
22
28
  var numLines = (text || '').split('\n').length;
23
29
  var globalTheme = useGlobalTheme();
24
30
  var theme = useMemo(function () {
@@ -46,6 +52,10 @@ var CodeBlock = /*#__PURE__*/memo(function CodeBlock(_ref) {
46
52
  }, [providedLanguage]); // https://product-fabric.atlassian.net/browse/DST-2472
47
53
 
48
54
  var languageToUse = text ? language : 'text';
55
+ var renderer = codeBidiWarnings ? createBidiWarningRenderer({
56
+ codeBidiWarningLabel: codeBidiWarningLabel,
57
+ codeBidiWarningTooltipEnabled: codeBidiWarningTooltipEnabled
58
+ }) : undefined;
49
59
  return jsx(SyntaxHighlighter, {
50
60
  "data-testid": testId,
51
61
  "data-code-lang": language,
@@ -57,7 +67,8 @@ var CodeBlock = /*#__PURE__*/memo(function CodeBlock(_ref) {
57
67
  ,
58
68
  wrapLines: highlight.length > 0 || !!testId,
59
69
  lineProps: getLineProps,
60
- useInlineStyles: false
70
+ useInlineStyles: false,
71
+ renderer: renderer
61
72
  }, text);
62
73
  });
63
74
  CodeBlock.displayName = 'CodeBlock';