@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.
- package/dist/module-link/apply-chain-config.d.ts +3 -0
- package/dist/module-link/apply-chain-config.mjs +114 -0
- package/dist/module-link/index.d.ts +4 -0
- package/dist/module-link/index.mjs +8 -0
- package/dist/module-link/manifest-plugin.d.ts +14 -0
- package/dist/module-link/manifest-plugin.mjs +141 -0
- package/dist/module-link/parse.d.ts +2 -0
- package/dist/module-link/parse.mjs +24 -0
- package/dist/module-link/types.d.ts +25 -0
- package/dist/module-link/types.mjs +0 -0
- package/dist/rspack/app.mjs +3 -8
- package/dist/rspack/chain-config.mjs +9 -6
- package/package.json +5 -5
- package/src/module-link/apply-chain-config.ts +180 -0
- package/src/module-link/index.ts +18 -0
- package/src/module-link/manifest-plugin.ts +175 -0
- package/src/module-link/parse.ts +31 -0
- package/src/module-link/types.ts +31 -0
- package/src/rspack/app.ts +3 -8
- package/src/rspack/chain-config.ts +11 -10
|
@@ -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,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,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
|
package/dist/rspack/app.mjs
CHANGED
|
@@ -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.
|
|
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.
|
|
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.
|
|
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": "
|
|
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
|
|