From 44b4af1ca190e31b3830e860dcffaee31e865051 Mon Sep 17 00:00:00 2001 From: Stephen Cresswell <229672+cressie176@users.noreply.github.com> Date: Thu, 4 Sep 2025 17:20:50 +0100 Subject: [PATCH 1/3] Replace string concatenation with modern template literals MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Enables the useTemplate Biome lint rule and converts all string concatenation patterns to use ES6 template literals for improved readability and maintainability. Excludes lib/defs.js from this rule as it is generated code. 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude --- CHANGELOG.md | 1 + bin/generate-defs.js | 30 +++++++++++++++--------------- lib/callback_model.js | 2 +- lib/channel.js | 6 +++--- lib/codec.js | 6 +++--- lib/connect.js | 4 ++-- lib/connection.js | 2 +- lib/error.js | 4 ++-- lib/format.js | 2 +- lib/frame.js | 4 ++-- 10 files changed, 31 insertions(+), 30 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index a44b879b..be6fee36 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -15,6 +15,7 @@ - Enforce const usage for variables that are never reassigned - Add node: protocol prefix to Node.js builtin module imports for clarity - Use modern exponentiation operator (**) instead of Math.pow() +- Replace string concatenation with modern template literals ## v0.10.9 - Add support for IPv6 urls diff --git a/bin/generate-defs.js b/bin/generate-defs.js index ada7d3b2..fc857f15 100644 --- a/bin/generate-defs.js +++ b/bin/generate-defs.js @@ -70,7 +70,7 @@ for (let i = 0, len = defs.classes.length; i < len; i++) { for (let j = 0, num = clazz.methods.length; j < num; j++) { const method = clazz.methods[j]; const name = methodName(clazz, method); - const info = 'methodInfo' + name; + const info = `methodInfo${name}`; methods[name] = { id: methodId(clazz, method), @@ -80,8 +80,8 @@ for (let i = 0, len = defs.classes.length; i < len; i++) { clazz: clazz.name, args: method['arguments'].map(argument), isReply: method.answer, - encoder: 'encode' + name, - decoder: 'decode' + name, + encoder: `encode${name}`, + decoder: `decode${name}`, info: info, }; } @@ -91,9 +91,9 @@ for (let i = 0, len = defs.classes.length; i < len; i++) { propertieses[name] = { id: clazz.id, name: name, - encoder: 'encode' + name, - decoder: 'decode' + name, - info: 'propertiesInfo' + name, + encoder: `encode${name}`, + decoder: `decode${name}`, + info: `propertiesInfo${name}`, args: props.map(argument), }; } @@ -203,7 +203,7 @@ function methodId(clazz, method) { } function propertiesName(clazz) { - return initial(clazz.name) + 'Properties'; + return `${initial(clazz.name)}Properties`; } function valTypeTest(arg) { @@ -298,11 +298,11 @@ function assignTable(a) { } function tableVar(a) { - return a.name + '_encoded'; + return `${a.name}_encoded`; } function stringLenVar(a) { - return a.name + '_len'; + return `${a.name}_len`; } function assignStringLen(a) { @@ -438,7 +438,7 @@ function encoderFn(method) { println('offset += %s.copy(buffer, offset);', tableVar(a)); break; default: - throw new Error('Unexpected argument type: ' + a.type); + throw new Error(`Unexpected argument type: ${a.type}`); } } @@ -473,7 +473,7 @@ function decoderFn(method) { for (let i = 0, num = args.length; i < num; i++) { const a = args[i]; - const field = "fields['" + a.name + "']"; + const field = `fields['${a.name}']`; // Flush any collected bits before doing a new field if (a.type != 'bit' && bitsInARow > 0) { @@ -520,7 +520,7 @@ function decoderFn(method) { println('offset += len;'); break; default: - throw new TypeError('Unexpected type in argument list: ' + a.type); + throw new TypeError(`Unexpected type in argument list: ${a.type}`); } println('%s = val;', field); } @@ -661,7 +661,7 @@ function encodePropsFn(props) { println('offset += %s.copy(buffer, offset);', tableVar(p)); break; default: - throw new Error('Unexpected argument type: ' + p.type); + throw new Error(`Unexpected argument type: ${p.type}`); } } println('}'); // != undefined @@ -688,7 +688,7 @@ function decodePropsFn(props) { for (let i = 0, num = args.length; i < num; i++) { const p = argument(args[i]); - const field = "fields['" + p.name + "']"; + const field = `fields['${p.name}']`; println('if (flags & %d) {', flagAt(i)); if (p.type === 'bit') { @@ -724,7 +724,7 @@ function decodePropsFn(props) { println('offset += len;'); break; default: - throw new TypeError('Unexpected type in argument list: ' + p.type); + throw new TypeError(`Unexpected type in argument list: ${p.type}`); } println('%s = val;', field); } diff --git a/lib/callback_model.js b/lib/callback_model.js index 90c41550..591c5963 100644 --- a/lib/callback_model.js +++ b/lib/callback_model.js @@ -198,7 +198,7 @@ class Channel extends BaseChannel { cb(null, m); }); } else { - cb(new Error('Unexpected response to BasicGet: ' + inspect(f))); + cb(new Error(`Unexpected response to BasicGet: ${inspect(f)}`)); } } }); diff --git a/lib/channel.js b/lib/channel.js index 1d08d188..9af453b6 100644 --- a/lib/channel.js +++ b/lib/channel.js @@ -188,7 +188,7 @@ class Channel extends EventEmitter { methodId: 0, classId: 0, }); - const s = stackCapture('closeBecause called: ' + reason); + const s = stackCapture(`closeBecause called: ${reason}`); this.toClosing(s, k); } @@ -296,7 +296,7 @@ class Channel extends EventEmitter { this.reply = null; reply(f); } - const emsg = 'Channel closed by server: ' + closeMsg(f); + const emsg = `Channel closed by server: ${closeMsg(f)}`; this.sendImmediately(defs.ChannelCloseOk, {}); const error = new Error(emsg); @@ -459,7 +459,7 @@ class BaseChannel extends Channel { return consumer(message); } else { // %%% Surely a race here - throw new Error('Unknown consumer: ' + consumerTag); + throw new Error(`Unknown consumer: ${consumerTag}`); } } diff --git a/lib/codec.js b/lib/codec.js index 181fac2c..5ef40f10 100644 --- a/lib/codec.js +++ b/lib/codec.js @@ -250,10 +250,10 @@ function encodeFieldValue(buffer, value, offset) { offset++; buffer.writeUInt32BE(val.digits, offset); offset += 4; - } else throw new TypeError("Decimal value must be {'places': 0..255, 'digits': uint32}, " + 'got ' + JSON.stringify(val)); + } else throw new TypeError(`Decimal value must be {'places': 0..255, 'digits': uint32}, got ${JSON.stringify(val)}`); break; default: - throw new TypeError('Unknown type to encode: ' + type); + throw new TypeError(`Unknown type to encode: ${type}`); } return offset - start; } @@ -352,7 +352,7 @@ function decodeFields(slice) { offset += len; break; default: - throw new TypeError('Unexpected type tag "' + tag + '"'); + throw new TypeError(`Unexpected type tag "${tag}"`); } } diff --git a/lib/connect.js b/lib/connect.js index c0efc261..5d13eefb 100644 --- a/lib/connect.js +++ b/lib/connect.js @@ -103,7 +103,7 @@ function connect(url, socketOptions, openCallback) { let protocol, fields; if (typeof url === 'object') { - protocol = (url.protocol || 'amqp') + ':'; + protocol = `${url.protocol || 'amqp'}:`; sockopts.host = url.hostname; sockopts.servername = sockopts.servername || url.hostname; sockopts.port = url.port || (protocol === 'amqp:' ? 5672 : 5671); @@ -167,7 +167,7 @@ function connect(url, socketOptions, openCallback) { } else if (protocol === 'amqps:') { sock = require('node:tls').connect(sockopts, onConnect); } else { - throw new Error('Expected amqp: or amqps: as the protocol; got ' + protocol); + throw new Error(`Expected amqp: or amqps: as the protocol; got ${protocol}`); } if (timeout) { diff --git a/lib/connection.js b/lib/connection.js index 4c9e9918..f4d4cded 100644 --- a/lib/connection.js +++ b/lib/connection.js @@ -281,7 +281,7 @@ class Connection extends EventEmitter { methodId: 0, classId: 0, }); - const s = stackCapture('closeBecause called: ' + reason); + const s = stackCapture(`closeBecause called: ${reason}`); this.toClosing(s, k); } diff --git a/lib/error.js b/lib/error.js index a3e864e2..9d28114c 100644 --- a/lib/error.js +++ b/lib/error.js @@ -7,7 +7,7 @@ function trimStack(stack, num) { function IllegalOperationError(msg, stack) { const tmp = new Error(); this.message = msg; - this.stack = this.toString() + '\n' + trimStack(tmp.stack, 2); + this.stack = `${this.toString()}\n${trimStack(tmp.stack, 2)}`; this.stackAtStateChange = stack; } inherits(IllegalOperationError, Error); @@ -16,7 +16,7 @@ IllegalOperationError.prototype.name = 'IllegalOperationError'; function stackCapture(reason) { const e = new Error(); - return 'Stack capture: ' + reason + '\n' + trimStack(e.stack, 2); + return `Stack capture: ${reason}\n${trimStack(e.stack, 2)}`; } module.exports.IllegalOperationError = IllegalOperationError; diff --git a/lib/format.js b/lib/format.js index 3129e159..bd69786d 100644 --- a/lib/format.js +++ b/lib/format.js @@ -26,6 +26,6 @@ module.exports.inspect = function (frame, showFields) { return format('', frame.channel, frame.size); } else { const info = defs.info(frame.id); - return format('<%s channel:%d%s>', info.name, frame.channel, showFields ? ' ' + JSON.stringify(frame.fields, undefined, 2) : ''); + return format('<%s channel:%d%s>', info.name, frame.channel, showFields ? ` ${JSON.stringify(frame.fields, undefined, 2)}` : ''); } }; diff --git a/lib/frame.js b/lib/frame.js index 919e397d..74dfe549 100644 --- a/lib/frame.js +++ b/lib/frame.js @@ -9,7 +9,7 @@ const defs = require('./defs'); const constants = defs.constants; const decode = defs.decode; -module.exports.PROTOCOL_HEADER = 'AMQP' + String.fromCharCode(0, 0, 9, 1); +module.exports.PROTOCOL_HEADER = `AMQP${String.fromCharCode(0, 0, 9, 1)}`; /* Frame format: @@ -162,7 +162,7 @@ module.exports.decodeFrame = (frame) => { case FRAME_HEARTBEAT: return HEARTBEAT; default: - throw new Error('Unknown frame type ' + frame.type); + throw new Error(`Unknown frame type ${frame.type}`); } }; From 5d1c528b1e3d7436fe82d2f7e9c4a2bfecbadae3 Mon Sep 17 00:00:00 2001 From: Stephen Cresswell <229672+cressie176@users.noreply.github.com> Date: Thu, 4 Sep 2025 17:21:53 +0100 Subject: [PATCH 2/3] Remove explicit useTemplate rule as it is enabled by default MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The useTemplate rule is enabled by default in Biome, so the explicit configuration is not needed. This simplifies the biome.json configuration file. 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude --- biome.json | 3 --- 1 file changed, 3 deletions(-) diff --git a/biome.json b/biome.json index 940dd3e7..a0469411 100644 --- a/biome.json +++ b/biome.json @@ -19,9 +19,6 @@ "noArguments": "off", "useLiteralKeys": "off" }, - "style": { - "useTemplate": "off" - }, "suspicious": { "noRedundantUseStrict": "off", "noAssignInExpressions": "off", From 9c045785b5fa9c2ea9e0658ff15e6300bda5a95c Mon Sep 17 00:00:00 2001 From: Stephen Cresswell <229672+cressie176@users.noreply.github.com> Date: Thu, 4 Sep 2025 17:27:07 +0100 Subject: [PATCH 3/3] Apply useTemplate rule to test directory MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Convert string concatenation to template literals in test files to maintain consistency across the entire codebase. 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude --- test/callback_api.js | 8 ++++---- test/channel.js | 2 +- test/channel_api.js | 6 +++--- test/codec.js | 8 ++++---- test/connect.js | 4 ++-- test/connection.js | 2 +- test/data.js | 2 +- test/util.js | 6 +++--- 8 files changed, 19 insertions(+), 19 deletions(-) diff --git a/test/callback_api.js b/test/callback_api.js index 93898d7c..89c19191 100644 --- a/test/callback_api.js +++ b/test/callback_api.js @@ -46,7 +46,7 @@ function twice(done) { // Adapt 'done' to a callback that's expected to fail function failCallback(done) { return function (err, _) { - if (err == null) done(new Error('Expected failure, got ' + val)); + if (err == null) done(new Error(`Expected failure, got ${val}`)); else done(); }; } @@ -199,7 +199,7 @@ suite('sending messages', function () { q.queue, function (m) { if (m.content.toString() == msg) done(); - else done(new Error("message content doesn't match:" + msg + ' =/= ' + m.content.toString())); + else done(new Error(`message content doesn't match:${msg} =/= ${m.content.toString()}`)); }, {noAck: true, exclusive: true}, ); @@ -217,7 +217,7 @@ suite('sending messages', function () { if (m.content.toString() == msg) { ch.ack(m); done(); - } else done(new Error("message content doesn't match:" + msg + ' =/= ' + m.content.toString())); + } else done(new Error(`message content doesn't match:${msg} =/= ${m.content.toString()}`)); }, {noAck: false, exclusive: true}, ); @@ -236,7 +236,7 @@ suite('sending messages', function () { if (e != null) return done(e); else if (!m) return done(new Error('Empty (false) not expected')); else if (m.content.toString() == msg) return done(); - else return done(new Error('Messages do not match: ' + msg + ' =/= ' + m.content.toString())); + else return done(new Error(`Messages do not match: ${msg} =/= ${m.content.toString()}`)); }); }); }); diff --git a/test/channel.js b/test/channel.js index 782e6933..f69dfcec 100644 --- a/test/channel.js +++ b/test/channel.js @@ -690,7 +690,7 @@ suite('channel machinery', function () { function confirmTest(variety, Method) { return test( - 'confirm ' + variety, + `confirm ${variety}`, channelTest( function (ch, done) { ch.on(variety, function (f) { diff --git a/test/channel_api.js b/test/channel_api.js index 7f9c1542..b4748e4d 100644 --- a/test/channel_api.js +++ b/test/channel_api.js @@ -105,11 +105,11 @@ suite('assert, check, delete', function () { }); chtest("fail on checking a queue that's not there", function (ch) { - return expectFail(ch.checkQueue('test.random-' + randomString())); + return expectFail(ch.checkQueue(`test.random-${randomString()}`)); }); chtest("fail on checking an exchange that's not there", function (ch) { - return expectFail(ch.checkExchange('test.random-' + randomString())); + return expectFail(ch.checkExchange(`test.random-${randomString()}`)); }); chtest('fail on reasserting exchange with different type', function (ch) { @@ -587,7 +587,7 @@ suite('confirms', function () { return Promise.all(cs).then(function () { if (multipleRainbows) return true; - else if (num > 500) throw new Error("Couldn't provoke the server" + ' into multi-acking with ' + num + ' messages; giving up'); + else if (num > 500) throw new Error(`Couldn't provoke the server into multi-acking with ${num} messages; giving up`); else { //console.warn("Failed with " + num + "; trying " + num * 2); return prod(num * 2); diff --git a/test/codec.js b/test/codec.js index 043e5e3d..5523e9b9 100644 --- a/test/codec.js +++ b/test/codec.js @@ -137,7 +137,7 @@ suite('Roundtrip values', function () { amqp.FieldArray, amqp.FieldTable, ].forEach(function (T) { - test(T.toString() + ' roundtrip', roundtrips(T).asTest()); + test(`${T.toString()} roundtrip`, roundtrips(T).asTest()); }); }); @@ -208,7 +208,7 @@ function assertEqualModuloDefaults(original, decodedFields) { } } catch (assertionErr) { const methodOrProps = defs.info(original.id).name; - assertionErr.message += ' (frame ' + methodOrProps + ' field ' + arg.name + ')'; + assertionErr.message += ` (frame ${methodOrProps} field ${arg.name})`; throw assertionErr; } } @@ -242,12 +242,12 @@ function roundtripProperties(Properties) { suite('Roundtrip methods', function () { amqp.methods.forEach(function (Method) { - test(Method.toString() + ' roundtrip', roundtripMethod(Method).asTest()); + test(`${Method.toString()} roundtrip`, roundtripMethod(Method).asTest()); }); }); suite('Roundtrip properties', function () { amqp.properties.forEach(function (Properties) { - test(Properties.toString() + ' roundtrip', roundtripProperties(Properties).asTest()); + test(`${Properties.toString()} roundtrip`, roundtripProperties(Properties).asTest()); }); }); diff --git a/test/connect.js b/test/connect.js index 80ef6132..8ada9d98 100644 --- a/test/connect.js +++ b/test/connect.js @@ -173,7 +173,7 @@ suite('Errors on connect', function () { server = net .createServer(function (socket) { socket.once('data', function (protocolHeader) { - assert.deepStrictEqual(protocolHeader, Buffer.from('AMQP' + String.fromCharCode(0, 0, 9, 1))); + assert.deepStrictEqual(protocolHeader, Buffer.from(`AMQP${String.fromCharCode(0, 0, 9, 1)}`)); util.runServer(socket, function (send, wait) { send(defs.ConnectionStart, { versionMajor: 0, @@ -200,7 +200,7 @@ suite('Errors on connect', function () { }) .listen(0); - connect('amqp://localhost:' + server.address().port, {}, function (err) { + connect(`amqp://localhost:${server.address().port}`, {}, function (err) { if (!err) bothDone(new Error('Expected authentication error')); bothDone(); }); diff --git a/test/connection.js b/test/connection.js index dcb00b93..bd54dea9 100644 --- a/test/connection.js +++ b/test/connection.js @@ -64,7 +64,7 @@ function connectionTest(client, server) { // NB only not a race here because the writes are synchronous const protocolHeader = pair.server.read(8); - assert.deepEqual(Buffer.from('AMQP' + String.fromCharCode(0, 0, 9, 1)), protocolHeader); + assert.deepEqual(Buffer.from(`AMQP${String.fromCharCode(0, 0, 9, 1)}`), protocolHeader); util.runServer(pair.server, function (send, wait) { server(send, wait, bothDone, pair.server); diff --git a/test/data.js b/test/data.js index bcd76153..f0a01697 100644 --- a/test/data.js +++ b/test/data.js @@ -309,7 +309,7 @@ const domainProps = [ suite('Domains', function () { domainProps.forEach(function (p) { - test(p[0] + ' domain', forAll(p[0]).satisfy(p[1]).asTest({times: 500})); + test(`${p[0]} domain`, forAll(p[0]).satisfy(p[1]).asTest({times: 500})); }); }); diff --git a/test/util.js b/test/util.js index e5b70866..2bcac09d 100644 --- a/test/util.js +++ b/test/util.js @@ -75,7 +75,7 @@ function runServer(socket, run) { frames.step(function (e, f) { if (e !== null) return reject(e); if (f.id === method) resolve(f); - else reject(new Error('Expected method: ' + method + ', got ' + f.id)); + else reject(new Error(`Expected method: ${method}, got ${f.id}`)); }); } else { frames.step(function (e, f) { @@ -106,7 +106,7 @@ function succeedIfAttributeEquals(attribute, value, done) { return done(); } - done(new Error(attribute + ' is not equal to ' + value)); + done(new Error(`${attribute} is not equal to ${value}`)); }; } @@ -116,7 +116,7 @@ function succeedIfAttributeEquals(attribute, value, done) { function fail(done) { return function (err) { if (err instanceof Error) done(err); - else done(new Error('Expected to fail, instead got ' + err.toString())); + else done(new Error(`Expected to fail, instead got ${err.toString()}`)); }; }