Let's start with this:
- projext is not an alternative to webpack.
- projext is not a module bundler.
Now, this is a tool that allows you to configure a project bundling options on an "almost-human" readable format so you don't have to deal with very complex rules and structures.
projext also has "zero configuration" support so you can start coding right away. Read more about this on the Zero Configuration document.
The idea is to divide your project bundling on a 4 layers architecture:
|Project configuration||Managed by projext|
|Bundler engine||A projext plugin for Webpack/Rollup/Parcel/etc.|
|Framework||A projext plugin with the framework settings of the used bundler.|
|Others...||Other plugins like a runner tool or bundler analyzer.|
You want to create an AngularJS app and you want to bundle it with webpack. You first need the following dependencies:
Then, if you want to use a configuration file, you would write something like this:
moduleexports =targets:browser:type: 'browser'engine: 'webpack'framework: 'angularjs';
Or you can just create your
src/index.js and start coding:
projext will look at your code and automatically assume a configuration like the one above.
There are a lot of "smart defaults" on the project configuration, but since this we just want a quick example, we are going to modify just what we need.
That's all you need to do in terms of configuration, after that you can start coding your app.
Module bundlers have been around for quite some time now and they are amazing tools, they allow us to do so much, from just putting the files together to transpiling, tree shaking, splitting for lazy load, etc.
I've been bundling projects since require.js was popular, and since mid 2016 I've been using webpack for almost everything. You can configure every detail, Babel integration, files optimization, Express middlewares, etc; But after year and half, and more than 20 projects built with it... I'm sick and tired of writing the configuration.
I even wrote a plugin to manage the configurations, but at the end of the day, I always ended up with huge configurations files, tons of rules and a lot of plugins/dependencies that needed to be up to date and comply with each other versions.
I'm well aware of the new features webpack 4 will bring to the table, including all the "smart defaults", and it looks really promising. But I still believe this tool is for a different purpose than just bundling.
At some point I started considering start using Rollup, at least for the non web apps projects. Rollup was getting a lot of traction and since Facebook started adopting it, it got really popular.
I tried it for a npm package project and the results were pretty impressive: It doesn't require a lot of configuration and there's not a lot of boiler plate code on the builds.
I considered migrating a few older projects, but I didn't want to have to go over their configurations, so I just kept it for new projects.
And at that point was when I had the first idea for this project: Preparing boiler plates for different bundlers for different scenarios: library, web page, web app, etc. Yes, it wasn't very original, but it was the start.
As you may have suspected, there were a few issues:
- Hard to maintain: Different projects for the same tool that needed to be up-to-date.
- I had to align every project to whatever the boiler plate needed in order to work.
But thanks to those problems was that I was able to come with a plan for this project:
- Make a tool that would understand your project configuration on an "almost-human" readable format, meaning, try to move almost every configuration to
stringvalue and no configuration functions.
- Add another layer, on a form of plugin, that would take the project configuration and apply it to a bundler configuration.
- And another layer/plugin that would be the framework implementation for that bundler.
And then I built it. Right now it's only webpack as an bundler engine and AngularJS for webpack as framework, but I'm already building the Rollup engine, adding the React to webpack and planning on then making ports of both AngularJS and React.
My plan is to ask the community for help putting these plugins/recipes together.
Of course there's no way this will be helpful for everyone: a tool that works as an abstraction of other tool could never cover all the possible scenarios.
This is aimed to those who use bundlers everyday to build web sites, libraries and apps without the need to go to the last optimal detail. If you need to do that, then use the bundler directly, it's the best choice.
|Node Version||>= v10.13.0|
After installing projext and the necessary dependencies for bundling (and framework possibly), you can start coding by just creating a
src/index.js file on your project.
When you run the CLI commands, projext will automatically find your file, it will check its contents and try to determine if you are writing a browser or a Node app based on the modules and syntax you are using.
In case you need to overwrite targets settings or enable other projext features, you can create a project configuration file called
The project configuration file can be located on the following paths:
projext will evaluate the list of paths and use the first one it finds.
The file needs to export a single object that projext will use to merge on top of the "smart defaults".
For example, you want to enable the feature that executes a target when bundled on development:
moduleexports =targets:myTarget:runOnDevelopment: true;
Bundling the code
For this example, we are going to assume this is what your targets look like:
moduleexports =targets:backend:type: 'node'frontend:type: 'browser';
The way you bundle your targets is by using the
build command from the projext CLI:
You can use scripts from the
npxtoo, but for these examples I'll be using
yarn projext build backend# oryarn projext build frontend
Really simple right? For the
frontend target, it will take all the source, bundle it and move it to the distribution directory (
dist/ by default, but again, configurable).
backend target it will give you a warning (not cool, I know), because the default build type is for a development environment and we didn't specify that the target needed to be bundled nor that it needed transpilation, so projext doesn't see the need to move it.
By default, projext doesn't bundle nor transpile Node targets code, but you can enable it by changing the
transpiletarget settings. More about this after the following example.
Now, time to build for production:
yarn projext build backend --type production# oryarn projext build frontend --type production
--type argument set to
production tells projext that you are preparing a build production, so it will move everything to the distribution directory.
projext and Node apps
By default, projext doesn't bundle nor transpile Node targets code (yes, it sounds ironic) as there's not a lot of advantages on doing it and the support for ES+ syntaxs on Node is pretty great.
When you try to bundle a Node target for development, if you didn't change the settings, you'll get a warning that says that there's no need for it; but if you do it for production, the code will be copied to the distribution directory as you may want to deploy it.
Now, Node targets have two special settings:
bundle you can specify whether you want to bundle the entire code on a single file or not; and with
transpile you can tell projext to just transpile the files content using Babel but keeping all the files.
Running the targets
It's not all about putting all the files together. You can also use projext to run your targets while you code.
node targets, it has a custom implementation of
nodemon that will take care of watching and, if needed, transpiling your files while you code.
browser targets it uses the bundle engine to run it so it can update your bundle on any change.
You can extend most of the things and overwrite EVERYTHING
The whole tool is built using Jimple, a port of Pimple Dependency Injection container for Node, and EVERYTHING is registered on the container. You can simple set your own version of a service with the same name in order to overwrite it.
If you haven't tried Jimple, give it a try, it's excellent for organizing your app dependencies and services.
Building plugins is really easy
By default, projext checks your package.json for dependencies which names start with
require them, they need to export a function that will receive the app container as parameter, thus allowing them to listen for events or even overwrite existing services.
For example, you want to create a plugin for browserify (If someone is interested, please go ahead :)):
You would call your plugin
projext-plugin-browserify to assure that projext will pick it and
require it, and then the code would look something like this:
Browser targets configuration
node targets, having multiple configuration files is simple, as they can
require files on runtime, but in the case of
browser targets, you would probably want to select the configuration you want to use when you bundle the code and be able to include it inside.
That's why, if enabled, projext creates an instance of wootil's
browser targets can use on the bundling process.
To enable it, you have to edit your target settings:
moduleexports =targets:frontend:type: 'browser'engine: 'webpack'configuration:enabled: false;
That's all you need to enable the feature, the rest is dictated by the setting "smart defaults":
- You target configurations will be on
- The default configuration will be loaded from
- Whenever you write
process.env.CONFIGon your code, when bundled, it will replaced by the configuration contents.
- If you add
projext buildcommand, the service will look for a file
browser.xyz.config.jsand the configuration will be created by extending the default one.
||Run the project unit tests.|
||Lint the modified files.|
||Lint the project code.|
||Generate the project documentation.|
||List all the pending to-do's.|
I use ESlint to validate all our JS code. The configuration file for the project code is on
./.eslintrc and for the tests on
./tests/.eslintrc (which inherits from the one on the root), there's also an
./.eslintignore to ignore some files on the process, and the script that runs it is on
I use ESDoc to generate HTML documentation for the project. The configuration file is on
./.esdocrc and the script that runs it is on
@todo comments to write all the pending improvements and fixes, and Leasot to generate a report. The script that runs it is on