mb-run 0.0.1-dev-20260520-cb15d2f → 0.0.1-dev-20260623-e0dcfa1

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/README.md CHANGED
@@ -59,31 +59,31 @@ Multiple flags can be combined. They are always executed in this fixed order reg
59
59
 
60
60
  ## Flags
61
61
 
62
- | Flag | Description |
63
- | ---------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
64
- | `--install` | Runs `npm install --no-fund --no-audit`. In a Matterbridge plugin project, also runs `npm link matterbridge`. |
65
- | `--reset` | Empties `.cache/` and `node_modules/` (preserving directory structure for devcontainer named volumes), then runs `npm install` and rebuilds. Implies `--install` and `--build`. |
66
- | `--reset --production` | Same as `--reset` but rebuilds using the production tsconfig. |
67
- | `--deep-clean` | Same filesystem cleanup as `--reset` but skips the install and build steps. |
68
- | `--clean` | Removes `dist/` and all `.tsbuildinfo` files. |
69
- | `--build` | Compiles TypeScript using `tsc`. Prefers `tsconfig.build.json` per workspace when present, falls back to `tsconfig.json`. |
70
- | `--build --production` | Compiles for production. Prefers `tsconfig.build.production.json`, falls back to `tsconfig.build.json`, then `tsconfig.json`. |
71
- | `--watch` | Runs `tsc` in watch mode using the build tsconfig. |
72
- | `--test` | Runs `jest --maxWorkers=100%` with `NODE_OPTIONS="--experimental-vm-modules --no-warnings"` set automatically. |
73
- | `--lint` | Runs `eslint --cache --max-warnings=0`. |
74
- | `--lint-fix` | Runs `eslint --cache --fix --max-warnings=0`. |
75
- | `--format` | Runs `prettier --cache --write`. |
76
- | `--format-check` | Runs `prettier --cache --check`. |
77
- | `--sort` | Sorts top-level keys in all `package.json` files (root and all workspaces) using the canonical Matterbridge key order, then formats with Prettier. |
78
- | `--update` | Upgrades all dependencies using `npm-check-updates` (root + all workspaces), then runs `npm install`. |
79
- | `--pack [tag]` | 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>`. |
80
- | `--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>`. |
81
- | `--esbuild` | Bundles the project with esbuild. |
82
- | `--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>`. |
83
- | `--info` | Prints system information: platform, hostname, memory, network interfaces, Node.js and npm versions. |
84
- | `--dry-run` | Logs every intended action without modifying files or executing commands. |
85
- | `--verbose` | Prints each external command before it is executed. |
86
- | `--help`, `-h` | Prints usage text. |
62
+ | Flag | Description |
63
+ | -------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
64
+ | `--install` | Runs `npm install --no-fund --no-audit`. In a Matterbridge plugin project, also runs `npm link matterbridge`. |
65
+ | `--reset` | Empties `.cache/` and `node_modules/` (preserving directory structure for devcontainer named volumes), then runs `npm install` and rebuilds. Implies `--install` and `--build`. |
66
+ | `--reset --production` | Same as `--reset` but rebuilds using the production tsconfig. |
67
+ | `--deep-clean` | Same filesystem cleanup as `--reset` but skips the install and build steps. |
68
+ | `--clean` | Removes `dist/` and all `.tsbuildinfo` files. |
69
+ | `--build` | Compiles TypeScript using `tsc`. Prefers `tsconfig.build.json` per workspace when present, falls back to `tsconfig.json`. |
70
+ | `--build --production` | Compiles for production. Prefers `tsconfig.build.production.json`, falls back to `tsconfig.build.json`, then `tsconfig.json`. |
71
+ | `--watch` | Runs `tsc` in watch mode using the build tsconfig. |
72
+ | `--test [verbose\|watch\|coverage...]` | Runs each eligible test runner: Jest requires `jest.config.js` and its local binary; Vitest requires `vite.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. |
73
+ | `--lint` | Runs `eslint --cache --max-warnings=0`. |
74
+ | `--lint-fix` | Runs `eslint --cache --fix --max-warnings=0`. |
75
+ | `--format` | Runs `prettier --cache --write`. |
76
+ | `--format-check` | Runs `prettier --cache --check`. |
77
+ | `--sort` | Sorts top-level keys in all `package.json` files (root and all workspaces) using the canonical Matterbridge key order, then formats with Prettier. |
78
+ | `--update` | Upgrades all dependencies using `npm-check-updates` (root + all workspaces), then runs `npm install`. |
79
+ | `--pack [tag]` | 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>`. |
80
+ | `--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>`. |
81
+ | `--esbuild` | Bundles the project with esbuild. |
82
+ | `--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>`. |
83
+ | `--info` | Prints system information: platform, hostname, memory, network interfaces, Node.js and npm versions. |
84
+ | `--dry-run` | Logs every intended action without modifying files or executing commands. |
85
+ | `--verbose` | Prints each external command before it is executed. |
86
+ | `--help`, `-h` | Prints usage text. |
87
87
 
88
88
  ## Version tags
89
89
 
package/bin/mb-run CHANGED
@@ -1,13 +1,13 @@
1
1
  #!/usr/bin/env node
2
- import { main } from '../dist/module.js';
3
- import { ExitError } from '../dist/spawn.js';
2
+ import { main } from "../dist/module.js";
3
+ import { ExitError } from "../dist/spawn.js";
4
4
 
5
5
  try {
6
6
  await main();
7
7
  } catch (error) {
8
8
  const exitCode = error instanceof ExitError ? error.code : 1;
9
9
 
10
- // eslint-disable-next-line no-console
10
+ // oxlint-disable-next-line no-console
11
11
  console.error(error instanceof Error ? error.message : error);
12
12
  process.exitCode = exitCode;
13
13
  }
package/dist/build.js CHANGED
@@ -2,6 +2,7 @@ import { access } from 'node:fs/promises';
2
2
  import path from 'node:path';
3
3
  import process from 'node:process';
4
4
  import { fileExists } from './clean.js';
5
+ import { isMonorepo } from './helpers.js';
5
6
  import { ExitError, runCommand } from './spawn.js';
6
7
  const BIN_ENTRYPOINTS = {
7
8
  tsc: ['typescript', 'bin/tsc'],
@@ -31,6 +32,17 @@ async function assertBinExists(binName, opts) {
31
32
  throw new ExitError(1, `Missing binary: ${p}. Did you run "npm install"?`);
32
33
  }
33
34
  }
35
+ export async function binExists(binName, opts) {
36
+ const entry = entrypointPath(binName, opts);
37
+ const p = entry ?? binPath(binName, opts);
38
+ try {
39
+ await access(p);
40
+ return true;
41
+ }
42
+ catch {
43
+ return false;
44
+ }
45
+ }
34
46
  async function pickWorkspaceTsconfig(mode, opts) {
35
47
  const candidates = mode === 'production' ? ['tsconfig.build.production.json', 'tsconfig.build.json', 'tsconfig.json'] : ['tsconfig.build.json', 'tsconfig.json'];
36
48
  for (const name of candidates) {
@@ -46,16 +58,32 @@ export async function runBin(binName, args, opts, runOptions = {}) {
46
58
  await assertBinExists(binName, opts);
47
59
  const entry = entrypointPath(binName, opts);
48
60
  if (entry) {
49
- await runCommand(process.execPath, [entry, ...args], { env: runOptions.env, dryRun: opts.dryRun, label: binName, displayArgs: args });
61
+ await runCommand(process.execPath, [entry, ...args], {
62
+ env: runOptions.env,
63
+ dryRun: opts.dryRun,
64
+ label: binName,
65
+ displayArgs: args,
66
+ });
50
67
  }
51
68
  else {
52
- await runCommand(binPath(binName, opts), args, { env: runOptions.env, dryRun: opts.dryRun, label: binName });
69
+ await runCommand(binPath(binName, opts), args, {
70
+ env: runOptions.env,
71
+ dryRun: opts.dryRun,
72
+ label: binName,
73
+ });
53
74
  }
54
75
  }
55
76
  export async function runWorkspaceBuild(options) {
56
77
  const configs = await pickWorkspaceTsconfig(options.mode, options);
57
- const args = ['-b', configs, '--pretty', 'false'];
78
+ const compiler = (await binExists('tsgo', options)) ? 'tsgo' : 'tsc';
79
+ const projectFlag = (await isMonorepo(options.rootDir)) ? '-b' : '-p';
80
+ const args = [projectFlag, configs, '--pretty', 'false'];
58
81
  if (options.watch)
59
82
  args.push('--watch');
60
- await runBin('tsc', args, options);
83
+ await runBin(compiler, args, options);
84
+ }
85
+ export async function runWorkspaceTypecheck(options) {
86
+ const compiler = (await binExists('tsgo', options)) ? 'tsgo' : 'tsc';
87
+ const configPath = path.join(options.rootDir, 'tsconfig.json');
88
+ await runBin(compiler, ['-p', configPath, '--noEmit', '--pretty', 'false'], options);
61
89
  }
package/dist/cache.js CHANGED
@@ -13,7 +13,9 @@ export async function resolveWorkspacePackageJsonPaths(rootDir) {
13
13
  if (Array.isArray(workspacesConfig)) {
14
14
  patterns = workspacesConfig.filter((p) => typeof p === 'string');
15
15
  }
16
- else if (workspacesConfig && typeof workspacesConfig === 'object' && Array.isArray(workspacesConfig.packages)) {
16
+ else if (workspacesConfig &&
17
+ typeof workspacesConfig === 'object' &&
18
+ Array.isArray(workspacesConfig.packages)) {
17
19
  patterns = workspacesConfig.packages.filter((p) => typeof p === 'string');
18
20
  }
19
21
  if (patterns.length === 0)
@@ -30,7 +32,7 @@ export async function resolveWorkspacePackageJsonPaths(rootDir) {
30
32
  results.push(candidate);
31
33
  continue;
32
34
  }
33
- if (trimmed.endsWith('/*') && trimmed.indexOf('*') === trimmed.length - 1 && !/[?\[]/.test(trimmed)) {
35
+ if (trimmed.endsWith('/*') && trimmed.indexOf('*') === trimmed.length - 1 && !/[?[]/.test(trimmed)) {
34
36
  const baseAbs = path.join(rootDir, trimmed.slice(0, -2));
35
37
  let entries;
36
38
  try {
package/dist/clean.js CHANGED
@@ -1,6 +1,7 @@
1
1
  import { access, readdir, rm } from 'node:fs/promises';
2
2
  import path from 'node:path';
3
- import { logDelete } from './logger.js';
3
+ import { logDelete, logWarning } from './logger.js';
4
+ const LOCKED_REMOVE_ERROR_CODES = new Set(['EPERM', 'EBUSY', 'ENOTEMPTY']);
4
5
  export async function fileExists(filePath) {
5
6
  try {
6
7
  await access(filePath);
@@ -10,11 +11,27 @@ export async function fileExists(filePath) {
10
11
  return false;
11
12
  }
12
13
  }
14
+ function getLockedRemoveError(error, targetPath) {
15
+ if (typeof error !== 'object' || error === null || !('code' in error) || typeof error.code !== 'string' || !LOCKED_REMOVE_ERROR_CODES.has(error.code))
16
+ return null;
17
+ const errorPath = 'path' in error && typeof error.path === 'string' ? error.path : targetPath;
18
+ return { code: error.code, path: errorPath };
19
+ }
13
20
  async function removePath(targetPath, opts) {
14
21
  logDelete(targetPath);
15
22
  if (opts.dryRun)
16
23
  return;
17
- await rm(targetPath, { recursive: true, force: true });
24
+ try {
25
+ await rm(targetPath, { recursive: true, force: true, maxRetries: 5, retryDelay: 10 });
26
+ }
27
+ catch (error) {
28
+ const lockedError = getLockedRemoveError(error, targetPath);
29
+ if (lockedError) {
30
+ logWarning(`Skipped locked path (${lockedError.code}): ${lockedError.path} - likely held by a running process.`);
31
+ return;
32
+ }
33
+ throw error;
34
+ }
18
35
  }
19
36
  export async function emptyDir(dirPath, opts) {
20
37
  if (!(await fileExists(dirPath)))
@@ -65,7 +82,6 @@ export async function cleanWorkspaceArtifacts(parentDir, opts) {
65
82
  await Promise.all([
66
83
  removePath(path.join(wsRoot, 'build'), opts),
67
84
  removePath(path.join(wsRoot, 'dist'), opts),
68
- removePath(path.join(wsRoot, 'dist-jest'), opts),
69
85
  removePath(path.join(wsRoot, 'coverage'), opts),
70
86
  removePath(path.join(wsRoot, 'jest'), opts),
71
87
  removePath(path.join(wsRoot, 'temp'), opts),
@@ -82,13 +98,14 @@ export async function commonClean(emptyRootNodeModules, opts) {
82
98
  await Promise.all([
83
99
  removePath(path.join(opts.rootDir, 'build'), opts),
84
100
  removePath(path.join(opts.rootDir, 'dist'), opts),
85
- removePath(path.join(opts.rootDir, 'dist-jest'), opts),
86
101
  removePath(path.join(opts.rootDir, 'coverage'), opts),
87
102
  removePath(path.join(opts.rootDir, 'jest'), opts),
88
103
  removePath(path.join(opts.rootDir, 'temp'), opts),
89
104
  removePath(path.join(opts.rootDir, 'npm-shrinkwrap.json'), opts),
90
105
  ]);
91
- await Promise.all([cleanWorkspaceArtifacts(path.join(opts.rootDir, 'packages'), opts), cleanWorkspaceArtifacts(path.join(opts.rootDir, 'apps'), opts)]);
106
+ await Promise.all([
107
+ cleanWorkspaceArtifacts(path.join(opts.rootDir, 'packages'), opts),
108
+ ]);
92
109
  await emptyDir(path.join(opts.rootDir, '.cache'), opts);
93
110
  if (emptyRootNodeModules) {
94
111
  await emptyDir(path.join(opts.rootDir, 'node_modules'), opts);
package/dist/esbuild.js CHANGED
@@ -7,8 +7,6 @@ import { fileExists } from './clean.js';
7
7
  import { isPlugin, parsePackageJson } from './helpers.js';
8
8
  import { logEsbuild } from './logger.js';
9
9
  export async function runEsbuild(opts) {
10
- if (opts.dryRun)
11
- return;
12
10
  const workspacePaths = await resolveWorkspacePackageJsonPaths(opts.rootDir);
13
11
  const allPkgPaths = [path.join(opts.rootDir, 'package.json'), ...workspacePaths];
14
12
  const localNames = new Set();
@@ -53,11 +51,21 @@ export async function runEsbuild(opts) {
53
51
  const wPkg = JSON.parse(wRaw);
54
52
  if (!wPkg.name)
55
53
  continue;
56
- const wExportsMain = typeof wPkg.exports === 'object' && wPkg.exports !== null ? wPkg.exports['.']?.['import'] : undefined;
54
+ const wExports = typeof wPkg.exports === 'object' && wPkg.exports !== null ? wPkg.exports : undefined;
55
+ const rootExport = wExports?.['.'];
56
+ const wExportsMain = typeof rootExport === 'object' && rootExport !== null && 'import' in rootExport && typeof rootExport.import === 'string' ? rootExport.import : undefined;
57
57
  const wMainRel = wPkg.main ?? wExportsMain;
58
- if (!wMainRel)
58
+ if (wMainRel)
59
+ alias[wPkg.name] = path.join(path.dirname(wPkgPath), toTsSrc(wMainRel));
60
+ if (!wExports)
59
61
  continue;
60
- alias[wPkg.name] = path.join(path.dirname(wPkgPath), toTsSrc(wMainRel));
62
+ for (const [exportPath, exportValue] of Object.entries(wExports)) {
63
+ if (exportPath === '.' || !exportPath.startsWith('./'))
64
+ continue;
65
+ if (typeof exportValue !== 'object' || exportValue === null || !('import' in exportValue) || typeof exportValue.import !== 'string')
66
+ continue;
67
+ alias[`${wPkg.name}/${exportPath.slice(2)}`] = path.join(path.dirname(wPkgPath), toTsSrc(exportValue.import));
68
+ }
61
69
  }
62
70
  const seenBinPaths = new Set();
63
71
  const resolvedBinEntries = [];
@@ -88,7 +96,16 @@ export async function runEsbuild(opts) {
88
96
  splitting: true,
89
97
  outdir: path.join(opts.rootDir, 'dist'),
90
98
  write: true,
99
+ minify: opts.minify ?? false,
100
+ minifyWhitespace: opts.minify ?? false,
101
+ legalComments: opts.minify ? 'none' : 'inline',
102
+ logLevel: 'info',
91
103
  };
92
- log(`esbuild options: ${JSON.stringify(esbuildOptions, null, 2)}`);
104
+ if (opts.verbose) {
105
+ log(`Esbuild options: ${JSON.stringify(esbuildOptions, null, 2)}`);
106
+ }
107
+ if (opts.dryRun) {
108
+ return;
109
+ }
93
110
  await build(esbuildOptions);
94
111
  }
package/dist/eslint.js ADDED
@@ -0,0 +1,22 @@
1
+ import path from 'node:path';
2
+ import url from 'node:url';
3
+ import { runBin } from './build.js';
4
+ import { fileExists } from './clean.js';
5
+ export async function runEsLint(opts) {
6
+ const projectConfig = path.join(opts.rootDir, 'eslint.config.js');
7
+ let configPath;
8
+ if (await fileExists(projectConfig)) {
9
+ configPath = projectConfig;
10
+ }
11
+ else {
12
+ const selfDir = path.dirname(url.fileURLToPath(import.meta.url));
13
+ configPath = path.join(selfDir, '..', 'vendor', 'eslint.config.js');
14
+ }
15
+ await runBin('eslint', ['--config', configPath, '--cache', '--cache-location', '.cache/.eslintcache', ...(opts.fix ? ['--fix'] : []), '--max-warnings=0', '.'], {
16
+ rootDir: opts.rootDir,
17
+ isWindows: opts.isWindows,
18
+ dryRun: opts.dryRun,
19
+ mode: 'build',
20
+ watch: false,
21
+ });
22
+ }
package/dist/format.js ADDED
@@ -0,0 +1,15 @@
1
+ import { binExists } from './build.js';
2
+ import { runOxFormat } from './oxfmt.js';
3
+ import { runPrettier } from './prettier.js';
4
+ import { ExitError } from './spawn.js';
5
+ export async function runFormatter(opts) {
6
+ if (await binExists('oxfmt', opts)) {
7
+ await runOxFormat(opts);
8
+ }
9
+ else if (await binExists('prettier', opts)) {
10
+ await runPrettier(opts);
11
+ }
12
+ else {
13
+ throw new ExitError(1, 'No formatter found in node_modules: install oxfmt or prettier.');
14
+ }
15
+ }
package/dist/help.js CHANGED
@@ -15,28 +15,27 @@ binaries in node_modules/.bin directly (does not call npm scripts).
15
15
 
16
16
  ${brightYellow('Usage:')} ${green('mb-run')} ${brightBlack('[options...]')}
17
17
 
18
- ${brightYellow('Execution order:')} ${brightBlack('install → update → deep-clean → reset → clean → build → testformatoxformat → lint → oxlint → sort → watch')}
18
+ ${brightYellow('Execution order:')} ${brightBlack('install → update → deep-clean → reset → clean → build → typechecktestformat → lint → sort → watch')}
19
19
 
20
20
  ${brightYellow('Options:')}
21
21
  ${f('--install')}run npm install --no-fund --no-audit
22
22
  ${f('--reset', '[--production]')}empty .cache/ and node_modules/, run npm install and build; --production uses tsconfig.build.production.json
23
23
  ${f('--deep-clean')}empty .cache/ and node_modules/ like --reset but skips install and build
24
24
  ${f('--clean')}remove build output directories
25
- ${f('--build', '[--production]')}compile with tsc; prefers tsconfig.build.json; --production prefers tsconfig.build.production.json
26
- ${f('--watch')}run tsc in watch mode
27
- ${f('--test')}run tests with NODE_OPTIONS="--experimental-vm-modules --no-warnings"
28
- ${f('--lint')}run eslint --max-warnings=0
29
- ${f('--lint-fix')}run eslint --fix --max-warnings=0
30
- ${f('--format')}run prettier --write
31
- ${f('--format-check')}run prettier --check
32
- ${f('--oxformat')}format source files via oxfmt JS API (reads .oxfmtrc.json when present)
33
- ${f('--oxlint')}lint source files via oxlint (reads .oxlintrc.json when present, otherwise uses bundled default)
25
+ ${f('--build', '[--production]')}compile with tsgo or tsc; prefers tsconfig.build.json; --production prefers tsconfig.build.production.json
26
+ ${f('--typecheck')}type-check with tsgo or tsc using tsconfig.json and --noEmit
27
+ ${f('--watch')}run tsgo or tsc in watch mode
28
+ ${f('--test', '[verbose|watch|coverage...]')}run configured Jest and/or Vitest tests; Jest sets NODE_OPTIONS
29
+ ${f('--lint')}lint with oxlint, falling back to eslint when oxlint is not installed
30
+ ${f('--format')}format with oxfmt, falling back to prettier when oxfmt is not installed
31
+ ${f('--check')}with --format, check formatting instead of writing changes
32
+ ${f('--fix')}with --lint, apply automatic fixes
34
33
  ${f('--sort')}sort top-level keys in all package.json files
35
34
  ${f('--update')}run ncu -u across all workspaces then npm install
36
35
  ${f('--upgrade', '[keywords...]')}upgrade config files; keywords: jest | vitest | promiserules | typeaware | experimental
37
36
  ${f('--pack', '[tag]')}back up, clean, build, npm pack, and restore; bumps version first if tag is provided
38
37
  ${f('--publish', '[tag]')}back up, npm publish for root and all workspaces, and restore; bumps version first if tag is provided
39
- ${f('--esbuild')}bundle with esbuild
38
+ ${f('--esbuild', '[minify]')}bundle with esbuild; minify compresses output and removes comments
40
39
  ${f('--version', '[tag]')}update versions for the current package and all workspaces
41
40
  ${f('--dry-run')}log intended actions without executing commands or writing files
42
41
  ${f('--verbose')}print each command before it is executed
package/dist/helpers.js CHANGED
@@ -1,7 +1,12 @@
1
- import { execFileSync, execSync } from 'node:child_process';
2
- import { copyFile, mkdir, mkdtemp, readdir, readFile } from 'node:fs/promises';
3
- import os from 'node:os';
1
+ import { readFile, rm } from 'node:fs/promises';
4
2
  import path from 'node:path';
3
+ import { logDelete } from './logger.js';
4
+ export async function removeFile(filePath, opts) {
5
+ logDelete(filePath);
6
+ if (opts.dryRun)
7
+ return;
8
+ await rm(filePath, { force: true });
9
+ }
5
10
  export async function parsePackageJson(rootDir) {
6
11
  const packageJsonPath = path.join(rootDir, 'package.json');
7
12
  try {
@@ -14,68 +19,24 @@ export async function parsePackageJson(rootDir) {
14
19
  }
15
20
  export async function isPlugin(rootDir) {
16
21
  const pkg = (await parsePackageJson(rootDir));
17
- return pkg?.scripts?.start === 'matterbridge' || pkg?.scripts?.['dev:link'] === 'npm link --no-fund --no-audit matterbridge';
22
+ return (pkg?.scripts?.start === 'matterbridge' ||
23
+ pkg?.scripts?.['link'] === 'npm link --no-fund --no-audit matterbridge' ||
24
+ pkg?.scripts?.['dev:link'] === 'npm link --no-fund --no-audit matterbridge');
25
+ }
26
+ export async function hasOxc(rootDir) {
27
+ const pkg = (await parsePackageJson(rootDir));
28
+ if (!pkg.devDependencies || typeof pkg.devDependencies !== 'object')
29
+ return false;
30
+ return Object.hasOwn(pkg.devDependencies, 'oxfmt') && Object.hasOwn(pkg.devDependencies, 'oxlint');
18
31
  }
19
32
  export async function isLibrary(rootDir) {
20
33
  const production = JSON.parse(await readFile(path.join(rootDir, 'tsconfig.build.production.json'), 'utf8'));
21
- return (production?.compilerOptions?.declaration === true &&
22
- production.compilerOptions.declarationMap === true &&
23
- production.compilerOptions.sourceMap === true &&
24
- production.compilerOptions.removeComments === false);
34
+ return (production.compilerOptions?.declaration === true &&
35
+ production.compilerOptions?.declarationMap === true &&
36
+ production.compilerOptions?.sourceMap === true &&
37
+ production.compilerOptions?.removeComments === false);
25
38
  }
26
39
  export async function isMonorepo(rootDir) {
27
40
  const pkg = await parsePackageJson(rootDir);
28
41
  return pkg?.workspaces !== undefined;
29
42
  }
30
- const COPY_REPO_SKIP_NAMES = new Set(['node_modules', 'dist', 'dist-jest', '.cache', 'coverage']);
31
- const COPY_REPO_SKIP_EXTS = ['.tsbuildinfo', '.tgz'];
32
- export async function copyRepo(sourceDir, opts = {}) {
33
- const { install = true, linkMatterbridge = false, gitInit = false } = opts;
34
- const tmpDir = await mkdtemp(path.join(os.tmpdir(), 'mb-run-repo-'));
35
- async function copyDir(src, dest) {
36
- const entries = await readdir(src, { withFileTypes: true });
37
- await mkdir(dest, { recursive: true });
38
- for (const entry of entries) {
39
- const base = entry.name;
40
- if (COPY_REPO_SKIP_NAMES.has(base))
41
- continue;
42
- if (COPY_REPO_SKIP_EXTS.some((ext) => base.endsWith(ext)))
43
- continue;
44
- const srcPath = path.join(src, base);
45
- const destPath = path.join(dest, base);
46
- if (entry.isDirectory()) {
47
- await copyDir(srcPath, destPath);
48
- }
49
- else if (entry.isFile()) {
50
- await copyFile(srcPath, destPath);
51
- }
52
- }
53
- }
54
- await copyDir(sourceDir, tmpDir);
55
- if (install) {
56
- execSync('npm install --no-fund --no-audit', { cwd: tmpDir, stdio: 'inherit' });
57
- }
58
- if (linkMatterbridge) {
59
- try {
60
- execSync('npm link --no-fund --no-audit matterbridge', { cwd: tmpDir, stdio: 'inherit' });
61
- }
62
- catch {
63
- execSync('npm install --no-fund --no-audit matterbridge', { cwd: tmpDir, stdio: 'inherit' });
64
- }
65
- }
66
- if (gitInit) {
67
- const gitEnv = {
68
- ...process.env,
69
- GIT_AUTHOR_NAME: 'mb-run',
70
- GIT_AUTHOR_EMAIL: 'mb-run@localhost',
71
- GIT_COMMITTER_NAME: 'mb-run',
72
- GIT_COMMITTER_EMAIL: 'mb-run@localhost',
73
- };
74
- const execOpts = { cwd: tmpDir, stdio: 'ignore', env: gitEnv };
75
- execFileSync('git', ['init'], execOpts);
76
- execFileSync('git', ['symbolic-ref', 'HEAD', 'refs/heads/main'], execOpts);
77
- execFileSync('git', ['add', '-A'], execOpts);
78
- execFileSync('git', ['commit', '--no-verify', '-m', 'init'], execOpts);
79
- }
80
- return tmpDir;
81
- }
package/dist/jest.js ADDED
@@ -0,0 +1,11 @@
1
+ import { runBin } from './build.js';
2
+ export async function runJest(opts) {
3
+ const args = [];
4
+ if (opts.verbose)
5
+ args.push('--verbose');
6
+ if (opts.watch)
7
+ args.push('--watch');
8
+ if (opts.coverage)
9
+ args.push('--coverage');
10
+ await runBin('jest', args, { ...opts, mode: 'build', watch: false }, { env: { NODE_OPTIONS: '--experimental-vm-modules --no-warnings' } });
11
+ }
package/dist/lint.js ADDED
@@ -0,0 +1,15 @@
1
+ import { binExists } from './build.js';
2
+ import { runEsLint } from './eslint.js';
3
+ import { runOxLint } from './oxlint.js';
4
+ import { ExitError } from './spawn.js';
5
+ export async function runLinter(opts) {
6
+ if (await binExists('oxlint', opts)) {
7
+ await runOxLint(opts);
8
+ }
9
+ else if (await binExists('eslint', opts)) {
10
+ await runEsLint(opts);
11
+ }
12
+ else {
13
+ throw new ExitError(1, 'No linter found in node_modules: install oxlint or eslint.');
14
+ }
15
+ }
package/dist/logger.js CHANGED
@@ -54,6 +54,9 @@ export class Logger {
54
54
  return;
55
55
  log(`${this.logPrefix()} ${cyan(dir)}> ${brightYellow('restore')}`);
56
56
  };
57
+ logWarning = (message) => {
58
+ log(`${this.logPrefix()} ${brightYellow('warning')} ${message}`);
59
+ };
57
60
  logOxFormatError = (severity, filePath, message) => {
58
61
  const resolvedPath = path.resolve(filePath);
59
62
  const coloredSeverity = severity === 'Error' ? brightRed(severity) : severity === 'Warning' ? brightYellow(severity) : cyan(severity);
@@ -88,6 +91,9 @@ export function logBackup(dir) {
88
91
  export function logRestore(dir) {
89
92
  logger.logRestore(dir);
90
93
  }
94
+ export function logWarning(message) {
95
+ logger.logWarning(message);
96
+ }
91
97
  export function logOxFormatError(severity, filePath, message) {
92
98
  logger.logOxFormatError(severity, filePath, message);
93
99
  }