@ethlete/query 5.43.1 → 6.0.0-next.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.
@@ -1,13 +1,9 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.default = migrate;
4
- const tslib_1 = require("tslib");
5
- const devkit_1 = require("@nx/devkit");
6
- const ts = tslib_1.__importStar(require("typescript"));
7
- async function migrate(tree, schema) {
1
+ import { formatFiles, getProjects, visitNotIgnoredFiles } from '@nx/devkit';
2
+ import * as ts from 'typescript';
3
+ export default async function migrate(tree, schema) {
8
4
  const queryClientFiles = new Map(); // filepath -> config names
9
5
  const variableRenames = new Map(); // old name -> new name
10
- (0, devkit_1.visitNotIgnoredFiles)(tree, '', (filePath) => {
6
+ visitNotIgnoredFiles(tree, '', (filePath) => {
11
7
  if (!filePath.endsWith('.ts'))
12
8
  return;
13
9
  const content = tree.read(filePath, 'utf-8');
@@ -15,10 +11,6 @@ async function migrate(tree, schema) {
15
11
  return;
16
12
  let modified = false;
17
13
  let newContent = content;
18
- if (needsExperimentalQueryImport(content)) {
19
- newContent = addExperimentalQueryImport(newContent);
20
- modified = true;
21
- }
22
14
  if (hasQueryClientInstantiation(content)) {
23
15
  const renames = new Map();
24
16
  newContent = migrateQueryClientToConfig(newContent, renames);
@@ -56,7 +48,7 @@ async function migrate(tree, schema) {
56
48
  if (variableRenames.size > 0) {
57
49
  updateImportsAcrossWorkspace(tree, variableRenames);
58
50
  }
59
- // Replace AnyQuery with E.AnyLegacyQuery everywhere
51
+ // Replace AnyV2Query with AnyLegacyQuery everywhere
60
52
  replaceAnyQueryWithLegacy(tree);
61
53
  // Remove devtools usage
62
54
  removeDevtoolsUsage(tree);
@@ -65,43 +57,10 @@ async function migrate(tree, schema) {
65
57
  // Check for legacy query creator usage (step 1: just collect and report)
66
58
  checkLegacyQueryCreatorUsage(tree);
67
59
  if (!schema.skipFormat) {
68
- await (0, devkit_1.formatFiles)(tree);
60
+ await formatFiles(tree);
69
61
  }
70
62
  }
71
63
  //#endregion
72
- //#region import ExperimentalQuery as E
73
- function needsExperimentalQueryImport(content) {
74
- // Check if file imports from @ethlete/query
75
- const hasQueryImport = /from\s+['"]@ethlete\/query['"]/.test(content);
76
- // Check if already has ExperimentalQuery import
77
- const hasExperimentalQueryImport = /import\s*{[^}]*ExperimentalQuery/.test(content);
78
- return hasQueryImport && !hasExperimentalQueryImport;
79
- }
80
- function addExperimentalQueryImport(content) {
81
- const sourceFile = ts.createSourceFile('temp.ts', content, ts.ScriptTarget.Latest, true);
82
- let queryImportNode;
83
- ts.forEachChild(sourceFile, (node) => {
84
- if (ts.isImportDeclaration(node) &&
85
- ts.isStringLiteral(node.moduleSpecifier) &&
86
- node.moduleSpecifier.text === '@ethlete/query') {
87
- queryImportNode = node;
88
- }
89
- });
90
- if (!queryImportNode || !queryImportNode.importClause?.namedBindings) {
91
- return content;
92
- }
93
- const namedBindings = queryImportNode.importClause.namedBindings;
94
- if (!ts.isNamedImports(namedBindings)) {
95
- return content;
96
- }
97
- const importStart = queryImportNode.getStart(sourceFile);
98
- const importEnd = queryImportNode.getEnd();
99
- const existingImports = namedBindings.elements.map((el) => el.getText(sourceFile));
100
- const newImports = [...existingImports, 'ExperimentalQuery as E'];
101
- const newImportStatement = `import { ${newImports.join(', ')} } from '@ethlete/query';`;
102
- return content.slice(0, importStart) + newImportStatement + content.slice(importEnd);
103
- }
104
- //#endregion
105
64
  //#region Query client to config
106
65
  function hasQueryClientInstantiation(content) {
107
66
  const sourceFile = ts.createSourceFile('temp.ts', content, ts.ScriptTarget.Latest, true);
@@ -113,12 +72,12 @@ function hasQueryClientInstantiation(content) {
113
72
  node.moduleSpecifier.text === '@ethlete/query' &&
114
73
  node.importClause?.namedBindings &&
115
74
  ts.isNamedImports(node.importClause.namedBindings)) {
116
- const hasQueryClient = node.importClause.namedBindings.elements.some((el) => el.name.text === 'QueryClient');
75
+ const hasQueryClient = node.importClause.namedBindings.elements.some((el) => el.name.text === 'V2QueryClient');
117
76
  if (hasQueryClient) {
118
77
  hasQueryClientImport = true;
119
78
  }
120
79
  }
121
- if (ts.isNewExpression(node) && ts.isIdentifier(node.expression) && node.expression.text === 'QueryClient') {
80
+ if (ts.isNewExpression(node) && ts.isIdentifier(node.expression) && node.expression.text === 'V2QueryClient') {
122
81
  hasNewQueryClient = true;
123
82
  }
124
83
  ts.forEachChild(node, visit);
@@ -134,7 +93,7 @@ function migrateQueryClientToConfig(content, renamesOut) {
134
93
  // Find: new QueryClient({ baseRoute: '...' })
135
94
  if (ts.isNewExpression(node) &&
136
95
  ts.isIdentifier(node.expression) &&
137
- node.expression.text === 'QueryClient' &&
96
+ node.expression.text === 'V2QueryClient' &&
138
97
  node.arguments &&
139
98
  node.arguments.length > 0) {
140
99
  const configArg = node.arguments[0];
@@ -188,25 +147,68 @@ function removeQueryClientImport(content) {
188
147
  }
189
148
  });
190
149
  if (!queryImportNode?.importClause?.namedBindings || !ts.isNamedImports(queryImportNode.importClause.namedBindings)) {
191
- return content;
150
+ // No @ethlete/query import exists - create one with necessary imports
151
+ const newImportsNeeded = [
152
+ 'createQueryClientConfig',
153
+ 'createGetQuery',
154
+ 'createPostQuery',
155
+ 'createPutQuery',
156
+ 'createPatchQuery',
157
+ 'createDeleteQuery',
158
+ ];
159
+ // Find last import to insert after it
160
+ let lastImportEnd = 0;
161
+ ts.forEachChild(sourceFile, (node) => {
162
+ if (ts.isImportDeclaration(node)) {
163
+ const end = node.getEnd();
164
+ if (end > lastImportEnd) {
165
+ lastImportEnd = end;
166
+ }
167
+ }
168
+ });
169
+ const newImportStatement = `\nimport { ${newImportsNeeded.join(', ')} } from '@ethlete/query';`;
170
+ if (lastImportEnd > 0) {
171
+ return content.slice(0, lastImportEnd) + newImportStatement + content.slice(lastImportEnd);
172
+ }
173
+ return `import { ${newImportsNeeded.join(', ')} } from '@ethlete/query';\n\n${content}`;
192
174
  }
193
175
  const namedBindings = queryImportNode.importClause.namedBindings;
194
- const elements = namedBindings.elements.filter((el) => el.name.text !== 'QueryClient');
195
- // If no imports left, remove the entire import statement
196
- if (elements.length === 0) {
176
+ const elements = namedBindings.elements.filter((el) => el.name.text !== 'V2QueryClient');
177
+ // Add the new imports we need
178
+ const newImportsNeeded = new Set([
179
+ 'createQueryClientConfig',
180
+ 'createGetQuery',
181
+ 'createPostQuery',
182
+ 'createPutQuery',
183
+ 'createPatchQuery',
184
+ 'createDeleteQuery',
185
+ ]);
186
+ // Check which imports already exist
187
+ elements.forEach((el) => {
188
+ newImportsNeeded.delete(el.name.text);
189
+ });
190
+ // Combine existing imports with new ones
191
+ const allImports = [...elements.map((el) => el.getText(sourceFile)), ...Array.from(newImportsNeeded).sort()].sort();
192
+ // If no imports left after removing V2QueryClient, we still need to add the new ones
193
+ if (elements.length === 0 && newImportsNeeded.size > 0) {
194
+ const newImportStatement = `import { ${Array.from(newImportsNeeded).sort().join(', ')} } from '@ethlete/query';`;
197
195
  const importStart = queryImportNode.getStart(sourceFile);
198
196
  const importEnd = queryImportNode.getEnd();
199
- // Also remove the newline after the import
200
- const nextChar = content[importEnd];
201
- const endPos = nextChar === '\n' ? importEnd + 1 : importEnd;
202
- return content.slice(0, importStart) + content.slice(endPos);
197
+ return content.slice(0, importStart) + newImportStatement + content.slice(importEnd);
198
+ }
199
+ // If we removed V2QueryClient but have other imports, reconstruct with new ones
200
+ if (elements.length > 0 || newImportsNeeded.size > 0) {
201
+ const importStart = queryImportNode.getStart(sourceFile);
202
+ const importEnd = queryImportNode.getEnd();
203
+ const newImportStatement = `import { ${allImports.join(', ')} } from '@ethlete/query';`;
204
+ return content.slice(0, importStart) + newImportStatement + content.slice(importEnd);
203
205
  }
204
- // Otherwise, reconstruct import without QueryClient
206
+ // If truly no imports needed, remove the import line
205
207
  const importStart = queryImportNode.getStart(sourceFile);
206
208
  const importEnd = queryImportNode.getEnd();
207
- const newImports = elements.map((el) => el.getText(sourceFile));
208
- const newImportStatement = `import { ${newImports.join(', ')} } from '@ethlete/query';`;
209
- return content.slice(0, importStart) + newImportStatement + content.slice(importEnd);
209
+ const nextChar = content[importEnd];
210
+ const endPos = nextChar === '\n' ? importEnd + 1 : importEnd;
211
+ return content.slice(0, importStart) + content.slice(endPos);
210
212
  }
211
213
  function renameVariables(content, renames) {
212
214
  if (renames.size === 0)
@@ -293,7 +295,7 @@ function migrateConfigObject(configArg, node, sourceFile) {
293
295
  newConfig.push(`retryFn: ${retryFnValue}`);
294
296
  }
295
297
  }
296
- return `E.createQueryClientConfig({\n ${newConfig.join(',\n ')}\n})`;
298
+ return `createQueryClientConfig({\n ${newConfig.join(',\n ')}\n})`;
297
299
  }
298
300
  function getVariableNameForQueryClient(node, sourceFile) {
299
301
  let parent = node.parent;
@@ -310,17 +312,13 @@ function extractClientConfigNames(content) {
310
312
  const sourceFile = ts.createSourceFile('temp.ts', content, ts.ScriptTarget.Latest, true);
311
313
  const configNames = [];
312
314
  function visit(node) {
313
- // Find: const XXX = E.createQueryClientConfig(...)
315
+ // Find: const XXX = createQueryClientConfig(...)
314
316
  if (ts.isVariableDeclaration(node) &&
315
317
  ts.isIdentifier(node.name) &&
316
318
  node.initializer &&
317
319
  ts.isCallExpression(node.initializer)) {
318
320
  const callExpr = node.initializer;
319
- if (ts.isPropertyAccessExpression(callExpr.expression) &&
320
- ts.isIdentifier(callExpr.expression.expression) &&
321
- callExpr.expression.expression.text === 'E' &&
322
- ts.isIdentifier(callExpr.expression.name) &&
323
- callExpr.expression.name.text === 'createQueryClientConfig') {
321
+ if (ts.isIdentifier(callExpr.expression) && callExpr.expression.text === 'createQueryClientConfig') {
324
322
  // This extracts the NEW variable name (apiClientConfig)
325
323
  // But we should extract the name from the config object instead!
326
324
  const nameArg = callExpr.arguments[0];
@@ -340,7 +338,7 @@ function extractClientConfigNames(content) {
340
338
  //#endregion
341
339
  //#region Add query client providers to apps
342
340
  function updateDependentApps(tree, queryClientFiles) {
343
- const projects = (0, devkit_1.getProjects)(tree);
341
+ const projects = getProjects(tree);
344
342
  const updatedApps = [];
345
343
  const warnedApps = [];
346
344
  // Find all apps
@@ -397,6 +395,8 @@ function findImportedClientConfigs(content, queryClientFiles) {
397
395
  return importedConfigs;
398
396
  }
399
397
  function addQueryClientProviders(content, clientConfigs) {
398
+ // Ensure provideQueryClient is imported
399
+ content = ensureImportFromQuery(content, ['provideQueryClient']);
400
400
  const sourceFile = ts.createSourceFile('temp.ts', content, ts.ScriptTarget.Latest, true);
401
401
  let providersArrayNode;
402
402
  function visit(node) {
@@ -411,7 +411,7 @@ function addQueryClientProviders(content, clientConfigs) {
411
411
  visit(sourceFile);
412
412
  if (!providersArrayNode)
413
413
  return content;
414
- const newProviders = clientConfigs.map((config) => `E.provideQueryClient(${config})`);
414
+ const newProviders = clientConfigs.map((config) => `provideQueryClient(${config})`);
415
415
  const elements = providersArrayNode.elements;
416
416
  if (elements.length > 0) {
417
417
  const insertPosition = elements[elements.length - 1].getEnd();
@@ -428,7 +428,7 @@ function addQueryClientProviders(content, clientConfigs) {
428
428
  //#region Update imports across workspace
429
429
  function updateImportsAcrossWorkspace(tree, renames) {
430
430
  const updatedFiles = [];
431
- (0, devkit_1.visitNotIgnoredFiles)(tree, '', (filePath) => {
431
+ visitNotIgnoredFiles(tree, '', (filePath) => {
432
432
  if (!filePath.endsWith('.ts'))
433
433
  return;
434
434
  const content = tree.read(filePath, 'utf-8');
@@ -520,11 +520,7 @@ function addQueryCreatorsToFile(content, configNames) {
520
520
  node.initializer &&
521
521
  ts.isCallExpression(node.initializer)) {
522
522
  const callExpr = node.initializer;
523
- if (ts.isPropertyAccessExpression(callExpr.expression) &&
524
- ts.isIdentifier(callExpr.expression.expression) &&
525
- callExpr.expression.expression.text === 'E' &&
526
- ts.isIdentifier(callExpr.expression.name) &&
527
- callExpr.expression.name.text === 'createQueryClientConfig') {
523
+ if (ts.isIdentifier(callExpr.expression) && callExpr.expression.text === 'createQueryClientConfig') {
528
524
  const configName = node.name.text;
529
525
  // Find the variable statement (const/let/var line)
530
526
  let parent = node.parent;
@@ -559,11 +555,11 @@ function addQueryCreatorsToFile(content, configNames) {
559
555
  function generateCreatorsForConfig(configName) {
560
556
  const configNameWithoutClientSuffix = configName.replace('Client', '');
561
557
  const creators = [
562
- `export const ${configNameWithoutClientSuffix}Get = E.createGetQuery(${configName}Config);`,
563
- `export const ${configNameWithoutClientSuffix}Post = E.createPostQuery(${configName}Config);`,
564
- `export const ${configNameWithoutClientSuffix}Put = E.createPutQuery(${configName}Config);`,
565
- `export const ${configNameWithoutClientSuffix}Patch = E.createPatchQuery(${configName}Config);`,
566
- `export const ${configNameWithoutClientSuffix}Delete = E.createDeleteQuery(${configName}Config);`,
558
+ `export const ${configNameWithoutClientSuffix}Get = createGetQuery(${configName}Config);`,
559
+ `export const ${configNameWithoutClientSuffix}Post = createPostQuery(${configName}Config);`,
560
+ `export const ${configNameWithoutClientSuffix}Put = createPutQuery(${configName}Config);`,
561
+ `export const ${configNameWithoutClientSuffix}Patch = createPatchQuery(${configName}Config);`,
562
+ `export const ${configNameWithoutClientSuffix}Delete = createDeleteQuery(${configName}Config);`,
567
563
  ];
568
564
  return creators.join('\n');
569
565
  }
@@ -583,7 +579,7 @@ function createNewQueryCreators(tree, queryClientFiles) {
583
579
  });
584
580
  });
585
581
  // Step 1: Find all legacy query creators and analyze them
586
- (0, devkit_1.visitNotIgnoredFiles)(tree, '', (filePath) => {
582
+ visitNotIgnoredFiles(tree, '', (filePath) => {
587
583
  if (!filePath.endsWith('.ts') || filePath.endsWith('.spec.ts'))
588
584
  return;
589
585
  const content = tree.read(filePath, 'utf-8');
@@ -748,6 +744,8 @@ function transformLegacyQueryCreators(content, legacyCreators, queryClientFiles)
748
744
  for (const { start, end, replacement } of replacements) {
749
745
  result = result.slice(0, start) + replacement + result.slice(end);
750
746
  }
747
+ // Ensure createLegacyQueryCreator is imported
748
+ result = ensureImportFromQuery(result, ['createLegacyQueryCreator']);
751
749
  // Update imports to include necessary creator functions
752
750
  result = addCreatorImports(result, creatorsByClient);
753
751
  return result;
@@ -895,7 +893,7 @@ function generateNewQueryCreator(creator, sourceFile) {
895
893
  }
896
894
  function generateLegacyWrapper(creator) {
897
895
  const legacyName = toLegacyName(creator.name);
898
- return `export const ${legacyName} = E.createLegacyQueryCreator({ creator: ${creator.name} });`;
896
+ return `export const ${legacyName} = createLegacyQueryCreator({ creator: ${creator.name} });`;
899
897
  }
900
898
  function createAuthProviders(tree, authProvidersNeeded, queryClientFiles) {
901
899
  const createdProviders = [];
@@ -922,7 +920,7 @@ function createAuthProviders(tree, authProvidersNeeded, queryClientFiles) {
922
920
  if (createdProviders.length > 0) {
923
921
  console.log('\nāœ… Created auth providers in:');
924
922
  createdProviders.forEach((file) => console.log(` - ${file}`));
925
- console.warn('\nāš ļø Make sure to provide them in the respective app.config/main.ts providers array using E.provideBearerAuthProvider(yourConfigName)');
923
+ console.warn('\nāš ļø Make sure to provide them in the respective app.config/main.ts providers array using provideBearerAuthProvider(yourConfigName)');
926
924
  }
927
925
  }
928
926
  function addAuthProviderInjectFunction(content, clientName) {
@@ -965,6 +963,8 @@ function addAuthProviderInjectFunction(content, clientName) {
965
963
  return result.slice(0, authProviderPosition) + injectFunction + result.slice(authProviderPosition);
966
964
  }
967
965
  function addAuthProviderToFile(content, clientName, configName) {
966
+ // Ensure createBearerAuthProviderConfig is imported
967
+ content = ensureImportFromQuery(content, ['createBearerAuthProviderConfig']);
968
968
  const sourceFile = ts.createSourceFile('temp.ts', content, ts.ScriptTarget.Latest, true);
969
969
  // Find where the config is declared
970
970
  let configPosition;
@@ -974,7 +974,6 @@ function addAuthProviderToFile(content, clientName, configName) {
974
974
  node.name.text === configName &&
975
975
  node.initializer &&
976
976
  ts.isCallExpression(node.initializer)) {
977
- // Find the variable statement
978
977
  let parent = node.parent;
979
978
  while (parent && !ts.isVariableStatement(parent)) {
980
979
  parent = parent.parent;
@@ -990,13 +989,21 @@ function addAuthProviderToFile(content, clientName, configName) {
990
989
  return content;
991
990
  const authProviderCode = `
992
991
 
993
- export const ${clientName}AuthProviderConfig = E.createBearerAuthProviderConfig({
992
+ export const ${clientName}AuthProviderConfig = createBearerAuthProviderConfig({
994
993
  name: '${clientName}',
995
994
  queryClientRef: ${configName}.token,
996
995
  });`;
997
996
  return content.slice(0, configPosition) + authProviderCode + content.slice(configPosition);
998
997
  }
999
998
  function generateSecureQueryCreators(content, clientName) {
999
+ // Ensure secure query creator functions are imported
1000
+ content = ensureImportFromQuery(content, [
1001
+ 'createSecureGetQuery',
1002
+ 'createSecurePostQuery',
1003
+ 'createSecurePutQuery',
1004
+ 'createSecurePatchQuery',
1005
+ 'createSecureDeleteQuery',
1006
+ ]);
1000
1007
  const sourceFile = ts.createSourceFile('temp.ts', content, ts.ScriptTarget.Latest, true);
1001
1008
  // Find where the auth provider is declared
1002
1009
  let authProviderPosition;
@@ -1023,11 +1030,11 @@ function generateSecureQueryCreators(content, clientName) {
1023
1030
  const clientNameWithoutClient = clientName.replace('Client', '');
1024
1031
  const configName = `${clientName}Config`;
1025
1032
  const secureCreators = [
1026
- `export const ${clientNameWithoutClient}GetSecure = E.createSecureGetQuery(${configName}, ${authProviderName});`,
1027
- `export const ${clientNameWithoutClient}PostSecure = E.createSecurePostQuery(${configName}, ${authProviderName});`,
1028
- `export const ${clientNameWithoutClient}PutSecure = E.createSecurePutQuery(${configName}, ${authProviderName});`,
1029
- `export const ${clientNameWithoutClient}PatchSecure = E.createSecurePatchQuery(${configName}, ${authProviderName});`,
1030
- `export const ${clientNameWithoutClient}DeleteSecure = E.createSecureDeleteQuery(${configName}, ${authProviderName});`,
1033
+ `export const ${clientNameWithoutClient}GetSecure = createSecureGetQuery(${configName}, ${authProviderName});`,
1034
+ `export const ${clientNameWithoutClient}PostSecure = createSecurePostQuery(${configName}, ${authProviderName});`,
1035
+ `export const ${clientNameWithoutClient}PutSecure = createSecurePutQuery(${configName}, ${authProviderName});`,
1036
+ `export const ${clientNameWithoutClient}PatchSecure = createSecurePatchQuery(${configName}, ${authProviderName});`,
1037
+ `export const ${clientNameWithoutClient}DeleteSecure = createSecureDeleteQuery(${configName}, ${authProviderName});`,
1031
1038
  ].join('\n');
1032
1039
  return content.slice(0, authProviderPosition) + '\n\n' + secureCreators + content.slice(authProviderPosition);
1033
1040
  }
@@ -1037,7 +1044,7 @@ function updateLegacyCreatorImportsAndUsages(tree, queryClientFiles) {
1037
1044
  const legacyCreators = new Map(); // original name -> legacy name
1038
1045
  const updatedFiles = [];
1039
1046
  // Step 1: Find all legacy wrappers that were created
1040
- (0, devkit_1.visitNotIgnoredFiles)(tree, '', (filePath) => {
1047
+ visitNotIgnoredFiles(tree, '', (filePath) => {
1041
1048
  if (!filePath.endsWith('.ts') || filePath.endsWith('.spec.ts'))
1042
1049
  return;
1043
1050
  const content = tree.read(filePath, 'utf-8');
@@ -1052,7 +1059,7 @@ function updateLegacyCreatorImportsAndUsages(tree, queryClientFiles) {
1052
1059
  return;
1053
1060
  console.log(`\nāœ… Found ${legacyCreators.size} legacy query creator wrappers`);
1054
1061
  // Step 2: Update imports and usages across the workspace
1055
- (0, devkit_1.visitNotIgnoredFiles)(tree, '', (filePath) => {
1062
+ visitNotIgnoredFiles(tree, '', (filePath) => {
1056
1063
  if (!filePath.endsWith('.ts') || filePath.endsWith('.spec.ts'))
1057
1064
  return;
1058
1065
  const content = tree.read(filePath, 'utf-8');
@@ -1078,25 +1085,21 @@ function updateLegacyCreatorImportsAndUsages(tree, queryClientFiles) {
1078
1085
  }
1079
1086
  }
1080
1087
  function containsLegacyWrapperDefinitions(content) {
1081
- return content.includes('E.createLegacyQueryCreator');
1088
+ return content.includes('createLegacyQueryCreator');
1082
1089
  }
1083
1090
  function findLegacyWrappers(content) {
1084
1091
  const wrappers = new Map(); // original name -> legacy name
1085
1092
  const sourceFile = ts.createSourceFile('temp.ts', content, ts.ScriptTarget.Latest, true);
1086
1093
  function visit(node) {
1087
- // Find: export const legacyGetUsers = E.createLegacyQueryCreator({ creator: getUsers });
1094
+ // Find: export const legacyGetUsers = createLegacyQueryCreator({ creator: getUsers });
1088
1095
  if (ts.isVariableDeclaration(node) &&
1089
1096
  ts.isIdentifier(node.name) &&
1090
1097
  node.name.text.startsWith('legacy') &&
1091
1098
  node.initializer &&
1092
1099
  ts.isCallExpression(node.initializer)) {
1093
1100
  const callExpr = node.initializer;
1094
- // Check if it's E.createLegacyQueryCreator
1095
- if (ts.isPropertyAccessExpression(callExpr.expression) &&
1096
- ts.isIdentifier(callExpr.expression.expression) &&
1097
- callExpr.expression.expression.text === 'E' &&
1098
- ts.isIdentifier(callExpr.expression.name) &&
1099
- callExpr.expression.name.text === 'createLegacyQueryCreator') {
1101
+ // Check if it's createLegacyQueryCreator
1102
+ if (ts.isIdentifier(callExpr.expression) && callExpr.expression.text === 'createLegacyQueryCreator') {
1100
1103
  // Extract the original creator name from the config object
1101
1104
  if (callExpr.arguments.length > 0 && ts.isObjectLiteralExpression(callExpr.arguments[0])) {
1102
1105
  const configObj = callExpr.arguments[0];
@@ -1274,7 +1277,7 @@ function updateLegacyCreatorUsages(content, legacyCreators) {
1274
1277
  //#endregion
1275
1278
  //#region Remove provideQueryClientForDevtools and QueryDevtoolsComponent in ts / et-query-devtools in html usage
1276
1279
  function removeDevtoolsUsage(tree) {
1277
- const projects = (0, devkit_1.getProjects)(tree);
1280
+ const projects = getProjects(tree);
1278
1281
  const removedFromFiles = [];
1279
1282
  // Only check app config files for provideQueryClientForDevtools
1280
1283
  for (const [, projectConfig] of projects.entries()) {
@@ -1295,7 +1298,7 @@ function removeDevtoolsUsage(tree) {
1295
1298
  }
1296
1299
  }
1297
1300
  // Check all TypeScript files for QueryDevtoolsComponent imports
1298
- (0, devkit_1.visitNotIgnoredFiles)(tree, '', (filePath) => {
1301
+ visitNotIgnoredFiles(tree, '', (filePath) => {
1299
1302
  if (!filePath.endsWith('.ts'))
1300
1303
  return;
1301
1304
  const content = tree.read(filePath, 'utf-8');
@@ -1313,7 +1316,7 @@ function removeDevtoolsUsage(tree) {
1313
1316
  }
1314
1317
  });
1315
1318
  // Check all HTML files for et-query-devtools
1316
- (0, devkit_1.visitNotIgnoredFiles)(tree, '', (filePath) => {
1319
+ visitNotIgnoredFiles(tree, '', (filePath) => {
1317
1320
  if (!filePath.endsWith('.html'))
1318
1321
  return;
1319
1322
  const content = tree.read(filePath, 'utf-8');
@@ -1336,80 +1339,33 @@ function removeDevtoolsUsage(tree) {
1336
1339
  }
1337
1340
  }
1338
1341
  function removeProvideQueryClientForDevtools(content) {
1339
- const sourceFile = ts.createSourceFile('temp.ts', content, ts.ScriptTarget.Latest, true);
1340
- const replacements = [];
1341
- function visit(node) {
1342
- // Remove provideQueryClientForDevtools from provider arrays
1343
- if (ts.isCallExpression(node)) {
1344
- if (ts.isIdentifier(node.expression) && node.expression.text === 'provideQueryClientForDevtools') {
1345
- // Find the array this call is in
1346
- let parent = node.parent;
1347
- // Walk up until we find an array literal expression
1348
- while (parent) {
1349
- if (ts.isArrayLiteralExpression(parent)) {
1350
- // Find which element contains our call expression
1351
- const elementIndex = parent.elements.findIndex((el) => {
1352
- // Check if this element or any of its descendants is our node
1353
- let found = false;
1354
- function search(n) {
1355
- if (n === node) {
1356
- found = true;
1357
- return;
1358
- }
1359
- ts.forEachChild(n, search);
1360
- }
1361
- search(el);
1362
- return found;
1363
- });
1364
- if (elementIndex !== -1) {
1365
- const element = parent.elements[elementIndex];
1366
- let start = element.getStart(sourceFile);
1367
- let end = element.getEnd();
1368
- // Handle comma and whitespace removal
1369
- if (elementIndex < parent.elements.length - 1) {
1370
- // Not the last element - remove trailing comma and whitespace
1371
- const nextElement = parent.elements[elementIndex + 1];
1372
- const textBetween = content.slice(end, nextElement.getStart(sourceFile));
1373
- const commaIndex = textBetween.indexOf(',');
1374
- if (commaIndex !== -1) {
1375
- end += commaIndex + 1;
1376
- // Also remove whitespace after comma
1377
- while (end < content.length && /\s/.test(content[end])) {
1378
- end++;
1379
- }
1380
- }
1381
- }
1382
- else if (elementIndex > 0) {
1383
- // Last element - remove leading comma and whitespace
1384
- const prevElement = parent.elements[elementIndex - 1];
1385
- const textBetween = content.slice(prevElement.getEnd(), start);
1386
- const commaIndex = textBetween.lastIndexOf(',');
1387
- if (commaIndex !== -1) {
1388
- start = prevElement.getEnd() + commaIndex;
1389
- }
1390
- }
1391
- // Remove leading whitespace/newlines
1392
- while (start > 0 && /\s/.test(content[start - 1])) {
1393
- start--;
1394
- }
1395
- replacements.push({ start, end, replacement: '' });
1396
- }
1397
- break;
1398
- }
1399
- parent = parent.parent;
1400
- }
1401
- }
1402
- }
1403
- ts.forEachChild(node, visit);
1404
- }
1405
- visit(sourceFile);
1406
- // Apply replacements in reverse order
1342
+ // Simple regex approach to handle the common case
1343
+ // Match: ...(condition ? [] : [provideQueryClientForDevtools(...), ...])
1344
+ // Pattern 1: Remove entire spread when it only contains devtools
1407
1345
  let result = content;
1408
- replacements.sort((a, b) => b.start - a.start);
1409
- for (const { start, end, replacement } of replacements) {
1410
- result = result.slice(0, start) + replacement + result.slice(end);
1411
- }
1412
- // Remove the import if it exists
1346
+ // Match spread expressions that contain only provideQueryClientForDevtools calls
1347
+ // This handles: ...(env.production ? [] : [devtools, devtools])
1348
+ const spreadPattern = /\.\.\.\([^)]+\?\s*\[\]\s*:\s*\[([\s\S]*?)\]\),?\s*/g;
1349
+ result = result.replace(spreadPattern, (match, innerContent) => {
1350
+ // Check if innerContent only has provideQueryClientForDevtools calls
1351
+ const lines = innerContent.split('\n').filter((line) => line.trim());
1352
+ const hasOnlyDevtools = lines.every((line) => line.trim().startsWith('provideQueryClientForDevtools') || line.trim() === '' || line.trim() === ',');
1353
+ if (hasOnlyDevtools) {
1354
+ return ''; // Remove the entire spread
1355
+ }
1356
+ // Otherwise, remove individual provideQueryClientForDevtools calls
1357
+ const cleaned = innerContent
1358
+ .split('\n')
1359
+ .filter((line) => !line.includes('provideQueryClientForDevtools'))
1360
+ .join('\n');
1361
+ return match.replace(innerContent, cleaned);
1362
+ });
1363
+ // Pattern 2: Remove standalone provideQueryClientForDevtools in regular arrays
1364
+ result = result.replace(/^\s*provideQueryClientForDevtools\([^)]*\),?\s*$/gm, '');
1365
+ // Clean up empty lines and trailing commas
1366
+ result = result.replace(/,\s*,/g, ','); // Double commas
1367
+ result = result.replace(/\[\s*,/g, '['); // Leading comma in array
1368
+ result = result.replace(/,\s*\]/g, ']'); // Trailing comma before closing bracket
1413
1369
  return removeDevtoolsImports(result);
1414
1370
  }
1415
1371
  function removeQueryDevtoolsComponent(content) {
@@ -1515,17 +1471,17 @@ function removeDevtoolsFromHtml(content) {
1515
1471
  return result;
1516
1472
  }
1517
1473
  //#endregion
1518
- //#region Turn AnyQuery into E.AnyLegacyQuery everywhere and do the same with AnyQueryCreator
1474
+ //#region Turn AnyV2Query into AnyLegacyQuery everywhere and do the same with AnyV2QueryCreator
1519
1475
  function replaceAnyQueryWithLegacy(tree) {
1520
1476
  const updatedFiles = [];
1521
- (0, devkit_1.visitNotIgnoredFiles)(tree, '', (filePath) => {
1477
+ visitNotIgnoredFiles(tree, '', (filePath) => {
1522
1478
  if (!filePath.endsWith('.ts') || filePath.endsWith('.spec.ts'))
1523
1479
  return;
1524
1480
  const content = tree.read(filePath, 'utf-8');
1525
1481
  if (!content)
1526
1482
  return;
1527
- // Only process if it contains AnyQuery or AnyQueryCreator
1528
- if (!content.includes('AnyQuery') && !content.includes('AnyQueryCreator'))
1483
+ // Only process if it contains AnyV2Query or AnyV2QueryCreator
1484
+ if (!content.includes('AnyV2Query') && !content.includes('AnyV2QueryCreator'))
1529
1485
  return;
1530
1486
  const newContent = replaceAnyQueryInFile(content);
1531
1487
  if (newContent !== content) {
@@ -1534,7 +1490,7 @@ function replaceAnyQueryWithLegacy(tree) {
1534
1490
  }
1535
1491
  });
1536
1492
  if (updatedFiles.length > 0) {
1537
- console.log('\nāœ… Replaced AnyQuery and AnyQueryCreator with E.AnyLegacyQuery and E.AnyLegacyQueryCreator in:');
1493
+ console.log('\nāœ… Replaced AnyV2Query and AnyV2QueryCreator with AnyLegacyQuery and AnyLegacyQueryCreator in:');
1538
1494
  updatedFiles.forEach((file) => console.log(` - ${file}`));
1539
1495
  }
1540
1496
  }
@@ -1542,25 +1498,25 @@ function replaceAnyQueryInFile(content) {
1542
1498
  const sourceFile = ts.createSourceFile('temp.ts', content, ts.ScriptTarget.Latest, true);
1543
1499
  const replacements = [];
1544
1500
  function visit(node) {
1545
- // Find type references to AnyQuery and AnyQueryCreator
1501
+ // Find type references to AnyV2Query and AnyV2QueryCreator
1546
1502
  if (ts.isTypeReferenceNode(node) && ts.isIdentifier(node.typeName)) {
1547
- if (node.typeName.text === 'AnyQuery') {
1503
+ if (node.typeName.text === 'AnyV2Query') {
1548
1504
  replacements.push({
1549
1505
  start: node.getStart(sourceFile),
1550
1506
  end: node.getEnd(),
1551
- replacement: 'E.AnyLegacyQuery',
1507
+ replacement: 'AnyLegacyQuery',
1552
1508
  });
1553
1509
  }
1554
- else if (node.typeName.text === 'AnyQueryCreator') {
1510
+ else if (node.typeName.text === 'AnyV2QueryCreator') {
1555
1511
  replacements.push({
1556
1512
  start: node.getStart(sourceFile),
1557
1513
  end: node.getEnd(),
1558
- replacement: 'E.AnyLegacyQueryCreator',
1514
+ replacement: 'AnyLegacyQueryCreator',
1559
1515
  });
1560
1516
  }
1561
1517
  }
1562
1518
  // Find identifier references (for runtime usage)
1563
- if (ts.isIdentifier(node) && (node.text === 'AnyQuery' || node.text === 'AnyQueryCreator')) {
1519
+ if (ts.isIdentifier(node) && (node.text === 'AnyV2Query' || node.text === 'AnyV2QueryCreator')) {
1564
1520
  // Check if this is part of a type reference (already handled above)
1565
1521
  const parent = node.parent;
1566
1522
  if (ts.isTypeReferenceNode(parent) && parent.typeName === node) {
@@ -1573,7 +1529,7 @@ function replaceAnyQueryInFile(content) {
1573
1529
  return;
1574
1530
  }
1575
1531
  // Replace standalone references
1576
- const replacement = node.text === 'AnyQuery' ? 'E.AnyLegacyQuery' : 'E.AnyLegacyQueryCreator';
1532
+ const replacement = node.text === 'AnyV2Query' ? 'AnyLegacyQuery' : 'AnyLegacyQueryCreator';
1577
1533
  replacements.push({
1578
1534
  start: node.getStart(sourceFile),
1579
1535
  end: node.getEnd(),
@@ -1589,11 +1545,18 @@ function replaceAnyQueryInFile(content) {
1589
1545
  for (const { start, end, replacement } of replacements) {
1590
1546
  result = result.slice(0, start) + replacement + result.slice(end);
1591
1547
  }
1592
- // Remove AnyQuery and AnyQueryCreator from imports and ensure ExperimentalQuery as E exists
1548
+ // Remove AnyV2Query and AnyV2QueryCreator from imports
1593
1549
  result = removeAnyQueryFromImports(result);
1594
- // Make sure we have ExperimentalQuery as E import
1595
- if (!result.includes('ExperimentalQuery as E')) {
1596
- result = addExperimentalQueryImport(result);
1550
+ // Add AnyLegacyQuery and AnyLegacyQueryCreator imports if needed
1551
+ if (result.includes('AnyLegacyQuery') || result.includes('AnyLegacyQueryCreator')) {
1552
+ const importsToAdd = [];
1553
+ if (result.includes('AnyLegacyQuery')) {
1554
+ importsToAdd.push('AnyLegacyQuery');
1555
+ }
1556
+ if (result.includes('AnyLegacyQueryCreator')) {
1557
+ importsToAdd.push('AnyLegacyQueryCreator');
1558
+ }
1559
+ result = ensureImportFromQuery(result, importsToAdd);
1597
1560
  }
1598
1561
  return result;
1599
1562
  }
@@ -1613,7 +1576,7 @@ function removeAnyQueryFromImports(content) {
1613
1576
  return content;
1614
1577
  }
1615
1578
  const namedBindings = queryImportNode.importClause.namedBindings;
1616
- const elementsToKeep = namedBindings.elements.filter((el) => el.name.text !== 'AnyQuery' && el.name.text !== 'AnyQueryCreator');
1579
+ const elementsToKeep = namedBindings.elements.filter((el) => el.name.text !== 'AnyV2Query' && el.name.text !== 'AnyV2QueryCreator');
1617
1580
  if (elementsToKeep.length === namedBindings.elements.length) {
1618
1581
  // Nothing to remove
1619
1582
  return content;
@@ -1629,7 +1592,7 @@ function removeAnyQueryFromImports(content) {
1629
1592
  end++;
1630
1593
  return content.slice(0, start) + content.slice(end);
1631
1594
  }
1632
- // Reconstruct import without AnyQuery and AnyQueryCreator
1595
+ // Reconstruct import without AnyV2Query and AnyV2QueryCreator
1633
1596
  const newImports = elementsToKeep.map((el) => el.getText(sourceFile));
1634
1597
  const moduleSpecifier = queryImportNode.moduleSpecifier.text;
1635
1598
  const newImportStatement = `import { ${newImports.join(', ')} } from '${moduleSpecifier}';`;
@@ -1671,11 +1634,7 @@ function addInjectFunctionsToFile(content, configNames) {
1671
1634
  node.initializer &&
1672
1635
  ts.isCallExpression(node.initializer)) {
1673
1636
  const callExpr = node.initializer;
1674
- if (ts.isPropertyAccessExpression(callExpr.expression) &&
1675
- ts.isIdentifier(callExpr.expression.expression) &&
1676
- callExpr.expression.expression.text === 'E' &&
1677
- ts.isIdentifier(callExpr.expression.name) &&
1678
- callExpr.expression.name.text === 'createQueryClientConfig') {
1637
+ if (ts.isIdentifier(callExpr.expression) && callExpr.expression.text === 'createQueryClientConfig') {
1679
1638
  const configName = node.name.text;
1680
1639
  // Find the variable statement
1681
1640
  let parent = node.parent;
@@ -1769,7 +1728,7 @@ function checkLegacyQueryCreatorUsage(tree) {
1769
1728
  console.log('\nšŸ” Checking for legacy query creator usages...');
1770
1729
  // Step 1: Collect all usages BEFORE any transformations
1771
1730
  const allUsages = [];
1772
- (0, devkit_1.visitNotIgnoredFiles)(tree, '', (filePath) => {
1731
+ visitNotIgnoredFiles(tree, '', (filePath) => {
1773
1732
  if (!filePath.endsWith('.ts') || filePath.endsWith('.spec.ts'))
1774
1733
  return;
1775
1734
  const content = tree.read(filePath, 'utf-8');
@@ -1873,11 +1832,7 @@ function findLegacyCreatorNames(sourceFile, content) {
1873
1832
  node.initializer &&
1874
1833
  ts.isCallExpression(node.initializer)) {
1875
1834
  const callExpr = node.initializer;
1876
- if (ts.isPropertyAccessExpression(callExpr.expression) &&
1877
- ts.isIdentifier(callExpr.expression.expression) &&
1878
- callExpr.expression.expression.text === 'E' &&
1879
- ts.isIdentifier(callExpr.expression.name) &&
1880
- callExpr.expression.name.text === 'createLegacyQueryCreator') {
1835
+ if (ts.isIdentifier(callExpr.expression) && callExpr.expression.text === 'createLegacyQueryCreator') {
1881
1836
  names.add(node.name.text);
1882
1837
  }
1883
1838
  }
@@ -1900,14 +1855,6 @@ function determineUsageContext(node, sourceFile) {
1900
1855
  if (ts.isIdentifier(expr) && expr.text === 'queryComputed') {
1901
1856
  return 'queryComputed';
1902
1857
  }
1903
- // Check for E.queryComputed()
1904
- if (ts.isPropertyAccessExpression(expr) &&
1905
- ts.isIdentifier(expr.expression) &&
1906
- expr.expression.text === 'E' &&
1907
- ts.isIdentifier(expr.name) &&
1908
- expr.name.text === 'queryComputed') {
1909
- return 'queryComputed';
1910
- }
1911
1858
  }
1912
1859
  }
1913
1860
  // Check for constructor
@@ -2217,7 +2164,7 @@ function transformLegacyCreatorPrepareCall(tree, classInjectors, pollingInfo) {
2217
2164
  filesByPath.get(classInfo.filePath).push(classInfo);
2218
2165
  });
2219
2166
  // Also check files with standalone function usages
2220
- (0, devkit_1.visitNotIgnoredFiles)(tree, '', (filePath) => {
2167
+ visitNotIgnoredFiles(tree, '', (filePath) => {
2221
2168
  if (!filePath.endsWith('.ts') || filePath.endsWith('.spec.ts'))
2222
2169
  return;
2223
2170
  if (filesByPath.has(filePath))
@@ -2764,7 +2711,7 @@ function containsInjectCall(node) {
2764
2711
  //#region Migrate empty .prepare() calls to .prepare({})
2765
2712
  function migrateEmptyPrepareCalls(tree) {
2766
2713
  const updatedFiles = [];
2767
- (0, devkit_1.visitNotIgnoredFiles)(tree, '', (filePath) => {
2714
+ visitNotIgnoredFiles(tree, '', (filePath) => {
2768
2715
  if (!filePath.endsWith('.ts') || filePath.endsWith('.spec.ts'))
2769
2716
  return;
2770
2717
  const content = tree.read(filePath, 'utf-8');
@@ -2820,7 +2767,7 @@ function transformEmptyPrepareCalls(content) {
2820
2767
  }
2821
2768
  function detectPollingUsage(tree) {
2822
2769
  const pollingInfo = new Map();
2823
- (0, devkit_1.visitNotIgnoredFiles)(tree, '', (filePath) => {
2770
+ visitNotIgnoredFiles(tree, '', (filePath) => {
2824
2771
  if (filePath.endsWith('.spec.ts'))
2825
2772
  return;
2826
2773
  const content = tree.read(filePath, 'utf-8');
@@ -2947,4 +2894,50 @@ function shouldAddDestroyOnResponse(queryVariableName, pollingInfo) {
2947
2894
  return true;
2948
2895
  }
2949
2896
  //#endregion
2897
+ //#region Helper function to ensure imports from @ethlete/query
2898
+ function ensureImportFromQuery(content, importsNeeded) {
2899
+ const sourceFile = ts.createSourceFile('temp.ts', content, ts.ScriptTarget.Latest, true);
2900
+ let queryImportNode;
2901
+ ts.forEachChild(sourceFile, (node) => {
2902
+ if (ts.isImportDeclaration(node) &&
2903
+ ts.isStringLiteral(node.moduleSpecifier) &&
2904
+ node.moduleSpecifier.text === '@ethlete/query') {
2905
+ queryImportNode = node;
2906
+ }
2907
+ });
2908
+ const missingImports = new Set(importsNeeded);
2909
+ if (queryImportNode?.importClause?.namedBindings && ts.isNamedImports(queryImportNode.importClause.namedBindings)) {
2910
+ const namedBindings = queryImportNode.importClause.namedBindings;
2911
+ // Check which imports already exist
2912
+ namedBindings.elements.forEach((el) => {
2913
+ missingImports.delete(el.name.text);
2914
+ });
2915
+ if (missingImports.size === 0) {
2916
+ return content; // All needed imports already exist
2917
+ }
2918
+ // Add missing imports to existing import statement
2919
+ const existingImports = namedBindings.elements.map((el) => el.getText(sourceFile));
2920
+ const allImports = [...existingImports, ...Array.from(missingImports).sort()].sort();
2921
+ const newImportStatement = `import { ${allImports.join(', ')} } from '@ethlete/query';`;
2922
+ const importStart = queryImportNode.getStart(sourceFile);
2923
+ const importEnd = queryImportNode.getEnd();
2924
+ return content.slice(0, importStart) + newImportStatement + content.slice(importEnd);
2925
+ }
2926
+ // No @ethlete/query import exists - create one
2927
+ let lastImportEnd = 0;
2928
+ ts.forEachChild(sourceFile, (node) => {
2929
+ if (ts.isImportDeclaration(node)) {
2930
+ const end = node.getEnd();
2931
+ if (end > lastImportEnd) {
2932
+ lastImportEnd = end;
2933
+ }
2934
+ }
2935
+ });
2936
+ const newImportStatement = `\nimport { ${Array.from(missingImports).sort().join(', ')} } from '@ethlete/query';`;
2937
+ if (lastImportEnd > 0) {
2938
+ return content.slice(0, lastImportEnd) + newImportStatement + content.slice(lastImportEnd);
2939
+ }
2940
+ return `import { ${Array.from(missingImports).sort().join(', ')} } from '@ethlete/query';\n\n${content}`;
2941
+ }
2942
+ //#endregion
2950
2943
  //# sourceMappingURL=migration.js.map