Skip to content

Repository files navigation

SyncMaster

A Chrome browser extension that extracts product data from Amazon and exports it to Shopify. Built with WXT (Web Extension Toolkit), Vue 3, and TypeScript.


Tech Stack

Tool Purpose
WXT Web extension framework (build, HMR, manifest generation)
Vue 3 + Composition API UI layer
TypeScript Type safety across the entire codebase
Pinia Reactive state management across extension contexts
TailwindCSS v4 Utility-first styling
PrimeVue UI component library (Aura theme)
Chrome MV3 Extension API target

Project Structure

vue-shopify-amazon/
├── entrypoints/               # WXT entrypoints — each folder/file maps to an extension page or script
│   ├── background.ts          # Service worker: routes chrome.runtime messages, manages side panel
│   ├── content.ts             # Content script: injected into Amazon pages, mounts Vue UI components
│   ├── styling.css            # Global CSS shared by the content script
│   ├── popup/                 # Extension popup (toolbar icon click)
│   │   ├── App.vue            # Root component — detects Amazon tab, triggers product extraction
│   │   ├── main.ts            # Popup Vue app bootstrap
│   │   ├── index.html         # Popup HTML shell
│   │   └── popup.css          # Popup-specific styles
│   └── sidepanel/             # Chrome side panel UI
│       ├── App.vue            # Root component — displays extracted product via router
│       ├── main.ts            # Side panel Vue app bootstrap
│       ├── index.html         # Side panel HTML shell
│       └── style.css          # Side panel styles
│
├── components/                # Shared Vue components
│   ├── Popup.vue              # Popup layout (product preview + store selector + actions)
│   ├── SidePanel.vue          # Side panel layout (product display + store meta)
│   ├── elements/              # Reusable leaf components
│   │   ├── Header.vue         # Extension header bar
│   │   ├── ProductPreview.vue # Product card (image, title, category, price)
│   │   ├── Stores.vue         # Export target selector (Shopify badge)
│   │   ├── CollectionButton.vue   # Add-to-collection action button
│   │   ├── AmazonButton.vue   # Inject button rendered on Amazon product pages
│   │   ├── DashboardStats.vue # Stats overlay injected into Amazon DOM
│   │   ├── StoreMeta.vue      # Shopify store metadata display
│   │   ├── Stats.vue          # Sync statistics
│   │   ├── NotSupported.vue   # Fallback when on an unsupported page
│   │   └── dashboard-ui/
│   │       ├── AddProduct.vue     # Form to push a product to Shopify
│   │       └── ListProducts.vue   # List of collected/synced products
│   └── ui/
│       ├── Dashboard.vue          # Dashboard overlay component
│       ├── HeaderAccordion.vue    # Collapsible header section
│       └── SyncMasterToolbar.vue  # Floating toolbar injected into Amazon pages
│
├── core/                      # Business logic — no Vue dependencies
│   ├── extractors/
│   │   ├── AbstractExtractor.ts   # Base class: shared fields (title, price, images…) + interface
│   │   ├── AmazonExtractor.ts     # Scrapes Amazon product pages via DOM selectors
│   │   └── TemuExtractor.ts       # Scrapes Temu product pages (secondary source)
│   ├── entity/
│   │   └── ShopifyRecord.ts       # Maps extracted product data to the Shopify CSV/API schema
│   ├── shopify/columns/
│   │   ├── shopify.interface.ts   # IShopifyColumnInterface (validate + transform contract)
│   │   ├── HandleColumn.ts        # Transforms the Shopify "Handle" column
│   │   └── HandleTags.ts          # Transforms the Shopify "Tags" column
│   └── queue/
│       └── QueueLoop.ts           # Async queue for batched sync operations
│
├── stores/                    # Pinia stores (shared across popup, sidepanel, and content script)
│   ├── current-product.store.ts       # Active product state; persists to chrome.storage.local
│   ├── amazon_collections.store.ts    # List of all scraped Amazon products
│   ├── dashboard.store.ts             # UI state: dashboard open/close, list view toggle
│   └── plugins/
│       └── chromeLocalStorage.ts      # Pinia plugin that syncs store state to chrome.storage.local
│
├── pages/
│   └── HomeView.vue           # Vue Router page (used inside sidepanel router)
│
├── interaces/                 # Shared TypeScript interfaces
│   └── interface.ts           # AmazonProductPage, Variant, StoreMetaProps, etc.
│
├── utils/                     # Pure utility functions
│   ├── helper.ts              # DOM helpers (q, dom), Chrome tab/message utilities, store helpers
│   └── amazon-helper.ts       # Amazon-specific helpers: isOnAmazonPage, convertImageSize
│
├── assets/                    # Static assets bundled by WXT
│   └── icons/                 # Extension icon set (16, 32, 48, 128 px)
│
├── public/                    # Copied verbatim into the build output
│   ├── icon/                  # Icon variants for the manifest
│   └── images/                # SVG/PNG assets referenced at runtime via chrome.runtime.getURL
│
├── wxt.config.ts              # WXT configuration: manifest, modules, Vite plugins
├── tailwind.config.js         # Tailwind configuration
├── tsconfig.json              # TypeScript configuration
└── web-ext.config.ts          # web-ext runner configuration (Firefox dev target)

How WXT Structures This Extension

WXT uses file-system routing for extension entrypoints. Every file or folder inside entrypoints/ becomes a distinct extension context:

Entrypoint Extension context When it runs
entrypoints/background.ts Service worker Always running in the background
entrypoints/content.ts Content script Injected into amazon.com, amazon.ca, amazon.co.uk pages
entrypoints/popup/ Popup page When the user clicks the toolbar icon
entrypoints/sidepanel/ Side panel Opened programmatically via chrome.sidePanel.open()

WXT auto-generates the manifest.json from wxt.config.ts — no manual manifest editing needed.


Data Flow

Amazon product page (DOM)
        │
        ▼
  content.ts (ContentScript)
        │  mounts Vue components into the page
        │  listens for chrome.runtime messages
        │
        ├──► AmazonExtractor.extractFullData()
        │         scrapes DOM selectors → returns product JSON
        │
        ├──► currentProductStore.$patch(data)
        │         persists to chrome.storage.local via saveCurrentProduct()
        │
        └──► amazonCollectionsStore.addCollection(data)
                  appends to chrome.storage.local["amazon_collections"]

background.ts (Service Worker)
        │  receives messages from popup/content
        │
        ├──► chrome.sidePanel.open()    (on "extract_information" or "display_product")
        └──► sendToContentScript()      (on "amazon_product_page")

popup (Popup page)
        │  on mount: queries active tab URL
        │  sends "amazon_product_page" message → content script responds
        └──► renders ProductPreview + Stores + CollectionButton

sidepanel (Side Panel page)
        │  on mount: calls sendToContentScript({type:"get_product_data"})
        └──► renders full product detail from amazonCollectionsStore

State Management

All Pinia stores survive across extension contexts because they read/write through chrome.storage.local:

Store Key Purpose
useCurrentProductStore currentProduct The product currently being viewed
useAmazonCollectionsStore amazon_collections All products the user has collected
useDashboardStore (in-memory only) UI toggle state for the injected dashboard

The chromeLocalStorage.ts Pinia plugin provides an optional generic persistence strategy for any store that opts in.


Core Extractor Architecture

Extractor (AbstractExtractor.ts)
    └── AmazonExtractor
            ├── extractTitle()       → #productTitle
            ├── extractImages()      → #altImages ul li span img
            ├── extractFeaturedImages() → #imgTagWrapperId > img
            ├── extractCategories()  → ul.a-unordered-list.a-size-small li
            ├── extractPrices()      → #centerCol .a-price
            ├── extractVendor()      → a#sellerProfileTriggerId
            ├── extractDescription() → #productDescription_feature_div
            ├── extractProductDetails() → #detailBullets_feature_div
            └── extractProductInformation() → #productDetails_techSpec_section_1

ShopifyRecord maps the extractor output to the exact column schema expected by Shopify's product CSV import format.


Getting Started

Install dependencies

npm install

Development (Chrome MV3 with HMR)

npm run dev

Development (Firefox)

npm run dev:firefox

Production build

npm run build

Package for distribution

npm run zip

Load the unpacked extension in Chrome

  1. Open chrome://extensions
  2. Enable Developer mode
  3. Click Load unpacked and select .output/chrome-mv3/

IDE Setup

  • VS Code + Volar (disable Vetur if installed)
  • WebStorm / IntelliJ — Vue plugin included by default

Type-check without building:

npm run compile

About

No description, website, or topics provided.

Resources

Stars

Watchers

Forks

Releases

Packages

Contributors

Languages