mb-run 1.1.1 → 1.1.2-dev-20261017-8d2a09b

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -23,6 +23,29 @@ If you like this project and find it useful, please consider giving it a star on
23
23
 
24
24
  <a href="https://www.buymeacoffee.com/luligugithub"><img src="https://matterbridge.io/assets/bmc-button.svg" alt="Buy me a coffee" width="120"></a>
25
25
 
26
+ ## [1.1.2] - Dev branch
27
+
28
+ ### Added
29
+
30
+ - [esbuild]: Add the `obfuscate` keyword to `--esbuild`, which obfuscates every bundled JavaScript file with `javascript-obfuscator` (same settings as the vendored `scripts/esbuild.mjs`), keeping bin launcher shebangs.
31
+ - [pack]: Add the `obfuscate` keyword to `--pack`, passed through to the esbuild step.
32
+
33
+ ### Changed
34
+
35
+ - [dts]: Bundle library declarations again with `rollup` and `rollup-plugin-dts` after the declaration-only emit, using `@typescript/typescript6` for the compiler API that TypeScript 7 removed.
36
+ - [package]: Add the `@typescript/native` alias of `typescript` 7 so `node_modules/.bin/tsc` runs TypeScript 7 instead of the TypeScript 6 pulled in by `@typescript/typescript6`.
37
+ - [esbuild]: Remove the per-file declarations superseded by the bundled declaration entries, so `dist` holds only bundled output.
38
+ - [pack]: Exclude the redirected source bin launchers from `files`, so they no longer ship in the tarball.
39
+
40
+ ### Fixed
41
+
42
+ - [dts]: Bundle all declaration entries in one Rollup run with shared chunks in `dist/_chunks/`, so classes exported from several subpaths are declared once and stay assignable across them.
43
+ - [esbuild]: Keep every package import external unless it is a workspace package or listed in `automator.esbuild.bundle`, and warn about imported packages no `package.json` declares, instead of bundling a second copy of them.
44
+ - [dts]: Drop `--composite` (rejected by `tsc --build`, TS5094) and add `--force` to the monorepo declaration build, so monorepo libraries bundle again.
45
+ - [pack]: Keep the final newline when restoring `package.json` and `tsconfig*.json` files.
46
+
47
+ <a href="https://www.buymeacoffee.com/luligugithub"><img src="https://matterbridge.io/assets/bmc-button.svg" alt="Buy me a coffee" width="80"></a>
48
+
26
49
  ## [1.1.1] - 2026-10-17
27
50
 
28
51
  ### Changed
package/README.md CHANGED
@@ -61,34 +61,34 @@ Multiple flags can be combined. They are always executed in this fixed order reg
61
61
 
62
62
  ## Flags
63
63
 
64
- | Flag | Description |
65
- | -------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
66
- | `--install` | Runs `npm install --no-fund --no-audit`. In a Matterbridge plugin project, also runs `npm link matterbridge`. |
67
- | `--upgrade [keywords...]` | Upgrades the project's config and scaffolding files, then runs `npm install`. Optional keywords enable extras: `node`, `bun`, `jest`, `vitest`, `buntest`, `bundle`, `obfuscate`. |
68
- | `--update` | Upgrades all dependencies with `npm-check-updates` (`ncu -u`) across the root and all workspaces, then runs `npm install`. Runs after `--upgrade` so dependency updates apply to the already-upgraded `package.json`. |
69
- | `--reset` | Empties `.cache/` and `node_modules/` (preserving directory structure for devcontainer named volumes), then runs `npm install` and rebuilds. Implies `--install` and `--build`. |
70
- | `--reset --production` | Same as `--reset` but rebuilds using the production tsconfig. |
71
- | `--deep-clean` | Same filesystem cleanup as `--reset` but skips the install and build steps. |
72
- | `--clean` | Removes `dist/` and all `.tsbuildinfo` files. |
73
- | `--set-version [tag]` | Updates the version in `package.json` (root and all workspaces) and regenerates `package-lock.json`. Without a tag, strips any prerelease suffix back to base semver. With a tag, produces `<baseVersion>-<tag>-<yyyymmdd>-<7charSha>`. |
74
- | `--build` | Compiles TypeScript with `tsc`. Prefers `tsconfig.build.json` per workspace when present, falls back to `tsconfig.json`. |
75
- | `--build --production` | Compiles for production. Prefers `tsconfig.build.production.json`, falls back to `tsconfig.build.json`, then `tsconfig.json`. |
76
- | `--typecheck` | Type-checks with `tsc` using the root `tsconfig.json` and `--noEmit`. |
77
- | `--watch` | Runs `tsc` in watch mode using the build tsconfig. |
78
- | `--test [verbose\|watch\|coverage...]` | Runs each eligible test runner: Jest requires `jest.config.js` and its local binary; Vitest requires `vitest.config.ts` and its local binary. When both qualify, Jest runs first, then Vitest. The optional keywords enable verbose output, watch mode, and coverage. Jest receives `NODE_OPTIONS="--experimental-vm-modules --no-warnings"` automatically. |
79
- | `--format` | Formats with `oxfmt`, falling back to `prettier`. |
80
- | `--format --check` | Checks formatting instead of writing changes (combine `--format` with `--check`). |
81
- | `--lint` | Lints with `oxlint`, falling back to `eslint`. |
82
- | `--lint --fix` | Applies automatic lint fixes (combine `--lint` with `--fix`). |
83
- | `--sort` | Sorts top-level keys in all `package.json` files (root and all workspaces) using the canonical Matterbridge key order, then formats. |
84
- | `--pack [tag] [minify]` | Full pack workflow: back up `package.json`, clean, production build, strip `devDependencies`/`scripts`, empty `node_modules`, `npm install --omit=dev`, `npm shrinkwrap`, `npm pack`, then restore everything. With an optional tag (`dev`, `edge`, `git`, `local`, `next`, `alpha`, `beta`), first bumps the version to `<baseVersion>-<tag>-<yyyymmdd>-<7charSha>`. `minify` compresses the bundle. |
85
- | `--publish [tag]` | Full publish workflow: back up all `package.json` files, strip `devDependencies`/`scripts` from root and workspaces, `npm publish` root and every workspace, then restore all files. With an optional tag, first bumps the version to `<baseVersion>-<tag>-<yyyymmdd>-<7charSha>`. |
86
- | `--esbuild [minify]` | Bundles the project with esbuild. `minify` compresses output and removes comments. |
87
- | `--info` | Prints system information: platform, hostname, memory, network interfaces, Node.js, npm, and Bun versions, plus Bun install, cache, binary, and global module locations with their related environment variables. |
88
- | `--dry-run` | Logs every intended action without modifying files or executing commands. |
89
- | `--verbose` | Prints each external command before it is executed. |
90
- | `--version`, `-v` | Prints the mb-run version and exits without changing any file. Use `--set-version` to update the package version. |
91
- | `--help`, `-h` | Prints usage text. |
64
+ | Flag | Description |
65
+ | -------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
66
+ | `--install` | Runs `npm install --no-fund --no-audit`. In a Matterbridge plugin project, also runs `npm link matterbridge`. |
67
+ | `--upgrade [keywords...]` | Upgrades the project's config and scaffolding files, then runs `npm install`. Optional keywords enable extras: `node`, `bun`, `jest`, `vitest`, `buntest`, `bundle`, `obfuscate`. |
68
+ | `--update` | Upgrades all dependencies with `npm-check-updates` (`ncu -u`) across the root and all workspaces, then runs `npm install`. Runs after `--upgrade` so dependency updates apply to the already-upgraded `package.json`. |
69
+ | `--reset` | Empties `.cache/` and `node_modules/` (preserving directory structure for devcontainer named volumes), then runs `npm install` and rebuilds. Implies `--install` and `--build`. |
70
+ | `--reset --production` | Same as `--reset` but rebuilds using the production tsconfig. |
71
+ | `--deep-clean` | Same filesystem cleanup as `--reset` but skips the install and build steps. |
72
+ | `--clean` | Removes `dist/` and all `.tsbuildinfo` files. |
73
+ | `--set-version [tag]` | Updates the version in `package.json` (root and all workspaces) and regenerates `package-lock.json`. Without a tag, strips any prerelease suffix back to base semver. With a tag, produces `<baseVersion>-<tag>-<yyyymmdd>-<7charSha>`. |
74
+ | `--build` | Compiles TypeScript with `tsc`. Prefers `tsconfig.build.json` per workspace when present, falls back to `tsconfig.json`. |
75
+ | `--build --production` | Compiles for production. Prefers `tsconfig.build.production.json`, falls back to `tsconfig.build.json`, then `tsconfig.json`. |
76
+ | `--typecheck` | Type-checks with `tsc` using the root `tsconfig.json` and `--noEmit`. |
77
+ | `--watch` | Runs `tsc` in watch mode using the build tsconfig. |
78
+ | `--test [verbose\|watch\|coverage...]` | Runs each eligible test runner: Jest requires `jest.config.js` and its local binary; Vitest requires `vitest.config.ts` and its local binary. When both qualify, Jest runs first, then Vitest. The optional keywords enable verbose output, watch mode, and coverage. Jest receives `NODE_OPTIONS="--experimental-vm-modules --no-warnings"` automatically. |
79
+ | `--format` | Formats with `oxfmt`, falling back to `prettier`. |
80
+ | `--format --check` | Checks formatting instead of writing changes (combine `--format` with `--check`). |
81
+ | `--lint` | Lints with `oxlint`, falling back to `eslint`. |
82
+ | `--lint --fix` | Applies automatic lint fixes (combine `--lint` with `--fix`). |
83
+ | `--sort` | Sorts top-level keys in all `package.json` files (root and all workspaces) using the canonical Matterbridge key order, then formats. |
84
+ | `--pack [tag] [minify] [obfuscate]` | Full pack workflow: back up `package.json`, clean, production build, strip `devDependencies`/`scripts`, empty `node_modules`, `npm install --omit=dev`, `npm shrinkwrap`, `npm pack`, then restore everything. With an optional tag (`dev`, `edge`, `git`, `local`, `next`, `alpha`, `beta`), first bumps the version to `<baseVersion>-<tag>-<yyyymmdd>-<7charSha>`. `minify` compresses the bundle and `obfuscate` obfuscates it. |
85
+ | `--publish [tag]` | Full publish workflow: back up all `package.json` files, strip `devDependencies`/`scripts` from root and workspaces, `npm publish` root and every workspace, then restore all files. With an optional tag, first bumps the version to `<baseVersion>-<tag>-<yyyymmdd>-<7charSha>`. |
86
+ | `--esbuild [minify] [obfuscate]` | Bundles the project with esbuild. `minify` compresses output and removes comments. `obfuscate` obfuscates every bundled JavaScript file (entries, chunks and bin launchers) with javascript-obfuscator. |
87
+ | `--info` | Prints system information: platform, hostname, memory, network interfaces, Node.js, npm, and Bun versions, plus Bun install, cache, binary, and global module locations with their related environment variables. |
88
+ | `--dry-run` | Logs every intended action without modifying files or executing commands. |
89
+ | `--verbose` | Prints each external command before it is executed. |
90
+ | `--version`, `-v` | Prints the mb-run version and exits without changing any file. Use `--set-version` to update the package version. |
91
+ | `--help`, `-h` | Prints usage text. |
92
92
 
93
93
  Bundled scaffolding and fallback lint/format configurations are shipped in the `template/` directory.
94
94
 
@@ -142,9 +142,7 @@ For each operation, `mb-run` resolves the tool binary from `node_modules/.bin` i
142
142
  - **Known entrypoints** (`tsc`, `jest`, `vitest`, `eslint`, `oxlint`, `prettier`) are invoked as `node <entrypoint>` for reliability on all platforms.
143
143
  - **Other binaries** are invoked via their shim in `node_modules/.bin` (using the `.cmd` variant on Windows).
144
144
 
145
- ## Known Issues
146
-
147
- ### Declaration bundlers on TypeScript 7
145
+ ## Declaration bundling
148
146
 
149
147
  `--pack`/`--publish` rebuild declaration files for library packages (`automator.library: true` or a production tsconfig with `declaration: true`) with TypeScript's declaration-only emit. Single-package projects use:
150
148
 
@@ -155,15 +153,14 @@ tsc --project tsconfig.build.json --incremental false --composite false --declar
155
153
  Monorepos use project references:
156
154
 
157
155
  ```sh
158
- tsc --build tsconfig.build.json --incremental true --composite true --declaration --declarationMap false --emitDeclarationOnly
156
+ tsc --build tsconfig.build.json --force --declaration --declarationMap false --emitDeclarationOnly
159
157
  ```
160
158
 
161
- This replaces third-party declaration bundling while TypeScript 7-compatible bundlers are unavailable. TypeScript 7's native compiler removed the classic Program API (`ts.sys`, `ts.createProgram`, …) from the package's default entry point — it now only exports `version`/`versionMajorMinor` — so tools that depend on that API fail at runtime when `typescript@7` is the resolved `typescript` package.
159
+ All public declaration entries (`types` and every `exports[*].types`) are then bundled in place in a single [`rollup-plugin-dts`](https://github.com/Swatinem/rollup-plugin-dts) run, inlining local workspace declarations while third-party packages stay external. Declarations shared by several entries go into common chunks under `dist/_chunks/`, so a class exported from two subpaths is declared once and stays assignable across them. The per-file declarations they supersede are then removed from `dist`, which keeps only bundled output.
162
160
 
163
- Neither previous bundler has shipped a fix as of this writing:
161
+ When bundling, every package import stays external except local workspace packages and the packages listed in `automator.esbuild.bundle`. A package that is imported but declared in no `package.json` (typically a hoisted transitive dependency) is kept external and reported with a warning, instead of being bundled next to the copy its declared parent loads at runtime.
164
162
 
165
- - [`dts-bundle-generator` #363](https://github.com/timocov/dts-bundle-generator/issues/363) is open with no progress.
166
- - [`rollup-plugin-dts` #395](https://github.com/Swatinem/rollup-plugin-dts/issues/395) has a maintainer-authored proposal and a working branch, but it is unmerged and unreleased.
163
+ TypeScript 7's native compiler removed the classic Program API (`ts.sys`, `ts.createProgram`, …) that declaration bundlers rely on. `rollup-plugin-dts` loads it from Microsoft's [`@typescript/typescript6`](https://www.npmjs.com/package/@typescript/typescript6) compatibility package, installed next to `typescript` 7.
167
164
 
168
165
  ## Repository toolchain
169
166
 
package/dist/cache.js CHANGED
@@ -98,12 +98,12 @@ export async function restore(rootDir) {
98
98
  logRestore(rootDir);
99
99
  for (const [relKey, content] of tsconfigMap) {
100
100
  const absPath = path.join(rootDir, ...relKey.split('/'));
101
- await writeFile(absPath, JSON.stringify(content, null, 2));
101
+ await writeFile(absPath, JSON.stringify(content, null, 2) + '\n');
102
102
  }
103
103
  for (const [name, content] of packageJsonMap) {
104
104
  const absPath = packageJsonPaths.get(name);
105
105
  if (absPath) {
106
- await writeFile(absPath, JSON.stringify(content, null, 2));
106
+ await writeFile(absPath, JSON.stringify(content, null, 2) + '\n');
107
107
  }
108
108
  }
109
109
  }
package/dist/dts.js CHANGED
@@ -1,10 +1,83 @@
1
1
  import path from 'node:path';
2
2
  import { runBin } from './build.js';
3
- import { isMonorepo } from './helpers.js';
3
+ import { resolveWorkspacePackageJsonPaths } from './cache.js';
4
+ import { fileExists } from './clean.js';
5
+ import { isMonorepo, parsePackageJson } from './helpers.js';
4
6
  export async function runDtsBuild(opts) {
5
7
  const tsconfigPath = path.join(opts.rootDir, 'tsconfig.build.json');
6
8
  const args = (await isMonorepo(opts.rootDir))
7
- ? ['--build', tsconfigPath, '--incremental', 'true', '--composite', 'true', '--declaration', '--declarationMap', 'false', '--emitDeclarationOnly']
9
+ ? ['--build', tsconfigPath, '--force', '--declaration', '--declarationMap', 'false', '--emitDeclarationOnly']
8
10
  : ['--project', tsconfigPath, '--incremental', 'false', '--composite', 'false', '--declaration', '--declarationMap', 'false', '--emitDeclarationOnly'];
9
11
  await runBin('tsc', args, { rootDir: opts.rootDir, isWindows: opts.isWindows, dryRun: opts.dryRun, mode: 'build', watch: false });
10
12
  }
13
+ export async function runDtsBundle(opts) {
14
+ if (opts.dryRun)
15
+ return [];
16
+ const packageJson = await parsePackageJson(opts.rootDir);
17
+ const declarationPaths = new Set();
18
+ if (typeof packageJson['types'] === 'string')
19
+ declarationPaths.add(packageJson['types']);
20
+ const packageExports = packageJson['exports'];
21
+ if (typeof packageExports === 'object' && packageExports !== null && !Array.isArray(packageExports)) {
22
+ for (const exportValue of Object.values(packageExports)) {
23
+ if (typeof exportValue !== 'object' || exportValue === null || Array.isArray(exportValue))
24
+ continue;
25
+ const declarationPath = exportValue['types'];
26
+ if (typeof declarationPath === 'string')
27
+ declarationPaths.add(declarationPath);
28
+ }
29
+ }
30
+ const entryPoints = [];
31
+ for (const declarationPath of declarationPaths) {
32
+ const filePath = path.resolve(opts.rootDir, declarationPath);
33
+ if (!(await fileExists(filePath))) {
34
+ throw new Error(`Missing declaration file for packed library: ${declarationPath}`);
35
+ }
36
+ entryPoints.push({ filePath });
37
+ }
38
+ if (entryPoints.length === 0)
39
+ return [];
40
+ const workspaceDeclarationPaths = {};
41
+ for (const packageJsonPath of await resolveWorkspacePackageJsonPaths(opts.rootDir)) {
42
+ const workspacePackage = await parsePackageJson(path.dirname(packageJsonPath));
43
+ if (typeof workspacePackage['name'] !== 'string')
44
+ continue;
45
+ const workspaceName = workspacePackage['name'];
46
+ const workspaceDirectory = path.dirname(packageJsonPath);
47
+ const toDeclarationPath = (declarationPath) => path.relative(opts.rootDir, path.resolve(workspaceDirectory, declarationPath)).split(path.sep).join('/');
48
+ const workspaceTypes = workspacePackage['types'];
49
+ if (typeof workspaceTypes === 'string') {
50
+ workspaceDeclarationPaths[workspaceName] = [toDeclarationPath(workspaceTypes)];
51
+ }
52
+ const workspaceExports = workspacePackage['exports'];
53
+ if (typeof workspaceExports !== 'object' || workspaceExports === null || Array.isArray(workspaceExports))
54
+ continue;
55
+ for (const [exportPath, exportValue] of Object.entries(workspaceExports)) {
56
+ if (typeof exportValue !== 'object' || exportValue === null || Array.isArray(exportValue))
57
+ continue;
58
+ const declarationPath = exportValue['types'];
59
+ if (typeof declarationPath !== 'string')
60
+ continue;
61
+ const importPath = exportPath === '.' ? workspaceName : `${workspaceName}/${exportPath.replace(/^\.\//, '')}`;
62
+ workspaceDeclarationPaths[importPath] = [toDeclarationPath(declarationPath)];
63
+ }
64
+ }
65
+ const { rollup } = await import('rollup');
66
+ const { dts } = await import('rollup-plugin-dts');
67
+ const input = {};
68
+ for (const entryPoint of entryPoints) {
69
+ input[path
70
+ .relative(opts.rootDir, entryPoint.filePath)
71
+ .split(path.sep)
72
+ .join('/')
73
+ .replace(/\.d\.ts$/, '')] = entryPoint.filePath;
74
+ }
75
+ const bundle = await rollup({
76
+ input,
77
+ external: (specifier) => !specifier.startsWith('.') && !path.isAbsolute(specifier) && !Object.hasOwn(workspaceDeclarationPaths, specifier),
78
+ plugins: [dts({ respectExternal: true, compilerOptions: { baseUrl: opts.rootDir, paths: workspaceDeclarationPaths } })],
79
+ });
80
+ const { output } = await bundle.write({ dir: opts.rootDir, format: 'es', entryFileNames: '[name].d.ts', chunkFileNames: 'dist/_chunks/[name]-[hash].d.ts' });
81
+ await bundle.close();
82
+ return output.map((file) => path.resolve(opts.rootDir, file.fileName));
83
+ }
package/dist/esbuild.js CHANGED
@@ -1,11 +1,12 @@
1
- import { copyFile, mkdir, readFile, readdir, rm } from 'node:fs/promises';
1
+ import { copyFile, mkdir, readFile, readdir, rm, writeFile } from 'node:fs/promises';
2
+ import { isBuiltin } from 'node:module';
2
3
  import path from 'node:path';
3
4
  import { build } from 'esbuild';
4
5
  import { resolveWorkspacePackageJsonPaths } from './cache.js';
5
6
  import { fileExists } from './clean.js';
6
- import { runDtsBuild } from './dts.js';
7
+ import { runDtsBuild, runDtsBundle } from './dts.js';
7
8
  import { isLibrary, isPlugin, parsePackageJson } from './helpers.js';
8
- import { logEsbuild, logEsbuildAction, logEsbuildOptions } from './logger.js';
9
+ import { logEsbuild, logEsbuildAction, logEsbuildOptions, logWarning } from './logger.js';
9
10
  async function resolveDeclaredCopyEntries(packageJson, rootDir) {
10
11
  const automator = packageJson['automator'];
11
12
  if (typeof automator !== 'object' || automator === null || Array.isArray(automator))
@@ -73,6 +74,25 @@ async function copyDeclaredEntries(entries, rootDir) {
73
74
  }
74
75
  return copiedPaths;
75
76
  }
77
+ async function obfuscateOutputs(filePaths, rootDir) {
78
+ const { default: JavaScriptObfuscator } = await import('javascript-obfuscator');
79
+ for (const filePath of filePaths) {
80
+ logEsbuildAction('obfuscate', [filePath], rootDir);
81
+ const code = await readFile(filePath, 'utf8');
82
+ const shebang = /^#![^\n]*\n/.exec(code)?.[0] ?? '';
83
+ const obfuscated = JavaScriptObfuscator.obfuscate(code.slice(shebang.length), {
84
+ compact: true,
85
+ renameGlobals: false,
86
+ identifierNamesGenerator: 'hexadecimal',
87
+ stringArray: true,
88
+ stringArrayThreshold: 0.8,
89
+ splitStrings: true,
90
+ splitStringsChunkLength: 10,
91
+ sourceMap: false,
92
+ }).getObfuscatedCode();
93
+ await writeFile(filePath, `${shebang}${obfuscated}\n`, 'utf8');
94
+ }
95
+ }
76
96
  async function pruneUnproducedDistFiles(distDir, keepPaths) {
77
97
  if (!(await fileExists(distDir)))
78
98
  return;
@@ -154,6 +174,34 @@ function resolveDeclaredPackageSpecifiers(packageJson, field) {
154
174
  }
155
175
  return packageSpecifiers;
156
176
  }
177
+ function toPackageName(specifier) {
178
+ const parts = specifier.split('/');
179
+ return specifier.startsWith('@') ? parts.slice(0, 2).join('/') : parts[0];
180
+ }
181
+ function matchesPackagePattern(specifier, patterns) {
182
+ for (const pattern of patterns) {
183
+ if (pattern.endsWith('*') ? specifier.startsWith(pattern.slice(0, -1)) : specifier === pattern || toPackageName(specifier) === pattern)
184
+ return true;
185
+ }
186
+ return false;
187
+ }
188
+ function createExternalPackagesPlugin(localNames, bundledPatterns, declaredPatterns, undeclaredPackages) {
189
+ return {
190
+ name: 'mb-run-external-packages',
191
+ setup(build) {
192
+ build.onResolve({ filter: /^[^./]/ }, (args) => {
193
+ if (args.kind === 'entry-point' || path.isAbsolute(args.path))
194
+ return undefined;
195
+ const packageName = toPackageName(args.path);
196
+ if (localNames.has(packageName) || matchesPackagePattern(args.path, bundledPatterns))
197
+ return undefined;
198
+ if (!args.path.includes(':') && !isBuiltin(args.path) && !matchesPackagePattern(args.path, declaredPatterns))
199
+ undeclaredPackages.add(packageName);
200
+ return { path: args.path, external: true };
201
+ });
202
+ },
203
+ };
204
+ }
157
205
  function createOwnOutputRedirectPlugin() {
158
206
  return {
159
207
  name: 'redirect-own-dist-outputs',
@@ -197,7 +245,8 @@ export async function runEsbuild(opts) {
197
245
  if (await isPlugin(opts.rootDir))
198
246
  externalSet.add('matterbridge');
199
247
  const rootPkg = (await parsePackageJson(opts.rootDir));
200
- for (const packageName of resolveDeclaredPackageSpecifiers(rootPkg, 'bundle')) {
248
+ const bundledPatterns = resolveDeclaredPackageSpecifiers(rootPkg, 'bundle');
249
+ for (const packageName of bundledPatterns) {
201
250
  externalSet.delete(packageName);
202
251
  }
203
252
  for (const packageName of resolveDeclaredPackageSpecifiers(rootPkg, 'external')) {
@@ -287,10 +336,11 @@ export async function runEsbuild(opts) {
287
336
  entryPoints.push(declaredEntryPoint);
288
337
  }
289
338
  const allEntryPoints = [...entryPoints, ...resolvedBinEntries];
339
+ const undeclaredPackages = new Set();
290
340
  logEsbuild(allEntryPoints, opts.rootDir);
291
341
  const esbuildOptions = {
292
342
  entryPoints: allEntryPoints,
293
- plugins: [createOwnOutputRedirectPlugin()],
343
+ plugins: [createOwnOutputRedirectPlugin(), createExternalPackagesPlugin(localNames, bundledPatterns, new Set([...externalSet, ...localNames]), undeclaredPackages)],
294
344
  bundle: true,
295
345
  format: 'esm',
296
346
  platform: 'node',
@@ -315,9 +365,19 @@ export async function runEsbuild(opts) {
315
365
  return;
316
366
  }
317
367
  const result = await build(esbuildOptions);
368
+ if (undeclaredPackages.size > 0) {
369
+ logWarning(`Imported but not declared in any package.json dependencies, kept external: ${[...undeclaredPackages].toSorted().join(', ')}. Declare them so npm installs them.`);
370
+ }
318
371
  const copiedPaths = await copyDeclaredEntries(declaredCopyEntries, opts.rootDir);
372
+ if (opts.obfuscate) {
373
+ const javascriptOutputs = Object.keys(result.metafile.outputs).filter((outputPath) => outputPath.endsWith('.js'));
374
+ await obfuscateOutputs(javascriptOutputs.map((outputPath) => path.resolve(opts.rootDir, outputPath)), opts.rootDir);
375
+ }
319
376
  const keepPaths = new Set([...Object.keys(result.metafile.outputs).map((outputPath) => path.resolve(opts.rootDir, outputPath)), ...copiedPaths]);
320
377
  await pruneUnproducedDistFiles(path.join(opts.rootDir, 'dist'), keepPaths);
321
- if (await isLibrary(opts.rootDir))
378
+ if (await isLibrary(opts.rootDir)) {
322
379
  await runDtsBuild({ rootDir: opts.rootDir, isWindows: opts.isWindows, dryRun: opts.dryRun });
380
+ const bundledDeclarations = await runDtsBundle({ rootDir: opts.rootDir, dryRun: opts.dryRun });
381
+ await pruneUnproducedDistFiles(path.join(opts.rootDir, 'dist'), new Set([...keepPaths, ...bundledDeclarations]));
382
+ }
323
383
  }
package/dist/help.js CHANGED
@@ -33,9 +33,9 @@ ${brightYellow('Options:')}
33
33
  ${f('--sort')}sort top-level keys in all package.json files
34
34
  ${f('--update')}run ncu -u across all workspaces then npm install
35
35
  ${f('--upgrade', '[keywords...]')}upgrade config files; keywords: node | bun | jest | vitest | buntest | bundle | obfuscate | promiserules | typeaware | experimental
36
- ${f('--pack', '[tag] [minify]')}back up, clean, build, npm pack, and restore; bumps version first if tag is provided
36
+ ${f('--pack', '[tag] [minify] [obfuscate]')}back up, clean, build, npm pack, and restore; bumps version first if tag is provided
37
37
  ${f('--publish', '[tag]')}back up, npm publish for root and all workspaces, and restore; bumps version first if tag is provided
38
- ${f('--esbuild', '[minify]')}bundle with esbuild; minify compresses output and removes comments
38
+ ${f('--esbuild', '[minify] [obfuscate]')}bundle with esbuild; minify compresses output and removes comments, obfuscate obfuscates the bundled JavaScript
39
39
  ${f('--set-version', '[tag]')}update versions for the current package and all workspaces
40
40
  ${f('--dry-run')}log intended actions without executing commands or writing files
41
41
  ${f('--verbose')}print each command before it is executed
@@ -61,10 +61,10 @@ export function printVersionUsage() {
61
61
  }
62
62
  export function printPackUsage() {
63
63
  const msg = [
64
- `${brightYellow('Usage:')} ${green('mb-run')} ${green('--pack')} [dev|edge|git|local|next|alpha|beta] [minify]`,
64
+ `${brightYellow('Usage:')} ${green('mb-run')} ${green('--pack')} [dev|edge|git|local|next|alpha|beta] [minify] [obfuscate]`,
65
65
  `${cyan('Runs')} the full pack workflow. With an optional tag, first bumps the version to:`,
66
66
  ` ${brightBlack('<baseVersion>-<dev|edge|git|local|next|alpha|beta>-<yyyymmdd>-<7charSha>')}`,
67
- `Then backs up package.json, builds for production, packs, and restores. Add ${brightBlack('minify')} to compress the esbuild output.`,
67
+ `Then backs up package.json, builds for production, packs, and restores. Add ${brightBlack('minify')} to compress the esbuild output and ${brightBlack('obfuscate')} to obfuscate it.`,
68
68
  ].join('\n');
69
69
  log(msg);
70
70
  }
package/dist/module.js CHANGED
@@ -90,6 +90,7 @@ export async function main() {
90
90
  const packArgIndices = new Set();
91
91
  let rawPackTag;
92
92
  let packMinify = false;
93
+ let packObfuscate = false;
93
94
  if (packIndex >= 0) {
94
95
  for (let i = packIndex + 1; i < rawArgs.length; i++) {
95
96
  const packArg = rawArgs[i];
@@ -99,6 +100,10 @@ export async function main() {
99
100
  packMinify = true;
100
101
  packArgIndices.add(i);
101
102
  }
103
+ else if (packArg === 'obfuscate') {
104
+ packObfuscate = true;
105
+ packArgIndices.add(i);
106
+ }
102
107
  else if (rawPackTag === undefined) {
103
108
  rawPackTag = packArg;
104
109
  packArgIndices.add(i);
@@ -137,7 +142,7 @@ export async function main() {
137
142
  }
138
143
  }
139
144
  const esbuildIndex = rawArgs.indexOf('--esbuild');
140
- const esbuildValidKeywords = new Set(['minify']);
145
+ const esbuildValidKeywords = new Set(['minify', 'obfuscate']);
141
146
  const esbuildArgIndices = new Set();
142
147
  const esbuildArgs = new Set();
143
148
  if (esbuildIndex >= 0) {
@@ -347,7 +352,7 @@ export async function main() {
347
352
  }
348
353
  if (want.pack) {
349
354
  log(`${savePos()}⏳ Packing...`);
350
- await runPack({ ...buildOpts, tag: packTag, minify: packMinify });
355
+ await runPack({ ...buildOpts, tag: packTag, minify: packMinify, obfuscate: packObfuscate });
351
356
  log(`${restorePos(0)}${green('✅')} Pack complete in ${getElapsed()}.${clearEnd()}`);
352
357
  }
353
358
  if (want.publish) {
@@ -362,7 +367,7 @@ export async function main() {
362
367
  }
363
368
  if (want.esbuild) {
364
369
  log(`${savePos()}⏳ Bundling with esbuild...`);
365
- await runEsbuild({ ...buildOpts, minify: esbuildArgs.has('minify') });
370
+ await runEsbuild({ ...buildOpts, minify: esbuildArgs.has('minify'), obfuscate: esbuildArgs.has('obfuscate') });
366
371
  log(`${restorePos()}${green('✅')} Esbuild complete in ${getElapsed()}.${clearEnd()}`);
367
372
  }
368
373
  if (want.watch) {
package/dist/pack.js CHANGED
@@ -44,6 +44,7 @@ export async function runPack(opts) {
44
44
  dryRun: opts.dryRun,
45
45
  verbose: opts.verbose,
46
46
  minify: opts.minify,
47
+ obfuscate: opts.obfuscate,
47
48
  });
48
49
  const workspacePkgPaths = await resolveWorkspacePackageJsonPaths(opts.rootDir);
49
50
  logWriteFile(packageJsonPath);
@@ -55,11 +56,15 @@ export async function runPack(opts) {
55
56
  const packageBin = pkg['bin'];
56
57
  if (typeof packageBin === 'object' && packageBin !== null && !Array.isArray(packageBin)) {
57
58
  const packageBinEntries = packageBin;
59
+ const packageFiles = pkg['files'];
58
60
  for (const [binName, binPath] of Object.entries(packageBinEntries)) {
59
61
  if (typeof binPath !== 'string' || binPath.startsWith('dist/'))
60
62
  continue;
61
63
  if (await fileExists(path.join(opts.rootDir, binPath))) {
62
- packageBinEntries[binName] = `dist/${binPath.replace(/^\.\//, '')}`;
64
+ const sourceLauncher = binPath.replace(/^\.\//, '');
65
+ packageBinEntries[binName] = `dist/${sourceLauncher}`;
66
+ if (Array.isArray(packageFiles) && !packageFiles.includes(`!${sourceLauncher}`))
67
+ packageFiles.push(`!${sourceLauncher}`);
63
68
  }
64
69
  }
65
70
  }