Merge pull request #9 from saleor/add-linting

Add code formatting, git hooks and improved CI
This commit is contained in:
Krzysztof Wolski 2022-07-25 09:29:58 +02:00 committed by GitHub
commit 6972dbd562
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
12 changed files with 1691 additions and 69 deletions

71
.eslintrc Normal file
View file

@ -0,0 +1,71 @@
{
"parser": "@typescript-eslint/parser",
"parserOptions": {
"tsconfigRootDir": "./",
"project": ["./tsconfig.json"]
},
"extends": [
"airbnb",
"plugin:@typescript-eslint/recommended",
"prettier" // prettier *has* to be the last one, to avoid conflicting rules
],
"ignorePatterns": ["pnpm-lock.yaml"],
"plugins": ["simple-import-sort", "@typescript-eslint"],
"rules": {
"quotes": ["error", "double"],
"react/react-in-jsx-scope": "off", // next does not require react imports
"import/extensions": "off", // file extension not required when importing
"react/jsx-filename-extension": "off",
"no-restricted-syntax": [
"error",
{
"selector": "ForInStatement",
"message": "for ... in disallowed, use for ... of instead"
}
],
"no-underscore-dangle": "off",
"no-await-in-loop": "off",
"react/jsx-props-no-spreading": "off",
"react/require-default-props": "off",
"simple-import-sort/imports": "warn",
"simple-import-sort/exports": "warn",
"import/first": "warn",
"import/newline-after-import": "warn",
"import/no-duplicates": "warn",
"no-unused-vars": "off",
"@typescript-eslint/no-unused-vars": ["error"],
"@typescript-eslint/ban-types": "off",
"no-console": [
"error",
{
"allow": ["warn", "error", "debug"]
}
],
"no-continue": "off",
"operator-linebreak": "off",
"max-len": "off",
"array-callback-return": "off",
"implicit-arrow-linebreak": "off",
"@typescript-eslint/no-non-null-asserted-optional-chain": "off",
"@typescript-eslint/no-non-null-assertion": "off",
"no-restricted-imports": "off",
"no-restricted-exports": "off",
"@typescript-eslint/ban-ts-comment": "off",
// TO FIX:
"import/no-cycle": "off", // pathpidia issue
"import/prefer-default-export": "off",
"@typescript-eslint/no-misused-promises": ["error"],
"@typescript-eslint/no-floating-promises": ["error"]
},
"settings": {
"import/parsers": {
"@typescript-eslint/parser": [".ts", ".tsx"]
},
"import/resolver": {
"typescript": {
"alwaysTryTypes": true // always try to resolve types under `<root>@types` directory even it doesn't contain any source code, like `@types/unist`
}
}
}
}

View file

@ -16,10 +16,7 @@ jobs:
run: pnpm install
- name: Check linters
run: pnpm lint
- name: Validate paths
run: |
pnpm paths
git diff --exit-code ./lib/\$path.ts
build:
runs-on: ubuntu-latest
steps:

4
.husky/pre-commit Executable file
View file

@ -0,0 +1,4 @@
#!/usr/bin/env sh
. "$(dirname -- "$0")/_/husky.sh"
pnpx lint-staged

5
.prettierignore Normal file
View file

@ -0,0 +1,5 @@
.next
saleor/api.tsx
pnpm-lock.yaml
graphql.schema.json
lib/$path.ts

4
.prettierrc Normal file
View file

@ -0,0 +1,4 @@
{
"singleQuote": false,
"printWidth": 100
}

View file

@ -4,8 +4,8 @@ SDK for building great Saleor Apps.
<div>
[![npm version badge](https://img.shields.io/npm/v/@saleor/app-sdk)](https://www.npmjs.com/package/@saleor/app-sdk)
[![npm downloads count](https://img.shields.io/npm/dt/@saleor/app-sdk)](https://www.npmjs.com/package/@saleor/app-sdk)
[![npm version badge](https://img.shields.io/npm/v/@saleor/app-sdk)](https://www.npmjs.com/package/@saleor/app-sdk)
[![npm downloads count](https://img.shields.io/npm/dt/@saleor/app-sdk)](https://www.npmjs.com/package/@saleor/app-sdk)
</div>
@ -14,3 +14,15 @@ SDK for building great Saleor Apps.
```bash
npm i @saleor/app-sdk
```
## Development
### Code style
Before committing the code, Git pre-hooks will check staged changes for
following the code styles. If you would like to format the code by yourself, run
the command:
```bash
pnpm lint
```

View file

@ -9,7 +9,9 @@
"watch": "tsc -w",
"build": "tsup-node src/* --format esm,cjs --dts && clear-package-json package.json -o dist/package.json --fields publishConfig",
"test": "uvu -r tsm spec",
"test-watch": "watchlist src spec -- pnpm test"
"test-watch": "watchlist src spec -- pnpm test",
"prepare": "husky install",
"lint": "prettier --loglevel warn --write . && eslint --fix ."
},
"keywords": [],
"author": "",
@ -21,14 +23,31 @@
"retes": "^0.29.4"
},
"devDependencies": {
"clean-publish": "^4.0.1",
"@types/node": "^18.0.4",
"@typescript-eslint/eslint-plugin": "^5.17.0",
"@typescript-eslint/parser": "^5.30.7",
"clean-publish": "^4.0.1",
"eslint": "8.15.0",
"eslint-config-airbnb": "^19.0.4",
"eslint-config-prettier": "^8.5.0",
"eslint-import-resolver-typescript": "^3.3.0",
"eslint-plugin-import": "^2.26.0",
"eslint-plugin-jsx-a11y": "^6.6.0",
"eslint-plugin-react": "^7.30.1",
"eslint-plugin-react-hooks": "^4.6.0",
"eslint-plugin-simple-import-sort": "^7.0.0",
"husky": "^8.0.1",
"prettier": "2.6.2",
"tsm": "^2.2.1",
"tsup": "^6.1.3",
"typescript": "^4.7.4",
"uvu": "^0.5.6",
"watchlist": "^0.3.1"
},
"lint-staged": {
"*.{js,ts,tsx}": "eslint --cache --fix",
"*.{js,ts,tsx,css,md,json}": "prettier --write"
},
"exports": {
"./package.json": "./package.json",
"./const": {

File diff suppressed because it is too large Load diff

View file

@ -1,43 +1,38 @@
import path from "path";
import fg from "fast-glob";
import { print } from "graphql/language/printer.js";
import path from "path";
const capitalize = (value: string) =>
value.charAt(0).toUpperCase() + value.slice(1);
const capitalize = (value: string) => value.charAt(0).toUpperCase() + value.slice(1);
const dropFileExtension = (filename: string) => path.parse(filename).name;
export const inferWebhooks = async (baseURL: string, webhooksPath: string, generatedGraphQL: any) => {
export const inferWebhooks = async (
baseURL: string,
webhooksPath: string,
generatedGraphQL: any
) => {
let entries;
if (process.env.NODE_ENV === "production") {
entries = await fg(["*.js"], { cwd: webhooksPath });
} else {
entries = await fg(["*.ts"], { cwd: `pages/api/webhooks` });
entries = await fg(["*.ts"], { cwd: "pages/api/webhooks" });
}
return entries.map(dropFileExtension).map((name: string) => {
const camelcaseName = name.split("-").map(capitalize).join("");
const camelCaseName = name.split("-").map(capitalize).join("");
const eventName = name.toUpperCase().replace(new RegExp("-", "g"), "_");
const eventName = name.toUpperCase().replace(/-/g, "_");
let eventType: string;
if (
Object.values(generatedGraphQL.WebhookEventTypeAsyncEnum).includes(
eventName
)
) {
if (Object.values(generatedGraphQL.WebhookEventTypeAsyncEnum).includes(eventName)) {
eventType = "asyncEvents";
} else if (
Object.values(generatedGraphQL.WebhookEventTypeSyncEnum).includes(
eventName
)
) {
} else if (Object.values(generatedGraphQL.WebhookEventTypeSyncEnum).includes(eventName)) {
eventType = "syncEvents";
} else {
throw Error("Event type not found.");
}
const statement = `${camelcaseName}SubscriptionDocument`;
const statement = `${camelCaseName}SubscriptionDocument`;
let query: string;
if (statement in generatedGraphQL) {
query = print((generatedGraphQL as any)[statement]);

View file

@ -1,5 +1,4 @@
import crypto from "crypto";
import * as jose from "jose";
import type { Middleware } from "retes";
import { Response } from "retes/response";
@ -16,19 +15,18 @@ export const withBaseURL: Middleware = (handler) => async (request) => {
return response;
};
export const withSaleorDomainPresent: Middleware =
(handler) => async (request) => {
const saleor_domain = request.headers[SALEOR_DOMAIN_HEADER];
export const withSaleorDomainPresent: Middleware = (handler) => async (request) => {
const saleorDomain = request.headers[SALEOR_DOMAIN_HEADER];
if (!saleor_domain) {
return Response.BadRequest({
success: false,
message: "Missing Saleor domain header.",
});
}
if (!saleorDomain) {
return Response.BadRequest({
success: false,
message: "Missing Saleor domain header.",
});
}
return handler(request);
};
return handler(request);
};
export const withSaleorEventMatch =
<E extends string>(expectedEvent: `${Lowercase<E>}`): Middleware =>
@ -46,23 +44,22 @@ export const withSaleorEventMatch =
return handler(request);
};
export const withAuthTokenRequired: Middleware =
(handler) => async (request) => {
const auth_token = request.params.auth_token;
if (!auth_token) {
return Response.BadRequest({
success: false,
message: "Missing auth token.",
});
}
export const withAuthTokenRequired: Middleware = (handler) => async (request) => {
const authToken = request.params.auth_token;
if (!authToken) {
return Response.BadRequest({
success: false,
message: "Missing auth token.",
});
}
return handler(request);
};
return handler(request);
};
export const withWebhookSignatureVerified = (
secretKey: string | undefined = undefined
): Middleware => {
return (handler) => async (request) => {
export const withWebhookSignatureVerified =
(secretKey: string | undefined = undefined): Middleware =>
(handler) =>
async (request) => {
if (request.rawBody === undefined) {
return Response.InternalServerError({
success: false,
@ -70,10 +67,8 @@ export const withWebhookSignatureVerified = (
});
}
const {
[SALEOR_DOMAIN_HEADER]: saleorDomain,
"saleor-signature": payloadSignature,
} = request.headers;
const { [SALEOR_DOMAIN_HEADER]: saleorDomain, "saleor-signature": payloadSignature } =
request.headers;
if (secretKey !== undefined) {
const calculatedSignature = crypto
@ -88,7 +83,7 @@ export const withWebhookSignatureVerified = (
});
}
} else {
const [header, _, signature] = payloadSignature.split(".");
const [header, , signature] = payloadSignature.split(".");
const jws = {
protected: header,
payload: request.rawBody,
@ -111,4 +106,3 @@ export const withWebhookSignatureVerified = (
return handler(request);
};
};

View file

@ -11,8 +11,10 @@
// "disableReferencedProjectLoad": true, /* Reduce the number of projects loaded automatically by TypeScript. */
/* Language and Environment */
"target": "ES2021", /* Set the JavaScript language version for emitted JavaScript and include compatible library declarations. */
"lib": ["ES2021"], /* Specify a set of bundled library declaration files that describe the target runtime environment. */
"target": "ES2021" /* Set the JavaScript language version for emitted JavaScript and include compatible library declarations. */,
"lib": [
"ES2021"
] /* Specify a set of bundled library declaration files that describe the target runtime environment. */,
// "jsx": "preserve", /* Specify what JSX code is generated. */
// "experimentalDecorators": true, /* Enable experimental support for TC39 stage 2 draft decorators. */
// "emitDecoratorMetadata": true, /* Emit design-type metadata for decorated declarations in source files. */
@ -25,7 +27,7 @@
// "moduleDetection": "auto", /* Control what method is used to detect module-format JS files. */
/* Modules */
"module": "commonjs", /* Specify what module code is generated. */
"module": "commonjs" /* Specify what module code is generated. */,
// "rootDir": "./", /* Specify the root folder within your source files. */
// "moduleResolution": "node", /* Specify how TypeScript looks up a file from a given module specifier. */
// "baseUrl": "./", /* Specify the base directory to resolve non-relative module names. */
@ -71,12 +73,12 @@
/* Interop Constraints */
// "isolatedModules": true, /* Ensure that each file can be safely transpiled without relying on other imports. */
// "allowSyntheticDefaultImports": true, /* Allow 'import x from y' when a module doesn't have a default export. */
"esModuleInterop": true, /* Emit additional JavaScript to ease support for importing CommonJS modules. This enables 'allowSyntheticDefaultImports' for type compatibility. */
"esModuleInterop": true /* Emit additional JavaScript to ease support for importing CommonJS modules. This enables 'allowSyntheticDefaultImports' for type compatibility. */,
// "preserveSymlinks": true, /* Disable resolving symlinks to their realpath. This correlates to the same flag in node. */
"forceConsistentCasingInFileNames": true, /* Ensure that casing is correct in imports. */
"forceConsistentCasingInFileNames": true /* Ensure that casing is correct in imports. */,
/* Type Checking */
"strict": true, /* Enable all strict type-checking options. */
"strict": true /* Enable all strict type-checking options. */,
// "noImplicitAny": true, /* Enable error reporting for expressions and declarations with an implied 'any' type. */
// "strictNullChecks": true, /* When type checking, take into account 'null' and 'undefined'. */
// "strictFunctionTypes": true, /* When assigning functions, check to ensure parameters and the return values are subtype-compatible. */
@ -98,6 +100,6 @@
/* Completeness */
// "skipDefaultLibCheck": true, /* Skip type checking .d.ts files that are included with TypeScript. */
"skipLibCheck": true /* Skip type checking all .d.ts files. */
"skipLibCheck": true /* Skip type checking all .d.ts files. */
}
}