@touk/federated-types 1.2.2-beta.1 → 1.3.0-beta.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.
Files changed (3) hide show
  1. package/README.md +41 -4
  2. package/cli.js +95 -24
  3. package/package.json +2 -1
package/README.md CHANGED
@@ -30,7 +30,6 @@ It's recommended that you spread these properties into your ModuleFederationPlug
30
30
  ```javascript
31
31
  //webpack.config.js
32
32
 
33
- const deps = require('../package.json').dependencies;
34
33
  const federationConfig = require('./federation.config.json');
35
34
 
36
35
  module.exports = {
@@ -40,9 +39,7 @@ module.exports = {
40
39
  new ModuleFederationPlugin({
41
40
  ...federationConfig,
42
41
  filename: "remoteEntry.js",
43
- shared: {
44
- ...deps,
45
- },
42
+ shared: { ... },
46
43
  }),
47
44
  ],
48
45
 
@@ -84,3 +81,43 @@ scripts: {
84
81
  "make-types": "make-federated-types --config ./path/to/my/config.json --outputDir ../../my_types/"
85
82
  }
86
83
  ```
84
+
85
+ ## Inline configuration (without config file)
86
+
87
+ If you prefer not to maintain a separate `federation.config.json` file, you can provide the configuration directly via CLI arguments using `--name` and `--exposes`:
88
+
89
+ ```bash
90
+ make-federated-types \
91
+ --name myApp \
92
+ --exposes App ./src/App.tsx \
93
+ --exposes Button ./src/components/Button.tsx \
94
+ --outputDir ./types
95
+ ```
96
+
97
+ ### Inline config features:
98
+
99
+ - **Multiple exposures**: Use `--exposes` multiple times, each followed by a key and file path
100
+ - **Auto-prefix**: Keys without `./` prefix are automatically normalized (e.g., `App` becomes `./App`)
101
+ - **Shell-friendly**: Works with tab completion for file paths
102
+ - **Priority**: Inline config takes precedence over `--config` and auto-discovery
103
+
104
+ ### Examples:
105
+
106
+ ```bash
107
+ # Simple inline config
108
+ make-federated-types --name app2 --exposes Button ./src/Button.tsx
109
+
110
+ # Multiple exposures
111
+ make-federated-types \
112
+ --name app2 \
113
+ --exposes ./Button ./src/Button.tsx \
114
+ --exposes ./Header ./src/Header.tsx
115
+
116
+ # Nested keys (auto-prefixed with ./)
117
+ make-federated-types \
118
+ --name app2 \
119
+ --exposes components/Button ./src/components/Button.tsx \
120
+ --exposes utils/helpers ./src/utils/helpers.ts
121
+ ```
122
+
123
+ **Note**: When using inline config, both `--name` and at least one `--exposes` are required.
package/cli.js CHANGED
@@ -4,6 +4,7 @@ const path = require('path');
4
4
  const fs = require('fs');
5
5
  const findNodeModules = require('find-node-modules');
6
6
  const ts = require('typescript');
7
+ const chalk = require('chalk');
7
8
 
8
9
  const formatHost = {
9
10
  getCurrentDirectory: ts.sys.getCurrentDirectory,
@@ -12,8 +13,8 @@ const formatHost = {
12
13
 
13
14
  function reportDiagnostic(diagnostic) {
14
15
  console.log(
15
- 'TS Error',
16
- diagnostic.code,
16
+ chalk.red('TS Error'),
17
+ chalk.yellow(diagnostic.code),
17
18
  ':',
18
19
  ts.flattenDiagnosticMessageText(diagnostic.messageText, formatHost.getNewLine())
19
20
  );
@@ -29,6 +30,26 @@ const getArg = (argName) => {
29
30
  const argIndex = process.argv.indexOf(argName);
30
31
  return argIndex !== -1 ? process.argv[argIndex + 1] : null;
31
32
  };
33
+ const getAllArgs = (argName) => {
34
+ const results = [];
35
+ let i = 2; // process.argv starts with node and script path
36
+
37
+ while (i < process.argv.length) {
38
+ if (process.argv[i] === argName) {
39
+ // Next two arguments are key and value
40
+ if (i + 2 < process.argv.length) {
41
+ results.push([process.argv[i + 1], process.argv[i + 2]]);
42
+ i += 3; // skip argName, key, value
43
+ } else {
44
+ console.error(chalk.red('ERROR:'), '--exposes requires two arguments (key path)');
45
+ process.exit(1);
46
+ }
47
+ } else {
48
+ i++;
49
+ }
50
+ }
51
+ return results;
52
+ };
32
53
 
33
54
  const nodeModulesOutputDir = path.resolve(nodeModules, '@types/__federated_types/');
34
55
  const saveToNodeMoulesArg = hasArg('--saveToNodeModules');
@@ -44,6 +65,13 @@ const outputDirs =
44
65
  const configPathArg = getArg('--config');
45
66
  const configPath = configPathArg ? path.resolve(configPathArg) : null;
46
67
 
68
+ const nameArg = getArg('--name');
69
+ const exposesArgs = getAllArgs('--exposes');
70
+ const verboseArg = hasArg('--verbose') || process.env.npm_config_loglevel === 'verbose';
71
+
72
+ const log = (...args) => console.log(...args);
73
+ const logDebug = (...args) => verboseArg && console.debug(chalk.gray.dim(...args));
74
+
47
75
  const findFederationConfig = (base) => {
48
76
  let files = fs.readdirSync(base);
49
77
  let queue = [];
@@ -63,21 +91,62 @@ const findFederationConfig = (base) => {
63
91
  }
64
92
  };
65
93
 
66
- if (configPath && !fs.existsSync(configPath)) {
67
- console.error(`ERROR: Unable to find a provided config: ${configPath}`);
68
- process.exit(1);
69
- }
94
+ let federationConfig;
70
95
 
71
- const federationConfigPath = configPath || findFederationConfig('./');
96
+ // Priority: inline config > --config > auto-find
97
+ if (nameArg || exposesArgs.length > 0) {
98
+ // Inline config mode
99
+ if (!nameArg || exposesArgs.length === 0) {
100
+ console.error(
101
+ chalk.red('ERROR:'),
102
+ 'Both --name and --exposes are required for inline config'
103
+ );
104
+ process.exit(1);
105
+ }
72
106
 
73
- if (federationConfigPath === undefined) {
74
- console.error(`ERROR: Unable to find a federation.config.json file in this package`);
75
- process.exit(1);
76
- }
107
+ if (configPath) {
108
+ console.warn(
109
+ chalk.yellow('WARNING:'),
110
+ 'Both inline config and --config provided. Using inline config.'
111
+ );
112
+ }
113
+
114
+ const exposes = {};
115
+
116
+ exposesArgs.forEach(([key, value]) => {
117
+ // Normalize key - add ./ if missing
118
+ const normalizedKey = key.startsWith('./') ? key : `./${key}`;
119
+ exposes[normalizedKey] = value;
120
+ });
121
+
122
+ federationConfig = {
123
+ name: nameArg,
124
+ exposes: exposes,
125
+ };
126
+
127
+ log('Using inline config');
128
+ logDebug(JSON.stringify(federationConfig, null, 2));
129
+ } else {
130
+ // File-based config (current behavior)
131
+ if (configPath && !fs.existsSync(configPath)) {
132
+ console.error(chalk.red('ERROR:'), `Unable to find a provided config: ${configPath}`);
133
+ process.exit(1);
134
+ }
77
135
 
78
- console.log(`Using config file: ${federationConfigPath}`);
136
+ const federationConfigPath = configPath || findFederationConfig('./');
137
+
138
+ if (federationConfigPath === undefined) {
139
+ console.error(
140
+ chalk.red('ERROR:'),
141
+ 'Unable to find a federation.config.json file in this package'
142
+ );
143
+ process.exit(1);
144
+ }
145
+
146
+ log('Using config file', chalk.blue.bold(federationConfigPath));
147
+ federationConfig = require(federationConfigPath);
148
+ }
79
149
 
80
- const federationConfig = require(federationConfigPath);
81
150
  const compileFiles = Object.values(federationConfig.exposes);
82
151
  const compileKeys = Object.keys(federationConfig.exposes);
83
152
 
@@ -87,6 +156,7 @@ function getModuleDeclareName(exposeName) {
87
156
  }
88
157
 
89
158
  try {
159
+ fs.mkdirSync(outputDir, { recursive: true });
90
160
  const outFile = path.resolve(outputDir, `${federationConfig.name}.d.ts`);
91
161
  if (fs.existsSync(outFile)) {
92
162
  fs.unlinkSync(outFile);
@@ -122,9 +192,10 @@ try {
122
192
 
123
193
  moduleNames.forEach((name) => {
124
194
  // exposeName - relative name of exposed component (if not found - just take moduleName)
125
- const [exposeName = name, ...aliases] = compileKeys.filter((key) =>
126
- federationConfig.exposes[key].endsWith(name)
127
- );
195
+ const [exposeName = name, ...aliases] = compileKeys.filter((key) => {
196
+ const normalizedPath = federationConfig.exposes[key].replace(/\.(tsx?|jsx?)$/, '');
197
+ return normalizedPath.endsWith(name);
198
+ });
128
199
  const regex = RegExp(`"${name}"`, 'g');
129
200
 
130
201
  const moduleDeclareName = getModuleDeclareName(exposeName);
@@ -145,23 +216,23 @@ try {
145
216
  outputDirs.forEach((_outputDir) => {
146
217
  const _outFile = path.resolve(_outputDir, `${federationConfig.name}.d.ts`);
147
218
 
148
- console.log('writing typing file:', _outFile);
219
+ logDebug('writing typing file:', _outFile);
149
220
  fs.mkdirSync(path.dirname(_outFile), { recursive: true });
150
221
  fs.writeFileSync(_outFile, typing);
151
222
 
152
- console.debug(`using output dir: ${_outputDir}`);
223
+ logDebug(`using output dir: ${_outputDir}`);
153
224
  // if we are writing to the node_modules/@types directory, add a package.json file
154
225
  if (_outputDir.includes(path.join('node_modules', '@types'))) {
155
226
  const packageJsonPath = path.resolve(_outputDir, 'package.json');
156
227
 
157
228
  if (!fs.existsSync(packageJsonPath)) {
158
- console.debug('writing package.json:', packageJsonPath);
229
+ logDebug('writing package.json:', packageJsonPath);
159
230
  fs.copyFileSync(
160
231
  path.resolve(__dirname, 'typings.package.tmpl.json'),
161
232
  packageJsonPath
162
233
  );
163
234
  } else {
164
- console.debug(packageJsonPath, 'already exists');
235
+ logDebug(packageJsonPath, 'already exists');
165
236
  }
166
237
  }
167
238
 
@@ -170,18 +241,18 @@ try {
170
241
  const importStatement = `export * from './${federationConfig.name}';`;
171
242
 
172
243
  if (!fs.existsSync(indexPath)) {
173
- console.log('creating index.d.ts file');
244
+ logDebug('creating index.d.ts file');
174
245
  fs.writeFileSync(indexPath, `${importStatement}\n`);
175
246
  } else {
176
- console.log('updating index.d.ts file');
247
+ logDebug('updating index.d.ts file');
177
248
  const contents = fs.readFileSync(indexPath);
178
249
  if (!contents.includes(importStatement)) {
179
250
  fs.writeFileSync(indexPath, `${contents}${importStatement}\n`);
180
251
  }
181
252
  }
182
253
  });
183
- console.debug('Success!');
254
+ log(chalk.green('✓'), `Generated types for ${chalk.blue.bold(federationConfig.name)}`);
184
255
  } catch (e) {
185
- console.error(`ERROR:`, e);
256
+ console.error(chalk.red('ERROR:'), e);
186
257
  process.exit(1);
187
258
  }
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@touk/federated-types",
3
3
  "description": "share typings for your federated typescript packages around your mono-repo",
4
- "version": "1.2.2-beta.1",
4
+ "version": "1.3.0-beta.2",
5
5
  "author": "Julian Wielga <j@touk.pl>",
6
6
  "bin": {
7
7
  "make-federated-types": "./cli.js"
@@ -10,6 +10,7 @@
10
10
  "Jonathan Greene <jgreene@pixability.com>"
11
11
  ],
12
12
  "dependencies": {
13
+ "chalk": "^4.1.2",
13
14
  "find-node-modules": "2.1.3"
14
15
  },
15
16
  "devDependencies": {