@teleporthq/teleport-plugin-next-data-source 0.43.54 → 0.43.57
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/__tests__/array-mapper-cache-wiring.test.ts +425 -0
- package/__tests__/data-source-cache-wrapping.test.ts +152 -0
- package/__tests__/ecommerce-product-storefront-tax.test.ts +118 -0
- package/__tests__/ecommerce-product-variant-availability.test.ts +264 -0
- package/dist/cjs/cache/ast.d.ts +71 -0
- package/dist/cjs/cache/ast.d.ts.map +1 -0
- package/dist/cjs/cache/ast.js +229 -0
- package/dist/cjs/cache/ast.js.map +1 -0
- package/dist/cjs/cache/config.d.ts +22 -0
- package/dist/cjs/cache/config.d.ts.map +1 -0
- package/dist/cjs/cache/config.js +67 -0
- package/dist/cjs/cache/config.js.map +1 -0
- package/dist/cjs/cache/types.d.ts +31 -0
- package/dist/cjs/cache/types.d.ts.map +1 -0
- package/dist/cjs/cache/types.js +3 -0
- package/dist/cjs/cache/types.js.map +1 -0
- package/dist/cjs/data-source-fetchers.d.ts +5 -3
- package/dist/cjs/data-source-fetchers.d.ts.map +1 -1
- package/dist/cjs/data-source-fetchers.js +51 -8
- package/dist/cjs/data-source-fetchers.js.map +1 -1
- package/dist/cjs/fetchers/teleport.d.ts +2 -2
- package/dist/cjs/fetchers/teleport.d.ts.map +1 -1
- package/dist/cjs/fetchers/teleport.js +3 -2
- package/dist/cjs/fetchers/teleport.js.map +1 -1
- package/dist/cjs/index.d.ts +2 -0
- package/dist/cjs/index.d.ts.map +1 -1
- package/dist/cjs/index.js +14 -9
- package/dist/cjs/index.js.map +1 -1
- package/dist/cjs/loading-state.d.ts.map +1 -1
- package/dist/cjs/loading-state.js +74 -2
- package/dist/cjs/loading-state.js.map +1 -1
- package/dist/cjs/pagination-plugin.d.ts.map +1 -1
- package/dist/cjs/pagination-plugin.js +204 -64
- package/dist/cjs/pagination-plugin.js.map +1 -1
- package/dist/cjs/transformations/ecommerce-product.d.ts +15 -1
- package/dist/cjs/transformations/ecommerce-product.d.ts.map +1 -1
- package/dist/cjs/transformations/ecommerce-product.js +7 -3
- package/dist/cjs/transformations/ecommerce-product.js.map +1 -1
- package/dist/cjs/transformations/index.d.ts +13 -2
- package/dist/cjs/transformations/index.d.ts.map +1 -1
- package/dist/cjs/transformations/index.js +33 -5
- package/dist/cjs/transformations/index.js.map +1 -1
- package/dist/cjs/tsconfig.tsbuildinfo +1 -1
- package/dist/cjs/utils.d.ts +4 -3
- package/dist/cjs/utils.d.ts.map +1 -1
- package/dist/cjs/utils.js +6 -4
- package/dist/cjs/utils.js.map +1 -1
- package/dist/esm/cache/ast.d.ts +71 -0
- package/dist/esm/cache/ast.d.ts.map +1 -0
- package/dist/esm/cache/ast.js +196 -0
- package/dist/esm/cache/ast.js.map +1 -0
- package/dist/esm/cache/config.d.ts +22 -0
- package/dist/esm/cache/config.d.ts.map +1 -0
- package/dist/esm/cache/config.js +62 -0
- package/dist/esm/cache/config.js.map +1 -0
- package/dist/esm/cache/types.d.ts +31 -0
- package/dist/esm/cache/types.d.ts.map +1 -0
- package/dist/esm/cache/types.js +2 -0
- package/dist/esm/cache/types.js.map +1 -0
- package/dist/esm/data-source-fetchers.d.ts +5 -3
- package/dist/esm/data-source-fetchers.d.ts.map +1 -1
- package/dist/esm/data-source-fetchers.js +51 -8
- package/dist/esm/data-source-fetchers.js.map +1 -1
- package/dist/esm/fetchers/teleport.d.ts +2 -2
- package/dist/esm/fetchers/teleport.d.ts.map +1 -1
- package/dist/esm/fetchers/teleport.js +3 -2
- package/dist/esm/fetchers/teleport.js.map +1 -1
- package/dist/esm/index.d.ts +2 -0
- package/dist/esm/index.d.ts.map +1 -1
- package/dist/esm/index.js +12 -8
- package/dist/esm/index.js.map +1 -1
- package/dist/esm/loading-state.d.ts.map +1 -1
- package/dist/esm/loading-state.js +74 -2
- package/dist/esm/loading-state.js.map +1 -1
- package/dist/esm/pagination-plugin.d.ts.map +1 -1
- package/dist/esm/pagination-plugin.js +205 -65
- package/dist/esm/pagination-plugin.js.map +1 -1
- package/dist/esm/transformations/ecommerce-product.d.ts +15 -1
- package/dist/esm/transformations/ecommerce-product.d.ts.map +1 -1
- package/dist/esm/transformations/ecommerce-product.js +7 -3
- package/dist/esm/transformations/ecommerce-product.js.map +1 -1
- package/dist/esm/transformations/index.d.ts +13 -2
- package/dist/esm/transformations/index.d.ts.map +1 -1
- package/dist/esm/transformations/index.js +32 -5
- package/dist/esm/transformations/index.js.map +1 -1
- package/dist/esm/tsconfig.tsbuildinfo +1 -1
- package/dist/esm/utils.d.ts +4 -3
- package/dist/esm/utils.d.ts.map +1 -1
- package/dist/esm/utils.js +6 -4
- package/dist/esm/utils.js.map +1 -1
- package/package.json +5 -5
- package/src/cache/ast.ts +265 -0
- package/src/cache/config.ts +79 -0
- package/src/cache/types.ts +31 -0
- package/src/data-source-fetchers.ts +66 -12
- package/src/fetchers/teleport.ts +3 -3
- package/src/index.ts +10 -4
- package/src/loading-state.ts +65 -2
- package/src/pagination-plugin.ts +315 -116
- package/src/transformations/ecommerce-product.ts +107 -11
- package/src/transformations/index.ts +48 -7
- package/src/utils.ts +10 -5
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { DataSourceType, UIDLDataSource
|
|
1
|
+
import { DataSourceType, UIDLDataSource } from '@teleporthq/teleport-types'
|
|
2
2
|
import {
|
|
3
3
|
generatePostgreSQLFetcher,
|
|
4
4
|
generateMySQLFetcher,
|
|
@@ -33,6 +33,9 @@ import {
|
|
|
33
33
|
} from './fetchers'
|
|
34
34
|
import { validateDatabaseConfig } from './validation'
|
|
35
35
|
import { generateCountFetcher } from './count-fetchers'
|
|
36
|
+
import type { EcommerceProductTransformOptions } from './transformations'
|
|
37
|
+
import { DataCache } from '@teleporthq/teleport-shared'
|
|
38
|
+
import type { DataSourceServerCacheOptions } from './cache/types'
|
|
36
39
|
|
|
37
40
|
export { generateRawQueryFetcher, parseQueryTemplateVariables }
|
|
38
41
|
|
|
@@ -81,7 +84,7 @@ export const getDataSourceDependencies = (
|
|
|
81
84
|
export function generateDataSourceFetcher(
|
|
82
85
|
dataSource: UIDLDataSource,
|
|
83
86
|
tableName: string,
|
|
84
|
-
|
|
87
|
+
transformOptions: EcommerceProductTransformOptions = {}
|
|
85
88
|
): string {
|
|
86
89
|
if (!dataSource || typeof dataSource !== 'object') {
|
|
87
90
|
throw new Error('Invalid data source: data source must be a valid object')
|
|
@@ -237,7 +240,7 @@ export function generateDataSourceFetcher(
|
|
|
237
240
|
if (!validation.isValid) {
|
|
238
241
|
throw new Error(`Teleport config validation failed: ${validation.error}`)
|
|
239
242
|
}
|
|
240
|
-
return generateTeleportFetcher(config, tableName,
|
|
243
|
+
return generateTeleportFetcher(config, tableName, transformOptions)
|
|
241
244
|
}
|
|
242
245
|
|
|
243
246
|
default:
|
|
@@ -258,9 +261,10 @@ export function generateDataSourceFetcherWithCore(
|
|
|
258
261
|
dataSource: UIDLDataSource,
|
|
259
262
|
tableName: string,
|
|
260
263
|
isApiRoute: boolean = false,
|
|
261
|
-
|
|
264
|
+
transformOptions: EcommerceProductTransformOptions = {},
|
|
265
|
+
cacheOptions?: DataSourceServerCacheOptions
|
|
262
266
|
): string {
|
|
263
|
-
const apiHandler = generateDataSourceFetcher(dataSource, tableName,
|
|
267
|
+
const apiHandler = generateDataSourceFetcher(dataSource, tableName, transformOptions)
|
|
264
268
|
|
|
265
269
|
// Extract the handler function body from the API route
|
|
266
270
|
// The API route is structured as: export default async function handler(req, res) { ... }
|
|
@@ -285,14 +289,64 @@ export function generateDataSourceFetcherWithCore(
|
|
|
285
289
|
// Combine and deduplicate imports
|
|
286
290
|
const allImports = Array.from(new Set([...imports, ...countImports]))
|
|
287
291
|
|
|
292
|
+
// Caching is applied HERE rather than in the API route because four separate
|
|
293
|
+
// places emit an `api/<file>` route and a fifth path (`getStaticProps` →
|
|
294
|
+
// `fetchData` → `handler`) never goes through a route at all. Wrapping the
|
|
295
|
+
// module's exports is the one point that covers all five, and leaves every
|
|
296
|
+
// route emitter untouched.
|
|
297
|
+
//
|
|
298
|
+
// The import depth differs by destination: this module is written to
|
|
299
|
+
// `utils/data-sources/` normally, but the `isApiRoute` variant is inlined
|
|
300
|
+
// straight into `pages/api/`.
|
|
301
|
+
const cachePreamble = cacheOptions
|
|
302
|
+
? `import { tqWithCache } from '${isApiRoute ? '../../' : '../'}tq-cache/server'`
|
|
303
|
+
: ''
|
|
304
|
+
|
|
305
|
+
const cacheWiring = cacheOptions
|
|
306
|
+
? `
|
|
307
|
+
const __tqDataCache = ${JSON.stringify({
|
|
308
|
+
scope: cacheOptions.scope,
|
|
309
|
+
ttl: cacheOptions.ttlSeconds,
|
|
310
|
+
sMaxAge: cacheOptions.sMaxAge || 0,
|
|
311
|
+
swr: cacheOptions.staleWhileRevalidate || 0,
|
|
312
|
+
})}
|
|
313
|
+
const __tqCountCache = ${JSON.stringify({
|
|
314
|
+
scope: DataCache.countScopeFor(cacheOptions.scope),
|
|
315
|
+
// Its OWN keyspace, but the DATA scope's version row. A write bumps one
|
|
316
|
+
// row; if the count kept a separate version nothing would ever bump it,
|
|
317
|
+
// and the total would stay stale after every create and delete — the
|
|
318
|
+
// grid showing one more row than the pager counts.
|
|
319
|
+
versionScope: cacheOptions.scope,
|
|
320
|
+
ttl: cacheOptions.ttlSeconds,
|
|
321
|
+
sMaxAge: cacheOptions.sMaxAge || 0,
|
|
322
|
+
swr: cacheOptions.staleWhileRevalidate || 0,
|
|
323
|
+
})}
|
|
324
|
+
|
|
325
|
+
const cachedHandler = tqWithCache(handler, __tqDataCache)
|
|
326
|
+
const cachedGetCount = tqWithCache(getCount, __tqCountCache)
|
|
327
|
+
`
|
|
328
|
+
: ''
|
|
329
|
+
|
|
330
|
+
const handlerExport = cacheOptions ? 'cachedHandler' : 'handler'
|
|
331
|
+
const countExport = cacheOptions ? 'cachedGetCount' : 'getCount'
|
|
332
|
+
|
|
288
333
|
// For API routes, export just the handler function
|
|
289
334
|
// For utils files, export the full object with all functions
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
335
|
+
//
|
|
336
|
+
// The uncached branch is spelled out verbatim rather than interpolated, so an
|
|
337
|
+
// uncached project keeps generating byte-identical output — `handler as
|
|
338
|
+
// handler` would be harmless at runtime and still a diff in every snapshot.
|
|
339
|
+
const exports = !cacheOptions
|
|
340
|
+
? isApiRoute
|
|
341
|
+
? 'export default handler'
|
|
342
|
+
: `export { fetchData, fetchCount, handler, getCount }
|
|
293
343
|
export default { fetchData, fetchCount, handler, getCount }`
|
|
344
|
+
: isApiRoute
|
|
345
|
+
? `export default ${handlerExport}`
|
|
346
|
+
: `export { fetchData, fetchCount, ${handlerExport} as handler, ${countExport} as getCount }
|
|
347
|
+
export default { fetchData, fetchCount, handler: ${handlerExport}, getCount: ${countExport} }`
|
|
294
348
|
|
|
295
|
-
return `${allImports.join('\n')}
|
|
349
|
+
return `${[...allImports, cachePreamble].filter(Boolean).join('\n')}
|
|
296
350
|
|
|
297
351
|
async function fetchData(params = {}) {
|
|
298
352
|
const req = {
|
|
@@ -314,7 +368,7 @@ async function fetchData(params = {}) {
|
|
|
314
368
|
},
|
|
315
369
|
}
|
|
316
370
|
|
|
317
|
-
await
|
|
371
|
+
await ${handlerExport}(req, res)
|
|
318
372
|
|
|
319
373
|
if (statusCode !== 200 || !result || !result.success) {
|
|
320
374
|
throw new Error(result?.error || 'Failed to fetch data')
|
|
@@ -343,7 +397,7 @@ async function fetchCount(params = {}) {
|
|
|
343
397
|
},
|
|
344
398
|
}
|
|
345
399
|
|
|
346
|
-
await
|
|
400
|
+
await ${countExport}(req, res)
|
|
347
401
|
|
|
348
402
|
if (statusCode !== 200 || !result || !result.success) {
|
|
349
403
|
throw new Error(result?.error || 'Failed to get count')
|
|
@@ -355,7 +409,7 @@ async function fetchCount(params = {}) {
|
|
|
355
409
|
${countFetcherWithoutImports}
|
|
356
410
|
|
|
357
411
|
${handlerWithoutImports}
|
|
358
|
-
|
|
412
|
+
${cacheWiring}
|
|
359
413
|
${exports}
|
|
360
414
|
`
|
|
361
415
|
}
|
package/src/fetchers/teleport.ts
CHANGED
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import type { UIDLEcommerceCategory } from '@teleporthq/teleport-types'
|
|
2
1
|
import {
|
|
3
2
|
replaceSecretReference,
|
|
4
3
|
generateDateFormatterCode,
|
|
@@ -9,6 +8,7 @@ import {
|
|
|
9
8
|
getTransformationCode,
|
|
10
9
|
getTransformExpression,
|
|
11
10
|
getTransformWrapperCode,
|
|
11
|
+
type EcommerceProductTransformOptions,
|
|
12
12
|
} from '../transformations'
|
|
13
13
|
|
|
14
14
|
interface TeleportDBConfig {
|
|
@@ -52,7 +52,7 @@ export const validateTeleportConfig = (
|
|
|
52
52
|
export const generateTeleportFetcher = (
|
|
53
53
|
config: Record<string, unknown>,
|
|
54
54
|
tableName: string,
|
|
55
|
-
|
|
55
|
+
transformOptions: EcommerceProductTransformOptions = {}
|
|
56
56
|
): string => {
|
|
57
57
|
const dbConfig = config as TeleportDBConfig
|
|
58
58
|
const schema = dbConfig.options?.schema
|
|
@@ -142,7 +142,7 @@ const getClient = () => {
|
|
|
142
142
|
${generateSafeJSONParseCode()}
|
|
143
143
|
|
|
144
144
|
${generateSearchEscapeHelpersCode()}
|
|
145
|
-
${getTransformationCode(tableName,
|
|
145
|
+
${getTransformationCode(tableName, transformOptions)}
|
|
146
146
|
${getTransformWrapperCode(tableName)}
|
|
147
147
|
const processFilters = (filters, conditions, queryParams, paramIndex) => {
|
|
148
148
|
if (!filters) return paramIndex
|
package/src/index.ts
CHANGED
|
@@ -14,6 +14,7 @@ import {
|
|
|
14
14
|
hasUnresolvableDynamicParams,
|
|
15
15
|
} from './utils'
|
|
16
16
|
import { createNextArrayMapperPaginationPlugin } from './pagination-plugin'
|
|
17
|
+
import { buildProductTransformOptions } from './transformations'
|
|
17
18
|
import { DATA_SOURCE_ISR_REVALIDATE_SECONDS } from './isr'
|
|
18
19
|
import * as types from '@babel/types'
|
|
19
20
|
|
|
@@ -608,7 +609,7 @@ export const createNextPagesDataSourcePlugin: ComponentPluginFactory<{}> = () =>
|
|
|
608
609
|
dependencies,
|
|
609
610
|
dynamicRouteAttr,
|
|
610
611
|
uidl.outputOptions?.folderPath,
|
|
611
|
-
options
|
|
612
|
+
buildProductTransformOptions(options)
|
|
612
613
|
)
|
|
613
614
|
|
|
614
615
|
if (result.success && result.chunk) {
|
|
@@ -646,7 +647,7 @@ export const createNextPagesDataSourcePlugin: ComponentPluginFactory<{}> = () =>
|
|
|
646
647
|
dataSources,
|
|
647
648
|
componentChunk,
|
|
648
649
|
options.extractedResources,
|
|
649
|
-
options
|
|
650
|
+
buildProductTransformOptions(options)
|
|
650
651
|
)
|
|
651
652
|
}
|
|
652
653
|
})
|
|
@@ -1268,7 +1269,7 @@ export const createNextComponentDataSourcePlugin: ComponentPluginFactory<{}> = (
|
|
|
1268
1269
|
dataSources,
|
|
1269
1270
|
componentChunk,
|
|
1270
1271
|
options.extractedResources,
|
|
1271
|
-
options
|
|
1272
|
+
buildProductTransformOptions(options)
|
|
1272
1273
|
)
|
|
1273
1274
|
})
|
|
1274
1275
|
|
|
@@ -1354,7 +1355,7 @@ export const createNextComponentDataSourcePlugin: ComponentPluginFactory<{}> = (
|
|
|
1354
1355
|
dataSource,
|
|
1355
1356
|
firstDataSourceInfo.tableName,
|
|
1356
1357
|
false,
|
|
1357
|
-
options
|
|
1358
|
+
buildProductTransformOptions(options)
|
|
1358
1359
|
)
|
|
1359
1360
|
options.extractedResources[`utils/${fileName}`] = {
|
|
1360
1361
|
fileName,
|
|
@@ -1398,3 +1399,8 @@ export * from './utils'
|
|
|
1398
1399
|
export * from './array-mapper-pagination'
|
|
1399
1400
|
export * from './pagination-plugin'
|
|
1400
1401
|
export * from './count-fetchers'
|
|
1402
|
+
// The product transform's build-time options (category taxonomy + storefront
|
|
1403
|
+
// tax rate) — every package that emits a data-source fetcher has to derive
|
|
1404
|
+
// them the same way.
|
|
1405
|
+
export { buildProductTransformOptions } from './transformations'
|
|
1406
|
+
export type { EcommerceProductTransformOptions } from './transformations'
|
package/src/loading-state.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import * as types from '@babel/types'
|
|
2
|
+
import { findCachedFetchNetworkChain } from './cache/ast'
|
|
2
3
|
|
|
3
4
|
/**
|
|
4
5
|
* ------------------------------------------------------------------
|
|
@@ -137,17 +138,79 @@ export function applyLoadingStateToDataProvider(
|
|
|
137
138
|
}
|
|
138
139
|
|
|
139
140
|
const fetchArrow = findMemoizedFetchDataArrow(fetchDataAttr)
|
|
140
|
-
if (!fetchArrow
|
|
141
|
+
if (!fetchArrow) {
|
|
141
142
|
return false
|
|
142
143
|
}
|
|
143
144
|
|
|
144
|
-
|
|
145
|
+
if (fetchArrow.body.type === 'CallExpression') {
|
|
146
|
+
fetchArrow.body = buildTrackedFetchBody(fetchArrow.body, vars)
|
|
147
|
+
} else if (fetchArrow.body.type === 'BlockStatement') {
|
|
148
|
+
if (!injectTrackingIntoCachedBody(fetchArrow.body, vars)) {
|
|
149
|
+
return false
|
|
150
|
+
}
|
|
151
|
+
} else {
|
|
152
|
+
return false
|
|
153
|
+
}
|
|
145
154
|
|
|
146
155
|
setPersistDataDuringLoading(attributes, vars)
|
|
147
156
|
|
|
148
157
|
return true
|
|
149
158
|
}
|
|
150
159
|
|
|
160
|
+
/**
|
|
161
|
+
* Adds the in-flight bookkeeping to a CACHED `fetchData` body.
|
|
162
|
+
*
|
|
163
|
+
* The body already returns early with `Promise.resolve(hit)` on a cache hit, so
|
|
164
|
+
* the tracking is attached to the network chain BELOW that — never to the early
|
|
165
|
+
* return. That is the whole point: a hit must not raise `isFetching`, because
|
|
166
|
+
* `persistDataDuringLoading={!isFetching}` would then drop the provider into its
|
|
167
|
+
* loading slot for a frame and produce exactly the skeleton flash the cache
|
|
168
|
+
* exists to remove.
|
|
169
|
+
*
|
|
170
|
+
* Returns `false` when the body is not the shape this module knows how to wrap,
|
|
171
|
+
* so the caller emits no state declarations rather than half-wiring it.
|
|
172
|
+
*/
|
|
173
|
+
function injectTrackingIntoCachedBody(body: types.BlockStatement, vars: LoadingStateVars): boolean {
|
|
174
|
+
// Idempotent: a second pass over the same AST must not double-count.
|
|
175
|
+
if (containsIdentifier(body, vars.setIsFetchingVar)) {
|
|
176
|
+
return false
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
const networkChain = findCachedFetchNetworkChain(body)
|
|
180
|
+
if (!networkChain) {
|
|
181
|
+
return false
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
const tracked = buildTrackedFetchBody(networkChain.statement.argument as types.Expression, vars)
|
|
185
|
+
body.body.splice(networkChain.index, 1, ...tracked.body)
|
|
186
|
+
|
|
187
|
+
return true
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
// tslint:disable-next-line:no-any
|
|
191
|
+
function containsIdentifier(node: any, name: string): boolean {
|
|
192
|
+
if (!node || typeof node !== 'object') {
|
|
193
|
+
return false
|
|
194
|
+
}
|
|
195
|
+
if (node.type === 'Identifier' && node.name === name) {
|
|
196
|
+
return true
|
|
197
|
+
}
|
|
198
|
+
for (const key of Object.keys(node)) {
|
|
199
|
+
if (key === 'loc' || key === 'start' || key === 'end') {
|
|
200
|
+
continue
|
|
201
|
+
}
|
|
202
|
+
const value = node[key]
|
|
203
|
+
if (Array.isArray(value)) {
|
|
204
|
+
if (value.some((entry) => containsIdentifier(entry, name))) {
|
|
205
|
+
return true
|
|
206
|
+
}
|
|
207
|
+
} else if (value && typeof value === 'object' && containsIdentifier(value, name)) {
|
|
208
|
+
return true
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
return false
|
|
212
|
+
}
|
|
213
|
+
|
|
151
214
|
// tslint:disable-next-line:no-any
|
|
152
215
|
function findAttribute(attributes: any[], name: string): types.JSXAttribute | undefined {
|
|
153
216
|
return attributes.find(
|