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
:
build
directorysrc
directory (if sources exist)- sourcemaps for each entry point (e.g.
index.js
->index.js.map
) test
directory (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!