@embeddable.com/sdk-core 4.2.0-next.0 → 4.3.0-next.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/lib/dev.d.ts CHANGED
@@ -1,10 +1,15 @@
1
+ import { CompilerBuildResults } from "@stencil/core/compiler";
1
2
  import { RollupWatcher } from "rollup";
2
3
  import { ChildProcess } from "node:child_process";
3
4
  import { ResolvedEmbeddableConfig } from "./defineConfig";
5
+ import { BuildResultsComponentGraph } from "@stencil/core/internal";
4
6
  export declare const buildWebComponent: (config: any) => Promise<void>;
5
7
  declare const _default: () => Promise<void>;
6
8
  export default _default;
7
9
  export declare const configureWatcher: (watcher: RollupWatcher, ctx: ResolvedEmbeddableConfig) => Promise<void>;
8
- export declare const globalHookWatcher: (watcher: RollupWatcher) => Promise<void>;
10
+ export declare const globalHookWatcher: (watcher: RollupWatcher, key: string) => Promise<void>;
9
11
  export declare const openDevWorkspacePage: (previewBaseUrl: string, workspaceId: string) => Promise<ChildProcess>;
10
12
  export declare const sendBuildChanges: (ctx: ResolvedEmbeddableConfig) => Promise<void>;
13
+ export declare function onWebComponentBuildFinish(e: CompilerBuildResults, config: ResolvedEmbeddableConfig): Promise<void>;
14
+ export declare function waitForStableHmrFiles(componentGraph: BuildResultsComponentGraph | undefined, config: ResolvedEmbeddableConfig): Promise<void>;
15
+ export declare function resetStateForTesting(): void;
package/lib/generate.d.ts CHANGED
@@ -1,3 +1,32 @@
1
+ import { CompilerWatcher } from "@stencil/core/compiler";
1
2
  import { PluginName, ResolvedEmbeddableConfig } from "./defineConfig";
2
- declare const _default: (ctx: ResolvedEmbeddableConfig, pluginName: PluginName) => Promise<void>;
3
+ /**
4
+ * Stencil watcher doesnt react on file metadata changes,
5
+ * so we have to change the file content to trigger a rebuild by appending a space character.
6
+ * This constant defines how many times the space character can be appended before the file is truncated back to its original size.
7
+ */
8
+ export declare const TRIGGER_BUILD_ITERATION_LIMIT = 5;
9
+ export declare function resetForTesting(): void;
10
+ /**
11
+ * Triggers a rebuild of a Stencil web component by modifying the `component.tsx` file.
12
+ *
13
+ * This function works by appending a space character to the file, which causes Stencil's watcher
14
+ * to detect a change and rebuild the component. After every TRIGGER_BUILD_ITERATION_LIMIT rebuilds, the file is truncated back
15
+ * to its original size to prevent indefinite growth and reset the internal rebuild counter.
16
+ *
17
+ * Append and truncate are used instead of rewriting the file to ensure minimal I/O overhead and preserve file metadata.
18
+ */
19
+ export declare function triggerWebComponentRebuild(ctx: ResolvedEmbeddableConfig): Promise<void>;
20
+ declare const _default: (ctx: ResolvedEmbeddableConfig, pluginName: PluginName) => Promise<void | CompilerWatcher>;
3
21
  export default _default;
22
+ /**
23
+ * Generates only the d.ts type declaration files using Stencil, without performing a full build.
24
+ * Used in dev mode to pre-generate types before the watcher starts, avoiding a double-build
25
+ * triggered by the watcher reacting to freshly generated d.ts files.
26
+ *
27
+ * Key differences from the default generate function:
28
+ * - Writes an empty style.css stub (no real CSS injection needed for type generation)
29
+ * - Injects a no-op render stub instead of the real render import
30
+ * - Always creates a fresh sys (never reuses ctx.dev?.sys) to avoid watcher interference
31
+ */
32
+ export declare function generateDTS(ctx: ResolvedEmbeddableConfig): Promise<void>;
package/lib/index.esm.js CHANGED
@@ -10,7 +10,7 @@ import * as path from 'path';
10
10
  import path__default$1, { basename } from 'path';
11
11
  import fg from 'fast-glob';
12
12
  import * as fsSync from 'node:fs';
13
- import { existsSync } from 'node:fs';
13
+ import { existsSync, createReadStream } from 'node:fs';
14
14
  import { createNodeLogger, createNodeSys } from '@stencil/core/sys/node';
15
15
  import { loadConfig, createCompiler } from '@stencil/core/compiler';
16
16
  import * as sorcery from 'sorcery';
@@ -23,7 +23,7 @@ import require$$1 from 'os';
23
23
  import require$$3 from 'http';
24
24
  import require$$4 from 'https';
25
25
  import require$$0$1 from 'url';
26
- import require$$2$1, { createReadStream } from 'fs';
26
+ import require$$2$1, { createReadStream as createReadStream$1 } from 'fs';
27
27
  import axios from 'axios';
28
28
  import yazl from 'yazl';
29
29
  import { select } from '@inquirer/prompts';
@@ -611,18 +611,77 @@ const STYLE_IMPORTS_TOKEN = "{{STYLES_IMPORT}}";
611
611
  const RENDER_IMPORT_TOKEN = "{{RENDER_IMPORT}}";
612
612
  // stencil doesn't support dynamic component tag name, so we need to replace it manually
613
613
  const COMPONENT_TAG_TOKEN = "replace-this-with-component-name";
614
+ let triggeredBuildCount = 0;
615
+ /**
616
+ * Stencil watcher doesnt react on file metadata changes,
617
+ * so we have to change the file content to trigger a rebuild by appending a space character.
618
+ * This constant defines how many times the space character can be appended before the file is truncated back to its original size.
619
+ */
620
+ const TRIGGER_BUILD_ITERATION_LIMIT = 5;
621
+ let originalFileStats = null;
622
+ /**
623
+ * Triggers a rebuild of a Stencil web component by modifying the `component.tsx` file.
624
+ *
625
+ * This function works by appending a space character to the file, which causes Stencil's watcher
626
+ * to detect a change and rebuild the component. After every TRIGGER_BUILD_ITERATION_LIMIT rebuilds, the file is truncated back
627
+ * to its original size to prevent indefinite growth and reset the internal rebuild counter.
628
+ *
629
+ * Append and truncate are used instead of rewriting the file to ensure minimal I/O overhead and preserve file metadata.
630
+ */
631
+ async function triggerWebComponentRebuild(ctx) {
632
+ const filePath = path$1.resolve(ctx.client.componentDir, "component.tsx");
633
+ if (triggeredBuildCount === 0) {
634
+ // store original file stats on the first build
635
+ originalFileStats = await fs.stat(filePath);
636
+ }
637
+ if (triggeredBuildCount === TRIGGER_BUILD_ITERATION_LIMIT && originalFileStats) {
638
+ await fs.truncate(filePath, originalFileStats.size);
639
+ triggeredBuildCount = 0; // reset the counter after resetting the file
640
+ }
641
+ else {
642
+ await fs.appendFile(filePath, " ");
643
+ triggeredBuildCount++;
644
+ }
645
+ }
614
646
  var generate = async (ctx, pluginName) => {
615
647
  await injectCSS(ctx, pluginName);
616
648
  await injectBundleRender(ctx, pluginName);
617
- await runStencil(ctx);
618
- await generateSourceMap(ctx, pluginName);
649
+ const watcher = await runStencil(ctx);
650
+ if (watcher) {
651
+ watcher.on("buildFinish", () => {
652
+ // stencil always changes the working directory to the root of the web component.
653
+ // We need to change it back to the client root directory
654
+ process.chdir(ctx.client.rootDir);
655
+ generateSourceMap(ctx, pluginName);
656
+ });
657
+ }
658
+ else {
659
+ await generateSourceMap(ctx, pluginName);
660
+ }
661
+ return watcher;
619
662
  };
663
+ /**
664
+ * Generates only the d.ts type declaration files using Stencil, without performing a full build.
665
+ * Used in dev mode to pre-generate types before the watcher starts, avoiding a double-build
666
+ * triggered by the watcher reacting to freshly generated d.ts files.
667
+ *
668
+ * Key differences from the default generate function:
669
+ * - Writes an empty style.css stub (no real CSS injection needed for type generation)
670
+ * - Injects a no-op render stub instead of the real render import
671
+ * - Always creates a fresh sys (never reuses ctx.dev?.sys) to avoid watcher interference
672
+ */
673
+ async function generateDTS(ctx) {
674
+ await injectEmptyCSS(ctx);
675
+ await injectBundleRenderStub(ctx);
676
+ await runStencil(ctx, { dtsOnly: true });
677
+ }
620
678
  async function injectCSS(ctx, pluginName) {
621
679
  const CUSTOMER_BUILD = path$1.resolve(ctx.client.buildDir, ctx[pluginName].outputOptions.buildName);
622
680
  const allFiles = await fs.readdir(CUSTOMER_BUILD);
681
+ const importFilePath = path$1.relative(ctx.client.componentDir, path$1.resolve(ctx.client.buildDir, ctx[pluginName].outputOptions.buildName));
623
682
  const imports = allFiles
624
683
  .filter((fileName) => fileName.endsWith(".css"))
625
- .map((fileName) => `@import '../../${ctx[pluginName].outputOptions.buildName}/${fileName}';`);
684
+ .map((fileName) => `@import '${importFilePath}/${fileName}';`);
626
685
  const componentLibraries = ctx.client.componentLibraries;
627
686
  for (const componentLibrary of componentLibraries) {
628
687
  const { libraryName } = getComponentLibraryConfig(componentLibrary);
@@ -637,13 +696,23 @@ async function injectCSS(ctx, pluginName) {
637
696
  }
638
697
  async function injectBundleRender(ctx, pluginName) {
639
698
  var _a;
640
- const importStr = `import render from '../../${ctx[pluginName].outputOptions.buildName}/${ctx[pluginName].outputOptions.fileName}';`;
699
+ const importFilePath = path$1.relative(ctx.client.componentDir, path$1.resolve(ctx.client.buildDir, ctx[pluginName].outputOptions.buildName));
700
+ const importStr = `import render from '${importFilePath}/${ctx[pluginName].outputOptions.fileName}';`;
641
701
  let content = await fs.readFile(path$1.resolve(ctx.core.templatesDir, "component.tsx.template"), "utf8");
642
702
  if (!!((_a = ctx.dev) === null || _a === void 0 ? void 0 : _a.watch)) {
643
703
  content = content.replace(COMPONENT_TAG_TOKEN, "embeddable-component");
644
704
  }
645
705
  await fs.writeFile(path$1.resolve(ctx.client.componentDir, "component.tsx"), content.replace(RENDER_IMPORT_TOKEN, importStr));
646
706
  }
707
+ async function injectEmptyCSS(ctx) {
708
+ await fs.writeFile(path$1.resolve(ctx.client.componentDir, "style.css"), "");
709
+ }
710
+ async function injectBundleRenderStub(ctx) {
711
+ const stubStr = `const render = () => {};`;
712
+ let content = await fs.readFile(path$1.resolve(ctx.core.templatesDir, "component.tsx.template"), "utf8");
713
+ content = content.replace(COMPONENT_TAG_TOKEN, "embeddable-component");
714
+ await fs.writeFile(path$1.resolve(ctx.client.componentDir, "component.tsx"), content.replace(RENDER_IMPORT_TOKEN, stubStr));
715
+ }
647
716
  async function addComponentTagName(filePath, bundleHash) {
648
717
  // find entry file with a name *.entry.js
649
718
  const entryFiles = await findFiles(path$1.dirname(filePath), /.*\.entry\.js/);
@@ -662,11 +731,18 @@ async function addComponentTagName(filePath, bundleHash) {
662
731
  fs.writeFile(entryFileName[1], newEntryFileContent),
663
732
  ]);
664
733
  }
665
- async function runStencil(ctx) {
734
+ async function runStencil(ctx, options) {
666
735
  var _a, _b, _c;
667
- const logger = ((_a = ctx.dev) === null || _a === void 0 ? void 0 : _a.logger) || createNodeLogger();
668
- const sys = ((_b = ctx.dev) === null || _b === void 0 ? void 0 : _b.sys) || createNodeSys({ process });
669
- const devMode = !!((_c = ctx.dev) === null || _c === void 0 ? void 0 : _c.watch);
736
+ const logger = ((options === null || options === void 0 ? void 0 : options.dtsOnly) ? createNodeLogger() : ((_a = ctx.dev) === null || _a === void 0 ? void 0 : _a.logger) || createNodeLogger());
737
+ const sys = (options === null || options === void 0 ? void 0 : options.dtsOnly) ? createNodeSys({ process }) : (((_b = ctx.dev) === null || _b === void 0 ? void 0 : _b.sys) || createNodeSys({ process }));
738
+ const devMode = !!((_c = ctx.dev) === null || _c === void 0 ? void 0 : _c.watch) && !(options === null || options === void 0 ? void 0 : options.dtsOnly);
739
+ if (options === null || options === void 0 ? void 0 : options.dtsOnly) {
740
+ logger.setLevel("error");
741
+ logger.createTimeSpan = () => ({
742
+ duration: () => 0,
743
+ finish: () => 0,
744
+ });
745
+ }
670
746
  const isWindows = process.platform === "win32";
671
747
  const validated = await loadConfig({
672
748
  initTsConfig: true,
@@ -675,14 +751,16 @@ async function runStencil(ctx) {
675
751
  config: {
676
752
  devMode,
677
753
  maxConcurrentWorkers: isWindows ? 0 : 8, // workers break on windows
754
+ // we will trigger a rebuild by updating the component.tsx file (see triggerBuild function)
755
+ watchIgnoredRegex: [/\.css$/, /\.d\.ts$/, /\.js$/],
678
756
  rootDir: ctx.client.webComponentRoot,
679
757
  configPath: path$1.resolve(ctx.client.webComponentRoot, "stencil.config.ts"),
680
758
  tsconfig: path$1.resolve(ctx.client.webComponentRoot, "tsconfig.json"),
681
759
  namespace: "embeddable-wrapper",
682
760
  srcDir: ctx.client.componentDir,
683
- sourceMap: true, // always generate source maps in both dev and prod
684
- minifyJs: !devMode,
685
- minifyCss: !devMode,
761
+ sourceMap: !(options === null || options === void 0 ? void 0 : options.dtsOnly), // always generate source maps in both dev and prod
762
+ minifyJs: !devMode && !(options === null || options === void 0 ? void 0 : options.dtsOnly),
763
+ minifyCss: !devMode && !(options === null || options === void 0 ? void 0 : options.dtsOnly),
686
764
  outputTargets: [
687
765
  {
688
766
  type: "dist",
@@ -692,17 +770,21 @@ async function runStencil(ctx) {
692
770
  },
693
771
  });
694
772
  const compiler = await createCompiler(validated.config);
773
+ if (devMode) {
774
+ sys.onProcessInterrupt(() => {
775
+ compiler.destroy();
776
+ });
777
+ return await compiler.createWatcher();
778
+ }
695
779
  const buildResults = await compiler.build();
696
- if (!devMode) {
697
- if (buildResults.hasError) {
698
- console.error("Stencil build error:", buildResults.diagnostics);
699
- throw new Error("Stencil build error");
700
- }
701
- else {
702
- await handleStencilBuildOutput(ctx);
703
- }
704
- await compiler.destroy();
780
+ if (buildResults.hasError) {
781
+ console.error("Stencil build error:", buildResults.diagnostics);
782
+ throw new Error("Stencil build error");
705
783
  }
784
+ else {
785
+ await handleStencilBuildOutput(ctx);
786
+ }
787
+ await compiler.destroy();
706
788
  process.chdir(ctx.client.rootDir);
707
789
  }
708
790
  async function handleStencilBuildOutput(ctx) {
@@ -21587,6 +21669,95 @@ function checkAndLogWarnings(response) {
21587
21669
  }
21588
21670
  }
21589
21671
 
21672
+ /**
21673
+ * Wraps a ServerResponse object to prevent setting the Content-Length header.
21674
+ */
21675
+ function preventContentLength(res) {
21676
+ const originalSetHeader = res.setHeader.bind(res);
21677
+ res.setHeader = function (key, value) {
21678
+ if (key.toLowerCase() === "content-length") {
21679
+ return this;
21680
+ }
21681
+ return originalSetHeader(key, value);
21682
+ };
21683
+ }
21684
+ function createWatcherLock() {
21685
+ let locked = false;
21686
+ let waiters = [];
21687
+ return {
21688
+ lock() {
21689
+ if (!locked) {
21690
+ locked = true;
21691
+ }
21692
+ },
21693
+ unlock() {
21694
+ if (locked) {
21695
+ locked = false;
21696
+ waiters.forEach((fn) => fn());
21697
+ waiters = [];
21698
+ }
21699
+ },
21700
+ async waitUntilFree() {
21701
+ if (!locked)
21702
+ return;
21703
+ await new Promise((resolve) => {
21704
+ waiters.push(resolve);
21705
+ });
21706
+ },
21707
+ };
21708
+ }
21709
+ const delay = (ms) => new Promise((res) => setTimeout(res, ms));
21710
+ /**
21711
+ * This function waits until a file stabilizes, meaning its size does not change for a certain number of attempts
21712
+ * and the content ends with a specific expected tail.
21713
+ * It uses stream reading to check the file's content, the same wait serve-static uses.
21714
+ * This will help to prevent when serve-static serves a file that is still being written to.
21715
+ * One of the issues, related to this, is "Constructor for "embeddable-component#undefined" was not found", that we saw quite often in the past.
21716
+ * @param filePath
21717
+ * @param expectedTail
21718
+ * @param maxAttempts
21719
+ * @param stableCount
21720
+ */
21721
+ async function waitUntilFileStable(filePath, expectedTail, { maxAttempts = 100, requiredStableCount = 2, } = {}) {
21722
+ let lastSize = -1;
21723
+ let stableCounter = 0;
21724
+ for (let i = 0; i < maxAttempts; i++) {
21725
+ try {
21726
+ const { size, tailMatches } = await checkFileTail(filePath, expectedTail);
21727
+ if (size === lastSize && size > 0 && tailMatches) {
21728
+ stableCounter++;
21729
+ if (stableCounter >= requiredStableCount) {
21730
+ return;
21731
+ }
21732
+ }
21733
+ else {
21734
+ stableCounter = 0;
21735
+ }
21736
+ lastSize = size;
21737
+ }
21738
+ catch (_a) {
21739
+ }
21740
+ await delay(50);
21741
+ }
21742
+ throw new Error("File did not stabilize");
21743
+ }
21744
+ async function checkFileTail(filePath, expectedTail, tailLength = 500) {
21745
+ const stats = await fs.stat(filePath);
21746
+ const size = stats.size;
21747
+ const start = Math.max(0, size - tailLength);
21748
+ return new Promise((resolve, reject) => {
21749
+ let tailBuffer = "";
21750
+ createReadStream(filePath, { encoding: "utf-8", start })
21751
+ .on("data", (chunk) => {
21752
+ tailBuffer += chunk;
21753
+ })
21754
+ .on("end", () => {
21755
+ resolve({ size, tailMatches: tailBuffer.includes(expectedTail) });
21756
+ })
21757
+ .on("error", reject);
21758
+ });
21759
+ }
21760
+
21590
21761
  dotenv.config();
21591
21762
  let wss;
21592
21763
  let changedFiles = [];
@@ -21599,8 +21770,27 @@ const SERVER_PORT = 8926;
21599
21770
  const BUILD_DEV_DIR = ".embeddable-dev-build";
21600
21771
  // NOTE: for backward compatibility, keep the file name as global.css
21601
21772
  const CUSTOM_CANVAS_CSS = "/global.css";
21773
+ let stencilWatcher;
21774
+ let isActiveBundleBuild = false;
21775
+ /** We use two steps compilation for embeddable components.
21776
+ * 1. Compile *emb.ts files using plugin complier (sdk-react)
21777
+ * 2. Compile the web component using Stencil compiler.
21778
+ * These compilations can happen in parallel, but we need to ensure that
21779
+ * the first step is not started until the second step is finished (if recompilation is needed).
21780
+ * We use this lock to lock it before the second step starts and unlock it after the second step is finished.
21781
+ * */
21782
+ const lock = createWatcherLock();
21602
21783
  const buildWebComponent = async (config) => {
21603
- await generate(config, "sdk-react");
21784
+ // if there is no watcher, then this is the first build. We need to create a watcher
21785
+ // otherwise we can just trigger a rebuild
21786
+ if (!stencilWatcher) {
21787
+ stencilWatcher = (await generate(config, "sdk-react"));
21788
+ stencilWatcher.on("buildFinish", (e) => onWebComponentBuildFinish(e, config));
21789
+ stencilWatcher.start();
21790
+ }
21791
+ else {
21792
+ await triggerWebComponentRebuild(config);
21793
+ }
21604
21794
  };
21605
21795
  const executePluginBuilds = async (config, watchers) => {
21606
21796
  if (pluginBuildInProgress) {
@@ -21690,7 +21880,14 @@ var dev = async () => {
21690
21880
  };
21691
21881
  breadcrumbs.push("prepare config");
21692
21882
  await prepare$1(config);
21693
- const serve = serveStatic(config.client.buildDir);
21883
+ const serve = serveStatic(config.client.buildDir, {
21884
+ setHeaders: (res, path) => {
21885
+ if (path.includes("/dist/embeddable-wrapper/")) {
21886
+ // Prevent content length for HMR files
21887
+ preventContentLength(res);
21888
+ }
21889
+ },
21890
+ });
21694
21891
  let workspacePreparation = ora("Preparing workspace...").start();
21695
21892
  breadcrumbs.push("get preview workspace");
21696
21893
  try {
@@ -21710,7 +21907,7 @@ var dev = async () => {
21710
21907
  }
21711
21908
  workspacePreparation.succeed("Workspace is ready");
21712
21909
  const server = http.createServer(async (request, res) => {
21713
- var _a;
21910
+ var _a, _b;
21714
21911
  res.setHeader("Access-Control-Allow-Origin", "*");
21715
21912
  res.setHeader("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, OPTIONS");
21716
21913
  res.setHeader("Access-Control-Allow-Headers", "Content-Type, Authorization");
@@ -21728,10 +21925,29 @@ var dev = async () => {
21728
21925
  return;
21729
21926
  }
21730
21927
  }
21731
- catch (_b) { }
21928
+ catch (_c) { }
21929
+ // Last line of defence: wait for the file to be fully written before
21930
+ // handing it to serve-static. This catches any race condition between
21931
+ // the WS "build success" notification and the actual HTTP request —
21932
+ // e.g. when buildFinish fires slightly before Stencil flushes files.
21933
+ const urlPath = ((_b = request.url) !== null && _b !== void 0 ? _b : "").split("?")[0];
21934
+ if (urlPath.includes("/dist/embeddable-wrapper/") &&
21935
+ urlPath.endsWith(".js")) {
21936
+ const filePath = path.resolve(config.client.buildDir, urlPath.slice(1));
21937
+ await waitUntilFileStable(filePath, "sourceMappingURL", {
21938
+ maxAttempts: 40, // up to ~2 s; fast in the happy path
21939
+ requiredStableCount: 2,
21940
+ }).catch(() => {
21941
+ // If the check times out we still serve — better a partial file
21942
+ // warning in the console than a hung request.
21943
+ });
21944
+ }
21732
21945
  serve(request, res, done);
21733
21946
  });
21734
21947
  const { themeWatcher, lifecycleWatcher } = await buildGlobalHooks(config);
21948
+ const dtsOra = ora("Generating component type files...").start();
21949
+ await generateDTS(config);
21950
+ dtsOra.succeed("Component type files generated");
21735
21951
  wss = new WebSocketServer({ server });
21736
21952
  server.listen(SERVER_PORT, async () => {
21737
21953
  const watchers = [];
@@ -21762,11 +21978,11 @@ var dev = async () => {
21762
21978
  const customCanvasCssWatch = globalCustomCanvasWatcher(config);
21763
21979
  watchers.push(customCanvasCssWatch);
21764
21980
  if (themeWatcher) {
21765
- await globalHookWatcher(themeWatcher);
21981
+ await globalHookWatcher(themeWatcher, "themeProvider");
21766
21982
  watchers.push(themeWatcher);
21767
21983
  }
21768
21984
  if (lifecycleWatcher) {
21769
- await globalHookWatcher(lifecycleWatcher);
21985
+ await globalHookWatcher(lifecycleWatcher, "lifecycleHook");
21770
21986
  watchers.push(lifecycleWatcher);
21771
21987
  }
21772
21988
  }
@@ -21789,30 +22005,46 @@ const configureWatcher = async (watcher, ctx) => {
21789
22005
  });
21790
22006
  watcher.on("event", async (e) => {
21791
22007
  var _a;
22008
+ if (e.code === "START") {
22009
+ await lock.waitUntilFree();
22010
+ }
21792
22011
  if (e.code === "BUNDLE_START") {
22012
+ isActiveBundleBuild = true;
21793
22013
  await onBuildStart(ctx);
21794
22014
  }
21795
22015
  if (e.code === "BUNDLE_END") {
22016
+ lock.lock();
22017
+ isActiveBundleBuild = false;
22018
+ if (stencilWatcher && shouldRebuildWebComponent()) {
22019
+ try {
22020
+ await fs.rm(path.resolve(ctx.client.buildDir, "dist", "embeddable-wrapper"), { recursive: true });
22021
+ }
22022
+ catch (error) {
22023
+ console.error("Error cleaning up build directory:", error);
22024
+ }
22025
+ }
21796
22026
  await onBundleBuildEnd(ctx);
21797
22027
  changedFiles = [];
21798
22028
  }
21799
22029
  if (e.code === "ERROR") {
22030
+ lock.unlock();
22031
+ isActiveBundleBuild = false;
21800
22032
  sendMessage("componentsBuildError", { error: (_a = e.error) === null || _a === void 0 ? void 0 : _a.message });
21801
22033
  changedFiles = [];
21802
22034
  }
21803
22035
  });
21804
22036
  };
21805
- const globalHookWatcher = async (watcher) => {
22037
+ const globalHookWatcher = async (watcher, key) => {
21806
22038
  watcher.on("change", (path) => {
21807
22039
  changedFiles.push(path);
21808
22040
  });
21809
22041
  watcher.on("event", async (e) => {
21810
22042
  var _a;
21811
22043
  if (e.code === "BUNDLE_START") {
21812
- sendMessage("componentsBuildStart", { changedFiles });
22044
+ sendMessage(`${key}BuildStart`, { changedFiles });
21813
22045
  }
21814
22046
  if (e.code === "BUNDLE_END") {
21815
- sendMessage("componentsBuildSuccess");
22047
+ sendMessage(`${key}BuildSuccess`, { version: new Date().getTime() });
21816
22048
  changedFiles = [];
21817
22049
  }
21818
22050
  if (e.code === "ERROR") {
@@ -21841,14 +22073,15 @@ const openDevWorkspacePage = async (previewBaseUrl, workspaceId) => {
21841
22073
  ora(`Preview workspace is available at ${previewBaseUrl}/workspace/${workspaceId}`).info();
21842
22074
  return await open(`${previewBaseUrl}/workspace/${workspaceId}`);
21843
22075
  };
22076
+ function shouldRebuildWebComponent() {
22077
+ return !onlyTypesChanged() || changedFiles.length === 0;
22078
+ }
21844
22079
  const onBundleBuildEnd = async (ctx) => {
21845
- if (!onlyTypesChanged() || changedFiles.length === 0) {
22080
+ if (shouldRebuildWebComponent()) {
21846
22081
  await buildWebComponent(ctx);
21847
22082
  }
21848
- if (browserWindow == null) {
21849
- browserWindow = await openDevWorkspacePage(ctx.previewBaseUrl, previewWorkspace);
21850
- }
21851
22083
  else {
22084
+ lock.unlock();
21852
22085
  sendMessage("componentsBuildSuccess");
21853
22086
  }
21854
22087
  };
@@ -21938,6 +22171,7 @@ const onClose = async (server, sys, watchers, config) => {
21938
22171
  server.close();
21939
22172
  wss.close();
21940
22173
  browserWindow === null || browserWindow === void 0 ? void 0 : browserWindow.unref();
22174
+ await (stencilWatcher === null || stencilWatcher === void 0 ? void 0 : stencilWatcher.close());
21941
22175
  for (const watcher of watchers) {
21942
22176
  if (watcher.close) {
21943
22177
  await watcher.close();
@@ -21986,6 +22220,43 @@ const getPreviewWorkspace = async (startedOra, ctx) => {
21986
22220
  }
21987
22221
  }
21988
22222
  };
22223
+ async function onWebComponentBuildFinish(e, config) {
22224
+ var _a;
22225
+ lock.unlock();
22226
+ if (!browserWindow) {
22227
+ browserWindow = await openDevWorkspacePage(config.previewBaseUrl, previewWorkspace);
22228
+ return;
22229
+ }
22230
+ await delay(50);
22231
+ if (isActiveBundleBuild) {
22232
+ return;
22233
+ }
22234
+ if (e.hasSuccessfulBuild &&
22235
+ ((_a = e.hmr) === null || _a === void 0 ? void 0 : _a.componentsUpdated) &&
22236
+ e.hmr.reloadStrategy === "hmr") {
22237
+ try {
22238
+ await waitForStableHmrFiles(e.componentGraph, config);
22239
+ }
22240
+ finally {
22241
+ sendMessage("componentsBuildSuccessHmr", e.hmr);
22242
+ }
22243
+ }
22244
+ else {
22245
+ sendMessage("componentsBuildSuccess");
22246
+ }
22247
+ }
22248
+ async function waitForStableHmrFiles(componentGraph, config) {
22249
+ const promises = [];
22250
+ for (const files of Object.values(componentGraph !== null && componentGraph !== void 0 ? componentGraph : {})) {
22251
+ for (const file of files) {
22252
+ if (file.startsWith("embeddable-component")) {
22253
+ const fullPath = path.resolve(config.client.buildDir, "dist", "embeddable-wrapper", file);
22254
+ promises.push(waitUntilFileStable(fullPath, "sourceMappingURL"));
22255
+ }
22256
+ }
22257
+ }
22258
+ await Promise.all(promises);
22259
+ }
21989
22260
 
21990
22261
  const REGION_CONFIGS = {
21991
22262
  EU: {
@@ -22530,7 +22801,7 @@ var _FileFromPath = class _FileFromPath {
22530
22801
  );
22531
22802
  }
22532
22803
  if (this.size) {
22533
- yield* createReadStream(__privateGet(this, _path), {
22804
+ yield* createReadStream$1(__privateGet(this, _path), {
22534
22805
  start: __privateGet(this, _start),
22535
22806
  end: __privateGet(this, _start) + this.size - 1
22536
22807
  });