@xfe-repo/web-app 2.0.0-beta.1 → 2.0.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/dist/config.d.mts CHANGED
@@ -1,9 +1,13 @@
1
1
  type WebAppConfig = {
2
2
  basename?: string;
3
+ /** 相对于项目根目录的输出路径,默认 dist;旧项目可显式设置 build。 */
4
+ buildDirectory?: string;
3
5
  host?: string;
4
6
  port?: number;
5
7
  /** 渲染方式由 XFE 转换为底层框架配置;省略时兼容已有服务端应用。 */
6
8
  renderMode?: 'client' | 'server';
9
+ /** 项目内默认导出 Node 工厂的源码入口;省略时保留原有 SPA / SSR 启动。 */
10
+ serverEntry?: string;
7
11
  title?: string;
8
12
  lang?: string;
9
13
  themeColor?: string;
@@ -16,8 +20,14 @@ type JwtEnv = {
16
20
  XFE_JWT_KEY_ID: string;
17
21
  XFE_JWT_PRIVATE_KEY: string;
18
22
  };
23
+ /** 读取业务配置,并在构建或启动前拒绝无效的渲染方式和输出路径。 */
19
24
  declare const loadXfeConfig: (root?: string) => XfeConfig;
25
+ /** 返回构建和启动共用的绝对路径,防止清空项目根目录或框架输入目录。 */
26
+ declare const getBuildDirectory: (projectRoot: string, webapp?: WebAppConfig) => string;
27
+ /** 生产仅校验入口约定而不读取源码,构建阶段由编译器检查文件存在。 */
28
+ declare const getServerEntry: (projectRoot: string, webapp?: WebAppConfig) => string | undefined;
29
+ /** 将输出目录传给 Docker 构建,并沿用现有的集群、域名与 Helm 配置。 */
20
30
  declare const configDeploy: () => void;
21
31
  declare const getJwtEnv: () => Promise<Partial<JwtEnv>>;
22
32
 
23
- export { type JwtEnv, type WebAppConfig, type XfeConfig, configDeploy, getJwtEnv, loadXfeConfig };
33
+ export { type JwtEnv, type WebAppConfig, type XfeConfig, configDeploy, getBuildDirectory, getJwtEnv, getServerEntry, loadXfeConfig };
package/dist/config.d.ts CHANGED
@@ -1,9 +1,13 @@
1
1
  type WebAppConfig = {
2
2
  basename?: string;
3
+ /** 相对于项目根目录的输出路径,默认 dist;旧项目可显式设置 build。 */
4
+ buildDirectory?: string;
3
5
  host?: string;
4
6
  port?: number;
5
7
  /** 渲染方式由 XFE 转换为底层框架配置;省略时兼容已有服务端应用。 */
6
8
  renderMode?: 'client' | 'server';
9
+ /** 项目内默认导出 Node 工厂的源码入口;省略时保留原有 SPA / SSR 启动。 */
10
+ serverEntry?: string;
7
11
  title?: string;
8
12
  lang?: string;
9
13
  themeColor?: string;
@@ -16,8 +20,14 @@ type JwtEnv = {
16
20
  XFE_JWT_KEY_ID: string;
17
21
  XFE_JWT_PRIVATE_KEY: string;
18
22
  };
23
+ /** 读取业务配置,并在构建或启动前拒绝无效的渲染方式和输出路径。 */
19
24
  declare const loadXfeConfig: (root?: string) => XfeConfig;
25
+ /** 返回构建和启动共用的绝对路径,防止清空项目根目录或框架输入目录。 */
26
+ declare const getBuildDirectory: (projectRoot: string, webapp?: WebAppConfig) => string;
27
+ /** 生产仅校验入口约定而不读取源码,构建阶段由编译器检查文件存在。 */
28
+ declare const getServerEntry: (projectRoot: string, webapp?: WebAppConfig) => string | undefined;
29
+ /** 将输出目录传给 Docker 构建,并沿用现有的集群、域名与 Helm 配置。 */
20
30
  declare const configDeploy: () => void;
21
31
  declare const getJwtEnv: () => Promise<Partial<JwtEnv>>;
22
32
 
23
- export { type JwtEnv, type WebAppConfig, type XfeConfig, configDeploy, getJwtEnv, loadXfeConfig };
33
+ export { type JwtEnv, type WebAppConfig, type XfeConfig, configDeploy, getBuildDirectory, getJwtEnv, getServerEntry, loadXfeConfig };
package/dist/config.js CHANGED
@@ -31,33 +31,89 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
31
31
  var config_exports = {};
32
32
  __export(config_exports, {
33
33
  configDeploy: () => configDeploy,
34
+ getBuildDirectory: () => getBuildDirectory,
34
35
  getJwtEnv: () => getJwtEnv,
36
+ getServerEntry: () => getServerEntry,
35
37
  loadXfeConfig: () => loadXfeConfig
36
38
  });
37
39
  module.exports = __toCommonJS(config_exports);
38
- var import_path = __toESM(require("path"));
39
- var import_fs = __toESM(require("fs"));
40
+ var import_node_fs = __toESM(require("fs"));
41
+ var import_node_path = __toESM(require("path"));
42
+ var import_node_url = require("url");
40
43
  var import_yaml = __toESM(require("yaml"));
44
+ var import_meta = {};
45
+ var DEFAULT_BUILD_DIRECTORY = "dist";
46
+ var BUILD_DIRECTORY_ARGUMENT = "XFE_BUILD_DIRECTORY";
47
+ var protectedDirectories = /* @__PURE__ */ new Set(["src", "types", "public", "node_modules", ".git"]);
48
+ var configDirectory = typeof __dirname === "string" ? __dirname : import_node_path.default.dirname((0, import_node_url.fileURLToPath)(import_meta.url));
41
49
  var loadXfeConfig = (root = process.cwd()) => {
42
- const xfeConfigPath = import_path.default.join(root, "xfe.json");
43
- if (!import_fs.default.existsSync(xfeConfigPath)) {
50
+ const xfeConfigPath = import_node_path.default.join(root, "xfe.json");
51
+ if (!import_node_fs.default.existsSync(xfeConfigPath)) {
44
52
  throw new Error("xfe.json \u914D\u7F6E\u6587\u4EF6\u4E0D\u5B58\u5728\uFF0C\u8BF7\u5728\u9879\u76EE\u6839\u76EE\u5F55\u4E0B\u521B\u5EFA\u8BE5\u6587\u4EF6\u3002");
45
53
  }
46
- const xfeConfigContent = import_fs.default.readFileSync(xfeConfigPath, "utf8");
54
+ const xfeConfigContent = import_node_fs.default.readFileSync(xfeConfigPath, "utf8");
47
55
  const config = JSON.parse(xfeConfigContent);
48
56
  const renderMode = config.webapp?.renderMode;
49
57
  if (renderMode !== void 0 && renderMode !== "client" && renderMode !== "server") {
50
58
  throw new Error("xfe.json.webapp.renderMode \u53EA\u80FD\u4E3A client \u6216 server");
51
59
  }
60
+ getBuildDirectory(root, config.webapp);
61
+ getServerEntry(root, config.webapp);
52
62
  return config;
53
63
  };
64
+ var getBuildDirectory = (projectRoot, webapp = {}) => {
65
+ const configured = webapp?.buildDirectory === void 0 ? DEFAULT_BUILD_DIRECTORY : webapp.buildDirectory;
66
+ if (typeof configured !== "string" || !configured.trim() || /[\0\r\n]/.test(configured)) {
67
+ throw new Error("xfe.json.webapp.buildDirectory \u5FC5\u987B\u662F\u975E\u7A7A\u7684\u5355\u884C\u5B57\u7B26\u4E32");
68
+ }
69
+ const directory = configured.trim().replaceAll("\\", "/");
70
+ const root = import_node_path.default.resolve(projectRoot);
71
+ const output = import_node_path.default.resolve(root, directory);
72
+ const relative = import_node_path.default.relative(root, output);
73
+ const [firstDirectory = ""] = relative.split(import_node_path.default.sep);
74
+ const escapesProject = import_node_path.default.isAbsolute(directory) || Boolean(import_node_path.default.win32.parse(directory).root) || firstDirectory === "..";
75
+ const overwritesInput = !relative || protectedDirectories.has(firstDirectory.toLowerCase());
76
+ if (escapesProject || overwritesInput) {
77
+ throw new Error("xfe.json.webapp.buildDirectory \u5FC5\u987B\u662F\u9879\u76EE\u5185\u72EC\u7ACB\u7684\u8F93\u51FA\u76EE\u5F55\uFF0C\u4E0D\u80FD\u8986\u76D6\u9879\u76EE\u3001\u6E90\u7801\u6216\u4F9D\u8D56");
78
+ }
79
+ let currentDirectory = root;
80
+ for (const segment of relative.split(import_node_path.default.sep)) {
81
+ currentDirectory = import_node_path.default.join(currentDirectory, segment);
82
+ if (import_node_fs.default.lstatSync(currentDirectory, { throwIfNoEntry: false })?.isSymbolicLink()) {
83
+ throw new Error("xfe.json.webapp.buildDirectory \u4E0D\u80FD\u7ECF\u8FC7\u7B26\u53F7\u94FE\u63A5\uFF0C\u4EE5\u514D\u6E05\u7406\u5176\u4ED6\u76EE\u5F55");
84
+ }
85
+ }
86
+ return output;
87
+ };
88
+ var getServerEntry = (projectRoot, webapp = {}) => {
89
+ const entry = webapp?.serverEntry;
90
+ if (entry === void 0) return;
91
+ if (typeof entry !== "string" || !entry.trim() || /[\0\r\n]/.test(entry)) {
92
+ throw new Error("xfe.json.webapp.serverEntry \u5FC5\u987B\u662F\u9879\u76EE\u5185\u7684 TypeScript / JavaScript \u76F8\u5BF9\u8DEF\u5F84");
93
+ }
94
+ const normalized = entry.trim().replaceAll("\\", "/");
95
+ const absolute = import_node_path.default.resolve(projectRoot, normalized);
96
+ const relative = import_node_path.default.relative(import_node_path.default.resolve(projectRoot), absolute);
97
+ const outside = Boolean(import_node_path.default.win32.parse(normalized).root) || relative.split(import_node_path.default.sep)[0] === "..";
98
+ if (outside || !/\.(?:[jt]sx?|m[jt]s)$/.test(relative)) {
99
+ throw new Error("xfe.json.webapp.serverEntry \u5FC5\u987B\u662F\u9879\u76EE\u5185\u7684 TypeScript / JavaScript \u76F8\u5BF9\u8DEF\u5F84");
100
+ }
101
+ return absolute;
102
+ };
54
103
  var configDeploy = () => {
55
104
  const xfeConfig = loadXfeConfig();
56
105
  if (!xfeConfig?.deploy) return;
57
- const droneConfigFilePath = import_path.default.resolve(__dirname, "../deploy/.drone.yml");
58
- import_fs.default.cpSync(droneConfigFilePath, xfeConfig.deploy.droneConfigPath || "./.drone.yml");
59
- const valuesYamlFilePath = import_path.default.resolve(__dirname, "../deploy/helm/values.yaml");
60
- const valuesYamlFile = import_fs.default.readFileSync(valuesYamlFilePath, "utf8");
106
+ const projectRoot = process.cwd();
107
+ const buildDirectory = import_node_path.default.relative(projectRoot, getBuildDirectory(projectRoot, xfeConfig.webapp)).replaceAll(import_node_path.default.sep, "/");
108
+ const droneConfigFilePath = import_node_path.default.resolve(configDirectory, "../deploy/.drone.yml");
109
+ const droneTarget = import_node_path.default.resolve(projectRoot, xfeConfig.deploy.droneConfigPath || "./.drone.yml");
110
+ const defaultArgument = JSON.stringify(`${BUILD_DIRECTORY_ARGUMENT}=${DEFAULT_BUILD_DIRECTORY}`);
111
+ const configuredArgument = JSON.stringify(`${BUILD_DIRECTORY_ARGUMENT}=${buildDirectory}`);
112
+ const droneConfig = import_node_fs.default.readFileSync(droneConfigFilePath, "utf8").replace(defaultArgument, configuredArgument);
113
+ import_node_fs.default.mkdirSync(import_node_path.default.dirname(droneTarget), { recursive: true });
114
+ import_node_fs.default.writeFileSync(droneTarget, droneConfig);
115
+ const valuesYamlFilePath = import_node_path.default.resolve(configDirectory, "../deploy/helm/values.yaml");
116
+ const valuesYamlFile = import_node_fs.default.readFileSync(valuesYamlFilePath, "utf8");
61
117
  const valuesYaml = import_yaml.default.parse(valuesYamlFile);
62
118
  if (!valuesYaml.virtualService) valuesYaml.virtualService = {};
63
119
  if (!valuesYaml.virtualService.hosts) valuesYaml.virtualService.hosts = { test: [], stage: [], prod: [] };
@@ -77,7 +133,7 @@ var configDeploy = () => {
77
133
  valuesYaml.targetK8s = targetK8s;
78
134
  valuesYaml.namespace = targetNamespace;
79
135
  const valuesYamlStr = import_yaml.default.stringify(valuesYaml, { defaultStringType: "QUOTE_SINGLE", defaultKeyType: "PLAIN" });
80
- import_fs.default.writeFileSync(valuesYamlFilePath, valuesYamlStr, "utf8");
136
+ import_node_fs.default.writeFileSync(valuesYamlFilePath, valuesYamlStr, "utf8");
81
137
  };
82
138
  var getJwtEnv = async () => {
83
139
  const xfeConfig = loadXfeConfig();
@@ -101,6 +157,8 @@ var getJwtEnv = async () => {
101
157
  // Annotate the CommonJS export names for ESM import in node:
102
158
  0 && (module.exports = {
103
159
  configDeploy,
160
+ getBuildDirectory,
104
161
  getJwtEnv,
162
+ getServerEntry,
105
163
  loadXfeConfig
106
164
  });
package/dist/config.mjs CHANGED
@@ -1,7 +1,12 @@
1
1
  // src/config.ts
2
- import path from "path";
3
2
  import fs from "fs";
3
+ import path from "path";
4
+ import { fileURLToPath } from "url";
4
5
  import yaml from "yaml";
6
+ var DEFAULT_BUILD_DIRECTORY = "dist";
7
+ var BUILD_DIRECTORY_ARGUMENT = "XFE_BUILD_DIRECTORY";
8
+ var protectedDirectories = /* @__PURE__ */ new Set(["src", "types", "public", "node_modules", ".git"]);
9
+ var configDirectory = typeof __dirname === "string" ? __dirname : path.dirname(fileURLToPath(import.meta.url));
5
10
  var loadXfeConfig = (root = process.cwd()) => {
6
11
  const xfeConfigPath = path.join(root, "xfe.json");
7
12
  if (!fs.existsSync(xfeConfigPath)) {
@@ -13,14 +18,62 @@ var loadXfeConfig = (root = process.cwd()) => {
13
18
  if (renderMode !== void 0 && renderMode !== "client" && renderMode !== "server") {
14
19
  throw new Error("xfe.json.webapp.renderMode \u53EA\u80FD\u4E3A client \u6216 server");
15
20
  }
21
+ getBuildDirectory(root, config.webapp);
22
+ getServerEntry(root, config.webapp);
16
23
  return config;
17
24
  };
25
+ var getBuildDirectory = (projectRoot, webapp = {}) => {
26
+ const configured = webapp?.buildDirectory === void 0 ? DEFAULT_BUILD_DIRECTORY : webapp.buildDirectory;
27
+ if (typeof configured !== "string" || !configured.trim() || /[\0\r\n]/.test(configured)) {
28
+ throw new Error("xfe.json.webapp.buildDirectory \u5FC5\u987B\u662F\u975E\u7A7A\u7684\u5355\u884C\u5B57\u7B26\u4E32");
29
+ }
30
+ const directory = configured.trim().replaceAll("\\", "/");
31
+ const root = path.resolve(projectRoot);
32
+ const output = path.resolve(root, directory);
33
+ const relative = path.relative(root, output);
34
+ const [firstDirectory = ""] = relative.split(path.sep);
35
+ const escapesProject = path.isAbsolute(directory) || Boolean(path.win32.parse(directory).root) || firstDirectory === "..";
36
+ const overwritesInput = !relative || protectedDirectories.has(firstDirectory.toLowerCase());
37
+ if (escapesProject || overwritesInput) {
38
+ throw new Error("xfe.json.webapp.buildDirectory \u5FC5\u987B\u662F\u9879\u76EE\u5185\u72EC\u7ACB\u7684\u8F93\u51FA\u76EE\u5F55\uFF0C\u4E0D\u80FD\u8986\u76D6\u9879\u76EE\u3001\u6E90\u7801\u6216\u4F9D\u8D56");
39
+ }
40
+ let currentDirectory = root;
41
+ for (const segment of relative.split(path.sep)) {
42
+ currentDirectory = path.join(currentDirectory, segment);
43
+ if (fs.lstatSync(currentDirectory, { throwIfNoEntry: false })?.isSymbolicLink()) {
44
+ throw new Error("xfe.json.webapp.buildDirectory \u4E0D\u80FD\u7ECF\u8FC7\u7B26\u53F7\u94FE\u63A5\uFF0C\u4EE5\u514D\u6E05\u7406\u5176\u4ED6\u76EE\u5F55");
45
+ }
46
+ }
47
+ return output;
48
+ };
49
+ var getServerEntry = (projectRoot, webapp = {}) => {
50
+ const entry = webapp?.serverEntry;
51
+ if (entry === void 0) return;
52
+ if (typeof entry !== "string" || !entry.trim() || /[\0\r\n]/.test(entry)) {
53
+ throw new Error("xfe.json.webapp.serverEntry \u5FC5\u987B\u662F\u9879\u76EE\u5185\u7684 TypeScript / JavaScript \u76F8\u5BF9\u8DEF\u5F84");
54
+ }
55
+ const normalized = entry.trim().replaceAll("\\", "/");
56
+ const absolute = path.resolve(projectRoot, normalized);
57
+ const relative = path.relative(path.resolve(projectRoot), absolute);
58
+ const outside = Boolean(path.win32.parse(normalized).root) || relative.split(path.sep)[0] === "..";
59
+ if (outside || !/\.(?:[jt]sx?|m[jt]s)$/.test(relative)) {
60
+ throw new Error("xfe.json.webapp.serverEntry \u5FC5\u987B\u662F\u9879\u76EE\u5185\u7684 TypeScript / JavaScript \u76F8\u5BF9\u8DEF\u5F84");
61
+ }
62
+ return absolute;
63
+ };
18
64
  var configDeploy = () => {
19
65
  const xfeConfig = loadXfeConfig();
20
66
  if (!xfeConfig?.deploy) return;
21
- const droneConfigFilePath = path.resolve(__dirname, "../deploy/.drone.yml");
22
- fs.cpSync(droneConfigFilePath, xfeConfig.deploy.droneConfigPath || "./.drone.yml");
23
- const valuesYamlFilePath = path.resolve(__dirname, "../deploy/helm/values.yaml");
67
+ const projectRoot = process.cwd();
68
+ const buildDirectory = path.relative(projectRoot, getBuildDirectory(projectRoot, xfeConfig.webapp)).replaceAll(path.sep, "/");
69
+ const droneConfigFilePath = path.resolve(configDirectory, "../deploy/.drone.yml");
70
+ const droneTarget = path.resolve(projectRoot, xfeConfig.deploy.droneConfigPath || "./.drone.yml");
71
+ const defaultArgument = JSON.stringify(`${BUILD_DIRECTORY_ARGUMENT}=${DEFAULT_BUILD_DIRECTORY}`);
72
+ const configuredArgument = JSON.stringify(`${BUILD_DIRECTORY_ARGUMENT}=${buildDirectory}`);
73
+ const droneConfig = fs.readFileSync(droneConfigFilePath, "utf8").replace(defaultArgument, configuredArgument);
74
+ fs.mkdirSync(path.dirname(droneTarget), { recursive: true });
75
+ fs.writeFileSync(droneTarget, droneConfig);
76
+ const valuesYamlFilePath = path.resolve(configDirectory, "../deploy/helm/values.yaml");
24
77
  const valuesYamlFile = fs.readFileSync(valuesYamlFilePath, "utf8");
25
78
  const valuesYaml = yaml.parse(valuesYamlFile);
26
79
  if (!valuesYaml.virtualService) valuesYaml.virtualService = {};
@@ -64,6 +117,8 @@ var getJwtEnv = async () => {
64
117
  };
65
118
  export {
66
119
  configDeploy,
120
+ getBuildDirectory,
67
121
  getJwtEnv,
122
+ getServerEntry,
68
123
  loadXfeConfig
69
124
  };
package/dist/framework.js CHANGED
@@ -34,49 +34,95 @@ __export(framework_exports, {
34
34
  prepareFramework: () => prepareFramework
35
35
  });
36
36
  module.exports = __toCommonJS(framework_exports);
37
- var import_node_fs = __toESM(require("fs"));
37
+ var import_node_fs2 = __toESM(require("fs"));
38
38
  var import_node_module = require("module");
39
- var import_node_path = __toESM(require("path"));
40
- var import_node_url = require("url");
39
+ var import_node_path2 = __toESM(require("path"));
40
+ var import_node_url2 = require("url");
41
41
 
42
42
  // src/config.ts
43
- var import_path = __toESM(require("path"));
44
- var import_fs = __toESM(require("fs"));
43
+ var import_node_fs = __toESM(require("fs"));
44
+ var import_node_path = __toESM(require("path"));
45
+ var import_node_url = require("url");
45
46
  var import_yaml = __toESM(require("yaml"));
47
+ var import_meta = {};
48
+ var DEFAULT_BUILD_DIRECTORY = "dist";
49
+ var protectedDirectories = /* @__PURE__ */ new Set(["src", "types", "public", "node_modules", ".git"]);
50
+ var configDirectory = typeof __dirname === "string" ? __dirname : import_node_path.default.dirname((0, import_node_url.fileURLToPath)(import_meta.url));
46
51
  var loadXfeConfig = (root = process.cwd()) => {
47
- const xfeConfigPath = import_path.default.join(root, "xfe.json");
48
- if (!import_fs.default.existsSync(xfeConfigPath)) {
52
+ const xfeConfigPath = import_node_path.default.join(root, "xfe.json");
53
+ if (!import_node_fs.default.existsSync(xfeConfigPath)) {
49
54
  throw new Error("xfe.json \u914D\u7F6E\u6587\u4EF6\u4E0D\u5B58\u5728\uFF0C\u8BF7\u5728\u9879\u76EE\u6839\u76EE\u5F55\u4E0B\u521B\u5EFA\u8BE5\u6587\u4EF6\u3002");
50
55
  }
51
- const xfeConfigContent = import_fs.default.readFileSync(xfeConfigPath, "utf8");
56
+ const xfeConfigContent = import_node_fs.default.readFileSync(xfeConfigPath, "utf8");
52
57
  const config = JSON.parse(xfeConfigContent);
53
58
  const renderMode = config.webapp?.renderMode;
54
59
  if (renderMode !== void 0 && renderMode !== "client" && renderMode !== "server") {
55
60
  throw new Error("xfe.json.webapp.renderMode \u53EA\u80FD\u4E3A client \u6216 server");
56
61
  }
62
+ getBuildDirectory(root, config.webapp);
63
+ getServerEntry(root, config.webapp);
57
64
  return config;
58
65
  };
66
+ var getBuildDirectory = (projectRoot, webapp = {}) => {
67
+ const configured = webapp?.buildDirectory === void 0 ? DEFAULT_BUILD_DIRECTORY : webapp.buildDirectory;
68
+ if (typeof configured !== "string" || !configured.trim() || /[\0\r\n]/.test(configured)) {
69
+ throw new Error("xfe.json.webapp.buildDirectory \u5FC5\u987B\u662F\u975E\u7A7A\u7684\u5355\u884C\u5B57\u7B26\u4E32");
70
+ }
71
+ const directory = configured.trim().replaceAll("\\", "/");
72
+ const root = import_node_path.default.resolve(projectRoot);
73
+ const output = import_node_path.default.resolve(root, directory);
74
+ const relative = import_node_path.default.relative(root, output);
75
+ const [firstDirectory = ""] = relative.split(import_node_path.default.sep);
76
+ const escapesProject = import_node_path.default.isAbsolute(directory) || Boolean(import_node_path.default.win32.parse(directory).root) || firstDirectory === "..";
77
+ const overwritesInput = !relative || protectedDirectories.has(firstDirectory.toLowerCase());
78
+ if (escapesProject || overwritesInput) {
79
+ throw new Error("xfe.json.webapp.buildDirectory \u5FC5\u987B\u662F\u9879\u76EE\u5185\u72EC\u7ACB\u7684\u8F93\u51FA\u76EE\u5F55\uFF0C\u4E0D\u80FD\u8986\u76D6\u9879\u76EE\u3001\u6E90\u7801\u6216\u4F9D\u8D56");
80
+ }
81
+ let currentDirectory = root;
82
+ for (const segment of relative.split(import_node_path.default.sep)) {
83
+ currentDirectory = import_node_path.default.join(currentDirectory, segment);
84
+ if (import_node_fs.default.lstatSync(currentDirectory, { throwIfNoEntry: false })?.isSymbolicLink()) {
85
+ throw new Error("xfe.json.webapp.buildDirectory \u4E0D\u80FD\u7ECF\u8FC7\u7B26\u53F7\u94FE\u63A5\uFF0C\u4EE5\u514D\u6E05\u7406\u5176\u4ED6\u76EE\u5F55");
86
+ }
87
+ }
88
+ return output;
89
+ };
90
+ var getServerEntry = (projectRoot, webapp = {}) => {
91
+ const entry = webapp?.serverEntry;
92
+ if (entry === void 0) return;
93
+ if (typeof entry !== "string" || !entry.trim() || /[\0\r\n]/.test(entry)) {
94
+ throw new Error("xfe.json.webapp.serverEntry \u5FC5\u987B\u662F\u9879\u76EE\u5185\u7684 TypeScript / JavaScript \u76F8\u5BF9\u8DEF\u5F84");
95
+ }
96
+ const normalized = entry.trim().replaceAll("\\", "/");
97
+ const absolute = import_node_path.default.resolve(projectRoot, normalized);
98
+ const relative = import_node_path.default.relative(import_node_path.default.resolve(projectRoot), absolute);
99
+ const outside = Boolean(import_node_path.default.win32.parse(normalized).root) || relative.split(import_node_path.default.sep)[0] === "..";
100
+ if (outside || !/\.(?:[jt]sx?|m[jt]s)$/.test(relative)) {
101
+ throw new Error("xfe.json.webapp.serverEntry \u5FC5\u987B\u662F\u9879\u76EE\u5185\u7684 TypeScript / JavaScript \u76F8\u5BF9\u8DEF\u5F84");
102
+ }
103
+ return absolute;
104
+ };
59
105
 
60
106
  // src/framework.ts
61
- var import_meta = {};
62
- var frameworkDirectory = typeof __dirname === "string" ? __dirname : import_node_path.default.dirname((0, import_node_url.fileURLToPath)(import_meta.url));
63
- var requireFromFramework = (0, import_node_module.createRequire)(import_node_path.default.join(frameworkDirectory, "framework.js"));
107
+ var import_meta2 = {};
108
+ var frameworkDirectory = typeof __dirname === "string" ? __dirname : import_node_path2.default.dirname((0, import_node_url2.fileURLToPath)(import_meta2.url));
109
+ var requireFromFramework = (0, import_node_module.createRequire)(import_node_path2.default.join(frameworkDirectory, "framework.js"));
64
110
  var extensions = [".tsx", ".ts", ".jsx", ".js"];
65
- var findSource = (directory, name) => extensions.map((extension) => import_node_path.default.join(directory, `${name}${extension}`)).find(import_node_fs.default.existsSync);
66
- var findValueExports = (file) => [...import_node_fs.default.readFileSync(file, "utf8").matchAll(/^\s*export\s+(?:async\s+)?(?:const|let|var|function|class)\s+([A-Za-z_$][\w$]*)/gm)].map(
111
+ var findSource = (directory, name) => extensions.map((extension) => import_node_path2.default.join(directory, `${name}${extension}`)).find(import_node_fs2.default.existsSync);
112
+ var findValueExports = (file) => [...import_node_fs2.default.readFileSync(file, "utf8").matchAll(/^\s*export\s+(?:async\s+)?(?:const|let|var|function|class)\s+([A-Za-z_$][\w$]*)/gm)].map(
67
113
  ([, name]) => name
68
114
  );
69
115
  var importPath = (from, target) => {
70
- const relative = import_node_path.default.relative(from, target).replace(import_node_path.default.extname(target), "").replaceAll(import_node_path.default.sep, "/");
116
+ const relative = import_node_path2.default.relative(from, target).replace(import_node_path2.default.extname(target), "").replaceAll(import_node_path2.default.sep, "/");
71
117
  return relative.startsWith(".") ? relative : `./${relative}`;
72
118
  };
73
119
  var writeGenerated = (file, content) => {
74
- if (import_node_fs.default.existsSync(file) && import_node_fs.default.readFileSync(file, "utf8") === content) return;
75
- import_node_fs.default.writeFileSync(file, content);
120
+ if (import_node_fs2.default.existsSync(file) && import_node_fs2.default.readFileSync(file, "utf8") === content) return;
121
+ import_node_fs2.default.writeFileSync(file, content);
76
122
  };
77
123
  var defaultRootSource = (projectRoot, appDirectory) => {
78
124
  const { webapp = {} } = loadXfeConfig(projectRoot);
79
- const layoutSource = findSource(import_node_path.default.join(projectRoot, "src"), "layout");
125
+ const layoutSource = findSource(import_node_path2.default.join(projectRoot, "src"), "layout");
80
126
  const layoutExports = layoutSource ? findValueExports(layoutSource) : [];
81
127
  const hasErrorView = layoutExports.includes("ErrorView");
82
128
  const layoutImport = layoutSource ? `import AppLayout${hasErrorView ? ", { ErrorView }" : ""} from ${JSON.stringify(importPath(appDirectory, layoutSource))}
@@ -92,24 +138,24 @@ export function ErrorBoundary() { return createElement(PageError, { ${hasErrorVi
92
138
  export function HydrateFallback() { return createElement(PageLoading) }
93
139
  `;
94
140
  };
95
- var getFrameworkRoot = (projectRoot) => import_node_path.default.join(projectRoot, "node_modules/.cache/xfe-web/react-router");
141
+ var getFrameworkRoot = (projectRoot) => import_node_path2.default.join(projectRoot, "node_modules/.cache/xfe-web/react-router");
96
142
  var prepareFramework = (projectRoot = process.cwd()) => {
97
- const sourceDirectory = import_node_path.default.join(projectRoot, "src");
143
+ const sourceDirectory = import_node_path2.default.join(projectRoot, "src");
98
144
  const rootSource = findSource(sourceDirectory, "root");
99
145
  const appServerSource = findSource(sourceDirectory, "app.server");
100
- const pagesSource = import_node_path.default.join(sourceDirectory, "pages");
101
- if (!import_node_fs.default.existsSync(pagesSource)) throw new Error("\u672A\u627E\u5230 src/pages");
146
+ const pagesSource = import_node_path2.default.join(sourceDirectory, "pages");
147
+ if (!import_node_fs2.default.existsSync(pagesSource)) throw new Error("\u672A\u627E\u5230 src/pages");
102
148
  const rootDirectory = getFrameworkRoot(projectRoot);
103
- const appDirectory = import_node_path.default.join(rootDirectory, "app");
104
- import_node_fs.default.mkdirSync(appDirectory, { recursive: true });
105
- const routerDirectory = import_node_path.default.dirname(requireFromFramework.resolve("react-router/package.json"));
106
- const routerLink = import_node_path.default.join(rootDirectory, "node_modules/react-router");
107
- import_node_fs.default.mkdirSync(import_node_path.default.dirname(routerLink), { recursive: true });
108
- if (!import_node_fs.default.lstatSync(routerLink, { throwIfNoEntry: false })?.isSymbolicLink() || import_node_fs.default.readlinkSync(routerLink) !== routerDirectory) {
109
- import_node_fs.default.rmSync(routerLink, { force: true });
110
- import_node_fs.default.symlinkSync(routerDirectory, routerLink, "junction");
149
+ const appDirectory = import_node_path2.default.join(rootDirectory, "app");
150
+ import_node_fs2.default.mkdirSync(appDirectory, { recursive: true });
151
+ const routerDirectory = import_node_path2.default.dirname(requireFromFramework.resolve("react-router/package.json"));
152
+ const routerLink = import_node_path2.default.join(rootDirectory, "node_modules/react-router");
153
+ import_node_fs2.default.mkdirSync(import_node_path2.default.dirname(routerLink), { recursive: true });
154
+ if (!import_node_fs2.default.lstatSync(routerLink, { throwIfNoEntry: false })?.isSymbolicLink() || import_node_fs2.default.readlinkSync(routerLink) !== routerDirectory) {
155
+ import_node_fs2.default.rmSync(routerLink, { force: true });
156
+ import_node_fs2.default.symlinkSync(routerDirectory, routerLink, "junction");
111
157
  }
112
- const rootTarget = import_node_path.default.join(appDirectory, "root.tsx");
158
+ const rootTarget = import_node_path2.default.join(appDirectory, "root.tsx");
113
159
  const rootImport = rootSource ? importPath(appDirectory, rootSource) : "";
114
160
  const rootExports = rootSource ? findValueExports(rootSource).filter((name) => name !== "middleware" || !appServerSource) : [];
115
161
  const namedExports = rootExports.length ? `export { ${rootExports.join(", ")} } from ${JSON.stringify(rootImport)}
@@ -123,36 +169,36 @@ var prepareFramework = (projectRoot = process.cwd()) => {
123
169
  export { default } from ${JSON.stringify(rootImport)}
124
170
  ${namedExports}${errorBoundaryExport}${middlewareExport}` : `${defaultRootSource(projectRoot, appDirectory)}${middlewareExport}`
125
171
  );
126
- const pagesLink = import_node_path.default.join(appDirectory, "pages");
127
- if (!import_node_fs.default.existsSync(pagesLink)) import_node_fs.default.symlinkSync(import_node_path.default.relative(appDirectory, pagesSource), pagesLink, "dir");
172
+ const pagesLink = import_node_path2.default.join(appDirectory, "pages");
173
+ if (!import_node_fs2.default.existsSync(pagesLink)) import_node_fs2.default.symlinkSync(import_node_path2.default.relative(appDirectory, pagesSource), pagesLink, "dir");
128
174
  writeGenerated(
129
- import_node_path.default.join(rootDirectory, "react-router.config.js"),
175
+ import_node_path2.default.join(rootDirectory, "react-router.config.js"),
130
176
  "export { default } from '@xfe-repo/web-app/dist/react-router-config.mjs'\n"
131
177
  );
132
178
  writeGenerated(
133
- import_node_path.default.join(appDirectory, "routes.js"),
179
+ import_node_path2.default.join(appDirectory, "routes.js"),
134
180
  `import { collectPageRoutes } from '@xfe-repo/web-router/routes'
135
181
  export default collectPageRoutes(${JSON.stringify(appDirectory)})
136
182
  `
137
183
  );
138
- const defaultsDirectory = import_node_path.default.join(import_node_path.default.dirname(requireFromFramework.resolve("@react-router/dev/package.json")), "dist/config/defaults");
184
+ const defaultsDirectory = import_node_path2.default.join(import_node_path2.default.dirname(requireFromFramework.resolve("@react-router/dev/package.json")), "dist/config/defaults");
139
185
  writeGenerated(
140
- import_node_path.default.join(appDirectory, "entry.server.tsx"),
141
- `export { default, streamTimeout } from ${JSON.stringify(importPath(appDirectory, import_node_path.default.join(defaultsDirectory, "entry.server.web.tsx")))}
186
+ import_node_path2.default.join(appDirectory, "entry.server.tsx"),
187
+ `export { default, streamTimeout } from ${JSON.stringify(importPath(appDirectory, import_node_path2.default.join(defaultsDirectory, "entry.server.web.tsx")))}
142
188
  `
143
189
  );
144
- const typeScriptConfig = import_node_path.default.join(rootDirectory, "tsconfig.json");
190
+ const typeScriptConfig = import_node_path2.default.join(rootDirectory, "tsconfig.json");
145
191
  writeGenerated(
146
192
  typeScriptConfig,
147
193
  JSON.stringify(
148
194
  {
149
- extends: import_node_path.default.join(projectRoot, "tsconfig.json"),
150
- compilerOptions: { rootDirs: [projectRoot, rootDirectory, import_node_path.default.join(rootDirectory, ".react-router/types")] },
195
+ extends: import_node_path2.default.join(projectRoot, "tsconfig.json"),
196
+ compilerOptions: { rootDirs: [projectRoot, rootDirectory, import_node_path2.default.join(rootDirectory, ".react-router/types")] },
151
197
  include: [
152
- import_node_path.default.join(sourceDirectory, "**/*"),
153
- import_node_path.default.join(projectRoot, "types/**/*"),
154
- import_node_path.default.join(appDirectory, "*.tsx"),
155
- import_node_path.default.join(rootDirectory, ".react-router/types/**/*")
198
+ import_node_path2.default.join(sourceDirectory, "**/*"),
199
+ import_node_path2.default.join(projectRoot, "types/**/*"),
200
+ import_node_path2.default.join(appDirectory, "*.tsx"),
201
+ import_node_path2.default.join(rootDirectory, ".react-router/types/**/*")
156
202
  ],
157
203
  exclude: []
158
204
  },
@@ -163,7 +209,7 @@ export default collectPageRoutes(${JSON.stringify(appDirectory)})
163
209
  return {
164
210
  appDirectory,
165
211
  rootDirectory,
166
- viteConfig: import_node_path.default.resolve(frameworkDirectory, "vite.mjs"),
212
+ viteConfig: import_node_path2.default.resolve(frameworkDirectory, "vite.mjs"),
167
213
  typeScriptConfig
168
214
  };
169
215
  };
@@ -2,12 +2,16 @@
2
2
  import fs2 from "fs";
3
3
  import { createRequire } from "module";
4
4
  import path2 from "path";
5
- import { fileURLToPath } from "url";
5
+ import { fileURLToPath as fileURLToPath2 } from "url";
6
6
 
7
7
  // src/config.ts
8
- import path from "path";
9
8
  import fs from "fs";
9
+ import path from "path";
10
+ import { fileURLToPath } from "url";
10
11
  import yaml from "yaml";
12
+ var DEFAULT_BUILD_DIRECTORY = "dist";
13
+ var protectedDirectories = /* @__PURE__ */ new Set(["src", "types", "public", "node_modules", ".git"]);
14
+ var configDirectory = typeof __dirname === "string" ? __dirname : path.dirname(fileURLToPath(import.meta.url));
11
15
  var loadXfeConfig = (root = process.cwd()) => {
12
16
  const xfeConfigPath = path.join(root, "xfe.json");
13
17
  if (!fs.existsSync(xfeConfigPath)) {
@@ -19,11 +23,52 @@ var loadXfeConfig = (root = process.cwd()) => {
19
23
  if (renderMode !== void 0 && renderMode !== "client" && renderMode !== "server") {
20
24
  throw new Error("xfe.json.webapp.renderMode \u53EA\u80FD\u4E3A client \u6216 server");
21
25
  }
26
+ getBuildDirectory(root, config.webapp);
27
+ getServerEntry(root, config.webapp);
22
28
  return config;
23
29
  };
30
+ var getBuildDirectory = (projectRoot, webapp = {}) => {
31
+ const configured = webapp?.buildDirectory === void 0 ? DEFAULT_BUILD_DIRECTORY : webapp.buildDirectory;
32
+ if (typeof configured !== "string" || !configured.trim() || /[\0\r\n]/.test(configured)) {
33
+ throw new Error("xfe.json.webapp.buildDirectory \u5FC5\u987B\u662F\u975E\u7A7A\u7684\u5355\u884C\u5B57\u7B26\u4E32");
34
+ }
35
+ const directory = configured.trim().replaceAll("\\", "/");
36
+ const root = path.resolve(projectRoot);
37
+ const output = path.resolve(root, directory);
38
+ const relative = path.relative(root, output);
39
+ const [firstDirectory = ""] = relative.split(path.sep);
40
+ const escapesProject = path.isAbsolute(directory) || Boolean(path.win32.parse(directory).root) || firstDirectory === "..";
41
+ const overwritesInput = !relative || protectedDirectories.has(firstDirectory.toLowerCase());
42
+ if (escapesProject || overwritesInput) {
43
+ throw new Error("xfe.json.webapp.buildDirectory \u5FC5\u987B\u662F\u9879\u76EE\u5185\u72EC\u7ACB\u7684\u8F93\u51FA\u76EE\u5F55\uFF0C\u4E0D\u80FD\u8986\u76D6\u9879\u76EE\u3001\u6E90\u7801\u6216\u4F9D\u8D56");
44
+ }
45
+ let currentDirectory = root;
46
+ for (const segment of relative.split(path.sep)) {
47
+ currentDirectory = path.join(currentDirectory, segment);
48
+ if (fs.lstatSync(currentDirectory, { throwIfNoEntry: false })?.isSymbolicLink()) {
49
+ throw new Error("xfe.json.webapp.buildDirectory \u4E0D\u80FD\u7ECF\u8FC7\u7B26\u53F7\u94FE\u63A5\uFF0C\u4EE5\u514D\u6E05\u7406\u5176\u4ED6\u76EE\u5F55");
50
+ }
51
+ }
52
+ return output;
53
+ };
54
+ var getServerEntry = (projectRoot, webapp = {}) => {
55
+ const entry = webapp?.serverEntry;
56
+ if (entry === void 0) return;
57
+ if (typeof entry !== "string" || !entry.trim() || /[\0\r\n]/.test(entry)) {
58
+ throw new Error("xfe.json.webapp.serverEntry \u5FC5\u987B\u662F\u9879\u76EE\u5185\u7684 TypeScript / JavaScript \u76F8\u5BF9\u8DEF\u5F84");
59
+ }
60
+ const normalized = entry.trim().replaceAll("\\", "/");
61
+ const absolute = path.resolve(projectRoot, normalized);
62
+ const relative = path.relative(path.resolve(projectRoot), absolute);
63
+ const outside = Boolean(path.win32.parse(normalized).root) || relative.split(path.sep)[0] === "..";
64
+ if (outside || !/\.(?:[jt]sx?|m[jt]s)$/.test(relative)) {
65
+ throw new Error("xfe.json.webapp.serverEntry \u5FC5\u987B\u662F\u9879\u76EE\u5185\u7684 TypeScript / JavaScript \u76F8\u5BF9\u8DEF\u5F84");
66
+ }
67
+ return absolute;
68
+ };
24
69
 
25
70
  // src/framework.ts
26
- var frameworkDirectory = typeof __dirname === "string" ? __dirname : path2.dirname(fileURLToPath(import.meta.url));
71
+ var frameworkDirectory = typeof __dirname === "string" ? __dirname : path2.dirname(fileURLToPath2(import.meta.url));
27
72
  var requireFromFramework = createRequire(path2.join(frameworkDirectory, "framework.js"));
28
73
  var extensions = [".tsx", ".ts", ".jsx", ".js"];
29
74
  var findSource = (directory, name) => extensions.map((extension) => path2.join(directory, `${name}${extension}`)).find(fs2.existsSync);
@@ -0,0 +1,2 @@
1
+
2
+ export { }
@@ -0,0 +1,2 @@
1
+
2
+ export { }