tarotdeprecated

Bleeding edge web architecture

Usage no npm install needed!

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

README

Tarot

🔮 Bleeding edge web architecture

Webpack configuration for TS, JS, SCSS, and React with ESNext and optional CSS modules, JS polyfills, and IE11 support

Objectives

     🌈  Excellent dev experience

     🚀  Powerful build tools

     🏛  Standardized architecture


Table of Contents

  1. Features
  2. CLI and Package.json
  3. Options
  4. Plugins
  5. Usage with TypeScript
  6. API

Features

Optional and configurable support for:

  • TypeScript and ESNext
  • React
  • SCSS and CSS Modules
  • JS polyfills and IE11 support
  • Linting
  • Prettification
  • Dev server
  • Production builds
  • Webpack plugins
  • Git hooks via husky
  • Style entry files

Simplicity

Tarot exports a build function, which takes care of configuring Webpack for you. Build, bundle, lint, and polyfill with dead simple config:

// webpack.config.js
const { build } = require('tarot')

module.exports = build({
  entries: {
    bundle: { file: 'index.js' },
  },
})

Flexibility

Generate multiple output files, change input and output directories, turn on CSS Modules or polyfilling, provide your own transpile or lint settings, tweak dev server config, and add Webpack plugins with minimal configuration.

// webpack.config.js
const { build, pluginCopyFiles } = require('tarot')

module.exports = build({
  source: 'src',
  output: 'dist',
  entries: {
    bundle: {
      file: 'index.tsx',
      scriptOutputFile: `bundle_${Date.now()}`,
      styleOutputFile: 'styles',
      usePolyfills: false,
      useStyleLinting: false,
      useCssModules: true,
      babelConfigPath: 'babel.config.js',
      babelOptions: {
        sourceMaps: 'inline',
      },
      tsConfigPath: 'tsconfig.json',
      tsCompilerOptions: {
        lib: [ 'esnext', 'dom' ],
      },
    },
    'vendor/vendor-styles': {
      file: 'vendor.scss',
      useScriptLoaders: true,
      stylelintConfigPath: '.stylelintrc'
    },
  },
  plugins: [
    pluginCopyFiles({ from: 'assets', to: 'assets' })
  ],
  useHttps: true,
  allowCors: true,
  alias: {
    'react-dom': '@hot-loader/react-dom',
    Components: 'components',
  },
  nodeModuleBabelIncludes: [ 'react-spring' ],
})

CLI

You can run tarot via the CLI with the following commands:

  • tarot dev - launch the local development server and watch for changes

  • tarot build - build every application entry in the webpack config with dev env settings

  • tarot prod - build every application entry in the webpack config with production env settings

Note that Tarot does not clean up your output directory on each consecutive build.

Package.json

This is a basic package.json for a project using Tarot. The dev, build, and prod scripts in the example below are equivalent to running the same script via the Tarot CLI. The Tarot node module is the only module required in devDependencies:

  "scripts": {
    "dev": "webpack-dev-server --open",
    "clean": "rimraf ./dist/",
    "build": "npm run clean && webpack",
    "prod": "npm run build -- --prod"
  },
  "devDependencies": {
    "tarot": "latest"
  }

Options

Tarot has sensible defaults to encourage standardized architecture. However it is possible to tweak every aspect of the build process. All options are also listed under the API section of the readme.

Build Options

"Build Options" are only available at the top level of the object passed to Tarot's build function. Only the entries object is required.

// webpack.config.js
const { build } = require('tarot')

module.exports = build({
  // You can put Build Options here
  useHttps: true,
  allowCors: false,
  entries: {
    bundle: { file: 'index.js' },
  },
})

  • entries - Required. An object which contains all entry files. Entries are identified by keys. When Tarot builds the entries, the output files will be named according to that entry's key by default. Entries can be individually configured, including the output file names.

  • alias - A Webpack alias object, paths are relative to source.

  • useHttps - Whether to use HTTPS with webpack-dev-server, defaults to false.

  • allowCors - Whether to allows CORS with webpack-dev-server, defaults to false.

  • nodeModuleBabelIncludes - Package names from node_modules to include in babel-loader's module resolution, defaults to empty.

Entry Options

"Entry Options" are only available in individual entries. Only the file option is required.

// webpack.config.js
const { build } = require('tarot')

module.exports = build({
  entries: {
    bundle: {
      file: 'index.js',
      // You can put Entry Options here
      scriptOutputFile: `bundle_${Date.now}`,
    },
  },
})

  • file - Required. The path to an entry file relative to source.

  • scriptOutputFile - Provide a name for the output JavaScript bundle file, defaults to the key for this entry.

  • styleOutputFile - Provide a name for the output CSS bundle file, defaults to the key for this entry.

Common Options

"Common Options" are available at the top level of the object passed to Tarot's build function, as well as in each individual entry. All Common Options are optional.

Options placed at the top level will be applied as the default to every entry. Options placed in each entry will only apply to that entry, and will override top-level options.

// webpack.config.js
const { build } = require('tarot')

module.exports = build({
  // You can put Common Options here
  useStyleLinting: false,
  entries: {
    bundle: {
      file: 'index.js',
      // And also here
      useStyleLinting: true,
    },
  },
})

  • source - The source directory in which to resolve files.

  • output - The output directory where built files will reside.

  • plugins - An array of Webpack plugins.

  • useScriptLoaders - Whether to use babel-loader and ts-loader, defaults to true unless the entry file is not ECMAScript (i.e. a SCSS entry file).

  • styleIncludes - An array of directory paths to be compiled as standard stylesheets. Paths are relative to source. Defaults to 'styles/'.

  • cssModuleIncludes - An array of directory paths to be compiled as CSS Modules. Paths are relative to source. Defaults to 'components/'.

  • useCssModules - Whether to build CSS Modules, defaults to false. When true, files in cssModuleIncludes directories will be treated as CSS Modules (but not files in styleIncludes).

  • usePolyfills - Whether to use core-js polyfills, defaults to false.

  • useLinting - Whether to use any kind of linting, defaults to true.

  • useStyleLinting - Whether to lint styles, defaults to true.

  • useScriptLinting - Whether to lint scripts, defaults to true.

  • eslintConfigPath - Provide a path to a custom eslint config relative to the cwd, defaults to Tarot's eslint config.

  • eslintIgnorePath - Provide a path to a custom eslint ignore config relative to the cwd, defaults to no eslint ignore file.

  • eslintFiles - An array of directories to be passed to eslint-webpack-plugins's files option.

  • eslintExcludes - An array of directories to be passed to eslint's ignorePatterns option (paths are resolved according to eslint's ignorePatterns rules).

  • stylelintConfigPath - Provide a path to a custom stylelint config relative to the cwd, defaults to Tarot's stylelint config.

  • babelConfigPath - Provide a path to a custom babel config relative to the cwd, defaults to Tarot's babel config.

  • babelOptions - An object of Babel options. This can override values in Babel config files. When this option is passed at both the top-level build function and in an individual entry, the entry's options will be merged with the top-level options.

  • tsConfigPath - Provide a path to a custom tsconfig relative to the cwd, defaults to Tarot's tsconfig.

  • tsCompilerOptions - An object of TypeScript compiler options. This can override values in TypeScript config files. When this option is passed at both the top-level build function and in an individual entry, the entry's options will be merged with the top-level options.

  • rules - A function that receives the Webpack config module.rules array and returns a modified version. This allows for arbitrarily modifying the configuration.


Plugins

Tarot exports a couple plugins for your convenience. Tarot also uses several other plugins internally that aren't exported, for tasks like linting and typechecking. Plugins are passed directly to Webpack, so you are not limited to using Tarot's plugins - any valid Webpack plugin should work.

  • pluginCopyFiles - Copy files or directories from one location to another. Accepts an unlimited number of arguments, where each argument is an object in the shape { from: 'vendor', to: 'vendor' }. If the paths don't start with a slash / then the from path is relative to source, while the to path is relative to output.
pluginCopyFiles(
  // Copy files from images/ in source, to assets/images/ in output
  { from: 'images', to: 'assets/images' },
  // Copy a file from source to output, and rename it
  { from: 'test.html', to: `test_${Date.now}.html` },
)

  • pluginIgnoreOutput - Prevent Webpack from emitting files with names that match a pattern. Accepts a string, RegExp, or an array of strings and/or RegExp to match filenames against.
pluginIgnoreOutput([
  'styles.css',
  /^bundle\.js/,
])

Usage with TypeScript

Tarot works with TypeScript out of the box, but we recommend adding a tsconfig.json to the root of your project that extends Tarot's config. This helps code editors like VS Code:

// tsconfig.json
{ "extends": "tarot-typescript-config" }

API

This is the type signature of the API, where Tarot's build function will return a complete Webpack configuration:

type CommonOptions = {
    source?: string
    output?: string
    plugins?: Function[]
    useScriptLoaders?: boolean
    useCssModules?: boolean
    styleIncludes?: string[]
    cssModuleIncludes?: string[]
    usePolyfills?: boolean
    useLinting?: boolean
    useStyleLinting?: boolean
    useScriptLinting?: boolean
    eslintConfigPath?: string
    eslintIgnorePath?: string
    eslintFiles?: string[]
    eslintExcludes?: string[]
    stylelintConfigPath?: string
    babelConfigPath?: string
    babelOptions?: { [key: string]: any }
    tsConfigPath?: string
    tsCompilerOptions?: { [key: string]: any }
}

type EntryOptions = CommonOptions & {
    file: string
    scriptOutputFile?: string
    styleOutputFile?: string
}

// Values from CommonOptions that are specified
// here will be used as the default value for all
// entries. Individual entries can override options.
type BuildOptions = CommonOptions & {
    entries: { [key: string]: EntryOptions }
    alias?: { [key: string]: string }
    useHttps?: boolean
    allowCors?: boolean
    nodeModuleBabelIncludes?: string[]
}

export const build = (options: BuildOptions) => WebpackConfig

export const pluginCopyFiles = (...patterns: { from: string, to: string }[]) => void

export const pluginIgnoreOutput = (...filenames: string[]) => void