ptech-preset 0.1.0 → 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 CHANGED
@@ -1,283 +1,108 @@
1
- # @precio/rsbuild-preset
1
+ # @precio/rsbuild-plugin-mf-auto
2
2
 
3
- A comprehensive Rsbuild preset with Module Federation, auto-expose, and auto-remotes functionality.
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
- ## Features
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 @rsbuild/core @rsbuild/plugin-react @module-federation/rsbuild-plugin
26
- npm i -D @precio/rsbuild-preset
11
+ npm i -D @precio/rsbuild-plugin-mf-auto @module-federation/rsbuild-plugin
27
12
  ```
28
13
 
29
- ## Quick Start
30
-
31
- ### Basic Usage
32
-
33
- ```typescript
34
- // rsbuild.config.ts
35
- import { defineConfig } from '@rsbuild/core';
36
- import { pluginPrecioPreset } from '@precio/rsbuild-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
- ### Advanced Configuration
16
+ ### rsbuild.config.ts
50
17
 
51
18
  ```typescript
52
- // rsbuild.config.ts
53
19
  import { defineConfig } from '@rsbuild/core';
54
- import { pluginPrecioPreset } from '@precio/rsbuild-preset';
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
- pluginPrecioPreset({
59
- cdn: 'https://cdn.example.com',
60
- html: {
61
- title: 'My Micro-Frontend App',
62
- description: 'A modern micro-frontend application',
63
- },
64
- output: {
65
- assetPrefixInDev: '/',
66
- sourceMapProd: false,
67
- hashFilenames: true,
68
- filename: {
69
- js: 'static/js/[name].[contenthash:8].js',
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
- ## Auto-Expose
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
- // src/components/Modal.tsx
122
- import { Modal } from './Modal';
123
- import { exposeComponent } from '@precio/rsbuild-preset';
43
+ /** @expose AnimatedButton */
44
+ export default function AnimatedButton(){ ... }
124
45
 
125
- export default exposeComponent(Modal, 'Modal');
46
+ // or:
47
+ exposeComponent(AnimatedButton, 'AnimatedButton');
126
48
  ```
127
49
 
128
- ### Configuration Options
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
- # .env
145
- REMOTE_USER_SERVICE=https://user-service.example.com/remoteEntry.js
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
- ### Manifest JSON
57
+ ### Or manifest (local or http)
150
58
 
151
59
  ```json
152
- // remotes.json
153
60
  {
154
- "user-service": "https://user-service.example.com/remoteEntry.js",
155
- "payment-service": "https://payment.example.com/remoteEntry.js",
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
- ### Configuration Options
66
+ ## Options
169
67
 
170
- ```typescript
171
- autoRemotes: {
172
- envPrefix: 'REMOTE_', // Environment variable prefix
173
- manifestPathOrUrl: './remotes.json', // Local file or HTTP URL
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
- ## API Reference
79
+ ## Notes
178
80
 
179
- ### PrecioPresetOptions
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
- ```typescript
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
- ### SharedEntry
215
-
216
- ```typescript
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 '@precio/rsbuild-preset';
88
+ ```bash
89
+ # 1) Login to npm
90
+ npm login
234
91
 
235
- export default defineConfig({
236
- plugins: [
237
- pluginPrecioPreset({
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
- ### Remote Application
251
-
252
- ```typescript
253
- // rsbuild.config.ts
254
- import { defineConfig } from '@rsbuild/core';
255
- import { pluginPrecioPreset } from '@precio/rsbuild-preset';
97
+ ## Consumer Usage
256
98
 
257
- export default defineConfig({
258
- plugins: [
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
- ## Development
103
+ ### Example ENV configuration:
272
104
 
273
105
  ```bash
274
- # Build the preset
275
- npm run build
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 SharedEntry = string | {
4
- singleton?: boolean;
5
- eager?: boolean;
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
- tag?: string;
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
- type PrecioPresetOptions = {
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 AutoExposeOptions, type AutoRemotesOptions, type PrecioPresetOptions, type SharedEntry, pluginPrecioPreset };
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
- async function collectAutoExposesWithWrapper(opts = {}) {
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 = fs2.readFileSync(abs, "utf8");
21
- const jsdocMatch = text.match(new RegExp(`/\\*\\*[^*]*\\*+[^/]*@${tag}\\s*([^*\\n\\r]*)`, "m"));
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 (!res.ok) {
55
- console.warn(`Failed to fetch manifest from ${manifestPathOrUrl}: ${res.status} ${res.statusText}`);
56
- } else {
57
- obj = await res.json();
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 = path.resolve(manifestPathOrUrl);
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(`Manifest file not found: ${manifestPath}`);
90
+ console.warn(`[mf-auto] manifest file not found: ${manifestPath}`);
69
91
  }
70
- } catch (error) {
71
- console.warn(`Error reading manifest file ${manifestPathOrUrl}:`, error);
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)) {
@@ -81,42 +103,40 @@ async function collectAutoRemotes(opts = {}) {
81
103
  }
82
104
 
83
105
  // src/index.ts
84
- function pluginPrecioPreset(opts) {
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;
85
117
  return {
86
- name: "precio-rsbuild-preset",
118
+ name: "plugin-mf-auto",
119
+ apply: "build",
87
120
  async setup(api) {
88
- const isProd = process.env.NODE_ENV === "production";
89
- const CDN = opts.cdn ?? process.env.CDN_URL;
90
- const autoExposes = await collectAutoExposesWithWrapper(opts.autoExpose);
91
- const autoRemotes = await collectAutoRemotes(opts.autoRemotes);
92
- api.modifyRsbuildConfig((config) => {
93
- config.html = {
94
- ...config.html ?? {},
95
- title: opts.html?.title ?? config.html?.title ?? "Precio App",
96
- meta: {
97
- viewport: "width=device-width, initial-scale=1",
98
- description: opts.html?.description ?? "Precio App",
99
- ...config.html?.meta ?? {}
100
- }
101
- };
102
- const out = opts.output ?? {};
103
- config.output = {
104
- ...config.output ?? {},
105
- assetPrefix: isProd ? CDN ?? "/" : out.assetPrefixInDev ?? "/",
106
- sourceMap: isProd ? out.sourceMapProd ?? false : true,
107
- filenameHash: out.hashFilenames ?? true,
108
- filename: {
109
- js: out.filename?.js ?? "static/js/[name].[contenthash:8].js",
110
- css: out.filename?.css ?? "static/css/[name].[contenthash:8].css",
111
- ...config.output?.filename ?? {}
112
- },
113
- distPath: {
114
- js: out.distPath?.js ?? "static/js",
115
- css: out.distPath?.css ?? "static/css",
116
- ...config.output?.distPath ?? {}
117
- }
118
- };
119
- 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 ? {
120
140
  react: {
121
141
  singleton: true,
122
142
  eager: true,
@@ -127,36 +147,44 @@ function pluginPrecioPreset(opts) {
127
147
  eager: true,
128
148
  requiredVersion: false
129
149
  },
130
- ...opts.mf?.shared ?? {}
131
- };
132
- const exposes = { ...autoExposes, ...opts.mf.exposes ?? {} };
133
- const remotes = { ...autoRemotes, ...opts.mf.remotes ?? {} };
134
- config.plugins = [
135
- ...config.plugins ?? [],
136
- pluginReact(),
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(
137
173
  pluginModuleFederation({
138
- name: opts.mf.name,
139
- filename: opts.mf.filename ?? "static/js/remoteEntry.js",
140
- manifest: opts.mf.manifest ?? false,
174
+ name: name ?? path3.basename(api.context.rootPath).replace(/\W+/g, ""),
175
+ filename,
141
176
  exposes,
142
177
  remotes,
143
- shared
178
+ shared: sharedDefaults
144
179
  })
145
- ];
146
- config.performance = {
147
- ...config.performance ?? {},
148
- chunkSplit: {
149
- strategy: "split-by-module",
150
- forceSplitting: [/node_modules\/react/, /node_modules\/react-dom/],
151
- ...config.performance?.chunkSplit ?? {}
152
- }
153
- };
180
+ );
154
181
  return config;
155
182
  });
156
183
  }
157
184
  };
158
185
  }
159
-
160
- export { pluginPrecioPreset };
161
- //# sourceMappingURL=index.js.map
162
- //# sourceMappingURL=index.js.map
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": "0.1.0",
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.4.16",
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-expose",
30
- "auto-remotes"
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/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 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\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 // Chuẩn bị dữ liệu \"thông minh\"\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 // HTML\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 // OUTPUT\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\r\n // SHARED (giữ literal type cho false)\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 // Exposes/Remotes: auto + cho phép override thủ công\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 } 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 forceSplitting: [/node_modules\\/react/, /node_modules\\/react-dom/],\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\n// Export types for consumers\r\nexport type {\r\n PrecioPresetOptions,\r\n AutoExposeOptions,\r\n AutoRemotesOptions,\r\n SharedEntry,\r\n} from \"./types\";\r\n"]}