@rsdoctor/core 0.1.0-beta → 0.1.1
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 +10 -3
- package/dist/build-utils/build/utils/loader.d.ts +7 -1
- package/dist/build-utils/build/utils/loader.js +95 -9
- package/dist/build-utils/build/utils/parseBundle.js +1 -1
- package/dist/build-utils/common/chunks/assetsModules.js +3 -4
- package/dist/build-utils/common/webpack/compatible.js +1 -1
- package/dist/inner-plugins/plugins/bundle.js +3 -1
- package/dist/inner-plugins/plugins/loader.js +1 -1
- package/dist/inner-plugins/plugins/summary.d.ts +2 -2
- package/dist/inner-plugins/utils/loader.js +2 -2
- package/dist/rules/rule.d.ts +1 -1
- package/dist/types/plugin.d.ts +6 -2
- package/package.json +6 -6
package/README.md
CHANGED
|
@@ -1,11 +1,18 @@
|
|
|
1
|
-
# Rsdoctor
|
|
1
|
+
# Rsdoctor Core
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
This is the core package of Rsdoctor, providing core tools and analysis capabilities for Rsdoctor plugins.
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
## features
|
|
6
|
+
|
|
7
|
+
- Rsdoctor is a one-stop tool for diagnosing and analyzing the build process and build artifacts.
|
|
8
|
+
- Rsdoctor is a tool that supports Webpack and Rspack build analysis.
|
|
9
|
+
- Rsdoctor is an analysis tool that can display the time-consuming and behavioral details of the compilation.
|
|
10
|
+
- Rsdoctor is a tool that provides bundle Diff and other anti-degradation capabilities simultaneously.
|
|
6
11
|
|
|
7
12
|
## Documentation
|
|
8
13
|
|
|
14
|
+
https://rsdoctor.dev/
|
|
15
|
+
|
|
9
16
|
## Contributing
|
|
10
17
|
|
|
11
18
|
Please read the [Contributing Guide](https://github.com/web-infra-dev/rsdoctor/blob/main/CONTRIBUTING.md).
|
|
@@ -1,7 +1,12 @@
|
|
|
1
1
|
/// <reference types="node" />
|
|
2
2
|
import type { Common, Plugin } from '@rsdoctor/types';
|
|
3
3
|
import { SourceMapInput as WebpackSourceMapInput } from '../../../types';
|
|
4
|
-
export declare function
|
|
4
|
+
export declare function parsePathQueryFragment(str: string): {
|
|
5
|
+
path: string;
|
|
6
|
+
query: string;
|
|
7
|
+
fragment: string;
|
|
8
|
+
};
|
|
9
|
+
export declare function loadLoaderModule(loaderPath: string, cwd?: string): {
|
|
5
10
|
default: Plugin.LoaderDefinition<Common.PlainObject, {}>;
|
|
6
11
|
pitch: Plugin.PitchLoaderDefinitionFunction;
|
|
7
12
|
raw: boolean | void;
|
|
@@ -9,4 +14,5 @@ export declare function loadLoaderModule(p: string, cwd?: string): {
|
|
|
9
14
|
export declare function getLoaderOptions<T>(loaderContext: Plugin.LoaderContext<T>): T;
|
|
10
15
|
export declare function extractLoaderName(loaderPath: string, cwd?: string): string;
|
|
11
16
|
export declare function mapEachRules<T extends Plugin.BuildRuleSetRule>(rules: T[], callback: (rule: T) => T): T[];
|
|
17
|
+
export declare function changeBuiltinLoader<T extends Plugin.BuildRuleSetRule>(rules: T[], loaderName: string, appendRules: (rule: T, index: number) => T): T[];
|
|
12
18
|
export declare function createLoaderContextTrap(this: Plugin.LoaderContext<Common.PlainObject>, final: (err: Error | null | undefined, res: string | Buffer | null, sourceMap?: WebpackSourceMapInput) => void): Plugin.LoaderContext<Common.PlainObject<any>>;
|
|
@@ -28,19 +28,31 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
|
|
|
28
28
|
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
29
29
|
var loader_exports = {};
|
|
30
30
|
__export(loader_exports, {
|
|
31
|
+
changeBuiltinLoader: () => changeBuiltinLoader,
|
|
31
32
|
createLoaderContextTrap: () => createLoaderContextTrap,
|
|
32
33
|
extractLoaderName: () => extractLoaderName,
|
|
33
34
|
getLoaderOptions: () => getLoaderOptions,
|
|
34
35
|
loadLoaderModule: () => loadLoaderModule,
|
|
35
|
-
mapEachRules: () => mapEachRules
|
|
36
|
+
mapEachRules: () => mapEachRules,
|
|
37
|
+
parsePathQueryFragment: () => parsePathQueryFragment
|
|
36
38
|
});
|
|
37
39
|
module.exports = __toCommonJS(loader_exports);
|
|
38
40
|
var import_loader_utils = require("loader-utils");
|
|
39
41
|
var import_path = __toESM(require("path"));
|
|
40
42
|
var import_lodash = require("lodash");
|
|
41
43
|
var import_common = require("@rsdoctor/utils/common");
|
|
42
|
-
|
|
43
|
-
|
|
44
|
+
const PATH_QUERY_FRAGMENT_REGEXP = /^((?:\0.|[^?#\0])*)(\?(?:\0.|[^#\0])*)?(#.*)?$/;
|
|
45
|
+
function parsePathQueryFragment(str) {
|
|
46
|
+
const match = PATH_QUERY_FRAGMENT_REGEXP.exec(str);
|
|
47
|
+
return {
|
|
48
|
+
path: match?.[1].replace(/\0(.)/g, "$1") || "",
|
|
49
|
+
query: match?.[2] ? match[2].replace(/\0(.)/g, "$1") : "",
|
|
50
|
+
fragment: match?.[3] || ""
|
|
51
|
+
};
|
|
52
|
+
}
|
|
53
|
+
function loadLoaderModule(loaderPath, cwd = process.cwd()) {
|
|
54
|
+
const cleanLoaderPath = parsePathQueryFragment(loaderPath).path;
|
|
55
|
+
const mod = process.env.DOCTOR_TEST ? require(import_path.default.resolve(cwd, cleanLoaderPath)) : require(require.resolve(cleanLoaderPath, {
|
|
44
56
|
paths: [cwd, import_path.default.resolve(cwd, "node_modules")]
|
|
45
57
|
}));
|
|
46
58
|
const isESM = mod.__esModule && typeof mod.default === "function";
|
|
@@ -73,7 +85,7 @@ function extractLoaderName(loaderPath, cwd = "") {
|
|
|
73
85
|
return res;
|
|
74
86
|
}
|
|
75
87
|
function mapEachRules(rules, callback) {
|
|
76
|
-
return rules.map((rule
|
|
88
|
+
return rules.map((rule) => {
|
|
77
89
|
if (typeof rule === "string") {
|
|
78
90
|
return callback({
|
|
79
91
|
loader: rule
|
|
@@ -104,6 +116,17 @@ function mapEachRules(rules, callback) {
|
|
|
104
116
|
)
|
|
105
117
|
};
|
|
106
118
|
}
|
|
119
|
+
if (typeof rule.use === "function") {
|
|
120
|
+
const funcUse = rule.use;
|
|
121
|
+
const newRule = {
|
|
122
|
+
...rule,
|
|
123
|
+
use: (...args) => {
|
|
124
|
+
const rules2 = funcUse.apply(null, args);
|
|
125
|
+
return mapEachRules(rules2, callback);
|
|
126
|
+
}
|
|
127
|
+
};
|
|
128
|
+
return newRule;
|
|
129
|
+
}
|
|
107
130
|
if (Array.isArray(rule.use)) {
|
|
108
131
|
return {
|
|
109
132
|
...rule,
|
|
@@ -114,9 +137,6 @@ function mapEachRules(rules, callback) {
|
|
|
114
137
|
...rule,
|
|
115
138
|
use: mapEachRules([rule.use], callback)
|
|
116
139
|
};
|
|
117
|
-
throw new Error(
|
|
118
|
-
`webpack.module.rules.use[${i}] parse error: ${rule.use}`
|
|
119
|
-
);
|
|
120
140
|
}
|
|
121
141
|
if ("rules" in rule && Array.isArray(rule.rules)) {
|
|
122
142
|
return {
|
|
@@ -133,6 +153,68 @@ function mapEachRules(rules, callback) {
|
|
|
133
153
|
return rule;
|
|
134
154
|
});
|
|
135
155
|
}
|
|
156
|
+
function getLoaderNameMatch(_r, loaderName) {
|
|
157
|
+
return typeof _r === "object" && typeof _r?.loader === "string" && _r.loader === loaderName || typeof _r === "string" && _r === loaderName;
|
|
158
|
+
}
|
|
159
|
+
function changeBuiltinLoader(rules, loaderName, appendRules) {
|
|
160
|
+
return rules.map((rule) => {
|
|
161
|
+
if (!rule || typeof rule === "string")
|
|
162
|
+
return rule;
|
|
163
|
+
if (getLoaderNameMatch(rule, loaderName)) {
|
|
164
|
+
const _rule = {
|
|
165
|
+
...rule,
|
|
166
|
+
use: [
|
|
167
|
+
{
|
|
168
|
+
loader: rule.loader,
|
|
169
|
+
options: rule.options
|
|
170
|
+
}
|
|
171
|
+
],
|
|
172
|
+
loader: void 0,
|
|
173
|
+
options: void 0
|
|
174
|
+
};
|
|
175
|
+
return appendRules(_rule, 0);
|
|
176
|
+
}
|
|
177
|
+
if (rule.use) {
|
|
178
|
+
if (Array.isArray(rule.use)) {
|
|
179
|
+
const _index = (0, import_lodash.findIndex)(
|
|
180
|
+
rule.use,
|
|
181
|
+
(_r) => getLoaderNameMatch(_r, loaderName)
|
|
182
|
+
);
|
|
183
|
+
if (_index > -1) {
|
|
184
|
+
return appendRules(rule, _index);
|
|
185
|
+
}
|
|
186
|
+
} else if (typeof rule.use === "object" && !Array.isArray(rule.use) && typeof rule.use !== "function") {
|
|
187
|
+
rule.use = [
|
|
188
|
+
{
|
|
189
|
+
...rule.use
|
|
190
|
+
}
|
|
191
|
+
];
|
|
192
|
+
return appendRules(rule, 0);
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
if ("oneOf" in rule && rule.oneOf) {
|
|
196
|
+
return {
|
|
197
|
+
...rule,
|
|
198
|
+
oneOf: changeBuiltinLoader(
|
|
199
|
+
rule.oneOf,
|
|
200
|
+
loaderName,
|
|
201
|
+
appendRules
|
|
202
|
+
)
|
|
203
|
+
};
|
|
204
|
+
}
|
|
205
|
+
if ("rules" in rule && rule.rules) {
|
|
206
|
+
return {
|
|
207
|
+
...rule,
|
|
208
|
+
rules: changeBuiltinLoader(
|
|
209
|
+
rule.rules,
|
|
210
|
+
loaderName,
|
|
211
|
+
appendRules
|
|
212
|
+
)
|
|
213
|
+
};
|
|
214
|
+
}
|
|
215
|
+
return rule;
|
|
216
|
+
});
|
|
217
|
+
}
|
|
136
218
|
function createLoaderContextTrap(final) {
|
|
137
219
|
const cb = this.callback;
|
|
138
220
|
let callback = (...args) => {
|
|
@@ -171,7 +253,9 @@ function createLoaderContextTrap(final) {
|
|
|
171
253
|
if (options.hasOptions) {
|
|
172
254
|
return (0, import_lodash.omit)(target.query, [import_common.Loader.LoaderInternalPropertyName]);
|
|
173
255
|
}
|
|
174
|
-
|
|
256
|
+
const innerLoaderPath = options?.loader;
|
|
257
|
+
const loaderQuery = parsePathQueryFragment(innerLoaderPath).query;
|
|
258
|
+
return loaderQuery;
|
|
175
259
|
}
|
|
176
260
|
}
|
|
177
261
|
return Reflect.get(target, key, receiver);
|
|
@@ -202,9 +286,11 @@ function createLoaderContextTrap(final) {
|
|
|
202
286
|
}
|
|
203
287
|
// Annotate the CommonJS export names for ESM import in node:
|
|
204
288
|
0 && (module.exports = {
|
|
289
|
+
changeBuiltinLoader,
|
|
205
290
|
createLoaderContextTrap,
|
|
206
291
|
extractLoaderName,
|
|
207
292
|
getLoaderOptions,
|
|
208
293
|
loadLoaderModule,
|
|
209
|
-
mapEachRules
|
|
294
|
+
mapEachRules,
|
|
295
|
+
parsePathQueryFragment
|
|
210
296
|
});
|
|
@@ -92,7 +92,7 @@ const parseBundle = (bundlePath, modulesData) => {
|
|
|
92
92
|
if (state.locations)
|
|
93
93
|
return;
|
|
94
94
|
const { left, right } = node;
|
|
95
|
-
if (left
|
|
95
|
+
if (left?.object && left.object.name === "exports" && left.property && left.property.name === "modules" && isModulesHash(right)) {
|
|
96
96
|
state.locations = getModulesLocations(right);
|
|
97
97
|
}
|
|
98
98
|
},
|
|
@@ -36,7 +36,6 @@ var import_lodash = require("lodash");
|
|
|
36
36
|
var import_path = __toESM(require("path"));
|
|
37
37
|
var import_logger = require("@rsdoctor/utils/logger");
|
|
38
38
|
var import_module_graph = require("../module-graph");
|
|
39
|
-
const logger = (0, import_logger.createLogger)();
|
|
40
39
|
async function getAssetsModulesData(bundleStats, bundleDir, opts) {
|
|
41
40
|
const { parseBundle = () => ({}) } = opts || {};
|
|
42
41
|
if ((0, import_lodash.isEmpty)(bundleStats.assets) && !(0, import_lodash.isEmpty)(bundleStats.children)) {
|
|
@@ -62,7 +61,7 @@ async function getAssetsModulesData(bundleStats, bundleDir, opts) {
|
|
|
62
61
|
if (bundleDir && bundleStats?.assets) {
|
|
63
62
|
bundlesSources = {};
|
|
64
63
|
parsedModules = {};
|
|
65
|
-
for (const statAsset of bundleStats
|
|
64
|
+
for (const statAsset of bundleStats.assets) {
|
|
66
65
|
const assetFile = import_path.default.join(bundleDir, statAsset.name);
|
|
67
66
|
let bundleInfo;
|
|
68
67
|
const collectedModules = [];
|
|
@@ -72,7 +71,7 @@ async function getAssetsModulesData(bundleStats, bundleDir, opts) {
|
|
|
72
71
|
} catch (err) {
|
|
73
72
|
const { code = "", message } = err;
|
|
74
73
|
const msg = code === "ENOENT" ? "no such file" : message;
|
|
75
|
-
process.env.DEVTOOLS_NODE_DEV === "1" && logger.warn(`Error parsing bundle asset "${assetFile}": ${msg}`);
|
|
74
|
+
process.env.DEVTOOLS_NODE_DEV === "1" && import_logger.logger.warn(`Error parsing bundle asset "${assetFile}": ${msg}`);
|
|
76
75
|
continue;
|
|
77
76
|
}
|
|
78
77
|
bundlesSources[statAsset.name] = (0, import_lodash.pick)(bundleInfo, "src", "runtimeSrc");
|
|
@@ -81,7 +80,7 @@ async function getAssetsModulesData(bundleStats, bundleDir, opts) {
|
|
|
81
80
|
if ((0, import_lodash.isEmpty)(bundlesSources)) {
|
|
82
81
|
bundlesSources = null;
|
|
83
82
|
parsedModules = null;
|
|
84
|
-
process.env.DEVTOOLS_DEV && logger.warn(
|
|
83
|
+
process.env.DEVTOOLS_DEV && import_logger.logger.warn(
|
|
85
84
|
"\nNo bundles were parsed. Analyzer will show only original module sizes from stats file.\n"
|
|
86
85
|
);
|
|
87
86
|
}
|
|
@@ -72,7 +72,7 @@ function getModuleExportsType(module2, moduleGraph, strict = false) {
|
|
|
72
72
|
if (moduleGraph) {
|
|
73
73
|
return module2.getExportsType(moduleGraph, strict);
|
|
74
74
|
}
|
|
75
|
-
const exportsType = module2.buildMeta
|
|
75
|
+
const exportsType = module2.buildMeta?.exportsType;
|
|
76
76
|
if (!exportsType && !strict) {
|
|
77
77
|
return "dynamic";
|
|
78
78
|
}
|
|
@@ -63,7 +63,9 @@ class InternalBundlePlugin extends import_base.InternalBasePlugin {
|
|
|
63
63
|
}
|
|
64
64
|
};
|
|
65
65
|
this.done = async () => {
|
|
66
|
-
|
|
66
|
+
if (this.scheduler.chunkGraph) {
|
|
67
|
+
import_common.Chunks.assetsContents(this.map, this.scheduler.chunkGraph);
|
|
68
|
+
}
|
|
67
69
|
this.sdk.addClientRoutes([
|
|
68
70
|
import_types.Manifest.RsdoctorManifestClientRoutes.ModuleGraph,
|
|
69
71
|
import_types.Manifest.RsdoctorManifestClientRoutes.BundleSize
|
|
@@ -128,7 +128,7 @@ class InternalLoaderPlugin extends import_base.InternalBasePlugin {
|
|
|
128
128
|
return tap;
|
|
129
129
|
}
|
|
130
130
|
};
|
|
131
|
-
if (compiler.webpack
|
|
131
|
+
if (compiler.webpack?.NormalModule?.getCompilationHooks) {
|
|
132
132
|
compiler.webpack.NormalModule.getCompilationHooks(
|
|
133
133
|
compilation
|
|
134
134
|
).loader.intercept(interceptor);
|
|
@@ -5,10 +5,10 @@ export declare class InternalSummaryPlugin<T extends Plugin.BaseCompiler> extend
|
|
|
5
5
|
private times;
|
|
6
6
|
private preTimes;
|
|
7
7
|
private postTimes;
|
|
8
|
-
apply(compiler:
|
|
8
|
+
apply(compiler: T): void;
|
|
9
9
|
private mark;
|
|
10
10
|
beforeCompile: () => Promise<void>;
|
|
11
11
|
afterCompile: (compilation: Plugin.BaseCompilation) => Promise<void>;
|
|
12
|
-
done: (compiler:
|
|
12
|
+
done: (compiler: T) => Promise<void>;
|
|
13
13
|
private report;
|
|
14
14
|
}
|
|
@@ -107,7 +107,7 @@ function interceptLoader(rules, loaderPath, options, cwd = process.cwd(), resolv
|
|
|
107
107
|
return target;
|
|
108
108
|
};
|
|
109
109
|
return import_build.Utils.mapEachRules(rules, (rule) => {
|
|
110
|
-
if (rule
|
|
110
|
+
if (rule.loader?.startsWith("builtin:")) {
|
|
111
111
|
return rule;
|
|
112
112
|
}
|
|
113
113
|
const opts = {
|
|
@@ -171,7 +171,7 @@ async function reportLoader(ctx, start, startHRTime, isPitch, sync, code, err, r
|
|
|
171
171
|
file: loaderData[0].resource.path
|
|
172
172
|
};
|
|
173
173
|
const sdk = (0, import_sdk.getSDK)();
|
|
174
|
-
if (sdk
|
|
174
|
+
if (sdk?.reportLoader) {
|
|
175
175
|
sdk.reportLoader(loaderData);
|
|
176
176
|
sdk.reportSourceMap(sourceMapData);
|
|
177
177
|
return loaderData;
|
package/dist/rules/rule.d.ts
CHANGED
|
@@ -16,7 +16,7 @@ export declare class Rule<Config = DefaultRuleConfig> implements Linter.RuleMeta
|
|
|
16
16
|
get title(): string;
|
|
17
17
|
get severity(): Linter.Severity;
|
|
18
18
|
get config(): Config | undefined;
|
|
19
|
-
get category(): "bundle" |
|
|
19
|
+
get category(): "bundle" | RuleTypes.RuleMessageCategory | "compile" | "emo";
|
|
20
20
|
setOption(opt: Linter.RuleConfigItem): void;
|
|
21
21
|
match(level: Linter.Severity): boolean;
|
|
22
22
|
validate(context: SDK.RuntimeContext): Promise<Linter.ValidateResult>;
|
package/dist/types/plugin.d.ts
CHANGED
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
import type { Linter as LinterType, Common, Plugin, SDK } from '@rsdoctor/types';
|
|
2
2
|
import type { RsdoctorSlaveSDK, RsdoctorWebpackSDK } from '@rsdoctor/sdk';
|
|
3
|
-
import { ModuleGraph } from '@rsdoctor/graph';
|
|
4
|
-
|
|
3
|
+
import { ChunkGraph, ModuleGraph } from '@rsdoctor/graph';
|
|
4
|
+
import { rules } from "../rules/rules";
|
|
5
|
+
import { RuleData } from '@rsdoctor/types/dist/linter';
|
|
6
|
+
type InternalRules = typeof rules[number] & RuleData[];
|
|
5
7
|
export interface RsdoctorWebpackPluginOptions<Rules extends LinterType.ExtendRuleData[]> {
|
|
6
8
|
/** Checker configuration */
|
|
7
9
|
linter?: LinterType.Options<Rules, InternalRules>;
|
|
@@ -65,6 +67,8 @@ export interface RsdoctorPluginInstance<T extends Plugin.BaseCompiler, Rules ext
|
|
|
65
67
|
readonly name: string;
|
|
66
68
|
readonly options: RsdoctorPluginOptionsNormalized<Rules>;
|
|
67
69
|
readonly sdk: RsdoctorWebpackSDK;
|
|
70
|
+
_modulesGraphApplied?: boolean;
|
|
71
|
+
chunkGraph?: ChunkGraph;
|
|
68
72
|
modulesGraph: ModuleGraph;
|
|
69
73
|
ensureModulesChunksGraphApplied(compiler: T): void;
|
|
70
74
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@rsdoctor/core",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.1",
|
|
4
4
|
"repository": {
|
|
5
5
|
"type": "git",
|
|
6
6
|
"url": "https://github.com/web-infra-dev/rsdoctor",
|
|
@@ -51,10 +51,10 @@
|
|
|
51
51
|
"semver": "^7.5.4",
|
|
52
52
|
"source-map": "^0.7.4",
|
|
53
53
|
"webpack-bundle-analyzer": "^4.9.1",
|
|
54
|
-
"@rsdoctor/
|
|
55
|
-
"@rsdoctor/
|
|
56
|
-
"@rsdoctor/types": "0.1.
|
|
57
|
-
"@rsdoctor/utils": "0.1.
|
|
54
|
+
"@rsdoctor/sdk": "0.1.1",
|
|
55
|
+
"@rsdoctor/graph": "0.1.1",
|
|
56
|
+
"@rsdoctor/types": "0.1.1",
|
|
57
|
+
"@rsdoctor/utils": "0.1.1"
|
|
58
58
|
},
|
|
59
59
|
"devDependencies": {
|
|
60
60
|
"@types/bytes": "3.1.1",
|
|
@@ -73,7 +73,7 @@
|
|
|
73
73
|
"tslib": "2.4.1",
|
|
74
74
|
"typescript": "^5.2.2",
|
|
75
75
|
"webpack": "^5.89.0",
|
|
76
|
-
"@rsdoctor/test-helper": "0.1.
|
|
76
|
+
"@rsdoctor/test-helper": "0.1.1"
|
|
77
77
|
},
|
|
78
78
|
"publishConfig": {
|
|
79
79
|
"access": "public",
|