Add a custom rule to prevent colors from being hardcoded outside of theme (#288)

* Add a custom rule to prevent colors from being hardcoded in ESLint

* Refactor colors

* Create packages folder and fix colors

* Remove external dependency for css alphabetical order linting

* Fix install with yarn

---------

Co-authored-by: Charles Bochet <charles@twenty.com>
This commit is contained in:
Félix Malfait 2023-06-14 16:56:29 +02:00 committed by GitHub
parent bf6fb0ba70
commit 31f3950439
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
61 changed files with 31490 additions and 62652 deletions

View File

@ -6,5 +6,5 @@
"ghcr.io/devcontainers-contrib/features/jshint:2": {}
},
"forwardPorts": [3000, 3001, 5432],
"postCreateCommand": "cd front && npm install && cd ../server && npm install"
"postCreateCommand": "cd front && yarn && cd ../server && yarn"
}

View File

@ -16,17 +16,6 @@ jobs:
uses: actions/setup-node@v3
with:
node-version: "18"
- name: Cache node modules
uses: actions/cache@v2
env:
cache-name: cache-node-modules
with:
path: ~/.npm
key: ${{ runner.os }}-build-${{ env.cache-name }}-${{ hashFiles('**/package-lock.json') }}
restore-keys: |
${{ runner.os }}-build-${{ env.cache-name }}-
${{ runner.os }}-build-
${{ runner.os }}-
- name: Front / Write .env
run: |
cd front
@ -34,7 +23,7 @@ jobs:
echo "REACT_APP_API_URL: $REACT_APP_API_URL" >> .env
echo "REACT_APP_AUTH_URL: $REACT_APP_AUTH_URL" >> .env
- name: Front / Install Dependencies
run: cd front && npm install
run: cd front && yarn
- name: Publish to Chromatic
uses: chromaui/action@v1
with:

View File

@ -10,18 +10,7 @@ jobs:
uses: actions/setup-node@v3
with:
node-version: "18"
- name: Cache node modules
uses: actions/cache@v2
env:
cache-name: cache-node-modules
with:
path: ~/.npm
key: ${{ runner.os }}-build-${{ env.cache-name }}-${{ hashFiles('**/package-lock.json') }}
restore-keys: |
${{ runner.os }}-build-${{ env.cache-name }}-
${{ runner.os }}-build-
${{ runner.os }}-
- name: Docs / Install Dependencies
run: cd docs && npm install
run: cd docs && yarn
- name: Docs / Build Documentation
run: cd docs && npm run build
run: cd docs && yarn build

View File

@ -13,17 +13,6 @@ jobs:
uses: actions/setup-node@v3
with:
node-version: "18"
- name: Cache node modules
uses: actions/cache@v2
env:
cache-name: cache-node-modules
with:
path: ~/.npm
key: ${{ runner.os }}-build-${{ env.cache-name }}-${{ hashFiles('**/package-lock.json') }}
restore-keys: |
${{ runner.os }}-build-${{ env.cache-name }}-
${{ runner.os }}-build-
${{ runner.os }}-
- name: Front / Write .env
run: |
cd front
@ -31,18 +20,18 @@ jobs:
echo "REACT_APP_API_URL: $REACT_APP_API_URL" >> .env
echo "REACT_APP_AUTH_URL: $REACT_APP_AUTH_URL" >> .env
- name: Front / Install Dependencies
run: cd front && npm install
run: cd front && yarn
- name: Front / Install Playwright
run: cd front && npx playwright install --with-deps
- name: Front / Run linter
run: cd front && npm run lint
run: cd front && yarn lint
- name: Front / Build Storybook
run: cd front && npm run build-storybook --quiet
run: cd front && yarn build-storybook --quiet
- name: Front / Run storybook tests
run: |
cd front && npx concurrently -k -s first -n "SB,TEST" -c "magenta,blue" \
"npx http-server storybook-static --silent --port 6006" \
"npm run coverage"
"yarn coverage"
- name: Front / Run jest tests
run: |
cd front && npm run test
cd front && yarn test

View File

@ -10,21 +10,10 @@ jobs:
uses: actions/setup-node@v3
with:
node-version: "18"
- name: Cache node modules
uses: actions/cache@v2
env:
cache-name: cache-node-modules
with:
path: ~/.npm
key: ${{ runner.os }}-build-${{ env.cache-name }}-${{ hashFiles('**/package-lock.json') }}
restore-keys: |
${{ runner.os }}-build-${{ env.cache-name }}-
${{ runner.os }}-build-
${{ runner.os }}-
- name: Server / Install Dependencies
run: cd server && npm install
run: cd server && yarn install
- name: Server / Run linter
run: cd server && npm run lint
run: cd server && yarn lint
- name: Server / Run jest tests
run: |
cd server && npm run test
cd server && yarn test

3
.gitignore vendored
View File

@ -1,2 +1,3 @@
**/**/.env
.DS_Store
.DS_Store
node_modules/

View File

@ -88,16 +88,16 @@ Most default value should work out of the box, but don't forget to update the da
On the frontend:
```
cd front
npm install
yarn
```
On the server side:
```
cd server
npm install
npm run prisma:migrate
yarn
yarn prisma:migrate
```
You can also add `npm run prisma:seed` to seed the database with mock data.
You can also add `yarn prisma:seed` to seed the database with mock data.
### 5. Auth Setup
Right now the only way to authenticate yourself is to setup Google Sign-in in `server/.env`
@ -107,10 +107,10 @@ We will add an easier option soon.
On the frontend:
```
cd front
npm run start
yarn start
```
On the server side:
```
cd server
npm run start
yarn start
```

13378
docs/package-lock.json generated

File diff suppressed because it is too large Load Diff

8061
docs/yarn.lock Normal file

File diff suppressed because it is too large Load Diff

View File

@ -5,7 +5,7 @@ module.exports = {
tsconfigRootDir: __dirname,
sourceType: 'module',
},
plugins: ['@typescript-eslint/eslint-plugin', 'simple-import-sort', 'better-styled-components'],
plugins: ['@typescript-eslint/eslint-plugin', 'simple-import-sort', 'twenty'],
extends: [
'plugin:@typescript-eslint/recommended',
'plugin:prettier/recommended',
@ -45,6 +45,7 @@ module.exports = {
'@typescript-eslint/no-explicit-any': 'off',
'simple-import-sort/imports': 'error',
'simple-import-sort/exports': 'error',
'better-styled-components/sort-declarations-alphabetically': 2
},
'twenty/sort-css-properties-alphabetically': 'error',
'twenty/no-hardcoded-colors': 'error'
}
};

View File

@ -6,7 +6,7 @@ This project was bootstrapped with [Create React App](https://github.com/faceboo
In the project directory, you can run:
### `npm start`
### `yarn start`
Runs the app in the development mode.\
Open [http://localhost:3000](http://localhost:3000) to view it in the browser.
@ -14,12 +14,12 @@ Open [http://localhost:3000](http://localhost:3000) to view it in the browser.
The page will reload if you make edits.\
You will also see any lint errors in the console.
### `npm test`
### `yarn test`
Launches the test runner in the interactive watch mode.\
See the section about [running tests](https://facebook.github.io/create-react-app/docs/running-tests) for more information.
### `npm run build`
### `yarn build`
Builds the app for production to the `build` folder.\
It correctly bundles React in production mode and optimizes the build for the best performance.
@ -29,7 +29,7 @@ Your app is ready to be deployed!
See the section about [deployment](https://facebook.github.io/create-react-app/docs/deployment) for more information.
### `npm run eject`
### `yarn eject`
**Note: this is a one-way operation. Once you `eject`, you cant go back!**

39485
front/package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -4,6 +4,7 @@
"private": true,
"dependencies": {
"@apollo/client": "^3.7.5",
"@babel/plugin-proposal-private-property-in-object": "^7.21.11",
"@emotion/react": "^11.10.6",
"@emotion/styled": "^11.10.5",
"@hello-pangea/dnd": "^16.2.0",
@ -115,7 +116,6 @@
"eslint-config-prettier": "^8.5.0",
"eslint-config-react-app": "^7.0.1",
"eslint-config-standard-with-typescript": "^23.0.0",
"eslint-plugin-better-styled-components": "^1.1.2",
"eslint-plugin-import": "^2.26.0",
"eslint-plugin-n": "^15.5.1",
"eslint-plugin-prettier": "^4.2.1",
@ -123,6 +123,7 @@
"eslint-plugin-react": "^7.31.11",
"eslint-plugin-simple-import-sort": "^10.0.0",
"eslint-plugin-storybook": "^0.6.11",
"eslint-plugin-twenty": "file:../packages/eslint-plugin-twenty",
"http-server": "^14.1.1",
"mock-apollo-client": "^1.2.1",
"msw": "^1.2.1",

View File

@ -2,7 +2,7 @@
/* tslint:disable */
/**
* Mock Service Worker (1.2.1).
* Mock Service Worker (1.2.2).
* @see https://github.com/mswjs/msw
* - Please do NOT modify this file.
* - Please do NOT serve this file on production.

View File

@ -49,9 +49,11 @@ const StyledDate = styled.div`
const StyledTooltip = styled(Tooltip)`
background-color: ${(props) => props.theme.primaryBackground};
box-shadow: 0px 2px 4px 3px rgba(0, 0, 0, 0.04);
box-shadow: 0px 2px 4px 3px
${(props) => props.theme.lightBackgroundTransparent};
box-shadow: 2px 4px 16px 6px rgba(0, 0, 0, 0.12);
box-shadow: 2px 4px 16px 6px
${(props) => props.theme.lightBackgroundTransparent};
color: ${(props) => props.theme.text100};

View File

@ -4,7 +4,7 @@ import { Command } from 'cmdk';
export const StyledDialog = styled(Command.Dialog)`
background: ${(props) => props.theme.primaryBackground};
border-radius: ${(props) => props.theme.borderRadius};
box-shadow: 0px 3px 12px rgba(0, 0, 0, 0.09);
box-shadow: ${(props) => props.theme.modalBoxShadow};
font-family: ${(props) => props.theme.fontFamily};
left: 50%;
max-width: 640px;
@ -45,7 +45,7 @@ export const StyledItem = styled(Command.Item)`
transition-property: none;
user-select: none;
&:hover {
background: ${(props) => props.theme.clickableElementBackgroundHover};
background: ${(props) => props.theme.lightBackgroundTransparent};
}
&[data-selected='true'] {
background: ${(props) => props.theme.secondaryBackground};

View File

@ -2,7 +2,7 @@ import styled from '@emotion/styled';
export const HoverableMenuItem = styled.div`
align-items: center;
background: rgba(0, 0, 0, 0);
background: ${(props) => props.theme.primaryBackground};
border-radius: 4px;
box-sizing: border-box;
cursor: pointer;
@ -14,6 +14,6 @@ export const HoverableMenuItem = styled.div`
width: 100%;
&:hover {
background: rgba(0, 0, 0, 0.04);
background: ${(props) => props.theme.lightBackgroundTransparent};
}
`;

View File

@ -26,7 +26,7 @@ const StyledCalendarContainer = styled.div<StyledCalendarContainerProps>`
background: ${(props) => props.theme.secondaryBackground};
border: 1px solid ${(props) => props.theme.primaryBorder};
border-radius: 8px;
box-shadow: 0px 3px 12px rgba(0, 0, 0, 0.09);
box-shadow: ${(props) => props.theme.modalBoxShadow};
left: -10px;
position: absolute;
top: 10px;

View File

@ -127,7 +127,7 @@ const StyledContainer = styled.div`
margin: 2px;
&:hover {
background: rgba(0, 0, 0, 0.04);
background: ${(props) => props.theme.lightBackgroundTransparent};
}
&:first-of-type {
@ -172,7 +172,7 @@ const StyledContainer = styled.div`
border-radius: 4px;
padding-top: 6px;
&:hover {
background: rgba(0, 0, 0, 0.04);
background: ${(props) => props.theme.lightBackgroundTransparent};
}
}
& .react-datepicker__navigation--previous {

View File

@ -90,7 +90,7 @@ const StyledDropdownItem = styled.li`
width: calc(160px - ${(props) => props.theme.spacing(4)});
&:hover {
background: rgba(0, 0, 0, 0.04);
background: ${(props) => props.theme.lightBackgroundTransparent};
}
`;
@ -112,7 +112,7 @@ const StyledDropdownTopOption = styled.li`
calc(${(props) => props.theme.spacing(2)});
&:hover {
background: rgba(0, 0, 0, 0.04);
background: ${(props) => props.theme.lightBackgroundTransparent};
}
user-select: none;
`;

View File

@ -22,7 +22,8 @@ type StyledItemProps = {
const StyledItem = styled.button<StyledItemProps>`
align-items: center;
background: ${(props) => (props.active ? 'rgba(0, 0, 0, 0.04)' : 'inherit')};
background: ${(props) =>
props.active ? props.theme.lightBackgroundTransparent : 'inherit'};
border: none;
border-radius: 4px;
color: ${(props) => {
@ -47,7 +48,7 @@ const StyledItem = styled.button<StyledItemProps>`
padding-top: ${(props) => props.theme.spacing(1)};
pointer-events: ${(props) => (props.soon ? 'none' : 'auto')};
:hover {
background: rgba(0, 0, 0, 0.04);
background: ${(props) => props.theme.lightBackgroundTransparent};
color: ${(props) => (props.danger ? props.theme.red : props.theme.text100)};
}
user-select: none;
@ -67,7 +68,7 @@ const StyledSoonPill = styled.div`
justify-content: center;
align-items: center;
border-radius: 50px;
background-color: rgba(0, 0, 0, 0.04);
background-color: ${(props) => props.theme.lightBackgroundTransparent};
font-size: ${(props) => props.theme.fontSizeExtraSmall};
padding: ${(props) => props.theme.spacing(1)}
${(props) => props.theme.spacing(2)} ${(props) => props.theme.spacing(1)}

View File

@ -20,7 +20,7 @@ const StyledButton = styled.button`
width: 24px;
&:hover {
background: ${(props) => props.theme.clickableElementBackgroundHover};
background: ${(props) => props.theme.lightBackgroundTransparent};
}
svg {
color: ${(props) => props.theme.text40};

View File

@ -48,12 +48,15 @@ const lightThemeSpecific = {
primaryBackgroundTransparent: 'rgba(255, 255, 255, 0.8)',
secondaryBackgroundTransparent: 'rgba(252, 252, 252, 0.8)',
strongBackgroundTransparent: 'rgba(0, 0, 0, 0.16)',
mediumBackgroundTransparent: 'rgba(0, 0, 0, 0.08)',
lightBackgroundTransparent: 'rgba(0, 0, 0, 0.04)',
lighterBackgroundTransparent: 'rgba(0, 0, 0, 0.02)',
primaryBorder: 'rgba(0, 0, 0, 0.08)',
lightBorder: '#f5f5f5',
mediumBorder: '#ebebeb',
clickableElementBackgroundHover: 'rgba(0, 0, 0, 0.04)',
clickableElementBackgroundTransition: 'background 0.1s ease',
text100: '#000',
@ -93,8 +96,11 @@ const darkThemeSpecific: typeof lightThemeSpecific = {
primaryBackgroundTransparent: 'rgba(20, 20, 20, 0.8)',
secondaryBackgroundTransparent: 'rgba(23, 23, 23, 0.8)',
strongBackgroundTransparent: 'rgba(255, 255, 255, 0.09)',
mediumBackgroundTransparent: 'rgba(255, 255, 255, 0.06)',
lightBackgroundTransparent: 'rgba(255, 255, 255, 0.03)',
lighterBackgroundTransparent: 'rgba(255, 255, 255, 0.02)',
clickableElementBackgroundHover: 'rgba(0, 0, 0, 0.04)',
clickableElementBackgroundTransition: 'background 0.1s ease',
primaryBorder: 'rgba(255, 255, 255, 0.08)',
@ -148,7 +154,7 @@ export const hoverBackground = (props: any) =>
css`
transition: background 0.1s ease;
&:hover {
background: rgba(0, 0, 0, 0.04);
background: ${props.theme.lightBackgroundTransparent};
}
`;

17119
front/yarn.lock Normal file

File diff suppressed because it is too large Load Diff

View File

@ -15,25 +15,25 @@ sh:
@docker-compose exec twenty-dev sh
front-start:
@docker-compose exec twenty-dev sh -c "cd /app/front && npm run start"
@docker-compose exec twenty-dev sh -c "cd /app/front && yarn start"
front-test:
@docker-compose exec twenty-dev sh -c "cd /app/front && npm run test"
@docker-compose exec twenty-dev sh -c "cd /app/front && yarn test"
front-graphql-generate:
@docker-compose exec twenty-dev sh -c "cd /app/server && npm run graphql:generate"
@docker-compose exec twenty-dev sh -c "cd /app/server && yarn graphql:generate"
front-storybook:
@docker-compose exec twenty-dev sh -c "cd /app/front && npm run storybook"
@docker-compose exec twenty-dev sh -c "cd /app/front && yarn storybook"
server-start:
@docker-compose exec twenty-dev sh -c "cd /app/server && npm run start:dev"
@docker-compose exec twenty-dev sh -c "cd /app/server && yarn start:dev"
server-prisma-generate:
@docker-compose exec twenty-dev sh -c "cd /app/server && npm run prisma:generate"
@docker-compose exec twenty-dev sh -c "cd /app/server && yarn prisma:generate"
server-prisma-migrate:
@docker-compose exec twenty-dev sh -c "cd /app/server && npm run prisma:migrate"
@docker-compose exec twenty-dev sh -c "cd /app/server && yarn prisma:migrate"
server-prisma-seed:
@docker-compose exec twenty-dev sh -c "cd /app/server && npm run prisma:seed"
@docker-compose exec twenty-dev sh -c "cd /app/server && yarn prisma:seed"

6
infra/dev/package-lock.json generated Normal file
View File

@ -0,0 +1,6 @@
{
"name": "dev",
"lockfileVersion": 3,
"requires": true,
"packages": {}
}

View File

@ -1,17 +1,19 @@
FROM node:18.16.0 as front
FROM node:18.16-bullseye as front
COPY /../../packages /app/packages
WORKDIR /app/front
COPY ../../front/package.json .
COPY ../../front/package-lock.json .
RUN npm install
COPY ../../front/yarn.lock .
RUN yarn install
RUN npx playwright install-deps
WORKDIR /app/server
COPY ../../server/package.json .
COPY ../../server/package-lock.json .
RUN npm install
COPY ../../server/yarn.lock .
RUN yarn install
WORKDIR /app

View File

@ -1,11 +1,11 @@
FROM node:18.16.0-alpine as docs
FROM node:18.16-bullseye as docs
WORKDIR /app/docs
COPY ../../docs/package.json .
COPY ../../docs/package-lock.json .
RUN npm install
COPY ../../docs/yarn.lock .
RUN yarn
COPY ../../docs .
CMD ["npm", "run", "start"]
CMD ["npm", "run", "start"]

View File

@ -3,12 +3,12 @@ FROM node:18.16.0-alpine as docs
WORKDIR /app/docs
COPY ./docs/package.json .
COPY ./docs/package-lock.json .
RUN npm install
COPY ./docs/yarn.lock .
RUN yarn install
COPY ./docs .
RUN npm run build
RUN npm install -g serve
RUN yarn install -g serve
CMD ["serve", "-s", "./build"]

View File

@ -6,9 +6,9 @@ ARG REACT_APP_AUTH_URL
WORKDIR /app/front
COPY ./front .
RUN npm install
RUN npm run build
RUN yarn install --install-links
RUN yarn build
RUN npm install -g serve
RUN yarn install -g serve
CMD ["serve", "-s", "build"]

View File

@ -2,12 +2,12 @@ FROM node:18.16.0-alpine as server
WORKDIR /app/server
COPY ./server/package.json ./
COPY ./server/package-lock.json ./
RUN npm install
COPY ./server/yarn.lock ./
RUN yarn install
COPY ./server .
RUN npx prisma generate
RUN npm run build
RUN yarn build
CMD ["node", "dist/main"]

View File

@ -0,0 +1,10 @@
// eslint-disable-next-line @typescript-eslint/no-var-requires
const noHardcodedColors = require('./rules/no-hardcoded-colors');
const cssAlphabetically = require('./rules/sort-css-properties-alphabetically');
module.exports = {
rules: {
'no-hardcoded-colors': noHardcodedColors,
'sort-css-properties-alphabetically': cssAlphabetically,
},
};

View File

@ -0,0 +1,8 @@
{
"name": "eslint-plugin-twenty",
"version": "0.0.1",
"main": "index.js",
"dependencies": {
"postcss": "^8.4.24"
}
}

View File

@ -0,0 +1,24 @@
module.exports = {
create: function (context) {
return {
TaggedTemplateExpression(node) {
if (context.getFilename().endsWith('themes.ts')) {
return;
}
node.quasi.quasis.forEach((quasi) => {
const colorRegex =
/(?:rgba?\((\d{1,3}),\s*(\d{1,3}),\s*(\d{1,3})(,\s*\d+\.?\d*)?\))|(?:#[0-9a-fA-F]{6})/i;
if (colorRegex.test(quasi.value.raw)) {
context.report({
node,
message:
'Do not use hardcoded RGBA or Hex colors. Please use a color from the theme file.',
});
}
});
},
};
},
};

View File

@ -0,0 +1,208 @@
"use strict";
const postcss = require("postcss");
function isStyledTagname(node) {
return (
(node.tag.type === "Identifier" && node.tag.name === "css") ||
(node.tag.type === "MemberExpression" &&
node.tag.object.name === "styled") ||
(node.tag.type === "CallExpression" &&
(node.tag.callee.name === "styled" ||
(node.tag.callee.object &&
((node.tag.callee.object.callee &&
node.tag.callee.object.callee.name === "styled") ||
(node.tag.callee.object.object &&
node.tag.callee.object.object.name === "styled")))))
);
}
/**
* An atomic rule is a rule without nested rules.
*/
function isValidAtomicRule(rule) {
const decls = rule.nodes.filter(node => node.type === "decl");
if (decls.length < 0) {
return { isValid: true };
}
for (let i = 1; i < decls.length; i++) {
const current = decls[i].prop;
const prev = decls[i - 1].prop;
if (current < prev) {
const loc = {
start: {
line: decls[i - 1].source.start.line,
column: decls[i - 1].source.start.column - 1
},
end: {
line: decls[i].source.end.line,
column: decls[i].source.end.column - 1
}
};
return { isValid: false, loc };
}
}
return { isValid: true };
}
function isValidRule(rule) {
// check each rule recursively
const { isValid, loc } = rule.nodes.reduce(
(map, node) => {
return node.type === "rule" ? isValidRule(node) : map;
},
{ isValid: true }
);
// if there is any invalid rule, return result
if (!isValid) {
return { isValid, loc };
}
// check declarations
return isValidAtomicRule(rule);
}
function getNodeStyles(node) {
const [firstQuasi, ...quasis] = node.quasi.quasis;
// remove line break added to the first quasi
const lineBreakCount = node.quasi.loc.start.line - 1;
let styles = `${"\n".repeat(lineBreakCount)}${" ".repeat(
node.quasi.loc.start.column + 1
)}${firstQuasi.value.raw}`;
// replace expression by spaces and line breaks
quasis.forEach(({ value, loc }, idx) => {
const prevLoc = idx === 0 ? firstQuasi.loc : quasis[idx - 1].loc;
const lineBreaksCount = loc.start.line - prevLoc.end.line;
const spacesCount =
loc.start.line === prevLoc.end.line
? loc.start.column - prevLoc.end.column + 2
: loc.start.column + 1;
styles = `${styles}${" "}${"\n".repeat(lineBreaksCount)}${" ".repeat(
spacesCount
)}${value.raw}`;
});
return styles;
}
function create(context) {
return {
TaggedTemplateExpression(node) {
if (isStyledTagname(node)) {
try {
const root = postcss.parse(getNodeStyles(node));
const { isValid, loc } = isValidRule(root);
if (!isValid) {
return context.report({
node,
messageId: "sort-css-properties-alphabetically",
loc,
fix: fixer =>
fix({
rule: root,
fixer,
src: context.getSourceCode()
})
});
}
} catch (e) {
return true;
}
}
}
};
}
function fix({ rule, fixer, src }) {
let fixings = [];
// concat fixings recursively
rule.nodes.forEach(node => {
if (node.type === "rule") {
fixings = [...fixings, ...fix({ rule: node, fixer, src })];
}
});
const declarations = rule.nodes.filter(node => node.type === "decl");
const sortedDeclarations = sortDeclarations(declarations);
declarations.forEach((decl, idx) => {
if (!areSameDeclarations(decl, sortedDeclarations[idx])) {
try {
const range = getDeclRange({ decl, src });
const sortedDeclText = getDeclText({
decl: sortedDeclarations[idx],
src
});
fixings.push(fixer.removeRange([range.startIdx, range.endIdx + 1]));
fixings.push(
fixer.insertTextAfterRange(
[range.startIdx, range.startIdx],
sortedDeclText
)
);
} catch (e) {
console.log(e);
}
}
});
return fixings;
}
function areSameDeclarations(a, b) {
return (
a.source.start.line === b.source.start.line &&
a.source.start.column === b.source.start.column
);
}
function getDeclRange({ decl, src }) {
const loc = {
start: {
line: decl.source.start.line,
column: decl.source.start.column - 1
},
end: {
line: decl.source.end.line,
column: decl.source.end.column - 1
}
};
const startIdx = src.getIndexFromLoc(loc.start);
const endIdx = src.getIndexFromLoc(loc.end);
return { startIdx, endIdx };
}
function getDeclText({ decl, src }) {
const { startIdx, endIdx } = getDeclRange({ decl, src });
return src.getText().substring(startIdx, endIdx + 1);
}
function sortDeclarations(declarations) {
return declarations
.slice()
.sort((declA, declB) => (declA.prop > declB.prop ? 1 : -1));
}
module.exports = {
meta: {
docs: {
description: "Styles are sorted alphabetically.",
category: "Fill me in",
recommended: false
},
messages: {
"sort-css-properties-alphabetically":
"Declarations should be sorted alphabetically."
},
fixable: "code"
},
create
};

View File

@ -0,0 +1,27 @@
# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY.
# yarn lockfile v1
nanoid@^3.3.6:
version "3.3.6"
resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-3.3.6.tgz#443380c856d6e9f9824267d960b4236ad583ea4c"
integrity sha512-BGcqMMJuToF7i1rt+2PWSNVnWIkGCU78jBG3RxO/bZlnZPK2Cmi2QaffxGO/2RvWi9sL+FAiRiXMgsyxQ1DIDA==
picocolors@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/picocolors/-/picocolors-1.0.0.tgz#cb5bdc74ff3f51892236eaf79d68bc44564ab81c"
integrity sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==
postcss@^8.4.24:
version "8.4.24"
resolved "https://registry.yarnpkg.com/postcss/-/postcss-8.4.24.tgz#f714dba9b2284be3cc07dbd2fc57ee4dc972d2df"
integrity sha512-M0RzbcI0sO/XJNucsGjvWU9ERWxb/ytp1w6dKtxTKgixdtQDq4rmx/g8W1hnaheq9jgwL/oyEdH5Bc4WwJKMqg==
dependencies:
nanoid "^3.3.6"
picocolors "^1.0.0"
source-map-js "^1.0.2"
source-map-js@^1.0.2:
version "1.0.2"
resolved "https://registry.yarnpkg.com/source-map-js/-/source-map-js-1.0.2.tgz#adbc361d9c62df380125e7f161f71c826f1e490c"
integrity sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw==

View File

@ -36,26 +36,26 @@ $ npm install
```bash
# development
$ npm run start
$ npm start
# watch mode
$ npm run start:dev
$ npm start:dev
# production mode
$ npm run start:prod
$ npm start:prod
```
## Test
```bash
# unit tests
$ npm run test
$ npm test
# e2e tests
$ npm run test:e2e
$ npm test:e2e
# test coverage
$ npm run test:cov
$ npm test:cov
```
## Support

9667
server/package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -24,10 +24,12 @@
"prisma:seed": "npx prisma db seed"
},
"dependencies": {
"@apollo/server": "^4.7.3",
"@nestjs/apollo": "^11.0.5",
"@nestjs/common": "^9.0.0",
"@nestjs/config": "^2.3.2",
"@nestjs/core": "^9.0.0",
"@nestjs/graphql": "^11.0.6",
"@nestjs/jwt": "^10.0.3",
"@nestjs/passport": "^9.0.3",
"@nestjs/platform-express": "^9.0.0",

5933
server/yarn.lock Normal file

File diff suppressed because it is too large Load Diff