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

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,157 @@
1
+ import type { ExternalItem, ExternalItemFunctionData } from '@rspack/core';
2
+ import type RspackChain from 'rspack-chain';
3
+ import type { ParsedModuleLinkPluginOptions } from './types';
4
+
5
+ export function applyEntryConfig(
6
+ chain: RspackChain,
7
+ opts: ParsedModuleLinkPluginOptions
8
+ ): void {
9
+ if (chain.entryPoints.has('main')) {
10
+ const mainEntry = chain.entry('main');
11
+ if (mainEntry.values().length === 0) {
12
+ chain.entryPoints.clear();
13
+ }
14
+ }
15
+
16
+ for (const value of Object.values(opts.exports)) {
17
+ if (value.file) {
18
+ const entry = chain.entry(value.name);
19
+ for (const preEntry of opts.preEntries) {
20
+ entry.add(preEntry);
21
+ }
22
+ entry.add(value.file);
23
+ }
24
+ }
25
+ }
26
+
27
+ export function applyModuleConfig(chain: RspackChain) {
28
+ chain.output
29
+ .set('module', true)
30
+ .set('chunkFormat', 'module')
31
+ .set('chunkLoading', 'import')
32
+ .set('workerChunkLoading', 'import');
33
+ chain.output.library({
34
+ type: 'module'
35
+ });
36
+ }
37
+
38
+ export function applyExternalsConfig(
39
+ chain: RspackChain,
40
+ opts: ParsedModuleLinkPluginOptions
41
+ ): void {
42
+ const existingExternals = chain.get('externals') || [];
43
+ const externals: ExternalItem[] = Array.isArray(existingExternals)
44
+ ? [...existingExternals]
45
+ : [existingExternals];
46
+
47
+ const compilerContext = chain.get('context') ?? process.cwd();
48
+ const externalFunc = createExternalsFunction(opts, compilerContext);
49
+ externals.push(externalFunc);
50
+
51
+ chain.externals(externals);
52
+ }
53
+
54
+ function createExternalsFunction(
55
+ opts: ParsedModuleLinkPluginOptions,
56
+ compilerContext: string
57
+ ) {
58
+ const importMap = new Map<string, string>();
59
+ let initPromise: Promise<void> | null = null;
60
+ type ResolvePath = (
61
+ request: string,
62
+ context?: string
63
+ ) => Promise<string | null>;
64
+ const init = (resolvePath: ResolvePath): Promise<void> => {
65
+ if (initPromise) return initPromise;
66
+
67
+ initPromise = (async () => {
68
+ await Promise.all(
69
+ Object.values(opts.exports).map(async (value) => {
70
+ const identifier = value.pkg
71
+ ? value.name
72
+ : value.identifier;
73
+ importMap.set(identifier, identifier);
74
+ importMap.set(value.name, identifier);
75
+
76
+ const resolvedPath = await resolvePath(value.file);
77
+ if (resolvedPath) {
78
+ importMap.set(resolvedPath, identifier);
79
+ }
80
+ })
81
+ );
82
+
83
+ for (const key of Object.keys(opts.imports)) {
84
+ importMap.set(key, key);
85
+ }
86
+ })();
87
+
88
+ return initPromise;
89
+ };
90
+
91
+ const match = async (
92
+ request: string,
93
+ context: string,
94
+ resolvePath: ResolvePath
95
+ ): Promise<string | null> => {
96
+ if (!request) return null;
97
+
98
+ if (opts.deps.length > 0) {
99
+ const matchedDep = opts.deps.find(
100
+ (dep) => request === dep || request.startsWith(`${dep}/`)
101
+ );
102
+ if (matchedDep) {
103
+ return request;
104
+ }
105
+ }
106
+
107
+ let importName = importMap.get(request);
108
+ if (!importName) {
109
+ const resolvedPath = await resolvePath(request, context);
110
+ if (resolvedPath) {
111
+ importName = importMap.get(resolvedPath);
112
+ }
113
+ }
114
+
115
+ return importName || null;
116
+ };
117
+
118
+ const FILE_EXT_REGEX =
119
+ /\.worker\.(js|mjs|cjs|jsx|mjsx|cjsx|ts|mts|cts|tsx|mtsx|ctsx)$/i;
120
+
121
+ return async (data: ExternalItemFunctionData) => {
122
+ if (
123
+ !data.request ||
124
+ !data.context ||
125
+ !data.contextInfo?.issuer ||
126
+ FILE_EXT_REGEX.test(data.contextInfo.issuer)
127
+ )
128
+ return;
129
+
130
+ const defaultContext = compilerContext;
131
+ const resolvePath: ResolvePath = async (
132
+ request: string,
133
+ context = defaultContext
134
+ ): Promise<string | null> => {
135
+ if (!data.getResolve) {
136
+ return null;
137
+ }
138
+ const resolveFunc = data.getResolve();
139
+ return new Promise<string | null>((resolve) => {
140
+ resolveFunc(context, request, (err, res) => {
141
+ resolve(typeof res === 'string' ? res : null);
142
+ });
143
+ });
144
+ };
145
+
146
+ await init(resolvePath);
147
+ const matchedIdentifier = await match(
148
+ data.request,
149
+ data.context,
150
+ resolvePath
151
+ );
152
+
153
+ if (matchedIdentifier) {
154
+ return `module-import ${matchedIdentifier}`;
155
+ }
156
+ };
157
+ }
@@ -0,0 +1,24 @@
1
+ import type RspackChain from 'rspack-chain';
2
+ import {
3
+ applyEntryConfig,
4
+ applyExternalsConfig,
5
+ applyModuleConfig
6
+ } from './config';
7
+ import type { ParsedModuleLinkPluginOptions } from './types';
8
+
9
+ export function applyChainConfig1(
10
+ chain: RspackChain,
11
+ opts: ParsedModuleLinkPluginOptions
12
+ ): void {
13
+ applyEntryConfig(chain, opts);
14
+ applyExternalsConfig(chain, opts);
15
+
16
+ // Set module compilation configuration
17
+ applyModuleConfig(chain);
18
+ if (chain.get('mode') === 'production') {
19
+ chain.output.library({
20
+ type: 'modern-module'
21
+ });
22
+ chain.optimization.set('avoidEntryIife', true);
23
+ }
24
+ }
@@ -0,0 +1,28 @@
1
+ import { rspack } from '@rspack/core';
2
+ import type RspackChain from 'rspack-chain';
3
+ import {
4
+ applyEntryConfig,
5
+ applyExternalsConfig,
6
+ applyModuleConfig
7
+ } from './config';
8
+ import type { ParsedModuleLinkPluginOptions } from './types';
9
+
10
+ export function applyChainConfig2(
11
+ chain: RspackChain,
12
+ opts: ParsedModuleLinkPluginOptions
13
+ ): void {
14
+ applyEntryConfig(chain, opts);
15
+ applyExternalsConfig(chain, opts);
16
+
17
+ // Set module compilation configuration
18
+ if (chain.get('mode') === 'production') {
19
+ chain.output.set('module', true);
20
+
21
+ chain
22
+ .plugin('esm-library')
23
+ .use(new rspack.experiments.EsmLibraryPlugin());
24
+ chain.optimization.set('runtimeChunk', 'single');
25
+ } else {
26
+ applyModuleConfig(chain);
27
+ }
28
+ }
@@ -0,0 +1,19 @@
1
+ import type RspackChain from 'rspack-chain';
2
+ import { applyChainConfig1 } from './config1';
3
+ // import { applyChainConfig2 } from './config2';
4
+ import { ManifestPlugin } from './manifest-plugin';
5
+ import { parseOptions } from './parse';
6
+ import type { ModuleLinkPluginOptions } from './types';
7
+
8
+ export function initModuleLink(
9
+ chain: RspackChain,
10
+ options: ModuleLinkPluginOptions
11
+ ): void {
12
+ const opts = parseOptions(options);
13
+ applyChainConfig1(chain, opts);
14
+ // applyChainConfig2(chain, opts);
15
+
16
+ chain.plugin('module-link-manifest').use(ManifestPlugin, [opts]);
17
+ }
18
+
19
+ export type { ModuleLinkPluginOptions } from './types';
@@ -0,0 +1,179 @@
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
+ for (const [key, value] of Object.entries(entrypoints)) {
100
+ const asset = value.assets?.find((item) => {
101
+ return (
102
+ item.name.endsWith(opts.ext) &&
103
+ item.name.startsWith(key) &&
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
+ }: {
169
+ root: string;
170
+ name: string;
171
+ filePath: string;
172
+ }) {
173
+ const unixFilePath = upath.toUnix(filePath);
174
+ if (!root) {
175
+ return `${name}@${unixFilePath}`;
176
+ }
177
+ const file = upath.relative(upath.toUnix(root), unixFilePath);
178
+ return `${name}@${file}`;
179
+ }
@@ -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
+ ManifestJsonChunk,
6
+ ManifestJsonChunks,
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
@@ -1,13 +1,13 @@
1
1
  import { pathToFileURL } from 'node:url';
2
2
  import {
3
3
  type App,
4
+ createApp,
4
5
  type Esmx,
5
6
  type Middleware,
7
+ mergeMiddlewares,
6
8
  RenderContext,
7
9
  type RenderContextOptions,
8
- type ServerRenderHandle,
9
- createApp,
10
- mergeMiddlewares
10
+ type ServerRenderHandle
11
11
  } from '@esmx/core';
12
12
  import { createVmImport } from '@esmx/import';
13
13
  import type { RspackOptions } from '@rspack/core';
@@ -239,14 +239,14 @@ async function createMiddleware(
239
239
  ]);
240
240
  rsBuild.watch();
241
241
 
242
- // @ts-ignore
242
+ // @ts-expect-error
243
243
  const hot = hotMiddleware(rsBuild.compilers[0], {
244
244
  path: `${esmx.basePath}hot-middleware`
245
245
  });
246
246
  return [
247
247
  (req, res, next) => {
248
248
  if (req.url?.startsWith(`${esmx.basePath}hot-middleware`)) {
249
- // @ts-ignore
249
+ // @ts-expect-error
250
250
  return hot(req, res, next);
251
251
  }
252
252
  return next();
@@ -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
- import { rspack } from '@rspack/core';
7
2
  import type { RspackOptions } from '@rspack/core';
3
+ import { rspack } 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
 
@@ -20,14 +18,14 @@ export function createChainConfig(
20
18
  const isServer = buildTarget === 'server';
21
19
  const isNode = buildTarget === 'node';
22
20
 
23
- const config = new RspackChain();
21
+ const chain = new RspackChain();
24
22
 
25
- config.context(esmx.root);
26
- config.mode(esmx.isProd ? 'production' : 'development');
27
- config.target(isClient ? 'web' : 'node24');
28
- config.cache(!esmx.isProd);
23
+ chain.context(esmx.root);
24
+ chain.mode(esmx.isProd ? 'production' : 'development');
25
+ chain.target(isClient ? 'web' : 'node24');
26
+ chain.cache(!esmx.isProd);
29
27
 
30
- config.output
28
+ chain.output
31
29
  .clean(esmx.isProd)
32
30
  .filename(
33
31
  !isNode && esmx.isProd
@@ -43,70 +41,78 @@ export function createChainConfig(
43
41
  isClient ? 'auto' : `${esmx.basePathPlaceholder}${esmx.basePath}`
44
42
  );
45
43
 
46
- config.output.set(
44
+ chain.output.set(
47
45
  'cssFilename',
48
46
  esmx.isProd ? '[name].[contenthash:8].final.css' : '[name].css'
49
47
  );
50
- config.output.set(
48
+ chain.output.set(
51
49
  'cssChunkFilename',
52
50
  esmx.isProd
53
51
  ? 'chunks/[name].[contenthash:8].final.css'
54
52
  : 'chunks/[name].css'
55
53
  );
56
- config.output.path(esmx.resolvePath('dist', buildTarget));
54
+ chain.output.path(esmx.resolvePath('dist', buildTarget));
57
55
 
58
- config.plugin('progress').use(rspack.ProgressPlugin, [
56
+ chain.plugin('progress').use(rspack.ProgressPlugin, [
59
57
  {
60
58
  prefix: buildTarget
61
59
  }
62
60
  ]);
63
61
 
64
- config
65
- .plugin('module-link')
66
- .use(moduleLinkPlugin, [createModuleLinkConfig(esmx, buildTarget)]);
67
-
68
62
  if (isHot) {
69
- config.plugin('hmr').use(rspack.HotModuleReplacementPlugin);
63
+ chain.plugin('hmr').use(rspack.HotModuleReplacementPlugin);
70
64
  }
71
65
 
72
- config.module.parser.set('javascript', {
73
- url: isClient ? true : 'relative'
66
+ chain.module.parser.set('javascript', {
67
+ url: isClient ? true : 'relative',
68
+ importMeta: false,
69
+ strictExportPresence: true
74
70
  });
75
71
 
76
- config.module.generator.set('asset', {
72
+ chain.module.generator.set('asset', {
77
73
  emit: isClient
78
74
  });
79
75
 
80
- config.module.generator.set('asset/resource', {
76
+ chain.module.generator.set('asset/resource', {
81
77
  emit: isClient
82
78
  });
83
79
 
84
- config.resolve.alias.set(esmx.name, esmx.root);
80
+ chain.resolve.alias.set(esmx.name, esmx.root);
85
81
 
86
- config.optimization
82
+ chain.optimization
87
83
  .minimize(options.minimize ?? esmx.isProd)
88
84
  .emitOnErrors(true);
89
85
 
90
- config.externalsPresets({
86
+ chain.externalsPresets({
91
87
  web: isClient,
92
88
  node: isServer || isNode
93
89
  });
94
- config.externalsType('module-import');
90
+
91
+ chain.experiments({
92
+ ...chain.get('experiments'),
93
+ outputModule: true
94
+ });
95
+ chain.externalsType('module-import');
95
96
 
96
97
  if (isNode) {
97
- config.externals([
98
- // @ts-ignore
98
+ chain.externals([
99
+ // @ts-expect-error
99
100
  nodeExternals({
100
- // @ts-ignore
101
+ // @ts-expect-error
101
102
  importType: 'module-import'
102
103
  })
103
104
  ]);
104
105
  }
105
- config.experiments({
106
- nativeWatcher: true
106
+ chain.experiments({
107
+ nativeWatcher: true,
108
+ rspackFuture: {
109
+ bundlerInfo: { force: false }
110
+ }
107
111
  });
108
112
 
109
- return config;
113
+ initModuleLink(chain, createModuleLinkConfig(esmx, buildTarget));
114
+
115
+ return chain;
110
116
  }
111
117
 
112
118
  function createModuleLinkConfig(
@@ -1,8 +1,8 @@
1
1
  export {
2
- type RspackAppConfigContext,
2
+ createRspackApp,
3
3
  type RspackAppChainContext,
4
- type RspackAppOptions,
5
- createRspackApp
4
+ type RspackAppConfigContext,
5
+ type RspackAppOptions
6
6
  } from './app';
7
7
  export type { BuildTarget } from './build-target';
8
8
  export { RSPACK_LOADER } from './loader';