@esmx/rspack 3.0.0-rc.77 → 3.0.0-rc.78

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.
@@ -0,0 +1,3 @@
1
+ import type RspackChain from 'rspack-chain';
2
+ import type { ParsedModuleLinkPluginOptions } from './types';
3
+ export declare function applyChainConfig(chain: RspackChain, opts: ParsedModuleLinkPluginOptions): void;
@@ -0,0 +1,114 @@
1
+ export function applyChainConfig(chain, opts) {
2
+ const isProduction = chain.get("mode") === "production";
3
+ chain.output.set("module", true).set("chunkFormat", "module").set("chunkLoading", "import").set("workerChunkLoading", "import");
4
+ chain.experiments({
5
+ ...chain.get("experiments"),
6
+ outputModule: true
7
+ });
8
+ if (isProduction) {
9
+ chain.output.library({
10
+ type: "modern-module"
11
+ });
12
+ chain.optimization.set("avoidEntryIife", true);
13
+ } else {
14
+ chain.output.library({
15
+ type: "module"
16
+ });
17
+ }
18
+ applyEntryConfig(chain, opts);
19
+ applyExternalsConfig(chain, opts);
20
+ }
21
+ function applyEntryConfig(chain, opts) {
22
+ if (chain.entryPoints.has("main")) {
23
+ const mainEntry = chain.entry("main");
24
+ if (mainEntry.values().length === 0) {
25
+ chain.entryPoints.clear();
26
+ }
27
+ }
28
+ for (const value of Object.values(opts.exports)) {
29
+ if (value.file) {
30
+ const entry = chain.entry(value.name);
31
+ for (const preEntry of opts.preEntries) {
32
+ entry.add(preEntry);
33
+ }
34
+ entry.add(value.file);
35
+ }
36
+ }
37
+ }
38
+ function applyExternalsConfig(chain, opts) {
39
+ const existingExternals = chain.get("externals") || [];
40
+ const externals = Array.isArray(existingExternals) ? [...existingExternals] : [existingExternals];
41
+ const compilerContext = chain.get("context") ?? process.cwd();
42
+ const externalFunc = createExternalsFunction(opts, compilerContext);
43
+ externals.push(externalFunc);
44
+ chain.externals(externals);
45
+ }
46
+ function createExternalsFunction(opts, compilerContext) {
47
+ const importMap = /* @__PURE__ */ new Map();
48
+ let initPromise = null;
49
+ const init = (resolvePath) => {
50
+ if (initPromise) return initPromise;
51
+ initPromise = (async () => {
52
+ await Promise.all(
53
+ Object.values(opts.exports).map(async (value) => {
54
+ const identifier = value.pkg ? value.name : value.identifier;
55
+ importMap.set(identifier, identifier);
56
+ importMap.set(value.name, identifier);
57
+ const resolvedPath = await resolvePath(value.file);
58
+ if (resolvedPath) {
59
+ importMap.set(resolvedPath, identifier);
60
+ }
61
+ })
62
+ );
63
+ for (const key of Object.keys(opts.imports)) {
64
+ importMap.set(key, key);
65
+ }
66
+ })();
67
+ return initPromise;
68
+ };
69
+ const match = async (request, context, resolvePath) => {
70
+ if (!request) return null;
71
+ if (opts.deps.length > 0) {
72
+ const matchedDep = opts.deps.find(
73
+ (dep) => request === dep || request.startsWith(`${dep}/`)
74
+ );
75
+ if (matchedDep) {
76
+ return request;
77
+ }
78
+ }
79
+ let importName = importMap.get(request);
80
+ if (!importName) {
81
+ const resolvedPath = await resolvePath(request, context);
82
+ if (resolvedPath) {
83
+ importName = importMap.get(resolvedPath);
84
+ }
85
+ }
86
+ return importName || null;
87
+ };
88
+ const FILE_EXT_REGEX = /\.worker\.(js|mjs|cjs|jsx|mjsx|cjsx|ts|mts|cts|tsx|mtsx|ctsx)$/i;
89
+ return async (data) => {
90
+ if (!data.request || !data.context || !data.contextInfo?.issuer || FILE_EXT_REGEX.test(data.contextInfo.issuer))
91
+ return;
92
+ const defaultContext = compilerContext;
93
+ const resolvePath = async (request, context = defaultContext) => {
94
+ if (!data.getResolve) {
95
+ return null;
96
+ }
97
+ const resolveFunc = data.getResolve();
98
+ return new Promise((resolve) => {
99
+ resolveFunc(context, request, (err, res) => {
100
+ resolve(typeof res === "string" ? res : null);
101
+ });
102
+ });
103
+ };
104
+ await init(resolvePath);
105
+ const matchedIdentifier = await match(
106
+ data.request,
107
+ data.context,
108
+ resolvePath
109
+ );
110
+ if (matchedIdentifier) {
111
+ return `module-import ${matchedIdentifier}`;
112
+ }
113
+ };
114
+ }
@@ -0,0 +1,4 @@
1
+ import type RspackChain from 'rspack-chain';
2
+ import type { ModuleLinkPluginOptions } from './types';
3
+ export declare function initModuleLink(chain: RspackChain, options: ModuleLinkPluginOptions): void;
4
+ export type { ModuleLinkPluginOptions } from './types';
@@ -0,0 +1,8 @@
1
+ import { applyChainConfig } from "./apply-chain-config.mjs";
2
+ import { ManifestPlugin } from "./manifest-plugin.mjs";
3
+ import { parseOptions } from "./parse.mjs";
4
+ export function initModuleLink(chain, options) {
5
+ const opts = parseOptions(options);
6
+ applyChainConfig(chain, opts);
7
+ chain.plugin("module-link-manifest").use(ManifestPlugin, [opts]);
8
+ }
@@ -0,0 +1,14 @@
1
+ import type { Compiler, StatsCompilation } from '@rspack/core';
2
+ import type { ManifestJsonExports, ParsedModuleLinkPluginOptions } from './types';
3
+ export declare const RSPACK_PLUGIN_NAME = "rspack-module-link-plugin";
4
+ export declare class ManifestPlugin {
5
+ private opts;
6
+ constructor(opts: ParsedModuleLinkPluginOptions);
7
+ apply(compiler: Compiler): void;
8
+ }
9
+ export declare function getExports(opts: ParsedModuleLinkPluginOptions, stats: StatsCompilation): ManifestJsonExports;
10
+ export declare function generateIdentifier({ root, name, filePath }: {
11
+ root: string;
12
+ name: string;
13
+ filePath: string;
14
+ }): string;
@@ -0,0 +1,141 @@
1
+ import upath from "upath";
2
+ export const RSPACK_PLUGIN_NAME = "rspack-module-link-plugin";
3
+ export class ManifestPlugin {
4
+ constructor(opts) {
5
+ this.opts = opts;
6
+ }
7
+ apply(compiler) {
8
+ const opts = this.opts;
9
+ const { Compilation } = compiler.rspack;
10
+ compiler.hooks.thisCompilation.tap(
11
+ RSPACK_PLUGIN_NAME,
12
+ (compilation) => {
13
+ let manifestJson = {
14
+ name: opts.name,
15
+ exports: {},
16
+ scopes: opts.scopes,
17
+ files: [],
18
+ chunks: {}
19
+ };
20
+ compilation.hooks.processAssets.tap(
21
+ {
22
+ name: RSPACK_PLUGIN_NAME,
23
+ stage: Compilation.PROCESS_ASSETS_STAGE_ADDITIONAL
24
+ },
25
+ (assets) => {
26
+ const stats = compilation.getStats().toJson({
27
+ hash: true,
28
+ entrypoints: true
29
+ });
30
+ const exports = getExports(opts, stats);
31
+ const resources = Object.keys(assets).map(transFileName).filter((file) => !file.includes("hot-update"));
32
+ manifestJson = {
33
+ name: opts.name,
34
+ exports,
35
+ scopes: opts.scopes,
36
+ files: resources,
37
+ chunks: getChunks(opts, compilation)
38
+ };
39
+ const { RawSource } = compiler.rspack.sources;
40
+ compilation.emitAsset(
41
+ "manifest.json",
42
+ new RawSource(JSON.stringify(manifestJson, null, 4))
43
+ );
44
+ }
45
+ );
46
+ if (opts.injectChunkName) {
47
+ compilation.hooks.processAssets.tap(
48
+ {
49
+ name: RSPACK_PLUGIN_NAME,
50
+ stage: Compilation.PROCESS_ASSETS_STAGE_ADDITIONS
51
+ },
52
+ (assets) => {
53
+ const { RawSource } = compiler.rspack.sources;
54
+ for (const [key, value] of Object.entries(
55
+ manifestJson.chunks
56
+ )) {
57
+ const asset = assets[value.js];
58
+ if (!asset) {
59
+ return;
60
+ }
61
+ const source = new RawSource(
62
+ `import.meta.chunkName = import.meta.chunkName ?? ${JSON.stringify(key)};
63
+ ${asset.source()}`
64
+ );
65
+ compilation.updateAsset(value.js, source);
66
+ }
67
+ }
68
+ );
69
+ }
70
+ }
71
+ );
72
+ }
73
+ }
74
+ function transFileName(fileName) {
75
+ return fileName.replace(/^.\//, "");
76
+ }
77
+ export function getExports(opts, stats) {
78
+ const entrypoints = stats.entrypoints || {};
79
+ const exports = {};
80
+ for (const [key, value] of Object.entries(entrypoints)) {
81
+ const asset = value.assets?.find((item) => {
82
+ return item.name.endsWith(opts.ext) && !item.name.includes("hot-update");
83
+ });
84
+ if (!asset) continue;
85
+ if (key in opts.exports) {
86
+ exports[key] = {
87
+ ...opts.exports[key],
88
+ file: asset.name
89
+ };
90
+ }
91
+ }
92
+ return exports;
93
+ }
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
+ });
102
+ const buildChunks = {};
103
+ if (!stats.chunks) return buildChunks;
104
+ 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
+ });
110
+ if (!module?.nameForCondition) continue;
111
+ const js = chunk.files?.find((file) => file.endsWith(opts.ext));
112
+ if (!js) continue;
113
+ const root = compilation.options.context ?? process.cwd();
114
+ const name = generateIdentifier({
115
+ root,
116
+ name: opts.name,
117
+ filePath: module.nameForCondition
118
+ });
119
+ const css = chunk.files?.filter((file) => file.endsWith(".css")) ?? [];
120
+ const resources = chunk.auxiliaryFiles ?? [];
121
+ buildChunks[name] = {
122
+ name,
123
+ js,
124
+ css,
125
+ resources
126
+ };
127
+ }
128
+ return buildChunks;
129
+ }
130
+ export function generateIdentifier({
131
+ root,
132
+ name,
133
+ filePath
134
+ }) {
135
+ const unixFilePath = upath.toUnix(filePath);
136
+ if (!root) {
137
+ return `${name}@${unixFilePath}`;
138
+ }
139
+ const file = upath.relative(upath.toUnix(root), unixFilePath);
140
+ return `${name}@${file}`;
141
+ }
@@ -0,0 +1,2 @@
1
+ import type { ModuleLinkPluginOptions, ParsedModuleLinkPluginOptions } from './types';
2
+ export declare function parseOptions(options: ModuleLinkPluginOptions): ParsedModuleLinkPluginOptions;
@@ -0,0 +1,24 @@
1
+ export function parseOptions(options) {
2
+ const exports = {};
3
+ if (options.exports) {
4
+ for (const [name, item] of Object.entries(options.exports)) {
5
+ exports[name] = {
6
+ name,
7
+ pkg: !!item.pkg,
8
+ file: item.file,
9
+ identifier: `${options.name}/${name}`
10
+ };
11
+ }
12
+ }
13
+ const deps = (options.deps ?? []).filter((name) => name !== options.name);
14
+ return {
15
+ name: options.name,
16
+ ext: options.ext ? `.${options.ext}` : ".mjs",
17
+ exports,
18
+ imports: options.imports ?? {},
19
+ scopes: options.scopes ?? {},
20
+ injectChunkName: options.injectChunkName ?? false,
21
+ preEntries: options.preEntries ?? [],
22
+ deps
23
+ };
24
+ }
@@ -0,0 +1,25 @@
1
+ import type { ManifestJsonExports } from '@esmx/core';
2
+ export type { ManifestJson, ManifestJsonChunks, ManifestJsonChunk, ManifestJsonExport, ManifestJsonExports } from '@esmx/core';
3
+ export interface ModuleLinkPluginOptions {
4
+ name: string;
5
+ ext?: string;
6
+ imports?: Record<string, string>;
7
+ scopes?: Record<string, Record<string, string>>;
8
+ exports?: Record<string, {
9
+ pkg?: boolean;
10
+ file: string;
11
+ }>;
12
+ injectChunkName?: boolean;
13
+ preEntries?: string[];
14
+ deps?: string[];
15
+ }
16
+ export interface ParsedModuleLinkPluginOptions {
17
+ name: string;
18
+ ext: string;
19
+ exports: ManifestJsonExports;
20
+ imports: Record<string, string>;
21
+ scopes: Record<string, Record<string, string>>;
22
+ injectChunkName: boolean;
23
+ preEntries: string[];
24
+ deps: string[];
25
+ }
File without changes
@@ -58,14 +58,7 @@ function rewriteRender(esmx) {
58
58
  const module = await vmImport(
59
59
  `${esmx.name}/src/entry.server`,
60
60
  import.meta.url,
61
- {
62
- console,
63
- setTimeout,
64
- clearTimeout,
65
- process,
66
- URL,
67
- global
68
- }
61
+ global
69
62
  );
70
63
  const serverRender = module[rc.entryName];
71
64
  if (typeof serverRender === "function") {
@@ -100,6 +93,8 @@ async function start() {
100
93
  start();
101
94
  `.trim()
102
95
  );
96
+ console.log("\n");
97
+ console.log(esmx.generateSizeReport().text);
103
98
  return pack(esmx);
104
99
  };
105
100
  }
@@ -1,9 +1,7 @@
1
- import {
2
- moduleLinkPlugin
3
- } from "@esmx/rspack-module-link-plugin";
4
1
  import { rspack } from "@rspack/core";
5
2
  import RspackChain from "rspack-chain";
6
3
  import nodeExternals from "webpack-node-externals";
4
+ import { initModuleLink } from "../module-link/index.mjs";
7
5
  export function createChainConfig(esmx, buildTarget, options) {
8
6
  const isHot = buildTarget === "client" && !esmx.isProd;
9
7
  const isClient = buildTarget === "client";
@@ -35,12 +33,13 @@ export function createChainConfig(esmx, buildTarget, options) {
35
33
  prefix: buildTarget
36
34
  }
37
35
  ]);
38
- config.plugin("module-link").use(moduleLinkPlugin, [createModuleLinkConfig(esmx, buildTarget)]);
39
36
  if (isHot) {
40
37
  config.plugin("hmr").use(rspack.HotModuleReplacementPlugin);
41
38
  }
42
39
  config.module.parser.set("javascript", {
43
- url: isClient ? true : "relative"
40
+ url: isClient ? true : "relative",
41
+ importMeta: false,
42
+ strictExportPresence: true
44
43
  });
45
44
  config.module.generator.set("asset", {
46
45
  emit: isClient
@@ -65,8 +64,12 @@ export function createChainConfig(esmx, buildTarget, options) {
65
64
  ]);
66
65
  }
67
66
  config.experiments({
68
- nativeWatcher: true
67
+ nativeWatcher: true,
68
+ rspackFuture: {
69
+ bundlerInfo: { force: false }
70
+ }
69
71
  });
72
+ initModuleLink(config, createModuleLinkConfig(esmx, buildTarget));
70
73
  return config;
71
74
  }
72
75
  function createModuleLinkConfig(esmx, buildTarget) {
package/package.json CHANGED
@@ -63,8 +63,7 @@
63
63
  }
64
64
  },
65
65
  "dependencies": {
66
- "@esmx/import": "3.0.0-rc.77",
67
- "@esmx/rspack-module-link-plugin": "3.0.0-rc.77",
66
+ "@esmx/import": "3.0.0-rc.78",
68
67
  "@npmcli/arborist": "^9.1.6",
69
68
  "@rspack/core": "1.6.5",
70
69
  "css-loader": "^7.1.2",
@@ -74,13 +73,14 @@
74
73
  "rspack-chain": "^1.4.1",
75
74
  "style-loader": "^4.0.0",
76
75
  "style-resources-loader": "^1.5.0",
76
+ "upath": "^2.0.1",
77
77
  "webpack-hot-middleware": "^2.26.1",
78
78
  "webpack-node-externals": "~3.0.0",
79
79
  "worker-rspack-loader": "^3.1.2"
80
80
  },
81
81
  "devDependencies": {
82
82
  "@biomejs/biome": "2.3.4",
83
- "@esmx/core": "3.0.0-rc.77",
83
+ "@esmx/core": "3.0.0-rc.78",
84
84
  "@types/node": "^24.10.0",
85
85
  "@types/npmcli__arborist": "^6.3.1",
86
86
  "@types/pacote": "^11.1.8",
@@ -91,7 +91,7 @@
91
91
  "unbuild": "3.6.1",
92
92
  "vitest": "3.2.4"
93
93
  },
94
- "version": "3.0.0-rc.77",
94
+ "version": "3.0.0-rc.78",
95
95
  "type": "module",
96
96
  "private": false,
97
97
  "exports": {
@@ -110,5 +110,5 @@
110
110
  "template",
111
111
  "public"
112
112
  ],
113
- "gitHead": "c56120965914f33eca4b21336f705b3cb5fc7f93"
113
+ "gitHead": "aa44e33ab3e05817977c0fda6148abff8351651f"
114
114
  }
@@ -0,0 +1,180 @@
1
+ import type { ExternalItem, ExternalItemFunctionData } from '@rspack/core';
2
+ import type RspackChain from 'rspack-chain';
3
+ import type { ParsedModuleLinkPluginOptions } from './types';
4
+
5
+ type ResolvePath = (
6
+ request: string,
7
+ context?: string
8
+ ) => Promise<string | null>;
9
+
10
+ export function applyChainConfig(
11
+ chain: RspackChain,
12
+ opts: ParsedModuleLinkPluginOptions
13
+ ): void {
14
+ const isProduction = chain.get('mode') === 'production';
15
+
16
+ chain.output
17
+ .set('module', true)
18
+ .set('chunkFormat', 'module')
19
+ .set('chunkLoading', 'import')
20
+ .set('workerChunkLoading', 'import');
21
+ chain.experiments({
22
+ ...chain.get('experiments'),
23
+ outputModule: true
24
+ });
25
+
26
+ if (isProduction) {
27
+ chain.output.library({
28
+ type: 'modern-module'
29
+ });
30
+ chain.optimization.set('avoidEntryIife', true);
31
+ } else {
32
+ chain.output.library({
33
+ type: 'module'
34
+ });
35
+ }
36
+
37
+ applyEntryConfig(chain, opts);
38
+
39
+ applyExternalsConfig(chain, opts);
40
+ }
41
+
42
+ function applyEntryConfig(
43
+ chain: RspackChain,
44
+ opts: ParsedModuleLinkPluginOptions
45
+ ): void {
46
+ if (chain.entryPoints.has('main')) {
47
+ const mainEntry = chain.entry('main');
48
+ if (mainEntry.values().length === 0) {
49
+ chain.entryPoints.clear();
50
+ }
51
+ }
52
+
53
+ for (const value of Object.values(opts.exports)) {
54
+ if (value.file) {
55
+ const entry = chain.entry(value.name);
56
+ for (const preEntry of opts.preEntries) {
57
+ entry.add(preEntry);
58
+ }
59
+ entry.add(value.file);
60
+ }
61
+ }
62
+ }
63
+
64
+ function applyExternalsConfig(
65
+ chain: RspackChain,
66
+ opts: ParsedModuleLinkPluginOptions
67
+ ): void {
68
+ const existingExternals = chain.get('externals') || [];
69
+ const externals: ExternalItem[] = Array.isArray(existingExternals)
70
+ ? [...existingExternals]
71
+ : [existingExternals];
72
+
73
+ const compilerContext = chain.get('context') ?? process.cwd();
74
+ const externalFunc = createExternalsFunction(opts, compilerContext);
75
+ externals.push(externalFunc);
76
+
77
+ chain.externals(externals);
78
+ }
79
+
80
+ function createExternalsFunction(
81
+ opts: ParsedModuleLinkPluginOptions,
82
+ compilerContext: string
83
+ ) {
84
+ const importMap = new Map<string, string>();
85
+ let initPromise: Promise<void> | null = null;
86
+
87
+ const init = (resolvePath: ResolvePath): Promise<void> => {
88
+ if (initPromise) return initPromise;
89
+
90
+ initPromise = (async () => {
91
+ await Promise.all(
92
+ Object.values(opts.exports).map(async (value) => {
93
+ const identifier = value.pkg
94
+ ? value.name
95
+ : value.identifier;
96
+ importMap.set(identifier, identifier);
97
+ importMap.set(value.name, identifier);
98
+
99
+ const resolvedPath = await resolvePath(value.file);
100
+ if (resolvedPath) {
101
+ importMap.set(resolvedPath, identifier);
102
+ }
103
+ })
104
+ );
105
+
106
+ for (const key of Object.keys(opts.imports)) {
107
+ importMap.set(key, key);
108
+ }
109
+ })();
110
+
111
+ return initPromise;
112
+ };
113
+
114
+ const match = async (
115
+ request: string,
116
+ context: string,
117
+ resolvePath: ResolvePath
118
+ ): Promise<string | null> => {
119
+ if (!request) return null;
120
+
121
+ if (opts.deps.length > 0) {
122
+ const matchedDep = opts.deps.find(
123
+ (dep) => request === dep || request.startsWith(`${dep}/`)
124
+ );
125
+ if (matchedDep) {
126
+ return request;
127
+ }
128
+ }
129
+
130
+ let importName = importMap.get(request);
131
+ if (!importName) {
132
+ const resolvedPath = await resolvePath(request, context);
133
+ if (resolvedPath) {
134
+ importName = importMap.get(resolvedPath);
135
+ }
136
+ }
137
+
138
+ return importName || null;
139
+ };
140
+
141
+ const FILE_EXT_REGEX =
142
+ /\.worker\.(js|mjs|cjs|jsx|mjsx|cjsx|ts|mts|cts|tsx|mtsx|ctsx)$/i;
143
+
144
+ return async (data: ExternalItemFunctionData) => {
145
+ if (
146
+ !data.request ||
147
+ !data.context ||
148
+ !data.contextInfo?.issuer ||
149
+ FILE_EXT_REGEX.test(data.contextInfo.issuer)
150
+ )
151
+ return;
152
+
153
+ const defaultContext = compilerContext;
154
+ const resolvePath: ResolvePath = async (
155
+ request: string,
156
+ context = defaultContext
157
+ ): Promise<string | null> => {
158
+ if (!data.getResolve) {
159
+ return null;
160
+ }
161
+ const resolveFunc = data.getResolve();
162
+ return new Promise<string | null>((resolve) => {
163
+ resolveFunc(context, request, (err, res) => {
164
+ resolve(typeof res === 'string' ? res : null);
165
+ });
166
+ });
167
+ };
168
+
169
+ await init(resolvePath);
170
+ const matchedIdentifier = await match(
171
+ data.request,
172
+ data.context,
173
+ resolvePath
174
+ );
175
+
176
+ if (matchedIdentifier) {
177
+ return `module-import ${matchedIdentifier}`;
178
+ }
179
+ };
180
+ }
@@ -0,0 +1,18 @@
1
+ import type RspackChain from 'rspack-chain';
2
+ import { applyChainConfig } from './apply-chain-config';
3
+ import { ManifestPlugin } from './manifest-plugin';
4
+ import { parseOptions } from './parse';
5
+ import type { ModuleLinkPluginOptions } from './types';
6
+
7
+ export function initModuleLink(
8
+ chain: RspackChain,
9
+ options: ModuleLinkPluginOptions
10
+ ): void {
11
+ const opts = parseOptions(options);
12
+
13
+ applyChainConfig(chain, opts);
14
+
15
+ chain.plugin('module-link-manifest').use(ManifestPlugin, [opts]);
16
+ }
17
+
18
+ export type { ModuleLinkPluginOptions } from './types';
@@ -0,0 +1,175 @@
1
+ import type { Compilation, Compiler, StatsCompilation } from '@rspack/core';
2
+ import upath from 'upath';
3
+ import type {
4
+ ManifestJson,
5
+ ManifestJsonChunks,
6
+ ManifestJsonExports,
7
+ ParsedModuleLinkPluginOptions
8
+ } from './types';
9
+
10
+ export const RSPACK_PLUGIN_NAME = 'rspack-module-link-plugin';
11
+
12
+ export class ManifestPlugin {
13
+ constructor(private opts: ParsedModuleLinkPluginOptions) {}
14
+
15
+ apply(compiler: Compiler) {
16
+ const opts = this.opts;
17
+ const { Compilation } = compiler.rspack;
18
+ compiler.hooks.thisCompilation.tap(
19
+ RSPACK_PLUGIN_NAME,
20
+ (compilation) => {
21
+ let manifestJson: ManifestJson = {
22
+ name: opts.name,
23
+ exports: {},
24
+ scopes: opts.scopes,
25
+ files: [],
26
+ chunks: {}
27
+ };
28
+
29
+ compilation.hooks.processAssets.tap(
30
+ {
31
+ name: RSPACK_PLUGIN_NAME,
32
+ stage: Compilation.PROCESS_ASSETS_STAGE_ADDITIONAL
33
+ },
34
+ (assets) => {
35
+ const stats = compilation.getStats().toJson({
36
+ hash: true,
37
+ entrypoints: true
38
+ });
39
+
40
+ const exports = getExports(opts, stats);
41
+ const resources = Object.keys(assets)
42
+ .map(transFileName)
43
+ .filter((file) => !file.includes('hot-update'));
44
+ manifestJson = {
45
+ name: opts.name,
46
+ exports: exports,
47
+ scopes: opts.scopes,
48
+ files: resources,
49
+ chunks: getChunks(opts, compilation)
50
+ };
51
+ const { RawSource } = compiler.rspack.sources;
52
+
53
+ compilation.emitAsset(
54
+ 'manifest.json',
55
+ new RawSource(JSON.stringify(manifestJson, null, 4))
56
+ );
57
+ }
58
+ );
59
+
60
+ if (opts.injectChunkName) {
61
+ compilation.hooks.processAssets.tap(
62
+ {
63
+ name: RSPACK_PLUGIN_NAME,
64
+ stage: Compilation.PROCESS_ASSETS_STAGE_ADDITIONS
65
+ },
66
+ (assets) => {
67
+ const { RawSource } = compiler.rspack.sources;
68
+ for (const [key, value] of Object.entries(
69
+ manifestJson.chunks
70
+ )) {
71
+ const asset = assets[value.js];
72
+ if (!asset) {
73
+ return;
74
+ }
75
+ const source = new RawSource(
76
+ `import.meta.chunkName = import.meta.chunkName ?? ${JSON.stringify(key)};\n${asset.source()}`
77
+ );
78
+
79
+ compilation.updateAsset(value.js, source);
80
+ }
81
+ }
82
+ );
83
+ }
84
+ }
85
+ );
86
+ }
87
+ }
88
+
89
+ function transFileName(fileName: string): string {
90
+ return fileName.replace(/^.\//, '');
91
+ }
92
+
93
+ export function getExports(
94
+ opts: ParsedModuleLinkPluginOptions,
95
+ stats: StatsCompilation
96
+ ): ManifestJsonExports {
97
+ const entrypoints = stats.entrypoints || {};
98
+ const exports: ManifestJsonExports = {};
99
+
100
+ for (const [key, value] of Object.entries(entrypoints)) {
101
+ const asset = value.assets?.find((item) => {
102
+ return (
103
+ item.name.endsWith(opts.ext) &&
104
+ !item.name.includes('hot-update')
105
+ );
106
+ });
107
+ if (!asset) continue;
108
+ if (key in opts.exports) {
109
+ exports[key] = {
110
+ ...opts.exports[key],
111
+ file: asset.name
112
+ };
113
+ }
114
+ }
115
+ return exports;
116
+ }
117
+
118
+ function getChunks(
119
+ opts: ParsedModuleLinkPluginOptions,
120
+ compilation: Compilation
121
+ ) {
122
+ const stats = compilation.getStats().toJson({
123
+ all: false,
124
+ chunks: true,
125
+ modules: true,
126
+ chunkModules: true,
127
+ ids: true
128
+ });
129
+ const buildChunks: ManifestJsonChunks = {};
130
+ if (!stats.chunks) return buildChunks;
131
+
132
+ for (const chunk of stats.chunks) {
133
+ const module = chunk.modules
134
+ ?.sort((a, b) => {
135
+ return (a.index ?? -1) - (b?.index ?? -1);
136
+ })
137
+ ?.find((module) => {
138
+ return module.moduleType?.includes('javascript/');
139
+ });
140
+ if (!module?.nameForCondition) continue;
141
+
142
+ const js = chunk.files?.find((file) => file.endsWith(opts.ext));
143
+ if (!js) continue;
144
+
145
+ const root = compilation.options.context ?? process.cwd();
146
+ const name = generateIdentifier({
147
+ root,
148
+ name: opts.name,
149
+ filePath: module.nameForCondition
150
+ });
151
+
152
+ const css = chunk.files?.filter((file) => file.endsWith('.css')) ?? [];
153
+ const resources = chunk.auxiliaryFiles ?? [];
154
+ buildChunks[name] = {
155
+ name,
156
+ js,
157
+ css,
158
+ resources
159
+ };
160
+ }
161
+ return buildChunks;
162
+ }
163
+
164
+ export function generateIdentifier({
165
+ root,
166
+ name,
167
+ filePath
168
+ }: { root: string; name: string; filePath: string }) {
169
+ const unixFilePath = upath.toUnix(filePath);
170
+ if (!root) {
171
+ return `${name}@${unixFilePath}`;
172
+ }
173
+ const file = upath.relative(upath.toUnix(root), unixFilePath);
174
+ return `${name}@${file}`;
175
+ }
@@ -0,0 +1,31 @@
1
+ import type {
2
+ ModuleLinkPluginOptions,
3
+ ParsedModuleLinkPluginOptions
4
+ } from './types';
5
+
6
+ export function parseOptions(
7
+ options: ModuleLinkPluginOptions
8
+ ): ParsedModuleLinkPluginOptions {
9
+ const exports: ParsedModuleLinkPluginOptions['exports'] = {};
10
+ if (options.exports) {
11
+ for (const [name, item] of Object.entries(options.exports)) {
12
+ exports[name] = {
13
+ name,
14
+ pkg: !!item.pkg,
15
+ file: item.file,
16
+ identifier: `${options.name}/${name}`
17
+ };
18
+ }
19
+ }
20
+ const deps = (options.deps ?? []).filter((name) => name !== options.name);
21
+ return {
22
+ name: options.name,
23
+ ext: options.ext ? `.${options.ext}` : '.mjs',
24
+ exports,
25
+ imports: options.imports ?? {},
26
+ scopes: options.scopes ?? {},
27
+ injectChunkName: options.injectChunkName ?? false,
28
+ preEntries: options.preEntries ?? [],
29
+ deps
30
+ };
31
+ }
@@ -0,0 +1,31 @@
1
+ import type { ManifestJsonExports } from '@esmx/core';
2
+
3
+ export type {
4
+ ManifestJson,
5
+ ManifestJsonChunks,
6
+ ManifestJsonChunk,
7
+ ManifestJsonExport,
8
+ ManifestJsonExports
9
+ } from '@esmx/core';
10
+
11
+ export interface ModuleLinkPluginOptions {
12
+ name: string;
13
+ ext?: string;
14
+ imports?: Record<string, string>;
15
+ scopes?: Record<string, Record<string, string>>;
16
+ exports?: Record<string, { pkg?: boolean; file: string }>;
17
+ injectChunkName?: boolean;
18
+ preEntries?: string[];
19
+ deps?: string[];
20
+ }
21
+
22
+ export interface ParsedModuleLinkPluginOptions {
23
+ name: string;
24
+ ext: string;
25
+ exports: ManifestJsonExports;
26
+ imports: Record<string, string>;
27
+ scopes: Record<string, Record<string, string>>;
28
+ injectChunkName: boolean;
29
+ preEntries: string[];
30
+ deps: string[];
31
+ }
package/src/rspack/app.ts CHANGED
@@ -271,14 +271,7 @@ function rewriteRender(esmx: Esmx) {
271
271
  const module = await vmImport(
272
272
  `${esmx.name}/src/entry.server`,
273
273
  import.meta.url,
274
- {
275
- console,
276
- setTimeout,
277
- clearTimeout,
278
- process,
279
- URL,
280
- global
281
- }
274
+ global
282
275
  );
283
276
  const serverRender: ServerRenderHandle = module[rc.entryName];
284
277
  if (typeof serverRender === 'function') {
@@ -314,6 +307,8 @@ async function start() {
314
307
  start();
315
308
  `.trim()
316
309
  );
310
+ console.log('\n');
311
+ console.log(esmx.generateSizeReport().text);
317
312
  return pack(esmx);
318
313
  };
319
314
  }
@@ -1,12 +1,10 @@
1
1
  import type { Esmx } from '@esmx/core';
2
- import {
3
- type ModuleLinkPluginOptions,
4
- moduleLinkPlugin
5
- } from '@esmx/rspack-module-link-plugin';
6
2
  import { rspack } from '@rspack/core';
7
3
  import type { RspackOptions } from '@rspack/core';
8
4
  import RspackChain from 'rspack-chain';
9
5
  import nodeExternals from 'webpack-node-externals';
6
+ import type { ModuleLinkPluginOptions } from '../module-link';
7
+ import { initModuleLink } from '../module-link';
10
8
  import type { RspackAppOptions } from './app';
11
9
  import type { BuildTarget } from './build-target';
12
10
 
@@ -61,16 +59,14 @@ export function createChainConfig(
61
59
  }
62
60
  ]);
63
61
 
64
- config
65
- .plugin('module-link')
66
- .use(moduleLinkPlugin, [createModuleLinkConfig(esmx, buildTarget)]);
67
-
68
62
  if (isHot) {
69
63
  config.plugin('hmr').use(rspack.HotModuleReplacementPlugin);
70
64
  }
71
65
 
72
66
  config.module.parser.set('javascript', {
73
- url: isClient ? true : 'relative'
67
+ url: isClient ? true : 'relative',
68
+ importMeta: false,
69
+ strictExportPresence: true
74
70
  });
75
71
 
76
72
  config.module.generator.set('asset', {
@@ -103,9 +99,14 @@ export function createChainConfig(
103
99
  ]);
104
100
  }
105
101
  config.experiments({
106
- nativeWatcher: true
102
+ nativeWatcher: true,
103
+ rspackFuture: {
104
+ bundlerInfo: { force: false }
105
+ }
107
106
  });
108
107
 
108
+ initModuleLink(config, createModuleLinkConfig(esmx, buildTarget));
109
+
109
110
  return config;
110
111
  }
111
112