Usage¶
Webpack integration layer for Python.
A simple example¶
Let’s take a simple package.json
that includes a Node module:
{
"private": true,
"name": "example",
"version": "0.0.1",
"author": "myself",
"license": "WTFPL",
"description": "example",
"dependencies": {
"jquery": "^3.2.1"
}
}
And let’s create a Webpack configuration file webpack.config.js
to load the
entry point src/index.js
and output the built version in the dist
folder:
var path = require('path');
module.exports = {
context: path.resolve(__dirname, 'src'),
entry: './index.js',
output: {
filename: '[name].js',
path: path.resolve(__dirname, 'dist')
}
};
Now, let’s add to the package.json
a run script build
that executes
webpack:
"scripts": {
"build": "webpack --config webpack.config.js"
}
We can easily wrap our project in a WebpackProject
object:
from pywebpack import WebpackProject
project_path = '.'
project = WebpackProject(project_path)
This will allow us to install the npm
dependencies:
project.install()
And invoke the npm
build
command to execute webpack
to build the
entry points:
project.build()
Alternatively, buildall()
can be used
to execute both tasks at once.
Build time config¶
If we need to inject extra configuration at build time we can define a
WebpackTemplateProject
:
from pywebpack import WebpackTemplateProject
project = WebpackTemplateProject(
working_dir='tmp', # where config and assets files will be copied
project_template_dir='buildconfig', # `webpack.config.js` location
config={'debug': True},
config_path='build/config.json', # location in `working_dir` where
# `config.json` will be written
)
debug: True
is an example of configuration that can be injected from Python
and expose it to Webpack via the generated config.json
.
Assets for multiple modules¶
When you have more complex Python projects with multiple modules, each module
could declare and use different assets. With Pywebpack, we can define a
WebpackBundle
for each module and list the needed
assets and npm dependencies.
We can then declare a WebpackBundleProject
that
will collect all bundles and build assets as you configure it.
Let’s try with a real example. The recommended folder structure for a project with modules is the following:
/buildconfig
/package.json
/webpack.config.js
/modules
/module1
/...
/static
/js
/css
/module2
/...
/static
/js
/css
main.py # or any script name
Since Pywebpack will copy all bundles static files in the same working directory, it is important to name assets with distinct filenames, or create a namespaced subfolder to contain your static files. In this way there won’t be any file naming conflict.
In our main script, let’s add two bundles (called mainsite
and
backoffice
):
from pywebpack import WebpackBundle
mainsite = WebpackBundle(
'./modules/mainsite/static',
entry={
'mainsite-base': './js/mainsite-base.js',
'mainsite-products': './js/mainsite-products.js',
},
dependencies={
'jquery': '^3.2.1'
}
)
backoffice = WebpackBundle(
'./modules/backoffice/static',
entry={
'backoffice-base': './js/backoffice-base.js',
'backoffice-admin': './js/backoffice-admin.js',
},
dependencies={
'jquery': '^3.1.0'
}
)
A WebpackBundle
requires the path to the static
files of the module, an entry for each asset with its relative path and any
extra npm package.
Then, we create a project WebpackBundleProject
for our bundles:
from pywebpack import WebpackBundleProject
project = WebpackBundleProject(
working_dir='build', # where config and assets files will be copied
project_template_dir='buildconfig', #`webpack.config.js` location
bundles=[mainsite, backoffice]
)
Pywebpack will generate a config.json
which will contain all the assets
with their paths. Each bundle entry will be a webpack entry:
{
"entry": {
"backoffice-base": "./js/backoffice-base.js",
"backoffice-admin": "./js/backoffice-admin.js",
"mainsite-base": "./js/mainsite-base.js",
"mainsite-products": "./js/mainsite-products.js"
}
}
As last step, in our webpack.config.js
, we set the entry to the
config.json
so webpack will build each asset:
var path = require('path');
var config = require('./config') // Read config.json written by Python
module.exports = {
context: path.resolve(__dirname),
entry: config.entry, // Entries from mainsite and backoffice bundles.
output: {
filename: '[name].js',
path: path.resolve(__dirname, 'dist')
}
};
When executing project.buildall()
, Pywebpack will copy all the files
contained in the project_template_dir
folder and all the assets of each
bundle to the working_dir
folder. Then, it will install npm
packages
and run webpack
to build the assets.
Node dependencies resolution¶
Bundles can declare extra npm packages needed to build the assets. Pywebpack
supports dependencies
, devDependencies
and peerDependencies
.
Dependencies will be merged to a common list. In case the same dependency is defined multiple times with different versions, only the highest version that satisfies all of them is kept. For more information, see the documentation of node-semver.
Extension points¶
With Pywebpack, we can “expose” bundles of our module so we can dynamically build assets of other installed modules.
Let’s define a module and expose it as a Python entry point. In
mymodule.bundles.py
:
from pywebpack import WebpackBundle
css = WebpackBundle(
__name__,
entry={
'mymodule-styles': './js/mymodule/styles.css',
},
dependencies={
'bootstrap-sass': '~3.3.5',
'font-awesome': '~4.4.0',
}
)
And in setup.py
:
setup(
...
entry_points={
...
'webpack_bundles': [
'mymodule_css = mymodule.bundles:css',
'mymodule_js = mymodule.bundles:js',
],
)
In our main project, we can now define a
WebpackBundleProject
that dynamically uses the
installed and exposed bundles:
from pywebpack import bundles_from_entry_point, WebpackBundleProject
project = WebpackBundleProject(
__name__,
project_folder='assets',
config_path='build/config.json',
bundles=bundles_from_entry_point('webpack_bundles'),
)
When executing project.buildall()
, the bundles exposed as entry points will
be collected and built.
Manifest¶
A manifest is an output file which contains the list of all generated assets. It is created by a webpack plugin during build time. It also helps you deal with long-term caching, by providing a mapping between the name of a resource and its “hashed” version:
{
"main.js": "main.75244bb780acd727ebd3.js"
}
Pywebpack can parse a manifest file and make it available to your Python project. It supports manifest files generated using webpack-manifest-plugin, webpack-yam-plugin and webpack-bundle-tracker.
You will normally want webpack to add a hash of a file’s contents to its name:
output: {
filename: '[name].[chunkhash].js',
path: path.resolve(__dirname, 'dist')
}
And then have it invoke your favorite manifest plugin:
plugins: [
new ManifestPlugin({
fileName: 'manifest.json'
})
]
ManifestLoader
should be able to load a manifest
in any of those formats:
manifest = ManifestLoader.load('/path/to/dist/manifest.json')
The manifest entries can be retrieved as object attributes or items:
manifest.myresource
manifest['main.js']
manifest['myresource']