@ecopages/core 0.2.0-beta.0 → 0.2.0-beta.2
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/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ecopages/core",
|
|
3
|
-
"version": "0.2.0-beta.
|
|
3
|
+
"version": "0.2.0-beta.2",
|
|
4
4
|
"description": "Core package for Ecopages",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"ecopages",
|
|
@@ -17,7 +17,7 @@
|
|
|
17
17
|
"directory": "packages/core"
|
|
18
18
|
},
|
|
19
19
|
"dependencies": {
|
|
20
|
-
"@ecopages/file-system": "0.2.0-beta.
|
|
20
|
+
"@ecopages/file-system": "0.2.0-beta.2",
|
|
21
21
|
"@ecopages/logger": "^0.2.3",
|
|
22
22
|
"@ecopages/scripts-injector": "^0.1.5",
|
|
23
23
|
"@oxc-project/runtime": "0.134.0",
|
|
@@ -152,6 +152,11 @@
|
|
|
152
152
|
"default": "./src/utils/hash.js",
|
|
153
153
|
"types": "./src/utils/hash.d.ts"
|
|
154
154
|
},
|
|
155
|
+
"./utils/resolve-entry-file": {
|
|
156
|
+
"import": "./src/utils/resolve-entry-file.js",
|
|
157
|
+
"types": "./src/utils/resolve-entry-file.d.ts",
|
|
158
|
+
"default": "./src/utils/resolve-entry-file.js"
|
|
159
|
+
},
|
|
155
160
|
"./errors": {
|
|
156
161
|
"types": "./src/errors/index.d.ts",
|
|
157
162
|
"default": "./src/errors/index.js"
|
|
@@ -276,6 +281,11 @@
|
|
|
276
281
|
"default": "./src/utils/hash.js",
|
|
277
282
|
"types": "./src/utils/hash.d.ts"
|
|
278
283
|
},
|
|
284
|
+
"./utils/resolve-entry-file.ts": {
|
|
285
|
+
"import": "./src/utils/resolve-entry-file.js",
|
|
286
|
+
"types": "./src/utils/resolve-entry-file.d.ts",
|
|
287
|
+
"default": "./src/utils/resolve-entry-file.js"
|
|
288
|
+
},
|
|
279
289
|
"./errors.ts": {
|
|
280
290
|
"types": "./src/errors/index.d.ts",
|
|
281
291
|
"default": "./src/errors/index.js"
|
|
@@ -18,6 +18,7 @@ export interface ServerStaticBuilderParams {
|
|
|
18
18
|
apiHandlers?: ApiHandler[];
|
|
19
19
|
logger?: ServerStaticBuilderLogger;
|
|
20
20
|
previewServerFactory?: ServerStaticPreviewServerFactory;
|
|
21
|
+
entryFile?: string;
|
|
21
22
|
}
|
|
22
23
|
/**
|
|
23
24
|
* Minimal logger dependency used by the static builder.
|
|
@@ -52,10 +53,32 @@ export declare class ServerStaticBuilder {
|
|
|
52
53
|
private readonly apiHandlers;
|
|
53
54
|
private readonly logger;
|
|
54
55
|
private readonly previewServerFactory;
|
|
55
|
-
|
|
56
|
+
private readonly entryFile;
|
|
57
|
+
constructor({ appConfig, staticSiteGenerator, serveOptions, apiHandlers, logger, previewServerFactory, entryFile, }: ServerStaticBuilderParams);
|
|
56
58
|
private warnApiHandlersUnavailableInStaticMode;
|
|
57
59
|
private prepareExportDirectory;
|
|
58
60
|
private refreshRuntimeAssets;
|
|
61
|
+
/**
|
|
62
|
+
* Bundles the server entry file for production use.
|
|
63
|
+
*
|
|
64
|
+
* @remarks
|
|
65
|
+
* When the project has API endpoints, the entry file must be bundled
|
|
66
|
+
* into a single JS file so the production server can start without
|
|
67
|
+
* on-the-fly TypeScript transpilation. The bundle is output to
|
|
68
|
+
* `dist/{SERVER_BUNDLE_DIR}/{SERVER_BUNDLE_FILENAME}` and used by
|
|
69
|
+
* `ecopages start` in production.
|
|
70
|
+
*
|
|
71
|
+
* Package imports remain external so native addons and runtime-owned
|
|
72
|
+
* dependencies continue to load through the app's installed
|
|
73
|
+
* `node_modules` tree. Only the app entry graph is bundled.
|
|
74
|
+
*
|
|
75
|
+
* Skips silently when no API endpoints are registered (static-only site).
|
|
76
|
+
* Throws if the build adapter is unavailable, is owned by a host
|
|
77
|
+
* runtime, or bundling fails.
|
|
78
|
+
*
|
|
79
|
+
* @throws If the build adapter is unavailable, is host-owned, or bundling fails.
|
|
80
|
+
*/
|
|
81
|
+
private bundleServerEntry;
|
|
59
82
|
/**
|
|
60
83
|
* Generates a static build of the site for deployment.
|
|
61
84
|
* @param options.preview - If true, starts a preview server after build
|
|
@@ -3,6 +3,8 @@ import { fileSystem } from "@ecopages/file-system";
|
|
|
3
3
|
import { DEFAULT_ECOPAGES_HOSTNAME, DEFAULT_ECOPAGES_PORT } from "../../config/constants.js";
|
|
4
4
|
import { StaticContentServer } from "../../dev/sc-server.js";
|
|
5
5
|
import { appLogger } from "../../global/app-logger.js";
|
|
6
|
+
import { getAppBuildAdapter } from "../../build/build-adapter.js";
|
|
7
|
+
import { resolveEntryFile, SERVER_BUNDLE_DIR, SERVER_BUNDLE_FILENAME } from "../../utils/resolve-entry-file.js";
|
|
6
8
|
class ServerStaticBuilder {
|
|
7
9
|
appConfig;
|
|
8
10
|
staticSiteGenerator;
|
|
@@ -10,13 +12,15 @@ class ServerStaticBuilder {
|
|
|
10
12
|
apiHandlers;
|
|
11
13
|
logger;
|
|
12
14
|
previewServerFactory;
|
|
15
|
+
entryFile;
|
|
13
16
|
constructor({
|
|
14
17
|
appConfig,
|
|
15
18
|
staticSiteGenerator,
|
|
16
19
|
serveOptions,
|
|
17
20
|
apiHandlers,
|
|
18
21
|
logger,
|
|
19
|
-
previewServerFactory
|
|
22
|
+
previewServerFactory,
|
|
23
|
+
entryFile
|
|
20
24
|
}) {
|
|
21
25
|
this.appConfig = appConfig;
|
|
22
26
|
this.staticSiteGenerator = staticSiteGenerator;
|
|
@@ -24,6 +28,7 @@ class ServerStaticBuilder {
|
|
|
24
28
|
this.apiHandlers = apiHandlers ?? [];
|
|
25
29
|
this.logger = logger ?? appLogger;
|
|
26
30
|
this.previewServerFactory = previewServerFactory ?? StaticContentServer;
|
|
31
|
+
this.entryFile = resolveEntryFile({ entryFile });
|
|
27
32
|
}
|
|
28
33
|
warnApiHandlersUnavailableInStaticMode() {
|
|
29
34
|
if (this.apiHandlers.length === 0) {
|
|
@@ -60,6 +65,62 @@ class ServerStaticBuilder {
|
|
|
60
65
|
await integration.setup();
|
|
61
66
|
}
|
|
62
67
|
}
|
|
68
|
+
/**
|
|
69
|
+
* Bundles the server entry file for production use.
|
|
70
|
+
*
|
|
71
|
+
* @remarks
|
|
72
|
+
* When the project has API endpoints, the entry file must be bundled
|
|
73
|
+
* into a single JS file so the production server can start without
|
|
74
|
+
* on-the-fly TypeScript transpilation. The bundle is output to
|
|
75
|
+
* `dist/{SERVER_BUNDLE_DIR}/{SERVER_BUNDLE_FILENAME}` and used by
|
|
76
|
+
* `ecopages start` in production.
|
|
77
|
+
*
|
|
78
|
+
* Package imports remain external so native addons and runtime-owned
|
|
79
|
+
* dependencies continue to load through the app's installed
|
|
80
|
+
* `node_modules` tree. Only the app entry graph is bundled.
|
|
81
|
+
*
|
|
82
|
+
* Skips silently when no API endpoints are registered (static-only site).
|
|
83
|
+
* Throws if the build adapter is unavailable, is owned by a host
|
|
84
|
+
* runtime, or bundling fails.
|
|
85
|
+
*
|
|
86
|
+
* @throws If the build adapter is unavailable, is host-owned, or bundling fails.
|
|
87
|
+
*/
|
|
88
|
+
async bundleServerEntry() {
|
|
89
|
+
if (this.apiHandlers.length === 0) {
|
|
90
|
+
return;
|
|
91
|
+
}
|
|
92
|
+
const buildAdapter = getAppBuildAdapter(this.appConfig);
|
|
93
|
+
if (buildAdapter.ownership === "vite-host") {
|
|
94
|
+
throw new Error(
|
|
95
|
+
'Cannot bundle the server entry file: build ownership is "vite-host". The host runtime is expected to produce its own server bundle.'
|
|
96
|
+
);
|
|
97
|
+
}
|
|
98
|
+
const entryPath = path.isAbsolute(this.entryFile) ? this.entryFile : path.join(this.appConfig.rootDir, this.entryFile);
|
|
99
|
+
if (!fileSystem.exists(entryPath)) {
|
|
100
|
+
throw new Error(
|
|
101
|
+
`Cannot bundle server entry: file "${this.entryFile}" not found in "${this.appConfig.rootDir}".`
|
|
102
|
+
);
|
|
103
|
+
}
|
|
104
|
+
const distDir = this.appConfig.absolutePaths?.distDir ?? path.join(this.appConfig.rootDir, this.appConfig.distDir);
|
|
105
|
+
const serverOutdir = path.join(distDir, SERVER_BUNDLE_DIR);
|
|
106
|
+
this.logger.info("Bundling server entry file...");
|
|
107
|
+
const result = await buildAdapter.build({
|
|
108
|
+
entrypoints: [entryPath],
|
|
109
|
+
outdir: serverOutdir,
|
|
110
|
+
naming: SERVER_BUNDLE_FILENAME,
|
|
111
|
+
target: "node",
|
|
112
|
+
format: "esm",
|
|
113
|
+
sourcemap: "hidden",
|
|
114
|
+
externalPackages: true,
|
|
115
|
+
root: this.appConfig.rootDir
|
|
116
|
+
});
|
|
117
|
+
if (!result.success) {
|
|
118
|
+
const errorMessages = result.logs.map((log) => log.message).join("\n");
|
|
119
|
+
throw new Error(`Failed to bundle server entry file:
|
|
120
|
+
${errorMessages}`);
|
|
121
|
+
}
|
|
122
|
+
this.logger.info("Server entry file bundled successfully");
|
|
123
|
+
}
|
|
63
124
|
/**
|
|
64
125
|
* Generates a static build of the site for deployment.
|
|
65
126
|
* @param options.preview - If true, starts a preview server after build
|
|
@@ -73,6 +134,7 @@ class ServerStaticBuilder {
|
|
|
73
134
|
this.warnApiHandlersUnavailableInStaticMode();
|
|
74
135
|
this.prepareExportDirectory();
|
|
75
136
|
await this.refreshRuntimeAssets();
|
|
137
|
+
await this.bundleServerEntry();
|
|
76
138
|
await this.staticSiteGenerator.run({
|
|
77
139
|
router: dependencies.router,
|
|
78
140
|
baseUrl,
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The default entry file name when no other source provides a value.
|
|
3
|
+
*/
|
|
4
|
+
export declare const DEFAULT_ENTRY_FILE = "app.ts";
|
|
5
|
+
/**
|
|
6
|
+
* The environment variable used to override the entry file path.
|
|
7
|
+
*/
|
|
8
|
+
export declare const ENTRY_FILE_ENV = "ECOPAGES_ENTRY_FILE";
|
|
9
|
+
/**
|
|
10
|
+
* Centralized definition of the entry-file CLI argument.
|
|
11
|
+
*
|
|
12
|
+
* Exported so callers (CLI, tests, docs) reference a single source of truth
|
|
13
|
+
* for the flag name, short alias, and type. Keep this in sync with
|
|
14
|
+
* `resolveEntryFile` below.
|
|
15
|
+
*/
|
|
16
|
+
export declare const entryFileOptions: {
|
|
17
|
+
readonly 'entry-file': {
|
|
18
|
+
readonly type: "string";
|
|
19
|
+
readonly short: "e";
|
|
20
|
+
};
|
|
21
|
+
};
|
|
22
|
+
/**
|
|
23
|
+
* The long flag name for the entry file option. Re-exported as a
|
|
24
|
+
* constant so consumers don't hardcode the string.
|
|
25
|
+
*/
|
|
26
|
+
export declare const ENTRY_FILE_FLAG = "entry-file";
|
|
27
|
+
/**
|
|
28
|
+
* Resolved sources for the entry file, in precedence order (highest first):
|
|
29
|
+
*
|
|
30
|
+
* 1. `entryFile` argument — explicit caller override (e.g. constructor)
|
|
31
|
+
* 2. `ECOPAGES_ENTRY_FILE` env var — set by the CLI when spawning
|
|
32
|
+
* 3. `--entry-file` / `-e` CLI flag — read from `process.argv` via `parseArgs`
|
|
33
|
+
* 4. `app.ts` — the framework default
|
|
34
|
+
*/
|
|
35
|
+
export interface ResolveEntryFileOptions {
|
|
36
|
+
/** Explicit override (e.g. constructor param or test fixture). */
|
|
37
|
+
entryFile?: string;
|
|
38
|
+
/** Environment to read `ECOPAGES_ENTRY_FILE` from. Defaults to `process.env`. */
|
|
39
|
+
env?: Record<string, string | undefined>;
|
|
40
|
+
/** Argv to parse. Defaults to `process.argv`. */
|
|
41
|
+
argv?: string[];
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* Resolves the entry file path using standard precedence.
|
|
45
|
+
*
|
|
46
|
+
* This is the single source of truth for entry-file resolution across
|
|
47
|
+
* the CLI, build pipeline, and runtime. Both the CLI layer and the
|
|
48
|
+
* runtime builders call into this function instead of reimplementing
|
|
49
|
+
* the precedence chain.
|
|
50
|
+
*
|
|
51
|
+
* @example
|
|
52
|
+
* resolveEntryFile(); // 'app.ts' (or env/argv)
|
|
53
|
+
* resolveEntryFile({ entryFile: 'src/server.ts' }); // 'src/server.ts'
|
|
54
|
+
* resolveEntryFile({ env: { ECOPAGES_ENTRY_FILE: 'x' }}); // 'x'
|
|
55
|
+
* resolveEntryFile({ argv: ['node', '-e', 'flag.ts'] }); // 'flag.ts'
|
|
56
|
+
*/
|
|
57
|
+
export declare function resolveEntryFile({ entryFile, env, argv, }?: ResolveEntryFileOptions): string;
|
|
58
|
+
/**
|
|
59
|
+
* Parses argv for the entry-file flag using the shared `parseArgs` config.
|
|
60
|
+
*
|
|
61
|
+
* Exposed for callers that need the raw flag value without the full
|
|
62
|
+
* precedence chain (e.g. CLI help text, validation).
|
|
63
|
+
*/
|
|
64
|
+
export declare function readEntryFileFlag(argv?: string[]): string | undefined;
|
|
65
|
+
/**
|
|
66
|
+
* The subdirectory inside `dist/` where the bundled server entry is placed.
|
|
67
|
+
*/
|
|
68
|
+
export declare const SERVER_BUNDLE_DIR = ".server";
|
|
69
|
+
/**
|
|
70
|
+
* The filename of the bundled server entry.
|
|
71
|
+
*/
|
|
72
|
+
export declare const SERVER_BUNDLE_FILENAME = "app.mjs";
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import { parseArgs } from "node:util";
|
|
2
|
+
import { getRuntimeArgv } from "./runtime.js";
|
|
3
|
+
const DEFAULT_ENTRY_FILE = "app.ts";
|
|
4
|
+
const ENTRY_FILE_ENV = "ECOPAGES_ENTRY_FILE";
|
|
5
|
+
const entryFileOptions = {
|
|
6
|
+
"entry-file": { type: "string", short: "e" }
|
|
7
|
+
};
|
|
8
|
+
const ENTRY_FILE_FLAG = "entry-file";
|
|
9
|
+
function resolveEntryFile({
|
|
10
|
+
entryFile,
|
|
11
|
+
env = process.env,
|
|
12
|
+
argv = getRuntimeArgv()
|
|
13
|
+
} = {}) {
|
|
14
|
+
if (entryFile && entryFile.length > 0) return entryFile;
|
|
15
|
+
const fromEnv = env[ENTRY_FILE_ENV];
|
|
16
|
+
if (fromEnv && fromEnv.length > 0) return fromEnv;
|
|
17
|
+
const fromFlag = readEntryFileFlag(argv);
|
|
18
|
+
if (fromFlag) return fromFlag;
|
|
19
|
+
return DEFAULT_ENTRY_FILE;
|
|
20
|
+
}
|
|
21
|
+
function readEntryFileFlag(argv = getRuntimeArgv()) {
|
|
22
|
+
const { values } = parseArgs({
|
|
23
|
+
args: argv.slice(1),
|
|
24
|
+
options: { ...entryFileOptions },
|
|
25
|
+
strict: false
|
|
26
|
+
});
|
|
27
|
+
const raw = values[ENTRY_FILE_FLAG];
|
|
28
|
+
return typeof raw === "string" && raw.length > 0 ? raw : void 0;
|
|
29
|
+
}
|
|
30
|
+
const SERVER_BUNDLE_DIR = ".server";
|
|
31
|
+
const SERVER_BUNDLE_FILENAME = "app.mjs";
|
|
32
|
+
export {
|
|
33
|
+
DEFAULT_ENTRY_FILE,
|
|
34
|
+
ENTRY_FILE_ENV,
|
|
35
|
+
ENTRY_FILE_FLAG,
|
|
36
|
+
SERVER_BUNDLE_DIR,
|
|
37
|
+
SERVER_BUNDLE_FILENAME,
|
|
38
|
+
entryFileOptions,
|
|
39
|
+
readEntryFileFlag,
|
|
40
|
+
resolveEntryFile
|
|
41
|
+
};
|