@proteinjs/build 2.0.0 → 2.0.2

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
@@ -3,6 +3,17 @@
3
3
  All notable changes to this project will be documented in this file.
4
4
  See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
5
5
 
6
+ ## [2.0.1](https://github.com/proteinjs/build/compare/@proteinjs/build@2.0.0...@proteinjs/build@2.0.1) (2026-05-21)
7
+
8
+
9
+ ### Performance Improvements
10
+
11
+ * **watch-workspace:** invoke watch scripts directly; clean up stale watchers ([e28d0a5](https://github.com/proteinjs/build/commit/e28d0a57fe258b3d0490dd492603d52cab4527fd))
12
+
13
+
14
+
15
+
16
+
6
17
  ## [1.9.2](https://github.com/proteinjs/build/compare/@proteinjs/build@1.9.1...@proteinjs/build@1.9.2) (2026-04-17)
7
18
 
8
19
 
@@ -1 +1 @@
1
- {"version":3,"file":"watchWorkspace.d.ts","sourceRoot":"","sources":["../../src/watchWorkspace.ts"],"names":[],"mappings":"AACA,OAAO,EAAe,iBAAiB,EAAwB,MAAM,sBAAsB,CAAC;AAI5F,eAAO,MAAM,cAAc,uBAA8B,iBAAiB,kBAqEzE,CAAC"}
1
+ {"version":3,"file":"watchWorkspace.d.ts","sourceRoot":"","sources":["../../src/watchWorkspace.ts"],"names":[],"mappings":"AACA,OAAO,EAAe,iBAAiB,EAAwB,MAAM,sBAAsB,CAAC;AAoE5F,eAAO,MAAM,cAAc,uBAA8B,iBAAiB,kBAiGzE,CAAC"}
@@ -1,4 +1,15 @@
1
1
  "use strict";
2
+ var __assign = (this && this.__assign) || function () {
3
+ __assign = Object.assign || function(t) {
4
+ for (var s, i = 1, n = arguments.length; i < n; i++) {
5
+ s = arguments[i];
6
+ for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p))
7
+ t[p] = s[p];
8
+ }
9
+ return t;
10
+ };
11
+ return __assign.apply(this, arguments);
12
+ };
2
13
  var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
14
  if (k2 === undefined) k2 = k;
4
15
  var desc = Object.getOwnPropertyDescriptor(m, k);
@@ -58,16 +69,92 @@ var __generator = (this && this.__generator) || function (thisArg, body) {
58
69
  if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
59
70
  }
60
71
  };
72
+ var __spreadArray = (this && this.__spreadArray) || function (to, from, pack) {
73
+ if (pack || arguments.length === 2) for (var i = 0, l = from.length, ar; i < l; i++) {
74
+ if (ar || !(i in from)) {
75
+ if (!ar) ar = Array.prototype.slice.call(from, 0, i);
76
+ ar[i] = from[i];
77
+ }
78
+ }
79
+ return to.concat(ar || Array.prototype.slice.call(from));
80
+ };
61
81
  Object.defineProperty(exports, "__esModule", { value: true });
62
82
  exports.watchWorkspace = void 0;
63
83
  var path = __importStar(require("path"));
64
84
  var util_node_1 = require("@proteinjs/util-node");
65
85
  var logger_1 = require("@proteinjs/logger");
66
86
  var logColors_1 = require("./logColors");
67
- var watchWorkspace = function (workspaceMetadata) { return __awaiter(void 0, void 0, void 0, function () {
68
- var cw, logger, workspacePath, _a, packageMap, sortedPackageNames, _b, skippedPackages, filteredPackageNames, loggingStartDelay, _loop_1, _i, filteredPackageNames_1, packageName;
87
+ /**
88
+ * Collect node_modules/.bin directories from `fromDir` up to (and including) `untilDir`.
89
+ * This mirrors the PATH that `npm run` provides, so a watch script's bins resolve when we
90
+ * invoke it directly via `sh -c` instead of paying for a resident `npm run` process per package.
91
+ */
92
+ var nodeModulesBinPaths = function (fromDir, untilDir) {
93
+ var binPaths = [];
94
+ var current = path.resolve(fromDir);
95
+ var stop = path.resolve(untilDir);
96
+ // eslint-disable-next-line no-constant-condition
97
+ while (true) {
98
+ binPaths.push(path.join(current, 'node_modules', '.bin'));
99
+ if (current === stop) {
100
+ break;
101
+ }
102
+ var parent_1 = path.dirname(current);
103
+ if (parent_1 === current) {
104
+ break;
105
+ }
106
+ current = parent_1;
107
+ }
108
+ return binPaths;
109
+ };
110
+ /**
111
+ * Find watcher processes (`reflection-watch`) belonging to this workspace. Used to clean up
112
+ * watchers orphaned by a previous `watch-workspace` run (e.g. one that was force-killed) —
113
+ * leftover watchers would otherwise make every file change trigger duplicate builds.
114
+ */
115
+ var findWorkspaceWatchers = function (workspacePath) { return __awaiter(void 0, void 0, void 0, function () {
116
+ var psStdout, result, _a, watcherSignature, workspacePrefix, pids, _i, _b, line, match, pid, command;
69
117
  return __generator(this, function (_c) {
70
118
  switch (_c.label) {
119
+ case 0:
120
+ _c.trys.push([0, 2, , 3]);
121
+ return [4 /*yield*/, (0, util_node_1.cmd)('ps', ['-eo', 'pid=,command='], {}, { omitLogs: { stdout: { omit: true }, stderr: { omit: true } } })];
122
+ case 1:
123
+ result = _c.sent();
124
+ psStdout = result.stdout;
125
+ return [3 /*break*/, 3];
126
+ case 2:
127
+ _a = _c.sent();
128
+ return [2 /*return*/, []];
129
+ case 3:
130
+ watcherSignature = '/node_modules/.bin/reflection-watch';
131
+ workspacePrefix = workspacePath.endsWith('/') ? workspacePath : "".concat(workspacePath, "/");
132
+ pids = [];
133
+ for (_i = 0, _b = psStdout.split('\n'); _i < _b.length; _i++) {
134
+ line = _b[_i];
135
+ match = line.match(/^\s*(\d+)\s+(.*)$/);
136
+ if (!match) {
137
+ continue;
138
+ }
139
+ pid = Number(match[1]);
140
+ command = match[2];
141
+ // Never match ourselves; only match watchers rooted under this exact workspace.
142
+ if (pid === process.pid) {
143
+ continue;
144
+ }
145
+ if (command.includes(workspacePrefix) && command.includes(watcherSignature)) {
146
+ pids.push(pid);
147
+ }
148
+ }
149
+ return [2 /*return*/, pids];
150
+ }
151
+ });
152
+ }); };
153
+ var watchWorkspace = function (workspaceMetadata) { return __awaiter(void 0, void 0, void 0, function () {
154
+ var cw, logger, workspacePath, _a, packageMap, sortedPackageNames, _b, skippedPackages, filteredPackageNames, staleWatchers, _i, staleWatchers_1, pid, loggingStartDelay, _loop_1, _c, filteredPackageNames_1, packageName;
155
+ var _d;
156
+ return __generator(this, function (_e) {
157
+ switch (_e.label) {
71
158
  case 0:
72
159
  cw = new util_node_1.LogColorWrapper();
73
160
  logger = new logger_1.Logger({ name: cw.color('workspace:', logColors_1.primaryLogColor) + cw.color('watch', logColors_1.secondaryLogColor) });
@@ -77,12 +164,29 @@ var watchWorkspace = function (workspaceMetadata) { return __awaiter(void 0, voi
77
164
  return [3 /*break*/, 3];
78
165
  case 1: return [4 /*yield*/, util_node_1.PackageUtil.getWorkspaceMetadata(workspacePath)];
79
166
  case 2:
80
- _b = _c.sent();
81
- _c.label = 3;
167
+ _b = _e.sent();
168
+ _e.label = 3;
82
169
  case 3:
83
170
  _a = _b, packageMap = _a.packageMap, sortedPackageNames = _a.sortedPackageNames;
84
171
  skippedPackages = ['root'];
85
172
  filteredPackageNames = sortedPackageNames.filter(function (packageName) { var _a; return !!((_a = packageMap[packageName].packageJson.scripts) === null || _a === void 0 ? void 0 : _a.watch) && !skippedPackages.includes(packageName); });
173
+ return [4 /*yield*/, findWorkspaceWatchers(workspacePath)];
174
+ case 4:
175
+ staleWatchers = _e.sent();
176
+ if (staleWatchers.length > 0) {
177
+ logger.info({
178
+ message: "> Cleaning up ".concat(cw.color("".concat(staleWatchers.length), logColors_1.secondaryLogColor), " stale watcher").concat(staleWatchers.length != 1 ? 's' : '', " from a previous run"),
179
+ });
180
+ for (_i = 0, staleWatchers_1 = staleWatchers; _i < staleWatchers_1.length; _i++) {
181
+ pid = staleWatchers_1[_i];
182
+ try {
183
+ process.kill(pid, 'SIGTERM');
184
+ }
185
+ catch (_f) {
186
+ // Process already exited; nothing to clean up.
187
+ }
188
+ }
189
+ }
86
190
  logger.info({
87
191
  message: "> Watching ".concat(cw.color("".concat(filteredPackageNames.length), logColors_1.secondaryLogColor), " package").concat(filteredPackageNames.length != 1 ? 's' : '', " in workspace (").concat(workspacePath, ")"),
88
192
  });
@@ -122,7 +226,15 @@ var watchWorkspace = function (workspaceMetadata) { return __awaiter(void 0, voi
122
226
  }
123
227
  return filteredOutput;
124
228
  };
125
- (0, util_node_1.cmd)('npm', ['run', 'watch'], { cwd: packageDir }, {
229
+ var watchScript = (_d = localPackage.packageJson.scripts) === null || _d === void 0 ? void 0 : _d.watch;
230
+ if (!watchScript) {
231
+ return "continue";
232
+ }
233
+ // Invoke the watch script directly instead of through `npm run`, which would leave a
234
+ // resident npm process per package. `sh -c` exec-replaces itself with the script's
235
+ // command, so no extra shell lingers either.
236
+ var watchEnv = __assign(__assign({}, process.env), { PATH: __spreadArray(__spreadArray([], nodeModulesBinPaths(packageDir, workspacePath), true), [process.env.PATH], false).filter(Boolean).join(path.delimiter) });
237
+ (0, util_node_1.cmd)('/bin/sh', ['-c', watchScript], { cwd: packageDir, env: watchEnv }, {
126
238
  omitLogs: {
127
239
  stdout: {
128
240
  filter: stdoutFilter,
@@ -130,8 +242,8 @@ var watchWorkspace = function (workspaceMetadata) { return __awaiter(void 0, voi
130
242
  },
131
243
  });
132
244
  };
133
- for (_i = 0, filteredPackageNames_1 = filteredPackageNames; _i < filteredPackageNames_1.length; _i++) {
134
- packageName = filteredPackageNames_1[_i];
245
+ for (_c = 0, filteredPackageNames_1 = filteredPackageNames; _c < filteredPackageNames_1.length; _c++) {
246
+ packageName = filteredPackageNames_1[_c];
135
247
  _loop_1(packageName);
136
248
  }
137
249
  return [2 /*return*/];
@@ -1 +1 @@
1
- {"version":3,"file":"watchWorkspace.js","sourceRoot":"","sources":["../../src/watchWorkspace.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"watchWorkspace.js","sourceRoot":"","sources":["../../src/watchWorkspace.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@proteinjs/build",
3
- "version": "2.0.0",
3
+ "version": "2.0.2",
4
4
  "description": "Workspace build tools",
5
5
  "main": "./dist/generated/index.js",
6
6
  "types": "./dist/generated/index.d.ts",
@@ -36,7 +36,7 @@
36
36
  },
37
37
  "dependencies": {
38
38
  "@octokit/core": "6.1.2",
39
- "@proteinjs/logger": "^1.0.18",
39
+ "@proteinjs/logger": "^1.0.19",
40
40
  "@proteinjs/util-node": "^1.8.1",
41
41
  "semver": "7.6.0"
42
42
  },
@@ -3,6 +3,70 @@ import { PackageUtil, WorkspaceMetadata, cmd, LogColorWrapper } from '@proteinjs
3
3
  import { Logger } from '@proteinjs/logger';
4
4
  import { primaryLogColor, secondaryLogColor } from './logColors';
5
5
 
6
+ /**
7
+ * Collect node_modules/.bin directories from `fromDir` up to (and including) `untilDir`.
8
+ * This mirrors the PATH that `npm run` provides, so a watch script's bins resolve when we
9
+ * invoke it directly via `sh -c` instead of paying for a resident `npm run` process per package.
10
+ */
11
+ const nodeModulesBinPaths = (fromDir: string, untilDir: string): string[] => {
12
+ const binPaths: string[] = [];
13
+ let current = path.resolve(fromDir);
14
+ const stop = path.resolve(untilDir);
15
+ // eslint-disable-next-line no-constant-condition
16
+ while (true) {
17
+ binPaths.push(path.join(current, 'node_modules', '.bin'));
18
+ if (current === stop) {
19
+ break;
20
+ }
21
+ const parent = path.dirname(current);
22
+ if (parent === current) {
23
+ break;
24
+ }
25
+ current = parent;
26
+ }
27
+ return binPaths;
28
+ };
29
+
30
+ /**
31
+ * Find watcher processes (`reflection-watch`) belonging to this workspace. Used to clean up
32
+ * watchers orphaned by a previous `watch-workspace` run (e.g. one that was force-killed) —
33
+ * leftover watchers would otherwise make every file change trigger duplicate builds.
34
+ */
35
+ const findWorkspaceWatchers = async (workspacePath: string): Promise<number[]> => {
36
+ let psStdout: string;
37
+ try {
38
+ const result = await cmd(
39
+ 'ps',
40
+ ['-eo', 'pid=,command='],
41
+ {},
42
+ { omitLogs: { stdout: { omit: true }, stderr: { omit: true } } }
43
+ );
44
+ psStdout = result.stdout;
45
+ } catch {
46
+ return [];
47
+ }
48
+
49
+ const watcherSignature = '/node_modules/.bin/reflection-watch';
50
+ const workspacePrefix = workspacePath.endsWith('/') ? workspacePath : `${workspacePath}/`;
51
+ const pids: number[] = [];
52
+ for (const line of psStdout.split('\n')) {
53
+ const match = line.match(/^\s*(\d+)\s+(.*)$/);
54
+ if (!match) {
55
+ continue;
56
+ }
57
+ const pid = Number(match[1]);
58
+ const command = match[2];
59
+ // Never match ourselves; only match watchers rooted under this exact workspace.
60
+ if (pid === process.pid) {
61
+ continue;
62
+ }
63
+ if (command.includes(workspacePrefix) && command.includes(watcherSignature)) {
64
+ pids.push(pid);
65
+ }
66
+ }
67
+ return pids;
68
+ };
69
+
6
70
  export const watchWorkspace = async (workspaceMetadata?: WorkspaceMetadata) => {
7
71
  const cw = new LogColorWrapper();
8
72
  const logger = new Logger({ name: cw.color('workspace:', primaryLogColor) + cw.color('watch', secondaryLogColor) });
@@ -15,6 +79,22 @@ export const watchWorkspace = async (workspaceMetadata?: WorkspaceMetadata) => {
15
79
  (packageName) => !!packageMap[packageName].packageJson.scripts?.watch && !skippedPackages.includes(packageName)
16
80
  );
17
81
 
82
+ // Kill watchers left behind by a previous run before spawning fresh ones, so a package is
83
+ // never watched twice (which would double every build).
84
+ const staleWatchers = await findWorkspaceWatchers(workspacePath);
85
+ if (staleWatchers.length > 0) {
86
+ logger.info({
87
+ message: `> Cleaning up ${cw.color(`${staleWatchers.length}`, secondaryLogColor)} stale watcher${staleWatchers.length != 1 ? 's' : ''} from a previous run`,
88
+ });
89
+ for (const pid of staleWatchers) {
90
+ try {
91
+ process.kill(pid, 'SIGTERM');
92
+ } catch {
93
+ // Process already exited; nothing to clean up.
94
+ }
95
+ }
96
+ }
97
+
18
98
  logger.info({
19
99
  message: `> Watching ${cw.color(`${filteredPackageNames.length}`, secondaryLogColor)} package${filteredPackageNames.length != 1 ? 's' : ''} in workspace (${workspacePath})`,
20
100
  });
@@ -59,10 +139,22 @@ export const watchWorkspace = async (workspaceMetadata?: WorkspaceMetadata) => {
59
139
 
60
140
  return filteredOutput;
61
141
  };
142
+ const watchScript = localPackage.packageJson.scripts?.watch;
143
+ if (!watchScript) {
144
+ continue;
145
+ }
146
+
147
+ // Invoke the watch script directly instead of through `npm run`, which would leave a
148
+ // resident npm process per package. `sh -c` exec-replaces itself with the script's
149
+ // command, so no extra shell lingers either.
150
+ const watchEnv = {
151
+ ...process.env,
152
+ PATH: [...nodeModulesBinPaths(packageDir, workspacePath), process.env.PATH].filter(Boolean).join(path.delimiter),
153
+ };
62
154
  cmd(
63
- 'npm',
64
- ['run', 'watch'],
65
- { cwd: packageDir },
155
+ '/bin/sh',
156
+ ['-c', watchScript],
157
+ { cwd: packageDir, env: watchEnv },
66
158
  {
67
159
  omitLogs: {
68
160
  stdout: {