@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 +6 -1
- package/lib/generate.d.ts +30 -1
- package/lib/index.esm.js +307 -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 +187 -3
- package/src/generate.ts +148 -24
- 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,32 @@
|
|
|
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>;
|
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
|
-
|
|
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 '
|
|
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
|
|
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:
|
|
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 (
|
|
697
|
-
|
|
698
|
-
|
|
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
|
-
|
|
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 (
|
|
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(
|
|
22044
|
+
sendMessage(`${key}BuildStart`, { changedFiles });
|
|
21813
22045
|
}
|
|
21814
22046
|
if (e.code === "BUNDLE_END") {
|
|
21815
|
-
sendMessage(
|
|
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 (
|
|
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
|
});
|