@venizia/dev-configs 0.4.2 → 0.4.3

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 (47) hide show
  1. package/README.md +5 -3
  2. package/dist/cli/clean.d.ts +2 -1
  3. package/dist/cli/clean.d.ts.map +1 -1
  4. package/dist/cli/clean.js +18 -1
  5. package/dist/cli/clean.js.map +1 -1
  6. package/dist/cli/codemod/files.d.ts +22 -0
  7. package/dist/cli/codemod/files.d.ts.map +1 -0
  8. package/dist/cli/codemod/files.js +68 -0
  9. package/dist/cli/codemod/files.js.map +1 -0
  10. package/dist/cli/codemod/index.d.ts +4 -4
  11. package/dist/cli/codemod/index.d.ts.map +1 -1
  12. package/dist/cli/codemod/index.js +57 -34
  13. package/dist/cli/codemod/index.js.map +1 -1
  14. package/dist/cli/codemod/model-settings.d.ts +2 -1
  15. package/dist/cli/codemod/model-settings.d.ts.map +1 -1
  16. package/dist/cli/codemod/model-settings.js +38 -1
  17. package/dist/cli/codemod/model-settings.js.map +1 -1
  18. package/dist/cli/index.js +13 -2
  19. package/dist/cli/index.js.map +1 -1
  20. package/dist/linters/house/common.d.ts +1 -0
  21. package/dist/linters/house/common.d.ts.map +1 -1
  22. package/dist/linters/house/common.js +1 -0
  23. package/dist/linters/house/common.js.map +1 -1
  24. package/dist/linters/house/configs.d.ts.map +1 -1
  25. package/dist/linters/house/configs.js +20 -1
  26. package/dist/linters/house/configs.js.map +1 -1
  27. package/dist/linters/house/const-class-values.rule.d.ts +7 -4
  28. package/dist/linters/house/const-class-values.rule.d.ts.map +1 -1
  29. package/dist/linters/house/const-class-values.rule.js +73 -21
  30. package/dist/linters/house/const-class-values.rule.js.map +1 -1
  31. package/dist/linters/rules/common.d.ts +48 -1
  32. package/dist/linters/rules/common.d.ts.map +1 -1
  33. package/dist/linters/rules/common.js +53 -0
  34. package/dist/linters/rules/common.js.map +1 -1
  35. package/dist/linters/rules/eqeqeq.rule.d.ts +2 -2
  36. package/dist/linters/rules/eqeqeq.rule.d.ts.map +1 -1
  37. package/dist/linters/rules/eqeqeq.rule.js +2 -1
  38. package/dist/linters/rules/eqeqeq.rule.js.map +1 -1
  39. package/dist/linters/rules/no-unused-vars.rule.d.ts +3 -3
  40. package/dist/linters/rules/no-unused-vars.rule.d.ts.map +1 -1
  41. package/dist/linters/rules/no-unused-vars.rule.js +2 -1
  42. package/dist/linters/rules/no-unused-vars.rule.js.map +1 -1
  43. package/dist/linters/rules/switch-case-braces.rule.d.ts +2 -2
  44. package/dist/linters/rules/switch-case-braces.rule.d.ts.map +1 -1
  45. package/dist/linters/rules/switch-case-braces.rule.js +2 -1
  46. package/dist/linters/rules/switch-case-braces.rule.js.map +1 -1
  47. package/package.json +1 -1
package/README.md CHANGED
@@ -169,10 +169,12 @@ booleans. `BrowserOxlintConfigs.create()` and `ReactOxlintConfigs.create()` take
169
169
  | `MaxParamsOwnRule` | `venizia/max-params-own` | `build({ max: 5 })` | A function we write takes at most `max` positional parameters. Not counted: an inline callback passed to a call or a JSX prop, a signature a declared type fixes, a constructor whose parameters are all `@inject`. A hook callback counts. |
170
170
  | `NoNewErrorRule` | `venizia/no-new-error` | `error` | Throw through `getError` (or an `ApplicationError`), never `new Error(...)` / `Error(...)`. Off in tests. |
171
171
  | `NoLeakedRenderRule` | `venizia/no-leaked-render` | `error`, in `reactHouseConfigs` only | A JSX child is drawn with `cond ? <X /> : null`, never `cond && <X />`, which draws `0` when `cond` is `0`. `--fix` rewrites it. `disabled={a && b}` is a value, not a child, and passes. |
172
- | `ConstClassValuesRule` | `venizia/const-class-values` | off; `build({ namePattern })` | An exported type named like a logic value (`State`, `Status`, `Stage`, `Mode`, `Kind`, `Type` by default) is a const class plus `TConstValue<typeof X>`, not a union of string literals. |
172
+ | `ConstClassValuesRule` | `venizia/const-class-values` | `warn` in both presets; `build({ severity, namePattern })` | V-C-08. A union of string literals in an exported declaration (a type alias, an interface member, a generic constraint, a parameter or return type) is a const class plus `TConstValue<typeof X>`. Not counted: `Pick`, `Omit`, `Extract`, `Exclude`, `Record` arguments and mapped-type keys, and a union with a non-string member. `namePattern` narrows it to exported type aliases whose name matches. Off in tests. |
173
+ | `@typescript-eslint/consistent-type-assertions` | `assertionStyle: never` | `warn` in both presets | V-C-10. `as X`, `as any` and `as unknown as X` warn; `as const` and `satisfies` do not. Off in tests. |
173
174
 
174
- `const-class-values` is registered in the plugin but no preset turns it on: a few IGNIS types still
175
- break it. Enable it with `[ConstClassValuesRule.NAME]: ConstClassValuesRule.build()`.
175
+ `const-class-values` and the cast rule only warn, so adopting them never fails a lint. Raise one to an error
176
+ with `[ConstClassValuesRule.NAME]: ConstClassValuesRule.build({ severity: 'error' })`. The 5-parameter
177
+ limit (V-C-02) is `venizia/max-params-own`, an error.
176
178
 
177
179
  ### namingConventionConfigs
178
180
 
@@ -1,7 +1,8 @@
1
- /** `devz clean`: removes what the build and the checks generate, and nothing else. */
1
+ /** `devz clean`: removes what the build and the checks generate, and nothing else. `--help` prints the usage; any other argument is refused, and nothing is deleted. */
2
2
  export declare class CleanCommand {
3
3
  static run(opts: {
4
4
  root: string;
5
+ args?: string[];
5
6
  }): number;
6
7
  }
7
8
  //# sourceMappingURL=clean.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"clean.d.ts","sourceRoot":"","sources":["../../src/cli/clean.ts"],"names":[],"mappings":"AAYA,sFAAsF;AACtF,qBAAa,YAAY;IACvB,MAAM,CAAC,GAAG,CAAC,IAAI,EAAE;QAAE,IAAI,EAAE,MAAM,CAAA;KAAE,GAAG,MAAM;CAa3C"}
1
+ {"version":3,"file":"clean.d.ts","sourceRoot":"","sources":["../../src/cli/clean.ts"],"names":[],"mappings":"AAsBA,wKAAwK;AACxK,qBAAa,YAAY;IACvB,MAAM,CAAC,GAAG,CAAC,IAAI,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,IAAI,CAAC,EAAE,MAAM,EAAE,CAAA;KAAE,GAAG,MAAM;CA2B5D"}
package/dist/cli/clean.js CHANGED
@@ -10,9 +10,26 @@ const GENERATED_PATHS = ['dist', 'node_modules/.cache'];
10
10
  * `<tsconfig name>.tsbuildinfo` next to the tsconfig. One inside `dist` is already removed with it.
11
11
  */
12
12
  const BUILD_INFO_SUFFIX = '.tsbuildinfo';
13
- /** `devz clean`: removes what the build and the checks generate, and nothing else. */
13
+ const USAGE = `Usage: devz clean
14
+
15
+ Removes what the build and the checks generate, in the current folder: dist, node_modules/.cache and
16
+ the *.tsbuildinfo files next to the tsconfig. It takes no argument.
17
+
18
+ --help print this text and exit`;
19
+ /** A usage error: nothing was deleted. */
20
+ const USAGE_ERROR_CODE = 2;
21
+ /** `devz clean`: removes what the build and the checks generate, and nothing else. `--help` prints the usage; any other argument is refused, and nothing is deleted. */
14
22
  class CleanCommand {
15
23
  static run(opts) {
24
+ const { args = [] } = opts;
25
+ if (args.includes('--help') || args.includes('-h')) {
26
+ console.log(USAGE);
27
+ return 0;
28
+ }
29
+ if (args.length > 0) {
30
+ console.error(`devz clean: unexpected argument ${args.map((arg) => `"${arg}"`).join(', ')}\n${USAGE}`);
31
+ return USAGE_ERROR_CODE;
32
+ }
16
33
  for (const path of GENERATED_PATHS) {
17
34
  (0, node_fs_1.rmSync)((0, node_path_1.join)(opts.root, path), { recursive: true, force: true });
18
35
  }
@@ -1 +1 @@
1
- {"version":3,"file":"clean.js","sourceRoot":"","sources":["../../src/cli/clean.ts"],"names":[],"mappings":";;;AAAA,qCAA8C;AAC9C,yCAAiC;AAEjC,0EAA0E;AAC1E,MAAM,eAAe,GAAG,CAAC,MAAM,EAAE,qBAAqB,CAAC,CAAC;AAExD;;;GAGG;AACH,MAAM,iBAAiB,GAAG,cAAc,CAAC;AAEzC,sFAAsF;AACtF,MAAa,YAAY;IACvB,MAAM,CAAC,GAAG,CAAC,IAAsB;QAC/B,KAAK,MAAM,IAAI,IAAI,eAAe,EAAE,CAAC;YACnC,IAAA,gBAAM,EAAC,IAAA,gBAAI,EAAC,IAAI,CAAC,IAAI,EAAE,IAAI,CAAC,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC;QAClE,CAAC;QAED,KAAK,MAAM,KAAK,IAAI,IAAA,qBAAW,EAAC,IAAI,CAAC,IAAI,EAAE,EAAE,aAAa,EAAE,IAAI,EAAE,CAAC,EAAE,CAAC;YACpE,IAAI,KAAK,CAAC,MAAM,EAAE,IAAI,KAAK,CAAC,IAAI,CAAC,QAAQ,CAAC,iBAAiB,CAAC,EAAE,CAAC;gBAC7D,IAAA,gBAAM,EAAC,IAAA,gBAAI,EAAC,IAAI,CAAC,IAAI,EAAE,KAAK,CAAC,IAAI,CAAC,EAAE,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC;YACvD,CAAC;QACH,CAAC;QAED,OAAO,CAAC,CAAC;IACX,CAAC;CACF;AAdD,oCAcC","sourcesContent":["import { readdirSync, rmSync } from 'node:fs';\nimport { join } from 'node:path';\n\n/** What a build and the checks generate, relative to the project root. */\nconst GENERATED_PATHS = ['dist', 'node_modules/.cache'];\n\n/**\n * `incremental` is on in the base tsconfig, so a type-check that emits nothing leaves\n * `<tsconfig name>.tsbuildinfo` next to the tsconfig. One inside `dist` is already removed with it.\n */\nconst BUILD_INFO_SUFFIX = '.tsbuildinfo';\n\n/** `devz clean`: removes what the build and the checks generate, and nothing else. */\nexport class CleanCommand {\n static run(opts: { root: string }): number {\n for (const path of GENERATED_PATHS) {\n rmSync(join(opts.root, path), { recursive: true, force: true });\n }\n\n for (const entry of readdirSync(opts.root, { withFileTypes: true })) {\n if (entry.isFile() && entry.name.endsWith(BUILD_INFO_SUFFIX)) {\n rmSync(join(opts.root, entry.name), { force: true });\n }\n }\n\n return 0;\n }\n}\n"]}
1
+ {"version":3,"file":"clean.js","sourceRoot":"","sources":["../../src/cli/clean.ts"],"names":[],"mappings":";;;AAAA,qCAA8C;AAC9C,yCAAiC;AAEjC,0EAA0E;AAC1E,MAAM,eAAe,GAAG,CAAC,MAAM,EAAE,qBAAqB,CAAC,CAAC;AAExD;;;GAGG;AACH,MAAM,iBAAiB,GAAG,cAAc,CAAC;AAEzC,MAAM,KAAK,GAAG;;;;;sCAKwB,CAAC;AAEvC,0CAA0C;AAC1C,MAAM,gBAAgB,GAAG,CAAC,CAAC;AAE3B,wKAAwK;AACxK,MAAa,YAAY;IACvB,MAAM,CAAC,GAAG,CAAC,IAAuC;QAChD,MAAM,EAAE,IAAI,GAAG,EAAE,EAAE,GAAG,IAAI,CAAC;QAE3B,IAAI,IAAI,CAAC,QAAQ,CAAC,QAAQ,CAAC,IAAI,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,EAAE,CAAC;YACnD,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC;YACnB,OAAO,CAAC,CAAC;QACX,CAAC;QAED,IAAI,IAAI,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;YACpB,OAAO,CAAC,KAAK,CACX,mCAAmC,IAAI,CAAC,GAAG,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,IAAI,GAAG,GAAG,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,KAAK,KAAK,EAAE,CACxF,CAAC;YACF,OAAO,gBAAgB,CAAC;QAC1B,CAAC;QAED,KAAK,MAAM,IAAI,IAAI,eAAe,EAAE,CAAC;YACnC,IAAA,gBAAM,EAAC,IAAA,gBAAI,EAAC,IAAI,CAAC,IAAI,EAAE,IAAI,CAAC,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC;QAClE,CAAC;QAED,KAAK,MAAM,KAAK,IAAI,IAAA,qBAAW,EAAC,IAAI,CAAC,IAAI,EAAE,EAAE,aAAa,EAAE,IAAI,EAAE,CAAC,EAAE,CAAC;YACpE,IAAI,KAAK,CAAC,MAAM,EAAE,IAAI,KAAK,CAAC,IAAI,CAAC,QAAQ,CAAC,iBAAiB,CAAC,EAAE,CAAC;gBAC7D,IAAA,gBAAM,EAAC,IAAA,gBAAI,EAAC,IAAI,CAAC,IAAI,EAAE,KAAK,CAAC,IAAI,CAAC,EAAE,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC;YACvD,CAAC;QACH,CAAC;QAED,OAAO,CAAC,CAAC;IACX,CAAC;CACF;AA5BD,oCA4BC","sourcesContent":["import { readdirSync, rmSync } from 'node:fs';\nimport { join } from 'node:path';\n\n/** What a build and the checks generate, relative to the project root. */\nconst GENERATED_PATHS = ['dist', 'node_modules/.cache'];\n\n/**\n * `incremental` is on in the base tsconfig, so a type-check that emits nothing leaves\n * `<tsconfig name>.tsbuildinfo` next to the tsconfig. One inside `dist` is already removed with it.\n */\nconst BUILD_INFO_SUFFIX = '.tsbuildinfo';\n\nconst USAGE = `Usage: devz clean\n\nRemoves what the build and the checks generate, in the current folder: dist, node_modules/.cache and\nthe *.tsbuildinfo files next to the tsconfig. It takes no argument.\n\n --help print this text and exit`;\n\n/** A usage error: nothing was deleted. */\nconst USAGE_ERROR_CODE = 2;\n\n/** `devz clean`: removes what the build and the checks generate, and nothing else. `--help` prints the usage; any other argument is refused, and nothing is deleted. */\nexport class CleanCommand {\n static run(opts: { root: string; args?: string[] }): number {\n const { args = [] } = opts;\n\n if (args.includes('--help') || args.includes('-h')) {\n console.log(USAGE);\n return 0;\n }\n\n if (args.length > 0) {\n console.error(\n `devz clean: unexpected argument ${args.map((arg) => `\"${arg}\"`).join(', ')}\\n${USAGE}`,\n );\n return USAGE_ERROR_CODE;\n }\n\n for (const path of GENERATED_PATHS) {\n rmSync(join(opts.root, path), { recursive: true, force: true });\n }\n\n for (const entry of readdirSync(opts.root, { withFileTypes: true })) {\n if (entry.isFile() && entry.name.endsWith(BUILD_INFO_SUFFIX)) {\n rmSync(join(opts.root, entry.name), { force: true });\n }\n }\n\n return 0;\n }\n}\n"]}
@@ -0,0 +1,22 @@
1
+ /**
2
+ * The source files a codemod may touch. Inside a git repository the list comes from git, so a
3
+ * `.gitignore`d folder (a worktree, a clone, a build output) is never entered. Outside git there is
4
+ * no ignore file to read, so the walk skips every dot-directory as well as `node_modules`, `dist`,
5
+ * `build` and `.git`.
6
+ */
7
+ export declare class CodemodFiles {
8
+ /** Every `.ts`, `.tsx`, `.mts` or `.cts` file (declaration files excluded) the codemod may touch under `target`; a file target is returned as it is. */
9
+ static collect(opts: {
10
+ target: string;
11
+ }): string[];
12
+ /** Tracked files plus untracked files git does not ignore; `undefined` outside a repository or without git. */
13
+ static fromGit(opts: {
14
+ target: string;
15
+ }): string[] | undefined;
16
+ /** The directory walk used outside git: skips dot-directories and the build folders. */
17
+ static fromWalk(opts: {
18
+ target: string;
19
+ }): string[];
20
+ private static isSource;
21
+ }
22
+ //# sourceMappingURL=files.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"files.d.ts","sourceRoot":"","sources":["../../../src/cli/codemod/files.ts"],"names":[],"mappings":"AAOA;;;;;GAKG;AACH,qBAAa,YAAY;IACvB,wJAAwJ;IACxJ,MAAM,CAAC,OAAO,CAAC,IAAI,EAAE;QAAE,MAAM,EAAE,MAAM,CAAA;KAAE,GAAG,MAAM,EAAE;IAUlD,+GAA+G;IAC/G,MAAM,CAAC,OAAO,CAAC,IAAI,EAAE;QAAE,MAAM,EAAE,MAAM,CAAA;KAAE,GAAG,MAAM,EAAE,GAAG,SAAS;IA2B9D,wFAAwF;IACxF,MAAM,CAAC,QAAQ,CAAC,IAAI,EAAE;QAAE,MAAM,EAAE,MAAM,CAAA;KAAE,GAAG,MAAM,EAAE;IAoBnD,OAAO,CAAC,MAAM,CAAC,QAAQ;CAGxB"}
@@ -0,0 +1,68 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.CodemodFiles = void 0;
4
+ const node_child_process_1 = require("node:child_process");
5
+ const node_fs_1 = require("node:fs");
6
+ const node_path_1 = require("node:path");
7
+ const SKIPPED_DIRECTORIES = new Set(['node_modules', 'dist', 'build', '.git']);
8
+ const SOURCE_PATTERN = /\.(?:ts|tsx|mts|cts)$/;
9
+ /**
10
+ * The source files a codemod may touch. Inside a git repository the list comes from git, so a
11
+ * `.gitignore`d folder (a worktree, a clone, a build output) is never entered. Outside git there is
12
+ * no ignore file to read, so the walk skips every dot-directory as well as `node_modules`, `dist`,
13
+ * `build` and `.git`.
14
+ */
15
+ class CodemodFiles {
16
+ /** Every `.ts`, `.tsx`, `.mts` or `.cts` file (declaration files excluded) the codemod may touch under `target`; a file target is returned as it is. */
17
+ static collect(opts) {
18
+ const { target } = opts;
19
+ if ((0, node_fs_1.statSync)(target).isFile()) {
20
+ return [target];
21
+ }
22
+ return CodemodFiles.fromGit({ target }) ?? CodemodFiles.fromWalk({ target });
23
+ }
24
+ /** Tracked files plus untracked files git does not ignore; `undefined` outside a repository or without git. */
25
+ static fromGit(opts) {
26
+ const { target } = opts;
27
+ let output;
28
+ try {
29
+ output = (0, node_child_process_1.execFileSync)('git', ['ls-files', '--cached', '--others', '--exclude-standard', '-z'], {
30
+ cwd: target,
31
+ encoding: 'utf8',
32
+ maxBuffer: 256 * 1024 * 1024,
33
+ stdio: ['ignore', 'pipe', 'ignore'],
34
+ });
35
+ }
36
+ catch {
37
+ return undefined;
38
+ }
39
+ return output
40
+ .split('\0')
41
+ .filter((path) => path !== '' && CodemodFiles.isSource({ name: path }))
42
+ .filter((path) => !path.split('/').some((segment) => SKIPPED_DIRECTORIES.has(segment)))
43
+ .map((path) => (0, node_path_1.join)(target, path))
44
+ .filter((path) => (0, node_fs_1.existsSync)(path) && (0, node_fs_1.statSync)(path).isFile());
45
+ }
46
+ /** The directory walk used outside git: skips dot-directories and the build folders. */
47
+ static fromWalk(opts) {
48
+ const { target } = opts;
49
+ const files = [];
50
+ for (const entry of (0, node_fs_1.readdirSync)(target, { withFileTypes: true })) {
51
+ if (entry.isDirectory()) {
52
+ if (!SKIPPED_DIRECTORIES.has(entry.name) && !entry.name.startsWith('.')) {
53
+ files.push(...CodemodFiles.fromWalk({ target: (0, node_path_1.join)(target, entry.name) }));
54
+ }
55
+ continue;
56
+ }
57
+ if (CodemodFiles.isSource({ name: entry.name })) {
58
+ files.push((0, node_path_1.join)(target, entry.name));
59
+ }
60
+ }
61
+ return files;
62
+ }
63
+ static isSource(opts) {
64
+ return SOURCE_PATTERN.test(opts.name) && !opts.name.endsWith('.d.ts');
65
+ }
66
+ }
67
+ exports.CodemodFiles = CodemodFiles;
68
+ //# sourceMappingURL=files.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"files.js","sourceRoot":"","sources":["../../../src/cli/codemod/files.ts"],"names":[],"mappings":"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","sourcesContent":["import { execFileSync } from 'node:child_process';\nimport { existsSync, readdirSync, statSync } from 'node:fs';\nimport { join } from 'node:path';\n\nconst SKIPPED_DIRECTORIES = new Set(['node_modules', 'dist', 'build', '.git']);\nconst SOURCE_PATTERN = /\\.(?:ts|tsx|mts|cts)$/;\n\n/**\n * The source files a codemod may touch. Inside a git repository the list comes from git, so a\n * `.gitignore`d folder (a worktree, a clone, a build output) is never entered. Outside git there is\n * no ignore file to read, so the walk skips every dot-directory as well as `node_modules`, `dist`,\n * `build` and `.git`.\n */\nexport class CodemodFiles {\n /** Every `.ts`, `.tsx`, `.mts` or `.cts` file (declaration files excluded) the codemod may touch under `target`; a file target is returned as it is. */\n static collect(opts: { target: string }): string[] {\n const { target } = opts;\n\n if (statSync(target).isFile()) {\n return [target];\n }\n\n return CodemodFiles.fromGit({ target }) ?? CodemodFiles.fromWalk({ target });\n }\n\n /** Tracked files plus untracked files git does not ignore; `undefined` outside a repository or without git. */\n static fromGit(opts: { target: string }): string[] | undefined {\n const { target } = opts;\n let output: string;\n\n try {\n output = execFileSync(\n 'git',\n ['ls-files', '--cached', '--others', '--exclude-standard', '-z'],\n {\n cwd: target,\n encoding: 'utf8',\n maxBuffer: 256 * 1024 * 1024,\n stdio: ['ignore', 'pipe', 'ignore'],\n },\n );\n } catch {\n return undefined;\n }\n\n return output\n .split('\\0')\n .filter((path) => path !== '' && CodemodFiles.isSource({ name: path }))\n .filter((path) => !path.split('/').some((segment) => SKIPPED_DIRECTORIES.has(segment)))\n .map((path) => join(target, path))\n .filter((path) => existsSync(path) && statSync(path).isFile());\n }\n\n /** The directory walk used outside git: skips dot-directories and the build folders. */\n static fromWalk(opts: { target: string }): string[] {\n const { target } = opts;\n const files: string[] = [];\n\n for (const entry of readdirSync(target, { withFileTypes: true })) {\n if (entry.isDirectory()) {\n if (!SKIPPED_DIRECTORIES.has(entry.name) && !entry.name.startsWith('.')) {\n files.push(...CodemodFiles.fromWalk({ target: join(target, entry.name) }));\n }\n continue;\n }\n\n if (CodemodFiles.isSource({ name: entry.name })) {\n files.push(join(target, entry.name));\n }\n }\n\n return files;\n }\n\n private static isSource(opts: { name: string }): boolean {\n return SOURCE_PATTERN.test(opts.name) && !opts.name.endsWith('.d.ts');\n }\n}\n"]}
@@ -1,8 +1,8 @@
1
1
  /**
2
- * `devz codemod model-settings [paths...] [--check]`: moves `@model` settings from the flat keys
3
- * (`defaultFilter`, `defaultLimit`, `maxLimit`, `scopeFilter`) to `query` and `access`. Without
4
- * `--check` it writes the files; with it, it only lists what would change and exits 1 when
5
- * anything would.
2
+ * `devz codemod model-settings [paths...] [--write | --check]`: moves `@model` settings from the
3
+ * flat keys (`defaultFilter`, `defaultLimit`, `maxLimit`, `scopeFilter`) to `query` and `access`,
4
+ * and `skipMigrate` to `migration.use`. It is a dry run unless `--write` is given: it lists what
5
+ * would change. `--check` is the same dry run, and exits 1 when anything would change.
6
6
  */
7
7
  export declare class CodemodCommand {
8
8
  static run(opts: {
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/cli/codemod/index.ts"],"names":[],"mappings":"AAkCA;;;;;GAKG;AACH,qBAAa,cAAc;WACZ,GAAG,CAAC,IAAI,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,EAAE,CAAA;KAAE,GAAG,OAAO,CAAC,MAAM,CAAC;CA4D1E"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/cli/codemod/index.ts"],"names":[],"mappings":"AAqCA;;;;;GAKG;AACH,qBAAa,cAAc;WACZ,GAAG,CAAC,IAAI,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,EAAE,CAAA;KAAE,GAAG,OAAO,CAAC,MAAM,CAAC;CA0F1E"}
@@ -3,45 +3,65 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.CodemodCommand = void 0;
4
4
  const node_fs_1 = require("node:fs");
5
5
  const node_path_1 = require("node:path");
6
+ const files_1 = require("./files");
6
7
  const model_settings_1 = require("./model-settings");
7
- const SKIPPED_DIRECTORIES = new Set(['node_modules', 'dist', 'build', '.git']);
8
- const SOURCE_PATTERN = /\.(?:ts|tsx|mts|cts)$/;
9
- const USAGE = 'Usage: devz codemod model-settings [paths...] [--check]';
10
- /** Every source file under `target`: the file itself, or the `.ts` files below a directory. */
11
- const collectFiles = (target) => {
12
- const stats = (0, node_fs_1.statSync)(target);
13
- if (stats.isFile()) {
14
- return [target];
8
+ const USAGE = `Usage: devz codemod model-settings [paths...] [--write | --check]
9
+
10
+ Moves @model settings to the grouped shape and skipMigrate to migration.use.
11
+
12
+ --write write the changed files (without it, nothing is written)
13
+ --check dry run that exits 1 when anything would change (for CI)
14
+ --help print this text and exit
15
+
16
+ With no flag it lists what would change and writes nothing. Inside a git repository it reads the
17
+ files git knows (tracked, or untracked and not ignored), so ignored folders are never touched.`;
18
+ /** A usage error: nothing was read or written. */
19
+ const USAGE_ERROR_CODE = 2;
20
+ class CodemodFlags {
21
+ static { this.WRITE = '--write'; }
22
+ static { this.CHECK = '--check'; }
23
+ static { this.HELP = '--help'; }
24
+ static { this.HELP_SHORT = '-h'; }
25
+ static { this.SCHEME_SET = new Set([
26
+ this.WRITE,
27
+ this.CHECK,
28
+ this.HELP,
29
+ this.HELP_SHORT,
30
+ ]); }
31
+ static isValid(value) {
32
+ return this.SCHEME_SET.has(value);
15
33
  }
16
- const files = [];
17
- for (const entry of (0, node_fs_1.readdirSync)(target, { withFileTypes: true })) {
18
- if (entry.isDirectory()) {
19
- if (!SKIPPED_DIRECTORIES.has(entry.name)) {
20
- files.push(...collectFiles((0, node_path_1.join)(target, entry.name)));
21
- }
22
- continue;
23
- }
24
- if (SOURCE_PATTERN.test(entry.name) && !entry.name.endsWith('.d.ts')) {
25
- files.push((0, node_path_1.join)(target, entry.name));
26
- }
27
- }
28
- return files;
29
- };
34
+ }
30
35
  /**
31
- * `devz codemod model-settings [paths...] [--check]`: moves `@model` settings from the flat keys
32
- * (`defaultFilter`, `defaultLimit`, `maxLimit`, `scopeFilter`) to `query` and `access`. Without
33
- * `--check` it writes the files; with it, it only lists what would change and exits 1 when
34
- * anything would.
36
+ * `devz codemod model-settings [paths...] [--write | --check]`: moves `@model` settings from the
37
+ * flat keys (`defaultFilter`, `defaultLimit`, `maxLimit`, `scopeFilter`) to `query` and `access`,
38
+ * and `skipMigrate` to `migration.use`. It is a dry run unless `--write` is given: it lists what
39
+ * would change. `--check` is the same dry run, and exits 1 when anything would change.
35
40
  */
36
41
  class CodemodCommand {
37
42
  static async run(opts) {
38
- const [name, ...rest] = opts.args;
43
+ const { args } = opts;
44
+ if (args.includes(CodemodFlags.HELP) || args.includes(CodemodFlags.HELP_SHORT)) {
45
+ console.log(USAGE);
46
+ return 0;
47
+ }
48
+ const [name, ...rest] = args;
39
49
  if (name !== 'model-settings') {
40
50
  console.error(`devz codemod: unknown codemod "${name ?? ''}"\n${USAGE}`);
41
- return 1;
51
+ return USAGE_ERROR_CODE;
52
+ }
53
+ const unknown = rest.filter((argument) => argument.startsWith('-') && !CodemodFlags.isValid(argument));
54
+ if (unknown.length > 0) {
55
+ console.error(`devz codemod: unknown option ${unknown.map((flag) => `"${flag}"`).join(', ')}\n${USAGE}`);
56
+ return USAGE_ERROR_CODE;
42
57
  }
43
- const isCheck = rest.includes('--check');
44
- const paths = rest.filter((argument) => !argument.startsWith('--'));
58
+ const isWrite = rest.includes(CodemodFlags.WRITE);
59
+ const isCheck = rest.includes(CodemodFlags.CHECK);
60
+ if (isWrite && isCheck) {
61
+ console.error(`devz codemod: ${CodemodFlags.WRITE} and ${CodemodFlags.CHECK} contradict each other\n${USAGE}`);
62
+ return USAGE_ERROR_CODE;
63
+ }
64
+ const paths = rest.filter((argument) => !argument.startsWith('-'));
45
65
  const targets = (paths.length > 0 ? paths : ['.']).map((path) => (0, node_path_1.resolve)(opts.root, path));
46
66
  let typescript;
47
67
  try {
@@ -54,7 +74,7 @@ class CodemodCommand {
54
74
  let changedCount = 0;
55
75
  let reportCount = 0;
56
76
  for (const target of targets) {
57
- for (const file of collectFiles(target)) {
77
+ for (const file of files_1.CodemodFiles.collect({ target })) {
58
78
  const source = (0, node_fs_1.readFileSync)(file, 'utf8');
59
79
  if (!source.includes('@model')) {
60
80
  continue;
@@ -69,13 +89,16 @@ class CodemodCommand {
69
89
  continue;
70
90
  }
71
91
  changedCount += 1;
72
- console.log(`${isCheck ? 'would change' : 'changed'} ${shown} (${result.rewritten} model(s))`);
73
- if (!isCheck) {
92
+ console.log(`${isWrite ? 'changed' : 'would change'} ${shown} (${result.rewritten} model(s))`);
93
+ if (isWrite) {
74
94
  (0, node_fs_1.writeFileSync)(file, result.output);
75
95
  }
76
96
  }
77
97
  }
78
- console.log(`devz codemod model-settings: ${changedCount} file(s) ${isCheck ? 'would change' : 'changed'}, ${reportCount} left untouched`);
98
+ console.log(`devz codemod model-settings: ${changedCount} file(s) ${isWrite ? 'changed' : 'would change'}, ${reportCount} left untouched`);
99
+ if (!isWrite && changedCount > 0) {
100
+ console.log(`Nothing was written. Run again with ${CodemodFlags.WRITE} to apply.`);
101
+ }
79
102
  return isCheck && changedCount > 0 ? 1 : 0;
80
103
  }
81
104
  }
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/cli/codemod/index.ts"],"names":[],"mappings":"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","sourcesContent":["import { readdirSync, readFileSync, statSync, writeFileSync } from 'node:fs';\nimport { join, relative, resolve } from 'node:path';\nimport { migrateModelSettings } from './model-settings';\n\nconst SKIPPED_DIRECTORIES = new Set(['node_modules', 'dist', 'build', '.git']);\nconst SOURCE_PATTERN = /\\.(?:ts|tsx|mts|cts)$/;\n\nconst USAGE = 'Usage: devz codemod model-settings [paths...] [--check]';\n\n/** Every source file under `target`: the file itself, or the `.ts` files below a directory. */\nconst collectFiles = (target: string): string[] => {\n const stats = statSync(target);\n\n if (stats.isFile()) {\n return [target];\n }\n\n const files: string[] = [];\n for (const entry of readdirSync(target, { withFileTypes: true })) {\n if (entry.isDirectory()) {\n if (!SKIPPED_DIRECTORIES.has(entry.name)) {\n files.push(...collectFiles(join(target, entry.name)));\n }\n continue;\n }\n\n if (SOURCE_PATTERN.test(entry.name) && !entry.name.endsWith('.d.ts')) {\n files.push(join(target, entry.name));\n }\n }\n\n return files;\n};\n\n/**\n * `devz codemod model-settings [paths...] [--check]`: moves `@model` settings from the flat keys\n * (`defaultFilter`, `defaultLimit`, `maxLimit`, `scopeFilter`) to `query` and `access`. Without\n * `--check` it writes the files; with it, it only lists what would change and exits 1 when\n * anything would.\n */\nexport class CodemodCommand {\n static async run(opts: { root: string; args: string[] }): Promise<number> {\n const [name, ...rest] = opts.args;\n\n if (name !== 'model-settings') {\n console.error(`devz codemod: unknown codemod \"${name ?? ''}\"\\n${USAGE}`);\n return 1;\n }\n\n const isCheck = rest.includes('--check');\n const paths = rest.filter((argument) => !argument.startsWith('--'));\n const targets = (paths.length > 0 ? paths : ['.']).map((path) => resolve(opts.root, path));\n\n let typescript: typeof import('typescript');\n try {\n typescript = (await import('typescript')).default;\n } catch {\n console.error('devz codemod: the \"typescript\" package is required to read your sources');\n return 1;\n }\n\n let changedCount = 0;\n let reportCount = 0;\n\n for (const target of targets) {\n for (const file of collectFiles(target)) {\n const source = readFileSync(file, 'utf8');\n\n if (!source.includes('@model')) {\n continue;\n }\n\n const result = migrateModelSettings({ typescript, fileName: file, source });\n const shown = relative(opts.root, file) || file;\n\n for (const report of result.reports) {\n reportCount += 1;\n console.warn(`${shown}:${report.line}:${report.column} ${report.reason}`);\n }\n\n if (!result.changed) {\n continue;\n }\n\n changedCount += 1;\n console.log(\n `${isCheck ? 'would change' : 'changed'} ${shown} (${result.rewritten} model(s))`,\n );\n\n if (!isCheck) {\n writeFileSync(file, result.output);\n }\n }\n }\n\n console.log(\n `devz codemod model-settings: ${changedCount} file(s) ${isCheck ? 'would change' : 'changed'}, ${reportCount} left untouched`,\n );\n\n return isCheck && changedCount > 0 ? 1 : 0;\n }\n}\n"]}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/cli/codemod/index.ts"],"names":[],"mappings":"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","sourcesContent":["import { readFileSync, writeFileSync } from 'node:fs';\nimport { relative, resolve } from 'node:path';\nimport { CodemodFiles } from './files';\nimport { migrateModelSettings } from './model-settings';\n\nconst USAGE = `Usage: devz codemod model-settings [paths...] [--write | --check]\n\nMoves @model settings to the grouped shape and skipMigrate to migration.use.\n\n --write write the changed files (without it, nothing is written)\n --check dry run that exits 1 when anything would change (for CI)\n --help print this text and exit\n\nWith no flag it lists what would change and writes nothing. Inside a git repository it reads the\nfiles git knows (tracked, or untracked and not ignored), so ignored folders are never touched.`;\n\n/** A usage error: nothing was read or written. */\nconst USAGE_ERROR_CODE = 2;\n\nclass CodemodFlags {\n static readonly WRITE = '--write';\n static readonly CHECK = '--check';\n static readonly HELP = '--help';\n static readonly HELP_SHORT = '-h';\n\n static readonly SCHEME_SET = new Set<string>([\n this.WRITE,\n this.CHECK,\n this.HELP,\n this.HELP_SHORT,\n ]);\n\n static isValid(value: string): boolean {\n return this.SCHEME_SET.has(value);\n }\n}\n\n/**\n * `devz codemod model-settings [paths...] [--write | --check]`: moves `@model` settings from the\n * flat keys (`defaultFilter`, `defaultLimit`, `maxLimit`, `scopeFilter`) to `query` and `access`,\n * and `skipMigrate` to `migration.use`. It is a dry run unless `--write` is given: it lists what\n * would change. `--check` is the same dry run, and exits 1 when anything would change.\n */\nexport class CodemodCommand {\n static async run(opts: { root: string; args: string[] }): Promise<number> {\n const { args } = opts;\n\n if (args.includes(CodemodFlags.HELP) || args.includes(CodemodFlags.HELP_SHORT)) {\n console.log(USAGE);\n return 0;\n }\n\n const [name, ...rest] = args;\n\n if (name !== 'model-settings') {\n console.error(`devz codemod: unknown codemod \"${name ?? ''}\"\\n${USAGE}`);\n return USAGE_ERROR_CODE;\n }\n\n const unknown = rest.filter(\n (argument) => argument.startsWith('-') && !CodemodFlags.isValid(argument),\n );\n if (unknown.length > 0) {\n console.error(\n `devz codemod: unknown option ${unknown.map((flag) => `\"${flag}\"`).join(', ')}\\n${USAGE}`,\n );\n return USAGE_ERROR_CODE;\n }\n\n const isWrite = rest.includes(CodemodFlags.WRITE);\n const isCheck = rest.includes(CodemodFlags.CHECK);\n\n if (isWrite && isCheck) {\n console.error(\n `devz codemod: ${CodemodFlags.WRITE} and ${CodemodFlags.CHECK} contradict each other\\n${USAGE}`,\n );\n return USAGE_ERROR_CODE;\n }\n\n const paths = rest.filter((argument) => !argument.startsWith('-'));\n const targets = (paths.length > 0 ? paths : ['.']).map((path) => resolve(opts.root, path));\n\n let typescript: typeof import('typescript');\n try {\n typescript = (await import('typescript')).default;\n } catch {\n console.error('devz codemod: the \"typescript\" package is required to read your sources');\n return 1;\n }\n\n let changedCount = 0;\n let reportCount = 0;\n\n for (const target of targets) {\n for (const file of CodemodFiles.collect({ target })) {\n const source = readFileSync(file, 'utf8');\n\n if (!source.includes('@model')) {\n continue;\n }\n\n const result = migrateModelSettings({ typescript, fileName: file, source });\n const shown = relative(opts.root, file) || file;\n\n for (const report of result.reports) {\n reportCount += 1;\n console.warn(`${shown}:${report.line}:${report.column} ${report.reason}`);\n }\n\n if (!result.changed) {\n continue;\n }\n\n changedCount += 1;\n console.log(\n `${isWrite ? 'changed' : 'would change'} ${shown} (${result.rewritten} model(s))`,\n );\n\n if (isWrite) {\n writeFileSync(file, result.output);\n }\n }\n }\n\n console.log(\n `devz codemod model-settings: ${changedCount} file(s) ${isWrite ? 'changed' : 'would change'}, ${reportCount} left untouched`,\n );\n\n if (!isWrite && changedCount > 0) {\n console.log(`Nothing was written. Run again with ${CodemodFlags.WRITE} to apply.`);\n }\n\n return isCheck && changedCount > 0 ? 1 : 0;\n }\n}\n"]}
@@ -15,7 +15,8 @@ export interface IModelSettingsResult {
15
15
  reports: IModelSettingsReport[];
16
16
  }
17
17
  /**
18
- * Rewrites the `settings` object literal of `@model({ ... })` from the flat keys to the grouped ones:
18
+ * Rewrites `@model({ ... })` from the old keys to the grouped ones. `skipMigrate` becomes
19
+ * `migration: { use: false }`. In the `settings` object literal:
19
20
  * `defaultFilter` to `query.filter`, `defaultLimit` and `maxLimit` to `query.limit.default` and
20
21
  * `query.limit.max`, `scopeFilter` to `access` (`writeGuard` to `write.mode`, `sharedRows` to
21
22
  * `write.sharedRows`).
@@ -1 +1 @@
1
- {"version":3,"file":"model-settings.d.ts","sourceRoot":"","sources":["../../../src/cli/codemod/model-settings.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,YAAY,CAAC;AAEjC,KAAK,WAAW,GAAG,OAAO,EAAE,CAAC;AAE7B,6DAA6D;AAC7D,MAAM,WAAW,oBAAoB;IACnC,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,WAAW,oBAAoB;IACnC,+EAA+E;IAC/E,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,OAAO,CAAC;IACjB,yDAAyD;IACzD,SAAS,EAAE,MAAM,CAAC;IAClB,OAAO,EAAE,oBAAoB,EAAE,CAAC;CACjC;AAWD;;;;;;;;;GASG;AACH,eAAO,MAAM,oBAAoB,GAAI,MAAM;IACzC,UAAU,EAAE,WAAW,CAAC;IACxB,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,EAAE,MAAM,CAAC;CAChB,KAAG,oBA+UH,CAAC"}
1
+ {"version":3,"file":"model-settings.d.ts","sourceRoot":"","sources":["../../../src/cli/codemod/model-settings.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,YAAY,CAAC;AAEjC,KAAK,WAAW,GAAG,OAAO,EAAE,CAAC;AAE7B,6DAA6D;AAC7D,MAAM,WAAW,oBAAoB;IACnC,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,WAAW,oBAAoB;IACnC,+EAA+E;IAC/E,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,OAAO,CAAC;IACjB,yDAAyD;IACzD,SAAS,EAAE,MAAM,CAAC;IAClB,OAAO,EAAE,oBAAoB,EAAE,CAAC;CACjC;AAWD;;;;;;;;;;GAUG;AACH,eAAO,MAAM,oBAAoB,GAAI,MAAM;IACzC,UAAU,EAAE,WAAW,CAAC;IACxB,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,EAAE,MAAM,CAAC;CAChB,KAAG,oBAuXH,CAAC"}
@@ -4,7 +4,8 @@ exports.migrateModelSettings = void 0;
4
4
  const QUERY_KEYS = { defaultFilter: 'filter', defaultLimit: 'default', maxLimit: 'max' };
5
5
  const OLD_KEYS = new Set([...Object.keys(QUERY_KEYS), 'scopeFilter']);
6
6
  /**
7
- * Rewrites the `settings` object literal of `@model({ ... })` from the flat keys to the grouped ones:
7
+ * Rewrites `@model({ ... })` from the old keys to the grouped ones. `skipMigrate` becomes
8
+ * `migration: { use: false }`. In the `settings` object literal:
8
9
  * `defaultFilter` to `query.filter`, `defaultLimit` and `maxLimit` to `query.limit.default` and
9
10
  * `query.limit.max`, `scopeFilter` to `access` (`writeGuard` to `write.mode`, `sharedRows` to
10
11
  * `write.sharedRows`).
@@ -226,6 +227,41 @@ const migrateModelSettings = (opts) => {
226
227
  edits.push(...localEdits);
227
228
  rewritten += 1;
228
229
  };
230
+ /**
231
+ * `skipMigrate: true` becomes `migration: { use: false }`; `skipMigrate: false` is the default and
232
+ * is removed. Anything that is not a boolean literal, or a model that already has `migration`, is
233
+ * reported and left alone.
234
+ */
235
+ const migrateSkipMigrate = (argument) => {
236
+ const property = argument.properties.find((candidate) => nameOf(candidate) === 'skipMigrate');
237
+ if (!property) {
238
+ return;
239
+ }
240
+ if (!typescript.isPropertyAssignment(property)) {
241
+ report(property, 'skipMigrate is a shorthand property; left untouched');
242
+ return;
243
+ }
244
+ if (argument.properties.some((candidate) => nameOf(candidate) === 'migration')) {
245
+ report(property, 'skipMigrate and migration are both set; left untouched');
246
+ return;
247
+ }
248
+ const { initializer } = property;
249
+ if (initializer.kind === typescript.SyntaxKind.TrueKeyword) {
250
+ edits.push({
251
+ start: property.getStart(sourceFile),
252
+ end: property.end,
253
+ text: 'migration: { use: false }',
254
+ });
255
+ }
256
+ else if (initializer.kind === typescript.SyntaxKind.FalseKeyword) {
257
+ edits.push(removal(property));
258
+ }
259
+ else {
260
+ report(property, 'skipMigrate is not a boolean literal; left untouched');
261
+ return;
262
+ }
263
+ rewritten += 1;
264
+ };
229
265
  const visit = (node) => {
230
266
  if (typescript.isDecorator(node) &&
231
267
  typescript.isCallExpression(node.expression) &&
@@ -243,6 +279,7 @@ const migrateModelSettings = (opts) => {
243
279
  report(settingsProperty, 'settings is not an object literal (a variable, a call or a shorthand); left untouched');
244
280
  }
245
281
  }
282
+ migrateSkipMigrate(argument);
246
283
  }
247
284
  }
248
285
  typescript.forEachChild(node, visit);
@@ -1 +1 @@
1
- {"version":3,"file":"model-settings.js","sourceRoot":"","sources":["../../../src/cli/codemod/model-settings.ts"],"names":[],"mappings":"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","sourcesContent":["import type ts from 'typescript';\n\ntype TTypeScript = typeof ts;\n\n/** One thing the codemod could not decide and left alone. */\nexport interface IModelSettingsReport {\n line: number;\n column: number;\n reason: string;\n}\n\nexport interface IModelSettingsResult {\n /** The source after the migration; equal to the input when nothing changed. */\n output: string;\n changed: boolean;\n /** How many `@model` settings objects were rewritten. */\n rewritten: number;\n reports: IModelSettingsReport[];\n}\n\ninterface IEdit {\n start: number;\n end: number;\n text: string;\n}\n\nconst QUERY_KEYS = { defaultFilter: 'filter', defaultLimit: 'default', maxLimit: 'max' } as const;\nconst OLD_KEYS = new Set<string>([...Object.keys(QUERY_KEYS), 'scopeFilter']);\n\n/**\n * Rewrites the `settings` object literal of `@model({ ... })` from the flat keys to the grouped ones:\n * `defaultFilter` to `query.filter`, `defaultLimit` and `maxLimit` to `query.limit.default` and\n * `query.limit.max`, `scopeFilter` to `access` (`writeGuard` to `write.mode`, `sharedRows` to\n * `write.sharedRows`).\n *\n * It edits by source range, so comments and formatting outside the rewritten properties stay. It\n * never guesses: a `settings` value that is not an object literal, a spread, a computed key, or a\n * `scopeFilter` that is not an object literal is reported and the model is left untouched.\n */\nexport const migrateModelSettings = (opts: {\n typescript: TTypeScript;\n fileName: string;\n source: string;\n}): IModelSettingsResult => {\n const { typescript, fileName, source } = opts;\n const sourceFile = typescript.createSourceFile(\n fileName,\n source,\n typescript.ScriptTarget.Latest,\n true,\n );\n const edits: IEdit[] = [];\n const reports: IModelSettingsReport[] = [];\n let rewritten = 0;\n\n const report = (node: ts.Node, reason: string) => {\n const { line, character } = sourceFile.getLineAndCharacterOfPosition(node.getStart(sourceFile));\n reports.push({ line: line + 1, column: character + 1, reason });\n };\n\n const nameOf = (property: ts.ObjectLiteralElementLike): string | undefined => {\n if (\n !typescript.isPropertyAssignment(property) &&\n !typescript.isShorthandPropertyAssignment(property)\n ) {\n return undefined;\n }\n\n const { name } = property;\n if (typescript.isIdentifier(name) || typescript.isStringLiteral(name)) {\n return name.text;\n }\n\n return undefined;\n };\n\n const text = (node: ts.Node) => source.slice(node.getStart(sourceFile), node.end);\n\n /**\n * The leading trivia of `first` (its own comments and indentation) followed by the comments that\n * were above the properties being merged into it, so no comment is dropped.\n */\n const composeLeading = (first: ts.Node, merged: ts.Node[]): string => {\n const trivia = source.slice(first.getFullStart(), first.getStart(sourceFile));\n const indent = /[ \\t]*$/.exec(trivia)?.[0] ?? '';\n const extra = merged\n .flatMap((node) => typescript.getLeadingCommentRanges(source, node.getFullStart()) ?? [])\n .map((range) => `${source.slice(range.pos, range.end)}\\n${indent}`)\n .join('');\n\n return trivia + extra;\n };\n\n /**\n * Removes a property with the comma after it; the last property has none, so the comma before it\n * goes instead, leaving no dangling `, }`.\n */\n const removal = (node: ts.Node): IEdit => {\n const after = /^\\s*,/.exec(source.slice(node.end));\n if (after) {\n return { start: node.getFullStart(), end: node.end + after[0].length, text: '' };\n }\n\n const before = /,\\s*$/.exec(source.slice(0, node.getFullStart()));\n const start = before ? node.getFullStart() - before[0].length : node.getFullStart();\n\n return { start, end: node.end, text: '' };\n };\n\n /** Appends members to an object literal: after its last member, or inside empty braces. */\n const append = (object: ts.ObjectLiteralExpression, members: string[]): IEdit => {\n const last = object.properties[object.properties.length - 1];\n if (last) {\n return { start: last.end, end: last.end, text: `, ${members.join(', ')}` };\n }\n\n const at = object.getStart(sourceFile) + 1;\n return { start: at, end: at, text: ` ${members.join(', ')} ` };\n };\n\n const migrateSettings = (settings: ts.ObjectLiteralExpression) => {\n const properties = [...settings.properties];\n const old = new Map<string, ts.PropertyAssignment>();\n let existingQuery: ts.PropertyAssignment | undefined;\n\n for (const property of properties) {\n if (typescript.isSpreadAssignment(property)) {\n // A spread can carry old keys we cannot see.\n report(property, 'settings contains a spread, so its keys are unknown; left untouched');\n return;\n }\n\n if (typescript.isMethodDeclaration(property)) {\n continue;\n }\n\n const name = nameOf(property);\n if (name === undefined) {\n report(property, 'settings contains a computed key; left untouched');\n return;\n }\n\n if (OLD_KEYS.has(name)) {\n if (!typescript.isPropertyAssignment(property)) {\n report(property, `settings.${name} is a shorthand property; left untouched`);\n return;\n }\n old.set(name, property);\n }\n\n if (name === 'query' && typescript.isPropertyAssignment(property)) {\n existingQuery = property;\n }\n }\n\n if (old.size === 0) {\n return;\n }\n\n const queryOld = [...old.keys()].filter((key) => key in QUERY_KEYS);\n const localEdits: IEdit[] = [];\n\n // ---- scopeFilter -> access (rename the key, rewrite writeGuard / sharedRows inside)\n const scopeFilter = old.get('scopeFilter');\n if (scopeFilter) {\n if (!typescript.isObjectLiteralExpression(scopeFilter.initializer)) {\n report(scopeFilter, 'settings.scopeFilter is not an object literal; left untouched');\n return;\n }\n\n const inner = scopeFilter.initializer;\n const guard = inner.properties.find(\n (property) =>\n nameOf(property) === 'writeGuard' && typescript.isPropertyAssignment(property),\n ) as ts.PropertyAssignment | undefined;\n const shared = inner.properties.find(\n (property) =>\n nameOf(property) === 'sharedRows' && typescript.isPropertyAssignment(property),\n ) as ts.PropertyAssignment | undefined;\n const hasSpread = inner.properties.some((property) =>\n typescript.isSpreadAssignment(property),\n );\n\n if (hasSpread) {\n report(scopeFilter, 'settings.scopeFilter contains a spread; left untouched');\n return;\n }\n\n if (inner.properties.some((property) => nameOf(property) === 'write')) {\n report(scopeFilter, 'settings.scopeFilter already has a write key; left untouched');\n return;\n }\n\n localEdits.push({\n start: scopeFilter.name.getStart(sourceFile),\n end: scopeFilter.name.end,\n text: 'access',\n });\n\n if (guard || shared) {\n const members = [\n guard ? `mode: ${text(guard.initializer)}` : undefined,\n shared ? `sharedRows: ${text(shared.initializer)}` : undefined,\n ].filter((member): member is string => member !== undefined);\n const ordered = [guard, shared]\n .filter((property): property is ts.PropertyAssignment => property !== undefined)\n .sort((a, b) => a.getStart(sourceFile) - b.getStart(sourceFile));\n const [first, ...merged] = ordered;\n\n localEdits.push({\n start: first.getFullStart(),\n end: first.end,\n text: `${composeLeading(first, merged)}write: { ${members.join(', ')} }`,\n });\n\n for (const second of merged) {\n localEdits.push(removal(second));\n }\n }\n }\n\n // ---- defaultFilter / defaultLimit / maxLimit -> query\n if (queryOld.length > 0) {\n const filter = old.get('defaultFilter');\n const defaultLimit = old.get('defaultLimit');\n const maxLimit = old.get('maxLimit');\n\n if (existingQuery) {\n if (!typescript.isObjectLiteralExpression(existingQuery.initializer)) {\n report(existingQuery, 'settings.query is not an object literal; left untouched');\n return;\n }\n\n const queryObject = existingQuery.initializer;\n const queryKeys = new Set(queryObject.properties.map((property) => nameOf(property)));\n const existingLimit = queryObject.properties.find(\n (property) => nameOf(property) === 'limit',\n ) as ts.PropertyAssignment | undefined;\n\n if (filter && queryKeys.has('filter')) {\n report(\n filter,\n 'settings.defaultFilter and settings.query.filter are both set; left untouched',\n );\n return;\n }\n\n if ((defaultLimit || maxLimit) && existingLimit) {\n if (!typescript.isObjectLiteralExpression(existingLimit.initializer)) {\n report(existingLimit, 'settings.query.limit is not an object literal; left untouched');\n return;\n }\n\n const limitKeys = new Set(\n existingLimit.initializer.properties.map((property) => nameOf(property)),\n );\n if ((defaultLimit && limitKeys.has('default')) || (maxLimit && limitKeys.has('max'))) {\n report(existingLimit, 'an old limit key and its new key are both set; left untouched');\n return;\n }\n }\n\n const additions: string[] = [];\n if (filter) {\n additions.push(`filter: ${text(filter.initializer)}`);\n }\n\n const limitMembers = [\n defaultLimit ? `default: ${text(defaultLimit.initializer)}` : undefined,\n maxLimit ? `max: ${text(maxLimit.initializer)}` : undefined,\n ].filter((member): member is string => member !== undefined);\n\n if (limitMembers.length > 0 && existingLimit) {\n localEdits.push(\n append(existingLimit.initializer as ts.ObjectLiteralExpression, limitMembers),\n );\n } else if (limitMembers.length > 0) {\n additions.push(`limit: { ${limitMembers.join(', ')} }`);\n }\n\n if (additions.length > 0) {\n localEdits.push(append(queryObject, additions));\n }\n\n for (const key of queryOld) {\n const property = old.get(key) as ts.PropertyAssignment;\n localEdits.push(removal(property));\n }\n } else {\n const members: string[] = [];\n if (filter) {\n members.push(`filter: ${text(filter.initializer)}`);\n }\n\n const limitMembers = [\n defaultLimit ? `default: ${text(defaultLimit.initializer)}` : undefined,\n maxLimit ? `max: ${text(maxLimit.initializer)}` : undefined,\n ].filter((member): member is string => member !== undefined);\n if (limitMembers.length > 0) {\n members.push(`limit: { ${limitMembers.join(', ')} }`);\n }\n\n const ordered = queryOld\n .map((key) => old.get(key) as ts.PropertyAssignment)\n .sort((a, b) => a.getStart(sourceFile) - b.getStart(sourceFile));\n const [first, ...others] = ordered;\n\n localEdits.push({\n start: first.getFullStart(),\n end: first.end,\n text: `${composeLeading(first, others)}query: { ${members.join(', ')} }`,\n });\n\n for (const other of others) {\n localEdits.push(removal(other));\n }\n }\n }\n\n edits.push(...localEdits);\n rewritten += 1;\n };\n\n const visit = (node: ts.Node) => {\n if (\n typescript.isDecorator(node) &&\n typescript.isCallExpression(node.expression) &&\n typescript.isIdentifier(node.expression.expression) &&\n node.expression.expression.text === 'model'\n ) {\n const argument = node.expression.arguments[0];\n\n if (argument && typescript.isObjectLiteralExpression(argument)) {\n const settingsProperty = argument.properties.find(\n (property) => nameOf(property) === 'settings',\n );\n\n if (settingsProperty) {\n if (\n typescript.isPropertyAssignment(settingsProperty) &&\n typescript.isObjectLiteralExpression(settingsProperty.initializer)\n ) {\n migrateSettings(settingsProperty.initializer);\n } else {\n report(\n settingsProperty,\n 'settings is not an object literal (a variable, a call or a shorthand); left untouched',\n );\n }\n }\n }\n }\n\n typescript.forEachChild(node, visit);\n };\n\n visit(sourceFile);\n\n // Neighbouring removals can touch the same comma: merge those, then apply from the end so\n // earlier offsets stay valid.\n const ascending = [...edits].sort((a, b) => a.start - b.start || a.end - b.end);\n const merged: IEdit[] = [];\n for (const edit of ascending) {\n const previous = merged[merged.length - 1];\n if (previous && edit.start < previous.end && previous.text === '' && edit.text === '') {\n previous.end = Math.max(previous.end, edit.end);\n continue;\n }\n\n merged.push({ ...edit });\n }\n\n let output = source;\n for (const edit of merged.reverse()) {\n output = output.slice(0, edit.start) + edit.text + output.slice(edit.end);\n }\n\n return { output, changed: output !== source, rewritten, reports };\n};\n"]}
1
+ {"version":3,"file":"model-settings.js","sourceRoot":"","sources":["../../../src/cli/codemod/model-settings.ts"],"names":[],"mappings":"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","sourcesContent":["import type ts from 'typescript';\n\ntype TTypeScript = typeof ts;\n\n/** One thing the codemod could not decide and left alone. */\nexport interface IModelSettingsReport {\n line: number;\n column: number;\n reason: string;\n}\n\nexport interface IModelSettingsResult {\n /** The source after the migration; equal to the input when nothing changed. */\n output: string;\n changed: boolean;\n /** How many `@model` settings objects were rewritten. */\n rewritten: number;\n reports: IModelSettingsReport[];\n}\n\ninterface IEdit {\n start: number;\n end: number;\n text: string;\n}\n\nconst QUERY_KEYS = { defaultFilter: 'filter', defaultLimit: 'default', maxLimit: 'max' } as const;\nconst OLD_KEYS = new Set<string>([...Object.keys(QUERY_KEYS), 'scopeFilter']);\n\n/**\n * Rewrites `@model({ ... })` from the old keys to the grouped ones. `skipMigrate` becomes\n * `migration: { use: false }`. In the `settings` object literal:\n * `defaultFilter` to `query.filter`, `defaultLimit` and `maxLimit` to `query.limit.default` and\n * `query.limit.max`, `scopeFilter` to `access` (`writeGuard` to `write.mode`, `sharedRows` to\n * `write.sharedRows`).\n *\n * It edits by source range, so comments and formatting outside the rewritten properties stay. It\n * never guesses: a `settings` value that is not an object literal, a spread, a computed key, or a\n * `scopeFilter` that is not an object literal is reported and the model is left untouched.\n */\nexport const migrateModelSettings = (opts: {\n typescript: TTypeScript;\n fileName: string;\n source: string;\n}): IModelSettingsResult => {\n const { typescript, fileName, source } = opts;\n const sourceFile = typescript.createSourceFile(\n fileName,\n source,\n typescript.ScriptTarget.Latest,\n true,\n );\n const edits: IEdit[] = [];\n const reports: IModelSettingsReport[] = [];\n let rewritten = 0;\n\n const report = (node: ts.Node, reason: string) => {\n const { line, character } = sourceFile.getLineAndCharacterOfPosition(node.getStart(sourceFile));\n reports.push({ line: line + 1, column: character + 1, reason });\n };\n\n const nameOf = (property: ts.ObjectLiteralElementLike): string | undefined => {\n if (\n !typescript.isPropertyAssignment(property) &&\n !typescript.isShorthandPropertyAssignment(property)\n ) {\n return undefined;\n }\n\n const { name } = property;\n if (typescript.isIdentifier(name) || typescript.isStringLiteral(name)) {\n return name.text;\n }\n\n return undefined;\n };\n\n const text = (node: ts.Node) => source.slice(node.getStart(sourceFile), node.end);\n\n /**\n * The leading trivia of `first` (its own comments and indentation) followed by the comments that\n * were above the properties being merged into it, so no comment is dropped.\n */\n const composeLeading = (first: ts.Node, merged: ts.Node[]): string => {\n const trivia = source.slice(first.getFullStart(), first.getStart(sourceFile));\n const indent = /[ \\t]*$/.exec(trivia)?.[0] ?? '';\n const extra = merged\n .flatMap((node) => typescript.getLeadingCommentRanges(source, node.getFullStart()) ?? [])\n .map((range) => `${source.slice(range.pos, range.end)}\\n${indent}`)\n .join('');\n\n return trivia + extra;\n };\n\n /**\n * Removes a property with the comma after it; the last property has none, so the comma before it\n * goes instead, leaving no dangling `, }`.\n */\n const removal = (node: ts.Node): IEdit => {\n const after = /^\\s*,/.exec(source.slice(node.end));\n if (after) {\n return { start: node.getFullStart(), end: node.end + after[0].length, text: '' };\n }\n\n const before = /,\\s*$/.exec(source.slice(0, node.getFullStart()));\n const start = before ? node.getFullStart() - before[0].length : node.getFullStart();\n\n return { start, end: node.end, text: '' };\n };\n\n /** Appends members to an object literal: after its last member, or inside empty braces. */\n const append = (object: ts.ObjectLiteralExpression, members: string[]): IEdit => {\n const last = object.properties[object.properties.length - 1];\n if (last) {\n return { start: last.end, end: last.end, text: `, ${members.join(', ')}` };\n }\n\n const at = object.getStart(sourceFile) + 1;\n return { start: at, end: at, text: ` ${members.join(', ')} ` };\n };\n\n const migrateSettings = (settings: ts.ObjectLiteralExpression) => {\n const properties = [...settings.properties];\n const old = new Map<string, ts.PropertyAssignment>();\n let existingQuery: ts.PropertyAssignment | undefined;\n\n for (const property of properties) {\n if (typescript.isSpreadAssignment(property)) {\n // A spread can carry old keys we cannot see.\n report(property, 'settings contains a spread, so its keys are unknown; left untouched');\n return;\n }\n\n if (typescript.isMethodDeclaration(property)) {\n continue;\n }\n\n const name = nameOf(property);\n if (name === undefined) {\n report(property, 'settings contains a computed key; left untouched');\n return;\n }\n\n if (OLD_KEYS.has(name)) {\n if (!typescript.isPropertyAssignment(property)) {\n report(property, `settings.${name} is a shorthand property; left untouched`);\n return;\n }\n old.set(name, property);\n }\n\n if (name === 'query' && typescript.isPropertyAssignment(property)) {\n existingQuery = property;\n }\n }\n\n if (old.size === 0) {\n return;\n }\n\n const queryOld = [...old.keys()].filter((key) => key in QUERY_KEYS);\n const localEdits: IEdit[] = [];\n\n // ---- scopeFilter -> access (rename the key, rewrite writeGuard / sharedRows inside)\n const scopeFilter = old.get('scopeFilter');\n if (scopeFilter) {\n if (!typescript.isObjectLiteralExpression(scopeFilter.initializer)) {\n report(scopeFilter, 'settings.scopeFilter is not an object literal; left untouched');\n return;\n }\n\n const inner = scopeFilter.initializer;\n const guard = inner.properties.find(\n (property) =>\n nameOf(property) === 'writeGuard' && typescript.isPropertyAssignment(property),\n ) as ts.PropertyAssignment | undefined;\n const shared = inner.properties.find(\n (property) =>\n nameOf(property) === 'sharedRows' && typescript.isPropertyAssignment(property),\n ) as ts.PropertyAssignment | undefined;\n const hasSpread = inner.properties.some((property) =>\n typescript.isSpreadAssignment(property),\n );\n\n if (hasSpread) {\n report(scopeFilter, 'settings.scopeFilter contains a spread; left untouched');\n return;\n }\n\n if (inner.properties.some((property) => nameOf(property) === 'write')) {\n report(scopeFilter, 'settings.scopeFilter already has a write key; left untouched');\n return;\n }\n\n localEdits.push({\n start: scopeFilter.name.getStart(sourceFile),\n end: scopeFilter.name.end,\n text: 'access',\n });\n\n if (guard || shared) {\n const members = [\n guard ? `mode: ${text(guard.initializer)}` : undefined,\n shared ? `sharedRows: ${text(shared.initializer)}` : undefined,\n ].filter((member): member is string => member !== undefined);\n const ordered = [guard, shared]\n .filter((property): property is ts.PropertyAssignment => property !== undefined)\n .sort((a, b) => a.getStart(sourceFile) - b.getStart(sourceFile));\n const [first, ...merged] = ordered;\n\n localEdits.push({\n start: first.getFullStart(),\n end: first.end,\n text: `${composeLeading(first, merged)}write: { ${members.join(', ')} }`,\n });\n\n for (const second of merged) {\n localEdits.push(removal(second));\n }\n }\n }\n\n // ---- defaultFilter / defaultLimit / maxLimit -> query\n if (queryOld.length > 0) {\n const filter = old.get('defaultFilter');\n const defaultLimit = old.get('defaultLimit');\n const maxLimit = old.get('maxLimit');\n\n if (existingQuery) {\n if (!typescript.isObjectLiteralExpression(existingQuery.initializer)) {\n report(existingQuery, 'settings.query is not an object literal; left untouched');\n return;\n }\n\n const queryObject = existingQuery.initializer;\n const queryKeys = new Set(queryObject.properties.map((property) => nameOf(property)));\n const existingLimit = queryObject.properties.find(\n (property) => nameOf(property) === 'limit',\n ) as ts.PropertyAssignment | undefined;\n\n if (filter && queryKeys.has('filter')) {\n report(\n filter,\n 'settings.defaultFilter and settings.query.filter are both set; left untouched',\n );\n return;\n }\n\n if ((defaultLimit || maxLimit) && existingLimit) {\n if (!typescript.isObjectLiteralExpression(existingLimit.initializer)) {\n report(existingLimit, 'settings.query.limit is not an object literal; left untouched');\n return;\n }\n\n const limitKeys = new Set(\n existingLimit.initializer.properties.map((property) => nameOf(property)),\n );\n if ((defaultLimit && limitKeys.has('default')) || (maxLimit && limitKeys.has('max'))) {\n report(existingLimit, 'an old limit key and its new key are both set; left untouched');\n return;\n }\n }\n\n const additions: string[] = [];\n if (filter) {\n additions.push(`filter: ${text(filter.initializer)}`);\n }\n\n const limitMembers = [\n defaultLimit ? `default: ${text(defaultLimit.initializer)}` : undefined,\n maxLimit ? `max: ${text(maxLimit.initializer)}` : undefined,\n ].filter((member): member is string => member !== undefined);\n\n if (limitMembers.length > 0 && existingLimit) {\n localEdits.push(\n append(existingLimit.initializer as ts.ObjectLiteralExpression, limitMembers),\n );\n } else if (limitMembers.length > 0) {\n additions.push(`limit: { ${limitMembers.join(', ')} }`);\n }\n\n if (additions.length > 0) {\n localEdits.push(append(queryObject, additions));\n }\n\n for (const key of queryOld) {\n const property = old.get(key) as ts.PropertyAssignment;\n localEdits.push(removal(property));\n }\n } else {\n const members: string[] = [];\n if (filter) {\n members.push(`filter: ${text(filter.initializer)}`);\n }\n\n const limitMembers = [\n defaultLimit ? `default: ${text(defaultLimit.initializer)}` : undefined,\n maxLimit ? `max: ${text(maxLimit.initializer)}` : undefined,\n ].filter((member): member is string => member !== undefined);\n if (limitMembers.length > 0) {\n members.push(`limit: { ${limitMembers.join(', ')} }`);\n }\n\n const ordered = queryOld\n .map((key) => old.get(key) as ts.PropertyAssignment)\n .sort((a, b) => a.getStart(sourceFile) - b.getStart(sourceFile));\n const [first, ...others] = ordered;\n\n localEdits.push({\n start: first.getFullStart(),\n end: first.end,\n text: `${composeLeading(first, others)}query: { ${members.join(', ')} }`,\n });\n\n for (const other of others) {\n localEdits.push(removal(other));\n }\n }\n }\n\n edits.push(...localEdits);\n rewritten += 1;\n };\n\n /**\n * `skipMigrate: true` becomes `migration: { use: false }`; `skipMigrate: false` is the default and\n * is removed. Anything that is not a boolean literal, or a model that already has `migration`, is\n * reported and left alone.\n */\n const migrateSkipMigrate = (argument: ts.ObjectLiteralExpression) => {\n const property = argument.properties.find((candidate) => nameOf(candidate) === 'skipMigrate');\n if (!property) {\n return;\n }\n\n if (!typescript.isPropertyAssignment(property)) {\n report(property, 'skipMigrate is a shorthand property; left untouched');\n return;\n }\n\n if (argument.properties.some((candidate) => nameOf(candidate) === 'migration')) {\n report(property, 'skipMigrate and migration are both set; left untouched');\n return;\n }\n\n const { initializer } = property;\n if (initializer.kind === typescript.SyntaxKind.TrueKeyword) {\n edits.push({\n start: property.getStart(sourceFile),\n end: property.end,\n text: 'migration: { use: false }',\n });\n } else if (initializer.kind === typescript.SyntaxKind.FalseKeyword) {\n edits.push(removal(property));\n } else {\n report(property, 'skipMigrate is not a boolean literal; left untouched');\n return;\n }\n\n rewritten += 1;\n };\n\n const visit = (node: ts.Node) => {\n if (\n typescript.isDecorator(node) &&\n typescript.isCallExpression(node.expression) &&\n typescript.isIdentifier(node.expression.expression) &&\n node.expression.expression.text === 'model'\n ) {\n const argument = node.expression.arguments[0];\n\n if (argument && typescript.isObjectLiteralExpression(argument)) {\n const settingsProperty = argument.properties.find(\n (property) => nameOf(property) === 'settings',\n );\n\n if (settingsProperty) {\n if (\n typescript.isPropertyAssignment(settingsProperty) &&\n typescript.isObjectLiteralExpression(settingsProperty.initializer)\n ) {\n migrateSettings(settingsProperty.initializer);\n } else {\n report(\n settingsProperty,\n 'settings is not an object literal (a variable, a call or a shorthand); left untouched',\n );\n }\n }\n\n migrateSkipMigrate(argument);\n }\n }\n\n typescript.forEachChild(node, visit);\n };\n\n visit(sourceFile);\n\n // Neighbouring removals can touch the same comma: merge those, then apply from the end so\n // earlier offsets stay valid.\n const ascending = [...edits].sort((a, b) => a.start - b.start || a.end - b.end);\n const merged: IEdit[] = [];\n for (const edit of ascending) {\n const previous = merged[merged.length - 1];\n if (previous && edit.start < previous.end && previous.text === '' && edit.text === '') {\n previous.end = Math.max(previous.end, edit.end);\n continue;\n }\n\n merged.push({ ...edit });\n }\n\n let output = source;\n for (const edit of merged.reverse()) {\n output = output.slice(0, edit.start) + edit.text + output.slice(edit.end);\n }\n\n return { output, changed: output !== source, rewritten, reports };\n};\n"]}