diff --git a/.editorconfig b/.editorconfig
deleted file mode 100644
index 11ebf4e4e4..0000000000
--- a/.editorconfig
+++ /dev/null
@@ -1,23 +0,0 @@
-root = true
-
-[*]
-indent_style = space
-indent_size = 2
-tab_width = 4
-end_of_line = lf
-charset = utf-8
-trim_trailing_whitespace = true
-insert_final_newline = true
-
-[*.md]
-trim_trailing_whitespace = false
-
-[*.js]
-quote_type = single
-
-[{*.c,*.cc,*.h,*.hh,*.cpp,*.hpp,*.m,*.mm,*.mpp,*.js,*.java,*.go,*.rs,*.php,*.ng,*.jsx,*.ts,*.d,*.cs,*.swift}]
-curly_bracket_next_line = false
-spaces_around_operators = true
-spaces_around_brackets = outside
-# close enough to 1TB
-indent_brace_style = K&R
diff --git a/.env.example b/.env.example
new file mode 100644
index 0000000000..bd952c5310
--- /dev/null
+++ b/.env.example
@@ -0,0 +1,3 @@
+SHOPIFY_STOREFRONT_ACCESS_TOKEN=
+SHOPIFY_STORE_DOMAIN=
+SHOPIFY_REVALIDATION_TOKEN=
diff --git a/.eslintrc.js b/.eslintrc.js
new file mode 100644
index 0000000000..b3e65ae8c4
--- /dev/null
+++ b/.eslintrc.js
@@ -0,0 +1,23 @@
+module.exports = {
+ extends: ['next', 'prettier'],
+ plugins: ['unicorn'],
+ rules: {
+ 'no-unused-vars': [
+ 'error',
+ {
+ args: 'after-used',
+ caughtErrors: 'none',
+ ignoreRestSiblings: true,
+ vars: 'all'
+ }
+ ],
+ 'prefer-const': 'error',
+ 'react-hooks/exhaustive-deps': 'error',
+ 'unicorn/filename-case': [
+ 'error',
+ {
+ case: 'kebabCase'
+ }
+ ]
+ }
+};
diff --git a/.github/ISSUE_TEMPLATE/1.core_bug_report.yml b/.github/ISSUE_TEMPLATE/1.core_bug_report.yml
deleted file mode 100644
index 6d3848884e..0000000000
--- a/.github/ISSUE_TEMPLATE/1.core_bug_report.yml
+++ /dev/null
@@ -1,55 +0,0 @@
-name: Core package Bug Report
-description: Create a bug report for the Next.js commerce core package
-labels: 'template: core bug'
-body:
- - type: markdown
- attributes:
- value: Thanks for taking the time to file a bug report! Please fill out this form as completely as possible.
- - type: checkboxes
- attributes:
- label: Verify latest commit
- description: `main` is the latest version of Next.js Commerce.
- options:
- - label: I verified that the issue exists on `main`
- required: true
- - type: textarea
- attributes:
- label: Provide environment information
- description: Please run `npx --no-install next info` in the root directory of your project and paste the results.
- validations:
- required: true
- - type: input
- attributes:
- label: What browser are you using? (if relevant)
- description: 'Please specify the exact version. For example: Chrome 100.0.4878.0'
- - type: input
- attributes:
- label: How are you deploying your application? (if relevant)
- description: 'For example: next start, next export, Vercel, Other platform'
- - type: textarea
- attributes:
- label: Describe the Bug
- description: A clear and concise description of what the bug is.
- validations:
- required: true
- - type: textarea
- attributes:
- label: Expected Behavior
- description: A clear and concise description of what you expected to happen.
- validations:
- required: true
- - type: textarea
- attributes:
- label: To Reproduce
- description: Steps to reproduce the behavior, please provide a clear code snippets that always reproduces the issue or a GitHub repository. Screenshots can be provided in the issue body below.
- validations:
- required: true
- - type: markdown
- attributes:
- value: Before posting the issue go through the steps you've written down to make sure the steps provided are detailed and clear.
- - type: markdown
- attributes:
- value: Contributors should be able to follow the steps provided in order to reproduce the bug.
- - type: markdown
- attributes:
- value: These steps are used to add integration tests to ensure the same issue does not happen again. Thanks in advance!
diff --git a/.github/ISSUE_TEMPLATE/2.provider_bug_report.yml b/.github/ISSUE_TEMPLATE/2.provider_bug_report.yml
deleted file mode 100644
index a600c3781d..0000000000
--- a/.github/ISSUE_TEMPLATE/2.provider_bug_report.yml
+++ /dev/null
@@ -1,59 +0,0 @@
-name: Provider package Bug Report
-description: Create a bug report for the Next.js commerce core package
-labels: 'template: provider bug'
-body:
- - type: markdown
- attributes:
- value: Thanks for taking the time to file a bug report! Please fill out this form as completely as possible.
- - type: checkboxes
- attributes:
- label: Verify latest commit
- description: `main` is the latest version of Next.js Commerce.
- options:
- - label: I verified that the issue exists on `main`
- required: true
- - type: textarea
- attributes:
- label: Provide environment information
- description: Please run `npx --no-install next info` in the root directory of your project and paste the results.
- validations:
- required: true
- - type: input
- attributes:
- label: What Provider are you using?
- description: 'Please specify the provider package name. For example: `bigcommerce`'
- - type: input
- attributes:
- label: What browser are you using? (if relevant)
- description: 'Please specify the exact version. For example: Chrome 100.0.4878.0'
- - type: input
- attributes:
- label: How are you deploying your application? (if relevant)
- description: 'For example: next start, next export, Vercel, Other platform'
- - type: textarea
- attributes:
- label: Describe the Bug
- description: A clear and concise description of what the bug is.
- validations:
- required: true
- - type: textarea
- attributes:
- label: Expected Behavior
- description: A clear and concise description of what you expected to happen.
- validations:
- required: true
- - type: textarea
- attributes:
- label: To Reproduce
- description: Steps to reproduce the behavior, please provide a clear code snippets that always reproduces the issue or a GitHub repository. Screenshots can be provided in the issue body below.
- validations:
- required: true
- - type: markdown
- attributes:
- value: Before posting the issue go through the steps you've written down to make sure the steps provided are detailed and clear.
- - type: markdown
- attributes:
- value: Contributors should be able to follow the steps provided in order to reproduce the bug.
- - type: markdown
- attributes:
- value: These steps are used to add integration tests to ensure the same issue does not happen again. Thanks in advance!
diff --git a/.github/ISSUE_TEMPLATE/3.feature_request.yml b/.github/ISSUE_TEMPLATE/3.feature_request.yml
deleted file mode 100644
index 2655aff44d..0000000000
--- a/.github/ISSUE_TEMPLATE/3.feature_request.yml
+++ /dev/null
@@ -1,28 +0,0 @@
-name: Feature Request
-description: Create a feature request for the Next.js core
-labels: 'template: story'
-body:
- - type: markdown
- attributes:
- value: Thanks for taking the time to file a feature request! Please fill out this form as completely as possible.
- - type: markdown
- attributes:
- value: 'Feature requests will be converted to the GitHub Discussions "Ideas" section.'
- - type: textarea
- attributes:
- label: Describe the feature you'd like to request
- description: A clear and concise description of what you want and what your use case is.
- validations:
- required: true
- - type: textarea
- attributes:
- label: Describe the solution you'd like
- description: A clear and concise description of what you want to happen.
- validations:
- required: true
- - type: textarea
- attributes:
- label: Describe alternatives you've considered
- description: A clear and concise description of any alternative solutions or features you've considered.
- validations:
- required: true
diff --git a/.github/ISSUE_TEMPLATE/4.docs_request.yml b/.github/ISSUE_TEMPLATE/4.docs_request.yml
deleted file mode 100644
index 72acd89ace..0000000000
--- a/.github/ISSUE_TEMPLATE/4.docs_request.yml
+++ /dev/null
@@ -1,18 +0,0 @@
-name: 'Docs Request for an Update or Improvement'
-description: A request to update or improve Next.js Commerce documentation
-title: 'Docs: '
-labels:
- - 'template: documentation'
-body:
- - type: textarea
- attributes:
- label: What is the improvement or update you wish to see?
- description: 'Example: I would like to see more examples of how to use hooks.'
- validations:
- required: true
- - type: textarea
- attributes:
- label: Is there any context that might help us understand?
- description: A clear description of any added context that might help us understand.
- validations:
- required: true
\ No newline at end of file
diff --git a/.github/ISSUE_TEMPLATE/config.yml b/.github/ISSUE_TEMPLATE/config.yml
deleted file mode 100644
index dcefd31c5e..0000000000
--- a/.github/ISSUE_TEMPLATE/config.yml
+++ /dev/null
@@ -1,5 +0,0 @@
-blank_issues_enabled: false
-contact_links:
- - name: Ask a question
- url: https://github.com/vercel/commerce/discussions
- about: Ask questions and discuss with other community members
diff --git a/.github/dependabot.yml b/.github/dependabot.yml
new file mode 100644
index 0000000000..b18fd29357
--- /dev/null
+++ b/.github/dependabot.yml
@@ -0,0 +1,6 @@
+version: 2
+updates:
+ - package-ecosystem: 'github-actions'
+ directory: '/'
+ schedule:
+ interval: 'weekly'
diff --git a/.github/workflows/e2e.yml b/.github/workflows/e2e.yml
new file mode 100644
index 0000000000..a52b961a23
--- /dev/null
+++ b/.github/workflows/e2e.yml
@@ -0,0 +1,49 @@
+name: e2e
+on:
+ schedule:
+ # Runs "at 09:00 and 15:00, Monday through Friday" (see https://crontab.guru)
+ - cron: '0 9,15 * * 1-5'
+jobs:
+ e2e:
+ runs-on: ubuntu-latest
+ steps:
+ - name: Cancel running workflows
+ uses: styfle/cancel-workflow-action@0.11.0
+ with:
+ access_token: ${{ github.token }}
+ - name: Checkout repo
+ uses: actions/checkout@v3
+ - name: Set node version
+ uses: actions/setup-node@v3
+ with:
+ node-version-file: '.nvmrc'
+ - name: Set pnpm version
+ uses: pnpm/action-setup@v2
+ with:
+ run_install: false
+ version: 7
+ - name: Cache node_modules
+ id: node-modules-cache
+ uses: actions/cache@v3
+ with:
+ path: '**/node_modules'
+ key: node-modules-cache-${{ hashFiles('**/pnpm-lock.yaml') }}
+ - name: Install dependencies
+ if: steps.node-modules-cache.outputs.cache-hit != 'true'
+ run: pnpm install
+ - name: Get playwright version
+ run: echo "PLAYWRIGHT_VERSION=$(node -e "console.log(require('./node_modules/@playwright/test/package.json').version)")" >> $GITHUB_ENV
+ - name: Cache playwright
+ uses: actions/cache@v3
+ id: playwright-cache
+ with:
+ path: '~/.cache/ms-playwright'
+ key: playwright-cache-${{ env.PLAYWRIGHT_VERSION }}
+ - name: Install playwright browsers
+ if: steps.playwright-cache.outputs.cache-hit != 'true'
+ run: npx playwright install --with-deps
+ - name: Install playwright browser dependencies
+ if: steps.playwright-cache.outputs.cache-hit == 'true'
+ run: npx playwright install-deps
+ - name: Run tests
+ run: pnpm test:e2e
diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml
new file mode 100644
index 0000000000..f9d940bec4
--- /dev/null
+++ b/.github/workflows/test.yml
@@ -0,0 +1,32 @@
+name: test
+on: pull_request
+jobs:
+ test:
+ runs-on: ubuntu-latest
+ steps:
+ - name: Cancel running workflows
+ uses: styfle/cancel-workflow-action@0.11.0
+ with:
+ access_token: ${{ github.token }}
+ - name: Checkout repo
+ uses: actions/checkout@v3
+ - name: Set node version
+ uses: actions/setup-node@v3
+ with:
+ node-version-file: '.nvmrc'
+ - name: Set pnpm version
+ uses: pnpm/action-setup@v2
+ with:
+ run_install: false
+ version: 7
+ - name: Cache node_modules
+ id: node-modules-cache
+ uses: actions/cache@v3
+ with:
+ path: '**/node_modules'
+ key: node-modules-cache-${{ hashFiles('**/pnpm-lock.yaml') }}
+ - name: Install dependencies
+ if: steps.node-modules-cache.outputs.cache-hit != 'true'
+ run: pnpm install
+ - name: Run tests
+ run: pnpm test
diff --git a/.gitignore b/.gitignore
index 10295bcccf..0298027e4f 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,39 +1,38 @@
+# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
+
# dependencies
-node_modules
-.pnp
+/node_modules
+/.pnp
.pnp.js
-.pnpm-debug.log
# testing
-coverage
+/coverage
+.playwright
# next.js
-.next
-out
+/.next/
+/out/
# production
-build
-dist
+/build
# misc
.DS_Store
*.pem
-.idea
# debug
npm-debug.log*
yarn-debug.log*
yarn-error.log*
+.pnpm-debug.log*
# local env files
-.env
-.env.local
-.env.development.local
-.env.test.local
-.env.production.local
+.env*
+!.env.example
# vercel
.vercel
-# Turborepo
-.turbo
+# typescript
+*.tsbuildinfo
+next-env.d.ts
diff --git a/.nvmrc b/.nvmrc
new file mode 100644
index 0000000000..b6a7d89c68
--- /dev/null
+++ b/.nvmrc
@@ -0,0 +1 @@
+16
diff --git a/.prettierignore b/.prettierignore
index 1c8b279ce8..71df57cbc4 100644
--- a/.prettierignore
+++ b/.prettierignore
@@ -1,5 +1,3 @@
-# Every package defines its prettier config
-node_modules
-dist
+.vercel
.next
-public
+pnpm-lock.yaml
diff --git a/.prettierrc b/.prettierrc
deleted file mode 100644
index e1076edfa8..0000000000
--- a/.prettierrc
+++ /dev/null
@@ -1,6 +0,0 @@
-{
- "semi": false,
- "singleQuote": true,
- "tabWidth": 2,
- "useTabs": false
-}
diff --git a/.vscode/extensions.json b/.vscode/extensions.json
deleted file mode 100644
index 60907f7fdd..0000000000
--- a/.vscode/extensions.json
+++ /dev/null
@@ -1,8 +0,0 @@
-{
- "recommendations": [
- "esbenp.prettier-vscode",
- "csstools.postcss",
- "bradlc.vscode-tailwindcss",
- "ms-vscode.vscode-typescript-next"
- ]
-}
diff --git a/.vscode/launch.json b/.vscode/launch.json
new file mode 100644
index 0000000000..448434dd01
--- /dev/null
+++ b/.vscode/launch.json
@@ -0,0 +1,28 @@
+{
+ "version": "0.2.0",
+ "configurations": [
+ {
+ "name": "Next.js: debug server-side",
+ "type": "node-terminal",
+ "request": "launch",
+ "command": "pnpm dev"
+ },
+ {
+ "name": "Next.js: debug client-side",
+ "type": "chrome",
+ "request": "launch",
+ "url": "http://localhost:3000"
+ },
+ {
+ "name": "Next.js: debug full stack",
+ "type": "node-terminal",
+ "request": "launch",
+ "command": "pnpm dev",
+ "serverReadyAction": {
+ "pattern": "started server on .+, url: (https?://.+)",
+ "uriFormat": "%s",
+ "action": "debugWithChrome"
+ }
+ }
+ ]
+}
diff --git a/.vscode/settings.json b/.vscode/settings.json
index 9bf4d12b52..3ffeeffd70 100644
--- a/.vscode/settings.json
+++ b/.vscode/settings.json
@@ -1,4 +1,9 @@
{
- "editor.defaultFormatter": "esbenp.prettier-vscode",
- "editor.formatOnSave": true
+ "typescript.tsdk": "node_modules/.pnpm/typescript@4.9.5/node_modules/typescript/lib",
+ "typescript.enablePromptUseWorkspaceTsdk": true,
+ "editor.codeActionsOnSave": {
+ "source.fixAll": true,
+ "source.organizeImports": true,
+ "source.sortMembers": true
+ }
}
diff --git a/README.md b/README.md
index 2bb8b55252..8a8fa68a69 100644
--- a/README.md
+++ b/README.md
@@ -1,199 +1,31 @@
-[![Deploy with Vercel](https://vercel.com/button)](https://vercel.com/new/clone?repository-url=https%3A%2F%2Fgithub.com%2Fvercel%2Fcommerce&project-name=commerce&repo-name=commerce&demo-title=Next.js%20Commerce&demo-description=An%20all-in-one%20starter%20kit%20for%20high-performance%20e-commerce%20sites.&demo-url=https%3A%2F%2Fdemo.vercel.store&demo-image=https%3A%2F%2Fbigcommerce-demo-asset-ksvtgfvnd.vercel.app%2Fbigcommerce.png&integration-ids=oac_MuWZiE4jtmQ2ejZQaQ7ncuDT,oac_9HSKtXld74NG0srzdxSiBGty&skippable-integrations=1&root-directory=site&build-command=cd%20..%20%26%26%20yarn%20build)
+[![Deploy with Vercel](https://vercel.com/button)](https://vercel.com/new/clone?repository-url=https%3A%2F%2Fgithub.com%2Fvercel%2Fcommerce&project-name=commerce&repo-name=commerce&demo-title=Next.js%20Commerce&demo-url=https%3A%2F%2Fdemo.vercel.store&demo-image=https%3A%2F%2Fbigcommerce-demo-asset-ksvtgfvnd.vercel.app%2Fbigcommerce.png&env=SHOPIFY_STOREFRONT_ACCESS_TOKEN,SHOPIFY_STORE_DOMAIN,SHOPIFY_REVALIDATION_TOKEN)
# Next.js Commerce
-The all-in-one starter kit for high-performance e-commerce sites. With a few clicks, Next.js developers can clone, deploy and fully customize their own store.
-Start right now at [nextjs.org/commerce](https://nextjs.org/commerce)
+Next.js 13 and App Router-ready ecommerce template, built with Shopify and Tailwind CSS.
-Demo live at: [demo.vercel.store](https://demo.vercel.store/)
+We will be shortly updating the demo at [demo.vercel.store](https://demo.vercel.store/) with this new version.
-- Shopify Demo: https://shopify.vercel.store/
-- Swell Demo: https://swell.vercel.store/
-- BigCommerce Demo: https://bigcommerce.vercel.store/
-- Vendure Demo: https://vendure.vercel.store
-- Saleor Demo: https://saleor.vercel.store/
-- Ordercloud Demo: https://ordercloud.vercel.store/
-- Spree Demo: https://spree.vercel.store/
-- Kibo Commerce Demo: https://kibocommerce.vercel.store/
-- Commerce.js Demo: https://commercejs.vercel.store/
-- SalesForce Cloud Commerce Demo: https://salesforce-cloud-commerce.vercel.store/
-
-## Run minimal version locally
-
-> To run a minimal version of Next.js Commerce you can start with the default local provider `@vercel/commerce-local` that has all features disabled (cart, auth) and uses static files for the backend
-
-```bash
-pnpm install & pnpm build # run these commands in the root folder of the mono repo
-pnpm dev # run this command in the site folder
-```
-
-> If you encounter any problems while installing and running for the first time, please see the Troubleshoot section
+Looking for Next.js Commerce v1? [View the release notes](https://github.com/vercel/commerce/releases/tag/v1).
## Features
-- Performant by default
-- SEO Ready
-- Internationalization
-- Responsive
-- UI Components
-- Theming
-- Standardized Data Hooks
-- Integrations - Integrate seamlessly with the most common ecommerce platforms.
-- Dark Mode Support
-
-## Integrations
-
-Next.js Commerce integrates out-of-the-box with BigCommerce, Shopify, Swell, Saleor, Vendure, Spree and Commerce.js. We plan to support all major ecommerce backends.
-
-## Considerations
-
-- `packages/commerce` contains all types, helpers and functions to be used as a base to build a new **provider**.
-- **Providers** live under `packages`'s root folder and they will extend Next.js Commerce types and functionality (`packages/commerce`).
-- We have a **Features API** to ensure feature parity between the UI and the Provider. The UI should update accordingly and no extra code should be bundled. All extra configuration for features will live under `features` in `commerce.config.json` and if needed it can also be accessed programmatically.
-- Each **provider** should add its corresponding `next.config.js` and `commerce.config.json` adding specific data related to the provider. For example in the case of BigCommerce, the images CDN and additional API routes.
-
-## Configuration
-
-### How to change providers
-
-Open `site/.env.local` and change the value of `COMMERCE_PROVIDER` to the provider you would like to use, then set the environment variables for that provider (use `site/.env.template` as the base).
-
-The setup for Shopify would look like this for example:
-
-```
-COMMERCE_PROVIDER=@vercel/commerce-shopify
-NEXT_PUBLIC_SHOPIFY_STOREFRONT_ACCESS_TOKEN=xxxxxxxxxxxxxxxxxxxxxxxxxxxx
-NEXT_PUBLIC_SHOPIFY_STORE_DOMAIN=xxxxxxx.myshopify.com
-```
-
-### Features
-
-Every provider defines the features that it supports under `packages/{provider}/src/commerce.config.json`
-
-#### Features Available
-
-The following features can be enabled or disabled. This means that the UI will remove all code related to the feature.
-For example: turning `cart` off will disable Cart capabilities.
-
-- cart
-- search
-- wishlist
-- customerAuth
-- customCheckout
+- Next.js App Router
+- Optimized for SEO
+- Styling with Tailwind CSS
+- Checkout/Cart with Shopify
+- Themeing with System (Light/Dark Mode) Support
-#### How to turn Features on and off
-
-> NOTE: The selected provider should support the feature that you are toggling. (This means that you can't turn wishlist on if the provider doesn't support this functionality out of the box)
-
-- Open `site/commerce.config.json`
-- You'll see a config file like this:
- ```json
- {
- "features": {
- "wishlist": false,
- "customCheckout": true
- }
- }
- ```
-- Turn `wishlist` on by setting `wishlist` to `true`.
-- Run the app and the wishlist functionality should be back on.
-
-### How to create a new provider
-
-Follow our docs for [Adding a new Commerce Provider](packages/commerce/new-provider.md).
-
-If you succeeded building a provider, submit a PR with a valid demo and we'll review it asap.
-
-## Contribute
-
-Our commitment to Open Source can be found [here](https://vercel.com/oss).
-
-1. [Fork](https://help.github.com/articles/fork-a-repo/) this repository to your own GitHub account and then [clone](https://help.github.com/articles/cloning-a-repository/) it to your local device.
-2. Create a new branch `git checkout -b MY_BRANCH_NAME`
-3. Install the dependencies: `pnpm install`
-4. Build the packages: `pnpm build`
-5. Duplicate `site/.env.template` and rename it to `site/.env.local`
-6. Add proper store values to `site/.env.local`
-7. Run `cd site` & `pnpm dev` to watch for code changes
-8. Run `pnpm turbo run build` to check the build after your changes
-
-## Work in progress
-
-We're using Github Projects to keep track of issues in progress and todo's. Here is our [Board](https://github.com/vercel/commerce/projects/1)
-
-People actively working on this project: @okbel, @lfades, @dominiksipowicz, @gbibeaul.
-
-## Troubleshoot
-
-
-I already own a BigCommerce store. What should I do?
-
-First thing you do is: set your environment variables
-
-
-.env.local
-
-```sh
-BIGCOMMERCE_STOREFRONT_API_URL=<>
-BIGCOMMERCE_STOREFRONT_API_TOKEN=<>
-BIGCOMMERCE_STORE_API_URL=<>
-BIGCOMMERCE_STORE_API_TOKEN=<>
-BIGCOMMERCE_STORE_API_CLIENT_ID=<>
-BIGCOMMERCE_CHANNEL_ID=<>
-```
-
-If your project was started with a "Deploy with Vercel" button, you can use Vercel's CLI to retrieve these credentials.
+## Running Locally
1. Install Vercel CLI: `npm i -g vercel`
2. Link local instance with Vercel and Github accounts (creates .vercel file): `vercel link`
3. Download your environment variables: `vercel env pull .env.local`
-Next, you're free to customize the starter. More updates coming soon. Stay tuned..
-
-
-
-
-BigCommerce shows a Coming Soon page and requests a Preview Code
-
-After Email confirmation, Checkout should be manually enabled through BigCommerce platform. Look for "Review & test your store" section through BigCommerce's dashboard.
-
-
-BigCommerce team has been notified and they plan to add more details about this subject.
-
-
-
-When run locally I get `Error: Cannot find module '...@vercel/commerce/dist/config'`
```bash
-commerce/site
-❯ yarn dev
-yarn run v1.22.17
-$ next dev
-ready - started server on 0.0.0.0:3000, url: http://localhost:3000
-info - Loaded env from /commerce/site/.env.local
-error - Failed to load next.config.js, see more info here https://nextjs.org/docs/messages/next-config-error
-Error: Cannot find module '/Users/dom/work/vercel/commerce/node_modules/@vercel/commerce/dist/config.cjs'
- at createEsmNotFoundErr (node:internal/modules/cjs/loader:960:15)
- at finalizeEsmResolution (node:internal/modules/cjs/loader:953:15)
- at resolveExports (node:internal/modules/cjs/loader:482:14)
- at Function.Module._findPath (node:internal/modules/cjs/loader:522:31)
- at Function.Module._resolveFilename (node:internal/modules/cjs/loader:919:27)
- at Function.mod._resolveFilename (/Users/dom/work/vercel/commerce/node_modules/next/dist/build/webpack/require-hook.js:179:28)
- at Function.Module._load (node:internal/modules/cjs/loader:778:27)
- at Module.require (node:internal/modules/cjs/loader:1005:19)
- at require (node:internal/modules/cjs/helpers:102:18)
- at Object. (/Users/dom/work/vercel/commerce/site/commerce-config.js:9:14) {
- code: 'MODULE_NOT_FOUND',
- path: '/Users/dom/work/vercel/commerce/node_modules/@vercel/commerce/package.json'
-}
-error Command failed with exit code 1.
-info Visit https://yarnpkg.com/en/docs/cli/run for documentation about this command.
+pnpm install
+pnpm dev
```
-The error usually occurs when running `pnpm dev` inside of the `/site/` folder after installing a fresh repository.
-
-In order to fix this, run `pnpm build` in the monorepo root folder first.
-
-> Using `pnpm dev` from the root is recommended for developing, which will run watch mode on all packages.
-
-
+Your app should now be running on [localhost:3000](http://localhost:3000/).
diff --git a/app/[page]/layout.tsx b/app/[page]/layout.tsx
new file mode 100644
index 0000000000..3339a5b1ad
--- /dev/null
+++ b/app/[page]/layout.tsx
@@ -0,0 +1,16 @@
+import Footer from 'components/layout/footer';
+import { Suspense } from 'react';
+
+export default function Layout({ children }: { children: React.ReactNode }) {
+ return (
+
+
+ );
+}
diff --git a/components/product/variant-selector.tsx b/components/product/variant-selector.tsx
new file mode 100644
index 0000000000..520818516f
--- /dev/null
+++ b/components/product/variant-selector.tsx
@@ -0,0 +1,135 @@
+'use client';
+
+import clsx from 'clsx';
+import { ProductOption, ProductVariant } from 'lib/shopify/types';
+import { createUrl } from 'lib/utils';
+import Link from 'next/link';
+import { usePathname, useRouter, useSearchParams } from 'next/navigation';
+
+type ParamsMap = {
+ [key: string]: string; // ie. { color: 'Red', size: 'Large', ... }
+};
+
+type OptimizedVariant = {
+ id: string;
+ availableForSale: boolean;
+ params: URLSearchParams;
+ [key: string]: string | boolean | URLSearchParams; // ie. { color: 'Red', size: 'Large', ... }
+};
+
+export function VariantSelector({
+ options,
+ variants
+}: {
+ options: ProductOption[];
+ variants: ProductVariant[];
+}) {
+ const pathname = usePathname();
+ const currentParams = useSearchParams();
+ const router = useRouter();
+ const hasNoOptionsOrJustOneOption =
+ !options.length || (options.length === 1 && options[0]?.values.length === 1);
+
+ if (hasNoOptionsOrJustOneOption) {
+ return null;
+ }
+
+ // Discard any unexpected options or values from url and create params map.
+ const paramsMap: ParamsMap = Object.fromEntries(
+ Array.from(currentParams.entries()).filter(([key, value]) =>
+ options.find((option) => option.name.toLowerCase() === key && option.values.includes(value))
+ )
+ );
+
+ // Optimize variants for easier lookups.
+ const optimizedVariants: OptimizedVariant[] = variants.map((variant) => {
+ const optimized: OptimizedVariant = {
+ id: variant.id,
+ availableForSale: variant.availableForSale,
+ params: new URLSearchParams()
+ };
+
+ variant.selectedOptions.forEach((selectedOption) => {
+ const name = selectedOption.name.toLowerCase();
+ const value = selectedOption.value;
+
+ optimized[name] = value;
+ optimized.params.set(name, value);
+ });
+
+ return optimized;
+ });
+
+ // Find the first variant that is:
+ //
+ // 1. Available for sale
+ // 2. Matches all options specified in the url (note that this
+ // could be a partial match if some options are missing from the url).
+ //
+ // If no match (full or partial) is found, use the first variant that is
+ // available for sale.
+ const selectedVariant: OptimizedVariant | undefined =
+ optimizedVariants.find(
+ (variant) =>
+ variant.availableForSale &&
+ Object.entries(paramsMap).every(([key, value]) => variant[key] === value)
+ ) || optimizedVariants.find((variant) => variant.availableForSale);
+
+ const selectedVariantParams = new URLSearchParams(selectedVariant?.params);
+ const currentUrl = createUrl(pathname, currentParams);
+ const selectedVariantUrl = createUrl(pathname, selectedVariantParams);
+
+ if (currentUrl !== selectedVariantUrl) {
+ router.replace(selectedVariantUrl);
+ }
+
+ return options.map((option) => (
+
+
{option.name}
+
+ {option.values.map((value) => {
+ // Base option params on selected variant params.
+ const optionParams = new URLSearchParams(selectedVariantParams);
+ // Update the params using the current option to reflect how the url would change.
+ optionParams.set(option.name.toLowerCase(), value);
+
+ const optionUrl = createUrl(pathname, optionParams);
+
+ // The option is active if it in the url params.
+ const isActive = selectedVariantParams.get(option.name.toLowerCase()) === value;
+
+ // The option is available for sale if it fully matches the variant in the option's url params.
+ // It's super important to note that this is the options params, *not* the selected variant's params.
+ // This is the "magic" that will cross check possible future variant combinations and preemptively
+ // disable combinations that are not possible.
+ const isAvailableForSale = optimizedVariants.find((a) =>
+ Array.from(optionParams.entries()).every(([key, value]) => a[key] === value)
+ )?.availableForSale;
+
+ const DynamicTag = isAvailableForSale ? Link : 'p';
+
+ return (
+
+ {value}
+
+ );
+ })}
+