Skip to content

Commit

Permalink
feat: changed destination path for downloaded types
Browse files Browse the repository at this point in the history
  • Loading branch information
steven-pribilinskiy committed Jul 22, 2022
1 parent 40518ff commit 521485f
Show file tree
Hide file tree
Showing 7 changed files with 30 additions and 56 deletions.
54 changes: 9 additions & 45 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ ambient type definitions for your packages so TypeScript can resolve the dynamic
While using `@ts-ignore` on your imports works, it is a bummer to lose intellisense and type-checking capabilities.

This package exposes a Webpack plugin and a node CLI command called `make-federated-types`.
It writes a typings file in _dist/@types_ folder and downloads remote types into _@remote-types_ folder.
It writes a typings file in _dist/@types_ folder and downloads remote types into _src/@types/remotes_ folder.

Synchronization of types happens after every compilation and with a 1-minute interval when idle.

Expand Down Expand Up @@ -58,7 +58,7 @@ Every time a `d.ts` file is downloaded, webpack recompiles the whole bundle beca
| ruanyl/dts-loader | folders in <br> `.wp_federation` | - | - |
| ruanyl/webpack-remote-types-plugin | - | `types/[name]-dts` | download on `beforeRun` and `watchRun` |
| @module-federation/typescript | folders in <br> `dist/@mf-typescript` | `@mf-typescript` | compile and download on `afterCompile` (leads to double compile), <br> redo every 1 minute when idle |
| @cloudbeds/wmf-types-plugin | file in <br> `dist/@types` | `@remote-types` | download on startup, <br> compile `afterEmit`, <br> download every 1 minute or custom interval when idle |
| @cloudbeds/wmf-types-plugin | file in <br> `dist/@types` | `src/@types/remotes` | download on startup, <br> compile `afterEmit`, <br> download every 1 minute or custom interval when idle |


## Installation
Expand Down Expand Up @@ -146,46 +146,7 @@ npx make-federated-types

## Consuming remote types

When you build your microapp, the plugin will download typings to _@remote-types_ folder in the root of the
repository.

### `tsconfig.json`

Configure tsconfig to start using typings in the _@remote-types_ folder.

```json
{
"include": [
"@remote-types",
"src/**/*.ts",
"webpack/**/*.ts"
]
}
```

Paths to global types should be added to `typeRoots`

```json
{
"compilerOptions": {
"typeRoots": [
"node_modules/@types",
"src/@types"
]
}
}
```

Such types should be added to a subfolder so that they can be discovered by the invoked Typescript compiler, e.g.:

```
src/
└── @types/
└── shared/
├── index.d.ts
├── utility.d.ts
└── wmf-remotes.d.ts
```
When you build your microapp, the plugin will download typings to _src/@types/remotes_ folder

### Importing from self as from remote

Expand All @@ -200,10 +161,13 @@ remotes: {

## CI/CD

It is suggested to put the folder with downloaded types to `.gitignore`
and commit the types using your CI in PRs that target `main`/`dev` branch.
It is suggested to download types in a CI workflow only when a branch in a PR targets `main` branch.

This way the downloaded types will always correspond to the latest versions of dependent microapps
and result in valid static type checking.

This way downloaded types will always correspond to the latest compatible version of microapps.
Branches may need to use dev branches of other microfrontends, thus will have to commit those types
to avoid failing workflows.

### `.gitignore`

Expand Down
2 changes: 1 addition & 1 deletion src/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ export const FEDERATION_CONFIG_FILE = 'federation.config.json';
export const DIR_EMITTED = '@types';

export const DIR_DIST = 'dist';
export const DIR_DOWNLOADED = '@remote-types';
export const DIR_DOWNLOADED = 'src/@types/remotes';

export const DEFAULT_SYNC_TYPES_INTERVAL_IN_SECONDS = 60;

Expand Down
1 change: 1 addition & 0 deletions src/helpers/compileTypes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ export function compileTypes(exposedComponents: string[], outFile: string): Comp
const host = ts.createCompilerHost(compilerOptions);
host.writeFile = (_fileName: string, contents: string) => fileContent = contents;

exposedFileNames.push('./src/@types/utility.d.ts');
const program = ts.createProgram(exposedFileNames, compilerOptions, host);
const { diagnostics, emitSkipped } = program.emit();
diagnostics.forEach(reportCompileDiagnostic);
Expand Down
10 changes: 6 additions & 4 deletions src/helpers/downloadTypes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,12 +15,15 @@ async function downloadRemoteEntryManifest(url: string): Promise<unknown> {
return JSON.parse(json);
}

async function downloadRemoteEntryTypes(dtsUrl: string): Promise<void> {
async function downloadRemoteEntryTypes(remoteName: string, dtsUrl: string): Promise<void> {
const logger = getLogger();
const types = (await download(dtsUrl, downloadOptions)).toString();
const outFile = path.join(DIR_DOWNLOADED, path.basename(dtsUrl));
const outDir = path.join(DIR_DOWNLOADED, remoteName);
const outFile = path.join(outDir, 'index.d.ts');
let shouldWriteFile = true;

mkdirp.sync(outDir);

// Prevent webpack from recompiling the bundle by not writing the file if it has not changed
if (fs.existsSync(outFile)) {
const typesFormer = fs.readFileSync(outFile).toString();
Expand Down Expand Up @@ -83,14 +86,13 @@ export async function downloadTypes(
}

const promises: Promise<void>[] = [];
mkdirp.sync(DIR_DOWNLOADED);

Object.entries(remotes).forEach(([remoteName, remoteLocation]) => {
try {
const remoteEntryUrl = remoteEntryURLs[remoteName] || remoteLocation.split('@')[1];
const remoteEntryBaseUrl = remoteEntryUrl.split('/').slice(0, -1).join('/');

promises.push(downloadRemoteEntryTypes(`${remoteEntryBaseUrl}/${DIR_EMITTED}/${remoteName}.d.ts`));
promises.push(downloadRemoteEntryTypes(remoteName, `${remoteEntryBaseUrl}/${DIR_EMITTED}/index.d.ts`));
} catch (err) {
logger.error(`${remoteName}: '${remoteLocation}' is not a valid remote federated module URL`);
logger.log(err);
Expand Down
8 changes: 7 additions & 1 deletion src/helpers/logger.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { Compilation } from 'webpack';
import { Compilation, Compiler } from 'webpack';

let loggerInstance: Compilation['logger'];

Expand All @@ -10,3 +10,9 @@ export function setLogger(logger: Compilation['logger']): Compilation['logger']
loggerInstance = logger;
return logger;
}

export function getLoggerHint(compiler: Compiler): string {
return ['none', 'error', 'warn', 'info'].includes(compiler.options.infrastructureLogging.level!)
? 'Increase infrastructureLogging level to "log" to see error details.'
: '';
}
3 changes: 1 addition & 2 deletions src/make-federated-types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,7 @@ assertRunningFromRoot();
const federationConfig = getFederationConfig();
const compileFiles = Object.values(federationConfig.exposes);

const outDir = path.join(DIR_DIST, DIR_EMITTED);
const outFile = path.resolve(outDir, `${federationConfig.name}.d.ts`);
const outFile = path.join(DIR_DIST, DIR_EMITTED, 'index.d.ts');

console.log(`Emitting types for ${compileFiles.length} exposed module(s)`);

Expand Down
8 changes: 5 additions & 3 deletions src/plugin.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import { DEFAULT_SYNC_TYPES_INTERVAL_IN_SECONDS, DIR_DIST, DIR_EMITTED } from '.
import { getRemoteManifestUrls } from './helpers/cloudbedsRemoteManifests';
import { compileTypes, rewritePathsWithExposedFederatedModules } from './helpers/compileTypes';
import { downloadTypes } from './helpers/downloadTypes';
import { setLogger } from './helpers/logger';
import { getLoggerHint, setLogger } from './helpers/logger';
import { isEveryUrlValid } from './helpers/validation';
import {
FederationConfig,
Expand Down Expand Up @@ -43,8 +43,8 @@ export class ModuleFederationTypesPlugin implements WebpackPluginInstance {
return;
}

const { name, exposes, remotes } = federationPluginOptions;
const outFile = path.join(distPath, DIR_EMITTED, `${name}.d.ts`);
const { exposes, remotes } = federationPluginOptions;
const outFile = path.join(distPath, DIR_EMITTED, 'index.d.ts');

// Create types for exposed modules
const compileTypesHook = () => {
Expand All @@ -53,6 +53,8 @@ export class ModuleFederationTypesPlugin implements WebpackPluginInstance {
const { isSuccess, typeDefinitions } = compileTypes(exposes as string[], outFile);
if (isSuccess) {
rewritePathsWithExposedFederatedModules(federationPluginOptions as FederationConfig, outFile, typeDefinitions);
} else {
logger.warn('Failed to compile types for exposed modules.', getLoggerHint(compiler));
}
};

Expand Down

0 comments on commit 521485f

Please sign in to comment.