Merge pull request #9 from saleor/add-linting
Add code formatting, git hooks and improved CI
This commit is contained in:
commit
6972dbd562
12 changed files with 1691 additions and 69 deletions
71
.eslintrc
Normal file
71
.eslintrc
Normal 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`
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
5
.github/workflows/main.yml
vendored
5
.github/workflows/main.yml
vendored
|
@ -16,10 +16,7 @@ jobs:
|
||||||
run: pnpm install
|
run: pnpm install
|
||||||
- name: Check linters
|
- name: Check linters
|
||||||
run: pnpm lint
|
run: pnpm lint
|
||||||
- name: Validate paths
|
|
||||||
run: |
|
|
||||||
pnpm paths
|
|
||||||
git diff --exit-code ./lib/\$path.ts
|
|
||||||
build:
|
build:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
|
|
4
.husky/pre-commit
Executable file
4
.husky/pre-commit
Executable file
|
@ -0,0 +1,4 @@
|
||||||
|
#!/usr/bin/env sh
|
||||||
|
. "$(dirname -- "$0")/_/husky.sh"
|
||||||
|
|
||||||
|
pnpx lint-staged
|
5
.prettierignore
Normal file
5
.prettierignore
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
.next
|
||||||
|
saleor/api.tsx
|
||||||
|
pnpm-lock.yaml
|
||||||
|
graphql.schema.json
|
||||||
|
lib/$path.ts
|
4
.prettierrc
Normal file
4
.prettierrc
Normal file
|
@ -0,0 +1,4 @@
|
||||||
|
{
|
||||||
|
"singleQuote": false,
|
||||||
|
"printWidth": 100
|
||||||
|
}
|
12
README.md
12
README.md
|
@ -14,3 +14,15 @@ SDK for building great Saleor Apps.
|
||||||
```bash
|
```bash
|
||||||
npm i @saleor/app-sdk
|
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
|
||||||
|
```
|
||||||
|
|
23
package.json
23
package.json
|
@ -9,7 +9,9 @@
|
||||||
"watch": "tsc -w",
|
"watch": "tsc -w",
|
||||||
"build": "tsup-node src/* --format esm,cjs --dts && clear-package-json package.json -o dist/package.json --fields publishConfig",
|
"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": "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": [],
|
"keywords": [],
|
||||||
"author": "",
|
"author": "",
|
||||||
|
@ -21,14 +23,31 @@
|
||||||
"retes": "^0.29.4"
|
"retes": "^0.29.4"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"clean-publish": "^4.0.1",
|
|
||||||
"@types/node": "^18.0.4",
|
"@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",
|
"tsm": "^2.2.1",
|
||||||
"tsup": "^6.1.3",
|
"tsup": "^6.1.3",
|
||||||
"typescript": "^4.7.4",
|
"typescript": "^4.7.4",
|
||||||
"uvu": "^0.5.6",
|
"uvu": "^0.5.6",
|
||||||
"watchlist": "^0.3.1"
|
"watchlist": "^0.3.1"
|
||||||
},
|
},
|
||||||
|
"lint-staged": {
|
||||||
|
"*.{js,ts,tsx}": "eslint --cache --fix",
|
||||||
|
"*.{js,ts,tsx,css,md,json}": "prettier --write"
|
||||||
|
},
|
||||||
"exports": {
|
"exports": {
|
||||||
"./package.json": "./package.json",
|
"./package.json": "./package.json",
|
||||||
"./const": {
|
"./const": {
|
||||||
|
|
1519
pnpm-lock.yaml
1519
pnpm-lock.yaml
File diff suppressed because it is too large
Load diff
33
src/index.ts
33
src/index.ts
|
@ -1,43 +1,38 @@
|
||||||
import path from "path";
|
|
||||||
import fg from "fast-glob";
|
import fg from "fast-glob";
|
||||||
|
|
||||||
import { print } from "graphql/language/printer.js";
|
import { print } from "graphql/language/printer.js";
|
||||||
|
import path from "path";
|
||||||
|
|
||||||
const capitalize = (value: string) =>
|
const capitalize = (value: string) => value.charAt(0).toUpperCase() + value.slice(1);
|
||||||
value.charAt(0).toUpperCase() + value.slice(1);
|
|
||||||
|
|
||||||
const dropFileExtension = (filename: string) => path.parse(filename).name;
|
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;
|
let entries;
|
||||||
|
|
||||||
if (process.env.NODE_ENV === "production") {
|
if (process.env.NODE_ENV === "production") {
|
||||||
entries = await fg(["*.js"], { cwd: webhooksPath });
|
entries = await fg(["*.js"], { cwd: webhooksPath });
|
||||||
} else {
|
} else {
|
||||||
entries = await fg(["*.ts"], { cwd: `pages/api/webhooks` });
|
entries = await fg(["*.ts"], { cwd: "pages/api/webhooks" });
|
||||||
}
|
}
|
||||||
|
|
||||||
return entries.map(dropFileExtension).map((name: string) => {
|
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;
|
let eventType: string;
|
||||||
if (
|
if (Object.values(generatedGraphQL.WebhookEventTypeAsyncEnum).includes(eventName)) {
|
||||||
Object.values(generatedGraphQL.WebhookEventTypeAsyncEnum).includes(
|
|
||||||
eventName
|
|
||||||
)
|
|
||||||
) {
|
|
||||||
eventType = "asyncEvents";
|
eventType = "asyncEvents";
|
||||||
} else if (
|
} else if (Object.values(generatedGraphQL.WebhookEventTypeSyncEnum).includes(eventName)) {
|
||||||
Object.values(generatedGraphQL.WebhookEventTypeSyncEnum).includes(
|
|
||||||
eventName
|
|
||||||
)
|
|
||||||
) {
|
|
||||||
eventType = "syncEvents";
|
eventType = "syncEvents";
|
||||||
} else {
|
} else {
|
||||||
throw Error("Event type not found.");
|
throw Error("Event type not found.");
|
||||||
}
|
}
|
||||||
|
|
||||||
const statement = `${camelcaseName}SubscriptionDocument`;
|
const statement = `${camelCaseName}SubscriptionDocument`;
|
||||||
let query: string;
|
let query: string;
|
||||||
if (statement in generatedGraphQL) {
|
if (statement in generatedGraphQL) {
|
||||||
query = print((generatedGraphQL as any)[statement]);
|
query = print((generatedGraphQL as any)[statement]);
|
||||||
|
|
|
@ -1,5 +1,4 @@
|
||||||
import crypto from "crypto";
|
import crypto from "crypto";
|
||||||
|
|
||||||
import * as jose from "jose";
|
import * as jose from "jose";
|
||||||
import type { Middleware } from "retes";
|
import type { Middleware } from "retes";
|
||||||
import { Response } from "retes/response";
|
import { Response } from "retes/response";
|
||||||
|
@ -16,11 +15,10 @@ export const withBaseURL: Middleware = (handler) => async (request) => {
|
||||||
return response;
|
return response;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const withSaleorDomainPresent: Middleware =
|
export const withSaleorDomainPresent: Middleware = (handler) => async (request) => {
|
||||||
(handler) => async (request) => {
|
const saleorDomain = request.headers[SALEOR_DOMAIN_HEADER];
|
||||||
const saleor_domain = request.headers[SALEOR_DOMAIN_HEADER];
|
|
||||||
|
|
||||||
if (!saleor_domain) {
|
if (!saleorDomain) {
|
||||||
return Response.BadRequest({
|
return Response.BadRequest({
|
||||||
success: false,
|
success: false,
|
||||||
message: "Missing Saleor domain header.",
|
message: "Missing Saleor domain header.",
|
||||||
|
@ -46,10 +44,9 @@ export const withSaleorEventMatch =
|
||||||
return handler(request);
|
return handler(request);
|
||||||
};
|
};
|
||||||
|
|
||||||
export const withAuthTokenRequired: Middleware =
|
export const withAuthTokenRequired: Middleware = (handler) => async (request) => {
|
||||||
(handler) => async (request) => {
|
const authToken = request.params.auth_token;
|
||||||
const auth_token = request.params.auth_token;
|
if (!authToken) {
|
||||||
if (!auth_token) {
|
|
||||||
return Response.BadRequest({
|
return Response.BadRequest({
|
||||||
success: false,
|
success: false,
|
||||||
message: "Missing auth token.",
|
message: "Missing auth token.",
|
||||||
|
@ -59,10 +56,10 @@ export const withAuthTokenRequired: Middleware =
|
||||||
return handler(request);
|
return handler(request);
|
||||||
};
|
};
|
||||||
|
|
||||||
export const withWebhookSignatureVerified = (
|
export const withWebhookSignatureVerified =
|
||||||
secretKey: string | undefined = undefined
|
(secretKey: string | undefined = undefined): Middleware =>
|
||||||
): Middleware => {
|
(handler) =>
|
||||||
return (handler) => async (request) => {
|
async (request) => {
|
||||||
if (request.rawBody === undefined) {
|
if (request.rawBody === undefined) {
|
||||||
return Response.InternalServerError({
|
return Response.InternalServerError({
|
||||||
success: false,
|
success: false,
|
||||||
|
@ -70,10 +67,8 @@ export const withWebhookSignatureVerified = (
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
const {
|
const { [SALEOR_DOMAIN_HEADER]: saleorDomain, "saleor-signature": payloadSignature } =
|
||||||
[SALEOR_DOMAIN_HEADER]: saleorDomain,
|
request.headers;
|
||||||
"saleor-signature": payloadSignature,
|
|
||||||
} = request.headers;
|
|
||||||
|
|
||||||
if (secretKey !== undefined) {
|
if (secretKey !== undefined) {
|
||||||
const calculatedSignature = crypto
|
const calculatedSignature = crypto
|
||||||
|
@ -88,7 +83,7 @@ export const withWebhookSignatureVerified = (
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
const [header, _, signature] = payloadSignature.split(".");
|
const [header, , signature] = payloadSignature.split(".");
|
||||||
const jws = {
|
const jws = {
|
||||||
protected: header,
|
protected: header,
|
||||||
payload: request.rawBody,
|
payload: request.rawBody,
|
||||||
|
@ -111,4 +106,3 @@ export const withWebhookSignatureVerified = (
|
||||||
|
|
||||||
return handler(request);
|
return handler(request);
|
||||||
};
|
};
|
||||||
};
|
|
||||||
|
|
|
@ -11,8 +11,10 @@
|
||||||
// "disableReferencedProjectLoad": true, /* Reduce the number of projects loaded automatically by TypeScript. */
|
// "disableReferencedProjectLoad": true, /* Reduce the number of projects loaded automatically by TypeScript. */
|
||||||
|
|
||||||
/* Language and Environment */
|
/* Language and Environment */
|
||||||
"target": "ES2021", /* Set the JavaScript language version for emitted JavaScript and include compatible library declarations. */
|
"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. */
|
"lib": [
|
||||||
|
"ES2021"
|
||||||
|
] /* Specify a set of bundled library declaration files that describe the target runtime environment. */,
|
||||||
// "jsx": "preserve", /* Specify what JSX code is generated. */
|
// "jsx": "preserve", /* Specify what JSX code is generated. */
|
||||||
// "experimentalDecorators": true, /* Enable experimental support for TC39 stage 2 draft decorators. */
|
// "experimentalDecorators": true, /* Enable experimental support for TC39 stage 2 draft decorators. */
|
||||||
// "emitDecoratorMetadata": true, /* Emit design-type metadata for decorated declarations in source files. */
|
// "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. */
|
// "moduleDetection": "auto", /* Control what method is used to detect module-format JS files. */
|
||||||
|
|
||||||
/* Modules */
|
/* 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. */
|
// "rootDir": "./", /* Specify the root folder within your source files. */
|
||||||
// "moduleResolution": "node", /* Specify how TypeScript looks up a file from a given module specifier. */
|
// "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. */
|
// "baseUrl": "./", /* Specify the base directory to resolve non-relative module names. */
|
||||||
|
@ -71,12 +73,12 @@
|
||||||
/* Interop Constraints */
|
/* Interop Constraints */
|
||||||
// "isolatedModules": true, /* Ensure that each file can be safely transpiled without relying on other imports. */
|
// "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. */
|
// "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. */
|
// "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 */
|
/* 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. */
|
// "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'. */
|
// "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. */
|
// "strictFunctionTypes": true, /* When assigning functions, check to ensure parameters and the return values are subtype-compatible. */
|
||||||
|
|
Loading…
Reference in a new issue