local-package-tester 1.0.0

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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Nick Davies
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,211 @@
1
+ # lpt — Local Package Tester
2
+
3
+ [![CI](https://github.com/nickdavies791/local-package-tester/actions/workflows/ci.yml/badge.svg)](https://github.com/nickdavies791/local-package-tester/actions/workflows/ci.yml)
4
+ [![npm version](https://img.shields.io/npm/v/local-package-tester)](https://www.npmjs.com/package/local-package-tester)
5
+ [![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](LICENSE)
6
+ [![Node.js](https://img.shields.io/badge/node-%3E%3D18-brightgreen)](https://nodejs.org)
7
+
8
+ Test local packages without symlink headaches. `lpt` uses `npm pack` + `npm install` to create real tarball installations that work with Vite, Nuxt, Next.js, SvelteKit, Astro, and every other modern bundler.
9
+
10
+ ## Why not npm link?
11
+
12
+ `npm link` creates symlinks. Symlinks break with:
13
+
14
+ | Problem | npm link | yalc | lpt |
15
+ |---------|----------|------|-----|
16
+ | Vite/Nuxt/Next.js resolve errors | Broken | Works | Works |
17
+ | Duplicate peer dependencies | Common | Rare | Never |
18
+ | node_modules nesting issues | Yes | Sometimes | Never |
19
+ | Works with all package managers | npm only | npm/yarn | npm/pnpm/yarn/bun |
20
+ | Multiple targets at once | Manual | Manual | Built-in |
21
+ | File watching + auto-rebuild | No | Plugin | Built-in |
22
+ | HMR triggering | No | No | Auto-detected |
23
+
24
+ `lpt` installs your package the same way your users will — as a tarball. No symlinks, no resolution hacks, no surprises.
25
+
26
+ ## Quick Start
27
+
28
+ ```bash
29
+ # Install globally
30
+ npm install -g local-package-tester
31
+
32
+ # In your package directory, link to a target project
33
+ cd ~/projects/my-ui-library
34
+ lpt link ../my-app
35
+
36
+ # Build, pack, and install in one step
37
+ lpt dev
38
+
39
+ # Or watch for changes and auto-rebuild
40
+ lpt watch
41
+ ```
42
+
43
+ ## Commands
44
+
45
+ ### `lpt link <target>`
46
+
47
+ Link the current package to a target project. Run from your package's root directory.
48
+
49
+ ```bash
50
+ # Link to one project
51
+ lpt link ../my-app
52
+
53
+ # Link to multiple projects
54
+ lpt link ../my-app
55
+ lpt link ../my-other-app
56
+ ```
57
+
58
+ ### `lpt unlink [target]`
59
+
60
+ Remove a link. Omit the target to remove all links for the current package.
61
+
62
+ ```bash
63
+ # Remove specific target
64
+ lpt unlink ../my-app
65
+
66
+ # Remove all targets
67
+ lpt unlink
68
+ ```
69
+
70
+ ### `lpt dev`
71
+
72
+ One-shot build + pack + install to all linked targets.
73
+
74
+ ```bash
75
+ lpt dev
76
+
77
+ # Preview what would happen without executing
78
+ lpt dev --dry-run
79
+ ```
80
+
81
+ ### `lpt watch`
82
+
83
+ Watch for file changes and auto-rebuild. Includes debouncing and a rebuild queue so rapid saves don't cause overlapping builds.
84
+
85
+ ```bash
86
+ lpt watch
87
+
88
+ # Custom watch paths (comma-separated)
89
+ lpt watch --paths "src/**/*,lib/**/*"
90
+
91
+ # Custom debounce interval
92
+ lpt watch --debounce 500
93
+
94
+ # Preview configuration
95
+ lpt watch --dry-run
96
+ ```
97
+
98
+ ### `lpt list`
99
+
100
+ Show all linked packages and their targets.
101
+
102
+ ```bash
103
+ lpt list
104
+ ```
105
+
106
+ ### `lpt status`
107
+
108
+ Health check for all links. Shows validation status, last install time, detected package manager, build command, and watch paths.
109
+
110
+ ```bash
111
+ lpt status
112
+ ```
113
+
114
+ Output:
115
+
116
+ ```
117
+ Package: @myorg/ui-components
118
+ Source: ~/projects/ui-components (valid)
119
+ Targets:
120
+ → ~/projects/web-app (valid, last installed 2m ago)
121
+ → ~/projects/mobile-app (valid, never installed)
122
+ Package manager: pnpm (detected)
123
+ Watch paths: src/**/* lib/**/* package.json
124
+ ```
125
+
126
+ ## Global Flags
127
+
128
+ | Flag | Description |
129
+ |------|-------------|
130
+ | `--verbose` | Show detailed output including build logs |
131
+ | `--quiet` | Only show errors |
132
+ | `--version` | Show version |
133
+ | `--help` | Show help |
134
+
135
+ ## Configuration
136
+
137
+ ### `.lptrc`
138
+
139
+ Create a `.lptrc` file in your package root to customize behaviour:
140
+
141
+ ```json
142
+ {
143
+ "watch": ["src/**/*", "lib/**/*", "package.json"],
144
+ "buildCommand": "npm run build:lib",
145
+ "skipBuild": false,
146
+ "packageManager": "auto",
147
+ "debounce": 300,
148
+ "hmr": {
149
+ "strategy": "auto",
150
+ "files": ["src/trigger.ts"]
151
+ },
152
+ "hooks": {
153
+ "preBuild": "npm run lint",
154
+ "postInstall": "echo done"
155
+ }
156
+ }
157
+ ```
158
+
159
+ | Option | Default | Description |
160
+ |--------|---------|-------------|
161
+ | `watch` | `["src/**/*", "lib/**/*", "package.json"]` | Glob patterns to watch for changes |
162
+ | `buildCommand` | Auto-detected (`npm run build`, etc.) | Custom build command |
163
+ | `skipBuild` | `false` | Skip the build step entirely |
164
+ | `packageManager` | `"auto"` | Force a package manager (`npm`, `pnpm`, `yarn`, `bun`) |
165
+ | `debounce` | `300` | Milliseconds to wait before rebuilding after a change |
166
+ | `hmr.strategy` | `"auto"` | HMR trigger strategy: `"auto"`, `"touch"`, or `"none"` |
167
+ | `hmr.files` | — | Files to touch when strategy is `"touch"` |
168
+ | `hooks.preBuild` | — | Command to run before building |
169
+ | `hooks.postInstall` | — | Command to run after installing |
170
+
171
+ ### Package Manager Detection
172
+
173
+ `lpt` automatically detects the package manager for both source and target projects:
174
+
175
+ 1. Lock file (`pnpm-lock.yaml`, `yarn.lock`, `bun.lockb`, `package-lock.json`)
176
+ 2. `packageManager` field in `package.json` (corepack)
177
+ 3. Falls back to `npm`
178
+
179
+ The source and target can use different package managers — `lpt` handles the translation.
180
+
181
+ ### HMR Triggering
182
+
183
+ After installing, `lpt` automatically triggers hot module replacement by touching a framework-specific file:
184
+
185
+ | Framework | File touched |
186
+ |-----------|-------------|
187
+ | Nuxt | `app.vue` |
188
+ | Vite | `vite.config.ts` |
189
+ | Next.js | `next.config.js` |
190
+ | SvelteKit | `svelte.config.js` |
191
+ | Astro | `astro.config.mjs` |
192
+
193
+ Set `hmr.strategy` to `"none"` to disable, or `"touch"` with custom `hmr.files` to control which files are touched.
194
+
195
+ ## How It Works
196
+
197
+ ```
198
+ lpt dev
199
+
200
+ 1. Build → npm run build (in your package)
201
+ 2. Pack → npm pack (creates a tarball)
202
+ 3. Install → npm install pkg.tgz (in each target)
203
+ 4. HMR → touch app.vue (triggers hot reload)
204
+ 5. Cleanup → remove tarball
205
+ ```
206
+
207
+ This is the same installation path your users take when they `npm install` your published package. No symlinks, no resolution quirks.
208
+
209
+ ## License
210
+
211
+ [MIT](LICENSE)
package/dist/cli.cjs ADDED
@@ -0,0 +1,926 @@
1
+ #!/usr/bin/env node
2
+ "use strict";
3
+ var __create = Object.create;
4
+ var __defProp = Object.defineProperty;
5
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
6
+ var __getOwnPropNames = Object.getOwnPropertyNames;
7
+ var __getProtoOf = Object.getPrototypeOf;
8
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
9
+ var __copyProps = (to, from, except, desc) => {
10
+ if (from && typeof from === "object" || typeof from === "function") {
11
+ for (let key of __getOwnPropNames(from))
12
+ if (!__hasOwnProp.call(to, key) && key !== except)
13
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
14
+ }
15
+ return to;
16
+ };
17
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
18
+ // If the importer is in node compatibility mode or this is not an ESM
19
+ // file that has been converted to a CommonJS file using a Babel-
20
+ // compatible transform (i.e. "__esModule" has not been set), then set
21
+ // "default" to the CommonJS "module.exports" for node compatibility.
22
+ isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
23
+ mod
24
+ ));
25
+
26
+ // src/cli.ts
27
+ var import_commander = require("commander");
28
+
29
+ // src/commands/link.ts
30
+ var import_path2 = __toESM(require("path"), 1);
31
+ var import_fs2 = require("fs");
32
+
33
+ // src/services/config.ts
34
+ var import_fs = require("fs");
35
+ var import_path = __toESM(require("path"), 1);
36
+ var HOME = process.env.HOME || process.env.USERPROFILE || "";
37
+ var CONFIG_DIR = import_path.default.join(HOME, ".config", "lpt");
38
+ var CONFIG_PATH = import_path.default.join(CONFIG_DIR, "config.json");
39
+ var OLD_CONFIG_PATH = import_path.default.join(HOME, ".lpt-config.json");
40
+ function migrateLinks(links) {
41
+ return links.map((link) => {
42
+ const base = {
43
+ packageName: link.packageName,
44
+ sourceDir: link.sourceDir,
45
+ targetDirs: link.targetDirs || (link.targetDir ? [link.targetDir] : [])
46
+ };
47
+ if (link.lastInstalled) {
48
+ base.lastInstalled = link.lastInstalled;
49
+ }
50
+ return base;
51
+ });
52
+ }
53
+ async function getConfig() {
54
+ try {
55
+ const data = await import_fs.promises.readFile(CONFIG_PATH, "utf-8");
56
+ const raw = JSON.parse(data);
57
+ const config = { links: migrateLinks(raw.links || []) };
58
+ return config;
59
+ } catch {
60
+ }
61
+ try {
62
+ const data = await import_fs.promises.readFile(OLD_CONFIG_PATH, "utf-8");
63
+ const raw = JSON.parse(data);
64
+ const config = { links: migrateLinks(raw.links || []) };
65
+ await saveConfig(config);
66
+ return config;
67
+ } catch {
68
+ return { links: [] };
69
+ }
70
+ }
71
+ async function saveConfig(config) {
72
+ await import_fs.promises.mkdir(CONFIG_DIR, { recursive: true });
73
+ await import_fs.promises.writeFile(CONFIG_PATH, JSON.stringify(config, null, 2));
74
+ }
75
+ function findLink(config, packageName) {
76
+ return config.links.find((l) => l.packageName === packageName);
77
+ }
78
+ async function updateLastInstalled(packageName, targetDirs) {
79
+ const config = await getConfig();
80
+ const link = findLink(config, packageName);
81
+ if (!link) return;
82
+ if (!link.lastInstalled) {
83
+ link.lastInstalled = {};
84
+ }
85
+ const now = Date.now();
86
+ for (const dir of targetDirs) {
87
+ link.lastInstalled[dir] = now;
88
+ }
89
+ await saveConfig(config);
90
+ }
91
+
92
+ // src/utils/errors.ts
93
+ var c = {
94
+ reset: "\x1B[0m",
95
+ red: "\x1B[31m",
96
+ dim: "\x1B[2m"
97
+ };
98
+ var LptError = class extends Error {
99
+ constructor(message, suggestions = []) {
100
+ super(message);
101
+ this.suggestions = suggestions;
102
+ this.name = "LptError";
103
+ }
104
+ format() {
105
+ let output = `${c.red}error${c.reset} ${this.message}`;
106
+ if (this.suggestions.length > 0) {
107
+ output += "\n";
108
+ for (const suggestion of this.suggestions) {
109
+ output += `
110
+ ${c.dim}${suggestion}${c.reset}`;
111
+ }
112
+ }
113
+ return output;
114
+ }
115
+ };
116
+ var NoLinkError = class extends LptError {
117
+ constructor(packageName) {
118
+ super(`No linked target found for ${packageName}`, [
119
+ 'Run "lpt link <path>" to link your package to a target project.',
120
+ 'Run "lpt list" to see existing links.'
121
+ ]);
122
+ }
123
+ };
124
+ var NotAPackageError = class extends LptError {
125
+ constructor(dir) {
126
+ super(`No package.json found in ${dir}`, [
127
+ "Run this command from a directory containing a package.json."
128
+ ]);
129
+ }
130
+ };
131
+
132
+ // src/utils/logger.ts
133
+ var LEVELS = {
134
+ silent: 0,
135
+ error: 1,
136
+ warn: 2,
137
+ info: 3,
138
+ debug: 4
139
+ };
140
+ var c2 = {
141
+ reset: "\x1B[0m",
142
+ red: "\x1B[31m",
143
+ yellow: "\x1B[33m",
144
+ green: "\x1B[32m",
145
+ cyan: "\x1B[36m",
146
+ dim: "\x1B[2m"
147
+ };
148
+ var Logger = class {
149
+ level = "info";
150
+ setLevel(level) {
151
+ this.level = level;
152
+ }
153
+ isDebug() {
154
+ return LEVELS[this.level] >= LEVELS.debug;
155
+ }
156
+ error(message, ...args) {
157
+ if (LEVELS[this.level] >= LEVELS.error) {
158
+ console.error(`${c2.red}error${c2.reset} ${message}`, ...args);
159
+ }
160
+ }
161
+ warn(message, ...args) {
162
+ if (LEVELS[this.level] >= LEVELS.warn) {
163
+ console.warn(`${c2.yellow}warn${c2.reset} ${message}`, ...args);
164
+ }
165
+ }
166
+ info(message, ...args) {
167
+ if (LEVELS[this.level] >= LEVELS.info) {
168
+ console.log(message, ...args);
169
+ }
170
+ }
171
+ debug(message, ...args) {
172
+ if (LEVELS[this.level] >= LEVELS.debug) {
173
+ console.log(`${c2.dim}debug${c2.reset} ${message}`, ...args);
174
+ }
175
+ }
176
+ success(message) {
177
+ this.info(`${c2.green}\u2713${c2.reset} ${message}`);
178
+ }
179
+ step(message) {
180
+ this.info(`${c2.cyan}\u2192${c2.reset} ${message}`);
181
+ }
182
+ async timed(label, fn) {
183
+ const start = Date.now();
184
+ this.step(label);
185
+ await fn();
186
+ const elapsed = ((Date.now() - start) / 1e3).toFixed(1);
187
+ this.success(`${label} ${c2.dim}(${elapsed}s)${c2.reset}`);
188
+ }
189
+ };
190
+ var logger = new Logger();
191
+
192
+ // src/commands/link.ts
193
+ async function linkCommand(targetPath) {
194
+ const sourceDir = process.cwd();
195
+ const targetDir = import_path2.default.resolve(targetPath);
196
+ let packageName;
197
+ try {
198
+ const data = await import_fs2.promises.readFile(import_path2.default.join(sourceDir, "package.json"), "utf-8");
199
+ const pkg = JSON.parse(data);
200
+ if (!pkg.name) {
201
+ throw new LptError("Source package.json must have a name field");
202
+ }
203
+ packageName = pkg.name;
204
+ } catch (error) {
205
+ if (error instanceof LptError) throw error;
206
+ throw new NotAPackageError(sourceDir);
207
+ }
208
+ try {
209
+ await import_fs2.promises.access(import_path2.default.join(targetDir, "package.json"));
210
+ } catch {
211
+ throw new NotAPackageError(targetDir);
212
+ }
213
+ const config = await getConfig();
214
+ const existing = config.links.find((l) => l.packageName === packageName);
215
+ if (existing) {
216
+ if (!existing.targetDirs.includes(targetDir)) {
217
+ existing.targetDirs.push(targetDir);
218
+ } else {
219
+ logger.info(`${packageName} is already linked to ${targetDir}`);
220
+ return;
221
+ }
222
+ } else {
223
+ config.links.push({ packageName, sourceDir, targetDirs: [targetDir] });
224
+ }
225
+ await saveConfig(config);
226
+ logger.success(`Linked ${packageName} \u2192 ${import_path2.default.basename(targetDir)}`);
227
+ logger.info("");
228
+ printLinks(config.links);
229
+ }
230
+ function printLinks(links) {
231
+ if (links.length === 0) {
232
+ logger.info("No linked packages.");
233
+ return;
234
+ }
235
+ for (const link of links) {
236
+ const source = import_path2.default.relative(process.cwd(), link.sourceDir) || link.sourceDir;
237
+ logger.info(` ${link.packageName}`);
238
+ logger.info(` Source: ${source}`);
239
+ if (link.targetDirs.length === 0) {
240
+ logger.info(" Targets: (none)");
241
+ } else {
242
+ logger.info(" Targets:");
243
+ for (const target of link.targetDirs) {
244
+ const rel = import_path2.default.relative(process.cwd(), target) || target;
245
+ logger.info(` \u2192 ${rel}`);
246
+ }
247
+ }
248
+ logger.info("");
249
+ }
250
+ }
251
+
252
+ // src/commands/unlink.ts
253
+ var import_fs3 = require("fs");
254
+ var import_path3 = __toESM(require("path"), 1);
255
+ async function unlinkCommand(targetPath, options = {}) {
256
+ const sourceDir = process.cwd();
257
+ let packageName;
258
+ try {
259
+ const data = await import_fs3.promises.readFile(import_path3.default.join(sourceDir, "package.json"), "utf-8");
260
+ const pkg = JSON.parse(data);
261
+ packageName = pkg.name;
262
+ } catch {
263
+ throw new NotAPackageError(sourceDir);
264
+ }
265
+ const config = await getConfig();
266
+ const link = config.links.find((l) => l.packageName === packageName);
267
+ if (!link) {
268
+ throw new LptError(`No links found for ${packageName}`, [
269
+ 'Run "lpt link <path>" to link your package first.'
270
+ ]);
271
+ }
272
+ if (targetPath) {
273
+ const resolvedTarget = import_path3.default.resolve(targetPath);
274
+ const idx = link.targetDirs.indexOf(resolvedTarget);
275
+ if (idx === -1) {
276
+ throw new LptError(`${packageName} is not linked to ${resolvedTarget}`, [
277
+ 'Run "lpt list" to see current links.'
278
+ ]);
279
+ }
280
+ link.targetDirs.splice(idx, 1);
281
+ logger.success(`Unlinked ${packageName} \u2192 ${import_path3.default.basename(resolvedTarget)}`);
282
+ if (link.targetDirs.length === 0) {
283
+ config.links = config.links.filter((l) => l.packageName !== packageName);
284
+ }
285
+ } else {
286
+ config.links = config.links.filter((l) => l.packageName !== packageName);
287
+ logger.success(`Unlinked ${packageName} (all targets)`);
288
+ }
289
+ await saveConfig(config);
290
+ if (config.links.length > 0) {
291
+ logger.info("");
292
+ printLinks(config.links);
293
+ }
294
+ }
295
+
296
+ // src/commands/list.ts
297
+ async function listCommand() {
298
+ const config = await getConfig();
299
+ printLinks(config.links);
300
+ }
301
+
302
+ // src/commands/dev.ts
303
+ var import_fs9 = require("fs");
304
+ var import_path9 = __toESM(require("path"), 1);
305
+
306
+ // src/utils/exec.ts
307
+ var import_child_process = require("child_process");
308
+ var activeProcesses = /* @__PURE__ */ new Set();
309
+ async function exec(command, options = {}) {
310
+ const { cwd, timeout = 12e4 } = options;
311
+ const verbose = logger.isDebug();
312
+ return new Promise((resolve, reject) => {
313
+ const child = (0, import_child_process.spawn)(command, {
314
+ cwd,
315
+ shell: true,
316
+ stdio: ["ignore", "pipe", "pipe"]
317
+ });
318
+ activeProcesses.add(child);
319
+ let stdout = "";
320
+ let stderr = "";
321
+ child.stdout.on("data", (data) => {
322
+ stdout += data;
323
+ if (verbose) process.stdout.write(data);
324
+ });
325
+ child.stderr.on("data", (data) => {
326
+ stderr += data;
327
+ if (verbose) process.stderr.write(data);
328
+ });
329
+ const timer = setTimeout(() => {
330
+ child.kill("SIGTERM");
331
+ reject(new Error(`Command timed out after ${timeout}ms: ${command}`));
332
+ }, timeout);
333
+ child.on("close", (code) => {
334
+ clearTimeout(timer);
335
+ activeProcesses.delete(child);
336
+ if (code === 0) {
337
+ resolve(stdout.trim());
338
+ } else {
339
+ reject(new Error(`Command failed (exit ${code}): ${command}
340
+ ${stderr}`));
341
+ }
342
+ });
343
+ child.on("error", (error) => {
344
+ clearTimeout(timer);
345
+ activeProcesses.delete(child);
346
+ reject(error);
347
+ });
348
+ });
349
+ }
350
+ function killAll() {
351
+ for (const child of activeProcesses) {
352
+ child.kill("SIGTERM");
353
+ }
354
+ activeProcesses.clear();
355
+ }
356
+
357
+ // src/services/package-manager.ts
358
+ var import_fs4 = require("fs");
359
+ var import_path4 = __toESM(require("path"), 1);
360
+ var LOCK_FILES = [
361
+ ["pnpm-lock.yaml", "pnpm"],
362
+ ["yarn.lock", "yarn"],
363
+ ["bun.lockb", "bun"],
364
+ ["package-lock.json", "npm"]
365
+ ];
366
+ async function detectPackageManager(dir) {
367
+ for (const [file, pm] of LOCK_FILES) {
368
+ try {
369
+ await import_fs4.promises.access(import_path4.default.join(dir, file));
370
+ logger.debug(`Detected ${pm} from ${file}`);
371
+ return pm;
372
+ } catch {
373
+ continue;
374
+ }
375
+ }
376
+ try {
377
+ const data = await import_fs4.promises.readFile(import_path4.default.join(dir, "package.json"), "utf-8");
378
+ const pkg = JSON.parse(data);
379
+ if (pkg.packageManager) {
380
+ const pm = pkg.packageManager.split("@")[0];
381
+ if (["npm", "pnpm", "yarn", "bun"].includes(pm)) {
382
+ logger.debug(`Detected ${pm} from packageManager field`);
383
+ return pm;
384
+ }
385
+ }
386
+ } catch {
387
+ }
388
+ return "npm";
389
+ }
390
+ function getCommands(pm) {
391
+ switch (pm) {
392
+ case "pnpm":
393
+ return {
394
+ build: "pnpm run build",
395
+ pack: "pnpm pack",
396
+ install: (tarball) => `pnpm install "${tarball}"`
397
+ };
398
+ case "yarn":
399
+ return {
400
+ build: "yarn build",
401
+ pack: "yarn pack --filename package.tgz",
402
+ install: (tarball) => `yarn add "${tarball}"`
403
+ };
404
+ case "bun":
405
+ return {
406
+ build: "bun run build",
407
+ pack: "bun pm pack",
408
+ install: (tarball) => `bun add "${tarball}"`
409
+ };
410
+ case "npm":
411
+ default:
412
+ return {
413
+ build: "npm run build",
414
+ pack: "npm pack",
415
+ install: (tarball) => `npm install "${tarball}"`
416
+ };
417
+ }
418
+ }
419
+
420
+ // src/services/builder.ts
421
+ async function build(options) {
422
+ if (options.skipBuild) {
423
+ logger.debug("Skipping build (skipBuild is true)");
424
+ return;
425
+ }
426
+ let command;
427
+ if (options.buildCommand) {
428
+ command = options.buildCommand;
429
+ } else {
430
+ const pm = options.packageManager || await detectPackageManager(options.cwd);
431
+ command = getCommands(pm).build;
432
+ }
433
+ await logger.timed("Building package", async () => {
434
+ await exec(command, { cwd: options.cwd });
435
+ });
436
+ }
437
+
438
+ // src/services/installer.ts
439
+ var import_fs5 = require("fs");
440
+ var import_path5 = __toESM(require("path"), 1);
441
+ async function pack(options) {
442
+ const { sourceDir } = options;
443
+ const pm = options.packageManager || await detectPackageManager(sourceDir);
444
+ const commands = getCommands(pm);
445
+ let tarballPath = "";
446
+ await logger.timed("Packing tarball", async () => {
447
+ if (pm === "yarn") {
448
+ const filename = "lpt-package.tgz";
449
+ await exec(`${commands.pack} --filename ${filename}`, { cwd: sourceDir });
450
+ tarballPath = import_path5.default.join(sourceDir, filename);
451
+ } else {
452
+ const output = await exec(commands.pack, { cwd: sourceDir });
453
+ const tarballName = output.trim().split("\n").pop();
454
+ tarballPath = import_path5.default.join(sourceDir, tarballName);
455
+ }
456
+ });
457
+ return tarballPath;
458
+ }
459
+ async function install(options) {
460
+ const { tarballPath, targetDir } = options;
461
+ const pm = await detectPackageManager(targetDir);
462
+ const commands = getCommands(pm);
463
+ await logger.timed(`Installing in ${import_path5.default.basename(targetDir)}`, async () => {
464
+ await exec(commands.install(tarballPath), { cwd: targetDir });
465
+ });
466
+ }
467
+ async function packAndInstallAll(sourceDir, targetDirs, sourcePm) {
468
+ const tarballPath = await pack({ sourceDir, packageManager: sourcePm });
469
+ try {
470
+ for (const targetDir of targetDirs) {
471
+ await install({ tarballPath, targetDir });
472
+ }
473
+ } finally {
474
+ try {
475
+ await import_fs5.promises.unlink(tarballPath);
476
+ } catch {
477
+ }
478
+ }
479
+ }
480
+
481
+ // src/services/project-detector.ts
482
+ var import_fs6 = require("fs");
483
+ var import_path6 = __toESM(require("path"), 1);
484
+ async function detectProjectType(targetDir) {
485
+ try {
486
+ const data = await import_fs6.promises.readFile(import_path6.default.join(targetDir, "package.json"), "utf-8");
487
+ const pkg = JSON.parse(data);
488
+ const deps = { ...pkg.dependencies, ...pkg.devDependencies };
489
+ if (deps.nuxt || deps["nuxt3"]) return "nuxt";
490
+ if (deps.next) return "next";
491
+ if (deps["@sveltejs/kit"]) return "sveltekit";
492
+ if (deps.astro) return "astro";
493
+ if (deps.vite) return "vite";
494
+ return "unknown";
495
+ } catch {
496
+ return "unknown";
497
+ }
498
+ }
499
+
500
+ // src/services/hmr.ts
501
+ var import_fs7 = require("fs");
502
+ var import_path7 = __toESM(require("path"), 1);
503
+ var HMR_FILES = {
504
+ nuxt: ["app.vue", "nuxt.config.ts", "nuxt.config.js", "pages/index.vue"],
505
+ vite: ["vite.config.js", "vite.config.ts", "src/main.js", "src/main.ts", "src/App.vue"],
506
+ next: ["next.config.js", "next.config.mjs", "src/app/page.tsx", "src/app/page.js", "pages/index.tsx", "pages/index.js"],
507
+ sveltekit: ["svelte.config.js", "src/routes/+page.svelte", "vite.config.js", "vite.config.ts"],
508
+ astro: ["astro.config.mjs", "astro.config.ts", "src/pages/index.astro"]
509
+ };
510
+ async function touchFile(filePath) {
511
+ try {
512
+ const content = await import_fs7.promises.readFile(filePath, "utf-8");
513
+ await import_fs7.promises.writeFile(filePath, content);
514
+ return true;
515
+ } catch {
516
+ return false;
517
+ }
518
+ }
519
+ async function triggerHmr(targetDir, projectType, hmrConfig) {
520
+ if (hmrConfig?.strategy === "none") {
521
+ logger.debug("HMR disabled via config");
522
+ return true;
523
+ }
524
+ if (hmrConfig?.strategy === "touch" && hmrConfig.files?.length) {
525
+ for (const file of hmrConfig.files) {
526
+ const filePath = import_path7.default.join(targetDir, file);
527
+ if (await touchFile(filePath)) {
528
+ logger.step(`Triggering HMR via ${file} (custom)`);
529
+ return true;
530
+ }
531
+ }
532
+ return false;
533
+ }
534
+ const files = HMR_FILES[projectType];
535
+ if (!files) return false;
536
+ for (const file of files) {
537
+ const filePath = import_path7.default.join(targetDir, file);
538
+ if (await touchFile(filePath)) {
539
+ logger.step(`Triggering ${projectType} HMR via ${file}`);
540
+ return true;
541
+ }
542
+ }
543
+ return false;
544
+ }
545
+
546
+ // src/services/lptrc.ts
547
+ var import_fs8 = require("fs");
548
+ var import_path8 = __toESM(require("path"), 1);
549
+ async function readLptrc(sourceDir) {
550
+ try {
551
+ const data = await import_fs8.promises.readFile(import_path8.default.join(sourceDir, ".lptrc"), "utf-8");
552
+ return JSON.parse(data);
553
+ } catch {
554
+ return {};
555
+ }
556
+ }
557
+
558
+ // src/commands/dev.ts
559
+ async function devCommand(options = {}) {
560
+ const sourceDir = process.cwd();
561
+ let packageName;
562
+ try {
563
+ const data = await import_fs9.promises.readFile(import_path9.default.join(sourceDir, "package.json"), "utf-8");
564
+ const pkg = JSON.parse(data);
565
+ packageName = pkg.name;
566
+ } catch {
567
+ throw new NotAPackageError(sourceDir);
568
+ }
569
+ const config = await getConfig();
570
+ const link = config.links.find((l) => l.packageName === packageName);
571
+ if (!link) {
572
+ throw new NoLinkError(packageName);
573
+ }
574
+ const lptrc = await readLptrc(link.sourceDir);
575
+ if (options.dryRun) {
576
+ await printDryRun(link.sourceDir, link.targetDirs, lptrc);
577
+ return;
578
+ }
579
+ await buildAndInstallAll(link.sourceDir, link.targetDirs, lptrc);
580
+ await updateLastInstalled(packageName, link.targetDirs);
581
+ }
582
+ async function buildAndInstallAll(sourceDir, targetDirs, lptrc = {}) {
583
+ let sourcePm;
584
+ if (lptrc.packageManager && lptrc.packageManager !== "auto") {
585
+ sourcePm = lptrc.packageManager;
586
+ } else {
587
+ sourcePm = await detectPackageManager(sourceDir);
588
+ }
589
+ if (lptrc.hooks?.preBuild) {
590
+ logger.step(`Running pre-build hook: ${lptrc.hooks.preBuild}`);
591
+ await exec(lptrc.hooks.preBuild, { cwd: sourceDir });
592
+ }
593
+ await build({
594
+ cwd: sourceDir,
595
+ buildCommand: lptrc.buildCommand,
596
+ skipBuild: lptrc.skipBuild,
597
+ packageManager: sourcePm
598
+ });
599
+ await packAndInstallAll(sourceDir, targetDirs, sourcePm);
600
+ for (const targetDir of targetDirs) {
601
+ const projectType = await detectProjectType(targetDir);
602
+ logger.debug(`Detected ${projectType} project in ${import_path9.default.basename(targetDir)}`);
603
+ const hmrTriggered = await triggerHmr(targetDir, projectType, lptrc.hmr);
604
+ if (!hmrTriggered) {
605
+ logger.warn(`Could not trigger HMR in ${import_path9.default.basename(targetDir)}. You may need to restart your dev server.`);
606
+ }
607
+ }
608
+ if (lptrc.hooks?.postInstall) {
609
+ logger.step(`Running post-install hook: ${lptrc.hooks.postInstall}`);
610
+ await exec(lptrc.hooks.postInstall, { cwd: sourceDir });
611
+ }
612
+ const targetCount = targetDirs.length;
613
+ logger.success(`Package installed to ${targetCount} target${targetCount === 1 ? "" : "s"}!`);
614
+ }
615
+ async function printDryRun(sourceDir, targetDirs, lptrc) {
616
+ const dim = "\x1B[2m";
617
+ const reset = "\x1B[0m";
618
+ let sourcePm;
619
+ if (lptrc.packageManager && lptrc.packageManager !== "auto") {
620
+ sourcePm = lptrc.packageManager;
621
+ } else {
622
+ sourcePm = await detectPackageManager(sourceDir);
623
+ }
624
+ const sourceCmd = getCommands(sourcePm);
625
+ logger.info(`${dim}Dry run \u2014 no commands will be executed${reset}
626
+ `);
627
+ if (lptrc.hooks?.preBuild) {
628
+ logger.info(`Would run: ${lptrc.hooks.preBuild} ${dim}(pre-build hook, in ${sourceDir})${reset}`);
629
+ }
630
+ if (lptrc.skipBuild) {
631
+ logger.info(`Would skip build ${dim}(skipBuild is true)${reset}`);
632
+ } else {
633
+ const buildCmd = lptrc.buildCommand || sourceCmd.build;
634
+ logger.info(`Would run: ${buildCmd} ${dim}(in ${sourceDir})${reset}`);
635
+ }
636
+ logger.info(`Would run: ${sourceCmd.pack} ${dim}(in ${sourceDir})${reset}`);
637
+ for (const targetDir of targetDirs) {
638
+ const targetPm = await detectPackageManager(targetDir);
639
+ const targetCmd = getCommands(targetPm);
640
+ logger.info(`Would run: ${targetCmd.install("<tarball>")} ${dim}(in ${targetDir})${reset}`);
641
+ const projectType = await detectProjectType(targetDir);
642
+ if (lptrc.hmr?.strategy === "none") {
643
+ logger.info(`Would skip HMR ${dim}(disabled via config)${reset}`);
644
+ } else if (lptrc.hmr?.strategy === "touch" && lptrc.hmr.files?.length) {
645
+ logger.info(`Would touch: ${lptrc.hmr.files.join(", ")} ${dim}(custom HMR in ${import_path9.default.basename(targetDir)})${reset}`);
646
+ } else if (projectType !== "unknown") {
647
+ logger.info(`Would trigger ${projectType} HMR ${dim}(auto-detected in ${import_path9.default.basename(targetDir)})${reset}`);
648
+ }
649
+ }
650
+ if (lptrc.hooks?.postInstall) {
651
+ logger.info(`Would run: ${lptrc.hooks.postInstall} ${dim}(post-install hook, in ${sourceDir})${reset}`);
652
+ }
653
+ }
654
+
655
+ // src/commands/watch.ts
656
+ var import_fs11 = require("fs");
657
+ var import_path11 = __toESM(require("path"), 1);
658
+
659
+ // src/services/watcher.ts
660
+ var import_chokidar = __toESM(require("chokidar"), 1);
661
+ var import_fs10 = require("fs");
662
+ var import_path10 = __toESM(require("path"), 1);
663
+ async function getWatchPaths(sourceDir, lptrcWatch) {
664
+ if (lptrcWatch) {
665
+ return Array.isArray(lptrcWatch) ? lptrcWatch : [lptrcWatch];
666
+ }
667
+ try {
668
+ const pkgData = await import_fs10.promises.readFile(import_path10.default.join(sourceDir, "package.json"), "utf-8");
669
+ const pkg = JSON.parse(pkgData);
670
+ if (pkg.lpt?.watch) {
671
+ return Array.isArray(pkg.lpt.watch) ? pkg.lpt.watch : [pkg.lpt.watch];
672
+ }
673
+ } catch {
674
+ }
675
+ return ["src/**/*", "lib/**/*", "package.json"];
676
+ }
677
+ function startWatcher(options) {
678
+ const { sourceDir, watchPaths, debounce: debounceMs = 300, onChange } = options;
679
+ let rebuilding = false;
680
+ let queued = false;
681
+ let debounceTimer = null;
682
+ const runBuild = async () => {
683
+ if (rebuilding) {
684
+ queued = true;
685
+ return;
686
+ }
687
+ rebuilding = true;
688
+ try {
689
+ await onChange();
690
+ } catch (error) {
691
+ logger.error(`Rebuild failed: ${error instanceof Error ? error.message : String(error)}`);
692
+ }
693
+ rebuilding = false;
694
+ if (queued) {
695
+ queued = false;
696
+ logger.step("Queued rebuild starting...");
697
+ await runBuild();
698
+ }
699
+ };
700
+ const watcher = import_chokidar.default.watch(watchPaths, {
701
+ cwd: sourceDir,
702
+ ignored: /(^|[/\\])\../,
703
+ persistent: true
704
+ });
705
+ watcher.on("change", (filePath) => {
706
+ logger.info(`
707
+ Change detected in ${filePath}`);
708
+ if (debounceTimer) {
709
+ clearTimeout(debounceTimer);
710
+ }
711
+ debounceTimer = setTimeout(() => {
712
+ debounceTimer = null;
713
+ runBuild();
714
+ }, debounceMs);
715
+ });
716
+ return watcher;
717
+ }
718
+
719
+ // src/commands/watch.ts
720
+ async function watchCommand(options) {
721
+ const sourceDir = process.cwd();
722
+ let packageName;
723
+ try {
724
+ const data = await import_fs11.promises.readFile(import_path11.default.join(sourceDir, "package.json"), "utf-8");
725
+ const pkg = JSON.parse(data);
726
+ packageName = pkg.name;
727
+ } catch {
728
+ throw new NotAPackageError(sourceDir);
729
+ }
730
+ const config = await getConfig();
731
+ const link = config.links.find((l) => l.packageName === packageName);
732
+ if (!link) {
733
+ throw new NoLinkError(packageName);
734
+ }
735
+ const lptrc = await readLptrc(link.sourceDir);
736
+ let watchPaths;
737
+ if (options.paths) {
738
+ watchPaths = options.paths.split(",").map((p) => p.trim());
739
+ } else {
740
+ watchPaths = await getWatchPaths(link.sourceDir, lptrc.watch);
741
+ }
742
+ const debounce = options.debounce ? parseInt(options.debounce, 10) : lptrc.debounce ?? 300;
743
+ if (options.dryRun) {
744
+ await printWatchDryRun(link.sourceDir, link.targetDirs, watchPaths, debounce, lptrc);
745
+ return;
746
+ }
747
+ const targetCount = link.targetDirs.length;
748
+ logger.info(`Watching ${packageName} \u2192 ${targetCount} target${targetCount === 1 ? "" : "s"}`);
749
+ logger.info(`Paths: ${watchPaths.join(", ")}`);
750
+ logger.info(`Debounce: ${debounce}ms
751
+ `);
752
+ const watcher = startWatcher({
753
+ sourceDir: link.sourceDir,
754
+ watchPaths,
755
+ debounce,
756
+ onChange: async () => {
757
+ await buildAndInstallAll(link.sourceDir, link.targetDirs, lptrc);
758
+ await updateLastInstalled(packageName, link.targetDirs);
759
+ }
760
+ });
761
+ const cleanup = () => {
762
+ logger.info("\nShutting down...");
763
+ killAll();
764
+ watcher.close();
765
+ process.exit(0);
766
+ };
767
+ process.on("SIGINT", cleanup);
768
+ process.on("SIGTERM", cleanup);
769
+ }
770
+ async function printWatchDryRun(sourceDir, targetDirs, watchPaths, debounce, lptrc) {
771
+ const dim = "\x1B[2m";
772
+ const reset = "\x1B[0m";
773
+ let sourcePm;
774
+ if (lptrc.packageManager && lptrc.packageManager !== "auto") {
775
+ sourcePm = lptrc.packageManager;
776
+ } else {
777
+ sourcePm = await detectPackageManager(sourceDir);
778
+ }
779
+ const sourceCmd = getCommands(sourcePm);
780
+ logger.info(`${dim}Dry run \u2014 no commands will be executed${reset}
781
+ `);
782
+ logger.info(`Would watch: ${watchPaths.join(", ")} ${dim}(in ${sourceDir})${reset}`);
783
+ logger.info(`Debounce: ${debounce}ms
784
+ `);
785
+ logger.info("On each change:");
786
+ if (lptrc.hooks?.preBuild) {
787
+ logger.info(` Would run: ${lptrc.hooks.preBuild} ${dim}(pre-build hook)${reset}`);
788
+ }
789
+ if (lptrc.skipBuild) {
790
+ logger.info(` Would skip build ${dim}(skipBuild is true)${reset}`);
791
+ } else {
792
+ const buildCmd = lptrc.buildCommand || sourceCmd.build;
793
+ logger.info(` Would run: ${buildCmd} ${dim}(build)${reset}`);
794
+ }
795
+ logger.info(` Would run: ${sourceCmd.pack} ${dim}(pack)${reset}`);
796
+ for (const targetDir of targetDirs) {
797
+ const targetPm = await detectPackageManager(targetDir);
798
+ const targetCmd = getCommands(targetPm);
799
+ logger.info(` Would run: ${targetCmd.install("<tarball>")} ${dim}(in ${import_path11.default.basename(targetDir)})${reset}`);
800
+ const projectType = await detectProjectType(targetDir);
801
+ if (lptrc.hmr?.strategy === "none") {
802
+ logger.info(` Would skip HMR ${dim}(disabled)${reset}`);
803
+ } else if (projectType !== "unknown") {
804
+ logger.info(` Would trigger ${projectType} HMR ${dim}(in ${import_path11.default.basename(targetDir)})${reset}`);
805
+ }
806
+ }
807
+ if (lptrc.hooks?.postInstall) {
808
+ logger.info(` Would run: ${lptrc.hooks.postInstall} ${dim}(post-install hook)${reset}`);
809
+ }
810
+ }
811
+
812
+ // src/commands/status.ts
813
+ var import_fs12 = require("fs");
814
+ async function statusCommand() {
815
+ const config = await getConfig();
816
+ if (config.links.length === 0) {
817
+ logger.info("No linked packages.");
818
+ return;
819
+ }
820
+ for (const link of config.links) {
821
+ logger.info(`Package: ${link.packageName}`);
822
+ const sourceValid = await dirExists(link.sourceDir);
823
+ logger.info(`Source: ${link.sourceDir} ${sourceValid ? "(valid)" : "(NOT FOUND)"}`);
824
+ logger.info("Targets:");
825
+ for (const target of link.targetDirs) {
826
+ const valid = await dirExists(target);
827
+ const lastTs = link.lastInstalled?.[target];
828
+ const timeStr = lastTs ? `, last installed ${timeAgo(lastTs)}` : ", never installed";
829
+ logger.info(` \u2192 ${target} ${valid ? `(valid${timeStr})` : "(NOT FOUND)"}`);
830
+ }
831
+ if (sourceValid) {
832
+ const lptrc = await readLptrc(link.sourceDir);
833
+ const pm = lptrc.packageManager && lptrc.packageManager !== "auto" ? lptrc.packageManager : await detectPackageManager(link.sourceDir);
834
+ logger.info(`Package manager: ${pm}${lptrc.packageManager ? " (config)" : " (detected)"}`);
835
+ if (lptrc.buildCommand) {
836
+ logger.info(`Build command: ${lptrc.buildCommand}`);
837
+ }
838
+ if (lptrc.skipBuild) {
839
+ logger.info("Build: skipped");
840
+ }
841
+ const watchPaths = await getWatchPaths(link.sourceDir, lptrc.watch);
842
+ logger.info(`Watch paths: ${watchPaths.join(" ")}`);
843
+ }
844
+ logger.info("");
845
+ }
846
+ }
847
+ async function dirExists(dir) {
848
+ try {
849
+ const stat = await import_fs12.promises.stat(dir);
850
+ return stat.isDirectory();
851
+ } catch {
852
+ return false;
853
+ }
854
+ }
855
+ function timeAgo(timestamp) {
856
+ const seconds = Math.floor((Date.now() - timestamp) / 1e3);
857
+ if (seconds < 5) return "just now";
858
+ if (seconds < 60) return `${seconds}s ago`;
859
+ const minutes = Math.floor(seconds / 60);
860
+ if (minutes < 60) return `${minutes}m ago`;
861
+ const hours = Math.floor(minutes / 60);
862
+ if (hours < 24) return `${hours}h ago`;
863
+ const days = Math.floor(hours / 24);
864
+ return `${days}d ago`;
865
+ }
866
+
867
+ // src/cli.ts
868
+ function handleError(error) {
869
+ if (error instanceof LptError) {
870
+ console.error(error.format());
871
+ } else if (error instanceof Error) {
872
+ logger.error(error.message);
873
+ } else {
874
+ logger.error(String(error));
875
+ }
876
+ process.exit(1);
877
+ }
878
+ import_commander.program.name("lpt").version("1.0.0").description("Local Package Tester \u2014 tarball-based npm link replacement").option("--verbose", "Show detailed output").option("--quiet", "Only show errors").hook("preAction", () => {
879
+ const opts = import_commander.program.opts();
880
+ if (opts.verbose) logger.setLevel("debug");
881
+ else if (opts.quiet) logger.setLevel("error");
882
+ });
883
+ import_commander.program.command("link <target>").description("Link current package to a target project").action(async (target) => {
884
+ try {
885
+ await linkCommand(target);
886
+ } catch (e) {
887
+ handleError(e);
888
+ }
889
+ });
890
+ import_commander.program.command("unlink [target]").description("Remove link for current package (specific target or all)").option("-a, --all", "Remove all targets (same as no argument)").action(async (target, options) => {
891
+ try {
892
+ await unlinkCommand(target, options);
893
+ } catch (e) {
894
+ handleError(e);
895
+ }
896
+ });
897
+ import_commander.program.command("list").description("List all linked packages").action(async () => {
898
+ try {
899
+ await listCommand();
900
+ } catch (e) {
901
+ handleError(e);
902
+ }
903
+ });
904
+ import_commander.program.command("dev").description("Build and install package once").option("-n, --dry-run", "Show what would run without executing").action(async (options) => {
905
+ try {
906
+ await devCommand(options);
907
+ } catch (e) {
908
+ handleError(e);
909
+ }
910
+ });
911
+ import_commander.program.command("watch").description("Watch for changes and auto-rebuild").option("-p, --paths <paths>", "Comma-separated watch paths").option("-d, --debounce <ms>", "Debounce interval in milliseconds").option("-n, --dry-run", "Show what would run without executing").action(async (options) => {
912
+ try {
913
+ await watchCommand(options);
914
+ } catch (e) {
915
+ handleError(e);
916
+ }
917
+ });
918
+ import_commander.program.command("status").description("Show status of all linked packages").action(async () => {
919
+ try {
920
+ await statusCommand();
921
+ } catch (e) {
922
+ handleError(e);
923
+ }
924
+ });
925
+ import_commander.program.parse();
926
+ //# sourceMappingURL=cli.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/cli.ts","../src/commands/link.ts","../src/services/config.ts","../src/utils/errors.ts","../src/utils/logger.ts","../src/commands/unlink.ts","../src/commands/list.ts","../src/commands/dev.ts","../src/utils/exec.ts","../src/services/package-manager.ts","../src/services/builder.ts","../src/services/installer.ts","../src/services/project-detector.ts","../src/services/hmr.ts","../src/services/lptrc.ts","../src/commands/watch.ts","../src/services/watcher.ts","../src/commands/status.ts"],"sourcesContent":["import { program } from 'commander';\nimport { linkCommand } from './commands/link';\nimport { unlinkCommand } from './commands/unlink';\nimport { listCommand } from './commands/list';\nimport { devCommand } from './commands/dev';\nimport { watchCommand } from './commands/watch';\nimport { statusCommand } from './commands/status';\nimport { logger } from './utils/logger';\nimport { LptError } from './utils/errors';\n\ndeclare const __VERSION__: string;\n\nfunction handleError(error: unknown): never {\n if (error instanceof LptError) {\n console.error(error.format());\n } else if (error instanceof Error) {\n logger.error(error.message);\n } else {\n logger.error(String(error));\n }\n process.exit(1);\n}\n\nprogram\n .name('lpt')\n .version(__VERSION__)\n .description('Local Package Tester — tarball-based npm link replacement')\n .option('--verbose', 'Show detailed output')\n .option('--quiet', 'Only show errors')\n .hook('preAction', () => {\n const opts = program.opts();\n if (opts.verbose) logger.setLevel('debug');\n else if (opts.quiet) logger.setLevel('error');\n });\n\nprogram\n .command('link <target>')\n .description('Link current package to a target project')\n .action(async (target: string) => {\n try {\n await linkCommand(target);\n } catch (e) {\n handleError(e);\n }\n });\n\nprogram\n .command('unlink [target]')\n .description('Remove link for current package (specific target or all)')\n .option('-a, --all', 'Remove all targets (same as no argument)')\n .action(async (target: string | undefined, options: { all?: boolean }) => {\n try {\n await unlinkCommand(target, options);\n } catch (e) {\n handleError(e);\n }\n });\n\nprogram\n .command('list')\n .description('List all linked packages')\n .action(async () => {\n try {\n await listCommand();\n } catch (e) {\n handleError(e);\n }\n });\n\nprogram\n .command('dev')\n .description('Build and install package once')\n .option('-n, --dry-run', 'Show what would run without executing')\n .action(async (options: { dryRun?: boolean }) => {\n try {\n await devCommand(options);\n } catch (e) {\n handleError(e);\n }\n });\n\nprogram\n .command('watch')\n .description('Watch for changes and auto-rebuild')\n .option('-p, --paths <paths>', 'Comma-separated watch paths')\n .option('-d, --debounce <ms>', 'Debounce interval in milliseconds')\n .option('-n, --dry-run', 'Show what would run without executing')\n .action(async (options: { paths?: string; debounce?: string; dryRun?: boolean }) => {\n try {\n await watchCommand(options);\n } catch (e) {\n handleError(e);\n }\n });\n\nprogram\n .command('status')\n .description('Show status of all linked packages')\n .action(async () => {\n try {\n await statusCommand();\n } catch (e) {\n handleError(e);\n }\n });\n\nprogram.parse();\n","import path from 'path';\nimport { promises as fs } from 'fs';\nimport { getConfig, saveConfig } from '../services/config';\nimport { LptError, NotAPackageError } from '../utils/errors';\nimport { logger } from '../utils/logger';\nimport type { LinkConfig } from '../types';\n\nexport async function linkCommand(targetPath: string): Promise<void> {\n const sourceDir = process.cwd();\n const targetDir = path.resolve(targetPath);\n\n // Validate source\n let packageName: string;\n try {\n const data = await fs.readFile(path.join(sourceDir, 'package.json'), 'utf-8');\n const pkg = JSON.parse(data);\n if (!pkg.name) {\n throw new LptError('Source package.json must have a name field');\n }\n packageName = pkg.name;\n } catch (error) {\n if (error instanceof LptError) throw error;\n throw new NotAPackageError(sourceDir);\n }\n\n // Validate target\n try {\n await fs.access(path.join(targetDir, 'package.json'));\n } catch {\n throw new NotAPackageError(targetDir);\n }\n\n // Update config — add target to existing link or create new\n const config = await getConfig();\n const existing = config.links.find((l) => l.packageName === packageName);\n\n if (existing) {\n if (!existing.targetDirs.includes(targetDir)) {\n existing.targetDirs.push(targetDir);\n } else {\n logger.info(`${packageName} is already linked to ${targetDir}`);\n return;\n }\n } else {\n config.links.push({ packageName, sourceDir, targetDirs: [targetDir] });\n }\n\n await saveConfig(config);\n\n logger.success(`Linked ${packageName} → ${path.basename(targetDir)}`);\n logger.info('');\n printLinks(config.links);\n}\n\nexport function printLinks(links: LinkConfig[]): void {\n if (links.length === 0) {\n logger.info('No linked packages.');\n return;\n }\n\n for (const link of links) {\n const source = path.relative(process.cwd(), link.sourceDir) || link.sourceDir;\n logger.info(` ${link.packageName}`);\n logger.info(` Source: ${source}`);\n if (link.targetDirs.length === 0) {\n logger.info(' Targets: (none)');\n } else {\n logger.info(' Targets:');\n for (const target of link.targetDirs) {\n const rel = path.relative(process.cwd(), target) || target;\n logger.info(` → ${rel}`);\n }\n }\n logger.info('');\n }\n}\n","import { promises as fs } from 'fs';\nimport path from 'path';\nimport type { LptConfig, LinkConfig, LegacyLinkConfig } from '../types';\n\nconst HOME = process.env.HOME || process.env.USERPROFILE || '';\nconst CONFIG_DIR = path.join(HOME, '.config', 'lpt');\nconst CONFIG_PATH = path.join(CONFIG_DIR, 'config.json');\nconst OLD_CONFIG_PATH = path.join(HOME, '.lpt-config.json');\n\ninterface RawLink {\n packageName: string;\n sourceDir: string;\n targetDir?: string;\n targetDirs?: string[];\n lastInstalled?: Record<string, number>;\n}\n\n/** Migrate links from old targetDir format to targetDirs array */\nfunction migrateLinks(links: RawLink[]): LinkConfig[] {\n return links.map((link) => {\n const base: LinkConfig = {\n packageName: link.packageName,\n sourceDir: link.sourceDir,\n targetDirs: link.targetDirs || (link.targetDir ? [link.targetDir] : []),\n };\n if (link.lastInstalled) {\n base.lastInstalled = link.lastInstalled;\n }\n return base;\n });\n}\n\nexport async function getConfig(): Promise<LptConfig> {\n // Try new config location\n try {\n const data = await fs.readFile(CONFIG_PATH, 'utf-8');\n const raw = JSON.parse(data);\n const config: LptConfig = { links: migrateLinks(raw.links || []) };\n return config;\n } catch {\n // Not found — try old location for migration\n }\n\n try {\n const data = await fs.readFile(OLD_CONFIG_PATH, 'utf-8');\n const raw = JSON.parse(data);\n const config: LptConfig = { links: migrateLinks(raw.links || []) };\n // Migrate to new location\n await saveConfig(config);\n return config;\n } catch {\n return { links: [] };\n }\n}\n\nexport async function saveConfig(config: LptConfig): Promise<void> {\n await fs.mkdir(CONFIG_DIR, { recursive: true });\n await fs.writeFile(CONFIG_PATH, JSON.stringify(config, null, 2));\n}\n\nexport function findLink(config: LptConfig, packageName: string): LinkConfig | undefined {\n return config.links.find((l) => l.packageName === packageName);\n}\n\n/** Record successful install timestamps for a package's targets */\nexport async function updateLastInstalled(packageName: string, targetDirs: string[]): Promise<void> {\n const config = await getConfig();\n const link = findLink(config, packageName);\n if (!link) return;\n\n if (!link.lastInstalled) {\n link.lastInstalled = {};\n }\n const now = Date.now();\n for (const dir of targetDirs) {\n link.lastInstalled[dir] = now;\n }\n await saveConfig(config);\n}\n","const c = {\n reset: '\\x1b[0m',\n red: '\\x1b[31m',\n dim: '\\x1b[2m',\n};\n\nexport class LptError extends Error {\n constructor(\n message: string,\n public suggestions: string[] = [],\n ) {\n super(message);\n this.name = 'LptError';\n }\n\n format(): string {\n let output = `${c.red}error${c.reset} ${this.message}`;\n if (this.suggestions.length > 0) {\n output += '\\n';\n for (const suggestion of this.suggestions) {\n output += `\\n ${c.dim}${suggestion}${c.reset}`;\n }\n }\n return output;\n }\n}\n\nexport class NoLinkError extends LptError {\n constructor(packageName: string) {\n super(`No linked target found for ${packageName}`, [\n 'Run \"lpt link <path>\" to link your package to a target project.',\n 'Run \"lpt list\" to see existing links.',\n ]);\n }\n}\n\nexport class NotAPackageError extends LptError {\n constructor(dir: string) {\n super(`No package.json found in ${dir}`, [\n 'Run this command from a directory containing a package.json.',\n ]);\n }\n}\n\nexport class BuildError extends LptError {\n constructor(message: string) {\n super(`Build failed: ${message}`, [\n 'Check the build output above for errors.',\n 'Run with --verbose for detailed output.',\n ]);\n }\n}\n","import type { LogLevel } from '../types';\n\nconst LEVELS: Record<LogLevel, number> = {\n silent: 0,\n error: 1,\n warn: 2,\n info: 3,\n debug: 4,\n};\n\nconst c = {\n reset: '\\x1b[0m',\n red: '\\x1b[31m',\n yellow: '\\x1b[33m',\n green: '\\x1b[32m',\n cyan: '\\x1b[36m',\n dim: '\\x1b[2m',\n};\n\nclass Logger {\n private level: LogLevel = 'info';\n\n setLevel(level: LogLevel): void {\n this.level = level;\n }\n\n isDebug(): boolean {\n return LEVELS[this.level] >= LEVELS.debug;\n }\n\n error(message: string, ...args: unknown[]): void {\n if (LEVELS[this.level] >= LEVELS.error) {\n console.error(`${c.red}error${c.reset} ${message}`, ...args);\n }\n }\n\n warn(message: string, ...args: unknown[]): void {\n if (LEVELS[this.level] >= LEVELS.warn) {\n console.warn(`${c.yellow}warn${c.reset} ${message}`, ...args);\n }\n }\n\n info(message: string, ...args: unknown[]): void {\n if (LEVELS[this.level] >= LEVELS.info) {\n console.log(message, ...args);\n }\n }\n\n debug(message: string, ...args: unknown[]): void {\n if (LEVELS[this.level] >= LEVELS.debug) {\n console.log(`${c.dim}debug${c.reset} ${message}`, ...args);\n }\n }\n\n success(message: string): void {\n this.info(`${c.green}✓${c.reset} ${message}`);\n }\n\n step(message: string): void {\n this.info(`${c.cyan}→${c.reset} ${message}`);\n }\n\n async timed(label: string, fn: () => Promise<void>): Promise<void> {\n const start = Date.now();\n this.step(label);\n await fn();\n const elapsed = ((Date.now() - start) / 1000).toFixed(1);\n this.success(`${label} ${c.dim}(${elapsed}s)${c.reset}`);\n }\n}\n\nexport const logger = new Logger();\n","import { promises as fs } from 'fs';\nimport path from 'path';\nimport { getConfig, saveConfig } from '../services/config';\nimport { LptError, NotAPackageError } from '../utils/errors';\nimport { logger } from '../utils/logger';\nimport { printLinks } from './link';\n\nexport interface UnlinkCommandOptions {\n all?: boolean;\n}\n\nexport async function unlinkCommand(targetPath?: string, options: UnlinkCommandOptions = {}): Promise<void> {\n const sourceDir = process.cwd();\n\n let packageName: string;\n try {\n const data = await fs.readFile(path.join(sourceDir, 'package.json'), 'utf-8');\n const pkg = JSON.parse(data);\n packageName = pkg.name;\n } catch {\n throw new NotAPackageError(sourceDir);\n }\n\n const config = await getConfig();\n const link = config.links.find((l) => l.packageName === packageName);\n\n if (!link) {\n throw new LptError(`No links found for ${packageName}`, [\n 'Run \"lpt link <path>\" to link your package first.',\n ]);\n }\n\n if (targetPath) {\n // Remove specific target\n const resolvedTarget = path.resolve(targetPath);\n const idx = link.targetDirs.indexOf(resolvedTarget);\n if (idx === -1) {\n throw new LptError(`${packageName} is not linked to ${resolvedTarget}`, [\n 'Run \"lpt list\" to see current links.',\n ]);\n }\n\n link.targetDirs.splice(idx, 1);\n logger.success(`Unlinked ${packageName} → ${path.basename(resolvedTarget)}`);\n\n // If no targets left, remove the entire link\n if (link.targetDirs.length === 0) {\n config.links = config.links.filter((l) => l.packageName !== packageName);\n }\n } else {\n // Remove all targets (default or --all)\n config.links = config.links.filter((l) => l.packageName !== packageName);\n logger.success(`Unlinked ${packageName} (all targets)`);\n }\n\n await saveConfig(config);\n\n if (config.links.length > 0) {\n logger.info('');\n printLinks(config.links);\n }\n}\n","import { getConfig } from '../services/config';\nimport { printLinks } from './link';\n\nexport async function listCommand(): Promise<void> {\n const config = await getConfig();\n printLinks(config.links);\n}\n","import { promises as fs } from 'fs';\nimport path from 'path';\nimport { getConfig, updateLastInstalled } from '../services/config';\nimport { build } from '../services/builder';\nimport { packAndInstallAll } from '../services/installer';\nimport { detectProjectType } from '../services/project-detector';\nimport { triggerHmr } from '../services/hmr';\nimport { readLptrc } from '../services/lptrc';\nimport { detectPackageManager, getCommands } from '../services/package-manager';\nimport { NoLinkError, NotAPackageError } from '../utils/errors';\nimport { logger } from '../utils/logger';\nimport { exec } from '../utils/exec';\nimport type { LptrcConfig, PackageManager } from '../types';\n\nexport interface DevCommandOptions {\n dryRun?: boolean;\n}\n\nexport async function devCommand(options: DevCommandOptions = {}): Promise<void> {\n const sourceDir = process.cwd();\n\n let packageName: string;\n try {\n const data = await fs.readFile(path.join(sourceDir, 'package.json'), 'utf-8');\n const pkg = JSON.parse(data);\n packageName = pkg.name;\n } catch {\n throw new NotAPackageError(sourceDir);\n }\n\n const config = await getConfig();\n const link = config.links.find((l) => l.packageName === packageName);\n if (!link) {\n throw new NoLinkError(packageName);\n }\n\n const lptrc = await readLptrc(link.sourceDir);\n\n if (options.dryRun) {\n await printDryRun(link.sourceDir, link.targetDirs, lptrc);\n return;\n }\n\n await buildAndInstallAll(link.sourceDir, link.targetDirs, lptrc);\n await updateLastInstalled(packageName, link.targetDirs);\n}\n\nexport async function buildAndInstallAll(\n sourceDir: string,\n targetDirs: string[],\n lptrc: LptrcConfig = {},\n): Promise<void> {\n // Resolve source package manager\n let sourcePm: PackageManager;\n if (lptrc.packageManager && lptrc.packageManager !== 'auto') {\n sourcePm = lptrc.packageManager;\n } else {\n sourcePm = await detectPackageManager(sourceDir);\n }\n\n // Pre-build hook\n if (lptrc.hooks?.preBuild) {\n logger.step(`Running pre-build hook: ${lptrc.hooks.preBuild}`);\n await exec(lptrc.hooks.preBuild, { cwd: sourceDir });\n }\n\n // Build once\n await build({\n cwd: sourceDir,\n buildCommand: lptrc.buildCommand,\n skipBuild: lptrc.skipBuild,\n packageManager: sourcePm,\n });\n\n // Pack once, install to all targets\n await packAndInstallAll(sourceDir, targetDirs, sourcePm);\n\n // HMR trigger for each target\n for (const targetDir of targetDirs) {\n const projectType = await detectProjectType(targetDir);\n logger.debug(`Detected ${projectType} project in ${path.basename(targetDir)}`);\n\n const hmrTriggered = await triggerHmr(targetDir, projectType, lptrc.hmr);\n if (!hmrTriggered) {\n logger.warn(`Could not trigger HMR in ${path.basename(targetDir)}. You may need to restart your dev server.`);\n }\n }\n\n // Post-install hook\n if (lptrc.hooks?.postInstall) {\n logger.step(`Running post-install hook: ${lptrc.hooks.postInstall}`);\n await exec(lptrc.hooks.postInstall, { cwd: sourceDir });\n }\n\n const targetCount = targetDirs.length;\n logger.success(`Package installed to ${targetCount} target${targetCount === 1 ? '' : 's'}!`);\n}\n\nasync function printDryRun(\n sourceDir: string,\n targetDirs: string[],\n lptrc: LptrcConfig,\n): Promise<void> {\n const dim = '\\x1b[2m';\n const reset = '\\x1b[0m';\n\n // Resolve source PM\n let sourcePm: PackageManager;\n if (lptrc.packageManager && lptrc.packageManager !== 'auto') {\n sourcePm = lptrc.packageManager;\n } else {\n sourcePm = await detectPackageManager(sourceDir);\n }\n const sourceCmd = getCommands(sourcePm);\n\n logger.info(`${dim}Dry run — no commands will be executed${reset}\\n`);\n\n // Pre-build hook\n if (lptrc.hooks?.preBuild) {\n logger.info(`Would run: ${lptrc.hooks.preBuild} ${dim}(pre-build hook, in ${sourceDir})${reset}`);\n }\n\n // Build\n if (lptrc.skipBuild) {\n logger.info(`Would skip build ${dim}(skipBuild is true)${reset}`);\n } else {\n const buildCmd = lptrc.buildCommand || sourceCmd.build;\n logger.info(`Would run: ${buildCmd} ${dim}(in ${sourceDir})${reset}`);\n }\n\n // Pack\n logger.info(`Would run: ${sourceCmd.pack} ${dim}(in ${sourceDir})${reset}`);\n\n // Install per target\n for (const targetDir of targetDirs) {\n const targetPm = await detectPackageManager(targetDir);\n const targetCmd = getCommands(targetPm);\n logger.info(`Would run: ${targetCmd.install('<tarball>')} ${dim}(in ${targetDir})${reset}`);\n\n // HMR\n const projectType = await detectProjectType(targetDir);\n if (lptrc.hmr?.strategy === 'none') {\n logger.info(`Would skip HMR ${dim}(disabled via config)${reset}`);\n } else if (lptrc.hmr?.strategy === 'touch' && lptrc.hmr.files?.length) {\n logger.info(`Would touch: ${lptrc.hmr.files.join(', ')} ${dim}(custom HMR in ${path.basename(targetDir)})${reset}`);\n } else if (projectType !== 'unknown') {\n logger.info(`Would trigger ${projectType} HMR ${dim}(auto-detected in ${path.basename(targetDir)})${reset}`);\n }\n }\n\n // Post-install hook\n if (lptrc.hooks?.postInstall) {\n logger.info(`Would run: ${lptrc.hooks.postInstall} ${dim}(post-install hook, in ${sourceDir})${reset}`);\n }\n}\n","import { spawn, ChildProcess } from 'child_process';\nimport { logger } from './logger';\n\nexport interface ExecOptions {\n cwd?: string;\n timeout?: number;\n}\n\n/** Active child processes — used by killAll for graceful shutdown */\nconst activeProcesses = new Set<ChildProcess>();\n\nexport async function exec(command: string, options: ExecOptions = {}): Promise<string> {\n const { cwd, timeout = 120_000 } = options;\n const verbose = logger.isDebug();\n\n return new Promise((resolve, reject) => {\n const child = spawn(command, {\n cwd,\n shell: true,\n stdio: ['ignore', 'pipe', 'pipe'],\n });\n\n activeProcesses.add(child);\n\n let stdout = '';\n let stderr = '';\n\n child.stdout.on('data', (data: Buffer) => {\n stdout += data;\n if (verbose) process.stdout.write(data);\n });\n\n child.stderr.on('data', (data: Buffer) => {\n stderr += data;\n if (verbose) process.stderr.write(data);\n });\n\n const timer = setTimeout(() => {\n child.kill('SIGTERM');\n reject(new Error(`Command timed out after ${timeout}ms: ${command}`));\n }, timeout);\n\n child.on('close', (code) => {\n clearTimeout(timer);\n activeProcesses.delete(child);\n if (code === 0) {\n resolve(stdout.trim());\n } else {\n reject(new Error(`Command failed (exit ${code}): ${command}\\n${stderr}`));\n }\n });\n\n child.on('error', (error) => {\n clearTimeout(timer);\n activeProcesses.delete(child);\n reject(error);\n });\n });\n}\n\n/** Kill all active child processes. Used during graceful shutdown. */\nexport function killAll(): void {\n for (const child of activeProcesses) {\n child.kill('SIGTERM');\n }\n activeProcesses.clear();\n}\n","import { promises as fs } from 'fs';\nimport path from 'path';\nimport { logger } from '../utils/logger';\nimport type { PackageManager } from '../types';\n\nconst LOCK_FILES: [string, PackageManager][] = [\n ['pnpm-lock.yaml', 'pnpm'],\n ['yarn.lock', 'yarn'],\n ['bun.lockb', 'bun'],\n ['package-lock.json', 'npm'],\n];\n\nexport async function detectPackageManager(dir: string): Promise<PackageManager> {\n // Check lock files\n for (const [file, pm] of LOCK_FILES) {\n try {\n await fs.access(path.join(dir, file));\n logger.debug(`Detected ${pm} from ${file}`);\n return pm;\n } catch {\n continue;\n }\n }\n\n // Check package.json packageManager field (corepack)\n try {\n const data = await fs.readFile(path.join(dir, 'package.json'), 'utf-8');\n const pkg = JSON.parse(data);\n if (pkg.packageManager) {\n const pm = pkg.packageManager.split('@')[0] as PackageManager;\n if (['npm', 'pnpm', 'yarn', 'bun'].includes(pm)) {\n logger.debug(`Detected ${pm} from packageManager field`);\n return pm;\n }\n }\n } catch {\n // Fall through\n }\n\n return 'npm';\n}\n\nexport interface PackCommands {\n build: string;\n pack: string;\n install: (tarball: string) => string;\n}\n\nexport function getCommands(pm: PackageManager): PackCommands {\n switch (pm) {\n case 'pnpm':\n return {\n build: 'pnpm run build',\n pack: 'pnpm pack',\n install: (tarball) => `pnpm install \"${tarball}\"`,\n };\n case 'yarn':\n return {\n build: 'yarn build',\n pack: 'yarn pack --filename package.tgz',\n install: (tarball) => `yarn add \"${tarball}\"`,\n };\n case 'bun':\n return {\n build: 'bun run build',\n pack: 'bun pm pack',\n install: (tarball) => `bun add \"${tarball}\"`,\n };\n case 'npm':\n default:\n return {\n build: 'npm run build',\n pack: 'npm pack',\n install: (tarball) => `npm install \"${tarball}\"`,\n };\n }\n}\n","import { exec } from '../utils/exec';\nimport { logger } from '../utils/logger';\nimport { detectPackageManager, getCommands } from './package-manager';\nimport type { PackageManager } from '../types';\n\nexport interface BuildOptions {\n cwd: string;\n buildCommand?: string;\n skipBuild?: boolean;\n packageManager?: PackageManager;\n}\n\nexport async function build(options: BuildOptions): Promise<void> {\n if (options.skipBuild) {\n logger.debug('Skipping build (skipBuild is true)');\n return;\n }\n\n let command: string;\n if (options.buildCommand) {\n command = options.buildCommand;\n } else {\n const pm = options.packageManager || await detectPackageManager(options.cwd);\n command = getCommands(pm).build;\n }\n\n await logger.timed('Building package', async () => {\n await exec(command, { cwd: options.cwd });\n });\n}\n","import { promises as fs } from 'fs';\nimport path from 'path';\nimport { exec } from '../utils/exec';\nimport { logger } from '../utils/logger';\nimport { detectPackageManager, getCommands } from './package-manager';\nimport type { PackageManager } from '../types';\n\nexport interface PackOptions {\n sourceDir: string;\n packageManager?: PackageManager;\n}\n\nexport interface InstallOptions {\n tarballPath: string;\n targetDir: string;\n}\n\n/**\n * Pack the source package into a tarball.\n * Returns the absolute path to the created tarball.\n */\nexport async function pack(options: PackOptions): Promise<string> {\n const { sourceDir } = options;\n const pm = options.packageManager || await detectPackageManager(sourceDir);\n const commands = getCommands(pm);\n\n let tarballPath = '';\n\n await logger.timed('Packing tarball', async () => {\n if (pm === 'yarn') {\n // yarn pack needs explicit filename\n const filename = 'lpt-package.tgz';\n await exec(`${commands.pack} --filename ${filename}`, { cwd: sourceDir });\n tarballPath = path.join(sourceDir, filename);\n } else {\n // npm/pnpm/bun: read tarball name from stdout\n const output = await exec(commands.pack, { cwd: sourceDir });\n const tarballName = output.trim().split('\\n').pop()!;\n tarballPath = path.join(sourceDir, tarballName);\n }\n });\n\n return tarballPath;\n}\n\n/**\n * Install a tarball into a target project using the target's package manager.\n */\nexport async function install(options: InstallOptions): Promise<void> {\n const { tarballPath, targetDir } = options;\n const pm = await detectPackageManager(targetDir);\n const commands = getCommands(pm);\n\n await logger.timed(`Installing in ${path.basename(targetDir)}`, async () => {\n await exec(commands.install(tarballPath), { cwd: targetDir });\n });\n}\n\n/**\n * Pack once, install to multiple targets, then cleanup.\n */\nexport async function packAndInstallAll(\n sourceDir: string,\n targetDirs: string[],\n sourcePm?: PackageManager,\n): Promise<void> {\n const tarballPath = await pack({ sourceDir, packageManager: sourcePm });\n\n try {\n for (const targetDir of targetDirs) {\n await install({ tarballPath, targetDir });\n }\n } finally {\n // Cleanup tarball\n try {\n await fs.unlink(tarballPath);\n } catch {\n // Ignore cleanup errors\n }\n }\n}\n","import { promises as fs } from 'fs';\nimport path from 'path';\nimport type { ProjectType } from '../types';\n\nexport async function detectProjectType(targetDir: string): Promise<ProjectType> {\n try {\n const data = await fs.readFile(path.join(targetDir, 'package.json'), 'utf-8');\n const pkg = JSON.parse(data);\n const deps = { ...pkg.dependencies, ...pkg.devDependencies };\n\n if (deps.nuxt || deps['nuxt3']) return 'nuxt';\n if (deps.next) return 'next';\n if (deps['@sveltejs/kit']) return 'sveltekit';\n if (deps.astro) return 'astro';\n if (deps.vite) return 'vite';\n\n return 'unknown';\n } catch {\n return 'unknown';\n }\n}\n","import { promises as fs } from 'fs';\nimport path from 'path';\nimport { logger } from '../utils/logger';\nimport type { ProjectType, LptrcConfig } from '../types';\n\nconst HMR_FILES: Partial<Record<ProjectType, string[]>> = {\n nuxt: ['app.vue', 'nuxt.config.ts', 'nuxt.config.js', 'pages/index.vue'],\n vite: ['vite.config.js', 'vite.config.ts', 'src/main.js', 'src/main.ts', 'src/App.vue'],\n next: ['next.config.js', 'next.config.mjs', 'src/app/page.tsx', 'src/app/page.js', 'pages/index.tsx', 'pages/index.js'],\n sveltekit: ['svelte.config.js', 'src/routes/+page.svelte', 'vite.config.js', 'vite.config.ts'],\n astro: ['astro.config.mjs', 'astro.config.ts', 'src/pages/index.astro'],\n};\n\nasync function touchFile(filePath: string): Promise<boolean> {\n try {\n const content = await fs.readFile(filePath, 'utf-8');\n await fs.writeFile(filePath, content);\n return true;\n } catch {\n return false;\n }\n}\n\nexport async function triggerHmr(\n targetDir: string,\n projectType: ProjectType,\n hmrConfig?: LptrcConfig['hmr'],\n): Promise<boolean> {\n // Strategy: none — skip HMR\n if (hmrConfig?.strategy === 'none') {\n logger.debug('HMR disabled via config');\n return true;\n }\n\n // Strategy: touch — use custom file list\n if (hmrConfig?.strategy === 'touch' && hmrConfig.files?.length) {\n for (const file of hmrConfig.files) {\n const filePath = path.join(targetDir, file);\n if (await touchFile(filePath)) {\n logger.step(`Triggering HMR via ${file} (custom)`);\n return true;\n }\n }\n return false;\n }\n\n // Strategy: auto (default) — use framework detection\n const files = HMR_FILES[projectType];\n if (!files) return false;\n\n for (const file of files) {\n const filePath = path.join(targetDir, file);\n if (await touchFile(filePath)) {\n logger.step(`Triggering ${projectType} HMR via ${file}`);\n return true;\n }\n }\n\n return false;\n}\n","import { promises as fs } from 'fs';\nimport path from 'path';\nimport type { LptrcConfig } from '../types';\n\nexport async function readLptrc(sourceDir: string): Promise<LptrcConfig> {\n try {\n const data = await fs.readFile(path.join(sourceDir, '.lptrc'), 'utf-8');\n return JSON.parse(data);\n } catch {\n return {};\n }\n}\n","import { promises as fs } from 'fs';\nimport path from 'path';\nimport { getConfig, updateLastInstalled } from '../services/config';\nimport { readLptrc } from '../services/lptrc';\nimport { detectPackageManager, getCommands } from '../services/package-manager';\nimport { detectProjectType } from '../services/project-detector';\nimport { getWatchPaths, startWatcher } from '../services/watcher';\nimport { NoLinkError, NotAPackageError } from '../utils/errors';\nimport { logger } from '../utils/logger';\nimport { killAll } from '../utils/exec';\nimport { buildAndInstallAll } from './dev';\nimport type { LptrcConfig, PackageManager } from '../types';\n\nexport interface WatchCommandOptions {\n paths?: string;\n debounce?: string;\n dryRun?: boolean;\n}\n\nexport async function watchCommand(options: WatchCommandOptions): Promise<void> {\n const sourceDir = process.cwd();\n\n let packageName: string;\n try {\n const data = await fs.readFile(path.join(sourceDir, 'package.json'), 'utf-8');\n const pkg = JSON.parse(data);\n packageName = pkg.name;\n } catch {\n throw new NotAPackageError(sourceDir);\n }\n\n const config = await getConfig();\n const link = config.links.find((l) => l.packageName === packageName);\n if (!link) {\n throw new NoLinkError(packageName);\n }\n\n const lptrc = await readLptrc(link.sourceDir);\n\n // Resolve watch paths: CLI flag > lptrc > package.json > defaults\n let watchPaths: string[];\n if (options.paths) {\n watchPaths = options.paths.split(',').map((p) => p.trim());\n } else {\n watchPaths = await getWatchPaths(link.sourceDir, lptrc.watch);\n }\n\n // Resolve debounce: CLI flag > lptrc > default (300ms)\n const debounce = options.debounce\n ? parseInt(options.debounce, 10)\n : lptrc.debounce ?? 300;\n\n if (options.dryRun) {\n await printWatchDryRun(link.sourceDir, link.targetDirs, watchPaths, debounce, lptrc);\n return;\n }\n\n const targetCount = link.targetDirs.length;\n logger.info(`Watching ${packageName} → ${targetCount} target${targetCount === 1 ? '' : 's'}`);\n logger.info(`Paths: ${watchPaths.join(', ')}`);\n logger.info(`Debounce: ${debounce}ms\\n`);\n\n const watcher = startWatcher({\n sourceDir: link.sourceDir,\n watchPaths,\n debounce,\n onChange: async () => {\n await buildAndInstallAll(link.sourceDir, link.targetDirs, lptrc);\n await updateLastInstalled(packageName, link.targetDirs);\n },\n });\n\n // Graceful shutdown\n const cleanup = () => {\n logger.info('\\nShutting down...');\n killAll();\n watcher.close();\n process.exit(0);\n };\n\n process.on('SIGINT', cleanup);\n process.on('SIGTERM', cleanup);\n}\n\nasync function printWatchDryRun(\n sourceDir: string,\n targetDirs: string[],\n watchPaths: string[],\n debounce: number,\n lptrc: LptrcConfig,\n): Promise<void> {\n const dim = '\\x1b[2m';\n const reset = '\\x1b[0m';\n\n let sourcePm: PackageManager;\n if (lptrc.packageManager && lptrc.packageManager !== 'auto') {\n sourcePm = lptrc.packageManager;\n } else {\n sourcePm = await detectPackageManager(sourceDir);\n }\n const sourceCmd = getCommands(sourcePm);\n\n logger.info(`${dim}Dry run — no commands will be executed${reset}\\n`);\n logger.info(`Would watch: ${watchPaths.join(', ')} ${dim}(in ${sourceDir})${reset}`);\n logger.info(`Debounce: ${debounce}ms\\n`);\n logger.info('On each change:');\n\n if (lptrc.hooks?.preBuild) {\n logger.info(` Would run: ${lptrc.hooks.preBuild} ${dim}(pre-build hook)${reset}`);\n }\n if (lptrc.skipBuild) {\n logger.info(` Would skip build ${dim}(skipBuild is true)${reset}`);\n } else {\n const buildCmd = lptrc.buildCommand || sourceCmd.build;\n logger.info(` Would run: ${buildCmd} ${dim}(build)${reset}`);\n }\n logger.info(` Would run: ${sourceCmd.pack} ${dim}(pack)${reset}`);\n\n for (const targetDir of targetDirs) {\n const targetPm = await detectPackageManager(targetDir);\n const targetCmd = getCommands(targetPm);\n logger.info(` Would run: ${targetCmd.install('<tarball>')} ${dim}(in ${path.basename(targetDir)})${reset}`);\n\n const projectType = await detectProjectType(targetDir);\n if (lptrc.hmr?.strategy === 'none') {\n logger.info(` Would skip HMR ${dim}(disabled)${reset}`);\n } else if (projectType !== 'unknown') {\n logger.info(` Would trigger ${projectType} HMR ${dim}(in ${path.basename(targetDir)})${reset}`);\n }\n }\n\n if (lptrc.hooks?.postInstall) {\n logger.info(` Would run: ${lptrc.hooks.postInstall} ${dim}(post-install hook)${reset}`);\n }\n}\n","import chokidar from 'chokidar';\nimport { promises as fs } from 'fs';\nimport path from 'path';\nimport { logger } from '../utils/logger';\n\nexport interface WatchOptions {\n sourceDir: string;\n watchPaths: string[];\n debounce?: number;\n onChange: () => Promise<void>;\n}\n\n/**\n * Resolve watch paths. lptrc watch config takes priority, then package.json lpt field, then defaults.\n */\nexport async function getWatchPaths(\n sourceDir: string,\n lptrcWatch?: string | string[],\n): Promise<string[]> {\n // Priority 1: lptrc watch config (passed from caller)\n if (lptrcWatch) {\n return Array.isArray(lptrcWatch) ? lptrcWatch : [lptrcWatch];\n }\n\n // Priority 2: package.json lpt field\n try {\n const pkgData = await fs.readFile(path.join(sourceDir, 'package.json'), 'utf-8');\n const pkg = JSON.parse(pkgData);\n if (pkg.lpt?.watch) {\n return Array.isArray(pkg.lpt.watch) ? pkg.lpt.watch : [pkg.lpt.watch];\n }\n } catch {\n // Fall through\n }\n\n // Priority 3: Defaults\n return ['src/**/*', 'lib/**/*', 'package.json'];\n}\n\nexport function startWatcher(options: WatchOptions): chokidar.FSWatcher {\n const { sourceDir, watchPaths, debounce: debounceMs = 300, onChange } = options;\n\n let rebuilding = false;\n let queued = false;\n let debounceTimer: ReturnType<typeof setTimeout> | null = null;\n\n const runBuild = async () => {\n if (rebuilding) {\n queued = true;\n return;\n }\n\n rebuilding = true;\n try {\n await onChange();\n } catch (error) {\n logger.error(`Rebuild failed: ${error instanceof Error ? error.message : String(error)}`);\n }\n rebuilding = false;\n\n if (queued) {\n queued = false;\n logger.step('Queued rebuild starting...');\n await runBuild();\n }\n };\n\n const watcher = chokidar.watch(watchPaths, {\n cwd: sourceDir,\n ignored: /(^|[/\\\\])\\../,\n persistent: true,\n });\n\n watcher.on('change', (filePath) => {\n logger.info(`\\nChange detected in ${filePath}`);\n\n if (debounceTimer) {\n clearTimeout(debounceTimer);\n }\n\n debounceTimer = setTimeout(() => {\n debounceTimer = null;\n runBuild();\n }, debounceMs);\n });\n\n return watcher;\n}\n","import { promises as fs } from 'fs';\nimport path from 'path';\nimport { getConfig } from '../services/config';\nimport { readLptrc } from '../services/lptrc';\nimport { detectPackageManager } from '../services/package-manager';\nimport { getWatchPaths } from '../services/watcher';\nimport { logger } from '../utils/logger';\n\nexport async function statusCommand(): Promise<void> {\n const config = await getConfig();\n\n if (config.links.length === 0) {\n logger.info('No linked packages.');\n return;\n }\n\n for (const link of config.links) {\n logger.info(`Package: ${link.packageName}`);\n\n const sourceValid = await dirExists(link.sourceDir);\n logger.info(`Source: ${link.sourceDir} ${sourceValid ? '(valid)' : '(NOT FOUND)'}`);\n\n logger.info('Targets:');\n for (const target of link.targetDirs) {\n const valid = await dirExists(target);\n const lastTs = link.lastInstalled?.[target];\n const timeStr = lastTs ? `, last installed ${timeAgo(lastTs)}` : ', never installed';\n logger.info(` → ${target} ${valid ? `(valid${timeStr})` : '(NOT FOUND)'}`);\n }\n\n if (sourceValid) {\n const lptrc = await readLptrc(link.sourceDir);\n const pm = (lptrc.packageManager && lptrc.packageManager !== 'auto')\n ? lptrc.packageManager\n : await detectPackageManager(link.sourceDir);\n logger.info(`Package manager: ${pm}${lptrc.packageManager ? ' (config)' : ' (detected)'}`);\n\n if (lptrc.buildCommand) {\n logger.info(`Build command: ${lptrc.buildCommand}`);\n }\n if (lptrc.skipBuild) {\n logger.info('Build: skipped');\n }\n\n const watchPaths = await getWatchPaths(link.sourceDir, lptrc.watch);\n logger.info(`Watch paths: ${watchPaths.join(' ')}`);\n }\n\n logger.info('');\n }\n}\n\nasync function dirExists(dir: string): Promise<boolean> {\n try {\n const stat = await fs.stat(dir);\n return stat.isDirectory();\n } catch {\n return false;\n }\n}\n\nfunction timeAgo(timestamp: number): string {\n const seconds = Math.floor((Date.now() - timestamp) / 1000);\n\n if (seconds < 5) return 'just now';\n if (seconds < 60) return `${seconds}s ago`;\n\n const minutes = Math.floor(seconds / 60);\n if (minutes < 60) return `${minutes}m ago`;\n\n const hours = Math.floor(minutes / 60);\n if (hours < 24) return `${hours}h ago`;\n\n const days = Math.floor(hours / 24);\n return `${days}d ago`;\n}\n"],"mappings":"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","names":["import_path","import_fs","path","fs","c","path","fs","import_fs","import_path","fs","path","import_fs","import_path","import_fs","import_path","fs","path","import_fs","import_path","path","fs","import_fs","import_path","fs","path","import_fs","import_path","fs","path","import_fs","import_path","fs","path","fs","path","import_fs","import_path","import_fs","import_path","fs","path","chokidar","fs","path","import_fs","fs"]}
package/package.json ADDED
@@ -0,0 +1,61 @@
1
+ {
2
+ "name": "local-package-tester",
3
+ "version": "1.0.0",
4
+ "type": "module",
5
+ "description": "Local package testing without symlink headaches. A tarball-based npm link replacement that works with Vite, Nuxt, and modern bundlers.",
6
+ "bin": {
7
+ "lpt": "dist/cli.cjs"
8
+ },
9
+ "scripts": {
10
+ "build": "tsup",
11
+ "dev": "tsup --watch",
12
+ "typecheck": "tsc --noEmit",
13
+ "test": "vitest run",
14
+ "test:watch": "vitest",
15
+ "test:coverage": "vitest run --coverage",
16
+ "prepublishOnly": "npm run build"
17
+ },
18
+ "keywords": [
19
+ "npm-link",
20
+ "local-development",
21
+ "package-testing",
22
+ "tarball",
23
+ "vite",
24
+ "nuxt",
25
+ "nextjs",
26
+ "sveltekit",
27
+ "astro",
28
+ "yalc-alternative",
29
+ "monorepo",
30
+ "cli"
31
+ ],
32
+ "author": "Nick Davies",
33
+ "license": "MIT",
34
+ "repository": {
35
+ "type": "git",
36
+ "url": "git+https://github.com/nickdavies791/local-package-tester.git"
37
+ },
38
+ "bugs": {
39
+ "url": "https://github.com/nickdavies791/local-package-tester/issues"
40
+ },
41
+ "homepage": "https://github.com/nickdavies791/local-package-tester#readme",
42
+ "engines": {
43
+ "node": ">=18"
44
+ },
45
+ "files": [
46
+ "dist",
47
+ "README.md",
48
+ "LICENSE"
49
+ ],
50
+ "dependencies": {
51
+ "chokidar": "^3.6.0",
52
+ "commander": "^13.0.0"
53
+ },
54
+ "devDependencies": {
55
+ "@types/node": "^25.2.3",
56
+ "@vitest/coverage-v8": "^3.0.0",
57
+ "tsup": "^8.3.0",
58
+ "typescript": "^5.7.0",
59
+ "vitest": "^3.0.0"
60
+ }
61
+ }