uql-codemod 0.43.0 → 0.44.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.
Files changed (2) hide show
  1. package/dist/transform.js +52 -16
  2. package/package.json +1 -1
package/dist/transform.js CHANGED
@@ -447,6 +447,37 @@ const BUILDER_INDEX_METHODS = new Map([
447
447
  ['addIndex', 1],
448
448
  ['createIndex', 2],
449
449
  ]);
450
+ /** The migration builder's `expr` helpers whose value an entity imports under the same name. */
451
+ const EXPR_VALUES = new Set(['currentDate', 'currentTime', 'uuid', 'uuidv7']);
452
+ /**
453
+ * Rewrites the migration builder's `expr.<helper>()` default as the value or `raw` an entity declares, or reports
454
+ * one with no rewrite. Called only where the file imports `expr` from uql, so a variable of its own is left alone.
455
+ */
456
+ function rewriteExprCall(call, ctx) {
457
+ const callee = call.expression;
458
+ if (!ts.isPropertyAccessExpression(callee) ||
459
+ !ts.isIdentifier(callee.expression) ||
460
+ callee.expression.text !== 'expr') {
461
+ return;
462
+ }
463
+ const helper = callee.name.text;
464
+ // The one renamed: an entity's clock is `currentTimestamp`.
465
+ const value = helper === 'now' ? 'currentTimestamp' : EXPR_VALUES.has(helper) ? helper : undefined;
466
+ const [sql] = call.arguments;
467
+ if (value) {
468
+ ctx.edits.push(replaced(call, value));
469
+ ctx.imports.set(value, `the expr.${helper}()`);
470
+ }
471
+ else if (helper === 'raw' && sql && ts.isStringLiteralLike(sql)) {
472
+ ctx.edits.push(replaced(call, rawTag(sql.text)));
473
+ ctx.imports.set('raw', 'the raw`...`');
474
+ }
475
+ else {
476
+ ctx.unresolved.push(`${ctx.describe(call)}: expr.${helper}() is gone: declare the column with m.raw(...), or use a stamp on the entity`);
477
+ return;
478
+ }
479
+ ctx.rewritten.expr += 1;
480
+ }
450
481
  /** Rewrites the `where` of the migration builder's `t.index()`, `t.unique()`, `t.addIndex()` and `m.createIndex()`. */
451
482
  function rewriteBuilderCall(call, ctx) {
452
483
  const at = ts.isPropertyAccessExpression(call.expression)
@@ -556,13 +587,7 @@ function unwrapRelationAlias(node, ctx) {
556
587
  declared.typeName.text === 'Relation' &&
557
588
  declared.typeArguments?.length === 1) {
558
589
  ctx.edits.push(replaced(declared, declared.typeArguments[0].getText()));
559
- ctx.relationAlias.unwrapped += 1;
560
- }
561
- }
562
- /** Counts `Relation<T>` wherever it appears, including the places this codemod does not rewrite. */
563
- function countRelationAlias(node, ctx) {
564
- if (ts.isTypeReferenceNode(node) && ts.isIdentifier(node.typeName) && node.typeName.text === 'Relation') {
565
- ctx.relationAlias.seen += 1;
590
+ ctx.rewritten.Relation += 1;
566
591
  }
567
592
  }
568
593
  /** Removes a statement together with the rest of its line, so nothing is left blank behind it. */
@@ -573,17 +598,25 @@ function removeStatement(node) {
573
598
  return { start: node.getStart(), end: nextLineStart ?? node.getEnd(), text: '' };
574
599
  }
575
600
  /**
576
- * Names `uql-orm` no longer exports and that the codemod does remove: only `Relation`, and only once
577
- * every usage was unwrapped. A removed *decorator* keeps its import on purpose (see
601
+ * Names `uql-orm` no longer exports and that the codemod does remove: `Relation` once every usage was
602
+ * unwrapped, and `expr` once every default was rewritten. A removed *decorator* keeps its import on purpose (see
578
603
  * {@link REMOVED_DECORATORS}).
579
604
  */
580
605
  function deadImportNames(source, ctx) {
581
- const { seen, unwrapped } = ctx.relationAlias;
582
- if (seen > unwrapped) {
583
- ctx.unresolved.push(`${source.fileName}: ${seen - unwrapped} 'Relation<T>' reference(s) are somewhere this codemod does ` +
584
- 'not rewrite; unwrap them and drop the import by hand');
606
+ const imported = uqlImports(source, true);
607
+ // Every use of the name the file imports, the places no rewrite reaches included.
608
+ const uses = (name) => {
609
+ const element = imported.find((specifier) => importedName(specifier) === name);
610
+ return element ? usesOf(source, element.name, ctx.checker).length : 0;
611
+ };
612
+ const { rewritten } = ctx;
613
+ const relations = uses('Relation');
614
+ if (relations > rewritten.Relation) {
615
+ ctx.unresolved.push(`${source.fileName}: ${relations - rewritten.Relation} 'Relation<T>' reference(s) are somewhere this codemod ` +
616
+ 'does not rewrite; unwrap them and drop the import by hand');
585
617
  }
586
- return new Set(seen > 0 && seen === unwrapped ? ['Relation'] : []);
618
+ const names = ['Relation', 'expr'];
619
+ return new Set(names.filter((name) => rewritten[name] > 0 && rewritten[name] === uses(name)));
587
620
  }
588
621
  /**
589
622
  * Removes `import 'reflect-metadata'`, since the polyfill only ever fed `design:type`, and each `dead` name
@@ -1037,16 +1070,16 @@ export function transformFile(source, checker) {
1037
1070
  unresolved: [],
1038
1071
  notes: [],
1039
1072
  imports: new Map(),
1040
- relationAlias: { seen: 0, unwrapped: 0 },
1073
+ rewritten: { Relation: 0, expr: 0 },
1041
1074
  describe: (node) => `${source.fileName}:${lineOf(node) + 1}`,
1042
1075
  };
1043
1076
  const lineOf = (node) => source.getLineAndCharacterOfPosition(node.getStart()).line;
1044
1077
  const rewritesRaw = uqlImports(source).some((element) => importedName(element) === 'raw');
1045
1078
  // The builder's methods are known by name alone, so they are read only in a file that imports uql-orm.
1046
1079
  const importsUql = uqlImportDeclarations(source, true).length > 0;
1080
+ const importsExpr = uqlImports(source, true).some((element) => importedName(element) === 'expr');
1047
1081
  const visit = (node) => {
1048
1082
  reportRemovedDecorators(node, ctx);
1049
- countRelationAlias(node, ctx);
1050
1083
  if (rewritesRaw) {
1051
1084
  rewriteRawCall(node, ctx, source);
1052
1085
  }
@@ -1062,6 +1095,9 @@ export function transformFile(source, checker) {
1062
1095
  if (importsUql) {
1063
1096
  rewriteBuilderCall(node, ctx);
1064
1097
  }
1098
+ if (importsExpr) {
1099
+ rewriteExprCall(node, ctx);
1100
+ }
1065
1101
  }
1066
1102
  if (ts.isPropertyAssignment(node)) {
1067
1103
  rewriteStatementKeys(node, ctx);
package/package.json CHANGED
@@ -2,7 +2,7 @@
2
2
  "name": "uql-codemod",
3
3
  "description": "Upgrade uql-orm across versions that introduce breaking changes",
4
4
  "license": "MIT",
5
- "version": "0.43.0",
5
+ "version": "0.44.0",
6
6
  "type": "module",
7
7
  "engines": {
8
8
  "node": ">=24"