Repository: lerna-lite/lerna-lite
Pull request #1085 (2025-07-08 00:17:46)

feat(deps): drop `strong-log-transformer` dependency
<!--- Provide a general summary of your changes in the Title above -->

## Description

Drop `strong-log-transformer` dependency 

## Motivation and Context

`strong-log-transformer` has this deprecation warning that existed for a while now

> DeprecationWarning: The `util._extend` API is deprecated. Please use `Object.assign()` instead

This PR follows the same approach as this Nx [PR](https://github.com/nrwl/nx/pull/28094) so that we can drop the dependency entirely and get rid of this deprecation warning

## How Has This Been Tested?

<!--- Please describe in detail how you tested your changes. -->
<!--- Include details of your testing environment, and the tests you ran to -->
<!--- see how your change affects other areas of the code, etc. -->

## Types of changes

<!--- What types of changes does your code introduce? Put an `x` in all the boxes that apply: -->

- [ ] Chore (change that has absolutely no effect on users)
- [ ] Bug fix (non-breaking change which fixes an issue)
- [x] New feature (non-breaking change which adds functionality)
- [ ] Breaking change (fix or feature that would cause existing functionality to change)

## Checklist:

<!--- Go over all the following points, and put an `x` in all the boxes that apply. -->
<!--- If you're unsure about any of these, don't hesitate to ask. We're here to help! -->

- [x] My code follows the code style of this project.
- [ ] My change requires a change to the documentation.
- [ ] I have updated the documentation accordingly.
- [ ] I have added tests to cover my changes.
- [x] All new and existing tests passed.


pkg-pr-new[bot]: 



<details><summary><b>@lerna-lite/changed</b></summary><p>

```
npm i https://pkg.pr.new/lerna-lite/lerna-lite/@lerna-lite/changed@1085
```

</p></details>
      
    

<details><summary><b>@lerna-lite/cli</b></summary><p>

```
npm i https://pkg.pr.new/lerna-lite/lerna-lite/@lerna-lite/cli@1085
```

</p></details>
      
    

<details><summary><b>@lerna-lite/core</b></summary><p>

```
npm i https://pkg.pr.new/lerna-lite/lerna-lite/@lerna-lite/core@1085
```

</p></details>
      
    

<details><summary><b>@lerna-lite/diff</b></summary><p>

```
npm i https://pkg.pr.new/lerna-lite/lerna-lite/@lerna-lite/diff@1085
```

</p></details>
      
    

<details><summary><b>@lerna-lite/exec</b></summary><p>

```
npm i https://pkg.pr.new/lerna-lite/lerna-lite/@lerna-lite/exec@1085
```

</p></details>
      
    

<details><summary><b>@lerna-lite/init</b></summary><p>

```
npm i https://pkg.pr.new/lerna-lite/lerna-lite/@lerna-lite/init@1085
```

</p></details>
      
    

<details><summary><b>@lerna-lite/list</b></summary><p>

```
npm i https://pkg.pr.new/lerna-lite/lerna-lite/@lerna-lite/list@1085
```

</p></details>
      
    

<details><summary><b>@lerna-lite/listable</b></summary><p>

```
npm i https://pkg.pr.new/lerna-lite/lerna-lite/@lerna-lite/listable@1085
```

</p></details>
      
    

<details><summary><b>@lerna-lite/npmlog</b></summary><p>

```
npm i https://pkg.pr.new/lerna-lite/lerna-lite/@lerna-lite/npmlog@1085
```

</p></details>
      
    

<details><summary><b>@lerna-lite/profiler</b></summary><p>

```
npm i https://pkg.pr.new/lerna-lite/lerna-lite/@lerna-lite/profiler@1085
```

</p></details>
      
    

<details><summary><b>@lerna-lite/publish</b></summary><p>

```
npm i https://pkg.pr.new/lerna-lite/lerna-lite/@lerna-lite/publish@1085
```

</p></details>
      
    

<details><summary><b>@lerna-lite/run</b></summary><p>

```
npm i https://pkg.pr.new/lerna-lite/lerna-lite/@lerna-lite/run@1085
```

</p></details>
      
    

<details><summary><b>@lerna-lite/version</b></summary><p>

```
npm i https://pkg.pr.new/lerna-lite/lerna-lite/@lerna-lite/version@1085
```

</p></details>
      
    

<details><summary><b>@lerna-lite/watch</b></summary><p>

```
npm i https://pkg.pr.new/lerna-lite/lerna-lite/@lerna-lite/watch@1085
```

</p></details>
      
    

_commit: <a href="https://github.com/lerna-lite/lerna-lite/runs/45517583438"><code>356c466</code></a>_

codecov[bot]: ## [Codecov](https://app.codecov.io/gh/lerna-lite/lerna-lite/pull/1085?dropdown=coverage&src=pr&el=h1&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=lerna-lite) Report
Attention: Patch coverage is `69.23077%` with `4 lines` in your changes missing coverage. Please review.
> Project coverage is 97.98%. Comparing base [(`096e390`)](https://app.codecov.io/gh/lerna-lite/lerna-lite/commit/096e39040ec406b8106b86bed08cf57035e22574?dropdown=coverage&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=lerna-lite) to head [(`356c466`)](https://app.codecov.io/gh/lerna-lite/lerna-lite/commit/356c46664756ea72383d9498ceaf3b7e9d5bd1b6?dropdown=coverage&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=lerna-lite).

| [Files with missing lines](https://app.codecov.io/gh/lerna-lite/lerna-lite/pull/1085?dropdown=coverage&src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=lerna-lite) | Patch % | Lines |
|---|---|---|
| [packages/core/src/child-process.ts](https://app.codecov.io/gh/lerna-lite/lerna-lite/pull/1085?src=pr&el=tree&filepath=packages%2Fcore%2Fsrc%2Fchild-process.ts&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=lerna-lite#diff-cGFja2FnZXMvY29yZS9zcmMvY2hpbGQtcHJvY2Vzcy50cw==) | 69.24% | [4 Missing :warning: ](https://app.codecov.io/gh/lerna-lite/lerna-lite/pull/1085?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=lerna-lite) |

<details><summary>Additional details and impacted files</summary>


```diff
@@            Coverage Diff             @@
##             main    #1085      +/-   ##
==========================================
- Coverage   98.02%   97.98%   -0.04%     
==========================================
  Files         154      154              
  Lines        9210     9220      +10     
  Branches     2378     2380       +2     
==========================================
+ Hits         9027     9033       +6     
- Misses        182      186       +4     
  Partials        1        1              
```

</details>

[:umbrella: View full report in Codecov by Sentry](https://app.codecov.io/gh/lerna-lite/lerna-lite/pull/1085?dropdown=coverage&src=pr&el=continue&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=lerna-lite).   
:loudspeaker: Have feedback on the report? [Share it here](https://about.codecov.io/codecov-pr-comment-feedback/?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=lerna-lite).

<details><summary> :rocket: New features to boost your workflow: </summary>

- :snowflake: [Test Analytics](https://docs.codecov.com/docs/test-analytics): Detect flaky tests, report on failures, and find test suite problems.
- :package: [JS Bundle Analysis](https://docs.codecov.com/docs/javascript-bundle-analysis): Save yourself from yourself by tracking and limiting bundle sizes in JS merges.
</details>

Patch:
diff --git a/packages/core/package.json b/packages/core/package.json
index 06ea93c0..535e90bf 100644
--- a/packages/core/package.json
+++ b/packages/core/package.json
@@ -58,7 +58,6 @@
     "resolve-from": "^5.0.0",
     "semver": "catalog:",
     "slash": "catalog:",
-    "strong-log-transformer": "^2.1.0",
     "tinyglobby": "catalog:",
     "tinyrainbow": "catalog:",
     "write-file-atomic": "^6.0.0",
@@ -75,7 +74,6 @@
     "@types/inquirer": "^9.0.8",
     "@types/npm-package-arg": "catalog:",
     "@types/semver": "catalog:",
-    "@types/strong-log-transformer": "^1.0.2",
     "@types/write-file-atomic": "^4.0.3",
     "npm-registry-fetch": "catalog:",
     "yargs": "catalog:",
diff --git a/packages/core/src/child-process.ts b/packages/core/src/child-process.ts
index a0827adb..379ed5ce 100644
--- a/packages/core/src/child-process.ts
+++ b/packages/core/src/child-process.ts
@@ -3,10 +3,10 @@ import { constants } from 'node:os';
 import { log } from '@lerna-lite/npmlog';
 import type { Options as ExecaOptions, ResultPromise, SyncOptions as ExacaSyncOptions } from 'execa';
 import { execa, execaSync } from 'execa';
-import logTransformer from 'strong-log-transformer';
 import c from 'tinyrainbow';
 
 import type { Package } from './package.js';
+import { addPrefixTransformer } from './utils/log-prefix-transformer.js';
 
 // bookkeeping for spawned processes
 const children = new Set();
@@ -100,8 +100,8 @@ export function spawnStreaming(
     process.stderr.setMaxListeners(children.size);
   }
 
-  spawned.stdout?.pipe(logTransformer(stdoutOpts)).pipe(process.stdout);
-  spawned.stderr?.pipe(logTransformer(stderrOpts)).pipe(process.stderr);
+  spawned.stdout?.pipe(addPrefixTransformer(stdoutOpts)).pipe(process.stdout);
+  spawned.stderr?.pipe(addPrefixTransformer(stderrOpts)).pipe(process.stderr);
 
   return wrapError(spawned);
 }
diff --git a/packages/core/src/utils/log-prefix-transformer.ts b/packages/core/src/utils/log-prefix-transformer.ts
new file mode 100644
index 00000000..d357e6ef
--- /dev/null
+++ b/packages/core/src/utils/log-prefix-transformer.ts
@@ -0,0 +1,22 @@
+import { Transform } from 'stream';
+
+/**
+ * Creates a Transform stream that adds a specified prefix to each line of input data.
+ * This function is designed to replace the `strong-log-transformer` and provides a way to
+ * format output by prepending a prefix to each line of text. It is particularly useful for
+ * enhancing log messages or command output by clearly identifying the source or context of
+ * the messages.
+ *
+ * @param {string} [prefix] - The prefix to be added to the beginning of each line. If no
+ * prefix is provided, the output will remain unchanged.
+ */
+export function addPrefixTransformer(prefix?: string) {
+  const newLineSeparator = process.platform.startsWith('win') ? '\r\n' : '\n';
+  return new Transform({
+    transform(chunk: Buffer | string, _encoding: BufferEncoding, callback: Function) {
+      const list = chunk.toString().split(/\r\n|[\n\v\f\r\x85\u2028\u2029]/g);
+      list.filter(Boolean).forEach((m) => this.push(prefix ? prefix + ' ' + m + newLineSeparator : m + newLineSeparator));
+      callback();
+    },
+  });
+}
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index 8e70cfdf..d5c6115a 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -355,9 +355,6 @@ importers:
       slash:
         specifier: 'catalog:'
         version: 5.1.0
-      strong-log-transformer:
-        specifier: ^2.1.0
-        version: 2.1.0
       tinyglobby:
         specifier: 'catalog:'
         version: 0.2.14
@@ -401,9 +398,6 @@ importers:
       '@types/semver':
         specifier: 'catalog:'
         version: 7.7.0
-      '@types/strong-log-transformer':
-        specifier: ^1.0.2
-        version: 1.0.2
       '@types/write-file-atomic':
         specifier: ^4.0.3
         version: 4.0.3
@@ -1559,9 +1553,6 @@ packages:
   '@types/semver@7.7.0':
     resolution: {integrity: sha512-k107IF4+Xr7UHjwDc7Cfd6PRQfbdkiRabXGRjo07b4WyPahFBZCZ1sE+BNxYIJPPg73UkfOsVOLwqVc/6ETrIA==}
 
-  '@types/strong-log-transformer@1.0.2':
-    resolution: {integrity: sha512-z3vq3aAB5Y5D/AcrKZa5j5bUQVAMcJuzV8NJfzPzRs+hk5mdJy5bgvthlpK8Wr3hiXqpkViNu9M18dnkGvmsPA==}
-
   '@types/through@0.0.33':
     resolution: {integrity: sha512-HsJ+z3QuETzP3cswwtzt2vEIiHBk/dCcHGhbmG5X3ecnwFD/lPrMpliGXxSCg03L9AhrdwA4Oz/qfspkDW+xGQ==}
 
@@ -2076,9 +2067,6 @@ packages:
     resolution: {integrity: sha512-A0BJ5lrpJVSfnMMXjmeO0xUnoxqsBHWCoqqTnGwGYVdnctqXXUEhJOO7LxmgxJon9tEZFGpe0xPRX0h2v3AANQ==}
     engines: {node: '>=12'}
 
-  duplexer@0.1.2:
-    resolution: {integrity: sha512-jtD6YG370ZCIi/9GTaJKQxWTZD045+4R4hTk/x1UyoqadyJ9x9CgSi1RlVDQF8U2sxLLSnFkCaMihqljHIWgMg==}
-
   eastasianwidth@0.2.0:
     resolution: {integrity: sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==}
 
@@ -3222,11 +3210,6 @@ packages:
   strip-literal@3.0.0:
     resolution: {integrity: sha512-TcccoMhJOM3OebGhSBEmp3UZ2SfDMZUEBdRA/9ynfLi8yYajyWX3JiXArcJt4Umh4vISpspkQIY8ZZoCqjbviA==}
 
-  strong-log-transformer@2.1.0:
-    resolution: {integrity: sha512-B3Hgul+z0L9a236FAUC9iZsL+nVHgoCJnqCbN588DjYxvGXaXaaFbfmQ/JhvKjZwsOukuR72XbHv71Qkug0HxA==}
-    engines: {node: '>=4'}
-    hasBin: true
-
   supports-color@7.2.0:
     resolution: {integrity: sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==}
     engines: {node: '>=8'}
@@ -3251,9 +3234,6 @@ packages:
     resolution: {integrity: sha512-pFYqmTw68LXVjeWJMST4+borgQP2AyMNbg1BpZh9LbyhUeNkeaPF9gzfPGUAnSMV3qPYdWUwDIjjCLiSDOl7vg==}
     engines: {node: '>=18'}
 
-  through@2.3.8:
-    resolution: {integrity: sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg==}
-
   tinybench@2.9.0:
     resolution: {integrity: sha512-0+DUvqWMValLmha6lr4kD8iAMK1HzV0/aKnCtWb9v9641TnP/MFb7Pc2bxoxQjTXAErryXVgUOfv2YqNllqGeg==}
 
@@ -4248,10 +4228,6 @@ snapshots:
 
   '@types/semver@7.7.0': {}
 
-  '@types/strong-log-transformer@1.0.2':
-    dependencies:
-      '@types/node': 22.15.17
-
   '@types/through@0.0.33':
     dependencies:
       '@types/node': 22.15.33
@@ -4815,8 +4791,6 @@ snapshots:
 
   dotenv@17.0.0: {}
 
-  duplexer@0.1.2: {}
-
   eastasianwidth@0.2.0: {}
 
   emoji-regex@10.4.0: {}
@@ -6006,12 +5980,6 @@ snapshots:
     dependencies:
       js-tokens: 9.0.1
 
-  strong-log-transformer@2.1.0:
-    dependencies:
-      duplexer: 0.1.2
-      minimist: 1.2.8
-      through: 2.3.8
-
   supports-color@7.2.0:
     dependencies:
       has-flag: 4.0.0
@@ -6049,8 +6017,6 @@ snapshots:
       glob: 10.4.5
       minimatch: 9.0.5
 
-  through@2.3.8: {}
-
   tinybench@2.9.0: {}
 
   tinyexec@0.3.2: {}


Test patch:
diff --git a/packages/core/src/utils/__tests__/log-prefix-transformer.spec.ts b/packages/core/src/utils/__tests__/log-prefix-transformer.spec.ts
new file mode 100644
index 00000000..8c864de7
--- /dev/null
+++ b/packages/core/src/utils/__tests__/log-prefix-transformer.spec.ts
@@ -0,0 +1,68 @@
+import { describe, expect, it } from 'vitest';
+
+import { addPrefixTransformer } from '../log-prefix-transformer.js';
+
+describe('addPrefixTransformer', () => {
+  const newLineSeparator = process.platform.startsWith('win') ? '\r\n' : '\n';
+
+  it('should add a prefix to each line of output', () =>
+    new Promise((done: any) => {
+      const prefix = 'PREFIX';
+      const transformer = addPrefixTransformer(prefix);
+
+      const input = 'line1\nline2\nline3';
+      const expectedOutput = `PREFIX line1${newLineSeparator}PREFIX line2${newLineSeparator}PREFIX line3${newLineSeparator}`;
+
+      const chunks: string[] = [];
+      transformer.on('data', (chunk: Buffer) => {
+        chunks.push(chunk.toString());
+      });
+
+      transformer.on('end', () => {
+        expect(chunks.join('')).toBe(expectedOutput);
+        done();
+      });
+
+      transformer.write(input);
+      transformer.end();
+    }));
+
+  it('should handle empty input', () =>
+    new Promise((done: any) => {
+      const transformer = addPrefixTransformer('PREFIX');
+
+      const chunks: string[] = [];
+      transformer.on('data', (chunk: Buffer) => {
+        chunks.push(chunk.toString());
+      });
+
+      transformer.on('end', () => {
+        expect(chunks.join('')).toBe('');
+        done();
+      });
+
+      transformer.write('');
+      transformer.end();
+    }));
+
+  it('should not add a prefix if no prefix is provided', () =>
+    new Promise((done: any) => {
+      const transformer = addPrefixTransformer();
+
+      const input = 'line1\nline2\nline3';
+      const expectedOutput = `line1${newLineSeparator}line2${newLineSeparator}line3${newLineSeparator}`;
+
+      const chunks: string[] = [];
+      transformer.on('data', (chunk: Buffer) => {
+        chunks.push(chunk.toString());
+      });
+
+      transformer.on('end', () => {
+        expect(chunks.join('')).toBe(expectedOutput);
+        done();
+      });
+
+      transformer.write(input);
+      transformer.end();
+    }));
+});