diff --git a/packages/preset-umi/src/features/mako/mako.ts b/packages/preset-umi/src/features/mako/mako.ts index d006b33874ae..66e48700d716 100644 --- a/packages/preset-umi/src/features/mako/mako.ts +++ b/packages/preset-umi/src/features/mako/mako.ts @@ -1,5 +1,9 @@ import path from 'path'; import { IApi } from '../../types'; +import { + EntryAssets, + extractEntryAssets, +} from '../../utils/extractEntryAssets'; import { isWindows } from '../../utils/platform'; export default (api: IApi) => { @@ -45,17 +49,52 @@ export default (api: IApi) => { enableBy: api.EnableBy.config, }); + // html 处理逻辑 + const assets: EntryAssets = { + // Will contain all js and mjs files + js: [], + // Will contain all css files + css: [], + }; + api.modifyConfig((memo) => { // @TODO remove this when mako support windows if (isWindows) { memo.mako = false; process.env.OKAM = ''; } + const makoPlugins = memo.mako?.plugins || []; + if (!api.config.mpa) { + makoPlugins.push({ + name: 'UmiHtmlGenerationMako', + generateEnd: ({ stats }: any) => { + const entryPointFiles = new Set(); + + for (const chunk of stats.entrypoints['umi'].chunks) { + const files = stats.chunks.find((c: any) => c.id === chunk).files; + for (const file of files) { + entryPointFiles.add(file); + } + } + + let entryAssets = extractEntryAssets(Array.from(entryPointFiles)); + Object.entries(entryAssets).forEach(([ext, files]) => { + if (!Array.isArray(assets[ext])) { + assets[ext] = []; + } + assets[ext].push(...files); + }); + }, + }); + } return { ...memo, mfsu: false, hmrGuardian: false, - makoPlugins: memo.mako?.plugins || [], + mako: { + ...memo.mako, + plugins: makoPlugins, + }, }; }); @@ -78,4 +117,21 @@ export default (api: IApi) => { console.error(e); } }); + + api.addHTMLStyles(() => { + const { publicPath } = api.config; + const displayPublicPath = publicPath === 'auto' ? '/' : publicPath; + return assets.css.map((css) => { + return `${displayPublicPath}${css}`; + }); + }); + + api.addHTMLHeadScripts(() => { + const { publicPath } = api.config; + const displayPublicPath = publicPath === 'auto' ? '/' : publicPath; + + return assets.js.map((js) => { + return `${displayPublicPath}${js}`; + }); + }); }; diff --git a/packages/preset-umi/src/features/webpack/webpack.ts b/packages/preset-umi/src/features/webpack/webpack.ts index 44f18c601dcd..6c727ff2f543 100644 --- a/packages/preset-umi/src/features/webpack/webpack.ts +++ b/packages/preset-umi/src/features/webpack/webpack.ts @@ -3,6 +3,10 @@ import type { Compiler, } from '@umijs/bundler-webpack/compiled/webpack'; import { IApi } from '../../types'; +import { + EntryAssets, + extractEntryAssets, +} from '../../utils/extractEntryAssets'; export default (api: IApi) => { api.describe({ @@ -11,7 +15,7 @@ export default (api: IApi) => { }); // html 处理逻辑 - const assets: { js: string[]; css: string[]; [key: string]: string[] } = { + const assets: EntryAssets = { // Will contain all js and mjs files js: [], // Will contain all css files @@ -26,45 +30,12 @@ export default (api: IApi) => { const entryPointFiles = compilation.entrypoints .get('umi')! .getFiles(); - - // Extract paths to .js, .mjs and .css files from the current compilation - const entryPointPublicPathMap: Record = {}; - const extensionRegexp = /\.(css|js|mjs)(\?|$)/; - - const UMI_ASSETS_REG = { - js: /^umi(\..+)?\.js$/, - css: /^umi(\..+)?\.css$/, - }; - - entryPointFiles.forEach((entryPointPublicPath) => { - const extMatch = extensionRegexp.exec(entryPointPublicPath); - // Skip if the public path is not a .css, .mjs or .js file - if (!extMatch) { - return; - } - - if (entryPointPublicPath.includes('.hot-update')) { - return; + let entryAssets = extractEntryAssets(entryPointFiles); + Object.entries(entryAssets).forEach(([ext, files]) => { + if (!Array.isArray(assets[ext])) { + assets[ext] = []; } - - // Skip if this file is already known - // (e.g. because of common chunk optimizations) - if (entryPointPublicPathMap[entryPointPublicPath]) { - return; - } - - // umi html 默认会注入 不做处理 - if ( - UMI_ASSETS_REG.js.test(entryPointPublicPath) || - UMI_ASSETS_REG.css.test(entryPointPublicPath) - ) { - return; - } - - entryPointPublicPathMap[entryPointPublicPath] = true; - // ext will contain .js or .css, because .mjs recognizes as .js - const ext = extMatch[1] === 'mjs' ? 'js' : extMatch[1]; - assets[ext].push(entryPointPublicPath); + assets[ext].push(...files); }); }, ); diff --git a/packages/preset-umi/src/utils/extractEntryAssets.ts b/packages/preset-umi/src/utils/extractEntryAssets.ts new file mode 100644 index 000000000000..e28f12c86a88 --- /dev/null +++ b/packages/preset-umi/src/utils/extractEntryAssets.ts @@ -0,0 +1,60 @@ +export type EntryAssets = { + js: string[]; + css: string[]; + [key: string]: string[]; +}; + +export function extractEntryAssets(entryPointFiles: string[]): EntryAssets { + const assets: { + js: string[]; + css: string[]; + [key: string]: string[]; + } = { + // Will contain all js and mjs files + js: [], + // Will contain all css files + css: [], + }; + + // Extract paths to .js, .mjs and .css files from the current compilation + const entryPointPublicPathMap: Record = {}; + const extensionRegexp = /\.(css|js|mjs)(\?|$)/; + + const UMI_ASSETS_REG = { + js: /^umi(\..+)?\.js$/, + css: /^umi(\..+)?\.css$/, + }; + + entryPointFiles.forEach((entryPointPublicPath) => { + const extMatch = extensionRegexp.exec(entryPointPublicPath); + // Skip if the public path is not a .css, .mjs or .js file + if (!extMatch) { + return; + } + + if (entryPointPublicPath.includes('.hot-update')) { + return; + } + + // Skip if this file is already known + // (e.g. because of common chunk optimizations) + if (entryPointPublicPathMap[entryPointPublicPath]) { + return; + } + + // umi html 默认会注入 不做处理 + if ( + UMI_ASSETS_REG.js.test(entryPointPublicPath) || + UMI_ASSETS_REG.css.test(entryPointPublicPath) + ) { + return; + } + + entryPointPublicPathMap[entryPointPublicPath] = true; + // ext will contain .js or .css, because .mjs recognizes as .js + const ext = extMatch[1] === 'mjs' ? 'js' : extMatch[1]; + assets[ext].push(entryPointPublicPath); + }); + + return assets; +}