ptech-preset 0.1.1 → 1.1.2
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 +63 -238
- package/dist/index.d.ts +8 -44
- package/dist/index.js +106 -107
- package/package.json +11 -13
- package/LICENSE +0 -21
- package/dist/index.js.map +0 -1
package/README.md
CHANGED
|
@@ -1,283 +1,108 @@
|
|
|
1
|
-
#
|
|
1
|
+
# @precio/rsbuild-plugin-mf-auto
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Auto-generate Module Federation (MF v2) config for Rsbuild:
|
|
4
|
+
- Scan components with `/** @expose Name */` or `exposeComponent(comp, 'Name')`.
|
|
5
|
+
- Gather `remotes` from ENV `REMOTE_*` and/or a manifest (file or URL).
|
|
6
|
+
- Inject MF v2 via `@module-federation/rsbuild-plugin`.
|
|
4
7
|
|
|
5
|
-
##
|
|
6
|
-
|
|
7
|
-
- 🚀 **Auto-Expose**: Automatically expose components using JSDoc `@expose` tags or `exposeComponent()` wrapper
|
|
8
|
-
- 🔗 **Auto-Remotes**: Automatically map remotes from environment variables or manifest JSON
|
|
9
|
-
- ⚡ **Zero Configuration**: Minimal setup with sensible defaults
|
|
10
|
-
- 🎯 **TypeScript Support**: Full TypeScript support with type definitions
|
|
11
|
-
- 📦 **Module Federation**: Built-in Module Federation support with optimized shared dependencies
|
|
12
|
-
|
|
13
|
-
## Installation
|
|
14
|
-
|
|
15
|
-
### In your preset package (for development):
|
|
16
|
-
|
|
17
|
-
```bash
|
|
18
|
-
npm i -D tsup typescript
|
|
19
|
-
npm i -S fast-glob
|
|
20
|
-
```
|
|
21
|
-
|
|
22
|
-
### In your application:
|
|
8
|
+
## Install
|
|
23
9
|
|
|
24
10
|
```bash
|
|
25
|
-
npm i -D @
|
|
26
|
-
npm i -D ptech-preset
|
|
11
|
+
npm i -D @precio/rsbuild-plugin-mf-auto @module-federation/rsbuild-plugin
|
|
27
12
|
```
|
|
28
13
|
|
|
29
|
-
##
|
|
30
|
-
|
|
31
|
-
### Basic Usage
|
|
32
|
-
|
|
33
|
-
```typescript
|
|
34
|
-
// rsbuild.config.ts
|
|
35
|
-
import { defineConfig } from '@rsbuild/core';
|
|
36
|
-
import { pluginPrecioPreset } from 'ptech-preset';
|
|
37
|
-
|
|
38
|
-
export default defineConfig({
|
|
39
|
-
plugins: [
|
|
40
|
-
pluginPrecioPreset({
|
|
41
|
-
mf: {
|
|
42
|
-
name: 'my-app',
|
|
43
|
-
},
|
|
44
|
-
}),
|
|
45
|
-
],
|
|
46
|
-
});
|
|
47
|
-
```
|
|
14
|
+
## Usage
|
|
48
15
|
|
|
49
|
-
###
|
|
16
|
+
### rsbuild.config.ts
|
|
50
17
|
|
|
51
18
|
```typescript
|
|
52
|
-
// rsbuild.config.ts
|
|
53
19
|
import { defineConfig } from '@rsbuild/core';
|
|
54
|
-
import {
|
|
20
|
+
import { pluginReact } from '@rsbuild/plugin-react';
|
|
21
|
+
import { pluginMFAuto } from '@precio/rsbuild-plugin-mf-auto';
|
|
55
22
|
|
|
56
23
|
export default defineConfig({
|
|
57
24
|
plugins: [
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
},
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
css: 'static/css/[name].[contenthash:8].css',
|
|
71
|
-
},
|
|
72
|
-
},
|
|
73
|
-
mf: {
|
|
74
|
-
name: 'my-app',
|
|
75
|
-
filename: 'static/js/remoteEntry.js',
|
|
76
|
-
manifest: false,
|
|
77
|
-
shared: {
|
|
78
|
-
lodash: { singleton: true },
|
|
79
|
-
},
|
|
80
|
-
// Manual overrides (will override auto-detected)
|
|
81
|
-
exposes: {
|
|
82
|
-
'./CustomComponent': './src/components/Custom.tsx',
|
|
83
|
-
},
|
|
84
|
-
remotes: {
|
|
85
|
-
'legacy-app': 'legacy-app@https://legacy.example.com/remoteEntry.js',
|
|
86
|
-
},
|
|
87
|
-
},
|
|
88
|
-
autoExpose: {
|
|
89
|
-
enabled: true,
|
|
90
|
-
globs: ['src/components/**/*.{ts,tsx}', 'src/pages/**/*.{ts,tsx}'],
|
|
91
|
-
baseDir: 'src',
|
|
92
|
-
tag: 'expose',
|
|
93
|
-
},
|
|
94
|
-
autoRemotes: {
|
|
95
|
-
envPrefix: 'REMOTE_',
|
|
96
|
-
manifestPathOrUrl: './remotes.json',
|
|
97
|
-
},
|
|
98
|
-
}),
|
|
99
|
-
],
|
|
25
|
+
pluginReact(),
|
|
26
|
+
pluginMFAuto({
|
|
27
|
+
name: 'ptechlibrary',
|
|
28
|
+
filename: 'static/js/remoteEntry.js',
|
|
29
|
+
baseDir: 'src',
|
|
30
|
+
globs: ['src/components/**/*.{ts,tsx}'],
|
|
31
|
+
exposesMode: 'both',
|
|
32
|
+
envPrefix: 'REMOTE_',
|
|
33
|
+
manifestPathOrUrl: process.env.MF_MANIFEST,
|
|
34
|
+
autoShareReact: true
|
|
35
|
+
})
|
|
36
|
+
]
|
|
100
37
|
});
|
|
101
38
|
```
|
|
102
39
|
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
### Using JSDoc Comments
|
|
106
|
-
|
|
107
|
-
```typescript
|
|
108
|
-
// src/components/Button.tsx
|
|
109
|
-
/**
|
|
110
|
-
* A reusable button component
|
|
111
|
-
* @expose Button
|
|
112
|
-
*/
|
|
113
|
-
export const Button = ({ children, ...props }) => {
|
|
114
|
-
return <button {...props}>{children}</button>;
|
|
115
|
-
};
|
|
116
|
-
```
|
|
117
|
-
|
|
118
|
-
### Using exposeComponent Wrapper
|
|
40
|
+
### Annotate component
|
|
119
41
|
|
|
120
42
|
```typescript
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
import { exposeComponent } from '@precio/rsbuild-preset';
|
|
43
|
+
/** @expose AnimatedButton */
|
|
44
|
+
export default function AnimatedButton(){ ... }
|
|
124
45
|
|
|
125
|
-
|
|
46
|
+
// or:
|
|
47
|
+
exposeComponent(AnimatedButton, 'AnimatedButton');
|
|
126
48
|
```
|
|
127
49
|
|
|
128
|
-
###
|
|
129
|
-
|
|
130
|
-
```typescript
|
|
131
|
-
autoExpose: {
|
|
132
|
-
enabled: true, // Enable/disable auto-expose
|
|
133
|
-
globs: ['src/components/**/*.{ts,tsx}'], // File patterns to scan
|
|
134
|
-
baseDir: 'src', // Base directory for relative paths
|
|
135
|
-
tag: 'expose', // JSDoc tag to look for
|
|
136
|
-
}
|
|
137
|
-
```
|
|
138
|
-
|
|
139
|
-
## Auto-Remotes
|
|
140
|
-
|
|
141
|
-
### Environment Variables
|
|
50
|
+
### Remotes via ENV
|
|
142
51
|
|
|
143
52
|
```bash
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
REMOTE_PAYMENT_SERVICE=https://payment.example.com/remoteEntry.js
|
|
53
|
+
REMOTE_admin=https://cdn.example.com/admin/remoteEntry.js
|
|
54
|
+
REMOTE_ui=https://cdn.example.com/ui/remoteEntry.js
|
|
147
55
|
```
|
|
148
56
|
|
|
149
|
-
###
|
|
57
|
+
### Or manifest (local or http)
|
|
150
58
|
|
|
151
59
|
```json
|
|
152
|
-
// remotes.json
|
|
153
60
|
{
|
|
154
|
-
"
|
|
155
|
-
"
|
|
156
|
-
"analytics": "https://analytics.example.com/remoteEntry.js"
|
|
157
|
-
}
|
|
158
|
-
```
|
|
159
|
-
|
|
160
|
-
### HTTP Manifest
|
|
161
|
-
|
|
162
|
-
```typescript
|
|
163
|
-
autoRemotes: {
|
|
164
|
-
manifestPathOrUrl: 'https://api.example.com/remotes.json',
|
|
61
|
+
"admin": "https://cdn.example.com/admin/remoteEntry.js",
|
|
62
|
+
"ui": "https://cdn.example.com/ui/remoteEntry.js"
|
|
165
63
|
}
|
|
166
64
|
```
|
|
167
65
|
|
|
168
|
-
|
|
66
|
+
## Options
|
|
169
67
|
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
68
|
+
| Option | Type | Default | Description |
|
|
69
|
+
|--------|------|---------|-------------|
|
|
70
|
+
| `name` | `string` | `path.basename(rootPath)` | Module Federation scope name |
|
|
71
|
+
| `filename` | `string` | `'static/js/remoteEntry.js'` | Remote entry filename |
|
|
72
|
+
| `baseDir` | `string` | `'src'` | Base directory for relative paths |
|
|
73
|
+
| `globs` | `string[]` | `['src/components/**/*.{ts,tsx}']` | File patterns to scan |
|
|
74
|
+
| `exposesMode` | `'jsdoc' \| 'wrapper' \| 'both'` | `'both'` | How to detect exposed components |
|
|
75
|
+
| `envPrefix` | `string` | `'REMOTE_'` | Environment variable prefix for remotes |
|
|
76
|
+
| `manifestPathOrUrl` | `string` | - | Local file path or HTTP URL to manifest |
|
|
77
|
+
| `autoShareReact` | `boolean` | `true` | Auto-share React and related libraries |
|
|
176
78
|
|
|
177
|
-
##
|
|
79
|
+
## Notes
|
|
178
80
|
|
|
179
|
-
|
|
81
|
+
- Requires: `@module-federation/rsbuild-plugin` (MF v2).
|
|
82
|
+
- Prevents multi-React by default via shared with `singleton: true`.
|
|
83
|
+
- Supports both JSDoc comments and wrapper function patterns for exposes.
|
|
84
|
+
- Can load remote configurations from environment variables or manifest files.
|
|
180
85
|
|
|
181
|
-
|
|
182
|
-
interface PrecioPresetOptions {
|
|
183
|
-
cdn?: string;
|
|
184
|
-
html?: {
|
|
185
|
-
title?: string;
|
|
186
|
-
description?: string;
|
|
187
|
-
};
|
|
188
|
-
output?: {
|
|
189
|
-
assetPrefixInDev?: string;
|
|
190
|
-
sourceMapProd?: boolean;
|
|
191
|
-
hashFilenames?: boolean;
|
|
192
|
-
filename?: {
|
|
193
|
-
js?: string;
|
|
194
|
-
css?: string;
|
|
195
|
-
};
|
|
196
|
-
distPath?: {
|
|
197
|
-
js?: string;
|
|
198
|
-
css?: string;
|
|
199
|
-
};
|
|
200
|
-
};
|
|
201
|
-
mf: {
|
|
202
|
-
name: string; // Required: Module Federation name
|
|
203
|
-
filename?: string; // Default: 'static/js/remoteEntry.js'
|
|
204
|
-
manifest?: boolean; // Default: false
|
|
205
|
-
shared?: Record<string, SharedEntry>;
|
|
206
|
-
exposes?: Record<string, string>; // Manual overrides
|
|
207
|
-
remotes?: Record<string, string>; // Manual overrides
|
|
208
|
-
};
|
|
209
|
-
autoExpose?: AutoExposeOptions;
|
|
210
|
-
autoRemotes?: AutoRemotesOptions;
|
|
211
|
-
}
|
|
212
|
-
```
|
|
86
|
+
## Publishing
|
|
213
87
|
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
type SharedEntry =
|
|
218
|
-
| string
|
|
219
|
-
| {
|
|
220
|
-
singleton?: boolean;
|
|
221
|
-
eager?: boolean;
|
|
222
|
-
requiredVersion?: string | false;
|
|
223
|
-
};
|
|
224
|
-
```
|
|
225
|
-
|
|
226
|
-
## Examples
|
|
227
|
-
|
|
228
|
-
### Host Application
|
|
229
|
-
|
|
230
|
-
```typescript
|
|
231
|
-
// rsbuild.config.ts
|
|
232
|
-
import { defineConfig } from '@rsbuild/core';
|
|
233
|
-
import { pluginPrecioPreset } from 'ptech-preset';
|
|
88
|
+
```bash
|
|
89
|
+
# 1) Login to npm
|
|
90
|
+
npm login
|
|
234
91
|
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
mf: {
|
|
239
|
-
name: 'host-app',
|
|
240
|
-
},
|
|
241
|
-
autoRemotes: {
|
|
242
|
-
envPrefix: 'REMOTE_',
|
|
243
|
-
manifestPathOrUrl: './remotes.json',
|
|
244
|
-
},
|
|
245
|
-
}),
|
|
246
|
-
],
|
|
247
|
-
});
|
|
92
|
+
# 2) Build & publish
|
|
93
|
+
npm version patch # or minor/major
|
|
94
|
+
npm publish --access public
|
|
248
95
|
```
|
|
249
96
|
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
```typescript
|
|
253
|
-
// rsbuild.config.ts
|
|
254
|
-
import { defineConfig } from '@rsbuild/core';
|
|
255
|
-
import { pluginPrecioPreset } from 'ptech-preset';
|
|
97
|
+
## Consumer Usage
|
|
256
98
|
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
pluginPrecioPreset({
|
|
260
|
-
mf: {
|
|
261
|
-
name: 'remote-app',
|
|
262
|
-
},
|
|
263
|
-
autoExpose: {
|
|
264
|
-
globs: ['src/components/**/*.{ts,tsx}'],
|
|
265
|
-
},
|
|
266
|
-
}),
|
|
267
|
-
],
|
|
268
|
-
});
|
|
99
|
+
```bash
|
|
100
|
+
npm i -D @precio/rsbuild-plugin-mf-auto @module-federation/rsbuild-plugin
|
|
269
101
|
```
|
|
270
102
|
|
|
271
|
-
|
|
103
|
+
### Example ENV configuration:
|
|
272
104
|
|
|
273
105
|
```bash
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
# Watch mode for development
|
|
278
|
-
npm run dev
|
|
106
|
+
REMOTE_ptechui=https://oneportal.blob.core.windows.net/external/ptechui/remoteEntry.js
|
|
107
|
+
REMOTE_admin=https://oneportal.blob.core.windows.net/external/admin/remoteEntry.js
|
|
279
108
|
```
|
|
280
|
-
|
|
281
|
-
## License
|
|
282
|
-
|
|
283
|
-
MIT
|
package/dist/index.d.ts
CHANGED
|
@@ -1,51 +1,15 @@
|
|
|
1
1
|
import { RsbuildPlugin } from '@rsbuild/core';
|
|
2
2
|
|
|
3
|
-
type
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
requiredVersion?: string | false;
|
|
7
|
-
};
|
|
8
|
-
type AutoExposeOptions = {
|
|
9
|
-
enabled?: boolean;
|
|
10
|
-
globs?: string[];
|
|
3
|
+
type MfAutoOptions = {
|
|
4
|
+
name?: string;
|
|
5
|
+
filename?: string;
|
|
11
6
|
baseDir?: string;
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
type AutoRemotesOptions = {
|
|
7
|
+
globs?: string[];
|
|
8
|
+
exposesMode?: "jsdoc" | "wrapper" | "both";
|
|
15
9
|
envPrefix?: string;
|
|
16
10
|
manifestPathOrUrl?: string;
|
|
11
|
+
autoShareReact?: boolean;
|
|
17
12
|
};
|
|
18
|
-
|
|
19
|
-
cdn?: string;
|
|
20
|
-
html?: {
|
|
21
|
-
title?: string;
|
|
22
|
-
description?: string;
|
|
23
|
-
};
|
|
24
|
-
output?: {
|
|
25
|
-
assetPrefixInDev?: string;
|
|
26
|
-
sourceMapProd?: boolean;
|
|
27
|
-
hashFilenames?: boolean;
|
|
28
|
-
filename?: {
|
|
29
|
-
js?: string;
|
|
30
|
-
css?: string;
|
|
31
|
-
};
|
|
32
|
-
distPath?: {
|
|
33
|
-
js?: string;
|
|
34
|
-
css?: string;
|
|
35
|
-
};
|
|
36
|
-
};
|
|
37
|
-
mf: {
|
|
38
|
-
name: string;
|
|
39
|
-
filename?: string;
|
|
40
|
-
manifest?: boolean;
|
|
41
|
-
shared?: Record<string, SharedEntry>;
|
|
42
|
-
exposes?: Record<string, string>;
|
|
43
|
-
remotes?: Record<string, string>;
|
|
44
|
-
};
|
|
45
|
-
autoExpose?: AutoExposeOptions;
|
|
46
|
-
autoRemotes?: AutoRemotesOptions;
|
|
47
|
-
};
|
|
48
|
-
|
|
49
|
-
declare function pluginPrecioPreset(opts: PrecioPresetOptions): RsbuildPlugin;
|
|
13
|
+
declare function pluginCore(opts?: MfAutoOptions): RsbuildPlugin;
|
|
50
14
|
|
|
51
|
-
export { type
|
|
15
|
+
export { type MfAutoOptions, pluginCore as default, pluginCore };
|
package/dist/index.js
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
|
-
import { pluginReact } from '@rsbuild/plugin-react';
|
|
2
|
-
import { pluginModuleFederation } from '@module-federation/rsbuild-plugin';
|
|
3
|
-
import path from 'path';
|
|
4
|
-
import fs2 from 'fs';
|
|
5
|
-
import fg from 'fast-glob';
|
|
6
|
-
|
|
7
1
|
// src/index.ts
|
|
8
|
-
|
|
2
|
+
import path3 from "path";
|
|
3
|
+
|
|
4
|
+
// src/collectAutoExposes.ts
|
|
5
|
+
import path from "path";
|
|
6
|
+
import fs from "fs";
|
|
7
|
+
import fg from "fast-glob";
|
|
8
|
+
async function collectAutoExposes(opts = {}) {
|
|
9
9
|
if (opts.enabled === false) return {};
|
|
10
10
|
const globs = opts.globs ?? ["src/components/**/*.{ts,tsx}"];
|
|
11
11
|
const baseDir = path.resolve(process.cwd(), opts.baseDir ?? "src");
|
|
@@ -17,8 +17,29 @@ async function collectAutoExposesWithWrapper(opts = {}) {
|
|
|
17
17
|
});
|
|
18
18
|
const exposes = {};
|
|
19
19
|
for (const abs of files) {
|
|
20
|
-
const text =
|
|
21
|
-
const
|
|
20
|
+
const text = fs.readFileSync(abs, "utf8");
|
|
21
|
+
const m = text.match(new RegExp(`/\\*\\*[^*]*\\*+[^/]*@${tag}\\s*([^*\\n\\r]*)`, "m"));
|
|
22
|
+
if (!m) continue;
|
|
23
|
+
const raw = (m[1] || "").trim();
|
|
24
|
+
const key = raw || path.basename(abs).replace(/\.(tsx?|jsx?)$/, "");
|
|
25
|
+
const rel = path.relative(baseDir, abs).replace(/\\/g, "/");
|
|
26
|
+
exposes[`./${key}`] = `./${rel}`;
|
|
27
|
+
}
|
|
28
|
+
return exposes;
|
|
29
|
+
}
|
|
30
|
+
async function collectAutoExposesWithWrapper(opts = {}) {
|
|
31
|
+
if (opts.enabled === false) return {};
|
|
32
|
+
const globs = opts.globs ?? ["src/components/**/*.{ts,tsx}"];
|
|
33
|
+
const baseDir = path.resolve(process.cwd(), opts.baseDir ?? "src");
|
|
34
|
+
const files = await fg(globs, {
|
|
35
|
+
cwd: process.cwd(),
|
|
36
|
+
absolute: true,
|
|
37
|
+
ignore: ["**/*.d.ts"]
|
|
38
|
+
});
|
|
39
|
+
const exposes = {};
|
|
40
|
+
for (const abs of files) {
|
|
41
|
+
const text = fs.readFileSync(abs, "utf8");
|
|
42
|
+
const jsdocMatch = text.match(/\/\*\*[^*]*\*+[^/]*@expose\s*([^*\n\r]*)/m);
|
|
22
43
|
if (jsdocMatch) {
|
|
23
44
|
const raw = (jsdocMatch[1] || "").trim();
|
|
24
45
|
const key = raw || path.basename(abs).replace(/\.(tsx?|jsx?)$/, "");
|
|
@@ -35,6 +56,10 @@ async function collectAutoExposesWithWrapper(opts = {}) {
|
|
|
35
56
|
}
|
|
36
57
|
return exposes;
|
|
37
58
|
}
|
|
59
|
+
|
|
60
|
+
// src/collectAutoRemotes.ts
|
|
61
|
+
import fs2 from "fs";
|
|
62
|
+
import path2 from "path";
|
|
38
63
|
async function collectAutoRemotes(opts = {}) {
|
|
39
64
|
const remotes = {};
|
|
40
65
|
const prefix = opts.envPrefix ?? "REMOTE_";
|
|
@@ -51,24 +76,21 @@ async function collectAutoRemotes(opts = {}) {
|
|
|
51
76
|
if (/^https?:\/\//i.test(manifestPathOrUrl)) {
|
|
52
77
|
try {
|
|
53
78
|
const res = await fetch(manifestPathOrUrl);
|
|
54
|
-
if (
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
}
|
|
59
|
-
} catch (error) {
|
|
60
|
-
console.warn(`Error fetching manifest from ${manifestPathOrUrl}:`, error);
|
|
79
|
+
if (res.ok) obj = await res.json();
|
|
80
|
+
else console.warn(`[mf-auto] fetch manifest failed: ${res.status} ${res.statusText}`);
|
|
81
|
+
} catch (e) {
|
|
82
|
+
console.warn(`[mf-auto] fetch manifest error:`, e);
|
|
61
83
|
}
|
|
62
84
|
} else {
|
|
63
85
|
try {
|
|
64
|
-
const manifestPath =
|
|
86
|
+
const manifestPath = path2.resolve(manifestPathOrUrl);
|
|
65
87
|
if (fs2.existsSync(manifestPath)) {
|
|
66
88
|
obj = JSON.parse(fs2.readFileSync(manifestPath, "utf8"));
|
|
67
89
|
} else {
|
|
68
|
-
console.warn(`
|
|
90
|
+
console.warn(`[mf-auto] manifest file not found: ${manifestPath}`);
|
|
69
91
|
}
|
|
70
|
-
} catch (
|
|
71
|
-
console.warn(`
|
|
92
|
+
} catch (e) {
|
|
93
|
+
console.warn(`[mf-auto] read manifest error:`, e);
|
|
72
94
|
}
|
|
73
95
|
}
|
|
74
96
|
for (const [scope, url] of Object.entries(obj)) {
|
|
@@ -79,60 +101,42 @@ async function collectAutoRemotes(opts = {}) {
|
|
|
79
101
|
}
|
|
80
102
|
return remotes;
|
|
81
103
|
}
|
|
82
|
-
function makeExposeCacheGroups(exposes) {
|
|
83
|
-
const groups = {};
|
|
84
|
-
for (const [key, rel] of Object.entries(exposes)) {
|
|
85
|
-
const safeName = key.replace(/[^a-z0-9]/gi, "-").replace(/^-+/, "");
|
|
86
|
-
const abs = path.resolve(process.cwd(), rel.replace(/^\.\//, ""));
|
|
87
|
-
const escaped = abs.replace(/\\/g, "\\\\");
|
|
88
|
-
groups[`expose-${safeName}`] = {
|
|
89
|
-
test: new RegExp(escaped),
|
|
90
|
-
name: `expose-${safeName}`,
|
|
91
|
-
// chunk name: expose-AnimatedButton
|
|
92
|
-
chunks: "all",
|
|
93
|
-
enforce: true
|
|
94
|
-
};
|
|
95
|
-
}
|
|
96
|
-
return groups;
|
|
97
|
-
}
|
|
98
104
|
|
|
99
105
|
// src/index.ts
|
|
100
|
-
function
|
|
106
|
+
function pluginCore(opts = {}) {
|
|
107
|
+
const {
|
|
108
|
+
name,
|
|
109
|
+
filename = "static/js/remoteEntry.js",
|
|
110
|
+
baseDir = "src",
|
|
111
|
+
globs = ["src/components/**/*.{ts,tsx}"],
|
|
112
|
+
exposesMode = "both",
|
|
113
|
+
envPrefix = "REMOTE_",
|
|
114
|
+
manifestPathOrUrl,
|
|
115
|
+
autoShareReact = true
|
|
116
|
+
} = opts;
|
|
101
117
|
return {
|
|
102
|
-
name: "
|
|
118
|
+
name: "plugin-mf-auto",
|
|
119
|
+
apply: "build",
|
|
103
120
|
async setup(api) {
|
|
104
|
-
const
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
const
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
filenameHash: out.hashFilenames ?? true,
|
|
124
|
-
filename: {
|
|
125
|
-
js: out.filename?.js ?? "static/js/[name].[contenthash:8].js",
|
|
126
|
-
css: out.filename?.css ?? "static/css/[name].[contenthash:8].css",
|
|
127
|
-
...config.output?.filename ?? {}
|
|
128
|
-
},
|
|
129
|
-
distPath: {
|
|
130
|
-
js: out.distPath?.js ?? "static/js",
|
|
131
|
-
css: out.distPath?.css ?? "static/css",
|
|
132
|
-
...config.output?.distPath ?? {}
|
|
133
|
-
}
|
|
134
|
-
};
|
|
135
|
-
const shared = {
|
|
121
|
+
const { pluginModuleFederation } = await import("@module-federation/rsbuild-plugin");
|
|
122
|
+
async function getExposes() {
|
|
123
|
+
if (exposesMode === "jsdoc")
|
|
124
|
+
return collectAutoExposes({ baseDir, globs });
|
|
125
|
+
if (exposesMode === "wrapper")
|
|
126
|
+
return collectAutoExposesWithWrapper({ baseDir, globs });
|
|
127
|
+
const a = await collectAutoExposes({ baseDir, globs });
|
|
128
|
+
const b = await collectAutoExposesWithWrapper({ baseDir, globs });
|
|
129
|
+
return { ...a, ...b };
|
|
130
|
+
}
|
|
131
|
+
async function getRemotes() {
|
|
132
|
+
return collectAutoRemotes({ envPrefix, manifestPathOrUrl });
|
|
133
|
+
}
|
|
134
|
+
api.modifyRsbuildConfig(async (config) => {
|
|
135
|
+
const [exposes, remotes] = await Promise.all([
|
|
136
|
+
getExposes(),
|
|
137
|
+
getRemotes()
|
|
138
|
+
]);
|
|
139
|
+
const sharedDefaults = autoShareReact ? {
|
|
136
140
|
react: {
|
|
137
141
|
singleton: true,
|
|
138
142
|
eager: true,
|
|
@@ -143,49 +147,44 @@ function pluginPrecioPreset(opts) {
|
|
|
143
147
|
eager: true,
|
|
144
148
|
requiredVersion: false
|
|
145
149
|
},
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
150
|
+
"react-router": {
|
|
151
|
+
singleton: true,
|
|
152
|
+
eager: true,
|
|
153
|
+
requiredVersion: false
|
|
154
|
+
},
|
|
155
|
+
"react-router-dom": {
|
|
156
|
+
singleton: true,
|
|
157
|
+
eager: true,
|
|
158
|
+
requiredVersion: false
|
|
159
|
+
},
|
|
160
|
+
"@azure/msal-react": {
|
|
161
|
+
singleton: true,
|
|
162
|
+
eager: true,
|
|
163
|
+
requiredVersion: false
|
|
164
|
+
},
|
|
165
|
+
"@azure/msal-browser": {
|
|
166
|
+
singleton: true,
|
|
167
|
+
eager: true,
|
|
168
|
+
requiredVersion: false
|
|
169
|
+
}
|
|
170
|
+
} : void 0;
|
|
171
|
+
config.plugins ||= [];
|
|
172
|
+
config.plugins.push(
|
|
153
173
|
pluginModuleFederation({
|
|
154
|
-
name:
|
|
155
|
-
filename
|
|
156
|
-
manifest: opts.mf.manifest ?? false,
|
|
174
|
+
name: name ?? path3.basename(api.context.rootPath).replace(/\W+/g, ""),
|
|
175
|
+
filename,
|
|
157
176
|
exposes,
|
|
158
177
|
remotes,
|
|
159
|
-
shared
|
|
160
|
-
dts: {
|
|
161
|
-
generateTypes: {
|
|
162
|
-
typesFolder: "@mf-types",
|
|
163
|
-
deleteTypesFolder: false,
|
|
164
|
-
compileInChildProcess: true,
|
|
165
|
-
abortOnError: false
|
|
166
|
-
},
|
|
167
|
-
consumeTypes: true
|
|
168
|
-
}
|
|
178
|
+
shared: sharedDefaults
|
|
169
179
|
})
|
|
170
|
-
|
|
171
|
-
config.performance = {
|
|
172
|
-
...config.performance ?? {},
|
|
173
|
-
chunkSplit: {
|
|
174
|
-
strategy: "split-by-module",
|
|
175
|
-
override: {
|
|
176
|
-
cacheGroups: {
|
|
177
|
-
...makeExposeCacheGroups(exposes)
|
|
178
|
-
}
|
|
179
|
-
},
|
|
180
|
-
...config.performance?.chunkSplit ?? {}
|
|
181
|
-
}
|
|
182
|
-
};
|
|
180
|
+
);
|
|
183
181
|
return config;
|
|
184
182
|
});
|
|
185
183
|
}
|
|
186
184
|
};
|
|
187
185
|
}
|
|
188
|
-
|
|
189
|
-
export {
|
|
190
|
-
|
|
191
|
-
|
|
186
|
+
var index_default = pluginCore;
|
|
187
|
+
export {
|
|
188
|
+
index_default as default,
|
|
189
|
+
pluginCore
|
|
190
|
+
};
|
package/package.json
CHANGED
|
@@ -1,35 +1,33 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "ptech-preset",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "1.1.2",
|
|
4
|
+
"description": "Auto Module.",
|
|
4
5
|
"type": "module",
|
|
5
6
|
"main": "dist/index.js",
|
|
6
7
|
"types": "dist/index.d.ts",
|
|
7
8
|
"files": ["dist", "README.md", "LICENSE"],
|
|
8
9
|
"scripts": {
|
|
9
10
|
"build": "tsup src/index.ts --format esm --dts --out-dir dist --clean",
|
|
10
|
-
"dev": "tsup src/index.ts --format esm --dts --out-dir dist --watch"
|
|
11
|
+
"dev": "tsup src/index.ts --format esm --dts --out-dir dist --watch",
|
|
12
|
+
"prepublishOnly": "npm run build"
|
|
11
13
|
},
|
|
12
14
|
"peerDependencies": {
|
|
13
|
-
"@rsbuild/core": ">=1.
|
|
14
|
-
"@rsbuild/plugin-react": ">=1.4.1",
|
|
15
|
+
"@rsbuild/core": ">=1.5.13",
|
|
15
16
|
"@module-federation/rsbuild-plugin": ">=0.19.1"
|
|
16
17
|
},
|
|
17
|
-
"devDependencies": {
|
|
18
|
-
"tsup": "^8.5.0",
|
|
19
|
-
"typescript": "^5.9.3"
|
|
20
|
-
},
|
|
21
18
|
"dependencies": {
|
|
22
19
|
"fast-glob": "^3.3.3"
|
|
23
20
|
},
|
|
21
|
+
"devDependencies": {
|
|
22
|
+
"tsup": "^8.0.1",
|
|
23
|
+
"typescript": "^5.6.2"
|
|
24
|
+
},
|
|
24
25
|
"keywords": [
|
|
25
26
|
"rsbuild",
|
|
26
27
|
"module-federation",
|
|
27
|
-
"preset",
|
|
28
28
|
"micro-frontend",
|
|
29
|
-
"auto
|
|
30
|
-
"auto
|
|
29
|
+
"auto expose",
|
|
30
|
+
"auto remotes"
|
|
31
31
|
],
|
|
32
|
-
"description": "A comprehensive Rsbuild preset",
|
|
33
|
-
"author": "PTech Team",
|
|
34
32
|
"license": "MIT"
|
|
35
33
|
}
|
package/LICENSE
DELETED
|
@@ -1,21 +0,0 @@
|
|
|
1
|
-
MIT License
|
|
2
|
-
|
|
3
|
-
Copyright (c) 2024 Precio Team
|
|
4
|
-
|
|
5
|
-
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
-
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
-
in the Software without restriction, including without limitation the rights
|
|
8
|
-
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
-
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
-
furnished to do so, subject to the following conditions:
|
|
11
|
-
|
|
12
|
-
The above copyright notice and this permission notice shall be included in all
|
|
13
|
-
copies or substantial portions of the Software.
|
|
14
|
-
|
|
15
|
-
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
-
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
-
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
-
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
-
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
-
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
-
SOFTWARE.
|
package/dist/index.js.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/autoExpose.ts","../src/autoRemotes.ts","../src/chunkUtils.ts","../src/index.ts"],"names":["fs","path"],"mappings":"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","file":"index.js","sourcesContent":["import path from 'node:path';\r\nimport fs from 'node:fs';\r\nimport fg from 'fast-glob';\r\nimport type { AutoExposeOptions } from './types';\r\n\r\nexport async function collectAutoExposes(opts: AutoExposeOptions = {}) {\r\n if (opts.enabled === false) return {};\r\n \r\n const globs = opts.globs ?? ['src/components/**/*.{ts,tsx}'];\r\n const baseDir = path.resolve(process.cwd(), opts.baseDir ?? 'src');\r\n const tag = opts.tag ?? 'expose';\r\n\r\n const files = await fg(globs, { \r\n cwd: process.cwd(), \r\n absolute: true, \r\n ignore: ['**/*.d.ts'] \r\n });\r\n \r\n const exposes: Record<string, string> = {};\r\n\r\n for (const abs of files) {\r\n const text = fs.readFileSync(abs, 'utf8');\r\n \r\n // Tìm /** @expose Name */\r\n const m = text.match(new RegExp(`/\\\\*\\\\*[^*]*\\\\*+[^/]*@${tag}\\\\s*([^*\\\\n\\\\r]*)`, 'm'));\r\n if (!m) continue;\r\n\r\n // lấy tên sau @expose, nếu trống => dùng baseName file\r\n const raw = (m[1] || '').trim();\r\n const key = raw || path.basename(abs).replace(/\\.(tsx?|jsx?)$/, '');\r\n const rel = path.relative(baseDir, abs).replace(/\\\\/g, '/');\r\n exposes[`./${key}`] = `./${rel}`;\r\n }\r\n \r\n return exposes;\r\n}\r\n\r\n// Extended version that also supports exposeComponent wrapper\r\nexport async function collectAutoExposesWithWrapper(opts: AutoExposeOptions = {}) {\r\n if (opts.enabled === false) return {};\r\n \r\n const globs = opts.globs ?? ['src/components/**/*.{ts,tsx}'];\r\n const baseDir = path.resolve(process.cwd(), opts.baseDir ?? 'src');\r\n const tag = opts.tag ?? 'expose';\r\n\r\n const files = await fg(globs, { \r\n cwd: process.cwd(), \r\n absolute: true, \r\n ignore: ['**/*.d.ts'] \r\n });\r\n \r\n const exposes: Record<string, string> = {};\r\n\r\n for (const abs of files) {\r\n const text = fs.readFileSync(abs, 'utf8');\r\n \r\n // 1. Tìm /** @expose Name */\r\n const jsdocMatch = text.match(new RegExp(`/\\\\*\\\\*[^*]*\\\\*+[^/]*@${tag}\\\\s*([^*\\\\n\\\\r]*)`, 'm'));\r\n if (jsdocMatch) {\r\n const raw = (jsdocMatch[1] || '').trim();\r\n const key = raw || path.basename(abs).replace(/\\.(tsx?|jsx?)$/, '');\r\n const rel = path.relative(baseDir, abs).replace(/\\\\/g, '/');\r\n exposes[`./${key}`] = `./${rel}`;\r\n continue;\r\n }\r\n\r\n // 2. Tìm exposeComponent(comp, 'Key')\r\n const wrapperMatch = text.match(/exposeComponent\\s*\\(\\s*[^,]+,\\s*['\"`]([^'\"`]+)['\"`]\\s*\\)/);\r\n if (wrapperMatch) {\r\n const key = wrapperMatch[1];\r\n const rel = path.relative(baseDir, abs).replace(/\\\\/g, '/');\r\n exposes[`./${key}`] = `./${rel}`;\r\n }\r\n }\r\n \r\n return exposes;\r\n}\r\n","import fs from 'node:fs';\r\nimport path from 'node:path';\r\nimport type { AutoRemotesOptions } from './types';\r\n\r\nexport async function collectAutoRemotes(opts: AutoRemotesOptions = {}) {\r\n const remotes: Record<string, string> = {};\r\n const prefix = opts.envPrefix ?? 'REMOTE_';\r\n\r\n // 1) ENV variables\r\n for (const [k, v] of Object.entries(process.env)) {\r\n if (!v) continue;\r\n if (k.startsWith(prefix)) {\r\n const scope = k.slice(prefix.length).toLowerCase();\r\n remotes[scope] = `${scope}@${v}`;\r\n }\r\n }\r\n\r\n // 2) Manifest JSON (local hoặc http)\r\n const manifestPathOrUrl = opts.manifestPathOrUrl;\r\n if (manifestPathOrUrl) {\r\n let obj: Record<string, string> = {};\r\n \r\n if (/^https?:\\/\\//i.test(manifestPathOrUrl)) {\r\n // HTTP/HTTPS URL\r\n try {\r\n const res = await fetch(manifestPathOrUrl);\r\n if (!res.ok) {\r\n console.warn(`Failed to fetch manifest from ${manifestPathOrUrl}: ${res.status} ${res.statusText}`);\r\n } else {\r\n obj = await res.json();\r\n }\r\n } catch (error) {\r\n console.warn(`Error fetching manifest from ${manifestPathOrUrl}:`, error);\r\n }\r\n } else {\r\n // Local file path\r\n try {\r\n const manifestPath = path.resolve(manifestPathOrUrl);\r\n if (fs.existsSync(manifestPath)) {\r\n obj = JSON.parse(fs.readFileSync(manifestPath, 'utf8'));\r\n } else {\r\n console.warn(`Manifest file not found: ${manifestPath}`);\r\n }\r\n } catch (error) {\r\n console.warn(`Error reading manifest file ${manifestPathOrUrl}:`, error);\r\n }\r\n }\r\n \r\n // Process manifest entries\r\n for (const [scope, url] of Object.entries(obj)) {\r\n if (typeof url === 'string' && url.trim()) {\r\n remotes[scope] = `${scope}@${url}`;\r\n }\r\n }\r\n }\r\n \r\n return remotes;\r\n}\r\n","import path from 'node:path';\r\n\r\n/**\r\n * Generate cache groups to force each expose into its own chunk\r\n * This ensures each exposed component gets its own separate JavaScript chunk\r\n */\r\nexport function makeExposeCacheGroups(exposes: Record<string, string>) {\r\n const groups: Record<string, any> = {};\r\n \r\n for (const [key, rel] of Object.entries(exposes)) {\r\n // Create a safe name for the chunk (replace special characters with dashes)\r\n const safeName = key.replace(/[^a-z0-9]/gi, '-').replace(/^-+/, '');\r\n \r\n // Convert relative path to absolute path\r\n const abs = path.resolve(process.cwd(), rel.replace(/^\\.\\//, ''));\r\n \r\n // Escape backslashes for RegExp (Windows compatibility)\r\n const escaped = abs.replace(/\\\\/g, '\\\\\\\\');\r\n \r\n groups[`expose-${safeName}`] = {\r\n test: new RegExp(escaped),\r\n name: `expose-${safeName}`, // chunk name: expose-AnimatedButton\r\n chunks: 'all',\r\n enforce: true,\r\n };\r\n }\r\n \r\n return groups;\r\n}\r\n","import type { RsbuildConfig, RsbuildPlugin } from \"@rsbuild/core\";\r\nimport { pluginReact } from \"@rsbuild/plugin-react\";\r\nimport { pluginModuleFederation } from \"@module-federation/rsbuild-plugin\";\r\nimport type { PrecioPresetOptions, SharedEntry } from \"./types\";\r\nimport { collectAutoExposesWithWrapper } from \"./autoExpose\";\r\nimport { collectAutoRemotes } from \"./autoRemotes\";\r\nimport { makeExposeCacheGroups } from \"./chunkUtils\";\r\n\r\nexport function pluginPrecioPreset(opts: PrecioPresetOptions): RsbuildPlugin {\r\n return {\r\n name: \"precio-rsbuild-preset\",\r\n async setup(api) {\r\n const isProd = process.env.NODE_ENV === \"production\";\r\n const CDN = opts.cdn ?? process.env.CDN_URL;\r\n\r\n const autoExposes = await collectAutoExposesWithWrapper(opts.autoExpose);\r\n const autoRemotes = await collectAutoRemotes(opts.autoRemotes);\r\n\r\n api.modifyRsbuildConfig((config: RsbuildConfig) => {\r\n \r\n config.html = {\r\n ...(config.html ?? {}),\r\n title: opts.html?.title ?? config.html?.title ?? \"Precio App\",\r\n meta: {\r\n viewport: \"width=device-width, initial-scale=1\",\r\n description: opts.html?.description ?? \"Precio App\",\r\n ...(config.html?.meta ?? {}),\r\n },\r\n };\r\n\r\n const out = opts.output ?? {};\r\n config.output = {\r\n ...(config.output ?? {}),\r\n assetPrefix: isProd ? CDN ?? \"/\" : out.assetPrefixInDev ?? \"/\",\r\n sourceMap: isProd ? out.sourceMapProd ?? false : true,\r\n filenameHash: out.hashFilenames ?? true,\r\n filename: {\r\n js: out.filename?.js ?? \"static/js/[name].[contenthash:8].js\",\r\n css: out.filename?.css ?? \"static/css/[name].[contenthash:8].css\",\r\n ...(config.output?.filename ?? {}),\r\n },\r\n distPath: {\r\n js: out.distPath?.js ?? \"static/js\",\r\n css: out.distPath?.css ?? \"static/css\",\r\n ...(config.output?.distPath ?? {}),\r\n },\r\n };\r\n const shared = {\r\n react: {\r\n singleton: true,\r\n eager: true,\r\n requiredVersion: false as const,\r\n },\r\n \"react-dom\": {\r\n singleton: true,\r\n eager: true,\r\n requiredVersion: false as const,\r\n },\r\n ...(opts.mf?.shared ?? {}),\r\n } satisfies Record<string, SharedEntry>;\r\n\r\n const exposes = { ...autoExposes, ...(opts.mf.exposes ?? {}) };\r\n const remotes = { ...autoRemotes, ...(opts.mf.remotes ?? {}) };\r\n\r\n config.plugins = [\r\n ...(config.plugins ?? []),\r\n pluginReact(),\r\n pluginModuleFederation({\r\n name: opts.mf.name,\r\n filename: opts.mf.filename ?? \"static/js/remoteEntry.js\",\r\n manifest: opts.mf.manifest ?? false,\r\n exposes,\r\n remotes,\r\n shared,\r\n dts: {\r\n generateTypes: {\r\n typesFolder: '@mf-types',\r\n deleteTypesFolder: false,\r\n compileInChildProcess: true,\r\n abortOnError: false,\r\n },\r\n consumeTypes: true,\r\n },\r\n } as any),\r\n ];\r\n\r\n config.performance = {\r\n ...(config.performance ?? {}),\r\n chunkSplit: {\r\n strategy: \"split-by-module\",\r\n override: {\r\n cacheGroups: {\r\n ...makeExposeCacheGroups(exposes),\r\n },\r\n },\r\n ...(config.performance?.chunkSplit ?? {}),\r\n },\r\n };\r\n\r\n return config;\r\n });\r\n },\r\n };\r\n}\r\n\r\nexport type {\r\n PrecioPresetOptions,\r\n AutoExposeOptions,\r\n AutoRemotesOptions,\r\n SharedEntry,\r\n} from \"./types\";\r\n"]}
|