Add prettier on pre-commit & reformat codebase (#137)
* Run prettier on project * Install lint-staged * Add Husky
This commit is contained in:
parent
9f843b2d31
commit
081d15e168
30 changed files with 140 additions and 119 deletions
1
.husky/.gitignore
vendored
Normal file
1
.husky/.gitignore
vendored
Normal file
|
@ -0,0 +1 @@
|
|||
_
|
4
.husky/pre-commit
Executable file
4
.husky/pre-commit
Executable file
|
@ -0,0 +1,4 @@
|
|||
#!/bin/sh
|
||||
. "$(dirname "$0")/_/husky.sh"
|
||||
|
||||
npx lint-staged
|
|
@ -2,3 +2,4 @@
|
|||
**/pnpm-lock.yaml
|
||||
**/graphql/schema.graphql
|
||||
**/generated
|
||||
.changeset/
|
|
@ -1,5 +1,5 @@
|
|||
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).
|
||||
|
|
|
@ -46,8 +46,8 @@ const nuvoSettings: SettingsAPI = {
|
|||
color: "#fff",
|
||||
},
|
||||
},
|
||||
loader:{
|
||||
loadAnimationColor: '#000'
|
||||
loader: {
|
||||
loadAnimationColor: "#000",
|
||||
},
|
||||
header: {
|
||||
description: {
|
||||
|
|
|
@ -13,7 +13,9 @@ export const CustomersImportingResults = ({
|
|||
|
||||
return (
|
||||
<div>
|
||||
<Typography paragraph variant="h3">Customers rows from imported file</Typography>
|
||||
<Typography paragraph variant="h3">
|
||||
Customers rows from imported file
|
||||
</Typography>
|
||||
|
||||
<Typography paragraph>
|
||||
Lines will be imported one by one. Failed imports can be retried, but performed operations
|
||||
|
@ -33,7 +35,7 @@ export const CustomersImportingResults = ({
|
|||
</Button>
|
||||
)}
|
||||
|
||||
<Table style={{marginTop: 50}}>
|
||||
<Table style={{ marginTop: 50 }}>
|
||||
<TableBody>
|
||||
{importedLines.map((row) => (
|
||||
<CustomerImportingRow
|
||||
|
|
|
@ -7,7 +7,6 @@ import GraphQLProvider from "../providers/GraphQLProvider";
|
|||
import { actions, useAppBridge } from "@saleor/app-sdk/app-bridge";
|
||||
import { AppIcon, TitleBar } from "@saleor/apps-shared";
|
||||
|
||||
|
||||
type Tab = "customers";
|
||||
|
||||
const useStyles = makeStyles((theme: SaleorTheme) => ({
|
||||
|
@ -36,7 +35,7 @@ const ImporterPage: NextPage = () => {
|
|||
<div className={styles.wrapper}>
|
||||
<TitleBar
|
||||
bottomMargin
|
||||
icon={<AppIcon theme="rgb(58, 86, 199)" text="DI"/>}
|
||||
icon={<AppIcon theme="rgb(58, 86, 199)" text="DI" />}
|
||||
name="Data Importer"
|
||||
author="By Saleor Commerce"
|
||||
rightColumnContent={
|
||||
|
|
|
@ -3,4 +3,4 @@
|
|||
*
|
||||
* https://vitest.dev/config/#setupfiles
|
||||
*/
|
||||
export {}
|
||||
export {};
|
||||
|
|
|
@ -71,26 +71,29 @@ pnpm dev
|
|||
### Without CLI
|
||||
|
||||
1. Install the dependencies by running:
|
||||
|
||||
```
|
||||
pnpm install
|
||||
```
|
||||
|
||||
2. Start the local server with:
|
||||
|
||||
```
|
||||
pnpm dev
|
||||
```
|
||||
|
||||
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:
|
||||
|
||||
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]
|
||||
```
|
||||
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.
|
||||
|
||||
|
|
|
@ -1,49 +1,49 @@
|
|||
import {describe, it, expect} from "vitest";
|
||||
import {appConfigInputSchema} from "./app-config-input-schema";
|
||||
import {AppConfig, SellerShopConfig} from "./app-config";
|
||||
import {getMockAddress} from "../../fixtures/mock-address";
|
||||
import { describe, it, expect } from "vitest";
|
||||
import { appConfigInputSchema } from "./app-config-input-schema";
|
||||
import { AppConfig, SellerShopConfig } from "./app-config";
|
||||
import { getMockAddress } from "../../fixtures/mock-address";
|
||||
|
||||
describe("appConfigInputSchema", () => {
|
||||
it('Passes with no channels at all', () => {
|
||||
expect(() =>
|
||||
appConfigInputSchema.parse({
|
||||
shopConfigPerChannel: {}
|
||||
} satisfies AppConfig)
|
||||
).not.to.throw()
|
||||
})
|
||||
it("Passes with no channels at all", () => {
|
||||
expect(() =>
|
||||
appConfigInputSchema.parse({
|
||||
shopConfigPerChannel: {},
|
||||
} satisfies AppConfig)
|
||||
).not.to.throw();
|
||||
});
|
||||
|
||||
it('Passes with all address fields empty', () => {
|
||||
expect(() =>
|
||||
appConfigInputSchema.parse({
|
||||
shopConfigPerChannel: {
|
||||
channel: {
|
||||
address: {
|
||||
city: "",
|
||||
cityArea: "",
|
||||
companyName: "",
|
||||
country: "",
|
||||
countryArea: "",
|
||||
firstName: "",
|
||||
lastName: "",
|
||||
postalCode: "",
|
||||
streetAddress1: "",
|
||||
streetAddress2: ""
|
||||
it("Passes with all address fields empty", () => {
|
||||
expect(() =>
|
||||
appConfigInputSchema.parse({
|
||||
shopConfigPerChannel: {
|
||||
channel: {
|
||||
address: {
|
||||
city: "",
|
||||
cityArea: "",
|
||||
companyName: "",
|
||||
country: "",
|
||||
countryArea: "",
|
||||
firstName: "",
|
||||
lastName: "",
|
||||
postalCode: "",
|
||||
streetAddress1: "",
|
||||
streetAddress2: "",
|
||||
},
|
||||
},
|
||||
},
|
||||
} satisfies AppConfig)
|
||||
).not.to.throw();
|
||||
});
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
} satisfies AppConfig)
|
||||
).not.to.throw()
|
||||
})
|
||||
|
||||
it('Passes with partial address', () => {
|
||||
expect(() =>
|
||||
appConfigInputSchema.parse({
|
||||
shopConfigPerChannel: {
|
||||
channel: {
|
||||
address: getMockAddress() }
|
||||
}
|
||||
} satisfies AppConfig)
|
||||
).not.to.throw()
|
||||
})
|
||||
})
|
||||
it("Passes with partial address", () => {
|
||||
expect(() =>
|
||||
appConfigInputSchema.parse({
|
||||
shopConfigPerChannel: {
|
||||
channel: {
|
||||
address: getMockAddress(),
|
||||
},
|
||||
},
|
||||
} satisfies AppConfig)
|
||||
).not.to.throw();
|
||||
});
|
||||
});
|
||||
|
|
|
@ -1,19 +1,22 @@
|
|||
import {OrderPayloadFragment} from "../../../generated/graphql";
|
||||
import { OrderPayloadFragment } from "../../../generated/graphql";
|
||||
|
||||
interface IInvoiceNumberGenerationStrategy {
|
||||
(order: OrderPayloadFragment): string;
|
||||
(order: OrderPayloadFragment): string;
|
||||
}
|
||||
|
||||
export const InvoiceNumberGenerationStrategy = {
|
||||
localizedDate: (locale: string) => (order: Pick<OrderPayloadFragment, 'created'>) => {
|
||||
const orderCreatedDate = new Date(order.created);
|
||||
localizedDate: (locale: string) => (order: Pick<OrderPayloadFragment, "created">) => {
|
||||
const orderCreatedDate = new Date(order.created);
|
||||
|
||||
return Intl.DateTimeFormat(locale,).format(orderCreatedDate)
|
||||
}
|
||||
} satisfies Record<string, (...args: any[]) => IInvoiceNumberGenerationStrategy>
|
||||
return Intl.DateTimeFormat(locale).format(orderCreatedDate);
|
||||
},
|
||||
} satisfies Record<string, (...args: any[]) => IInvoiceNumberGenerationStrategy>;
|
||||
|
||||
export class InvoiceNumberGenerator {
|
||||
generateFromOrder(order: OrderPayloadFragment, strategy: IInvoiceNumberGenerationStrategy): string {
|
||||
return strategy(order)
|
||||
}
|
||||
generateFromOrder(
|
||||
order: OrderPayloadFragment,
|
||||
strategy: IInvoiceNumberGenerationStrategy
|
||||
): string {
|
||||
return strategy(order);
|
||||
}
|
||||
}
|
|
@ -39,7 +39,7 @@ switch (aplType) {
|
|||
|
||||
if (!process.env.SECRET_KEY && process.env.NODE_ENV === "production") {
|
||||
throw new Error(
|
||||
"For production deployment SECRET_KEY is mandatory to use EncryptedSettingsManager.",
|
||||
"For production deployment SECRET_KEY is mandatory to use EncryptedSettingsManager."
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
@ -80,7 +80,7 @@ export const AlgoliaConfigurationCard = () => {
|
|||
},
|
||||
});
|
||||
},
|
||||
},
|
||||
}
|
||||
);
|
||||
|
||||
const onFormSubmit = handleSubmit(async (conf) => mutate(conf));
|
||||
|
|
|
@ -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 AlgoliaConfigurationCard from "./AlgoliaConfigurationCard";
|
||||
import { makeStyles, PageTab, PageTabs } from "@saleor/macaw-ui";
|
||||
|
|
|
@ -19,7 +19,7 @@ export const ImportProductsToAlgolia = () => {
|
|||
const { appBridgeState } = useAppBridge();
|
||||
const algoliaConfiguration = useConfiguration(
|
||||
appBridgeState?.saleorApiUrl,
|
||||
appBridgeState?.token,
|
||||
appBridgeState?.token
|
||||
);
|
||||
|
||||
const searchProvider = useMemo(() => {
|
||||
|
|
|
@ -5,28 +5,28 @@ import { useSearchBox } from "react-instantsearch-hooks-web";
|
|||
import styles from "../styles/search.module.css";
|
||||
|
||||
export function SearchBox() {
|
||||
const { query, refine } = useSearchBox();
|
||||
const { query, refine } = useSearchBox();
|
||||
|
||||
const handleChange = (e: ChangeEvent<HTMLInputElement>) => {
|
||||
refine(e.target.value);
|
||||
};
|
||||
return (
|
||||
<div className={styles.textFieldContainer}>
|
||||
<TextField
|
||||
fullWidth
|
||||
InputProps={{
|
||||
startAdornment: (
|
||||
<InputAdornment position="start">
|
||||
<SearchIcon />
|
||||
</InputAdornment>
|
||||
),
|
||||
}}
|
||||
value={query}
|
||||
onChange={handleChange}
|
||||
placeholder={"Search products..."}
|
||||
inputProps={{ style: { padding: "16px" } }}
|
||||
className={styles.textField}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
const handleChange = (e: ChangeEvent<HTMLInputElement>) => {
|
||||
refine(e.target.value);
|
||||
};
|
||||
return (
|
||||
<div className={styles.textFieldContainer}>
|
||||
<TextField
|
||||
fullWidth
|
||||
InputProps={{
|
||||
startAdornment: (
|
||||
<InputAdornment position="start">
|
||||
<SearchIcon />
|
||||
</InputAdornment>
|
||||
),
|
||||
}}
|
||||
value={query}
|
||||
onChange={handleChange}
|
||||
placeholder={"Search products..."}
|
||||
inputProps={{ style: { padding: "16px" } }}
|
||||
className={styles.textField}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
|
|
@ -35,7 +35,7 @@ export class AlgoliaSearchProvider implements SearchProvider {
|
|||
Object.entries(groupedByIndex).map(([indexName, objects]) => {
|
||||
const index = this.#algolia.initIndex(indexName);
|
||||
return index.saveObjects(objects);
|
||||
}),
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -46,7 +46,7 @@ export class AlgoliaSearchProvider implements SearchProvider {
|
|||
Object.entries(groupedByIndex).map(([indexName, objects]) => {
|
||||
const index = this.#algolia.initIndex(indexName);
|
||||
return index.deleteObjects(objects.map((o) => o.objectID));
|
||||
}),
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -141,7 +141,7 @@ const groupVariantByIndexName = (
|
|||
{
|
||||
visibleInListings,
|
||||
indexNamePrefix,
|
||||
}: { visibleInListings: true | false | null; indexNamePrefix: string | undefined },
|
||||
}: { visibleInListings: true | false | null; indexNamePrefix: string | undefined }
|
||||
) => {
|
||||
if (!productVariant.product.channelListings) {
|
||||
return null;
|
||||
|
@ -150,7 +150,7 @@ const groupVariantByIndexName = (
|
|||
const objectsToSaveByIndexName = productVariant.product.channelListings
|
||||
.filter((channelListing) =>
|
||||
// don't filter if `visibleInListings` is null
|
||||
visibleInListings === null ? true : channelListing.visibleInListings === visibleInListings,
|
||||
visibleInListings === null ? true : channelListing.visibleInListings === visibleInListings
|
||||
)
|
||||
.map((channelListing) => {
|
||||
const object = productAndVariantToAlgolia({
|
||||
|
@ -176,7 +176,7 @@ const groupProductsByIndexName = (
|
|||
{
|
||||
visibleInListings,
|
||||
indexNamePrefix,
|
||||
}: { visibleInListings: true | false | null; indexNamePrefix: string | undefined },
|
||||
}: { visibleInListings: true | false | null; indexNamePrefix: string | undefined }
|
||||
) => {
|
||||
debug(`groupProductsByIndexName called`);
|
||||
const batchesAndIndices = productsBatch
|
||||
|
|
|
@ -10,7 +10,7 @@ type PartialChannelListing = {
|
|||
|
||||
export function channelListingToAlgoliaIndexId(
|
||||
channelListing: PartialChannelListing,
|
||||
indexNamePrefix: string | undefined,
|
||||
indexNamePrefix: string | undefined
|
||||
) {
|
||||
const nameSegments = [
|
||||
indexNamePrefix,
|
||||
|
|
|
@ -51,7 +51,7 @@ export const nextClient = (url: string, getAuth: AuthConfig<IAuthState>["getAuth
|
|||
url,
|
||||
exchanges: getExchanges(getAuth),
|
||||
},
|
||||
false,
|
||||
false
|
||||
);
|
||||
};
|
||||
|
||||
|
|
|
@ -37,7 +37,7 @@ export async function mutateMetadata(client: Client, metadata: MetadataEntry[])
|
|||
if (idQueryError) {
|
||||
console.debug("Could not fetch the app id: ", idQueryError);
|
||||
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."
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
@ -20,7 +20,7 @@ const sendResponse = async (
|
|||
res: NextApiResponse<SettingsApiResponse>,
|
||||
statusCode: number,
|
||||
settings: SettingsManager,
|
||||
domain: string,
|
||||
domain: string
|
||||
) => {
|
||||
res.status(statusCode).json({
|
||||
success: statusCode === 200,
|
||||
|
@ -36,7 +36,7 @@ const sendResponse = async (
|
|||
export const handler = async (
|
||||
req: NextApiRequest,
|
||||
res: NextApiResponse,
|
||||
ctx: ProtectedHandlerContext,
|
||||
ctx: ProtectedHandlerContext
|
||||
) => {
|
||||
debug("Configuration handler received request");
|
||||
|
||||
|
@ -54,7 +54,7 @@ export const handler = async (
|
|||
} else if (req.method === "POST") {
|
||||
debug("Updating the configuration");
|
||||
const { appId, searchKey, secretKey, indexNamePrefix } = JSON.parse(
|
||||
req.body,
|
||||
req.body
|
||||
) as AlgoliaConfigurationFields;
|
||||
await settings.set([
|
||||
{ key: "secretKey", value: secretKey || "", domain },
|
||||
|
|
|
@ -11,15 +11,15 @@ const debug = createDebug("Webhooks handler");
|
|||
export const handler: NextWebhookApiHandler<ProductEditedSubscription["event"]> = async (
|
||||
req,
|
||||
res,
|
||||
context,
|
||||
context
|
||||
) => {
|
||||
const { event, authData } = context;
|
||||
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 () =>
|
||||
Promise.resolve({ token: authData.token }),
|
||||
Promise.resolve({ token: authData.token })
|
||||
);
|
||||
|
||||
const settings = createSettingsManager(client);
|
||||
|
|
|
@ -37,7 +37,7 @@ function Search() {
|
|||
const [indexName, setIndexName] = useState<string>();
|
||||
const algoliaConfiguration = useConfiguration(
|
||||
appBridgeState?.saleorApiUrl,
|
||||
appBridgeState?.token,
|
||||
appBridgeState?.token
|
||||
);
|
||||
|
||||
const searchClient = useMemo(() => {
|
||||
|
|
|
@ -13,7 +13,7 @@ function GraphQLProvider(props: PropsWithChildren<{}>) {
|
|||
}
|
||||
|
||||
const client = createClient(saleorApiUrl, async () =>
|
||||
Promise.resolve({ token: appBridgeState?.token! }),
|
||||
Promise.resolve({ token: appBridgeState?.token! })
|
||||
);
|
||||
|
||||
return <Provider value={client} {...props} />;
|
||||
|
|
12
package.json
12
package.json
|
@ -14,17 +14,23 @@
|
|||
"test": "turbo run test",
|
||||
"test:ci": "turbo run test:ci",
|
||||
"format": "prettier --write \"**/*.{ts,tsx,md}\"",
|
||||
"generate": "turbo run generate"
|
||||
"generate": "turbo run generate",
|
||||
"prepare": "husky install"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@changesets/cli": "^2.26.0",
|
||||
"eslint-config-saleor": "workspace:*",
|
||||
"prettier": "^2.8.3",
|
||||
"turbo": "^1.7.4",
|
||||
"eslint": "^8.33.0"
|
||||
"eslint": "^8.33.0",
|
||||
"husky": "^8.0.3"
|
||||
},
|
||||
"engines": {
|
||||
"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"
|
||||
}
|
||||
}
|
|
@ -7,12 +7,14 @@ importers:
|
|||
'@changesets/cli': ^2.26.0
|
||||
eslint: ^8.33.0
|
||||
eslint-config-saleor: workspace:*
|
||||
husky: ^8.0.3
|
||||
prettier: ^2.8.3
|
||||
turbo: ^1.7.4
|
||||
devDependencies:
|
||||
'@changesets/cli': 2.26.0
|
||||
eslint: 8.33.0
|
||||
eslint-config-saleor: link:packages/eslint-config-saleor
|
||||
husky: 8.0.3
|
||||
prettier: 2.8.3
|
||||
turbo: 1.7.4
|
||||
|
||||
|
|
Loading…
Reference in a new issue