@vaadin/hilla-generator-plugin-subtypes 25.3.0-beta1 → 25.3.0-beta2
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/ModelFixProcessor.d.ts +1 -1
- package/ModelFixProcessor.js +6 -4
- package/ModelFixProcessor.js.map +1 -1
- package/SubTypesProcessor.d.ts +2 -1
- package/SubTypesProcessor.js +9 -5
- package/SubTypesProcessor.js.map +1 -1
- package/TypeFixProcessor.d.ts +5 -1
- package/TypeFixProcessor.js +13 -7
- package/TypeFixProcessor.js.map +1 -1
- package/index.js +75 -22
- package/index.js.map +1 -1
- package/package.json +5 -5
- package/utils.d.ts +36 -1
- package/utils.js +59 -0
- package/utils.js.map +1 -1
package/ModelFixProcessor.d.ts
CHANGED
package/ModelFixProcessor.js
CHANGED
|
@@ -1,20 +1,22 @@
|
|
|
1
1
|
import ts from "@typescript/typescript6";
|
|
2
2
|
import createSourceFile from "@vaadin/hilla-generator-utils/createSourceFile.js";
|
|
3
|
-
import { propertyNameToString } from "./utils.js";
|
|
3
|
+
import { propertyNameToString, removeUnusedImports } from "./utils.js";
|
|
4
4
|
export class ModelFixProcessor {
|
|
5
5
|
#source;
|
|
6
|
-
|
|
6
|
+
#discriminatorPropertyName;
|
|
7
|
+
constructor(source, discriminatorPropertyName) {
|
|
7
8
|
this.#source = source;
|
|
9
|
+
this.#discriminatorPropertyName = discriminatorPropertyName;
|
|
8
10
|
}
|
|
9
11
|
process() {
|
|
10
12
|
const statements = this.#source.statements.map((statement) => {
|
|
11
13
|
if (ts.isClassDeclaration(statement)) {
|
|
12
|
-
const members = statement.members.filter((member) => !(ts.isGetAccessor(member) && propertyNameToString(member.name) ===
|
|
14
|
+
const members = statement.members.filter((member) => !(ts.isGetAccessor(member) && propertyNameToString(member.name) === this.#discriminatorPropertyName));
|
|
13
15
|
return ts.factory.createClassDeclaration(statement.modifiers, statement.name, statement.typeParameters, statement.heritageClauses, members);
|
|
14
16
|
}
|
|
15
17
|
return statement;
|
|
16
18
|
});
|
|
17
|
-
return createSourceFile(statements, this.#source.fileName);
|
|
19
|
+
return createSourceFile(removeUnusedImports(statements), this.#source.fileName);
|
|
18
20
|
}
|
|
19
21
|
}
|
|
20
22
|
//# sourceMappingURL=./ModelFixProcessor.js.map
|
package/ModelFixProcessor.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"mappings":"AAAA,OAAO,iCAAuD;AAC9D,OAAO,yEAA0E;AACjF,SAAS,
|
|
1
|
+
{"mappings":"AAAA,OAAO,iCAAuD;AAC9D,OAAO,yEAA0E;AACjF,SAAS,sBAAsB,uCAAwC;AAEvE,OAAO,MAAM,kBAAkB;CAC7B,AAASA;CACT,AAASC;CAET,YAAYC,QAAoBC,2BAAmC;AACjE,OAAKH,UAAU;AACf,OAAKC,6BAA6B;CACnC;CAED,UAAsB;EACpB,MAAM,aAAa,KAAKD,QAAQ,WAAW,IAAI,CAAC,cAAc;AAE5D,OAAI,GAAG,mBAAmB,UAAU,EAAE;IACpC,MAAM,UAAU,UAAU,QAAQ,OAChC,CAAC,aACG,GAAG,cAAc,OAAO,IAAI,qBAAqB,OAAO,KAAK,KAAK,KAAKC,4BAC5E;AAED,WAAO,GAAG,QAAQ,uBAChB,UAAU,WACV,UAAU,MACV,UAAU,gBACV,UAAU,iBACV,QACD;GACF;AAED,UAAO;EACR,EAAC;AAEF,SAAO,iBAAiB,oBAAoB,WAAW,EAAE,KAAKD,QAAQ,SAAS;CAChF;AACF","names":["#source","#discriminatorPropertyName","source: SourceFile","discriminatorPropertyName: string"],"sources":["/opt/agent/work/649c11185a3798db/packages/ts/generator-plugin-subtypes/src/ModelFixProcessor.ts"],"sourcesContent":["import ts, { type SourceFile } from '@typescript/typescript6';\nimport createSourceFile from '@vaadin/hilla-generator-utils/createSourceFile.js';\nimport { propertyNameToString, removeUnusedImports } from './utils.js';\n\nexport class ModelFixProcessor {\n readonly #source: SourceFile;\n readonly #discriminatorPropertyName: string;\n\n constructor(source: SourceFile, discriminatorPropertyName: string) {\n this.#source = source;\n this.#discriminatorPropertyName = discriminatorPropertyName;\n }\n\n process(): SourceFile {\n const statements = this.#source.statements.map((statement) => {\n // filter out the discriminator property from all models\n if (ts.isClassDeclaration(statement)) {\n const members = statement.members.filter(\n (member) =>\n !(ts.isGetAccessor(member) && propertyNameToString(member.name) === this.#discriminatorPropertyName),\n );\n\n return ts.factory.createClassDeclaration(\n statement.modifiers,\n statement.name,\n statement.typeParameters,\n statement.heritageClauses,\n members,\n );\n }\n\n return statement;\n });\n\n return createSourceFile(removeUnusedImports(statements), this.#source.fileName);\n }\n}\n"],"version":3}
|
package/SubTypesProcessor.d.ts
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import { type SourceFile } from "@typescript/typescript6";
|
|
2
2
|
import { type ReferenceSchema } from "@vaadin/hilla-generator-core/Schema.js";
|
|
3
|
+
import type { Discriminator } from "./utils.js";
|
|
3
4
|
export declare class SubTypesProcessor {
|
|
4
5
|
#private;
|
|
5
|
-
constructor(typeName: string, source: SourceFile, oneOf: readonly ReferenceSchema[]);
|
|
6
|
+
constructor(typeName: string, source: SourceFile, oneOf: readonly ReferenceSchema[], discriminator: Discriminator);
|
|
6
7
|
process(): SourceFile;
|
|
7
8
|
}
|
package/SubTypesProcessor.js
CHANGED
|
@@ -4,15 +4,18 @@ import { convertReferenceSchemaToPath, convertReferenceSchemaToSpecifier, simpli
|
|
|
4
4
|
import createSourceFile from "@vaadin/hilla-generator-utils/createSourceFile.js";
|
|
5
5
|
import DependencyManager from "@vaadin/hilla-generator-utils/dependencies/DependencyManager.js";
|
|
6
6
|
import PathManager from "@vaadin/hilla-generator-utils/dependencies/PathManager.js";
|
|
7
|
+
import { createDiscriminatedType, schemaKey } from "./utils.js";
|
|
7
8
|
export class SubTypesProcessor {
|
|
8
9
|
#typeName;
|
|
9
10
|
#source;
|
|
10
11
|
#oneOf;
|
|
12
|
+
#discriminator;
|
|
11
13
|
#dependencies;
|
|
12
|
-
constructor(typeName, source, oneOf) {
|
|
14
|
+
constructor(typeName, source, oneOf, discriminator) {
|
|
13
15
|
this.#typeName = typeName;
|
|
14
16
|
this.#source = source;
|
|
15
17
|
this.#oneOf = oneOf;
|
|
18
|
+
this.#discriminator = discriminator;
|
|
16
19
|
this.#dependencies = new DependencyManager(new PathManager({
|
|
17
20
|
extension: ".js",
|
|
18
21
|
relativeTo: dirname(source.fileName)
|
|
@@ -23,17 +26,18 @@ export class SubTypesProcessor {
|
|
|
23
26
|
const subTypes = this.#oneOf.map((schema) => {
|
|
24
27
|
const path = paths.createRelativePath(convertReferenceSchemaToPath(schema));
|
|
25
28
|
const subType = convertReferenceSchemaToSpecifier(schema);
|
|
26
|
-
|
|
29
|
+
const type = ts.factory.createTypeReferenceNode(imports.default.add(path, subType, true));
|
|
30
|
+
const typeValues = this.#discriminator.values.get(schemaKey(schema)) ?? [];
|
|
31
|
+
return typeValues.length > 1 ? createDiscriminatedType(type, this.#discriminator.propertyName, typeValues[0]) : type;
|
|
27
32
|
});
|
|
28
|
-
const union = ts.factory.createUnionTypeNode(subTypes
|
|
29
|
-
const { fileName
|
|
33
|
+
const union = ts.factory.createUnionTypeNode(subTypes);
|
|
34
|
+
const { fileName } = this.#source;
|
|
30
35
|
const unionTypeName = `${simplifyFullyQualifiedName(this.#typeName)}`;
|
|
31
36
|
const unionIdentifier = ts.factory.createIdentifier(unionTypeName);
|
|
32
37
|
const statement = ts.factory.createTypeAliasDeclaration(undefined, unionIdentifier, undefined, union);
|
|
33
38
|
exports.default.set(unionTypeName);
|
|
34
39
|
return createSourceFile([
|
|
35
40
|
...imports.toCode(),
|
|
36
|
-
...statements,
|
|
37
41
|
statement,
|
|
38
42
|
...exports.toCode()
|
|
39
43
|
], fileName);
|
package/SubTypesProcessor.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"mappings":"AAAA,SAAS,2BAA4B;AACrC,OAAO,
|
|
1
|
+
{"mappings":"AAAA,SAAS,2BAA4B;AACrC,OAAO,iCAAsE;AAC7E,SACE,8BACA,mCAEA,0EAC8C;AAChD,OAAO,yEAA0E;AACjF,OAAO,wFAAyF;AAChG,OAAO,4EAA6E;AAEpF,SAAS,yBAAyB,6BAA8B;AAEhE,OAAO,MAAM,kBAAkB;CAC7B,AAASA;CACT,AAASC;CACT,AAASC;CACT,AAASC;CACT,AAASC;CAET,YAAYC,UAAkBC,QAAoBC,OAAmCC,eAA8B;AACjH,OAAKR,YAAY;AACjB,OAAKC,UAAU;AACf,OAAKC,SAAS;AACd,OAAKC,iBAAiB;AACtB,OAAKC,gBAAgB,IAAI,kBACvB,IAAI,YAAY;GAAE,WAAW;GAAO,YAAY,QAAQ,OAAO,SAAS;EAAE;CAE7E;CAED,UAAsB;EACpB,MAAM,EAAE,SAAS,SAAS,OAAO,GAAG,KAAKA;EAGzC,MAAM,WAAW,KAAKF,OAAO,IAAI,CAAC,WAAqB;GACrD,MAAM,OAAO,MAAM,mBAAmB,6BAA6B,OAAO,CAAC;GAC3E,MAAM,UAAU,kCAAkC,OAAO;GACzD,MAAM,OAAO,GAAG,QAAQ,wBAAwB,QAAQ,QAAQ,IAAI,MAAM,SAAS,KAAK,CAAC;GAIzF,MAAM,aAAa,KAAKC,eAAe,OAAO,IAAI,UAAU,OAAO,CAAC,IAAI,CAAE;AAE1E,UAAO,WAAW,SAAS,IACvB,wBAAwB,MAAM,KAAKA,eAAe,cAAc,WAAW,GAAG,GAC9E;EACL,EAAC;EAGF,MAAM,QAAQ,GAAG,QAAQ,oBAAoB,SAAS;EAItD,MAAM,EAAE,UAAU,GAAG,KAAKF;EAC1B,MAAM,iBAAiB,EAAE,2BAA2B,KAAKD,UAAU,CAAC;EACpE,MAAM,kBAAkB,GAAG,QAAQ,iBAAiB,cAAc;EAClE,MAAM,YAAY,GAAG,QAAQ,2BAA2B,WAAW,iBAAiB,WAAW,MAAM;AAErG,UAAQ,QAAQ,IAAI,cAAc;AAElC,SAAO,iBAAiB;GAAC,GAAG,QAAQ,QAAQ;GAAE;GAAW,GAAG,QAAQ,QAAQ;EAAC,GAAE,SAAS;CACzF;AACF","names":["#typeName","#source","#oneOf","#discriminator","#dependencies","typeName: string","source: SourceFile","oneOf: readonly ReferenceSchema[]","discriminator: Discriminator"],"sources":["/opt/agent/work/649c11185a3798db/packages/ts/generator-plugin-subtypes/src/SubTypesProcessor.ts"],"sourcesContent":["import { dirname } from 'path/posix';\nimport ts, { type SourceFile, type TypeNode } from '@typescript/typescript6';\nimport {\n convertReferenceSchemaToPath,\n convertReferenceSchemaToSpecifier,\n type ReferenceSchema,\n simplifyFullyQualifiedName,\n} from '@vaadin/hilla-generator-core/Schema.js';\nimport createSourceFile from '@vaadin/hilla-generator-utils/createSourceFile.js';\nimport DependencyManager from '@vaadin/hilla-generator-utils/dependencies/DependencyManager.js';\nimport PathManager from '@vaadin/hilla-generator-utils/dependencies/PathManager.js';\nimport type { Discriminator } from './utils.js';\nimport { createDiscriminatedType, schemaKey } from './utils.js';\n\nexport class SubTypesProcessor {\n readonly #typeName: string;\n readonly #source: SourceFile;\n readonly #oneOf: readonly ReferenceSchema[];\n readonly #discriminator: Discriminator;\n readonly #dependencies: DependencyManager;\n\n constructor(typeName: string, source: SourceFile, oneOf: readonly ReferenceSchema[], discriminator: Discriminator) {\n this.#typeName = typeName;\n this.#source = source;\n this.#oneOf = oneOf;\n this.#discriminator = discriminator;\n this.#dependencies = new DependencyManager(\n new PathManager({ extension: '.js', relativeTo: dirname(source.fileName) }),\n );\n }\n\n process(): SourceFile {\n const { exports, imports, paths } = this.#dependencies;\n\n // import all subtypes and return them\n const subTypes = this.#oneOf.map((schema): TypeNode => {\n const path = paths.createRelativePath(convertReferenceSchemaToPath(schema));\n const subType = convertReferenceSchemaToSpecifier(schema);\n const type = ts.factory.createTypeReferenceNode(imports.default.add(path, subType, true));\n\n // a subtype that is the supertype of another subtype accepts the values\n // of the subtypes below it too, so its own value has to be pinned here\n const typeValues = this.#discriminator.values.get(schemaKey(schema)) ?? [];\n\n return typeValues.length > 1\n ? createDiscriminatedType(type, this.#discriminator.propertyName, typeValues[0])\n : type;\n });\n\n // create a union type from the subtypes\n const union = ts.factory.createUnionTypeNode(subTypes);\n\n // create the statement: the source is fully replaced, as whatever the\n // backbone plugin made of a schema that only has a `oneOf` is of no use\n const { fileName } = this.#source;\n const unionTypeName = `${simplifyFullyQualifiedName(this.#typeName)}`;\n const unionIdentifier = ts.factory.createIdentifier(unionTypeName);\n const statement = ts.factory.createTypeAliasDeclaration(undefined, unionIdentifier, undefined, union);\n\n exports.default.set(unionTypeName);\n\n return createSourceFile([...imports.toCode(), statement, ...exports.toCode()], fileName);\n }\n}\n"],"version":3}
|
package/TypeFixProcessor.d.ts
CHANGED
|
@@ -1,6 +1,10 @@
|
|
|
1
1
|
import ts, { type SourceFile } from "@typescript/typescript6";
|
|
2
2
|
export declare class TypeFixProcessor {
|
|
3
3
|
#private;
|
|
4
|
-
|
|
4
|
+
/**
|
|
5
|
+
* @param typeValues - the discriminator values the type accepts: its own
|
|
6
|
+
* value, followed by the values of the subtypes below it, if any.
|
|
7
|
+
*/
|
|
8
|
+
constructor(source: ts.SourceFile, discriminatorPropertyName: string, typeValues: readonly string[]);
|
|
5
9
|
process(): SourceFile;
|
|
6
10
|
}
|
package/TypeFixProcessor.js
CHANGED
|
@@ -1,21 +1,27 @@
|
|
|
1
1
|
import ts from "@typescript/typescript6";
|
|
2
2
|
import createSourceFile from "@vaadin/hilla-generator-utils/createSourceFile.js";
|
|
3
|
-
import { propertyNameToString } from "./utils.js";
|
|
3
|
+
import { createDiscriminatorProperty, propertyNameToString } from "./utils.js";
|
|
4
4
|
export class TypeFixProcessor {
|
|
5
5
|
#source;
|
|
6
|
-
#
|
|
7
|
-
|
|
6
|
+
#discriminatorPropertyName;
|
|
7
|
+
#typeValues;
|
|
8
|
+
/**
|
|
9
|
+
* @param typeValues - the discriminator values the type accepts: its own
|
|
10
|
+
* value, followed by the values of the subtypes below it, if any.
|
|
11
|
+
*/
|
|
12
|
+
constructor(source, discriminatorPropertyName, typeValues) {
|
|
8
13
|
this.#source = source;
|
|
9
|
-
this.#
|
|
14
|
+
this.#discriminatorPropertyName = discriminatorPropertyName;
|
|
15
|
+
this.#typeValues = typeValues;
|
|
10
16
|
}
|
|
11
17
|
process() {
|
|
12
18
|
const statements = this.#source.statements.map((statement) => {
|
|
13
19
|
if (ts.isInterfaceDeclaration(statement)) {
|
|
14
20
|
const members = statement.members.map((member) => {
|
|
15
|
-
if (ts.isPropertySignature(member)
|
|
16
|
-
return
|
|
21
|
+
if (!ts.isPropertySignature(member) || propertyNameToString(member.name) !== this.#discriminatorPropertyName) {
|
|
22
|
+
return member;
|
|
17
23
|
}
|
|
18
|
-
return
|
|
24
|
+
return createDiscriminatorProperty(this.#discriminatorPropertyName, this.#typeValues);
|
|
19
25
|
});
|
|
20
26
|
return ts.factory.createInterfaceDeclaration(statement.modifiers, statement.name, statement.typeParameters, statement.heritageClauses, members);
|
|
21
27
|
}
|
package/TypeFixProcessor.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"mappings":"AAAA,OAAO,iCAAuD;AAC9D,OAAO,yEAA0E;AACjF,SAAS,wCAAyC;
|
|
1
|
+
{"mappings":"AAAA,OAAO,iCAAuD;AAC9D,OAAO,yEAA0E;AACjF,SAAS,6BAA6B,wCAAyC;AAE/E,OAAO,MAAM,iBAAiB;CAC5B,AAASA;CACT,AAASC;CACT,AAASC;;;;;CAMT,YAAYC,QAAuBC,2BAAmCC,YAA+B;AACnG,OAAKL,UAAU;AACf,OAAKC,6BAA6B;AAClC,OAAKC,cAAc;CACpB;CAED,UAAsB;EACpB,MAAM,aAAa,KAAKF,QAAQ,WAAW,IAAI,CAAC,cAAc;AAE5D,OAAI,GAAG,uBAAuB,UAAU,EAAE;IACxC,MAAM,UAAU,UAAU,QAAQ,IAAI,CAAC,WAAW;AAEhD,UACG,GAAG,oBAAoB,OAAO,IAC/B,qBAAqB,OAAO,KAAK,KAAK,KAAKC,4BAC3C;AACA,aAAO;KACR;AAED,YAAO,4BAA4B,KAAKA,4BAA4B,KAAKC,YAAY;IACtF,EAAC;AAEF,WAAO,GAAG,QAAQ,2BAChB,UAAU,WACV,UAAU,MACV,UAAU,gBACV,UAAU,iBACV,QACD;GACF;AAED,UAAO;EACR,EAAC;AAEF,SAAO,iBAAiB,YAAY,KAAKF,QAAQ,SAAS;CAC3D;AACF","names":["#source","#discriminatorPropertyName","#typeValues","source: ts.SourceFile","discriminatorPropertyName: string","typeValues: readonly string[]"],"sources":["/opt/agent/work/649c11185a3798db/packages/ts/generator-plugin-subtypes/src/TypeFixProcessor.ts"],"sourcesContent":["import ts, { type SourceFile } from '@typescript/typescript6';\nimport createSourceFile from '@vaadin/hilla-generator-utils/createSourceFile.js';\nimport { createDiscriminatorProperty, propertyNameToString } from './utils.js';\n\nexport class TypeFixProcessor {\n readonly #source: SourceFile;\n readonly #discriminatorPropertyName: string;\n readonly #typeValues: readonly string[];\n\n /**\n * @param typeValues - the discriminator values the type accepts: its own\n * value, followed by the values of the subtypes below it, if any.\n */\n constructor(source: ts.SourceFile, discriminatorPropertyName: string, typeValues: readonly string[]) {\n this.#source = source;\n this.#discriminatorPropertyName = discriminatorPropertyName;\n this.#typeValues = typeValues;\n }\n\n process(): SourceFile {\n const statements = this.#source.statements.map((statement) => {\n // search in the interface definition\n if (ts.isInterfaceDeclaration(statement)) {\n const members = statement.members.map((member) => {\n // search for the discriminator property\n if (\n !ts.isPropertySignature(member) ||\n propertyNameToString(member.name) !== this.#discriminatorPropertyName\n ) {\n return member;\n }\n\n return createDiscriminatorProperty(this.#discriminatorPropertyName, this.#typeValues);\n });\n\n return ts.factory.createInterfaceDeclaration(\n statement.modifiers,\n statement.name,\n statement.typeParameters,\n statement.heritageClauses,\n members,\n );\n }\n\n return statement;\n });\n\n return createSourceFile(statements, this.#source.fileName);\n }\n}\n"],"version":3}
|
package/index.js
CHANGED
|
@@ -3,6 +3,72 @@ import { isReferenceSchema, convertFullyQualifiedNameToRelativePath } from "@vaa
|
|
|
3
3
|
import { ModelFixProcessor } from "./ModelFixProcessor.js";
|
|
4
4
|
import { SubTypesProcessor } from "./SubTypesProcessor.js";
|
|
5
5
|
import { TypeFixProcessor } from "./TypeFixProcessor.js";
|
|
6
|
+
import { schemaKey } from "./utils.js";
|
|
7
|
+
const DEFAULT_DISCRIMINATOR = "@type";
|
|
8
|
+
/**
|
|
9
|
+
* Returns the schemas that hold the properties declared by the type itself: a
|
|
10
|
+
* subtype is a composed schema whose `anyOf` list contains a reference to the
|
|
11
|
+
* supertype and an object schema with the own properties, while a type that has
|
|
12
|
+
* no supertype is a plain object schema.
|
|
13
|
+
*/
|
|
14
|
+
function ownSchemas(component) {
|
|
15
|
+
if (isReferenceSchema(component)) {
|
|
16
|
+
return [];
|
|
17
|
+
}
|
|
18
|
+
return component.anyOf ? component.anyOf.filter((schema) => "properties" in schema) : [component];
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* Returns the values that the discriminator property of the type accepts: the
|
|
22
|
+
* Java parser stores them as the `enum` of that property, the own value of the
|
|
23
|
+
* type first, followed by the values of the subtypes below it, which narrow the
|
|
24
|
+
* discriminator further. The property is left alone when the values are
|
|
25
|
+
* missing, as pinning a type to its own value alone would clash with the
|
|
26
|
+
* subtypes below it.
|
|
27
|
+
*/
|
|
28
|
+
function discriminatorValues(component, discriminatorPropertyName) {
|
|
29
|
+
if (!component) {
|
|
30
|
+
return undefined;
|
|
31
|
+
}
|
|
32
|
+
for (const schema of ownSchemas(component)) {
|
|
33
|
+
const property = schema.properties?.[discriminatorPropertyName];
|
|
34
|
+
if (property && "enum" in property && property.enum?.length) {
|
|
35
|
+
return property.enum.filter((value) => typeof value === "string");
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
return undefined;
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* Returns the values that the discriminator accepts in each subtype, mapped by
|
|
42
|
+
* schema name. A subtype that is also the supertype of another subtype accepts
|
|
43
|
+
* more than one value: pinning it to its own value would leave the subtype
|
|
44
|
+
* unable to extend it, or to be used as the type parameter of its model, so the
|
|
45
|
+
* union type pins the own value of such a subtype instead.
|
|
46
|
+
*/
|
|
47
|
+
function findDiscriminatorValues(components, keys, discriminatorPropertyName) {
|
|
48
|
+
const values = new Map();
|
|
49
|
+
keys.forEach((key) => {
|
|
50
|
+
const subTypeValues = discriminatorValues(components[key], discriminatorPropertyName);
|
|
51
|
+
if (subTypeValues?.length) {
|
|
52
|
+
values.set(key, subTypeValues);
|
|
53
|
+
}
|
|
54
|
+
});
|
|
55
|
+
return values;
|
|
56
|
+
}
|
|
57
|
+
function fixSubType(sources, subKey, discriminator) {
|
|
58
|
+
const { propertyName: discriminatorPropertyName, values } = discriminator;
|
|
59
|
+
const typeValues = values.get(subKey);
|
|
60
|
+
if (!typeValues) {
|
|
61
|
+
return;
|
|
62
|
+
}
|
|
63
|
+
const subFn = `${convertFullyQualifiedNameToRelativePath(subKey)}.ts`;
|
|
64
|
+
const subSource = sources.find(({ fileName }) => fileName === subFn);
|
|
65
|
+
const fixedSource = new TypeFixProcessor(subSource, discriminatorPropertyName, typeValues).process();
|
|
66
|
+
sources.splice(sources.indexOf(subSource), 1, fixedSource);
|
|
67
|
+
const modelFn = `${convertFullyQualifiedNameToRelativePath(subKey)}Model.ts`;
|
|
68
|
+
const modelSource = sources.find(({ fileName }) => fileName === modelFn);
|
|
69
|
+
const fixedModelSource = new ModelFixProcessor(modelSource, discriminatorPropertyName).process();
|
|
70
|
+
sources.splice(sources.indexOf(modelSource), 1, fixedModelSource);
|
|
71
|
+
}
|
|
6
72
|
export default class SubTypesPlugin extends Plugin {
|
|
7
73
|
get path() {
|
|
8
74
|
return import.meta.url;
|
|
@@ -15,31 +81,18 @@ export default class SubTypesPlugin extends Plugin {
|
|
|
15
81
|
}
|
|
16
82
|
Object.entries(components).forEach(([baseKey, baseComponent]) => {
|
|
17
83
|
if ("oneOf" in baseComponent && Array.isArray(baseComponent.oneOf) && baseComponent.oneOf.every((schema) => isReferenceSchema(schema))) {
|
|
84
|
+
const discriminatorPropertyName = baseComponent.discriminator?.propertyName ?? DEFAULT_DISCRIMINATOR;
|
|
85
|
+
const subKeys = baseComponent.oneOf.map(schemaKey);
|
|
86
|
+
const discriminator = {
|
|
87
|
+
propertyName: discriminatorPropertyName,
|
|
88
|
+
values: findDiscriminatorValues(components, subKeys, discriminatorPropertyName)
|
|
89
|
+
};
|
|
18
90
|
const fn = `${convertFullyQualifiedNameToRelativePath(baseKey)}.ts`;
|
|
19
91
|
const source = sources.find(({ fileName }) => fileName === fn);
|
|
20
|
-
const newSource = new SubTypesProcessor(baseKey, source, baseComponent.oneOf).process();
|
|
92
|
+
const newSource = new SubTypesProcessor(baseKey, source, baseComponent.oneOf, discriminator).process();
|
|
21
93
|
sources.splice(sources.indexOf(source), 1, newSource);
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
const path = schema.$ref;
|
|
25
|
-
Object.entries(components).forEach(([subKey, subComponent]) => {
|
|
26
|
-
if ("anyOf" in subComponent && subKey === path.substring("#/components/schemas/".length)) {
|
|
27
|
-
subComponent.anyOf?.forEach((s) => {
|
|
28
|
-
if ("properties" in s && "@type" in s.properties && "example" in s.properties["@type"]) {
|
|
29
|
-
const typeValue = s.properties["@type"].example;
|
|
30
|
-
const subFn = `${convertFullyQualifiedNameToRelativePath(subKey)}.ts`;
|
|
31
|
-
const subSource = sources.find(({ fileName }) => fileName === subFn);
|
|
32
|
-
const fixedSource = new TypeFixProcessor(subSource, typeValue).process();
|
|
33
|
-
sources.splice(sources.indexOf(subSource), 1, fixedSource);
|
|
34
|
-
const modelFn = `${convertFullyQualifiedNameToRelativePath(subKey)}Model.ts`;
|
|
35
|
-
const modelSource = sources.find(({ fileName }) => fileName === modelFn);
|
|
36
|
-
const fixedModelSource = new ModelFixProcessor(modelSource).process();
|
|
37
|
-
sources.splice(sources.indexOf(modelSource), 1, fixedModelSource);
|
|
38
|
-
}
|
|
39
|
-
});
|
|
40
|
-
}
|
|
41
|
-
});
|
|
42
|
-
}
|
|
94
|
+
subKeys.forEach((subKey) => {
|
|
95
|
+
fixSubType(sources, subKey, discriminator);
|
|
43
96
|
});
|
|
44
97
|
const unionFn = `${convertFullyQualifiedNameToRelativePath(baseKey)}Model.ts`;
|
|
45
98
|
const unionSource = sources.find(({ fileName }) => fileName === unionFn);
|
package/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"mappings":"
|
|
1
|
+
{"mappings":"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","names":["component: Schema","component: Schema | undefined","discriminatorPropertyName: string","components: Components","keys: readonly string[]","sources: SourceFile[]","subKey: string","discriminator: Discriminator","storage: SharedStorage"],"sources":["/opt/agent/work/649c11185a3798db/packages/ts/generator-plugin-subtypes/src/index.ts"],"sourcesContent":["import type { SourceFile } from '@typescript/typescript6';\nimport Plugin from '@vaadin/hilla-generator-core/Plugin.js';\nimport {\n isReferenceSchema,\n convertFullyQualifiedNameToRelativePath,\n type Schema,\n} from '@vaadin/hilla-generator-core/Schema.js';\nimport type { SharedStorage } from '@vaadin/hilla-generator-core/SharedStorage.js';\nimport type { OpenAPIV3 } from 'openapi-types';\nimport { ModelFixProcessor } from './ModelFixProcessor.js';\nimport { SubTypesProcessor } from './SubTypesProcessor.js';\nimport { TypeFixProcessor } from './TypeFixProcessor.js';\nimport type { Discriminator } from './utils.js';\nimport { schemaKey } from './utils.js';\n\n// the property name used by Jackson unless `@JsonTypeInfo` specifies another\n// one: kept as a fallback for OpenAPI documents without a `discriminator`\nconst DEFAULT_DISCRIMINATOR = '@type';\n\ntype Components = Readonly<Record<string, Schema>>;\n\n/**\n * Returns the schemas that hold the properties declared by the type itself: a\n * subtype is a composed schema whose `anyOf` list contains a reference to the\n * supertype and an object schema with the own properties, while a type that has\n * no supertype is a plain object schema.\n */\nfunction ownSchemas(component: Schema): readonly OpenAPIV3.SchemaObject[] {\n if (isReferenceSchema(component)) {\n return [];\n }\n\n return component.anyOf\n ? component.anyOf.filter((schema): schema is OpenAPIV3.SchemaObject => 'properties' in schema)\n : [component];\n}\n\n/**\n * Returns the values that the discriminator property of the type accepts: the\n * Java parser stores them as the `enum` of that property, the own value of the\n * type first, followed by the values of the subtypes below it, which narrow the\n * discriminator further. The property is left alone when the values are\n * missing, as pinning a type to its own value alone would clash with the\n * subtypes below it.\n */\nfunction discriminatorValues(\n component: Schema | undefined,\n discriminatorPropertyName: string,\n): readonly string[] | undefined {\n if (!component) {\n return undefined;\n }\n\n for (const schema of ownSchemas(component)) {\n const property = schema.properties?.[discriminatorPropertyName];\n\n if (property && 'enum' in property && property.enum?.length) {\n return property.enum.filter((value): value is string => typeof value === 'string');\n }\n }\n\n return undefined;\n}\n\n/**\n * Returns the values that the discriminator accepts in each subtype, mapped by\n * schema name. A subtype that is also the supertype of another subtype accepts\n * more than one value: pinning it to its own value would leave the subtype\n * unable to extend it, or to be used as the type parameter of its model, so the\n * union type pins the own value of such a subtype instead.\n */\nfunction findDiscriminatorValues(\n components: Components,\n keys: readonly string[],\n discriminatorPropertyName: string,\n): Map<string, readonly string[]> {\n const values = new Map<string, readonly string[]>();\n\n keys.forEach((key) => {\n const subTypeValues = discriminatorValues(components[key], discriminatorPropertyName);\n\n if (subTypeValues?.length) {\n values.set(key, subTypeValues);\n }\n });\n\n return values;\n}\n\nfunction fixSubType(sources: SourceFile[], subKey: string, discriminator: Discriminator): void {\n const { propertyName: discriminatorPropertyName, values } = discriminator;\n const typeValues = values.get(subKey);\n\n if (!typeValues) {\n return;\n }\n\n const subFn = `${convertFullyQualifiedNameToRelativePath(subKey)}.ts`;\n const subSource = sources.find(({ fileName }) => fileName === subFn)!;\n // fix the source to turn the discriminator property into the string literals\n // that the type accepts\n const fixedSource = new TypeFixProcessor(subSource, discriminatorPropertyName, typeValues).process();\n sources.splice(sources.indexOf(subSource), 1, fixedSource);\n\n // fix the model to remove the discriminator property\n const modelFn = `${convertFullyQualifiedNameToRelativePath(subKey)}Model.ts`;\n const modelSource = sources.find(({ fileName }) => fileName === modelFn)!;\n const fixedModelSource = new ModelFixProcessor(modelSource, discriminatorPropertyName).process();\n sources.splice(sources.indexOf(modelSource), 1, fixedModelSource);\n}\n\nexport default class SubTypesPlugin extends Plugin {\n declare ['constructor']: typeof SubTypesPlugin;\n\n override get path(): string {\n return import.meta.url;\n }\n\n // eslint-disable-next-line @typescript-eslint/require-await\n override async execute(storage: SharedStorage): Promise<void> {\n const { api, sources } = storage;\n\n const components = api.components?.schemas;\n\n if (!components) {\n return;\n }\n\n Object.entries(components).forEach(([baseKey, baseComponent]) => {\n // search for components with oneOf: those are union types\n if (\n 'oneOf' in baseComponent &&\n Array.isArray(baseComponent.oneOf) &&\n baseComponent.oneOf.every((schema) => isReferenceSchema(schema))\n ) {\n // `@JsonTypeInfo` may use any property name as the discriminator\n const discriminatorPropertyName = baseComponent.discriminator?.propertyName ?? DEFAULT_DISCRIMINATOR;\n const subKeys = baseComponent.oneOf.map(schemaKey);\n const discriminator = {\n propertyName: discriminatorPropertyName,\n values: findDiscriminatorValues(components, subKeys, discriminatorPropertyName),\n };\n\n const fn = `${convertFullyQualifiedNameToRelativePath(baseKey)}.ts`;\n const source = sources.find(({ fileName }) => fileName === fn)!;\n // replace the (empty) source with a newly-generated one\n const newSource = new SubTypesProcessor(baseKey, source, baseComponent.oneOf, discriminator).process();\n sources.splice(sources.indexOf(source), 1, newSource);\n\n // mentioned types in the oneOf need to be fixed as well\n subKeys.forEach((subKey) => {\n fixSubType(sources, subKey, discriminator);\n });\n\n // remove the union type model file\n const unionFn = `${convertFullyQualifiedNameToRelativePath(baseKey)}Model.ts`;\n const unionSource = sources.find(({ fileName }) => fileName === unionFn)!;\n sources.splice(sources.indexOf(unionSource), 1);\n }\n });\n }\n}\n"],"version":3}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@vaadin/hilla-generator-plugin-subtypes",
|
|
3
|
-
"version": "25.3.0-
|
|
3
|
+
"version": "25.3.0-beta2",
|
|
4
4
|
"description": "A Hilla TypeScript Generator plugin to support JsonSubTypes",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"type": "module",
|
|
@@ -50,10 +50,10 @@
|
|
|
50
50
|
},
|
|
51
51
|
"dependencies": {
|
|
52
52
|
"@typescript/typescript6": "6.0.2",
|
|
53
|
-
"@vaadin/hilla-generator-core": "25.3.0-
|
|
54
|
-
"@vaadin/hilla-generator-plugin-client": "25.3.0-
|
|
55
|
-
"@vaadin/hilla-generator-plugin-model": "25.3.0-
|
|
56
|
-
"@vaadin/hilla-generator-utils": "25.3.0-
|
|
53
|
+
"@vaadin/hilla-generator-core": "25.3.0-beta2",
|
|
54
|
+
"@vaadin/hilla-generator-plugin-client": "25.3.0-beta2",
|
|
55
|
+
"@vaadin/hilla-generator-plugin-model": "25.3.0-beta2",
|
|
56
|
+
"@vaadin/hilla-generator-utils": "25.3.0-beta2",
|
|
57
57
|
"fast-deep-equal": "^3.1.3",
|
|
58
58
|
"openapi-types": "^12.1.3"
|
|
59
59
|
}
|
package/utils.d.ts
CHANGED
|
@@ -1,2 +1,37 @@
|
|
|
1
|
-
import { type PropertyName } from "@typescript/typescript6";
|
|
1
|
+
import ts, { type PropertyName, type TypeNode } from "@typescript/typescript6";
|
|
2
|
+
import type { ReferenceSchema } from "@vaadin/hilla-generator-core/Schema.js";
|
|
3
|
+
/**
|
|
4
|
+
* The discriminator of a union type: the name of the property that holds it,
|
|
5
|
+
* and the values that the property accepts in each subtype, mapped by schema
|
|
6
|
+
* name. The own value of a subtype comes first, followed by the values of the
|
|
7
|
+
* subtypes below it.
|
|
8
|
+
*/
|
|
9
|
+
export type Discriminator = Readonly<{
|
|
10
|
+
propertyName: string
|
|
11
|
+
values: ReadonlyMap<string, readonly string[]>
|
|
12
|
+
}>;
|
|
2
13
|
export declare function propertyNameToString(node: PropertyName): string | null;
|
|
14
|
+
export declare function schemaKey(schema: ReferenceSchema): string;
|
|
15
|
+
/**
|
|
16
|
+
* Creates a property name node, quoting it when it is not a valid identifier:
|
|
17
|
+
* the default discriminator property of Jackson is `@type`.
|
|
18
|
+
*/
|
|
19
|
+
export declare function createPropertyName(name: string): PropertyName;
|
|
20
|
+
/**
|
|
21
|
+
* Creates the property signature that restricts the discriminator to the given
|
|
22
|
+
* values, e. g. `"@type": "add"`. A subtype that is also the supertype of
|
|
23
|
+
* another subtype accepts more than one value, as the discriminator is narrowed
|
|
24
|
+
* further down the hierarchy.
|
|
25
|
+
*/
|
|
26
|
+
export declare function createDiscriminatorProperty(propertyName: string, typeValues: readonly string[]): ts.PropertySignature;
|
|
27
|
+
/**
|
|
28
|
+
* Creates a type that pins the discriminator of a type accepting more than one
|
|
29
|
+
* value, e. g. `(BaseEvent & { "@type": "base" })`.
|
|
30
|
+
*/
|
|
31
|
+
export declare function createDiscriminatedType(type: TypeNode, propertyName: string, typeValue: string): TypeNode;
|
|
32
|
+
/**
|
|
33
|
+
* Removes the named imports that are not referenced anymore, which happens when
|
|
34
|
+
* the discriminator property was the only user of a model type. The default
|
|
35
|
+
* import of a generated model is its entity type, which is always used.
|
|
36
|
+
*/
|
|
37
|
+
export declare function removeUnusedImports(statements: readonly ts.Statement[]): readonly ts.Statement[];
|
package/utils.js
CHANGED
|
@@ -1,8 +1,67 @@
|
|
|
1
1
|
import ts from "@typescript/typescript6";
|
|
2
|
+
const SCHEMA_PREFIX = "#/components/schemas/";
|
|
3
|
+
const IDENTIFIER_PATTERN = /^[A-Za-z_$][A-Za-z0-9_$]*$/u;
|
|
2
4
|
export function propertyNameToString(node) {
|
|
3
5
|
if (ts.isIdentifier(node) || ts.isStringLiteral(node) || ts.isNumericLiteral(node)) {
|
|
4
6
|
return node.text;
|
|
5
7
|
}
|
|
6
8
|
return null;
|
|
7
9
|
}
|
|
10
|
+
export function schemaKey(schema) {
|
|
11
|
+
return schema.$ref.substring(SCHEMA_PREFIX.length);
|
|
12
|
+
}
|
|
13
|
+
/**
|
|
14
|
+
* Creates a property name node, quoting it when it is not a valid identifier:
|
|
15
|
+
* the default discriminator property of Jackson is `@type`.
|
|
16
|
+
*/
|
|
17
|
+
export function createPropertyName(name) {
|
|
18
|
+
return IDENTIFIER_PATTERN.test(name) ? ts.factory.createIdentifier(name) : ts.factory.createStringLiteral(name);
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* Creates the property signature that restricts the discriminator to the given
|
|
22
|
+
* values, e. g. `"@type": "add"`. A subtype that is also the supertype of
|
|
23
|
+
* another subtype accepts more than one value, as the discriminator is narrowed
|
|
24
|
+
* further down the hierarchy.
|
|
25
|
+
*/
|
|
26
|
+
export function createDiscriminatorProperty(propertyName, typeValues) {
|
|
27
|
+
const literals = typeValues.map((typeValue) => ts.factory.createLiteralTypeNode(ts.factory.createStringLiteral(typeValue)));
|
|
28
|
+
return ts.factory.createPropertySignature(undefined, createPropertyName(propertyName), undefined, literals.length === 1 ? literals[0] : ts.factory.createUnionTypeNode(literals));
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* Creates a type that pins the discriminator of a type accepting more than one
|
|
32
|
+
* value, e. g. `(BaseEvent & { "@type": "base" })`.
|
|
33
|
+
*/
|
|
34
|
+
export function createDiscriminatedType(type, propertyName, typeValue) {
|
|
35
|
+
return ts.factory.createParenthesizedType(ts.factory.createIntersectionTypeNode([type, ts.factory.createTypeLiteralNode([createDiscriminatorProperty(propertyName, [typeValue])])]));
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* Removes the named imports that are not referenced anymore, which happens when
|
|
39
|
+
* the discriminator property was the only user of a model type. The default
|
|
40
|
+
* import of a generated model is its entity type, which is always used.
|
|
41
|
+
*/
|
|
42
|
+
export function removeUnusedImports(statements) {
|
|
43
|
+
const used = new Set();
|
|
44
|
+
function collect(node) {
|
|
45
|
+
if (ts.isIdentifier(node)) {
|
|
46
|
+
used.add(node.text);
|
|
47
|
+
}
|
|
48
|
+
ts.forEachChild(node, collect);
|
|
49
|
+
}
|
|
50
|
+
statements.filter((statement) => !ts.isImportDeclaration(statement)).forEach(collect);
|
|
51
|
+
return statements.map((statement) => {
|
|
52
|
+
if (!ts.isImportDeclaration(statement) || !statement.importClause) {
|
|
53
|
+
return statement;
|
|
54
|
+
}
|
|
55
|
+
const { importClause } = statement;
|
|
56
|
+
const bindings = importClause.namedBindings;
|
|
57
|
+
if (!bindings || !ts.isNamedImports(bindings)) {
|
|
58
|
+
return statement;
|
|
59
|
+
}
|
|
60
|
+
const elements = bindings.elements.filter((element) => used.has(element.name.text));
|
|
61
|
+
if (elements.length === bindings.elements.length) {
|
|
62
|
+
return statement;
|
|
63
|
+
}
|
|
64
|
+
return ts.factory.updateImportDeclaration(statement, statement.modifiers, ts.factory.updateImportClause(importClause, importClause.phaseModifier, importClause.name, ts.factory.updateNamedImports(bindings, elements)), statement.moduleSpecifier, statement.attributes);
|
|
65
|
+
});
|
|
66
|
+
}
|
|
8
67
|
//# sourceMappingURL=./utils.js.map
|
package/utils.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"mappings":"AAAA,OAAO,
|
|
1
|
+
{"mappings":"AAAA,OAAO,iCAAwE;AAG/E,MAAM,gBAAgB;AAYtB,MAAM,qBAAqB;AAE3B,OAAO,SAAS,qBAAqBA,MAAmC;AACtE,KAAI,GAAG,aAAa,KAAK,IAAI,GAAG,gBAAgB,KAAK,IAAI,GAAG,iBAAiB,KAAK,EAAE;AAClF,SAAO,KAAK;CACb;AACD,QAAO;AACR;AAED,OAAO,SAAS,UAAUC,QAAiC;AACzD,QAAO,OAAO,KAAK,UAAU,cAAc,OAAO;AACnD;;;;;AAMD,OAAO,SAAS,mBAAmBC,MAA4B;AAC7D,QAAO,mBAAmB,KAAK,KAAK,GAAG,GAAG,QAAQ,iBAAiB,KAAK,GAAG,GAAG,QAAQ,oBAAoB,KAAK;AAChH;;;;;;;AAQD,OAAO,SAAS,4BAA4BC,cAAsBC,YAAqD;CACrH,MAAM,WAAW,WAAW,IAAI,CAAC,cAC/B,GAAG,QAAQ,sBAAsB,GAAG,QAAQ,oBAAoB,UAAU,CAAC,CAC5E;AAED,QAAO,GAAG,QAAQ,wBAChB,WACA,mBAAmB,aAAa,EAChC,WACA,SAAS,WAAW,IAAI,SAAS,KAAK,GAAG,QAAQ,oBAAoB,SAAS,CAC/E;AACF;;;;;AAMD,OAAO,SAAS,wBAAwBC,MAAgBF,cAAsBG,WAA6B;AACzG,QAAO,GAAG,QAAQ,wBAChB,GAAG,QAAQ,2BAA2B,CACpC,MACA,GAAG,QAAQ,sBAAsB,CAAC,4BAA4B,cAAc,CAAC,SAAU,EAAC,AAAC,EAAC,AAC3F,EAAC,CACH;AACF;;;;;;AAOD,OAAO,SAAS,oBAAoBC,YAA8D;CAChG,MAAM,OAAO,IAAI;CAEjB,SAAS,QAAQC,MAAqB;AACpC,MAAI,GAAG,aAAa,KAAK,EAAE;AACzB,QAAK,IAAI,KAAK,KAAK;EACpB;AAED,KAAG,aAAa,MAAM,QAAQ;CAC/B;AAED,YAAW,OAAO,CAAC,eAAe,GAAG,oBAAoB,UAAU,CAAC,CAAC,QAAQ,QAAQ;AAErF,QAAO,WAAW,IAAI,CAAC,cAAc;AACnC,OAAK,GAAG,oBAAoB,UAAU,KAAK,UAAU,cAAc;AACjE,UAAO;EACR;EAED,MAAM,EAAE,cAAc,GAAG;EACzB,MAAM,WAAW,aAAa;AAE9B,OAAK,aAAa,GAAG,eAAe,SAAS,EAAE;AAC7C,UAAO;EACR;EAED,MAAM,WAAW,SAAS,SAAS,OAAO,CAAC,YAAY,KAAK,IAAI,QAAQ,KAAK,KAAK,CAAC;AAEnF,MAAI,SAAS,WAAW,SAAS,SAAS,QAAQ;AAChD,UAAO;EACR;AAED,SAAO,GAAG,QAAQ,wBAChB,WACA,UAAU,WACV,GAAG,QAAQ,mBACT,cACA,aAAa,eACb,aAAa,MACb,GAAG,QAAQ,mBAAmB,UAAU,SAAS,CAClD,EACD,UAAU,iBACV,UAAU,WACX;CACF,EAAC;AACH","names":["node: PropertyName","schema: ReferenceSchema","name: string","propertyName: string","typeValues: readonly string[]","type: TypeNode","typeValue: string","statements: readonly ts.Statement[]","node: ts.Node"],"sources":["/opt/agent/work/649c11185a3798db/packages/ts/generator-plugin-subtypes/src/utils.ts"],"sourcesContent":["import ts, { type PropertyName, type TypeNode } from '@typescript/typescript6';\nimport type { ReferenceSchema } from '@vaadin/hilla-generator-core/Schema.js';\n\nconst SCHEMA_PREFIX = '#/components/schemas/';\n\n/**\n * The discriminator of a union type: the name of the property that holds it,\n * and the values that the property accepts in each subtype, mapped by schema\n * name. The own value of a subtype comes first, followed by the values of the\n * subtypes below it.\n */\nexport type Discriminator = Readonly<{\n propertyName: string;\n values: ReadonlyMap<string, readonly string[]>;\n}>;\nconst IDENTIFIER_PATTERN = /^[A-Za-z_$][A-Za-z0-9_$]*$/u;\n\nexport function propertyNameToString(node: PropertyName): string | null {\n if (ts.isIdentifier(node) || ts.isStringLiteral(node) || ts.isNumericLiteral(node)) {\n return node.text;\n }\n return null;\n}\n\nexport function schemaKey(schema: ReferenceSchema): string {\n return schema.$ref.substring(SCHEMA_PREFIX.length);\n}\n\n/**\n * Creates a property name node, quoting it when it is not a valid identifier:\n * the default discriminator property of Jackson is `@type`.\n */\nexport function createPropertyName(name: string): PropertyName {\n return IDENTIFIER_PATTERN.test(name) ? ts.factory.createIdentifier(name) : ts.factory.createStringLiteral(name);\n}\n\n/**\n * Creates the property signature that restricts the discriminator to the given\n * values, e. g. `\"@type\": \"add\"`. A subtype that is also the supertype of\n * another subtype accepts more than one value, as the discriminator is narrowed\n * further down the hierarchy.\n */\nexport function createDiscriminatorProperty(propertyName: string, typeValues: readonly string[]): ts.PropertySignature {\n const literals = typeValues.map((typeValue) =>\n ts.factory.createLiteralTypeNode(ts.factory.createStringLiteral(typeValue)),\n );\n\n return ts.factory.createPropertySignature(\n undefined,\n createPropertyName(propertyName),\n undefined,\n literals.length === 1 ? literals[0] : ts.factory.createUnionTypeNode(literals),\n );\n}\n\n/**\n * Creates a type that pins the discriminator of a type accepting more than one\n * value, e. g. `(BaseEvent & { \"@type\": \"base\" })`.\n */\nexport function createDiscriminatedType(type: TypeNode, propertyName: string, typeValue: string): TypeNode {\n return ts.factory.createParenthesizedType(\n ts.factory.createIntersectionTypeNode([\n type,\n ts.factory.createTypeLiteralNode([createDiscriminatorProperty(propertyName, [typeValue])]),\n ]),\n );\n}\n\n/**\n * Removes the named imports that are not referenced anymore, which happens when\n * the discriminator property was the only user of a model type. The default\n * import of a generated model is its entity type, which is always used.\n */\nexport function removeUnusedImports(statements: readonly ts.Statement[]): readonly ts.Statement[] {\n const used = new Set<string>();\n\n function collect(node: ts.Node): void {\n if (ts.isIdentifier(node)) {\n used.add(node.text);\n }\n\n ts.forEachChild(node, collect);\n }\n\n statements.filter((statement) => !ts.isImportDeclaration(statement)).forEach(collect);\n\n return statements.map((statement) => {\n if (!ts.isImportDeclaration(statement) || !statement.importClause) {\n return statement;\n }\n\n const { importClause } = statement;\n const bindings = importClause.namedBindings;\n\n if (!bindings || !ts.isNamedImports(bindings)) {\n return statement;\n }\n\n const elements = bindings.elements.filter((element) => used.has(element.name.text));\n\n if (elements.length === bindings.elements.length) {\n return statement;\n }\n\n return ts.factory.updateImportDeclaration(\n statement,\n statement.modifiers,\n ts.factory.updateImportClause(\n importClause,\n importClause.phaseModifier,\n importClause.name,\n ts.factory.updateNamedImports(bindings, elements),\n ),\n statement.moduleSpecifier,\n statement.attributes,\n );\n });\n}\n"],"version":3}
|