@shopify/shopify-app-react-router
Version:
Shopify React Router - to simplify the building of Shopify Apps with React Router
161 lines (158 loc) • 7.21 kB
JavaScript
import '@shopify/shopify-api/adapters/web-api';
import { ShopifyError, shopifyApi } from '@shopify/shopify-api';
import { AppDistribution } from './types.mjs';
import { SHOPIFY_REACT_ROUTER_LIBRARY_VERSION } from './version.mjs';
import { registerWebhooksFactory } from './authenticate/webhooks/register.mjs';
import { authStrategyFactory } from './authenticate/admin/authenticate.mjs';
import { authenticateWebhookFactory } from './authenticate/webhooks/authenticate.mjs';
import { overrideLogger } from './override-logger.mjs';
import { addDocumentResponseHeadersFactory } from './authenticate/helpers/add-response-headers.mjs';
import 'react-router';
import 'isbot';
import { loginFactory } from './authenticate/login/login.mjs';
import { unauthenticatedAdminContextFactory } from './unauthenticated/admin/factory.mjs';
import { authenticatePublicFactory } from './authenticate/public/factory.mjs';
import { unauthenticatedStorefrontContextFactory } from './unauthenticated/storefront/factory.mjs';
import { createTokenExchangeStrategy } from './authenticate/admin/strategies/token-exchange.mjs';
import { createMerchantCustomAuthStrategy } from './authenticate/admin/strategies/merchant-custom-app.mjs';
import { IdempotentPromiseHandler } from './authenticate/helpers/idempotent-promise-handler.mjs';
import { authenticateFlowFactory } from './authenticate/flow/authenticate.mjs';
import { authenticateFulfillmentServiceFactory } from './authenticate/fulfillment-service/authenticate.mjs';
import { authenticatePOSFactory } from './authenticate/pos/authenticate.mjs';
/**
* Creates an object your app will use to interact with Shopify.
*
* @param appConfig Configuration options for your Shopify app, such as the scopes your app needs.
* @returns `ShopifyApp` An object constructed using your appConfig. It has methods for interacting with Shopify.
*
* @example
* <caption>The minimum viable configuration</caption>
* ```ts
* // /shopify.server.ts
* import { shopifyApp } from "@shopify/shopify-app-react-router/server";
*
* const shopify = shopifyApp({
* apiKey: process.env.SHOPIFY_API_KEY!,
* apiSecretKey: process.env.SHOPIFY_API_SECRET!,
* scopes: process.env.SCOPES?.split(",")!,
* appUrl: process.env.SHOPIFY_APP_URL!,
* });
* export default shopify;
* ```
*/
function shopifyApp(appConfig) {
const api = deriveApi(appConfig);
const config = deriveConfig(appConfig, api.config);
const logger = overrideLogger(api.logger);
if (appConfig.webhooks) {
api.webhooks.addHandlers(appConfig.webhooks);
}
const params = { api, config, logger };
let strategy;
if (config.distribution === AppDistribution.ShopifyAdmin) {
strategy = createMerchantCustomAuthStrategy(params);
}
else {
strategy = createTokenExchangeStrategy(params);
}
const authStrategy = authStrategyFactory({
...params,
strategy,
});
const shopify = {
sessionStorage: config.sessionStorage,
addDocumentResponseHeaders: addDocumentResponseHeadersFactory(params),
registerWebhooks: registerWebhooksFactory(params),
authenticate: {
admin: authStrategy,
flow: authenticateFlowFactory(params),
fulfillmentService: authenticateFulfillmentServiceFactory(params),
pos: authenticatePOSFactory(params),
public: authenticatePublicFactory(params),
webhook: authenticateWebhookFactory(params),
},
unauthenticated: {
admin: unauthenticatedAdminContextFactory(params),
storefront: unauthenticatedStorefrontContextFactory(params),
},
};
if (isAppStoreApp(shopify, appConfig) ||
isSingleMerchantApp(shopify, appConfig)) {
shopify.login = loginFactory(params);
}
return shopify;
}
function isAppStoreApp(_shopify, config) {
return config.distribution === AppDistribution.AppStore;
}
function isSingleMerchantApp(_shopify, config) {
return config.distribution === AppDistribution.SingleMerchant;
}
// This function is only exported so we can unit test it without having to mock the underlying module.
// It's not available to consumers of the library because it is not exported in the index module, and never should be.
function deriveApi(appConfig) {
let appUrl;
try {
appUrl = new URL(appConfig.appUrl);
}
catch (error) {
const message = appConfig.appUrl === ''
? `Detected an empty appUrl configuration, please make sure to set the necessary environment variables.\n` +
`If you're deploying your app, you can find more information at https://shopify.dev/docs/apps/launch/deployment/deploy-web-app/deploy-to-hosting-service#step-4-set-up-environment-variables`
: `Invalid appUrl configuration '${appConfig.appUrl}', please provide a valid URL.`;
throw new ShopifyError(message);
}
/* eslint-disable no-process-env */
if (appUrl.hostname === 'localhost' && !appUrl.port && process.env.PORT) {
appUrl.port = process.env.PORT;
}
/* eslint-enable no-process-env */
appConfig.appUrl = appUrl.origin;
let userAgentPrefix = `Shopify React Router Library v${SHOPIFY_REACT_ROUTER_LIBRARY_VERSION}`;
if (appConfig.userAgentPrefix) {
userAgentPrefix = `${appConfig.userAgentPrefix} | ${userAgentPrefix}`;
}
return shopifyApi({
...appConfig,
hostName: appUrl.host,
hostScheme: appUrl.protocol.replace(':', ''),
userAgentPrefix,
isEmbeddedApp: true,
isCustomStoreApp: appConfig.distribution === AppDistribution.ShopifyAdmin,
billing: appConfig.billing,
future: {
unstable_managedPricingSupport: true,
},
_logDisabledFutureFlags: false,
});
}
function deriveConfig(appConfig, apiConfig) {
if (!appConfig.sessionStorage &&
appConfig.distribution !== AppDistribution.ShopifyAdmin) {
throw new ShopifyError('Please provide a valid session storage. Refer to https://github.com/Shopify/shopify-app-js/blob/main/README.md#session-storage-options for options.');
}
const authPathPrefix = appConfig.authPathPrefix || '/auth';
appConfig.distribution = appConfig.distribution ?? AppDistribution.AppStore;
return {
...appConfig,
...apiConfig,
billing: appConfig.billing,
scopes: apiConfig.scopes,
idempotentPromiseHandler: new IdempotentPromiseHandler(),
canUseLoginForm: appConfig.distribution !== AppDistribution.ShopifyAdmin,
useOnlineTokens: appConfig.useOnlineTokens ?? false,
hooks: appConfig.hooks ?? {},
sessionStorage: appConfig.sessionStorage,
future: appConfig.future ?? {},
auth: {
path: authPathPrefix,
callbackPath: `${authPathPrefix}/callback`,
patchSessionTokenPath: `${authPathPrefix}/session-token`,
exitIframePath: `${authPathPrefix}/exit-iframe`,
loginPath: `${authPathPrefix}/login`,
},
distribution: appConfig.distribution,
};
}
export { deriveApi, shopifyApp };
//# sourceMappingURL=shopify-app.mjs.map