@anker-in/lib 1.0.0-beta.5 → 1.0.0-beta.6

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/package.json CHANGED
@@ -1,12 +1,33 @@
1
1
  {
2
2
  "name": "@anker-in/lib",
3
- "version": "1.0.0-beta.5",
3
+ "version": "1.0.0-beta.6",
4
+ "description": "Convenience package that re-exports all Anker headless packages",
4
5
  "main": "dist/index.js",
6
+ "module": "dist/index.mjs",
5
7
  "types": "dist/index.d.ts",
8
+ "files": [
9
+ "dist"
10
+ ],
11
+ "exports": {
12
+ ".": {
13
+ "import": {
14
+ "types": "./dist/index.d.mts",
15
+ "default": "./dist/index.mjs"
16
+ },
17
+ "require": {
18
+ "types": "./dist/index.d.ts",
19
+ "default": "./dist/index.js"
20
+ }
21
+ }
22
+ },
6
23
  "dependencies": {
7
- "@anker-in/shopify": "0.0.0-beta.5",
8
- "@anker-in/cart": "1.0.0-beta.1",
9
- "@anker-in/shared": "1.0.0-beta.1"
24
+ "@anker-in/shared": "1.0.0-beta.2",
25
+ "@anker-in/shopify": "0.0.0-beta.6",
26
+ "@anker-in/cart": "1.0.0-beta.2"
27
+ },
28
+ "peerDependencies": {
29
+ "react": "^18.0.0",
30
+ "react-dom": "^18.0.0"
10
31
  },
11
32
  "devDependencies": {
12
33
  "@types/jest": "^29.5.12",
@@ -14,12 +35,24 @@
14
35
  "jest": "^29.0.0",
15
36
  "ts-jest": "^29.2.6",
16
37
  "tsup": "^8.4.0",
17
- "typescript": "^5.0.0"
38
+ "typescript": "^5.0.0",
39
+ "@anker-in/shared": "1.0.0-beta.2",
40
+ "@anker-in/shopify": "0.0.0-beta.6",
41
+ "@anker-in/cart": "1.0.0-beta.2"
18
42
  },
19
- "peerDependencies": {
20
- "react": "^18.0.0",
21
- "react-dom": "^18.0.0"
43
+ "publishConfig": {
44
+ "access": "public",
45
+ "registry": "https://registry.npmjs.org/"
22
46
  },
47
+ "keywords": [
48
+ "anker",
49
+ "headless",
50
+ "shopify",
51
+ "ecommerce",
52
+ "react"
53
+ ],
54
+ "author": "Anker",
55
+ "license": "MIT",
23
56
  "scripts": {
24
57
  "dev": "tsup --watch",
25
58
  "build": "tsup",
package/.gitkeep DELETED
File without changes
@@ -1,27 +0,0 @@
1
-
2
- > @anker-in/lib@1.0.0 build /Users/anker/Code/headless-ui/packages/lib
3
- > tsup
4
-
5
- CLI Building entry: src/index.ts
6
- CLI Using tsconfig: tsconfig.json
7
- CLI tsup v8.4.0
8
- CLI Using tsup config: /Users/anker/Code/headless-ui/packages/lib/tsup.config.ts
9
- CLI Target: esnext
10
- CLI Cleaning output folder
11
- ESM Build start
12
- CJS Build start
13
- IIFE Build start
14
- If you do not supply "output.name", you may not be able to access the exports of an IIFE bundle.
15
- IIFE dist/index.global.js 198.00 B
16
- IIFE dist/index.global.js.map 397.00 B
17
- IIFE ⚡️ Build success in 436ms
18
- CJS dist/index.js 143.00 B
19
- CJS dist/index.js.map 389.00 B
20
- CJS ⚡️ Build success in 438ms
21
- ESM dist/index.mjs 130.00 B
22
- ESM dist/index.mjs.map 390.00 B
23
- ESM ⚡️ Build success in 438ms
24
- DTS Build start
25
- DTS ⚡️ Build success in 4317ms
26
- DTS dist/index.d.mts 161.00 B
27
- DTS dist/index.d.ts 161.00 B
@@ -1,14 +0,0 @@
1
-
2
- > @anker-in/lib@1.0.0 test /Users/anker/Code/headless-ui/packages/lib
3
- > jest --passWithNoTests
4
-
5
- PASS src/__tests__/math.test.ts
6
- math functions
7
- ✓ add should correctly add two numbers (1 ms)
8
- ✓ subtract should correctly subtract two numbers
9
-
10
- Test Suites: 1 passed, 1 total
11
- Tests: 2 passed, 2 total
12
- Snapshots: 0 total
13
- Time: 1.197 s
14
- Ran all test suites.
package/USAGE_EXAMPLE.md DELETED
@@ -1,425 +0,0 @@
1
- # 使用示例
2
-
3
- ## 完整的电商应用示例
4
-
5
- 以下是一个完整的示例,展示如何使用 `@anker-in/lib` 构建电商应用:
6
-
7
- ### 1. 应用入口(App.tsx)
8
-
9
- ```tsx
10
- import { HeadlessProvider } from '@anker-in/lib'
11
- import { ProductList } from './components/ProductList'
12
- import { Cart } from './components/Cart'
13
-
14
- const headlessConfig = {
15
- storefrontToken: process.env.NEXT_PUBLIC_SHOPIFY_TOKEN!,
16
- storeDomain: process.env.NEXT_PUBLIC_SHOPIFY_DOMAIN!,
17
- locale: 'zh-CN',
18
- comboMetafieldsNamespace: 'custom',
19
- cartIdCookieName: 'anker_cart_id',
20
- brand: 'Anker',
21
- appName: 'Anker Store'
22
- }
23
-
24
- export default function App() {
25
- return (
26
- <HeadlessProvider headlessConfig={headlessConfig}>
27
- <div className="app">
28
- <header>
29
- <h1>Anker 商店</h1>
30
- <Cart />
31
- </header>
32
- <main>
33
- <ProductList />
34
- </main>
35
- </div>
36
- </HeadlessProvider>
37
- )
38
- }
39
- ```
40
-
41
- ### 2. 产品列表(ProductList.tsx)
42
-
43
- ```tsx
44
- import { useProducts, useHeadlessContext } from '@anker-in/lib'
45
- import { ProductCard } from './ProductCard'
46
-
47
- export function ProductList() {
48
- const { locale } = useHeadlessContext()
49
- const { data: products, isLoading } = useProducts({
50
- first: 12,
51
- // 其他查询参数...
52
- })
53
-
54
- if (isLoading) return <div>加载中...</div>
55
-
56
- return (
57
- <div className="product-grid">
58
- {products?.map(product => (
59
- <ProductCard key={product.id} product={product} />
60
- ))}
61
- </div>
62
- )
63
- }
64
- ```
65
-
66
- ### 3. 产品卡片(ProductCard.tsx)
67
-
68
- ```tsx
69
- import {
70
- useAddToCart,
71
- useBuyNow,
72
- type Product
73
- } from '@anker-in/lib'
74
-
75
- interface ProductCardProps {
76
- product: Product
77
- }
78
-
79
- export function ProductCard({ product }: ProductCardProps) {
80
- const { trigger: addToCart, isMutating: isAdding } = useAddToCart()
81
- const { trigger: buyNow, isMutating: isBuying } = useBuyNow()
82
-
83
- const handleAddToCart = async () => {
84
- try {
85
- await addToCart({
86
- lineItems: [{
87
- variant: { id: product.variants[0].id },
88
- quantity: 1
89
- }]
90
- })
91
- alert('已添加到购物车!')
92
- } catch (error) {
93
- console.error('添加失败:', error)
94
- }
95
- }
96
-
97
- const handleBuyNow = async () => {
98
- await buyNow({
99
- lineItems: [{
100
- variant: { id: product.variants[0].id },
101
- quantity: 1
102
- }],
103
- gtmParams: {
104
- currency: 'USD',
105
- value: parseFloat(product.variants[0].price.amount)
106
- }
107
- })
108
- }
109
-
110
- return (
111
- <div className="product-card">
112
- <img src={product.images[0]?.url} alt={product.title} />
113
- <h3>{product.title}</h3>
114
- <p className="price">${product.variants[0].price.amount}</p>
115
-
116
- <div className="actions">
117
- <button
118
- onClick={handleAddToCart}
119
- disabled={isAdding}
120
- >
121
- {isAdding ? '添加中...' : '加入购物车'}
122
- </button>
123
-
124
- <button
125
- onClick={handleBuyNow}
126
- disabled={isBuying}
127
- className="primary"
128
- >
129
- {isBuying ? '处理中...' : '立即购买'}
130
- </button>
131
- </div>
132
- </div>
133
- )
134
- }
135
- ```
136
-
137
- ### 4. 购物车(Cart.tsx)
138
-
139
- ```tsx
140
- import {
141
- useCart,
142
- useUpdateCartLines,
143
- useRemoveCartLines,
144
- useApplyCartCodes
145
- } from '@anker-in/lib'
146
- import { useState } from 'react'
147
-
148
- export function Cart() {
149
- const { data: cart } = useCart()
150
- const { trigger: updateLines } = useUpdateCartLines()
151
- const { trigger: removeLines } = useRemoveCartLines()
152
- const { trigger: applyCode } = useApplyCartCodes()
153
- const [discountCode, setDiscountCode] = useState('')
154
-
155
- const handleQuantityChange = (lineId: string, quantity: number) => {
156
- updateLines({
157
- lines: [{
158
- id: lineId,
159
- quantity
160
- }]
161
- })
162
- }
163
-
164
- const handleRemove = (lineId: string) => {
165
- removeLines({ lineIds: [lineId] })
166
- }
167
-
168
- const handleApplyDiscount = () => {
169
- if (discountCode) {
170
- applyCode({ discountCodes: [discountCode] })
171
- }
172
- }
173
-
174
- if (!cart) return <div>购物车为空</div>
175
-
176
- return (
177
- <div className="cart">
178
- <h2>购物车 ({cart.lines.length})</h2>
179
-
180
- {cart.lines.map(line => (
181
- <div key={line.id} className="cart-item">
182
- <img src={line.merchandise.image?.url} alt={line.merchandise.title} />
183
- <div className="info">
184
- <h4>{line.merchandise.product.title}</h4>
185
- <p>{line.merchandise.title}</p>
186
- <p className="price">${line.cost.totalAmount.amount}</p>
187
- </div>
188
-
189
- <div className="quantity">
190
- <button onClick={() => handleQuantityChange(line.id, line.quantity - 1)}>
191
- -
192
- </button>
193
- <span>{line.quantity}</span>
194
- <button onClick={() => handleQuantityChange(line.id, line.quantity + 1)}>
195
- +
196
- </button>
197
- </div>
198
-
199
- <button onClick={() => handleRemove(line.id)}>删除</button>
200
- </div>
201
- ))}
202
-
203
- <div className="discount">
204
- <input
205
- type="text"
206
- placeholder="优惠码"
207
- value={discountCode}
208
- onChange={(e) => setDiscountCode(e.target.value)}
209
- />
210
- <button onClick={handleApplyDiscount}>应用</button>
211
- </div>
212
-
213
- <div className="total">
214
- <h3>总计: ${cart.cost.totalAmount.amount}</h3>
215
- <a href={cart.checkoutUrl} className="checkout-button">
216
- 去结账
217
- </a>
218
- </div>
219
- </div>
220
- )
221
- }
222
- ```
223
-
224
- ### 5. 产品详情页(ProductDetail.tsx)
225
-
226
- ```tsx
227
- import {
228
- useProduct,
229
- useBuyNow,
230
- useAddToCart,
231
- useHeadlessContext
232
- } from '@anker-in/lib'
233
- import { useState } from 'react'
234
-
235
- interface ProductDetailProps {
236
- productId: string
237
- }
238
-
239
- export function ProductDetail({ productId }: ProductDetailProps) {
240
- const { brand } = useHeadlessContext()
241
- const { data: product, isLoading } = useProduct(productId)
242
- const [selectedVariant, setSelectedVariant] = useState(0)
243
- const [quantity, setQuantity] = useState(1)
244
-
245
- const { trigger: buyNow } = useBuyNow()
246
- const { trigger: addToCart } = useAddToCart()
247
-
248
- if (isLoading) return <div>加载中...</div>
249
- if (!product) return <div>产品未找到</div>
250
-
251
- const currentVariant = product.variants[selectedVariant]
252
-
253
- return (
254
- <div className="product-detail">
255
- <div className="gallery">
256
- {product.images.map((image, idx) => (
257
- <img key={idx} src={image.url} alt={`${product.title} ${idx + 1}`} />
258
- ))}
259
- </div>
260
-
261
- <div className="info">
262
- <span className="brand">{brand}</span>
263
- <h1>{product.title}</h1>
264
- <div dangerouslySetInnerHTML={{ __html: product.descriptionHtml }} />
265
-
266
- <div className="variants">
267
- <h3>选择规格</h3>
268
- {product.variants.map((variant, idx) => (
269
- <button
270
- key={variant.id}
271
- onClick={() => setSelectedVariant(idx)}
272
- className={selectedVariant === idx ? 'active' : ''}
273
- >
274
- {variant.title}
275
- </button>
276
- ))}
277
- </div>
278
-
279
- <div className="price">
280
- <h2>${currentVariant.price.amount}</h2>
281
- </div>
282
-
283
- <div className="quantity">
284
- <label>数量:</label>
285
- <input
286
- type="number"
287
- min="1"
288
- value={quantity}
289
- onChange={(e) => setQuantity(parseInt(e.target.value))}
290
- />
291
- </div>
292
-
293
- <div className="actions">
294
- <button
295
- onClick={() => addToCart({
296
- lineItems: [{
297
- variant: { id: currentVariant.id },
298
- quantity
299
- }]
300
- })}
301
- className="add-to-cart"
302
- >
303
- 加入购物车
304
- </button>
305
-
306
- <button
307
- onClick={() => buyNow({
308
- lineItems: [{
309
- variant: { id: currentVariant.id },
310
- quantity
311
- }],
312
- gtmParams: {
313
- currency: 'USD',
314
- value: parseFloat(currentVariant.price.amount) * quantity,
315
- items: [{
316
- item_id: product.id,
317
- item_name: product.title,
318
- price: parseFloat(currentVariant.price.amount),
319
- quantity
320
- }]
321
- }
322
- })}
323
- className="buy-now"
324
- >
325
- 立即购买
326
- </button>
327
- </div>
328
- </div>
329
- </div>
330
- )
331
- }
332
- ```
333
-
334
- ## 高级功能示例
335
-
336
- ### 使用购物车特性 Hooks
337
-
338
- ```tsx
339
- import {
340
- useCartAttributes,
341
- usePriceDiscount,
342
- useCalcOrderDiscount,
343
- useCartItemQuantityLimit
344
- } from '@anker-in/lib'
345
-
346
- function AdvancedCart() {
347
- // 购物车自定义属性
348
- const { attributes, updateAttribute } = useCartAttributes()
349
-
350
- // 价格折扣
351
- const { discountedPrice } = usePriceDiscount()
352
-
353
- // 订单折扣计算
354
- const { orderDiscount } = useCalcOrderDiscount()
355
-
356
- // 数量限制
357
- const { maxQuantity, isLimitReached } = useCartItemQuantityLimit()
358
-
359
- return (
360
- <div>
361
- {/* 使用这些 hooks 的数据 */}
362
- </div>
363
- )
364
- }
365
- ```
366
-
367
- ### 自定义埋点
368
-
369
- ```tsx
370
- import { useBuyNow } from '@anker-in/lib'
371
-
372
- function CustomTracking() {
373
- const { trigger: buyNow } = useBuyNow({
374
- withTrack: false // 禁用默认埋点
375
- })
376
-
377
- const handleBuyNow = async () => {
378
- // 自定义埋点逻辑
379
- window.dataLayer?.push({
380
- event: 'custom_buy_now',
381
- // ... 自定义数据
382
- })
383
-
384
- await buyNow({
385
- lineItems: [/* ... */],
386
- fbqTrackConfig: {
387
- // Facebook Pixel 配置
388
- content_ids: ['product-123'],
389
- content_type: 'product',
390
- value: 99.99,
391
- currency: 'USD'
392
- }
393
- })
394
- }
395
-
396
- return <button onClick={handleBuyNow}>购买</button>
397
- }
398
- ```
399
-
400
- ## TypeScript 支持
401
-
402
- 所有导出的类型都可以直接使用:
403
-
404
- ```tsx
405
- import type {
406
- Product,
407
- Cart,
408
- CartLine,
409
- HeadlessConfig,
410
- BuyNowArgs,
411
- // ... 更多类型
412
- } from '@anker-in/lib'
413
-
414
- const config: HeadlessConfig = {
415
- storefrontToken: '...',
416
- storeDomain: '...',
417
- // ...
418
- }
419
-
420
- function handleProduct(product: Product) {
421
- // TypeScript 完整支持
422
- }
423
- ```
424
-
425
-
package/jest.config.ts DELETED
@@ -1,12 +0,0 @@
1
- import { JestConfigWithTsJest } from 'ts-jest'
2
-
3
- const config: JestConfigWithTsJest = {
4
- preset: 'ts-jest',
5
- testEnvironment: 'jsdom', // 使用 node 环境(如果是浏览器库可改成 'jsdom')
6
- clearMocks: true, // 每次测试后自动清除 mock
7
- coverageDirectory: 'coverage', // 输出覆盖率报告
8
- testMatch: ['**/__tests__/**/*.test.ts'], // 匹配测试文件路径
9
- moduleFileExtensions: ['ts', 'js', 'json'],
10
- }
11
-
12
- export default config
@@ -1,15 +0,0 @@
1
- import { add, subtract } from '../math'
2
-
3
- describe('math functions', () => {
4
- test('add should correctly add two numbers', () => {
5
- expect(add(2, 3)).toBe(5)
6
- expect(add(-1, -1)).toBe(-2)
7
- expect(add(0, 5)).toBe(5)
8
- })
9
-
10
- test('subtract should correctly subtract two numbers', () => {
11
- expect(subtract(5, 3)).toBe(2)
12
- expect(subtract(0, 5)).toBe(-5)
13
- expect(subtract(-1, -1)).toBe(0)
14
- })
15
- })
package/src/index.ts DELETED
@@ -1,7 +0,0 @@
1
- // 导出本地功能
2
- export * from './math'
3
-
4
- // 重新导出所有包的内容(作为便利导出)
5
- export * from '@anker-in/shared'
6
- export * from '@anker-in/shopify'
7
- export * from '@anker-in/cart'
package/src/math.ts DELETED
@@ -1,5 +0,0 @@
1
- /**
2
- * 这是一个demo 模版
3
- */
4
- export const add = (a: number, b: number) => a + b
5
- export const subtract = (a: number, b: number) => a - b
package/tsconfig.json DELETED
@@ -1,20 +0,0 @@
1
- {
2
- "extends": "../../tsconfig.json",
3
- "compilerOptions": {
4
- "outDir": "dist",
5
- "target": "esnext",
6
- "module": "esnext",
7
- "moduleResolution": "bundler",
8
- "declaration": true,
9
- "declarationMap": true,
10
- "sourceMap": true,
11
- "strict": true,
12
- "esModuleInterop": true,
13
- "lib": ["DOM", "ESNext", "DOM.Iterable"],
14
- "skipLibCheck": true,
15
- "jsx": "react-jsx",
16
- "types": ["jest", "node"]
17
- },
18
- "include": ["src"],
19
- "exclude": ["node_modules", "dist"]
20
- }
package/tsup.config.ts DELETED
@@ -1,20 +0,0 @@
1
- import { defineConfig } from 'tsup'
2
-
3
- export default defineConfig({
4
- entry: ['src/index.ts'],
5
- format: ['esm', 'cjs'],
6
- dts: true,
7
- splitting: false,
8
- sourcemap: true,
9
- clean: true,
10
- external: [
11
- 'react',
12
- 'react-dom',
13
- '@anker-in/shared',
14
- '@anker-in/shopify',
15
- '@anker-in/cart',
16
- 'next/router',
17
- '@sentry/nextjs',
18
- 'swr',
19
- ],
20
- })