@teleporthq/teleport-plugin-next-data-source 0.43.55 → 0.43.59
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-allow-backorders.test.ts +156 -0
- package/__tests__/ecommerce-product-model.test.ts +179 -0
- package/__tests__/ecommerce-product-storefront-tax.test.ts +118 -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 +27 -1
- package/dist/cjs/transformations/ecommerce-product.d.ts.map +1 -1
- package/dist/cjs/transformations/ecommerce-product.js +8 -3
- package/dist/cjs/transformations/ecommerce-product.js.map +1 -1
- package/dist/cjs/transformations/index.d.ts +14 -2
- package/dist/cjs/transformations/index.d.ts.map +1 -1
- package/dist/cjs/transformations/index.js +37 -3
- package/dist/cjs/transformations/index.js.map +1 -1
- package/dist/cjs/transformations/shared-utils.d.ts.map +1 -1
- package/dist/cjs/transformations/shared-utils.js +1 -1
- package/dist/cjs/transformations/shared-utils.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 +27 -1
- package/dist/esm/transformations/ecommerce-product.d.ts.map +1 -1
- package/dist/esm/transformations/ecommerce-product.js +8 -3
- package/dist/esm/transformations/ecommerce-product.js.map +1 -1
- package/dist/esm/transformations/index.d.ts +14 -2
- package/dist/esm/transformations/index.d.ts.map +1 -1
- package/dist/esm/transformations/index.js +36 -3
- package/dist/esm/transformations/index.js.map +1 -1
- package/dist/esm/transformations/shared-utils.d.ts.map +1 -1
- package/dist/esm/transformations/shared-utils.js +1 -1
- package/dist/esm/transformations/shared-utils.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 +161 -5
- package/src/transformations/index.ts +43 -4
- package/src/transformations/shared-utils.ts +14 -0
- package/src/utils.ts +10 -5
|
@@ -37,13 +37,16 @@ var __generator = (this && this.__generator) || function (thisArg, body) {
|
|
|
37
37
|
import { ChunkType, FileType, } from '@teleporthq/teleport-types';
|
|
38
38
|
import * as types from '@babel/types';
|
|
39
39
|
import { parseExpression } from '@babel/parser';
|
|
40
|
-
import { StringUtils } from '@teleporthq/teleport-shared';
|
|
40
|
+
import { ASTStatementOrder, StringUtils } from '@teleporthq/teleport-shared';
|
|
41
41
|
import { ASTUtils, URLSearchParamSync } from '@teleporthq/teleport-plugin-common';
|
|
42
42
|
import { generateSafeFileName } from './utils';
|
|
43
43
|
import { generateDataSourceFetcherWithCore } from './data-source-fetchers';
|
|
44
|
+
import { buildProductTransformOptions, } from './transformations';
|
|
44
45
|
import { appendSortsParam, extractDynamicSort } from './sort-utils';
|
|
45
46
|
import { appendFiltersParam, pushStateIdsAsDeps, pushPropIdsAsDeps } from './filter-utils';
|
|
46
47
|
import { applyLoadingStateToDataProvider, buildLoadingStateDeclarations, getLoadingStateVars, } from './loading-state';
|
|
48
|
+
import { mergeServerCacheOptions, resolveCacheFromRepeaterContent } from './cache/config';
|
|
49
|
+
import { buildCacheHydrationEffect, buildCacheStoreThen, buildCachedFetchBody, buildCachedParamsDeclarations, registerCacheClientImports, } from './cache/ast';
|
|
47
50
|
// tslint:disable-next-line:no-any
|
|
48
51
|
function parseSearchDefaultValue(raw) {
|
|
49
52
|
if (!raw || typeof raw !== 'object') {
|
|
@@ -419,6 +422,7 @@ function buildStateRegistry(uidlNode) {
|
|
|
419
422
|
filterStateIds: filterStateIds,
|
|
420
423
|
filterPropIds: filterPropIds,
|
|
421
424
|
filterUrlSearchParamKeys: filterUrlSearchParamKeys,
|
|
425
|
+
cache: resolveCacheFromRepeaterContent(content.cache, parentDataSource.resourceDef),
|
|
422
426
|
category: 'plain',
|
|
423
427
|
};
|
|
424
428
|
// Determine category
|
|
@@ -552,9 +556,9 @@ function pushSearchUrlSyncEffects(effectStatements, usage, vars) {
|
|
|
552
556
|
// ==================== MAIN PLUGIN ====================
|
|
553
557
|
export var createNextArrayMapperPaginationPlugin = function () {
|
|
554
558
|
var paginationPlugin = function (structure) { return __awaiter(void 0, void 0, void 0, function () {
|
|
555
|
-
var uidl, chunks, dependencies, options, componentChunk, variableDeclaration, declarator, arrowFunction, blockStatement, registry, getStaticPropsChunk, isPage, stateDeclarations, effectStatements, needsUseRouter, hasRouterDecl, returnIndex, insertIndex, dataProviders, dataProvidersWithRepeaters, usageIndexByDataSourceId, loadingStateDeclarations, dataProvidersWithoutRepeaters, searchInputs, searchEnabledUsages, paginationNodes, paginatedUsages;
|
|
556
|
-
var _a;
|
|
557
|
-
return __generator(this, function (
|
|
559
|
+
var uidl, chunks, dependencies, options, componentChunk, variableDeclaration, declarator, arrowFunction, blockStatement, registry, getStaticPropsChunk, isPage, stateDeclarations, effectStatements, needsUseRouter, hasRouterDecl, returnIndex, insertIndex, dataProviders, dataProvidersWithRepeaters, usageIndexByDataSourceId, loadingStateDeclarations, cacheDeclarations, sharedOptions, cachedScopes, dataProvidersWithoutRepeaters, searchInputs, searchEnabledUsages, paginationNodes, paginatedUsages;
|
|
560
|
+
var _a, _b;
|
|
561
|
+
return __generator(this, function (_c) {
|
|
558
562
|
uidl = structure.uidl, chunks = structure.chunks, dependencies = structure.dependencies, options = structure.options;
|
|
559
563
|
componentChunk = chunks.find(function (chunk) { return chunk.name === 'jsx-component'; });
|
|
560
564
|
if (!componentChunk || componentChunk.type !== ChunkType.AST) {
|
|
@@ -916,8 +920,9 @@ export var createNextArrayMapperPaginationPlugin = function () {
|
|
|
916
920
|
});
|
|
917
921
|
usageIndexByDataSourceId = new Map();
|
|
918
922
|
loadingStateDeclarations = [];
|
|
923
|
+
cacheDeclarations = [];
|
|
919
924
|
dataProvidersWithRepeaters.forEach(function (dp) {
|
|
920
|
-
var _a, _b
|
|
925
|
+
var _a, _b;
|
|
921
926
|
var nameAttr = dp.openingElement.attributes.find(function (attr) { return attr.type === 'JSXAttribute' && attr.name.name === 'name'; });
|
|
922
927
|
if (!((_b = (_a = nameAttr === null || nameAttr === void 0 ? void 0 : nameAttr.value) === null || _a === void 0 ? void 0 : _a.expression) === null || _b === void 0 ? void 0 : _b.value)) {
|
|
923
928
|
return;
|
|
@@ -935,13 +940,13 @@ export var createNextArrayMapperPaginationPlugin = function () {
|
|
|
935
940
|
var fileName = generateSafeFileName(usage.resourceDefinition.dataSourceType, usage.resourceDefinition.tableName, usage.resourceDefinition.dataSourceId);
|
|
936
941
|
// Update DataProvider based on category
|
|
937
942
|
if (usage.category === 'paginated+search') {
|
|
938
|
-
updateDataProviderForPaginatedSearch(dp, usage, vars, fileName);
|
|
943
|
+
updateDataProviderForPaginatedSearch(dp, usage, vars, fileName, cacheDeclarations);
|
|
939
944
|
}
|
|
940
945
|
else if (usage.category === 'paginated-only') {
|
|
941
|
-
updateDataProviderForPaginationOnly(dp, usage, vars, fileName);
|
|
946
|
+
updateDataProviderForPaginationOnly(dp, usage, vars, fileName, cacheDeclarations);
|
|
942
947
|
}
|
|
943
948
|
else if (usage.category === 'search-only') {
|
|
944
|
-
updateDataProviderForSearchOnly(dp, usage, vars, fileName);
|
|
949
|
+
updateDataProviderForSearchOnly(dp, usage, vars, fileName, cacheDeclarations);
|
|
945
950
|
}
|
|
946
951
|
else if (usage.category === 'plain') {
|
|
947
952
|
updateDataProviderForPlain(dp, fileName, usage);
|
|
@@ -955,12 +960,37 @@ export var createNextArrayMapperPaginationPlugin = function () {
|
|
|
955
960
|
loadingStateDeclarations.push.apply(loadingStateDeclarations, buildLoadingStateDeclarations(loadingVars));
|
|
956
961
|
}
|
|
957
962
|
// Create API route for all categories (including 'plain' for components)
|
|
958
|
-
ensureAPIRouteExists(options.extractedResources, usage, options.dataSources, (
|
|
963
|
+
ensureAPIRouteExists(options.extractedResources, usage, options.dataSources, buildProductTransformOptions(options));
|
|
959
964
|
});
|
|
965
|
+
sharedOptions = options;
|
|
966
|
+
if (!sharedOptions.serverCacheByFile) {
|
|
967
|
+
sharedOptions.serverCacheByFile = new Map();
|
|
968
|
+
}
|
|
969
|
+
applyServerCacheToDataSourceModules(options.extractedResources, registry, options.dataSources, buildProductTransformOptions(options), sharedOptions.serverCacheByFile);
|
|
960
970
|
// Declare the loading-state hooks alongside the other data-source state, at
|
|
961
971
|
// the top of the component body. Unconditional and in a fixed order, so the
|
|
962
972
|
// hook order stays stable across renders.
|
|
963
973
|
loadingStateDeclarations.reverse().forEach(function (s) { return blockStatement.body.unshift(s); });
|
|
974
|
+
cachedScopes = Array.from(new Set(registry.usages
|
|
975
|
+
.filter(function (usage) { var _a; return (_a = usage.cache) === null || _a === void 0 ? void 0 : _a.client; })
|
|
976
|
+
.map(function (usage) { return usage.cache.scope; })));
|
|
977
|
+
if (cacheDeclarations.length || cachedScopes.length) {
|
|
978
|
+
// One effect per page covering every scope on it: flips the hydration
|
|
979
|
+
// latch, then asks once whether any of this page's tables changed while
|
|
980
|
+
// the visitor was away. Without it a browser serving everything from its
|
|
981
|
+
// own cache would never learn about a write at all. It has to land
|
|
982
|
+
// BEFORE the component's `return` — a plain `push` at this point in the
|
|
983
|
+
// pipeline appends after it, where React never registers the hook.
|
|
984
|
+
ASTStatementOrder.insertStatementsBeforeReturn(blockStatement, [
|
|
985
|
+
buildCacheHydrationEffect(cachedScopes),
|
|
986
|
+
]);
|
|
987
|
+
// `ds_N_params` / `ds_N_cached` read the `ds_N_state` hooks declared
|
|
988
|
+
// above, so they are placed at the earliest point where everything they
|
|
989
|
+
// reference is already in scope — never blindly at the top, which is
|
|
990
|
+
// exactly the `Cannot access 'ds_0_state' before initialization` crash.
|
|
991
|
+
ASTStatementOrder.insertStatementsAfterDependencies(blockStatement, cacheDeclarations);
|
|
992
|
+
registerCacheClientImports(dependencies, (_a = uidl.outputOptions) === null || _a === void 0 ? void 0 : _a.folderPath);
|
|
993
|
+
}
|
|
964
994
|
dataProvidersWithoutRepeaters = dataProviders.filter(function (dp) {
|
|
965
995
|
var hasRepeater = findArrayMapperRenderPropInDataProvider(dp) !== undefined;
|
|
966
996
|
return !hasRepeater;
|
|
@@ -992,7 +1022,7 @@ export var createNextArrayMapperPaginationPlugin = function () {
|
|
|
992
1022
|
});
|
|
993
1023
|
// STEP 6: Update getStaticProps if this is a page
|
|
994
1024
|
if (isPage) {
|
|
995
|
-
updateGetStaticProps(chunks, registry, dependencies, (
|
|
1025
|
+
updateGetStaticProps(chunks, registry, dependencies, (_b = uidl.outputOptions) === null || _b === void 0 ? void 0 : _b.folderPath);
|
|
996
1026
|
}
|
|
997
1027
|
return [2 /*return*/, structure];
|
|
998
1028
|
});
|
|
@@ -1160,7 +1190,40 @@ function findAllPaginationNodesInJSX(blockStatement) {
|
|
|
1160
1190
|
traverse(blockStatement);
|
|
1161
1191
|
return results;
|
|
1162
1192
|
}
|
|
1163
|
-
|
|
1193
|
+
/**
|
|
1194
|
+
* Emits the provider's `params` and, for a cached mapper, the peek beside it.
|
|
1195
|
+
*
|
|
1196
|
+
* An uncached mapper keeps the inline `params={useMemo(...)}` it has always had,
|
|
1197
|
+
* byte for byte. A cached one hoists that memo into a component const so the
|
|
1198
|
+
* peek can depend on the same object identity instead of rebuilding it.
|
|
1199
|
+
*
|
|
1200
|
+
* Returns the identifier holding the peeked rows, or `undefined` when this
|
|
1201
|
+
* mapper does not cache.
|
|
1202
|
+
*/
|
|
1203
|
+
function pushParamsAttribute(
|
|
1204
|
+
// tslint:disable-next-line:no-any
|
|
1205
|
+
dp, usage, paramsProps, memoDeps, cacheDeclarations) {
|
|
1206
|
+
var _a;
|
|
1207
|
+
var paramsMemo = types.callExpression(types.identifier('useMemo'), [
|
|
1208
|
+
types.arrowFunctionExpression([], types.objectExpression(paramsProps)),
|
|
1209
|
+
types.arrayExpression(memoDeps),
|
|
1210
|
+
]);
|
|
1211
|
+
// A state-bound sort deliberately suppresses `initialData` (see the comment at
|
|
1212
|
+
// the call site): the prefetch ran without sort params, so reusing anything on
|
|
1213
|
+
// mount would mask the current sort. The cache peek is `initialData` by
|
|
1214
|
+
// another name, so it has to be suppressed for exactly the same reason.
|
|
1215
|
+
var cache = ((_a = usage.cache) === null || _a === void 0 ? void 0 : _a.client) && !usage.dynamicSort ? usage.cache : undefined;
|
|
1216
|
+
if (!cache) {
|
|
1217
|
+
dp.openingElement.attributes.push(types.jsxAttribute(types.jsxIdentifier('params'), types.jsxExpressionContainer(paramsMemo)));
|
|
1218
|
+
return undefined;
|
|
1219
|
+
}
|
|
1220
|
+
var hoisted = buildCachedParamsDeclarations({ index: usage.index, paramsMemo: paramsMemo, cache: cache });
|
|
1221
|
+
cacheDeclarations.push.apply(cacheDeclarations, hoisted.declarations);
|
|
1222
|
+
dp.openingElement.attributes.push(types.jsxAttribute(types.jsxIdentifier('params'), types.jsxExpressionContainer(types.identifier(hoisted.paramsIdentifier))));
|
|
1223
|
+
return hoisted.cachedIdentifier;
|
|
1224
|
+
}
|
|
1225
|
+
function updateDataProviderForPaginatedSearch(dp, usage, vars, fileName, cacheDeclarations) {
|
|
1226
|
+
var _a;
|
|
1164
1227
|
var attrs = dp.openingElement.attributes;
|
|
1165
1228
|
// Remove existing params, key, initialData, fetchData, persistDataDuringLoading
|
|
1166
1229
|
dp.openingElement.attributes = attrs.filter(function (attr) {
|
|
@@ -1189,10 +1252,7 @@ function updateDataProviderForPaginatedSearch(dp, usage, vars, fileName) {
|
|
|
1189
1252
|
pushStateIdsAsDeps(memoDeps, seenDeps, usage.dynamicSort.depStateIds);
|
|
1190
1253
|
}
|
|
1191
1254
|
pushUrlSearchParamMemoDeps(memoDeps, usage);
|
|
1192
|
-
dp
|
|
1193
|
-
types.arrowFunctionExpression([], types.objectExpression(paramsProps)),
|
|
1194
|
-
types.arrayExpression(memoDeps),
|
|
1195
|
-
]))));
|
|
1255
|
+
var cachedInitialData = pushParamsAttribute(dp, usage, paramsProps, memoDeps, cacheDeclarations);
|
|
1196
1256
|
// Add initialData. Skip the server-prefetched data entirely when a dynamic
|
|
1197
1257
|
// state-bound sort is active — the prefetch ran without sort parameters, so
|
|
1198
1258
|
// reusing it would mask the current sort state AND cause DataProvider to skip
|
|
@@ -1201,7 +1261,7 @@ function updateDataProviderForPaginatedSearch(dp, usage, vars, fileName) {
|
|
|
1201
1261
|
// refetch via the useMemo params dependency chain.
|
|
1202
1262
|
if (!usage.dynamicSort) {
|
|
1203
1263
|
var initialDataCondition = wrapInitialDataWithUrlFilterGuard(types.logicalExpression('&&', types.binaryExpression('===', types.memberExpression(types.identifier(vars.combinedStateVar), types.identifier('page')), types.numericLiteral(1)), types.unaryExpression('!', types.memberExpression(types.identifier(vars.combinedStateVar), types.identifier('debouncedQuery')), true)), usage);
|
|
1204
|
-
dp.openingElement.attributes.push(types.jsxAttribute(types.jsxIdentifier('initialData'), types.jsxExpressionContainer(types.conditionalExpression(initialDataCondition, types.optionalMemberExpression(types.identifier('props'), types.identifier(vars.propsPrefix), false, true), types.identifier('undefined')))));
|
|
1264
|
+
dp.openingElement.attributes.push(types.jsxAttribute(types.jsxIdentifier('initialData'), types.jsxExpressionContainer(types.conditionalExpression(initialDataCondition, types.optionalMemberExpression(types.identifier('props'), types.identifier(vars.propsPrefix), false, true), cachedInitialData ? types.identifier(cachedInitialData) : types.identifier('undefined')))));
|
|
1205
1265
|
}
|
|
1206
1266
|
// Add key. Sort is intentionally NOT part of the key — a key change would
|
|
1207
1267
|
// remount the DataProvider, and a fresh mount re-arms the internal
|
|
@@ -1220,11 +1280,12 @@ function updateDataProviderForPaginatedSearch(dp, usage, vars, fileName) {
|
|
|
1220
1280
|
types.memberExpression(types.identifier(vars.combinedStateVar), types.identifier('debouncedQuery')),
|
|
1221
1281
|
]))));
|
|
1222
1282
|
// Add fetchData
|
|
1223
|
-
dp.openingElement.attributes.push(createFetchDataAttribute(fileName));
|
|
1283
|
+
dp.openingElement.attributes.push(createFetchDataAttribute(fileName, ((_a = usage.cache) === null || _a === void 0 ? void 0 : _a.client) ? usage.cache : undefined));
|
|
1224
1284
|
// Add persistDataDuringLoading
|
|
1225
1285
|
dp.openingElement.attributes.push(types.jsxAttribute(types.jsxIdentifier('persistDataDuringLoading'), types.jsxExpressionContainer(types.booleanLiteral(true))));
|
|
1226
1286
|
}
|
|
1227
|
-
function updateDataProviderForPaginationOnly(dp, usage, vars, fileName) {
|
|
1287
|
+
function updateDataProviderForPaginationOnly(dp, usage, vars, fileName, cacheDeclarations) {
|
|
1288
|
+
var _a;
|
|
1228
1289
|
var attrs = dp.openingElement.attributes;
|
|
1229
1290
|
dp.openingElement.attributes = attrs.filter(function (attr) {
|
|
1230
1291
|
var _a;
|
|
@@ -1249,14 +1310,11 @@ function updateDataProviderForPaginationOnly(dp, usage, vars, fileName) {
|
|
|
1249
1310
|
}
|
|
1250
1311
|
pushUrlSearchParamMemoDeps(memoDeps, usage);
|
|
1251
1312
|
// Add params
|
|
1252
|
-
dp
|
|
1253
|
-
types.arrowFunctionExpression([], types.objectExpression(paramsProps)),
|
|
1254
|
-
types.arrayExpression(memoDeps),
|
|
1255
|
-
]))));
|
|
1313
|
+
var cachedInitialData = pushParamsAttribute(dp, usage, paramsProps, memoDeps, cacheDeclarations);
|
|
1256
1314
|
// Add initialData. See paginated+search updater for why we skip prefetch
|
|
1257
1315
|
// reuse when a dynamic state-bound sort is active.
|
|
1258
1316
|
if (!usage.dynamicSort) {
|
|
1259
|
-
dp.openingElement.attributes.push(types.jsxAttribute(types.jsxIdentifier('initialData'), types.jsxExpressionContainer(types.conditionalExpression(wrapInitialDataWithUrlFilterGuard(types.binaryExpression('===', types.identifier(vars.pageStateVar), types.numericLiteral(1)), usage), types.optionalMemberExpression(types.identifier('props'), types.identifier(vars.propsPrefix), false, true), types.identifier('undefined')))));
|
|
1317
|
+
dp.openingElement.attributes.push(types.jsxAttribute(types.jsxIdentifier('initialData'), types.jsxExpressionContainer(types.conditionalExpression(wrapInitialDataWithUrlFilterGuard(types.binaryExpression('===', types.identifier(vars.pageStateVar), types.numericLiteral(1)), usage), types.optionalMemberExpression(types.identifier('props'), types.identifier(vars.propsPrefix), false, true), cachedInitialData ? types.identifier(cachedInitialData) : types.identifier('undefined')))));
|
|
1260
1318
|
}
|
|
1261
1319
|
// Add key — sort is intentionally NOT included; see paginated+search updater.
|
|
1262
1320
|
dp.openingElement.attributes.push(types.jsxAttribute(types.jsxIdentifier('key'), types.jsxExpressionContainer(types.templateLiteral([
|
|
@@ -1267,10 +1325,11 @@ function updateDataProviderForPaginationOnly(dp, usage, vars, fileName) {
|
|
|
1267
1325
|
types.templateElement({ raw: '', cooked: '' }),
|
|
1268
1326
|
], [types.identifier(vars.pageStateVar)]))));
|
|
1269
1327
|
// Add fetchData
|
|
1270
|
-
dp.openingElement.attributes.push(createFetchDataAttribute(fileName));
|
|
1328
|
+
dp.openingElement.attributes.push(createFetchDataAttribute(fileName, ((_a = usage.cache) === null || _a === void 0 ? void 0 : _a.client) ? usage.cache : undefined));
|
|
1271
1329
|
dp.openingElement.attributes.push(types.jsxAttribute(types.jsxIdentifier('persistDataDuringLoading'), types.jsxExpressionContainer(types.booleanLiteral(true))));
|
|
1272
1330
|
}
|
|
1273
|
-
function updateDataProviderForSearchOnly(dp, usage, vars, fileName) {
|
|
1331
|
+
function updateDataProviderForSearchOnly(dp, usage, vars, fileName, cacheDeclarations) {
|
|
1332
|
+
var _a;
|
|
1274
1333
|
var attrs = dp.openingElement.attributes;
|
|
1275
1334
|
dp.openingElement.attributes = attrs.filter(function (attr) {
|
|
1276
1335
|
var _a;
|
|
@@ -1296,14 +1355,11 @@ function updateDataProviderForSearchOnly(dp, usage, vars, fileName) {
|
|
|
1296
1355
|
pushStateIdsAsDeps(memoDeps, seenDeps, usage.dynamicSort.depStateIds);
|
|
1297
1356
|
}
|
|
1298
1357
|
pushUrlSearchParamMemoDeps(memoDeps, usage);
|
|
1299
|
-
dp
|
|
1300
|
-
types.arrowFunctionExpression([], types.objectExpression(paramsProps)),
|
|
1301
|
-
types.arrayExpression(memoDeps),
|
|
1302
|
-
]))));
|
|
1358
|
+
var cachedInitialData = pushParamsAttribute(dp, usage, paramsProps, memoDeps, cacheDeclarations);
|
|
1303
1359
|
// Add initialData. See paginated+search updater for why we skip prefetch
|
|
1304
1360
|
// reuse when a dynamic state-bound sort is active.
|
|
1305
1361
|
if (!usage.dynamicSort) {
|
|
1306
|
-
dp.openingElement.attributes.push(types.jsxAttribute(types.jsxIdentifier('initialData'), types.jsxExpressionContainer(types.conditionalExpression(wrapInitialDataWithUrlFilterGuard(types.unaryExpression('!', types.identifier(vars.debouncedSearchQueryVar), true), usage), types.optionalMemberExpression(types.identifier('props'), types.identifier(vars.propsPrefix), false, true), types.identifier('undefined')))));
|
|
1362
|
+
dp.openingElement.attributes.push(types.jsxAttribute(types.jsxIdentifier('initialData'), types.jsxExpressionContainer(types.conditionalExpression(wrapInitialDataWithUrlFilterGuard(types.unaryExpression('!', types.identifier(vars.debouncedSearchQueryVar), true), usage), types.optionalMemberExpression(types.identifier('props'), types.identifier(vars.propsPrefix), false, true), cachedInitialData ? types.identifier(cachedInitialData) : types.identifier('undefined')))));
|
|
1307
1363
|
}
|
|
1308
1364
|
// Add key — sort is intentionally NOT included; see paginated+search updater.
|
|
1309
1365
|
dp.openingElement.attributes.push(types.jsxAttribute(types.jsxIdentifier('key'), types.jsxExpressionContainer(types.templateLiteral([
|
|
@@ -1311,16 +1367,23 @@ function updateDataProviderForSearchOnly(dp, usage, vars, fileName) {
|
|
|
1311
1367
|
types.templateElement({ raw: '', cooked: '' }),
|
|
1312
1368
|
], [types.identifier(vars.debouncedSearchQueryVar)]))));
|
|
1313
1369
|
// Add fetchData
|
|
1314
|
-
dp.openingElement.attributes.push(createFetchDataAttribute(fileName));
|
|
1370
|
+
dp.openingElement.attributes.push(createFetchDataAttribute(fileName, ((_a = usage.cache) === null || _a === void 0 ? void 0 : _a.client) ? usage.cache : undefined));
|
|
1315
1371
|
dp.openingElement.attributes.push(types.jsxAttribute(types.jsxIdentifier('persistDataDuringLoading'), types.jsxExpressionContainer(types.booleanLiteral(true))));
|
|
1316
1372
|
}
|
|
1317
1373
|
function updateDataProviderForPlain(dp, fileName, usage) {
|
|
1318
|
-
var _a, _b, _c;
|
|
1374
|
+
var _a, _b, _c, _d;
|
|
1319
1375
|
var attrs = dp.openingElement.attributes;
|
|
1376
|
+
var clientCache = ((_a = usage.cache) === null || _a === void 0 ? void 0 : _a.client) ? usage.cache : undefined;
|
|
1320
1377
|
// Check if fetchData already exists
|
|
1321
1378
|
var hasFetchData = attrs.some(function (attr) { var _a; return attr.type === 'JSXAttribute' && ((_a = attr.name) === null || _a === void 0 ? void 0 : _a.name) === 'fetchData'; });
|
|
1322
|
-
|
|
1323
|
-
|
|
1379
|
+
if (clientCache) {
|
|
1380
|
+
// A plain provider may already carry the UNMEMOIZED fetcher `utils.ts`
|
|
1381
|
+
// emits. Caching has to replace it rather than skip: the memoized form is
|
|
1382
|
+
// what both the cache peek and the in-flight tracking are attached to.
|
|
1383
|
+
dp.openingElement.attributes = attrs.filter(function (attr) { var _a; return !(attr.type === 'JSXAttribute' && ((_a = attr.name) === null || _a === void 0 ? void 0 : _a.name) === 'fetchData'); });
|
|
1384
|
+
dp.openingElement.attributes.push(createFetchDataAttribute(fileName, clientCache));
|
|
1385
|
+
}
|
|
1386
|
+
else if (!hasFetchData) {
|
|
1324
1387
|
attrs.push(createFetchDataAttribute(fileName));
|
|
1325
1388
|
}
|
|
1326
1389
|
// Check if persistDataDuringLoading already exists
|
|
@@ -1336,14 +1399,14 @@ function updateDataProviderForPlain(dp, fileName, usage) {
|
|
|
1336
1399
|
}
|
|
1337
1400
|
var paramsAttr = attrs[paramsAttrIndex];
|
|
1338
1401
|
// Check if params is already wrapped in useMemo
|
|
1339
|
-
if (((
|
|
1402
|
+
if (((_b = paramsAttr.value) === null || _b === void 0 ? void 0 : _b.type) === 'JSXExpressionContainer' &&
|
|
1340
1403
|
paramsAttr.value.expression.type === 'CallExpression' &&
|
|
1341
|
-
((
|
|
1404
|
+
((_c = paramsAttr.value.expression.callee) === null || _c === void 0 ? void 0 : _c.name) === 'useMemo') {
|
|
1342
1405
|
return;
|
|
1343
1406
|
}
|
|
1344
1407
|
// Get the current params value expression
|
|
1345
1408
|
var paramsExpression = null;
|
|
1346
|
-
if (((
|
|
1409
|
+
if (((_d = paramsAttr.value) === null || _d === void 0 ? void 0 : _d.type) === 'JSXExpressionContainer') {
|
|
1347
1410
|
paramsExpression = paramsAttr.value.expression;
|
|
1348
1411
|
}
|
|
1349
1412
|
if (!paramsExpression) {
|
|
@@ -1395,32 +1458,32 @@ function stabilizeDataProviderWithoutRepeater(dp) {
|
|
|
1395
1458
|
// Replace the params attribute
|
|
1396
1459
|
attrs[paramsAttrIndex] = types.jsxAttribute(types.jsxIdentifier('params'), types.jsxExpressionContainer(memoizedParams));
|
|
1397
1460
|
}
|
|
1398
|
-
function createFetchDataAttribute(fileName) {
|
|
1399
|
-
|
|
1400
|
-
types.
|
|
1401
|
-
types.
|
|
1402
|
-
|
|
1403
|
-
|
|
1404
|
-
|
|
1405
|
-
|
|
1406
|
-
|
|
1407
|
-
|
|
1408
|
-
|
|
1409
|
-
|
|
1410
|
-
|
|
1411
|
-
|
|
1412
|
-
|
|
1413
|
-
|
|
1414
|
-
|
|
1415
|
-
|
|
1416
|
-
|
|
1417
|
-
|
|
1418
|
-
|
|
1419
|
-
|
|
1420
|
-
|
|
1421
|
-
]))
|
|
1422
|
-
types.
|
|
1423
|
-
])));
|
|
1461
|
+
function createFetchDataAttribute(fileName, cache) {
|
|
1462
|
+
var fetchCall = types.callExpression(types.identifier('fetch'), [
|
|
1463
|
+
types.templateLiteral([
|
|
1464
|
+
types.templateElement({
|
|
1465
|
+
raw: "/api/".concat(fileName, "?"),
|
|
1466
|
+
cooked: "/api/".concat(fileName, "?"),
|
|
1467
|
+
}),
|
|
1468
|
+
types.templateElement({ raw: '', cooked: '' }),
|
|
1469
|
+
], [types.newExpression(types.identifier('URLSearchParams'), [types.identifier('params')])]),
|
|
1470
|
+
types.objectExpression([
|
|
1471
|
+
types.objectProperty(types.identifier('headers'), types.objectExpression([
|
|
1472
|
+
types.objectProperty(types.stringLiteral('Content-Type'), types.stringLiteral('application/json')),
|
|
1473
|
+
])),
|
|
1474
|
+
]),
|
|
1475
|
+
]);
|
|
1476
|
+
var parsedJson = types.callExpression(types.memberExpression(fetchCall, types.identifier('then')), [
|
|
1477
|
+
types.arrowFunctionExpression([types.identifier('res')], types.callExpression(types.memberExpression(types.identifier('res'), types.identifier('json')), [])),
|
|
1478
|
+
]);
|
|
1479
|
+
// The uncached tail is left exactly as it has always been emitted, so a
|
|
1480
|
+
// project that does not cache produces byte-identical output.
|
|
1481
|
+
var uncachedTail = types.arrowFunctionExpression([types.identifier('response')], types.optionalMemberExpression(types.identifier('response'), types.identifier('data'), false, true));
|
|
1482
|
+
var networkChain = types.callExpression(types.memberExpression(parsedJson, types.identifier('then')), [cache ? buildCacheStoreThen(cache) : uncachedTail]);
|
|
1483
|
+
var arrow = cache
|
|
1484
|
+
? types.arrowFunctionExpression([types.identifier('params')], buildCachedFetchBody(networkChain, cache))
|
|
1485
|
+
: types.arrowFunctionExpression([types.identifier('params')], networkChain);
|
|
1486
|
+
return types.jsxAttribute(types.jsxIdentifier('fetchData'), types.jsxExpressionContainer(types.callExpression(types.identifier('useCallback'), [arrow, types.arrayExpression([])])));
|
|
1424
1487
|
}
|
|
1425
1488
|
function wireSearchInput(inputNode, vars) {
|
|
1426
1489
|
// Remove existing onChange and value
|
|
@@ -1527,7 +1590,84 @@ function wirePaginationButtons(paginationNode, usage, vars) {
|
|
|
1527
1590
|
}
|
|
1528
1591
|
}
|
|
1529
1592
|
}
|
|
1530
|
-
|
|
1593
|
+
/**
|
|
1594
|
+
* Applies the SERVER cache to the generated data-source modules, once, after
|
|
1595
|
+
* every provider has been visited.
|
|
1596
|
+
*
|
|
1597
|
+
* A post-pass rather than a parameter to `ensureAPIRouteExists` because four
|
|
1598
|
+
* different places race to create `utils/data-sources/<file>.js` — the
|
|
1599
|
+
* getStaticProps path, the component path, the client-API path and this plugin —
|
|
1600
|
+
* and whichever wins would otherwise decide whether the file is cached. Since
|
|
1601
|
+
* the generator is deterministic, regenerating the winner with the cache options
|
|
1602
|
+
* is order-independent and cannot half-apply.
|
|
1603
|
+
*
|
|
1604
|
+
* The config is merged per FILE, not per mapper: one module serves every mapper
|
|
1605
|
+
* reading that `(type, table, dataSourceId)` triple, so two mappers on two pages
|
|
1606
|
+
* have to agree — and the fresher one wins (see `mergeServerCacheOptions`).
|
|
1607
|
+
*/
|
|
1608
|
+
function applyServerCacheToDataSourceModules(
|
|
1609
|
+
// tslint:disable-next-line:no-any
|
|
1610
|
+
extractedResources, registry,
|
|
1611
|
+
// tslint:disable-next-line:no-any
|
|
1612
|
+
dataSources, transformOptions,
|
|
1613
|
+
// Accumulated across every page/component this generation has processed. The
|
|
1614
|
+
// module is shared project-wide, so merging only the CURRENT page's usages
|
|
1615
|
+
// would let page ORDER decide the TTL: a page asking for 300s processed after
|
|
1616
|
+
// one asking for 60s would widen the window the second page was configured
|
|
1617
|
+
// with. Carried on the shared options object so the merge stays global.
|
|
1618
|
+
accumulator) {
|
|
1619
|
+
if (transformOptions === void 0) { transformOptions = {}; }
|
|
1620
|
+
var byFileName = accumulator !== null && accumulator !== void 0 ? accumulator : new Map();
|
|
1621
|
+
registry.usages.forEach(function (usage) {
|
|
1622
|
+
var _a;
|
|
1623
|
+
if (!((_a = usage.cache) === null || _a === void 0 ? void 0 : _a.server)) {
|
|
1624
|
+
return;
|
|
1625
|
+
}
|
|
1626
|
+
var fileName = generateSafeFileName(usage.resourceDefinition.dataSourceType, usage.resourceDefinition.tableName, usage.resourceDefinition.dataSourceId);
|
|
1627
|
+
var bucket = byFileName.get(fileName) || { usage: usage, entries: [] };
|
|
1628
|
+
bucket.entries.push({
|
|
1629
|
+
scope: usage.cache.scope,
|
|
1630
|
+
ttlSeconds: usage.cache.ttlSeconds,
|
|
1631
|
+
sMaxAge: usage.cache.sMaxAge,
|
|
1632
|
+
staleWhileRevalidate: usage.cache.staleWhileRevalidate,
|
|
1633
|
+
});
|
|
1634
|
+
byFileName.set(fileName, bucket);
|
|
1635
|
+
});
|
|
1636
|
+
byFileName.forEach(function (bucket, fileName) {
|
|
1637
|
+
var cacheOptions = mergeServerCacheOptions(bucket.entries);
|
|
1638
|
+
if (!cacheOptions) {
|
|
1639
|
+
return;
|
|
1640
|
+
}
|
|
1641
|
+
var dataSource = dataSources[bucket.usage.resourceDefinition.dataSourceId];
|
|
1642
|
+
if (!dataSource) {
|
|
1643
|
+
return;
|
|
1644
|
+
}
|
|
1645
|
+
var tableName = bucket.usage.resourceDefinition.tableName || 'data';
|
|
1646
|
+
var utilsKey = "utils/".concat(fileName);
|
|
1647
|
+
var apiKey = "api/".concat(fileName);
|
|
1648
|
+
try {
|
|
1649
|
+
if (extractedResources[utilsKey]) {
|
|
1650
|
+
extractedResources[utilsKey].content = generateDataSourceFetcherWithCore(dataSource, tableName, false, transformOptions, cacheOptions);
|
|
1651
|
+
return;
|
|
1652
|
+
}
|
|
1653
|
+
// The variant that inlines the whole fetcher straight into `pages/api`
|
|
1654
|
+
// instead of re-exporting a utils module. Detected by content rather than
|
|
1655
|
+
// by which code path produced it, so a future fifth emitter is covered too.
|
|
1656
|
+
var apiEntry = extractedResources[apiKey];
|
|
1657
|
+
if (apiEntry &&
|
|
1658
|
+
typeof apiEntry.content === 'string' &&
|
|
1659
|
+
apiEntry.content.includes('async function handler')) {
|
|
1660
|
+
apiEntry.content = generateDataSourceFetcherWithCore(dataSource, tableName, true, transformOptions, cacheOptions);
|
|
1661
|
+
}
|
|
1662
|
+
}
|
|
1663
|
+
catch (error) {
|
|
1664
|
+
// Caching must never be the reason a project fails to generate; the
|
|
1665
|
+
// uncached module that is already in place stays exactly as it was.
|
|
1666
|
+
}
|
|
1667
|
+
});
|
|
1668
|
+
}
|
|
1669
|
+
function ensureAPIRouteExists(extractedResources, usage, dataSources, transformOptions) {
|
|
1670
|
+
if (transformOptions === void 0) { transformOptions = {}; }
|
|
1531
1671
|
// Generate file name for the API route
|
|
1532
1672
|
var fileName = generateSafeFileName(usage.resourceDefinition.dataSourceType, usage.resourceDefinition.tableName, usage.resourceDefinition.dataSourceId);
|
|
1533
1673
|
// Check if the utils data source file exists - if not, create it
|
|
@@ -1537,7 +1677,7 @@ function ensureAPIRouteExists(extractedResources, usage, dataSources, categories
|
|
|
1537
1677
|
if (dataSource) {
|
|
1538
1678
|
try {
|
|
1539
1679
|
// Generate the utils file with fetchData, fetchCount, handler, and getCount
|
|
1540
|
-
var fetcherCode = generateDataSourceFetcherWithCore(dataSource, usage.resourceDefinition.tableName || 'data', false,
|
|
1680
|
+
var fetcherCode = generateDataSourceFetcherWithCore(dataSource, usage.resourceDefinition.tableName || 'data', false, transformOptions);
|
|
1541
1681
|
extractedResources["utils/".concat(fileName)] = {
|
|
1542
1682
|
fileName: fileName,
|
|
1543
1683
|
fileType: FileType.JS,
|