@imtf/profile-scripts 1.5.2 → 2.0.0-beta.0

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.
@@ -7,13 +7,26 @@ 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(`../scripts/esbuild.mjs`);
10
+ const useVite = args.includes("--vite");
11
+ const useViteFederation = args.includes("--federation");
12
+
13
+ // federation automatically implies vite
14
+ const useViteBuild = useVite || useViteFederation;
15
+
16
+ // Remove internal routing flags before forwarding
17
+ const cleanedArgs = args.filter(
18
+ (arg) => arg !== "--vite"
19
+ );
20
+
21
+ const scriptPath = useViteBuild
22
+ ? require.resolve("../scripts/viteBuild.mjs")
23
+ : require.resolve("../scripts/esbuild.mjs");
11
24
 
12
25
  switch (script) {
13
26
  case "start":
14
27
  case "build": {
15
- const nodeArgs = scriptIndex > 0 ? args.slice(0, scriptIndex) : [];
16
- const scriptArgs = args.slice(scriptIndex + 1);
28
+ const nodeArgs = scriptIndex > 0 ? cleanedArgs.slice(0, scriptIndex) : [];
29
+ const scriptArgs = cleanedArgs.slice(scriptIndex + 1);
17
30
 
18
31
  if (script === "start") {
19
32
  scriptArgs.push("--watch");
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,10 +1,10 @@
1
1
  {
2
2
  "name": "@imtf/profile-scripts",
3
- "version": "1.5.2",
3
+ "version": "2.0.0-beta.0",
4
4
  "description": "Default scripts to bundle & transpile imtf front-end plugins",
5
5
  "main": "index.js",
6
6
  "bin": {
7
- "profile-scripts": "bin/profile-scripts.js"
7
+ "profile-scripts": "./bin/profile-scripts.js"
8
8
  },
9
9
  "files": [
10
10
  "bin/",
@@ -19,14 +19,23 @@
19
19
  "license": "UNLICENSED",
20
20
  "private": false,
21
21
  "dependencies": {
22
+ "@originjs/vite-plugin-federation": "^1.4.1",
22
23
  "@svgr/core": "^8.1.0",
23
24
  "@svgr/plugin-jsx": "^8.1.0",
24
25
  "@svgr/plugin-svgo": "^8.1.0",
26
+ "@vitejs/plugin-react": "^5.1.4",
25
27
  "esbuild": "^0.27.0",
26
28
  "esbuild-css-modules-plugin": "^3.1.4",
27
29
  "esbuild-plugin-external-global": "^1.0.1",
28
30
  "esbuild-plugin-inline-css": "^0.0.1",
31
+ "http-server": "^14.1.1",
29
32
  "minimist": "^1.2.8",
30
- "serve-handler": "^6.1.6"
33
+ "serve-handler": "^6.1.6",
34
+ "vite": "^7.3.1",
35
+ "vite-plugin-css-injected-by-js": "^4.0.1",
36
+ "vite-tsconfig-paths": "^6.1.1"
37
+ },
38
+ "devDependencies": {
39
+ "@types/minimist": "^1.2.5"
31
40
  }
32
41
  }
@@ -3,8 +3,7 @@ import esbuild from "esbuild-plugin-external-global";
3
3
  import InlineCSSPlugin from "esbuild-plugin-inline-css";
4
4
  import CssModulesPlugin from "esbuild-css-modules-plugin";
5
5
  import minimist from "minimist";
6
- import handler from "serve-handler";
7
- import http from "http";
6
+ import HttpServer from "http-server";
8
7
 
9
8
  // eslint-disable-next-line import/no-named-as-default-member
10
9
  const externalGlobalPlugin = esbuild.externalGlobalPlugin;
@@ -88,9 +87,8 @@ if (watch) {
88
87
  await ctx.watch();
89
88
 
90
89
  // Create a server for the build directory
91
- http
92
- .createServer((...params) => handler(...params, { public: "build" }))
93
- .listen(3010, () => console.log("Running at http://localhost:3010"));
90
+ const server = HttpServer.createServer({ root: "build" });
91
+ server.listen(3010, () => console.log("Running at http://localhost:3010"));
94
92
  } else {
95
93
  await build(config);
96
94
  }
@@ -0,0 +1,202 @@
1
+ /* eslint-disable @typescript-eslint/no-unsafe-return */
2
+ /* eslint-disable @typescript-eslint/no-unsafe-member-access */
3
+ /* eslint-disable @typescript-eslint/no-unsafe-argument */
4
+ /* eslint-disable @typescript-eslint/no-unsafe-assignment */
5
+ /* eslint-disable @typescript-eslint/no-unsafe-call */
6
+
7
+ import http from "http";
8
+ import fs from "node:fs";
9
+ import path from "path";
10
+ import { pathToFileURL } from "url";
11
+
12
+ import federation from "@originjs/vite-plugin-federation";
13
+ import react from "@vitejs/plugin-react";
14
+ import { transform } from "esbuild";
15
+ import minimist from "minimist";
16
+ import handler from "serve-handler";
17
+ import { build as viteBuild } from "vite";
18
+ import cssInjectedByJsPlugin from "vite-plugin-css-injected-by-js";
19
+ import tsconfigPaths from "vite-tsconfig-paths";
20
+
21
+ import notifierPlugin from "./vitePlugins/viteNotifierPlugin.mjs";
22
+ import svgPlugin from "./vitePlugins/viteSvgPlugin.mjs";
23
+
24
+ const {
25
+ watch,
26
+ entryPoint,
27
+ externalGlobal,
28
+ federation: enableFederation,
29
+ } = minimist(process.argv.slice(2));
30
+
31
+ const consumerRoot = process.cwd();
32
+ const outDir = "build";
33
+
34
+ const entryAbs = entryPoint
35
+ ? path.resolve(consumerRoot, entryPoint)
36
+ : path.resolve(consumerRoot, "src/index.js");
37
+
38
+ // Force JSX transform for .js files in src
39
+ const jsxPreTransformPlugin = {
40
+ name: "jsx-pre-transform",
41
+ enforce: "pre",
42
+ async transform(code, id) {
43
+ if (id.endsWith(".js") && id.includes(path.resolve(consumerRoot, "src"))) {
44
+ const result = await transform(code, {
45
+ loader: "jsx",
46
+ jsx: "automatic",
47
+ target: "esnext",
48
+ sourcemap: !watch,
49
+ });
50
+
51
+ return {
52
+ code: result.code,
53
+ map: result.map || null,
54
+ };
55
+ }
56
+ return null;
57
+ },
58
+ };
59
+
60
+ // Custom plugin to resolve package.json imports from node_modules
61
+ const packageJsonResolver = {
62
+ name: "package-json-resolver",
63
+ enforce: "pre",
64
+ resolveId(source) {
65
+ if (!source.endsWith("/package.json")) return null;
66
+
67
+ const pkgName = source.replace(/\/package\.json$/, "");
68
+
69
+ const resolvedPath = path.resolve(
70
+ consumerRoot,
71
+ "node_modules",
72
+ pkgName,
73
+ "package.json",
74
+ );
75
+
76
+ if (fs.existsSync(resolvedPath)) {
77
+ return resolvedPath;
78
+ }
79
+
80
+ return null;
81
+ },
82
+ };
83
+
84
+ // Parse external globals
85
+ const externalGlobals = externalGlobal
86
+ ? (typeof externalGlobal === "string"
87
+ ? [externalGlobal]
88
+ : externalGlobal
89
+ ).reduce((acc, curr) => {
90
+ const [key, value] = curr.split("=");
91
+ acc[key] = value;
92
+ return acc;
93
+ }, {})
94
+ : {};
95
+
96
+ // Dynamically resolve optional federation.config.(js|mjs) from consumer root
97
+ const resolveFederationConfig = async () => {
98
+ if (!enableFederation) return {};
99
+
100
+ const candidates = ["federation.config.mjs", "federation.config.js"];
101
+
102
+ for (const file of candidates) {
103
+ const fullPath = path.resolve(consumerRoot, file);
104
+ if (fs.existsSync(fullPath)) {
105
+ const mod = await import(pathToFileURL(fullPath).href);
106
+ return mod.default ?? mod;
107
+ }
108
+ }
109
+
110
+ throw new Error(
111
+ "[profile-scripts] --federation requires federation.config.(js|mjs) in project root.",
112
+ );
113
+ };
114
+
115
+ // Vite plugins
116
+ const vitePlugins = [
117
+ // Must run before other plugins: transforms JSX inside .js files
118
+ // so Vite's import analysis can parse them correctly
119
+ jsxPreTransformPlugin,
120
+
121
+ // Custom resolver for package.json imports
122
+ packageJsonResolver,
123
+
124
+ // React JSX transform + automatic runtime
125
+ react({
126
+ jsxRuntime: "automatic",
127
+ include: /\.(js|jsx|ts|tsx)$/,
128
+ }),
129
+
130
+ // Resolve tsconfig path aliases
131
+ tsconfigPaths(),
132
+
133
+ // Inject CSS into the bundle for library consumers without needing separate CSS files
134
+ cssInjectedByJsPlugin(),
135
+
136
+ // Handle SVG as React components or assets
137
+ svgPlugin(),
138
+
139
+ // Log build and rebuild status to console
140
+ notifierPlugin(Boolean(watch)),
141
+ ];
142
+
143
+ // Inject Module Federation support when --federation flag is provided
144
+ if (enableFederation) {
145
+ const federationConfig = await resolveFederationConfig();
146
+
147
+ vitePlugins.push(federation(federationConfig));
148
+ }
149
+
150
+ /**
151
+ * Vite build configuration
152
+ * @type {import('vite').UserConfig}
153
+ */
154
+ const viteConfig = {
155
+ root: consumerRoot,
156
+ plugins: vitePlugins,
157
+ logLevel: watch ? "error" : "info", // !!! Need to discuss
158
+ build: {
159
+ outDir,
160
+ emptyOutDir: true,
161
+ target: "esnext",
162
+ sourcemap: watch,
163
+ rollupOptions: {
164
+ input: entryAbs,
165
+ external: [
166
+ "react",
167
+ "react-dom",
168
+ "react-router-dom",
169
+ "IMTFPlugins",
170
+ ...Object.keys(externalGlobals),
171
+ ],
172
+ output: {
173
+ entryFileNames: "index.js",
174
+ format: "iife",
175
+ inlineDynamicImports: false,
176
+ globals: {
177
+ react: "React",
178
+ "react-dom": "ReactDOM",
179
+ "react-router-dom": "reactRouterDom",
180
+ IMTFPlugins: "IMTFPlugins",
181
+ ...externalGlobals,
182
+ },
183
+ },
184
+ },
185
+ minify: watch ? false : process.env.IMTF_MINIFY_WEBAPP !== "false",
186
+ watch: watch ? {} : null,
187
+ },
188
+ };
189
+
190
+ // Run Vite build in either watch or build mode
191
+ if (watch) {
192
+ await viteBuild(viteConfig);
193
+
194
+ http
195
+ .createServer((...params) => handler(...params, { public: outDir }))
196
+ .listen(3010, () => {
197
+ // eslint-disable-next-line no-console
198
+ console.log("Running at http://localhost:3010");
199
+ });
200
+ } else {
201
+ await viteBuild(viteConfig);
202
+ }
@@ -0,0 +1,27 @@
1
+ const viteNotifierPlugin = (watch) => {
2
+ let count = 0;
3
+
4
+ return {
5
+ name: "notifier-plugin-vite",
6
+
7
+ buildStart() {
8
+ count = 0;
9
+ },
10
+
11
+ buildEnd(error) {
12
+ if (error) return;
13
+
14
+ if (count++ === 0) {
15
+ if (watch) {
16
+ console.log(`1st build: ${Date.now()}`);
17
+ } else {
18
+ console.log("Successfully built");
19
+ }
20
+ } else {
21
+ console.log(`Rebuilt: ${Date.now()}`);
22
+ }
23
+ },
24
+ };
25
+ };
26
+
27
+ export default viteNotifierPlugin;
@@ -0,0 +1,47 @@
1
+ const viteSvgPlugin = (options = {}) => {
2
+ return {
3
+ name: "vite-svgr-plugin",
4
+ enforce: "pre",
5
+ async transform(code, id) {
6
+ if (!id.endsWith(".svg")) {
7
+ return null;
8
+ }
9
+ const svgPath = id.split("?")[0].split("#")[0];
10
+
11
+ // Read file content
12
+ const svg = await fs.promises.readFile(svgPath, "utf8");
13
+
14
+ // Determine relative path
15
+ const relativePath = path.relative(process.cwd(), svgPath);
16
+
17
+ // This is an asset ==> dataurl
18
+ if (
19
+ !relativePath.startsWith("src/") ||
20
+ relativePath.startsWith("src/assets/")
21
+ ) {
22
+ const dataurl = `data:image/svg+xml;base64,${Buffer.from(svg).toString("base64")}`;
23
+ return {
24
+ code: `export default ${JSON.stringify(dataurl)};`,
25
+ map: null
26
+ };
27
+ }
28
+
29
+ // Otherwise it's a react component
30
+ const contents = await transform(
31
+ svg,
32
+ {
33
+ plugins: ["@svgr/plugin-svgo", "@svgr/plugin-jsx"],
34
+ ...options
35
+ },
36
+ { filePath: svgPath }
37
+ );
38
+
39
+ return {
40
+ code: contents,
41
+ map: null
42
+ };
43
+ }
44
+ };
45
+ }
46
+
47
+ export default viteSvgPlugin;