@vaadin/hilla-generator-utils 25.4.0-alpha2 → 25.4.0-alpha4
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/dependencies/DependencyManager.d.ts +2 -0
- package/dependencies/DependencyManager.js +5 -2
- package/dependencies/DependencyManager.js.map +1 -1
- package/dependencies/ExportManager.d.ts +12 -3
- package/dependencies/ExportManager.js +46 -9
- package/dependencies/ExportManager.js.map +1 -1
- package/dependencies/ImportManager.d.ts +6 -1
- package/dependencies/ImportManager.js +80 -18
- package/dependencies/ImportManager.js.map +1 -1
- package/dependencies/NameRegistry.d.ts +23 -0
- package/dependencies/NameRegistry.js +108 -0
- package/dependencies/NameRegistry.js.map +1 -0
- package/formatMemberChains.d.ts +10 -0
- package/formatMemberChains.js +73 -0
- package/formatMemberChains.js.map +1 -0
- package/package.json +4 -2
- package/testing/typeCheck.d.ts +13 -0
- package/testing/typeCheck.js +62 -0
- package/testing/typeCheck.js.map +1 -0
- package/createFullyUniqueIdentifier.d.ts +0 -2
- package/createFullyUniqueIdentifier.js +0 -9
- package/createFullyUniqueIdentifier.js.map +0 -1
|
@@ -1,9 +1,11 @@
|
|
|
1
1
|
import ExportManager from "./ExportManager.js";
|
|
2
2
|
import ImportManager from "./ImportManager.js";
|
|
3
|
+
import NameRegistry from "./NameRegistry.js";
|
|
3
4
|
import type PathManager from "./PathManager.js";
|
|
4
5
|
export default class DependencyManager {
|
|
5
6
|
readonly exports: ExportManager;
|
|
6
7
|
readonly imports: ImportManager;
|
|
8
|
+
readonly names: NameRegistry;
|
|
7
9
|
readonly paths: PathManager;
|
|
8
10
|
constructor(paths: PathManager, collator?: Intl.Collator);
|
|
9
11
|
}
|
|
@@ -1,12 +1,15 @@
|
|
|
1
1
|
import ExportManager from "./ExportManager.js";
|
|
2
2
|
import ImportManager from "./ImportManager.js";
|
|
3
|
+
import NameRegistry from "./NameRegistry.js";
|
|
3
4
|
export default class DependencyManager {
|
|
4
5
|
exports;
|
|
5
6
|
imports;
|
|
7
|
+
names;
|
|
6
8
|
paths;
|
|
7
9
|
constructor(paths, collator = new Intl.Collator("en", { sensitivity: "case" })) {
|
|
8
|
-
this.
|
|
9
|
-
this.
|
|
10
|
+
this.names = new NameRegistry();
|
|
11
|
+
this.exports = new ExportManager(collator, this.names);
|
|
12
|
+
this.imports = new ImportManager(collator, this.names);
|
|
10
13
|
this.paths = paths;
|
|
11
14
|
}
|
|
12
15
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"mappings":"AAAA,OAAO,uCAAwC;AAC/C,OAAO,uCAAwC;
|
|
1
|
+
{"mappings":"AAAA,OAAO,uCAAwC;AAC/C,OAAO,uCAAwC;AAC/C,OAAO,qCAAsC;AAG7C,eAAe,MAAM,kBAAkB;CACrC,AAAS;CACT,AAAS;CACT,AAAS;CACT,AAAS;CAET,YAAYA,OAAoBC,WAA0B,IAAI,KAAK,SAAS,MAAM,EAAE,aAAa,OAAQ,IAAG;AAC1G,OAAK,QAAQ,IAAI;AACjB,OAAK,UAAU,IAAI,cAAc,UAAU,KAAK;AAChD,OAAK,UAAU,IAAI,cAAc,UAAU,KAAK;AAChD,OAAK,QAAQ;CACd;AACF","names":["paths: PathManager","collator: Intl.Collator"],"sources":["/opt/agent/work/649c11185a3798db/packages/ts/generator-utils/src/dependencies/DependencyManager.ts"],"sourcesContent":["import ExportManager from './ExportManager.js';\nimport ImportManager from './ImportManager.js';\nimport NameRegistry from './NameRegistry.js';\nimport type PathManager from './PathManager.js';\n\nexport default class DependencyManager {\n readonly exports: ExportManager;\n readonly imports: ImportManager;\n readonly names: NameRegistry;\n readonly paths: PathManager;\n\n constructor(paths: PathManager, collator: Intl.Collator = new Intl.Collator('en', { sensitivity: 'case' })) {\n this.names = new NameRegistry();\n this.exports = new ExportManager(collator, this.names);\n this.imports = new ImportManager(collator, this.names);\n this.paths = paths;\n }\n}\n"],"version":3}
|
|
@@ -1,11 +1,12 @@
|
|
|
1
|
-
import { type ExportAssignment, type ExportDeclaration, type Identifier, type Statement } from "@typescript/typescript6";
|
|
1
|
+
import ts, { type ExportAssignment, type ExportDeclaration, type Identifier, type Statement } from "@typescript/typescript6";
|
|
2
2
|
import type CodeConvertable from "./CodeConvertable.js";
|
|
3
|
+
import NameRegistry from "./NameRegistry.js";
|
|
3
4
|
import StatementRecordManager, { type StatementRecord } from "./StatementRecordManager.js";
|
|
4
5
|
export declare class NamedExportManager implements CodeConvertable<ExportDeclaration | undefined> {
|
|
5
6
|
#private;
|
|
6
7
|
readonly collator: Intl.Collator;
|
|
7
8
|
get size(): number;
|
|
8
|
-
constructor(collator: Intl.Collator);
|
|
9
|
+
constructor(collator: Intl.Collator, names: NameRegistry);
|
|
9
10
|
add(name: string, isType?: boolean, uniqueId?: Identifier): Identifier;
|
|
10
11
|
getIdentifier(name: string): Identifier | undefined;
|
|
11
12
|
identifiers(): IterableIterator<readonly [id: Identifier, isType: boolean]>;
|
|
@@ -15,6 +16,7 @@ export declare class NamedExportManager implements CodeConvertable<ExportDeclara
|
|
|
15
16
|
}
|
|
16
17
|
export declare class NamespaceExportManager extends StatementRecordManager<ExportDeclaration> {
|
|
17
18
|
#private;
|
|
19
|
+
constructor(collator: Intl.Collator, names: NameRegistry);
|
|
18
20
|
get size(): number;
|
|
19
21
|
addCombined(path: string, name: string, uniqueId?: Identifier): Identifier;
|
|
20
22
|
addSpread(path: string): void;
|
|
@@ -28,15 +30,22 @@ export declare class NamespaceExportManager extends StatementRecordManager<Expor
|
|
|
28
30
|
}
|
|
29
31
|
export declare class DefaultExportManager implements CodeConvertable<ExportAssignment | undefined> {
|
|
30
32
|
#private;
|
|
33
|
+
constructor(names: NameRegistry);
|
|
31
34
|
get isEmpty(): boolean;
|
|
35
|
+
/**
|
|
36
|
+
* The name is taken as given rather than made unique: it belongs to a
|
|
37
|
+
* declaration the caller emits, so the caller decides how it is spelled.
|
|
38
|
+
*/
|
|
32
39
|
set(id: Identifier | string): Identifier;
|
|
33
40
|
toCode(): ExportAssignment | undefined;
|
|
34
41
|
}
|
|
35
42
|
export default class ExportManager implements CodeConvertable<readonly Statement[]> {
|
|
36
43
|
readonly default: DefaultExportManager;
|
|
37
44
|
readonly named: NamedExportManager;
|
|
45
|
+
readonly names: NameRegistry;
|
|
38
46
|
readonly namespace: NamespaceExportManager;
|
|
39
47
|
get size(): number;
|
|
40
|
-
constructor(collator: Intl.Collator);
|
|
48
|
+
constructor(collator: Intl.Collator, names?: NameRegistry);
|
|
41
49
|
toCode(): readonly Statement[];
|
|
50
|
+
fromCode(source: ts.SourceFile): void;
|
|
42
51
|
}
|
|
@@ -1,18 +1,23 @@
|
|
|
1
1
|
import ts from "@typescript/typescript6";
|
|
2
|
-
import
|
|
2
|
+
import NameRegistry from "./NameRegistry.js";
|
|
3
3
|
import StatementRecordManager from "./StatementRecordManager.js";
|
|
4
4
|
import { createDependencyRecord } from "./utils.js";
|
|
5
5
|
export class NamedExportManager {
|
|
6
6
|
collator;
|
|
7
7
|
#map = new Map();
|
|
8
|
+
#names;
|
|
8
9
|
get size() {
|
|
9
10
|
return this.#map.size;
|
|
10
11
|
}
|
|
11
|
-
constructor(collator) {
|
|
12
|
+
constructor(collator, names) {
|
|
12
13
|
this.collator = collator;
|
|
14
|
+
this.#names = names;
|
|
13
15
|
}
|
|
14
16
|
add(name, isType, uniqueId) {
|
|
15
|
-
|
|
17
|
+
if (uniqueId) {
|
|
18
|
+
this.#names.claim(uniqueId.text);
|
|
19
|
+
}
|
|
20
|
+
const id = uniqueId ?? this.getIdentifier(name) ?? this.#names.reserveIdentifier(name);
|
|
16
21
|
this.#map.set(name, createDependencyRecord(id, isType));
|
|
17
22
|
return id;
|
|
18
23
|
}
|
|
@@ -38,17 +43,25 @@ export class NamedExportManager {
|
|
|
38
43
|
names.sort(this.collator.compare);
|
|
39
44
|
return ts.factory.createExportDeclaration(undefined, false, ts.factory.createNamedExports(names.map((name) => {
|
|
40
45
|
const { id, isType } = this.#map.get(name);
|
|
41
|
-
return ts.factory.createExportSpecifier(isType, id, ts.factory.createIdentifier(name));
|
|
46
|
+
return ts.factory.createExportSpecifier(isType, id.text === name ? undefined : id, ts.factory.createIdentifier(name));
|
|
42
47
|
})), undefined);
|
|
43
48
|
}
|
|
44
49
|
}
|
|
45
50
|
export class NamespaceExportManager extends StatementRecordManager {
|
|
46
51
|
#map = new Map();
|
|
52
|
+
#names;
|
|
53
|
+
constructor(collator, names) {
|
|
54
|
+
super(collator);
|
|
55
|
+
this.#names = names;
|
|
56
|
+
}
|
|
47
57
|
get size() {
|
|
48
58
|
return this.#map.size;
|
|
49
59
|
}
|
|
50
60
|
addCombined(path, name, uniqueId) {
|
|
51
|
-
|
|
61
|
+
if (uniqueId) {
|
|
62
|
+
this.#names.claim(uniqueId.text);
|
|
63
|
+
}
|
|
64
|
+
const id = uniqueId ?? this.#map.get(path) ?? this.#names.reserveIdentifier(name);
|
|
52
65
|
this.#map.set(path, id);
|
|
53
66
|
return id;
|
|
54
67
|
}
|
|
@@ -80,12 +93,21 @@ export class NamespaceExportManager extends StatementRecordManager {
|
|
|
80
93
|
}
|
|
81
94
|
}
|
|
82
95
|
export class DefaultExportManager {
|
|
96
|
+
#names;
|
|
83
97
|
#id;
|
|
98
|
+
constructor(names) {
|
|
99
|
+
this.#names = names;
|
|
100
|
+
}
|
|
84
101
|
get isEmpty() {
|
|
85
102
|
return !this.#id;
|
|
86
103
|
}
|
|
104
|
+
/**
|
|
105
|
+
* The name is taken as given rather than made unique: it belongs to a
|
|
106
|
+
* declaration the caller emits, so the caller decides how it is spelled.
|
|
107
|
+
*/
|
|
87
108
|
set(id) {
|
|
88
109
|
this.#id = typeof id === "string" ? ts.factory.createIdentifier(id) : id;
|
|
110
|
+
this.#names.claim(this.#id.text);
|
|
89
111
|
return this.#id;
|
|
90
112
|
}
|
|
91
113
|
toCode() {
|
|
@@ -93,15 +115,18 @@ export class DefaultExportManager {
|
|
|
93
115
|
}
|
|
94
116
|
}
|
|
95
117
|
export default class ExportManager {
|
|
96
|
-
default
|
|
118
|
+
default;
|
|
97
119
|
named;
|
|
120
|
+
names;
|
|
98
121
|
namespace;
|
|
99
122
|
get size() {
|
|
100
123
|
return (this.default.isEmpty ? 0 : 1) + this.named.size + this.namespace.size;
|
|
101
124
|
}
|
|
102
|
-
constructor(collator) {
|
|
103
|
-
this.
|
|
104
|
-
this.
|
|
125
|
+
constructor(collator, names = new NameRegistry()) {
|
|
126
|
+
this.default = new DefaultExportManager(names);
|
|
127
|
+
this.named = new NamedExportManager(collator, names);
|
|
128
|
+
this.namespace = new NamespaceExportManager(collator, names);
|
|
129
|
+
this.names = names;
|
|
105
130
|
}
|
|
106
131
|
toCode() {
|
|
107
132
|
const defaultStatement = this.default.toCode();
|
|
@@ -117,5 +142,17 @@ export default class ExportManager {
|
|
|
117
142
|
}
|
|
118
143
|
return result;
|
|
119
144
|
}
|
|
145
|
+
fromCode(source) {
|
|
146
|
+
for (const statement of source.statements) {
|
|
147
|
+
if (ts.isExportDeclaration(statement) && statement.exportClause && ts.isNamedExports(statement.exportClause)) {
|
|
148
|
+
for (const { isTypeOnly, name, propertyName } of statement.exportClause.elements) {
|
|
149
|
+
const local = propertyName ?? name;
|
|
150
|
+
if (ts.isIdentifier(local)) {
|
|
151
|
+
this.named.add(name.text, isTypeOnly, local);
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
}
|
|
120
157
|
}
|
|
121
158
|
//# sourceMappingURL=./ExportManager.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"mappings":"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","names":["#map","collator: Intl.Collator","name: string","isType?: boolean","uniqueId?: Identifier","path: string","#id","id: Identifier | string","result: Statement[]"],"sources":["/opt/agent/work/649c11185a3798db/packages/ts/generator-utils/src/dependencies/ExportManager.ts"],"sourcesContent":["import ts, {\n type ExportAssignment,\n type ExportDeclaration,\n type Identifier,\n type Statement,\n} from '@typescript/typescript6';\nimport createFullyUniqueIdentifier from '../createFullyUniqueIdentifier.js';\nimport type CodeConvertable from './CodeConvertable.js';\nimport StatementRecordManager, { type StatementRecord } from './StatementRecordManager.js';\nimport { createDependencyRecord, type DependencyRecord } from './utils.js';\n\nexport class NamedExportManager implements CodeConvertable<ExportDeclaration | undefined> {\n readonly collator: Intl.Collator;\n readonly #map = new Map<string, DependencyRecord>();\n\n get size(): number {\n return this.#map.size;\n }\n\n constructor(collator: Intl.Collator) {\n this.collator = collator;\n }\n\n add(name: string, isType?: boolean, uniqueId?: Identifier): Identifier {\n const id = uniqueId ?? createFullyUniqueIdentifier(name);\n this.#map.set(name, createDependencyRecord(id, isType));\n return id;\n }\n\n getIdentifier(name: string): Identifier | undefined {\n return this.#map.get(name)?.id;\n }\n\n *identifiers(): IterableIterator<readonly [id: Identifier, isType: boolean]> {\n for (const { id, isType } of this.#map.values()) {\n yield [id, isType];\n }\n }\n\n isType(name: string): boolean | undefined {\n return this.#map.get(name)?.isType;\n }\n\n names(): IterableIterator<string> {\n return this.#map.keys();\n }\n\n toCode(): ExportDeclaration | undefined {\n if (this.#map.size === 0) {\n return undefined;\n }\n\n const names = [...this.#map.keys()];\n // eslint-disable-next-line @typescript-eslint/unbound-method\n names.sort(this.collator.compare);\n\n return ts.factory.createExportDeclaration(\n undefined,\n false,\n ts.factory.createNamedExports(\n names.map((name) => {\n const { id, isType } = this.#map.get(name)!;\n return ts.factory.createExportSpecifier(isType, id, ts.factory.createIdentifier(name));\n }),\n ),\n undefined,\n );\n }\n}\n\nexport class NamespaceExportManager extends StatementRecordManager<ExportDeclaration> {\n readonly #map = new Map<string, Identifier | null>();\n\n get size(): number {\n return this.#map.size;\n }\n\n addCombined(path: string, name: string, uniqueId?: Identifier): Identifier {\n const id = uniqueId ?? createFullyUniqueIdentifier(name);\n this.#map.set(path, id);\n return id;\n }\n\n addSpread(path: string): void {\n this.#map.set(path, null);\n }\n\n override clear(): void {\n this.#map.clear();\n }\n\n getIdentifier(path: string): Identifier | null | undefined {\n return this.#map.get(path);\n }\n\n identifiers(): IterableIterator<Identifier | null> {\n return this.#map.values();\n }\n\n isCombined(path: string): boolean | undefined {\n return this.#map.has(path) ? this.#map.get(path) !== null : undefined;\n }\n\n isSpread(path: string): boolean | undefined {\n return this.#map.has(path) ? this.#map.get(path) === null : undefined;\n }\n\n paths(): IterableIterator<string> {\n return this.#map.keys();\n }\n\n override *statementRecords(): IterableIterator<StatementRecord<ExportDeclaration>> {\n for (const [path, id] of this.#map) {\n yield [\n path,\n ts.factory.createExportDeclaration(\n undefined,\n false,\n id !== null ? ts.factory.createNamespaceExport(id) : undefined,\n ts.factory.createStringLiteral(path),\n ),\n ];\n }\n }\n}\n\nexport class DefaultExportManager implements CodeConvertable<ExportAssignment | undefined> {\n #id?: Identifier;\n\n get isEmpty(): boolean {\n return !this.#id;\n }\n\n set(id: Identifier | string): Identifier {\n this.#id = typeof id === 'string' ? ts.factory.createIdentifier(id) : id;\n return this.#id;\n }\n\n toCode(): ExportAssignment | undefined {\n return this.#id ? ts.factory.createExportAssignment(undefined, undefined, this.#id) : undefined;\n }\n}\n\nexport default class ExportManager implements CodeConvertable<readonly Statement[]> {\n readonly default: DefaultExportManager = new DefaultExportManager();\n readonly named: NamedExportManager;\n readonly namespace: NamespaceExportManager;\n\n get size(): number {\n return (this.default.isEmpty ? 0 : 1) + this.named.size + this.namespace.size;\n }\n\n constructor(collator: Intl.Collator) {\n this.named = new NamedExportManager(collator);\n this.namespace = new NamespaceExportManager(collator);\n }\n\n toCode(): readonly Statement[] {\n const defaultStatement = this.default.toCode();\n const namedStatement = this.named.toCode();\n const namespaceStatements = this.namespace.toCode();\n\n const result: Statement[] = [];\n\n if (namedStatement) {\n result.push(namedStatement);\n }\n\n result.push(...namespaceStatements);\n\n if (defaultStatement) {\n result.push(defaultStatement);\n }\n\n return result;\n }\n}\n"],"version":3}
|
|
1
|
+
{"mappings":"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","names":["#map","#names","collator: Intl.Collator","names: NameRegistry","name: string","isType?: boolean","uniqueId?: Identifier","path: string","#id","id: Identifier | string","result: Statement[]","source: ts.SourceFile"],"sources":["/opt/agent/work/649c11185a3798db/packages/ts/generator-utils/src/dependencies/ExportManager.ts"],"sourcesContent":["import ts, {\n type ExportAssignment,\n type ExportDeclaration,\n type Identifier,\n type Statement,\n} from '@typescript/typescript6';\nimport type CodeConvertable from './CodeConvertable.js';\nimport NameRegistry from './NameRegistry.js';\nimport StatementRecordManager, { type StatementRecord } from './StatementRecordManager.js';\nimport { createDependencyRecord, type DependencyRecord } from './utils.js';\n\nexport class NamedExportManager implements CodeConvertable<ExportDeclaration | undefined> {\n readonly collator: Intl.Collator;\n readonly #map = new Map<string, DependencyRecord>();\n readonly #names: NameRegistry;\n\n get size(): number {\n return this.#map.size;\n }\n\n constructor(collator: Intl.Collator, names: NameRegistry) {\n this.collator = collator;\n this.#names = names;\n }\n\n add(name: string, isType?: boolean, uniqueId?: Identifier): Identifier {\n if (uniqueId) {\n this.#names.claim(uniqueId.text);\n }\n\n const id = uniqueId ?? this.getIdentifier(name) ?? this.#names.reserveIdentifier(name);\n this.#map.set(name, createDependencyRecord(id, isType));\n return id;\n }\n\n getIdentifier(name: string): Identifier | undefined {\n return this.#map.get(name)?.id;\n }\n\n *identifiers(): IterableIterator<readonly [id: Identifier, isType: boolean]> {\n for (const { id, isType } of this.#map.values()) {\n yield [id, isType];\n }\n }\n\n isType(name: string): boolean | undefined {\n return this.#map.get(name)?.isType;\n }\n\n names(): IterableIterator<string> {\n return this.#map.keys();\n }\n\n toCode(): ExportDeclaration | undefined {\n if (this.#map.size === 0) {\n return undefined;\n }\n\n const names = [...this.#map.keys()];\n // eslint-disable-next-line @typescript-eslint/unbound-method\n names.sort(this.collator.compare);\n\n return ts.factory.createExportDeclaration(\n undefined,\n false,\n ts.factory.createNamedExports(\n names.map((name) => {\n const { id, isType } = this.#map.get(name)!;\n return ts.factory.createExportSpecifier(\n isType,\n id.text === name ? undefined : id,\n ts.factory.createIdentifier(name),\n );\n }),\n ),\n undefined,\n );\n }\n}\n\nexport class NamespaceExportManager extends StatementRecordManager<ExportDeclaration> {\n readonly #map = new Map<string, Identifier | null>();\n readonly #names: NameRegistry;\n\n constructor(collator: Intl.Collator, names: NameRegistry) {\n super(collator);\n this.#names = names;\n }\n\n get size(): number {\n return this.#map.size;\n }\n\n addCombined(path: string, name: string, uniqueId?: Identifier): Identifier {\n if (uniqueId) {\n this.#names.claim(uniqueId.text);\n }\n\n const id = uniqueId ?? this.#map.get(path) ?? this.#names.reserveIdentifier(name);\n this.#map.set(path, id);\n return id;\n }\n\n addSpread(path: string): void {\n this.#map.set(path, null);\n }\n\n override clear(): void {\n this.#map.clear();\n }\n\n getIdentifier(path: string): Identifier | null | undefined {\n return this.#map.get(path);\n }\n\n identifiers(): IterableIterator<Identifier | null> {\n return this.#map.values();\n }\n\n isCombined(path: string): boolean | undefined {\n return this.#map.has(path) ? this.#map.get(path) !== null : undefined;\n }\n\n isSpread(path: string): boolean | undefined {\n return this.#map.has(path) ? this.#map.get(path) === null : undefined;\n }\n\n paths(): IterableIterator<string> {\n return this.#map.keys();\n }\n\n override *statementRecords(): IterableIterator<StatementRecord<ExportDeclaration>> {\n for (const [path, id] of this.#map) {\n yield [\n path,\n ts.factory.createExportDeclaration(\n undefined,\n false,\n id !== null ? ts.factory.createNamespaceExport(id) : undefined,\n ts.factory.createStringLiteral(path),\n ),\n ];\n }\n }\n}\n\nexport class DefaultExportManager implements CodeConvertable<ExportAssignment | undefined> {\n readonly #names: NameRegistry;\n #id?: Identifier;\n\n constructor(names: NameRegistry) {\n this.#names = names;\n }\n\n get isEmpty(): boolean {\n return !this.#id;\n }\n\n /**\n * The name is taken as given rather than made unique: it belongs to a\n * declaration the caller emits, so the caller decides how it is spelled.\n */\n set(id: Identifier | string): Identifier {\n this.#id = typeof id === 'string' ? ts.factory.createIdentifier(id) : id;\n this.#names.claim(this.#id.text);\n return this.#id;\n }\n\n toCode(): ExportAssignment | undefined {\n return this.#id ? ts.factory.createExportAssignment(undefined, undefined, this.#id) : undefined;\n }\n}\n\nexport default class ExportManager implements CodeConvertable<readonly Statement[]> {\n readonly default: DefaultExportManager;\n readonly named: NamedExportManager;\n readonly names: NameRegistry;\n readonly namespace: NamespaceExportManager;\n\n get size(): number {\n return (this.default.isEmpty ? 0 : 1) + this.named.size + this.namespace.size;\n }\n\n constructor(collator: Intl.Collator, names: NameRegistry = new NameRegistry()) {\n this.default = new DefaultExportManager(names);\n this.named = new NamedExportManager(collator, names);\n this.namespace = new NamespaceExportManager(collator, names);\n this.names = names;\n }\n\n toCode(): readonly Statement[] {\n const defaultStatement = this.default.toCode();\n const namedStatement = this.named.toCode();\n const namespaceStatements = this.namespace.toCode();\n\n const result: Statement[] = [];\n\n if (namedStatement) {\n result.push(namedStatement);\n }\n\n result.push(...namespaceStatements);\n\n if (defaultStatement) {\n result.push(defaultStatement);\n }\n\n return result;\n }\n\n fromCode(source: ts.SourceFile): void {\n for (const statement of source.statements) {\n if (ts.isExportDeclaration(statement) && statement.exportClause && ts.isNamedExports(statement.exportClause)) {\n for (const { isTypeOnly, name, propertyName } of statement.exportClause.elements) {\n // in an export specifier `propertyName` holds the local binding and\n // `name` the exported name, the other way around than in an import\n const local = propertyName ?? name;\n\n // a local binding is always an identifier, unlike an exported name,\n // which a re-export may spell as a string literal\n if (ts.isIdentifier(local)) {\n this.named.add(name.text, isTypeOnly, local);\n }\n }\n }\n }\n }\n}\n"],"version":3}
|
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
import ts, { type Identifier, type ImportDeclaration, type Statement } from "@typescript/typescript6";
|
|
2
2
|
import type CodeConvertable from "./CodeConvertable.js";
|
|
3
|
+
import NameRegistry from "./NameRegistry.js";
|
|
3
4
|
import StatementRecordManager, { type StatementRecord } from "./StatementRecordManager.js";
|
|
4
5
|
export declare class NamedImportManager extends StatementRecordManager<ImportDeclaration> {
|
|
5
6
|
#private;
|
|
7
|
+
constructor(collator: Intl.Collator, names: NameRegistry);
|
|
6
8
|
get size(): number;
|
|
7
9
|
add(path: string, specifier: string, isType?: boolean, uniqueId?: Identifier): Identifier;
|
|
8
10
|
remove(path: string, specifier: string): void;
|
|
@@ -17,6 +19,7 @@ export declare class NamedImportManager extends StatementRecordManager<ImportDec
|
|
|
17
19
|
}
|
|
18
20
|
export declare class NamespaceImportManager extends StatementRecordManager<ImportDeclaration> {
|
|
19
21
|
#private;
|
|
22
|
+
constructor(collator: Intl.Collator, names: NameRegistry);
|
|
20
23
|
get size(): number;
|
|
21
24
|
add(path: string, name: string, uniqueId?: Identifier): Identifier;
|
|
22
25
|
clear(): void;
|
|
@@ -29,6 +32,7 @@ export declare class NamespaceImportManager extends StatementRecordManager<Impor
|
|
|
29
32
|
}
|
|
30
33
|
export declare class DefaultImportManager extends StatementRecordManager<ImportDeclaration> {
|
|
31
34
|
#private;
|
|
35
|
+
constructor(collator: Intl.Collator, names: NameRegistry);
|
|
32
36
|
get size(): number;
|
|
33
37
|
add(path: string, name: string, isType?: boolean, uniqueId?: Identifier): Identifier;
|
|
34
38
|
getIdentifier(path: string): Identifier | undefined;
|
|
@@ -44,8 +48,9 @@ export default class ImportManager implements CodeConvertable<readonly Statement
|
|
|
44
48
|
readonly collator: Intl.Collator;
|
|
45
49
|
readonly default: DefaultImportManager;
|
|
46
50
|
readonly named: NamedImportManager;
|
|
51
|
+
readonly names: NameRegistry;
|
|
47
52
|
readonly namespace: NamespaceImportManager;
|
|
48
|
-
constructor(collator: Intl.Collator);
|
|
53
|
+
constructor(collator: Intl.Collator, names?: NameRegistry);
|
|
49
54
|
get size(): number;
|
|
50
55
|
toCode(): readonly Statement[];
|
|
51
56
|
fromCode(source: ts.SourceFile): void;
|
|
@@ -1,14 +1,23 @@
|
|
|
1
1
|
import ts from "@typescript/typescript6";
|
|
2
|
-
import
|
|
2
|
+
import NameRegistry from "./NameRegistry.js";
|
|
3
3
|
import StatementRecordManager, { createComparator } from "./StatementRecordManager.js";
|
|
4
4
|
import { createDependencyRecord } from "./utils.js";
|
|
5
5
|
export class NamedImportManager extends StatementRecordManager {
|
|
6
6
|
#map = new Map();
|
|
7
|
+
#names;
|
|
8
|
+
constructor(collator, names) {
|
|
9
|
+
super(collator);
|
|
10
|
+
this.#names = names;
|
|
11
|
+
}
|
|
7
12
|
get size() {
|
|
8
13
|
return this.#map.size;
|
|
9
14
|
}
|
|
10
15
|
add(path, specifier, isType, uniqueId) {
|
|
11
|
-
|
|
16
|
+
if (uniqueId) {
|
|
17
|
+
this.#names.claim(uniqueId.text);
|
|
18
|
+
}
|
|
19
|
+
const id = uniqueId ?? this.getIdentifier(path, specifier) ?? this.#names.reserveIdentifier(specifier);
|
|
20
|
+
const record = createDependencyRecord(id, isType);
|
|
12
21
|
if (this.#map.has(path)) {
|
|
13
22
|
this.#map.get(path).set(specifier, record);
|
|
14
23
|
} else {
|
|
@@ -54,7 +63,7 @@ export class NamedImportManager extends StatementRecordManager {
|
|
|
54
63
|
const isTypeOnly = names.every((name) => specifiers.get(name).isType);
|
|
55
64
|
yield [path, ts.factory.createImportDeclaration(undefined, ts.factory.createImportClause(isTypeOnly, undefined, ts.factory.createNamedImports(names.map((name) => {
|
|
56
65
|
const { id, isType } = specifiers.get(name);
|
|
57
|
-
return ts.factory.createImportSpecifier(isTypeOnly ? false : isType, ts.factory.createIdentifier(name), id);
|
|
66
|
+
return ts.factory.createImportSpecifier(isTypeOnly ? false : isType, id.text === name ? undefined : ts.factory.createIdentifier(name), id);
|
|
58
67
|
}))), ts.factory.createStringLiteral(path))];
|
|
59
68
|
}
|
|
60
69
|
}
|
|
@@ -73,11 +82,19 @@ export class NamedImportManager extends StatementRecordManager {
|
|
|
73
82
|
}
|
|
74
83
|
export class NamespaceImportManager extends StatementRecordManager {
|
|
75
84
|
#map = new Map();
|
|
85
|
+
#names;
|
|
86
|
+
constructor(collator, names) {
|
|
87
|
+
super(collator);
|
|
88
|
+
this.#names = names;
|
|
89
|
+
}
|
|
76
90
|
get size() {
|
|
77
91
|
return this.#map.size;
|
|
78
92
|
}
|
|
79
93
|
add(path, name, uniqueId) {
|
|
80
|
-
|
|
94
|
+
if (uniqueId) {
|
|
95
|
+
this.#names.claim(uniqueId.text);
|
|
96
|
+
}
|
|
97
|
+
const id = uniqueId ?? this.#map.get(path) ?? this.#names.reserveIdentifier(name);
|
|
81
98
|
this.#map.set(path, id);
|
|
82
99
|
return id;
|
|
83
100
|
}
|
|
@@ -109,11 +126,19 @@ export class NamespaceImportManager extends StatementRecordManager {
|
|
|
109
126
|
}
|
|
110
127
|
export class DefaultImportManager extends StatementRecordManager {
|
|
111
128
|
#map = new Map();
|
|
129
|
+
#names;
|
|
130
|
+
constructor(collator, names) {
|
|
131
|
+
super(collator);
|
|
132
|
+
this.#names = names;
|
|
133
|
+
}
|
|
112
134
|
get size() {
|
|
113
135
|
return this.#map.size;
|
|
114
136
|
}
|
|
115
137
|
add(path, name, isType, uniqueId) {
|
|
116
|
-
|
|
138
|
+
if (uniqueId) {
|
|
139
|
+
this.#names.claim(uniqueId.text);
|
|
140
|
+
}
|
|
141
|
+
const id = uniqueId ?? this.getIdentifier(path) ?? this.#names.reserveIdentifier(name);
|
|
117
142
|
this.#map.set(path, createDependencyRecord(id, isType));
|
|
118
143
|
return id;
|
|
119
144
|
}
|
|
@@ -152,26 +177,55 @@ export class DefaultImportManager extends StatementRecordManager {
|
|
|
152
177
|
}
|
|
153
178
|
}
|
|
154
179
|
}
|
|
180
|
+
/**
|
|
181
|
+
* Combines a default and a named import of the same module into one
|
|
182
|
+
* declaration, i.e. `import m, { StringModel } from "..."`.
|
|
183
|
+
*
|
|
184
|
+
* A type-only default import cannot take named bindings along, so those stay
|
|
185
|
+
* apart. A named group that is entirely type-only can still be merged, by
|
|
186
|
+
* marking its specifiers individually instead of the declaration.
|
|
187
|
+
*/
|
|
188
|
+
function mergeDefaultAndNamed(defaultDeclaration, namedDeclaration) {
|
|
189
|
+
const defaultClause = defaultDeclaration.importClause;
|
|
190
|
+
const namedClause = namedDeclaration.importClause;
|
|
191
|
+
if (!defaultClause?.name || defaultClause.isTypeOnly || !namedClause?.namedBindings) {
|
|
192
|
+
return undefined;
|
|
193
|
+
}
|
|
194
|
+
const bindings = namedClause.namedBindings;
|
|
195
|
+
if (!ts.isNamedImports(bindings)) {
|
|
196
|
+
return undefined;
|
|
197
|
+
}
|
|
198
|
+
const elements = namedClause.isTypeOnly ? bindings.elements.map((element) => ts.factory.createImportSpecifier(true, element.propertyName, element.name)) : bindings.elements;
|
|
199
|
+
return ts.factory.createImportDeclaration(defaultDeclaration.modifiers, ts.factory.createImportClause(false, defaultClause.name, ts.factory.createNamedImports(elements)), defaultDeclaration.moduleSpecifier);
|
|
200
|
+
}
|
|
155
201
|
export default class ImportManager {
|
|
156
202
|
collator;
|
|
157
203
|
default;
|
|
158
204
|
named;
|
|
205
|
+
names;
|
|
159
206
|
namespace;
|
|
160
|
-
constructor(collator) {
|
|
161
|
-
this.default = new DefaultImportManager(collator);
|
|
162
|
-
this.named = new NamedImportManager(collator);
|
|
163
|
-
this.namespace = new NamespaceImportManager(collator);
|
|
207
|
+
constructor(collator, names = new NameRegistry()) {
|
|
208
|
+
this.default = new DefaultImportManager(collator, names);
|
|
209
|
+
this.named = new NamedImportManager(collator, names);
|
|
210
|
+
this.namespace = new NamespaceImportManager(collator, names);
|
|
164
211
|
this.collator = collator;
|
|
212
|
+
this.names = names;
|
|
165
213
|
}
|
|
166
214
|
get size() {
|
|
167
215
|
return this.default.size + this.named.size + this.namespace.size;
|
|
168
216
|
}
|
|
169
217
|
toCode() {
|
|
170
|
-
const
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
218
|
+
const named = new Map(this.named.statementRecords());
|
|
219
|
+
const records = [];
|
|
220
|
+
for (const [path, declaration] of this.default.statementRecords()) {
|
|
221
|
+
const namedDeclaration = named.get(path);
|
|
222
|
+
const merged = namedDeclaration && mergeDefaultAndNamed(declaration, namedDeclaration);
|
|
223
|
+
if (merged) {
|
|
224
|
+
named.delete(path);
|
|
225
|
+
}
|
|
226
|
+
records.push([path, merged ?? declaration]);
|
|
227
|
+
}
|
|
228
|
+
records.push(...named, ...this.namespace.statementRecords());
|
|
175
229
|
records.sort(createComparator(this.collator));
|
|
176
230
|
return records.map(([, statement]) => statement);
|
|
177
231
|
}
|
|
@@ -179,6 +233,13 @@ export default class ImportManager {
|
|
|
179
233
|
this.default.clear();
|
|
180
234
|
this.named.clear();
|
|
181
235
|
this.namespace.clear();
|
|
236
|
+
const claim = (node) => {
|
|
237
|
+
if (ts.isIdentifier(node)) {
|
|
238
|
+
this.names.claim(node.text);
|
|
239
|
+
}
|
|
240
|
+
ts.forEachChild(node, claim);
|
|
241
|
+
};
|
|
242
|
+
claim(source);
|
|
182
243
|
const imports = source.statements.filter((statement) => ts.isImportDeclaration(statement));
|
|
183
244
|
for (const { importClause, moduleSpecifier } of imports) {
|
|
184
245
|
if (!importClause) {
|
|
@@ -186,16 +247,17 @@ export default class ImportManager {
|
|
|
186
247
|
}
|
|
187
248
|
const { name, namedBindings } = importClause;
|
|
188
249
|
const path = moduleSpecifier.text;
|
|
250
|
+
if (name) {
|
|
251
|
+
this.default.add(path, name.text, importClause.isTypeOnly, name);
|
|
252
|
+
}
|
|
189
253
|
if (namedBindings) {
|
|
190
254
|
if (ts.isNamespaceImport(namedBindings)) {
|
|
191
255
|
this.namespace.add(path, namedBindings.name.text, namedBindings.name);
|
|
192
256
|
} else {
|
|
193
|
-
for (const { isTypeOnly, name:
|
|
194
|
-
this.named.add(path,
|
|
257
|
+
for (const { isTypeOnly, name: local, propertyName } of namedBindings.elements) {
|
|
258
|
+
this.named.add(path, (propertyName ?? local).text, isTypeOnly, local);
|
|
195
259
|
}
|
|
196
260
|
}
|
|
197
|
-
} else if (name) {
|
|
198
|
-
this.default.add(path, name.text, importClause.isTypeOnly, name);
|
|
199
261
|
}
|
|
200
262
|
}
|
|
201
263
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"mappings":"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","names":["#map","path: string","specifier: string","isType?: boolean","uniqueId?: Identifier","name: string","collator: Intl.Collator","source: ts.SourceFile"],"sources":["/opt/agent/work/649c11185a3798db/packages/ts/generator-utils/src/dependencies/ImportManager.ts"],"sourcesContent":["import ts, { type Identifier, type ImportDeclaration, type Statement } from '@typescript/typescript6';\nimport createFullyUniqueIdentifier from '../createFullyUniqueIdentifier.js';\nimport type CodeConvertable from './CodeConvertable.js';\nimport StatementRecordManager, { createComparator, type StatementRecord } from './StatementRecordManager.js';\nimport { createDependencyRecord, type DependencyRecord } from './utils.js';\n\nexport class NamedImportManager extends StatementRecordManager<ImportDeclaration> {\n readonly #map = new Map<string, Map<string, DependencyRecord>>();\n\n get size(): number {\n return this.#map.size;\n }\n\n add(path: string, specifier: string, isType?: boolean, uniqueId?: Identifier): Identifier {\n const record = createDependencyRecord(uniqueId ?? createFullyUniqueIdentifier(specifier), isType);\n\n if (this.#map.has(path)) {\n this.#map.get(path)!.set(specifier, record);\n } else {\n this.#map.set(path, new Map([[specifier, record]]));\n }\n\n return record.id;\n }\n\n remove(path: string, specifier: string): void {\n const specifiers = this.#map.get(path);\n\n if (specifiers) {\n specifiers.delete(specifier);\n\n if (specifiers.size === 0) {\n this.#map.delete(path);\n }\n }\n }\n\n override clear(): void {\n this.#map.clear();\n }\n\n getIdentifier(path: string, specifier: string): Identifier | undefined {\n return this.#map.get(path)?.get(specifier)?.id;\n }\n\n iter(): IterableIterator<readonly [path: string, specifier: string, id: Identifier, isType: boolean]> {\n return this[Symbol.iterator]();\n }\n\n isType(path: string, specifier: string): boolean | undefined {\n return this.#map.get(path)?.get(specifier)?.isType;\n }\n\n paths(): IterableIterator<string> {\n return this.#map.keys();\n }\n\n *specifiers(): IterableIterator<readonly [path: string, specifier: string]> {\n for (const [path, specifiers] of this.#map) {\n for (const specifier of specifiers.keys()) {\n yield [path, specifier];\n }\n }\n }\n\n override *statementRecords(): IterableIterator<StatementRecord<ImportDeclaration>> {\n for (const [path, specifiers] of this.#map) {\n const names = [...specifiers.keys()];\n // eslint-disable-next-line @typescript-eslint/unbound-method\n names.sort(this.collator.compare);\n\n const isTypeOnly = names.every((name) => specifiers.get(name)!.isType);\n\n yield [\n path,\n ts.factory.createImportDeclaration(\n undefined,\n ts.factory.createImportClause(\n isTypeOnly,\n undefined,\n ts.factory.createNamedImports(\n names.map((name) => {\n const { id, isType } = specifiers.get(name)!;\n return ts.factory.createImportSpecifier(\n isTypeOnly ? false : isType,\n ts.factory.createIdentifier(name),\n id,\n );\n }),\n ),\n ),\n ts.factory.createStringLiteral(path),\n ),\n ];\n }\n }\n\n *[Symbol.iterator](): IterableIterator<readonly [path: string, specifier: string, id: Identifier, isType: boolean]> {\n for (const [path, specifiers] of this.#map) {\n for (const [specifier, { id, isType }] of specifiers) {\n yield [path, specifier, id, isType];\n }\n }\n }\n}\n\nexport class NamespaceImportManager extends StatementRecordManager<ImportDeclaration> {\n readonly #map = new Map<string, Identifier>();\n\n get size(): number {\n return this.#map.size;\n }\n\n add(path: string, name: string, uniqueId?: Identifier): Identifier {\n const id = uniqueId ?? createFullyUniqueIdentifier(name);\n this.#map.set(path, id);\n return id;\n }\n\n override clear(): void {\n this.#map.clear();\n }\n\n getIdentifier(path: string): Identifier | undefined {\n return this.#map.get(path);\n }\n\n iter(): IterableIterator<readonly [path: string, id: Identifier]> {\n return this[Symbol.iterator]();\n }\n\n paths(): IterableIterator<string> {\n return this.#map.keys();\n }\n\n override *statementRecords(): IterableIterator<StatementRecord<ImportDeclaration>> {\n for (const [path, id] of this.#map) {\n yield [\n path,\n ts.factory.createImportDeclaration(\n undefined,\n ts.factory.createImportClause(false, undefined, ts.factory.createNamespaceImport(id)),\n ts.factory.createStringLiteral(path),\n ),\n ];\n }\n }\n\n remove(path: string): void {\n this.#map.delete(path);\n }\n\n *[Symbol.iterator](): IterableIterator<readonly [path: string, id: Identifier]> {\n for (const [path, id] of this.#map) {\n yield [path, id];\n }\n }\n}\n\nexport class DefaultImportManager extends StatementRecordManager<ImportDeclaration> {\n readonly #map = new Map<string, DependencyRecord>();\n\n get size(): number {\n return this.#map.size;\n }\n\n add(path: string, name: string, isType?: boolean, uniqueId?: Identifier): Identifier {\n const id = uniqueId ?? createFullyUniqueIdentifier(name);\n this.#map.set(path, createDependencyRecord(id, isType));\n return id;\n }\n\n getIdentifier(path: string): Identifier | undefined {\n return this.#map.get(path)?.id;\n }\n\n remove(path: string): void {\n if (this.#map.has(path)) {\n this.#map.delete(path);\n }\n }\n\n override clear(): void {\n this.#map.clear();\n }\n\n iter(): IterableIterator<readonly [path: string, id: Identifier, isType: boolean]> {\n return this[Symbol.iterator]();\n }\n\n isType(path: string): boolean | undefined {\n return this.#map.get(path)?.isType;\n }\n\n paths(): IterableIterator<string> {\n return this.#map.keys();\n }\n\n override *statementRecords(): IterableIterator<StatementRecord<ImportDeclaration>> {\n for (const [path, { id, isType }] of this.#map) {\n yield [\n path,\n ts.factory.createImportDeclaration(\n undefined,\n ts.factory.createImportClause(isType, id, undefined),\n ts.factory.createStringLiteral(path),\n ),\n ];\n }\n }\n\n *[Symbol.iterator](): IterableIterator<readonly [path: string, id: Identifier, isType: boolean]> {\n for (const [path, { id, isType }] of this.#map) {\n yield [path, id, isType];\n }\n }\n}\n\nexport default class ImportManager implements CodeConvertable<readonly Statement[]> {\n readonly collator: Intl.Collator;\n readonly default: DefaultImportManager;\n readonly named: NamedImportManager;\n readonly namespace: NamespaceImportManager;\n\n constructor(collator: Intl.Collator) {\n this.default = new DefaultImportManager(collator);\n this.named = new NamedImportManager(collator);\n this.namespace = new NamespaceImportManager(collator);\n this.collator = collator;\n }\n\n get size(): number {\n return this.default.size + this.named.size + this.namespace.size;\n }\n\n toCode(): readonly Statement[] {\n const records = [\n ...this.default.statementRecords(),\n ...this.named.statementRecords(),\n ...this.namespace.statementRecords(),\n ];\n records.sort(createComparator(this.collator));\n\n return records.map(([, statement]) => statement);\n }\n\n fromCode(source: ts.SourceFile): void {\n this.default.clear();\n this.named.clear();\n this.namespace.clear();\n\n const imports = source.statements.filter((statement): statement is ImportDeclaration =>\n ts.isImportDeclaration(statement),\n );\n\n for (const { importClause, moduleSpecifier } of imports) {\n if (!importClause) {\n // eslint-disable-next-line no-continue\n continue;\n }\n\n const { name, namedBindings } = importClause;\n const path = (moduleSpecifier as ts.StringLiteral).text;\n\n if (namedBindings) {\n if (ts.isNamespaceImport(namedBindings)) {\n this.namespace.add(path, namedBindings.name.text, namedBindings.name);\n } else {\n for (const { isTypeOnly, name: specifier } of namedBindings.elements) {\n this.named.add(path, specifier.text, isTypeOnly, specifier);\n }\n }\n } else if (name) {\n this.default.add(path, name.text, importClause.isTypeOnly, name);\n }\n }\n }\n}\n"],"version":3}
|
|
1
|
+
{"mappings":"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","names":["#map","#names","collator: Intl.Collator","names: NameRegistry","path: string","specifier: string","isType?: boolean","uniqueId?: Identifier","name: string","defaultDeclaration: ImportDeclaration","namedDeclaration: ImportDeclaration","records: Array<StatementRecord<ImportDeclaration>>","source: ts.SourceFile","node: ts.Node"],"sources":["/opt/agent/work/649c11185a3798db/packages/ts/generator-utils/src/dependencies/ImportManager.ts"],"sourcesContent":["import ts, { type Identifier, type ImportDeclaration, type Statement } from '@typescript/typescript6';\nimport type CodeConvertable from './CodeConvertable.js';\nimport NameRegistry from './NameRegistry.js';\nimport StatementRecordManager, { createComparator, type StatementRecord } from './StatementRecordManager.js';\nimport { createDependencyRecord, type DependencyRecord } from './utils.js';\n\nexport class NamedImportManager extends StatementRecordManager<ImportDeclaration> {\n readonly #map = new Map<string, Map<string, DependencyRecord>>();\n readonly #names: NameRegistry;\n\n constructor(collator: Intl.Collator, names: NameRegistry) {\n super(collator);\n this.#names = names;\n }\n\n get size(): number {\n return this.#map.size;\n }\n\n add(path: string, specifier: string, isType?: boolean, uniqueId?: Identifier): Identifier {\n if (uniqueId) {\n this.#names.claim(uniqueId.text);\n }\n\n const id = uniqueId ?? this.getIdentifier(path, specifier) ?? this.#names.reserveIdentifier(specifier);\n const record = createDependencyRecord(id, isType);\n\n if (this.#map.has(path)) {\n this.#map.get(path)!.set(specifier, record);\n } else {\n this.#map.set(path, new Map([[specifier, record]]));\n }\n\n return record.id;\n }\n\n remove(path: string, specifier: string): void {\n const specifiers = this.#map.get(path);\n\n if (specifiers) {\n specifiers.delete(specifier);\n\n if (specifiers.size === 0) {\n this.#map.delete(path);\n }\n }\n }\n\n override clear(): void {\n this.#map.clear();\n }\n\n getIdentifier(path: string, specifier: string): Identifier | undefined {\n return this.#map.get(path)?.get(specifier)?.id;\n }\n\n iter(): IterableIterator<readonly [path: string, specifier: string, id: Identifier, isType: boolean]> {\n return this[Symbol.iterator]();\n }\n\n isType(path: string, specifier: string): boolean | undefined {\n return this.#map.get(path)?.get(specifier)?.isType;\n }\n\n paths(): IterableIterator<string> {\n return this.#map.keys();\n }\n\n *specifiers(): IterableIterator<readonly [path: string, specifier: string]> {\n for (const [path, specifiers] of this.#map) {\n for (const specifier of specifiers.keys()) {\n yield [path, specifier];\n }\n }\n }\n\n override *statementRecords(): IterableIterator<StatementRecord<ImportDeclaration>> {\n for (const [path, specifiers] of this.#map) {\n const names = [...specifiers.keys()];\n // eslint-disable-next-line @typescript-eslint/unbound-method\n names.sort(this.collator.compare);\n\n const isTypeOnly = names.every((name) => specifiers.get(name)!.isType);\n\n yield [\n path,\n ts.factory.createImportDeclaration(\n undefined,\n ts.factory.createImportClause(\n isTypeOnly,\n undefined,\n ts.factory.createNamedImports(\n names.map((name) => {\n const { id, isType } = specifiers.get(name)!;\n return ts.factory.createImportSpecifier(\n isTypeOnly ? false : isType,\n id.text === name ? undefined : ts.factory.createIdentifier(name),\n id,\n );\n }),\n ),\n ),\n ts.factory.createStringLiteral(path),\n ),\n ];\n }\n }\n\n *[Symbol.iterator](): IterableIterator<readonly [path: string, specifier: string, id: Identifier, isType: boolean]> {\n for (const [path, specifiers] of this.#map) {\n for (const [specifier, { id, isType }] of specifiers) {\n yield [path, specifier, id, isType];\n }\n }\n }\n}\n\nexport class NamespaceImportManager extends StatementRecordManager<ImportDeclaration> {\n readonly #map = new Map<string, Identifier>();\n readonly #names: NameRegistry;\n\n constructor(collator: Intl.Collator, names: NameRegistry) {\n super(collator);\n this.#names = names;\n }\n\n get size(): number {\n return this.#map.size;\n }\n\n add(path: string, name: string, uniqueId?: Identifier): Identifier {\n if (uniqueId) {\n this.#names.claim(uniqueId.text);\n }\n\n const id = uniqueId ?? this.#map.get(path) ?? this.#names.reserveIdentifier(name);\n this.#map.set(path, id);\n return id;\n }\n\n override clear(): void {\n this.#map.clear();\n }\n\n getIdentifier(path: string): Identifier | undefined {\n return this.#map.get(path);\n }\n\n iter(): IterableIterator<readonly [path: string, id: Identifier]> {\n return this[Symbol.iterator]();\n }\n\n paths(): IterableIterator<string> {\n return this.#map.keys();\n }\n\n override *statementRecords(): IterableIterator<StatementRecord<ImportDeclaration>> {\n for (const [path, id] of this.#map) {\n yield [\n path,\n ts.factory.createImportDeclaration(\n undefined,\n ts.factory.createImportClause(false, undefined, ts.factory.createNamespaceImport(id)),\n ts.factory.createStringLiteral(path),\n ),\n ];\n }\n }\n\n remove(path: string): void {\n this.#map.delete(path);\n }\n\n *[Symbol.iterator](): IterableIterator<readonly [path: string, id: Identifier]> {\n for (const [path, id] of this.#map) {\n yield [path, id];\n }\n }\n}\n\nexport class DefaultImportManager extends StatementRecordManager<ImportDeclaration> {\n readonly #map = new Map<string, DependencyRecord>();\n readonly #names: NameRegistry;\n\n constructor(collator: Intl.Collator, names: NameRegistry) {\n super(collator);\n this.#names = names;\n }\n\n get size(): number {\n return this.#map.size;\n }\n\n add(path: string, name: string, isType?: boolean, uniqueId?: Identifier): Identifier {\n if (uniqueId) {\n this.#names.claim(uniqueId.text);\n }\n\n const id = uniqueId ?? this.getIdentifier(path) ?? this.#names.reserveIdentifier(name);\n this.#map.set(path, createDependencyRecord(id, isType));\n return id;\n }\n\n getIdentifier(path: string): Identifier | undefined {\n return this.#map.get(path)?.id;\n }\n\n remove(path: string): void {\n if (this.#map.has(path)) {\n this.#map.delete(path);\n }\n }\n\n override clear(): void {\n this.#map.clear();\n }\n\n iter(): IterableIterator<readonly [path: string, id: Identifier, isType: boolean]> {\n return this[Symbol.iterator]();\n }\n\n isType(path: string): boolean | undefined {\n return this.#map.get(path)?.isType;\n }\n\n paths(): IterableIterator<string> {\n return this.#map.keys();\n }\n\n override *statementRecords(): IterableIterator<StatementRecord<ImportDeclaration>> {\n for (const [path, { id, isType }] of this.#map) {\n yield [\n path,\n ts.factory.createImportDeclaration(\n undefined,\n ts.factory.createImportClause(isType, id, undefined),\n ts.factory.createStringLiteral(path),\n ),\n ];\n }\n }\n\n *[Symbol.iterator](): IterableIterator<readonly [path: string, id: Identifier, isType: boolean]> {\n for (const [path, { id, isType }] of this.#map) {\n yield [path, id, isType];\n }\n }\n}\n\n/**\n * Combines a default and a named import of the same module into one\n * declaration, i.e. `import m, { StringModel } from \"...\"`.\n *\n * A type-only default import cannot take named bindings along, so those stay\n * apart. A named group that is entirely type-only can still be merged, by\n * marking its specifiers individually instead of the declaration.\n */\nfunction mergeDefaultAndNamed(\n defaultDeclaration: ImportDeclaration,\n namedDeclaration: ImportDeclaration,\n): ImportDeclaration | undefined {\n const defaultClause = defaultDeclaration.importClause;\n const namedClause = namedDeclaration.importClause;\n\n if (!defaultClause?.name || defaultClause.isTypeOnly || !namedClause?.namedBindings) {\n return undefined;\n }\n\n const bindings = namedClause.namedBindings;\n\n if (!ts.isNamedImports(bindings)) {\n return undefined;\n }\n\n const elements = namedClause.isTypeOnly\n ? bindings.elements.map((element) => ts.factory.createImportSpecifier(true, element.propertyName, element.name))\n : bindings.elements;\n\n return ts.factory.createImportDeclaration(\n defaultDeclaration.modifiers,\n ts.factory.createImportClause(false, defaultClause.name, ts.factory.createNamedImports(elements)),\n defaultDeclaration.moduleSpecifier,\n );\n}\n\nexport default class ImportManager implements CodeConvertable<readonly Statement[]> {\n readonly collator: Intl.Collator;\n readonly default: DefaultImportManager;\n readonly named: NamedImportManager;\n readonly names: NameRegistry;\n readonly namespace: NamespaceImportManager;\n\n constructor(collator: Intl.Collator, names: NameRegistry = new NameRegistry()) {\n this.default = new DefaultImportManager(collator, names);\n this.named = new NamedImportManager(collator, names);\n this.namespace = new NamespaceImportManager(collator, names);\n this.collator = collator;\n this.names = names;\n }\n\n get size(): number {\n return this.default.size + this.named.size + this.namespace.size;\n }\n\n toCode(): readonly Statement[] {\n const named = new Map(this.named.statementRecords());\n const records: Array<StatementRecord<ImportDeclaration>> = [];\n\n for (const [path, declaration] of this.default.statementRecords()) {\n const namedDeclaration = named.get(path);\n const merged = namedDeclaration && mergeDefaultAndNamed(declaration, namedDeclaration);\n\n if (merged) {\n named.delete(path);\n }\n\n records.push([path, merged ?? declaration]);\n }\n\n records.push(...named, ...this.namespace.statementRecords());\n records.sort(createComparator(this.collator));\n\n return records.map(([, statement]) => statement);\n }\n\n fromCode(source: ts.SourceFile): void {\n this.default.clear();\n this.named.clear();\n this.namespace.clear();\n\n // Anything already spelled out in the source is off limits for names the\n // registry hands out later.\n const claim = (node: ts.Node): void => {\n if (ts.isIdentifier(node)) {\n this.names.claim(node.text);\n }\n\n ts.forEachChild(node, claim);\n };\n claim(source);\n\n const imports = source.statements.filter((statement): statement is ImportDeclaration =>\n ts.isImportDeclaration(statement),\n );\n\n for (const { importClause, moduleSpecifier } of imports) {\n if (!importClause) {\n // eslint-disable-next-line no-continue\n continue;\n }\n\n const { name, namedBindings } = importClause;\n const path = (moduleSpecifier as ts.StringLiteral).text;\n\n // a default import may come along with named bindings in a single\n // declaration, i.e. `import m, { StringModel } from \"...\"`\n if (name) {\n this.default.add(path, name.text, importClause.isTypeOnly, name);\n }\n\n if (namedBindings) {\n if (ts.isNamespaceImport(namedBindings)) {\n this.namespace.add(path, namedBindings.name.text, namedBindings.name);\n } else {\n for (const { isTypeOnly, name: local, propertyName } of namedBindings.elements) {\n // `propertyName` holds the exported name whenever the import is\n // aliased; the local binding alone would not survive the round trip.\n this.named.add(path, (propertyName ?? local).text, isTypeOnly, local);\n }\n }\n }\n }\n }\n}\n"],"version":3}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { type Identifier } from "@typescript/typescript6";
|
|
2
|
+
/**
|
|
3
|
+
* Tracks the identifier names taken in a single generated file so that a name
|
|
4
|
+
* is only suffixed when it actually collides.
|
|
5
|
+
*
|
|
6
|
+
* The TypeScript printer cannot do this for us: its own unique-name generator
|
|
7
|
+
* only compares generated names against each other, so a generated import would
|
|
8
|
+
* still collide with a plain declaration in the same file.
|
|
9
|
+
*/
|
|
10
|
+
export default class NameRegistry {
|
|
11
|
+
#private;
|
|
12
|
+
/**
|
|
13
|
+
* Marks a name as taken as-is. Used for names the registry does not get to
|
|
14
|
+
* choose, such as a declaration name or an identifier parsed from existing
|
|
15
|
+
* code.
|
|
16
|
+
*/
|
|
17
|
+
claim(name: string): void;
|
|
18
|
+
/**
|
|
19
|
+
* Takes the given name, appending a counter if it is taken already.
|
|
20
|
+
*/
|
|
21
|
+
reserve(name: string): string;
|
|
22
|
+
reserveIdentifier(name: string): Identifier;
|
|
23
|
+
}
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
import ts from "@typescript/typescript6";
|
|
2
|
+
/**
|
|
3
|
+
* Words that cannot serve as a binding name. Generated files are modules, hence
|
|
4
|
+
* the strict mode ones are in as well, including `arguments` and `eval`, which
|
|
5
|
+
* are not keywords but are equally illegal. Contextual keywords (`type`, `as`,
|
|
6
|
+
* `get`, `string`, ...) are valid identifiers and thus are not listed.
|
|
7
|
+
*/
|
|
8
|
+
const reservedWords = [
|
|
9
|
+
"arguments",
|
|
10
|
+
"await",
|
|
11
|
+
"break",
|
|
12
|
+
"case",
|
|
13
|
+
"catch",
|
|
14
|
+
"class",
|
|
15
|
+
"const",
|
|
16
|
+
"continue",
|
|
17
|
+
"debugger",
|
|
18
|
+
"default",
|
|
19
|
+
"delete",
|
|
20
|
+
"do",
|
|
21
|
+
"else",
|
|
22
|
+
"enum",
|
|
23
|
+
"eval",
|
|
24
|
+
"export",
|
|
25
|
+
"extends",
|
|
26
|
+
"false",
|
|
27
|
+
"finally",
|
|
28
|
+
"for",
|
|
29
|
+
"function",
|
|
30
|
+
"if",
|
|
31
|
+
"implements",
|
|
32
|
+
"import",
|
|
33
|
+
"in",
|
|
34
|
+
"instanceof",
|
|
35
|
+
"interface",
|
|
36
|
+
"let",
|
|
37
|
+
"new",
|
|
38
|
+
"null",
|
|
39
|
+
"package",
|
|
40
|
+
"private",
|
|
41
|
+
"protected",
|
|
42
|
+
"public",
|
|
43
|
+
"return",
|
|
44
|
+
"static",
|
|
45
|
+
"super",
|
|
46
|
+
"switch",
|
|
47
|
+
"this",
|
|
48
|
+
"throw",
|
|
49
|
+
"true",
|
|
50
|
+
"try",
|
|
51
|
+
"typeof",
|
|
52
|
+
"var",
|
|
53
|
+
"void",
|
|
54
|
+
"while",
|
|
55
|
+
"with",
|
|
56
|
+
"yield"
|
|
57
|
+
];
|
|
58
|
+
/**
|
|
59
|
+
* Globals the generated code refers to without importing them, so an import
|
|
60
|
+
* taking one of these names would shadow the type it refers to.
|
|
61
|
+
*/
|
|
62
|
+
const globalNames = [
|
|
63
|
+
"Array",
|
|
64
|
+
"File",
|
|
65
|
+
"Promise",
|
|
66
|
+
"ReadonlyArray",
|
|
67
|
+
"Record"
|
|
68
|
+
];
|
|
69
|
+
/**
|
|
70
|
+
* Tracks the identifier names taken in a single generated file so that a name
|
|
71
|
+
* is only suffixed when it actually collides.
|
|
72
|
+
*
|
|
73
|
+
* The TypeScript printer cannot do this for us: its own unique-name generator
|
|
74
|
+
* only compares generated names against each other, so a generated import would
|
|
75
|
+
* still collide with a plain declaration in the same file.
|
|
76
|
+
*/
|
|
77
|
+
export default class NameRegistry {
|
|
78
|
+
#used = new Set([...reservedWords, ...globalNames]);
|
|
79
|
+
/**
|
|
80
|
+
* Marks a name as taken as-is. Used for names the registry does not get to
|
|
81
|
+
* choose, such as a declaration name or an identifier parsed from existing
|
|
82
|
+
* code.
|
|
83
|
+
*/
|
|
84
|
+
claim(name) {
|
|
85
|
+
this.#used.add(name);
|
|
86
|
+
}
|
|
87
|
+
/**
|
|
88
|
+
* Takes the given name, appending a counter if it is taken already.
|
|
89
|
+
*/
|
|
90
|
+
reserve(name) {
|
|
91
|
+
if (!this.#used.has(name)) {
|
|
92
|
+
this.#used.add(name);
|
|
93
|
+
return name;
|
|
94
|
+
}
|
|
95
|
+
let i = 1;
|
|
96
|
+
let candidate = `${name}_${i}`;
|
|
97
|
+
while (this.#used.has(candidate)) {
|
|
98
|
+
i += 1;
|
|
99
|
+
candidate = `${name}_${i}`;
|
|
100
|
+
}
|
|
101
|
+
this.#used.add(candidate);
|
|
102
|
+
return candidate;
|
|
103
|
+
}
|
|
104
|
+
reserveIdentifier(name) {
|
|
105
|
+
return ts.factory.createIdentifier(this.reserve(name));
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
//# sourceMappingURL=./NameRegistry.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"mappings":"AAAA,OAAO,iCAAuD;;;;;;;AAQ9D,MAAM,gBAAgB;CACpB;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;AACD;;;;;AAMD,MAAM,cAAc;CAAC;CAAS;CAAQ;CAAW;CAAiB;AAAS;;;;;;;;;AAU3E,eAAe,MAAM,aAAa;CAGhC,AAASA,QAAQ,IAAI,IAAI,CAAC,GAAG,eAAe,GAAG,WAAY;;;;;;CAO3D,MAAMC,MAAoB;AACxB,OAAKD,MAAM,IAAI,KAAK;CACrB;;;;CAKD,QAAQC,MAAsB;AAC5B,OAAK,KAAKD,MAAM,IAAI,KAAK,EAAE;AACzB,QAAKA,MAAM,IAAI,KAAK;AACpB,UAAO;EACR;EAED,IAAI,IAAI;EACR,IAAI,aAAa,EAAE,KAAK,GAAG,EAAE;AAE7B,SAAO,KAAKA,MAAM,IAAI,UAAU,EAAE;AAChC,QAAK;AACL,gBAAa,EAAE,KAAK,GAAG,EAAE;EAC1B;AAED,OAAKA,MAAM,IAAI,UAAU;AACzB,SAAO;CACR;CAED,kBAAkBC,MAA0B;AAC1C,SAAO,GAAG,QAAQ,iBAAiB,KAAK,QAAQ,KAAK,CAAC;CACvD;AACF","names":["#used","name: string"],"sources":["/opt/agent/work/649c11185a3798db/packages/ts/generator-utils/src/dependencies/NameRegistry.ts"],"sourcesContent":["import ts, { type Identifier } from '@typescript/typescript6';\n\n/**\n * Words that cannot serve as a binding name. Generated files are modules, hence\n * the strict mode ones are in as well, including `arguments` and `eval`, which\n * are not keywords but are equally illegal. Contextual keywords (`type`, `as`,\n * `get`, `string`, ...) are valid identifiers and thus are not listed.\n */\nconst reservedWords = [\n 'arguments',\n 'await',\n 'break',\n 'case',\n 'catch',\n 'class',\n 'const',\n 'continue',\n 'debugger',\n 'default',\n 'delete',\n 'do',\n 'else',\n 'enum',\n 'eval',\n 'export',\n 'extends',\n 'false',\n 'finally',\n 'for',\n 'function',\n 'if',\n 'implements',\n 'import',\n 'in',\n 'instanceof',\n 'interface',\n 'let',\n 'new',\n 'null',\n 'package',\n 'private',\n 'protected',\n 'public',\n 'return',\n 'static',\n 'super',\n 'switch',\n 'this',\n 'throw',\n 'true',\n 'try',\n 'typeof',\n 'var',\n 'void',\n 'while',\n 'with',\n 'yield',\n];\n\n/**\n * Globals the generated code refers to without importing them, so an import\n * taking one of these names would shadow the type it refers to.\n */\nconst globalNames = ['Array', 'File', 'Promise', 'ReadonlyArray', 'Record'];\n\n/**\n * Tracks the identifier names taken in a single generated file so that a name\n * is only suffixed when it actually collides.\n *\n * The TypeScript printer cannot do this for us: its own unique-name generator\n * only compares generated names against each other, so a generated import would\n * still collide with a plain declaration in the same file.\n */\nexport default class NameRegistry {\n // reserved words and shadowable globals count as taken, so that a name\n // matching one is suffixed\n readonly #used = new Set([...reservedWords, ...globalNames]);\n\n /**\n * Marks a name as taken as-is. Used for names the registry does not get to\n * choose, such as a declaration name or an identifier parsed from existing\n * code.\n */\n claim(name: string): void {\n this.#used.add(name);\n }\n\n /**\n * Takes the given name, appending a counter if it is taken already.\n */\n reserve(name: string): string {\n if (!this.#used.has(name)) {\n this.#used.add(name);\n return name;\n }\n\n let i = 1;\n let candidate = `${name}_${i}`;\n\n while (this.#used.has(candidate)) {\n i += 1;\n candidate = `${name}_${i}`;\n }\n\n this.#used.add(candidate);\n return candidate;\n }\n\n reserveIdentifier(name: string): Identifier {\n return ts.factory.createIdentifier(this.reserve(name));\n }\n}\n"],"version":3}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Breaks long fluent chains over several lines, one call per line.
|
|
3
|
+
*
|
|
4
|
+
* The TypeScript printer emits a member chain on a single line and offers no
|
|
5
|
+
* way to influence that: `setStartsOnNewLine` only applies to nodes in a list,
|
|
6
|
+
* and no emit flag covers chains. So the text it produced is parsed back and
|
|
7
|
+
* the line breaks are spliced in at the offsets the syntax tree reports, which
|
|
8
|
+
* keeps string literals — a `Pattern` regexp may contain anything — untouched.
|
|
9
|
+
*/
|
|
10
|
+
export default function formatMemberChains(code: string, fileName?: string): string;
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
import ts from "@typescript/typescript6";
|
|
2
|
+
/**
|
|
3
|
+
* The number of calls a chain needs before it is worth breaking up. Two links
|
|
4
|
+
* cover `m.object(…).build()`, which reads better on one line; three is where
|
|
5
|
+
* the builder starts declaring properties.
|
|
6
|
+
*/
|
|
7
|
+
const MIN_LINKS = 3;
|
|
8
|
+
const INDENT = " ";
|
|
9
|
+
/**
|
|
10
|
+
* The offset of the dot of a property access, which sits between the end of the
|
|
11
|
+
* object expression and the start of the property name. Comments cannot appear
|
|
12
|
+
* there in printed output, so the first dot found is the right one.
|
|
13
|
+
*/
|
|
14
|
+
function findDot(source, node) {
|
|
15
|
+
return source.text.indexOf(".", node.expression.end);
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* Collects the calls of a chain, from its outermost link inwards, for as long
|
|
19
|
+
* as every link is a call on a property access. A chain interrupted by anything
|
|
20
|
+
* else, such as an element access or a bare property read, ends there.
|
|
21
|
+
*/
|
|
22
|
+
function collectChain(node) {
|
|
23
|
+
const links = [];
|
|
24
|
+
for (let current = node;;) {
|
|
25
|
+
if (!ts.isCallExpression(current) || !ts.isPropertyAccessExpression(current.expression)) {
|
|
26
|
+
return links;
|
|
27
|
+
}
|
|
28
|
+
links.push(current);
|
|
29
|
+
current = current.expression.expression;
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* The column the statement starts at, so that the continuation lines line up
|
|
34
|
+
* under it rather than at the left margin.
|
|
35
|
+
*/
|
|
36
|
+
function columnOf(source, position) {
|
|
37
|
+
const lineStart = source.text.lastIndexOf("\n", position - 1) + 1;
|
|
38
|
+
return position - lineStart;
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* Breaks long fluent chains over several lines, one call per line.
|
|
42
|
+
*
|
|
43
|
+
* The TypeScript printer emits a member chain on a single line and offers no
|
|
44
|
+
* way to influence that: `setStartsOnNewLine` only applies to nodes in a list,
|
|
45
|
+
* and no emit flag covers chains. So the text it produced is parsed back and
|
|
46
|
+
* the line breaks are spliced in at the offsets the syntax tree reports, which
|
|
47
|
+
* keeps string literals — a `Pattern` regexp may contain anything — untouched.
|
|
48
|
+
*/
|
|
49
|
+
export default function formatMemberChains(code, fileName = "generated.ts") {
|
|
50
|
+
const source = ts.createSourceFile(fileName, code, ts.ScriptTarget.Latest, true, ts.ScriptKind.TS);
|
|
51
|
+
const breaks = [];
|
|
52
|
+
function visit(node) {
|
|
53
|
+
if (ts.isCallExpression(node)) {
|
|
54
|
+
const links = collectChain(node);
|
|
55
|
+
if (links.length >= MIN_LINKS) {
|
|
56
|
+
const statement = ts.findAncestor(node, ts.isStatement) ?? node;
|
|
57
|
+
const indent = " ".repeat(columnOf(source, statement.getStart(source))) + INDENT;
|
|
58
|
+
links.forEach((link) => {
|
|
59
|
+
breaks.push([findDot(source, link.expression), indent]);
|
|
60
|
+
link.arguments.forEach(visit);
|
|
61
|
+
});
|
|
62
|
+
return;
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
ts.forEachChild(node, visit);
|
|
66
|
+
}
|
|
67
|
+
visit(source);
|
|
68
|
+
if (breaks.length === 0) {
|
|
69
|
+
return code;
|
|
70
|
+
}
|
|
71
|
+
return breaks.toSorted(([a], [b]) => b - a).reduce((text, [offset, indent]) => `${text.slice(0, offset)}\n${indent}${text.slice(offset)}`, code);
|
|
72
|
+
}
|
|
73
|
+
//# sourceMappingURL=./formatMemberChains.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"mappings":"AAAA,OAAO,iCAAuF;;;;;;AAO9F,MAAM,YAAY;AAElB,MAAM,SAAS;;;;;;AAOf,SAAS,QAAQA,QAAoBC,MAA2C;AAC9E,QAAO,OAAO,KAAK,QAAQ,KAAK,KAAK,WAAW,IAAI;AACrD;;;;;;AAOD,SAAS,aAAaC,MAAiD;CACrE,MAAMC,QAA0B,CAAE;AAElC,MAAK,IAAIC,UAAyB,QAAU;AAC1C,OAAK,GAAG,iBAAiB,QAAQ,KAAK,GAAG,2BAA2B,QAAQ,WAAW,EAAE;AAEvF,UAAO;EACR;AAED,QAAM,KAAK,QAAQ;AACnB,YAAU,QAAQ,WAAW;CAC9B;AACF;;;;;AAMD,SAAS,SAASJ,QAAoBK,UAA0B;CAC9D,MAAM,YAAY,OAAO,KAAK,YAAY,MAAM,WAAW,EAAE,GAAG;AAChE,QAAO,WAAW;AACnB;;;;;;;;;;AAWD,eAAe,SAAS,mBAAmBC,MAAc,WAAW,gBAAwB;CAC1F,MAAM,SAAS,GAAG,iBAAiB,UAAU,MAAM,GAAG,aAAa,QAAQ,MAAM,GAAG,WAAW,GAAG;CAClG,MAAMC,SAA2D,CAAE;CAEnE,SAAS,MAAMC,MAAkB;AAC/B,MAAI,GAAG,iBAAiB,KAAK,EAAE;GAC7B,MAAM,QAAQ,aAAa,KAAK;AAEhC,OAAI,MAAM,UAAU,WAAW;IAC7B,MAAM,YAAY,GAAG,aAAa,MAAM,GAAG,YAAY,IAAI;IAC3D,MAAM,SAAS,IAAI,OAAO,SAAS,QAAQ,UAAU,SAAS,OAAO,CAAC,CAAC,GAAG;AAE1E,UAAM,QAAQ,CAAC,SAAS;AACtB,YAAO,KAAK,CAAC,QAAQ,QAAQ,KAAK,WAA0C,EAAE,MAAO,EAAC;AAGtF,UAAK,UAAU,QAAQ,MAAM;IAC9B,EAAC;AAEF;GACD;EACF;AAED,KAAG,aAAa,MAAM,MAAM;CAC7B;AAED,OAAM,OAAO;AAEb,KAAI,OAAO,WAAW,GAAG;AACvB,SAAO;CACR;AAID,QAAO,OACJ,SAAS,CAAC,CAAC,EAAE,EAAE,CAAC,EAAE,KAAK,IAAI,EAAE,CAC7B,OAAO,CAAC,MAAM,CAAC,QAAQ,OAAO,MAAM,EAAE,KAAK,MAAM,GAAG,OAAO,CAAC,IAAI,OAAO,EAAE,KAAK,MAAM,OAAO,CAAC,GAAG,KAAK;AACxG","names":["source: SourceFile","node: ts.PropertyAccessExpression","node: CallExpression","links: CallExpression[]","current: ts.Expression","position: number","code: string","breaks: Array<readonly [offset: number, indent: string]>","node: Node"],"sources":["/opt/agent/work/649c11185a3798db/packages/ts/generator-utils/src/formatMemberChains.ts"],"sourcesContent":["import ts, { type CallExpression, type Node, type SourceFile } from '@typescript/typescript6';\n\n/**\n * The number of calls a chain needs before it is worth breaking up. Two links\n * cover `m.object(…).build()`, which reads better on one line; three is where\n * the builder starts declaring properties.\n */\nconst MIN_LINKS = 3;\n\nconst INDENT = ' ';\n\n/**\n * The offset of the dot of a property access, which sits between the end of the\n * object expression and the start of the property name. Comments cannot appear\n * there in printed output, so the first dot found is the right one.\n */\nfunction findDot(source: SourceFile, node: ts.PropertyAccessExpression): number {\n return source.text.indexOf('.', node.expression.end);\n}\n\n/**\n * Collects the calls of a chain, from its outermost link inwards, for as long\n * as every link is a call on a property access. A chain interrupted by anything\n * else, such as an element access or a bare property read, ends there.\n */\nfunction collectChain(node: CallExpression): readonly CallExpression[] {\n const links: CallExpression[] = [];\n\n for (let current: ts.Expression = node; ; ) {\n if (!ts.isCallExpression(current) || !ts.isPropertyAccessExpression(current.expression)) {\n // the head of the chain, e.g. the `m` of `m.object(…)`\n return links;\n }\n\n links.push(current);\n current = current.expression.expression;\n }\n}\n\n/**\n * The column the statement starts at, so that the continuation lines line up\n * under it rather than at the left margin.\n */\nfunction columnOf(source: SourceFile, position: number): number {\n const lineStart = source.text.lastIndexOf('\\n', position - 1) + 1;\n return position - lineStart;\n}\n\n/**\n * Breaks long fluent chains over several lines, one call per line.\n *\n * The TypeScript printer emits a member chain on a single line and offers no\n * way to influence that: `setStartsOnNewLine` only applies to nodes in a list,\n * and no emit flag covers chains. So the text it produced is parsed back and\n * the line breaks are spliced in at the offsets the syntax tree reports, which\n * keeps string literals — a `Pattern` regexp may contain anything — untouched.\n */\nexport default function formatMemberChains(code: string, fileName = 'generated.ts'): string {\n const source = ts.createSourceFile(fileName, code, ts.ScriptTarget.Latest, true, ts.ScriptKind.TS);\n const breaks: Array<readonly [offset: number, indent: string]> = [];\n\n function visit(node: Node): void {\n if (ts.isCallExpression(node)) {\n const links = collectChain(node);\n\n if (links.length >= MIN_LINKS) {\n const statement = ts.findAncestor(node, ts.isStatement) ?? node;\n const indent = ' '.repeat(columnOf(source, statement.getStart(source))) + INDENT;\n\n links.forEach((link) => {\n breaks.push([findDot(source, link.expression as ts.PropertyAccessExpression), indent]);\n // an argument may hold a chain of its own; the links themselves must\n // not be walked again, or they would be broken twice\n link.arguments.forEach(visit);\n });\n\n return;\n }\n }\n\n ts.forEachChild(node, visit);\n }\n\n visit(source);\n\n if (breaks.length === 0) {\n return code;\n }\n\n // spliced from the end, so that an insertion does not shift the offsets of\n // the breaks still to come\n return breaks\n .toSorted(([a], [b]) => b - a)\n .reduce((text, [offset, indent]) => `${text.slice(0, offset)}\\n${indent}${text.slice(offset)}`, code);\n}\n"],"version":3}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@vaadin/hilla-generator-utils",
|
|
3
|
-
"version": "25.4.0-
|
|
3
|
+
"version": "25.4.0-alpha4",
|
|
4
4
|
"description": "A set of utils for developing Hilla generator plugins",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"type": "module",
|
|
@@ -21,12 +21,14 @@
|
|
|
21
21
|
"./dependencies/DependencyManager.js": "./dependencies/DependencyManager.js",
|
|
22
22
|
"./dependencies/ExportManager.js": "./dependencies/ExportManager.js",
|
|
23
23
|
"./dependencies/ImportManager.js": "./dependencies/ImportManager.js",
|
|
24
|
+
"./dependencies/NameRegistry.js": "./dependencies/NameRegistry.js",
|
|
24
25
|
"./dependencies/PathManager.js": "./dependencies/PathManager.js",
|
|
25
26
|
"./dependencies/StatementRecordManager.js": "./dependencies/StatementRecordManager.js",
|
|
26
27
|
"./testing/snapshotMatcher.js": "./testing/snapshotMatcher.js",
|
|
28
|
+
"./testing/typeCheck.js": "./testing/typeCheck.js",
|
|
27
29
|
"./ast.js": "./ast.js",
|
|
28
|
-
"./createFullyUniqueIdentifier.js": "./createFullyUniqueIdentifier.js",
|
|
29
30
|
"./createSourceFile.js": "./createSourceFile.js",
|
|
31
|
+
"./formatMemberChains.js": "./formatMemberChains.js",
|
|
30
32
|
"./memoize.js": "./memoize.js",
|
|
31
33
|
"./LoggerFactory.js": "./LoggerFactory.js",
|
|
32
34
|
"./PluginError.js": "./PluginError.js",
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Type-checks the generated sources, together with the given extra sources,
|
|
3
|
+
* which can be used to assert that the generated types are actually usable.
|
|
4
|
+
* The sources are compiled from memory.
|
|
5
|
+
*
|
|
6
|
+
* @param root - the directory to compile the sources in. It has to be inside
|
|
7
|
+
* the package under test, so that the imported Hilla packages resolve the way
|
|
8
|
+
* they do for that package.
|
|
9
|
+
* @param files - the generated sources
|
|
10
|
+
* @param extraSources - additional sources, by file name relative to `root`
|
|
11
|
+
* @returns the compiler diagnostics, as readable messages
|
|
12
|
+
*/
|
|
13
|
+
export declare function typeCheck(root: string, files: readonly File[], extraSources?: Readonly<Record<string, string>>): Promise<readonly string[]>;
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
import { dirname, join } from "node:path";
|
|
2
|
+
import ts from "@typescript/typescript6";
|
|
3
|
+
const TYPE_CHECK_OPTIONS = {
|
|
4
|
+
module: ts.ModuleKind.ESNext,
|
|
5
|
+
moduleResolution: ts.ModuleResolutionKind.Bundler,
|
|
6
|
+
noEmit: true,
|
|
7
|
+
noUnusedLocals: true,
|
|
8
|
+
skipLibCheck: true,
|
|
9
|
+
strict: true,
|
|
10
|
+
target: ts.ScriptTarget.ES2022
|
|
11
|
+
};
|
|
12
|
+
const CLIENT_STUB = [
|
|
13
|
+
"declare const client: {",
|
|
14
|
+
" call(endpoint: string, method: string, params?: unknown, init?: unknown): Promise<never>;",
|
|
15
|
+
"};",
|
|
16
|
+
"export default client;"
|
|
17
|
+
].join("\n");
|
|
18
|
+
/**
|
|
19
|
+
* Type-checks the generated sources, together with the given extra sources,
|
|
20
|
+
* which can be used to assert that the generated types are actually usable.
|
|
21
|
+
* The sources are compiled from memory.
|
|
22
|
+
*
|
|
23
|
+
* @param root - the directory to compile the sources in. It has to be inside
|
|
24
|
+
* the package under test, so that the imported Hilla packages resolve the way
|
|
25
|
+
* they do for that package.
|
|
26
|
+
* @param files - the generated sources
|
|
27
|
+
* @param extraSources - additional sources, by file name relative to `root`
|
|
28
|
+
* @returns the compiler diagnostics, as readable messages
|
|
29
|
+
*/
|
|
30
|
+
export async function typeCheck(root, files, extraSources = {}) {
|
|
31
|
+
const sources = new Map([[join(root, "connect-client.default.ts"), CLIENT_STUB]]);
|
|
32
|
+
await Promise.all(files.map(async (file) => sources.set(join(root, file.name), await file.text())));
|
|
33
|
+
Object.entries(extraSources).forEach(([name, content]) => sources.set(join(root, name), content));
|
|
34
|
+
const directories = new Set();
|
|
35
|
+
sources.forEach((_, file) => {
|
|
36
|
+
for (let dir = dirname(file); !directories.has(dir); dir = dirname(dir)) {
|
|
37
|
+
directories.add(dir);
|
|
38
|
+
}
|
|
39
|
+
});
|
|
40
|
+
const host = ts.createCompilerHost(TYPE_CHECK_OPTIONS, true);
|
|
41
|
+
const directoryExists = host.directoryExists?.bind(host);
|
|
42
|
+
const fileExists = host.fileExists.bind(host);
|
|
43
|
+
const getSourceFile = host.getSourceFile.bind(host);
|
|
44
|
+
const hostReadFile = host.readFile.bind(host);
|
|
45
|
+
const realpath = host.realpath?.bind(host);
|
|
46
|
+
host.fileExists = (file) => sources.has(file) || fileExists(file);
|
|
47
|
+
host.readFile = (file) => sources.get(file) ?? hostReadFile(file);
|
|
48
|
+
host.directoryExists = (dir) => directories.has(dir) || (directoryExists?.(dir) ?? false);
|
|
49
|
+
host.realpath = (file) => sources.has(file) ? file : realpath?.(file) ?? file;
|
|
50
|
+
host.getSourceFile = (file, languageVersion, onError, shouldCreate) => {
|
|
51
|
+
const source = sources.get(file);
|
|
52
|
+
return source === undefined ? getSourceFile(file, languageVersion, onError, shouldCreate) : ts.createSourceFile(file, source, languageVersion, true);
|
|
53
|
+
};
|
|
54
|
+
const program = ts.createProgram([...sources.keys()], TYPE_CHECK_OPTIONS, host);
|
|
55
|
+
const prefix = `${root}/`;
|
|
56
|
+
return ts.getPreEmitDiagnostics(program).filter((diagnostic) => !diagnostic.file || sources.has(diagnostic.file.fileName)).map((diagnostic) => {
|
|
57
|
+
const name = diagnostic.file?.fileName.replace(prefix, "") ?? "";
|
|
58
|
+
const message = ts.flattenDiagnosticMessageText(diagnostic.messageText, " ");
|
|
59
|
+
return `${name}: ${message}`;
|
|
60
|
+
});
|
|
61
|
+
}
|
|
62
|
+
//# sourceMappingURL=./typeCheck.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"mappings":"AAAA,SAAS,SAAS,uBAAwB;AAC1C,OAAO,iCAAkC;AAEzC,MAAMA,qBAAyC;CAC7C,QAAQ,GAAG,WAAW;CACtB,kBAAkB,GAAG,qBAAqB;CAC1C,QAAQ;CAER,gBAAgB;CAChB,cAAc;CACd,QAAQ;CACR,QAAQ,GAAG,aAAa;AACzB;AAID,MAAM,cAAc;CAClB;CACA;CACA;CACA;AACD,EAAC,KAAK,KAAK;;;;;;;;;;;;;AAcZ,OAAO,eAAe,UACpBC,MACAC,OACAC,eAAiD,CAAE,GACvB;CAC5B,MAAM,UAAU,IAAI,IAAoB,CAAC,CAAC,KAAK,MAAM,4BAA4B,EAAE,WAAY,CAAC;AAEhG,OAAM,QAAQ,IAAI,MAAM,IAAI,OAAO,SAAS,QAAQ,IAAI,KAAK,MAAM,KAAK,KAAK,EAAE,MAAM,KAAK,MAAM,CAAC,CAAC,CAAC;AACnG,QAAO,QAAQ,aAAa,CAAC,QAAQ,CAAC,CAAC,MAAM,QAAQ,KAAK,QAAQ,IAAI,KAAK,MAAM,KAAK,EAAE,QAAQ,CAAC;CAEjG,MAAM,cAAc,IAAI;AACxB,SAAQ,QAAQ,CAAC,GAAG,SAAS;AAC3B,OAAK,IAAI,MAAM,QAAQ,KAAK,GAAG,YAAY,IAAI,IAAI,EAAE,MAAM,QAAQ,IAAI,EAAE;AACvE,eAAY,IAAI,IAAI;EACrB;CACF,EAAC;CAEF,MAAM,OAAO,GAAG,mBAAmB,oBAAoB,KAAK;CAC5D,MAAM,kBAAkB,KAAK,iBAAiB,KAAK,KAAK;CACxD,MAAM,aAAa,KAAK,WAAW,KAAK,KAAK;CAC7C,MAAM,gBAAgB,KAAK,cAAc,KAAK,KAAK;CACnD,MAAM,eAAe,KAAK,SAAS,KAAK,KAAK;CAC7C,MAAM,WAAW,KAAK,UAAU,KAAK,KAAK;AAE1C,MAAK,aAAa,CAAC,SAAS,QAAQ,IAAI,KAAK,IAAI,WAAW,KAAK;AACjE,MAAK,WAAW,CAAC,SAAS,QAAQ,IAAI,KAAK,IAAI,aAAa,KAAK;AACjE,MAAK,kBAAkB,CAAC,QAAQ,YAAY,IAAI,IAAI,KAAK,kBAAkB,IAAI,IAAI;AACnF,MAAK,WAAW,CAAC,SAAU,QAAQ,IAAI,KAAK,GAAG,OAAQ,WAAW,KAAK,IAAI;AAC3E,MAAK,gBAAgB,CAAC,MAAM,iBAAiB,SAAS,iBAAiB;EACrE,MAAM,SAAS,QAAQ,IAAI,KAAK;AAChC,SAAO,WAAW,YACd,cAAc,MAAM,iBAAiB,SAAS,aAAa,GAC3D,GAAG,iBAAiB,MAAM,QAAQ,iBAAiB,KAAK;CAC7D;CAED,MAAM,UAAU,GAAG,cAAc,CAAC,GAAG,QAAQ,MAAM,AAAC,GAAE,oBAAoB,KAAK;CAC/E,MAAM,UAAU,EAAE,KAAK;AAEvB,QAAO,GACJ,sBAAsB,QAAQ,CAC9B,OAAO,CAAC,gBAAgB,WAAW,QAAQ,QAAQ,IAAI,WAAW,KAAK,SAAS,CAAC,CACjF,IAAI,CAAC,eAAe;EACnB,MAAM,OAAO,WAAW,MAAM,SAAS,QAAQ,QAAQ,GAAG,IAAI;EAC9D,MAAM,UAAU,GAAG,6BAA6B,WAAW,aAAa,IAAI;AAE5E,UAAQ,EAAE,KAAK,IAAI,QAAQ;CAC5B,EAAC;AACL","names":["TYPE_CHECK_OPTIONS: ts.CompilerOptions","root: string","files: readonly File[]","extraSources: Readonly<Record<string, string>>"],"sources":["/opt/agent/work/649c11185a3798db/packages/ts/generator-utils/src/testing/typeCheck.ts"],"sourcesContent":["import { dirname, join } from 'node:path';\nimport ts from '@typescript/typescript6';\n\nconst TYPE_CHECK_OPTIONS: ts.CompilerOptions = {\n module: ts.ModuleKind.ESNext,\n moduleResolution: ts.ModuleResolutionKind.Bundler,\n noEmit: true,\n // the generated sources must not leave imports behind\n noUnusedLocals: true,\n skipLibCheck: true,\n strict: true,\n target: ts.ScriptTarget.ES2022,\n};\n\n// the client module is generated by another plugin, which is not part of the\n// plugin tests\nconst CLIENT_STUB = [\n 'declare const client: {',\n ' call(endpoint: string, method: string, params?: unknown, init?: unknown): Promise<never>;',\n '};',\n 'export default client;',\n].join('\\n');\n\n/**\n * Type-checks the generated sources, together with the given extra sources,\n * which can be used to assert that the generated types are actually usable.\n * The sources are compiled from memory.\n *\n * @param root - the directory to compile the sources in. It has to be inside\n * the package under test, so that the imported Hilla packages resolve the way\n * they do for that package.\n * @param files - the generated sources\n * @param extraSources - additional sources, by file name relative to `root`\n * @returns the compiler diagnostics, as readable messages\n */\nexport async function typeCheck(\n root: string,\n files: readonly File[],\n extraSources: Readonly<Record<string, string>> = {},\n): Promise<readonly string[]> {\n const sources = new Map<string, string>([[join(root, 'connect-client.default.ts'), CLIENT_STUB]]);\n\n await Promise.all(files.map(async (file) => sources.set(join(root, file.name), await file.text())));\n Object.entries(extraSources).forEach(([name, content]) => sources.set(join(root, name), content));\n\n const directories = new Set<string>();\n sources.forEach((_, file) => {\n for (let dir = dirname(file); !directories.has(dir); dir = dirname(dir)) {\n directories.add(dir);\n }\n });\n\n const host = ts.createCompilerHost(TYPE_CHECK_OPTIONS, true);\n const directoryExists = host.directoryExists?.bind(host);\n const fileExists = host.fileExists.bind(host);\n const getSourceFile = host.getSourceFile.bind(host);\n const hostReadFile = host.readFile.bind(host);\n const realpath = host.realpath?.bind(host);\n\n host.fileExists = (file) => sources.has(file) || fileExists(file);\n host.readFile = (file) => sources.get(file) ?? hostReadFile(file);\n host.directoryExists = (dir) => directories.has(dir) || (directoryExists?.(dir) ?? false);\n host.realpath = (file) => (sources.has(file) ? file : (realpath?.(file) ?? file));\n host.getSourceFile = (file, languageVersion, onError, shouldCreate) => {\n const source = sources.get(file);\n return source === undefined\n ? getSourceFile(file, languageVersion, onError, shouldCreate)\n : ts.createSourceFile(file, source, languageVersion, true);\n };\n\n const program = ts.createProgram([...sources.keys()], TYPE_CHECK_OPTIONS, host);\n const prefix = `${root}/`;\n\n return ts\n .getPreEmitDiagnostics(program)\n .filter((diagnostic) => !diagnostic.file || sources.has(diagnostic.file.fileName))\n .map((diagnostic) => {\n const name = diagnostic.file?.fileName.replace(prefix, '') ?? '';\n const message = ts.flattenDiagnosticMessageText(diagnostic.messageText, ' ');\n\n return `${name}: ${message}`;\n });\n}\n"],"version":3}
|
|
@@ -1,9 +0,0 @@
|
|
|
1
|
-
import ts from "@typescript/typescript6";
|
|
2
|
-
export default function createFullyUniqueIdentifier(name, flags) {
|
|
3
|
-
return ts.factory.createUniqueName(
|
|
4
|
-
name,
|
|
5
|
-
// eslint-disable-next-line no-bitwise
|
|
6
|
-
flags ?? ts.GeneratedIdentifierFlags.Optimistic & ts.GeneratedIdentifierFlags.FileLevel
|
|
7
|
-
);
|
|
8
|
-
}
|
|
9
|
-
//# sourceMappingURL=./createFullyUniqueIdentifier.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"mappings":"AAAA,OAAO,iCAAsF;AAE7F,eAAe,SAAS,4BAA4BA,MAAcC,OAA8C;AAC9G,QAAO,GAAG,QAAQ;EAChB;;EAEA,SAAS,GAAG,yBAAyB,aAAa,GAAG,yBAAyB;CAC/E;AACF","names":["name: string","flags?: GeneratedIdentifierFlags"],"sources":["/opt/agent/work/649c11185a3798db/packages/ts/generator-utils/src/createFullyUniqueIdentifier.ts"],"sourcesContent":["import ts, { type GeneratedIdentifierFlags, type Identifier } from '@typescript/typescript6';\n\nexport default function createFullyUniqueIdentifier(name: string, flags?: GeneratedIdentifierFlags): Identifier {\n return ts.factory.createUniqueName(\n name,\n // eslint-disable-next-line no-bitwise\n flags ?? ts.GeneratedIdentifierFlags.Optimistic & ts.GeneratedIdentifierFlags.FileLevel,\n );\n}\n"],"version":3}
|