Big News: Socket raises $60M Series C at a $1B valuation to secure software supply chains for AI-driven development.Announcement
Sign In

protobufjs-cli

Package Overview
Dependencies
Maintainers
3
Versions
32
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

protobufjs-cli - npm Package Compare versions

Comparing version
2.2.1
to
2.3.0
+7
-0
CHANGELOG.md
# Changelog
## [2.3.0](https://github.com/protobufjs/protobuf.js/compare/protobufjs-cli-v2.2.1...protobufjs-cli-v2.3.0) (2026-05-13)
### Features
* Improve generated typings ([#2244](https://github.com/protobufjs/protobuf.js/issues/2244)) ([faa424e](https://github.com/protobufjs/protobuf.js/commit/faa424e3837fe43f1f010b0ccdeb583d808a57cf))
## [2.2.1](https://github.com/protobufjs/protobuf.js/compare/protobufjs-cli-v2.2.0...protobufjs-cli-v2.2.1) (2026-05-13)

@@ -4,0 +11,0 @@

+1
-1
{
"tags": {
"allowUnknownTags": false
"allowUnknownTags": true
},

@@ -5,0 +5,0 @@ "source": {

"use strict";
var typeParserPatched = false;
function normalizeType(type) {
return type.replace(/\r?\n|\r/g, "\n");
}
function patchTypeScriptTypes(dictionary) {
var type = require("jsdoc/tag/type");
var parse = type.parse,
typeTag = dictionary.lookUp("type");
if (typeTag && !typeTag.protobufjsPatched) {
typeTag.onTagText = function onTypeTagText(text) {
var openIdx = text.indexOf("{"),
closeIdx = text.indexOf("}");
if (openIdx !== 0 || closeIdx <= openIdx + 1)
text = "{" + text + "}";
return text;
};
typeTag.protobufjsPatched = true;
}
if (typeParserPatched)
return;
typeParserPatched = true;
type.parse = function parseTypeScriptType(tagValue, canHaveName, canHaveType) {
try {
return parse(tagValue, canHaveName, canHaveType);
} catch (e) {
if (!canHaveType)
throw e;
var text = String(tagValue || "");
var open = text.indexOf("{");
if (open < 0)
throw e;
var depth = 0;
var close = -1;
for (var i = open; i < text.length; ++i) {
var ch = text.charAt(i);
if (ch === "{")
++depth;
else if (ch === "}" && --depth === 0) {
close = i;
break;
}
}
if (close < 0)
throw e;
var expression = normalizeType(text.substring(open + 1, close)).trim();
if (!/[&;]|\?:|=>|\bkeyof\b|\btypeof\b|^\s*\{/.test(expression))
throw e;
var name = canHaveName ? text.substring(close + 1).trim() : "";
return {
type: [ expression ],
typeExpression: expression,
name: name
};
}
};
}
exports.defineTags = function(dictionary) {
patchTypeScriptTypes(dictionary);

@@ -18,5 +88,5 @@ dictionary.defineTag("template", {

onTagged: function(doclet, tag) {
doclet.tsType = tag.text;
doclet.tsType = normalizeType(tag.text);
}
});
};

@@ -23,5 +23,2 @@ "use strict";

// queued interfaces
var queuedInterfaces = [];
// whether writing the first line

@@ -86,10 +83,2 @@ var firstLine = true;

// process queued
while (queuedInterfaces.length) {
var element = queuedInterfaces.shift();
begin(element);
writeInterface(element);
writeln(";");
}
// end wrap

@@ -109,3 +98,2 @@ if (options.module) {

seen = options = {};
queuedInterfaces = [];
}

@@ -214,2 +202,4 @@ };

});
} else if (tag.name === "deprecated") {
writeln(" * @deprecated");
}

@@ -281,5 +271,2 @@ });

// Ensure upper case Object for map expressions below
name = name.replace(/\bobject\b/g, "Object");
// Convert innermost generic types first so Array.<Object.<...>>

@@ -309,5 +296,5 @@ // and Object.<string,Array.<...>> are both handled correctly

// Convert plain Object back to just object
name = name.replace(/\b(Object\b(?!\.))/g, function($0, $1) {
return $1.toLowerCase();
// Convert plain Object back to just object, but preserve qualified names like foo.Object
name = name.replace(/(^|[^\w$.])Object\b(?![.<])/g, function($0, prefix) {
return prefix + "object";
});

@@ -318,2 +305,95 @@

function splitTypeList(text, delimiter) {
var parts = [];
var start = 0;
var depth = {
brace: 0,
bracket: 0,
paren: 0,
angle: 0
};
var quote = null;
for (var i = 0; i < text.length; ++i) {
var ch = text.charAt(i);
if (quote) {
if (ch === "\\")
++i;
else if (ch === quote)
quote = null;
continue;
}
if (ch === "\"" || ch === "'") {
quote = ch;
continue;
}
switch (ch) {
case "{": ++depth.brace; break;
case "}": --depth.brace; break;
case "[": ++depth.bracket; break;
case "]": --depth.bracket; break;
case "(": ++depth.paren; break;
case ")": --depth.paren; break;
case "<": ++depth.angle; break;
case ">":
if (depth.angle > 0)
--depth.angle;
break;
default:
if (ch === delimiter && !depth.brace && !depth.bracket && !depth.paren && !depth.angle) {
parts.push(text.substring(start, i).trim());
start = i + 1;
}
break;
}
}
parts.push(text.substring(start).trim());
return parts.filter(function(part) {
return part.length;
});
}
function getCallSignatures(type) {
type = type && type.trim();
if (!type || type.charAt(0) !== "{" || type.charAt(type.length - 1) !== "}")
return null;
var body = type.substring(1, type.length - 1).trim();
if (body.charAt(0) !== "(")
return null;
var signatures = splitTypeList(body, ";");
if (!signatures.every(function(signature) {
return getCallSignatureParametersEnd(signature) > 0;
}))
return null;
return signatures.map(function(signature) {
return /;\s*$/.test(signature) ? signature : signature + ";";
});
}
function getCallSignatureParametersEnd(signature) {
var depth = 0;
var quote = null;
for (var i = 0; i < signature.length; ++i) {
var ch = signature.charAt(i);
if (quote) {
if (ch === "\\")
++i;
else if (ch === quote)
quote = null;
continue;
}
if (ch === "\"" || ch === "'") {
quote = ch;
continue;
}
if (ch === "(")
++depth;
else if (ch === ")" && --depth === 0) {
while (++i < signature.length && /\s/.test(signature.charAt(i)))
continue;
return signature.charAt(i) === ":" ? i : -1;
}
}
return -1;
}
// begins writing the definition of the specified element

@@ -389,3 +469,5 @@ function begin(element, is_interface) {

var typeName;
if (element.returns && element.returns.length && (typeName = getTypeOf(element.returns[0])) !== "undefined")
if (element.tsType)
write(element.tsType.replace(/\r?\n|\r/g, "\n"));
else if (element.returns && element.returns.length && (typeName = getTypeOf(element.returns[0])) !== "undefined")
write(typeName);

@@ -399,3 +481,3 @@ else

function writeInterface(element) {
write("interface ", element.name);
write("interface ", element.name, " ");
writeInterfaceBody(element);

@@ -418,5 +500,3 @@ writeln();

writeComment(property.description);
if (inClass)
write("public ");
else if (declare)
if (declare && !inClass)
write("let ");

@@ -632,2 +712,3 @@ write(property.name);

element.properties.forEach(function(property, i) {
writeComment(property.description);
write(property.name);

@@ -662,3 +743,4 @@ if (property.defaultvalue !== undefined)

if (inClass) {
write(element.access || "public", " ");
if (element.access && element.access !== "public")
write(element.access, " ");
if (element.scope === "static")

@@ -688,2 +770,20 @@ write("static ");

function writeFunctionName(element, insideClass, exported) {
if (insideClass) {
if (element.access && element.access !== "public")
write(element.access, " ");
if (element.scope === "static")
write("static ");
else if (element.virtual)
write("abstract ");
} else {
if (exported)
write("export ");
write("function ");
}
write(element.name);
if (element.templates && element.templates.length)
write("<", element.templates.join(", "), ">");
}
// handles a function or method

@@ -698,12 +798,18 @@ function handleFunction(element, parent, isConstructor) {

insideClass = isClassLike(parent);
if (insideClass) {
write(element.access || "public", " ");
if (element.scope === "static")
write("static ");
} else
write("function ");
write(element.name);
if (element.templates && element.templates.length)
write("<", element.templates.join(", "), ">");
writeFunctionName(element, insideClass);
}
var signatures = null;
if (element.type && element.type.names && element.type.names.length === 1)
signatures = getCallSignatures(element.type.names[0]);
if (signatures) {
writeln(signatures[0]);
var exported = !insideClass && (element.scope === "global" || element.isEnum && element.scope === undefined) && !options.module;
for (var i = 1; i < signatures.length; ++i) {
writeFunctionName(element, insideClass, exported);
writeln(signatures[i]);
}
if (!insideClass)
handleNamespace(element);
return;
}
writeFunctionSignature(element, isConstructor, false);

@@ -717,11 +823,26 @@ writeln(";");

function handleTypeDef(element, parent) {
if (isInterface(element)) {
if (isClassLike(parent))
queuedInterfaces.push(element);
else {
begin(element);
writeInterface(element);
}
var suffix = "." + element.name;
var owner = element.longname && element.longname !== element.name && element.longname.lastIndexOf(suffix) === element.longname.length - suffix.length
? element.longname.substring(0, element.longname.length - suffix.length)
: null;
// Emit nested typedefs like Foo.Bar as declarations in namespace Foo
if (owner && (!parent || parent.longname !== owner)) {
if (element.scope === "global" && !options.module)
write("export ");
writeln("namespace ", owner, " {");
++indent;
handleTypeDef(element, { longname: owner });
--indent;
writeln("}");
return;
}
// Emit object typedefs with properties as interfaces, any others as type aliases
var type = getTypeOf(element);
if (isInterface(element) || type === "object" && element.properties && element.properties.length) {
begin(element);
writeInterface(element);
} else {
writeComment(element.comment, true);
begin(element);
write("type ", element.name);

@@ -734,3 +855,2 @@ if (element.templates && element.templates.length)

else {
var type = getTypeOf(element);
if (element.type && element.type.names.length === 1 && element.type.names[0] === "function")

@@ -737,0 +857,0 @@ writeFunctionSignature(element, false, true);

{
"name": "protobufjs-cli",
"description": "Translates between file formats and generates static code as well as TypeScript definitions.",
"version": "2.2.1",
"version": "2.3.0",
"author": "Daniel Wirtz <dcode+protobufjs@dcode.io>",

@@ -6,0 +6,0 @@ "repository": {

@@ -13,3 +13,3 @@ type pbtsCallback = (err: Error|null, output?: string) => void;

* Generates TypeScript definitions from a JavaScript source.
* @param {string|Buffer} source JavaScript source
* @param {string|Uint8Array} source JavaScript source
* @param {string[]} args Command line arguments

@@ -19,2 +19,2 @@ * @param {function(?Error, string=)} [callback] Optional completion callback

*/
export function process(source: string|Buffer, args: string[], callback?: pbtsCallback): number|undefined;
export function process(source: string|Uint8Array, args: string[], callback?: pbtsCallback): number|undefined;
+31
-10

@@ -26,3 +26,3 @@ "use strict";

* Generates TypeScript definitions from a JavaScript source.
* @param {string|Buffer} source JavaScript source
* @param {string|Uint8Array} source JavaScript source
* @param {string[]} args Command line arguments

@@ -82,3 +82,3 @@ * @param {function(?Error, string=)} [callback] Optional completion callback

"",
" -i, --import Comma delimited list of imports. Local names will equal camelCase of the basename.",
" -i, --import Comma delimited list of imports, optionally as localName=path.",
"",

@@ -93,3 +93,3 @@ " --no-constructor Emits private constructors for reflection-backed declarations.",

"",
" -m, --main Whether building the main library without any imports.",
" -m, --main Whether building a standalone file without any imports.",
"",

@@ -197,2 +197,16 @@ "usage: " + chalk.bold.green("pbts") + " [options] file1.js file2.js ..." + chalk.bold.gray(" (or) ") + "other | " + chalk.bold.green("pbts") + " [options] -",

function addImport(imports, importItem) {
var equals = importItem.indexOf("=");
if (equals < 0) {
imports[getImportName(importItem)] = importItem;
return;
}
var importName = importItem.substring(0, equals),
importPath = importItem.substring(equals + 1);
if (importName[0] === "\\" && importName[1] === "$")
importName = importName.substring(1);
imports[importName] = importPath;
}
function finish() {

@@ -202,3 +216,3 @@ var output = [];

output.push(
"// DO NOT EDIT! This is a generated file. Edit the JSDoc in src/*.js instead and run 'npm run build:types'.",
"// DO NOT EDIT! This is a generated file. Edit the source file instead and regenerate.",
""

@@ -212,12 +226,15 @@ );

if (!argv.main) {
if (!argv.main || argv.import) {
// Ensure we have a usable array of imports
var importArray = typeof argv.import === "string" ? argv.import.split(",") : argv.import || [];
var importArray = [];
[].concat(argv.import || []).forEach(function(importItem) {
importArray = importArray.concat(importItem.split(","));
});
// Build an object of imports and paths
var imports = {
var imports = argv.main ? {} : {
$protobuf: "protobufjs"
};
importArray.forEach(function(importItem) {
imports[getImportName(importItem)] = importItem;
addImport(imports, importItem);
});

@@ -230,6 +247,10 @@

output.push("import Long = require(\"long\");");
if (!argv.main)
output.push("import Long = require(\"long\");");
output.push("");
}
output = output.join("\n") + "\n" + out.join("");
output = output.length
? output.join("\n") + "\n" + out.join("")
: out.join("");

@@ -236,0 +257,0 @@ try {

@@ -101,3 +101,3 @@ # protobufjs-cli

-i, --import Comma delimited list of imports. Local names will equal camelCase of the basename.
-i, --import Comma delimited list of imports, optionally as localName=path.

@@ -112,3 +112,3 @@ --no-constructor Emits private constructors for reflection-backed declarations.

-m, --main Whether building the main library without any imports.
-m, --main Whether building a standalone file without any imports.

@@ -115,0 +115,0 @@ usage: pbts [options] file1.js file2.js ... (or) other | pbts [options] -

@@ -39,3 +39,3 @@ "use strict";

if (root.comment) {
pushComment("@fileoverview " + root.comment);
pushComment([ "@fileoverview " + root.comment ]);
push("");

@@ -110,3 +110,3 @@ }

function aOrAn(name) {
return ((/^[hH](?:ou|on|ei)/.test(name) || /^[aeiouAEIOU][a-z]/.test(name)) && !/^us/i.test(name)
return ((/^[hH](?:ou|on|ei)/.test(name) || /^[aeiouAEIOU][a-z]/.test(name)) && !/^(?:use?|uni([^nmd]|mo)|one|once)/i.test(name)
? "an "

@@ -354,3 +354,3 @@ : "a ") + name;

function toJsType(field, parentIsInterface = false) {
function toJsType(field, parentIsInterface = false, withNarrowing = false) {
var type;

@@ -392,3 +392,10 @@

if (field.resolve().resolvedType) {
type = exportName(field.resolvedType, asInterface);
if (field.resolvedType instanceof protobuf.Type)
type = withNarrowing
? shapeName(field.resolvedType)
: asInterface
? propertiesName(field.resolvedType)
: exportName(field.resolvedType, asInterface);
else
type = exportName(field.resolvedType, asInterface);
}

@@ -430,2 +437,13 @@ else {

function toTypePropName(name, optional) {
var prop = util.safeProp(name); // either .name or ["name"]
if (prop.charAt(0) === ".")
prop = prop.substring(1);
else
prop = JSON.parse(prop.substring(1, prop.length - 1));
if (!/^[$\w]+$/.test(prop) || util.patterns.reservedRe.test(prop))
prop = JSON.stringify(prop);
return prop + (optional ? "?" : "");
}
function isNullable(field) {

@@ -435,3 +453,99 @@ return field.hasPresence && !field.required;

function propertiesName(type) {
return exportName(type) + ".$Properties";
}
function shapeName(type) {
return exportName(type) + ".$Shape";
}
function narrowedType(type) {
return exportName(type) + " & " + shapeName(type);
}
function oneofType(oneof, selectedField) {
var props = [ toTypePropName(oneof.name, true) + ": " + (selectedField ? JSON.stringify(selectedField.name) : "undefined") ];
oneof.oneof.forEach(function(fieldName) {
var field = oneof.parent.fields[fieldName];
props.push(toTypePropName(field.name, field !== selectedField) + ": " + (field === selectedField ? toJsType(field, true, true) : "null"));
});
return "{ " + props.join("; ") + " }";
}
function oneofTypes(type) {
return type.oneofsArray.filter(function(oneof) {
return !oneof.isProto3Optional;
}).map(function(oneof) {
oneof.resolve();
var cases = [ oneofType(oneof) ];
oneof.oneof.forEach(function(fieldName) {
cases.push(oneofType(oneof, oneof.parent.fields[fieldName]));
});
return "(" + cases.join("|") + ")";
});
}
function hasNarrowing(type, seen) {
if (!seen)
seen = {};
if (seen[type.fullName])
return false;
seen[type.fullName] = true;
if (type.oneofsArray.some(function(oneof) { return !oneof.isProto3Optional; })) {
delete seen[type.fullName];
return true;
}
var narrowed = type.fieldsArray.some(function(field) {
field.resolve();
return field.resolvedType instanceof protobuf.Type && hasNarrowing(field.resolvedType, seen);
});
delete seen[type.fullName];
return narrowed;
}
function returnTags(typeName, description, tsType) {
return [ "@returns {" + (tsType || typeName) + "} " + description ];
}
function propertyType(field, withNarrowing) {
var jsType = toJsType(field, /* parentIsInterface = */ true, withNarrowing);
var nullable = false;
if (config["null-semantics"]) {
// With semantic nulls, only explicit optional fields and one-of members can be set to null
// Implicit fields (proto3), maps and lists can be omitted, but if specified must be non-null
// Implicit fields will take their default value when the message is constructed
if (field.optional) {
if (isNullable(field))
jsType = jsType + "|null";
nullable = true;
}
}
else {
// Without semantic nulls, everything is optional in proto3
// Do not allow |undefined to keep backwards compatibility
if (field.optional) {
jsType = jsType + "|null";
nullable = true;
}
}
return {
jsType: jsType,
nullable: nullable
};
}
function shapeType(type, oneofs) {
if (!hasNarrowing(type))
return propertiesName(type);
var props = [];
type.fieldsArray.forEach(function(field) {
var prop = propertyType(field, /* withNarrowing = */ true);
props.push(" " + toTypePropName(field.name, prop.nullable) + ": " + prop.jsType + ";");
});
props.push(" " + toTypePropName("$unknowns", true) + ": Array.<Uint8Array>;");
return "{\n" + props.join("\n") + "\n}" + (oneofs.length ? " & (\n " + oneofs.join("\n) & (\n ") + "\n)" : "");
}
function buildType(ref, type) {
var oneofs = oneofTypes(type);

@@ -441,36 +555,31 @@ if (config.comments) {

"Properties of " + aOrAn(type.name) + ".",
type.parent instanceof protobuf.Root ? "@exports " + escapeName("I" + type.name) : "@memberof " + exportName(type.parent),
"@interface " + escapeName("I" + type.name)
"@typedef {Object} " + propertiesName(type)
];
type.fieldsArray.forEach(function(field) {
var jsType = toJsType(field, /* parentIsInterface = */ true);
var nullable = false;
if (config["null-semantics"]) {
// With semantic nulls, only explicit optional fields and one-of members can be set to null
// Implicit fields (proto3), maps and lists can be omitted, but if specified must be non-null
// Implicit fields will take their default value when the message is constructed
if (field.optional) {
if (isNullable(field)) {
jsType = jsType + "|null|undefined";
nullable = true;
}
else {
jsType = jsType + "|undefined";
nullable = true;
}
}
}
else {
// Without semantic nulls, everything is optional in proto3
// Do not allow |undefined to keep backwards compatibility
if (field.optional) {
jsType = jsType + "|null";
nullable = true;
}
}
typeDef.push("@property {" + jsType + "} " + toPropName(field, nullable) + " " + (field.comment || type.name + " " + field.name));
var prop = propertyType(field, /* withNarrowing = */ false);
typeDef.push("@property {" + prop.jsType + "} " + toPropName(field, prop.nullable) + " " + (field.comment || type.name + " " + field.name));
});
if (oneofs.length) {
type.oneofsArray.forEach(function(oneof) {
if (oneof.isProto3Optional)
return;
typeDef.push("@property {" + oneof.oneof.map(JSON.stringify).join("|") + "} [" + oneof.name + "] " + (oneof.comment || type.name + " " + oneof.name));
});
}
typeDef.push("@property {Array.<Uint8Array>} [$unknowns] Unknown fields preserved while decoding");
push("");
pushComment(typeDef);
push("");
pushComment([
"Properties of " + aOrAn(type.name) + ".",
type.parent instanceof protobuf.Root ? "@exports " + escapeName("I" + type.name) : "@memberof " + exportName(type.parent),
"@interface " + escapeName("I" + type.name),
"@augments " + propertiesName(type),
"@deprecated Use " + propertiesName(type) + " instead."
]);
push("");
pushComment([
(oneofs.length ? "Narrowed shape" : "Shape") + " of " + aOrAn(type.name) + ".",
"@typedef {" + shapeType(type, oneofs) + "} " + shapeName(type)
]);
}

@@ -484,5 +593,4 @@

"@classdesc " + (type.comment || "Represents " + aOrAn(type.name) + "."),
config.comments ? "@implements " + escapeName("I" + type.name) : null,
"@constructor",
"@param {" + exportName(type, true) + "=} [" + (config.beautify ? "properties" : "p") + "] Properties to set"
"@param {" + propertiesName(type) + "=} [" + (config.beautify ? "properties" : "p") + "] Properties to set"
];

@@ -582,5 +690,10 @@ if (config.comments) {

"@static",
"@param {" + exportName(type, true) + "=} [properties] Properties to set",
"@param {" + propertiesName(type) + "=} [properties] Properties to set",
"@returns {" + exportName(type) + "} " + type.name + " instance"
]);
].concat([
"@type {{\n" +
" (properties: " + shapeName(type) + "): " + narrowedType(type) + ";\n" +
" (properties?: " + propertiesName(type) + "): " + exportName(type) + ";\n" +
"}}"
]));
push(escapeName(type.name) + ".create = function create(properties) {");

@@ -600,3 +713,3 @@ ++indent;

"@static",
"@param {" + exportName(type, !config.forceMessage) + "} " + (config.beautify ? "message" : "m") + " " + type.name + " message or plain object to encode",
"@param {" + (config.forceMessage ? exportName(type) : propertiesName(type)) + "} " + (config.beautify ? "message" : "m") + " " + type.name + " message or plain object to encode",
"@param {$protobuf.Writer} [" + (config.beautify ? "writer" : "w") + "] Writer to encode to",

@@ -614,3 +727,3 @@ "@returns {$protobuf.Writer} Writer"

"@static",
"@param {" + exportName(type, !config.forceMessage) + "} message " + type.name + " message or plain object to encode",
"@param {" + (config.forceMessage ? exportName(type) : propertiesName(type)) + "} message " + type.name + " message or plain object to encode",
"@param {$protobuf.Writer} [writer] Writer to encode to",

@@ -635,7 +748,7 @@ "@returns {$protobuf.Writer} Writer"

"@param {$protobuf.Reader|Uint8Array} " + (config.beautify ? "reader" : "r") + " Reader or buffer to decode from",
"@param {number} [" + (config.beautify ? "length" : "l") + "] Message length if known beforehand",
"@returns {" + exportName(type) + "} " + type.name,
"@param {number} [" + (config.beautify ? "length" : "l") + "] Message length if known beforehand"
].concat(returnTags(exportName(type), type.name, narrowedType(type))).concat([
"@throws {Error} If the payload is not a reader or valid buffer",
"@throws {$protobuf.util.ProtocolError} If required fields are missing"
]);
]));
buildFunction(type, "decode", protobuf.decoder(type));

@@ -650,7 +763,7 @@

"@static",
"@param {$protobuf.Reader|Uint8Array} reader Reader or buffer to decode from",
"@returns {" + exportName(type) + "} " + type.name,
"@param {$protobuf.Reader|Uint8Array} reader Reader or buffer to decode from"
].concat(returnTags(exportName(type), type.name, narrowedType(type))).concat([
"@throws {Error} If the payload is not a reader or valid buffer",
"@throws {$protobuf.util.ProtocolError} If required fields are missing"
]);
]));
push(escapeName(type.name) + ".decodeDelimited = function decodeDelimited(reader) {");

@@ -657,0 +770,0 @@ ++indent;