-
Notifications
You must be signed in to change notification settings - Fork 598
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[heft-web-rig] Add an "app" profile, upgrade to Webpack 5, implement new CSS rules #3204
Conversation
localIdentName: production ? '[hash:base64]' : '[local]__[hash:base64:5]' | ||
}, | ||
|
||
sourceMap: !production |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
One weirdness of the above approach is that global .css files get sourcemaps injected, which you can see in the Chrome F12 debugger. (Does that even make sense when CSS modules are not being used?)
However because we are using 'mini-css-extract-plugin', the problem disappears when the CSS is extracted into a separate .css bundle.
DEBUG: !production | ||
}), | ||
|
||
process.env.REPORT ? new BundleAnalyzerPlugin({ analyzerMode: 'static' }) : undefined |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The idea is to generate the .html reports optionally, only if the REPORT
environment variable is set. (Other ideas welcome.) Currently we are using dotenv at HBO.
// this allows projects to override the default rig behavior if applicable (ex. when targeting older browsers) | ||
customizeArray: unique( | ||
'plugins', | ||
['HtmlWebpackPlugin'], |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
(This is slightly awkward -- is there a better way?)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm starting to think we should invent a more rigorous inheritance mechanism for webpack.config.js than these copy+paste createWebpackConfig()
functions. When we tackle that, we can also figure out a better way to handle these overrides.
… peer dependency problems
…ft-web-rig-revamp
common/changes/@rushstack/heft-web-rig/octogonz-heft-web-rig-revamp_2022-02-05-01-55.json
Outdated
Show resolved
Hide resolved
"outputFolderNames": ["dist", "lib", "lib-commonjs", "temp"] | ||
}, | ||
{ | ||
"operationName": "build", | ||
"outputFolderNames": ["dist", "lib", "lib-commonjs", "temp"] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
These lists should probably match the folders that heft clean
deletes.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good feedback, fixed!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Not in scope for this, but we should update Rush to always clean the output folders if it is not skipping the operation, not only if there is a cache hit. That way we get consistent behavior regarding rush cleaning regardless of the build cache.
{ | ||
// The source-map-loader extracts existing source maps from all JavaScript entries. This includes both | ||
// inline source maps as well as those linked via URL. All source map data is passed to Webpack for | ||
// processing as per a chosen source map style specified by the devtool option in webpack.config.js. | ||
// https://www.npmjs.com/package/source-map-loader | ||
test: /\.js$/, | ||
include: [path.resolve(projectRoot, 'lib')], | ||
enforce: 'pre', | ||
use: [ | ||
{ | ||
loader: require.resolve('source-map-loader') | ||
} | ||
] | ||
}, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This isn't built-in to Webpack at this point?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This rule still appears in the docs for Webpack 5:
https://webpack.js.org/loaders/source-map-loader/#getting-started
👉 Seems we should keep the above config as-is.
{ | ||
// Compiles SASS syntax into CSS | ||
// https://www.npmjs.com/package/sass-loader | ||
loader: require.resolve('sass-loader'), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I kinda think our recommended approach should be to compile SASS to CSS with heft-sass-plugin
so we don't have to parse it twice.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@iclanton is that possible today?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ian said that Heft has this feature today. However, compiling src/*.scss
-> lib/*.css
today will conflict with this requirement:
rushstack/rigs/heft-web-rig/shared/webpack-base.config.js
Lines 65 to 72 in c458420
// Handling of formats: | |
// - *.scss: sass, autoprefixer, modules <--- recommended | |
// - *.global.scss: sass, autoprefixer, NO modules | |
// - *.css: NO sass, NO autoprefixer, NO modules | |
// | |
// If someone wants CSS with modules, they can simply use the .sass file extension, since SASS | |
// is essentially a superset of CSS. (There's a small performance penalty for applying SASS to | |
// a plain CSS file, but the extra syntax validation probably justifies that cost.) |
To handle that, we need to add a heft-sass-plugin
setting that allows the file extensions to be remapped (e.g. src/*.scss
-> lib/*.module.css
BUT src/*.global.scss
--> lib/*.global.css
).
That needs to be implemented in a separate PR.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
When using heft-sass-plugin
to compile SASS to CSS, the mapping is:
src/**/*.scss
->lib/**/*.scss.css
src/**/*.sass
->lib/**/*.sass.css
src/**/*.css
->lib/**/*.css
This was done so that source code imports like:
import styles from './Foo.scss';
resolve correctly, whether you compile the file in advance or have webpack point at the source code. The only required change in webpack is to append ".css"
to resolve.extensions
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@dmichon-msft I tried to add this, however it turns out that sass.json
is not riggable because "cssOutputFolders"
gets resolved relative to the rig project rather than the app project. This is a design problem with heft-sass-plugin
.
We should do a separate PR to fix heft-sass-plugin
, then we can follow up with heft-web-rig
to enable this optimization.
// IMPORTANT: To simplify versioning, 'webpack-dev-server' is a devDependency, not a regular dependency | ||
import type { Configuration as WebpackDevServerConfig } from 'webpack-dev-server'; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why is it important for the reader to know, in this context, that 'webpack-dev-server'
is a devDependency
? I see "IMPORTANT:", and a fact -- but no explanation of why that fact is important, or what I-the-reader need to do about it.
My apologies if this question merely reflects my own ignorance, as is entirely possible.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If you try to import the value and reference it, it will compile and work locally, but fail at runtime when published. Only the type information is safe to use, hence the choice of type-only import.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@MickeyPhoenix Right, normally when you import from a package, that package must be a regular dependency (since a dev dependency will be missing for library consumers, as David explained). So the IMPORTANT
note is explaining why we are doing import type
.
It's a common situation for published libraries, but I can add a bit more context to the comment.
// "For production builds it's recommended to extract the CSS from your bundle being able to | ||
// use parallel loading of CSS/JS resources later on. This can be achieved by using the |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should this maybe be
// "For production builds it's recommended to extract the CSS from your bundle being able to | |
// use parallel loading of CSS/JS resources later on. This can be achieved by using the | |
// "For production builds it's recommended to extract the CSS from your bundle so that you are | |
// able to use parallel loading of CSS/JS resources later on. This can be achieved by using the |
or some such? I'm not sure how to parse it as it is.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The optimization is really less about parallelization and more that it lets you use the browser's optimized <link rel="stylesheet" href="..." />
hot path for importing your CSS. Also improves caching.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The quotation marks indicate that it is an excerpt from the Webpack website. The main point was that Webpack recommends to do this.
// Enable CSS modules... | ||
const useCssModules = | ||
// ...UNLESS the filename opts out using a file extension like "filename.global.scss" | ||
!/\.global\.\w+$/i.test(resourcePath) && | ||
// ...UNLESS this is a .css file. | ||
!/\.css$/i.test(resourcePath); | ||
return useCssModules; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is weird enough, maybe:
// Enable CSS modules... | |
const useCssModules = | |
// ...UNLESS the filename opts out using a file extension like "filename.global.scss" | |
!/\.global\.\w+$/i.test(resourcePath) && | |
// ...UNLESS this is a .css file. | |
!/\.css$/i.test(resourcePath); | |
return useCssModules; | |
// Files with ".global." in the name should NOT use modules | |
const isGlobalFileByName = /\.global\.\w+$/i.test(resourcePath); | |
// Files with the ".css" extension should NOT use modules | |
const isCssFile = /\.css$/i.test(resourcePath); | |
// Use modules (i.e.return `true`) if NEITHER of the above exceptions applies | |
const useCssModules = !isGlobalFileByName && !isCssFile; | |
return useCssModules; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why shouldn't you be allowed to write raw CSS modules? They're perfectly valid.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@halfnibble had the viewpoint that if you want to make raw CSS modules, you should use the .scss
file extension. The comments in this file explain the rationale:
// If someone wants CSS with modules, they can simply use the .sass file extension, since SASS
// is essentially a superset of CSS. (There's a small performance penalty for applying SASS to
// a plain CSS file, but the extra syntax validation probably justifies that cost.)
So the idea is that .css
should only be used for actual traditional .css
files without any fancy features.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
However I chatted with @iclanton and we've decided to enable autoprefixer
for .css
files.
// The "auto" setting has a confusing design: | ||
// - "false" disables CSS modules, i.e. ":local" and ":global" selectors can't be used at all | ||
// - "true" means magically disable CSS modules if the file extension isn't like ".module.css" | ||
// or ".module.scss" | ||
// - a lambda disables CSS modules only if the lambda returns false; the function parameter is | ||
// the resource path | ||
// - a RegExp disables CSS modules only if the resource path does not match the RegExp | ||
// | ||
// NOTE: Counterintuitively, if you instead set "modules=true" then CSS modules are enabled | ||
// without magic, equivalent to "auto: () => true" instead of "auto: true" | ||
// | ||
// DEFAULT: "true" (i.e. path based magic) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This feels like it belongs in the css-modules documentation, not in our webpack-base.config
. I suggest, instead:
// The "auto" setting has a confusing design: | |
// - "false" disables CSS modules, i.e. ":local" and ":global" selectors can't be used at all | |
// - "true" means magically disable CSS modules if the file extension isn't like ".module.css" | |
// or ".module.scss" | |
// - a lambda disables CSS modules only if the lambda returns false; the function parameter is | |
// the resource path | |
// - a RegExp disables CSS modules only if the resource path does not match the RegExp | |
// | |
// NOTE: Counterintuitively, if you instead set "modules=true" then CSS modules are enabled | |
// without magic, equivalent to "auto: () => true" instead of "auto: true" | |
// | |
// DEFAULT: "true" (i.e. path based magic) | |
// We define `auto` as a function so that we can selectively disable CSS modules only in the | |
// specific cases of (1) files with ".global." in the filename and (2) ".css" files. | |
// For all other files, we enable CSS modules. See the css-modules documentation for details. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This feels like it belongs in the css-modules documentation, not in our
webpack-base.config
.
We don't own in the css-modules
project. I added these notes for posterity, to save someone else the headache of trying to understand the docs for that setting. :-)
… sample project to illustrate usage
…ass-plugin is not quite ready yet
…ft-web-rig-revamp
Summary
Update
@rushstack/heft-web-rig
with a bunch of new features.Details
useUnknownInCatchVariables": false
to work around Allow "catch (e as Error)" instead of "catch (e: unknown)" TypeScript#42596webpack.config.js
defaults from the rig usingwebpack-merge
mini-css-extract-plugin
,css-minimizer-webpack-plugin
,webpack-bundle-analyzer
Import.resolveModule()
to ensurerequire('webpack')
finds the correct instanceRelated ideas that are outside the scope of this PR:
node-sass
withsass
forheft-sass-plugin
lib-commonjs
withemitCjsExtensionForCommonJS
@rushstack/heft-node-rig
strictPeerDependencies fix
While trying to get the various Webpack 5 plugin versions straightened out, I encountered a bunch of peer dependency issues. The
strictPeerDependencies
validation for this repo apparently got disabled by accident a few months ago, and a bunch of warnings accumulated in the interim. Ideally that should be solved in a separate PR with its own code review, but in the interest of time it is more efficient to solve the equation with the final set of package.json files, so I've rolled it into this PR. Sorry for the huge diff. :-)How it was tested
The new
@rushstack/heft-web-rig
was prototyped in therushstack.io-service
repo.CC @iclanton @elliot-nelson @dmichon-msft