Unverified Commit 84d78397 authored by Douglas Fabris's avatar Douglas Fabris Committed by GitHub

chore: Prettier installation (#317)

Co-authored-by: default avatarGuilherme Gazzo <guilherme@gazzo.xyz>
Co-authored-by: default avatarGuilherme Gazzo <guilhermegazzo@gmail.com>
parent e57e51ce
......@@ -3,6 +3,7 @@ module.exports = {
'plugin:@typescript-eslint/recommended',
'plugin:@typescript-eslint/eslint-recommended',
'@rocket.chat/eslint-config',
'prettier',
],
parser: '@typescript-eslint/parser',
parserOptions: {
......@@ -14,17 +15,25 @@ module.exports = {
legacyDecorators: true,
},
},
plugins: ['@typescript-eslint'],
plugins: ['@typescript-eslint', 'prettier'],
rules: {
'func-call-spacing': 'off',
indent: 'off',
'import/order': ['error', {
'newlines-between': 'always',
groups: ['builtin', 'external', 'internal', ['parent', 'sibling', 'index']],
alphabetize: {
order: 'asc',
'indent': 'off',
'import/order': [
'error',
{
'newlines-between': 'always',
'groups': [
'builtin',
'external',
'internal',
['parent', 'sibling', 'index'],
],
'alphabetize': {
order: 'asc',
},
},
}],
],
'no-empty-function': 'off',
'no-extra-parens': 'off',
'no-redeclare': 'off',
......@@ -33,32 +42,34 @@ module.exports = {
'no-unused-vars': 'off',
'no-useless-constructor': 'off',
'no-use-before-define': 'off',
'operator-linebreak': ['error', 'before', {
overrides: {
'=': 'after',
},
}],
'operator-linebreak': 'off',
'@typescript-eslint/ban-ts-ignore': 'off',
'@typescript-eslint/func-call-spacing': 'error',
'@typescript-eslint/indent': ['error', 2, {
SwitchCase: 1,
}],
'@typescript-eslint/indent': 'off',
'@typescript-eslint/no-explicit-any': 'off',
'@typescript-eslint/no-extra-parens': ['error', 'all', {
conditionalAssign: true,
nestedBinaryExpressions: false,
returnAssign: true,
ignoreJSX: 'all',
enforceForArrowConditionals: false,
}],
'@typescript-eslint/no-extra-parens': [
'error',
'all',
{
conditionalAssign: true,
nestedBinaryExpressions: false,
returnAssign: true,
ignoreJSX: 'all',
enforceForArrowConditionals: false,
},
],
'@typescript-eslint/no-redeclare': ['error'],
'@typescript-eslint/no-use-before-define': ['error'],
'@typescript-eslint/interface-name-prefix': 'off',
'@typescript-eslint/explicit-function-return-type': ['warn', {
allowExpressions: true,
}],
'@typescript-eslint/explicit-function-return-type': [
'warn',
{
allowExpressions: true,
},
],
'@typescript-eslint/no-unused-vars': 'off',
'@typescript-eslint/no-unused-vars-experimental': 'warn',
'prettier/prettier': 2,
},
env: {
browser: true,
......@@ -70,11 +81,7 @@ module.exports = {
settings: {
'import/resolver': {
node: {
extensions: [
'.js',
'.ts',
'.tsx',
],
extensions: ['.js', '.ts', '.tsx'],
},
},
},
......
{
"singleQuote": true,
"semi": true,
"quoteProps": "consistent",
"jsxSingleQuote": true,
"printWidth": 80
}
\ No newline at end of file
......@@ -31,6 +31,7 @@
"start": "microbundle watch",
"build": "microbundle",
"test": "jest",
"prettier-format": "prettier --config .prettierrc 'src/**/*.ts' --write",
"test:ci": "jest --runInBand",
"lint": "eslint --ext js,ts,tsx src",
"lint-staged": "lint-staged",
......@@ -42,11 +43,14 @@
"@typescript-eslint/eslint-plugin": "^4.5.0",
"@typescript-eslint/parser": "^4.5.0",
"documentation": "^13.0.2",
"eslint": "^7.12.0",
"eslint": "^7.13.0",
"eslint-config-prettier": "^6.15.0",
"eslint-plugin-import": "^2.19.1",
"eslint-plugin-prettier": "^3.1.4",
"jest": "^26.1.0",
"lint-staged": "^10.0.8",
"microbundle": "^0.12.2",
"prettier": "2.1.2",
"ts-jest": "^26.1.1",
"typescript": "^4.0.3"
},
......
......@@ -3,7 +3,9 @@ import { memoize } from './memoize';
/**
* Memoized and SSR-compatible facade of CSS.supports API
*/
export const cssSupports: ((value: string) => boolean) =
typeof window !== 'undefined' && typeof window.CSS !== 'undefined' && window.CSS.supports
export const cssSupports: (value: string) => boolean =
typeof window !== 'undefined' &&
typeof window.CSS !== 'undefined' &&
window.CSS.supports
? memoize((value: string) => window.CSS.supports(value))
: () => false;
......@@ -3,7 +3,7 @@ const store = new WeakMap<(arg: unknown) => unknown, Map<unknown, unknown>>();
export const memoize = <A, R>(fn: (arg: A) => R): typeof fn => {
const cache = new Map<A, R>();
const memoized: typeof fn = function(this: unknown, arg) {
const memoized: typeof fn = function (this: unknown, arg) {
if (cache.has(arg)) {
return cache.get(arg);
}
......
import hash from '@emotion/hash';
export const createAnimationName = (content: string): string =>
(content ? `rcx-@${ hash(content) }` : 'none');
content ? `rcx-@${hash(content)}` : 'none';
export const createClassName = (content: string): string =>
`rcx-@${ hash(content) }`;
`rcx-@${hash(content)}`;
export const escapeName = (animationOrClassName: string): string =>
animationOrClassName.replace(/@|#|:/g, (char) => `\\${ char }`);
animationOrClassName.replace(/@|#|:/g, (char) => `\\${char}`);
......@@ -4,7 +4,8 @@ const elementId = 'rcx-styles';
let element: HTMLStyleElement;
const getStyleTag = (): HTMLStyleElement => {
if (!element) {
element = (document.getElementById(elementId) || document.createElement('style')) as HTMLStyleElement;
element = (document.getElementById(elementId) ||
document.createElement('style')) as HTMLStyleElement;
element.id = elementId;
element.appendChild(document.createTextNode(''));
if (document.head) {
......@@ -19,7 +20,11 @@ let styleSheet: CSSStyleSheet;
const getStyleSheet = (): CSSStyleSheet => {
if (!styleSheet) {
const styleTag = getStyleTag();
const _styleSheet = styleTag.sheet || Array.from(document.styleSheets).find(({ ownerNode }) => ownerNode === styleTag);
const _styleSheet =
styleTag.sheet ||
Array.from(document.styleSheets).find(
({ ownerNode }) => ownerNode === styleTag
);
if (!_styleSheet) {
throw Error('could not get style sheet');
......@@ -46,13 +51,16 @@ const attachRulesIntoElement: RuleAttacher = (rules) => {
const attachRulesIntoStyleSheet: RuleAttacher = (rules) => {
const styleSheet = getStyleSheet();
const index = styleSheet.insertRule(`@media all{${ rules }}`, styleSheet.cssRules.length);
const index = styleSheet.insertRule(
`@media all{${rules}}`,
styleSheet.cssRules.length
);
const insertedRule = styleSheet.cssRules[index];
return () => {
const index = Array.prototype.findIndex.call(
styleSheet.cssRules,
(cssRule: CSSRule): boolean => cssRule === insertedRule,
(cssRule: CSSRule): boolean => cssRule === insertedRule
);
styleSheet.deleteRule(index);
};
......@@ -62,7 +70,10 @@ const wrapReferenceCounting = (attacher: RuleAttacher): RuleAttacher => {
const refs = {};
const queueMicrotask = (fn: () => void): void => {
if (typeof window === 'undefined' || typeof window.queueMicrotask !== 'function') {
if (
typeof window === 'undefined' ||
typeof window.queueMicrotask !== 'function'
) {
Promise.resolve().then(fn);
return;
}
......@@ -110,10 +121,8 @@ const wrapReferenceCounting = (attacher: RuleAttacher): RuleAttacher => {
* @returns a callback to detach the rules
*/
export const attachRules: RuleAttacher =
(typeof window === 'undefined' && discardRules)
|| (
process.env.NODE_ENV === 'production'
&& !!CSSStyleSheet.prototype.insertRule
&& wrapReferenceCounting(attachRulesIntoStyleSheet)
)
|| wrapReferenceCounting(attachRulesIntoElement);
(typeof window === 'undefined' && discardRules) ||
(process.env.NODE_ENV === 'production' &&
!!CSSStyleSheet.prototype.insertRule &&
wrapReferenceCounting(attachRulesIntoStyleSheet)) ||
wrapReferenceCounting(attachRulesIntoElement);
......@@ -12,47 +12,89 @@ describe('tags', () => {
});
it('evaluates as a trimmed string', () => {
expect(css` color: red; `()).toBe('color: red;');
expect(
css`
color: red;
`()
).toBe('color: red;');
});
it('interpolates numbers and strings', () => {
expect(css`border: ${ 0 };`()).toBe('border: 0;');
expect(css`color: ${ 'red' };`()).toBe('color: red;');
expect(
css`
border: ${0};
`()
).toBe('border: 0;');
expect(
css`
color: ${'red'};
`()
).toBe('color: red;');
});
it('interpolates array', () => {
expect(css`border: ${ ['1', 'px'] };`()).toBe('border: 1px;');
expect(
css`
border: ${['1', 'px']};
`()
).toBe('border: 1px;');
});
it('evaluates as a string ignoring undefined, null and false as interpolated values', () => {
expect(css`color: ${ false } ${ undefined } ${ null };`()).toBe('color: ;');
expect(
css`
color: ${false} ${undefined} ${null};
`()
).toBe('color: ;');
});
it('evaluates as a string if some interpolated value is a function', () => {
expect(css`color: ${ (): string => 'red' };`()).toBe('color: red;');
expect(
css`
color: ${(): string => 'red'};
`()
).toBe('color: red;');
});
it('interpolates functions with args passed', () => {
expect(css`color: ${ (colorValue: string): string => colorValue };`('red')).toBe('color: red;');
expect(
css`
color: ${(colorValue: string): string => colorValue};
`('red')
).toBe('color: red;');
});
it('interpolates other `css` tagged template strings', () => {
expect(css`color: ${ css`red` };`()).toBe('color: red;');
expect(
css`
color: ${css`red`};
`()
).toBe('color: red;');
});
it('interpolates `keyframes` tagged template strings', () => {
expect(css`animation-name: ${ keyframes`
expect(
css`
animation-name: ${keyframes`
from {
opacity: 0;
}
to {
opacity: 1;
}
` };`()).toEqual(expect.stringMatching(/^animation-name: rcx-(\\@[0-9a-z]+);@keyframes rcx-\1{/));
`};
`()
).toEqual(
expect.stringMatching(
/^animation-name: rcx-(\\@[0-9a-z]+);@keyframes rcx-\1{/
)
);
});
it('fails safely with invalid arguments', () => {
const cssUntyped = css as unknown as (...args: any) => (...args: unknown[]) => string;
const cssUntyped = (css as unknown) as (
...args: any
) => (...args: unknown[]) => string;
expect(cssUntyped()()).toBe('');
expect(cssUntyped(null)()).toBe('');
expect(cssUntyped([])()).toBe('');
......@@ -74,51 +116,69 @@ describe('tags', () => {
const escapedAnimationName = keyframes` from { opacity: 0; } `();
expect(freeContext()).toBe(`@keyframes ${ escapedAnimationName }{from { opacity: 0; }}`);
expect(freeContext()).toBe(
`@keyframes ${escapedAnimationName}{from { opacity: 0; }}`
);
});
it('interpolates numbers and strings', () => {
const [, freeContext] = holdContext();
const escapedAnimationName = keyframes`from { opacity: ${ 0 }; }`();
const escapedAnimationName = keyframes`from { opacity: ${0}; }`();
expect(freeContext()).toBe(`@keyframes ${ escapedAnimationName }{from { opacity: 0; }}`);
expect(freeContext()).toBe(
`@keyframes ${escapedAnimationName}{from { opacity: 0; }}`
);
});
it('evaluates as a string ignoring undefined, null and false as interpolated values', () => {
const [, freeContext] = holdContext();
const escapedAnimationName = keyframes`from { opacity: ${ false } ${ undefined } ${ null }; }`();
const escapedAnimationName = keyframes`from { opacity: ${false} ${undefined} ${null}; }`();
expect(freeContext()).toBe(`@keyframes ${ escapedAnimationName }{from { opacity: ; }}`);
expect(freeContext()).toBe(
`@keyframes ${escapedAnimationName}{from { opacity: ; }}`
);
});
it('evaluates as a string if some interpolated value is a function', () => {
const [, freeContext] = holdContext();
const escapedAnimationName = keyframes`from { opacity: ${ () => 0 }; }`();
const escapedAnimationName = keyframes`from { opacity: ${() => 0}; }`();
expect(freeContext()).toBe(`@keyframes ${ escapedAnimationName }{from { opacity: 0; }}`);
expect(freeContext()).toBe(
`@keyframes ${escapedAnimationName}{from { opacity: 0; }}`
);
});
it('interpolates functions with args passed', () => {
const [, freeContext] = holdContext();
const escapedAnimationName = keyframes`from { opacity: ${ (opacityValue: number) => opacityValue }; }`(0.5);
const escapedAnimationName = keyframes`from { opacity: ${(
opacityValue: number
) => opacityValue}; }`(0.5);
expect(freeContext()).toBe(`@keyframes ${ escapedAnimationName }{from { opacity: 0.5; }}`);
expect(freeContext()).toBe(
`@keyframes ${escapedAnimationName}{from { opacity: 0.5; }}`
);
});
it('interpolates `css` tagged template strings', () => {
const [, freeContext] = holdContext();
const escapedAnimationName = keyframes`from { ${ css`opacity: 0;` } }`();
const escapedAnimationName = keyframes`from { ${css`
opacity: 0;
`} }`();
expect(freeContext()).toBe(`@keyframes ${ escapedAnimationName }{from { opacity: 0; }}`);
expect(freeContext()).toBe(
`@keyframes ${escapedAnimationName}{from { opacity: 0; }}`
);
});
it('fails safely with invalid arguments', () => {
const keyframesUntyped = keyframes as unknown as (...args: any) => (...args: unknown[]) => string;
const keyframesUntyped = (keyframes as unknown) as (
...args: any
) => (...args: unknown[]) => string;
expect(keyframesUntyped()()).toBe('none');
expect(keyframesUntyped(null)()).toBe('none');
expect(keyframesUntyped([])()).toBe('none');
......
......@@ -18,18 +18,21 @@ let currentContext: EvaluationContext | undefined = undefined;
* @returns a pair of the evaluation context and a function to free it,
* returning the additional evaluation stored at the context.
*/
export const holdContext = (): [EvaluationContext, (() => string)] => {
export const holdContext = (): [EvaluationContext, () => string] => {
if (currentContext) {
return [currentContext, () => ''];
}
currentContext = [];
return [currentContext, () => {
const additions = (currentContext || []).join('');
currentContext = undefined;
return additions;
}];
return [
currentContext,
() => {
const additions = (currentContext || []).join('');
currentContext = undefined;
return additions;
},
];
};
/**
......@@ -40,7 +43,7 @@ type Evaluable = (...args: readonly unknown[]) => string;
const isEvaluable = (x: unknown): x is Evaluable => typeof x === 'function';
const staticEvaluable = memoize(
<T extends Evaluable>(content: string): T => Object.freeze(() => content) as T,
<T extends Evaluable>(content: string): T => Object.freeze(() => content) as T
);
export type cssFn = Evaluable;
......@@ -65,20 +68,29 @@ const evaluateValue = (value: unknown, args: readonly unknown[]): string => {
const reduceEvaluable = (
[first, ...rest]: readonly string[],
values: readonly unknown[],
args: readonly unknown[],
args: readonly unknown[]
): string =>
values.reduce<string>(
(string, value, i) => string + evaluateValue(value, args) + rest[i],
first,
).trim();
values
.reduce<string>(
(string, value, i) => string + evaluateValue(value, args) + rest[i],
first
)
.trim();
/**
* Template string tag to declare CSS content chunks.
*
* @returns a callback to render the CSS content
*/
export const css = (slices: TemplateStringsArray, ...values: readonly unknown[]): cssFn => {
if (!slices || slices.length === 0 || slices.some((slice) => typeof slice !== 'string')) {
export const css = (
slices: TemplateStringsArray,
...values: readonly unknown[]
): cssFn => {
if (
!slices ||
slices.length === 0 ||
slices.some((slice) => typeof slice !== 'string')
) {
return staticEvaluable('');
}
......@@ -102,8 +114,15 @@ export const css = (slices: TemplateStringsArray, ...values: readonly unknown[])
*
* @returns a callback to render the CSS at-rule content
*/
export const keyframes = (slices: TemplateStringsArray, ...values: unknown[]): keyframesFn => {
if (!slices || slices.length === 0 || slices.some((slice) => typeof slice !== 'string')) {
export const keyframes = (
slices: TemplateStringsArray,
...values: unknown[]
): keyframesFn => {
if (
!slices ||
slices.length === 0 ||
slices.some((slice) => typeof slice !== 'string')
) {