Add prettier on pre-commit & reformat codebase (#137)

* Run prettier on project

* Install lint-staged

* Add Husky
This commit is contained in:
Lukasz Ostrowski 2023-02-10 11:13:59 +01:00 committed by GitHub
parent 9f843b2d31
commit 081d15e168
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
30 changed files with 140 additions and 119 deletions

1
.husky/.gitignore vendored Normal file
View file

@ -0,0 +1 @@
_

4
.husky/pre-commit Executable file
View file

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

View file

@ -2,3 +2,4 @@
**/pnpm-lock.yaml **/pnpm-lock.yaml
**/graphql/schema.graphql **/graphql/schema.graphql
**/generated **/generated
.changeset/

View file

@ -1,5 +1,5 @@
import { SaleorApp } from "@saleor/app-sdk/saleor-app"; import { SaleorApp } from "@saleor/app-sdk/saleor-app";
import {APL, FileAPL, SaleorCloudAPL, UpstashAPL, VercelAPL} from "@saleor/app-sdk/APL"; import { APL, FileAPL, SaleorCloudAPL, UpstashAPL, VercelAPL } from "@saleor/app-sdk/APL";
/** /**
* By default auth data are stored in the `.auth-data.json` (FileAPL). * By default auth data are stored in the `.auth-data.json` (FileAPL).

View file

@ -46,8 +46,8 @@ const nuvoSettings: SettingsAPI = {
color: "#fff", color: "#fff",
}, },
}, },
loader:{ loader: {
loadAnimationColor: '#000' loadAnimationColor: "#000",
}, },
header: { header: {
description: { description: {

View file

@ -13,7 +13,9 @@ export const CustomersImportingResults = ({
return ( return (
<div> <div>
<Typography paragraph variant="h3">Customers rows from imported file</Typography> <Typography paragraph variant="h3">
Customers rows from imported file
</Typography>
<Typography paragraph> <Typography paragraph>
Lines will be imported one by one. Failed imports can be retried, but performed operations Lines will be imported one by one. Failed imports can be retried, but performed operations
@ -33,7 +35,7 @@ export const CustomersImportingResults = ({
</Button> </Button>
)} )}
<Table style={{marginTop: 50}}> <Table style={{ marginTop: 50 }}>
<TableBody> <TableBody>
{importedLines.map((row) => ( {importedLines.map((row) => (
<CustomerImportingRow <CustomerImportingRow

View file

@ -7,7 +7,6 @@ import GraphQLProvider from "../providers/GraphQLProvider";
import { actions, useAppBridge } from "@saleor/app-sdk/app-bridge"; import { actions, useAppBridge } from "@saleor/app-sdk/app-bridge";
import { AppIcon, TitleBar } from "@saleor/apps-shared"; import { AppIcon, TitleBar } from "@saleor/apps-shared";
type Tab = "customers"; type Tab = "customers";
const useStyles = makeStyles((theme: SaleorTheme) => ({ const useStyles = makeStyles((theme: SaleorTheme) => ({
@ -36,7 +35,7 @@ const ImporterPage: NextPage = () => {
<div className={styles.wrapper}> <div className={styles.wrapper}>
<TitleBar <TitleBar
bottomMargin bottomMargin
icon={<AppIcon theme="rgb(58, 86, 199)" text="DI"/>} icon={<AppIcon theme="rgb(58, 86, 199)" text="DI" />}
name="Data Importer" name="Data Importer"
author="By Saleor Commerce" author="By Saleor Commerce"
rightColumnContent={ rightColumnContent={

View file

@ -3,4 +3,4 @@
* *
* https://vitest.dev/config/#setupfiles * https://vitest.dev/config/#setupfiles
*/ */
export {} export {};

View file

@ -71,26 +71,29 @@ pnpm dev
### Without CLI ### Without CLI
1. Install the dependencies by running: 1. Install the dependencies by running:
``` ```
pnpm install pnpm install
``` ```
2. Start the local server with: 2. Start the local server with:
``` ```
pnpm dev pnpm dev
``` ```
3. Expose local environment using tunnel: 3. Expose local environment using tunnel:
Use tunneling tools like [localtunnel](https://github.com/localtunnel/localtunnel) or [ngrok](https://ngrok.com/). Use tunneling tools like [localtunnel](https://github.com/localtunnel/localtunnel) or [ngrok](https://ngrok.com/).
4. Install aplication at your dashboard: 4. Install aplication at your dashboard:
If you use Saleor Cloud or your local server is exposed, you can install your app by following this link: If you use Saleor Cloud or your local server is exposed, you can install your app by following this link:
``` ```
[YOUR_SALEOR_DASHBOARD_URL]/apps/install?manifestUrl=[YOUR_APP_TUNNEL_MANIFEST_URL] [YOUR_SALEOR_DASHBOARD_URL]/apps/install?manifestUrl=[YOUR_APP_TUNNEL_MANIFEST_URL]
``` ```
This template host manifest at `/api/manifest`
This template host manifest at `/api/manifest`
You can also install application using GQL or command line. Follow the guide [how to install your app](https://docs.saleor.io/docs/3.x/developer/extending/apps/installing-apps#installation-using-graphql-api) to learn more. You can also install application using GQL or command line. Follow the guide [how to install your app](https://docs.saleor.io/docs/3.x/developer/extending/apps/installing-apps#installation-using-graphql-api) to learn more.

View file

@ -1,18 +1,18 @@
import {describe, it, expect} from "vitest"; import { describe, it, expect } from "vitest";
import {appConfigInputSchema} from "./app-config-input-schema"; import { appConfigInputSchema } from "./app-config-input-schema";
import {AppConfig, SellerShopConfig} from "./app-config"; import { AppConfig, SellerShopConfig } from "./app-config";
import {getMockAddress} from "../../fixtures/mock-address"; import { getMockAddress } from "../../fixtures/mock-address";
describe("appConfigInputSchema", () => { describe("appConfigInputSchema", () => {
it('Passes with no channels at all', () => { it("Passes with no channels at all", () => {
expect(() => expect(() =>
appConfigInputSchema.parse({ appConfigInputSchema.parse({
shopConfigPerChannel: {} shopConfigPerChannel: {},
} satisfies AppConfig) } satisfies AppConfig)
).not.to.throw() ).not.to.throw();
}) });
it('Passes with all address fields empty', () => { it("Passes with all address fields empty", () => {
expect(() => expect(() =>
appConfigInputSchema.parse({ appConfigInputSchema.parse({
shopConfigPerChannel: { shopConfigPerChannel: {
@ -27,23 +27,23 @@ describe("appConfigInputSchema", () => {
lastName: "", lastName: "",
postalCode: "", postalCode: "",
streetAddress1: "", streetAddress1: "",
streetAddress2: "" streetAddress2: "",
},
} },
} },
}
} satisfies AppConfig) } satisfies AppConfig)
).not.to.throw() ).not.to.throw();
}) });
it('Passes with partial address', () => { it("Passes with partial address", () => {
expect(() => expect(() =>
appConfigInputSchema.parse({ appConfigInputSchema.parse({
shopConfigPerChannel: { shopConfigPerChannel: {
channel: { channel: {
address: getMockAddress() } address: getMockAddress(),
} },
},
} satisfies AppConfig) } satisfies AppConfig)
).not.to.throw() ).not.to.throw();
}) });
}) });

View file

@ -1,19 +1,22 @@
import {OrderPayloadFragment} from "../../../generated/graphql"; import { OrderPayloadFragment } from "../../../generated/graphql";
interface IInvoiceNumberGenerationStrategy { interface IInvoiceNumberGenerationStrategy {
(order: OrderPayloadFragment): string; (order: OrderPayloadFragment): string;
} }
export const InvoiceNumberGenerationStrategy = { export const InvoiceNumberGenerationStrategy = {
localizedDate: (locale: string) => (order: Pick<OrderPayloadFragment, 'created'>) => { localizedDate: (locale: string) => (order: Pick<OrderPayloadFragment, "created">) => {
const orderCreatedDate = new Date(order.created); const orderCreatedDate = new Date(order.created);
return Intl.DateTimeFormat(locale,).format(orderCreatedDate) return Intl.DateTimeFormat(locale).format(orderCreatedDate);
} },
} satisfies Record<string, (...args: any[]) => IInvoiceNumberGenerationStrategy> } satisfies Record<string, (...args: any[]) => IInvoiceNumberGenerationStrategy>;
export class InvoiceNumberGenerator { export class InvoiceNumberGenerator {
generateFromOrder(order: OrderPayloadFragment, strategy: IInvoiceNumberGenerationStrategy): string { generateFromOrder(
return strategy(order) order: OrderPayloadFragment,
strategy: IInvoiceNumberGenerationStrategy
): string {
return strategy(order);
} }
} }

View file

@ -39,7 +39,7 @@ switch (aplType) {
if (!process.env.SECRET_KEY && process.env.NODE_ENV === "production") { if (!process.env.SECRET_KEY && process.env.NODE_ENV === "production") {
throw new Error( throw new Error(
"For production deployment SECRET_KEY is mandatory to use EncryptedSettingsManager.", "For production deployment SECRET_KEY is mandatory to use EncryptedSettingsManager."
); );
} }

View file

@ -80,7 +80,7 @@ export const AlgoliaConfigurationCard = () => {
}, },
}); });
}, },
}, }
); );
const onFormSubmit = handleSubmit(async (conf) => mutate(conf)); const onFormSubmit = handleSubmit(async (conf) => mutate(conf));

View file

@ -1,4 +1,4 @@
import {Card, CardContent, CardHeader} from "@material-ui/core"; import { Card, CardContent, CardHeader } from "@material-ui/core";
import { ImportProductsToAlgolia } from "./ImportProductsToAlgolia"; import { ImportProductsToAlgolia } from "./ImportProductsToAlgolia";
import AlgoliaConfigurationCard from "./AlgoliaConfigurationCard"; import AlgoliaConfigurationCard from "./AlgoliaConfigurationCard";
import { makeStyles, PageTab, PageTabs } from "@saleor/macaw-ui"; import { makeStyles, PageTab, PageTabs } from "@saleor/macaw-ui";

View file

@ -19,7 +19,7 @@ export const ImportProductsToAlgolia = () => {
const { appBridgeState } = useAppBridge(); const { appBridgeState } = useAppBridge();
const algoliaConfiguration = useConfiguration( const algoliaConfiguration = useConfiguration(
appBridgeState?.saleorApiUrl, appBridgeState?.saleorApiUrl,
appBridgeState?.token, appBridgeState?.token
); );
const searchProvider = useMemo(() => { const searchProvider = useMemo(() => {

View file

@ -29,4 +29,4 @@ export function SearchBox() {
/> />
</div> </div>
); );
} }

View file

@ -35,7 +35,7 @@ export class AlgoliaSearchProvider implements SearchProvider {
Object.entries(groupedByIndex).map(([indexName, objects]) => { Object.entries(groupedByIndex).map(([indexName, objects]) => {
const index = this.#algolia.initIndex(indexName); const index = this.#algolia.initIndex(indexName);
return index.saveObjects(objects); return index.saveObjects(objects);
}), })
); );
} }
@ -46,7 +46,7 @@ export class AlgoliaSearchProvider implements SearchProvider {
Object.entries(groupedByIndex).map(([indexName, objects]) => { Object.entries(groupedByIndex).map(([indexName, objects]) => {
const index = this.#algolia.initIndex(indexName); const index = this.#algolia.initIndex(indexName);
return index.deleteObjects(objects.map((o) => o.objectID)); return index.deleteObjects(objects.map((o) => o.objectID));
}), })
); );
} }
@ -141,7 +141,7 @@ const groupVariantByIndexName = (
{ {
visibleInListings, visibleInListings,
indexNamePrefix, indexNamePrefix,
}: { visibleInListings: true | false | null; indexNamePrefix: string | undefined }, }: { visibleInListings: true | false | null; indexNamePrefix: string | undefined }
) => { ) => {
if (!productVariant.product.channelListings) { if (!productVariant.product.channelListings) {
return null; return null;
@ -150,7 +150,7 @@ const groupVariantByIndexName = (
const objectsToSaveByIndexName = productVariant.product.channelListings const objectsToSaveByIndexName = productVariant.product.channelListings
.filter((channelListing) => .filter((channelListing) =>
// don't filter if `visibleInListings` is null // don't filter if `visibleInListings` is null
visibleInListings === null ? true : channelListing.visibleInListings === visibleInListings, visibleInListings === null ? true : channelListing.visibleInListings === visibleInListings
) )
.map((channelListing) => { .map((channelListing) => {
const object = productAndVariantToAlgolia({ const object = productAndVariantToAlgolia({
@ -176,7 +176,7 @@ const groupProductsByIndexName = (
{ {
visibleInListings, visibleInListings,
indexNamePrefix, indexNamePrefix,
}: { visibleInListings: true | false | null; indexNamePrefix: string | undefined }, }: { visibleInListings: true | false | null; indexNamePrefix: string | undefined }
) => { ) => {
debug(`groupProductsByIndexName called`); debug(`groupProductsByIndexName called`);
const batchesAndIndices = productsBatch const batchesAndIndices = productsBatch

View file

@ -10,7 +10,7 @@ type PartialChannelListing = {
export function channelListingToAlgoliaIndexId( export function channelListingToAlgoliaIndexId(
channelListing: PartialChannelListing, channelListing: PartialChannelListing,
indexNamePrefix: string | undefined, indexNamePrefix: string | undefined
) { ) {
const nameSegments = [ const nameSegments = [
indexNamePrefix, indexNamePrefix,

View file

@ -51,7 +51,7 @@ export const nextClient = (url: string, getAuth: AuthConfig<IAuthState>["getAuth
url, url,
exchanges: getExchanges(getAuth), exchanges: getExchanges(getAuth),
}, },
false, false
); );
}; };

View file

@ -37,7 +37,7 @@ export async function mutateMetadata(client: Client, metadata: MetadataEntry[])
if (idQueryError) { if (idQueryError) {
console.debug("Could not fetch the app id: ", idQueryError); console.debug("Could not fetch the app id: ", idQueryError);
throw new Error( throw new Error(
"Could not fetch the app id. Please check if auth data for the client are valid.", "Could not fetch the app id. Please check if auth data for the client are valid."
); );
} }

View file

@ -20,7 +20,7 @@ const sendResponse = async (
res: NextApiResponse<SettingsApiResponse>, res: NextApiResponse<SettingsApiResponse>,
statusCode: number, statusCode: number,
settings: SettingsManager, settings: SettingsManager,
domain: string, domain: string
) => { ) => {
res.status(statusCode).json({ res.status(statusCode).json({
success: statusCode === 200, success: statusCode === 200,
@ -36,7 +36,7 @@ const sendResponse = async (
export const handler = async ( export const handler = async (
req: NextApiRequest, req: NextApiRequest,
res: NextApiResponse, res: NextApiResponse,
ctx: ProtectedHandlerContext, ctx: ProtectedHandlerContext
) => { ) => {
debug("Configuration handler received request"); debug("Configuration handler received request");
@ -54,7 +54,7 @@ export const handler = async (
} else if (req.method === "POST") { } else if (req.method === "POST") {
debug("Updating the configuration"); debug("Updating the configuration");
const { appId, searchKey, secretKey, indexNamePrefix } = JSON.parse( const { appId, searchKey, secretKey, indexNamePrefix } = JSON.parse(
req.body, req.body
) as AlgoliaConfigurationFields; ) as AlgoliaConfigurationFields;
await settings.set([ await settings.set([
{ key: "secretKey", value: secretKey || "", domain }, { key: "secretKey", value: secretKey || "", domain },

View file

@ -11,15 +11,15 @@ const debug = createDebug("Webhooks handler");
export const handler: NextWebhookApiHandler<ProductEditedSubscription["event"]> = async ( export const handler: NextWebhookApiHandler<ProductEditedSubscription["event"]> = async (
req, req,
res, res,
context, context
) => { ) => {
const { event, authData } = context; const { event, authData } = context;
debug( debug(
`New event ${event} (${context.payload?.__typename}) from the ${authData.domain} domain has been received!`, `New event ${event} (${context.payload?.__typename}) from the ${authData.domain} domain has been received!`
); );
const client = createClient(authData.saleorApiUrl, async () => const client = createClient(authData.saleorApiUrl, async () =>
Promise.resolve({ token: authData.token }), Promise.resolve({ token: authData.token })
); );
const settings = createSettingsManager(client); const settings = createSettingsManager(client);

View file

@ -37,7 +37,7 @@ function Search() {
const [indexName, setIndexName] = useState<string>(); const [indexName, setIndexName] = useState<string>();
const algoliaConfiguration = useConfiguration( const algoliaConfiguration = useConfiguration(
appBridgeState?.saleorApiUrl, appBridgeState?.saleorApiUrl,
appBridgeState?.token, appBridgeState?.token
); );
const searchClient = useMemo(() => { const searchClient = useMemo(() => {

View file

@ -13,7 +13,7 @@ function GraphQLProvider(props: PropsWithChildren<{}>) {
} }
const client = createClient(saleorApiUrl, async () => const client = createClient(saleorApiUrl, async () =>
Promise.resolve({ token: appBridgeState?.token! }), Promise.resolve({ token: appBridgeState?.token! })
); );
return <Provider value={client} {...props} />; return <Provider value={client} {...props} />;

View file

@ -14,17 +14,23 @@
"test": "turbo run test", "test": "turbo run test",
"test:ci": "turbo run test:ci", "test:ci": "turbo run test:ci",
"format": "prettier --write \"**/*.{ts,tsx,md}\"", "format": "prettier --write \"**/*.{ts,tsx,md}\"",
"generate": "turbo run generate" "generate": "turbo run generate",
"prepare": "husky install"
}, },
"devDependencies": { "devDependencies": {
"@changesets/cli": "^2.26.0", "@changesets/cli": "^2.26.0",
"eslint-config-saleor": "workspace:*", "eslint-config-saleor": "workspace:*",
"prettier": "^2.8.3", "prettier": "^2.8.3",
"turbo": "^1.7.4", "turbo": "^1.7.4",
"eslint": "^8.33.0" "eslint": "^8.33.0",
"husky": "^8.0.3"
}, },
"engines": { "engines": {
"node": ">=18.0.0" "node": ">=18.0.0"
}, },
"packageManager": "pnpm@7.26.2" "packageManager": "pnpm@7.26.2",
"lint-staged": {
"*.{js,ts,tsx}": "eslint --cache --fix",
"*.{ts,tsx,md}": "prettier --write"
}
} }

View file

@ -7,12 +7,14 @@ importers:
'@changesets/cli': ^2.26.0 '@changesets/cli': ^2.26.0
eslint: ^8.33.0 eslint: ^8.33.0
eslint-config-saleor: workspace:* eslint-config-saleor: workspace:*
husky: ^8.0.3
prettier: ^2.8.3 prettier: ^2.8.3
turbo: ^1.7.4 turbo: ^1.7.4
devDependencies: devDependencies:
'@changesets/cli': 2.26.0 '@changesets/cli': 2.26.0
eslint: 8.33.0 eslint: 8.33.0
eslint-config-saleor: link:packages/eslint-config-saleor eslint-config-saleor: link:packages/eslint-config-saleor
husky: 8.0.3
prettier: 2.8.3 prettier: 2.8.3
turbo: 1.7.4 turbo: 1.7.4