dufl

Modern zero-config JavaScript toolchain.

Usage no npm install needed!

<script type="module">
  import dufl from 'https://cdn.skypack.dev/dufl';
</script>

README

Dufl (Do u feel lucky)

Modern zero-config JavaScript toolchain.

A fork of create-react-app, work with multiple JavaScript platform and support mono-repository.

This project was made to match my personal use-case but i think it's relevant to share it publicly.

Dufl is not production-ready, you should be aware that some edge case can ruin everything so inspect your bundle with analyzer before deployment.

Philosophy

Most of create-react-app philosphy apply to Dufl :

  • One Dependency: There is just one build dependency. It uses Webpack, Babel, ESLint, and other amazing projects, but provides a cohesive curated experience on top of them (like create-react-app).

  • No Configuration Required: You don't need to configure anything. Reasonably good configuration of both development and production builds is handled for you so you can focus on writing code (like create-react-app).

With some addition :

  • Multi Platform : Dufl support multiple platform (Node.js, Web) and take care of library (sharing) / application (consuming) separation.

  • You Are Locked : You can't “eject”. If you need to change configuration or add specific Babel/Webpack plugin, you should not use Dufl in your project.

  • Modern Environment Only : Dufl produce smaller output than standard solution, because we support Node.js >= v10 and last 2 Chrome version, last Edge version, last Firefox version, last Safari version, last Android Chrome version, last iOS Safari version. There's no plan to add support for old browsers.

  • Support Mono Repository : Dufl support mono-repository with Lerna. It's the biggest trade-off when you work with create-react-app, you can't share code easily. Dufl support mono-repository with fast recompilation across package and map internal dependencies to avoid bloated bundle / dependencies duplication.

Dufl works on Windows/MacOS/Linux with :

Node.js >= 10
NPM >= 6

Creating Package

Get Started Immediately

You don’t need to install or configure tools like Webpack or Babel.
They are preconfigured and hidden so that you can focus on the code.

Just create a project, and you’re good to go, nothing more.

You’ll need to have Node 10 or later and NPM 6 or later on your local development machine (but it’s not required on the server). You can use nvm (MacOS/Linux) or nvm-windows to easily switch Node versions between different projects.

npx dufl-scaffold

(npx comes with npm 5.2+ and higher)

Warning : we highly discourage to use global installation, instead you should prefer npx.

It will create a directory [project-name] in the current folder.

When you make new package with dufl-scaffold, you will be prompted for :

  • Package name : choose a NPM package name (example : @organization/project, project, organization-project, my-long-project, ...).

  • Package type : this determine which platform and project type you will make (Node.js application or library ? React.js application or library ?).

Note : We use package.json dufl key to determine which platform is targeted. Don't be surprised if you notice this new key.

{
  ...
  "dufl": {
    "type": "react-app"
  }
}

Folder Structure And Installation

If you have a "packages" folder, your project will be created inside this folder instead of current folder (we support Lerna mono-repository).

Install project dependencies with NPM :

cd project-name
npm i

If you use a mono-repository, install dependencies with Lerna :

lerna bootstrap # Root of mono-repo
cd packages/project-name

Inside that directory, it will generate the initial project structure.

This structure mostly depend of the project type, in general you will end with this :

project-name
├── node_modules
├── package.json
└── src
    ├── __tests__
        └── index.spec.js
    ├── .env
    └── index.js

Note: You must create custom environment variables beginning with DUFL_. Any other variables except NODE_ENV will be ignored to avoid accidentally exposing a private key on the machine that could have the same name. Changing any environment variables will require you to restart the development server if it is running.

No configuration or complicated folder structures, just the files you need to build your app.

Inside the newly created project, you can run some built-in commands which depends on your package type :

|---------------------------------------------------------------------------------------------------|
|     #     | build  | dev   | watch  | styleguidev  | styleguibuild | pkg    | test   | analyzer   |
|:---------:|:------:|:-----:|:------:|:------------:|:-------------:|:------:|:------:|:-----------|
|  node-app |   ✅   |  ❌  |   ✅   |     ❌      |       ❌      |  ✅   |   ✅   |     ✅    |
|---------------------------------------------------------------------------------------------------|
|  node-lib |   ✅   |  ❌  |   ✅   |     ❌      |       ❌      |  ❌   |   ✅   |     ✅    |
|---------------------------------------------------------------------------------------------------|
| react-app |   ✅   |  ✅  |   ❌   |     ❌      |       ❌      |  ❌   |   ✅   |     ✅    |
|---------------------------------------------------------------------------------------------------|
| react-lib |   ✅   |  ❌  |   ✅   |     ✅      |       ✅      |  ❌   |   ✅   |     ✅    |
|---------------------------------------------------------------------------------------------------|

Use dufl-cli to show available command :

npm run dufl-cli

Show help for specific command (like test) :

npm run dufl-cli help test

npm run build

Supported package type : ALL

Builds the project for production to the build folder.
It correctly enforce production mode, bundle source code and optimizes the build for the best performance.

The build is minified and the filenames include the hashes if necessary.

Your project is ready to be deployed.

npm run dev

Supported package type : react-app

Runs the project in development mode.
Open http://localhost:3000 to view it in the browser.

The page will automatically reload if you make changes to the code.
You will see the build errors and lint warnings in the console.

Build errors

npm run styleguidev

Supported package type : react-lib

Runs the style guide in isolated development mode.
Open http://localhost:6060 to view it in the browser.

The page will automatically reload if you make changes to the code.

npm run styleguibuild

Supported package type : react-lib

Builds the style guide for production to the styleguide folder.
It correctly enforce production mode, bundle source code and optimizes the build for the best performance.

The build is minified and the filenames include the hashes if necessary.

npm run watch

Supported package type : node-app, node-lib, react-lib

Runs the project in watch mode.
There's no development server, it watch source code and build if you make changes to the code.

You will see the build errors and lint warnings in the console.

npm run pkg

Supported package type : node-app

Package your app into binaries with Node.js v10 included.

This will produce binaries for Windows/MacOS/Linux (64 bits).

npm test

Supported package type : ALL

Runs the test in non-interactive mode.
By default, runs tests related to files changed since the last commit.

If you want to run watch mode, put --watch flag after the command.

npm run test -- --watch

npm run analyzer

Supported package type : ALL

Builds the project for production to the build folder and start analyzer server.
Open http://localhost:8888 to view it in the browser.

You will see the dependencies tree for production build.

What’s Included ?

Your environment will have everything you need to build project :

  • Latest ES6/ES7 features WITHOUT transformation (except for things like static-class-properties).
  • React, JSX.
  • Autoprefixed CSS, so you don’t need -webkit- or other prefixes.
  • Emotion transformation and Babel macro support.
  • A fast interactive unit test runner with built-in support for coverage reporting.
  • A live development server that warns about common mistakes.
  • A isolated live development server to create React components and write styleguide with Markdown.
  • A build script to bundle JS, JSON, CSS, SVG and images for production, with hashes and sourcemaps.
  • A package script to bundle your app + Node.js v10 into x64 Windows/MacOS/Linux binaries.
  • A analyzer script to check if your bundle is bloated with duplicate dependencies easily.
  • Hassle-free updates for the above tools with a single dependency.

The tradeoff is that these tools are preconfigured to work in a specific way.

If your project needs more customization, you should not use Dufl.

Dufl will probably start to output ESM module at some moment and will support the new script module tag (as an optionnal feature).

Note : There's no plan to :

  • Support CSS pre-processor like SASS.
  • Support TypeScript/Flow.
  • Support all new ES features immediatly (example : Decorator and pipeline operator are not supported by Dufl).

Difference with create-react-app when you use Dufl with react-app package type :

  • No old browser/environment support (see philosophy).
  • No Yarn support.
  • No CSS module and no SASS preprocessor.
  • No TypeScript/Flow and no decorator.
  • No polyfills and less code transformation (features like class, object-rest-spread or async/await are not transformed, just minified).
  • Env var supported but without expansion.
  • Webpack Bundle Analyzer is available to inspect bundle size.
  • Mono-repository with Lerna is supported.
  • Less configurable (for example, no "browserlist" in "package.json").
  • There's probably more minor difference.

Internally, Dufl use theses packages :

  • Webpack
  • Babel
  • Jest
  • ESlint
  • Pkg
  • Styleguidist

Future Work

  • Ambigous package name is not supported, the CLI throw an error if something goes wrong with aliasing but scaffold don't care (we had to work more on how we handle aliasing) ?
  • No ESM output ? "nomodule" script tag ?
  • Modern browsers only (should we support more version) ?
  • More help in CLI, documentation and clear error/debug mode ?
  • Internal "eslint" config ?
  • Update dependencies to latest version ? Rebase things from create-react-app ?
  • New package type ?

Updating To New Release

Dufl is divided into 3 packages :

  • dufl-scaffold is a command-line utility that you use to create new projects.
  • dufl-cli is a development dependency in the generated projects.
  • dufl is a development dependency with configuration for all packages types (required by dufl-cli).

You never need to install dufl and dufl-cli itself, don't install them globally.

Instead, use dufl-scaffold with NPX.

When you run dufl-scaffold, it always creates the project with the latest version of dufl-cli so you’ll get all the new features and improvements in newly created apps automatically.

To update an existing project to a new version of dufl-cli, open the changelog, find the version you’re currently on (check package.json in this folder if you’re not sure), and apply the migration instructions for the newer versions.

In most cases bumping the dufl-cli version in package.json and running npm install (or lerna bootstrap if you are in a Lerna mono-repository) in this folder should be enough.

We commit to keeping the breaking changes minimal so you can upgrade dufl-cli painlessly.

What Other .env Files Can Be Used ?

  • .env: Default.
  • .env.local: Local overrides. This file is loaded for all environments except test.
  • .env.development, .env.test, .env.production: Environment-specific settings.
  • .env.development.local, .env.test.local, .env.production.local: Local overrides of environment-specific settings.

Files on the left have more priority than files on the right :

  • npm run dev | npm run watch: .env.development.local, .env.development, .env.local, .env
  • npm run build | npm run analyzer: .env.production.local, .env.production, .env.local, .env
  • npm test: .env.test.local, .env.test, .env (note .env.local is missing)

These variables will act as the defaults if the machine does not explicitly set them.
Please refer to the dotenv documentation for more details.

Configure Proxy

People often serve the front-end web app from the same host and port as their backend implementation.
For example, a production setup might look like this after the app is deployed:

/             - static server returns index.html with React app
/todos        - static server returns index.html with React app
/api/todos    - server handles any /api/* requests using the backend implementation

Such setup is not required. However, if you do have a setup like this, it is convenient to write requests like fetch('/api/todos') without worrying about redirecting them to another host or port during development.

To tell the development server to proxy any unknown requests to your API server in development, add a proxy field to your package.json, for example:

  "proxy": "http://localhost:4000",

This way, when you fetch('/api/todos') in development, the development server will recognize that it’s not a static asset, and will proxy your request to http://localhost:4000/api/todos as a fallback. The development server will only attempt to send requests without text/html in its Accept header to the proxy.

Conveniently, this avoids CORS issues and error messages like this in development:

Fetch API cannot load http://localhost:4000/api/todos. No 'Access-Control-Allow-Origin' header is present on the requested resource. Origin 'http://localhost:3000' is therefore not allowed access. If an opaque response serves your needs, set the request's mode to 'no-cors' to fetch the resource with CORS disabled.

Keep in mind that proxy only has effect in development (with npm run dev), and it is up to you to ensure that URLs like /api/todos point to the right thing in production. You don’t have to use the /api prefix. Any unrecognized request without a text/html accept header will be redirected to the specified proxy.

The proxy option supports HTTP, HTTPS and WebSocket connections.

"Invalid Host Header" Errors After Configuring Proxy

When you enable the proxy option, you opt into a more strict set of host checks. This is necessary because leaving the backend open to remote hosts makes your computer vulnerable to DNS rebinding attacks. The issue is explained in this article and this issue.

This shouldn’t affect you when developing on localhost, but if you develop remotely like described here, you will see this error in the browser after enabling the proxy option:

Invalid Host header

To work around it, you can specify your public development host in a file called .env.development in the root of your project:

HOST=mypublicdevhost.com

If you restart the development server now and load the app from the specified host, it should work.

If you are still having issues or if you’re using a more exotic environment like a cloud editor, you can bypass the host check completely by adding a line to .env.development.local.

Note that this is dangerous and exposes your machine to remote code execution from malicious websites:

# NOTE: THIS IS DANGEROUS!
# It exposes your machine to attacks from the websites you visit.
DANGEROUSLY_DISABLE_HOST_CHECK=true

We don’t recommend this approach.

Import New File Type

Dufl support Babel macros, you can install any macros or make your own.

Use preval.macro to evaluate code at build time :

import preval from 'preval.macro';

const myFileContent = preval`
  const fs = require('fs');

  module.exports = fs.readFileSync(require.resolve('./my-file.txt'), 'utf8');
`;

↓ ↓ ↓ ↓ ↓ ↓

const myFileContent = 'file content';

Optimize GraphQL query/mutation to avoid runtime cost with graphql.macro :

import { loader } from 'graphql.macro';

const query = loader('./query.graphql');

↓ ↓ ↓ ↓ ↓ ↓

const query = {
  "kind": "Document",
  "definitions": [{
    ...

Acknowledgements

Dufl is a fork of create-react-app and we are grateful to the authors and maintainers of create-react-app and related projects.

License

Dufl is open source software licensed as MIT.