@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.
- package/README.md +41 -4
- package/cli.js +95 -24
- 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
|
-
|
|
67
|
-
console.error(`ERROR: Unable to find a provided config: ${configPath}`);
|
|
68
|
-
process.exit(1);
|
|
69
|
-
}
|
|
94
|
+
let federationConfig;
|
|
70
95
|
|
|
71
|
-
|
|
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 (
|
|
74
|
-
|
|
75
|
-
|
|
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
|
-
|
|
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].
|
|
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
|
-
|
|
219
|
+
logDebug('writing typing file:', _outFile);
|
|
149
220
|
fs.mkdirSync(path.dirname(_outFile), { recursive: true });
|
|
150
221
|
fs.writeFileSync(_outFile, typing);
|
|
151
222
|
|
|
152
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
244
|
+
logDebug('creating index.d.ts file');
|
|
174
245
|
fs.writeFileSync(indexPath, `${importStatement}\n`);
|
|
175
246
|
} else {
|
|
176
|
-
|
|
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
|
-
|
|
254
|
+
log(chalk.green('✓'), `Generated types for ${chalk.blue.bold(federationConfig.name)}`);
|
|
184
255
|
} catch (e) {
|
|
185
|
-
console.error(
|
|
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.
|
|
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": {
|