@imtf/profile-scripts 1.5.3 → 2.0.0-beta.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 +275 -0
- package/bin/profile-scripts.js +1 -1
- package/changelog.md +8 -0
- package/package.json +13 -11
- package/scripts/utils/resolveEntryPoint.mjs +50 -0
- package/scripts/viteBuild.mjs +195 -0
- package/scripts/vitePlugins/viteNotifierPlugin.mjs +35 -0
- package/scripts/vitePlugins/viteSvgPlugin.mjs +56 -0
- package/scripts/esbuild.mjs +0 -94
- package/scripts/notifierPlugin.mjs +0 -24
- package/scripts/svgPlugin.mjs +0 -44
package/README.md
ADDED
|
@@ -0,0 +1,275 @@
|
|
|
1
|
+
# @imtf/profile-scripts (Vite + Module Federation)
|
|
2
|
+
|
|
3
|
+
## Overview
|
|
4
|
+
|
|
5
|
+
`profile-scripts` is a standardized build and development utility designed for Profile applications within the ICOS.
|
|
6
|
+
|
|
7
|
+
It provides a **vite-based build** with optional **Module Federation integration**.
|
|
8
|
+
|
|
9
|
+
---
|
|
10
|
+
|
|
11
|
+
## Installation
|
|
12
|
+
|
|
13
|
+
```bash
|
|
14
|
+
npm install --save-dev @imtf/profile-scripts
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
or
|
|
18
|
+
|
|
19
|
+
```bash
|
|
20
|
+
yarn add -D @imtf/profile-scripts
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
---
|
|
24
|
+
|
|
25
|
+
## Usage
|
|
26
|
+
|
|
27
|
+
### Start (development/watch mode)
|
|
28
|
+
|
|
29
|
+
```bash
|
|
30
|
+
profile-scripts start
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
Starts the build in watch mode and serves output at:
|
|
34
|
+
|
|
35
|
+
```
|
|
36
|
+
http://localhost:3010
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
---
|
|
40
|
+
|
|
41
|
+
### Build (Production)
|
|
42
|
+
|
|
43
|
+
```bash
|
|
44
|
+
profile-scripts build
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
Generates the production bundle in the `build/` directory.
|
|
48
|
+
|
|
49
|
+
---
|
|
50
|
+
|
|
51
|
+
## CLI Options
|
|
52
|
+
|
|
53
|
+
### `--entryPoint`
|
|
54
|
+
|
|
55
|
+
Specify a custom entry file.
|
|
56
|
+
|
|
57
|
+
```bash
|
|
58
|
+
profile-scripts start --entryPoint src/index.ts
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
If not provided, the following files are checked (in order):
|
|
62
|
+
|
|
63
|
+
```
|
|
64
|
+
index.ts
|
|
65
|
+
index.tsx
|
|
66
|
+
index.js
|
|
67
|
+
index.jsx
|
|
68
|
+
src/index.ts
|
|
69
|
+
src/index.tsx
|
|
70
|
+
src/index.js
|
|
71
|
+
src/index.jsx
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
---
|
|
75
|
+
|
|
76
|
+
### `--federation`
|
|
77
|
+
|
|
78
|
+
Enable Module Federation support.
|
|
79
|
+
|
|
80
|
+
```bash
|
|
81
|
+
profile-scripts start --federation
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
When enabled:
|
|
85
|
+
- Loads configuration from `federation.config.js` or `federation.config.mjs` in project root
|
|
86
|
+
- Integrates with `vite-plugin-federation`
|
|
87
|
+
|
|
88
|
+
---
|
|
89
|
+
|
|
90
|
+
### `--externalGlobal`
|
|
91
|
+
|
|
92
|
+
Map external dependencies to global variables (optional).
|
|
93
|
+
|
|
94
|
+
```bash
|
|
95
|
+
profile-scripts build --externalGlobal=react/jsx-runtime=window.jsxRuntime
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
Use this when a dependency is provided globally by the host application and should not be bundled.
|
|
99
|
+
|
|
100
|
+
---
|
|
101
|
+
|
|
102
|
+
## Project Requirements
|
|
103
|
+
|
|
104
|
+
Minimal structure:
|
|
105
|
+
|
|
106
|
+
```
|
|
107
|
+
project-root/
|
|
108
|
+
src/
|
|
109
|
+
index.tsx (or equivalent entry)
|
|
110
|
+
package.json
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
For Module Federation:
|
|
114
|
+
|
|
115
|
+
```
|
|
116
|
+
federation.config.js
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
---
|
|
120
|
+
|
|
121
|
+
## Remote Application Requirements
|
|
122
|
+
|
|
123
|
+
To enable Module Federation integration, the **remote application** must be configured as follows.
|
|
124
|
+
|
|
125
|
+
### Module Federation Configuration
|
|
126
|
+
|
|
127
|
+
```js
|
|
128
|
+
module.exports = {
|
|
129
|
+
name: "remote-app",
|
|
130
|
+
exposes: {
|
|
131
|
+
"./App": "./src/App",
|
|
132
|
+
},
|
|
133
|
+
filename: "remoteEntry.js",
|
|
134
|
+
library: { type: "module" }, // **required**
|
|
135
|
+
};
|
|
136
|
+
```
|
|
137
|
+
|
|
138
|
+
---
|
|
139
|
+
|
|
140
|
+
### Build Configuration
|
|
141
|
+
|
|
142
|
+
```js
|
|
143
|
+
experiments: { outputModule: true },
|
|
144
|
+
|
|
145
|
+
externalsType: "global",
|
|
146
|
+
|
|
147
|
+
externals: {
|
|
148
|
+
react: "React",
|
|
149
|
+
"react-dom": "ReactDOM",
|
|
150
|
+
};
|
|
151
|
+
```
|
|
152
|
+
|
|
153
|
+
### Notes
|
|
154
|
+
|
|
155
|
+
* The `library.type = "module"` configuration is **mandatory** for compatibility with vite-based host applications
|
|
156
|
+
* `outputModule: true` ensures the remote is emitted as an ES module
|
|
157
|
+
* React dependencies must be externalised and provided by the host application
|
|
158
|
+
|
|
159
|
+
---
|
|
160
|
+
|
|
161
|
+
## SVG Handling
|
|
162
|
+
|
|
163
|
+
Built-in SVG support:
|
|
164
|
+
|
|
165
|
+
- Files inside `src/` → transformed into React components
|
|
166
|
+
- Files inside `src/assets/` or outside `src/` → converted to base64 data URLs
|
|
167
|
+
|
|
168
|
+
---
|
|
169
|
+
|
|
170
|
+
## CSS Handling
|
|
171
|
+
|
|
172
|
+
CSS is automatically injected into the JavaScript bundle.
|
|
173
|
+
No separate CSS files are generated.
|
|
174
|
+
|
|
175
|
+
---
|
|
176
|
+
|
|
177
|
+
## Output
|
|
178
|
+
|
|
179
|
+
Build output:
|
|
180
|
+
|
|
181
|
+
```
|
|
182
|
+
build/
|
|
183
|
+
index.js
|
|
184
|
+
```
|
|
185
|
+
|
|
186
|
+
- Format: IIFE
|
|
187
|
+
- Designed for integration into host applications
|
|
188
|
+
|
|
189
|
+
---
|
|
190
|
+
|
|
191
|
+
## Environment Variable
|
|
192
|
+
|
|
193
|
+
### `IMTF_MINIFY_WEBAPP`
|
|
194
|
+
|
|
195
|
+
Controls minification:
|
|
196
|
+
|
|
197
|
+
```bash
|
|
198
|
+
IMTF_MINIFY_WEBAPP=false profile-scripts build
|
|
199
|
+
```
|
|
200
|
+
|
|
201
|
+
- Default: minification enabled
|
|
202
|
+
- Uses Vite default minifier
|
|
203
|
+
|
|
204
|
+
---
|
|
205
|
+
|
|
206
|
+
## Notes
|
|
207
|
+
|
|
208
|
+
- vite is the default build system
|
|
209
|
+
- Module Federation is enabled only via `--federation`
|
|
210
|
+
- Entry point is optional but must resolve to a valid file
|
|
211
|
+
- External globals should be used only when dependencies are provided by the host
|
|
212
|
+
|
|
213
|
+
---
|
|
214
|
+
## Profile Configuration
|
|
215
|
+
Two supported approaches are available based on how the remote is resolved.
|
|
216
|
+
|
|
217
|
+
---
|
|
218
|
+
|
|
219
|
+
### Option 1: URL-based Remote (Static)
|
|
220
|
+
|
|
221
|
+
```js
|
|
222
|
+
export default {
|
|
223
|
+
name: "host-app",
|
|
224
|
+
remotes: {
|
|
225
|
+
remoteModule: {
|
|
226
|
+
externalType: "url",
|
|
227
|
+
external: "http://localhost:3021/remoteEntry.js",
|
|
228
|
+
from: "webpack",
|
|
229
|
+
format: "esm",
|
|
230
|
+
},
|
|
231
|
+
},
|
|
232
|
+
};
|
|
233
|
+
```
|
|
234
|
+
|
|
235
|
+
---
|
|
236
|
+
|
|
237
|
+
### Option 2: Promise-based Remote (Dynamic)
|
|
238
|
+
|
|
239
|
+
Use this when the remote URL needs to be resolved dynamically at runtime.
|
|
240
|
+
|
|
241
|
+
```js
|
|
242
|
+
export default {
|
|
243
|
+
name: "host-app",
|
|
244
|
+
remotes: {
|
|
245
|
+
remoteapp: {
|
|
246
|
+
externalType: "promise",
|
|
247
|
+
external: "<<resolver function as string>>",
|
|
248
|
+
from: "webpack",
|
|
249
|
+
format: "esm",
|
|
250
|
+
},
|
|
251
|
+
},
|
|
252
|
+
};
|
|
253
|
+
```
|
|
254
|
+
|
|
255
|
+
---
|
|
256
|
+
|
|
257
|
+
## Troubleshooting
|
|
258
|
+
|
|
259
|
+
### Entry point not found
|
|
260
|
+
|
|
261
|
+
Ensure one of the supported entry files exists or pass `--entryPoint`.
|
|
262
|
+
|
|
263
|
+
---
|
|
264
|
+
|
|
265
|
+
### Federation configuration missing
|
|
266
|
+
|
|
267
|
+
Ensure `federation.config.js` or `.mjs` exists when using `--federation`.
|
|
268
|
+
|
|
269
|
+
---
|
|
270
|
+
|
|
271
|
+
### Runtime dependency issues
|
|
272
|
+
|
|
273
|
+
If a dependency is expected from the host, configure it using `--externalGlobal`.
|
|
274
|
+
|
|
275
|
+
---
|
package/bin/profile-scripts.js
CHANGED
|
@@ -7,7 +7,7 @@ const args = process.argv.slice(2);
|
|
|
7
7
|
const scriptIndex = args.findIndex((x) => x === "build" || x === "start");
|
|
8
8
|
const script = scriptIndex === -1 ? args[0] : args[scriptIndex];
|
|
9
9
|
|
|
10
|
-
const scriptPath = require.resolve(
|
|
10
|
+
const scriptPath = require.resolve("../scripts/viteBuild.mjs");
|
|
11
11
|
|
|
12
12
|
switch (script) {
|
|
13
13
|
case "start":
|
package/changelog.md
CHANGED
|
@@ -2,6 +2,14 @@
|
|
|
2
2
|
|
|
3
3
|
Here you can find a resume of all changes between versions.
|
|
4
4
|
|
|
5
|
+
## 2.0.0
|
|
6
|
+
|
|
7
|
+
### Breaking changes
|
|
8
|
+
|
|
9
|
+
- ViteJS will now be used as the default bundler for development and production builds.
|
|
10
|
+
The previous version of the package used esbuild.
|
|
11
|
+
This change allows Module Federation to be supported.
|
|
12
|
+
|
|
5
13
|
## 1.5.0
|
|
6
14
|
|
|
7
15
|
### Features
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@imtf/profile-scripts",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "2.0.0-beta.1",
|
|
4
4
|
"description": "Default scripts to bundle & transpile imtf front-end plugins",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"bin": {
|
|
@@ -12,21 +12,23 @@
|
|
|
12
12
|
"changelog.md"
|
|
13
13
|
],
|
|
14
14
|
"scripts": {
|
|
15
|
-
"build": "NODE_ENV=production node scripts/
|
|
16
|
-
"start": "node scripts/
|
|
15
|
+
"build": "NODE_ENV=production node scripts/viteBuild.mjs",
|
|
16
|
+
"start": "node scripts/viteBuild.mjs --watch"
|
|
17
17
|
},
|
|
18
18
|
"prettier": "@imtf/prettier-config",
|
|
19
19
|
"license": "UNLICENSED",
|
|
20
20
|
"private": false,
|
|
21
21
|
"dependencies": {
|
|
22
|
-
"@
|
|
23
|
-
"@
|
|
24
|
-
"@svgr/plugin-svgo": "^8.1.0",
|
|
22
|
+
"@originjs/vite-plugin-federation": "^1.4.1",
|
|
23
|
+
"@vitejs/plugin-react": "^5.1.4",
|
|
25
24
|
"esbuild": "^0.27.0",
|
|
26
|
-
"
|
|
27
|
-
"
|
|
28
|
-
"
|
|
29
|
-
"
|
|
30
|
-
"
|
|
25
|
+
"minimist": "^1.2.8",
|
|
26
|
+
"serve-handler": "^6.1.6",
|
|
27
|
+
"vite": "^7.3.1",
|
|
28
|
+
"vite-plugin-css-injected-by-js": "^4.0.1",
|
|
29
|
+
"vite-tsconfig-paths": "^6.0.1"
|
|
30
|
+
},
|
|
31
|
+
"devDependencies": {
|
|
32
|
+
"@types/minimist": "^1.2.5"
|
|
31
33
|
}
|
|
32
34
|
}
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import fs from "node:fs";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
|
|
4
|
+
const DEFAULT_ENTRYPOINTS = [
|
|
5
|
+
"src/index.ts",
|
|
6
|
+
"src/index.tsx",
|
|
7
|
+
"src/index.js",
|
|
8
|
+
"src/index.jsx",
|
|
9
|
+
"index.ts",
|
|
10
|
+
"index.tsx",
|
|
11
|
+
"index.js",
|
|
12
|
+
"index.jsx",
|
|
13
|
+
];
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Resolves the application entry point.
|
|
17
|
+
* Uses the provided entryPoint, otherwise falls back to defaults.
|
|
18
|
+
* Throws a simple error if no valid entry file is found.
|
|
19
|
+
*
|
|
20
|
+
* @param {string} [entryPoint] - Entry point passed via CLI
|
|
21
|
+
* @returns {string} Absolute path to resolved entry file
|
|
22
|
+
*/
|
|
23
|
+
const resolveEntryPoint = (entryPoint) => {
|
|
24
|
+
const consumerRoot = process.cwd();
|
|
25
|
+
|
|
26
|
+
// Use CLI entryPoint if provided
|
|
27
|
+
if (entryPoint) {
|
|
28
|
+
const resolvedPath = path.isAbsolute(entryPoint)
|
|
29
|
+
? entryPoint
|
|
30
|
+
: path.resolve(consumerRoot, entryPoint);
|
|
31
|
+
|
|
32
|
+
if (!fs.existsSync(resolvedPath)) {
|
|
33
|
+
throw new Error(`[profile-scripts] entryPoint not found: ${entryPoint}`);
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
return resolvedPath;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
// Resolve from defaults
|
|
40
|
+
for (const candidate of DEFAULT_ENTRYPOINTS) {
|
|
41
|
+
const fullPath = path.resolve(consumerRoot, candidate);
|
|
42
|
+
if (fs.existsSync(fullPath)) {
|
|
43
|
+
return fullPath;
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
throw new Error(`[profile-scripts] entryPoint not found`);
|
|
48
|
+
};
|
|
49
|
+
|
|
50
|
+
export default resolveEntryPoint;
|
|
@@ -0,0 +1,195 @@
|
|
|
1
|
+
import http from "http";
|
|
2
|
+
import fs from "node:fs";
|
|
3
|
+
import path from "path";
|
|
4
|
+
import { pathToFileURL } from "url";
|
|
5
|
+
|
|
6
|
+
import federation from "@originjs/vite-plugin-federation";
|
|
7
|
+
import react from "@vitejs/plugin-react";
|
|
8
|
+
import { transform } from "esbuild";
|
|
9
|
+
import minimist from "minimist";
|
|
10
|
+
import handler from "serve-handler";
|
|
11
|
+
import { build as viteBuild } from "vite";
|
|
12
|
+
import cssInjectedByJsPlugin from "vite-plugin-css-injected-by-js";
|
|
13
|
+
import tsconfigPaths from "vite-tsconfig-paths";
|
|
14
|
+
|
|
15
|
+
import resolveEntryPoint from "./utils/resolveEntryPoint.mjs";
|
|
16
|
+
import notifierPlugin from "./vitePlugins/viteNotifierPlugin.mjs";
|
|
17
|
+
import svgPlugin from "./vitePlugins/viteSvgPlugin.mjs";
|
|
18
|
+
|
|
19
|
+
const {
|
|
20
|
+
watch,
|
|
21
|
+
entryPoint,
|
|
22
|
+
externalGlobal,
|
|
23
|
+
federation: enableFederation,
|
|
24
|
+
} = minimist(process.argv.slice(2));
|
|
25
|
+
|
|
26
|
+
const consumerRoot = process.cwd();
|
|
27
|
+
const outDir = "build";
|
|
28
|
+
|
|
29
|
+
const entryAbs = resolveEntryPoint(entryPoint);
|
|
30
|
+
|
|
31
|
+
// Force JSX transform for .js files in src
|
|
32
|
+
const jsxPreTransformPlugin = {
|
|
33
|
+
name: "jsx-pre-transform",
|
|
34
|
+
enforce: "pre",
|
|
35
|
+
async transform(code, id) {
|
|
36
|
+
if (id.endsWith(".js") && id.includes(path.resolve(consumerRoot, "src"))) {
|
|
37
|
+
const result = await transform(code, {
|
|
38
|
+
loader: "jsx",
|
|
39
|
+
jsx: "automatic",
|
|
40
|
+
target: "esnext",
|
|
41
|
+
sourcemap: !watch,
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
return {
|
|
45
|
+
code: result.code,
|
|
46
|
+
map: result.map || null,
|
|
47
|
+
};
|
|
48
|
+
}
|
|
49
|
+
return null;
|
|
50
|
+
},
|
|
51
|
+
};
|
|
52
|
+
|
|
53
|
+
// Custom plugin to resolve package.json imports from node_modules
|
|
54
|
+
const packageJsonResolver = {
|
|
55
|
+
name: "package-json-resolver",
|
|
56
|
+
enforce: "pre",
|
|
57
|
+
resolveId(source) {
|
|
58
|
+
if (!source.endsWith("/package.json")) return null;
|
|
59
|
+
|
|
60
|
+
const pkgName = source.replace(/\/package\.json$/, "");
|
|
61
|
+
|
|
62
|
+
const resolvedPath = path.resolve(
|
|
63
|
+
consumerRoot,
|
|
64
|
+
"node_modules",
|
|
65
|
+
pkgName,
|
|
66
|
+
"package.json",
|
|
67
|
+
);
|
|
68
|
+
|
|
69
|
+
if (fs.existsSync(resolvedPath)) {
|
|
70
|
+
return resolvedPath;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
return null;
|
|
74
|
+
},
|
|
75
|
+
};
|
|
76
|
+
|
|
77
|
+
// Parse external globals
|
|
78
|
+
const externalGlobals = externalGlobal
|
|
79
|
+
? (typeof externalGlobal === "string"
|
|
80
|
+
? [externalGlobal]
|
|
81
|
+
: externalGlobal
|
|
82
|
+
).reduce((acc, curr) => {
|
|
83
|
+
const [key, value] = curr.split("=");
|
|
84
|
+
acc[key] = value;
|
|
85
|
+
return acc;
|
|
86
|
+
}, {})
|
|
87
|
+
: {};
|
|
88
|
+
|
|
89
|
+
// Dynamically resolve optional federation.config.(js|mjs) from consumer root
|
|
90
|
+
const resolveFederationConfig = async () => {
|
|
91
|
+
if (!enableFederation) return {};
|
|
92
|
+
|
|
93
|
+
const candidates = ["federation.config.mjs", "federation.config.js"];
|
|
94
|
+
|
|
95
|
+
for (const file of candidates) {
|
|
96
|
+
const fullPath = path.resolve(consumerRoot, file);
|
|
97
|
+
if (fs.existsSync(fullPath)) {
|
|
98
|
+
const mod = await import(pathToFileURL(fullPath).href);
|
|
99
|
+
return mod.default ?? mod;
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
throw new Error(
|
|
104
|
+
"[profile-scripts] --federation requires federation.config.(js|mjs) in project root.",
|
|
105
|
+
);
|
|
106
|
+
};
|
|
107
|
+
|
|
108
|
+
// Vite plugins
|
|
109
|
+
const vitePlugins = [
|
|
110
|
+
// Must run before other plugins: transforms JSX inside .js files
|
|
111
|
+
// so Vite's import analysis can parse them correctly
|
|
112
|
+
jsxPreTransformPlugin,
|
|
113
|
+
|
|
114
|
+
// Custom resolver for package.json imports
|
|
115
|
+
packageJsonResolver,
|
|
116
|
+
|
|
117
|
+
// React JSX transform + automatic runtime
|
|
118
|
+
react({
|
|
119
|
+
jsxRuntime: "automatic",
|
|
120
|
+
include: /\.(js|jsx|ts|tsx)$/,
|
|
121
|
+
}),
|
|
122
|
+
|
|
123
|
+
// Resolve tsconfig path aliases
|
|
124
|
+
tsconfigPaths(),
|
|
125
|
+
|
|
126
|
+
// Inject CSS into the bundle for library consumers without needing separate CSS files
|
|
127
|
+
cssInjectedByJsPlugin(),
|
|
128
|
+
|
|
129
|
+
// Handle SVG as React components or assets
|
|
130
|
+
svgPlugin(),
|
|
131
|
+
|
|
132
|
+
// Log build and rebuild status to console
|
|
133
|
+
notifierPlugin(Boolean(watch)),
|
|
134
|
+
];
|
|
135
|
+
|
|
136
|
+
// Inject Module Federation support when --federation flag is provided
|
|
137
|
+
if (enableFederation) {
|
|
138
|
+
const federationConfig = await resolveFederationConfig();
|
|
139
|
+
vitePlugins.push(federation(federationConfig));
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
/**
|
|
143
|
+
* Vite build configuration
|
|
144
|
+
* @type {import('vite').UserConfig}
|
|
145
|
+
*/
|
|
146
|
+
const viteConfig = {
|
|
147
|
+
root: consumerRoot,
|
|
148
|
+
plugins: vitePlugins,
|
|
149
|
+
logLevel: watch ? "error" : "info",
|
|
150
|
+
build: {
|
|
151
|
+
outDir,
|
|
152
|
+
emptyOutDir: true,
|
|
153
|
+
target: "esnext",
|
|
154
|
+
sourcemap: watch,
|
|
155
|
+
chunkSizeWarningLimit: 3000,
|
|
156
|
+
rollupOptions: {
|
|
157
|
+
input: entryAbs,
|
|
158
|
+
external: [
|
|
159
|
+
"react",
|
|
160
|
+
"react-dom",
|
|
161
|
+
"react-router-dom",
|
|
162
|
+
"IMTFPlugins",
|
|
163
|
+
...Object.keys(externalGlobals),
|
|
164
|
+
],
|
|
165
|
+
output: {
|
|
166
|
+
entryFileNames: "index.js",
|
|
167
|
+
format: "iife",
|
|
168
|
+
inlineDynamicImports: false,
|
|
169
|
+
globals: {
|
|
170
|
+
react: "React",
|
|
171
|
+
"react-dom": "ReactDOM",
|
|
172
|
+
"react-router-dom": "reactRouterDom",
|
|
173
|
+
IMTFPlugins: "IMTFPlugins",
|
|
174
|
+
...externalGlobals,
|
|
175
|
+
},
|
|
176
|
+
},
|
|
177
|
+
},
|
|
178
|
+
minify: watch ? false : process.env.IMTF_MINIFY_WEBAPP !== "false",
|
|
179
|
+
watch: watch ? {} : null,
|
|
180
|
+
},
|
|
181
|
+
};
|
|
182
|
+
|
|
183
|
+
// Run Vite build in either watch or build mode
|
|
184
|
+
if (watch) {
|
|
185
|
+
await viteBuild(viteConfig);
|
|
186
|
+
|
|
187
|
+
http
|
|
188
|
+
.createServer((...params) => handler(...params, { public: outDir }))
|
|
189
|
+
.listen(3010, () => {
|
|
190
|
+
// eslint-disable-next-line no-console
|
|
191
|
+
console.log("Running at http://localhost:3010");
|
|
192
|
+
});
|
|
193
|
+
} else {
|
|
194
|
+
await viteBuild(viteConfig);
|
|
195
|
+
}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Logs Vite build and rebuild status with rebuild count.
|
|
3
|
+
* Distinguishes initial build vs subsequent rebuilds in watch mode.
|
|
4
|
+
*
|
|
5
|
+
* @param {boolean} watch - Whether running in watch mode
|
|
6
|
+
* @returns {import('vite').Plugin}
|
|
7
|
+
*/
|
|
8
|
+
const viteNotifierPlugin = (watch) => {
|
|
9
|
+
let isFirstBuild = true;
|
|
10
|
+
let rebuildCount = 0;
|
|
11
|
+
|
|
12
|
+
return {
|
|
13
|
+
name: "notifier-plugin-vite",
|
|
14
|
+
|
|
15
|
+
buildEnd(error) {
|
|
16
|
+
if (error) return;
|
|
17
|
+
|
|
18
|
+
if (!watch) {
|
|
19
|
+
console.log("Successfully built");
|
|
20
|
+
return;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
if (isFirstBuild) {
|
|
24
|
+
console.log(`1st build: ${Date.now()}`);
|
|
25
|
+
isFirstBuild = false;
|
|
26
|
+
return;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
rebuildCount += 1;
|
|
30
|
+
console.log(`Rebuilt (${rebuildCount}): ${Date.now()}`);
|
|
31
|
+
},
|
|
32
|
+
};
|
|
33
|
+
};
|
|
34
|
+
|
|
35
|
+
export default viteNotifierPlugin;
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Vite plugin to transform SVG imports based on project conventions.
|
|
3
|
+
*
|
|
4
|
+
* - SVGs outside `src/` or inside `src/assets/` are converted to base64 data URLs
|
|
5
|
+
* - SVGs inside `src/` are transformed into React components using SVGR
|
|
6
|
+
*
|
|
7
|
+
* @param {Object} [options] - SVGR configuration options
|
|
8
|
+
* @returns {import('vite').Plugin}
|
|
9
|
+
*/
|
|
10
|
+
const viteSvgPlugin = (options = {}) => {
|
|
11
|
+
return {
|
|
12
|
+
name: "vite-svgr-plugin",
|
|
13
|
+
enforce: "pre",
|
|
14
|
+
async transform(code, id) {
|
|
15
|
+
if (!id.endsWith(".svg")) {
|
|
16
|
+
return null;
|
|
17
|
+
}
|
|
18
|
+
const svgPath = id.split("?")[0].split("#")[0];
|
|
19
|
+
|
|
20
|
+
// Read file content
|
|
21
|
+
const svg = await fs.promises.readFile(svgPath, "utf8");
|
|
22
|
+
|
|
23
|
+
// Determine relative path
|
|
24
|
+
const relativePath = path.relative(process.cwd(), svgPath);
|
|
25
|
+
|
|
26
|
+
// This is an asset ==> dataurl
|
|
27
|
+
if (
|
|
28
|
+
!relativePath.startsWith("src/") ||
|
|
29
|
+
relativePath.startsWith("src/assets/")
|
|
30
|
+
) {
|
|
31
|
+
const dataurl = `data:image/svg+xml;base64,${Buffer.from(svg).toString("base64")}`;
|
|
32
|
+
return {
|
|
33
|
+
code: `export default ${JSON.stringify(dataurl)};`,
|
|
34
|
+
map: null
|
|
35
|
+
};
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
// Otherwise it's a react component
|
|
39
|
+
const contents = await transform(
|
|
40
|
+
svg,
|
|
41
|
+
{
|
|
42
|
+
plugins: ["@svgr/plugin-svgo", "@svgr/plugin-jsx"],
|
|
43
|
+
...options
|
|
44
|
+
},
|
|
45
|
+
{ filePath: svgPath }
|
|
46
|
+
);
|
|
47
|
+
|
|
48
|
+
return {
|
|
49
|
+
code: contents,
|
|
50
|
+
map: null
|
|
51
|
+
};
|
|
52
|
+
}
|
|
53
|
+
};
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
export default viteSvgPlugin;
|
package/scripts/esbuild.mjs
DELETED
|
@@ -1,94 +0,0 @@
|
|
|
1
|
-
import { build, context } from "esbuild";
|
|
2
|
-
import esbuild from "esbuild-plugin-external-global";
|
|
3
|
-
import InlineCSSPlugin from "esbuild-plugin-inline-css";
|
|
4
|
-
import CssModulesPlugin from "esbuild-css-modules-plugin";
|
|
5
|
-
import minimist from "minimist";
|
|
6
|
-
import HttpServer from "http-server";
|
|
7
|
-
|
|
8
|
-
// eslint-disable-next-line import/no-named-as-default-member
|
|
9
|
-
const externalGlobalPlugin = esbuild.externalGlobalPlugin;
|
|
10
|
-
|
|
11
|
-
import notifierPlugin from "./notifierPlugin.mjs";
|
|
12
|
-
import svgPlugin from "./svgPlugin.mjs";
|
|
13
|
-
|
|
14
|
-
const {
|
|
15
|
-
watch,
|
|
16
|
-
entryPoint,
|
|
17
|
-
externalGlobal,
|
|
18
|
-
outbase = "src",
|
|
19
|
-
} = minimist(process.argv.slice(2));
|
|
20
|
-
|
|
21
|
-
const externalGlobals = externalGlobal
|
|
22
|
-
? (typeof externalGlobal === "string"
|
|
23
|
-
? [externalGlobal]
|
|
24
|
-
: externalGlobal
|
|
25
|
-
).reduce((acc, curr) => {
|
|
26
|
-
const [key, value] = curr.split("=");
|
|
27
|
-
acc[key] = value;
|
|
28
|
-
return acc;
|
|
29
|
-
}, {})
|
|
30
|
-
: {};
|
|
31
|
-
|
|
32
|
-
const entryPoints = entryPoint
|
|
33
|
-
? typeof entryPoint === "string"
|
|
34
|
-
? [entryPoint]
|
|
35
|
-
: entryPoint
|
|
36
|
-
: ["src/index.js"];
|
|
37
|
-
|
|
38
|
-
/**
|
|
39
|
-
* @type {import('esbuild').BuildOptions}
|
|
40
|
-
*/
|
|
41
|
-
const config = {
|
|
42
|
-
entryPoints,
|
|
43
|
-
outbase,
|
|
44
|
-
outdir: "build",
|
|
45
|
-
platform: "browser",
|
|
46
|
-
bundle: true,
|
|
47
|
-
target: "es2024",
|
|
48
|
-
metafile: true,
|
|
49
|
-
|
|
50
|
-
sourcemap: watch ? "linked" : "external",
|
|
51
|
-
|
|
52
|
-
minify: watch === true ? false : process.env.IMTF_MINIFY_WEBAPP !== "false",
|
|
53
|
-
|
|
54
|
-
loader: {
|
|
55
|
-
// Enable JSX in .js files too
|
|
56
|
-
".js": "jsx",
|
|
57
|
-
// Embed fonts
|
|
58
|
-
".eot": "dataurl",
|
|
59
|
-
".ttf": "dataurl",
|
|
60
|
-
".woff": "dataurl",
|
|
61
|
-
".woff2": "dataurl",
|
|
62
|
-
// Embed images
|
|
63
|
-
".gif": "dataurl",
|
|
64
|
-
".jpg": "dataurl",
|
|
65
|
-
".png": "dataurl",
|
|
66
|
-
},
|
|
67
|
-
|
|
68
|
-
plugins: [
|
|
69
|
-
CssModulesPlugin({ inject: true }),
|
|
70
|
-
InlineCSSPlugin(),
|
|
71
|
-
svgPlugin(),
|
|
72
|
-
externalGlobalPlugin({
|
|
73
|
-
react: "window.React",
|
|
74
|
-
"react-dom": "window.ReactDOM",
|
|
75
|
-
"react-router-dom": "window.reactRouterDom",
|
|
76
|
-
IMTFPlugins: "window.IMTFPlugins",
|
|
77
|
-
...externalGlobals,
|
|
78
|
-
}),
|
|
79
|
-
notifierPlugin(watch),
|
|
80
|
-
],
|
|
81
|
-
};
|
|
82
|
-
|
|
83
|
-
if (watch) {
|
|
84
|
-
const ctx = await context(config);
|
|
85
|
-
|
|
86
|
-
// Enable watch mode
|
|
87
|
-
await ctx.watch();
|
|
88
|
-
|
|
89
|
-
// Create a server for the build directory
|
|
90
|
-
const server = HttpServer.createServer({ root: "build" });
|
|
91
|
-
server.listen(3010, () => console.log("Running at http://localhost:3010"));
|
|
92
|
-
} else {
|
|
93
|
-
await build(config);
|
|
94
|
-
}
|
|
@@ -1,24 +0,0 @@
|
|
|
1
|
-
const notifierPlugin = (watch) => ({
|
|
2
|
-
name: "svgr",
|
|
3
|
-
setup(build) {
|
|
4
|
-
let count = 0;
|
|
5
|
-
|
|
6
|
-
build.onEnd((result) => {
|
|
7
|
-
if (result.errors.length > 0) {
|
|
8
|
-
return;
|
|
9
|
-
}
|
|
10
|
-
|
|
11
|
-
if (count++ === 0) {
|
|
12
|
-
if(watch) {
|
|
13
|
-
console.log(`1st build: ${new Date().getTime()}`);
|
|
14
|
-
} else {
|
|
15
|
-
console.log(`Successfully built`);
|
|
16
|
-
}
|
|
17
|
-
} else {
|
|
18
|
-
console.log(`Rebuilt: ${new Date().getTime()}`);
|
|
19
|
-
}
|
|
20
|
-
});
|
|
21
|
-
},
|
|
22
|
-
});
|
|
23
|
-
|
|
24
|
-
export default notifierPlugin;
|
package/scripts/svgPlugin.mjs
DELETED
|
@@ -1,44 +0,0 @@
|
|
|
1
|
-
import { transform } from "@svgr/core";
|
|
2
|
-
import fs from "fs";
|
|
3
|
-
import path from "path";
|
|
4
|
-
|
|
5
|
-
const svgPlugin = (options = {}) => ({
|
|
6
|
-
name: "svgr",
|
|
7
|
-
setup(build) {
|
|
8
|
-
build.onLoad({ filter: /\.svg$/ }, async (args) => {
|
|
9
|
-
// Read file content
|
|
10
|
-
const svg = await fs.promises.readFile(args.path, "utf8");
|
|
11
|
-
|
|
12
|
-
// Determine relative path
|
|
13
|
-
const relativePath = path.relative(process.cwd(), args.path);
|
|
14
|
-
|
|
15
|
-
// This is an asset ==> dataurl
|
|
16
|
-
if (
|
|
17
|
-
!relativePath.startsWith("src/") ||
|
|
18
|
-
relativePath.startsWith("src/assets/")
|
|
19
|
-
) {
|
|
20
|
-
return {
|
|
21
|
-
contents: svg,
|
|
22
|
-
loader: "dataurl",
|
|
23
|
-
};
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
// Otherwise it's a react component
|
|
27
|
-
const contents = await transform(
|
|
28
|
-
svg,
|
|
29
|
-
{
|
|
30
|
-
plugins: ["@svgr/plugin-svgo", "@svgr/plugin-jsx"],
|
|
31
|
-
...options,
|
|
32
|
-
},
|
|
33
|
-
{ filePath: args.path }
|
|
34
|
-
);
|
|
35
|
-
|
|
36
|
-
return {
|
|
37
|
-
contents,
|
|
38
|
-
loader: "jsx",
|
|
39
|
-
};
|
|
40
|
-
});
|
|
41
|
-
},
|
|
42
|
-
});
|
|
43
|
-
|
|
44
|
-
export default svgPlugin;
|