🎩 You're Invited:Meet the Socket team at Black Hat in Las Vegas, August 3-6.RSVP
Sign In

@ecopages/core

Package Overview
Dependencies
Maintainers
1
Versions
86
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@ecopages/core - npm Package Compare versions

Comparing version
0.2.0-beta.17
to
0.2.0-beta.18
+8
src/dev/hmr-manager-registry.d.ts
/**
* Per-app registry for the shared HMR manager used by host-owned dev clients.
*/
import type { EcoPagesAppConfig } from '../types/public-types.js';
import type { IHmrManager } from '../types/public-types.js';
export declare function setAppHmrManager(appConfig: EcoPagesAppConfig, hmrManager: IHmrManager): void;
export declare function getAppHmrManager(appConfig: EcoPagesAppConfig): IHmrManager | undefined;
export declare function clearAppHmrManager(appConfig: EcoPagesAppConfig): void;
const appHmrManagers = /* @__PURE__ */ new WeakMap();
function setAppHmrManager(appConfig, hmrManager) {
appHmrManagers.set(appConfig, hmrManager);
}
function getAppHmrManager(appConfig) {
return appHmrManagers.get(appConfig);
}
function clearAppHmrManager(appConfig) {
appHmrManagers.delete(appConfig);
}
export {
clearAppHmrManager,
getAppHmrManager,
setAppHmrManager
};
export declare function encodeHmrDynamicSegments(filepath: string): string;
/**
* Resolves the on-disk and browser URL targets for one HMR script entrypoint.
*/
export declare function resolveHmrEntrypointOutputPaths(srcDir: string, distDir: string, entrypointPath: string): {
outputPath: string;
outputUrl: string;
};
/**
* @remarks
* Rebuilds must delete the previous artifact so browser-hmr does not reuse a
* stale bundle when the source file changed.
*/
export declare function removeStaleHmrEntrypointOutput(outputPath: string, scope: string): void;
import path from "node:path";
import { fileSystem } from "@ecopages/file-system";
import { RESOLVED_ASSETS_DIR } from "../config/constants.js";
import { appLogger } from "../global/app-logger.js";
function encodeHmrDynamicSegments(filepath) {
return filepath.replace(/\[([^\]]+)\]/g, "_$1_");
}
function resolveHmrEntrypointOutputPaths(srcDir, distDir, entrypointPath) {
const normalizedEntrypoint = path.resolve(entrypointPath);
const relativePath = path.relative(srcDir, normalizedEntrypoint);
const relativePathJs = relativePath.replace(/\.(tsx?|jsx?|mdx?)$/, ".js");
const encodedPathJs = encodeHmrDynamicSegments(relativePathJs);
const urlPath = encodedPathJs.split(path.sep).join("/");
return {
outputUrl: `/${path.join(RESOLVED_ASSETS_DIR, "_hmr", urlPath).split(path.sep).join("/")}`,
outputPath: path.join(distDir, urlPath)
};
}
function removeStaleHmrEntrypointOutput(outputPath, scope) {
if (!fileSystem.exists(outputPath)) {
return;
}
try {
fileSystem.remove(outputPath);
} catch (error) {
appLogger.warn(
`[${scope}] Failed to remove stale entrypoint output ${outputPath}: ${error instanceof Error ? error.message : String(error)}`
);
}
}
export {
encodeHmrDynamicSegments,
removeStaleHmrEntrypointOutput,
resolveHmrEntrypointOutputPaths
};
+10
-2
{
"name": "@ecopages/core",
"version": "0.2.0-beta.17",
"version": "0.2.0-beta.18",
"description": "Core package for Ecopages",

@@ -20,3 +20,3 @@ "keywords": [

"dependencies": {
"@ecopages/file-system": "0.2.0-beta.17",
"@ecopages/file-system": "0.2.0-beta.18",
"@ecopages/logger": "^0.2.3",

@@ -137,2 +137,6 @@ "@ecopages/scripts-injector": "^0.1.5",

},
"./dev/hmr-manager-registry": {
"types": "./src/dev/hmr-manager-registry.d.ts",
"default": "./src/dev/hmr-manager-registry.js"
},
"./dev/dev-client-ownership": {

@@ -343,2 +347,6 @@ "types": "./src/dev/dev-client-ownership.d.ts",

},
"./dev/hmr-manager-registry.ts": {
"types": "./src/dev/hmr-manager-registry.d.ts",
"default": "./src/dev/hmr-manager-registry.js"
},
"./dev/dev-client-ownership.ts": {

@@ -345,0 +353,0 @@ "types": "./src/dev/dev-client-ownership.d.ts",

@@ -27,2 +27,3 @@ import path from "node:path";

import { setAppDevClientBridge } from "../../dev/client-bridge-registry.js";
import { setAppHmrManager } from "../../dev/hmr-manager-registry.js";
import { HmrManager } from "./hmr-manager.js";

@@ -604,2 +605,3 @@ import { BunStaticPreviewHost } from "./static-preview-host.js";

setAppDevClientBridge(params.appConfig, bridge);
setAppHmrManager(params.appConfig, hmrManager);
const previewHost = params.previewHost ?? new BunStaticPreviewHost();

@@ -606,0 +608,0 @@ const adapter = new BunServerAdapter({

import { WebSocketServer } from "ws";
import { setAppDevClientBridge } from "../../dev/client-bridge-registry.js";
import { setAppHmrManager } from "../../dev/hmr-manager-registry.js";
import { NodeClientBridge } from "./node-client-bridge.js";

@@ -11,2 +12,3 @@ import { NodeHmrManager } from "./node-hmr-manager.js";

setAppDevClientBridge(options.appConfig, bridge);
setAppHmrManager(options.appConfig, hmrManager);
return {

@@ -13,0 +15,0 @@ websocketServer,

+0
-2

@@ -53,4 +53,2 @@ /**

private getEntrypointOutput;
private removeStaleEntrypointOutput;
private encodeDynamicSegments;
}
import path from "node:path";
import { fileSystem } from "@ecopages/file-system";
import { appLogger } from "../../global/app-logger.js";
import { RESOLVED_ASSETS_DIR } from "../../config/constants.js";
import { removeStaleHmrEntrypointOutput, resolveHmrEntrypointOutputPaths } from "../../hmr/hmr-entrypoint-output.js";
class HmrEntrypointRegistrar {

@@ -39,3 +38,3 @@ options;

this.options.watchedFiles.set(entrypointPath, outputUrl);
this.removeStaleEntrypointOutput(outputPath);
removeStaleHmrEntrypointOutput(outputPath, "HMR");
await registrationOptions.emit(entrypointPath, outputPath);

@@ -61,26 +60,4 @@ if (!fileSystem.exists(outputPath)) {

getEntrypointOutput(entrypointPath) {
const relativePath = path.relative(this.options.srcDir, entrypointPath);
const relativePathJs = relativePath.replace(/\.(tsx?|jsx?|mdx?)$/, ".js");
const encodedPathJs = this.encodeDynamicSegments(relativePathJs);
const urlPath = encodedPathJs.split(path.sep).join("/");
return {
outputUrl: `/${path.join(RESOLVED_ASSETS_DIR, "_hmr", urlPath)}`,
outputPath: path.join(this.options.distDir, urlPath)
};
return resolveHmrEntrypointOutputPaths(this.options.srcDir, this.options.distDir, entrypointPath);
}
removeStaleEntrypointOutput(outputPath) {
if (!fileSystem.exists(outputPath)) {
return;
}
try {
fileSystem.remove(outputPath);
} catch (error) {
appLogger.warn(
`[HMR] Failed to remove stale entrypoint output ${outputPath}: ${error instanceof Error ? error.message : String(error)}`
);
}
}
encodeDynamicSegments(filepath) {
return filepath.replace(/\[([^\]]+)\]/g, "_$1_");
}
}

@@ -87,0 +64,0 @@ export {

@@ -7,2 +7,4 @@ import path from "node:path";

import { copyRuntimePublicDirIfChanged } from "./copy-runtime-public-dir.js";
import { clearAppDevClientBridge } from "../../dev/client-bridge-registry.js";
import { clearAppHmrManager } from "../../dev/hmr-manager-registry.js";
import { injectHmrRuntimeIntoHtmlResponse, isHtmlResponse, shouldInjectHmrHtmlResponse } from "./hmr-html-response.js";

@@ -33,2 +35,4 @@ function prepareRuntimePublicDir(appConfig) {

options.bridge?.destroy();
clearAppDevClientBridge(options.appConfig);
clearAppHmrManager(options.appConfig);
await options.previewHost.stop();

@@ -35,0 +39,0 @@ }

@@ -50,6 +50,8 @@ import fs from 'node:fs';

/**
* Returns the emitted HMR script output when the artifact already exists on disk.
* Returns the emitted HMR script output when the entrypoint is already registered
* and its browser bundle exists on disk.
*
* SSR must not block on entrypoint registration when a previous build already
* produced the browser bundle.
* @remarks
* Disk artifacts alone are not enough: a fresh dev session must still register
* the entrypoint so file watchers can rebuild it on change.
*/

@@ -56,0 +58,0 @@ getResolvedScriptOutput(entrypointPath: string): {

@@ -5,2 +5,3 @@ import fs from "node:fs";

import { RESOLVED_ASSETS_DIR } from "../../config/constants.js";
import { resolveHmrEntrypointOutputPaths } from "../../hmr/hmr-entrypoint-output.js";
import { requireBuildRuntime } from "../../build/build-runtime.js";

@@ -188,17 +189,23 @@ import { fileSystem } from "@ecopages/file-system";

/**
* Returns the emitted HMR script output when the artifact already exists on disk.
* Returns the emitted HMR script output when the entrypoint is already registered
* and its browser bundle exists on disk.
*
* SSR must not block on entrypoint registration when a previous build already
* produced the browser bundle.
* @remarks
* Disk artifacts alone are not enough: a fresh dev session must still register
* the entrypoint so file watchers can rebuild it on change.
*/
getResolvedScriptOutput(entrypointPath) {
const normalizedEntrypoint = path.resolve(entrypointPath);
const relativePath = path.relative(this.appConfig.absolutePaths.srcDir, normalizedEntrypoint);
const relativePathJs = relativePath.replace(/\.(tsx?|jsx?|mdx?)$/, ".js").replace(/\[([^\]]+)\]/g, "_$1_");
const urlPath = relativePathJs.split(path.sep).join("/");
const outputPath = path.join(this.distDir, urlPath);
if (!this.watchedFiles.has(normalizedEntrypoint)) {
return void 0;
}
const { outputPath, outputUrl: derivedOutputUrl } = resolveHmrEntrypointOutputPaths(
this.appConfig.absolutePaths.srcDir,
this.distDir,
normalizedEntrypoint
);
if (!fileSystem.exists(outputPath)) {
return void 0;
}
const outputUrl = this.watchedFiles.get(normalizedEntrypoint) ?? `/${path.join(RESOLVED_ASSETS_DIR, "_hmr", urlPath).split(path.sep).join("/")}`;
const outputUrl = this.watchedFiles.get(normalizedEntrypoint) ?? derivedOutputUrl;
return { outputUrl, outputPath };

@@ -205,0 +212,0 @@ }

@@ -101,2 +101,3 @@ /**

* 2. The changed file is a JS/TS file in the src directory
* 3. Registered entrypoints always match, even when they share an integration template extension
*

@@ -120,2 +121,4 @@ * @param filePath - Absolute path to the changed file

process(filePath: string): Promise<HmrAction>;
private resolveEntrypointOutputPath;
private removeStaleEntrypointOutput;
/**

@@ -122,0 +125,0 @@ * Bundles one or more entrypoints in a single build invocation.

@@ -5,2 +5,3 @@ import path from "node:path";

import { appLogger } from "../../global/app-logger.js";
import { removeStaleHmrEntrypointOutput, resolveHmrEntrypointOutputPaths } from "../hmr-entrypoint-output.js";
class JsHmrStrategy extends HmrStrategy {

@@ -19,2 +20,3 @@ type = HmrStrategyType.SCRIPT;

* 2. The changed file is a JS/TS file in the src directory
* 3. Registered entrypoints always match, even when they share an integration template extension
*

@@ -26,5 +28,6 @@ * @param filePath - Absolute path to the changed file

const watchedFiles = this.context.getWatchedFiles();
const isJsTs = /\.(ts|tsx|js|jsx)$/.test(filePath);
const isInSrc = filePath.startsWith(this.context.getSrcDir());
const isIntegrationTemplate = this.context.getTemplateExtensions().some((extension) => filePath.endsWith(extension));
const resolvedPath = path.resolve(filePath);
const isJsTs = /\.(ts|tsx|js|jsx)$/.test(resolvedPath);
const isInSrc = resolvedPath.startsWith(this.context.getSrcDir());
const isIntegrationTemplate = this.context.getTemplateExtensions().some((extension) => resolvedPath.endsWith(extension));
if (watchedFiles.size === 0) {

@@ -36,8 +39,8 @@ return false;

}
if (watchedFiles.has(resolvedPath)) {
return true;
}
if (isIntegrationTemplate) {
return false;
}
if (watchedFiles.has(filePath)) {
return true;
}
return true;

@@ -66,3 +69,3 @@ }

const hasDependencyHit = dependencyHits.size > 0;
const impactedEntrypoints = hasDependencyHit ? Array.from(dependencyHits).filter((entrypoint) => watchedFiles.has(entrypoint)) : Array.from(watchedFiles.keys());
const impactedEntrypoints = hasDependencyHit ? Array.from(dependencyHits).filter((entrypoint) => watchedFiles.has(path.resolve(entrypoint))) : Array.from(watchedFiles.keys());
const buildableEntrypoints = impactedEntrypoints.filter(

@@ -77,2 +80,5 @@ (entrypoint) => this.context.shouldProcessEntrypoint?.(entrypoint) ?? true

}
for (const entrypoint of buildableEntrypoints) {
this.removeStaleEntrypointOutput(this.resolveEntrypointOutputPath(entrypoint));
}
const buildResult = await this.bundleEntrypoints(buildableEntrypoints);

@@ -91,6 +97,3 @@ if (!buildResult.success) {

}
const srcDir = this.context.getSrcDir();
const relativePath = path.relative(srcDir, entrypoint);
const relativePathJs = relativePath.replace(/\.(tsx?|jsx?|mdx?)$/, ".js");
const outputPath = path.join(this.context.getDistDir(), relativePathJs);
const outputPath = this.resolveEntrypointOutputPath(entrypoint);
const result = await this.processOutput(outputPath, outputUrl);

@@ -123,2 +126,8 @@ if (result.success) {

}
resolveEntrypointOutputPath(entrypointPath) {
return resolveHmrEntrypointOutputPaths(this.context.getSrcDir(), this.context.getDistDir(), entrypointPath).outputPath;
}
removeStaleEntrypointOutput(outputPath) {
removeStaleHmrEntrypointOutput(outputPath, "JsHmrStrategy");
}
/**

@@ -131,2 +140,26 @@ * Bundles one or more entrypoints in a single build invocation.

try {
if (entrypoints.length === 1) {
const entrypoint = entrypoints[0];
const outputPath = this.resolveEntrypointOutputPath(entrypoint);
const naming = path.relative(this.context.getDistDir(), outputPath).split(path.sep).join("/");
const result2 = await this.context.getBrowserBundleService().bundle({
profile: "hmr-entrypoint",
entrypoints: [entrypoint],
outdir: this.context.getDistDir(),
naming,
plugins: this.context.getPlugins(),
minify: false
});
if (!result2.success) {
appLogger.error("[JsHmrStrategy] Entrypoint build failed:", result2.logs);
return { success: false };
}
const dependencies2 = /* @__PURE__ */ new Map();
if (result2.dependencyGraph?.entrypoints) {
for (const [resolvedEntrypoint, deps] of Object.entries(result2.dependencyGraph.entrypoints)) {
dependencies2.set(path.resolve(resolvedEntrypoint), deps);
}
}
return { success: true, dependencies: dependencies2 };
}
const result = await this.context.getBrowserBundleService().bundle({

@@ -133,0 +166,0 @@ profile: "hmr-entrypoint",