@embeddable.com/sdk-core 4.2.0 → 4.3.0-next.1
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 +6 -1
- package/lib/generate.d.ts +32 -1
- package/lib/index.esm.js +311 -36
- package/lib/index.esm.js.map +1 -1
- package/lib/utils/dev.utils.d.ts +26 -0
- package/package.json +1 -1
- package/src/dev.test.ts +184 -14
- package/src/dev.ts +163 -15
- package/src/generate.test.ts +273 -3
- package/src/generate.ts +154 -26
- package/src/utils/dev.utils.test.ts +126 -0
- package/src/utils/dev.utils.ts +117 -0
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,34 @@
|
|
|
1
|
+
import { CompilerWatcher } from "@stencil/core/compiler";
|
|
1
2
|
import { PluginName, ResolvedEmbeddableConfig } from "./defineConfig";
|
|
2
|
-
|
|
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>;
|
|
33
|
+
export declare function injectCSS(ctx: ResolvedEmbeddableConfig, pluginName: PluginName): Promise<void>;
|
|
34
|
+
export declare function injectBundleRender(ctx: ResolvedEmbeddableConfig, pluginName: PluginName): 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,79 @@ 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
|
-
|
|
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
|
|
682
|
+
.relative(ctx.client.componentDir, path$1.resolve(ctx.client.buildDir, ctx[pluginName].outputOptions.buildName))
|
|
683
|
+
.replaceAll("\\", "/");
|
|
623
684
|
const imports = allFiles
|
|
624
685
|
.filter((fileName) => fileName.endsWith(".css"))
|
|
625
|
-
.map((fileName) => `@import '
|
|
686
|
+
.map((fileName) => `@import '${importFilePath}/${fileName}';`);
|
|
626
687
|
const componentLibraries = ctx.client.componentLibraries;
|
|
627
688
|
for (const componentLibrary of componentLibraries) {
|
|
628
689
|
const { libraryName } = getComponentLibraryConfig(componentLibrary);
|
|
@@ -637,13 +698,25 @@ async function injectCSS(ctx, pluginName) {
|
|
|
637
698
|
}
|
|
638
699
|
async function injectBundleRender(ctx, pluginName) {
|
|
639
700
|
var _a;
|
|
640
|
-
const
|
|
701
|
+
const importFilePath = path$1
|
|
702
|
+
.relative(ctx.client.componentDir, path$1.resolve(ctx.client.buildDir, ctx[pluginName].outputOptions.buildName))
|
|
703
|
+
.replaceAll("\\", "/");
|
|
704
|
+
const importStr = `import render from '${importFilePath}/${ctx[pluginName].outputOptions.fileName}';`;
|
|
641
705
|
let content = await fs.readFile(path$1.resolve(ctx.core.templatesDir, "component.tsx.template"), "utf8");
|
|
642
706
|
if (!!((_a = ctx.dev) === null || _a === void 0 ? void 0 : _a.watch)) {
|
|
643
707
|
content = content.replace(COMPONENT_TAG_TOKEN, "embeddable-component");
|
|
644
708
|
}
|
|
645
709
|
await fs.writeFile(path$1.resolve(ctx.client.componentDir, "component.tsx"), content.replace(RENDER_IMPORT_TOKEN, importStr));
|
|
646
710
|
}
|
|
711
|
+
async function injectEmptyCSS(ctx) {
|
|
712
|
+
await fs.writeFile(path$1.resolve(ctx.client.componentDir, "style.css"), "");
|
|
713
|
+
}
|
|
714
|
+
async function injectBundleRenderStub(ctx) {
|
|
715
|
+
const stubStr = `const render = () => {};`;
|
|
716
|
+
let content = await fs.readFile(path$1.resolve(ctx.core.templatesDir, "component.tsx.template"), "utf8");
|
|
717
|
+
content = content.replace(COMPONENT_TAG_TOKEN, "embeddable-component");
|
|
718
|
+
await fs.writeFile(path$1.resolve(ctx.client.componentDir, "component.tsx"), content.replace(RENDER_IMPORT_TOKEN, stubStr));
|
|
719
|
+
}
|
|
647
720
|
async function addComponentTagName(filePath, bundleHash) {
|
|
648
721
|
// find entry file with a name *.entry.js
|
|
649
722
|
const entryFiles = await findFiles(path$1.dirname(filePath), /.*\.entry\.js/);
|
|
@@ -662,11 +735,18 @@ async function addComponentTagName(filePath, bundleHash) {
|
|
|
662
735
|
fs.writeFile(entryFileName[1], newEntryFileContent),
|
|
663
736
|
]);
|
|
664
737
|
}
|
|
665
|
-
async function runStencil(ctx) {
|
|
738
|
+
async function runStencil(ctx, options) {
|
|
666
739
|
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);
|
|
740
|
+
const logger = ((options === null || options === void 0 ? void 0 : options.dtsOnly) ? createNodeLogger() : ((_a = ctx.dev) === null || _a === void 0 ? void 0 : _a.logger) || createNodeLogger());
|
|
741
|
+
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 }));
|
|
742
|
+
const devMode = !!((_c = ctx.dev) === null || _c === void 0 ? void 0 : _c.watch) && !(options === null || options === void 0 ? void 0 : options.dtsOnly);
|
|
743
|
+
if (options === null || options === void 0 ? void 0 : options.dtsOnly) {
|
|
744
|
+
logger.setLevel("error");
|
|
745
|
+
logger.createTimeSpan = () => ({
|
|
746
|
+
duration: () => 0,
|
|
747
|
+
finish: () => 0,
|
|
748
|
+
});
|
|
749
|
+
}
|
|
670
750
|
const isWindows = process.platform === "win32";
|
|
671
751
|
const validated = await loadConfig({
|
|
672
752
|
initTsConfig: true,
|
|
@@ -675,14 +755,16 @@ async function runStencil(ctx) {
|
|
|
675
755
|
config: {
|
|
676
756
|
devMode,
|
|
677
757
|
maxConcurrentWorkers: isWindows ? 0 : 8, // workers break on windows
|
|
758
|
+
// we will trigger a rebuild by updating the component.tsx file (see triggerBuild function)
|
|
759
|
+
watchIgnoredRegex: [/\.css$/, /\.d\.ts$/, /\.js$/],
|
|
678
760
|
rootDir: ctx.client.webComponentRoot,
|
|
679
761
|
configPath: path$1.resolve(ctx.client.webComponentRoot, "stencil.config.ts"),
|
|
680
762
|
tsconfig: path$1.resolve(ctx.client.webComponentRoot, "tsconfig.json"),
|
|
681
763
|
namespace: "embeddable-wrapper",
|
|
682
764
|
srcDir: ctx.client.componentDir,
|
|
683
|
-
sourceMap:
|
|
684
|
-
minifyJs: !devMode,
|
|
685
|
-
minifyCss: !devMode,
|
|
765
|
+
sourceMap: !(options === null || options === void 0 ? void 0 : options.dtsOnly), // always generate source maps in both dev and prod
|
|
766
|
+
minifyJs: !devMode && !(options === null || options === void 0 ? void 0 : options.dtsOnly),
|
|
767
|
+
minifyCss: !devMode && !(options === null || options === void 0 ? void 0 : options.dtsOnly),
|
|
686
768
|
outputTargets: [
|
|
687
769
|
{
|
|
688
770
|
type: "dist",
|
|
@@ -692,17 +774,21 @@ async function runStencil(ctx) {
|
|
|
692
774
|
},
|
|
693
775
|
});
|
|
694
776
|
const compiler = await createCompiler(validated.config);
|
|
777
|
+
if (devMode) {
|
|
778
|
+
sys.onProcessInterrupt(() => {
|
|
779
|
+
compiler.destroy();
|
|
780
|
+
});
|
|
781
|
+
return await compiler.createWatcher();
|
|
782
|
+
}
|
|
695
783
|
const buildResults = await compiler.build();
|
|
696
|
-
if (
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
throw new Error("Stencil build error");
|
|
700
|
-
}
|
|
701
|
-
else {
|
|
702
|
-
await handleStencilBuildOutput(ctx);
|
|
703
|
-
}
|
|
704
|
-
await compiler.destroy();
|
|
784
|
+
if (buildResults.hasError) {
|
|
785
|
+
console.error("Stencil build error:", buildResults.diagnostics);
|
|
786
|
+
throw new Error("Stencil build error");
|
|
705
787
|
}
|
|
788
|
+
else {
|
|
789
|
+
await handleStencilBuildOutput(ctx);
|
|
790
|
+
}
|
|
791
|
+
await compiler.destroy();
|
|
706
792
|
process.chdir(ctx.client.rootDir);
|
|
707
793
|
}
|
|
708
794
|
async function handleStencilBuildOutput(ctx) {
|
|
@@ -21587,6 +21673,95 @@ function checkAndLogWarnings(response) {
|
|
|
21587
21673
|
}
|
|
21588
21674
|
}
|
|
21589
21675
|
|
|
21676
|
+
/**
|
|
21677
|
+
* Wraps a ServerResponse object to prevent setting the Content-Length header.
|
|
21678
|
+
*/
|
|
21679
|
+
function preventContentLength(res) {
|
|
21680
|
+
const originalSetHeader = res.setHeader.bind(res);
|
|
21681
|
+
res.setHeader = function (key, value) {
|
|
21682
|
+
if (key.toLowerCase() === "content-length") {
|
|
21683
|
+
return this;
|
|
21684
|
+
}
|
|
21685
|
+
return originalSetHeader(key, value);
|
|
21686
|
+
};
|
|
21687
|
+
}
|
|
21688
|
+
function createWatcherLock() {
|
|
21689
|
+
let locked = false;
|
|
21690
|
+
let waiters = [];
|
|
21691
|
+
return {
|
|
21692
|
+
lock() {
|
|
21693
|
+
if (!locked) {
|
|
21694
|
+
locked = true;
|
|
21695
|
+
}
|
|
21696
|
+
},
|
|
21697
|
+
unlock() {
|
|
21698
|
+
if (locked) {
|
|
21699
|
+
locked = false;
|
|
21700
|
+
waiters.forEach((fn) => fn());
|
|
21701
|
+
waiters = [];
|
|
21702
|
+
}
|
|
21703
|
+
},
|
|
21704
|
+
async waitUntilFree() {
|
|
21705
|
+
if (!locked)
|
|
21706
|
+
return;
|
|
21707
|
+
await new Promise((resolve) => {
|
|
21708
|
+
waiters.push(resolve);
|
|
21709
|
+
});
|
|
21710
|
+
},
|
|
21711
|
+
};
|
|
21712
|
+
}
|
|
21713
|
+
const delay = (ms) => new Promise((res) => setTimeout(res, ms));
|
|
21714
|
+
/**
|
|
21715
|
+
* This function waits until a file stabilizes, meaning its size does not change for a certain number of attempts
|
|
21716
|
+
* and the content ends with a specific expected tail.
|
|
21717
|
+
* It uses stream reading to check the file's content, the same wait serve-static uses.
|
|
21718
|
+
* This will help to prevent when serve-static serves a file that is still being written to.
|
|
21719
|
+
* One of the issues, related to this, is "Constructor for "embeddable-component#undefined" was not found", that we saw quite often in the past.
|
|
21720
|
+
* @param filePath
|
|
21721
|
+
* @param expectedTail
|
|
21722
|
+
* @param maxAttempts
|
|
21723
|
+
* @param stableCount
|
|
21724
|
+
*/
|
|
21725
|
+
async function waitUntilFileStable(filePath, expectedTail, { maxAttempts = 100, requiredStableCount = 2, } = {}) {
|
|
21726
|
+
let lastSize = -1;
|
|
21727
|
+
let stableCounter = 0;
|
|
21728
|
+
for (let i = 0; i < maxAttempts; i++) {
|
|
21729
|
+
try {
|
|
21730
|
+
const { size, tailMatches } = await checkFileTail(filePath, expectedTail);
|
|
21731
|
+
if (size === lastSize && size > 0 && tailMatches) {
|
|
21732
|
+
stableCounter++;
|
|
21733
|
+
if (stableCounter >= requiredStableCount) {
|
|
21734
|
+
return;
|
|
21735
|
+
}
|
|
21736
|
+
}
|
|
21737
|
+
else {
|
|
21738
|
+
stableCounter = 0;
|
|
21739
|
+
}
|
|
21740
|
+
lastSize = size;
|
|
21741
|
+
}
|
|
21742
|
+
catch (_a) {
|
|
21743
|
+
}
|
|
21744
|
+
await delay(50);
|
|
21745
|
+
}
|
|
21746
|
+
throw new Error("File did not stabilize");
|
|
21747
|
+
}
|
|
21748
|
+
async function checkFileTail(filePath, expectedTail, tailLength = 500) {
|
|
21749
|
+
const stats = await fs.stat(filePath);
|
|
21750
|
+
const size = stats.size;
|
|
21751
|
+
const start = Math.max(0, size - tailLength);
|
|
21752
|
+
return new Promise((resolve, reject) => {
|
|
21753
|
+
let tailBuffer = "";
|
|
21754
|
+
createReadStream(filePath, { encoding: "utf-8", start })
|
|
21755
|
+
.on("data", (chunk) => {
|
|
21756
|
+
tailBuffer += chunk;
|
|
21757
|
+
})
|
|
21758
|
+
.on("end", () => {
|
|
21759
|
+
resolve({ size, tailMatches: tailBuffer.includes(expectedTail) });
|
|
21760
|
+
})
|
|
21761
|
+
.on("error", reject);
|
|
21762
|
+
});
|
|
21763
|
+
}
|
|
21764
|
+
|
|
21590
21765
|
dotenv.config();
|
|
21591
21766
|
let wss;
|
|
21592
21767
|
let changedFiles = [];
|
|
@@ -21599,8 +21774,27 @@ const SERVER_PORT = 8926;
|
|
|
21599
21774
|
const BUILD_DEV_DIR = ".embeddable-dev-build";
|
|
21600
21775
|
// NOTE: for backward compatibility, keep the file name as global.css
|
|
21601
21776
|
const CUSTOM_CANVAS_CSS = "/global.css";
|
|
21777
|
+
let stencilWatcher;
|
|
21778
|
+
let isActiveBundleBuild = false;
|
|
21779
|
+
/** We use two steps compilation for embeddable components.
|
|
21780
|
+
* 1. Compile *emb.ts files using plugin complier (sdk-react)
|
|
21781
|
+
* 2. Compile the web component using Stencil compiler.
|
|
21782
|
+
* These compilations can happen in parallel, but we need to ensure that
|
|
21783
|
+
* the first step is not started until the second step is finished (if recompilation is needed).
|
|
21784
|
+
* We use this lock to lock it before the second step starts and unlock it after the second step is finished.
|
|
21785
|
+
* */
|
|
21786
|
+
const lock = createWatcherLock();
|
|
21602
21787
|
const buildWebComponent = async (config) => {
|
|
21603
|
-
|
|
21788
|
+
// if there is no watcher, then this is the first build. We need to create a watcher
|
|
21789
|
+
// otherwise we can just trigger a rebuild
|
|
21790
|
+
if (!stencilWatcher) {
|
|
21791
|
+
stencilWatcher = (await generate(config, "sdk-react"));
|
|
21792
|
+
stencilWatcher.on("buildFinish", (e) => onWebComponentBuildFinish(e, config));
|
|
21793
|
+
stencilWatcher.start();
|
|
21794
|
+
}
|
|
21795
|
+
else {
|
|
21796
|
+
await triggerWebComponentRebuild(config);
|
|
21797
|
+
}
|
|
21604
21798
|
};
|
|
21605
21799
|
const executePluginBuilds = async (config, watchers) => {
|
|
21606
21800
|
if (pluginBuildInProgress) {
|
|
@@ -21690,7 +21884,14 @@ var dev = async () => {
|
|
|
21690
21884
|
};
|
|
21691
21885
|
breadcrumbs.push("prepare config");
|
|
21692
21886
|
await prepare$1(config);
|
|
21693
|
-
const serve = serveStatic(config.client.buildDir
|
|
21887
|
+
const serve = serveStatic(config.client.buildDir, {
|
|
21888
|
+
setHeaders: (res, path) => {
|
|
21889
|
+
if (path.includes("/dist/embeddable-wrapper/")) {
|
|
21890
|
+
// Prevent content length for HMR files
|
|
21891
|
+
preventContentLength(res);
|
|
21892
|
+
}
|
|
21893
|
+
},
|
|
21894
|
+
});
|
|
21694
21895
|
let workspacePreparation = ora("Preparing workspace...").start();
|
|
21695
21896
|
breadcrumbs.push("get preview workspace");
|
|
21696
21897
|
try {
|
|
@@ -21710,7 +21911,7 @@ var dev = async () => {
|
|
|
21710
21911
|
}
|
|
21711
21912
|
workspacePreparation.succeed("Workspace is ready");
|
|
21712
21913
|
const server = http.createServer(async (request, res) => {
|
|
21713
|
-
var _a;
|
|
21914
|
+
var _a, _b;
|
|
21714
21915
|
res.setHeader("Access-Control-Allow-Origin", "*");
|
|
21715
21916
|
res.setHeader("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, OPTIONS");
|
|
21716
21917
|
res.setHeader("Access-Control-Allow-Headers", "Content-Type, Authorization");
|
|
@@ -21728,10 +21929,29 @@ var dev = async () => {
|
|
|
21728
21929
|
return;
|
|
21729
21930
|
}
|
|
21730
21931
|
}
|
|
21731
|
-
catch (
|
|
21932
|
+
catch (_c) { }
|
|
21933
|
+
// Last line of defence: wait for the file to be fully written before
|
|
21934
|
+
// handing it to serve-static. This catches any race condition between
|
|
21935
|
+
// the WS "build success" notification and the actual HTTP request —
|
|
21936
|
+
// e.g. when buildFinish fires slightly before Stencil flushes files.
|
|
21937
|
+
const urlPath = ((_b = request.url) !== null && _b !== void 0 ? _b : "").split("?")[0];
|
|
21938
|
+
if (urlPath.includes("/dist/embeddable-wrapper/") &&
|
|
21939
|
+
urlPath.endsWith(".js")) {
|
|
21940
|
+
const filePath = path.resolve(config.client.buildDir, urlPath.slice(1));
|
|
21941
|
+
await waitUntilFileStable(filePath, "sourceMappingURL", {
|
|
21942
|
+
maxAttempts: 40, // up to ~2 s; fast in the happy path
|
|
21943
|
+
requiredStableCount: 2,
|
|
21944
|
+
}).catch(() => {
|
|
21945
|
+
// If the check times out we still serve — better a partial file
|
|
21946
|
+
// warning in the console than a hung request.
|
|
21947
|
+
});
|
|
21948
|
+
}
|
|
21732
21949
|
serve(request, res, done);
|
|
21733
21950
|
});
|
|
21734
21951
|
const { themeWatcher, lifecycleWatcher } = await buildGlobalHooks(config);
|
|
21952
|
+
const dtsOra = ora("Generating component type files...").start();
|
|
21953
|
+
await generateDTS(config);
|
|
21954
|
+
dtsOra.succeed("Component type files generated");
|
|
21735
21955
|
wss = new WebSocketServer({ server });
|
|
21736
21956
|
server.listen(SERVER_PORT, async () => {
|
|
21737
21957
|
const watchers = [];
|
|
@@ -21762,11 +21982,11 @@ var dev = async () => {
|
|
|
21762
21982
|
const customCanvasCssWatch = globalCustomCanvasWatcher(config);
|
|
21763
21983
|
watchers.push(customCanvasCssWatch);
|
|
21764
21984
|
if (themeWatcher) {
|
|
21765
|
-
await globalHookWatcher(themeWatcher);
|
|
21985
|
+
await globalHookWatcher(themeWatcher, "themeProvider");
|
|
21766
21986
|
watchers.push(themeWatcher);
|
|
21767
21987
|
}
|
|
21768
21988
|
if (lifecycleWatcher) {
|
|
21769
|
-
await globalHookWatcher(lifecycleWatcher);
|
|
21989
|
+
await globalHookWatcher(lifecycleWatcher, "lifecycleHook");
|
|
21770
21990
|
watchers.push(lifecycleWatcher);
|
|
21771
21991
|
}
|
|
21772
21992
|
}
|
|
@@ -21789,30 +22009,46 @@ const configureWatcher = async (watcher, ctx) => {
|
|
|
21789
22009
|
});
|
|
21790
22010
|
watcher.on("event", async (e) => {
|
|
21791
22011
|
var _a;
|
|
22012
|
+
if (e.code === "START") {
|
|
22013
|
+
await lock.waitUntilFree();
|
|
22014
|
+
}
|
|
21792
22015
|
if (e.code === "BUNDLE_START") {
|
|
22016
|
+
isActiveBundleBuild = true;
|
|
21793
22017
|
await onBuildStart(ctx);
|
|
21794
22018
|
}
|
|
21795
22019
|
if (e.code === "BUNDLE_END") {
|
|
22020
|
+
lock.lock();
|
|
22021
|
+
isActiveBundleBuild = false;
|
|
22022
|
+
if (stencilWatcher && shouldRebuildWebComponent()) {
|
|
22023
|
+
try {
|
|
22024
|
+
await fs.rm(path.resolve(ctx.client.buildDir, "dist", "embeddable-wrapper"), { recursive: true });
|
|
22025
|
+
}
|
|
22026
|
+
catch (error) {
|
|
22027
|
+
console.error("Error cleaning up build directory:", error);
|
|
22028
|
+
}
|
|
22029
|
+
}
|
|
21796
22030
|
await onBundleBuildEnd(ctx);
|
|
21797
22031
|
changedFiles = [];
|
|
21798
22032
|
}
|
|
21799
22033
|
if (e.code === "ERROR") {
|
|
22034
|
+
lock.unlock();
|
|
22035
|
+
isActiveBundleBuild = false;
|
|
21800
22036
|
sendMessage("componentsBuildError", { error: (_a = e.error) === null || _a === void 0 ? void 0 : _a.message });
|
|
21801
22037
|
changedFiles = [];
|
|
21802
22038
|
}
|
|
21803
22039
|
});
|
|
21804
22040
|
};
|
|
21805
|
-
const globalHookWatcher = async (watcher) => {
|
|
22041
|
+
const globalHookWatcher = async (watcher, key) => {
|
|
21806
22042
|
watcher.on("change", (path) => {
|
|
21807
22043
|
changedFiles.push(path);
|
|
21808
22044
|
});
|
|
21809
22045
|
watcher.on("event", async (e) => {
|
|
21810
22046
|
var _a;
|
|
21811
22047
|
if (e.code === "BUNDLE_START") {
|
|
21812
|
-
sendMessage(
|
|
22048
|
+
sendMessage(`${key}BuildStart`, { changedFiles });
|
|
21813
22049
|
}
|
|
21814
22050
|
if (e.code === "BUNDLE_END") {
|
|
21815
|
-
sendMessage(
|
|
22051
|
+
sendMessage(`${key}BuildSuccess`, { version: new Date().getTime() });
|
|
21816
22052
|
changedFiles = [];
|
|
21817
22053
|
}
|
|
21818
22054
|
if (e.code === "ERROR") {
|
|
@@ -21841,14 +22077,15 @@ const openDevWorkspacePage = async (previewBaseUrl, workspaceId) => {
|
|
|
21841
22077
|
ora(`Preview workspace is available at ${previewBaseUrl}/workspace/${workspaceId}`).info();
|
|
21842
22078
|
return await open(`${previewBaseUrl}/workspace/${workspaceId}`);
|
|
21843
22079
|
};
|
|
22080
|
+
function shouldRebuildWebComponent() {
|
|
22081
|
+
return !onlyTypesChanged() || changedFiles.length === 0;
|
|
22082
|
+
}
|
|
21844
22083
|
const onBundleBuildEnd = async (ctx) => {
|
|
21845
|
-
if (
|
|
22084
|
+
if (shouldRebuildWebComponent()) {
|
|
21846
22085
|
await buildWebComponent(ctx);
|
|
21847
22086
|
}
|
|
21848
|
-
if (browserWindow == null) {
|
|
21849
|
-
browserWindow = await openDevWorkspacePage(ctx.previewBaseUrl, previewWorkspace);
|
|
21850
|
-
}
|
|
21851
22087
|
else {
|
|
22088
|
+
lock.unlock();
|
|
21852
22089
|
sendMessage("componentsBuildSuccess");
|
|
21853
22090
|
}
|
|
21854
22091
|
};
|
|
@@ -21938,6 +22175,7 @@ const onClose = async (server, sys, watchers, config) => {
|
|
|
21938
22175
|
server.close();
|
|
21939
22176
|
wss.close();
|
|
21940
22177
|
browserWindow === null || browserWindow === void 0 ? void 0 : browserWindow.unref();
|
|
22178
|
+
await (stencilWatcher === null || stencilWatcher === void 0 ? void 0 : stencilWatcher.close());
|
|
21941
22179
|
for (const watcher of watchers) {
|
|
21942
22180
|
if (watcher.close) {
|
|
21943
22181
|
await watcher.close();
|
|
@@ -21986,6 +22224,43 @@ const getPreviewWorkspace = async (startedOra, ctx) => {
|
|
|
21986
22224
|
}
|
|
21987
22225
|
}
|
|
21988
22226
|
};
|
|
22227
|
+
async function onWebComponentBuildFinish(e, config) {
|
|
22228
|
+
var _a;
|
|
22229
|
+
lock.unlock();
|
|
22230
|
+
if (!browserWindow) {
|
|
22231
|
+
browserWindow = await openDevWorkspacePage(config.previewBaseUrl, previewWorkspace);
|
|
22232
|
+
return;
|
|
22233
|
+
}
|
|
22234
|
+
await delay(50);
|
|
22235
|
+
if (isActiveBundleBuild) {
|
|
22236
|
+
return;
|
|
22237
|
+
}
|
|
22238
|
+
if (e.hasSuccessfulBuild &&
|
|
22239
|
+
((_a = e.hmr) === null || _a === void 0 ? void 0 : _a.componentsUpdated) &&
|
|
22240
|
+
e.hmr.reloadStrategy === "hmr") {
|
|
22241
|
+
try {
|
|
22242
|
+
await waitForStableHmrFiles(e.componentGraph, config);
|
|
22243
|
+
}
|
|
22244
|
+
finally {
|
|
22245
|
+
sendMessage("componentsBuildSuccessHmr", e.hmr);
|
|
22246
|
+
}
|
|
22247
|
+
}
|
|
22248
|
+
else {
|
|
22249
|
+
sendMessage("componentsBuildSuccess");
|
|
22250
|
+
}
|
|
22251
|
+
}
|
|
22252
|
+
async function waitForStableHmrFiles(componentGraph, config) {
|
|
22253
|
+
const promises = [];
|
|
22254
|
+
for (const files of Object.values(componentGraph !== null && componentGraph !== void 0 ? componentGraph : {})) {
|
|
22255
|
+
for (const file of files) {
|
|
22256
|
+
if (file.startsWith("embeddable-component")) {
|
|
22257
|
+
const fullPath = path.resolve(config.client.buildDir, "dist", "embeddable-wrapper", file);
|
|
22258
|
+
promises.push(waitUntilFileStable(fullPath, "sourceMappingURL"));
|
|
22259
|
+
}
|
|
22260
|
+
}
|
|
22261
|
+
}
|
|
22262
|
+
await Promise.all(promises);
|
|
22263
|
+
}
|
|
21989
22264
|
|
|
21990
22265
|
const REGION_CONFIGS = {
|
|
21991
22266
|
EU: {
|
|
@@ -22530,7 +22805,7 @@ var _FileFromPath = class _FileFromPath {
|
|
|
22530
22805
|
);
|
|
22531
22806
|
}
|
|
22532
22807
|
if (this.size) {
|
|
22533
|
-
yield* createReadStream(__privateGet(this, _path), {
|
|
22808
|
+
yield* createReadStream$1(__privateGet(this, _path), {
|
|
22534
22809
|
start: __privateGet(this, _start),
|
|
22535
22810
|
end: __privateGet(this, _start) + this.size - 1
|
|
22536
22811
|
});
|