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.
| 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 |
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)
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.
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
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.
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.
Install dependencies
npm installDevelopment (Chrome MV3 with HMR)
npm run devDevelopment (Firefox)
npm run dev:firefoxProduction build
npm run buildPackage for distribution
npm run zipLoad the unpacked extension in Chrome
- Open
chrome://extensions - Enable Developer mode
- Click Load unpacked and select
.output/chrome-mv3/
- VS Code + Volar (disable Vetur if installed)
- WebStorm / IntelliJ — Vue plugin included by default
Type-check without building:
npm run compile