@esmx/rspack 3.0.0-rc.117 → 3.0.0-rc.118

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/README.md CHANGED
@@ -32,7 +32,7 @@
32
32
  - **High-Performance Build** - Ultra-fast building based on Rspack, providing excellent development experience
33
33
  - **Application Support** - Complete support for standard applications and HTML applications development and building
34
34
  - **Asset Processing** - Smart processing of various assets including JavaScript, CSS, images
35
- - **SSR Support** - Built-in server-side rendering support for easy isomorphic application building
35
+ - **SSR Support** - Built-in SSR support for easy isomorphic application building
36
36
  - **Developer Experience** - Supports hot reload, intelligent hints, and TypeScript
37
37
 
38
38
  ## 📦 Installation
@@ -48,6 +48,20 @@ pnpm add @esmx/rspack -D
48
48
  yarn add @esmx/rspack -D
49
49
  ```
50
50
 
51
+ ## 🚀 Quick Start
52
+
53
+ Use it from your `entry.node.ts`:
54
+
55
+ ```typescript
56
+ import type { EsmxOptions } from '@esmx/core';
57
+
58
+ export default {
59
+ async devApp(esmx) {
60
+ return import('@esmx/rspack').then((m) => m.createRspackHtmlApp(esmx));
61
+ }
62
+ } satisfies EsmxOptions;
63
+ ```
64
+
51
65
  ## 📚 Documentation
52
66
 
53
67
  Visit the [official documentation](https://esmx.dev/api/app/rspack.html) for detailed usage guides and API reference.
package/README.zh-CN.md CHANGED
@@ -29,11 +29,11 @@
29
29
 
30
30
  ## 🚀 特性
31
31
 
32
- - **高性能构建** - 基于 Rspack 的极速构建,为应用提供卓越的开发体验
33
- - **应用支持** - 完整支持标准应用和 HTML 应用的开发与构建
32
+ - **高性能构建** - 基于 Rspack 的极速构建,提供卓越的开发体验
33
+ - **应用支持** - 完整支持标准应用与 HTML 应用的开发与构建
34
34
  - **资源处理** - 智能处理各类资源,支持 JavaScript、CSS、图片等
35
- - **SSR 支持** - 内置服务端渲染支持,轻松构建同构应用
36
- - **开发体验** - 支持热更新、智能提示和 TypeScript
35
+ - **SSR 支持** - 内置 SSR 支持,轻松构建同构应用
36
+ - **开发体验** - 支持热更新、智能提示与 TypeScript
37
37
 
38
38
  ## 📦 安装
39
39
 
@@ -48,6 +48,20 @@ pnpm add @esmx/rspack -D
48
48
  yarn add @esmx/rspack -D
49
49
  ```
50
50
 
51
+ ## 🚀 快速开始
52
+
53
+ 在 `entry.node.ts` 中使用:
54
+
55
+ ```typescript
56
+ import type { EsmxOptions } from '@esmx/core';
57
+
58
+ export default {
59
+ async devApp(esmx) {
60
+ return import('@esmx/rspack').then((m) => m.createRspackHtmlApp(esmx));
61
+ }
62
+ } satisfies EsmxOptions;
63
+ ```
64
+
51
65
  ## 📚 文档
52
66
 
53
67
  访问[官方文档](https://esmx.dev/api/app/rspack.html)获取详细的使用指南和 API 参考。
@@ -1,4 +1,4 @@
1
- import type RspackChain from 'rspack-chain';
1
+ import type { RspackChain } from 'rspack-chain';
2
2
  import type { ParsedModuleLinkPluginOptions } from './types';
3
3
  export declare function applyEntryConfig(chain: RspackChain, opts: ParsedModuleLinkPluginOptions): void;
4
4
  export declare function applyModuleConfig(chain: RspackChain): void;
@@ -40,9 +40,11 @@ function createExternalsFunction(opts, compilerContext) {
40
40
  const identifier = value.pkg ? value.name : value.identifier;
41
41
  importMap.set(identifier, identifier);
42
42
  importMap.set(value.name, identifier);
43
- const resolvedPath = await resolvePath(value.file);
44
- if (resolvedPath) {
45
- importMap.set(resolvedPath, identifier);
43
+ if (value.file) {
44
+ const resolvedPath = await resolvePath(value.file);
45
+ if (resolvedPath) {
46
+ importMap.set(resolvedPath, identifier);
47
+ }
46
48
  }
47
49
  })
48
50
  );
@@ -72,9 +74,11 @@ function createExternalsFunction(opts, compilerContext) {
72
74
  return importName || null;
73
75
  };
74
76
  const FILE_EXT_REGEX = /\.worker\.(js|mjs|cjs|jsx|mjsx|cjsx|ts|mts|cts|tsx|mtsx|ctsx)$/i;
77
+ const wrapperFiles = new Set(opts.wrapperFiles);
75
78
  return async (data) => {
76
79
  if (!data.request || !data.context || !data.contextInfo?.issuer || FILE_EXT_REGEX.test(data.contextInfo.issuer))
77
80
  return;
81
+ if (wrapperFiles.has(data.contextInfo.issuer)) return;
78
82
  const defaultContext = compilerContext;
79
83
  const resolvePath = async (request, context = defaultContext) => {
80
84
  if (!data.getResolve) {
@@ -1,3 +1,3 @@
1
- import type RspackChain from 'rspack-chain';
1
+ import type { RspackChain } from 'rspack-chain';
2
2
  import type { ParsedModuleLinkPluginOptions } from './types';
3
3
  export declare function applyChainConfig1(chain: RspackChain, opts: ParsedModuleLinkPluginOptions): void;
@@ -1,4 +1,4 @@
1
- import type RspackChain from 'rspack-chain';
1
+ import type { RspackChain } from 'rspack-chain';
2
2
  import type { ModuleLinkPluginOptions } from './types';
3
- export declare function initModuleLink(chain: RspackChain, options: ModuleLinkPluginOptions): void;
3
+ export declare function initModuleLink(chain: RspackChain, options: ModuleLinkPluginOptions, isProduction?: boolean): void;
4
4
  export type { ModuleLinkPluginOptions } from './types';
@@ -1,8 +1,8 @@
1
1
  import { applyChainConfig1 } from "./config1.mjs";
2
2
  import { ManifestPlugin } from "./manifest-plugin.mjs";
3
3
  import { parseOptions } from "./parse.mjs";
4
- export function initModuleLink(chain, options) {
4
+ export function initModuleLink(chain, options, isProduction = false) {
5
5
  const opts = parseOptions(options);
6
6
  applyChainConfig1(chain, opts);
7
- chain.plugin("module-link-manifest").use(ManifestPlugin, [opts]);
7
+ chain.plugin("module-link-manifest").use(ManifestPlugin, [opts, isProduction]);
8
8
  }
@@ -1,12 +1,32 @@
1
1
  import type { Compiler, StatsCompilation } from '@rspack/core';
2
- import type { ManifestJsonExports, ParsedModuleLinkPluginOptions } from './types';
2
+ import type { ManifestJsonChunks, ManifestJsonExports, ParsedModuleLinkPluginOptions } from './types';
3
3
  export declare const RSPACK_PLUGIN_NAME = "rspack-module-link-plugin";
4
4
  export declare class ManifestPlugin {
5
5
  private opts;
6
- constructor(opts: ParsedModuleLinkPluginOptions);
6
+ constructor(opts: ParsedModuleLinkPluginOptions, isProduction: boolean);
7
7
  apply(compiler: Compiler): void;
8
8
  }
9
9
  export declare function getExports(opts: ParsedModuleLinkPluginOptions, stats: StatsCompilation): ManifestJsonExports;
10
+ /**
11
+ * Build the `chunks` section of the federation manifest from rspack stats.
12
+ *
13
+ * Each emitted chunk that owns at least one JS module gets a manifest entry
14
+ * keyed by a stable, root-relative identifier (`<remote>@<source-path>`). The
15
+ * entry lists:
16
+ *
17
+ * - `js` — the chunk's primary JS asset
18
+ * - `css` — every `.css` file the chunk emitted; consumed by the host's
19
+ * renderHost to inject `<link rel="stylesheet">` into `<head>`
20
+ * (G5). This is how `import './x.css'` in a remote translates
21
+ * into stylesheet delivery in the browser without the runtime
22
+ * having to evaluate CSS as JS.
23
+ * - `resources` — auxiliary assets (sourcemaps, images, fonts) so the host
24
+ * can preload / fingerprint them.
25
+ *
26
+ * Pure function over stats + root path — kept testable without booting a
27
+ * real rspack build.
28
+ */
29
+ export declare function getChunks(opts: ParsedModuleLinkPluginOptions, stats: StatsCompilation, root: string): ManifestJsonChunks;
10
30
  export declare function generateIdentifier({ root, name, filePath }: {
11
31
  root: string;
12
32
  name: string;
@@ -1,7 +1,8 @@
1
+ import { buildManifestProtocolFields } from "@esmx/core";
1
2
  import upath from "upath";
2
3
  export const RSPACK_PLUGIN_NAME = "rspack-module-link-plugin";
3
4
  export class ManifestPlugin {
4
- constructor(opts) {
5
+ constructor(opts, isProduction) {
5
6
  this.opts = opts;
6
7
  }
7
8
  apply(compiler) {
@@ -11,6 +12,10 @@ export class ManifestPlugin {
11
12
  RSPACK_PLUGIN_NAME,
12
13
  (compilation) => {
13
14
  let manifestJson = {
15
+ ...buildManifestProtocolFields(
16
+ compiler.options.context ?? process.cwd(),
17
+ []
18
+ ),
14
19
  name: opts.name,
15
20
  exports: {},
16
21
  scopes: opts.scopes,
@@ -29,12 +34,26 @@ export class ManifestPlugin {
29
34
  });
30
35
  const exports = getExports(opts, stats);
31
36
  const resources = Object.keys(assets).map(transFileName).filter((file) => !file.includes("hot-update"));
37
+ const chunksStats = compilation.getStats().toJson({
38
+ all: false,
39
+ chunks: true,
40
+ modules: true,
41
+ chunkModules: true,
42
+ ids: true,
43
+ entrypoints: true,
44
+ chunkRelations: true
45
+ });
46
+ const root = compilation.options.context ?? process.cwd();
32
47
  manifestJson = {
48
+ ...buildManifestProtocolFields(
49
+ root,
50
+ Object.values(exports).filter((exp) => exp.pkg).map((exp) => exp.name)
51
+ ),
33
52
  name: opts.name,
34
53
  exports,
35
54
  scopes: opts.scopes,
36
55
  files: resources,
37
- chunks: getChunks(opts, compilation)
56
+ chunks: getChunks(opts, chunksStats, root)
38
57
  };
39
58
  const { RawSource } = compiler.rspack.sources;
40
59
  compilation.emitAsset(
@@ -91,26 +110,14 @@ export function getExports(opts, stats) {
91
110
  }
92
111
  return exports;
93
112
  }
94
- function getChunks(opts, compilation) {
95
- const stats = compilation.getStats().toJson({
96
- all: false,
97
- chunks: true,
98
- modules: true,
99
- chunkModules: true,
100
- ids: true
101
- });
113
+ export function getChunks(opts, stats, root) {
102
114
  const buildChunks = {};
103
115
  if (!stats.chunks) return buildChunks;
104
116
  for (const chunk of stats.chunks) {
105
- const module = chunk.modules?.sort((a, b) => {
106
- return (a.index ?? -1) - (b?.index ?? -1);
107
- })?.find((module2) => {
108
- return module2.moduleType?.includes("javascript/");
109
- });
117
+ const module = pickPrimaryJsModule(chunk);
110
118
  if (!module?.nameForCondition) continue;
111
119
  const js = chunk.files?.find((file) => file.endsWith(opts.ext));
112
120
  if (!js) continue;
113
- const root = compilation.options.context ?? process.cwd();
114
121
  const name = generateIdentifier({
115
122
  root,
116
123
  name: opts.name,
@@ -127,6 +134,9 @@ function getChunks(opts, compilation) {
127
134
  }
128
135
  return buildChunks;
129
136
  }
137
+ function pickPrimaryJsModule(chunk) {
138
+ return chunk.modules?.sort((a, b) => (a.index ?? -1) - (b?.index ?? -1))?.find((module) => module.moduleType?.includes("javascript/"));
139
+ }
130
140
  export function generateIdentifier({
131
141
  root,
132
142
  name,
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,195 @@
1
+ import { describe, expect, it } from "vitest";
2
+ import { generateIdentifier, getChunks, getExports } from "./manifest-plugin.mjs";
3
+ const baseOpts = {
4
+ name: "remote",
5
+ exports: {},
6
+ imports: {},
7
+ scopes: {},
8
+ ext: ".mjs",
9
+ deps: [],
10
+ preEntries: [],
11
+ wrapperFiles: [],
12
+ injectChunkName: false
13
+ };
14
+ const ROOT = "/abs/project";
15
+ function mkChunk(o) {
16
+ return {
17
+ files: o.files ?? [],
18
+ auxiliaryFiles: o.auxiliaryFiles ?? [],
19
+ modules: (o.modules ?? []).map((m, i) => ({
20
+ id: i,
21
+ index: m.index ?? i,
22
+ nameForCondition: m.nameForCondition,
23
+ moduleType: m.moduleType ?? "javascript/auto"
24
+ }))
25
+ };
26
+ }
27
+ describe("getChunks \u2014 CSS extraction", () => {
28
+ it("populates css[] when a chunk emitted .css files", () => {
29
+ const stats = {
30
+ chunks: [
31
+ mkChunk({
32
+ files: [
33
+ "src/entry.client.95f6085b.final.mjs",
34
+ "src/entry.client.a73d6772.final.css"
35
+ ],
36
+ modules: [
37
+ {
38
+ nameForCondition: `${ROOT}/src/entry.client.ts`,
39
+ moduleType: "javascript/auto"
40
+ }
41
+ ]
42
+ })
43
+ ]
44
+ };
45
+ const chunks = getChunks(baseOpts, stats, ROOT);
46
+ const entry = chunks["remote@src/entry.client.ts"];
47
+ expect(entry).toBeDefined();
48
+ expect(entry.js).toBe("src/entry.client.95f6085b.final.mjs");
49
+ expect(entry.css).toEqual(["src/entry.client.a73d6772.final.css"]);
50
+ });
51
+ it("returns empty css[] when chunk has no stylesheet", () => {
52
+ const stats = {
53
+ chunks: [
54
+ mkChunk({
55
+ files: ["vue.b8a9c2d3.final.mjs"],
56
+ modules: [
57
+ {
58
+ nameForCondition: `${ROOT}/node_modules/vue/index.mjs`,
59
+ moduleType: "javascript/esm"
60
+ }
61
+ ]
62
+ })
63
+ ]
64
+ };
65
+ const entry = getChunks(baseOpts, stats, ROOT)["remote@node_modules/vue/index.mjs"];
66
+ expect(entry).toBeDefined();
67
+ expect(entry.css).toEqual([]);
68
+ });
69
+ it("extracts multiple CSS chunks per JS chunk (split stylesheets)", () => {
70
+ const stats = {
71
+ chunks: [
72
+ mkChunk({
73
+ files: [
74
+ "chunks/widget.111.final.mjs",
75
+ "chunks/widget.111.final.css",
76
+ "chunks/widget.111.theme.css"
77
+ ],
78
+ modules: [
79
+ {
80
+ nameForCondition: `${ROOT}/src/widget.ts`,
81
+ moduleType: "javascript/esm"
82
+ }
83
+ ]
84
+ })
85
+ ]
86
+ };
87
+ const entry = getChunks(baseOpts, stats, ROOT)["remote@src/widget.ts"];
88
+ expect(entry.css).toEqual([
89
+ "chunks/widget.111.final.css",
90
+ "chunks/widget.111.theme.css"
91
+ ]);
92
+ });
93
+ it("ignores chunks without a JS module (CSS-only / asset-only)", () => {
94
+ const stats = {
95
+ chunks: [
96
+ mkChunk({
97
+ files: ["orphan.111.final.css"],
98
+ modules: []
99
+ })
100
+ ]
101
+ };
102
+ expect(getChunks(baseOpts, stats, ROOT)).toEqual({});
103
+ });
104
+ it("respects opts.ext for the JS file lookup (.cjs / .js / .mjs)", () => {
105
+ const stats = {
106
+ chunks: [
107
+ mkChunk({
108
+ files: ["src/x.111.js", "src/x.111.css"],
109
+ modules: [
110
+ {
111
+ nameForCondition: `${ROOT}/src/x.ts`,
112
+ moduleType: "javascript/auto"
113
+ }
114
+ ]
115
+ })
116
+ ]
117
+ };
118
+ const chunks = getChunks({ ...baseOpts, ext: ".js" }, stats, ROOT);
119
+ expect(chunks["remote@src/x.ts"].js).toBe("src/x.111.js");
120
+ expect(chunks["remote@src/x.ts"].css).toEqual(["src/x.111.css"]);
121
+ });
122
+ it("writes auxiliaryFiles (sourcemaps, images) into resources[]", () => {
123
+ const stats = {
124
+ chunks: [
125
+ mkChunk({
126
+ files: ["src/a.111.mjs"],
127
+ auxiliaryFiles: ["src/a.111.mjs.map", "assets/logo.svg"],
128
+ modules: [
129
+ {
130
+ nameForCondition: `${ROOT}/src/a.ts`,
131
+ moduleType: "javascript/esm"
132
+ }
133
+ ]
134
+ })
135
+ ]
136
+ };
137
+ const entry = getChunks(baseOpts, stats, ROOT)["remote@src/a.ts"];
138
+ expect(entry.resources).toEqual([
139
+ "src/a.111.mjs.map",
140
+ "assets/logo.svg"
141
+ ]);
142
+ });
143
+ it("returns {} when stats has no chunks at all", () => {
144
+ expect(getChunks(baseOpts, {}, ROOT)).toEqual({});
145
+ });
146
+ });
147
+ describe("generateIdentifier", () => {
148
+ it("normalises absolute path to root-relative POSIX form", () => {
149
+ expect(
150
+ generateIdentifier({
151
+ root: "/abs/project",
152
+ name: "remote",
153
+ filePath: "/abs/project/src/entry.client.ts"
154
+ })
155
+ ).toBe("remote@src/entry.client.ts");
156
+ });
157
+ it("falls back to absolute path when no root provided", () => {
158
+ expect(
159
+ generateIdentifier({
160
+ root: "",
161
+ name: "remote",
162
+ filePath: "/abs/project/src/x.ts"
163
+ })
164
+ ).toBe("remote@/abs/project/src/x.ts");
165
+ });
166
+ });
167
+ describe("getExports", () => {
168
+ it("binds export entries to their hashed asset filenames", () => {
169
+ const opts = {
170
+ ...baseOpts,
171
+ exports: {
172
+ "src/entry.client": {
173
+ name: "src/entry.client",
174
+ file: "./src/entry.client",
175
+ pkg: false,
176
+ identifier: "remote/src/entry.client"
177
+ }
178
+ }
179
+ };
180
+ const stats = {
181
+ entrypoints: {
182
+ "src/entry.client": {
183
+ chunks: [],
184
+ assets: [
185
+ { name: "src/entry.client.95f6085b.final.mjs" }
186
+ ]
187
+ }
188
+ }
189
+ };
190
+ const out = getExports(opts, stats);
191
+ expect(out["src/entry.client"].file).toBe(
192
+ "src/entry.client.95f6085b.final.mjs"
193
+ );
194
+ });
195
+ });
@@ -19,6 +19,7 @@ export function parseOptions(options) {
19
19
  scopes: options.scopes ?? {},
20
20
  injectChunkName: options.injectChunkName ?? false,
21
21
  preEntries: options.preEntries ?? [],
22
- deps
22
+ deps,
23
+ wrapperFiles: options.wrapperFiles ?? []
23
24
  };
24
25
  }
@@ -12,6 +12,15 @@ export interface ModuleLinkPluginOptions {
12
12
  injectChunkName?: boolean;
13
13
  preEntries?: string[];
14
14
  deps?: string[];
15
+ /**
16
+ * Absolute paths of pkg-export wrapper files (one per pkg export). When
17
+ * the externals function sees an import whose issuer is one of these
18
+ * wrappers, it skips externalization — the wrapper IS the federation
19
+ * chunk for that package, so externalizing its inner
20
+ * `import __m from '<pkg-path>'` would route back to the wrapper itself
21
+ * (cyclic).
22
+ */
23
+ wrapperFiles?: string[];
15
24
  }
16
25
  export interface ParsedModuleLinkPluginOptions {
17
26
  name: string;
@@ -22,4 +31,5 @@ export interface ParsedModuleLinkPluginOptions {
22
31
  injectChunkName: boolean;
23
32
  preEntries: string[];
24
33
  deps: string[];
34
+ wrapperFiles: string[];
25
35
  }
@@ -76,7 +76,7 @@ export interface RspackAppChainContext {
76
76
  * rspack-chain configuration object.
77
77
  * You can use the chain API in chain hooks to modify the configuration.
78
78
  */
79
- chain: import('rspack-chain').default;
79
+ chain: import('rspack-chain').RspackChain;
80
80
  /**
81
81
  * Options object passed when creating the application.
82
82
  */
@@ -1,3 +1,6 @@
1
+ import crypto from "node:crypto";
2
+ import fs from "node:fs/promises";
3
+ import path from "node:path";
1
4
  import { pathToFileURL } from "node:url";
2
5
  import {
3
6
  createApp,
@@ -29,10 +32,12 @@ async function createMiddleware(esmx, options = {}) {
29
32
  if (esmx.command !== esmx.COMMAND.dev) {
30
33
  return [];
31
34
  }
32
- const rsBuild = createRsBuild([
33
- generateBuildConfig(esmx, options, "client"),
34
- generateBuildConfig(esmx, options, "server")
35
- ]);
35
+ const rsBuild = createRsBuild(
36
+ await Promise.all([
37
+ generateBuildConfig(esmx, options, "client"),
38
+ generateBuildConfig(esmx, options, "server")
39
+ ])
40
+ );
36
41
  rsBuild.watch();
37
42
  const hot = hotMiddleware(rsBuild.compilers[0], {
38
43
  path: `${esmx.basePath}hot-middleware`
@@ -46,7 +51,7 @@ async function createMiddleware(esmx, options = {}) {
46
51
  }
47
52
  ];
48
53
  }
49
- function generateBuildConfig(esmx, options, buildTarget) {
54
+ async function generateBuildConfig(esmx, options, buildTarget) {
50
55
  return createRspackConfig(esmx, buildTarget, options);
51
56
  }
52
57
  function rewriteRender(esmx) {
@@ -74,11 +79,18 @@ function rewriteBuild(esmx, options = {}) {
74
79
  }
75
80
  return async () => {
76
81
  const ok = await createRsBuild(
77
- targets.map((target) => generateBuildConfig(esmx, options, target))
82
+ await Promise.all(
83
+ targets.map(
84
+ (target) => generateBuildConfig(esmx, options, target)
85
+ )
86
+ )
78
87
  ).build();
79
88
  if (!ok) {
80
89
  return false;
81
90
  }
91
+ if (esmx.isProd) {
92
+ await updateManifestIntegrity(esmx);
93
+ }
82
94
  esmx.writeSync(
83
95
  esmx.resolvePath("dist/index.mjs"),
84
96
  `
@@ -100,3 +112,36 @@ start();
100
112
  return pack(esmx);
101
113
  };
102
114
  }
115
+ async function updateManifestIntegrity(esmx) {
116
+ const clientDir = esmx.resolvePath("dist/client");
117
+ const manifestPath = path.join(clientDir, "manifest.json");
118
+ const manifestExists = await fs.access(manifestPath).then(() => true).catch(() => false);
119
+ if (!manifestExists) {
120
+ console.warn(
121
+ `SRI generation skipped: client manifest not found at ${manifestPath}`
122
+ );
123
+ return;
124
+ }
125
+ const manifestContent = await fs.readFile(manifestPath, "utf-8");
126
+ const manifest = JSON.parse(manifestContent);
127
+ const integrity = {};
128
+ async function walkDir(dir, relativeDir = "") {
129
+ const entries = await fs.readdir(dir, { withFileTypes: true });
130
+ for (const entry of entries) {
131
+ const fullPath = path.join(dir, entry.name);
132
+ const relativePath = relativeDir ? `${relativeDir}/${entry.name}` : entry.name;
133
+ if (entry.isDirectory()) {
134
+ await walkDir(fullPath, relativePath);
135
+ } else if (entry.isFile() && (entry.name.endsWith(".mjs") || entry.name.endsWith(".js")) && !entry.name.includes("hot-update") && entry.name !== "manifest.json") {
136
+ const content = await fs.readFile(fullPath);
137
+ const hash = crypto.createHash("sha384").update(content).digest("base64");
138
+ integrity[relativePath] = `sha384-${hash}`;
139
+ }
140
+ }
141
+ }
142
+ await walkDir(clientDir);
143
+ if (Object.keys(integrity).length > 0) {
144
+ manifest.integrity = integrity;
145
+ await fs.writeFile(manifestPath, JSON.stringify(manifest, null, 4));
146
+ }
147
+ }
@@ -1,7 +1,7 @@
1
1
  import type { Esmx } from '@esmx/core';
2
2
  import type { RspackOptions } from '@rspack/core';
3
- import RspackChain from 'rspack-chain';
3
+ import { RspackChain } from 'rspack-chain';
4
4
  import type { RspackAppOptions } from './app';
5
5
  import type { BuildTarget } from './build-target';
6
- export declare function createChainConfig(esmx: Esmx, buildTarget: BuildTarget, options: RspackAppOptions): RspackChain;
7
- export declare function createRspackConfig(esmx: Esmx, buildTarget: BuildTarget, options: RspackAppOptions): RspackOptions;
6
+ export declare function createChainConfig(esmx: Esmx, buildTarget: BuildTarget, options: RspackAppOptions): Promise<RspackChain>;
7
+ export declare function createRspackConfig(esmx: Esmx, buildTarget: BuildTarget, options: RspackAppOptions): Promise<RspackOptions>;