@4399ywkf/core 5.1.5 → 5.3.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/dist/cli/index.js CHANGED
@@ -879,7 +879,7 @@ function createMockMiddleware(mockPath, options) {
879
879
  scanMockFiles(mockPath);
880
880
  return async (_req, _res, next) => {
881
881
  if (delay > 0) {
882
- await new Promise((resolve5) => setTimeout(resolve5, delay));
882
+ await new Promise((resolve6) => setTimeout(resolve6, delay));
883
883
  }
884
884
  next();
885
885
  };
@@ -1833,11 +1833,11 @@ var zustandPlugin = createPlugin((options = {}) => ({
1833
1833
  const hooks = {
1834
1834
  modifyRspackConfig(rspackConfig) {
1835
1835
  logger.info("Zustand \u5DF2\u542F\u7528");
1836
- const resolve5 = rspackConfig.resolve || {};
1837
- const alias = resolve5.alias || {};
1836
+ const resolve6 = rspackConfig.resolve || {};
1837
+ const alias = resolve6.alias || {};
1838
1838
  alias["@store"] = storePath;
1839
- resolve5.alias = alias;
1840
- rspackConfig.resolve = resolve5;
1839
+ resolve6.alias = alias;
1840
+ rspackConfig.resolve = resolve6;
1841
1841
  return rspackConfig;
1842
1842
  },
1843
1843
  generateFiles(_ctx) {
@@ -2175,7 +2175,9 @@ var defaultConfig = {
2175
2175
  tools: {},
2176
2176
  env: {
2177
2177
  publicEnvFile: "config/env/.env.public",
2178
- envDir: "config/env"
2178
+ envDir: "config/env",
2179
+ expose: [],
2180
+ exposePrefixes: ["YWKF_PUBLIC_", "NEXT_PUBLIC_"]
2179
2181
  },
2180
2182
  alias: {},
2181
2183
  plugins: []
@@ -2743,12 +2745,13 @@ function LazyRoute({
2743
2745
 
2744
2746
  import React, { lazy, Suspense } from "react";
2745
2747
  import { createBrowserRouter, type RouteObject } from "react-router";
2748
+ import { RouteErrorBoundary } from "@4399ywkf/core/runtime";
2749
+ ${errorImports.length > 0 ? `
2750
+ // \u540C\u6B65\u9519\u8BEF\u8FB9\u754C\u7EC4\u4EF6
2751
+ ${errorImports.join("\n")}` : ""}
2746
2752
 
2747
2753
  // \u61D2\u52A0\u8F7D\u9875\u9762\u7EC4\u4EF6
2748
2754
  ${lazyImports.join("\n")}
2749
- ${errorImports.length > 0 ? `
2750
- // \u9519\u8BEF\u8FB9\u754C\u7EC4\u4EF6
2751
- ${errorImports.join("\n")}` : ""}
2752
2755
  ${loadingImports.length > 0 ? `
2753
2756
  // \u52A0\u8F7D\u72B6\u6001\u7EC4\u4EF6
2754
2757
  ${loadingImports.join("\n")}` : ""}
@@ -2783,7 +2786,7 @@ export default routes;
2783
2786
  }
2784
2787
  if (route.errorFile) {
2785
2788
  errorImports.push(
2786
- `const ${name}Error = lazy(() => import("@/pages/${toImportPath(route.errorFile)}"));`
2789
+ `import ${name}Error from "@/pages/${toImportPath(route.errorFile)}";`
2787
2790
  );
2788
2791
  }
2789
2792
  if (route.loadingFile) {
@@ -2798,13 +2801,13 @@ export default routes;
2798
2801
  }
2799
2802
  }
2800
2803
  }
2801
- emitRouteArray(routes, parentPath) {
2802
- const items = routes.map((r) => this.emitRouteObject(r, parentPath));
2804
+ emitRouteArray(routes, parentPath, depth = 0) {
2805
+ const items = routes.map((r) => this.emitRouteObject(r, parentPath, depth));
2803
2806
  return `[
2804
2807
  ${items.join(",\n ")}
2805
2808
  ]`;
2806
2809
  }
2807
- emitRouteObject(route, parentPath) {
2810
+ emitRouteObject(route, parentPath, depth) {
2808
2811
  const parts = [];
2809
2812
  const name = route.name;
2810
2813
  if (route.index) {
@@ -2825,11 +2828,13 @@ export default routes;
2825
2828
  parts.push(`element: <LazyRoute Component={${name}Page} />`);
2826
2829
  }
2827
2830
  if (route.errorFile) {
2828
- parts.push(`errorElement: <LazyRoute Component={${name}Error} />`);
2831
+ parts.push(`errorElement: <RouteErrorBoundary fallback={<${name}Error />} />`);
2832
+ } else if (depth === 0) {
2833
+ parts.push(`errorElement: <RouteErrorBoundary />`);
2829
2834
  }
2830
2835
  if (route.children && route.children.length > 0) {
2831
2836
  const childParent = route.pathless ? parentPath : route.path;
2832
- parts.push(`children: ${this.emitRouteArray(route.children, childParent)}`);
2837
+ parts.push(`children: ${this.emitRouteArray(route.children, childParent, depth + 1)}`);
2833
2838
  }
2834
2839
  return `{
2835
2840
  ${parts.join(",\n ")}
@@ -3434,14 +3439,93 @@ var YwkfGeneratorPlugin = class {
3434
3439
  }
3435
3440
  };
3436
3441
 
3442
+ // src/rspack/client-env.ts
3443
+ import { existsSync as existsSync12, readFileSync as readFileSync4 } from "fs";
3444
+ import { resolve as resolve3 } from "path";
3445
+ import dotenv from "dotenv";
3446
+ var FRAMEWORK_ENV_KEYS = [
3447
+ "APP_CNAME",
3448
+ "APP_HOST",
3449
+ "APP_MODE",
3450
+ "APP_NAME",
3451
+ "APP_PORT",
3452
+ "DEBUG",
3453
+ "NODE_ENV",
3454
+ "OUTPUT_PATH",
3455
+ "PUBLIC_PATH",
3456
+ "YWKF_BUILD_ID",
3457
+ "YWKF_PREFIX_CLS"
3458
+ ];
3459
+ var DEFAULT_EXPOSE_PREFIXES = ["YWKF_PUBLIC_", "NEXT_PUBLIC_"];
3460
+ var SECRET_KEY_PATTERN = /TOKEN|SECRET|PASSWORD|PASSWD|CREDENTIAL|PRIVATE|AUTH|ACCESS_KEY|API_KEY|SIGNING_KEY/i;
3461
+ var SECRET_VALUE_PATTERN = /^(sntry[su]_|sk-|ghp_|gho_|ghu_|ghs_|github_pat_|glpat-|xox[abprs]-|AKIA|ya29\.)/;
3462
+ function readEnvFileKeys(filePath) {
3463
+ if (!existsSync12(filePath)) return [];
3464
+ try {
3465
+ return Object.keys(dotenv.parse(readFileSync4(filePath, "utf-8")));
3466
+ } catch {
3467
+ return [];
3468
+ }
3469
+ }
3470
+ function collectClientEnvKeys(options) {
3471
+ const { cwd, env = {}, mode = process.env.APP_MODE || "development" } = options;
3472
+ const keys = new Set(FRAMEWORK_ENV_KEYS);
3473
+ const files = [
3474
+ env.publicEnvFile ?? "config/env/.env.public",
3475
+ ".env.public",
3476
+ ".env",
3477
+ `${env.envDir ?? "config/env"}/.env.${mode}`,
3478
+ `.env.${mode}`
3479
+ ];
3480
+ for (const file of files) {
3481
+ for (const key of readEnvFileKeys(resolve3(cwd, file))) keys.add(key);
3482
+ }
3483
+ for (const key of env.expose ?? []) keys.add(key);
3484
+ return keys;
3485
+ }
3486
+ function buildClientEnv(options) {
3487
+ const { cwd, env = {}, mode, processEnv = process.env } = options;
3488
+ const prefixes = env.exposePrefixes ?? DEFAULT_EXPOSE_PREFIXES;
3489
+ const allowed = collectClientEnvKeys({ cwd, env, mode });
3490
+ const result = {};
3491
+ const dropped = [];
3492
+ for (const [key, value] of Object.entries(processEnv)) {
3493
+ if (value === void 0) continue;
3494
+ const isAllowed = allowed.has(key) || prefixes.some((prefix) => key.startsWith(prefix));
3495
+ if (!isAllowed) continue;
3496
+ if (SECRET_KEY_PATTERN.test(key) || SECRET_VALUE_PATTERN.test(value)) {
3497
+ dropped.push(key);
3498
+ continue;
3499
+ }
3500
+ result[key] = value;
3501
+ }
3502
+ return { env: result, dropped };
3503
+ }
3504
+
3437
3505
  // src/rspack/base.ts
3438
3506
  var require2 = createRequire(import.meta.url);
3439
3507
  var __dirname = dirname(fileURLToPath(import.meta.url));
3440
3508
  var coreNodeModules = join12(__dirname, "../../node_modules");
3509
+ function resolveYwkfBuildId(appName) {
3510
+ const releaseId = [
3511
+ process.env.YWKF_BUILD_ID,
3512
+ process.env.CI_COMMIT_SHA,
3513
+ process.env.GITHUB_SHA,
3514
+ process.env.GIT_COMMIT
3515
+ ].find((value) => value?.trim())?.trim();
3516
+ return `${appName}:${releaseId || Date.now().toString(36)}`;
3517
+ }
3441
3518
  function createBaseConfig(config, cwd, options = {}) {
3442
3519
  const isDev = options.isDev ?? process.env.NODE_ENV !== "production";
3443
3520
  const { resolveApp } = createPathResolver(cwd);
3444
3521
  const { appName, appCName, output, html, alias: userAlias, microFrontend, router, reactCompiler } = config;
3522
+ const buildId = resolveYwkfBuildId(appName);
3523
+ const { env: clientEnv, dropped: droppedEnvKeys } = buildClientEnv({ cwd, env: config.env });
3524
+ if (droppedEnvKeys.length > 0) {
3525
+ console.warn(
3526
+ `[ywkf] \u4EE5\u4E0B\u73AF\u5883\u53D8\u91CF\u7591\u4F3C\u5BC6\u94A5,\u5DF2\u4ECE\u5BA2\u6237\u7AEF\u4EA7\u7269\u4E2D\u5254\u9664:${droppedEnvKeys.join(", ")}\u3002\u5982\u786E\u4E3A\u516C\u5F00\u914D\u7F6E,\u8BF7\u91CD\u547D\u540D\u6216\u8C03\u6574 env.expose\u3002`
3527
+ );
3528
+ }
3445
3529
  const ywkfOutputDir = resolveApp(".ywkf");
3446
3530
  const defaultAlias = {
3447
3531
  "@": resolveApp("src"),
@@ -3565,7 +3649,11 @@ function createBaseConfig(config, cwd, options = {}) {
3565
3649
  Buffer: ["buffer", "Buffer"]
3566
3650
  }),
3567
3651
  new rspack2.DefinePlugin({
3568
- "process.env": JSON.stringify(process.env)
3652
+ "globalThis.__YWKF_BUILD_ID__": JSON.stringify(buildId),
3653
+ // 只内联白名单内的公开配置。整包 process.env 会把构建机环境(CI 凭据、内网地址)
3654
+ // 写进公网可下载的产物,见 client-env.ts。外层括号不可省:该值以 `{` 开头,
3655
+ // `() => process.env.X` 会被替换成 `() => {…}` 而当成函数块体,导致整条 chunk 语法失败。
3656
+ "process.env": `(${JSON.stringify(clientEnv)})`
3569
3657
  }),
3570
3658
  new rspack2.HtmlRspackPlugin({
3571
3659
  template: resolveApp(html.template ?? "public/index.html"),
@@ -4083,28 +4171,28 @@ function createRspackConfig(config, cwd, options = {}) {
4083
4171
  }
4084
4172
 
4085
4173
  // src/cli/env.ts
4086
- import { existsSync as existsSync12, readFileSync as readFileSync4 } from "fs";
4087
- import { resolve as resolve3 } from "path";
4088
- import dotenv from "dotenv";
4174
+ import { existsSync as existsSync13, readFileSync as readFileSync5 } from "fs";
4175
+ import { resolve as resolve4 } from "path";
4176
+ import dotenv2 from "dotenv";
4089
4177
  function preloadEnv(cwd, mode, nodeEnv) {
4090
4178
  process.env.NODE_ENV = nodeEnv;
4091
4179
  process.env.APP_MODE = mode;
4092
4180
  const defaultPaths = [
4093
- resolve3(cwd, "config/env/.env.public"),
4094
- resolve3(cwd, ".env.public"),
4095
- resolve3(cwd, ".env")
4181
+ resolve4(cwd, "config/env/.env.public"),
4182
+ resolve4(cwd, ".env.public"),
4183
+ resolve4(cwd, ".env")
4096
4184
  ];
4097
4185
  for (const envPath of defaultPaths) {
4098
- if (existsSync12(envPath)) {
4099
- dotenv.config({ path: envPath });
4186
+ if (existsSync13(envPath)) {
4187
+ dotenv2.config({ path: envPath });
4100
4188
  break;
4101
4189
  }
4102
4190
  }
4103
- const modeEnvPaths = [resolve3(cwd, `config/env/.env.${mode}`), resolve3(cwd, `.env.${mode}`)];
4191
+ const modeEnvPaths = [resolve4(cwd, `config/env/.env.${mode}`), resolve4(cwd, `.env.${mode}`)];
4104
4192
  for (const envPath of modeEnvPaths) {
4105
- if (existsSync12(envPath)) {
4106
- const envContent = readFileSync4(envPath, "utf-8");
4107
- const parsed = dotenv.parse(envContent);
4193
+ if (existsSync13(envPath)) {
4194
+ const envContent = readFileSync5(envPath, "utf-8");
4195
+ const parsed = dotenv2.parse(envContent);
4108
4196
  for (const key in parsed) {
4109
4197
  process.env[key] = parsed[key];
4110
4198
  }
@@ -4116,14 +4204,14 @@ function preloadEnv(cwd, mode, nodeEnv) {
4116
4204
  }
4117
4205
  function loadEnv(config, cwd, mode, nodeEnv) {
4118
4206
  const { env } = config;
4119
- const publicEnvPath = resolve3(cwd, env.publicEnvFile ?? "config/env/.env.public");
4120
- if (existsSync12(publicEnvPath)) {
4121
- dotenv.config({ path: publicEnvPath });
4122
- }
4123
- const modeEnvPath = resolve3(cwd, env.envDir ?? "config/env", `.env.${mode}`);
4124
- if (existsSync12(modeEnvPath)) {
4125
- const envContent = readFileSync4(modeEnvPath, "utf-8");
4126
- const parsed = dotenv.parse(envContent);
4207
+ const publicEnvPath = resolve4(cwd, env.publicEnvFile ?? "config/env/.env.public");
4208
+ if (existsSync13(publicEnvPath)) {
4209
+ dotenv2.config({ path: publicEnvPath });
4210
+ }
4211
+ const modeEnvPath = resolve4(cwd, env.envDir ?? "config/env", `.env.${mode}`);
4212
+ if (existsSync13(modeEnvPath)) {
4213
+ const envContent = readFileSync5(modeEnvPath, "utf-8");
4214
+ const parsed = dotenv2.parse(envContent);
4127
4215
  for (const key in parsed) {
4128
4216
  process.env[key] = parsed[key];
4129
4217
  }
@@ -4401,7 +4489,7 @@ async function build(options = {}) {
4401
4489
  rspackConfig.stats = "none";
4402
4490
  rspackConfig.infrastructureLogging = { level: "none" };
4403
4491
  const compiler = rspack4(rspackConfig);
4404
- const stats = await new Promise((resolve5, reject) => {
4492
+ const stats = await new Promise((resolve6, reject) => {
4405
4493
  compiler.run((err, stats2) => {
4406
4494
  if (err) {
4407
4495
  reject(err);
@@ -4411,13 +4499,13 @@ async function build(options = {}) {
4411
4499
  reject(new Error("Build failed: no stats available"));
4412
4500
  return;
4413
4501
  }
4414
- resolve5(stats2);
4502
+ resolve6(stats2);
4415
4503
  });
4416
4504
  });
4417
- await new Promise((resolve5, reject) => {
4505
+ await new Promise((resolve6, reject) => {
4418
4506
  compiler.close((err) => {
4419
4507
  if (err) reject(err);
4420
- else resolve5();
4508
+ else resolve6();
4421
4509
  });
4422
4510
  });
4423
4511
  const hasErrors = stats.hasErrors();
@@ -4459,7 +4547,7 @@ var PortInUseError = class extends Error {
4459
4547
  }
4460
4548
  };
4461
4549
  function ensurePortAvailable(port, host) {
4462
- return new Promise((resolve5, reject) => {
4550
+ return new Promise((resolve6, reject) => {
4463
4551
  const server = net.createServer();
4464
4552
  server.once("error", (err) => {
4465
4553
  if (err.code === "EADDRINUSE") {
@@ -4469,7 +4557,7 @@ function ensurePortAvailable(port, host) {
4469
4557
  }
4470
4558
  });
4471
4559
  server.once("listening", () => {
4472
- server.close(() => resolve5());
4560
+ server.close(() => resolve6());
4473
4561
  });
4474
4562
  server.listen(port, host);
4475
4563
  });
@@ -4517,8 +4605,8 @@ function parseWindowsNetstatPid(stdout, port) {
4517
4605
  }
4518
4606
 
4519
4607
  // src/cli/restart-watcher.ts
4520
- import { existsSync as existsSync13, watch as watch2 } from "fs";
4521
- import { resolve as resolve4 } from "path";
4608
+ import { existsSync as existsSync14, watch as watch2 } from "fs";
4609
+ import { resolve as resolve5 } from "path";
4522
4610
  function isTriggerFile(filename, mode) {
4523
4611
  if (!filename) return false;
4524
4612
  if (CONFIG_FILES.includes(filename)) return true;
@@ -4548,8 +4636,8 @@ function watchForRestart(opts) {
4548
4636
  }, debounceMs);
4549
4637
  };
4550
4638
  watchers.push(watch2(cwd, { recursive: false }, (_event, filename) => trigger(filename)));
4551
- const envDir = resolve4(cwd, "config/env");
4552
- if (existsSync13(envDir)) {
4639
+ const envDir = resolve5(cwd, "config/env");
4640
+ if (existsSync14(envDir)) {
4553
4641
  watchers.push(watch2(envDir, { recursive: false }, (_event, filename) => trigger(filename)));
4554
4642
  }
4555
4643
  return {
@@ -4656,10 +4744,10 @@ async function startDevServer(cwd, config) {
4656
4744
  if (closed) return;
4657
4745
  closed = true;
4658
4746
  await server.stop();
4659
- await new Promise((resolve5, reject) => {
4747
+ await new Promise((resolve6, reject) => {
4660
4748
  compiler.close((err) => {
4661
4749
  if (err) reject(err);
4662
- else resolve5();
4750
+ else resolve6();
4663
4751
  });
4664
4752
  });
4665
4753
  };