Webpack 生成 SCSS 源映射但不生成 JS
Webpack generating SCSS source maps but not JS
我已在 https://github.com/franklin626/custom_webpack_undebuggable 重现了我的问题。
从标准 Angular 9 CLI 应用程序开始,我需要自定义 webpack 构建,以便我的 SCSS 文件可以导入 JSON 配置。这意味着,在 angular.json
,从
"serve": {
"builder": "@angular-devkit/build-angular:dev-server"
到
"serve": {
"builder": "@angular-builders/custom-webpack:dev-server",
"options": {
"customWebpackConfig": {
"path": "webpack.config.js"
}
...
webpack.config.js
包含以下内容:
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'
}
}
}
]
}
]
}
};
我只获得 css 的源映射,而不是 javascript 的源映射。是的,我的 Chrome 启用了 JS 和 CSS 地图。不确定这里发生了什么?
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"
}
}
老实说,我无法用 sourcemaps 重现你的问题(我的意思是 js sourcemaps,也许你在浏览器中关闭了它们?我通常在 angular 应用程序中关闭 js source maps 以调试 ngfactories 等), 但我几乎可以肯定你遇到了 webpack 规则冲突的问题。
请将您的自定义 webpack 配置更改为以下内容以了解问题:
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);
}
在此日志中,您将看到默认的 anguar 配置已经有两个 /\.scss$|\.sass$/
的规则。
第一个用于组件或指令样式(component.scss
):
{
exclude: [
'/Users/nikitabalakirev/Desktop/Projects/angular-webpack-custom-sass-rule-example/src/styles.scss'
],
test: /\.scss$|\.sass$/,
use: [ ... ]
},
第二个用于全局样式:
{
include: [
'/Users/nikitabalakirev/Desktop/Projects/angular-webpack-custom-sass-rule-example/src/styles.scss'
],
test: /\.scss$|\.sass$/,
use: [
...
]
},
这意味着您的规则和默认规则相互重叠,可能会导致意外行为。
您可以使用此信息做什么:
- 如果您需要
scss
个文件才能导入 json 个文件,您需要更改现有的默认规则,而不是添加您自己的规则,例如:
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);
- 如果您不需要所有的 scss 文件都能够加载 jsons,请为此类文件使用特殊的 "domain" 名称:
test: /\.special\.scss$|\.sass$/,
和文件名,例如 styles.special.scss
,但您仍然需要将它们排除在 Angular 的规则之外!
- 如果您不需要 Angular 的规则,您可以将它们从默认配置中删除。但根据我的经验,支持具有自定义样式规则的 angular 应用程序是一个巨大的页面。每次更新后它都可能崩溃。
提示:您可以找到并研究 angular 默认配置 here
另外请确保您的 angular.json
看起来像这样:
"build": {
"builder": "@angular-builders/custom-webpack:browser",
"options": {
"customWebpackConfig": {
"path": "./webpack.config.js"
},
和
"serve": {
"builder": "@angular-builders/custom-webpack:dev-server",
"options": {
"browserTarget": "angular-webpack-custom-sass-rule-example:build"
},
UPD
我在你的 repo 中打开了一个 pr,感谢你的简单复制。
正如我之前所说,这是关于默认和自定义配置冲突。
Angular 使用 SourceMapDevToolPlugin
作为 source maps 所以你不需要设置 devtool
属性,留空即可。
这里正在使用 webpack
和 angular.json
配置:
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
工作。我得到了这个结果(更改了 json 文件中的颜色)
总的来说,过去的答案看起来是正确的。但是合并默认和自定义 webpack 配置有问题。 @angular-builders/custom-webpack
如果自定义 webpack 配置 returns 一个对象,则合并配置本身。如果它 returns 一个函数,那么这个函数应该自己合并它们。也许这种合并产生了问题。
我已在 https://github.com/franklin626/custom_webpack_undebuggable 重现了我的问题。
从标准 Angular 9 CLI 应用程序开始,我需要自定义 webpack 构建,以便我的 SCSS 文件可以导入 JSON 配置。这意味着,在 angular.json
,从
"serve": {
"builder": "@angular-devkit/build-angular:dev-server"
到
"serve": {
"builder": "@angular-builders/custom-webpack:dev-server",
"options": {
"customWebpackConfig": {
"path": "webpack.config.js"
}
...
webpack.config.js
包含以下内容:
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'
}
}
}
]
}
]
}
};
我只获得 css 的源映射,而不是 javascript 的源映射。是的,我的 Chrome 启用了 JS 和 CSS 地图。不确定这里发生了什么?
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"
}
}
老实说,我无法用 sourcemaps 重现你的问题(我的意思是 js sourcemaps,也许你在浏览器中关闭了它们?我通常在 angular 应用程序中关闭 js source maps 以调试 ngfactories 等), 但我几乎可以肯定你遇到了 webpack 规则冲突的问题。
请将您的自定义 webpack 配置更改为以下内容以了解问题:
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);
}
在此日志中,您将看到默认的 anguar 配置已经有两个 /\.scss$|\.sass$/
的规则。
第一个用于组件或指令样式(component.scss
):
{
exclude: [
'/Users/nikitabalakirev/Desktop/Projects/angular-webpack-custom-sass-rule-example/src/styles.scss'
],
test: /\.scss$|\.sass$/,
use: [ ... ]
},
第二个用于全局样式:
{
include: [
'/Users/nikitabalakirev/Desktop/Projects/angular-webpack-custom-sass-rule-example/src/styles.scss'
],
test: /\.scss$|\.sass$/,
use: [
...
]
},
这意味着您的规则和默认规则相互重叠,可能会导致意外行为。
您可以使用此信息做什么:
- 如果您需要
scss
个文件才能导入 json 个文件,您需要更改现有的默认规则,而不是添加您自己的规则,例如:
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);
- 如果您不需要所有的 scss 文件都能够加载 jsons,请为此类文件使用特殊的 "domain" 名称:
test: /\.special\.scss$|\.sass$/,
和文件名,例如styles.special.scss
,但您仍然需要将它们排除在 Angular 的规则之外! - 如果您不需要 Angular 的规则,您可以将它们从默认配置中删除。但根据我的经验,支持具有自定义样式规则的 angular 应用程序是一个巨大的页面。每次更新后它都可能崩溃。
提示:您可以找到并研究 angular 默认配置 here
另外请确保您的 angular.json
看起来像这样:
"build": {
"builder": "@angular-builders/custom-webpack:browser",
"options": {
"customWebpackConfig": {
"path": "./webpack.config.js"
},
和
"serve": {
"builder": "@angular-builders/custom-webpack:dev-server",
"options": {
"browserTarget": "angular-webpack-custom-sass-rule-example:build"
},
UPD
我在你的 repo 中打开了一个 pr,感谢你的简单复制。
正如我之前所说,这是关于默认和自定义配置冲突。
Angular 使用 SourceMapDevToolPlugin
作为 source maps 所以你不需要设置 devtool
属性,留空即可。
这里正在使用 webpack
和 angular.json
配置:
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
工作。我得到了这个结果(更改了 json 文件中的颜色)
总的来说,过去的答案看起来是正确的。但是合并默认和自定义 webpack 配置有问题。 @angular-builders/custom-webpack
如果自定义 webpack 配置 returns 一个对象,则合并配置本身。如果它 returns 一个函数,那么这个函数应该自己合并它们。也许这种合并产生了问题。