31 |
{this.props['data-title']}
32 | {
33 |
34 | /*
35 | * Несмотря на то, что мы пробрасываем направление,
36 | * сам элемент изменит его только спустя какое-то время
37 | */
38 | }
39 |
40 | {marquee}
41 |
42 |
43 | {text}
44 |
45 |
46 | );
47 | }
48 |
49 | public handleClick = () => {
50 | const { isLeft } = this.state;
51 | this.setState({
52 | isLeft: !isLeft
53 | });
54 | }
55 | }
56 |
--------------------------------------------------------------------------------
/lints/test/no-async.js:
--------------------------------------------------------------------------------
1 | const { RuleTester } = require('eslint');
2 |
3 | const { getCreateSuite } = require('./utils');
4 | const rule = require('../rules/no-async');
5 |
6 | const ruleTester = new RuleTester();
7 |
8 | const createErrorMessage = varName => `Variable [${varName}] should be used only in safe react lifecycle methods`;
9 | const createSuite = getCreateSuite(createErrorMessage);
10 |
11 | ruleTester.run('no-undefined-window', rule, {
12 | valid: [
13 | // React safe lifecycle and namespace import
14 | createSuite('import * as React from \'react\'; class myComponent extends React.PureComponent' +
15 | ' { componentDidMount() { setTimeout(() => console.log(123), 123) } }'),
16 |
17 | // React safe lifecycle and namespace import
18 | createSuite('function asyncFn(setTimeout) {setTimeout(() => alert(2), 100)}')
19 | ],
20 | invalid: [
21 | // React unsafe lifecycle and namespace import — setTimeout
22 | createSuite('import * as React from \'react\'; class myComponent extends React.PureComponent' +
23 | ' { render() { setTimeout(() => console.log(123), 123) } }', 'setTimeout'),
24 |
25 | // React unsafe lifecycle and namespace import - setInterval
26 | createSuite('import * as React from \'react\'; class myComponent extends React.PureComponent' +
27 | ' { constructor() { setInterval(() => console.log(123), 123) } }', 'setInterval'),
28 |
29 | // React unsafe lifecycle and namespace import - new Promise
30 | createSuite('import * as React from \'react\'; class myComponent extends React.PureComponent' +
31 | ' { getDerivedStateFromProps() { new Promise(\'a\') } }', 'Promise'),
32 |
33 | // React unsafe lifecycle and namespace import - Promise.resolve()
34 | createSuite('import * as React from \'react\'; class myComponent extends React.PureComponent' +
35 | ' { render() { Promise.resolve() } }', 'Promise')
36 | ]
37 | });
38 |
--------------------------------------------------------------------------------
/components/ExtEmbed/ExtEmbed.scss:
--------------------------------------------------------------------------------
1 | @keyframes turbo-loading-dots {
2 | 0% {
3 | opacity: .4;
4 | }
5 |
6 | 5% {
7 | opacity: 1;
8 | }
9 |
10 | 25% {
11 | opacity: 1;
12 | }
13 |
14 | 33% {
15 | opacity: .4;
16 | }
17 | }
18 |
19 | .ext-embed {
20 | position: relative;
21 |
22 | &__iframe {
23 | overflow: hidden;
24 |
25 | border: none;
26 | }
27 |
28 | &__loader {
29 | position: absolute;
30 | z-index: 1;
31 | top: 0;
32 | right: 0;
33 | bottom: 0;
34 | left: 0;
35 |
36 | background: #f5f5f5;
37 | }
38 |
39 | &__loader-inner {
40 | position: absolute;
41 | top: 50%;
42 | left: 50%;
43 |
44 | transform: translate(-50%, -50%);
45 | }
46 |
47 | &__loader-text {
48 | font-size: 11px;
49 | font-style: normal;
50 | line-height: 13px;
51 | white-space: nowrap;
52 | letter-spacing: .5px;
53 | text-transform: uppercase;
54 | text-overflow: ellipsis;
55 |
56 | color: #999;
57 | }
58 |
59 | &__loader-dots {
60 | position: relative;
61 |
62 | height: 4px;
63 | margin-top: 5px;
64 | }
65 |
66 | &__loader-dot {
67 | position: absolute;
68 |
69 | left: 50%;
70 |
71 | width: 4px;
72 | height: 4px;
73 |
74 | opacity: .4;
75 |
76 | border-radius: 2px;
77 | background-color: #999;
78 |
79 | transform: translate(-50%, 0);
80 |
81 | animation-name: turbo-loading-dots;
82 | animation-duration: 1s;
83 | animation-timing-function: linear;
84 | animation-iteration-count: infinite;
85 |
86 | &:nth-child(1) {
87 | margin-left: -8px;
88 |
89 | animation-delay: -.5s;
90 | }
91 |
92 | &:nth-child(2) {
93 | animation-delay: -.25s;
94 | }
95 |
96 | &:nth-child(3) {
97 | margin-left: 8px;
98 | }
99 | }
100 | }
101 |
--------------------------------------------------------------------------------
/lints/rules/shared/isSafeLogicalExpression.js:
--------------------------------------------------------------------------------
1 |
2 | const { isValidBinary, isValidBinaryReverse } = require('./isValidBinaryExpression');
3 | const { isValidLogicalInner, isValidLogicalInnerReverse } = require('./isValidLogicalInner');
4 | const { isValidIdentifier, isValidIdentifierReverse } = require('./isValidIdentifier');
5 | const { isConjunction, isDisjunction, isLogicalExpression, getLogicalExpressionParent } = require('./utils');
6 |
7 | /** Checks the node is the right part of logical statement */
8 | const isRight = node => node.parent.right === node;
9 |
10 | /** Considering node valid if test is the left part guards the right part */
11 | const isValid = (node, parent) => (isValidBinary(parent.left) ||
12 | isValidLogicalInner(parent.left) ||
13 | isValidIdentifier(parent.left)) &&
14 | isConjunction(parent) && isRight(node);
15 |
16 | /** Mirrored version of upper function */
17 | const isValidReverse = (node, parent) => (isValidBinaryReverse(parent.left) ||
18 | isValidLogicalInnerReverse(parent.left) ||
19 | isValidIdentifierReverse(parent.left)) &&
20 | isDisjunction(parent) && isRight(node);
21 |
22 | /** Safe logical is the first function to call on node */
23 | const isValidLogical = node => {
24 | const { item, parent } = getLogicalExpressionParent(node);
25 |
26 | if (!isLogicalExpression(parent)) {
27 | return false;
28 | }
29 |
30 | /*
31 | * If is not valid still can be guarded by upper logical expression:
32 | * E.g. window.document.title || window.savedTitle will be correct if an expression is guarded with conjunction,
33 | * e.g. typeof window !== undefined && (window.document.title || window.savedTitle).
34 | * Which can be done by the function itself recursively.
35 | */
36 |
37 | return isValid(item, parent) || isValidLogical(parent);
38 | };
39 |
40 | const isValidLogicalReverse = node => {
41 | const { item, parent } = getLogicalExpressionParent(node);
42 |
43 | if (!isLogicalExpression(parent)) {
44 | return false;
45 | }
46 |
47 | return isValidReverse(item, parent) || isValidLogicalReverse(parent);
48 | };
49 |
50 | const isSafeLogicalExpression = node => isValidLogical(node) || isValidLogicalReverse(node);
51 |
52 | module.exports = { isSafeLogicalExpression };
53 |
--------------------------------------------------------------------------------
/lints/rules/no-undefined-window/index.js:
--------------------------------------------------------------------------------
1 | /** @module Eslint plugin to check if window is checked for existence in scope */
2 |
3 | const globals = require('./globals');
4 |
5 | const { isSafeTypeofExpression } = require('../shared/isValidBinaryExpression');
6 | const { isSafeLogicalExpression } = require('../shared/isSafeLogicalExpression');
7 | const isGuardedUpper = require('../shared/isGuardedUpper');
8 | const isSafeReactMethod = require('../shared/isSafeReactMethod');
9 |
10 | const getFirstParent = require('../shared/getFirstParent');
11 |
12 | module.exports = {
13 | create(context) {
14 | const checkIsSafe = ({ identifier: node }) => {
15 | const startFrom = getFirstParent(node);
16 |
17 | // From fastest to slowest
18 |
19 | // Typeof window
20 | if (isSafeTypeofExpression(startFrom) ||
21 | // Typeof window !== undefined && document
22 | isSafeLogicalExpression(startFrom) ||
23 | // ComponentDidMount() { alert('mounted!' }
24 | isSafeReactMethod(startFrom) ||
25 | // If (typeof window !== 'undefined') {alert('window is defined!')}
26 | isGuardedUpper(startFrom)) {
27 | return;
28 | }
29 | context.report({
30 | message: `Variable [${node.name}] should be protected via (typeof window !== 'undefined')`,
31 | node
32 | });
33 | };
34 |
35 | return {
36 | Program() {
37 | // Get the context of the program
38 | const scope = context.getScope();
39 | // Find window variable
40 | scope.variables.forEach(variable => {
41 | if (!variable.defs.length && globals.has(variable.name)) {
42 | variable.references.forEach(checkIsSafe);
43 | }
44 | });
45 | }
46 | };
47 | },
48 |
49 | meta: {
50 | docs: {
51 | category: 'Turbo Custom Components custom lints',
52 | description: 'Disallow usage of window in node.js and browser environments without typeof guard',
53 | url: 'https://github.com/turboext/ugc/tree/master/lints/eslint/no-undefined-window/Readme.md'
54 | },
55 | schema: []
56 | }
57 | };
58 |
--------------------------------------------------------------------------------
/tools/lint-filesystem.ts:
--------------------------------------------------------------------------------
1 | import { resolve } from 'path';
2 | import { readdir, lstatSync } from 'fs-extra';
3 | import pascalCase = require('pascal-case');
4 | import chalk from 'chalk';
5 |
6 | const { yellow, green, cyanBright: cyan, redBright: red } = chalk;
7 |
8 | const componentsRoot = resolve(__dirname, '..', 'components');
9 | const getErrs = () => readdir(componentsRoot)
10 | .then(dirs => {
11 | const errs: string[] = [];
12 |
13 | const promises = dirs.map((dir: string): Promise