Skip to content

Commit

Permalink
feat(webpack): remove support for legacy browsers (nrwl#14190)
Browse files Browse the repository at this point in the history
  • Loading branch information
jaysoo authored and Jack Hsu committed Jan 10, 2023
1 parent 5970246 commit 5b69c18
Show file tree
Hide file tree
Showing 20 changed files with 965 additions and 1,276 deletions.
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { TsconfigPathsPlugin } from 'tsconfig-paths-webpack-plugin';
import { Configuration } from 'webpack';
import { getCSSModuleLocalIdent } from '@nrwl/webpack/src/executors/webpack/lib/get-webpack-config';
import { getCSSModuleLocalIdent } from '@nrwl/webpack';

export function buildBaseWebpackConfig({
tsConfigPath = 'tsconfig.cy.json',
Expand Down
15 changes: 2 additions & 13 deletions packages/react/plugins/storybook/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@ import {
} from '@nrwl/devkit';
import { getBaseWebpackPartial } from '@nrwl/webpack/src/utils/config';
import { NormalizedWebpackExecutorOptions } from '@nrwl/webpack/src/executors/webpack/schema';
import { getStylesPartial } from '@nrwl/webpack/src/executors/webpack/lib/get-webpack-config';
import { checkAndCleanWithSemver } from '@nrwl/workspace/src/utilities/version-utils';
import { join } from 'path';
import { gte } from 'semver';
Expand Down Expand Up @@ -118,18 +117,8 @@ export const webpack = async (
const extractCss = storybookWebpackConfig.mode === 'production';

// ESM build for modern browsers.
const baseWebpackConfig = mergeWebpack.merge([
getBaseWebpackPartial(builderOptions, {
isScriptOptimizeOn,
skipTypeCheck: true,
}),
getStylesPartial(
options.workspaceRoot,
options.configDir,
builderOptions,
extractCss
),
]);
let baseWebpackConfig: Configuration = {};
baseWebpackConfig = getBaseWebpackPartial(builderOptions);

// run it through the React customizations
const finalConfig = reactWebpackConfig(baseWebpackConfig);
Expand Down
4 changes: 4 additions & 0 deletions packages/webpack/index.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
export * from './src/utils/create-copy-plugin';
export * from './src/utils/config';
export * from './src/generators/init/init';
export * from './src/generators/webpack-project/webpack-project';
Expand All @@ -11,3 +12,6 @@ export type {
FileReplacement,
} from './src/executors/webpack/schema';
export * from './src/executors/webpack/webpack.impl';
export * from './src/utils/get-css-module-local-ident';
export * from './src/utils/with-nx';
export * from './src/utils/with-web';
311 changes: 10 additions & 301 deletions packages/webpack/src/executors/webpack/lib/get-webpack-config.ts
Original file line number Diff line number Diff line change
@@ -1,28 +1,10 @@
import * as path from 'path';
import { posix, resolve } from 'path';
import { readTsConfig } from '@nrwl/workspace/src/utilities/typescript';
import { getHashDigest, interpolateName } from 'loader-utils';
import type { Configuration } from 'webpack';

import { NormalizedWebpackExecutorOptions } from '../schema';

// TODO(jack): These should be inlined in a single function so it is easier to understand
import { getBaseWebpackPartial } from '../../../utils/config';
import { getBrowserConfig } from '../../../utils/webpack/partials/browser';
import { getCommonConfig } from '../../../utils/webpack/partials/common';
import { getStylesConfig } from '../../../utils/webpack/partials/styles';
import { ExecutorContext } from '@nrwl/devkit';
import MiniCssExtractPlugin = require('mini-css-extract-plugin');
import webpackMerge = require('webpack-merge');
import postcssImports = require('postcss-import');

// PostCSS options depend on the webpack loader, but we need to set the `config` path as a string due to this check:
// https://github.com/webpack-contrib/postcss-loader/blob/0d342b1/src/utils.js#L36
interface PostcssOptions {
(loader: any): any;

config?: string;
}
import { NormalizedWebpackExecutorOptions } from '../schema';
import { withNx } from '../../../utils/with-nx';
import { withWeb } from '../../../utils/with-web';
import { composePlugins } from '@nrwl/webpack';

interface GetWebpackConfigOverrides {
root: string;
Expand All @@ -33,285 +15,12 @@ interface GetWebpackConfigOverrides {
export function getWebpackConfig(
context: ExecutorContext,
options: NormalizedWebpackExecutorOptions,
isScriptOptimizeOn?: boolean,
overrides?: GetWebpackConfigOverrides
): Configuration {
const tsConfig = readTsConfig(options.tsConfig);
const workspaceRoot = context.root;

let sourceRoot: string;
let projectRoot: string;
if (overrides) {
projectRoot = overrides.root;
sourceRoot = overrides.sourceRoot;
} else {
const project =
context.projectsConfigurations.projects[context.projectName];
projectRoot = project.root;
sourceRoot = project.sourceRoot;
}

const wco: any = {
root: workspaceRoot,
projectRoot: resolve(workspaceRoot, projectRoot),
sourceRoot: resolve(workspaceRoot, sourceRoot),
buildOptions: convertBuildOptions(options),
console,
tsConfig,
tsConfigPath: options.tsConfig,
};
// TODO(jack): Replace merge behavior with an inlined config so it is easier to understand.
return webpackMerge.merge([
_getBaseWebpackPartial(
context,
options,
isScriptOptimizeOn,
tsConfig.options.emitDecoratorMetadata,
overrides
),
options.target === 'web'
? getPolyfillsPartial(options.polyfills, isScriptOptimizeOn)
: {},
options.target === 'web'
? getStylesPartial(
wco.root,
wco.projectRoot,
wco.buildOptions,
options.extractCss,
options.postcssConfig
)
: {},
getCommonPartial(wco),
options.target === 'web' ? getBrowserConfig(wco) : {},
]);
}

function _getBaseWebpackPartial(
context: ExecutorContext,
options: NormalizedWebpackExecutorOptions,
isScriptOptimizeOn: boolean,
emitDecoratorMetadata: boolean,
/** @deprecated Check options object instead */
_isScriptOptimizeOn?: boolean,
// TODO(jack): The overrides object is needed for component testing (can we set it on context object?)
overrides?: GetWebpackConfigOverrides
) {
let partial = getBaseWebpackPartial(
options,
{
isScriptOptimizeOn,
emitDecoratorMetadata,
configuration: overrides?.configuration ?? context.configurationName,
},
context
);
delete partial.resolve.mainFields;
return partial;
}

function getCommonPartial(wco: any): Configuration {
const commonConfig: Configuration = <Configuration>getCommonConfig(wco);
delete commonConfig.entry;
delete commonConfig.resolve.modules;
delete commonConfig.resolve.extensions;
delete commonConfig.output.path;
delete commonConfig.module;
return commonConfig;
}

export function getStylesPartial(
workspaceRoot: string,
projectRoot: string,
options: any,
extractCss: boolean,
postcssConfig?: string
): Configuration {
const includePaths: string[] = [];

if (options?.stylePreprocessorOptions?.includePaths?.length > 0) {
options.stylePreprocessorOptions.includePaths.forEach(
(includePath: string) =>
includePaths.push(path.resolve(workspaceRoot, includePath))
);
}

const partial = getStylesConfig(workspaceRoot, options, includePaths);
const rules = partial.module.rules.map((rule) => {
if (!Array.isArray(rule.use)) {
return rule;
}
rule.use = rule.use.map((loaderConfig) => {
if (
typeof loaderConfig === 'object' &&
loaderConfig.loader === require.resolve('raw-loader')
) {
return {
loader: require.resolve('style-loader'),
};
}
return loaderConfig;
});
return rule;
});

const loaderModulesOptions = {
modules: {
mode: 'local',
getLocalIdent: getCSSModuleLocalIdent,
},
importLoaders: 1,
};
const postcssOptions: PostcssOptions = () => ({
plugins: [
postcssImports({
addModulesDirectories: includePaths,
resolve: (url: string) => (url.startsWith('~') ? url.slice(1) : url),
}),
],
});
// If a path to postcssConfig is passed in, set it for app and all libs, otherwise
// use automatic detection.
if (typeof postcssConfig === 'string') {
postcssOptions.config = path.join(workspaceRoot, postcssConfig);
}

const commonLoaders = [
{
loader: extractCss
? MiniCssExtractPlugin.loader
: require.resolve('style-loader'),
},
{
loader: require.resolve('css-loader'),
options: loaderModulesOptions,
},
{
loader: require.resolve('postcss-loader'),
options: {
implementation: require('postcss'),
postcssOptions: postcssOptions,
},
},
];

partial.module.rules = [
{
test: /\.css$|\.scss$|\.sass$|\.less$|\.styl$/,
oneOf: [
{
test: /\.module\.css$/,
use: commonLoaders,
},
{
test: /\.module\.(scss|sass)$/,
use: [
...commonLoaders,
{
loader: require.resolve('sass-loader'),
options: {
implementation: require('sass'),
sassOptions: {
fiber: false,
precision: 8,
includePaths,
},
},
},
],
},
{
test: /\.module\.less$/,
use: [
...commonLoaders,
{
loader: require.resolve('less-loader'),
options: {
lessOptions: {
paths: includePaths,
},
},
},
],
},
{
test: /\.module\.styl$/,
use: [
...commonLoaders,
{
loader: require.resolve('stylus-loader'),
options: {
stylusOptions: {
include: includePaths,
},
},
},
],
},
...rules,
],
},
];
return partial;
}

export function getPolyfillsPartial(
polyfills: string,
isScriptOptimizeOn: boolean
): Configuration {
const config = {
entry: {} as { [key: string]: string[] },
};

if (polyfills && isScriptOptimizeOn) {
// Safari 10.1 supports <script type="module"> but not <script nomodule>.
// Need to patch it up so the browser doesn't load both sets.
config.entry.polyfills = [
require.resolve('@nrwl/webpack/src/utils/webpack/safari-nomodule.js'),
...(polyfills ? [polyfills] : []),
];
} else {
if (polyfills) {
config.entry.polyfills = [polyfills];
}
}

return config;
}

export function getCSSModuleLocalIdent(
context,
localIdentName,
localName,
options
) {
// Use the filename or folder name, based on some uses the index.js / index.module.(css|scss|sass) project style
const fileNameOrFolder = context.resourcePath.match(
/index\.module\.(css|scss|sass|styl)$/
)
? '[folder]'
: '[name]';
// Create a hash based on a the file location and class name. Will be unique across a project, and close to globally unique.
const hash = getHashDigest(
posix.relative(context.rootContext, context.resourcePath) + localName,
'md5',
'base64',
5
);
// Use loaderUtils to find the file or folder name
const className = interpolateName(
context,
`${fileNameOrFolder}_${localName}__${hash}`,
options
);
// Remove the .module that appears in every classname when based on the file and replace all "." with "_".
return className.replace('.module_', '_').replace(/\./g, '_');
}

export function convertBuildOptions(
buildOptions: NormalizedWebpackExecutorOptions
): any {
const options = buildOptions as any;
return {
...options,
buildOptimizer: options.optimization,
forkTypeChecker: false,
lazyModules: [] as string[],
};
const config: Configuration = {};
const configure = composePlugins(withNx(), withWeb());
return configure(config, { options, context });
}
10 changes: 10 additions & 0 deletions packages/webpack/src/plugins/stats-json-plugin.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import { Compiler, sources } from 'webpack';

export class StatsJsonPlugin {
apply(compiler: Compiler) {
compiler.hooks.emit.tap('angular-cli-stats', (compilation) => {
const data = JSON.stringify(compilation.getStats().toJson('verbose'));
compilation.assets[`stats.json`] = new sources.RawSource(data);
});
}
}
Loading

0 comments on commit 5b69c18

Please sign in to comment.