Skip to content

CodeCoupler Webpack Development Details: Boilerplate Files

All the files of the boilerplate explained:

.vscode/settings.json

{

  //Tell VSCode what type some files are:
  "files.associations": {
    "*.postcssrc": "json",
    "*.lintstagedrc": "json"
    "LICENSE": "plaintext",
    "BANNER": "plaintext"
  },

  //Disable built-In Validators and let only stylelint validate and fix:
  "css.validate": false,
  "less.validate": false,
  "scss.validate": false,

  //We will use the linter markdownlint, eslint and stylelint to automatic fix code on save.
  //ESLint and Stylelint are configured to work together with prettier which will format the code on
  //save.  
  "eslint.enable": true,
  "stylelint.enable": true,
  "files.autoSave": "off",
  "editor.defaultFormatter": "esbenp.prettier-vscode",
  "[javascript][vue][typescript]": {
    "editor.defaultFormatter": "esbenp.prettier-vscode"
  },
  "[css][scss][less]": {
    "editor.defaultFormatter": "esbenp.prettier-vscode"
  },
  "[markdown]": {
    "editor.defaultFormatter": "DavidAnson.vscode-markdownlint"
  },
  "editor.codeActionsOnSave": {
    "source.fixAll.eslint": "explicit",
    "source.fixAll.stylelint": "explicit",
    "source.fixAll.markdownlint": "explicit"
  },
  "editor.formatOnSave": true,

  //Some Helper Extension for Comments and Markdown
  "rewrap.autoWrap.enabled": true,
  "markdowntable.formatOnSave": true,

  //Unify tab sizes
  "editor.tabSize": 2,

  //Unify EOL
  "files.eol": "\n"

  //Removed support of Vetur in Boilerplate Version 2.2
  //"vetur.validation.template": false,

  //Enable linting for vue files
  "eslint.validate": [
    "javascript",
    "javascriptreact",
    "vue"
  ],

  //Show all three breakpoints that will be uitilized by the boilerplates
  "editor.rulers": [80, 100, 120]

}

.eslintrc

{

  // Starting with the global configuration for all JavaScript Files

  // Use ignore pattern here instead of "".eslintignore". This will ensure working with
  // nested configurations in workspaces.
  "ignorePatterns": ["dist/", "node_modules/"],
  "extends": [
    // Set of rules which are recommended for all projects by the ESLint Team
    "eslint:recommended",
    // Rules for Vue validation
    "plugin:vue/recommended",
    // Enables eslint-plugin-prettier and eslint-config-prettier.
    // Turns off all rules that are unnecessary or might conflict with Prettier
    // This will display prettier errors as ESLint errors.
    // Make sure this is always the last configuration in the extends array.
    "plugin:prettier/recommended"
  ],
  // Normally we have here to use babel as parser to recognize experimental features.
  // But "vue-eslint-parser" have to be in first place. Therefore the babel parser is
  // moved into "parserOptions.parser".
  "parser": "vue-eslint-parser"
  "parserOptions": {
    // Parser to used internally by "vue-eslint-parser"
    "parser": "@babel/eslint-parser",
    // Using version 11 to allow dynamic imports and implement lazy loading
    "ecmaVersion": 11,
    "sourceType": "module",
    // For linting files that are not transformed by Babel (such as tooling configuration files)
    "requireConfigFile": false
  },
  "env": {
    "browser": true,
    "node": true,
    "es6": true
  },
  // Plugins for parser and formatting rules
  "plugins": ["babel", "prettier"],
  "rules": {
    // Add Prettier Ruls and use the file ".prettierc". With this the rules which will use
    // ESLint are in sync with the rules that will be used by Prettier. After changing the
    // ".prettierrc" file the ESLint server must be restartet!
    "prettier/prettier": [
      "error",
      {},
      {
        "usePrettierrc": true
      }
    ]
  },

  // Now override special configuration for TypeScript Files

  "overrides": [
    {
      "files": ["**/*.ts"],
      // Specifies the ESLint parser that will allow ESLint to lint TypeScript code
      "parser": "@typescript-eslint/parser",
      "extends": [
        // set of rules which are recommended for all projects by the ESLint Team
        "eslint:recommended",
        // Uses the recommended rules from the @typescript-eslint/eslint-plugin
        "plugin:@typescript-eslint/recommended",
        // Enables eslint-plugin-prettier and eslint-config-prettier.
        // This will display prettier errors as ESLint errors.
        // Make sure this is always the last configuration in the extends array.
        "plugin:prettier/recommended"
      ],
      "parserOptions": {
        // Using version 11 to allow dynamic imports and implement lazy loading
        "ecmaVersion": 11,
        "sourceType": "module"
      },
      "env": {
        "browser": true,
        "node": true,
        "es6": true
      },
      "plugins": [
        // A plugin that contains a bunch of ESLint rules that are TypeScript specific
        "@typescript-eslint/eslint-plugin",
        // Plugin with formatting rules
        "prettier"
      ],
      "rules": {
        "prettier/prettier": [
          "error",
          {},
          {
            "usePrettierrc": true
          }
        ]
      }
    }
  ]
}

.prettierc

The "trailingComma" was done to align the rules of the eslint-prettier-plugin rules and the default prettier rules used by the IDE on save.

For HTML we like to have more space.

{
  "printWidth": 80,
  "trailingComma": "none",
  "overrides": [
    {
      "files": ["*.html", "*.vue"],
      "options": {
        "printWidth": 120
      }
    }
  ]
}

.markdownlint.json

This rule is disabled by default, because lines are often longer than 80 chars. But the markdown looks odd has long lines and short lines distributed unevenly. Maybe a line length of 100 is a better approach:

1
2
3
{
  "MD013": { "line_length": 100 }
}

.postcssrc

Compile modern (and experimental) CSS for targeted browsers defined in .browserslistrc:

1
2
3
4
5
6
7
8
{
  "plugins": {
    // 
    "postcss-preset-env": {
      stage: 0
    }
  }
}

.stylelintrc

{
  // Moved from ".stylelintignore" because this will not work in monorepos
  // with nested packages (https://github.com/stylelint/vscode-stylelint/issues/155).
  "ignoreFiles": ["**/dist/**", "**/node_modules/**"],
  // This configuration turns off all rules that might conflict with Prettier
  "extends": [ "stylelint-config-standard", "stylelint-prettier/recommended" ],
  "plugins": [ "stylelint-prettier" ],
  "rules": {
    "prettier/prettier": true,
    // This addition make using CSS Module Pseudo Classes usable
    "selector-pseudo-class-no-unknown": [
      true,
      {
        "ignorePseudoClasses": [
          "local",
          "global"
        ]
      }
    ]
  }
}

tsconfig.json

{
  "compilerOptions": {
    // The directory for the output
    // Is this needed? Maybe because it will be automatically set as "exclude" glob.
    "outDir": "./dist/",
    // Is this needed?
    "noImplicitAny": true,
    // Without this (or with another value like "es6") we cannot use 'import "a-node-library"' from
    // TypeScript files.
    "module": "commonjs",
    // Enable Decorators for class style vue apps
    "experimentalDecorators": true,
    // As we use babel this maybe is not needed, or other values acceptable.
    // From Vue.js docs: This aligns with Vue's browser support
    "target": "es5",
    // Create source maps in combination with "devtool"
    "sourceMap": true,
    // From Vue.js docs:
    // This enables stricter inference for data properties on `this`.
    // Note that you have to include strict: true (or at least noImplicitThis: true which is a part
    // of strict flag) to leverage type checking of this in component methods otherwise it is always
    // treated as any type.
    "strict": true,
    //Support for Volar Plugin. Without this setting the plugin complain
    //the tag <script> in vue files because of missing the attribute lang="ts"
    "allowJs": true
    // Vue.js docs note this two settings, but they are not used here:
    // If using webpack 2+ or rollup, to leverage tree shaking:
    // "module": "es2015",
    // "moduleResolution": "node"
  }
}

.gitignore

# Dependency directories
node_modules/

# Transpiled source files
dist/

# Cache used by TypeScript's incremental build
*.tsbuildinfo

# Output of 'npm pack'
*.tgz

# Optional eslint cache
.eslintcache

husky setup: hook files, .lintstagedrc and package.json

In package.json thusky will be initialized everytime npm i is executed:

1
2
3
4
5
{
  "scripts": {
    "prepare": "husky"
  }
}

In the driectory .husky the prepare commit script is defined in the pre-commit file:

npx lint-staged

In .lintstagedrc will be defined what files to lint before commit:

1
2
3
4
5
6
{
  "src/**/*.ts": "npm run tslint",
  "src/**/*.js": "npm run jslint",
  "src/**/*.vue": "npm run vuelint",
  "src/**/*.css": "npm run csslint"
}

babel.config.json (.babelrc till version 4.4.0)

This will be used by Babel and the eslint-babel Plugin.

{
  // This is the all-in-one preset which will manage all needed syntax transforms based on the
  // target browsers configured in ".browserlistrc".
  //
  // To handle polyfills as well we have to set the "useBuiltIns" option. Otherwise an error 
  // "regeneratorRuntime is not defined" will be thrown if you use features like "async". This is 
  // because the preset will decide to use the plugin "@babel/plugin-transform-regenerator" and 
  // the runtime have to be globally accessible. See
  // [https://github.com/babel/babel/issues/9849#issuecomment-487040428]
  //
  // To make this global accessible you could write in one point of your code (and it have) to be 
  // exactly one point) "import 'core-js'; import 'regenerator-runtime/runtime';". A better 
  // solution is to tell babel to handle all these imports. With "useBuiltIns: usage" all the 
  // needed import statements will be injected by babel. And then Webpack will do the rest. See 
  // [https://github.com/babel/babel/issues/9849#issuecomment-646245498]
  //
  // Furthermore we tell babel to use core-js version 3.41 and use also polyfills for proposals
  // and not just for stable ECMAScript features. We have choosed 3.41 because this is the
  // currently provided version in CodeCoupler Webpack. And It is recommended to specify the minor
  // version otherwise "3" will be interpreted as "3.0" which may not include polyfills for the
  // latest features. See [https://babeljs.io/docs/babel-preset-env#corejs]
  //
  // A last note: In package.json you will find the depndency to "core-js" which is needed to make
  // everything work. But there is no dependency to "@babel/plugin-transform-regenerator". This is
  // because this plugin is included as dependecy of "@babel/preset-env".
  presets: [
    [
      "@babel/preset-env",
      {
        "useBuiltIns": "usage",
        "corejs": { "version": "3.41", "proposals": true }
      }
    ]
  ],

  // Under `useBuiltins: "usage"`, Babel needs to take a guess wrt. ES6 vs. CJS import styles; and 
  // it better guess the same way as Webpack. The simplest way to make that happen is to also set 
  // `sourceType: "unambiguous"` in the options. See [https://stackoverflow.com/a/68352125], 
  // [https://stackoverflow.com/a/52415747]
  "sourceType": "unambiguous",

  // Using Babel together with Webpack cause many different errors in case we do not exclude
  // `node_modules` from the Webpack rules where `babel-loader` is used. We do not want to exclude 
  // all modules from transpiling, because it is sometimes usefull not only to bundle scripts from 
  // `node_modules`but also to transpile them, so we can use them in a browser context.
  // 
  // In this case we have to exclude some of them explicitly from transpiling. First of all we have 
  // to exclude `core-js`. Because otherwise the transformation under `useBuiltins: "usage"` will 
  // actually be inserting references to core-js into itself causing dependency cycles.
  //
  // The Webpack documentation recommends excluding `node_modules/webpack/buildin` from this as 
  // well. From own observations, this is not enough, as errors and warnings are also seen when 
  // using the Webpack Dev Server. Therefore, we exclude all files that start with 
  // `node_modules/webpack`.
  //
  // Another problem is the combination of Babel, the `css-loader` and `mini-css-extract-plugin`. 
  // The produced javascript modules of the `css-loader` will be detected by babel any processed 
  // like any other javascript file. Since we have set `useBuiltins: "usage"` babel will include an 
  // import to a core-js polyfill, which use a function named `$`. This function is not available 
  // when `mini-css-extract-plugin` gets the code, which leads to an error.
  // 
  // See:
  //
  // - [https://github.com/webpack-contrib/mini-css-extract-plugin/issues/471#issuecomment-750266195]
  // - [https://stackoverflow.com/a/58517865]
  // - [https://stackoverflow.com/a/68836211]
  // - [https://webpack.js.org/loaders/babel-loader/#exclude-libraries-that-should-not-be-transpiled]
  // - [https://stackoverflow.com/a/52415747]
  "ignore": [
    "node_modules/css-loader",
    "node_modules/core-js",
    "node_modules/webpack"
  ],

  // This one will be added only in production mode and will strip "console.debug"
  // statements away.
  "env": {
    "production": {
        "plugins": [
            ["strip-function-call", {
                "strip": ["console.debug"]
            }]
        ]
    }
  }
}

.browserlistrc

Many components access this configuration. The included .browserlistrc contains only a very basic configuration:

1
2
3
last 1 version
> 1%
not dead

Just one note: Do not include node targets like maintained node versions because Webpack 5 will throw an error: https://github.com/webpack/webpack/issues/11660