def _process_loaders_paths(toolchain, spec, loaders_paths_map): """ This function processes the loaders_paths_map in place into a form that may be encoded into JSON """ fake_spec = { CALMJS_LOADERPLUGIN_REGISTRY: spec.get(CALMJS_LOADERPLUGIN_REGISTRY), WEBPACK_RESOLVELOADER_ALIAS: {}, } # work with this copy for the mean time before applying the maps # back filtered_loaders_paths_map = normalize_and_register_webpackloaders( fake_spec, loaders_paths_map) spec_update_sourcepath_filter_loaderplugins(fake_spec, filtered_loaders_paths_map, 'default') toolchain_spec_prepare_loaderplugins(toolchain, fake_spec, 'testloaders', WEBPACK_RESOLVELOADER_ALIAS) # borrow the private entry generator by the toolchain. entries = toolchain._gen_modname_source_target_modpath( spec, fake_spec['testloaders_sourcepath']) # manually trigger the compile entries using the loaderplugin rules. modpaths, targetpaths, export_module_names = process_compile_entries( toolchain.compile_loaderplugin_entry, spec, entries) # the targets will be injected into the alias. config = spec[karma.KARMA_CONFIG] webpack_conf = config['webpack'] # directly update these as aliases. resolve = dict_setget_dict(webpack_conf, 'resolve') resolve_alias = dict_setget_dict(resolve, 'alias') for modname, p in targetpaths.items(): # add the finalized modname as alaises. resolve_alias[modname] = join(spec[BUILD_DIR], normpath(p)) # also remove it from the test module paths map as these are not # tests. spec[TEST_MODULE_PATHS_MAP].pop(modname, None) # since the registry should be the same, assume the results are # as expected; so just do a simple merge. resolve_loader = dict_setget_dict(webpack_conf, 'resolveLoader') resolve_loader_alias = dict_setget_dict(resolve_loader, 'alias') resolve_loader_alias.update(fake_spec[WEBPACK_RESOLVELOADER_ALIAS]) update_spec_webpack_loaders_modules(fake_spec, resolve_alias) # also grab the extra loader rules that got generated loaders = fake_spec.get(WEBPACK_MODULE_RULES, []) if loaders: rules = _apply_webpack_module_rules(webpack_conf) rules.extend(loaders) # grab all the raw modpath keys and store them for the module # generation process later. spec[TEST_LOADER_MODNAMES] = set(modpaths) # update the actual mapping loaders_paths_map.clear() loaders_paths_map.update(filtered_loaders_paths_map)
def test_update_spec_webpack_loaders_modules(self): spec = Spec(calmjs_webpack_modname_loader_map={ 'some/style.css': ['style', 'css'], }, ) alias = { 'some/style.css': '/path/to/some/style.css', } update_spec_webpack_loaders_modules(spec, alias) self.assertEqual([{ 'test': '/path/to/some/style.css', 'loaders': ['style', 'css'], }], spec['webpack_module_rules'])
def test_update_spec_webpack_loaders_modules_missing_alias(self): spec = Spec(calmjs_webpack_modname_loader_map={ 'some/style.css': ['style', 'css'], }, ) alias = {} with pretty_logging(stream=StringIO()) as s: update_spec_webpack_loaders_modules(spec, alias) self.assertIn( "WARNING modname 'some/style.css' requires loader chain " "['style', 'css'] but it does not have a corresponding webpack " "resolve.alias; webpack build failure may result as loaders are " "not configured for this modname", s.getvalue(), ) self.assertEqual([], spec['webpack_module_rules'])
def test_update_spec_webpack_loaders_modules_empty(self): spec = Spec() alias = {} update_spec_webpack_loaders_modules(spec, alias) self.assertEqual([], spec['webpack_module_rules'])
def assemble(self, spec): """ Assemble the library by compiling everything and generate the required files for the final bundling. """ def generate_alias(prefix): alias = {} key = prefix + self.targetpath_suffix for modname, target in spec.get(key, {}).items(): # the alias must point to the full path. alias[modname] = join(spec[BUILD_DIR], *target.split('/')) return alias # the build config is the file that will be passed to webpack for # building the final bundle. webpack_config = WebpackConfig({ 'mode': spec.get(WEBPACK_MODE, DEFAULT_WEBPACK_MODE), 'devtool': spec.get(WEBPACK_DEVTOOL, DEFAULT_WEBPACK_DEVTOOL), 'output': { 'path': dirname(spec[EXPORT_TARGET]), 'filename': basename(spec[EXPORT_TARGET]), # TODO determine if publicPath is needed. # TODO these are using magic values. The library target # should be configured, along with umdNamedDefine also # when the way to expose the relevant options as proper # sets are determined. 'libraryTarget': 'umd', 'umdNamedDefine': True, }, 'resolve': {}, # TODO should omit this if no alias are found 'resolveLoader': { 'alias': spec.get(WEBPACK_RESOLVELOADER_ALIAS, {}), }, 'externals': spec.get(WEBPACK_EXTERNALS, {}), 'module': {}, }) if spec.get(WEBPACK_OPTIMIZE_MINIMIZE): webpack_config['optimization'] = {'minimize': True} if WEBPACK_OUTPUT_LIBRARY in spec: webpack_config['output']['library'] = spec[WEBPACK_OUTPUT_LIBRARY] version = get_bin_version( spec[self.webpack_bin_key], kw={ 'env': webpack_env(pathsep.join(self.find_node_modules_basedir())), }) logger.debug("found webpack at '%s' to be version '%s'", spec[self.webpack_bin_key], version) webpack_config['__webpack_target__'] = version # set up alias lookup mapping. webpack_config['resolve']['alias'] = alias = {} # generate the aliases - yes, we include the bundled sources to # be explicit as there are cases where an alternative bundle may # be specified using optional advices. main_prefixes = ( 'transpiled', 'bundled', ) source_alias = {} for prefix in main_prefixes: source_alias.update(generate_alias(prefix)) # Do the same for loaders, but keep it in a separate mapping for # now. other_prefixes = ('loaderplugins', ) other_alias = {} for prefix in other_prefixes: other_alias.update(generate_alias(prefix)) # It is assumed that if WEBPACK_ENTRY_POINT is defined, it will # resolve into a target through the generated alias mapping. # Otherwise, assume one of the default calmjs style exports. if (spec.get(WEBPACK_ENTRY_POINT, DEFAULT_BOOTSTRAP_EXPORT) == DEFAULT_BOOTSTRAP_EXPORT): # now resolve whether the webpack.externals has been defined # in a manner that requires the complete lookup module logger.info("spec webpack_entry_point defined to be '%s'", DEFAULT_BOOTSTRAP_EXPORT) if (webpack_config['externals'].get(DEFAULT_BOOTSTRAP_EXPORT) == DEFAULT_BOOTSTRAP_EXPORT_CONFIG): logger.info( "webpack.externals defined '%s' with value that enables " "the calmjs webpack bootstrap module; generating module " "with the complete bootstrap template", DEFAULT_BOOTSTRAP_EXPORT) # assign the internal loader to the alias # TODO check that the default loader not being passed # through check_all_alias_declared is not an issue. alias[DEFAULT_CALMJS_EXPORT_NAME] = self.write_lookup_module( spec, _DEFAULT_LOADER_FILENAME, *_WEBPACK_CALMJS_MODULE_LOADER_TEMPLATE) # the bootstrap module will be the entry point in this # case. webpack_config['entry'] = self.write_bootstrap_module(spec) if (spec.get(WEBPACK_OUTPUT_LIBRARY) != DEFAULT_BOOTSTRAP_EXPORT): # a simple warning will do, as this may only be an # inconvenience. logger.warning( "exporting complete calmjs bootstrap module with " "webpack.output.library as '%s' (expected '%s')", spec.get(WEBPACK_OUTPUT_LIBRARY), DEFAULT_BOOTSTRAP_EXPORT, ) else: logger.info( "webpack.externals does not have '%s' defined for " "the complete calmjs webpack bootstrap module; " "generating simple export of modules", DEFAULT_BOOTSTRAP_EXPORT) # one key bit: to avoid the default webpack behavior of # potentially overriding values on the root node (say, # there exists window.modules already by some other # package), only use the template that exports to module # if that is defined; otherwise simply use the load only # template that will just require the selected modules. if spec.get(WEBPACK_OUTPUT_LIBRARY): template = _WEBPACK_ENTRY_CALMJS_EXPORT_ONLY_TEMPLATE else: template = _WEBPACK_ENTRY_CALMJS_LOAD_ONLY_TEMPLATE # the resulting file is the entry point. webpack_config['entry'] = self.write_lookup_module( spec, _DEFAULT_BOOTSTRAP_FILENAME, *template) if (spec.get(WEBPACK_OUTPUT_LIBRARY) == DEFAULT_BOOTSTRAP_EXPORT): logger.critical( "cowardly aborting export to webpack.output.library " "as '%s' without the complete bootstrap; generating " "module with export only template", DEFAULT_BOOTSTRAP_EXPORT, ) raise ValueError( "aborting export of webpack.output.library as '%s' " "with incomplete settings and bootstrap module" % DEFAULT_BOOTSTRAP_EXPORT) else: # need to manually resolve the entry # if externals has been defined, use the complete lookup module # otherwise, use the simplified version. wp_ep = spec[WEBPACK_ENTRY_POINT] if wp_ep not in source_alias: msg = "'%s' not found in the source alias map" % wp_ep logger.error(msg) logger.info( 'source alias map {alias: targetpath}: %s', json_dumps(source_alias), ) raise ToolchainAbort(msg) webpack_config['entry'] = source_alias[wp_ep] # merge all aliases for writing of configuration file alias.update(source_alias) alias.update(other_alias) # record the webpack config to the spec spec[WEBPACK_CONFIG] = webpack_config if spec.get(VERIFY_IMPORTS, True): missing = self.check_all_alias_declared( source_alias, partial( check_name_declared, webpack_config['resolve']['alias'], webpack_config['resolveLoader']['alias'], webpack_config['externals'], spec.get(CALMJS_LOADERPLUGIN_REGISTRY), )) if missing: logger.warning( "source file(s) referenced modules that are not in alias " "or externals: %s", ', '.join(sorted(repr_compat(m) for m in missing))) update_spec_webpack_loaders_modules(spec, alias) webpack_config['module']['rules'] = spec.get(WEBPACK_MODULE_RULES, []) # write the configuration file, after everything is checked. self.write_webpack_config(spec, webpack_config)