Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix(dts-plugin): optimize generate types in dev #3450

Open
wants to merge 6 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 5 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/clever-pianos-cough.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@module-federation/dts-plugin': patch
---

fix(dts-plugin): only block build process in prod env when generating types
5 changes: 5 additions & 0 deletions .github/pr-labeler.yml
Original file line number Diff line number Diff line change
@@ -1,3 +1,8 @@
permissions:
# Permits `github/issue-labeler` to add a label to a pull request
pull-requests: write
contents: read

'change: feat':
- '/^(feat|types|style)/'
'change: fix':
Expand Down
194 changes: 128 additions & 66 deletions packages/dts-plugin/src/plugins/GenerateTypesPlugin.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import type { Compiler, WebpackPluginInstance } from 'webpack';
import type { Compilation, Compiler, WebpackPluginInstance } from 'webpack';
import fs from 'fs';
import path from 'path';
import { isDev, getCompilerOutputDir } from './utils';
Expand Down Expand Up @@ -88,7 +88,130 @@ export class GenerateTypesPlugin implements WebpackPluginInstance {
return fn;
};
const generateTypesFn = getGenerateTypesFn();
let compiledOnce = false;

const emitTypesFiles = async (compilation: Compilation) => {
// Dev types will be generated by DevPlugin, the archive filename usually is dist/.dev-server.zip
try {
const { zipTypesPath, apiTypesPath, zipName, apiFileName } =
retrieveTypesAssetsInfo(finalOptions.remote);

if (isProd && zipName && compilation.getAsset(zipName)) {
callback();
return;
}

await generateTypesFn(finalOptions);
const config = finalOptions.remote.moduleFederationConfig;
let zipPrefix = '';
if (typeof config.manifest === 'object' && config.manifest.filePath) {
zipPrefix = config.manifest.filePath;
} else if (
typeof config.manifest === 'object' &&
config.manifest.fileName
) {
zipPrefix = path.dirname(config.manifest.fileName);
} else if (config.filename) {
zipPrefix = path.dirname(config.filename);
}

if (isProd) {
const zipAssetName = path.join(zipPrefix, zipName);
const apiAssetName = path.join(zipPrefix, apiFileName);
if (zipTypesPath && !compilation.getAsset(zipAssetName)) {
compilation.emitAsset(
zipAssetName,
new compiler.webpack.sources.RawSource(
fs.readFileSync(zipTypesPath),
false,
),
);
}

if (apiTypesPath && !compilation.getAsset(apiAssetName)) {
compilation.emitAsset(
apiAssetName,
new compiler.webpack.sources.RawSource(
fs.readFileSync(apiTypesPath),
false,
),
);
}
callback();
} else {
const isEEXIST = (err: NodeJS.ErrnoException) => {
return err.code == 'EEXIST';
};
if (zipTypesPath) {
const zipContent = fs.readFileSync(zipTypesPath);
const zipOutputPath = path.join(
compiler.outputPath,
zipPrefix,
zipName,
);
await new Promise<void>((resolve, reject) => {
compiler.outputFileSystem.mkdir(
path.dirname(zipOutputPath),
(err) => {
if (err && !isEEXIST(err)) {
reject(err);
} else {
compiler.outputFileSystem.writeFile(
zipOutputPath,
zipContent,
(writeErr) => {
if (writeErr && !isEEXIST(writeErr)) {
reject(writeErr);
} else {
resolve();
}
},
);
}
},
);
});
}

if (apiTypesPath) {
const apiContent = fs.readFileSync(apiTypesPath);
const apiOutputPath = path.join(
compiler.outputPath,
zipPrefix,
apiFileName,
);
await new Promise<void>((resolve, reject) => {
compiler.outputFileSystem.mkdir(
path.dirname(apiOutputPath),
(err) => {
if (err && !isEEXIST(err)) {
reject(err);
} else {
compiler.outputFileSystem.writeFile(
apiOutputPath,
apiContent,
(writeErr) => {
if (writeErr && !isEEXIST(writeErr)) {
reject(writeErr);
} else {
resolve();
}
},
);
}
},
);
});
}

callback();
}
} catch (err) {
callback();
if (finalOptions.displayErrorInTerminal) {
console.error('Error in mf:generateTypes processAssets hook:', err);
}
}
};

compiler.hooks.thisCompilation.tap('mf:generateTypes', (compilation) => {
compilation.hooks.processAssets.tapPromise(
Expand All @@ -100,70 +223,9 @@ export class GenerateTypesPlugin implements WebpackPluginInstance {
},
async () => {
await consumeTypesPromise;
try {
if (pluginOptions.dev === false && compiledOnce) {
return;
}

if (compiledOnce) {
// Dev types will be generated by DevPlugin, the archive filename usually is dist/.dev-server.zip
return;
}

const { zipTypesPath, apiTypesPath, zipName, apiFileName } =
retrieveTypesAssetsInfo(finalOptions.remote);
if (zipName && compilation.getAsset(zipName)) {
return;
}

await generateTypesFn(finalOptions);
const config = finalOptions.remote.moduleFederationConfig;
let zipPrefix = '';
if (
typeof config.manifest === 'object' &&
config.manifest.filePath
) {
zipPrefix = config.manifest.filePath;
} else if (
typeof config.manifest === 'object' &&
config.manifest.fileName
) {
zipPrefix = path.dirname(config.manifest.fileName);
} else if (config.filename) {
zipPrefix = path.dirname(config.filename);
}

const zipAssetName = path.join(zipPrefix, zipName);
if (zipTypesPath && !compilation.getAsset(zipAssetName)) {
compilation.emitAsset(
path.join(zipPrefix, zipName),
new compiler.webpack.sources.RawSource(
fs.readFileSync(zipTypesPath),
false,
),
);
}

const apiAssetName = path.join(zipPrefix, apiFileName);
if (apiTypesPath && !compilation.getAsset(apiAssetName)) {
compilation.emitAsset(
path.join(zipPrefix, apiFileName),
new compiler.webpack.sources.RawSource(
fs.readFileSync(apiTypesPath),
false,
),
);
}
compiledOnce = true;
callback();
} catch (err) {
callback();
if (finalOptions.displayErrorInTerminal) {
console.error(
'Error in mf:generateTypes processAssets hook:',
err,
);
}
const emitTypesFilesPromise = emitTypesFiles(compilation);
if (isProd) {
await emitTypesFilesPromise;
}
},
);
Expand Down
19 changes: 1 addition & 18 deletions packages/manifest/src/utils.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { Chunk, Compilation, StatsCompilation, StatsModule } from 'webpack';
import path from 'path';
import fs from 'fs';
import {
StatsAssets,
moduleFederationPlugin,
Expand Down Expand Up @@ -307,24 +308,6 @@ export function getTypesMetaInfo(
const zip = path.join(zipPrefix, zipName);
const api = path.join(zipPrefix, apiFileName);

if (!zip || !compilation.getAsset(zip)) {
return {
path: '',
name: '',
zip: '',
api: '',
};
}

if (!api || !compilation.getAsset(api)) {
return {
path: '',
name: '',
zip,
api: '',
};
}

return {
path: '',
name: '',
Expand Down
Loading