django-webpack-loader icon indicating copy to clipboard operation
django-webpack-loader copied to clipboard

Making webpack loader work for reusable django apps

Open ericbolo opened this issue 5 years ago • 4 comments

I've used django-webpack-loader in a single-app django site, and it serves me well, thanks @owais !

Now I want to break up my django app into reusable apps which I'll add to INSTALLED_APPS. These reusable apps will contain templates and javascript which I'd like to compile with webpack and reference in the templates with django-webpack-loader (as I currently do in my single-app site).

I want the templates and the js they contain to work out of the box when I add the app to the django site. I haven't found a way to do this yet.

Currently, the site's settings.py can contain something like:

WEBPACK_LOADER = {
'DEFAULT': {
'BUNDLE_DIR_NAME': 'bundles/',
'STATS_FILE': os.path.join(BASE_DIR, 'webpack-stats.json'),
},
'DASHBOARD': {
'BUNDLE_DIR_NAME': 'dashboard_bundles/',
'STATS_FILE': os.path.join(BASE_DIR, 'webpack-stats-dashboard.json'),
}
}

Great: I can define as many webpack projects as I want.

However, if one of my webpack projects is an INSTALLED_APP (as opposed to a subdirectory of BASE_DIR), I need to find the path to the app's webpack stats. I can think of a hacky way to do this (importing the app's package, then looking up its __file__ attribute, then finding webpack-stats from there). But that makes integrating the app that much harder, and introduces more coupling between the site and the app than is necessary.

Ideally, I'd like something analogous to django's collectstatic. I'd give webpack loader a STATS_FILE_DIR_NAME config telling the loader the name(s) of the directories containing webpack-stats.json files. The loader would collect webpack-stats from all app directories with that name.

So my webpack config in settings.py might look like:

WEBPACK_LOADER = {
'DEFAULT': {
'BUNDLE_DIR_NAME': 'bundles/',
'STATS_FILE_DIR_NAME': '/stats/'
},
}

Then when using render_bundle in an html template, I could specify the app's name like so:

{% render_bundle DJANGO-APP-NAME BUNDLE-NAME %}

The template tag would look up the appropriate webpack-stats.json file for that app.

The author of this SO question seems to be looking for the same thing: https://stackoverflow.com/questions/52104733/django-vue-with-multiple-apps

Does anyone have thoughts on how to accomplish this ?

ericbolo avatar Jan 06 '20 22:01 ericbolo

How would the loader find the appropriate stats file? Would it just pick the first one it finds? Merge all and treat them as one? Those questions need to be answered before a staticfiles dir like feature can be added.

It looks like the problem is that there is no way for an installable django app to specify that it ships with webpack assets and a stats file. We don't need a static files like feature to accomplish that. Apps can ship with a simple function that returns the path of a static file. Something like:

from app_with_webpack_stats import webpack_stats_path


WEBPACK_LOADER = {
  'DEFAULT': {
    'STATS_FILE_DIR_NAME': webpack_stats_path()
  },
}

We can add utility function to make implementation of such function very simple if needed.

owais avatar Jan 07 '20 14:01 owais

I for one would like to have this feature as it allows me create reusable apps with webpack-loaded frontend.

This is the hack I wrote yesterday:

bv_apps = ['topics'] # app with its own webpack frontend

def add_webpack_loader_config(app_name):
    module = importlib.import_module(app_name)
    path = os.path.join(
            os.path.dirname(module.__file__),
            'webpack-stats',
            'webpack-stats.json'
            )
    WEBPACK_LOADER[app_name] = {
        'BUNDLE_DIR_NAME': 'bundles/',
        'STATS_FILE': path
    }

for app in bv_apps:
    add_webpack_loader_config(app)

In template: render_bundle 'bundle' 'extension' 'app_name'.

It works but it's dirty, we could instead get the loader config directly from the app as you suggest. But how do that ? In my setup, the webpack loader config is defined in the settings.py of the importing site, not the app.

Also, for the templates from reusable apps to work out of the box, the arguments to render_bundle need to be the same, i.e. bundle dir, config key (e.g. 'DEFAULT'). How to ensure this without running into naming conflicts, e.g. two apps with the same 'DEFAULT' config key ? We would need a way to manage app scopes. Curious to hear your thoughts on this, and I'd be happy to contribute if we have a game plan

ericbolo avatar Jan 07 '20 16:01 ericbolo

If an app's intention is to ship a webpack stats file that can be consumed by others, the app must confirm to some sort of interface. That interface could just be the presence of webpack-stats.json in some well known app directory but I think it wouldn't be robust enough. I think it'd be better if the app confirmed to some sort of python interface. For example, if the app package had an attribute called WEBPACK_STATS_FILE_PATH or a function called get_webpack_stats_file_path(). Then any app that wants to ship webpack-stats.json files could just confirm to the interface by adding this function.

I'm not suggesting the apps ship with webpack loader configs. That would be for the user to compose from different stats files. I'm suggesting that the apps should have a way to say that they contain a stats file and provide it's path to users.

We could go a step further and actually return the content of the file from such functions. That would allow apps to compile the stats file into a .py file at build time before publishing to PyPI. Webpack loader config can then accept a file like interface instead of a path. This should also open up possibilities of loading remote stats files from S3 or a database.

Whatever direction we take, I don't thinks apps should define webpack loader config at all. Users should define the config in their project's settings and be free to compose it using multiple webpack-stats.json from multiple apps.

owais avatar Jan 08 '20 13:01 owais

The apps must confirm to some interface: agreed.

The issue I see with just getting the path to the webpack-stats.json is that you're not getting 1/ the BUNDLE_DIR_NAME and 2/ the key of the config (e.g. 'DEFAULT').

You need this information for the app templates that use render_bundle to render correctly without modification, right? Unless you assume that the BUNDLE_DIR_NAME will always be 'bundles' and the key 'app_name'. But that affects robustness too

It also constrains the app to a single webpack-stats. That might be ok though, dunno...

ericbolo avatar Jan 09 '20 09:01 ericbolo

Closing due to high complexity.

fjsj avatar Nov 29 '23 20:11 fjsj