Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Webpack generating SCSS source maps but not JS

I have reproduced my issue at https://github.com/franklin626/custom_webpack_undebuggable.

Starting with a standard Angular 9 CLI application, I had a need to customize the webpack build so that my SCSS files can import JSON configs. This means, in angular.json, moving from

"serve": {
      "builder": "@angular-devkit/build-angular:dev-server"

to

"serve": {
      "builder": "@angular-builders/custom-webpack:dev-server",
      "options": {
        "customWebpackConfig": {
          "path": "webpack.config.js"
        }
       ...

With webpack.config.js containing the following:

const jsonImporter = require('node-sass-json-importer');

module.exports = {
  mode: 'development',
  module: {
    rules: [
      {
        test: /\.scss$|\.sass$/,
        use: [
          {
            loader: require.resolve('sass-loader'),
            options: {
              implementation: require('node-sass'),
              sassOptions: {
                // bootstrap-sass requires a minimum precision of 8
                precision: 8,
                importer: jsonImporter(),
                outputStyle: 'expanded'
              }
            }
          }
        ]
      }
    ]
  }
};

I am only getting the source maps for the css, but not for the javascript. And yes, my Chrome has JS and CSS maps enabled. Not sure what is going on here ?

angular.json:

{
  "$schema": "./node_modules/@angular/cli/lib/config/schema.json",
  "version": 1,
  "newProjectRoot": "projects",
  "projects": {
    "my-web-app": {
      "projectType": "application",
      "schematics": {
        "@schematics/angular:component": {
          "style": "scss"
        }
      },
      "root": "",
      "sourceRoot": "src",
      "prefix": "app",
      "architect": {
        "build": {
          "builder": "@angular-builders/custom-webpack:browser",
          "options": {
            "customWebpackConfig": {
              "path": "./webpack.deploy.config.js",
              "replaceDuplicatePlugins": true
            },
            "outputPath": "dist/my-web-app",
            "index": "src/index.html",
            "main": "src/main.ts",
            "polyfills": "src/polyfills.ts",
            "tsConfig": "tsconfig.app.json",
            "aot": true,
            "assets": ["src/favicon.ico", "src/assets"],
            "styles": ["src/styles.scss"],
            "scripts": []
          },
          "configurations": {
            "production": {
              "fileReplacements": [
                {
                  "replace": "src/environments/environment.ts",
                  "with": "src/environments/environment.prod.ts"
                }
              ],
              "optimization": true,
              "outputHashing": "all",
              "sourceMap": false,
              "extractCss": true,
              "namedChunks": false,
              "extractLicenses": true,
              "vendorChunk": false,
              "buildOptimizer": true,
              "budgets": [
                {
                  "type": "initial",
                  "maximumWarning": "2mb",
                  "maximumError": "5mb"
                },
                {
                  "type": "anyComponentStyle",
                  "maximumWarning": "6kb",
                  "maximumError": "10kb"
                }
              ]
            }
          }
        },
        "serve": {
          "builder": "@angular-builders/custom-webpack:dev-server",
          "options": {
            "customWebpackConfig": {
              "path": "webpack.config.js",
              "sourceMap": true
            },
            "browserTarget": "my-web-app:build"
          },
          "configurations": {
            "production": {
              "browserTarget": "my-web-app:build:production"
            }
          }
        },
        "extract-i18n": {
          "builder": "@angular-devkit/build-angular:extract-i18n",
          "options": {
            "browserTarget": "my-web-app:build"
          }
        },
        "test": {
          "builder": "@angular-builders/custom-webpack:karma",
          "options": {
            "customWebpackConfig": {
              "path": "webpack.config.js"
            },
            "main": "src/test.ts",
            "polyfills": "src/polyfills.ts",
            "tsConfig": "tsconfig.spec.json",
            "karmaConfig": "karma.conf.js",
            "assets": ["src/favicon.ico", "src/assets"],
            "styles": ["src/styles.scss"],
            "scripts": []
          }
        },
        "lint": {
          "builder": "@angular-devkit/build-angular:tslint",
          "options": {
            "tsConfig": ["tsconfig.app.json", "tsconfig.spec.json", "e2e/tsconfig.json"],
            "exclude": ["**/node_modules/**"]
          }
        },
        "e2e": {
          "builder": "@angular-builders/custom-webpack:protractor",
          "options": {
            "customWebpackConfig": {
              "path": "webpack.config.js"
            },
            "protractorConfig": "e2e/protractor.conf.js",
            "devServerTarget": "my-web-app:serve"
          },
          "configurations": {
            "production": {
              "devServerTarget": "my-web-app:serve:production"
            }
          }
        }
      }
    }
  },
  "defaultProject": "my-web-app"
}

package.json

{
  "name": "my-web-app",
  "version": "0.0.0",
  "scripts": {
    "ng": "ng",
    "start": "ng serve",
    "build": "ng build",
    "test": "ng test",
    "test-headless": "ng test --watch=false --browsers=ChromeHeadless",
    "lint": "ng lint",
    "e2e": "ng e2e",
    "doc": "node src/scripts/runMarked.js",
    "prestart": "npm run doc",
    "storybook": "start-storybook -p 6006",
    "build-storybook": "build-storybook"
  },
  "dependencies": {
    "@angular/animations": "^9.1.4",
    "@angular/cdk": "^9.0.0",
    "@angular/common": "^9.1.4",
    "@angular/compiler": "^9.1.4",
    "@angular/core": "^9.1.4",
    "@angular/forms": "^9.1.4",
    "@angular/material": "^9.0.0",
    "@angular/platform-browser": "^9.1.4",
    "@angular/platform-browser-dynamic": "^9.1.4",
    "@angular/router": "^9.1.4",
    "@azure/msal-angular": "^1.0.0-beta.3",
    "@types/vega": "^3.2.0",
    "build": "^0.1.4",
    "d3": "^5.15.0",
    "karma-viewport": "^1.0.5",
    "marked": "^0.8.0",
    "msal": "^1.2.2-beta.0",
    "ng": "0.0.0",
    "ngx-spinner": "^9.0.2",
    "rxjs": "~6.5.4",
    "tslib": "^1.10.0",
    "vega": "^5.9.1",
    "vega-embed": "^6.2.2",
    "vega-lite": "^4.4.0",
    "vega-typings": "^0.12.4",
    "zone.js": "~0.10.2"
  },
  "devDependencies": {
    "@angular-builders/custom-webpack": "^9.0.0",
    "@angular-devkit/build-angular": "^0.901.0",
    "@angular/cli": "^9.1.4",
    "@angular/compiler-cli": "^9.1.4",
    "@angular/language-service": "^9.1.4",
    "@babel/core": "^7.9.0",
    "@storybook/addon-actions": "^5.3.18",
    "@storybook/addon-links": "^5.3.18",
    "@storybook/addon-notes": "^5.3.18",
    "@storybook/addons": "^5.3.18",
    "@storybook/angular": "^5.3.18",
    "@types/jasmine": "~3.5.0",
    "@types/jasminewd2": "~2.0.3",
    "@types/node": "^12.11.1",
    "babel-loader": "^8.1.0",
    "codelyzer": "^5.1.2",
    "compression-webpack-plugin": "^3.1.0",
    "jasmine-core": "~3.5.0",
    "jasmine-spec-reporter": "~4.2.1",
    "karma": "~4.3.0",
    "karma-chrome-launcher": "~3.1.0",
    "karma-coverage-istanbul-reporter": "~2.1.0",
    "karma-jasmine": "~2.0.1",
    "karma-jasmine-html-reporter": "^1.4.2",
    "karma-junit-reporter": "^2.0.1",
    "minimist": "^1.2.0",
    "node-sass": "^4.13.1",
    "node-sass-json-importer": "^4.1.2",
    "prettier": "1.19.1",
    "protractor": "~5.4.3",
    "terser-webpack-plugin": "^2.3.5",
    "ts-node": "~8.3.0",
    "tslint": "~5.18.0",
    "tslint-config-prettier": "^1.18.0",
    "typescript": "~3.7.5"
  }
}
like image 512
BuZz Avatar asked Apr 27 '20 11:04

BuZz


2 Answers

To be honest I can't reproduce your issue with sourcemaps (I mean js sourcemaps, maybe you turned them off in browser? I usually turn js source maps off in angular applications to debug ngfactories and etc), but I almost sure that you got into conflicting webpack rules problem.

Please change your custom webpack config to the following to understand the problem:

const merge = require('webpack-merge');
const jsonImporter = require('node-sass-json-importer');

module.exports = function (defaultConfig) {

  console.log('>>>>> debug default config rules', defaultConfig.module.rules);

  const config = {
    mode: 'development',
    module: {
      rules: [
        {
          test: /\.scss$|\.sass$/,
          use: [
            {
              loader: require.resolve('sass-loader'),
              options: {
                implementation: require('node-sass'),
                sassOptions: {
                  // bootstrap-sass requires a minimum precision of 8
                  precision: 8,
                  importer: jsonImporter(),
                  outputStyle: 'expanded'
                }
              }
            }
          ]
        }
      ]
    }
  };

  return merge(defaultConfig, config);
}

In this log you will see that default anguar config already have two rules for /\.scss$|\.sass$/.
First one for component or directive styles (component.scss):

  {
    exclude: [
      '/Users/nikitabalakirev/Desktop/Projects/angular-webpack-custom-sass-rule-example/src/styles.scss'
    ],
    test: /\.scss$|\.sass$/,
    use: [ ... ]
  },

And the second for global styles:

  {
    include: [
      '/Users/nikitabalakirev/Desktop/Projects/angular-webpack-custom-sass-rule-example/src/styles.scss'
    ],
    test: /\.scss$|\.sass$/,
    use: [
      ...
    ]
  },

This means that your rule and default rules overlapping each other and that can lead to unexpected behavior.
What you can do with this information:

  1. If you need scss files to be able to import json files you need to change existing default rule, not to add your own, something like that:
const myRule =    {
              loader: require.resolve('sass-loader'),
              options: {
                implementation: require('node-sass'),
                sassOptions: {
                  // bootstrap-sass requires a minimum precision of 8
                  precision: 8,
                  importer: jsonImporter(),
                  outputStyle: 'expanded'
                }
              }
            }
const scssComponentRule = defaultConfig.module.rules.find(rule => rule.test.toString().includes('scsss'));
scssComponentRule.use.unshift(myRule);

  1. If you need NOT ALL of your scss files to be able to load jsons, use special "domain" name for such files:
    test: /\.special\.scss$|\.sass$/, and file name for example styles.special.scss, but you still need to exclude them from Angular's rules!
  2. If you don't need Angular's rules you can remove them from default config. But from my experience it's a huge page to support angular application with custom styling rules. It can crash after each update.

Tip: you can find and research angular default config here

Also please make sure that your angular.json looks like this:

        "build": {
          "builder": "@angular-builders/custom-webpack:browser",
          "options": {
            "customWebpackConfig": {
              "path": "./webpack.config.js"
            },

and

        "serve": {
          "builder": "@angular-builders/custom-webpack:dev-server",
          "options": {
            "browserTarget": "angular-webpack-custom-sass-rule-example:build"
          },

UPD
I opened a pr in your repo, thanks for the easy reproduction.
As I said previously it is about default and custom config conflicts.
Angular uses SourceMapDevToolPlugin for source maps so you don't need to set devtool property, just leave it blank.

like image 172
nickbullock Avatar answered Nov 15 '22 07:11

nickbullock


Here is working the webpack and angular.json configurations:

angular.json

{
  ...
  "architect": {
    "build": {
      "builder": "@angular-builders/custom-webpack:browser", // <---
      "options": {
        "customWebpackConfig": {
          "path": "./webpack.config.js", // <---
        },
        ...
      }
    }
  },
  "serve": {
    "builder": "@angular-builders/custom-webpack:dev-server", // <---
    "options": {
      "browserTarget": "test:build" // <---
    },
    ...
  },
  ...
}

webpack.config.js

const jsonImporter = require("node-sass-json-importer");

module.exports = {
  module: {
    rules: [
      {
        test: /\.scss$|\.sass$/,
        use: [
          {
            loader: require.resolve("sass-loader"),
            options: {
              implementation: require("node-sass"),
              sassOptions: {
                // bootstrap-sass requires a minimum precision of 8
                precision: 8,
                importer: jsonImporter(),
                outputStyle: "expanded",
              },
            },
          },
        ],
      },
    ],
  }
};

ng serve/build/build --prod work. And I got this result (changed colors as in your json file) enter image description here

In general, the past answer looks correct. But something wrong with the merging of default and custom webpack configs. @angular-builders/custom-webpack merges configs itself if the custom webpack config returns an object. If it returns a function then this function should merge them itself. And maybe this merging produced and issue.

like image 30
Dmitrii Makarov Avatar answered Nov 15 '22 08:11

Dmitrii Makarov