简体   繁体   中英

WebPack: how do I access dependencies required in a separate class / file

My team is switching to webpack and I've run into a problem I need help with.

I'm not sure why, but webpack does not appear to expose dependencies outside of each file. Unfortunately, this is breaking a pattern we use for abstracting table render logic.

foo.renderers.DefaultCellRenderer

class DefaultCellRenderer
  renderCell: (data) ->
    ... render logic goes here ...

foo.renderers.ComplexCellRenderer

class ComplexCellRenderer
  renderCell: (data) ->
    ... render logic goes here ...

foo.mixins.SimpleTableMixin

require foo.mixins.BaseTableMixin

class SimpleTableMixin
  constructor: (@data) ->
    foo.mixins.BaseTableMixin.apply @
    @columns = [
      { label: 'Col 1' }
      { label: 'Col 2' }
    ]

foo.mixins.ComplexTableMixin

require foo.mixins.BaseTableMixin
require foo.renderers.ComplexCellRenderer

class ComplexTableMixin
  constructor: (@data) ->
    foo.mixins.BaseTableMixin.apply @
    @columns = [
      { label: 'Col 1' }
      { label: 'Col 2', renderer: 'ComplexCellRenderer' }
    ]

foo.mixins.BaseTableMixin

require foo.renderers.DefaultCellRenderer

class BaseTableMixin
  render: (container) =>
    # render table header...

    # render table body...
    for row in @data
      for metadata, cellIndex in @columns
        name = metadata.renderer or 'DefaultCellRenderer'
        target = foo.renderers[name]
        container.append target.renderCell row[cellIndex]

This is an extremely simplified example, but essentially this approach allows us to compose very complex tables with layers of behavior that are dynamically configurable at run time.

Prior to implementing webpack, this approach worked because 'foo.renderers' was global and any class requiring custom rendering was responsible for declaring that dependency, guaranteeing its existence when called by the base .render() function...

However, since moving to webpack, these renderers are no longer available unless I explicitly require then within the base table mix-in. This breaks the fundamental benefit of this approach, which is to make our tables extensible without having to modify the underlying logic.

Additionally, I cannot pass the renderer reference directly in the columns array, as app state needs to be serializable to JSON so it can be dehydrated / rehydrated at will.

Taking all of this into account, can anyone tell me if there is a way to ensure these dependencies are available within the base table mix-in without having to explicitly declare them in that file (thus breaking the Open/Close principle)?

Thanks!

UPDATE So based on dtothefp's answer below, I ended up creating a wrapper class that dynamically required all the renderers in the same or child directories... so I just have to require one reference instead of individual ones (although, they can each be required individually as well, to avoid circular references)

/foo/common/renderers/Renderers.coffee

goog.provide 'foo.common.renderers'

context = require.context './', true, /^.+\.js$/
context.keys().forEach (key) ->
  renderers = context(key)?.foo?.common?.renderers
  _.extend foo.common.renderers, renderers if renderers?
  return

Try using the expose-loader https://github.com/webpack-contrib/expose-loader

module: {
  rules: [{
    test: path.join(yourSrcRoot, 'foo', 'renderers', 'DefaultCellRenderer'),
    use: [{
      loader: 'expose-loader',
      options: 'foo.renderers.DefaultCellRenderer'  // not sure if this works???
    }]
  }]
}

Seems like trying to move from whatever you had before to Webpack using globals on a namespace is an anti-pattern, but this might get you started.

or better yet why don't you just bootstrap this whole thing at the start of your bundle?

// index.js

const foos = require.context('./foo', true, /(renderers|mixins)\/.+\.js$/);

global.foo = {};
global.foo.mixins = {};
global.foo.renderers = {};

foos.keys().forEach((fp) => {
  const [dir] = fp.split('/');

  global.foo[dir] = foos(fp);
});

Not sure what you mean by "This breaks the fundamental benefit of this approach, which is to make our tables extensible without having to modify the underlying logic." If you want to use a module loader but don't want do import dependencies seems like you've missed the point.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM