judgeme-hydrogen-fixed 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2024 Ben Goodman
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,154 @@
1
+ # judgeme-hydrogen-fixed
2
+
3
+ A fixed version of the official `@judgeme/shopify-hydrogen` package for integrating Judge.me reviews into Shopify Hydrogen stores.
4
+
5
+ ## Why this package?
6
+
7
+ The official `@judgeme/shopify-hydrogen` package has a bug in its `useJudgeme` hook - it contains a `useEffect` with **no dependency array**, which causes it to run on every render. This can lead to:
8
+
9
+ - Infinite refresh loops
10
+ - Performance issues
11
+ - Memory leaks from uncleaned timeouts
12
+
13
+ This package provides a properly implemented version that:
14
+
15
+ - ✅ Loads Judge.me scripts only once on mount
16
+ - ✅ Re-renders widgets only on route changes (not every render)
17
+ - ✅ Properly cleans up timeouts to prevent memory leaks
18
+ - ✅ Includes proper TypeScript types
19
+
20
+ ## Installation
21
+
22
+ ```bash
23
+ npm install judgeme-hydrogen-fixed
24
+ ```
25
+
26
+ ## Usage
27
+
28
+ ### 1. Initialize Judge.me in your root component
29
+
30
+ ```tsx
31
+ // app/root.tsx
32
+ import { useJudgeme } from "judgeme-hydrogen-fixed";
33
+
34
+ export default function App() {
35
+ useJudgeme({
36
+ shopDomain: "your-store.myshopify.com",
37
+ publicToken: "your-judge-me-public-token",
38
+ cdnHost: "https://cdn.judge.me",
39
+ delay: 500, // optional, defaults to 500ms
40
+ });
41
+
42
+ return <Outlet />;
43
+ }
44
+ ```
45
+
46
+ ### 2. Add widgets to your product pages
47
+
48
+ ```tsx
49
+ // app/routes/products.$handle.tsx
50
+ import {
51
+ JudgemePreviewBadge,
52
+ JudgemeReviewWidget,
53
+ } from "judgeme-hydrogen-fixed";
54
+
55
+ function ProductPage({ product }) {
56
+ const productId = product.id.replace("gid://shopify/Product/", "");
57
+
58
+ return (
59
+ <div>
60
+ <h1>{product.title}</h1>
61
+
62
+ {/* Star rating badge - place near title */}
63
+ <JudgemePreviewBadge id={productId} template="product" />
64
+
65
+ {/* Product details... */}
66
+
67
+ {/* Full review widget - place at bottom */}
68
+ <JudgemeReviewWidget id={productId} />
69
+ </div>
70
+ );
71
+ }
72
+ ```
73
+
74
+ ## Available Components
75
+
76
+ | Component | Description |
77
+ | ------------------------- | ---------------------------------------- |
78
+ | `JudgemePreviewBadge` | Star rating badge for product pages |
79
+ | `JudgemeReviewWidget` | Full reviews list with write review form |
80
+ | `JudgemeCarousel` | Carousel of recent reviews |
81
+ | `JudgemeReviewsTab` | Reviews tab for tabbed layouts |
82
+ | `JudgemeAllReviewsRating` | Overall store rating |
83
+ | `JudgemeVerifiedBadge` | Verified store badge |
84
+ | `JudgemeAllReviewsCount` | Total review count |
85
+ | `JudgemeMedals` | Store medals/achievements |
86
+
87
+ ## Configuration
88
+
89
+ ### useJudgeme Options
90
+
91
+ | Option | Type | Required | Default | Description |
92
+ | ------------- | -------- | -------- | ------- | -------------------------------------------------- |
93
+ | `shopDomain` | `string` | Yes | - | Your Shopify store domain |
94
+ | `publicToken` | `string` | Yes | - | Your Judge.me public token |
95
+ | `cdnHost` | `string` | Yes | - | Judge.me CDN host (usually `https://cdn.judge.me`) |
96
+ | `delay` | `number` | No | `500` | Delay before re-rendering widgets on route change |
97
+
98
+ ## CSP Configuration
99
+
100
+ If you're using Content Security Policy, add these domains:
101
+
102
+ ```typescript
103
+ // entry.server.tsx
104
+ const scriptSrc = [
105
+ // ... other sources
106
+ "https://cdn.judge.me",
107
+ "https://cdnwidget.judge.me",
108
+ "https://*.judge.me",
109
+ ];
110
+
111
+ const connectSrc = [
112
+ // ... other sources
113
+ "https://cdn.judge.me",
114
+ "https://*.judge.me",
115
+ ];
116
+
117
+ const styleSrc = [
118
+ // ... other sources
119
+ "https://*.judge.me",
120
+ ];
121
+
122
+ const imgSrc = [
123
+ // ... other sources
124
+ "https://*.judge.me",
125
+ ];
126
+ ```
127
+
128
+ ## The Bug Fix Explained
129
+
130
+ The original package has this code:
131
+
132
+ ```javascript
133
+ // ❌ Original (buggy) - runs on EVERY render
134
+ useEffect(() => {
135
+ // Widget re-render logic...
136
+ }); // No dependency array!
137
+ ```
138
+
139
+ This package fixes it:
140
+
141
+ ```javascript
142
+ // ✅ Fixed - runs only on route changes
143
+ useEffect(() => {
144
+ // Widget re-render logic...
145
+ }, [location.pathname, location.search, delay]);
146
+ ```
147
+
148
+ ## License
149
+
150
+ MIT
151
+
152
+ ## Contributing
153
+
154
+ Issues and PRs welcome! If Judge.me fixes the official package, this one will be deprecated.
@@ -0,0 +1,132 @@
1
+ import React from 'react';
2
+
3
+ declare global {
4
+ interface Window {
5
+ jdgm?: {
6
+ SHOP_DOMAIN?: string;
7
+ PLATFORM?: string;
8
+ PUBLIC_TOKEN?: string;
9
+ productData?: unknown;
10
+ };
11
+ jdgm_preloader?: () => void;
12
+ jdgmCacheServer?: {
13
+ reloadAll: () => void;
14
+ };
15
+ jdgm_rerender?: ReturnType<typeof setTimeout>;
16
+ }
17
+ }
18
+ interface UseJudgemeConfig {
19
+ /**
20
+ * Your Shopify store domain (e.g., 'your-store.myshopify.com')
21
+ */
22
+ shopDomain: string;
23
+ /**
24
+ * Your Judge.me public token
25
+ */
26
+ publicToken: string;
27
+ /**
28
+ * Judge.me CDN host (usually 'https://cdn.judge.me')
29
+ */
30
+ cdnHost: string;
31
+ /**
32
+ * Delay in milliseconds before re-rendering widgets on route change
33
+ * @default 500
34
+ */
35
+ delay?: number;
36
+ }
37
+ interface JudgemePreviewBadgeProps {
38
+ /**
39
+ * Shopify product ID (numeric ID only, not the full GID)
40
+ */
41
+ id: string;
42
+ /**
43
+ * Template type
44
+ * @default 'product'
45
+ */
46
+ template?: 'product' | 'collection';
47
+ }
48
+ interface JudgemeReviewWidgetProps {
49
+ /**
50
+ * Shopify product ID (numeric ID only, not the full GID)
51
+ */
52
+ id: string;
53
+ }
54
+ interface JudgemeCarouselProps {
55
+ }
56
+ interface JudgemeReviewsTabProps {
57
+ }
58
+ interface JudgemeAllReviewsRatingProps {
59
+ }
60
+ interface JudgemeVerifiedBadgeProps {
61
+ }
62
+ interface JudgemeAllReviewsCountProps {
63
+ }
64
+ interface JudgemeMedalsProps {
65
+ }
66
+
67
+ /**
68
+ * Fixed version of useJudgeme that properly handles React's render cycle.
69
+ *
70
+ * The original @judgeme/shopify-hydrogen package has a useEffect with no
71
+ * dependency array that runs on every render, causing infinite refresh loops.
72
+ *
73
+ * This fixed version:
74
+ * 1. Loads Judge.me scripts only once on mount
75
+ * 2. Re-renders widgets only on route changes (not every render)
76
+ * 3. Properly cleans up timeouts to prevent memory leaks
77
+ *
78
+ * @example
79
+ * ```tsx
80
+ * // In your root.tsx or App component
81
+ * import { useJudgeme } from '@judgeme/hydrogen-fixed';
82
+ *
83
+ * function App() {
84
+ * useJudgeme({
85
+ * shopDomain: 'your-store.myshopify.com',
86
+ * publicToken: 'your-public-token',
87
+ * cdnHost: 'https://cdn.judge.me',
88
+ * delay: 500, // optional, defaults to 500ms
89
+ * });
90
+ *
91
+ * return <Outlet />;
92
+ * }
93
+ * ```
94
+ */
95
+ declare function useJudgeme({ shopDomain, publicToken, cdnHost, delay, }: UseJudgemeConfig): void;
96
+
97
+ /**
98
+ * Preview badge showing star rating for a product
99
+ * Place this near the product title on product pages
100
+ */
101
+ declare function JudgemePreviewBadge({ id, template, }: JudgemePreviewBadgeProps): React.ReactElement;
102
+ /**
103
+ * Full review widget with reviews list and write review form
104
+ * Place this at the bottom of product pages
105
+ */
106
+ declare function JudgemeReviewWidget({ id, }: JudgemeReviewWidgetProps): React.ReactElement;
107
+ /**
108
+ * Carousel showing recent reviews across all products
109
+ */
110
+ declare function JudgemeCarousel(): React.ReactElement;
111
+ /**
112
+ * Tab showing all reviews (typically used in a tabbed layout)
113
+ */
114
+ declare function JudgemeReviewsTab(): React.ReactElement;
115
+ /**
116
+ * Shows the overall rating across all products
117
+ */
118
+ declare function JudgemeAllReviewsRating(): React.ReactElement;
119
+ /**
120
+ * Verified badge for social proof
121
+ */
122
+ declare function JudgemeVerifiedBadge(): React.ReactElement;
123
+ /**
124
+ * Shows the total review count across all products
125
+ */
126
+ declare function JudgemeAllReviewsCount(): React.ReactElement;
127
+ /**
128
+ * Displays Judge.me medals/badges earned by the store
129
+ */
130
+ declare function JudgemeMedals(): React.ReactElement;
131
+
132
+ export { JudgemeAllReviewsCount, type JudgemeAllReviewsCountProps, JudgemeAllReviewsRating, type JudgemeAllReviewsRatingProps, JudgemeCarousel, type JudgemeCarouselProps, JudgemeMedals, type JudgemeMedalsProps, JudgemePreviewBadge, type JudgemePreviewBadgeProps, JudgemeReviewWidget, type JudgemeReviewWidgetProps, JudgemeReviewsTab, type JudgemeReviewsTabProps, JudgemeVerifiedBadge, type JudgemeVerifiedBadgeProps, type UseJudgemeConfig, useJudgeme };
@@ -0,0 +1,132 @@
1
+ import React from 'react';
2
+
3
+ declare global {
4
+ interface Window {
5
+ jdgm?: {
6
+ SHOP_DOMAIN?: string;
7
+ PLATFORM?: string;
8
+ PUBLIC_TOKEN?: string;
9
+ productData?: unknown;
10
+ };
11
+ jdgm_preloader?: () => void;
12
+ jdgmCacheServer?: {
13
+ reloadAll: () => void;
14
+ };
15
+ jdgm_rerender?: ReturnType<typeof setTimeout>;
16
+ }
17
+ }
18
+ interface UseJudgemeConfig {
19
+ /**
20
+ * Your Shopify store domain (e.g., 'your-store.myshopify.com')
21
+ */
22
+ shopDomain: string;
23
+ /**
24
+ * Your Judge.me public token
25
+ */
26
+ publicToken: string;
27
+ /**
28
+ * Judge.me CDN host (usually 'https://cdn.judge.me')
29
+ */
30
+ cdnHost: string;
31
+ /**
32
+ * Delay in milliseconds before re-rendering widgets on route change
33
+ * @default 500
34
+ */
35
+ delay?: number;
36
+ }
37
+ interface JudgemePreviewBadgeProps {
38
+ /**
39
+ * Shopify product ID (numeric ID only, not the full GID)
40
+ */
41
+ id: string;
42
+ /**
43
+ * Template type
44
+ * @default 'product'
45
+ */
46
+ template?: 'product' | 'collection';
47
+ }
48
+ interface JudgemeReviewWidgetProps {
49
+ /**
50
+ * Shopify product ID (numeric ID only, not the full GID)
51
+ */
52
+ id: string;
53
+ }
54
+ interface JudgemeCarouselProps {
55
+ }
56
+ interface JudgemeReviewsTabProps {
57
+ }
58
+ interface JudgemeAllReviewsRatingProps {
59
+ }
60
+ interface JudgemeVerifiedBadgeProps {
61
+ }
62
+ interface JudgemeAllReviewsCountProps {
63
+ }
64
+ interface JudgemeMedalsProps {
65
+ }
66
+
67
+ /**
68
+ * Fixed version of useJudgeme that properly handles React's render cycle.
69
+ *
70
+ * The original @judgeme/shopify-hydrogen package has a useEffect with no
71
+ * dependency array that runs on every render, causing infinite refresh loops.
72
+ *
73
+ * This fixed version:
74
+ * 1. Loads Judge.me scripts only once on mount
75
+ * 2. Re-renders widgets only on route changes (not every render)
76
+ * 3. Properly cleans up timeouts to prevent memory leaks
77
+ *
78
+ * @example
79
+ * ```tsx
80
+ * // In your root.tsx or App component
81
+ * import { useJudgeme } from '@judgeme/hydrogen-fixed';
82
+ *
83
+ * function App() {
84
+ * useJudgeme({
85
+ * shopDomain: 'your-store.myshopify.com',
86
+ * publicToken: 'your-public-token',
87
+ * cdnHost: 'https://cdn.judge.me',
88
+ * delay: 500, // optional, defaults to 500ms
89
+ * });
90
+ *
91
+ * return <Outlet />;
92
+ * }
93
+ * ```
94
+ */
95
+ declare function useJudgeme({ shopDomain, publicToken, cdnHost, delay, }: UseJudgemeConfig): void;
96
+
97
+ /**
98
+ * Preview badge showing star rating for a product
99
+ * Place this near the product title on product pages
100
+ */
101
+ declare function JudgemePreviewBadge({ id, template, }: JudgemePreviewBadgeProps): React.ReactElement;
102
+ /**
103
+ * Full review widget with reviews list and write review form
104
+ * Place this at the bottom of product pages
105
+ */
106
+ declare function JudgemeReviewWidget({ id, }: JudgemeReviewWidgetProps): React.ReactElement;
107
+ /**
108
+ * Carousel showing recent reviews across all products
109
+ */
110
+ declare function JudgemeCarousel(): React.ReactElement;
111
+ /**
112
+ * Tab showing all reviews (typically used in a tabbed layout)
113
+ */
114
+ declare function JudgemeReviewsTab(): React.ReactElement;
115
+ /**
116
+ * Shows the overall rating across all products
117
+ */
118
+ declare function JudgemeAllReviewsRating(): React.ReactElement;
119
+ /**
120
+ * Verified badge for social proof
121
+ */
122
+ declare function JudgemeVerifiedBadge(): React.ReactElement;
123
+ /**
124
+ * Shows the total review count across all products
125
+ */
126
+ declare function JudgemeAllReviewsCount(): React.ReactElement;
127
+ /**
128
+ * Displays Judge.me medals/badges earned by the store
129
+ */
130
+ declare function JudgemeMedals(): React.ReactElement;
131
+
132
+ export { JudgemeAllReviewsCount, type JudgemeAllReviewsCountProps, JudgemeAllReviewsRating, type JudgemeAllReviewsRatingProps, JudgemeCarousel, type JudgemeCarouselProps, JudgemeMedals, type JudgemeMedalsProps, JudgemePreviewBadge, type JudgemePreviewBadgeProps, JudgemeReviewWidget, type JudgemeReviewWidgetProps, JudgemeReviewsTab, type JudgemeReviewsTabProps, JudgemeVerifiedBadge, type JudgemeVerifiedBadgeProps, type UseJudgemeConfig, useJudgeme };
package/dist/index.js ADDED
@@ -0,0 +1,161 @@
1
+ "use strict";
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __export = (target, all) => {
7
+ for (var name in all)
8
+ __defProp(target, name, { get: all[name], enumerable: true });
9
+ };
10
+ var __copyProps = (to, from, except, desc) => {
11
+ if (from && typeof from === "object" || typeof from === "function") {
12
+ for (let key of __getOwnPropNames(from))
13
+ if (!__hasOwnProp.call(to, key) && key !== except)
14
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
15
+ }
16
+ return to;
17
+ };
18
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
+
20
+ // src/index.ts
21
+ var index_exports = {};
22
+ __export(index_exports, {
23
+ JudgemeAllReviewsCount: () => JudgemeAllReviewsCount,
24
+ JudgemeAllReviewsRating: () => JudgemeAllReviewsRating,
25
+ JudgemeCarousel: () => JudgemeCarousel,
26
+ JudgemeMedals: () => JudgemeMedals,
27
+ JudgemePreviewBadge: () => JudgemePreviewBadge,
28
+ JudgemeReviewWidget: () => JudgemeReviewWidget,
29
+ JudgemeReviewsTab: () => JudgemeReviewsTab,
30
+ JudgemeVerifiedBadge: () => JudgemeVerifiedBadge,
31
+ useJudgeme: () => useJudgeme
32
+ });
33
+ module.exports = __toCommonJS(index_exports);
34
+
35
+ // src/useJudgeme.ts
36
+ var import_react = require("react");
37
+ var import_react2 = require("@remix-run/react");
38
+ function useJudgeme({
39
+ shopDomain,
40
+ publicToken,
41
+ cdnHost,
42
+ delay = 500
43
+ }) {
44
+ const location = (0, import_react2.useLocation)();
45
+ const isLoadedRef = (0, import_react.useRef)(false);
46
+ const rerenderTimeoutRef = (0, import_react.useRef)(null);
47
+ (0, import_react.useEffect)(() => {
48
+ if (isLoadedRef.current) return;
49
+ if (!shopDomain || !publicToken || !cdnHost) {
50
+ console.warn(
51
+ "Judge.me: Missing config values for store domain, store public token, or cdn host"
52
+ );
53
+ return;
54
+ }
55
+ const shopCredentials = `
56
+ if (typeof window.jdgm === 'undefined') {
57
+ window.jdgm = {};
58
+ window.jdgm.SHOP_DOMAIN = '${shopDomain}';
59
+ window.jdgm.PLATFORM = 'shopify';
60
+ window.jdgm.PUBLIC_TOKEN = '${publicToken}';
61
+ }
62
+ `;
63
+ fetch(`${cdnHost}/widget_preloader.js`).then((res) => {
64
+ if (!res.ok) throw new Error(`HTTP ${res.status}`);
65
+ return res.text();
66
+ }).then((text) => {
67
+ const preloaderFunction = `function jdgm_preloader(){${text}}`;
68
+ const shopCredentialsScript = document.createElement("script");
69
+ const preloaderScript = document.createElement("script");
70
+ const installedScript = document.createElement("script");
71
+ shopCredentialsScript.innerText = shopCredentials;
72
+ preloaderScript.innerText = preloaderFunction;
73
+ installedScript.src = `${cdnHost}/assets/installed.js`;
74
+ document.head.append(
75
+ shopCredentialsScript,
76
+ preloaderScript,
77
+ installedScript
78
+ );
79
+ isLoadedRef.current = true;
80
+ console.log("Judge.me scripts loaded");
81
+ }).catch((error) => {
82
+ console.error("Judge.me: Failed to load scripts", error);
83
+ });
84
+ return () => {
85
+ if (rerenderTimeoutRef.current) {
86
+ clearTimeout(rerenderTimeoutRef.current);
87
+ }
88
+ };
89
+ }, [shopDomain, publicToken, cdnHost]);
90
+ (0, import_react.useEffect)(() => {
91
+ if (rerenderTimeoutRef.current) {
92
+ clearTimeout(rerenderTimeoutRef.current);
93
+ }
94
+ rerenderTimeoutRef.current = setTimeout(() => {
95
+ if (window.jdgm_preloader && !window.jdgmCacheServer) {
96
+ window.jdgm_preloader();
97
+ } else if (window.jdgmCacheServer) {
98
+ window.jdgmCacheServer.reloadAll();
99
+ }
100
+ }, delay);
101
+ return () => {
102
+ if (rerenderTimeoutRef.current) {
103
+ clearTimeout(rerenderTimeoutRef.current);
104
+ }
105
+ };
106
+ }, [location.pathname, location.search, delay]);
107
+ }
108
+
109
+ // src/components.tsx
110
+ var import_jsx_runtime = require("react/jsx-runtime");
111
+ function JudgemePreviewBadge({
112
+ id,
113
+ template = "product"
114
+ }) {
115
+ const shopifyId = id ? id.replace("gid://shopify/Product/", "") : "";
116
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
117
+ "div",
118
+ {
119
+ className: "jdgm-widget jdgm-preview-badge",
120
+ "data-id": shopifyId,
121
+ "data-template": template,
122
+ "data-auto-install": "false"
123
+ }
124
+ );
125
+ }
126
+ function JudgemeReviewWidget({
127
+ id
128
+ }) {
129
+ const shopifyId = id ? id.replace("gid://shopify/Product/", "") : "";
130
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "jdgm-widget jdgm-review-widget", "data-id": shopifyId });
131
+ }
132
+ function JudgemeCarousel() {
133
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "jdgm-widget jdgm-carousel-widget" });
134
+ }
135
+ function JudgemeReviewsTab() {
136
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "jdgm-widget jdgm-reviews-tab" });
137
+ }
138
+ function JudgemeAllReviewsRating() {
139
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "jdgm-widget jdgm-all-reviews-rating" });
140
+ }
141
+ function JudgemeVerifiedBadge() {
142
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "jdgm-widget jdgm-verified-badge" });
143
+ }
144
+ function JudgemeAllReviewsCount() {
145
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "jdgm-widget jdgm-all-reviews-count" });
146
+ }
147
+ function JudgemeMedals() {
148
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "jdgm-widget jdgm-medals-wrapper" });
149
+ }
150
+ // Annotate the CommonJS export names for ESM import in node:
151
+ 0 && (module.exports = {
152
+ JudgemeAllReviewsCount,
153
+ JudgemeAllReviewsRating,
154
+ JudgemeCarousel,
155
+ JudgemeMedals,
156
+ JudgemePreviewBadge,
157
+ JudgemeReviewWidget,
158
+ JudgemeReviewsTab,
159
+ JudgemeVerifiedBadge,
160
+ useJudgeme
161
+ });
package/dist/index.mjs ADDED
@@ -0,0 +1,126 @@
1
+ // src/useJudgeme.ts
2
+ import { useEffect, useRef } from "react";
3
+ import { useLocation } from "@remix-run/react";
4
+ function useJudgeme({
5
+ shopDomain,
6
+ publicToken,
7
+ cdnHost,
8
+ delay = 500
9
+ }) {
10
+ const location = useLocation();
11
+ const isLoadedRef = useRef(false);
12
+ const rerenderTimeoutRef = useRef(null);
13
+ useEffect(() => {
14
+ if (isLoadedRef.current) return;
15
+ if (!shopDomain || !publicToken || !cdnHost) {
16
+ console.warn(
17
+ "Judge.me: Missing config values for store domain, store public token, or cdn host"
18
+ );
19
+ return;
20
+ }
21
+ const shopCredentials = `
22
+ if (typeof window.jdgm === 'undefined') {
23
+ window.jdgm = {};
24
+ window.jdgm.SHOP_DOMAIN = '${shopDomain}';
25
+ window.jdgm.PLATFORM = 'shopify';
26
+ window.jdgm.PUBLIC_TOKEN = '${publicToken}';
27
+ }
28
+ `;
29
+ fetch(`${cdnHost}/widget_preloader.js`).then((res) => {
30
+ if (!res.ok) throw new Error(`HTTP ${res.status}`);
31
+ return res.text();
32
+ }).then((text) => {
33
+ const preloaderFunction = `function jdgm_preloader(){${text}}`;
34
+ const shopCredentialsScript = document.createElement("script");
35
+ const preloaderScript = document.createElement("script");
36
+ const installedScript = document.createElement("script");
37
+ shopCredentialsScript.innerText = shopCredentials;
38
+ preloaderScript.innerText = preloaderFunction;
39
+ installedScript.src = `${cdnHost}/assets/installed.js`;
40
+ document.head.append(
41
+ shopCredentialsScript,
42
+ preloaderScript,
43
+ installedScript
44
+ );
45
+ isLoadedRef.current = true;
46
+ console.log("Judge.me scripts loaded");
47
+ }).catch((error) => {
48
+ console.error("Judge.me: Failed to load scripts", error);
49
+ });
50
+ return () => {
51
+ if (rerenderTimeoutRef.current) {
52
+ clearTimeout(rerenderTimeoutRef.current);
53
+ }
54
+ };
55
+ }, [shopDomain, publicToken, cdnHost]);
56
+ useEffect(() => {
57
+ if (rerenderTimeoutRef.current) {
58
+ clearTimeout(rerenderTimeoutRef.current);
59
+ }
60
+ rerenderTimeoutRef.current = setTimeout(() => {
61
+ if (window.jdgm_preloader && !window.jdgmCacheServer) {
62
+ window.jdgm_preloader();
63
+ } else if (window.jdgmCacheServer) {
64
+ window.jdgmCacheServer.reloadAll();
65
+ }
66
+ }, delay);
67
+ return () => {
68
+ if (rerenderTimeoutRef.current) {
69
+ clearTimeout(rerenderTimeoutRef.current);
70
+ }
71
+ };
72
+ }, [location.pathname, location.search, delay]);
73
+ }
74
+
75
+ // src/components.tsx
76
+ import { jsx } from "react/jsx-runtime";
77
+ function JudgemePreviewBadge({
78
+ id,
79
+ template = "product"
80
+ }) {
81
+ const shopifyId = id ? id.replace("gid://shopify/Product/", "") : "";
82
+ return /* @__PURE__ */ jsx(
83
+ "div",
84
+ {
85
+ className: "jdgm-widget jdgm-preview-badge",
86
+ "data-id": shopifyId,
87
+ "data-template": template,
88
+ "data-auto-install": "false"
89
+ }
90
+ );
91
+ }
92
+ function JudgemeReviewWidget({
93
+ id
94
+ }) {
95
+ const shopifyId = id ? id.replace("gid://shopify/Product/", "") : "";
96
+ return /* @__PURE__ */ jsx("div", { className: "jdgm-widget jdgm-review-widget", "data-id": shopifyId });
97
+ }
98
+ function JudgemeCarousel() {
99
+ return /* @__PURE__ */ jsx("div", { className: "jdgm-widget jdgm-carousel-widget" });
100
+ }
101
+ function JudgemeReviewsTab() {
102
+ return /* @__PURE__ */ jsx("div", { className: "jdgm-widget jdgm-reviews-tab" });
103
+ }
104
+ function JudgemeAllReviewsRating() {
105
+ return /* @__PURE__ */ jsx("div", { className: "jdgm-widget jdgm-all-reviews-rating" });
106
+ }
107
+ function JudgemeVerifiedBadge() {
108
+ return /* @__PURE__ */ jsx("div", { className: "jdgm-widget jdgm-verified-badge" });
109
+ }
110
+ function JudgemeAllReviewsCount() {
111
+ return /* @__PURE__ */ jsx("div", { className: "jdgm-widget jdgm-all-reviews-count" });
112
+ }
113
+ function JudgemeMedals() {
114
+ return /* @__PURE__ */ jsx("div", { className: "jdgm-widget jdgm-medals-wrapper" });
115
+ }
116
+ export {
117
+ JudgemeAllReviewsCount,
118
+ JudgemeAllReviewsRating,
119
+ JudgemeCarousel,
120
+ JudgemeMedals,
121
+ JudgemePreviewBadge,
122
+ JudgemeReviewWidget,
123
+ JudgemeReviewsTab,
124
+ JudgemeVerifiedBadge,
125
+ useJudgeme
126
+ };
package/package.json ADDED
@@ -0,0 +1,48 @@
1
+ {
2
+ "name": "judgeme-hydrogen-fixed",
3
+ "version": "1.0.0",
4
+ "description": "Fixed version of @judgeme/shopify-hydrogen with proper React hooks implementation. Fixes infinite refresh loop bug.",
5
+ "main": "./dist/index.js",
6
+ "module": "./dist/index.mjs",
7
+ "types": "./dist/index.d.ts",
8
+ "exports": {
9
+ ".": {
10
+ "types": "./dist/index.d.ts",
11
+ "import": "./dist/index.mjs",
12
+ "require": "./dist/index.js"
13
+ }
14
+ },
15
+ "files": [
16
+ "dist",
17
+ "README.md",
18
+ "LICENSE"
19
+ ],
20
+ "scripts": {
21
+ "build": "tsup src/index.ts --format cjs,esm --dts --clean",
22
+ "dev": "tsup src/index.ts --format cjs,esm --dts --watch",
23
+ "prepublishOnly": "npm run build"
24
+ },
25
+ "keywords": [
26
+ "judgeme",
27
+ "judge.me",
28
+ "shopify",
29
+ "hydrogen",
30
+ "react",
31
+ "reviews",
32
+ "remix",
33
+ "bug-fix"
34
+ ],
35
+ "author": "Ben Goodman",
36
+ "license": "MIT",
37
+ "peerDependencies": {
38
+ "react": ">=18.0.0",
39
+ "@remix-run/react": ">=2.0.0"
40
+ },
41
+ "devDependencies": {
42
+ "@types/react": "^18.2.0",
43
+ "react": "^18.2.0",
44
+ "@remix-run/react": "^2.0.0",
45
+ "tsup": "^8.0.0",
46
+ "typescript": "^5.0.0"
47
+ }
48
+ }