@olegkuibar/plunk 0.7.3 → 0.7.5

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.
@@ -1,3 +1,3 @@
1
1
  #!/usr/bin/env node
2
2
  import {createRequire}from'node:module';import {a as a$6}from'./chunk-SMIGYQFG.mjs';import {a as a$8}from'./chunk-7JG555TZ.mjs';import {a as a$7}from'./chunk-AC5FETT7.mjs';import {j,f,d as d$1}from'./chunk-LLVBXPQN.mjs';import {a as a$3}from'./chunk-PVMVWPLG.mjs';import {a as a$4}from'./chunk-HPF6K6WO.mjs';import {b as b$1}from'./chunk-EMRPZYLU.mjs';import {a as a$2}from'./chunk-MBKCCWSD.mjs';import {a as a$5}from'./chunk-OJJZ7CLB.mjs';import {a}from'./chunk-TMH7HIJ2.mjs';import {d}from'./chunk-R3RSOZXN.mjs';import {b}from'./chunk-ICCM7US5.mjs';import {a as a$1}from'./chunk-2VCW5RWI.mjs';import {readFile}from'fs/promises';import {join}from'path';globalThis.require=createRequire(import.meta.url);
3
- var E=a(4);async function X(n,i={}){let a=new a$2,e=await a$3(n,{runScripts:i.runScripts,force:i.force});if(e.skipped){b.info("No changes to push");return}let t=await b$1(e.name,e.version);if(!t){a$4(`Failed to read store entry for ${e.name}@${e.version} after publish`);return}let r=await j(e.name);if(r.length===0){b.success(`Published ${e.name}@${e.version} to store`),b.info("No consumers registered yet. Run 'plunk add "+e.name+"' in a consumer project to start receiving pushes."),a$5({name:e.name,version:e.version,buildId:e.buildId,consumers:0,failedConsumers:0,copied:0,skipped:0,elapsed:a.elapsedMs()});return}let d$2=0,o=0,l=0,b$2=0,N=await Promise.all(r.map(c=>E(async()=>{let f$1=await f(c,e.name);if(!f$1)return d(`[push] No link found for ${e.name} in ${c}, skipping`),null;try{let m=await a$6(t,c,f$1.packageManager,{force:i.force});return await d$1(c,e.name,{...f$1,contentHash:t.meta.contentHash,linkedAt:new Date().toISOString(),buildId:t.meta.buildId??""}),m}catch(m){return b.warn(`Failed to push to ${c}: ${m instanceof Error?m.message:String(m)}`),null}})));for(let c of N)c?(d$2+=c.copied,o+=c.skipped,l++):b$2++;let M=e.buildId?` [${e.buildId}]`:"";b.success(`Pushed ${e.name}@${e.version}${M} to ${l} consumer(s) in ${a.elapsed()} (${d$2} files changed, ${o} unchanged)`),a$5({name:e.name,version:e.version,buildId:e.buildId,consumers:l,failedConsumers:b$2,copied:d$2,skipped:o,elapsed:a.elapsedMs()});}a$1(X,"doPush");function W(n){if(!n)return;let i=parseInt(n,10);return Number.isFinite(i)?i:void 0}a$1(W,"parseMs");async function Y(n,i,a){let{startWatcher:e}=await import('./watcher-JAR4XGGM.mjs'),{buildCmd:t,patterns:r}=await F(n,i),d=await e(n,{patterns:r,buildCmd:t,debounce:W(i.debounce),cooldown:W(i.cooldown)},a);await new Promise(o=>{let l=a$1(async()=>{b.info("Stopping watcher..."),await d.close(),o();},"cleanup");process.once("SIGINT",l),process.once("SIGTERM",l);});}a$1(Y,"startWatchMode");async function F(n,i){let a=i.build,e;if(!i.build){if(!i["skip-build"]){let t=await a$7(n),r=await a$8(n,t);r&&(a=r,b.info(`Auto-detected build command: ${r}`));}}if(a){let{exists:t}=await import('./fs-BUNURH4P.mjs'),r=["src","lib","source","app","pages","components"],d$1=(await Promise.all(r.map(async o=>({dir:o,exists:await t(join(n,o))})))).filter(o=>o.exists).map(o=>o.dir);e=d$1.length>0?d$1:["src","lib"],d(`[watch] Using source patterns with build command: ${e.join(", ")}`);}else {b.info("No build command detected \u2014 watching output directories directly");try{let t=JSON.parse(await readFile(join(n,"package.json"),"utf-8"));t.files&&t.files.length>0?(e=t.files,b.info(`Watching from package.json "files": ${e.join(", ")}`)):b.warn('No "files" field in package.json \u2014 falling back to watching src/ and lib/. Add a "files" field or use --build to specify a build command.');}catch(t){d(`[watch] Could not read package.json: ${t instanceof Error?t.message:String(t)}`);}}return {buildCmd:a,patterns:e}}a$1(F,"resolveWatchConfig");export{X as a,Y as b};
3
+ var E=a(4);async function X(n,i={}){let a=new a$2,e=await a$3(n,{runScripts:i.runScripts,force:i.force});if(e.skipped){b.info("No changes to push");return}let t=await b$1(e.name,e.version);if(!t){a$4(`Failed to read store entry for ${e.name}@${e.version} after publish`);return}let r=await j(e.name);if(r.length===0){b.success(`Published ${e.name}@${e.version} to store`),b.info("No consumers registered yet. Run 'plunk add "+e.name+"' in a consumer project to start receiving pushes."),a$5({name:e.name,version:e.version,buildId:e.buildId,consumers:0,failedConsumers:0,copied:0,skipped:0,elapsed:a.elapsedMs()});return}let d$2=0,o=0,l=0,b$2=0,N=await Promise.all(r.map(c=>E(async()=>{let f$1=await f(c,e.name);if(!f$1)return d(`[push] No link found for ${e.name} in ${c}, skipping`),null;try{let m=await a$6(t,c,f$1.packageManager,{force:i.force});return await d$1(c,e.name,{...f$1,contentHash:t.meta.contentHash,linkedAt:new Date().toISOString(),buildId:t.meta.buildId??""}),m}catch(m){return b.warn(`Failed to push to ${c}: ${m instanceof Error?m.message:String(m)}`),null}})));for(let c of N)c?(d$2+=c.copied,o+=c.skipped,l++):b$2++;let M=e.buildId?` [${e.buildId}]`:"";b.success(`Pushed ${e.name}@${e.version}${M} to ${l} consumer(s) in ${a.elapsed()} (${d$2} files changed, ${o} unchanged)`),a$5({name:e.name,version:e.version,buildId:e.buildId,consumers:l,failedConsumers:b$2,copied:d$2,skipped:o,elapsed:a.elapsedMs()});}a$1(X,"doPush");function W(n){if(!n)return;let i=parseInt(n,10);return Number.isFinite(i)?i:void 0}a$1(W,"parseMs");async function Y(n,i,a){let{startWatcher:e}=await import('./watcher-SY45L3SS.mjs'),{buildCmd:t,patterns:r}=await F(n,i),d=await e(n,{patterns:r,buildCmd:t,debounce:W(i.debounce),cooldown:W(i.cooldown)},a);await new Promise(o=>{let l=a$1(async()=>{b.info("Stopping watcher..."),await d.close(),o();},"cleanup");process.once("SIGINT",l),process.once("SIGTERM",l);});}a$1(Y,"startWatchMode");async function F(n,i){let a=i.build,e;if(!i.build){if(!i["skip-build"]){let t=await a$7(n),r=await a$8(n,t);r&&(a=r,b.info(`Auto-detected build command: ${r}`));}}if(a){let{exists:t}=await import('./fs-BUNURH4P.mjs'),r=["src","lib","source","app","pages","components"],d$1=(await Promise.all(r.map(async o=>({dir:o,exists:await t(join(n,o))})))).filter(o=>o.exists).map(o=>o.dir);e=d$1.length>0?d$1:["src","lib"],d(`[watch] Using source patterns with build command: ${e.join(", ")}`);}else {b.info("No build command detected \u2014 watching output directories directly");try{let t=JSON.parse(await readFile(join(n,"package.json"),"utf-8"));t.files&&t.files.length>0?(e=t.files,b.info(`Watching from package.json "files": ${e.join(", ")}`)):b.warn('No "files" field in package.json \u2014 falling back to watching src/ and lib/. Add a "files" field or use --build to specify a build command.');}catch(t){d(`[watch] Could not read package.json: ${t instanceof Error?t.message:String(t)}`);}}return {buildCmd:a,patterns:e}}a$1(F,"resolveWatchConfig");export{X as a,Y as b};
package/dist/cli.mjs CHANGED
@@ -8,4 +8,4 @@ var o=c(a$1(),1);var r=`
8
8
  \u2588\u2588\u2551 \u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2557\u255A\u2588\u2588\u2588\u2588\u2588\u2588\u2554\u255D\u2588\u2588\u2551 \u255A\u2588\u2588\u2588\u2588\u2551\u2588\u2588\u2551 \u2588\u2588\u2557
9
9
  \u255A\u2550\u255D \u255A\u2550\u2550\u2550\u2550\u2550\u2550\u255D \u255A\u2550\u2550\u2550\u2550\u2550\u255D \u255A\u2550\u255D \u255A\u2550\u2550\u2550\u255D\u255A\u2550\u255D \u255A\u2550\u255D
10
10
  `;function a(){console.log(o.default.yellow(r)),console.log(o.default.cyan(" \u{1F4E6} Local npm package development without symlinks")),console.log(o.default.dim(` Copies built files into consumer node_modules with incremental sync
11
- `));}a$2(a,"showBanner");process.env.UV_THREADPOOL_SIZE??=String(Math.max(availableParallelism(),8));a$3();var d=process.argv.slice(2),p=d.some(e=>!e.startsWith("-")&&["init","publish","add","remove","push","dev","restore","list","status","update","clean","gc","doctor","migrate"].includes(e));p||a();var u={meta:{name:"plunk",version:"0.7.3",description:"Local npm package development \u2014 copies built files into consumer node_modules"},args:{verbose:{type:"boolean",alias:"v",description:"Enable verbose debug logging",default:false},"dry-run":{type:"boolean",description:"Preview changes without writing files",default:false},json:{type:"boolean",description:"Output machine-readable JSON",default:false}},subCommands:{init:a$2(()=>import('./init-C6XCSFCU.mjs').then(e=>e.default),"init"),publish:a$2(()=>import('./publish-6A7PX5IH.mjs').then(e=>e.default),"publish"),add:a$2(()=>import('./add-GYBX4VAZ.mjs').then(e=>e.default),"add"),remove:a$2(()=>import('./remove-5DAQD627.mjs').then(e=>e.default),"remove"),push:a$2(()=>import('./push-AM6JDGUN.mjs').then(e=>e.default),"push"),dev:a$2(()=>import('./dev-QMDH32K7.mjs').then(e=>e.default),"dev"),restore:a$2(()=>import('./restore-JVH6INAG.mjs').then(e=>e.default),"restore"),list:a$2(()=>import('./list-QSPN7FE5.mjs').then(e=>e.default),"list"),status:a$2(()=>import('./status-22YV26A3.mjs').then(e=>e.default),"status"),update:a$2(()=>import('./update-33ICRFYZ.mjs').then(e=>e.default),"update"),clean:a$2(()=>import('./clean-F2IWAVRP.mjs').then(e=>e.default),"clean"),gc:a$2(()=>import('./clean-F2IWAVRP.mjs').then(e=>e.default),"gc"),doctor:a$2(()=>import('./doctor-GJGAAT6J.mjs').then(e=>e.default),"doctor"),migrate:a$2(()=>import('./migrate-4TFDXO4G.mjs').then(e=>e.default),"migrate")}};a$4(u);
11
+ `));}a$2(a,"showBanner");process.env.UV_THREADPOOL_SIZE??=String(Math.max(availableParallelism(),8));a$3();var d=process.argv.slice(2),p=d.some(e=>!e.startsWith("-")&&["init","publish","add","remove","push","dev","restore","list","status","update","clean","gc","doctor","migrate"].includes(e));p||a();var u={meta:{name:"plunk",version:"0.7.5",description:"Local npm package development \u2014 copies built files into consumer node_modules"},args:{verbose:{type:"boolean",alias:"v",description:"Enable verbose debug logging",default:false},"dry-run":{type:"boolean",description:"Preview changes without writing files",default:false},json:{type:"boolean",description:"Output machine-readable JSON",default:false}},subCommands:{init:a$2(()=>import('./init-C6XCSFCU.mjs').then(e=>e.default),"init"),publish:a$2(()=>import('./publish-6A7PX5IH.mjs').then(e=>e.default),"publish"),add:a$2(()=>import('./add-GYBX4VAZ.mjs').then(e=>e.default),"add"),remove:a$2(()=>import('./remove-5DAQD627.mjs').then(e=>e.default),"remove"),push:a$2(()=>import('./push-AUAGCBYK.mjs').then(e=>e.default),"push"),dev:a$2(()=>import('./dev-DBGYZOIV.mjs').then(e=>e.default),"dev"),restore:a$2(()=>import('./restore-JVH6INAG.mjs').then(e=>e.default),"restore"),list:a$2(()=>import('./list-QSPN7FE5.mjs').then(e=>e.default),"list"),status:a$2(()=>import('./status-22YV26A3.mjs').then(e=>e.default),"status"),update:a$2(()=>import('./update-33ICRFYZ.mjs').then(e=>e.default),"update"),clean:a$2(()=>import('./clean-F2IWAVRP.mjs').then(e=>e.default),"clean"),gc:a$2(()=>import('./clean-F2IWAVRP.mjs').then(e=>e.default),"gc"),doctor:a$2(()=>import('./doctor-GJGAAT6J.mjs').then(e=>e.default),"doctor"),migrate:a$2(()=>import('./migrate-4TFDXO4G.mjs').then(e=>e.default),"migrate")}};a$4(u);
@@ -1,3 +1,3 @@
1
1
  #!/usr/bin/env node
2
- import {createRequire}from'node:module';import {a as a$1,b as b$1}from'./chunk-XAVMCPKI.mjs';import'./chunk-SMIGYQFG.mjs';import'./chunk-7JG555TZ.mjs';import'./chunk-AC5FETT7.mjs';import'./chunk-LLVBXPQN.mjs';import'./chunk-PVMVWPLG.mjs';import'./chunk-HPF6K6WO.mjs';import'./chunk-EMRPZYLU.mjs';import'./chunk-MBKCCWSD.mjs';import'./chunk-22YCXJTS.mjs';import {b}from'./chunk-OJJZ7CLB.mjs';import'./chunk-OL7SNXMX.mjs';import'./chunk-HW7AEGI3.mjs';import'./chunk-TMH7HIJ2.mjs';import'./chunk-R3RSOZXN.mjs';import'./chunk-ICCM7US5.mjs';import {a}from'./chunk-2VCW5RWI.mjs';import {resolve}from'path';globalThis.require=createRequire(import.meta.url);
2
+ import {createRequire}from'node:module';import {a as a$1,b as b$1}from'./chunk-CIY4E6PA.mjs';import'./chunk-SMIGYQFG.mjs';import'./chunk-7JG555TZ.mjs';import'./chunk-AC5FETT7.mjs';import'./chunk-LLVBXPQN.mjs';import'./chunk-PVMVWPLG.mjs';import'./chunk-HPF6K6WO.mjs';import'./chunk-EMRPZYLU.mjs';import'./chunk-MBKCCWSD.mjs';import'./chunk-22YCXJTS.mjs';import {b}from'./chunk-OJJZ7CLB.mjs';import'./chunk-OL7SNXMX.mjs';import'./chunk-HW7AEGI3.mjs';import'./chunk-TMH7HIJ2.mjs';import'./chunk-R3RSOZXN.mjs';import'./chunk-ICCM7US5.mjs';import {a}from'./chunk-2VCW5RWI.mjs';import {resolve}from'path';globalThis.require=createRequire(import.meta.url);
3
3
  var u={meta:{name:"dev",description:"Watch, rebuild, and push to all consumers. Auto-detects build command."},args:{build:{type:"string",description:"Override build command (default: auto-detect from package.json)"},"skip-build":{type:"boolean",description:"Watch output dirs directly, skip build command detection",default:false},debounce:{type:"string",description:"Debounce delay in ms (default: 500)"},cooldown:{type:"string",description:"Minimum time between builds in ms (default: 500)"},"no-scripts":{type:"boolean",description:"Skip prepack/postpack lifecycle hooks",default:false}},async run({args:e}){b();let t=resolve("."),i=a(()=>a$1(t,{runScripts:!e["no-scripts"]}),"push");await i(),await b$1(t,e,i);}};export{u as default};
package/dist/index.mjs CHANGED
@@ -659,13 +659,51 @@ var init_workspace = __esm({
659
659
 
660
660
  // src/core/watcher.ts
661
661
  import { spawn as spawn2 } from "child_process";
662
+ import { readdir as readdir6, stat as stat8 } from "fs/promises";
662
663
  import { platform as platform3 } from "os";
664
+ import { join as join13 } from "path";
663
665
  function killActiveBuild() {
664
666
  if (activeChild && !activeChild.killed) {
665
667
  activeChild.kill("SIGTERM");
666
668
  activeChild = null;
667
669
  }
668
670
  }
671
+ async function walkDir(dir, snapshot) {
672
+ let entries;
673
+ try {
674
+ entries = await readdir6(dir, { withFileTypes: true });
675
+ } catch {
676
+ return;
677
+ }
678
+ for (const entry of entries) {
679
+ if (IGNORED_DIRS.has(entry.name)) continue;
680
+ const fullPath = join13(dir, entry.name);
681
+ if (entry.isDirectory()) {
682
+ await walkDir(fullPath, snapshot);
683
+ } else {
684
+ try {
685
+ const st = await stat8(fullPath);
686
+ snapshot.set(fullPath, st.mtimeMs);
687
+ } catch {
688
+ }
689
+ }
690
+ }
691
+ }
692
+ async function buildSnapshot(watchPaths) {
693
+ const snapshot = /* @__PURE__ */ new Map();
694
+ for (const p of watchPaths) {
695
+ try {
696
+ const st = await stat8(p);
697
+ if (st.isDirectory()) {
698
+ await walkDir(p, snapshot);
699
+ } else {
700
+ snapshot.set(p, st.mtimeMs);
701
+ }
702
+ } catch {
703
+ }
704
+ }
705
+ return snapshot;
706
+ }
669
707
  async function startWatcher(watchDir, options, onChange) {
670
708
  const { watch } = await import("chokidar");
671
709
  const patterns = options.patterns ?? ["src", "lib"];
@@ -742,9 +780,9 @@ async function startWatcher(watchDir, options, onChange) {
742
780
  const watcher = watch(watchPaths, {
743
781
  ignoreInitial: true,
744
782
  ignored: [
745
- "**/node_modules/**",
746
- "**/.git/**",
747
- "**/.plunk/**"
783
+ /[/\\]node_modules[/\\]/,
784
+ /[/\\]\.git[/\\]/,
785
+ /[/\\]\.plunk[/\\]/
748
786
  ],
749
787
  awaitWriteFinish: awfOption,
750
788
  usePolling,
@@ -756,9 +794,37 @@ async function startWatcher(watchDir, options, onChange) {
756
794
  watcher.on("error", (err) => {
757
795
  consola.error(`Watcher error: ${err instanceof Error ? err.message : String(err)}`);
758
796
  });
797
+ let pollInterval = null;
798
+ if (usePolling) {
799
+ let lastSnapshot = await buildSnapshot(watchPaths);
800
+ pollInterval = setInterval(async () => {
801
+ if (closed) return;
802
+ try {
803
+ const current = await buildSnapshot(watchPaths);
804
+ for (const [path, mtime] of current) {
805
+ const prev = lastSnapshot.get(path);
806
+ if (prev === void 0 || prev !== mtime) {
807
+ onFileEvent(path);
808
+ break;
809
+ }
810
+ }
811
+ if (!closed) {
812
+ for (const path of lastSnapshot.keys()) {
813
+ if (!current.has(path)) {
814
+ onFileEvent(path);
815
+ break;
816
+ }
817
+ }
818
+ }
819
+ lastSnapshot = current;
820
+ } catch {
821
+ }
822
+ }, 1e3);
823
+ }
759
824
  const watcherHandle = {
760
825
  close: async () => {
761
826
  closed = true;
827
+ if (pollInterval) clearInterval(pollInterval);
762
828
  if (debounceTimer) clearTimeout(debounceTimer);
763
829
  killActiveBuild();
764
830
  await watcher.close();
@@ -797,13 +863,14 @@ function runBuildCommand(cmd, cwd) {
797
863
  });
798
864
  });
799
865
  }
800
- var activeChild, activeWatcher;
866
+ var activeChild, activeWatcher, IGNORED_DIRS;
801
867
  var init_watcher = __esm({
802
868
  "src/core/watcher.ts"() {
803
869
  "use strict";
804
870
  init_console();
805
871
  activeChild = null;
806
872
  activeWatcher = null;
873
+ IGNORED_DIRS = /* @__PURE__ */ new Set(["node_modules", ".git", ".plunk"]);
807
874
  }
808
875
  });
809
876
 
@@ -1,3 +1,3 @@
1
1
  #!/usr/bin/env node
2
- import {createRequire}from'node:module';import {a as a$1,b as b$1}from'./chunk-XAVMCPKI.mjs';import'./chunk-SMIGYQFG.mjs';import'./chunk-7JG555TZ.mjs';import'./chunk-AC5FETT7.mjs';import'./chunk-LLVBXPQN.mjs';import'./chunk-PVMVWPLG.mjs';import'./chunk-HPF6K6WO.mjs';import'./chunk-EMRPZYLU.mjs';import'./chunk-MBKCCWSD.mjs';import'./chunk-22YCXJTS.mjs';import {b}from'./chunk-OJJZ7CLB.mjs';import'./chunk-OL7SNXMX.mjs';import'./chunk-HW7AEGI3.mjs';import'./chunk-TMH7HIJ2.mjs';import'./chunk-R3RSOZXN.mjs';import'./chunk-ICCM7US5.mjs';import {a}from'./chunk-2VCW5RWI.mjs';import {resolve}from'path';globalThis.require=createRequire(import.meta.url);
2
+ import {createRequire}from'node:module';import {a as a$1,b as b$1}from'./chunk-CIY4E6PA.mjs';import'./chunk-SMIGYQFG.mjs';import'./chunk-7JG555TZ.mjs';import'./chunk-AC5FETT7.mjs';import'./chunk-LLVBXPQN.mjs';import'./chunk-PVMVWPLG.mjs';import'./chunk-HPF6K6WO.mjs';import'./chunk-EMRPZYLU.mjs';import'./chunk-MBKCCWSD.mjs';import'./chunk-22YCXJTS.mjs';import {b}from'./chunk-OJJZ7CLB.mjs';import'./chunk-OL7SNXMX.mjs';import'./chunk-HW7AEGI3.mjs';import'./chunk-TMH7HIJ2.mjs';import'./chunk-R3RSOZXN.mjs';import'./chunk-ICCM7US5.mjs';import {a}from'./chunk-2VCW5RWI.mjs';import {resolve}from'path';globalThis.require=createRequire(import.meta.url);
3
3
  var u={meta:{name:"push",description:"Publish and push to all consumers. Use --watch for continuous mode."},args:{watch:{type:"boolean",description:"Watch for changes and auto-push",default:false},build:{type:"string",description:"Build command to run before publishing (watch mode)"},"skip-build":{type:"boolean",description:"Watch output dirs directly, skip build command detection",default:false},debounce:{type:"string",description:"Debounce delay in ms for watch mode (default: 500)"},cooldown:{type:"string",description:"Minimum time between builds in ms (default: 500)"},"no-scripts":{type:"boolean",description:"Skip prepack/postpack lifecycle hooks",default:false},force:{type:"boolean",alias:"f",description:"Force copy all files, bypassing hash comparison",default:false}},async run({args:e}){b();let o=resolve("."),t=a(()=>a$1(o,{runScripts:!e["no-scripts"],force:e.force}),"push");await t(),e.watch&&await b$1(o,e,t);}};export{u as default};
@@ -1,6 +1,6 @@
1
1
  // src/vite-plugin.ts
2
2
  import { join, normalize } from "path";
3
- import { readFileSync, existsSync, rmSync } from "fs";
3
+ import { readFileSync } from "fs";
4
4
  function readLinkedPackagesSync(stateFile) {
5
5
  try {
6
6
  const content = readFileSync(stateFile, "utf-8");
@@ -12,8 +12,6 @@ function readLinkedPackagesSync(stateFile) {
12
12
  }
13
13
  function plunkPlugin() {
14
14
  let plunkStateFile;
15
- let rootDir;
16
- let cacheDir;
17
15
  let nodeModulesDir;
18
16
  let pollTimer;
19
17
  return {
@@ -47,8 +45,6 @@ function plunkPlugin() {
47
45
  return result;
48
46
  },
49
47
  configResolved(config) {
50
- rootDir = config.root;
51
- cacheDir = config.cacheDir;
52
48
  nodeModulesDir = join(config.root, "node_modules");
53
49
  plunkStateFile = normalize(join(config.root, ".plunk", "state.json"));
54
50
  console.log(`[plunk] Watching state file: ${plunkStateFile}`);
@@ -58,6 +54,7 @@ function plunkPlugin() {
58
54
  let isRestarting = false;
59
55
  let pendingRestart = false;
60
56
  let debounceTimer = null;
57
+ let reloadTimer = null;
61
58
  function syncPackageWatchers() {
62
59
  let added = false;
63
60
  for (const pkg of readLinkedPackagesSync(plunkStateFile)) {
@@ -67,13 +64,13 @@ function plunkPlugin() {
67
64
  }
68
65
  }
69
66
  if (added && watchedPackages.size > 0) {
70
- const escaped = [...watchedPackages].map((p) => p.replace(/[/\\.*+?^${}()|[\]]/g, "\\$&")).join("|");
67
+ const escaped = [...watchedPackages].sort((a, b) => b.length - a.length).map((p) => p.replace(/[/\\.*+?^${}()|[\]]/g, "\\$&")).join("|");
71
68
  server.watcher.options = {
72
69
  ...server.watcher.options,
73
70
  ignored: [
74
71
  new RegExp(`node_modules\\/(?!(?:${escaped})(?:\\/|$)).*`),
75
- "**/.git/**",
76
- "**/test-results/**"
72
+ /[/\\]\.git[/\\]/,
73
+ /[/\\]test-results[/\\]/
77
74
  ]
78
75
  };
79
76
  server.watcher._userIgnored = void 0;
@@ -82,23 +79,9 @@ function plunkPlugin() {
82
79
  server.watcher.add(pkgPath);
83
80
  console.log(`[plunk] Added watcher for package: ${pkgPath}`);
84
81
  }
85
- server.watcher.on("change", (changedPath) => {
86
- const normalized = normalize(changedPath);
87
- for (const pkg of watchedPackages) {
88
- if (normalized.includes(join("node_modules", pkg))) {
89
- console.log(`[plunk] Linked package file changed: ${changedPath}`);
90
- const mods = server.moduleGraph.getModulesByFile(normalized);
91
- if (mods) {
92
- mods.forEach((m) => server.moduleGraph.invalidateModule(m));
93
- }
94
- server.hot.send({ type: "full-reload", path: "*" });
95
- return;
96
- }
97
- }
98
- });
99
82
  }
100
83
  }
101
- async function clearCacheAndRestart(source) {
84
+ async function restartServer(source) {
102
85
  if (isRestarting) {
103
86
  pendingRestart = true;
104
87
  console.log(`[plunk] Restart already in progress, queued: ${source}`);
@@ -111,44 +94,78 @@ function plunkPlugin() {
111
94
  `[plunk] ${source}, restarting server...`,
112
95
  { timestamp: true }
113
96
  );
114
- try {
115
- if (existsSync(cacheDir)) {
116
- rmSync(cacheDir, { recursive: true, force: true });
117
- console.log(`[plunk] Cleared cache: ${cacheDir}`);
118
- }
119
- } catch (err) {
120
- console.error(`[plunk] Failed to clear cache:`, err);
121
- }
122
97
  try {
123
98
  await server.restart();
124
99
  } finally {
125
100
  isRestarting = false;
126
101
  if (pendingRestart) {
127
102
  pendingRestart = false;
128
- await clearCacheAndRestart("Queued change detected");
103
+ await restartServer("Queued change detected");
129
104
  }
130
105
  }
131
106
  }
132
107
  function scheduleRestart(source) {
133
- if (debounceTimer) clearTimeout(debounceTimer);
108
+ if (debounceTimer) return;
134
109
  debounceTimer = setTimeout(() => {
135
110
  debounceTimer = null;
136
- clearCacheAndRestart(source);
137
- }, 100);
111
+ restartServer(source);
112
+ }, 1500);
113
+ }
114
+ function scheduleReload() {
115
+ if (reloadTimer) clearTimeout(reloadTimer);
116
+ reloadTimer = setTimeout(() => {
117
+ reloadTimer = null;
118
+ console.log("[plunk] Linked package updated, reloading");
119
+ server.hot.send({ type: "full-reload", path: "*" });
120
+ }, 200);
121
+ }
122
+ function invalidateAndReload(changedPath) {
123
+ const normalized = normalize(changedPath);
124
+ const mods = server.moduleGraph.getModulesByFile(normalized);
125
+ if (mods) {
126
+ mods.forEach((m) => server.moduleGraph.invalidateModule(m));
127
+ }
128
+ scheduleReload();
129
+ }
130
+ function invalidateLinkedModules() {
131
+ for (const [url, mod] of server.moduleGraph.urlToModuleMap) {
132
+ for (const pkg of watchedPackages) {
133
+ if (url.includes(pkg)) {
134
+ server.moduleGraph.invalidateModule(mod);
135
+ break;
136
+ }
137
+ }
138
+ }
139
+ scheduleReload();
138
140
  }
139
141
  server.watcher.add(plunkStateFile);
140
142
  console.log(`[plunk] Added watcher for: ${plunkStateFile}`);
141
143
  syncPackageWatchers();
142
144
  server.watcher.on("change", async (changedPath) => {
143
145
  const normalizedChanged = normalize(changedPath);
144
- if (normalizedChanged !== plunkStateFile) return;
145
- const currentPackages = readLinkedPackagesSync(plunkStateFile);
146
- const hasNew = currentPackages.some((pkg) => !watchedPackages.has(pkg));
147
- if (!hasNew) {
148
- console.log("[plunk] state.json changed but no new packages, skipping restart");
146
+ if (normalizedChanged === plunkStateFile) {
147
+ const currentPackages = readLinkedPackagesSync(plunkStateFile);
148
+ const hasNew = currentPackages.some((pkg) => !watchedPackages.has(pkg));
149
+ if (hasNew) {
150
+ scheduleRestart("New package linked");
151
+ }
149
152
  return;
150
153
  }
151
- scheduleRestart("New package linked");
154
+ const isLinkedPackage = [...watchedPackages].some(
155
+ (pkg) => normalizedChanged.includes(normalize(join(nodeModulesDir, pkg)))
156
+ );
157
+ if (isLinkedPackage) {
158
+ invalidateAndReload(changedPath);
159
+ }
160
+ });
161
+ server.watcher.on("add", (addedPath) => {
162
+ const normalizedAdded = normalize(addedPath);
163
+ const isLinkedPackage = [...watchedPackages].some(
164
+ (pkg) => normalizedAdded.includes(normalize(join(nodeModulesDir, pkg)))
165
+ );
166
+ if (isLinkedPackage) {
167
+ invalidateAndReload(addedPath);
168
+ }
152
169
  });
153
170
  if (process.versions?.webcontainer) {
154
171
  if (pollTimer) clearInterval(pollTimer);
@@ -168,6 +185,8 @@ function plunkPlugin() {
168
185
  );
169
186
  if (hasNew) {
170
187
  scheduleRestart("New package linked (polling fallback)");
188
+ } else {
189
+ invalidateLinkedModules();
171
190
  }
172
191
  }
173
192
  if (!lastStateContent) lastStateContent = content;
@@ -176,6 +195,20 @@ function plunkPlugin() {
176
195
  }, 1e3);
177
196
  console.log("[plunk] WebContainer polling fallback active (1s interval)");
178
197
  }
198
+ server.httpServer?.on("close", () => {
199
+ if (pollTimer) {
200
+ clearInterval(pollTimer);
201
+ pollTimer = void 0;
202
+ }
203
+ if (debounceTimer) {
204
+ clearTimeout(debounceTimer);
205
+ debounceTimer = null;
206
+ }
207
+ if (reloadTimer) {
208
+ clearTimeout(reloadTimer);
209
+ reloadTimer = null;
210
+ }
211
+ });
179
212
  }
180
213
  };
181
214
  }
@@ -0,0 +1,3 @@
1
+ #!/usr/bin/env node
2
+ import {createRequire}from'node:module';import {b}from'./chunk-ICCM7US5.mjs';import {a}from'./chunk-2VCW5RWI.mjs';import {spawn}from'child_process';import {readdir,stat}from'fs/promises';import {platform}from'os';import {join}from'path';globalThis.require=createRequire(import.meta.url);
3
+ var m=null;function j(){m&&!m.killed&&(m.kill("SIGTERM"),m=null);}a(j,"killActiveBuild");var _=new Set(["node_modules",".git",".plunk"]);async function M(o,t){let n;try{n=await readdir(o,{withFileTypes:!0});}catch{return}for(let r of n){if(_.has(r.name))continue;let u=join(o,r.name);if(r.isDirectory())await M(u,t);else try{let f=await stat(u);t.set(u,f.mtimeMs);}catch{}}}a(M,"walkDir");async function I(o){let t=new Map;for(let n of o)try{let r=await stat(n);r.isDirectory()?await M(n,t):t.set(n,r.mtimeMs);}catch{}return t}a(I,"buildSnapshot");async function q(o,t,n){let{watch:r}=await import('./chokidar-LVDD2IK4.mjs'),u=t.patterns??["src","lib"],f=u.map(e=>e.startsWith("/")||e.includes(":")?e:`${o}/${e}`),h=t.debounce??500,l=t.cooldown??500,i=null,p=false,b$1=false,w=0,v=false,P=a(async()=>{if(p||b$1)return;let e=Date.now()-w;if(!(w>0&&e<l)){b$1=true,v=false;try{if(t.buildCmd&&!await x(t.buildCmd,o)){b.warn("Build failed (see output above), skipping push");return}await n();}catch(s){b.error(`Push failed: ${s instanceof Error?s.message:String(s)}`);}finally{b$1=false,w=Date.now(),v&&!p&&(v=false,i=setTimeout(()=>{i=null,P();},l));}}},"doBuild"),g=a(e=>{if(p)return;if(b$1){v=true;return}let s=Date.now()-w;if(w>0&&s<l){i&&clearTimeout(i);let d=l-s;i=setTimeout(()=>{i=null,P();},d+h);return}i&&clearTimeout(i),i=setTimeout(()=>{i=null,P();},h);},"onFileEvent"),$=t.buildCmd?false:t.awaitWriteFinish??{stabilityThreshold:200,pollInterval:50},T=!!process.versions?.webcontainer,y=r(f,{ignoreInitial:true,ignored:[/[/\\]node_modules[/\\]/,/[/\\]\.git[/\\]/,/[/\\]\.plunk[/\\]/],awaitWriteFinish:$,usePolling:T,...T&&{interval:1e3}});y.on("change",g),y.on("add",g),y.on("unlink",g),y.on("error",e=>{b.error(`Watcher error: ${e instanceof Error?e.message:String(e)}`);});let k=null;if(T){let e=await I(f);k=setInterval(async()=>{if(!p)try{let s=await I(f);for(let[d,D]of s){let W=e.get(d);if(W===void 0||W!==D){g(d);break}}if(!p){for(let d of e.keys())if(!s.has(d)){g(d);break}}e=s;}catch{}},1e3);}let B={close:a(async()=>{p=true,k&&clearInterval(k),i&&clearTimeout(i),j(),await y.close();},"close")};return b.info(`Watching for changes in: ${u.join(", ")}`),B}a(q,"startWatcher");function x(o,t){return new Promise(n=>{let r=platform()==="win32",u=r?"cmd":"sh",f=r?"/c":"-c";b.start(`Running: ${o}`);let h=spawn(u,[f,o],{cwd:t,stdio:"inherit"});m=h,h.on("close",l=>{m=null,l===0?(b.success("Build succeeded"),n(true)):(b.error(`Build failed with code ${l}`),n(false));}),h.on("error",l=>{m=null,b.error(`Build error: ${l.message}`),n(false);});})}a(x,"runBuildCommand");export{j as killActiveBuild,q as startWatcher};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@olegkuibar/plunk",
3
- "version": "0.7.3",
3
+ "version": "0.7.5",
4
4
  "description": "Local npm package development without symlinks. Copies built files into consumer node_modules with incremental sync and watch mode.",
5
5
  "type": "module",
6
6
  "bin": {
@@ -1,3 +0,0 @@
1
- #!/usr/bin/env node
2
- import {createRequire}from'node:module';import {b}from'./chunk-ICCM7US5.mjs';import {a}from'./chunk-2VCW5RWI.mjs';import {spawn}from'child_process';import {platform}from'os';globalThis.require=createRequire(import.meta.url);
3
- var r=null;function E(){r&&!r.killed&&(r.kill("SIGTERM"),r=null);}a(E,"killActiveBuild");async function O(c,n,u){let{watch:m}=await import('./chokidar-LVDD2IK4.mjs'),h=n.patterns??["src","lib"],P=h.map(t=>t.startsWith("/")||t.includes(":")?t:`${c}/${t}`),a$1=n.debounce??500,i=n.cooldown??500,e=null,g=false,w=false,d=0,p=false,T=a(async()=>{if(g||w)return;let t=Date.now()-d;if(!(d>0&&t<i)){w=true,p=false;try{if(n.buildCmd&&!await S(n.buildCmd,c)){b.warn("Build failed (see output above), skipping push");return}await u();}catch(l){b.error(`Push failed: ${l instanceof Error?l.message:String(l)}`);}finally{w=false,d=Date.now(),p&&!g&&(p=false,e=setTimeout(()=>{e=null,T();},i));}}},"doBuild"),b$1=a(t=>{if(g)return;if(w){p=true;return}let l=Date.now()-d;if(d>0&&l<i){e&&clearTimeout(e);let C=i-l;e=setTimeout(()=>{e=null,T();},C+a$1);return}e&&clearTimeout(e),e=setTimeout(()=>{e=null,T();},a$1);},"onFileEvent"),W=n.buildCmd?false:n.awaitWriteFinish??{stabilityThreshold:200,pollInterval:50},y=!!process.versions?.webcontainer,f=m(P,{ignoreInitial:true,ignored:["**/node_modules/**","**/.git/**","**/.plunk/**"],awaitWriteFinish:W,usePolling:y,...y&&{interval:1e3}});f.on("change",b$1),f.on("add",b$1),f.on("unlink",b$1),f.on("error",t=>{b.error(`Watcher error: ${t instanceof Error?t.message:String(t)}`);});let v={close:a(async()=>{g=true,e&&clearTimeout(e),E(),await f.close();},"close")};return b.info(`Watching for changes in: ${h.join(", ")}`),v}a(O,"startWatcher");function S(c,n){return new Promise(u=>{let m=platform()==="win32",h=m?"cmd":"sh",P=m?"/c":"-c";b.start(`Running: ${c}`);let a=spawn(h,[P,c],{cwd:n,stdio:"inherit"});r=a,a.on("close",i=>{r=null,i===0?(b.success("Build succeeded"),u(true)):(b.error(`Build failed with code ${i}`),u(false));}),a.on("error",i=>{r=null,b.error(`Build error: ${i.message}`),u(false);});})}a(S,"runBuildCommand");export{E as killActiveBuild,O as startWatcher};