From d0fa9ba0087a6da9b0036c77d1257389865e42e2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o=20Lucas?= Date: Sun, 8 Sep 2024 03:30:58 -0300 Subject: [PATCH 1/4] feat: Add TypeScript support Adds the ability to generate TypeScript declarations for PBF-encoded messages. A new `--typescript` flag has been added to the CLI and as an option to the `compileRaw` function. When enabled, this will produce a TypeScript `.d.ts` file containing interfaces for each of the messages defined in the input protobuf schema. --- .vscode/settings.json | 5 + bin/pbf | 5 +- compile.js | 350 +++++++++++++++++++++++++++++++++--------- eslint.config.js | 8 +- package.json | 2 +- 5 files changed, 295 insertions(+), 75 deletions(-) create mode 100644 .vscode/settings.json diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 0000000..ef41c8a --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,5 @@ +{ + "editor.tabSize": 4, + "editor.insertSpaces": true, + "editor.detectIndentation": false +} \ No newline at end of file diff --git a/bin/pbf b/bin/pbf index 7981f06..28d64e5 100755 --- a/bin/pbf +++ b/bin/pbf @@ -4,14 +4,15 @@ import resolve from 'resolve-protobuf-schema'; import {compileRaw} from '../compile.js'; if (process.argv.length < 3) { - console.error('Usage: pbf [file.proto] [--no-read] [--no-write] [--legacy]'); + console.error('Usage: pbf [file.proto] [--no-read] [--no-write] [--legacy] [--typescript]'); process.exit(0); } const code = compileRaw(resolve.sync(process.argv[2]), { noRead: process.argv.indexOf('--no-read') >= 0, noWrite: process.argv.indexOf('--no-write') >= 0, - legacy: process.argv.indexOf('--legacy') >= 0 + legacy: process.argv.indexOf('--legacy') >= 0, + typescript: process.argv.indexOf('--typescript') >= 0, }); process.stdout.write(code); diff --git a/compile.js b/compile.js index 220de6d..c8630d9 100644 --- a/compile.js +++ b/compile.js @@ -1,15 +1,22 @@ - import {readFileSync} from 'fs'; -const version = JSON.parse(readFileSync(new URL('package.json', import.meta.url))).version; +const version = JSON.parse( + readFileSync(new URL('package.json', import.meta.url)) +).version; export function compile(proto) { - return new Function(`const exports = {};\n${compileRaw(proto, {legacy: true})}\nreturn exports;`)(); + return new Function( + `const exports = {};\n${compileRaw(proto, { + legacy: true, + })}\nreturn exports;` + )(); } export function compileRaw(proto, options = {}) { const context = buildDefaults(buildContext(proto, null), proto.syntax); - return `${options.dev ? '' : `// code generated by pbf v${version}\n`}${writeContext(context, options)}`; + const isTypeScript = options.typescript === true; + return `${options.dev ? '' : `// code generated by pbf v${version}\n` + }${writeContext(context, {...options, isTypeScript})}`; } function writeContext(ctx, options) { @@ -25,14 +32,24 @@ function writeContext(ctx, options) { function writeMessage(ctx, options) { const fields = ctx._proto.fields; + const isTypeScript = options.isTypeScript; let code = '\n'; + if (isTypeScript) { + code += writeTypeScriptInterface(ctx); + } + if (!options.noRead) { const readName = `read${ctx._name}`; - code += -`${writeFunctionExport(options, readName)}function ${readName}(pbf, end) { - return pbf.readFields(${readName}Field, ${compileDest(ctx)}, end); + code += `${writeFunctionExport( + options, + readName + )}function ${readName}(pbf, end) { + return pbf.readFields(${readName}Field, ${compileDest( + ctx, + isTypeScript +)}, end); } function ${readName}Field(tag, obj, pbf) { `; @@ -40,32 +57,46 @@ function ${readName}Field(tag, obj, pbf) { const field = fields[i]; const {type, name, repeated, oneof, tag} = field; const readCode = compileFieldRead(ctx, field); + const packed = willSupportPacked(ctx, field); let fieldRead = - type === 'map' ? compileMapRead(readCode, name) : - repeated ? (packed ? readCode : `obj.${name}.push(${readCode})`) : - `obj.${name} = ${readCode}`; + type === 'map' ? + compileMapRead(readCode, name) : + repeated ? + packed ? + readCode : + `obj.${name}.push(${readCode})` : + `obj.${name} = ${readCode}`; if (oneof) { fieldRead += `; obj.${oneof} = ${JSON.stringify(name)}`; } - fieldRead = type === 'map' || oneof ? `{ ${fieldRead}; }` : `${fieldRead};`; + fieldRead = + type === 'map' || oneof ? `{ ${fieldRead}; }` : `${fieldRead};`; - code += -` ${i ? 'else ' : ''}if (tag === ${tag}) ${fieldRead}\n`; + code += ` ${i ? 'else ' : ''}if (tag === ${tag}) ${fieldRead}\n`; } code += '}\n'; } if (!options.noWrite) { const writeName = `write${ctx._name}`; - code += `${writeFunctionExport(options, writeName)}function ${writeName}(obj, pbf) {\n`; + code += `${writeFunctionExport( + options, + writeName + )}function ${writeName}(obj${isTypeScript ? `: ${ctx._name}` : '' + }, pbf) {\n`; for (const field of fields) { const writeCode = - field.repeated && !isPacked(field) ? compileRepeatedWrite(ctx, field) : - field.type === 'map' ? compileMapWrite(ctx, field) : compileFieldWrite(ctx, field, `obj.${field.name}`); + field.type === 'map' ? + compileMapWrite(ctx, field) : + isPacked(field) ? + compilePackedWrite(ctx, field) : + field.repeated ? + compileRepeatedWrite(ctx, field) : + compileFieldWrite(ctx, field, `obj.${field.name}`); code += getDefaultWriteTest(ctx, field); code += `${writeCode};\n`; } @@ -74,6 +105,119 @@ function ${readName}Field(tag, obj, pbf) { return code; } +function compilePackedWrite(ctx, field) { + const type = getType(ctx, field); + const fieldType = isEnum(type) ? 'enum' : field.type; + let writeMethod; + + switch (fieldType) { + case 'float': + writeMethod = 'Float'; + break; + case 'double': + writeMethod = 'Double'; + break; + case 'enum': + case 'uint32': + case 'uint64': + case 'int32': + case 'int64': + writeMethod = 'Varint'; + break; + case 'sint32': + case 'sint64': + writeMethod = 'SVarint'; + break; + case 'fixed32': + writeMethod = 'Fixed32'; + break; + case 'fixed64': + writeMethod = 'Fixed64'; + break; + case 'sfixed32': + writeMethod = 'SFixed32'; + break; + case 'sfixed64': + writeMethod = 'SFixed64'; + break; + case 'bool': + writeMethod = 'Boolean'; + break; + default: + throw new Error(`Unexpected packed field type: ${fieldType}`); + } + + return `pbf.writePacked${writeMethod}(${field.tag}, obj.${field.name})`; +} + +function writeTypeScriptInterface(ctx) { + const fields = ctx._proto.fields; + let code = `export interface ${ctx._name} {\n`; + + for (const field of fields) { + const typeScriptType = getTypeScriptType(ctx, field); + const optional = !field.required ? '?' : ''; + const isArray = field.repeated || isPacked(field); + const arraySymbol = isArray ? '[]' : ''; + code += ` ${field.name}${optional}: ${typeScriptType}${arraySymbol};\n`; + } + + code += '}\n\n'; + return code; +} + +function getTypeScriptType(ctx, field) { + const type = getType(ctx, field); + + if (type) { + if (type._proto.fields) return type._name; + if (isEnum(type)) return type._name; + } + + switch (field.type) { + case 'string': + return 'string'; + case 'float': + case 'double': + return 'number'; + case 'bool': + return 'boolean'; + case 'enum': + return field.type.split('.').pop(); // Use the enum name + case 'uint32': + case 'uint64': + case 'int32': + case 'int64': + case 'sint32': + case 'sint64': + case 'fixed32': + case 'fixed64': + case 'sfixed32': + case 'sfixed64': + return fieldShouldUseStringAsNumber(field) ? 'string' : 'number'; + case 'bytes': + return 'Uint8Array'; + case 'map': + return `{[key: string]: ${getTypeScriptType(ctx, { + type: field.map.to, + })}}`; + default: + throw new Error(`Unexpected type: ${field.type}`); + } +} + +function getTypeAssertion(defaultValue, typeScriptType, isArray, isTypeScript) { + if (!isTypeScript) return ''; + + const arrayAssertion = isArray ? '[]' : ''; + + if (defaultValue === undefined) { + // Use double assertion for undefined values + return ` as unknown ${typeScriptType}${arrayAssertion}`; + } + return ` ${typeScriptType}${arrayAssertion}`; +} + function writeFunctionExport({legacy}, name) { return legacy ? `exports.${name} = ${name};\n` : 'export '; } @@ -87,17 +231,41 @@ function getEnumValues(ctx) { return enums; } -function writeEnum(ctx, {legacy}) { - const enums = JSON.stringify(getEnumValues(ctx), null, 4); +function writeEnum(ctx, {legacy, isTypeScript}) { + const enums = getEnumValues(ctx); const name = ctx._name; - return `\n${legacy ? `const ${name} = exports.${name}` : `export const ${name}`} = ${enums};\n`; + let code = '\n'; + + if (isTypeScript) { + code += `export enum ${name} {\n`; + for (const [key, value] of Object.entries(enums)) { + code += ` ${key} = ${value},\n`; + } + code += '}\n'; + + return code; + } + + code += `${legacy ? `const ${name} = exports.${name}` : `export const ${name}` + } = ${JSON.stringify(enums, null, 4)};\n`; + + return code; } -function compileDest(ctx) { +function compileDest(ctx, isTypeScript) { const props = new Set(); - for (const {name, oneof} of ctx._proto.fields) { - props.add(`${name}: ${JSON.stringify(ctx._defaults[name])}`); - if (oneof) props.add(`${oneof }: undefined`); + for (const field of ctx._proto.fields) { + const {name, oneof, repeated} = field; + const defaultValue = JSON.stringify(ctx._defaults[name]); + const isArray = repeated || isPacked(field); + const typeScriptType = isTypeScript ? + `as ${getTypeScriptType(ctx, field)}${isArray ? '[]' : ''}` : + ''; + const typeAssertion = isTypeScript ? + getTypeAssertion(defaultValue, typeScriptType, isArray, isTypeScript) : + ''; + props.add(`${name}: ${isArray ? '[]' : defaultValue}${typeAssertion}`); + if (oneof) props.add(`${oneof}: undefined`); } return `{${[...props].join(', ')}}`; } @@ -115,7 +283,7 @@ function getType(ctx, field) { } function fieldShouldUseStringAsNumber(field) { - if (field.options.jstype === 'JS_STRING') { + if (field.options && field.options.jstype === 'JS_STRING') { switch (field.type) { case 'float': case 'double': @@ -128,8 +296,10 @@ function fieldShouldUseStringAsNumber(field) { case 'fixed32': case 'fixed64': case 'sfixed32': - case 'sfixed64': return true; - default: return false; + case 'sfixed64': + return true; + default: + return false; } } return false; @@ -138,7 +308,8 @@ function fieldShouldUseStringAsNumber(field) { function compileFieldRead(ctx, field) { const type = getType(ctx, field); if (type) { - if (type._proto.fields) return `read${type._name}(pbf, pbf.readVarint() + pbf.pos)`; + if (type._proto.fields) + return `read${type._name}(pbf, pbf.readVarint() + pbf.pos)`; if (!isEnum(type)) throw new Error(`Unexpected type: ${type._name}`); } @@ -158,23 +329,35 @@ function compileFieldRead(ctx, field) { } switch (fieldType) { - case 'string': return `${prefix}String${suffix}`; - case 'float': return `${prefix}Float${suffix}`; - case 'double': return `${prefix}Double${suffix}`; - case 'bool': return `${prefix}Boolean${suffix}`; + case 'string': + return `${prefix}String${suffix}`; + case 'float': + return `${prefix}Float${suffix}`; + case 'double': + return `${prefix}Double${suffix}`; + case 'bool': + return `${prefix}Boolean${suffix}`; case 'enum': case 'uint32': case 'uint64': case 'int32': - case 'int64': return `${prefix}Varint${suffix}`; + case 'int64': + return `${prefix}Varint${suffix}`; case 'sint32': - case 'sint64': return `${prefix}SVarint${suffix}`; - case 'fixed32': return `${prefix}Fixed32${suffix}`; - case 'fixed64': return `${prefix}Fixed64${suffix}`; - case 'sfixed32': return `${prefix}SFixed32${suffix}`; - case 'sfixed64': return `${prefix}SFixed64${suffix}`; - case 'bytes': return `${prefix}Bytes${suffix}`; - default: throw new Error(`Unexpected type: ${field.type}`); + case 'sint64': + return `${prefix}SVarint${suffix}`; + case 'fixed32': + return `${prefix}Fixed32${suffix}`; + case 'fixed64': + return `${prefix}Fixed64${suffix}`; + case 'sfixed32': + return `${prefix}SFixed32${suffix}`; + case 'sfixed64': + return `${prefix}SFixed64${suffix}`; + case 'bytes': + return `${prefix}Bytes${suffix}`; + default: + throw new Error(`Unexpected type: ${field.type}`); } } @@ -193,29 +376,42 @@ function compileFieldWrite(ctx, field, name) { const type = getType(ctx, field); if (type) { - if (type._proto.fields) return `${prefix}Message(${field.tag}, write${type._name}, ${name})`; + if (type._proto.fields) + return `${prefix}Message(${field.tag}, write${type._name}, ${name})`; if (type._proto.values) return `${prefix}Varint${postfix}`; throw new Error(`Unexpected type: ${type._name}`); } switch (field.type) { - case 'string': return `${prefix}String${postfix}`; - case 'float': return `${prefix}Float${postfix}`; - case 'double': return `${prefix}Double${postfix}`; - case 'bool': return `${prefix}Boolean${postfix}`; + case 'string': + return `${prefix}String${postfix}`; + case 'float': + return `${prefix}Float${postfix}`; + case 'double': + return `${prefix}Double${postfix}`; + case 'bool': + return `${prefix}Boolean${postfix}`; case 'enum': case 'uint32': case 'uint64': case 'int32': - case 'int64': return `${prefix}Varint${postfix}`; + case 'int64': + return `${prefix}Varint${postfix}`; case 'sint32': - case 'sint64': return `${prefix}SVarint${postfix}`; - case 'fixed32': return `${prefix}Fixed32${postfix}`; - case 'fixed64': return `${prefix}Fixed64${postfix}`; - case 'sfixed32': return `${prefix}SFixed32${postfix}`; - case 'sfixed64': return `${prefix}SFixed64${postfix}`; - case 'bytes': return `${prefix}Bytes${postfix}`; - default: throw new Error(`Unexpected type: ${field.type}`); + case 'sint64': + return `${prefix}SVarint${postfix}`; + case 'fixed32': + return `${prefix}Fixed32${postfix}`; + case 'fixed64': + return `${prefix}Fixed64${postfix}`; + case 'sfixed32': + return `${prefix}SFixed32${postfix}`; + case 'sfixed64': + return `${prefix}SFixed64${postfix}`; + case 'bytes': + return `${prefix}Bytes${postfix}`; + default: + throw new Error(`Unexpected type: ${field.type}`); } } @@ -224,15 +420,21 @@ function compileMapRead(readCode, name) { } function compileRepeatedWrite(ctx, field) { - return `for (const item of obj.${field.name}) ${ - compileFieldWrite(ctx, field, 'item')}`; + return `for (const item of obj.${field.name}) ${compileFieldWrite( + ctx, + field, + 'item' + )}`; } function compileMapWrite(ctx, field) { const name = `obj.${field.name}`; - return `for (const key of Object.keys(${name})) ${ - compileFieldWrite(ctx, field, `{key, value: ${name}[key]}`)}`; + return `for (const key of Object.keys(${name})) ${compileFieldWrite( + ctx, + field, + `{key, value: ${name}[key]}` + )}`; } function getMapMessageName(tag) { @@ -248,7 +450,7 @@ function getMapField(name, type, tag) { oneof: null, required: false, repeated: false, - options: {} + options: {}, }; } @@ -260,8 +462,8 @@ function getMapMessage(field) { extensions: null, fields: [ getMapField('key', field.map.from, 1), - getMapField('value', field.map.to, 2) - ] + getMapField('value', field.map.to, 2), + ], }; } @@ -301,14 +503,19 @@ function buildContext(proto, parent) { function getDefaultValue(field, value) { // Defaults not supported for repeated fields if (field.repeated) return []; - let convertToStringIfNeeded = function (val) { return val; }; + let convertToStringIfNeeded = function (val) { + return val; + }; if (fieldShouldUseStringAsNumber(field)) { - convertToStringIfNeeded = function (val) { return val.toString(); }; + convertToStringIfNeeded = function (val) { + return val.toString(); + }; } switch (field.type) { case 'float': - case 'double': return convertToStringIfNeeded(value ? parseFloat(value) : 0); + case 'double': + return convertToStringIfNeeded(value ? parseFloat(value) : 0); case 'uint32': case 'uint64': case 'int32': @@ -318,11 +525,16 @@ function getDefaultValue(field, value) { case 'fixed32': case 'fixed64': case 'sfixed32': - case 'sfixed64': return convertToStringIfNeeded(value ? parseInt(value, 10) : 0); - case 'string': return value || ''; - case 'bool': return value === 'true'; - case 'map': return {}; - default: return undefined; + case 'sfixed64': + return convertToStringIfNeeded(value ? parseInt(value, 10) : 0); + case 'string': + return value || ''; + case 'bool': + return value === 'true'; + case 'map': + return {}; + default: + return undefined; } } @@ -342,7 +554,8 @@ function willSupportPacked(ctx, field) { case 'fixed64': case 'sfixed32': case 'enum': - case 'bool': return true; + case 'bool': + return true; } return false; @@ -372,7 +585,6 @@ function setDefaultValue(ctx, field, syntax) { // Set default for enum values if (enumValues && !field.repeated) { ctx._defaults[field.name] = enumValues[explicitDefault] || 0; - } else { ctx._defaults[field.name] = getDefaultValue(field, explicitDefault); } diff --git a/eslint.config.js b/eslint.config.js index 5da1771..98b39d9 100644 --- a/eslint.config.js +++ b/eslint.config.js @@ -6,7 +6,8 @@ export default [ files: ['**/*.js', 'bin/pbf'], rules: { 'no-empty': 0, - 'no-cond-assign': 0 + 'no-cond-assign': 0, + 'quotes': ['error', 'single'] } }, { @@ -16,7 +17,8 @@ export default [ '@stylistic/js/quotes': 0, '@stylistic/js/semi': 0, '@stylistic/js/brace-style': 0, - 'no-unused-vars': 0 + 'no-unused-vars': 0, + 'quotes': 'off' } - } + }, ]; diff --git a/package.json b/package.json index 499b0f6..1043350 100644 --- a/package.json +++ b/package.json @@ -12,7 +12,7 @@ "bench": "node bench/bench.js", "pretest": "eslint *.js compile.js test/*.js test/fixtures/*.js bin/pbf", "test": "tsc && node --test", - "cov": "node --test --experimental-test-covetage", + "cov": "node --test --experimental-test-coverage", "build": "rollup -c", "prepublishOnly": "npm run test && npm run build" }, From 936fd533a3b7735f110938537e923020b1555190 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o=20Lucas?= Date: Sun, 8 Sep 2024 04:37:25 -0300 Subject: [PATCH 2/4] fix: some types issues --- compile.js | 16 +++++++++------- test/compile.test.js | 5 ++++- test/fixtures/defaults_implicit.js | 2 +- test/fixtures/embedded_type.js | 2 +- test/fixtures/vector_tile.js | 8 ++++---- 5 files changed, 19 insertions(+), 14 deletions(-) diff --git a/compile.js b/compile.js index c8630d9..dabac32 100644 --- a/compile.js +++ b/compile.js @@ -15,7 +15,7 @@ export function compile(proto) { export function compileRaw(proto, options = {}) { const context = buildDefaults(buildContext(proto, null), proto.syntax); const isTypeScript = options.typescript === true; - return `${options.dev ? '' : `// code generated by pbf v${version}\n` + return `${options.dev ? '' : `// code generated by pbf v${version}\n${isTypeScript ? 'import type Pbf from \'pbf\';' : ''}\n` }${writeContext(context, {...options, isTypeScript})}`; } @@ -45,13 +45,13 @@ function writeMessage(ctx, options) { code += `${writeFunctionExport( options, readName - )}function ${readName}(pbf, end) { + )}function ${readName}(pbf${isTypeScript ? ': Pbf' : ''}, end${isTypeScript ? '?: number' : ''}) { return pbf.readFields(${readName}Field, ${compileDest( ctx, isTypeScript )}, end); } -function ${readName}Field(tag, obj, pbf) { +function ${readName}Field(tag${isTypeScript ? ': number' : ''}, obj${isTypeScript ? `: ${ ctx._name}` : ''}, pbf${isTypeScript ? ': Pbf' : ''}) { `; for (let i = 0; i < fields.length; i++) { const field = fields[i]; @@ -59,6 +59,7 @@ function ${readName}Field(tag, obj, pbf) { const readCode = compileFieldRead(ctx, field); const packed = willSupportPacked(ctx, field); + const optional = !field.required ? '?' : ''; let fieldRead = type === 'map' ? @@ -66,11 +67,12 @@ function ${readName}Field(tag, obj, pbf) { repeated ? packed ? readCode : - `obj.${name}.push(${readCode})` : + `obj.${name}${optional}.push(${readCode})` : `obj.${name} = ${readCode}`; if (oneof) { - fieldRead += `; obj.${oneof} = ${JSON.stringify(name)}`; + // @TODO: fix this + fieldRead += `;${isTypeScript ? '\n//@ts-ignore' : ''} \nobj.${oneof} = ${JSON.stringify(name)}\n`; } fieldRead = @@ -87,7 +89,7 @@ function ${readName}Field(tag, obj, pbf) { options, writeName )}function ${writeName}(obj${isTypeScript ? `: ${ctx._name}` : '' - }, pbf) {\n`; + }, pbf${isTypeScript ? ': Pbf' : ''}) {\n`; for (const field of fields) { const writeCode = field.type === 'map' ? @@ -215,7 +217,7 @@ function getTypeAssertion(defaultValue, typeScriptType, isArray, isTypeScript) { // Use double assertion for undefined values return ` as unknown ${typeScriptType}${arrayAssertion}`; } - return ` ${typeScriptType}${arrayAssertion}`; + return ` ${typeScriptType}`; } function writeFunctionExport({legacy}, name) { diff --git a/test/compile.test.js b/test/compile.test.js index d2e5157..09410fc 100644 --- a/test/compile.test.js +++ b/test/compile.test.js @@ -6,6 +6,8 @@ import {sync as resolve} from 'resolve-protobuf-schema'; import Pbf from '../index.js'; import {compile, compileRaw} from '../compile.js'; +const formatTextFile = textFile => textFile.replace(/\r\n|\r|\n/g, '').replace(/ +/g, ' ').trim(); + test('compiles all proto files to proper js', () => { const files = fs.readdirSync(new URL('fixtures', import.meta.url)); @@ -18,7 +20,8 @@ test('compiles all proto files to proper js', () => { // fs.writeFileSync(new URL(`fixtures/${path}`.replace('.proto', '.js'), import.meta.url), js); const expectedJS = fs.readFileSync(new URL(`fixtures/${path}`.replace('.proto', '.js'), import.meta.url), 'utf8'); - assert.equal(js, expectedJS); + + assert.equal(formatTextFile(js), formatTextFile(expectedJS)); } }); diff --git a/test/fixtures/defaults_implicit.js b/test/fixtures/defaults_implicit.js index 5603350..fa536d0 100644 --- a/test/fixtures/defaults_implicit.js +++ b/test/fixtures/defaults_implicit.js @@ -21,7 +21,7 @@ function readEnvelopeField(tag, obj, pbf) { else if (tag === 3) obj.flag = pbf.readBoolean(); else if (tag === 4) obj.weight = pbf.readFloat(); else if (tag === 5) obj.id = pbf.readVarint(true); - else if (tag === 6) obj.tags.push(pbf.readString()); + else if (tag === 6) obj.tags?.push(pbf.readString()); else if (tag === 7) pbf.readPackedVarint(obj.numbers, true); else if (tag === 8) obj.bytes = pbf.readBytes(); else if (tag === 9) obj.custom = readCustomType(pbf, pbf.readVarint() + pbf.pos); diff --git a/test/fixtures/embedded_type.js b/test/fixtures/embedded_type.js index 4c41ddf..947a452 100644 --- a/test/fixtures/embedded_type.js +++ b/test/fixtures/embedded_type.js @@ -17,7 +17,7 @@ export function readEmbeddedTypeContainer(pbf, end) { return pbf.readFields(readEmbeddedTypeContainerField, {values: []}, end); } function readEmbeddedTypeContainerField(tag, obj, pbf) { - if (tag === 1) obj.values.push(readEmbeddedTypeContainerInner(pbf, pbf.readVarint() + pbf.pos)); + if (tag === 1) obj.values?.push(readEmbeddedTypeContainerInner(pbf, pbf.readVarint() + pbf.pos)); } export function writeEmbeddedTypeContainer(obj, pbf) { if (obj.values) for (const item of obj.values) pbf.writeMessage(1, writeEmbeddedTypeContainerInner, item); diff --git a/test/fixtures/vector_tile.js b/test/fixtures/vector_tile.js index 5796828..5d5427a 100644 --- a/test/fixtures/vector_tile.js +++ b/test/fixtures/vector_tile.js @@ -3,7 +3,7 @@ export function readTile(pbf, end) { return pbf.readFields(readTileField, {layers: []}, end); } function readTileField(tag, obj, pbf) { - if (tag === 3) obj.layers.push(readTileLayer(pbf, pbf.readVarint() + pbf.pos)); + if (tag === 3) obj.layers?.push(readTileLayer(pbf, pbf.readVarint() + pbf.pos)); } export function writeTile(obj, pbf) { if (obj.layers) for (const item of obj.layers) pbf.writeMessage(3, writeTileLayer, item); @@ -60,9 +60,9 @@ export function readTileLayer(pbf, end) { function readTileLayerField(tag, obj, pbf) { if (tag === 15) obj.version = pbf.readVarint(); else if (tag === 1) obj.name = pbf.readString(); - else if (tag === 2) obj.features.push(readTileFeature(pbf, pbf.readVarint() + pbf.pos)); - else if (tag === 3) obj.keys.push(pbf.readString()); - else if (tag === 4) obj.values.push(readTileValue(pbf, pbf.readVarint() + pbf.pos)); + else if (tag === 2) obj.features?.push(readTileFeature(pbf, pbf.readVarint() + pbf.pos)); + else if (tag === 3) obj.keys?.push(pbf.readString()); + else if (tag === 4) obj.values?.push(readTileValue(pbf, pbf.readVarint() + pbf.pos)); else if (tag === 5) obj.extent = pbf.readVarint(); } export function writeTileLayer(obj, pbf) { From cb14411539597bba9b019bce733833c3d2051b73 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o=20Lucas?= Date: Sun, 8 Sep 2024 10:19:27 -0300 Subject: [PATCH 3/4] fix: remove oneof from types --- compile.js | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/compile.js b/compile.js index dabac32..f67c077 100644 --- a/compile.js +++ b/compile.js @@ -257,7 +257,7 @@ function writeEnum(ctx, {legacy, isTypeScript}) { function compileDest(ctx, isTypeScript) { const props = new Set(); for (const field of ctx._proto.fields) { - const {name, oneof, repeated} = field; + const {name, repeated} = field; const defaultValue = JSON.stringify(ctx._defaults[name]); const isArray = repeated || isPacked(field); const typeScriptType = isTypeScript ? @@ -267,7 +267,6 @@ function compileDest(ctx, isTypeScript) { getTypeAssertion(defaultValue, typeScriptType, isArray, isTypeScript) : ''; props.add(`${name}: ${isArray ? '[]' : defaultValue}${typeAssertion}`); - if (oneof) props.add(`${oneof}: undefined`); } return `{${[...props].join(', ')}}`; } From 6e0de1ab0d7733db3c68d9c5f8a25ca88ca6707c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o=20Lucas?= Date: Mon, 9 Sep 2024 01:08:33 -0300 Subject: [PATCH 4/4] fix: test oneof --- test/fixtures/oneof.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/fixtures/oneof.js b/test/fixtures/oneof.js index 5055689..146c33b 100644 --- a/test/fixtures/oneof.js +++ b/test/fixtures/oneof.js @@ -1,6 +1,6 @@ export function readEnvelope(pbf, end) { - return pbf.readFields(readEnvelopeField, {id: 0, int: 0, value: undefined, float: 0, string: ""}, end); + return pbf.readFields(readEnvelopeField, {id: 0, int: 0, float: 0, string: ""}, end); } function readEnvelopeField(tag, obj, pbf) { if (tag === 1) obj.id = pbf.readVarint(true);