miniread 1.83.0 → 1.84.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.
- package/dist/core/stable-naming.d.ts +4 -0
- package/dist/core/stable-naming.js +1 -0
- package/dist/transforms/_generated/manifest.js +5 -0
- package/dist/transforms/_generated/registry.js +2 -0
- package/dist/transforms/recommended-transform-order.d.ts +1 -1
- package/dist/transforms/recommended-transform-order.js +1 -0
- package/dist/transforms/rename-platform-win32-flags/apply-hash-stable-win32-renames.d.ts +8 -0
- package/dist/transforms/rename-platform-win32-flags/apply-hash-stable-win32-renames.js +37 -0
- package/dist/transforms/rename-platform-win32-flags/is-safe-process-binding.d.ts +3 -0
- package/dist/transforms/rename-platform-win32-flags/is-safe-process-binding.js +48 -0
- package/dist/transforms/rename-platform-win32-flags/manifest.json +12 -0
- package/dist/transforms/rename-platform-win32-flags/rename-platform-win32-flags-transform.d.ts +2 -0
- package/dist/transforms/rename-platform-win32-flags/rename-platform-win32-flags-transform.js +112 -0
- package/package.json +1 -1
|
@@ -41,6 +41,10 @@ type RenameEntry = {
|
|
|
41
41
|
/** Desired semantic base name (e.g., "timeoutId", "error") */
|
|
42
42
|
baseName: string;
|
|
43
43
|
};
|
|
44
|
+
export declare const findAvailableStableRenameName: (scope: Scope, binding: Binding | undefined, baseName: string, startIndex: number, currentName: string) => {
|
|
45
|
+
name: string;
|
|
46
|
+
nextIndex: number;
|
|
47
|
+
};
|
|
44
48
|
/**
|
|
45
49
|
* Collects rename operations and applies them with automatic stability logic.
|
|
46
50
|
*
|
|
@@ -154,6 +154,7 @@ const findAvailableName = (scope, binding, baseName, startIndex, canBeStable, cu
|
|
|
154
154
|
return { name: candidateReadable, nextIndex: index + 1 };
|
|
155
155
|
}
|
|
156
156
|
};
|
|
157
|
+
export const findAvailableStableRenameName = (scope, binding, baseName, startIndex, currentName) => findAvailableName(scope, binding, baseName, startIndex, true, currentName);
|
|
157
158
|
/**
|
|
158
159
|
* Collects rename operations and applies them with automatic stability logic.
|
|
159
160
|
*
|
|
@@ -236,6 +236,10 @@ const manifestData = {
|
|
|
236
236
|
notes: "Extends v1 to also handle this.property = param assignments.",
|
|
237
237
|
evaluations: { "legacy:evaluation": { "evaluatedAt": "2026-01-25T20:07:10.063Z", "changedLines": 20493, "durationSeconds": 0, "stableNames": 0, "diffSizePercent": 99.99244056393393 }, "claude-code-2.1.10:claude-code-2.1.11": { "diffSizePercent": 99.99244056393393, "evaluatedAt": "2026-01-25T23:24:32.608Z", "changedLines": 20493, "durationSeconds": 56.743425875, "stableNames": 1357 } },
|
|
238
238
|
},
|
|
239
|
+
"rename-platform-win32-flags": {
|
|
240
|
+
recommended: true,
|
|
241
|
+
evaluations: { "claude-code-2.1.10:claude-code-2.1.11": { "diffSizePercent": 99.99623032701913, "evaluatedAt": "2026-02-06T14:58:00.533Z", "changedLines": 42, "durationSeconds": 224.696743585, "stableNames": 1358 } },
|
|
242
|
+
},
|
|
239
243
|
"rename-process-stdout-handlers": {
|
|
240
244
|
recommended: false,
|
|
241
245
|
notes: "Measured with baseline none: -0.0019%. Kept out of recommended due to slight negative diff impact, but useful for readability in stdout hook handlers.",
|
|
@@ -466,6 +470,7 @@ export const recommendedTransformIds = [
|
|
|
466
470
|
"rename-charcode-variables-v2",
|
|
467
471
|
"rename-client-aliases",
|
|
468
472
|
"rename-comparison-flags",
|
|
473
|
+
"rename-platform-win32-flags",
|
|
469
474
|
"rename-date-now-start-times",
|
|
470
475
|
"rename-default-options-parameters-v2",
|
|
471
476
|
"rename-deferred-resolve-parameters",
|
|
@@ -48,6 +48,7 @@ import { renameObjectKeysReducerParametersTransform } from "../rename-object-key
|
|
|
48
48
|
import { renameObjectKeysVariablesTransform } from "../rename-object-keys-variables/rename-object-keys-variables-transform.js";
|
|
49
49
|
import { renameParametersToMatchPropertiesTransform } from "../rename-parameters-to-match-properties/rename-parameters-to-match-properties-transform.js";
|
|
50
50
|
import { renameParametersToMatchPropertiesV2Transform } from "../rename-parameters-to-match-properties-v2/rename-parameters-to-match-properties-v2-transform.js";
|
|
51
|
+
import { renamePlatformWin32FlagsTransform } from "../rename-platform-win32-flags/rename-platform-win32-flags-transform.js";
|
|
51
52
|
import { renameProcessStdoutHandlersTransform } from "../rename-process-stdout-handlers/rename-process-stdout-handlers-transform.js";
|
|
52
53
|
import { renamePromiseCatchParametersTransform } from "../rename-promise-catch-parameters/rename-promise-catch-parameters-transform.js";
|
|
53
54
|
import { renamePromiseExecutorParametersTransform } from "../rename-promise-executor-parameters/rename-promise-executor-parameters-transform.js";
|
|
@@ -137,6 +138,7 @@ export const transformRegistry = {
|
|
|
137
138
|
[renameObjectKeysVariablesTransform.id]: renameObjectKeysVariablesTransform,
|
|
138
139
|
[renameParametersToMatchPropertiesTransform.id]: renameParametersToMatchPropertiesTransform,
|
|
139
140
|
[renameParametersToMatchPropertiesV2Transform.id]: renameParametersToMatchPropertiesV2Transform,
|
|
141
|
+
[renamePlatformWin32FlagsTransform.id]: renamePlatformWin32FlagsTransform,
|
|
140
142
|
[renameProcessStdoutHandlersTransform.id]: renameProcessStdoutHandlersTransform,
|
|
141
143
|
[renamePromiseCatchParametersTransform.id]: renamePromiseCatchParametersTransform,
|
|
142
144
|
[renamePromiseExecutorParametersTransform.id]: renamePromiseExecutorParametersTransform,
|
|
@@ -4,4 +4,4 @@
|
|
|
4
4
|
* This lets us tune transform interactions intentionally instead of relying on
|
|
5
5
|
* alphabetical ID sorting.
|
|
6
6
|
*/
|
|
7
|
-
export declare const recommendedTransformOrder: readonly ["stabilize-top-level-bindings", "stabilize-nested-bindings", "stabilize-deferred-top-level-bindings", "stabilize-deferred-stable-rhs", "expand-boolean-literals", "expand-sequence-expressions-v5", "expand-special-number-literals", "expand-typeof-undefined-comparisons", "expand-undefined-literals", "remove-redundant-else", "rename-arguments-length-flags", "rename-asap-wrappers", "rename-awaiter-parameters", "rename-awaiter-helper-functions", "rename-buffer-variables", "rename-to-buffer-results", "rename-catch-parameters", "rename-promise-catch-parameters", "rename-char-code-at", "rename-charcode-variables-v2", "rename-client-aliases", "rename-comparison-flags", "rename-date-now-start-times", "rename-default-options-parameters-v2", "rename-deferred-resolve-parameters", "rename-deferred-resolve-parameters-v2", "rename-destructured-aliases", "rename-rest-parameters", "rename-rest-pop-callbacks", "rename-execfile-arguments", "rename-file-reader-variables", "rename-error-first-callback-parameters", "rename-error-first-callback-parameters-v2", "rename-error-variables", "rename-event-parameters", "rename-add-event-listener-parameters", "rename-http-server-parameters", "rename-fs-sync-variables", "rename-http-method-parameters", "rename-interval-ids", "rename-indexeddb-request-variables", "rename-loop-index-variables-v3", "rename-loop-length-variables", "rename-document-fragment-variables", "rename-object-keys-variables", "rename-object-keys-iterator-variables", "rename-object-keys-reducer-parameters", "rename-parameters-to-match-properties-v2", "rename-invalid-parameter-arguments", "rename-promise-executor-parameters-v2", "rename-range-parameters", "rename-read-file-lines", "rename-regex-builders", "rename-regex-source-parameters", "rename-search-parameters-variables", "rename-file-extension-variables", "rename-string-split-variables", "rename-this-aliases", "rename-timeout-promises", "rename-timeout-ids", "rename-typeof-variables", "rename-typescript-helper-aliases", "rename-uint8array-concat-variables", "rename-url-parameters", "rename-url-variables", "rename-use-reference-guards-v2", "rename-use-reference-sets", "rename-worker-handles", "rename-zod-check-parameters", "simplify-boolean-negations", "simplify-string-trim", "split-variable-declarations", "use-optional-chaining", "use-object-property-shorthand", "use-object-shorthand", "replace-dynamic-require-eval"];
|
|
7
|
+
export declare const recommendedTransformOrder: readonly ["stabilize-top-level-bindings", "stabilize-nested-bindings", "stabilize-deferred-top-level-bindings", "stabilize-deferred-stable-rhs", "expand-boolean-literals", "expand-sequence-expressions-v5", "expand-special-number-literals", "expand-typeof-undefined-comparisons", "expand-undefined-literals", "remove-redundant-else", "rename-arguments-length-flags", "rename-asap-wrappers", "rename-awaiter-parameters", "rename-awaiter-helper-functions", "rename-buffer-variables", "rename-to-buffer-results", "rename-catch-parameters", "rename-promise-catch-parameters", "rename-char-code-at", "rename-charcode-variables-v2", "rename-client-aliases", "rename-comparison-flags", "rename-platform-win32-flags", "rename-date-now-start-times", "rename-default-options-parameters-v2", "rename-deferred-resolve-parameters", "rename-deferred-resolve-parameters-v2", "rename-destructured-aliases", "rename-rest-parameters", "rename-rest-pop-callbacks", "rename-execfile-arguments", "rename-file-reader-variables", "rename-error-first-callback-parameters", "rename-error-first-callback-parameters-v2", "rename-error-variables", "rename-event-parameters", "rename-add-event-listener-parameters", "rename-http-server-parameters", "rename-fs-sync-variables", "rename-http-method-parameters", "rename-interval-ids", "rename-indexeddb-request-variables", "rename-loop-index-variables-v3", "rename-loop-length-variables", "rename-document-fragment-variables", "rename-object-keys-variables", "rename-object-keys-iterator-variables", "rename-object-keys-reducer-parameters", "rename-parameters-to-match-properties-v2", "rename-invalid-parameter-arguments", "rename-promise-executor-parameters-v2", "rename-range-parameters", "rename-read-file-lines", "rename-regex-builders", "rename-regex-source-parameters", "rename-search-parameters-variables", "rename-file-extension-variables", "rename-string-split-variables", "rename-this-aliases", "rename-timeout-promises", "rename-timeout-ids", "rename-typeof-variables", "rename-typescript-helper-aliases", "rename-uint8array-concat-variables", "rename-url-parameters", "rename-url-variables", "rename-use-reference-guards-v2", "rename-use-reference-sets", "rename-worker-handles", "rename-zod-check-parameters", "simplify-boolean-negations", "simplify-string-trim", "split-variable-declarations", "use-optional-chaining", "use-object-property-shorthand", "use-object-shorthand", "replace-dynamic-require-eval"];
|
|
@@ -36,6 +36,7 @@ export const recommendedTransformOrder = [
|
|
|
36
36
|
"rename-charcode-variables-v2",
|
|
37
37
|
"rename-client-aliases",
|
|
38
38
|
"rename-comparison-flags",
|
|
39
|
+
"rename-platform-win32-flags",
|
|
39
40
|
"rename-date-now-start-times",
|
|
40
41
|
"rename-default-options-parameters-v2",
|
|
41
42
|
"rename-deferred-resolve-parameters",
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import type { Binding, Scope } from "@babel/traverse";
|
|
2
|
+
export type HashRenameEntry = {
|
|
3
|
+
baseName: string;
|
|
4
|
+
binding: Binding;
|
|
5
|
+
currentName: string;
|
|
6
|
+
scope: Scope;
|
|
7
|
+
};
|
|
8
|
+
export declare const applyHashStableWin32Renames: (hashEntries: HashRenameEntry[]) => number;
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { findAvailableStableRenameName } from "../../core/stable-naming.js";
|
|
2
|
+
export const applyHashStableWin32Renames = (hashEntries) => {
|
|
3
|
+
let transformationsApplied = 0;
|
|
4
|
+
const hashScopeGroups = new Map();
|
|
5
|
+
for (const entry of hashEntries) {
|
|
6
|
+
let baseNameMap = hashScopeGroups.get(entry.scope);
|
|
7
|
+
if (!baseNameMap) {
|
|
8
|
+
baseNameMap = new Map();
|
|
9
|
+
hashScopeGroups.set(entry.scope, baseNameMap);
|
|
10
|
+
}
|
|
11
|
+
let baseGroup = baseNameMap.get(entry.baseName);
|
|
12
|
+
if (!baseGroup) {
|
|
13
|
+
baseGroup = [];
|
|
14
|
+
baseNameMap.set(entry.baseName, baseGroup);
|
|
15
|
+
}
|
|
16
|
+
baseGroup.push(entry);
|
|
17
|
+
}
|
|
18
|
+
for (const [scope, baseNameMap] of hashScopeGroups) {
|
|
19
|
+
for (const [baseName, baseGroup] of baseNameMap) {
|
|
20
|
+
const sortedGroup = baseGroup.toSorted((a, b) => a.currentName < b.currentName
|
|
21
|
+
? -1
|
|
22
|
+
: a.currentName > b.currentName
|
|
23
|
+
? 1
|
|
24
|
+
: 0);
|
|
25
|
+
let index = 1;
|
|
26
|
+
for (const entry of sortedGroup) {
|
|
27
|
+
const target = findAvailableStableRenameName(scope, entry.binding, baseName, index, entry.currentName);
|
|
28
|
+
index = target.nextIndex;
|
|
29
|
+
if (target.name === entry.currentName)
|
|
30
|
+
continue;
|
|
31
|
+
scope.rename(entry.currentName, target.name);
|
|
32
|
+
transformationsApplied++;
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
return transformationsApplied;
|
|
37
|
+
};
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import { isNodeRequireInScope } from "../is-require-from-url-module.js";
|
|
2
|
+
const isProcessSpecifier = (value) => value === "process" ||
|
|
3
|
+
value === "node:process" ||
|
|
4
|
+
value === "process/browser" ||
|
|
5
|
+
value === "node:process/browser";
|
|
6
|
+
export const isSafeProcessBinding = (path, identifierName) => {
|
|
7
|
+
const processBinding = path.scope.getBinding(identifierName);
|
|
8
|
+
if (!processBinding)
|
|
9
|
+
return identifierName === "process";
|
|
10
|
+
const bindingPath = processBinding.path;
|
|
11
|
+
if (bindingPath.isImportDefaultSpecifier() ||
|
|
12
|
+
bindingPath.isImportNamespaceSpecifier() ||
|
|
13
|
+
bindingPath.isImportSpecifier()) {
|
|
14
|
+
const parent = bindingPath.parent;
|
|
15
|
+
if (parent.type !== "ImportDeclaration")
|
|
16
|
+
return false;
|
|
17
|
+
if (!isProcessSpecifier(parent.source.value))
|
|
18
|
+
return false;
|
|
19
|
+
if (bindingPath.isImportSpecifier()) {
|
|
20
|
+
const imported = bindingPath.node.imported;
|
|
21
|
+
if (imported.type !== "Identifier")
|
|
22
|
+
return false;
|
|
23
|
+
return imported.name === "default";
|
|
24
|
+
}
|
|
25
|
+
return true;
|
|
26
|
+
}
|
|
27
|
+
if (!bindingPath.isVariableDeclarator())
|
|
28
|
+
return false;
|
|
29
|
+
const init = bindingPath.node.init;
|
|
30
|
+
if (!init)
|
|
31
|
+
return false;
|
|
32
|
+
if (init.type !== "CallExpression")
|
|
33
|
+
return false;
|
|
34
|
+
if (init.callee.type !== "Identifier")
|
|
35
|
+
return false;
|
|
36
|
+
if (init.callee.name !== "require")
|
|
37
|
+
return false;
|
|
38
|
+
if (!isNodeRequireInScope(bindingPath.scope))
|
|
39
|
+
return false;
|
|
40
|
+
if (init.arguments.length !== 1)
|
|
41
|
+
return false;
|
|
42
|
+
const argument0 = init.arguments[0];
|
|
43
|
+
if (!argument0)
|
|
44
|
+
return false;
|
|
45
|
+
if (argument0.type !== "StringLiteral")
|
|
46
|
+
return false;
|
|
47
|
+
return isProcessSpecifier(argument0.value);
|
|
48
|
+
};
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
{
|
|
2
|
+
"recommended": true,
|
|
3
|
+
"evaluations": {
|
|
4
|
+
"claude-code-2.1.10:claude-code-2.1.11": {
|
|
5
|
+
"diffSizePercent": 99.99623032701913,
|
|
6
|
+
"evaluatedAt": "2026-02-06T14:58:00.533Z",
|
|
7
|
+
"changedLines": 42,
|
|
8
|
+
"durationSeconds": 224.696743585,
|
|
9
|
+
"stableNames": 1358
|
|
10
|
+
}
|
|
11
|
+
}
|
|
12
|
+
}
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
import { createRequire } from "node:module";
|
|
2
|
+
import { collectExportedNames } from "../../core/collect-exported-names.js";
|
|
3
|
+
import { isHashBasedStableName, RenameGroup, } from "../../core/stable-naming.js";
|
|
4
|
+
import { getFilesToProcess, } from "../../core/types.js";
|
|
5
|
+
import { applyHashStableWin32Renames, } from "./apply-hash-stable-win32-renames.js";
|
|
6
|
+
import { isSafeProcessBinding } from "./is-safe-process-binding.js";
|
|
7
|
+
const require = createRequire(import.meta.url);
|
|
8
|
+
// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
|
|
9
|
+
const traverse = require("@babel/traverse").default;
|
|
10
|
+
const WIN32_LITERAL = "win32";
|
|
11
|
+
const isProcessPlatformExpression = (node) => {
|
|
12
|
+
if (node.type !== "MemberExpression")
|
|
13
|
+
return false;
|
|
14
|
+
if (node.computed)
|
|
15
|
+
return false;
|
|
16
|
+
if (node.object.type !== "Identifier")
|
|
17
|
+
return false;
|
|
18
|
+
// We intentionally keep this structural and verify process identity separately.
|
|
19
|
+
if (node.property.type !== "Identifier")
|
|
20
|
+
return false;
|
|
21
|
+
if (node.property.name !== "platform")
|
|
22
|
+
return false;
|
|
23
|
+
return true;
|
|
24
|
+
};
|
|
25
|
+
const isWin32Literal = (node) => {
|
|
26
|
+
if (node.type !== "StringLiteral")
|
|
27
|
+
return false;
|
|
28
|
+
return node.value === WIN32_LITERAL;
|
|
29
|
+
};
|
|
30
|
+
const getWin32BaseName = (node) => {
|
|
31
|
+
if (node.operator !== "===" && node.operator !== "!==")
|
|
32
|
+
return undefined;
|
|
33
|
+
const matchesLeft = isProcessPlatformExpression(node.left) && isWin32Literal(node.right);
|
|
34
|
+
const matchesRight = isProcessPlatformExpression(node.right) && isWin32Literal(node.left);
|
|
35
|
+
if (!matchesLeft && !matchesRight)
|
|
36
|
+
return undefined;
|
|
37
|
+
return node.operator === "===" ? "isWindows" : "isNotWindows";
|
|
38
|
+
};
|
|
39
|
+
const getPlatformIdentifierName = (node) => {
|
|
40
|
+
if (isProcessPlatformExpression(node.left)) {
|
|
41
|
+
return node.left.object.name;
|
|
42
|
+
}
|
|
43
|
+
if (isProcessPlatformExpression(node.right)) {
|
|
44
|
+
return node.right.object.name;
|
|
45
|
+
}
|
|
46
|
+
return undefined;
|
|
47
|
+
};
|
|
48
|
+
export const renamePlatformWin32FlagsTransform = {
|
|
49
|
+
id: "rename-platform-win32-flags",
|
|
50
|
+
description: 'Renames process.platform ===/!== "win32" variables to readable Windows flag names',
|
|
51
|
+
scope: "file",
|
|
52
|
+
parallelizable: true,
|
|
53
|
+
transform(context) {
|
|
54
|
+
let nodesVisited = 0;
|
|
55
|
+
let transformationsApplied = 0;
|
|
56
|
+
for (const fileInfo of getFilesToProcess(context)) {
|
|
57
|
+
const group = new RenameGroup();
|
|
58
|
+
const hashEntries = [];
|
|
59
|
+
const exportedNames = collectExportedNames(fileInfo.ast.program);
|
|
60
|
+
traverse(fileInfo.ast, {
|
|
61
|
+
VariableDeclarator(path) {
|
|
62
|
+
nodesVisited++;
|
|
63
|
+
const id = path.node.id;
|
|
64
|
+
if (id.type !== "Identifier")
|
|
65
|
+
return;
|
|
66
|
+
const init = path.node.init;
|
|
67
|
+
if (init?.type !== "BinaryExpression")
|
|
68
|
+
return;
|
|
69
|
+
const platformIdentifierName = getPlatformIdentifierName(init);
|
|
70
|
+
if (!platformIdentifierName)
|
|
71
|
+
return;
|
|
72
|
+
if (!isSafeProcessBinding(path, platformIdentifierName))
|
|
73
|
+
return;
|
|
74
|
+
const baseName = getWin32BaseName(init);
|
|
75
|
+
if (!baseName)
|
|
76
|
+
return;
|
|
77
|
+
const binding = path.scope.getBinding(id.name);
|
|
78
|
+
if (!binding)
|
|
79
|
+
return;
|
|
80
|
+
if (!binding.constant)
|
|
81
|
+
return;
|
|
82
|
+
// Intentionally skip single-declaration dead flags to avoid churn;
|
|
83
|
+
// unlike generic comparison-flags, this transform targets a narrow,
|
|
84
|
+
// semantically named pattern where dead-code renames add little value.
|
|
85
|
+
if (binding.referencePaths.length === 0)
|
|
86
|
+
return;
|
|
87
|
+
if (binding.scope.block.type === "Program" &&
|
|
88
|
+
exportedNames.has(id.name)) {
|
|
89
|
+
return;
|
|
90
|
+
}
|
|
91
|
+
if (isHashBasedStableName(id.name)) {
|
|
92
|
+
hashEntries.push({
|
|
93
|
+
baseName,
|
|
94
|
+
binding,
|
|
95
|
+
currentName: id.name,
|
|
96
|
+
scope: path.scope,
|
|
97
|
+
});
|
|
98
|
+
return;
|
|
99
|
+
}
|
|
100
|
+
group.add({
|
|
101
|
+
scope: path.scope,
|
|
102
|
+
currentName: id.name,
|
|
103
|
+
baseName,
|
|
104
|
+
});
|
|
105
|
+
},
|
|
106
|
+
});
|
|
107
|
+
transformationsApplied += applyHashStableWin32Renames(hashEntries);
|
|
108
|
+
transformationsApplied += group.apply();
|
|
109
|
+
}
|
|
110
|
+
return Promise.resolve({ nodesVisited, transformationsApplied });
|
|
111
|
+
},
|
|
112
|
+
};
|
package/package.json
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"name": "miniread",
|
|
3
3
|
"author": "Łukasz Jerciński",
|
|
4
4
|
"license": "MIT",
|
|
5
|
-
"version": "1.
|
|
5
|
+
"version": "1.84.0",
|
|
6
6
|
"description": "Transform minified JavaScript/TypeScript into a more readable form using deterministic AST-based transforms.",
|
|
7
7
|
"repository": {
|
|
8
8
|
"type": "git",
|