修改了webpack的初始化过程,增加了注入

This commit is contained in:
Xu Chang 2024-02-14 13:34:32 +08:00
parent 0ad9aa5e4a
commit 49454f9ee0
4 changed files with 338 additions and 204 deletions

View File

@ -81,7 +81,7 @@ const resolveModule = (resolveFn, filePath) => {
return resolveFn(`${filePath}.js`); return resolveFn(`${filePath}.js`);
}; };
// config after eject: we're in ./config/ // config after eject: we're in ./web
module.exports = { module.exports = {
dotenv: resolveApp('.env'), dotenv: resolveApp('.env'),
appPath: resolveApp('.'), appPath: resolveApp('.'),

View File

@ -205,15 +205,20 @@ module.exports = function (webpackEnv) {
return loaders; return loaders;
}; };
// 读取编译配置
const compilerConfigurationFile = path.join(paths.appRootPath, 'configuration', 'compiler.js');
const projectConfigration = fs.existsSync(compilerConfigurationFile) && require(compilerConfigurationFile).webpack;
const getOakInclude = () => { const getOakInclude = () => {
return [ const result = [
/oak-domain/,
/oak-external-sdk/,
/oak-frontend-base/, /oak-frontend-base/,
/oak-general-business/, /oak-general-business/,
/oak-memory-tree-store/,
/oak-common-aspect/,
]; ];
if (projectConfigration && projectConfigration.extraOakModules) {
result.push(...projectConfigration.extraOakModules);
}
return result;
}; };
return { return {
@ -255,14 +260,14 @@ module.exports = function (webpackEnv) {
// Point sourcemap entries to original disk location (format as URL on Windows) // Point sourcemap entries to original disk location (format as URL on Windows)
devtoolModuleFilenameTemplate: isEnvProduction devtoolModuleFilenameTemplate: isEnvProduction
? (info) => ? (info) =>
path path
.relative(paths.appSrc, info.absoluteResourcePath) .relative(paths.appSrc, info.absoluteResourcePath)
.replace(/\\/g, '/') .replace(/\\/g, '/')
: isEnvDevelopment && : isEnvDevelopment &&
((info) => ((info) =>
path path
.resolve(info.absoluteResourcePath) .resolve(info.absoluteResourcePath)
.replace(/\\/g, '/')), .replace(/\\/g, '/')),
}, },
cache: { cache: {
type: 'filesystem', type: 'filesystem',
@ -369,18 +374,24 @@ module.exports = function (webpackEnv) {
}, },
}, },
resolve: { resolve: {
fallback: { fallback: (() => {
crypto: require.resolve('crypto-browserify'), const defaultFb = {
buffer: require.resolve('safe-buffer'), crypto: require.resolve('crypto-browserify'),
stream: require.resolve('stream-browserify'), buffer: require.resolve('safe-buffer'),
zlib: require.resolve('browserify-zlib'), stream: require.resolve('stream-browserify'),
querystring: require.resolve('querystring-es3'), zlib: require.resolve('browserify-zlib'),
url: false, querystring: require.resolve('querystring-es3'),
path: false, url: false,
fs: false, path: false,
net: false, fs: false,
tls: false, net: false,
}, tls: false,
}
if (projectConfigration && projectConfigration.resolve && projectConfigration.resolve.fallback) {
Object.assign(defaultFb, projectConfigration.resolve.fallback);
}
return defaultFb;
})(),
// This allows you to set a fallback for where webpack should look for modules. // This allows you to set a fallback for where webpack should look for modules.
// We placed these paths second because we want `node_modules` to "win" // We placed these paths second because we want `node_modules` to "win"
// if there are any conflicts. This matches Node resolution mechanism. // if there are any conflicts. This matches Node resolution mechanism.
@ -397,24 +408,25 @@ module.exports = function (webpackEnv) {
extensions: paths.moduleFileExtensions extensions: paths.moduleFileExtensions
.map((ext) => `.${ext}`) .map((ext) => `.${ext}`)
.filter((ext) => useTypeScript || !ext.includes('ts')), .filter((ext) => useTypeScript || !ext.includes('ts')),
alias: { alias: (() => {
// Support React Native Web const defaultAlias = {
// https://www.smashingmagazine.com/2016/08/a-glimpse-into-the-future-with-react-native-for-web/ // Support React Native Web
'react-native': 'react-native-web', // https://www.smashingmagazine.com/2016/08/a-glimpse-into-the-future-with-react-native-for-web/
// Allows for better profiling with ReactDevTools 'react-native': 'react-native-web',
...(isEnvProductionProfile && { // Allows for better profiling with ReactDevTools
'react-dom$': 'react-dom/profiling', ...(isEnvProductionProfile && {
'scheduler/tracing': 'scheduler/tracing-profiling', 'react-dom$': 'react-dom/profiling',
}), 'scheduler/tracing': 'scheduler/tracing-profiling',
...(modules.webpackAliases || {}), }),
'@': paths.appSrc, ...(modules.webpackAliases || {}),
'@project': paths.appRootSrc, 'bn.js': require.resolve('bn.js'),
'@oak-general-business': paths.oakGeneralBusinessPath, assert: require.resolve('browser-assert'),
'@oak-frontend-base': paths.oakFrontendBasePath, };
'@oak-app-domain': paths.oakAppDomainPath, if (projectConfigration && projectConfigration.resolve && projectConfigration.resolve.alias) {
'bn.js': require.resolve('bn.js'), Object.assign(defaultAlias, projectConfigration.resolve.alias);
assert: require.resolve('browser-assert'), }
}, return defaultAlias;
})(),
plugins: [ plugins: [
// Prevents users from importing files from outside of src/ (or node_modules/). // Prevents users from importing files from outside of src/ (or node_modules/).
// This often causes confusion because we only process files within src/ with babel. // This often causes confusion because we only process files within src/ with babel.
@ -541,10 +553,10 @@ module.exports = function (webpackEnv) {
plugins: [ plugins: [
isEnvDevelopment && isEnvDevelopment &&
shouldUseReactRefresh && shouldUseReactRefresh &&
require.resolve( require.resolve(
'react-refresh/babel' 'react-refresh/babel'
), ),
oakPathTsxPlugin, oakPathTsxPlugin,
oakRenderTsxPlugin, oakRenderTsxPlugin,
// oakRouterPlugin, // oakRouterPlugin,
@ -759,14 +771,14 @@ module.exports = function (webpackEnv) {
}, },
plugins: [ plugins: [
isEnvProduction && isEnvProduction &&
new CompressionWebpackPlugin({ new CompressionWebpackPlugin({
filename: '[path][base].gz', //压缩后的文件名 filename: '[path][base].gz', //压缩后的文件名
algorithm: 'gzip', //压缩格式 有gzip、brotliCompress algorithm: 'gzip', //压缩格式 有gzip、brotliCompress
test: /\.(js|css|svg)$/, test: /\.(js|css|svg)$/,
threshold: 10240, // 只处理比这个值大的资源,按字节算 threshold: 10240, // 只处理比这个值大的资源,按字节算
minRatio: 0.8, //只有压缩率比这个值小的文件才会被处理,压缩率=压缩大小/原始大小,如果压缩后和原始文件大小没有太大区别,就不用压缩 minRatio: 0.8, //只有压缩率比这个值小的文件才会被处理,压缩率=压缩大小/原始大小,如果压缩后和原始文件大小没有太大区别,就不用压缩
deleteOriginalAssets: false, //是否删除原文件,最好不删除,服务器会自动优先返回同名的.gzip资源如果找不到还可以拿原始文件 deleteOriginalAssets: false, //是否删除原文件,最好不删除,服务器会自动优先返回同名的.gzip资源如果找不到还可以拿原始文件
}), }),
// Generates an `index.html` file with the <script> injected. // Generates an `index.html` file with the <script> injected.
new HtmlWebpackPlugin( new HtmlWebpackPlugin(
Object.assign( Object.assign(
@ -777,19 +789,19 @@ module.exports = function (webpackEnv) {
}, },
isEnvProduction isEnvProduction
? { ? {
minify: { minify: {
removeComments: true, removeComments: true,
collapseWhitespace: true, collapseWhitespace: true,
removeRedundantAttributes: true, removeRedundantAttributes: true,
useShortDoctype: true, useShortDoctype: true,
removeEmptyAttributes: true, removeEmptyAttributes: true,
removeStyleLinkTypeAttributes: true, removeStyleLinkTypeAttributes: true,
keepClosingSlash: true, keepClosingSlash: true,
minifyJS: true, minifyJS: true,
minifyCSS: true, minifyCSS: true,
minifyURLs: true, minifyURLs: true,
}, },
} }
: undefined : undefined
) )
), ),
@ -797,10 +809,10 @@ module.exports = function (webpackEnv) {
// a network request. // a network request.
// https://github.com/facebook/create-react-app/issues/5358 // https://github.com/facebook/create-react-app/issues/5358
isEnvProduction && isEnvProduction &&
shouldInlineRuntimeChunk && shouldInlineRuntimeChunk &&
new InlineChunkHtmlPlugin(HtmlWebpackPlugin, [ new InlineChunkHtmlPlugin(HtmlWebpackPlugin, [
/runtime-.+[.]js/, /runtime-.+[.]js/,
]), ]),
// Makes some environment variables available in index.html. // Makes some environment variables available in index.html.
// The public URL is available as %PUBLIC_URL% in index.html, e.g.: // The public URL is available as %PUBLIC_URL% in index.html, e.g.:
// <link rel="icon" href="%PUBLIC_URL%/favicon.ico"> // <link rel="icon" href="%PUBLIC_URL%/favicon.ico">
@ -819,22 +831,22 @@ module.exports = function (webpackEnv) {
// Experimental hot reloading for React . // Experimental hot reloading for React .
// https://github.com/facebook/react/tree/main/packages/react-refresh // https://github.com/facebook/react/tree/main/packages/react-refresh
isEnvDevelopment && isEnvDevelopment &&
shouldUseReactRefresh && shouldUseReactRefresh &&
new ReactRefreshWebpackPlugin({ new ReactRefreshWebpackPlugin({
overlay: false, overlay: false,
}), }),
// Watcher doesn't work well if you mistype casing in a path so we use // Watcher doesn't work well if you mistype casing in a path so we use
// a plugin that prints an error when you attempt to do this. // a plugin that prints an error when you attempt to do this.
// See https://github.com/facebook/create-react-app/issues/240 // See https://github.com/facebook/create-react-app/issues/240
isEnvDevelopment && new CaseSensitivePathsPlugin(), isEnvDevelopment && new CaseSensitivePathsPlugin(),
isEnvProduction && isEnvProduction &&
new MiniCssExtractPlugin({ new MiniCssExtractPlugin({
// Options similar to the same options in webpackOptions.output // Options similar to the same options in webpackOptions.output
// both options are optional // both options are optional
filename: 'static/css/[name].[contenthash:8].css', filename: 'static/css/[name].[contenthash:8].css',
chunkFilename: chunkFilename:
'static/css/[name].[contenthash:8].chunk.css', 'static/css/[name].[contenthash:8].chunk.css',
}), }),
// Generate an asset manifest file with the following content: // Generate an asset manifest file with the following content:
// - "files" key: Mapping of all asset filenames to their corresponding // - "files" key: Mapping of all asset filenames to their corresponding
// output file so that tools can pick it up without having to parse // output file so that tools can pick it up without having to parse
@ -871,136 +883,136 @@ module.exports = function (webpackEnv) {
// Generate a service worker script that will precache, and keep up to date, // Generate a service worker script that will precache, and keep up to date,
// the HTML & assets that are part of the webpack build. // the HTML & assets that are part of the webpack build.
isEnvProduction && isEnvProduction &&
fs.existsSync(swSrc) && fs.existsSync(swSrc) &&
new WorkboxWebpackPlugin.InjectManifest({ new WorkboxWebpackPlugin.InjectManifest({
swSrc, swSrc,
dontCacheBustURLsMatching: /\.[0-9a-f]{8}\./, dontCacheBustURLsMatching: /\.[0-9a-f]{8}\./,
exclude: [/\.map$/, /asset-manifest\.json$/, /LICENSE/], exclude: [/\.map$/, /asset-manifest\.json$/, /LICENSE/],
// Bump up the default maximum size (2mb) that's precached, // Bump up the default maximum size (2mb) that's precached,
// to make lazy-loading failure scenarios less likely. // to make lazy-loading failure scenarios less likely.
// See https://github.com/cra-template/pwa/issues/13#issuecomment-722667270 // See https://github.com/cra-template/pwa/issues/13#issuecomment-722667270
maximumFileSizeToCacheInBytes: 5 * 1024 * 1024, maximumFileSizeToCacheInBytes: 5 * 1024 * 1024,
}), }),
// TypeScript type checking // TypeScript type checking
useTypeScript && useTypeScript &&
new ForkTsCheckerWebpackPlugin({ new ForkTsCheckerWebpackPlugin({
async: isEnvDevelopment, async: isEnvDevelopment,
typescript: { typescript: {
typescriptPath: resolve.sync('typescript', { typescriptPath: resolve.sync('typescript', {
basedir: paths.appNodeModules, basedir: paths.appNodeModules,
}), }),
// configOverwrite: { // configOverwrite: {
// compilerOptions: { // compilerOptions: {
// sourceMap: isEnvProduction // sourceMap: isEnvProduction
// ? shouldUseSourceMap // ? shouldUseSourceMap
// : isEnvDevelopment, // : isEnvDevelopment,
// skipLibCheck: true, // skipLibCheck: true,
// inlineSourceMap: false, // inlineSourceMap: false,
// declarationMap: false, // declarationMap: false,
// noEmit: true, // noEmit: true,
// incremental: true, // incremental: true,
// tsBuildInfoFile: paths.appTsBuildInfoFile, // tsBuildInfoFile: paths.appTsBuildInfoFile,
// }, // },
// }, // },
configFile: paths.appTsConfig, configFile: paths.appTsConfig,
context: paths.appRootPath, context: paths.appRootPath,
diagnosticOptions: { diagnosticOptions: {
syntactic: true, syntactic: true,
},
mode: 'write-references',
// profile: true,
memoryLimit,
}, },
issue: { mode: 'write-references',
// This one is specifically to match during CI tests, // profile: true,
// as micromatch doesn't match memoryLimit,
// '../cra-template-typescript/template/src/App.tsx' },
// otherwise. issue: {
include: [ // This one is specifically to match during CI tests,
{ file: '../**/app/**/*.{ts,tsx}' }, // as micromatch doesn't match
{ file: '**/app/**/*.{ts,tsx}' }, // '../cra-template-typescript/template/src/App.tsx'
{ file: '../**/app/**/*.*.{ts,tsx}' }, // otherwise.
{ file: '**/app/**/*.*.{ts,tsx}' }, include: [
{ file: '../**/src/**/*.{ts,tsx}' }, { file: '../**/app/**/*.{ts,tsx}' },
{ file: '**/src/**/*.{ts,tsx}' }, { file: '**/app/**/*.{ts,tsx}' },
{ file: '../**/src/**/*.*.{ts,tsx}' }, { file: '../**/app/**/*.*.{ts,tsx}' },
{ file: '**/src/**/*.*.{ts,tsx}' }, { file: '**/app/**/*.*.{ts,tsx}' },
], { file: '../**/src/**/*.{ts,tsx}' },
exclude: [ { file: '**/src/**/*.{ts,tsx}' },
{ file: '**/src/**/__tests__/**' }, { file: '../**/src/**/*.*.{ts,tsx}' },
{ file: '**/src/**/?(*.){spec|test}.*' }, { file: '**/src/**/*.*.{ts,tsx}' },
{ file: '**/src/setupProxy.*' }, ],
{ file: '**/src/setupTests.*' }, exclude: [
], { file: '**/src/**/__tests__/**' },
}, { file: '**/src/**/?(*.){spec|test}.*' },
logger: { { file: '**/src/setupProxy.*' },
log: (message) => console.log(message), { file: '**/src/setupTests.*' },
error: (message) => console.error(message), ],
}, },
}), logger: {
log: (message) => console.log(message),
error: (message) => console.error(message),
},
}),
!disableESLintPlugin && !disableESLintPlugin &&
new ESLintPlugin({ new ESLintPlugin({
// Plugin options // Plugin options
extensions: ['js', 'mjs', 'jsx', 'ts', 'tsx'], extensions: ['js', 'mjs', 'jsx', 'ts', 'tsx'],
formatter: require.resolve( formatter: require.resolve(
'react-dev-utils/eslintFormatter' 'react-dev-utils/eslintFormatter'
), ),
eslintPath: require.resolve('eslint'), eslintPath: require.resolve('eslint'),
failOnError: !(isEnvDevelopment && emitErrorsAsWarnings), failOnError: !(isEnvDevelopment && emitErrorsAsWarnings),
context: paths.appSrc, context: paths.appSrc,
cache: true, cache: true,
cacheLocation: path.resolve( cacheLocation: path.resolve(
paths.appNodeModules, paths.appNodeModules,
'.cache/.eslintcache' '.cache/.eslintcache'
), ),
// ESLint class options // ESLint class options
cwd: paths.appPath, cwd: paths.appPath,
resolvePluginsRelativeTo: __dirname, resolvePluginsRelativeTo: __dirname,
baseConfig: { baseConfig: {
extends: [ extends: [
require.resolve('eslint-config-react-app/base'), require.resolve('eslint-config-react-app/base'),
], ],
rules: { rules: {
...(!hasJsxRuntime && { ...(!hasJsxRuntime && {
'react/react-in-jsx-scope': 'error', 'react/react-in-jsx-scope': 'error',
}), }),
},
}, },
}), },
}),
new webpack.ProvidePlugin({ new webpack.ProvidePlugin({
Buffer: ['buffer', 'Buffer'], Buffer: ['buffer', 'Buffer'],
}), }),
shouldAnalyze && shouldAnalyze &&
new BundleAnalyzerPlugin({ new BundleAnalyzerPlugin({
// 可以是`server``static`或`disabled`。 // 可以是`server``static`或`disabled`。
// 在`server`模式下分析器将启动HTTP服务器来显示软件包报告。 // 在`server`模式下分析器将启动HTTP服务器来显示软件包报告。
// 在“静态”模式下会生成带有报告的单个HTML文件。 // 在“静态”模式下会生成带有报告的单个HTML文件。
// 在`disabled`模式下,你可以使用这个插件来将`generateStatsFile`设置为`true`来生成Webpack Stats JSON文件。 // 在`disabled`模式下,你可以使用这个插件来将`generateStatsFile`设置为`true`来生成Webpack Stats JSON文件。
analyzerMode: 'server', analyzerMode: 'server',
// 将在“服务器”模式下使用的主机启动HTTP服务器。 // 将在“服务器”模式下使用的主机启动HTTP服务器。
analyzerHost: '127.0.0.1', analyzerHost: '127.0.0.1',
// 将在“服务器”模式下使用的端口启动HTTP服务器。 // 将在“服务器”模式下使用的端口启动HTTP服务器。
analyzerPort: 8888, analyzerPort: 8888,
// 路径捆绑,将在`static`模式下生成的报告文件。 // 路径捆绑,将在`static`模式下生成的报告文件。
// 相对于捆绑输出目录。 // 相对于捆绑输出目录。
reportFilename: 'report.html', reportFilename: 'report.html',
// 模块大小默认显示在报告中。 // 模块大小默认显示在报告中。
// 应该是`stat``parsed`或者`gzip`中的一个。 // 应该是`stat``parsed`或者`gzip`中的一个。
// 有关更多信息,请参见“定义”一节。 // 有关更多信息,请参见“定义”一节。
defaultSizes: 'parsed', defaultSizes: 'parsed',
// 在默认浏览器中自动打开报告 // 在默认浏览器中自动打开报告
openAnalyzer: true, openAnalyzer: true,
// 如果为true则Webpack Stats JSON文件将在bundle输出目录中生成 // 如果为true则Webpack Stats JSON文件将在bundle输出目录中生成
generateStatsFile: false, generateStatsFile: false,
// 如果`generateStatsFile`为`true`将会生成Webpack Stats JSON文件的名字。 // 如果`generateStatsFile`为`true`将会生成Webpack Stats JSON文件的名字。
// 相对于捆绑输出目录。 // 相对于捆绑输出目录。
statsFilename: 'stats.json', statsFilename: 'stats.json',
// stats.toJson方法的选项。 // stats.toJson方法的选项。
// 例如,您可以使用`sourcefalse`选项排除统计文件中模块的来源。 // 例如,您可以使用`sourcefalse`选项排除统计文件中模块的来源。
// 在这里查看更多选项https //github.com/webpack/webpack/blob/webpack-1/lib/Stats.js#L21 // 在这里查看更多选项https //github.com/webpack/webpack/blob/webpack-1/lib/Stats.js#L21
statsOptions: null, statsOptions: null,
logLevel: 'info', logLevel: 'info',
}), }),
].filter(Boolean), ].filter(Boolean),
// Turn off performance processing because we utilize // Turn off performance processing because we utilize
// our own hints via the FileSizeReporter // our own hints via the FileSizeReporter

View File

@ -0,0 +1,12 @@
// import { CompilerConfiguration } from 'oak-domain/lib/types/Configuration';
const path = require('path');
module.exports = {
webpack: {
resolve: {
alias: {
'@project': path.resolve('src'),
'@oak-app-domain': path.resolve('src', 'oak-app-domain'),
}
}
}
};

View File

@ -0,0 +1,110 @@
import { EntityDict } from '@project/oak-app-domain';
import { Feature } from 'oak-frontend-base';
import { CommonAspectDict } from 'oak-common-aspect';
import { AspectDict } from '../aspects/AspectDict';
import { BackendRuntimeContext } from '@project/context/BackendRuntimeContext';
import { FrontendRuntimeContext } from '@project/context/FrontendRuntimeContext';
import { groupBy } from 'oak-domain/lib/utils/lodash';
import { ContextMenuFactory } from 'oak-frontend-base/es/features/contextMenuFactory';
import Console from './Console';
type GroupName = 'System';
type Groups = {
icon: string;
name: GroupName;
}[];
interface IMenu<T extends keyof EntityDict> {
name: string;
icon: string;
url: string;
entity?: T;
paths?: string[];
action?: EntityDict[T]['Action'];
parent?: GroupName;
};
export interface OMenu {
name: GroupName | string;
icon: string;
url?: string;
children?: Array<OMenu>;
};
const groups: Groups = [
{
name: 'System', // 系统级别配置
icon: 'setup_fill',
},
];
const menus: IMenu<keyof EntityDict>[] = [
{
name: 'Dashboard',
icon: 'document',
url: '',
},
{
name: 'relationManage',
icon: 'share',
url: '/relation/entityList',
parent: 'System',
entity: 'relation',
action: 'create',
paths: [],
},
];
export default class Menu extends Feature {
private contextMenuFactory: ContextMenuFactory<EntityDict,
BackendRuntimeContext,
FrontendRuntimeContext,
AspectDict & CommonAspectDict<EntityDict, BackendRuntimeContext>>;
private console: Console;
private menus?: OMenu[];
constructor(
contextMenuFactory: ContextMenuFactory<EntityDict,
BackendRuntimeContext,
FrontendRuntimeContext,
AspectDict & CommonAspectDict<EntityDict, BackendRuntimeContext>>,
console: Console
) {
super();
this.contextMenuFactory = contextMenuFactory;
this.contextMenuFactory.setMenus(menus);
this.console = console;
this.console.subscribe(
() => {
this.refreshMenus();
}
);
}
refreshMenus() {
const roomId = this.console.getRoomId();
const menus = this.contextMenuFactory.getMenusByContext<IMenu<keyof EntityDict>>('room', roomId);
const menuGroup = groupBy(menus, 'parent');
this.menus = (menus as any[]).filter(ele => !ele.parent).concat(
groups.map((ele) => {
const { name, icon } = ele;
const children = menuGroup[name];
return {
name,
icon,
children,
};
}).filter((ele) => !!ele.children)
);
this.publish();
}
getMenus() {
if (!this.menus) {
this.refreshMenus();
}
return this.menus;
}
}