Huge News!Announcing our $40M Series B led by Abstract Ventures.Learn More
Socket
Sign inDemoInstall
Socket

@ovotech/avro-ts

Package Overview
Dependencies
Maintainers
94
Versions
33
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@ovotech/avro-ts - npm Package Compare versions

Comparing version 2.0.0 to 3.0.0

6

dist/index.d.ts

@@ -6,6 +6,11 @@ import { Schema, schema } from 'avsc';

}
declare type UnionRegistry = {
[key: string]: string[];
};
export interface Context {
recordAlias: string;
namesAlias: string;
namespacedPrefix: string;
registry: Registry;
unionRegistry: UnionRegistry;
unionMember: boolean;

@@ -54,4 +59,5 @@ namespace?: string;

namespacedPrefix?: string;
namesAlias?: string;
};
export declare function avroTs(recordType: schema.RecordType, options?: AvroTsOptions): string;
export {};

77

dist/index.js

@@ -51,4 +51,12 @@ "use strict";

const namespaced = fullyQualifiedName(context, type);
const prop = ts.createPropertySignature(undefined, ts.createStringLiteral(namespaced), undefined, ts.createTypeReferenceNode(type.name, undefined), undefined);
const namespacedInterfaceType = ts.createInterfaceDeclaration(undefined, [ts.createToken(ts.SyntaxKind.ExportKeyword)], `${context.namespacedPrefix}${type.name}`, undefined, undefined, [prop]);
const currentNamespace = type.namespace || context.namespace;
const props = [
ts.createPropertySignature(undefined, ts.createStringLiteral(namespaced), undefined, ts.createTypeReferenceNode(type.name, undefined), undefined),
];
if (currentNamespace) {
props.push(...(context.unionRegistry[currentNamespace] || [])
.filter((name) => name !== type.name)
.map(name => ts.createPropertySignature(undefined, ts.createStringLiteral(`${currentNamespace}.${name}`), ts.createToken(ts.SyntaxKind.QuestionToken), ts.createKeywordTypeNode(ts.SyntaxKind.NeverKeyword), undefined)));
}
const namespacedInterfaceType = ts.createInterfaceDeclaration(undefined, [ts.createToken(ts.SyntaxKind.ExportKeyword)], `${context.namespacedPrefix}${type.name}`, undefined, undefined, props);
return exports.result(exports.withEntry(recordContext, namespacedInterfaceType), ts.createTypeReferenceNode(namespacedInterfaceType.name.text, undefined));

@@ -139,2 +147,4 @@ }

const isUnion = (type) => typeof type === 'object' && Array.isArray(type);
const isRecordParent = (type) => typeof type === 'object' && typeof type.type === 'object';
const isUnionParent = (type) => Array.isArray(type.type);
const isOptional = (type) => {

@@ -153,11 +163,6 @@ if (isUnion(type)) {

};
const printAstNode = (node, extras = {}) => {
const printAstNode = (nodes, { importLines }) => {
const resultFile = ts.createSourceFile('someFileName.ts', '', ts.ScriptTarget.Latest);
const printer = ts.createPrinter({ newLine: ts.NewLineKind.LineFeed });
const entries = Object.values(node.context.registry);
const fullSourceFile = ts.updateSourceFileNode(resultFile, entries);
const importLines = extras.importLines || [];
return importLines
.concat(printer.printNode(ts.EmitHint.Unspecified, node.type, fullSourceFile), entries.map(entry => printer.printNode(ts.EmitHint.Unspecified, entry, fullSourceFile)))
.join('\n\n');
return importLines.concat(nodes.map(n => printer.printNode(ts.EmitHint.Unspecified, n, resultFile))).join('\n\n');
};

@@ -167,10 +172,15 @@ const defaultOptions = {

namespacedPrefix: 'Namespaced',
namesAlias: 'Names',
};
function avroTs(recordType, options = {}) {
const logicalTypes = options.logicalTypes || {};
const isRootUnion = Array.isArray(recordType);
const context = {
...defaultOptions,
...options,
unionMember: Array.isArray(recordType),
recordAlias: options.recordAlias || defaultOptions.recordAlias,
namesAlias: options.namesAlias || defaultOptions.namesAlias,
namespacedPrefix: options.namespacedPrefix || defaultOptions.namespacedPrefix,
unionMember: isRootUnion,
registry: {},
unionRegistry: buildUnionRegistry({}, recordType, { namespace: recordType.namespace, unionMember: isRootUnion }),
namespaces: {},

@@ -186,12 +196,47 @@ visitedLogicalTypes: [],

};
const nodes = convertType(context, recordType);
const mainNode = convertType(context, recordType);
const importLines = context.visitedLogicalTypes
.map(visitedType => logicalTypes[visitedType].import)
.filter(Boolean);
return printAstNode({
context: nodes.context,
type: ts.createTypeAliasDeclaration(undefined, [ts.createToken(ts.SyntaxKind.ExportKeyword)], context.recordAlias, undefined, nodes.type),
}, { importLines });
const nodes = [
ts.createTypeAliasDeclaration(undefined, [ts.createToken(ts.SyntaxKind.ExportKeyword)], context.recordAlias, undefined, mainNode.type),
].concat(Object.values(mainNode.context.registry));
const namesNamespace = unionRegisryToNamespace(context.unionRegistry, context.namesAlias);
if (namesNamespace) {
nodes.unshift(namesNamespace);
}
return printAstNode(nodes, { importLines });
}
exports.avroTs = avroTs;
function unionRegisryToNamespace(registry, namespaceName) {
const names = Object.keys(registry).reduce((nodes, namespace) => nodes.concat(registry[namespace].map(name => ts.createVariableStatement([ts.createToken(ts.SyntaxKind.ExportKeyword)], ts.createVariableDeclarationList([ts.createVariableDeclaration(name, undefined, ts.createLiteral(`${namespace}.${name}`))], ts.NodeFlags.Const)))), []);
if (!names.length) {
return;
}
const nsNode = ts.createModuleDeclaration([], [ts.createToken(ts.SyntaxKind.ExportKeyword)], ts.createIdentifier(namespaceName), ts.createModuleBlock(names), ts.NodeFlags.Namespace);
return nsNode;
}
function buildUnionRegistry(registry, schema, context) {
if (Array.isArray(schema)) {
return schema.reduce((acc, schema) => buildUnionRegistry(acc, schema, context), registry);
}
if (isRecordParent(schema)) {
return buildUnionRegistry(registry, schema.type, { ...context, unionMember: isUnionParent(schema) });
}
if (isRecordType(schema)) {
const { name, fields } = schema;
const currentNamespace = schema.namespace || context.namespace;
if (currentNamespace && context.unionMember) {
(registry[currentNamespace] = registry[currentNamespace] || []).push(name);
}
fields.reduce((acc, field) =>
// @ts-ignore This works, but a bit too dynamic for TS
buildUnionRegistry(acc, field, {
...context,
unionMember: false,
namespace: schema.namespace || context.namespace,
}), registry);
}
return registry;
}
//# sourceMappingURL=index.js.map
{
"name": "@ovotech/avro-ts",
"description": "Convert avro schemas into typescript interfaces",
"version": "2.0.0",
"version": "3.0.0",
"main": "dist/index.js",

@@ -12,3 +12,3 @@ "source": "src/index.ts",

"test-js": "jest --runInBand",
"test-ts": "tsc test/integration.ts --strict --noEmit",
"test-ts": "tsc test/integration.ts --strict --noEmit && ! tsc test/integration-should-fail.ts --strict --noEmit",
"test": "yarn test-js && yarn test-ts",

@@ -37,3 +37,3 @@ "lint-prettier": "prettier --list-different {src,test}/**/*.ts",

},
"gitHead": "0b6a82e10b0cfd897d9d215d078cff399ad49fdf"
"gitHead": "2a3ad08e9fb4427a9bb5f384ac66095678b7e3fe"
}

@@ -30,2 +30,3 @@ # Avro TS

recordAlias: 'Record',
namesAlias: 'Names',
namespacedPrefix: 'Namespaced',

@@ -50,2 +51,16 @@ });

## Union types helpers.
When complex union types are defined, the output will include a namespace (named `Names` by default), containing the namespaced address of properties.
This allows usecases as such:
```typescript
import { Names, WeatherEvent } from './my-type';
const event: WeatherEvent = {};
event[Names.RainEvent];
```
The union members uses [`never`](https://www.typescriptlang.org/docs/handbook/basic-types.html#never) to prevent erroneously creating/accessing a union member with mutiple keys.
## Running the tests

@@ -52,0 +67,0 @@

@@ -8,6 +8,12 @@ import { Schema, schema } from 'avsc';

type UnionRegistry = {
[key: string]: string[];
};
export interface Context {
recordAlias: string;
namesAlias: string;
namespacedPrefix: string;
registry: Registry;
unionRegistry: UnionRegistry;
unionMember: boolean;

@@ -25,2 +31,4 @@ namespace?: string;

type TypeRecordType = { type: schema.RecordType };
export type Convert<TType = Schema> = (context: Context, type: TType) => Result<any>;

@@ -118,9 +126,29 @@

const namespaced = fullyQualifiedName(context, type);
const prop = ts.createPropertySignature(
undefined,
ts.createStringLiteral(namespaced),
undefined,
ts.createTypeReferenceNode(type.name, undefined),
undefined,
);
const currentNamespace = type.namespace || context.namespace;
const props = [
ts.createPropertySignature(
undefined,
ts.createStringLiteral(namespaced),
undefined,
ts.createTypeReferenceNode(type.name, undefined),
undefined,
),
];
if (currentNamespace) {
props.push(
...(context.unionRegistry[currentNamespace] || [])
.filter((name: string) => name !== type.name)
.map(name =>
ts.createPropertySignature(
undefined,
ts.createStringLiteral(`${currentNamespace}.${name}`),
ts.createToken(ts.SyntaxKind.QuestionToken),
ts.createKeywordTypeNode(ts.SyntaxKind.NeverKeyword),
undefined,
),
),
);
}
const namespacedInterfaceType = ts.createInterfaceDeclaration(

@@ -132,4 +160,5 @@ undefined,

undefined,
[prop],
props,
);
return result(

@@ -254,2 +283,6 @@ withEntry(recordContext, namespacedInterfaceType),

const isRecordParent = (type: any): type is TypeRecordType => typeof type === 'object' && typeof type.type === 'object';
const isUnionParent = (type: any): type is { type: Array<schema.AvroSchema> } => Array.isArray(type.type);
const isOptional = (type: Schema): boolean => {

@@ -270,16 +303,7 @@ if (isUnion(type)) {

const printAstNode = (node: Result<ts.Node>, extras: { importLines?: Array<string> } = {}): string => {
const printAstNode = (nodes: Array<ts.Node>, { importLines }: { importLines: Array<string> }): string => {
const resultFile = ts.createSourceFile('someFileName.ts', '', ts.ScriptTarget.Latest);
const printer = ts.createPrinter({ newLine: ts.NewLineKind.LineFeed });
const entries = Object.values(node.context.registry);
const fullSourceFile = ts.updateSourceFileNode(resultFile, entries);
const importLines = extras.importLines || [];
return importLines
.concat(
printer.printNode(ts.EmitHint.Unspecified, node.type, fullSourceFile),
entries.map(entry => printer.printNode(ts.EmitHint.Unspecified, entry, fullSourceFile)),
)
.join('\n\n');
return importLines.concat(nodes.map(n => printer.printNode(ts.EmitHint.Unspecified, n, resultFile))).join('\n\n');
};

@@ -294,2 +318,3 @@

namespacedPrefix?: string;
namesAlias?: string;
};

@@ -299,2 +324,3 @@ const defaultOptions = {

namespacedPrefix: 'Namespaced',
namesAlias: 'Names',
};

@@ -304,7 +330,12 @@

const logicalTypes = options.logicalTypes || {};
const isRootUnion = Array.isArray(recordType);
const context: Context = {
...defaultOptions,
...options,
unionMember: Array.isArray(recordType),
recordAlias: options.recordAlias || defaultOptions.recordAlias,
namesAlias: options.namesAlias || defaultOptions.namesAlias,
namespacedPrefix: options.namespacedPrefix || defaultOptions.namespacedPrefix,
unionMember: isRootUnion,
registry: {},
unionRegistry: buildUnionRegistry({}, recordType, { namespace: recordType.namespace, unionMember: isRootUnion }),
namespaces: {},

@@ -321,3 +352,3 @@ visitedLogicalTypes: [],

const nodes = convertType(context, recordType);
const mainNode = convertType(context, recordType);

@@ -328,16 +359,85 @@ const importLines = context.visitedLogicalTypes

return printAstNode(
{
context: nodes.context,
const nodes: Array<ts.Node> = [
ts.createTypeAliasDeclaration(
undefined,
[ts.createToken(ts.SyntaxKind.ExportKeyword)],
context.recordAlias,
undefined,
mainNode.type,
) as ts.Node,
].concat(Object.values(mainNode.context.registry));
type: ts.createTypeAliasDeclaration(
undefined,
[ts.createToken(ts.SyntaxKind.ExportKeyword)],
context.recordAlias,
undefined,
nodes.type,
const namesNamespace = unionRegisryToNamespace(context.unionRegistry, context.namesAlias);
if (namesNamespace) {
nodes.unshift(namesNamespace);
}
return printAstNode(nodes, { importLines });
}
function unionRegisryToNamespace(registry: UnionRegistry, namespaceName: string): ts.Node | undefined {
const names = Object.keys(registry).reduce<Array<ts.Statement>>(
(nodes, namespace) =>
nodes.concat(
registry[namespace].map(name =>
ts.createVariableStatement(
[ts.createToken(ts.SyntaxKind.ExportKeyword)],
ts.createVariableDeclarationList(
[ts.createVariableDeclaration(name, undefined, ts.createLiteral(`${namespace}.${name}`))],
ts.NodeFlags.Const,
),
),
),
),
},
{ importLines },
[],
);
if (!names.length) {
return;
}
const nsNode = ts.createModuleDeclaration(
[],
[ts.createToken(ts.SyntaxKind.ExportKeyword)],
ts.createIdentifier(namespaceName),
ts.createModuleBlock(names),
ts.NodeFlags.Namespace,
);
return nsNode;
}
function buildUnionRegistry(
registry: UnionRegistry,
schema: schema.AvroSchema,
context: { namespace?: string; unionMember: boolean },
): UnionRegistry {
if (Array.isArray(schema)) {
return schema.reduce((acc, schema) => buildUnionRegistry(acc, schema, context), registry);
}
if (isRecordParent(schema)) {
return buildUnionRegistry(registry, schema.type, { ...context, unionMember: isUnionParent(schema) });
}
if (isRecordType(schema)) {
const { name, fields } = schema;
const currentNamespace = schema.namespace || context.namespace;
if (currentNamespace && context.unionMember) {
(registry[currentNamespace] = registry[currentNamespace] || []).push(name);
}
fields.reduce(
(acc, field) =>
// @ts-ignore This works, but a bit too dynamic for TS
buildUnionRegistry(acc, field, {
...context,
unionMember: false,
namespace: schema.namespace || context.namespace,
}),
registry,
);
}
return registry;
}

Sorry, the diff of this file is not supported yet

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc