@rsdoctor/docs 0.3.5 → 0.3.6

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,558 @@
1
+ # Custom Extension Rules
2
+
3
+ ## Introduction
4
+
5
+ Considering that users may have their own specific rule definition requirements, Rsdoctor provides an external interface for users to customize their own rule checks in addition to the built-in rules.
6
+ The external extension interface needs to be configured on the Rsdoctor plugin through the `extends` field, and its configuration is also placed in the `rules` field. See the example below:
7
+
8
+ ```ts
9
+ // src/rules/assets-count-limit.ts
10
+ import { defineRule } from '@rsdoctor/core/rules';
11
+
12
+ export const AssetsCountLimit = defineRule(() => ({
13
+ meta: {
14
+ category: 'bundle',
15
+ severity: 'Warn',
16
+ title: 'assets-count-limit',
17
+ defaultConfig: {
18
+ limit: 10,
19
+ },
20
+ },
21
+ check({ chunkGraph, report, ruleConfig }) {
22
+ const assets = chunkGraph.getAssets();
23
+
24
+ if (assets.length > ruleConfig.limit) {
25
+ report({
26
+ message: 'The count of assets is bigger than limit',
27
+ detail: {
28
+ type: 'link',
29
+ link: 'https://rsdoctor.dev/zh/guide/start/quick-start', // This link just for show case.
30
+ },
31
+ });
32
+ }
33
+ },
34
+ }));
35
+ ```
36
+
37
+ ```ts
38
+ // rsbuild.config.ts
39
+ import { AssetsCountLimit } from './rules/assets-count-limit';
40
+
41
+ export default {
42
+ tools: {
43
+ bundlerChain: (chain) => {
44
+ chain.plugin('Rsdoctor').use(RsdoctorRspackPlugin, [
45
+ {
46
+ linter: {
47
+ level: 'Error',
48
+ extends: [AssetsCountLimit],
49
+ rules: {
50
+ 'assets-count-limit': [
51
+ 'on',
52
+ {
53
+ limit: 1, // rule custom configs
54
+ },
55
+ ],
56
+ },
57
+ },
58
+ },
59
+ ]);
60
+ },
61
+ },
62
+ };
63
+ ```
64
+
65
+ You can follow the detailed steps below to define and write custom rules.
66
+
67
+ ## Steps for Custom Rules
68
+
69
+ ### 1. Installation
70
+
71
+ When writing custom rules, in addition to installing the basic `@rsdoctor/rspack-plugin (@rsdoctor/webpack-plugin)` dependencies, you also need to install `@rsdoctor/core` and use the defineRule function from `@rsdoctor/core/rules` to define unified Rsdoctor rules.
72
+
73
+ import { PackageManagerTabs } from '@theme';
74
+
75
+ import { Tab, Tabs } from 'rspress/theme';
76
+
77
+ <PackageManagerTabs command="add @rsdoctor/core -D" />
78
+
79
+ ### 2. Writing Rules
80
+
81
+ To write rules, you need to use the `defineRule` function, which takes a function as input and returns an object in a fixed format. Refer to the following example:
82
+
83
+ ```ts
84
+ // src/rules/some-rule.ts
85
+ import { defineRule } from '@rsdoctor/core/rules';
86
+
87
+ const ruleTitle = 'check-rule';
88
+ const ruleConfig = {
89
+ // some rule configs
90
+ };
91
+
92
+ export const CheckRule = defineRule<typeof ruleTitle, config>(() => ({
93
+ meta: {
94
+ category: 'bundle', // rule category
95
+ severity: 'Warn', // rule severity
96
+ title: ruleTitle, // rule title
97
+ defaultConfig: {
98
+ // rule default config
99
+ },
100
+ },
101
+ check(ruleContext) {
102
+ // rule check...
103
+ },
104
+ }));
105
+ ```
106
+
107
+ The `meta` field contains the fixed configuration and content of this rule, and the `check` field contains the callback that includes the specific logic for rule checking. Their types are as follows.
108
+
109
+ #### meta Object
110
+
111
+ For the definition of the meta type, please refer to [RuleMeta](#rulemeta).
112
+
113
+ ##### Property Meanings
114
+
115
+ - meta
116
+ - category
117
+ - info: Defines the category of the rule: compilation rule or build packaging rule.
118
+ - type: 'compile' | 'bundle'.
119
+ - title
120
+ - info: The title of the rule, used to display in the Rsdoctor report page.
121
+ - type: string | generics, can be passed down through generics.
122
+ - severity
123
+ - info: The severity level of the rule.
124
+ - type: Refer to the ErrorLevel type below.
125
+ - default: 'Warn'
126
+ - defaultConfig
127
+ - info: The default configuration of the rule. Custom rules may require specific configurations, and defaultConfig can be used to configure the default rule configuration.
128
+ - type: Generics, can be defined through generics. As shown in the example above.
129
+ - referenceUrl
130
+ - info: The documentation link for the rule.
131
+ - type: string.
132
+
133
+ #### check Function
134
+
135
+ The check function is mainly used for rule judgment. The parameter `ruleContext` is all the build information that Rsdoctor integrates during the build analysis process, and its type is defined as follows.
136
+ You can use the build information in the body of the check function to make custom rule judgments. After the judgment, if the rule check fails, you can report it using the `report` method in the parameter. See the next step for details.
137
+
138
+ ##### CheckCallback Type
139
+
140
+ ```ts
141
+ type CheckCallback<Config = DefaultRuleConfig> = (
142
+ context: RuleCheckerContext<Config>,
143
+ ) => void | Promise<void>;
144
+ ```
145
+
146
+ [`RuleCheckerContext` type definition, please refer to the details](#rulecheckercontext)
147
+
148
+ ##### Example
149
+
150
+ The following example is a custom rule that limits the number of assets:
151
+
152
+ ```ts
153
+ // src/rules/some-rule.ts
154
+ const CheckRule = defineRule<typeof ruleTitle, config>(() => ({
155
+ // .....
156
+
157
+ check({ chunkGraph, report, ruleConfig }) {
158
+ const assets = chunkGraph.getAssets();
159
+
160
+ if (assets.length > ruleConfig.limit) {
161
+ report({
162
+ message: 'The count of assets is bigger than limit',
163
+ detail: {
164
+ type: 'link',
165
+ link: 'https://rsdoctor.dev/zh/guide/start/quick-start', // This link just for show case.
166
+ },
167
+ });
168
+ }
169
+ },
170
+ }));
171
+ ```
172
+
173
+ ### 3. Reporting Rule Results
174
+
175
+ To report errors, you need to use the `report` method in the `check` callback function's parameter. The `report` method's parameters mainly include the following parts:
176
+
177
+ - message: The error message.
178
+ - document: File data used to describe the location of the error code and code position.
179
+ - suggestions: Rule suggestions.
180
+ - detail: Detailed information, mainly providing additional data to the frontend.
181
+
182
+ For detailed type definitions, refer to: [ReportData](#reportdata)
183
+
184
+ ### 4. Displaying Rule Results
185
+
186
+ The `report` function will pass the error information of custom rules to the compilation's errors or warnings. It will display the rule results in the terminal during the build process, and even interrupt the build.
187
+ At the same time, Rsdoctor also has two components that can be used to display rules. For more details, see [Display Components](#display-components).
188
+
189
+ - Basic Rule Warning Component
190
+
191
+ <img src="https://lf3-static.bytednsdoc.com/obj/eden-cn/lognuvj/rsdoctor/docs/rule-1.jpeg" />
192
+
193
+ - Code Display Component
194
+
195
+ <img src="https://lf3-static.bytednsdoc.com/obj/eden-cn/lognuvj/rsdoctor/docs/rule-2.jpeg" />
196
+
197
+ ## Display Components
198
+
199
+ ### Basic Rule Warning Component
200
+
201
+ - Component Type
202
+
203
+ [LinkRule Type](#linkrulestoredata)
204
+
205
+ - Component Input
206
+
207
+ - type
208
+
209
+ - The type of the component.
210
+ - value: 'link'.
211
+
212
+ - title
213
+
214
+ - The title of the rule.
215
+ - type: string.
216
+
217
+ - description
218
+
219
+ - The description of the rule. The data comes from the `message` or `detail.description` in the `report` function:
220
+ ```js
221
+ report({
222
+ message: 'The count of assets is bigger than limit',
223
+ detail: {
224
+ // ......
225
+ description: 'The count of assets is bigger than limit',
226
+ },
227
+ });
228
+ ```
229
+ - type: string.
230
+
231
+ - level
232
+
233
+ - The level of the rule.
234
+ - type: warn | error.
235
+
236
+ - link:
237
+ - The details of the rule. The data comes from `detail.link`:
238
+ ```js
239
+ report({
240
+ detail: {
241
+ // ......
242
+ link: 'http://....',
243
+ },
244
+ });
245
+ ```
246
+ - type:string。
247
+
248
+ - Example
249
+
250
+ ```ts
251
+ report({
252
+ message: 'The count of assets is bigger than limit',
253
+ detail: {
254
+ type: 'link',
255
+ link: 'https://rsdoctor.dev/zh/guide/start/quick-start', // This link just for show case.
256
+ },
257
+ });
258
+ ```
259
+
260
+ - Display Components
261
+
262
+ <img src="https://lf3-static.bytednsdoc.com/obj/eden-cn/lognuvj/rsdoctor/docs/rule-1.jpeg" />
263
+
264
+ - Component Code [Code](https://github.com/web-infra-dev/rsdoctor/blob/main/packages/components/src/components/Alert/link.tsx)
265
+
266
+ ### Code Display Component
267
+
268
+ - Component Type
269
+
270
+ [CodeViewRule Type](#codeviewrule)
271
+
272
+ - Component Input
273
+
274
+ - type
275
+
276
+ - The type of the component.
277
+ - value: 'code-view'.
278
+
279
+ - title
280
+
281
+ - The title of the rule.
282
+ - type: string.
283
+
284
+ - description
285
+
286
+ - The description of the rule. The data comes from the `message` or `detail.description` in the `report` function:
287
+ ```js
288
+ report({
289
+ message: 'The count of assets is bigger than limit',
290
+ detail: {
291
+ // ......
292
+ description: 'The count of assets is bigger than limit',
293
+ },
294
+ });
295
+ ```
296
+ - type: string.
297
+
298
+ - level
299
+
300
+ - The level of the rule.
301
+ - type: warn | error.
302
+
303
+ - file
304
+ - Code details for display.
305
+ - [type](#codeviewrule):
306
+ - file: string, code file path.
307
+ - content: string, code content.
308
+ - ranges: SourceRange, code line and column ranges.
309
+
310
+ - Example
311
+
312
+ ```js
313
+ const detail {
314
+ type: 'code-view',
315
+ file: {
316
+ path: document.path,
317
+ content: document.content,
318
+ ranges: [node.loc!],
319
+ },
320
+ };
321
+
322
+ report({
323
+ message,
324
+ document,
325
+ detail,
326
+ });
327
+
328
+ ```
329
+
330
+ - [More Examples](https://github.com/web-infra-dev/rsdoctor/blob/main/packages/core/src/rules/rules/default-import-check/index.ts#L103)
331
+
332
+ - Component Display
333
+
334
+ <img src="https://lf3-static.bytednsdoc.com/obj/eden-cn/lognuvj/rsdoctor/docs/rule-2.jpeg" />
335
+
336
+ <img src="https://lf3-static.bytednsdoc.com/obj/eden-cn/lognuvj/rsdoctor/docs/rule-3.jpeg" />
337
+
338
+ - Component Code: [Code](https://github.com/web-infra-dev/rsdoctor/blob/main/packages/components/src/components/Alert/view.tsx)
339
+
340
+ ## Type Definitions
341
+
342
+ ### RuleMeta
343
+
344
+ ```ts
345
+ interface RuleMeta<
346
+ Config = DefaultRuleConfig,
347
+ Title extends DefaultRuleTitle = DefaultRuleTitle,
348
+ > {
349
+ title: Title;
350
+ category:
351
+ severity: ErrorLevel;
352
+ referenceUrl?: string;
353
+ defaultConfig?: Config;
354
+ }
355
+
356
+ /** Error Level */
357
+ export enum ErrorLevel {
358
+ Ignore = 0,
359
+ Warn = 1,
360
+ Error = 2,
361
+ }
362
+ ```
363
+
364
+ ### RuleCheckerContext
365
+
366
+ ```ts
367
+ interface RuleCheckerContext<Config> {
368
+ /** Project root directory */
369
+ root: string;
370
+ /** Current rule configuration */
371
+ ruleConfig: Config;
372
+ /** Project configuration */
373
+ configs: ConfigData;
374
+ /** Build errors */
375
+ errors: Error[];
376
+ /** Chunk graph */
377
+ chunkGraph: ChunkGraph;
378
+ /** Module graph */
379
+ moduleGraph: ModuleGraph;
380
+ /** Package graph */
381
+ packageGraph: PackageGraph;
382
+ /** Loader data */
383
+ loader: LoaderData;
384
+ /**
385
+ * Report Error
386
+ * @param {any} error - error info
387
+ * @param {any} replacer - Replace the original error
388
+ */
389
+ report(error: ReportData, replacer?: any): void;
390
+ }
391
+ ```
392
+
393
+ ### ReportData
394
+
395
+ ```ts
396
+ interface ReportData {
397
+ /** Error message */
398
+ message: string;
399
+ /** File data */
400
+ document?: ReportDocument;
401
+ /** Diagnostic suggestions */
402
+ suggestions?: Suggestion;
403
+ /**
404
+ * Detailed information
405
+ * - Mainly provides additional data for the frontend
406
+ */
407
+ detail?: any;
408
+ }
409
+
410
+ /** Error file information */
411
+ interface ReportDocument {
412
+ /** file path */
413
+ path: string;
414
+ /** Is it a transformed code */
415
+ isTransformed?: boolean;
416
+ /** code content */
417
+ content: string;
418
+ range: Range;
419
+ }
420
+ ```
421
+
422
+ ### LinkRuleStoreData
423
+
424
+ ```ts
425
+ interface BaseRuleStoreData extends Pick<RuleMessage, 'code' | 'category'> {
426
+ /**
427
+ * unique of error
428
+ */
429
+ id: number | string;
430
+ /**
431
+ * title of alerts
432
+ */
433
+ title: string;
434
+ /**
435
+ * description of alerts
436
+ */
437
+ description?: string;
438
+ /**
439
+ * level of error
440
+ */
441
+ level: 'warn' | 'error';
442
+ /**
443
+ * rule doc link
444
+ */
445
+ link?: string;
446
+ }
447
+
448
+ interface LinkRuleStoreData extends BaseRuleStoreData {
449
+ type: 'link';
450
+ }
451
+ ```
452
+
453
+ ### CodeViewRule
454
+
455
+ ```ts
456
+ interface CodeViewRuleStoreData extends BaseRuleStoreData {
457
+ type: 'code-view';
458
+ file: {
459
+ /**
460
+ * file path
461
+ */
462
+ path: string;
463
+ /**
464
+ * the code content
465
+ */
466
+ content: string;
467
+ /**
468
+ * fix highlight range in source
469
+ */
470
+ ranges?: SourceRange[];
471
+ };
472
+ }
473
+
474
+ /** Source code location */
475
+ interface SourcePosition {
476
+ line?: number;
477
+ column?: number;
478
+ index?: number;
479
+ }
480
+
481
+ /** Source code range */
482
+ interface SourceRange {
483
+ start: SourcePosition;
484
+ end?: SourcePosition;
485
+ }
486
+ ```
487
+
488
+ ## Tools
489
+
490
+ ### AST Processing
491
+
492
+ When performing rule detection and analysis, it is common to perform AST analysis on modules and other operations. To provide more auxiliary functions, we also provide `@rsdoctor/utils/rule-utils` in the `@rsdoctor/utils` package, which contains many useful utility functions and methods.
493
+
494
+ ```ts
495
+ /** This includes the type definitions for all AST nodes */
496
+ type Node = /* SyntaxNode */;
497
+
498
+ interface parser {
499
+ /** AST iterator */
500
+ walk,
501
+ /**
502
+ * Compile code
503
+ * - The root node is `Node.Program`
504
+ */
505
+ parse,
506
+ /**
507
+ * Compile the next expression
508
+ * - The root node is `Node.ExpressionStatement`
509
+ */
510
+ parseExpressionAt,
511
+ /** Assertion methods */
512
+ asserts,
513
+ /** Utility methods */
514
+ utils,
515
+ }
516
+
517
+ /** Document class */
518
+ interface Document {
519
+ /** Get the position in the text at the given offset */
520
+ positionAt!: (offset: number) => Position | undefined;
521
+ /** Get the position in the file at the given point */
522
+ offsetAt!: (position: Position) => number | undefined;
523
+ }
524
+ ```
525
+
526
+ The `asserts` assertion method set provides type assertion methods for all AST nodes, while the `utils` utility method set provides commonly used methods such as determining whether certain statements have the same semantics and retrieving Import nodes.
527
+
528
+ ### Reporting Code Position
529
+
530
+ Some errors require providing the position of the code, so the content of the `document` field needs to be provided. However, there is an important distinction here: each module actually has two sets of code, transformed and source, which means the code after being processed by the loader and the user's original code. The AST is actually the transformed code format.
531
+ To facilitate display for users, we need to use the original code as much as possible. Therefore, after selecting the corresponding AST node, users need to use the SourceMap module provided by the module to convert the position information to the original code. If the module does not have the original code or SourceMap for some special reasons, then using the transformed code/position is more appropriate. A typical workflow is as follows:
532
+
533
+ ```ts
534
+ const module: SDK.ModuleInstance;
535
+ const node: Node.ImportDeclaration;
536
+ /** The default type is optional, but in reality, they all have values */
537
+ const transformedRange = node.loc!;
538
+ /** If the module's SourceMap is not available, this value is null */
539
+ const sourceRange = module.getSourceRange(transformedRange);
540
+ /** Get the code */
541
+ const source = mod.getSource();
542
+
543
+ // Determine which value to use based on whether the original position is generated
544
+ const range = (sourceRange ?? transformed) as Linter.Range;
545
+ const content = sourceRange ? source.source : source.transformed;
546
+
547
+ report({
548
+ document: {
549
+ path: module.path,
550
+ content,
551
+ range,
552
+ },
553
+ });
554
+ ```
555
+
556
+ ## Data Reporting
557
+
558
+ Please go to [Data Reporting](./upload-data) for viewing.
@@ -0,0 +1,45 @@
1
+ # Data Upload
2
+
3
+ To perform data upload, you need to use the interface of custom extension rules. Please refer to [Custom Extension Rules](./rule-custom) for more information.
4
+
5
+ The same approach used for custom extension rules can also be used for collecting and uploading user data. The only difference is that you don't need to report any errors in the check function. For example:
6
+
7
+ ```ts
8
+ // src/rules/upload-data.ts
9
+ import { defineRule } from '@rsdoctor/core/rules';
10
+
11
+ export const UploadData = defineRule(() => ({
12
+ meta: {
13
+ category: 'bundle',
14
+ severity: 'Warn',
15
+ title: 'upload-data',
16
+ defaultConfig: {
17
+ limit: 10,
18
+ },
19
+ },
20
+ check({ chunkGraph, moduleGraph, ruleConfig }) {
21
+ // upload some data
22
+ Upload({ chunkGraph, moduleGraph });
23
+ },
24
+ }));
25
+ ```
26
+
27
+ ```ts
28
+ // rsbuild.config.ts
29
+ import { UploadData } from './rules/upload-data';
30
+
31
+ export default {
32
+ tools: {
33
+ bundlerChain: (chain) => {
34
+ chain.plugin('Rsdoctor').use(RsdoctorRspackPlugin, [
35
+ {
36
+ linter: {
37
+ level: 'Warn',
38
+ extends: [UploadData],
39
+ },
40
+ },
41
+ ]);
42
+ },
43
+ },
44
+ };
45
+ ```
@@ -11,7 +11,7 @@
11
11
  },
12
12
  {
13
13
  "text": "博客",
14
- "link": "/blog/release/release-note-1",
14
+ "link": "/blog/release/release-note-0_1",
15
15
  "activeMatch": "/blog/"
16
16
  }
17
17
  ]
@@ -1 +1 @@
1
- ["release-note-1"]
1
+ ["release-note-0_1", "release-note-0_3"]