UNPKG

mmir-webpack

Version:

Integration for MMIR library in webpack builds

703 lines (589 loc) 32.2 kB
import { dirname, join, basename, isAbsolute, resolve } from 'path'; import { isArray } from 'lodash'; import type { WebpackAppConfig , WebpackModule , WebpackModuleConfiguration, WebpackRule } from './index.d'; import type { BuildConfig } from 'mmir-tooling'; import fileUtils from 'mmir-tooling/utils/filepath-utils'; import packageUtils from './utils/package-utils'; import { createBuildConfig } from 'mmir-tooling/tools/create-build-config'; import grammarUtils from 'mmir-tooling/grammar/grammar-utils'; import appConfigUtils from './utils/webpack-module-init-gen.js'; import workerLoaderUtils from './utils/webpack-worker-loader-utils'; import { ReplaceModuleIdPlugin, FixedIdModule } from './utils/webpack-plugin-replace-id.js'; import resourcesConfig from './webpack-resources-config'; import cliUtils from 'mmir-tooling/utils/cli-utils'; import { createResolveAlias , getAliasEntry , setAliasEntry , isExactAliasEntry } from './utils/webpack-resolve-utils'; import { WebpackError, Compilation } from 'webpack'; import logUtils from 'mmir-tooling/utils/log-utils'; const log = logUtils.log; const warn = logUtils.warn; type TestFileCondOption = string | ((file: string)=>boolean) | RegExp; type ModuleRule = any;//FIXME webpack module-rule type Plugin = any;//FIXME webpack plugin ({ apply: (arg0: Resolver) => void } | ((this: Resolver, arg1: Resolver) => void)) // log('mmir-lib: ', require('mmir-lib')) /** root directory of the mmir-lib package */ const rootDir = dirname(require.resolve('mmir-lib')); /** root directory of the mmir-webpack package (i.e. this package) */ const webpackRootDir = __dirname; // log('mmir-lib path: ', rootDir); // log('mmir-webpack path: ', webpackRootDir); function enableJQuery(mmirAppConfig: WebpackAppConfig): void { mmirAppConfig.jquery = true; var paths = resourcesConfig.paths; paths['mmirf/util'] = 'tools/util_jquery'; } function createModuleRules(mmirAppConfig: WebpackAppConfig, buildConfig: BuildConfig){ var appRootDir = mmirAppConfig.rootPath; //FIXME currently cannot include grammar.json as file because of json-grammar-loader TODO include same resouce multiple times with different formats(?) multi-loader? buildConfig.settings.forEach(function(s){ if(!s.exclude){ if(isArray(s.file) && s.include !== 'inline'){ warn('WARN settings-utils: encountered multiple file resources for "'+s.id+'" ('+s.type+'): cannot be included as (single) file, inlining resources instead...'); s.include = 'inline'; } else if(s.type === 'grammar' && s.include !== 'inline'){ warn('WARN: cannot include JSON grammars as file, inlining grammar source for "'+s.id+'" instead ...'); s.include = 'inline'; } else if(s.type === 'speech' && s.value && s.include !== 'inline'){ warn('WARN cannot include speech-config with modified value as JSON file, inlining speech-config for "'+s.id+'" instead ..."'); s.include = 'inline'; } } }); var mmirAppConfigContent = appConfigUtils.generateModuleCode(mmirAppConfig); var appConfigModulePath = fileUtils.normalizePath(join(webpackRootDir, 'runtime', 'webpackModuleInit.js')); // log('###### creating module webpack-app-module-config.js with contents: '+ JSON.stringify(mmirAppConfigContent)); var binFilePaths = resourcesConfig.fileResources.map(function(val){ return fileUtils.normalizePath(isAbsolute(val)? val : join(rootDir, val)); }); //log('###### including as raw files: '+ binFilePaths); var textFilePaths = resourcesConfig.textResources.map(function(val){ return fileUtils.normalizePath(isAbsolute(val)? val : join(rootDir, val)); }); // log('###### including as text files: '+ textFilePaths); var fileResourcesPathMap = resourcesConfig.resourcesPaths; var moduleRules: ModuleRule[] = [ // handle binary files (e.g. *.mp3): { test: fileUtils.createFileTestFunc(binFilePaths, ' for [raw file]'), use: { loader: 'file-loader', options: packageUtils.setOptionIf({ name: function(file: string): string { if(fileResourcesPathMap && fileResourcesPathMap[fileUtils.normalizePath(file)]){ //if there is an explicit mapping entry, use the entry: log(' including [raw file] from plugin, remapping include-path "'+file+'" -> ', fileResourcesPathMap[fileUtils.normalizePath(file)]); file = fileResourcesPathMap[fileUtils.normalizePath(file)]; } else if(file.indexOf(rootDir) === 0){ //use relative path path (from mmir-lib root) in target/output directory for the resouce: log(' including [raw file], remapping include-path "'+file+'" -> ', file.substring(rootDir.length).replace(/^(\\|\/)/, '')); file = file.substring(rootDir.length).replace(/^(\\|\/)/, ''); } else { //otherwise: include as bare file-name log(' including [raw file] remapping include-path "'+file+'" -> ', basename(file)); file = basename(file); } //do normalize path-separators to '/' for proper usage in JS code -> "var url = require(<file resource>)" return fileUtils.normalizePath(file); } }, //set compatiblity option: if file-loader >= 5.0.0 set 'esModule' to false, otherwise set 'esModules' to false 'esModule', 'esModules', false, false, 'file-loader', '>= 5.0.0') } }, // creates a module implementation for the app-config: { test: fileUtils.createFileTestFunc([appConfigModulePath], ' for [app config]'), use: { loader: 'val-loader', options: { appConfigCode: mmirAppConfigContent //TODO add: dependencies: [...] -> resolve file-paths via alias for: // mmirAppConfig.includeModules // mmirAppConfig.loadAfterInit // mmirAppConfig.loadBeforeInit // mmirAppConfig.runtimeSettings // -> 1. need to check if configuration.json (or other JSON-configs) & via mmir-build.config.js and add those as file-dependencies // 2. need check all entries "mmirf/settings/[dictionary|grammar|speech]" // 3. evaluate directories.json? } } }, // handle/include "raw" text files: { test: fileUtils.createFileTestFunc(textFilePaths, ' for [text files]'), use: { loader: 'raw-loader' } } ]; if(buildConfig.grammars.length > 0){ // compile JSON grammars & include executables if necessary: moduleRules.push({ test: fileUtils.createFileTestFunc(buildConfig.grammars.map(function(g){return g.file;}), ' for [grammar] files'), use: { loader: resolve(webpackRootDir, 'loader', 'grammar-loader.js'), options: {mapping: buildConfig.grammars, config: buildConfig.grammarOptions}, }, type: 'javascript/auto' }); } if(buildConfig.views.length > 0){ // compile view templates & include if necessary: moduleRules.push({ test: fileUtils.createFileTestFunc(buildConfig.views.map(function(v){return v.file;}), ' for [view] files'), use: { loader: resolve(webpackRootDir, 'loader', 'view-loader.js'), options: {mapping: buildConfig.views, config: buildConfig.viewOptions}, }, type: 'javascript/auto' }); } if(buildConfig.states.length > 0){ // compile SCXML models & include if necessary: moduleRules.push({ test: fileUtils.createFileTestFunc(buildConfig.states.map(function(s){return s.file;}), ' for [scxml] files'), use: { loader: resolve(webpackRootDir, 'loader', 'scxml-loader.js'), options: {mapping: buildConfig.states, config: buildConfig.stateOptions}, }, type: 'javascript/auto' }); } //FIXME TODO: else include default/minimal state engines if(buildConfig.implList.length > 0){ // load & pre-process implemetation files if necessary moduleRules.push({ test: fileUtils.createFileTestFunc(buildConfig.implList.map(function(s){return s.file;}), ' for [controller | helper | model] files'), use: { loader: resolve(webpackRootDir, 'loader', 'impl-loader.js'), options: {mapping: buildConfig.implList}, }, type: 'javascript/auto' }); } var settingsFiles = buildConfig.settings.filter(function(s){return s.include === 'file';}); if(settingsFiles && settingsFiles.length > 0){ if(!mmirAppConfig.webpackPlugins){ mmirAppConfig.webpackPlugins = []; } mmirAppConfig.webpackPlugins.push(function(webpackInstance: WebpackModule, alias: {[id: string]: string}){ return new webpackInstance.NormalModuleReplacementPlugin( /mmirf\/settings\/(configuration|dictionary|grammar|speech)\//i, function(resource) { const aliasEntry = getAliasEntry(resource.request, alias); if(aliasEntry){ // log('NormalModuleReplacementPlugin: redirecting resource: ', resource, ' -> ', aliasEntry);//DEBU // resource.request = aliasEntry;//DISABLED hard-rewire request ... instead, add an alias resolver (see below) const ca = {}; const isExact = isExactAliasEntry(resource.request, alias); setAliasEntry(resource.request, aliasEntry, ca, isExact); resource.resolveOptions = {alias: ca}; } } )}); moduleRules.push({ test: fileUtils.createFileTestFunc(buildConfig.settings.filter(function(s){return !isArray(s.file) && s.type !== 'grammar';}).map(function(s){return s.file;}), ' for [language resource] files'), use: { loader: 'file-loader', options: { name: function(file: string): string { // var ofile = file;//DEBU var root = appRootDir; //use relative path (from root) in target/output directory for the resouce: if(file.indexOf(root) === 0){ file = file.substring(root.length).replace(/^(\\|\/)/, ''); } // log('including [from '+root+'] dictionary file ', ofile , ' -> ', file)//DEBU return file; } } }, type: 'javascript/auto' }); } return moduleRules; } function createPlugins(webpackInstance: WebpackModule, alias: {[id: string]: string}, mmirAppConfig: WebpackAppConfig, buildConfig: BuildConfig): Plugin[] { // var EncodingPlugin = require('webpack-encoding-plugin'); // console.log('buildConfig', buildConfig) const isWebpack3 = !webpackInstance.version || parseFloat(webpackInstance.version) <= 4 ? true : false; const reIgnoreNodePlugins = /^(?:xmlhttprequest|worker_threads|webworker-threads)$/; var plugins: Plugin[] = [ // enable replacement implementation for requirejs' module.config() // NOTE: in difference to the requirejs implementation, this does need the module as first parameter, i.e. something like // module.config(module) -> webpack_build_tool_module_config_helper_config(module) new webpackInstance.ProvidePlugin({ 'module.config': ['build-tool/module-config-helper', 'config'], }), // ignore modules that are specific for running mmir in node environment: new webpackInstance.IgnorePlugin(!isWebpack3? {resourceRegExp: reIgnoreNodePlugins} : reIgnoreNodePlugins as any), // // FIXM ignore internal node modules that are often require'd in disabled node-detection code (-> emscripten etc): // new webpackInstance.IgnorePlugin({resourceRegExp: /^crypto|fs|path$/}), // set custom module-IDs from alias-definitions for mmir-modules (enables mmir.require(<moduleId>)) new ReplaceModuleIdPlugin(alias, rootDir, /\.((ehtml)|(js(on)?))$/i), // set environment variable WEBPACK_BUILD for enabling webpack-specific code new webpackInstance.DefinePlugin({ 'WEBPACK_BUILD': JSON.stringify(true), }), // redirect entry-point of mmir-lib to the webpack bootstrap/entry point new webpackInstance.NormalModuleReplacementPlugin( /requirejs-main\.js/, function(resource) { if(resource.rawRequest === 'mmir-lib' || /*FIX HACK webpack v5: no field rawRequest -> use field request, TODO proper migration */ resource.request === 'mmir-lib'){//<- only redirect, if the file was requested via 'mmir-lib' (e.g. if requested directly via its path, do not redirect) // log('replacing module ', resource.request, ' -> ', join(webpackRootDir, 'webpack-main.js'))//, ', for ', resource); resource.request = join(webpackRootDir, 'webpack-main.js'); // FIX webpack4: need to set resource.resource field: resource.resource = resource.request; // FIX webpack v5: need to set resource in field createData // see implementation details for NormalModuleReplacementPlugin // https://github.com/webpack/webpack/blob/ff1b314d30fbf996dc0acf37b7b789eab8ca33e1/lib/NormalModuleReplacementPlugin.js#L45 if(resource.createData){ resource.createData.resource = resource.request; } } // else { // log('ingnoring module replacement for ', resource.request); // } } ), // // redirect 'mmirf/util/fileLoader' new webpackInstance.NormalModuleReplacementPlugin( /^mmirf\/util\/loadFile(__webpack_proxied)?$/, function(resource) { if(/__webpack_proxied$/.test(resource.request)){ var modPath = resolve(getAliasEntry('mmirf/util', alias), 'loadFile.js'); // log('------------- replacing module ', resource.request, ' -> ', modPath); resource.request = modPath; } else { // log('############# replacing module ', resource.request, ' -> ', getAliasEntry('mmirf/util/resourceLoader', alias)); resource.request = getAliasEntry('mmirf/util/resourceLoader', alias); } // FIX webpack v5: need to set resource in field createData if(resource.createData){ resource.createData.resource = resource.request; } } ), // FIXME somehow require-ing .ehtml resources (i.e. "mmirf/view/...") is not processed as module-request, so neither normal alias-resolving nor the replace-id plugin is triggered... // ... hard-rewire the requires view-IDs to the new webpackInstance.NormalModuleReplacementPlugin( /mmirf\/(view|controller|grammar|helper|model|scxml)\//i, function(resource) { const aliasEntry = getAliasEntry(resource.request, alias); if(aliasEntry){ // log('NormalModuleReplacementPlugin: redirecting resource: ', resource, ' -> ', aliasEntry); const ca = {}; const isExact = isExactAliasEntry(resource.request, alias); setAliasEntry(resource.request, aliasEntry, ca, isExact); resource.resolveOptions = {alias: ca}; // console.log('NormalModuleReplacementPlugin: redirecting resource: -> ', resource.resolveOptions); } } ), // //TEST try to limit/tell webpack the restrictions of require() calls in order to avoid compilation warnings // // TODO enable: limit includes for mmir-lib/lib/env/media/*.js: either by require('./'+<var>) or require('../env/media/'+<var>) or (internal) require('.') // new webpackInstance.ContextReplacementPlugin(/^\.\.?\/?(env\/media)?$/, (context) => { // // if ( !/[\\/]mmir-lib[\\/]lib[\\/](env[\\/]media|manager)$/.test(context.context) ) return; // var envMedia = resolve(rootDir, 'env', 'media'); // if(resolve(context.context, context.request) !== envMedia) return; // // console.log('------------------------------ mmir-lib/env/media/* -> ', context);//, alias) // // //TODO create list of required media modules & RegExp outside closure // var includeMediaModules = ['audiotts', 'webAudio', 'ttsMary', 'webspeechAudioInput', 'webMicLevels']; // var re = new RegExp('^\\./('+ includeMediaModules.join('|') +')(\.js)?$'); // // Object.assign(context, { // context: envMedia, //<- normalize: set context to env/media directory // regExp: re, ///^\.\/(audiotts|webAudio|ttsMary|webspeechAudioInput|webMicLevels)$/,//<- regExp for included modules of env/media // request: '.' //<- trigger evaluation of files-names via regExp in env/media // }); // }), // new EncodingPlugin({ // encoding: 'utf16le' // }), ]; //configure dependencies within WebWorker in case async-execution for grammar is enable for at least 1 grammar: // create resource/ID mapping for including async-exec grammars in WebWorker script if(buildConfig.grammars && buildConfig.grammars.length > 0){ var execAsyncGrammarIdMap = {}; var isAsyncExec = false; buildConfig.grammars.forEach(function(g){ if(g.async){ isAsyncExec = true; execAsyncGrammarIdMap['./'+g.id] = grammarUtils.toAliasId(g); } }); //only create mapping function & plugin instance etc, if at least 1 async-exec grammar is present: if(isAsyncExec){ var ContextElementDependency = require('webpack/lib/dependencies/ContextElementDependency'); var createAsyncGrammarContextMap = (_fs: any, callback: any) => { // console.log('###################### ContextReplacementPlugin.newContentCreateContextMap() -> ', callback) callback(null, execAsyncGrammarIdMap); }; plugins.push( //TODO for async-grammar-exec: do include all referenced compiled grammars in its web-worker script new webpackInstance.ContextReplacementPlugin( /^.\/\<mmir-async-grammar-exec\>/, //<- placeholder URI in mmir-lib's asyncGrammarWorker for loading/including async-exec grammars in WebWorker script function(context){ //NOTE adapted from ContextReplacementPlugin's handling of map-option context.resolveDependencies = (fs, options, callback) => { createAsyncGrammarContextMap(fs, (err, map) => { if (err) return callback(err); const dependencies = Object.keys(map).map(key => { return new ContextElementDependency( map[key] + options.resourceQuery, key ); }); callback(null, dependencies); }); }; Object.assign(context, { //NOTE need any directory in order to trigger parsing of directory which in turn allows to attach multiple ContextElementDependency request: __dirname }); } )//END: new ... );//END: plugins.push(... }//END: if(isAsyncExec) }//END: if(createAsyncGrammarContextMap) if(mmirAppConfig.webpackPlugins){ mmirAppConfig.webpackPlugins.forEach(function(p){ plugins.push(typeof p === 'function'? p(webpackInstance, alias, mmirAppConfig) : p); }) } return plugins; }; /** * HELPER check existing module-rules and exclude mmir-files if necessary * * @param {Array<webpack/Rule>} moduleRules the module.rules list of the webpack configuration (INOUT parameter) * @returns {Array<webpack/Rule>} the (possibly) modified rules */ function modifyModuleRuleFilters(moduleRules: Array<WebpackRule>): Array<WebpackRule> { if(!moduleRules || moduleRules.length === 0){ return; } var rawFilePaths = resourcesConfig.fileResources.map(function(val){ return fileUtils.normalizePath(isAbsolute(val)? val : join(rootDir, val)); }); moduleRules.forEach(function(rule){ // console.log('checking module.rule ', rule) var test = rule.test || (rule.resource? (rule.resource as any).test : null); if(test){ var isMatch: boolean = false, file: string; for(var i=0, size = rawFilePaths.length; !isMatch && i < size; ++i){ file = rawFilePaths[i]; // console.log('checkin module.rule (test type: '+(typeof test)+') for file ', file); isMatch = testFileForModuleRuleCondition(file, test); } if(isMatch){ // console.log('WARN module.rule does match mmir-file -> ', rule) excludeFilesFromModuleRule(rawFilePaths, rule); } } }); return moduleRules; }; /** * HELPER test if file matches the module.rule test-condition(s) * * @param {string} file the file(-path) to test * @param {webpack/Condition} test the module.rule's condition(s) * @return {boolean} the test result */ function testFileForModuleRuleCondition(file: string, test: TestFileCondOption | TestFileCondOption[]): boolean { var isMatch = false; switch(typeof test){ case 'string': isMatch = test === file; // console.log(' checked string module.rule.test for match: ', isMatch) break; case 'function': isMatch = test(file); // console.log(' checked function module.rule.test for match: ', isMatch) break; case 'object': // console.log(' checking ojbect module.rule.test ...'); if(test instanceof RegExp){ test.lastIndex = 0; isMatch = test.test(file); test.lastIndex = 0; // console.log(' checked RegExp module.rule.test for match: ', isMatch) break; } else if(Array.isArray(test)){ for(var i=0, size = test.length; i < size; ++i){ isMatch = testFileForModuleRuleCondition(file, test[i]); if(isMatch){ break; } } break; } //FIXME add support for testing condition-object!!! default: log('WARN cannot test module.rule ', file); } return isMatch; }; /** * HELPER add exclude-condition to module.rule for a list of (mmir) files * * @param {Array<string>} fileList the file-list which should be added to the exclude condition * @param {webpack/Rule} moduleRule the module rule */ function excludeFilesFromModuleRule(fileList: string[], moduleRule: WebpackRule): void { var exclude = moduleRule.exclude || (moduleRule.resource? (moduleRule.resource as any).exclude : null); var exclFunc = fileUtils.createFileTestFunc(fileList, ' for excluding [raw file] from module.rule'); if(exclude){ if(Array.isArray(exclude)){ exclude.push(exclFunc); } else { exclude = [exclude, exclFunc]; if(moduleRule.exclude){ moduleRule.exclude = exclude; } else { (moduleRule.resource as any).exclude = exclude; } } } else { moduleRule.exclude = exclFunc; } }; function createIgnoreWepackWarningsFunction(): (error: WebpackError, compilation: Compilation) => boolean { return function(error: WebpackError, _compilation: Compilation){ // console.log('ignoreWarnings', error);//, compilation); if(/Critical dependency: require function is used in a way in which dependencies cannot be statically extracted/.test(error.message)){ return (error?.module as FixedIdModule)?.libIdent('') === 'mmirf/main'; } return false; } } /** * apply webpack configuration for mmir-lib to an existing webpack configuration * * @param {[type]} webpackInstance the webpack instance, e.g. retrieve via require('webpack') * @param {[type]} webpackConfig the existing webpack configuration * @param {[type]} mmirAppConfig app-specific configuration for mmir-lib */ function apply(webpackInstance: WebpackModule, webpackConfig: WebpackModuleConfiguration, mmirAppConfig: WebpackAppConfig): WebpackModuleConfiguration { // log('############################## webpack-version: ', JSON.stringify(webpackInstance.version)) // log('############################## webpack instance: ', webpackInstance) cliUtils.parseCli(); const useRulesForLoaders = webpackInstance.version && parseFloat(webpackInstance.version) >= 4? true : false; if(typeof mmirAppConfig === 'string'){ mmirAppConfig = JSON.parse(mmirAppConfig); } else if(!mmirAppConfig){ mmirAppConfig = {}; } if(mmirAppConfig.jquery){ enableJQuery(mmirAppConfig); } // var resourcesConfig = require('./webpack-resources-config'); var buildConfig = createBuildConfig(mmirAppConfig, resourcesConfig); if(typeof buildConfig === 'string'){ throw new Error(buildConfig); } //add loader configurations: // (NOTE must do this before creating alias definition as some loaders may add alias mappings to mmirAppConfig) var moduleRules = createModuleRules(mmirAppConfig, buildConfig); if(!useRulesForLoaders){ moduleRules.forEach(function(rule){ if(rule.type){ if(rule.use && rule.use.options){ rule.use.options.isRuleTypeDisabled = true; } delete rule.type; } }); } if(!webpackConfig.module){ webpackConfig.module = {}; } //NOTE: compatiblity code for supporting webpack < 4 (i.e. detect/use outdated webpackConfig.module.loaders) let targetList = webpackConfig.module.rules || (useRulesForLoaders? null : (webpackConfig.module as any).loaders); if(!targetList){ targetList = []; webpackConfig.module[useRulesForLoaders? 'rules': 'loaders'] = targetList; } //check & add exclude-filters (for mmir files/resources) to existing module-rules if necessary: modifyModuleRuleFilters(targetList); //HACK for webpack >= v4: does add some modules.defaultRules, which may prevent loading *.wasm files as raw files, and instead tries to use an (experimental!!!) wasm-loader if(Array.isArray(webpackConfig.module.defaultRules)){ modifyModuleRuleFilters(webpackConfig.module.defaultRules as WebpackRule[]); } else if(useRulesForLoaders && mmirAppConfig.fixWebpack4DefaultRulesForWASM) { //TODO "auto-fix" by detecting webpack version instead of explicit option fixWebpack4DefaultRulesForWASM? // (e.g. use require('mmir-tooling/utils/packageUtils').checkPackageVersion('webpack', '= 4.x.x')) // copied (slightly modified) default rules from webpack sources: // https://github.com/webpack/webpack/blob/v4.46.0/lib/WebpackOptionsDefaulter.js#L60-L85 webpackConfig.module.defaultRules = modifyModuleRuleFilters([ { type: "javascript/auto", resolve: {} }, { test: /\.mjs$/i, type: "javascript/esm", resolve: { mainFields: webpackConfig.target === "web" || webpackConfig.target === "webworker" || webpackConfig.target === "electron-renderer" ? ["browser", "main"] : ["main"] } }, { test: /\.json$/i, type: "json" }, { test: /\.wasm$/i, type: "webassembly/experimental" } ]); } for(let i = 0, size = moduleRules.length; i < size; ++i){ targetList.push(moduleRules[i]); } //add alias resolving: const alias = createResolveAlias(mmirAppConfig, rootDir, webpackRootDir); if(!webpackConfig.resolve){ webpackConfig.resolve = {alias: alias}; } else { if(webpackConfig.resolve.alias){ Object.assign(webpackConfig.resolve.alias, alias); } else { webpackConfig.resolve.alias = alias; } } // console.log('webpackConfig.resolve.alias: ', JSON.stringify(webpackConfig.resolve.alias)); //add plugins var plugins = createPlugins(webpackInstance, alias, mmirAppConfig, buildConfig); if(!webpackConfig.plugins){ webpackConfig.plugins = plugins; } else { for(var i = 0, size = plugins.length; i < size; ++i){ webpackConfig.plugins.push(plugins[i]); } } //add webworker loader configuration workerLoaderUtils.apply(webpackConfig, rootDir, useRulesForLoaders); //ignore known errors when compiling mmir-lib const canIgnoreWarnings = webpackInstance.version && parseFloat(webpackInstance.version) >= 5? true : false; if(canIgnoreWarnings){ if(!webpackConfig.ignoreWarnings){ webpackConfig.ignoreWarnings = []; } else if(!isArray(webpackConfig.ignoreWarnings)){ webpackConfig.ignoreWarnings = [ webpackConfig.ignoreWarnings ]; } webpackConfig.ignoreWarnings.push(createIgnoreWepackWarningsFunction()); if(Array.isArray(mmirAppConfig.webpackIgnoreWarnings)){ for(const iw of mmirAppConfig.webpackIgnoreWarnings){ webpackConfig.ignoreWarnings.push(iw); } } } return webpackConfig; }; export = apply;