Building and testing
@flecks/create-fleck sets up an environment that makes building, testing, and distributing
your flecks easy.
Mixins
Bootstrap
Your fleck may implement a build/flecks.bootstrap.js script. If one exists, it will be loaded as
a bootstrap fleck during the bootstrap phase. This includes the boostrap phase that occurs when
the fleck itself is compiled.
The bootstrap fleck is executed by Node.JS as-is as CommonJS and is not subject to any compilation or preprocessing. You must use e.g.
exports.hooks = {};
and not
const exports hooks = {};
In addition to exporting hooks, a bootstrap fleck may also export:
Dependencies
exports.dependencies = ['@some/fleck'];
Dependent flecks will automatically be added to the build manifest when this fleck is present.
If you run flecks add @some/fleck in your fleck root, @some/fleck will be added to your
package.json and your exports.dependencies will be automatically updated to include
@some/fleck.
Stubs
Some code isn't careful when it comes to things like accessing window. flecks can stub out
problem modules on any platform:
exports.stubs = {
server: ['@pixi'],
};
This is effectively the Node.JS require() version of
Webpack's {'alias': false} functionality.
package.json and entry points
flecks automatically uses the files key in your package.json to determine the
entry points of your fleck. Entry points are
automatically discovered from the src directory of your fleck.
Think of your files key as a form of
Subpath exports with less hassle. If your
package.json's files key looks like this:
"files": [
"client.js",
"index.js",
"something-else.js"
],
Other code could import any of those paths (e.g. @your/fleck/client, @your/fleck,
@your/fleck/something-else), while the actual paths would be, respectively,
@your/fleck/src/client, @your/fleck/src, @your/fleck/src/something-else. Smooth!
Who cares? Everything works without it!
Who cares about files or exporting or whatever? I created a fleck and everything Just Works™.
This seems like extra busywork for no reason!
The reason we take care of the files key in our package.json is because this
is how we make sure we can publish working flecks to npm! this tooling is directed toward
making it easier and frictionless to share code.
Processing package.json
flecks augments your source package.json during the build process and outputs a
built package.json to dist.
If you generate a fleck using @flecks/create-fleck, its files key will look like this:
"files": [
"index.js"
],
flecks automatically adds some paths to the files key of your built package.json:
builddirectorysrcdirectory (if sources exist)- sourcemaps for each entry point (e.g.
index.js->index.js.map) testdirectory (if tests exist)
@flecks/fleck invokes a hook
@flecks/fleck.packageJson which exposes this for any
other fleck to process package.json when building a fleck.
For instance, @flecks/web implements this hook. @flecks/web will automatically output
CSS, fonts, and other frontend assets to the assets directory in your build output. If any of
these frontend assets exist, @flecks/web will automatically add the assets directory to the
files key of your built package.json. You nor anyone using your fleck will have to think about
it!
What this means is that when we publish a fleck we don't publish the root directory, we publish
the dist output directory.
webpack build configuration
build/fleck.webpack.config.js
flecks provides a default webpack configuration for your fleck. This may not be what you want, so you may override it:
const flecksWebpackDefaults = require('@flecks/fleck/build/fleck.webpack.config');
module.exports = async (env, argv, flecks) => {
const config = await flecksWebpackDefaults(env, argv, flecks);
// ...
return config;
};
You don't actually have to extend the configuration like that, you could return a new configuration entirely! That's just an illustration of how you can override the defaults.
That being said, the defaults (including the automatic entry point stuff above and so much more) will absolutely make your life easier.
Testing
flecks uses Mocha and Chai to run your tests. When you create a fleck, it includes a run script
test. If you run it in your new empty fleck, you will see the output:
No fleck tests found.
No tests exist by default. Let's look at some example code and tests to understand how it works.
Example source
export const add2 = (n) => n + 2;
export const add3 = (n) => n + 3;
Example test
import {expect} from 'chai';
const {add2} = require('@testing/unit');
it('can add two to a number', () => {
expect(add2(2)).to.equal(5);
});
We intentionally made an error so we can see what a failed test looks like.
If we run the following command from within packages/example:
- npm
npm run test
We would see the following output:
1) can add two to a number
0 passing (11ms)
1 failing
1) can add two to a number:
AssertionError: expected 4 to equal 5
+ expected - actual
-4
+5
It catches the error! If we fixed it:
import {expect} from 'chai';
const {add2} = require('@testing/unit');
it('can add two to a number', () => {
expect(add2(2)).to.equal(4);
});
Try again:
- npm
npm run test
✓ can add two to a number
1 passing (4ms)
Everything's good!
The test task has more options like -w that support test watching and automatic running on
change. For an exhaustive list of options, see the CLI documentation
Linting
flecks automatically includes support for ESLint through a lint run script. flecks configures a
lot of default rules including a lightly-tweaked version of
eslint-config-airbnb as well as overrides for special
circumstances: for instance, loosening some rules for the test directory.
build/default.eslint.config.js
You may want to completely change these defaults and you can do so by creating a file
build/default.eslint.config.js:
const flecksEslintDefaults = require('@flecks/build/build/default.eslint.config');
module.exports = async (flecks) => ({
extends: [
await flecksEslintDefaults(flecks),
],
// ...
});
Again, you don't actually have to extend the configuration like that, you could return your own!
ESLint has made *async noises* lately, but as of this writing, the actually-working version requires you to write synchronous configuration files. So how does flecks do it? Honestly? Don't ask... We interface with torturous APIs so you don't have to!